Mantid
Loading...
Searching...
No Matches
LoadILLDiffraction.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
12#include "MantidAPI/Run.h"
24#include "MantidNexus/H5Util.h"
27#include "MantidNexus/NexusFile.h"
28
29#include <H5Cpp.h>
30#include <boost/algorithm/string.hpp>
31#include <boost/math/special_functions/round.hpp>
32#include <filesystem>
33#include <numeric>
34
35namespace Mantid::DataHandling {
36
37using namespace API;
38using namespace Geometry;
39using namespace H5;
40using namespace Kernel;
41using namespace Nexus;
42using Types::Core::DateAndTime;
43
44namespace {
45// This defines the number of physical pixels in D20 (low resolution mode)
46// Then each pixel can be split into 2 (nominal) or 3 (high resolution) by DAQ
47constexpr size_t D20_NUMBER_PIXELS = 1600;
48// This defines the number of dead pixels on each side in low resolution mode
49constexpr size_t D20_NUMBER_DEAD_PIXELS = 32;
50// This defines the number of monitors in the instrument. If there are cases
51// where this is no longer one this decleration should be moved.
52constexpr size_t NUMBER_MONITORS = 1;
53// This is the angular size of a pixel in degrees (in low resolution mode)
54constexpr double D20_PIXEL_SIZE = 0.1;
55// The conversion factor from radian to degree
56constexpr double RAD_TO_DEG = 180. / M_PI;
57// A factor to compute E from lambda: E (mev) = waveToE/lambda(A)
58constexpr double WAVE_TO_E = 81.8;
59} // namespace
60
61// Register the algorithm into the AlgorithmFactory
63
64
65int LoadILLDiffraction::confidence(Nexus::NexusDescriptorLazy &descriptor) const {
66
67 // fields existent only at the ILL Diffraction
68 // the second one is to recognize D1B, Tx field eliminates SALSA
69 // the third one is to recognize IN5/PANTHER/SHARP scan mode
70 if ((descriptor.isEntry("/entry0/instrument/2theta") && !descriptor.isEntry("/entry0/instrument/Tx")) ||
71 descriptor.isEntry("/entry0/instrument/Canne")) {
72 return 80;
73 } else {
74 return 0;
75 }
76}
77
79const std::string LoadILLDiffraction::name() const { return "LoadILLDiffraction"; }
80
82int LoadILLDiffraction::version() const { return 1; }
83
85const std::string LoadILLDiffraction::category() const { return "DataHandling\\Nexus;ILL\\Diffraction"; }
86
88const std::string LoadILLDiffraction::summary() const { return "Loads ILL diffraction nexus files."; }
89
93LoadILLDiffraction::LoadILLDiffraction() : m_instNames({"D20", "D2B", "D1B", "D4C", "IN5", "PANTHER", "SHARP"}) {}
98 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, ".nxs"),
99 "File path of the data file to load");
100 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("OutputWorkspace", "", Direction::Output),
101 "The output workspace.");
102 declareProperty("TwoThetaOffset", 0.0, "2 theta offset for D1B data, in degrees.");
103 declareProperty(std::make_unique<PropertyWithValue<bool>>("AlignTubes", false, Direction::Input),
104 "Apply vertical and horizontal alignment of tubes as defined in IPF");
105 declareProperty("ConvertAxisAndTranspose", false,
106 "Whether to convert the spectrum axis to 2theta and "
107 "transpose (for 1D detector and no-scan configuration)");
108}
109
110std::map<std::string, std::string> LoadILLDiffraction::validateInputs() {
111 std::map<std::string, std::string> issues;
112 return issues;
113}
114
119
120 Progress progress(this, 0, 1, 4);
121
122 m_filename = getPropertyValue("Filename");
123
124 m_scanVar.clear();
125 progress.report("Loading the scanned variables");
126 loadScanVars();
127
128 progress.report("Loading the detector scan data");
129 loadDataScan();
130
131 progress.report("Loading the metadata");
132 loadMetaData();
133
134 progress.report("Setting additional sample logs");
136
137 if (m_instName != "D2B" && m_scanType != DetectorScan && getProperty("ConvertAxisAndTranspose"))
139
140 setProperty("OutputWorkspace", m_outWorkspace);
141}
142
147
148 // open the root entry
149 NXRoot dataRoot(m_filename);
150 NXEntry firstEntry = dataRoot.openFirstEntry();
151 m_instName = firstEntry.getString("instrument/name");
152 m_startTime = DateAndTime(LoadHelper::dateTimeInIsoFormat(firstEntry.getString("start_time")));
153 // Load the data
154 std::string dataName = "data_scan/detector_data/data";
155 g_log.notice() << "Loading data from " + dataName;
156 auto data = firstEntry.openNXDataSet<int>(dataName);
157 data.load();
158
159 // read the scan data
160 auto scan = LoadHelper::getDoubleDataset(firstEntry, "data_scan/scanned_variables");
161 scan.load();
162
163 // read which variables are scanned
164 NXInt scanned = firstEntry.openNXInt("data_scan/scanned_variables/variables_names/scanned");
165 scanned.load();
166
167 // read what is going to be the axis
168 NXInt axis = firstEntry.openNXInt("data_scan/scanned_variables/variables_names/axis");
169 axis.load();
170
171 // read the starting two theta
172 double twoThetaValue = 0;
173 if (m_instName == "D1B") {
174 if (getPointerToProperty("TwoThetaOffset")->isDefault()) {
175 g_log.notice("A 2theta offset angle is necessary for D1B data.");
176 twoThetaValue = 0;
177 } else {
178 twoThetaValue = getProperty("TwoThetaOffset");
179 }
180 } else {
181 std::string twoThetaPath = "instrument/2theta/value";
182 NXFloat twoTheta0 = firstEntry.openNXFloat(twoThetaPath);
183 twoTheta0.load();
184 twoThetaValue = double(twoTheta0[0]);
185 }
186
187 // figure out the dimensions
188 m_sizeDim1 = static_cast<size_t>(data.dim1());
189 m_sizeDim2 = static_cast<size_t>(data.dim2());
191 m_numberScanPoints = static_cast<size_t>(data.dim0());
192 g_log.debug() << "Read " << m_numberDetectorsRead << " detectors and " << m_numberScanPoints << "\n";
193
194 // set which scanned variables are scanned, which should be the axis
195 for (size_t i = 0; i < m_scanVar.size(); ++i) {
196 m_scanVar[i].setAxis(axis[static_cast<int>(i)]);
197 m_scanVar[i].setScanned(scanned[static_cast<int>(i)]);
198 }
199
203
204 if (m_scanType == DetectorScan) {
206 fillMovingInstrumentScan(data, scan);
207 } else {
209 fillStaticInstrumentScan(data, scan, twoThetaValue);
210 }
211
213
214 firstEntry.close();
215 dataRoot.close();
216}
217
222
223 auto &mutableRun = m_outWorkspace->mutableRun();
224 mutableRun.addProperty("Facility", std::string("ILL"));
225
226 // get some information from the NeXus file
227 try {
228 Nexus::File filehandle(m_filename, NXaccess::READ);
229 LoadHelper::addNexusFieldsToWsRun(filehandle, mutableRun);
230 } catch (Nexus::Exception const &e) {
231 g_log.debug() << "Failed to open nexus file \"" << m_filename << "\" in read mode: " << e.what() << "\n";
232 }
233
234 if (mutableRun.hasProperty("run_number"))
235 mutableRun.addProperty("run_list", mutableRun.getPropertyValueAsType<int>("run_number"));
236 else
237 throw std::runtime_error("Failed to find run_number in Run object");
238
239 if (!mutableRun.hasProperty("Detector.calibration_file"))
240 mutableRun.addProperty("Detector.calibration_file", std::string("none"));
241}
242
248 size_t nSpectra = m_numberDetectorsActual + NUMBER_MONITORS;
249 size_t nBins = 1;
250
251 if (m_scanType == DetectorScan) {
253 } else if (m_scanType == OtherScan) {
254 nBins = m_numberScanPoints;
255 }
256
257 m_outWorkspace = WorkspaceFactory::Instance().create("Workspace2D", nSpectra, nBins, nBins);
258
259 // the start time is needed in the workspace when loading the parameter file
260 m_outWorkspace->mutableRun().addProperty("start_time", m_startTime.toISO8601String());
261}
262
269 const size_t nTimeIndexes = m_numberScanPoints;
270 const size_t nBins = 1;
271 const bool isPointData = true;
272
273 const auto instrumentWorkspace = WorkspaceFactory::Instance().create("Workspace2D", 1, 1, 1);
274 auto &run = instrumentWorkspace->mutableRun();
275 // the start time is needed in the workspace when loading the parameter file
276 run.addProperty("start_time", m_startTime.toISO8601String());
277
279 LoadHelper::loadEmptyInstrument(instrumentWorkspace, m_instName);
280 const auto &instrument = instrumentWorkspace->getInstrument();
281 auto &params = instrumentWorkspace->instrumentParameters();
282
283 const auto &referenceComponentPosition = getReferenceComponentPosition(instrumentWorkspace);
284
285 double refR, refTheta, refPhi;
286 referenceComponentPosition.getSpherical(refR, refTheta, refPhi);
287
288 if (m_instName == "D2B") {
289 const bool doAlign = getProperty("AlignTubes");
290 auto &compInfo = instrumentWorkspace->mutableComponentInfo();
291
292 Geometry::IComponent_const_sptr detectors = instrument->getComponentByName("detectors");
293 const auto detCompIndex = compInfo.indexOf(detectors->getComponentID());
294 const auto tubes = compInfo.children(detCompIndex);
295 const size_t nTubes = tubes.size();
296 Geometry::IComponent_const_sptr tube1 = instrument->getComponentByName("tube_1");
297 const auto tube1CompIndex = compInfo.indexOf(tube1->getComponentID());
298 const auto pixels = compInfo.children(tube1CompIndex);
299 const size_t nPixels = pixels.size();
300
301 Geometry::IComponent_const_sptr pixel = instrument->getComponentByName("standard_pixel");
303 pixel->getBoundingBox(bb);
304 m_pixelHeight = bb.yMax() - bb.yMin();
305
306 const auto tubeAnglesStr = params.getString("D2B", "tube_angles");
307 if (!tubeAnglesStr.empty() && doAlign) {
308 std::vector<std::string> tubeAngles;
309 boost::split(tubeAngles, tubeAnglesStr[0], boost::is_any_of(","));
310 const double ref = -refTheta;
311 for (size_t i = 1; i <= nTubes; ++i) {
312 const std::string compName = "tube_" + std::to_string(i);
313 Geometry::IComponent_const_sptr component = instrument->getComponentByName(compName);
314 double r, theta, phi;
315 V3D oldPos = component->getPos();
316 oldPos.getSpherical(r, theta, phi);
317 V3D newPos;
318 const double angle = std::stod(tubeAngles[i - 1]);
319 const double finalAngle = fabs(ref - angle);
320 g_log.debug() << "Rotating " << compName << "to " << finalAngle << "rad\n";
321 newPos.spherical(r, finalAngle, phi);
322 const auto componentIndex = compInfo.indexOf(component->getComponentID());
323 compInfo.setPosition(componentIndex, newPos);
324 }
325 }
326
327 const auto tubeCentersStr = params.getString("D2B", "tube_centers");
328 if (!tubeCentersStr.empty() && doAlign) {
329 std::vector<std::string> tubeCenters;
330 double maxYOffset = 0.;
331 boost::split(tubeCenters, tubeCentersStr[0], boost::is_any_of(","));
332 for (size_t i = 1; i <= nTubes; ++i) {
333 const std::string compName = "tube_" + std::to_string(i);
334 Geometry::IComponent_const_sptr component = instrument->getComponentByName(compName);
335 const double offset = std::stod(tubeCenters[i - 1]) - (double(nPixels) / 2 - 0.5);
336 const double y = -offset * m_pixelHeight;
337 V3D translation(0, y, 0);
338 if (std::fabs(y) > maxYOffset) {
339 maxYOffset = std::fabs(y);
340 }
341 g_log.debug() << "Moving " << compName << " to " << y << "\n";
342 V3D pos = component->getPos() + translation;
343 const auto componentIndex = compInfo.indexOf(component->getComponentID());
344 compInfo.setPosition(componentIndex, pos);
345 }
346 m_maxHeight = double(nPixels + 1) * m_pixelHeight / 2 + maxYOffset;
347 }
348 }
349
350 auto scanningWorkspaceBuilder = DataObjects::ScanningWorkspaceBuilder(instrument, nTimeIndexes, nBins, isPointData);
351
352 std::vector<double> timeDurations = getScannedVaribleByPropertyName(scan, "Time");
353 scanningWorkspaceBuilder.setTimeRanges(m_startTime, timeDurations);
354
355 g_log.debug() << "First time index starts at:" << m_startTime.toISO8601String() << "\n";
356
357 g_log.debug() << "Last time index ends at:"
358 << (m_startTime + std::accumulate(timeDurations.begin(), timeDurations.end(), 0.0)).toISO8601String()
359 << "\n";
360
361 // Angles in the NeXus files are the absolute position for tube 1
362 std::vector<double> tubeAngles = getScannedVaribleByPropertyName(scan, "Position");
363
364 // Convert the tube positions to relative rotations for all detectors
365 calculateRelativeRotations(tubeAngles, referenceComponentPosition);
366
367 auto rotationCentre = V3D(0, 0, 0);
368 auto rotationAxis = V3D(0, 1, 0);
369 scanningWorkspaceBuilder.setRelativeRotationsForScans(std::move(tubeAngles), rotationCentre, rotationAxis);
370
371 m_outWorkspace = scanningWorkspaceBuilder.buildWorkspace();
372}
373
384 if (m_instName == "D2B") {
385 return instrumentWorkspace->getInstrument()->getComponentByName("tube_128")->getPos();
386 }
387
388 const auto &detInfo = instrumentWorkspace->detectorInfo();
389 const auto &indexOfFirstDet = detInfo.indexOf(1);
390 return detInfo.position(indexOfFirstDet);
391}
392
403void LoadILLDiffraction::calculateRelativeRotations(std::vector<double> &tubeRotations, const V3D &firstTubePosition) {
404 // The rotations in the NeXus file are the absolute rotation of the first
405 // tube. Here we get the angle of that tube as defined in the IDF.
406
407 double firstTubeRotationAngle = firstTubePosition.angle(V3D(0, 0, 1)) * RAD_TO_DEG;
408
409 // note that for D20 we have to subtract the offset here
410 // unlike in the static detector case, because in the transform
411 // below, we take (angle - firstTubeRotatingAngle)
412 if (m_instName == "D20") {
413 firstTubeRotationAngle -= m_offsetTheta;
414 } else if (m_instName == "D2B") {
415 firstTubeRotationAngle = -firstTubeRotationAngle;
416 std::transform(tubeRotations.begin(), tubeRotations.end(), tubeRotations.begin(),
417 [&](double angle) { return (-angle); });
418 }
419
420 g_log.debug() << "First tube rotation:" << firstTubeRotationAngle << "\n";
421
422 // Now pass calculate the rotations to apply for each time index.
423 std::transform(tubeRotations.begin(), tubeRotations.end(), tubeRotations.begin(),
424 [&](double angle) { return (angle - firstTubeRotationAngle); });
425
426 g_log.debug() << "Instrument rotations to be applied : " << tubeRotations.front() << " to " << tubeRotations.back()
427 << "\n";
428}
429
437
438 std::vector<double> axis = {0.};
439 std::vector<double> monitor = getMonitor(scan);
440
441 // First load the monitors
442 for (size_t i = 0; i < NUMBER_MONITORS; ++i) {
443 for (size_t j = 0; j < m_numberScanPoints; ++j) {
444 const auto wsIndex = j + i * m_numberScanPoints;
445 m_outWorkspace->mutableY(wsIndex) = monitor[j];
446 m_outWorkspace->mutableE(wsIndex) = sqrt(monitor[j]);
447 m_outWorkspace->mutableX(wsIndex) = axis;
448 }
449 }
450
451 // prepare inputs, dimension orders and list of accepted IDs for exclusion of inactive detectors (D20)
452 std::tuple<int, int, int> dimOrder{2, 1, 0}; // scan - tube - pixel
453 std::set<int> acceptedIDs;
454 if (m_numberDetectorsActual != data.dim1() * data.dim2()) {
455 for (auto index = NUMBER_MONITORS; index < m_numberDetectorsActual + NUMBER_MONITORS; ++index)
456 acceptedIDs.insert(static_cast<detid_t>(index));
457 }
458 std::vector<int> customDetectorIDs;
459 // Assign detector counts
460 LoadHelper::fillMovingWorkspace(m_outWorkspace, data, axis, NUMBER_MONITORS, acceptedIDs, customDetectorIDs,
461 dimOrder);
462}
463
472void LoadILLDiffraction::fillStaticInstrumentScan(const NXInt &data, const NXDouble &scan, const double &twoTheta0) {
473
474 const std::vector<double> axis = getAxis(scan);
475 const std::vector<double> monitor = getMonitor(scan);
476
477 std::size_t startIndex = NUMBER_MONITORS;
478
479 // Assign monitor counts
480 m_outWorkspace->mutableX(0) = axis;
481 m_outWorkspace->mutableY(0) = monitor;
482 std::transform(monitor.begin(), monitor.end(), m_outWorkspace->mutableE(0).begin(), [](double e) { return sqrt(e); });
483
484 // prepare inputs, dimension orders and list of accepted IDs for exclusion of inactive detectors (D20)
485 std::tuple<int, int, int> dimOrder{2, 1, 0}; // scan - tube - pixel
486 std::set<detid_t> acceptedIDs;
487 if (m_numberDetectorsActual != data.dim1() * data.dim2()) {
488 for (std::size_t i = startIndex; i < startIndex + m_numberDetectorsActual; ++i)
489 acceptedIDs.insert(static_cast<detid_t>(i));
490 }
491 // Assign detector counts
492 LoadHelper::fillStaticWorkspace(m_outWorkspace, data, axis, startIndex, true, std::vector<int>(), acceptedIDs,
493 dimOrder);
494
495 // Link the instrument
497 // Move to the starting 2theta
498 moveTwoThetaZero(twoTheta0);
499}
500
505 H5File h5file(m_filename, H5F_ACC_RDONLY, Nexus::H5Util::defaultFileAcc());
506
507 Group entry0 = h5file.openGroup("entry0");
508 Group dataScan = entry0.openGroup("data_scan");
509 Group scanVar = dataScan.openGroup("scanned_variables");
510 Group varNames = scanVar.openGroup("variables_names");
511
512 const auto names = Nexus::H5Util::readStringVector(varNames, "name");
513 const auto properties = Nexus::H5Util::readStringVector(varNames, "property");
514 const auto units = Nexus::H5Util::readStringVector(varNames, "unit");
515
516 for (size_t i = 0; i < names.size(); ++i) {
517 m_scanVar.emplace_back(ScannedVariables(names[i], properties[i], units[i]));
518 }
519
520 varNames.close();
521 scanVar.close();
522 dataScan.close();
523 entry0.close();
524 h5file.close();
525}
526
532 auto absoluteTimes = getAbsoluteTimes(scan);
533 auto &mutableRun = m_outWorkspace->mutableRun();
534 for (size_t i = 0; i < m_scanVar.size(); ++i) {
535 if (!m_scanVar[i].property.starts_with("Monitor")) {
536 const std::string scanVarName = boost::algorithm::to_lower_copy(m_scanVar[i].name);
537 const std::string scanVarProp = boost::algorithm::to_lower_copy(m_scanVar[i].property);
538 const std::string propName = scanVarName + "." + scanVarProp;
539 if (m_scanVar[i].scanned == 1) {
540 mutableRun.addProperty("ScanVar", propName, true);
541 }
542 auto property = std::make_unique<TimeSeriesProperty<double>>(propName);
543 for (size_t j = 0; j < m_numberScanPoints; ++j) {
544 property->addValue(absoluteTimes[j], scan(static_cast<int>(i), static_cast<int>(j)));
545 }
546 mutableRun.addLogData(std::move(property), true);
547 }
548 }
549}
550
562 const std::string &propertyName) const {
563 std::vector<double> scannedVariable;
564
565 for (size_t i = 0; i < m_scanVar.size(); ++i) {
566 if (m_scanVar[i].property == propertyName) {
567 for (size_t j = 0; j < m_numberScanPoints; ++j) {
568 scannedVariable.emplace_back(scan(static_cast<int>(i), static_cast<int>(j)));
569 }
570 break;
571 }
572 }
573
574 if (scannedVariable.empty())
575 throw std::runtime_error("Can not load file because scanned variable with property name " + propertyName +
576 " was not found");
577
578 return scannedVariable;
579}
580
588std::vector<double> LoadILLDiffraction::getMonitor(const NXDouble &scan) const {
589
590 std::vector<double> monitor = {0.};
591 for (size_t i = 0; i < m_scanVar.size(); ++i) {
592 if ((m_scanVar[i].name == "Monitor1") || (m_scanVar[i].name == "Monitor_1") || (m_scanVar[i].name == "monitor1")) {
593 monitor.assign(scan() + m_numberScanPoints * i, scan() + m_numberScanPoints * (i + 1));
594 return monitor;
595 }
596 }
597 throw std::runtime_error("Monitors not found in scanned variables");
598}
599
605std::vector<double> LoadILLDiffraction::getAxis(const NXDouble &scan) const {
606
607 std::vector<double> axis = {0.};
608 if (m_scanType == OtherScan) {
609 for (size_t i = 0; i < m_scanVar.size(); ++i) {
610 if (m_scanVar[i].axis == 1) {
611 axis.assign(scan() + m_numberScanPoints * i, scan() + m_numberScanPoints * (i + 1));
612 break;
613 }
614 }
615 }
616 return axis;
617}
618
624std::vector<double> LoadILLDiffraction::getDurations(const NXDouble &scan) const {
625 std::vector<double> timeDurations;
626 for (size_t i = 0; i < m_scanVar.size(); ++i) {
627 if (m_scanVar[i].property.starts_with("Time")) {
628 timeDurations.assign(scan() + m_numberScanPoints * i, scan() + m_numberScanPoints * (i + 1));
629 break;
630 }
631 }
632 return timeDurations;
633}
634
640std::vector<DateAndTime> LoadILLDiffraction::getAbsoluteTimes(const NXDouble &scan) const {
641 std::vector<DateAndTime> times;
642 std::vector<double> durations = getDurations(scan);
643 DateAndTime time = m_startTime;
644 times.emplace_back(time);
645 size_t timeIndex = 1;
646 while (timeIndex < m_numberScanPoints) {
647 time += durations[timeIndex - 1];
648 times.emplace_back(time);
649 ++timeIndex;
650 }
651 return times;
652}
653
658 ScanType result = NoScan;
659 if (m_instName == "D2B") {
660 result = DetectorScan;
661 } else {
662 if (m_numberScanPoints != 1) {
663 for (const auto &scanVar : m_scanVar) {
664 if (scanVar.scanned == 1) {
665 result = OtherScan;
666 if (scanVar.name == "2theta") {
667 result = DetectorScan;
668 break;
669 }
670 }
671 }
672 }
673 }
674 m_scanType = result;
675}
676
682 if (m_instNames.find(m_instName) == m_instNames.end()) {
683 throw std::runtime_error("Instrument " + m_instName + " not supported.");
684 } else {
686 if (m_instName == "D20") {
687 // Here we have to hardcode the numbers of pixels.
688 // The only way is to read the size of the detectors read from the files
689 // and based on it decide which of the 3 alternative IDFs to load.
690 // Some amount of pixels are dead on at right end, these have to be
691 // subtracted
692 // correspondingly dependent on the resolution mode
693 m_resolutionMode = m_numberDetectorsRead / D20_NUMBER_PIXELS;
694 size_t activePixels = D20_NUMBER_PIXELS - 2 * D20_NUMBER_DEAD_PIXELS;
696
697 if (m_resolutionMode > 3 || m_resolutionMode < 1) {
698 throw std::runtime_error("Unknown resolution mode for instrument " + m_instName);
699 }
700 if (m_resolutionMode == 1) {
701 m_instName += "_lr";
702 } else if (m_resolutionMode == 3) {
703 m_instName += "_hr";
704 }
705 }
706 g_log.debug() << "Instrument name is " << m_instName << " and has " << m_numberDetectorsActual
707 << " actual detectors.\n";
708 }
709}
710
715void LoadILLDiffraction::moveTwoThetaZero(double twoTheta0Read) {
716 Instrument_const_sptr instrument = m_outWorkspace->getInstrument();
717 IComponent_const_sptr component = instrument->getComponentByName("detector");
718 double twoTheta0Actual = twoTheta0Read;
719 if (m_instName == "D20") {
720 twoTheta0Actual += m_offsetTheta;
721 }
722 Quat rotation(twoTheta0Actual, V3D(0, 1, 0));
723 g_log.debug() << "Setting 2theta0 to " << twoTheta0Actual;
724 auto &componentInfo = m_outWorkspace->mutableComponentInfo();
725 const auto componentIndex = componentInfo.indexOf(component->getComponentID());
726 componentInfo.setRotation(componentIndex, rotation);
727}
728
735std::string LoadILLDiffraction::getInstrumentFilePath(const std::string &instName) const {
736
737 std::filesystem::path directory(ConfigService::Instance().getInstrumentDirectory());
738 std::filesystem::path file(instName + "_Definition.xml");
739 std::filesystem::path fullPath = std::filesystem::path(directory) / file;
740 return fullPath.string();
741}
742
747 Run &run = m_outWorkspace->mutableRun();
748 std::string scanTypeStr = "NoScan";
749 if (m_scanType == DetectorScan) {
750 scanTypeStr = "DetectorScan";
751 } else if (m_scanType == OtherScan) {
752 scanTypeStr = "OtherScan";
753 }
754 run.addLogData(new PropertyWithValue<std::string>("ScanType", std::move(scanTypeStr)));
755 run.addLogData(new PropertyWithValue<double>("PixelSize", D20_PIXEL_SIZE / static_cast<double>(m_resolutionMode)));
756 std::string resModeStr = "Nominal";
757 if (m_resolutionMode == 1) {
758 resModeStr = "Low";
759 } else if (m_resolutionMode == 3) {
760 resModeStr = "High";
761 }
762 run.addLogData(new PropertyWithValue<std::string>("ResolutionMode", std::move(resModeStr)));
763 if (m_scanType != NoScan) {
764 run.addLogData(new PropertyWithValue<int>("ScanSteps", static_cast<int>(m_numberScanPoints)));
765 }
766 double eFixed;
767 if (run.hasProperty("wavelength")) {
768 double lambda = run.getLogAsSingleValue("wavelength");
769 eFixed = WAVE_TO_E / (lambda * lambda);
770 } else if (run.hasProperty("Monochromator.ei")) { // D4C, wavelength is not specified and Ei is provided directly
771 eFixed = run.getPropertyValueAsType<double>("Monochromator.ei");
772 } else {
773 throw std::runtime_error("Neither wavelength nor Monochromator.ei are not specified in the loaded file.");
774 }
775 run.addLogData(std::make_unique<Kernel::PropertyWithValue<double>>(PropertyWithValue<double>("Ei", eFixed)), true);
776 run.addLogData(new PropertyWithValue<size_t>("NumberOfDetectors", m_numberDetectorsActual));
777 if (m_pixelHeight != 0.) {
778 run.addLogData(new PropertyWithValue<double>("PixelHeight", m_pixelHeight));
779 }
780 if (m_maxHeight != 0.) {
781 run.addLogData(new PropertyWithValue<double>("MaxHeight", m_maxHeight));
782 }
783}
784
789 m_offsetTheta = static_cast<double>(D20_NUMBER_DEAD_PIXELS) * D20_PIXEL_SIZE -
790 D20_PIXEL_SIZE / (static_cast<double>(m_resolutionMode) * 2);
791}
792
797 auto extractor = createChildAlgorithm("ExtractSpectra");
798 extractor->setProperty("InputWorkspace", m_outWorkspace);
799 extractor->setProperty("StartWorkspaceIndex", 1);
800 extractor->setProperty("OutputWorkspace", "__unused");
801 extractor->execute();
802 API::MatrixWorkspace_sptr det = extractor->getProperty("OutputWorkspace");
803 auto converter = createChildAlgorithm("ConvertSpectrumAxis");
804 converter->setProperty("InputWorkspace", det);
805 converter->setProperty("OutputWorkspace", "__unused");
806 converter->setProperty("Target", "SignedTheta");
807 converter->execute();
808 API::MatrixWorkspace_sptr converted = converter->getProperty("OutputWorkspace");
809 auto transposer = createChildAlgorithm("Transpose");
810 transposer->setProperty("InputWorkspace", converted);
811 transposer->setProperty("OutputWorkspace", "__unused");
812 transposer->execute();
813 API::MatrixWorkspace_sptr transposed = transposer->getProperty("OutputWorkspace");
814 m_outWorkspace = transposed;
815}
816
817} // namespace Mantid::DataHandling
const std::vector< double > * lambda
std::map< DeltaEMode::Type, std::string > index
int64_t nSpectra
#define fabs(x)
Definition Matrix.cpp:22
Mantid::Kernel::Quat(ComponentInfo::* rotation)(const size_t) const
#define DECLARE_NEXUS_LAZY_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_LAZY_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM mac...
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Kernel::Property * getPointerToProperty(const std::string &name) const override
Get a property by name.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Kernel::Logger & g_log
Definition Algorithm.h:422
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
bool isDefault(const std::string &name) const
@ Load
allowed here which will be passed to the algorithm
void addLogData(Kernel::Property *p)
Add a log entry.
Definition LogManager.h:126
bool hasProperty(const std::string &name) const
Does the property exist on the object.
HeldType getPropertyValueAsType(const std::string &name) const
Get the value of a property as the given TYPE.
double getLogAsSingleValue(const std::string &name, Kernel::Math::StatisticType statistic=Kernel::Math::Mean) const
Definition LogManager.h:160
Helper class for reporting progress from algorithms.
Definition Progress.h:25
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
A property class for workspaces.
LoadILLDiffraction : Loads ILL diffraction nexus files.
std::vector< ScannedVariables > m_scanVar
holds the scan info
int version() const override
Algorithm's version for identification.
size_t m_sizeDim1
size of dim1, number of tubes (D2B) or the whole detector (D20)
size_t m_sizeDim2
size of dim2, number of pixels (1 for D20!)
size_t m_resolutionMode
resolution mode; 1:low, 2:nominal, 3:high
void moveTwoThetaZero(double)
Rotates the detector to the 2theta0 read from the file.
API::MatrixWorkspace_sptr m_outWorkspace
output workspace
void calculateRelativeRotations(std::vector< double > &instrumentAngles, const Kernel::V3D &firstTubePosition)
Convert from absolute rotation angle, around the sample, of tube 1, to a relative rotation angle arou...
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
std::string m_instName
instrument name to load the IDF
size_t m_numberDetectorsRead
number of cells read from file
Kernel::V3D getReferenceComponentPosition(const API::MatrixWorkspace_sptr &instrumentWorkspace)
Get the position of the component in the workspace which corresponds to the angle stored in the scann...
void fillStaticInstrumentScan(const Nexus::NXInt &, const Nexus::NXDouble &, const double &)
Fills the loaded data to the workspace when the detector is not moving during the run,...
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
std::vector< Types::Core::DateAndTime > getAbsoluteTimes(const Nexus::NXDouble &) const
Returns the vector of absolute times for each scan point.
std::string getInstrumentFilePath(const std::string &) const
Makes up the full path of the relevant IDF dependent on resolution mode.
const std::string category() const override
Algorithm's category for identification.
double m_pixelHeight
height of the pixel in D2B
void fillMovingInstrumentScan(const Nexus::NXInt &, const Nexus::NXDouble &)
Fills the counts for the instrument with moving detectors.
size_t m_numberDetectorsActual
number of cells actually active
ScanType m_scanType
NoScan, DetectorScan or OtherScan.
std::set< std::string > m_instNames
supported instruments
const std::string name() const override
Algorithms name for identification.
std::vector< double > getAxis(const Nexus::NXDouble &) const
Returns the x-axis.
std::vector< double > getScannedVaribleByPropertyName(const Nexus::NXDouble &scan, const std::string &propertyName) const
Gets a scanned variable based on its property type in the scanned_variables block.
void initStaticWorkspace()
Initializes the output workspace based on the resolved instrument, scan points, and scan type.
void initMovingWorkspace(const Nexus::NXDouble &scan)
Use the ScanningWorkspaceBuilder to create a time indexed workspace.
void computeThetaOffset()
Computes the 2theta offset of the decoder for D20.
void loadDataScan()
Loads the scanned detector data.
std::string m_filename
file name to load
void resolveInstrument()
Resolves the instrument based on instrument name and resolution mode.
void init() override
Initialize the algorithm's properties.
size_t m_numberScanPoints
number of scan points
double m_maxHeight
maximum absolute height of the D2B tubes
void setSampleLogs()
Adds some sample logs needed later by reduction.
void fillDataScanMetaData(const Nexus::NXDouble &)
Creates time series sample logs for the scanned variables.
std::vector< double > getMonitor(const Nexus::NXDouble &) const
Returns the monitor spectrum.
std::vector< double > getDurations(const Nexus::NXDouble &) const
Returns the durations in seconds for each scan point.
void exec() override
Executes the algorithm.
Types::Core::DateAndTime m_startTime
start time of acquisition
void convertAxisAndTranspose()
the 2theta offset for D20 to account for dead pixels
void loadScanVars()
Loads the scanned_variables/variables_names block.
void loadMetaData()
Dumps the metadata from the whole file to SampleLogs.
void resolveScanType()
Resolves the scan type.
ScanningWorkspaceBuilder : This is a helper class to make it easy to build a scanning workspace (a wo...
A simple structure that defines an axis-aligned cuboid shaped bounding box for a geometrical object.
Definition BoundingBox.h:33
double yMax() const
Return the maximum value of Y.
Definition BoundingBox.h:83
double yMin() const
Return the minimum value of Y.
Definition BoundingBox.h:81
The class Group represents a set of symmetry operations (or symmetry group).
Definition Group.h:135
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
The concrete, templated class for properties.
Class for quaternions.
Definition Quat.h:39
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Class for 3D vectors.
Definition V3D.h:34
void spherical(const double R, const double theta, const double phi) noexcept
Sets the vector position based on spherical coordinates.
Definition V3D.cpp:56
double angle(const V3D &) const
Angle between this and another vector.
Definition V3D.cpp:162
void getSpherical(double &R, double &theta, double &phi) const noexcept
Return the vector's position in spherical coordinates.
Definition V3D.cpp:116
Class that provides for a standard Nexus exception.
std::string getString(const std::string &name) const
Returns a string.
NXFloat openNXFloat(const std::string &name) const
Creates and opens a float dataset.
void close()
Close this class.
NXInt openNXInt(const std::string &name) const
Creates and opens an integer dataset.
NXDataSetTyped< T > openNXDataSet(const std::string &name) const
Templated method for creating datasets.
Templated class implementation of NXDataSet.
void load()
Read all of the datablock in.
dimsize_t dim2() const
Returns the number of elements along the third dimension.
dimsize_t dim1() const
Returns the number of elements along the second dimension.
Implements NXentry Nexus class.
Implements NXroot Nexus class.
NXEntry openFirstEntry()
Open the first NXentry in the file.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
void fillMovingWorkspace(const API::MatrixWorkspace_sptr &, const Mantid::Nexus::NXInt &, const std::vector< double > &xAxis, int64_t initialSpectrum=0, const std::set< detid_t > &acceptedID=std::set< int >(), const std::vector< detid_t > &customID=std::vector< int >(), const std::tuple< short, short, short > &axisOrder=std::tuple< short, short, short >(0, 1, 2))
Fills workspace with histogram data from provided data structure.
void addNexusFieldsToWsRun(Nexus::File &filehandle, API::Run &runDetails, const std::string &entryName="", bool useFullAddress=false)
Add properties from a nexus file to the workspace run.
Nexus::NXDouble getDoubleDataset(const Nexus::NXEntry &, const std::string &)
Fetches NXDouble data from the requested group name in the entry provided.
void fillStaticWorkspace(const API::MatrixWorkspace_sptr &, const Mantid::Nexus::NXInt &, const std::vector< double > &xAxis, int64_t initialSpectrum=0, bool pointData=false, const std::vector< detid_t > &detectorIDs=std::vector< int >(), const std::set< detid_t > &acceptedID=std::set< int >(), const std::tuple< short, short, short > &axisOrder=std::tuple< short, short, short >(0, 1, 2))
Fills workspace with histogram data from provided data structure.
std::string dateTimeInIsoFormat(const std::string &)
Parses the date as formatted at the ILL: 29-Jun-12 11:27:26 and converts it to the ISO format used in...
void loadEmptyInstrument(const API::MatrixWorkspace_sptr &ws, const std::string &instrumentName, const std::string &instrumentAddress="")
Loads empty instrument of chosen name into a provided workspace.
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition IComponent.h:167
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
MANTID_NEXUS_DLL H5::FileAccPropList defaultFileAcc()
Default file access is H5F_CLOSE_STRONG.
Definition H5Util.cpp:119
MANTID_NEXUS_DLL std::vector< std::string > readStringVector(H5::Group &, const std::string &)
int32_t detid_t
Typedef for a detector ID.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54