Mantid
Loading...
Searching...
No Matches
LoadILLReflectometry.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
9#include "MantidAPI/Axis.h"
16#include "MantidAPI/Run.h"
28#include "MantidKernel/Quat.h"
31#include "MantidKernel/V3D.h"
32
33namespace {
34
36double wavelengthToTOF(const double lambda, const double l1, const double l2) {
37 return Mantid::Kernel::UnitConversion::run("Wavelength", "TOF", lambda, l1, l2, 0.,
39}
40
45constexpr double degToRad(const double x) { return x * M_PI / 180.; }
46
51constexpr double radToDeg(const double x) { return x * 180. / M_PI; }
52
57constexpr double mmToMeter(const double x) { return x * 1.e-3; }
58
63std::pair<int, int> fitIntegrationWSIndexRange(const Mantid::API::MatrixWorkspace &ws) {
64 const size_t nHisto = ws.getNumberHistograms();
65 int begin = 0;
66 const auto &spectrumInfo = ws.spectrumInfo();
67 for (size_t i = 0; i < nHisto; ++i) {
68 if (!spectrumInfo.isMonitor(i)) {
69 break;
70 }
71 ++begin;
72 }
73 int end = static_cast<int>(nHisto) - 1;
74 for (ptrdiff_t i = static_cast<ptrdiff_t>(nHisto) - 1; i != 0; --i) {
75 if (!spectrumInfo.isMonitor(i)) {
76 break;
77 }
78 --end;
79 }
80 return std::pair<int, int>{begin, end};
81}
82
86void rebinIntegralWorkspace(Mantid::API::MatrixWorkspace &ws) {
87 auto &xs = ws.mutableX(0);
88 std::iota(xs.begin(), xs.end(), 0.0);
89}
90
92enum class RotationPlane { horizontal, vertical };
93
100Mantid::Kernel::V3D detectorPosition(const RotationPlane plane, const double distance, const double angle) {
101 const double a = degToRad(angle);
102 double x, y, z;
103 switch (plane) {
104 case RotationPlane::horizontal:
105 x = distance * std::sin(a);
106 y = 0;
107 z = distance * std::cos(a);
108 break;
109 case RotationPlane::vertical:
110 x = 0;
111 y = distance * std::sin(a);
112 z = distance * std::cos(a);
113 break;
114 }
115 return Mantid::Kernel::V3D(x, y, z);
116}
117
123Mantid::Kernel::Quat detectorFaceRotation(const RotationPlane plane, const double angle) {
124 const Mantid::Kernel::V3D axis = [plane]() {
125 double x, y;
126 switch (plane) {
127 case RotationPlane::horizontal:
128 x = 0;
129 y = 1;
130 break;
131 case RotationPlane::vertical:
132 x = -1;
133 y = 0;
134 break;
135 }
136 return Mantid::Kernel::V3D(x, y, 0);
137 }();
138 return Mantid::Kernel::Quat(angle, axis);
139}
140} // anonymous namespace
141
142namespace Mantid::DataHandling {
143
144using namespace Kernel;
145using namespace API;
146using namespace NeXus;
147using Mantid::Types::Core::DateAndTime;
148
149// Register the algorithm into the AlgorithmFactory
150DECLARE_NEXUS_FILELOADER_ALGORITHM(LoadILLReflectometry)
151
152
158int LoadILLReflectometry::confidence(Kernel::NexusDescriptor &descriptor) const {
159
160 // fields existent only at the ILL
161 if ((descriptor.pathExists("/entry0/wavelength") || // ILL D17
162 descriptor.pathExists("/entry0/theta")) // ILL FIGARO
163 && descriptor.pathExists("/entry0/experiment_identifier") && descriptor.pathExists("/entry0/mode") &&
164 (descriptor.pathExists("/entry0/instrument/VirtualChopper") || // ILL D17
165 descriptor.pathExists("/entry0/instrument/Theta")) // ILL FIGARO
166 )
167 return 80;
168 else
169 return 0;
170}
171
175 std::make_unique<FileProperty>("Filename", std::string(), FileProperty::Load, ".nxs", Direction::Input),
176 "Name of the Nexus file to load");
177 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", std::string(), Direction::Output),
178 "Name of the output workspace");
179 declareProperty("ForegroundPeakCentre", EMPTY_DBL(),
180 "Foreground peak position in fractional workspace "
181 "index (if not given the peak is searched for and fitted).");
182 declareProperty("DetectorCentreFractionalIndex", 127.5,
183 "The fractional workspace index of the geometric centre of "
184 "the detector at incident beam axis (127.5 for D17 and Figaro).");
185 const std::vector<std::string> measurements({"DirectBeam", "ReflectedBeam"});
186 declareProperty("Measurement", "DirectBeam", std::make_unique<StringListValidator>(measurements),
187 "Load as direct or reflected beam.");
188 declareProperty("BraggAngle", EMPTY_DBL(), "The bragg angle necessary for reflected beam.");
189 declareProperty("FitStartWorkspaceIndex", 0, std::make_unique<BoundedValidator<int>>(0, 255),
190 "Start workspace index used for peak fitting.");
191 declareProperty("FitEndWorkspaceIndex", 255, std::make_unique<BoundedValidator<int>>(0, 255),
192 "End workspace index used for peak fitting.");
193 declareProperty("FitRangeLower", -1., "Minimum wavelength used for peak fitting.");
194 declareProperty("FitRangeUpper", -1., "Maximum wavelength used for peak fitting.");
195 const std::vector<std::string> availableUnits{"Wavelength", "TimeOfFlight"};
196 declareProperty("XUnit", "Wavelength", std::make_shared<StringListValidator>(availableUnits),
197 "X unit of the OutputWorkspace");
198}
199
202 NeXus::NXRoot root(getPropertyValue("Filename"));
203 NXEntry firstEntry{root.openFirstEntry()};
204 initNames(firstEntry);
205 sampleAngle(firstEntry);
206 std::vector<std::vector<int>> monitorsData{loadMonitors(firstEntry)};
207 loadDataDetails(firstEntry);
208 initWorkspace(monitorsData);
211 loadData(firstEntry, monitorsData, getXValues());
212 firstEntry.close();
213 root.close();
216 placeSource();
218 placeSlits();
220 setProperty("OutputWorkspace", m_localWorkspace);
221}
222
225 auto loadInst = createChildAlgorithm("LoadInstrument");
226 const std::string instrumentName = m_instrument == Supported::D17 ? "D17" : "FIGARO";
227 loadInst->setPropertyValue("InstrumentName", instrumentName);
228 loadInst->setProperty("RewriteSpectraMap", Mantid::Kernel::OptionalBool(true));
229 loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", m_localWorkspace);
230 loadInst->executeAsChildAlg();
231}
232
240 std::string instrumentNamePath = LoadHelper::findInstrumentNexusPath(entry);
241 std::string instrumentName = entry.getString(instrumentNamePath.append("/name"));
242 if (instrumentName.empty())
243 throw std::runtime_error("Cannot set the instrument name from the Nexus file!");
244 boost::to_lower(instrumentName);
245 if (instrumentName == "d17") {
247 } else if (instrumentName == "figaro") {
249 } else {
250 std::ostringstream str;
251 str << "Unsupported instrument: " << instrumentName << '.';
252 throw std::runtime_error(str.str());
253 }
254 g_log.debug() << "Instrument name: " << instrumentName << '\n';
256 m_offsetFrom = "VirtualChopper";
257 m_chopper1Name = "Chopper1";
258 m_chopper2Name = "Chopper2";
259 } else if (m_instrument == Supported::FIGARO) {
260 m_sampleAngleName = "CollAngle.actual_coll_angle";
261 m_offsetFrom = "CollAngle";
262 // FIGARO: find out which of the four choppers are used
263 NXInt firstChopper = entry.openNXInt("instrument/ChopperSetting/firstChopper");
264 firstChopper.load();
265 NXInt secondChopper = entry.openNXInt("instrument/ChopperSetting/secondChopper");
266 secondChopper.load();
267 m_chopper1Name = "chopper" + std::to_string(firstChopper[0]);
268 m_chopper2Name = "chopper" + std::to_string(secondChopper[0]);
269 }
270 // get acquisition mode
271 NXInt acqMode = entry.openNXInt("acquisition_mode");
272 acqMode.load();
273 m_acqMode = acqMode[0];
274 m_acqMode ? g_log.debug("TOF mode") : g_log.debug("Monochromatic Mode");
275}
276
283 if (m_acqMode && (getPropertyValue("XUnit") == "Wavelength")) {
284 auto convertToWavelength = createChildAlgorithm("ConvertUnits", -1, -1, true);
285 convertToWavelength->initialize();
286 convertToWavelength->setProperty<MatrixWorkspace_sptr>("InputWorkspace", m_localWorkspace);
287 convertToWavelength->setProperty<MatrixWorkspace_sptr>("OutputWorkspace", m_localWorkspace);
288 convertToWavelength->setPropertyValue("Target", "Wavelength");
289 convertToWavelength->executeAsChildAlg();
290 }
291}
292
299void LoadILLReflectometry::initWorkspace(const std::vector<std::vector<int>> &monitorsData) {
300
301 g_log.debug() << "Number of monitors: " << monitorsData.size() << '\n';
302 for (size_t i = 0; i < monitorsData.size(); ++i) {
303 if (monitorsData[i].size() != m_numberOfChannels)
304 g_log.debug() << "Data size of monitor ID " << i << " is " << monitorsData[i].size() << '\n';
305 }
306 // create the workspace
307 m_localWorkspace = DataObjects::create<DataObjects::Workspace2D>(m_numberOfHistograms + monitorsData.size(),
308 HistogramData::BinEdges(m_numberOfChannels + 1));
309
310 if (m_acqMode)
311 m_localWorkspace->getAxis(0)->unit() = UnitFactory::Instance().create("TOF");
312 m_localWorkspace->setYUnitLabel("Counts");
313
314 // the start time is needed in the workspace when loading the parameter file
315 m_localWorkspace->mutableRun().addProperty<std::string>("start_time", m_startTime.toISO8601String());
316}
317
324 // PSD data D17 256 x 1 x 1000
325 // PSD data FIGARO 1 x 256 x 1000
326 m_startTime = DateAndTime(LoadHelper::dateTimeInIsoFormat(entry.getString("start_time")));
327 if (m_acqMode) {
328 NXFloat timeOfFlight = entry.openNXFloat("instrument/PSD/time_of_flight");
329 timeOfFlight.load();
330 m_channelWidth = static_cast<double>(timeOfFlight[0]);
331 m_numberOfChannels = size_t(timeOfFlight[1]);
332 m_tofDelay = timeOfFlight[2];
333 } else { // monochromatic mode
335 }
336 NXInt nChannels = entry.openNXInt("instrument/PSD/detsize");
337 nChannels.load();
338 m_numberOfHistograms = nChannels[0];
339}
340
348double LoadILLReflectometry::doubleFromRun(const std::string &entryName) const {
349 if (m_localWorkspace->run().hasProperty(entryName)) {
350 return m_localWorkspace->run().getPropertyValueAsType<double>(entryName);
351 } else {
352 throw std::runtime_error("The log with the given name does not exist " + entryName);
353 }
354}
355
364std::vector<int> LoadILLReflectometry::loadSingleMonitor(const NeXus::NXEntry &entry, const std::string &monitor_data) {
365 NXData dataGroup = entry.openNXData(monitor_data);
366 NXInt data = dataGroup.openIntData();
367 data.load();
368 return std::vector<int>(data(), data() + data.size());
369}
370
377std::vector<std::vector<int>> LoadILLReflectometry::loadMonitors(const NeXus::NXEntry &entry) {
378 g_log.debug("Read monitor data...");
379 // vector of monitors with one entry
380 const std::vector<std::vector<int>> monitors{loadSingleMonitor(entry, "monitor1/data"),
381 loadSingleMonitor(entry, "monitor2/data")};
382 return monitors;
383}
384
390std::vector<double> LoadILLReflectometry::getXValues() {
391 const auto &instrument = m_localWorkspace->getInstrument();
392 const auto &run = m_localWorkspace->run();
393 std::vector<double> xVals; // no initialisation
394 xVals.reserve(m_numberOfChannels + 1); // reserve memory
395 if (m_acqMode) {
397 if (run.hasProperty("Distance.edelay_delay"))
398 m_tofDelay += doubleFromRun("Distance.edelay_delay");
399 else if (run.hasProperty("Theta.edelay_delay"))
400 m_tofDelay += doubleFromRun("Theta.edelay_delay");
401 else if (run.hasProperty("MainParameters.edelay_delay")) {
402 m_tofDelay += doubleFromRun("MainParameters.edelay_delay");
403 } else {
404 g_log.warning() << "Unable to find edelay_delay from the file\n";
405 }
406 }
407 g_log.debug() << "TOF delay: " << m_tofDelay << '\n';
408 std::string chopper{"Chopper"};
409 double chop1Speed{0.0}, chop1Phase{0.0}, chop2Speed{0.0}, chop2Phase{0.0};
411 const auto duration = doubleFromRun("duration");
412 std::string chop1SpeedName, chop1PhaseName, chop2SpeedName, chop2PhaseName;
413 if (duration > 30.0) {
414 chop1SpeedName = instrument->getStringParameter("chopper1_speed")[0];
415 chop1PhaseName = instrument->getStringParameter("chopper1_phase")[0];
416 chop2SpeedName = instrument->getStringParameter("chopper2_speed")[0];
417 chop2PhaseName = instrument->getStringParameter("chopper2_phase")[0];
418 } else {
419 chop1SpeedName = instrument->getStringParameter("chopper1_speed_alt")[0];
420 chop1PhaseName = instrument->getStringParameter("chopper1_phase_alt")[0];
421 chop2SpeedName = instrument->getStringParameter("chopper2_speed_alt")[0];
422 chop2PhaseName = instrument->getStringParameter("chopper2_phase_alt")[0];
423 }
424 chop1Speed = doubleFromRun(chop1SpeedName);
425 chop1Phase = doubleFromRun(chop1PhaseName);
426 chop2Speed = doubleFromRun(chop2SpeedName);
427 chop2Phase = doubleFromRun(chop2PhaseName);
428 if (chop1Phase > 360.) {
429 // Pre-2018 D17 files which have chopper 1 phase and chopper 2 speed
430 // swapped.
431 std::swap(chop1Phase, chop2Speed);
432 }
433 } else if (m_instrument == Supported::FIGARO) {
434 chop1Phase = doubleFromRun(m_chopper1Name + ".phase");
435 // Chopper 1 phase on FIGARO is set to an arbitrary value (999.9)
436 if (chop1Phase > 360.0)
437 chop1Phase = 0.0;
438 }
439 double POFF;
440 if (run.hasProperty(m_offsetFrom + ".poff")) {
441 POFF = doubleFromRun(m_offsetFrom + ".poff");
442 } else if (run.hasProperty(m_offsetFrom + ".pickup_offset")) {
443 POFF = doubleFromRun(m_offsetFrom + ".pickup_offset");
444 } else {
445 throw std::runtime_error("Unable to find chopper pickup offset");
446 }
447 double openOffset;
448 if (run.hasProperty(m_offsetFrom + ".open_offset")) {
449 openOffset = doubleFromRun(m_offsetFrom + ".open_offset");
450 } else if (run.hasProperty(m_offsetFrom + ".openOffset")) {
451 openOffset = doubleFromRun(m_offsetFrom + ".openOffset");
452 } else {
453 throw std::runtime_error("Unable to find chopper open offset");
454 }
455 if (m_instrument == Supported::D17 && chop1Speed != 0.0 && chop2Speed != 0.0 && chop2Phase != 0.0) {
456 // virtual chopper entries are valid
457 chopper = "Virtual chopper";
458 } else {
459 // use chopper values
460 chop1Speed = doubleFromRun(m_chopper1Name + ".rotation_speed");
461 chop2Speed = doubleFromRun(m_chopper2Name + ".rotation_speed");
462 chop2Phase = doubleFromRun(m_chopper2Name + ".phase");
463 }
464 // logging
465 g_log.debug() << "Poff: " << POFF << '\n';
466 g_log.debug() << "Open offset: " << openOffset << '\n';
467 g_log.debug() << "Chopper 1 phase: " << chop1Phase << '\n';
468 g_log.debug() << chopper << " 1 speed: " << chop1Speed << '\n';
469 g_log.debug() << chopper << " 2 phase: " << chop2Phase << '\n';
470 g_log.debug() << chopper << " 2 speed: " << chop2Speed << '\n';
471
472 if (chop1Speed <= 0.0) {
473 g_log.error() << "First chopper velocity " << chop1Speed << ". Check you NeXus file.\n";
474 }
475
476 const double chopWindow = instrument->getNumberParameter("chopper_window_opening")[0];
477 m_localWorkspace->mutableRun().addProperty("ChopperWindow", chopWindow, "degree", true);
478 g_log.debug() << "Chopper Opening Window [degrees]" << chopWindow << '\n';
479
480 const double t_TOF2 = m_tofDelay - 1.e+6 * 60.0 * (POFF - chopWindow + chop2Phase - chop1Phase + openOffset) /
481 (2.0 * 360 * chop1Speed);
482 g_log.debug() << "t_TOF2: " << t_TOF2 << '\n';
483 // compute tof values
484 for (int channelIndex = 0; channelIndex < static_cast<int>(m_numberOfChannels) + 1; ++channelIndex) {
485 const double t_TOF1 = channelIndex * m_channelWidth;
486 xVals.emplace_back(t_TOF1 + t_TOF2);
487 }
488 } else {
489 g_log.debug("Time channel index for axis description \n");
490 for (size_t t = 0; t <= m_numberOfChannels; ++t)
491 xVals.emplace_back(static_cast<double>(t));
492 }
493
494 return xVals;
495}
496
504void LoadILLReflectometry::loadData(const NeXus::NXEntry &entry, const std::vector<std::vector<int>> &monitorsData,
505 const std::vector<double> &xVals) {
506 NXData dataGroup = entry.openNXData("data");
507 NXInt data = dataGroup.openIntData();
508 data.load();
509 const size_t nb_monitors = monitorsData.size();
510 Progress progress(this, 0, 1, m_numberOfHistograms + nb_monitors);
511 if (!xVals.empty()) {
512 HistogramData::BinEdges binEdges(xVals);
514 for (int j = 0; j < static_cast<int>(m_numberOfHistograms); ++j) {
515 const int *data_p = &data(0, static_cast<int>(j), 0);
516 const HistogramData::Counts counts(data_p, data_p + m_numberOfChannels);
517 m_localWorkspace->setHistogram(j, binEdges, counts);
518 m_localWorkspace->getSpectrum(j).setSpectrumNo(j);
519 progress.report();
520 }
521 for (size_t im = 0; im < nb_monitors; ++im) {
522 const int *monitor_p = monitorsData[im].data();
523 const HistogramData::Counts monitorCounts(monitor_p, monitor_p + m_numberOfChannels);
524 const size_t spectrum = im + m_numberOfHistograms;
525 m_localWorkspace->setHistogram(spectrum, binEdges, monitorCounts);
526 m_localWorkspace->getSpectrum(spectrum).setSpectrumNo(static_cast<specnum_t>(spectrum));
527 progress.report();
528 }
529 }
530}
531
537 const std::string filename{getPropertyValue("Filename")};
538 NXhandle nxfileID;
539 NXstatus stat = NXopen(filename.c_str(), NXACC_READ, &nxfileID);
540 if (stat == NX_ERROR)
541 throw Kernel::Exception::FileError("Unable to open File:", filename);
543 NXclose(&nxfileID);
545 auto const bgs3 = m_localWorkspace->mutableRun().getLogAsSingleValue("BGS3.value");
546 // log data below should be a boolean, but boolean is broken in NeXus so it cannot be saved properly
547 // when exporting data.
548 m_localWorkspace->mutableRun().addLogData(
549 new Kernel::PropertyWithValue<int>("refdown", static_cast<int>(bgs3 > 45)));
550 }
551}
552
560 if (!isDefault("ForegroundPeakCentre")) {
561 return getProperty("ForegroundPeakCentre");
562 }
563 const auto autoIndices = fitIntegrationWSIndexRange(*m_localWorkspace);
564 auto startIndex = autoIndices.first;
565 auto endIndex = autoIndices.second;
566 if (!isDefault("FitStartWorkspaceIndex")) {
567 startIndex = getProperty("FitStartWorkspaceIndex");
568 }
569 if (!isDefault("FitEndWorkspaceIndex")) {
570 endIndex = getProperty("FitEndWorkspaceIndex");
571 }
572 auto integration = createChildAlgorithm("Integration");
573 integration->initialize();
574 integration->setProperty("InputWorkspace", m_localWorkspace);
575 integration->setProperty("OutputWorkspace", "__unused_for_child");
576 integration->setProperty("StartWorkspaceIndex", startIndex);
577 integration->setProperty("EndWorkspaceIndex", endIndex);
578 if (!isDefault("FitRangeLower")) {
579 integration->setProperty("RangeLower",
580 wavelengthToTOF(getProperty("FitRangeLower"), m_sourceDistance, m_detectorDistance));
581 }
582 if (!isDefault("FitRangeUpper")) {
583 integration->setProperty("RangeUpper",
584 wavelengthToTOF(getProperty("FitRangeUpper"), m_sourceDistance, m_detectorDistance));
585 }
586 integration->execute();
587 MatrixWorkspace_sptr integralWS = integration->getProperty("OutputWorkspace");
588 auto transpose = createChildAlgorithm("Transpose");
589 transpose->initialize();
590 transpose->setProperty("InputWorkspace", integralWS);
591 transpose->setProperty("OutputWorkspace", "__unused_for_child");
592 transpose->execute();
593 integralWS = transpose->getProperty("OutputWorkspace");
594 rebinIntegralWorkspace(*integralWS);
595 // determine initial height: maximum value
596 const auto maxValueIt = std::max_element(integralWS->y(0).cbegin(), integralWS->y(0).cend());
597 const double height = *maxValueIt;
598 // determine initial centre: index of the maximum value
599 const size_t maxIndex = std::distance(integralWS->y(0).cbegin(), maxValueIt);
600 const auto centreByMax = static_cast<double>(maxIndex);
601 const auto &ys = integralWS->y(0);
602 auto lessThanHalfMax = [height](const double x) { return x < 0.5 * height; };
603 using IterType = HistogramData::HistogramY::const_iterator;
604 std::reverse_iterator<IterType> revMaxValueIt{maxValueIt};
605 auto revMinFwhmIt = std::find_if(revMaxValueIt, ys.crend(), lessThanHalfMax);
606 auto maxFwhmIt = std::find_if(maxValueIt, ys.cend(), lessThanHalfMax);
607 std::reverse_iterator<IterType> revMaxFwhmIt{maxFwhmIt};
608 if (revMinFwhmIt == ys.crend() || maxFwhmIt == ys.cend()) {
609 return centreByMax + startIndex;
610 }
611 const auto fwhm = static_cast<double>(std::distance(revMaxFwhmIt, revMinFwhmIt) + 1);
612 // generate Gaussian
613 auto func = API::FunctionFactory::Instance().createFunction("CompositeFunction");
614 auto sum = std::dynamic_pointer_cast<API::CompositeFunction>(func);
615 func = API::FunctionFactory::Instance().createFunction("Gaussian");
616 auto gaussian = std::dynamic_pointer_cast<API::IPeakFunction>(func);
617 gaussian->setHeight(height);
618 gaussian->setCentre(centreByMax);
619 gaussian->setFwhm(fwhm);
620 sum->addFunction(gaussian);
621 func = API::FunctionFactory::Instance().createFunction("LinearBackground");
622 func->setParameter("A0", 0.);
623 func->setParameter("A1", 0.);
624 sum->addFunction(func);
625 // call Fit child algorithm
626 auto fit = createChildAlgorithm("Fit");
627 fit->initialize();
628 fit->setProperty("Function", std::dynamic_pointer_cast<API::IFunction>(sum));
629 fit->setProperty("InputWorkspace", integralWS);
630 fit->setProperty("StartX", centreByMax - 3 * fwhm);
631 fit->setProperty("EndX", centreByMax + 3 * fwhm);
632 fit->execute();
633 const std::string fitStatus = fit->getProperty("OutputStatus");
634 if (fitStatus != "success") {
635 g_log.warning("Fit not successful, using position of max value.\n");
636 return centreByMax + startIndex;
637 }
638 const auto centre = gaussian->centre();
639 return centre + startIndex;
640}
641
646 const double peakCentre = reflectometryPeak();
647 m_localWorkspace->mutableRun().addProperty("reduction.line_position", peakCentre, true);
648 const double detectorCentre = getProperty("DetectorCentreFractionalIndex");
649 const std::string measurement = getPropertyValue("Measurement");
650 double two_theta = offsetAngle(peakCentre, detectorCentre, m_detectorDistance);
651 if (measurement == "ReflectedBeam") {
652 if (isDefault("BraggAngle")) {
653 if (m_sampleAngle == 0.) {
654 g_log.warning("Sample angle is either 0 or doesn't exist in the file. "
655 "Please specify BraggAngle manually for reflected beams.");
656 }
657 }
658 two_theta += 2 * (isDefault("BraggAngle") ? m_sampleAngle : getProperty("BraggAngle"));
659 }
660 return two_theta;
661}
662
672 std::string entryName;
674 if (entry.isValid("instrument/SAN/value")) {
675 entryName = "instrument/SAN/value";
676 } else if (entry.isValid("instrument/san/value")) {
677 entryName = "instrument/san/value";
678 }
679 } else {
680 if (entry.isValid("instrument/Theta/wanted_theta")) {
681 entryName = "instrument/Theta/wanted_theta";
682 }
683 }
684 if (!entryName.empty()) {
685 NXFloat angle = entry.openNXFloat(entryName);
686 angle.load();
687 m_sampleAngle = angle[0];
688 }
689}
690
693 const auto &instrument = m_localWorkspace->getInstrument();
694 const auto &detectorPanels = instrument->getAllComponentsWithName("detector");
695 if (detectorPanels.size() != 1) {
696 throw std::runtime_error("IDF should have a single 'detector' component.");
697 }
698 const auto &detector = std::dynamic_pointer_cast<const Geometry::RectangularDetector>(detectorPanels.front());
700 m_pixelWidth = std::abs(detector->xstep());
701 } else {
702 m_pixelWidth = std::abs(detector->ystep());
703 }
704}
705
709 m_localWorkspace->mutableRun().addProperty<double>("L2", m_detectorDistance, true);
710 const auto detectorRotationAngle = detectorRotation();
711 const std::string componentName = "detector";
712 const RotationPlane rotPlane = m_instrument == Supported::D17 ? RotationPlane::horizontal : RotationPlane::vertical;
713 const auto newpos = detectorPosition(rotPlane, m_detectorDistance, detectorRotationAngle);
714 LoadHelper::moveComponent(m_localWorkspace, componentName, newpos);
715 // apply a local rotation to stay perpendicular to the beam
716 const auto rotation = detectorFaceRotation(rotPlane, detectorRotationAngle);
718}
719
722 double slit1ToSample{0.0};
723 double slit2ToSample{0.0};
724 const auto &run = m_localWorkspace->run();
726 const double deflectionAngle = doubleFromRun(m_sampleAngleName);
727 const double offset = m_sampleZOffset / std::cos(degToRad(deflectionAngle));
728 if (run.hasProperty("Distance.S2_Sample")) {
729 slit1ToSample = mmToMeter(doubleFromRun("Distance.S2_Sample"));
730 } else {
731 throw std::runtime_error("Unable to find slit 2 to sample distance");
732 }
733 if (run.hasProperty("Distance.S3_Sample")) {
734 slit2ToSample = mmToMeter(doubleFromRun("Distance.S3_Sample"));
735 } else {
736 throw std::runtime_error("Unable to find slit 3 to sample distance");
737 }
738 slit2ToSample += offset;
739 slit1ToSample += offset;
740 } else {
741 if (run.hasProperty("Distance.S2toSample")) {
742 slit1ToSample = mmToMeter(doubleFromRun("Distance.S2toSample"));
743 } else if (run.hasProperty("Distance.S2_Sample")) {
744 slit1ToSample = mmToMeter(doubleFromRun("Distance.S2_Sample"));
745 } else {
746 throw std::runtime_error("Unable to find slit 2 to sample distance");
747 }
748 if (run.hasProperty("Distance.S3toSample")) {
749 slit2ToSample = mmToMeter(doubleFromRun("Distance.S3toSample"));
750 } else if (run.hasProperty("Distance.S3_Sample")) {
751 slit2ToSample = mmToMeter(doubleFromRun("Distance.S3_Sample"));
752 } else {
753 throw std::runtime_error("Unable to find slit 3 to sample distance");
754 }
755 }
756 V3D pos{0.0, 0.0, -slit1ToSample};
758 pos = {0.0, 0.0, -slit2ToSample};
760}
761
765 const std::string source = "chopper1";
766 const V3D newPos{0.0, 0.0, -m_sourceDistance};
768}
769
773}
774
781double LoadILLReflectometry::offsetAngle(const double peakCentre, const double detectorCentre,
782 const double detectorDistance) const {
783 const double offsetWidth = (detectorCentre - peakCentre) * m_pixelWidth;
784 // Sign depends on the definition of detector angle and which way
785 // spectrum numbers increase. Negative convention is used for D17 and positive for FIGARO.
786 auto const sign = m_instrument == Supported::FIGARO ? 1 : -1;
787 return sign * radToDeg(std::atan2(offsetWidth, detectorDistance));
788}
789
794 std::string distanceEntry;
796 distanceEntry = "det.value";
797 } else {
798 distanceEntry = "Distance.Sample_CenterOfDetector_distance";
799 }
800 return mmToMeter(doubleFromRun(distanceEntry));
801}
802
806 std::string offsetEntry;
807 const auto &run = m_localWorkspace->run();
808 if (run.hasProperty("Theta.sampleHorizontalOffset"))
809 offsetEntry = "Theta.sampleHorizontalOffset";
810 else if (run.hasProperty("Distance.sampleHorizontalOffset")) {
811 offsetEntry = "Distance.sampleHorizontalOffset";
812 } else if (run.hasProperty("Distance.sample_changer_horizontal_offset")) {
813 offsetEntry = "Distance.sample_changer_horizontal_offset";
814 } else if (run.hasProperty("Theta.sample_horizontal_offset")) {
815 offsetEntry = "Theta.sample_horizontal_offset";
816 } else {
817 throw std::runtime_error("Unable to find sample horizontal offset in the file");
818 }
819 m_sampleZOffset = mmToMeter(doubleFromRun(offsetEntry));
820 }
821}
822
828 const std::string chopperGapUnit = m_localWorkspace->getInstrument()->getStringParameter("chopper_gap_unit")[0];
829 const double scale = (chopperGapUnit == "cm") ? 0.01 : (chopperGapUnit == "mm") ? 0.001 : 1.;
830 const auto &run = m_localWorkspace->run();
831 double pairCentre;
832 double pairSeparation;
833 if (run.hasProperty("VirtualChopper.dist_chop_samp")) {
834 // This is valid up to cycle 191 included
835 pairCentre = doubleFromRun("VirtualChopper.dist_chop_samp"); // in [m]
836 // It is in meter, just restate its unit
837 m_localWorkspace->mutableRun().addProperty("VirtualChopper.dist_chop_samp", pairCentre, "meter", true);
838 pairSeparation = doubleFromRun("Distance.ChopperGap") * scale; // [cm] to [m]
839 // Here it's the first chopper to sample, so we need to subtract half of
840 // the gap
841 pairCentre -= 0.5 * pairSeparation;
842 } else if (run.hasProperty("VirtualChopper.MidChopper_Sample_distance")) {
843 // Valid from cycle 192 onwards, here it's directly the mid-chopper to
844 // sample, but in mm
845 pairCentre = mmToMeter(doubleFromRun("VirtualChopper.MidChopper_Sample_distance")); // [mm] to [m]
846 pairSeparation = doubleFromRun("Distance.ChopperGap") * scale; // in [m]
847 m_localWorkspace->mutableRun().addProperty("VirtualChopper.MidChopper_Sample_distance", pairCentre, "meter",
848 true);
849 } else if (run.hasProperty("Distance.Chopper1_Sample")) {
850 // Valid from cycle 212 onwards
851 pairCentre = mmToMeter(doubleFromRun("Distance.MidChopper_Sample")); // [mm] to [m]
852 pairSeparation = doubleFromRun("Distance.ChopperGap") * scale; // in [m]
853 m_localWorkspace->mutableRun().addProperty("VirtualChopper.MidChopper_Sample_distance", pairCentre, "meter",
854 true);
855 } else {
856 throw std::runtime_error("Unable to extract chopper to sample distance");
857 }
858 // in any case we overwrite the chopper gap now in meters, so that the
859 // reduction code works universally
860 m_localWorkspace->mutableRun().addProperty("Distance.ChopperGap", pairSeparation, "meter", true);
861 return pairCentre;
862 } else {
863 const double chopperDist = mmToMeter(doubleFromRun("ChopperSetting.chopperpair_sample_distance"));
864 std::string entryName = "correct_chopper_sample_distance";
865 bool correctChopperSampleDistance = m_localWorkspace->getInstrument()->getBoolParameter(entryName)[0];
866 auto offset = 0.0;
867 if (correctChopperSampleDistance) {
868 const double deflectionAngle = doubleFromRun(m_sampleAngleName);
869 offset = m_sampleZOffset / std::cos(degToRad(deflectionAngle));
870 }
871 return chopperDist + offset;
872 }
873}
874
875} // namespace Mantid::DataHandling
const std::vector< double > * lambda
double height
Definition: GetAllEi.cpp:155
#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
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
bool isDefault(const std::string &name) const
Definition: Algorithm.cpp:2084
const SpectrumInfo & spectrumInfo() const
Return a reference to the SpectrumInfo object.
@ Load
allowed here which will be passed to the algorithm
Definition: FileProperty.h:52
Base MatrixWorkspace Abstract Class.
HistogramData::HistogramX & mutableX(const size_t index) &
virtual std::size_t getNumberHistograms() const =0
Returns the number of histograms in the workspace.
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
A property class for workspaces.
LoadILLReflectometry : Loads an ILL reflectometry Nexus data file.
size_t m_numberOfChannels
(1: TOF (default), 0: monochromatic)
void exec() override
Execute the algorithm.
double doubleFromRun(const std::string &entryName) const
LoadILLReflectometry::doubleFromRun Returns a sample log a single double.
void loadNexusEntriesIntoProperties()
Use the LoadHelper utility to load most of the nexus entries into workspace sample log properties.
std::vector< int > loadSingleMonitor(const NeXus::NXEntry &entry, const std::string &monitor_data)
Load single monitor.
double detectorRotation()
Compute the detector rotation angle around origin.
double offsetAngle(const double peakCentre, const double detectorCentre, const double detectorDistance) const
Calculate the offset angle between detector center and peak.
double sampleDetectorDistance() const
Return the sample to detector distance for the current instrument.
double sourceSampleDistance() const
Return the source to sample distance for the current instrument.
void loadInstrument()
Run the Child Algorithm LoadInstrument.
Mantid::Types::Core::DateAndTime m_startTime
void initPixelWidth()
Initialize m_pixelWidth from the IDF as the step of rectangular detector.
void initWorkspace(const std::vector< std::vector< int > > &monitorsData)
Creates the workspace and initialises member variables with the corresponding values.
void sampleHorizontalOffset()
Return the horizontal offset along the z axis.
void initNames(const NeXus::NXEntry &entry)
Init names of sample logs based on instrument specific NeXus file entries.
void convertTofToWavelength()
Call child algorithm ConvertUnits for conversion from TOF to wavelength Note that DAN calibration is ...
void sampleAngle(const NeXus::NXEntry &entry)
Sets the sample angle (i.e.
std::vector< std::vector< int > > loadMonitors(const NeXus::NXEntry &entry)
Load monitor data.
void placeDetector()
Update detector position according to data file.
std::vector< double > getXValues()
Determine x values (unit time-of-flight)
double reflectometryPeak()
Gaussian fit to determine peak position if no user position given.
double collimationAngle() const
Return the incident neutron deflection angle.
void loadData(const NeXus::NXEntry &entry, const std::vector< std::vector< int > > &monitorsData, const std::vector< double > &xVals)
Load data from nexus file.
void loadDataDetails(const NeXus::NXEntry &entry)
Load Data details (number of tubes, channels, etc)
void placeSlits()
Update the slit positions.
void init() override
Initialize the algorithm's properties.
BoundedValidator is a validator that requires the values to be between upper or lower bounds,...
Records the filename and the description of failure.
Definition: Exception.h:98
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 error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
Defines a wrapper around a file whose internal structure can be accessed using the NeXus API.
OptionalBool : Tri-state bool.
Definition: OptionalBool.h:25
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...
static double run(const std::string &src, const std::string &dest, const double srcValue, const double l1, const double l2, const double theta, const DeltaEMode::Type emode, const double efixed)
Convert a single value between the given units (as strings)
Class for 3D vectors.
Definition: V3D.h:34
NXInt openNXInt(const std::string &name) const
Creates and opens an integer dataset.
Definition: NexusClasses.h:546
void close()
Close this class.
NXFloat openNXFloat(const std::string &name) const
Creates and opens a float dataset.
Definition: NexusClasses.h:551
bool isValid(const std::string &path) const
Check if a path exists relative to the current class path.
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 size() const
Returns the size of the data buffer.
Definition: NexusClasses.h:267
Implements NXdata Nexus class.
Definition: NexusClasses.h:795
NXInt openIntData()
Opens data of int type.
Definition: NexusClasses.h:828
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.
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
void rotateComponent(const API::MatrixWorkspace_sptr &ws, const std::string &componentName, const Kernel::Quat &rot)
LoadHelper::rotateComponent.
Definition: LoadHelper.cpp:472
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 findInstrumentNexusPath(const Mantid::NeXus::NXEntry &)
Finds the path for the instrument name in the nexus file Usually of the form: entry0/<NXinstrument cl...
Definition: LoadHelper.cpp:37
void moveComponent(const API::MatrixWorkspace_sptr &ws, const std::string &componentName, const Kernel::V3D &newPos)
LoadHelper::moveComponent.
Definition: LoadHelper.cpp:454
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::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
int32_t specnum_t
Typedef for a spectrum Number.
Definition: IDTypes.h:16
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition: EmptyValues.h:43
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