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"
26
27#include <H5Cpp.h>
28#include <Poco/Path.h>
29#include <boost/algorithm/string.hpp>
30#include <boost/math/special_functions/round.hpp>
31#include <nexus/napi.h>
32#include <numeric>
33
34namespace Mantid::DataHandling {
35
36using namespace API;
37using namespace Geometry;
38using namespace H5;
39using namespace Kernel;
40using namespace NeXus;
41using Types::Core::DateAndTime;
42
43namespace {
44// This defines the number of physical pixels in D20 (low resolution mode)
45// Then each pixel can be split into 2 (nominal) or 3 (high resolution) by DAQ
46constexpr size_t D20_NUMBER_PIXELS = 1600;
47// This defines the number of dead pixels on each side in low resolution mode
48constexpr size_t D20_NUMBER_DEAD_PIXELS = 32;
49// This defines the number of monitors in the instrument. If there are cases
50// where this is no longer one this decleration should be moved.
51constexpr size_t NUMBER_MONITORS = 1;
52// This is the angular size of a pixel in degrees (in low resolution mode)
53constexpr double D20_PIXEL_SIZE = 0.1;
54// The conversion factor from radian to degree
55constexpr double RAD_TO_DEG = 180. / M_PI;
56// A factor to compute E from lambda: E (mev) = waveToE/lambda(A)
57constexpr double WAVE_TO_E = 81.8;
58// Number of pixels in the tubes for D2B
59constexpr size_t D2B_NUMBER_PIXELS_IN_TUBES = 128;
60} // namespace
61
62// Register the algorithm into the AlgorithmFactory
63DECLARE_NEXUS_FILELOADER_ALGORITHM(LoadILLDiffraction)
64
65
66int LoadILLDiffraction::confidence(NexusDescriptor &descriptor) const {
67
68 // fields existent only at the ILL Diffraction
69 // the second one is to recognize D1B, Tx field eliminates SALSA
70 // the third one is to recognize IN5/PANTHER/SHARP scan mode
71 if ((descriptor.pathExists("/entry0/instrument/2theta") && !descriptor.pathExists("/entry0/instrument/Tx")) ||
72 descriptor.pathExists("/entry0/instrument/Canne") ||
73 (descriptor.pathExists("/entry0/data_scan") && descriptor.pathExists("/entry0/experiment_identifier") &&
74 descriptor.pathExists("/entry0/instrument/Detector"))) {
75 return 80;
76 } else {
77 return 0;
78 }
79}
80
82const std::string LoadILLDiffraction::name() const { return "LoadILLDiffraction"; }
83
85int LoadILLDiffraction::version() const { return 1; }
86
88const std::string LoadILLDiffraction::category() const { return "DataHandling\\Nexus;ILL\\Diffraction"; }
89
91const std::string LoadILLDiffraction::summary() const { return "Loads ILL diffraction nexus files."; }
92
97 : IFileLoader<NexusDescriptor>(), m_instNames({"D20", "D2B", "D1B", "D4C", "IN5", "PANTHER", "SHARP"}) {}
102 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, ".nxs"),
103 "File path of the data file to load");
104 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("OutputWorkspace", "", Direction::Output),
105 "The output workspace.");
106 std::vector<std::string> calibrationOptions{"Auto", "Raw", "Calibrated"};
107 declareProperty("DataType", "Auto", std::make_shared<StringListValidator>(calibrationOptions),
108 "Select the type of data, with or without calibration "
109 "already applied. If Auto then the calibrated data is "
110 "loaded if available, otherwise the raw data is loaded.");
111 declareProperty("TwoThetaOffset", 0.0, "2 theta offset for D1B data, in degrees.");
112 declareProperty(std::make_unique<PropertyWithValue<bool>>("AlignTubes", true, Direction::Input),
113 "Apply vertical and horizontal alignment of tubes as defined in IPF");
114 declareProperty("ConvertAxisAndTranspose", false,
115 "Whether to convert the spectrum axis to 2theta and "
116 "transpose (for 1D detector and no-scan configuration)");
117}
118
119std::map<std::string, std::string> LoadILLDiffraction::validateInputs() {
120 std::map<std::string, std::string> issues;
121 if (getPropertyValue("DataType") == "Calibrated" && !containsCalibratedData(getPropertyValue("Filename"))) {
122 issues["DataType"] = "Calibrated data requested, but only raw data exists "
123 "in this NeXus file.";
124 }
125 return issues;
126}
127
132
133 Progress progress(this, 0, 1, 4);
134
135 m_filename = getPropertyValue("Filename");
136
137 m_scanVar.clear();
138 progress.report("Loading the scanned variables");
139 loadScanVars();
140
141 progress.report("Loading the detector scan data");
142 loadDataScan();
143
144 progress.report("Loading the metadata");
145 loadMetaData();
146
147 progress.report("Setting additional sample logs");
149
150 if (m_instName != "D2B" && m_scanType != DetectorScan && getProperty("ConvertAxisAndTranspose"))
152
153 setProperty("OutputWorkspace", m_outWorkspace);
154}
155
160
161 // open the root entry
162 NXRoot dataRoot(m_filename);
163 NXEntry firstEntry = dataRoot.openFirstEntry();
164 m_instName = firstEntry.getString("instrument/name");
165 if (m_instName == "IN5" || m_instName == "PANTHER" || m_instName == "SHARP") {
166 m_isSpectrometer = true;
167 }
168 m_startTime = DateAndTime(LoadHelper::dateTimeInIsoFormat(firstEntry.getString("start_time")));
169 const std::string dataType = getPropertyValue("DataType");
170 const bool hasCalibratedData = containsCalibratedData(m_filename);
171 if (dataType != "Raw" && hasCalibratedData) {
172 m_useCalibratedData = true;
173 }
174 // Load the data
175 std::string dataName;
176 if (dataType == "Raw" && hasCalibratedData)
177 dataName = "data_scan/detector_data/raw_data";
178 else
179 dataName = "data_scan/detector_data/data";
180 g_log.notice() << "Loading data from " + dataName;
181 NXUInt data = firstEntry.openNXDataSet<unsigned int>(dataName);
182 data.load();
183
184 // read the scan data
185 NXData scanGroup = firstEntry.openNXData("data_scan/scanned_variables");
186 NXDouble scan = scanGroup.openDoubleData();
187 scan.load();
188
189 // read which variables are scanned
190 NXInt scanned = firstEntry.openNXInt("data_scan/scanned_variables/variables_names/scanned");
191 scanned.load();
192
193 // read what is going to be the axis
194 NXInt axis = firstEntry.openNXInt("data_scan/scanned_variables/variables_names/axis");
195 axis.load();
196
197 // read the starting two theta
198 double twoThetaValue = 0;
199 if (m_instName == "D1B") {
200 if (getPointerToProperty("TwoThetaOffset")->isDefault()) {
201 g_log.notice("A 2theta offset angle is necessary for D1B data.");
202 twoThetaValue = 0;
203 } else {
204 twoThetaValue = getProperty("TwoThetaOffset");
205 }
206 } else if (!m_isSpectrometer) {
207 std::string twoThetaPath = "instrument/2theta/value";
208 NXFloat twoTheta0 = firstEntry.openNXFloat(twoThetaPath);
209 twoTheta0.load();
210 twoThetaValue = double(twoTheta0[0]);
211 }
212
213 // figure out the dimensions
214 m_sizeDim1 = static_cast<size_t>(data.dim1());
215 m_sizeDim2 = static_cast<size_t>(data.dim2());
217 m_numberScanPoints = static_cast<size_t>(data.dim0());
218 g_log.debug() << "Read " << m_numberDetectorsRead << " detectors and " << m_numberScanPoints << "\n";
219
220 // set which scanned variables are scanned, which should be the axis
221 for (size_t i = 0; i < m_scanVar.size(); ++i) {
222 m_scanVar[i].setAxis(axis[static_cast<int>(i)]);
223 m_scanVar[i].setScanned(scanned[static_cast<int>(i)]);
224 }
225
229
230 std::string start_time = firstEntry.getString("start_time");
231 start_time = LoadHelper::dateTimeInIsoFormat(start_time);
232
233 if (m_scanType == DetectorScan) {
234 initMovingWorkspace(scan, start_time);
235 fillMovingInstrumentScan(data, scan);
236 } else {
237 initStaticWorkspace(start_time);
238 fillStaticInstrumentScan(data, scan, twoThetaValue);
239 }
240
242
243 scanGroup.close();
244 firstEntry.close();
245 dataRoot.close();
246}
247
252
253 auto &mutableRun = m_outWorkspace->mutableRun();
254 mutableRun.addProperty("Facility", std::string("ILL"));
255
256 // Open NeXus file
257 NXhandle nxHandle;
258 NXstatus nxStat = NXopen(m_filename.c_str(), NXACC_READ, &nxHandle);
259
260 if (nxStat != NX_ERROR) {
261 LoadHelper::addNexusFieldsToWsRun(nxHandle, m_outWorkspace->mutableRun());
262 NXclose(&nxHandle);
263 }
264 mutableRun.addProperty("run_list", mutableRun.getPropertyValueAsType<int>("run_number"));
265
266 if (mutableRun.hasProperty("Detector.calibration_file")) {
267 if (getPropertyValue("DataType") == "Raw")
268 mutableRun.getProperty("Detector.calibration_file")->setValue("none");
269 } else
270 mutableRun.addProperty("Detector.calibration_file", std::string("none"));
271}
272
279void LoadILLDiffraction::initStaticWorkspace(const std::string &start_time) {
280 size_t nSpectra = m_numberDetectorsActual + NUMBER_MONITORS;
281 size_t nBins = 1;
282
283 if (m_scanType == DetectorScan) {
285 } else if (m_scanType == OtherScan) {
286 nBins = m_numberScanPoints;
287 }
288
289 m_outWorkspace = WorkspaceFactory::Instance().create("Workspace2D", nSpectra, nBins, nBins);
290
291 // the start time is needed in the workspace when loading the parameter file
292 m_outWorkspace->mutableRun().addProperty("start_time", start_time);
293}
294
301void LoadILLDiffraction::initMovingWorkspace(const NXDouble &scan, const std::string &start_time) {
302 const size_t nTimeIndexes = m_numberScanPoints;
303 const size_t nBins = 1;
304 const bool isPointData = true;
305
306 const auto instrumentWorkspace = loadEmptyInstrument(start_time);
307 const auto &instrument = instrumentWorkspace->getInstrument();
308 auto &params = instrumentWorkspace->instrumentParameters();
309
310 const auto &referenceComponentPosition = getReferenceComponentPosition(instrumentWorkspace);
311
312 double refR, refTheta, refPhi;
313 referenceComponentPosition.getSpherical(refR, refTheta, refPhi);
314
315 if (m_instName == "D2B") {
316 const bool doAlign = getProperty("AlignTubes");
317 auto &compInfo = instrumentWorkspace->mutableComponentInfo();
318
319 Geometry::IComponent_const_sptr detectors = instrument->getComponentByName("detectors");
320 const auto detCompIndex = compInfo.indexOf(detectors->getComponentID());
321 const auto tubes = compInfo.children(detCompIndex);
322 const size_t nTubes = tubes.size();
323 Geometry::IComponent_const_sptr tube1 = instrument->getComponentByName("tube_1");
324 const auto tube1CompIndex = compInfo.indexOf(tube1->getComponentID());
325 const auto pixels = compInfo.children(tube1CompIndex);
326 const size_t nPixels = pixels.size();
327
328 Geometry::IComponent_const_sptr pixel = instrument->getComponentByName("standard_pixel");
330 pixel->getBoundingBox(bb);
331 m_pixelHeight = bb.yMax() - bb.yMin();
332
333 const auto tubeAnglesStr = params.getString("D2B", "tube_angles");
334 if (!tubeAnglesStr.empty() && doAlign) {
335 std::vector<std::string> tubeAngles;
336 boost::split(tubeAngles, tubeAnglesStr[0], boost::is_any_of(","));
337 const double ref = -refTheta;
338 for (size_t i = 1; i <= nTubes; ++i) {
339 const std::string compName = "tube_" + std::to_string(i);
340 Geometry::IComponent_const_sptr component = instrument->getComponentByName(compName);
341 double r, theta, phi;
342 V3D oldPos = component->getPos();
343 oldPos.getSpherical(r, theta, phi);
344 V3D newPos;
345 const double angle = std::stod(tubeAngles[i - 1]);
346 const double finalAngle = fabs(ref - angle);
347 g_log.debug() << "Rotating " << compName << "to " << finalAngle << "rad\n";
348 newPos.spherical(r, finalAngle, phi);
349 const auto componentIndex = compInfo.indexOf(component->getComponentID());
350 compInfo.setPosition(componentIndex, newPos);
351 }
352 }
353
354 const auto tubeCentersStr = params.getString("D2B", "tube_centers");
355 if (!tubeCentersStr.empty() && doAlign) {
356 std::vector<std::string> tubeCenters;
357 double maxYOffset = 0.;
358 boost::split(tubeCenters, tubeCentersStr[0], boost::is_any_of(","));
359 for (size_t i = 1; i <= nTubes; ++i) {
360 const std::string compName = "tube_" + std::to_string(i);
361 Geometry::IComponent_const_sptr component = instrument->getComponentByName(compName);
362 const double offset = std::stod(tubeCenters[i - 1]) - (double(nPixels) / 2 - 0.5);
363 const double y = -offset * m_pixelHeight;
364 V3D translation(0, y, 0);
365 if (std::fabs(y) > maxYOffset) {
366 maxYOffset = std::fabs(y);
367 }
368 g_log.debug() << "Moving " << compName << " to " << y << "\n";
369 V3D pos = component->getPos() + translation;
370 const auto componentIndex = compInfo.indexOf(component->getComponentID());
371 compInfo.setPosition(componentIndex, pos);
372 }
373 m_maxHeight = double(nPixels + 1) * m_pixelHeight / 2 + maxYOffset;
374 }
375 }
376
377 auto scanningWorkspaceBuilder = DataObjects::ScanningWorkspaceBuilder(instrument, nTimeIndexes, nBins, isPointData);
378
379 std::vector<double> timeDurations = getScannedVaribleByPropertyName(scan, "Time");
380 scanningWorkspaceBuilder.setTimeRanges(m_startTime, timeDurations);
381
382 g_log.debug() << "First time index starts at:" << m_startTime.toISO8601String() << "\n";
383
384 g_log.debug() << "Last time index ends at:"
385 << (m_startTime + std::accumulate(timeDurations.begin(), timeDurations.end(), 0.0)).toISO8601String()
386 << "\n";
387
388 // Angles in the NeXus files are the absolute position for tube 1
389 std::vector<double> tubeAngles = getScannedVaribleByPropertyName(scan, "Position");
390
391 // Convert the tube positions to relative rotations for all detectors
392 calculateRelativeRotations(tubeAngles, referenceComponentPosition);
393
394 auto rotationCentre = V3D(0, 0, 0);
395 auto rotationAxis = V3D(0, 1, 0);
396 scanningWorkspaceBuilder.setRelativeRotationsForScans(std::move(tubeAngles), rotationCentre, rotationAxis);
397
398 m_outWorkspace = scanningWorkspaceBuilder.buildWorkspace();
399}
400
411 if (m_instName == "D2B") {
412 return instrumentWorkspace->getInstrument()->getComponentByName("tube_128")->getPos();
413 }
414
415 const auto &detInfo = instrumentWorkspace->detectorInfo();
416 const auto &indexOfFirstDet = detInfo.indexOf(1);
417 return detInfo.position(indexOfFirstDet);
418}
419
430void LoadILLDiffraction::calculateRelativeRotations(std::vector<double> &tubeRotations, const V3D &firstTubePosition) {
431 // The rotations in the NeXus file are the absolute rotation of the first
432 // tube. Here we get the angle of that tube as defined in the IDF.
433
434 double firstTubeRotationAngle = firstTubePosition.angle(V3D(0, 0, 1)) * RAD_TO_DEG;
435
436 // note that for D20 we have to subtract the offset here
437 // unlike in the static detector case, because in the transform
438 // below, we take (angle - firstTubeRotatingAngle)
439 if (m_instName == "D20") {
440 firstTubeRotationAngle -= m_offsetTheta;
441 } else if (m_instName == "D2B") {
442 firstTubeRotationAngle = -firstTubeRotationAngle;
443 std::transform(tubeRotations.begin(), tubeRotations.end(), tubeRotations.begin(),
444 [&](double angle) { return (-angle); });
445 }
446
447 g_log.debug() << "First tube rotation:" << firstTubeRotationAngle << "\n";
448
449 // Now pass calculate the rotations to apply for each time index.
450 std::transform(tubeRotations.begin(), tubeRotations.end(), tubeRotations.begin(),
451 [&](double angle) { return (angle - firstTubeRotationAngle); });
452
453 g_log.debug() << "Instrument rotations to be applied : " << tubeRotations.front() << " to " << tubeRotations.back()
454 << "\n";
455}
456
464
465 std::vector<double> axis = {0.};
466 std::vector<double> monitor = getMonitor(scan);
467
468 // First load the monitors
469 for (size_t i = 0; i < NUMBER_MONITORS; ++i) {
470 for (size_t j = 0; j < m_numberScanPoints; ++j) {
471 const auto wsIndex = j + i * m_numberScanPoints;
472 m_outWorkspace->mutableY(wsIndex) = monitor[j];
473 m_outWorkspace->mutableE(wsIndex) = sqrt(monitor[j]);
474 m_outWorkspace->mutableX(wsIndex) = axis;
475 }
476 }
477
478 // Then load the detector spectra
480 for (int i = NUMBER_MONITORS; i < static_cast<int>(m_numberDetectorsActual + NUMBER_MONITORS); ++i) {
481 for (size_t j = 0; j < m_numberScanPoints; ++j) {
482 const auto tubeNumber = (i - NUMBER_MONITORS) / m_sizeDim2;
483 auto pixelInTubeNumber = (i - NUMBER_MONITORS) % m_sizeDim2;
484 if (m_instName == "D2B" && !m_useCalibratedData && tubeNumber % 2 == 1) {
485 pixelInTubeNumber = D2B_NUMBER_PIXELS_IN_TUBES - 1 - pixelInTubeNumber;
486 }
487 unsigned int y = data(static_cast<int>(j), static_cast<int>(tubeNumber), static_cast<int>(pixelInTubeNumber));
488 const auto wsIndex = j + i * m_numberScanPoints;
489 m_outWorkspace->mutableY(wsIndex) = y;
490 m_outWorkspace->mutableE(wsIndex) = sqrt(y);
491 m_outWorkspace->mutableX(wsIndex) = axis;
492 }
493 }
494}
495
504void LoadILLDiffraction::fillStaticInstrumentScan(const NXUInt &data, const NXDouble &scan, const double &twoTheta0) {
505
506 const std::vector<double> axis = getAxis(scan);
507 const std::vector<double> monitor = getMonitor(scan);
508
509 size_t monitorIndex = 0;
510 size_t startIndex = NUMBER_MONITORS;
511 if (m_isSpectrometer) {
512 startIndex = 0;
513 monitorIndex = m_numberDetectorsActual;
514 }
515
516 // Assign monitor counts
517 m_outWorkspace->mutableX(monitorIndex) = axis;
518 m_outWorkspace->mutableY(monitorIndex) = monitor;
519 std::transform(monitor.begin(), monitor.end(), m_outWorkspace->mutableE(monitorIndex).begin(),
520 [](double e) { return sqrt(e); });
521
522 // Assign detector counts
524 for (int i = static_cast<int>(startIndex); i < static_cast<int>(m_numberDetectorsActual + startIndex); ++i) {
525 auto &spectrum = m_outWorkspace->mutableY(i);
526 auto &errors = m_outWorkspace->mutableE(i);
527 const auto tubeNumber = (i - startIndex) / m_sizeDim2;
528 auto pixelInTubeNumber = (i - startIndex) % m_sizeDim2;
529 if (m_instName == "D2B" && !m_useCalibratedData && tubeNumber % 2 == 1) {
530 pixelInTubeNumber = D2B_NUMBER_PIXELS_IN_TUBES - 1 - pixelInTubeNumber;
531 }
532 for (size_t j = 0; j < m_numberScanPoints; ++j) {
533 unsigned int y = data(static_cast<int>(j), static_cast<int>(tubeNumber), static_cast<int>(pixelInTubeNumber));
534 spectrum[j] = y;
535 errors[j] = sqrt(y);
536 }
537 m_outWorkspace->mutableX(i) = axis;
538 }
539
540 // Link the instrument
542 if (!m_isSpectrometer) {
543 // Move to the starting 2theta
544 moveTwoThetaZero(twoTheta0);
545 }
546}
547
552
553 H5File h5file(m_filename, H5F_ACC_RDONLY);
554
555 Group entry0 = h5file.openGroup("entry0");
556 Group dataScan = entry0.openGroup("data_scan");
557 Group scanVar = dataScan.openGroup("scanned_variables");
558 Group varNames = scanVar.openGroup("variables_names");
559
560 const auto names = H5Util::readStringVector(varNames, "name");
561 const auto properties = H5Util::readStringVector(varNames, "property");
562 const auto units = H5Util::readStringVector(varNames, "unit");
563
564 for (size_t i = 0; i < names.size(); ++i) {
565 m_scanVar.emplace_back(ScannedVariables(names[i], properties[i], units[i]));
566 }
567
568 varNames.close();
569 scanVar.close();
570 dataScan.close();
571 entry0.close();
572 h5file.close();
573}
574
580 auto absoluteTimes = getAbsoluteTimes(scan);
581 auto &mutableRun = m_outWorkspace->mutableRun();
582 for (size_t i = 0; i < m_scanVar.size(); ++i) {
583 if (!boost::starts_with(m_scanVar[i].property, "Monitor")) {
584 const std::string scanVarName = boost::algorithm::to_lower_copy(m_scanVar[i].name);
585 const std::string scanVarProp = boost::algorithm::to_lower_copy(m_scanVar[i].property);
586 const std::string propName = scanVarName + "." + scanVarProp;
587 if (m_scanVar[i].scanned == 1) {
588 mutableRun.addProperty("ScanVar", propName, true);
589 }
590 auto property = std::make_unique<TimeSeriesProperty<double>>(propName);
591 for (size_t j = 0; j < m_numberScanPoints; ++j) {
592 property->addValue(absoluteTimes[j], scan(static_cast<int>(i), static_cast<int>(j)));
593 }
594 mutableRun.addLogData(std::move(property), true);
595 }
596 }
597}
598
610 const std::string &propertyName) const {
611 std::vector<double> scannedVariable;
612
613 for (size_t i = 0; i < m_scanVar.size(); ++i) {
614 if (m_scanVar[i].property == propertyName) {
615 for (size_t j = 0; j < m_numberScanPoints; ++j) {
616 scannedVariable.emplace_back(scan(static_cast<int>(i), static_cast<int>(j)));
617 }
618 break;
619 }
620 }
621
622 if (scannedVariable.empty())
623 throw std::runtime_error("Can not load file because scanned variable with property name " + propertyName +
624 " was not found");
625
626 return scannedVariable;
627}
628
636std::vector<double> LoadILLDiffraction::getMonitor(const NXDouble &scan) const {
637
638 std::vector<double> monitor = {0.};
639 for (size_t i = 0; i < m_scanVar.size(); ++i) {
640 if ((m_scanVar[i].name == "Monitor1") || (m_scanVar[i].name == "Monitor_1") || (m_scanVar[i].name == "monitor1")) {
641 monitor.assign(scan() + m_numberScanPoints * i, scan() + m_numberScanPoints * (i + 1));
642 return monitor;
643 }
644 }
645 throw std::runtime_error("Monitors not found in scanned variables");
646}
647
653std::vector<double> LoadILLDiffraction::getAxis(const NXDouble &scan) const {
654
655 std::vector<double> axis = {0.};
656 if (m_scanType == OtherScan) {
657 for (size_t i = 0; i < m_scanVar.size(); ++i) {
658 if (m_scanVar[i].axis == 1) {
659 axis.assign(scan() + m_numberScanPoints * i, scan() + m_numberScanPoints * (i + 1));
660 break;
661 }
662 }
663 }
664 return axis;
665}
666
672std::vector<double> LoadILLDiffraction::getDurations(const NXDouble &scan) const {
673 std::vector<double> timeDurations;
674 for (size_t i = 0; i < m_scanVar.size(); ++i) {
675 if (boost::starts_with(m_scanVar[i].property, "Time")) {
676 timeDurations.assign(scan() + m_numberScanPoints * i, scan() + m_numberScanPoints * (i + 1));
677 break;
678 }
679 }
680 return timeDurations;
681}
682
688std::vector<DateAndTime> LoadILLDiffraction::getAbsoluteTimes(const NXDouble &scan) const {
689 std::vector<DateAndTime> times;
690 std::vector<double> durations = getDurations(scan);
691 DateAndTime time = m_startTime;
692 times.emplace_back(time);
693 size_t timeIndex = 1;
694 while (timeIndex < m_numberScanPoints) {
695 time += durations[timeIndex - 1];
696 times.emplace_back(time);
697 ++timeIndex;
698 }
699 return times;
700}
701
706 ScanType result = NoScan;
707 if (m_instName == "D2B") {
708 result = DetectorScan;
709 } else {
710 if (m_numberScanPoints != 1) {
711 for (const auto &scanVar : m_scanVar) {
712 if (scanVar.scanned == 1) {
713 result = OtherScan;
714 if (scanVar.name == "2theta") {
715 result = DetectorScan;
716 break;
717 }
718 }
719 }
720 }
721 }
722 m_scanType = result;
723}
724
730 if (m_instNames.find(m_instName) == m_instNames.end()) {
731 throw std::runtime_error("Instrument " + m_instName + " not supported.");
732 } else {
734 if (m_instName == "D20") {
735 // Here we have to hardcode the numbers of pixels.
736 // The only way is to read the size of the detectors read from the files
737 // and based on it decide which of the 3 alternative IDFs to load.
738 // Some amount of pixels are dead on at right end, these have to be
739 // subtracted
740 // correspondingly dependent on the resolution mode
741 m_resolutionMode = m_numberDetectorsRead / D20_NUMBER_PIXELS;
742 size_t activePixels = D20_NUMBER_PIXELS - 2 * D20_NUMBER_DEAD_PIXELS;
744
745 if (m_resolutionMode > 3 || m_resolutionMode < 1) {
746 throw std::runtime_error("Unknown resolution mode for instrument " + m_instName);
747 }
748 if (m_resolutionMode == 1) {
749 m_instName += "_lr";
750 } else if (m_resolutionMode == 3) {
751 m_instName += "_hr";
752 }
753 }
754 g_log.debug() << "Instrument name is " << m_instName << " and has " << m_numberDetectorsActual
755 << " actual detectors.\n";
756 }
757}
758
763 auto loadInst = createChildAlgorithm("LoadInstrument");
764 loadInst->setPropertyValue("Filename", getInstrumentFilePath(m_instName));
765 loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", m_outWorkspace);
766 loadInst->setProperty("RewriteSpectraMap", OptionalBool(true));
767 loadInst->execute();
768}
769
777 auto loadInst = createChildAlgorithm("LoadInstrument");
778 loadInst->setPropertyValue("InstrumentName", m_instName);
779 auto ws = WorkspaceFactory::Instance().create("Workspace2D", 1, 1, 1);
780 auto &run = ws->mutableRun();
781
782 // the start time is needed in the workspace when loading the parameter file
783 run.addProperty("start_time", start_time);
784
785 loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", ws);
786 loadInst->setProperty("RewriteSpectraMap", OptionalBool(true));
787 loadInst->execute();
788 return loadInst->getProperty("Workspace");
789}
790
795void LoadILLDiffraction::moveTwoThetaZero(double twoTheta0Read) {
796 Instrument_const_sptr instrument = m_outWorkspace->getInstrument();
797 IComponent_const_sptr component = instrument->getComponentByName("detector");
798 double twoTheta0Actual = twoTheta0Read;
799 if (m_instName == "D20") {
800 twoTheta0Actual += m_offsetTheta;
801 }
802 Quat rotation(twoTheta0Actual, V3D(0, 1, 0));
803 g_log.debug() << "Setting 2theta0 to " << twoTheta0Actual;
804 auto &componentInfo = m_outWorkspace->mutableComponentInfo();
805 const auto componentIndex = componentInfo.indexOf(component->getComponentID());
806 componentInfo.setRotation(componentIndex, rotation);
807}
808
815std::string LoadILLDiffraction::getInstrumentFilePath(const std::string &instName) const {
816
817 Poco::Path directory(ConfigService::Instance().getInstrumentDirectory());
818 Poco::Path file(instName + "_Definition.xml");
819 Poco::Path fullPath(directory, file);
820 return fullPath.toString();
821}
822
827 Run &run = m_outWorkspace->mutableRun();
828 std::string scanTypeStr = "NoScan";
829 if (m_scanType == DetectorScan) {
830 scanTypeStr = "DetectorScan";
831 } else if (m_scanType == OtherScan) {
832 scanTypeStr = "OtherScan";
833 }
834 run.addLogData(new PropertyWithValue<std::string>("ScanType", std::move(scanTypeStr)));
835 run.addLogData(new PropertyWithValue<double>("PixelSize", D20_PIXEL_SIZE / static_cast<double>(m_resolutionMode)));
836 std::string resModeStr = "Nominal";
837 if (m_resolutionMode == 1) {
838 resModeStr = "Low";
839 } else if (m_resolutionMode == 3) {
840 resModeStr = "High";
841 }
842 run.addLogData(new PropertyWithValue<std::string>("ResolutionMode", std::move(resModeStr)));
843 if (m_scanType != NoScan) {
844 run.addLogData(new PropertyWithValue<int>("ScanSteps", static_cast<int>(m_numberScanPoints)));
845 }
846 double eFixed;
847 if (run.hasProperty("wavelength")) {
848 double lambda = run.getLogAsSingleValue("wavelength");
849 eFixed = WAVE_TO_E / (lambda * lambda);
850 } else if (run.hasProperty("Monochromator.ei")) { // D4C, wavelength is not specified and Ei is provided directly
851 eFixed = run.getPropertyValueAsType<double>("Monochromator.ei");
852 } else {
853 throw std::runtime_error("Neither wavelength nor Monochromator.ei are not specified in the loaded file.");
854 }
855 run.addLogData(std::make_unique<Kernel::PropertyWithValue<double>>(PropertyWithValue<double>("Ei", eFixed)), true);
856 run.addLogData(new PropertyWithValue<size_t>("NumberOfDetectors", m_numberDetectorsActual));
857 if (m_pixelHeight != 0.) {
858 run.addLogData(new PropertyWithValue<double>("PixelHeight", m_pixelHeight));
859 }
860 if (m_maxHeight != 0.) {
861 run.addLogData(new PropertyWithValue<double>("MaxHeight", m_maxHeight));
862 }
863}
864
871bool LoadILLDiffraction::containsCalibratedData(const std::string &filename) const {
872 NexusDescriptor descriptor(filename);
873 // This is unintuitive, but if the file has calibrated data there are entries
874 // for 'data' and 'raw_data'. If there is no calibrated data only 'data' is
875 // present.
876 return descriptor.pathExists("/entry0/data_scan/detector_data/raw_data");
877}
878
883 m_offsetTheta = static_cast<double>(D20_NUMBER_DEAD_PIXELS) * D20_PIXEL_SIZE -
884 D20_PIXEL_SIZE / (static_cast<double>(m_resolutionMode) * 2);
885}
886
891 auto extractor = createChildAlgorithm("ExtractSpectra");
892 extractor->setProperty("InputWorkspace", m_outWorkspace);
893 extractor->setProperty("StartWorkspaceIndex", 1);
894 extractor->setProperty("OutputWorkspace", "__unused");
895 extractor->execute();
896 API::MatrixWorkspace_sptr det = extractor->getProperty("OutputWorkspace");
897 auto converter = createChildAlgorithm("ConvertSpectrumAxis");
898 converter->setProperty("InputWorkspace", det);
899 converter->setProperty("OutputWorkspace", "__unused");
900 converter->setProperty("Target", "SignedTheta");
901 converter->execute();
902 API::MatrixWorkspace_sptr converted = converter->getProperty("OutputWorkspace");
903 auto transposer = createChildAlgorithm("Transpose");
904 transposer->setProperty("InputWorkspace", converted);
905 transposer->setProperty("OutputWorkspace", "__unused");
906 transposer->execute();
907 API::MatrixWorkspace_sptr transposed = transposer->getProperty("OutputWorkspace");
908 m_outWorkspace = transposed;
909}
910
911} // namespace Mantid::DataHandling
const std::vector< double > * lambda
int nSpectra
#define fabs(x)
Definition: Matrix.cpp:22
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
Mantid::Kernel::Quat(ComponentInfo::* rotation)(const size_t) const
#define DECLARE_NEXUS_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro wh...
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
Kernel::Property * getPointerToProperty(const std::string &name) const override
Get a property by name.
Definition: Algorithm.cpp:2033
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
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.
Definition: Algorithm.cpp:842
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
@ Load
allowed here which will be passed to the algorithm
Definition: FileProperty.h:52
Defines an interface to an algorithm that loads a file so that it can take part in the automatic sele...
Definition: IFileLoader.h:19
void addLogData(Kernel::Property *p)
Add a log entry.
Definition: LogManager.h:115
bool hasProperty(const std::string &name) const
Does the property exist on the object.
Definition: LogManager.cpp:265
HeldType getPropertyValueAsType(const std::string &name) const
Get the value of a property as the given TYPE.
Definition: LogManager.cpp:332
double getLogAsSingleValue(const std::string &name, Kernel::Math::StatisticType statistic=Kernel::Math::Mean) const
Definition: LogManager.h:149
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:38
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 fillMovingInstrumentScan(const NeXus::NXUInt &, const NeXus::NXDouble &)
Fills the counts for the instrument with moving detectors.
bool containsCalibratedData(const std::string &filename) const
Returns true if the file contains calibrated data.
void moveTwoThetaZero(double)
Rotates the detector to the 2theta0 read from the file.
API::MatrixWorkspace_sptr m_outWorkspace
output workspace
void fillStaticInstrumentScan(const NeXus::NXUInt &, const NeXus::NXDouble &, const double &)
Fills the loaded data to the workspace when the detector is not moving during the run,...
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...
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.
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.
API::MatrixWorkspace_sptr loadEmptyInstrument(const std::string &start_time)
Runs LoadInstrument and returns a workspace with the instrument, to be used in the ScanningWorkspaceB...
std::string getInstrumentFilePath(const std::string &) const
Makes up the full path of the relevant IDF dependent on resolution mode.
bool m_useCalibratedData
whether to use the calibrated data in the nexus (D2B only)
const std::string category() const override
Algorithm's category for identification.
double m_pixelHeight
height of the pixel in D2B
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.
void fillDataScanMetaData(const NeXus::NXDouble &)
Creates time series sample logs for the scanned variables.
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.
std::vector< double > getAxis(const NeXus::NXDouble &) const
Returns the x-axis.
void initMovingWorkspace(const NeXus::NXDouble &scan, const std::string &start_time)
Use the ScanningWorkspaceBuilder to create a time indexed workspace.
void exec() override
Executes the algorithm.
Types::Core::DateAndTime m_startTime
start time of acquisition
std::vector< double > getMonitor(const NeXus::NXDouble &) const
Returns the monitor spectrum.
void convertAxisAndTranspose()
the 2theta offset for D20 to account for dead pixels
void loadStaticInstrument()
Runs LoadInstrument as child to link the non-moving instrument to workspace.
void loadScanVars()
Loads the scanned_variables/variables_names block.
void loadMetaData()
Dumps the metadata from the whole file to SampleLogs.
std::vector< double > getDurations(const NeXus::NXDouble &) const
Returns the durations in seconds for each scan point.
void initStaticWorkspace(const std::string &start_time)
Initializes the output workspace based on the resolved instrument, scan points, and scan type.
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:34
double yMax() const
Return the maximum value of Y.
Definition: BoundingBox.h:84
double yMin() const
Return the minimum value of Y.
Definition: BoundingBox.h:82
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:114
void notice(const std::string &msg)
Logs at notice level.
Definition: Logger.cpp:95
Defines a wrapper around a file whose internal structure can be accessed using the NeXus API.
bool pathExists(const std::string &path) const
Query if a path exists.
OptionalBool : Tri-state bool.
Definition: OptionalBool.h:25
The concrete, templated class for properties.
virtual bool isDefault() const =0
Overriden function that returns if property has the same value that it was initialised with,...
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:57
double angle(const V3D &) const
Angle between this and another vector.
Definition: V3D.cpp:165
void getSpherical(double &R, double &theta, double &phi) const noexcept
Return the vector's position in spherical coordinates.
Definition: V3D.cpp:117
NXInt openNXInt(const std::string &name) const
Creates and opens an integer dataset.
Definition: NexusClasses.h:546
void close()
Close this class.
NXDataSetTyped< T > openNXDataSet(const std::string &name) const
Templated method for creating datasets.
Definition: NexusClasses.h:536
NXFloat openNXFloat(const std::string &name) const
Creates and opens a float dataset.
Definition: NexusClasses.h:551
std::string getString(const std::string &name) const
Returns a string.
Templated class implementation of NXDataSet.
Definition: NexusClasses.h:203
void load(const int blocksize=1, int i=-1, int j=-1, int k=-1, int l=-1) override
Implementation of the virtual NXDataSet::load(...) method.
Definition: NexusClasses.h:289
int dim2() const
Returns the number of elements along the third dimension.
int dim0() const
Returns the number of elements along the first dimension.
int dim1() const
Returns the number of elements along the second dimension.
Implements NXdata Nexus class.
Definition: NexusClasses.h:795
NXDouble openDoubleData()
Opens data of double type.
Definition: NexusClasses.h:824
Implements NXentry Nexus class.
Definition: NexusClasses.h:898
NXData openNXData(const std::string &name) const
Opens a NXData.
Definition: NexusClasses.h:912
Implements NXroot Nexus class.
Definition: NexusClasses.h:922
NXEntry openFirstEntry()
Open the first NXentry in the file.
Definition: H5Util.h:16
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
MANTID_DATAHANDLING_DLL std::vector< std::string > readStringVector(H5::Group &, const std::string &)
void addNexusFieldsToWsRun(NXhandle nxfileID, API::Run &runDetails, const std::string &entryName="", bool useFullPath=false)
Add properties from a nexus file to the workspace run.
Definition: LoadHelper.cpp:131
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...
Definition: LoadHelper.cpp:421
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition: IComponent.h:161
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
Definition: MultiThreaded.h:22
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