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#include "MantidAPI/Axis.h"
15#include "MantidAPI/Run.h"
28#include "MantidKernel/Quat.h"
31#include "MantidKernel/V3D.h"
34#include "MantidNexus/NexusFile.h"
35
36namespace {
37
39double wavelengthToTOF(const double lambda, const double l1, const double l2) {
40 return Mantid::Kernel::UnitConversion::run("Wavelength", "TOF", lambda, l1, l2, 0.,
42}
43
48constexpr double degToRad(const double x) { return x * M_PI / 180.; }
49
54constexpr double radToDeg(const double x) { return x * 180. / M_PI; }
55
60constexpr double mmToMeter(const double x) { return x * 1.e-3; }
61
66std::pair<int, int> fitIntegrationWSIndexRange(const Mantid::API::MatrixWorkspace &ws) {
67 const size_t nHisto = ws.getNumberHistograms();
68 int begin = 0;
69 const auto &spectrumInfo = ws.spectrumInfo();
70 for (size_t i = 0; i < nHisto; ++i) {
71 if (!spectrumInfo.isMonitor(i)) {
72 break;
73 }
74 ++begin;
75 }
76 int end = static_cast<int>(nHisto) - 1;
77 for (ptrdiff_t i = static_cast<ptrdiff_t>(nHisto) - 1; i != 0; --i) {
78 if (!spectrumInfo.isMonitor(i)) {
79 break;
80 }
81 --end;
82 }
83 return std::pair<int, int>{begin, end};
84}
85
89void rebinIntegralWorkspace(Mantid::API::MatrixWorkspace &ws) {
90 auto &xs = ws.mutableX(0);
91 std::iota(xs.begin(), xs.end(), 0.0);
92}
93
95enum class RotationPlane { horizontal, vertical };
96
103Mantid::Kernel::V3D detectorPosition(const RotationPlane plane, const double distance, const double angle) {
104 const double a = degToRad(angle);
105 double x = 0, y = 0, z = 0;
106 switch (plane) {
107 case RotationPlane::horizontal:
108 x = distance * std::sin(a);
109 z = distance * std::cos(a);
110 break;
111 case RotationPlane::vertical:
112 y = distance * std::sin(a);
113 z = distance * std::cos(a);
114 break;
115 }
116 return Mantid::Kernel::V3D(x, y, z);
117}
118
124Mantid::Kernel::Quat detectorFaceRotation(const RotationPlane plane, const double angle) {
125 const Mantid::Kernel::V3D axis = [plane]() {
126 double x = 0, y = 0;
127 switch (plane) {
128 case RotationPlane::horizontal:
129 y = 1;
130 break;
131 case RotationPlane::vertical:
132 x = -1;
133 break;
134 }
135 return Mantid::Kernel::V3D(x, y, 0);
136 }();
137 return Mantid::Kernel::Quat(angle, axis);
138}
139} // anonymous namespace
140
141namespace Mantid::DataHandling {
142
143using namespace Kernel;
144using namespace API;
145using namespace Nexus;
146using Mantid::Types::Core::DateAndTime;
147
148// Register the algorithm into the AlgorithmFactory
149DECLARE_NEXUS_FILELOADER_ALGORITHM(LoadILLReflectometry)
150
151
157int LoadILLReflectometry::confidence(Nexus::NexusDescriptor &descriptor) const {
158
159 // fields existent only at the ILL
160 if ((descriptor.isEntry("/entry0/wavelength") || // ILL D17
161 descriptor.isEntry("/entry0/theta")) // ILL FIGARO
162 && descriptor.isEntry("/entry0/experiment_identifier") && descriptor.isEntry("/entry0/mode") &&
163 (descriptor.isEntry("/entry0/instrument/VirtualChopper") || // ILL D17
164 descriptor.isEntry("/entry0/instrument/Theta")) // ILL FIGARO
165 )
166 return 80;
167 else
168 return 0;
169}
170
174 std::make_unique<FileProperty>("Filename", std::string(), FileProperty::Load, ".nxs", Direction::Input),
175 "Name of the Nexus file to load");
176 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", std::string(), Direction::Output),
177 "Name of the output workspace");
178 declareProperty("ForegroundPeakCentre", EMPTY_DBL(),
179 "Foreground peak position in fractional workspace "
180 "index (if not given the peak is searched for and fitted).");
181 declareProperty("DetectorCentreFractionalIndex", 127.5,
182 "The fractional workspace index of the geometric centre of "
183 "the detector at incident beam axis (127.5 for D17 and Figaro).");
184 const std::vector<std::string> measurements({"DirectBeam", "ReflectedBeam"});
185 declareProperty("Measurement", "DirectBeam", std::make_unique<StringListValidator>(measurements),
186 "Load as direct or reflected beam.");
187 declareProperty("BraggAngle", EMPTY_DBL(), "The bragg angle necessary for reflected beam.");
188 declareProperty("FitStartWorkspaceIndex", 0, std::make_unique<BoundedValidator<int>>(0, 255),
189 "Start workspace index used for peak fitting.");
190 declareProperty("FitEndWorkspaceIndex", 255, std::make_unique<BoundedValidator<int>>(0, 255),
191 "End workspace index used for peak fitting.");
192 declareProperty("FitRangeLower", -1., "Minimum wavelength used for peak fitting.");
193 declareProperty("FitRangeUpper", -1., "Maximum wavelength used for peak fitting.");
194 const std::vector<std::string> availableUnits{"Wavelength", "TimeOfFlight"};
195 declareProperty("XUnit", "Wavelength", std::make_shared<StringListValidator>(availableUnits),
196 "X unit of the OutputWorkspace");
197 declareProperty(std::make_unique<PropertyManagerProperty>("LogsToReplace", Direction::Input),
198 "A dictionary of key-pair values for logs to be replaced.");
199}
200
203 Nexus::NXRoot root(getPropertyValue("Filename"));
204 NXEntry firstEntry{root.openFirstEntry()};
205 initNames(firstEntry);
206 sampleAngle(firstEntry);
207 std::vector<std::string> monitorNames{getMonitorNames()};
208 loadDataDetails(firstEntry);
209 initWorkspace(monitorNames);
212 loadData(firstEntry, monitorNames, getXValues());
213 firstEntry.close();
214 root.close();
217 placeSource();
219 placeSlits();
221 setProperty("OutputWorkspace", m_localWorkspace);
222}
223
231 std::string instrumentNameAddress = LoadHelper::findInstrumentNexusAddress(entry);
232 std::string instrumentName = entry.getString(instrumentNameAddress.append("/name"));
233 if (instrumentName.empty())
234 throw std::runtime_error("Cannot set the instrument name from the Nexus file!");
235 boost::to_lower(instrumentName);
236 if (instrumentName == "d17") {
238 } else if (instrumentName == "figaro") {
240 } else {
241 std::ostringstream str;
242 str << "Unsupported instrument: " << instrumentName << '.';
243 throw std::runtime_error(str.str());
244 }
245 g_log.debug() << "Instrument name: " << instrumentName << '\n';
247 m_offsetFrom = "VirtualChopper";
248 m_chopper1Name = "Chopper1";
249 m_chopper2Name = "Chopper2";
250 } else if (m_instrument == Supported::FIGARO) {
251 m_sampleAngleName = "CollAngle.actual_coll_angle";
252 m_offsetFrom = "CollAngle";
253 // FIGARO: find out which of the four choppers are used
254 NXInt firstChopper = entry.openNXInt("instrument/ChopperSetting/firstChopper");
255 firstChopper.load();
256 NXInt secondChopper = entry.openNXInt("instrument/ChopperSetting/secondChopper");
257 secondChopper.load();
258 m_chopper1Name = "chopper" + std::to_string(firstChopper[0]);
259 m_chopper2Name = "chopper" + std::to_string(secondChopper[0]);
260 }
261 // get acquisition mode
262 NXInt acqMode = entry.openNXInt("acquisition_mode");
263 acqMode.load();
264 m_acqMode = acqMode[0];
265 m_acqMode ? g_log.debug("TOF mode") : g_log.debug("Monochromatic Mode");
266}
267
274 if (m_acqMode && (getPropertyValue("XUnit") == "Wavelength")) {
275 auto convertToWavelength = createChildAlgorithm("ConvertUnits", -1, -1, true);
276 convertToWavelength->initialize();
277 convertToWavelength->setProperty<MatrixWorkspace_sptr>("InputWorkspace", m_localWorkspace);
278 convertToWavelength->setProperty<MatrixWorkspace_sptr>("OutputWorkspace", m_localWorkspace);
279 convertToWavelength->setPropertyValue("Target", "Wavelength");
280 convertToWavelength->executeAsChildAlg();
281 }
282}
283
290void LoadILLReflectometry::initWorkspace(const std::vector<std::string> &monitorNames) {
291
292 g_log.debug() << "Number of monitors: " << monitorNames.size() << '\n';
293 for (size_t i = 0; i < monitorNames.size(); ++i) {
294 if (monitorNames[i].size() != m_numberOfChannels)
295 g_log.debug() << "Data size of monitor ID " << i << " is " << monitorNames[i].size() << '\n';
296 }
297 // create the workspace
298 m_localWorkspace = DataObjects::create<DataObjects::Workspace2D>(m_numberOfHistograms + monitorNames.size(),
299 HistogramData::BinEdges(m_numberOfChannels + 1));
300
301 if (m_acqMode)
302 m_localWorkspace->getAxis(0)->unit() = UnitFactory::Instance().create("TOF");
303 m_localWorkspace->setYUnitLabel("Counts");
304
305 // the start time is needed in the workspace when loading the parameter file
306 m_localWorkspace->mutableRun().addProperty<std::string>("start_time", m_startTime.toISO8601String());
307}
308
315 // PSD data D17 256 x 1 x 1000
316 // PSD data FIGARO 1 x 256 x 1000
317 m_startTime = DateAndTime(LoadHelper::dateTimeInIsoFormat(entry.getString("start_time")));
318 if (m_acqMode) {
319 NXFloat timeOfFlight = entry.openNXFloat("instrument/PSD/time_of_flight");
320 timeOfFlight.load();
321 m_channelWidth = static_cast<double>(timeOfFlight[0]);
322 m_numberOfChannels = size_t(timeOfFlight[1]);
323 m_tofDelay = timeOfFlight[2];
324 } else { // monochromatic mode
326 }
327 NXInt nChannels = entry.openNXInt("instrument/PSD/detsize");
328 nChannels.load();
329 m_numberOfHistograms = nChannels[0];
330}
331
339double LoadILLReflectometry::doubleFromRun(const std::string &entryName) const {
340 if (m_localWorkspace->run().hasProperty(entryName)) {
341 return m_localWorkspace->run().getPropertyValueAsType<double>(entryName);
342 } else {
343 throw std::runtime_error("The log with the given name does not exist " + entryName);
344 }
345}
346
352std::vector<std::string> LoadILLReflectometry::getMonitorNames() {
353 // vector of addresses to monitor data
354 const std::vector<std::string> monitors{"monitor1/data", "monitor2/data"};
355 return monitors;
356}
357
363std::vector<double> LoadILLReflectometry::getXValues() {
364 const auto &instrument = m_localWorkspace->getInstrument();
365 const auto &run = m_localWorkspace->run();
366 std::vector<double> xVals; // no initialisation
367 xVals.reserve(m_numberOfChannels + 1); // reserve memory
368 if (m_acqMode) {
370 if (run.hasProperty("Distance.edelay_delay"))
371 m_tofDelay += doubleFromRun("Distance.edelay_delay");
372 else if (run.hasProperty("Theta.edelay_delay"))
373 m_tofDelay += doubleFromRun("Theta.edelay_delay");
374 else if (run.hasProperty("MainParameters.edelay_delay")) {
375 m_tofDelay += doubleFromRun("MainParameters.edelay_delay");
376 } else {
377 g_log.warning() << "Unable to find edelay_delay from the file\n";
378 }
379 }
380 g_log.debug() << "TOF delay: " << m_tofDelay << '\n';
381 std::string chopper{"Chopper"};
382 double chop1Speed{0.0}, chop1Phase{0.0}, chop2Speed{0.0}, chop2Phase{0.0};
384 const auto duration = doubleFromRun("duration");
385 std::string chop1SpeedName, chop1PhaseName, chop2SpeedName, chop2PhaseName;
386 if (duration > 30.0) {
387 chop1SpeedName = instrument->getStringParameter("chopper1_speed")[0];
388 chop1PhaseName = instrument->getStringParameter("chopper1_phase")[0];
389 chop2SpeedName = instrument->getStringParameter("chopper2_speed")[0];
390 chop2PhaseName = instrument->getStringParameter("chopper2_phase")[0];
391 } else {
392 chop1SpeedName = instrument->getStringParameter("chopper1_speed_alt")[0];
393 chop1PhaseName = instrument->getStringParameter("chopper1_phase_alt")[0];
394 chop2SpeedName = instrument->getStringParameter("chopper2_speed_alt")[0];
395 chop2PhaseName = instrument->getStringParameter("chopper2_phase_alt")[0];
396 }
397 chop1Speed = doubleFromRun(chop1SpeedName);
398 chop1Phase = doubleFromRun(chop1PhaseName);
399 chop2Speed = doubleFromRun(chop2SpeedName);
400 chop2Phase = doubleFromRun(chop2PhaseName);
401 if (chop1Phase > 360.) {
402 // Pre-2018 D17 files which have chopper 1 phase and chopper 2 speed
403 // swapped.
404 std::swap(chop1Phase, chop2Speed);
405 }
406 } else if (m_instrument == Supported::FIGARO) {
407 chop1Phase = doubleFromRun(m_chopper1Name + ".phase");
408 // Chopper 1 phase on FIGARO is set to an arbitrary value (999.9)
409 if (chop1Phase > 360.0)
410 chop1Phase = 0.0;
411 }
412 double POFF;
413 if (run.hasProperty(m_offsetFrom + ".poff")) {
414 POFF = doubleFromRun(m_offsetFrom + ".poff");
415 } else if (run.hasProperty(m_offsetFrom + ".pickup_offset")) {
416 POFF = doubleFromRun(m_offsetFrom + ".pickup_offset");
417 } else {
418 throw std::runtime_error("Unable to find chopper pickup offset");
419 }
420 double openOffset;
421 if (run.hasProperty(m_offsetFrom + ".open_offset")) {
422 openOffset = doubleFromRun(m_offsetFrom + ".open_offset");
423 } else if (run.hasProperty(m_offsetFrom + ".openOffset")) {
424 openOffset = doubleFromRun(m_offsetFrom + ".openOffset");
425 } else {
426 throw std::runtime_error("Unable to find chopper open offset");
427 }
428 if (m_instrument == Supported::D17 && chop1Speed != 0.0 && chop2Speed != 0.0 && chop2Phase != 0.0) {
429 // virtual chopper entries are valid
430 chopper = "Virtual chopper";
431 } else {
432 // use chopper values
433 chop1Speed = doubleFromRun(m_chopper1Name + ".rotation_speed");
434 chop2Speed = doubleFromRun(m_chopper2Name + ".rotation_speed");
435 chop2Phase = doubleFromRun(m_chopper2Name + ".phase");
436 }
437 // logging
438 g_log.debug() << "Poff: " << POFF << '\n';
439 g_log.debug() << "Open offset: " << openOffset << '\n';
440 g_log.debug() << "Chopper 1 phase: " << chop1Phase << '\n';
441 g_log.debug() << chopper << " 1 speed: " << chop1Speed << '\n';
442 g_log.debug() << chopper << " 2 phase: " << chop2Phase << '\n';
443 g_log.debug() << chopper << " 2 speed: " << chop2Speed << '\n';
444
445 if (chop1Speed <= 0.0) {
446 g_log.error() << "First chopper velocity " << chop1Speed << ". Check you NeXus file.\n";
447 }
448
449 const double chopWindow = instrument->getNumberParameter("chopper_window_opening")[0];
450 m_localWorkspace->mutableRun().addProperty("ChopperWindow", chopWindow, "degree", true);
451 g_log.debug() << "Chopper Opening Window [degrees]" << chopWindow << '\n';
452
453 const double t_TOF2 = m_tofDelay - 1.e+6 * 60.0 * (POFF - chopWindow + chop2Phase - chop1Phase + openOffset) /
454 (2.0 * 360 * chop1Speed);
455 g_log.debug() << "t_TOF2: " << t_TOF2 << '\n';
456 // compute tof values
457 for (int channelIndex = 0; channelIndex < static_cast<int>(m_numberOfChannels) + 1; ++channelIndex) {
458 const double t_TOF1 = channelIndex * m_channelWidth;
459 xVals.emplace_back(t_TOF1 + t_TOF2);
460 }
461 } else {
462 g_log.debug("Time channel index for axis description \n");
463 for (size_t t = 0; t <= m_numberOfChannels; ++t)
464 xVals.emplace_back(static_cast<double>(t));
465 }
466
467 return xVals;
468}
469
477void LoadILLReflectometry::loadData(const Nexus::NXEntry &entry, const std::vector<std::string> &monitorNames,
478 const std::vector<double> &xVals) {
479 auto data = LoadHelper::getIntDataset(entry, "data");
480 data.load();
481 const int nb_monitors = static_cast<int>(monitorNames.size());
482 Progress progress(this, 0, 1, m_numberOfHistograms + nb_monitors);
483 if (!xVals.empty()) {
484 // first, load data
486 progress.report();
487 // then, the monitor data
488 for (auto im = 0; im < nb_monitors; ++im) {
489 const std::string monitorDataSetName("monitor" + std::to_string(im + 1) + "/data");
490 auto monitorData = LoadHelper::getIntDataset(entry, monitorDataSetName);
491 monitorData.load();
493 static_cast<int>(m_numberOfHistograms) + im);
494 progress.report();
495 }
496 }
497}
498
504 const std::string filename{getPropertyValue("Filename")};
505 API::Run &runDetails = m_localWorkspace->mutableRun();
506
507 try {
508 Nexus::File nxfileID(filename, NXaccess::READ);
509 LoadHelper::addNexusFieldsToWsRun(nxfileID, runDetails);
510 } catch (Nexus::Exception const &) {
511 throw Kernel::Exception::FileError("Unable to open File:", filename);
512 }
513
515 auto const bgs3 = m_localWorkspace->mutableRun().getLogAsSingleValue("BGS3.value");
516 // log data below should be a boolean, but boolean is broken in NeXus so it cannot be saved properly
517 // when exporting data.
518 m_localWorkspace->mutableRun().addLogData(
519 new Kernel::PropertyWithValue<int>("refdown", static_cast<int>(bgs3 > 45)));
520 }
521 const PropertyManager_const_sptr logsToReplace = getProperty("LogsToReplace");
522 if (logsToReplace != nullptr && logsToReplace->propertyCount() > 0) {
523 for (auto *prop : logsToReplace->getProperties()) {
524 if (prop->type() == "number") { // logs are either treated as numbers and cast to double or are saved as strings
525 runDetails.addProperty(prop->name(), std::stod(prop->value()), true);
526 } else
527 runDetails.addProperty(prop->name(), prop->value(), true);
528 }
529 }
530}
531
539 if (!isDefault("ForegroundPeakCentre")) {
540 return getProperty("ForegroundPeakCentre");
541 }
542 const auto autoIndices = fitIntegrationWSIndexRange(*m_localWorkspace);
543 auto startIndex = autoIndices.first;
544 auto endIndex = autoIndices.second;
545 if (!isDefault("FitStartWorkspaceIndex")) {
546 startIndex = getProperty("FitStartWorkspaceIndex");
547 }
548 if (!isDefault("FitEndWorkspaceIndex")) {
549 endIndex = getProperty("FitEndWorkspaceIndex");
550 }
551 auto integration = createChildAlgorithm("Integration");
552 integration->initialize();
553 integration->setProperty("InputWorkspace", m_localWorkspace);
554 integration->setProperty("OutputWorkspace", "__unused_for_child");
555 integration->setProperty("StartWorkspaceIndex", startIndex);
556 integration->setProperty("EndWorkspaceIndex", endIndex);
557 if (!isDefault("FitRangeLower")) {
558 integration->setProperty("RangeLower",
559 wavelengthToTOF(getProperty("FitRangeLower"), m_sourceDistance, m_detectorDistance));
560 }
561 if (!isDefault("FitRangeUpper")) {
562 integration->setProperty("RangeUpper",
563 wavelengthToTOF(getProperty("FitRangeUpper"), m_sourceDistance, m_detectorDistance));
564 }
565 integration->execute();
566 MatrixWorkspace_sptr integralWS = integration->getProperty("OutputWorkspace");
567 auto transpose = createChildAlgorithm("Transpose");
568 transpose->initialize();
569 transpose->setProperty("InputWorkspace", integralWS);
570 transpose->setProperty("OutputWorkspace", "__unused_for_child");
571 transpose->execute();
572 integralWS = transpose->getProperty("OutputWorkspace");
573 rebinIntegralWorkspace(*integralWS);
574 // determine initial height: maximum value
575 const auto maxValueIt = std::max_element(integralWS->y(0).cbegin(), integralWS->y(0).cend());
576 const double height = *maxValueIt;
577 // determine initial centre: index of the maximum value
578 const size_t maxIndex = std::distance(integralWS->y(0).cbegin(), maxValueIt);
579 const auto centreByMax = static_cast<double>(maxIndex);
580 const auto &ys = integralWS->y(0);
581 auto lessThanHalfMax = [height](const double x) { return x < 0.5 * height; };
582 using IterType = HistogramData::HistogramY::const_iterator;
583 std::reverse_iterator<IterType> revMaxValueIt{maxValueIt};
584 auto revMinFwhmIt = std::find_if(revMaxValueIt, ys.crend(), lessThanHalfMax);
585 auto maxFwhmIt = std::find_if(maxValueIt, ys.cend(), lessThanHalfMax);
586 std::reverse_iterator<IterType> revMaxFwhmIt{maxFwhmIt};
587 if (revMinFwhmIt == ys.crend() || maxFwhmIt == ys.cend()) {
588 return centreByMax + startIndex;
589 }
590 const auto fwhm = static_cast<double>(std::distance(revMaxFwhmIt, revMinFwhmIt) + 1);
591 // generate Gaussian
592 auto func = API::FunctionFactory::Instance().createFunction("CompositeFunction");
593 auto sum =
594 Kernel::DynamicPointerCastHelper::dynamicPointerCastWithCheck<API::CompositeFunction, API::IFunction>(func);
595 func = API::FunctionFactory::Instance().createFunction("Gaussian");
596 auto gaussian =
597 Kernel::DynamicPointerCastHelper::dynamicPointerCastWithCheck<API::IPeakFunction, API::IFunction>(func);
598 gaussian->setHeight(height);
599 gaussian->setCentre(centreByMax);
600 gaussian->setFwhm(fwhm);
601 sum->addFunction(gaussian);
602 func = API::FunctionFactory::Instance().createFunction("LinearBackground");
603 func->setParameter("A0", 0.);
604 func->setParameter("A1", 0.);
605 sum->addFunction(func);
606 // call Fit child algorithm
607 auto fit = createChildAlgorithm("Fit");
608 fit->initialize();
609 fit->setProperty("Function", std::dynamic_pointer_cast<API::IFunction>(sum));
610 fit->setProperty("InputWorkspace", integralWS);
611 fit->setProperty("StartX", centreByMax - 3 * fwhm);
612 fit->setProperty("EndX", centreByMax + 3 * fwhm);
613 fit->execute();
614 const std::string fitStatus = fit->getProperty("OutputStatus");
615 if (fitStatus != "success") {
616 g_log.warning("Fit not successful, using position of max value.\n");
617 return centreByMax + startIndex;
618 }
619 const auto centre = gaussian->centre();
620 return centre + startIndex;
621}
622
627 const double peakCentre = reflectometryPeak();
628 m_localWorkspace->mutableRun().addProperty("reduction.line_position", peakCentre, true);
629 const double detectorCentre = getProperty("DetectorCentreFractionalIndex");
630 const std::string measurement = getPropertyValue("Measurement");
631 double two_theta = offsetAngle(peakCentre, detectorCentre, m_detectorDistance);
632 if (measurement == "ReflectedBeam") {
633 if (isDefault("BraggAngle")) {
634 if (m_sampleAngle == 0.) {
635 g_log.warning("Sample angle is either 0 or doesn't exist in the file. "
636 "Please specify BraggAngle manually for reflected beams.");
637 }
638 }
639 two_theta += 2 * (isDefault("BraggAngle") ? m_sampleAngle : getProperty("BraggAngle"));
640 }
641 return two_theta;
642}
643
655 std::string entryName;
657 if (entry.isValid("instrument/SAN/value")) {
658 entryName = "instrument/SAN/value";
659 } else if (entry.isValid("instrument/san/value")) {
660 entryName = "instrument/san/value";
661 }
662 } else {
663 if (entry.isValid("instrument/Theta/wanted_theta")) {
664 entryName = "instrument/Theta/wanted_theta";
665 }
666 }
667 if (!entryName.empty()) {
668 NXFloat angle = entry.openNXFloat(entryName);
669 angle.load();
670 m_sampleAngle = angle[0];
671 }
672}
673
676 const auto &instrument = m_localWorkspace->getInstrument();
677 const auto &detectorPanels = instrument->getAllComponentsWithName("detector");
678 if (detectorPanels.size() != 1) {
679 throw std::runtime_error("IDF should have a single 'detector' component.");
680 }
681 const auto &detector = std::dynamic_pointer_cast<const Geometry::RectangularDetector>(detectorPanels.front());
683 m_pixelWidth = std::abs(detector->xstep());
684 } else {
685 m_pixelWidth = std::abs(detector->ystep());
686 }
687}
688
692 m_localWorkspace->mutableRun().addProperty<double>("L2", m_detectorDistance, true);
693 const auto detectorRotationAngle = detectorRotation();
694 const std::string componentName = "detector";
695 const RotationPlane rotPlane = m_instrument == Supported::D17 ? RotationPlane::horizontal : RotationPlane::vertical;
696 const auto newpos = detectorPosition(rotPlane, m_detectorDistance, detectorRotationAngle);
697 LoadHelper::moveComponent(m_localWorkspace, componentName, newpos);
698 // apply a local rotation to stay perpendicular to the beam
699 const auto rotation = detectorFaceRotation(rotPlane, detectorRotationAngle);
701}
702
705 double slit1ToSample{0.0};
706 double slit2ToSample{0.0};
707 const auto &run = m_localWorkspace->run();
709 const double deflectionAngle = doubleFromRun(m_sampleAngleName);
710 const double offset = m_sampleZOffset / std::cos(degToRad(deflectionAngle));
711 if (run.hasProperty("Distance.S2_Sample")) {
712 slit1ToSample = mmToMeter(doubleFromRun("Distance.S2_Sample"));
713 } else {
714 throw std::runtime_error("Unable to find slit 2 to sample distance");
715 }
716 if (run.hasProperty("Distance.S3_Sample")) {
717 slit2ToSample = mmToMeter(doubleFromRun("Distance.S3_Sample"));
718 } else {
719 throw std::runtime_error("Unable to find slit 3 to sample distance");
720 }
721 slit2ToSample += offset;
722 slit1ToSample += offset;
723 } else {
724 if (run.hasProperty("Distance.S2toSample")) {
725 slit1ToSample = mmToMeter(doubleFromRun("Distance.S2toSample"));
726 } else if (run.hasProperty("Distance.S2_Sample")) {
727 slit1ToSample = mmToMeter(doubleFromRun("Distance.S2_Sample"));
728 } else {
729 throw std::runtime_error("Unable to find slit 2 to sample distance");
730 }
731 if (run.hasProperty("Distance.S3toSample")) {
732 slit2ToSample = mmToMeter(doubleFromRun("Distance.S3toSample"));
733 } else 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 }
739 V3D pos{0.0, 0.0, -slit1ToSample};
741 pos = {0.0, 0.0, -slit2ToSample};
743}
744
748 const std::string source = "chopper1";
749 const V3D newPos{0.0, 0.0, -m_sourceDistance};
751}
752
757
764double LoadILLReflectometry::offsetAngle(const double peakCentre, const double detectorCentre,
765 const double detectorDistance) const {
766 const double offsetWidth = (detectorCentre - peakCentre) * m_pixelWidth;
767 // Sign depends on the definition of detector angle and which way
768 // spectrum numbers increase. Negative convention is used for D17 and positive for FIGARO.
769 auto const sign = m_instrument == Supported::FIGARO ? 1 : -1;
770 return sign * radToDeg(std::atan2(offsetWidth, detectorDistance));
771}
772
777 std::string distanceEntry;
779 distanceEntry = "det.value";
780 } else {
781 distanceEntry = "Distance.Sample_CenterOfDetector_distance";
782 }
783 return mmToMeter(doubleFromRun(distanceEntry));
784}
785
789 std::string offsetEntry;
790 const auto &run = m_localWorkspace->run();
791 if (run.hasProperty("Theta.sampleHorizontalOffset"))
792 offsetEntry = "Theta.sampleHorizontalOffset";
793 else if (run.hasProperty("Distance.sampleHorizontalOffset")) {
794 offsetEntry = "Distance.sampleHorizontalOffset";
795 } else if (run.hasProperty("Distance.sample_changer_horizontal_offset")) {
796 offsetEntry = "Distance.sample_changer_horizontal_offset";
797 } else if (run.hasProperty("Theta.sample_horizontal_offset")) {
798 offsetEntry = "Theta.sample_horizontal_offset";
799 } else {
800 throw std::runtime_error("Unable to find sample horizontal offset in the file");
801 }
802 m_sampleZOffset = mmToMeter(doubleFromRun(offsetEntry));
803 }
804}
805
810 const auto &run = m_localWorkspace->run();
812 const std::string chopperGapUnit = m_localWorkspace->getInstrument()->getStringParameter("chopper_gap_unit")[0];
813 const double scale = (chopperGapUnit == "cm") ? 0.01 : (chopperGapUnit == "mm") ? 0.001 : 1.;
814 double pairCentre;
815 double pairSeparation;
816 if (run.hasProperty("VirtualChopper.dist_chop_samp")) {
817 // This is valid up to cycle 191 included
818 pairCentre = doubleFromRun("VirtualChopper.dist_chop_samp"); // in [m]
819 // It is in meter, just restate its unit
820 m_localWorkspace->mutableRun().addProperty("VirtualChopper.dist_chop_samp", pairCentre, "meter", true);
821 pairSeparation = doubleFromRun("Distance.ChopperGap") * scale; // [cm] to [m]
822 // Here it's the first chopper to sample, so we need to subtract half of
823 // the gap
824 pairCentre -= 0.5 * pairSeparation;
825 } else if (run.hasProperty("VirtualChopper.MidChopper_Sample_distance")) {
826 // Valid from cycle 192 onwards, here it's directly the mid-chopper to
827 // sample, but in mm
828 pairCentre = mmToMeter(doubleFromRun("VirtualChopper.MidChopper_Sample_distance")); // [mm] to [m]
829 pairSeparation = doubleFromRun("Distance.ChopperGap") * scale; // in [m]
830 m_localWorkspace->mutableRun().addProperty("VirtualChopper.MidChopper_Sample_distance", pairCentre, "meter",
831 true);
832 } else if (run.hasProperty("Distance.Chopper1_Sample")) {
833 // Valid from cycle 212 onwards
834 pairCentre = mmToMeter(doubleFromRun("Distance.MidChopper_Sample")); // [mm] to [m]
835 pairSeparation = doubleFromRun("Distance.ChopperGap") * scale; // in [m]
836 m_localWorkspace->mutableRun().addProperty("VirtualChopper.MidChopper_Sample_distance", pairCentre, "meter",
837 true);
838 } else {
839 throw std::runtime_error("Unable to extract chopper to sample distance");
840 }
841 // in any case we overwrite the chopper gap now in meters, so that the
842 // reduction code works universally
843 m_localWorkspace->mutableRun().addProperty("Distance.ChopperGap", pairSeparation, "meter", true);
844 return pairCentre;
845 } else {
846 if (run.hasProperty("ChopperSetting.chopperpair_sample_distance")) { // until cycle 231
847 const double chopperDist = mmToMeter(doubleFromRun("ChopperSetting.chopperpair_sample_distance"));
848 std::string entryName = "correct_chopper_sample_distance";
849 bool correctChopperSampleDistance = m_localWorkspace->getInstrument()->getBoolParameter(entryName)[0];
850 auto offset = 0.0;
851 if (correctChopperSampleDistance) {
852 const double deflectionAngle = doubleFromRun(m_sampleAngleName);
853 offset = m_sampleZOffset / std::cos(degToRad(deflectionAngle));
854 }
855 return chopperDist + offset;
856 } else if (run.hasProperty("Distance.MidChopper_Sample")) { // since cycle 231
857 return mmToMeter(doubleFromRun("Distance.MidChopper_Sample"));
858 } else {
859 throw std::runtime_error("Unable to extract chopper to sample distance");
860 }
861 }
862}
863
864} // namespace Mantid::DataHandling
const std::vector< double > * lambda
double height
Definition GetAllEi.cpp:155
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.
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
const SpectrumInfo & spectrumInfo() const
Return a reference to the SpectrumInfo object.
@ Load
allowed here which will be passed to the algorithm
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition LogManager.h:91
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
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
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.
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.
Mantid::Types::Core::DateAndTime m_startTime
std::vector< std::string > getMonitorNames()
Load monitor data.
void initPixelWidth()
Initialize m_pixelWidth from the IDF as the step of rectangular detector.
void sampleHorizontalOffset()
Return the horizontal offset along the z axis.
void convertTofToWavelength()
Call child algorithm ConvertUnits for conversion from TOF to wavelength Note that DAN calibration is ...
void initNames(const Nexus::NXEntry &entry)
Init names of sample logs based on instrument specific NeXus file entries.
void sampleAngle(const Nexus::NXEntry &entry)
Sets the sample angle (i.e.
void initWorkspace(const std::vector< std::string > &monitorNames)
Creates the workspace and initialises member variables with the corresponding values.
void placeDetector()
Update detector position according to data file.
void loadDataDetails(const Nexus::NXEntry &entry)
Load Data details (number of tubes, channels, etc)
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::string > &monitorNames, const std::vector< double > &xVals)
Load data from nexus file.
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:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
The concrete, templated class for properties.
Class for quaternions.
Definition Quat.h:39
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
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.
bool isValid(const std::string &address) const
Check if a address exists relative to the current class address.
Templated class implementation of NXDataSet.
void load()
Read all of the datablock in.
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 addNexusFieldsToWsRun(Nexus::File &filehandle, API::Run &runDetails, const std::string &entryName="", bool useFullAddress=false)
Add properties from a nexus file to the workspace run.
void rotateComponent(const API::MatrixWorkspace_sptr &ws, const std::string &componentName, const Kernel::Quat &rot)
LoadHelper::rotateComponent.
Nexus::NXInt getIntDataset(const Nexus::NXEntry &, const std::string &)
Fetches NXInt data from the requested group name in the entry provided.
void moveComponent(const API::MatrixWorkspace_sptr &ws, const std::string &componentName, const Kernel::V3D &newPos)
LoadHelper::moveComponent.
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::string findInstrumentNexusAddress(const Mantid::Nexus::NXEntry &)
Finds the address for the instrument name in the nexus file Usually of the form: entry0/<NXinstrument...
std::shared_ptr< const PropertyManager > PropertyManager_const_sptr
shared pointer to Mantid::Kernel::PropertyManager(const version)
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
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