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"
12#include "MantidAPI/Run.h"
24#include "MantidKernel/Quat.h"
27#include "MantidKernel/V3D.h"
30#include "MantidNexus/NexusFile.h"
31
32namespace {
33
35double wavelengthToTOF(const double lambda, const double l1, const double l2) {
36 return Mantid::Kernel::UnitConversion::run("Wavelength", "TOF", lambda, l1, l2, 0.,
38}
39
44constexpr double degToRad(const double x) { return x * M_PI / 180.; }
45
50constexpr double radToDeg(const double x) { return x * 180. / M_PI; }
51
56constexpr double mmToMeter(const double x) { return x * 1.e-3; }
57
62std::pair<int, int> fitIntegrationWSIndexRange(const Mantid::API::MatrixWorkspace &ws) {
63 const size_t nHisto = ws.getNumberHistograms();
64 int begin = 0;
65 const auto &spectrumInfo = ws.spectrumInfo();
66 for (size_t i = 0; i < nHisto; ++i) {
67 if (!spectrumInfo.isMonitor(i)) {
68 break;
69 }
70 ++begin;
71 }
72 int end = static_cast<int>(nHisto) - 1;
73 for (ptrdiff_t i = static_cast<ptrdiff_t>(nHisto) - 1; i != 0; --i) {
74 if (!spectrumInfo.isMonitor(i)) {
75 break;
76 }
77 --end;
78 }
79 return std::pair<int, int>{begin, end};
80}
81
83enum class RotationPlane { horizontal, vertical };
84
91Mantid::Kernel::V3D detectorPosition(const RotationPlane plane, const double distance, const double angle) {
92 const double a = degToRad(angle);
93 double x = 0, y = 0, z = 0;
94 switch (plane) {
95 case RotationPlane::horizontal:
96 x = distance * std::sin(a);
97 z = distance * std::cos(a);
98 break;
99 case RotationPlane::vertical:
100 y = distance * std::sin(a);
101 z = distance * std::cos(a);
102 break;
103 }
104 return Mantid::Kernel::V3D(x, y, z);
105}
106
112Mantid::Kernel::Quat detectorFaceRotation(const RotationPlane plane, const double angle) {
113 const Mantid::Kernel::V3D axis = [plane]() {
114 double x = 0, y = 0;
115 switch (plane) {
116 case RotationPlane::horizontal:
117 y = 1;
118 break;
119 case RotationPlane::vertical:
120 x = -1;
121 break;
122 }
123 return Mantid::Kernel::V3D(x, y, 0);
124 }();
125 return Mantid::Kernel::Quat(angle, axis);
126}
127} // anonymous namespace
128
129namespace Mantid::DataHandling {
130
131using namespace Kernel;
132using namespace API;
133using namespace Nexus;
134using Mantid::Types::Core::DateAndTime;
135
136// Register the algorithm into the AlgorithmFactory
137DECLARE_NEXUS_FILELOADER_ALGORITHM(LoadILLReflectometry)
138
139
145int LoadILLReflectometry::confidence(Nexus::NexusDescriptorLazy &descriptor) const {
146
147 // fields existent only at the ILL
148 if ((descriptor.isEntry("/entry0/wavelength") || // ILL D17
149 descriptor.isEntry("/entry0/theta")) // ILL FIGARO
150 && descriptor.isEntry("/entry0/experiment_identifier") && descriptor.isEntry("/entry0/mode") &&
151 (descriptor.isEntry("/entry0/instrument/VirtualChopper") || // ILL D17
152 descriptor.isEntry("/entry0/instrument/Theta")) // ILL FIGARO
153 )
154 return 80;
155 else
156 return 0;
157}
158
162 std::make_unique<FileProperty>("Filename", std::string(), FileProperty::Load, ".nxs", Direction::Input),
163 "Name of the Nexus file to load");
164 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", std::string(), Direction::Output),
165 "Name of the output workspace");
166 declareProperty("ForegroundPeakCentre", EMPTY_DBL(),
167 "Foreground peak position in fractional workspace "
168 "index (if not given the peak is searched for and fitted).");
169 declareProperty("DetectorCentreFractionalIndex", 127.5,
170 "The fractional workspace index of the geometric centre of "
171 "the detector at incident beam axis (127.5 for D17 and Figaro).");
172 const std::vector<std::string> measurements({"DirectBeam", "ReflectedBeam"});
173 declareProperty("Measurement", "DirectBeam", std::make_unique<StringListValidator>(measurements),
174 "Load as direct or reflected beam.");
175 declareProperty("BraggAngle", EMPTY_DBL(), "The bragg angle necessary for reflected beam.");
176 declareProperty("FitStartWorkspaceIndex", 0, std::make_unique<BoundedValidator<int>>(0, 255),
177 "Start workspace index used for peak fitting.");
178 declareProperty("FitEndWorkspaceIndex", 255, std::make_unique<BoundedValidator<int>>(0, 255),
179 "End workspace index used for peak fitting.");
180 declareProperty("FitRangeLower", -1., "Minimum wavelength used for peak fitting.");
181 declareProperty("FitRangeUpper", -1., "Maximum wavelength used for peak fitting.");
182 const std::vector<std::string> availableUnits{"Wavelength", "TimeOfFlight"};
183 declareProperty("XUnit", "Wavelength", std::make_shared<StringListValidator>(availableUnits),
184 "X unit of the OutputWorkspace");
185 declareProperty(std::make_unique<PropertyManagerProperty>("LogsToReplace", Direction::Input),
186 "A dictionary of key-pair values for logs to be replaced.");
187}
188
191 Nexus::NXRoot root(getPropertyValue("Filename"));
192 NXEntry firstEntry{root.openFirstEntry()};
193 initNames(firstEntry);
194 sampleAngle(firstEntry);
195 std::vector<std::string> monitorNames{getMonitorNames()};
196 loadDataDetails(firstEntry);
197 initWorkspace(monitorNames);
200 loadData(firstEntry, monitorNames, getXValues());
201 firstEntry.close();
202 root.close();
205 placeSource();
207 placeSlits();
209 setProperty("OutputWorkspace", m_localWorkspace);
210}
211
219 std::string instrumentNameAddress = LoadHelper::findInstrumentNexusAddress(entry);
220 std::string instrumentName = entry.getString(instrumentNameAddress.append("/name"));
221 if (instrumentName.empty())
222 throw std::runtime_error("Cannot set the instrument name from the Nexus file!");
223 boost::to_lower(instrumentName);
224 if (instrumentName == "d17") {
226 } else if (instrumentName == "figaro") {
228 } else {
229 std::ostringstream str;
230 str << "Unsupported instrument: " << instrumentName << '.';
231 throw std::runtime_error(str.str());
232 }
233 g_log.debug() << "Instrument name: " << instrumentName << '\n';
235 m_offsetFrom = "VirtualChopper";
236 m_chopper1Name = "Chopper1";
237 m_chopper2Name = "Chopper2";
238 } else if (m_instrument == Supported::FIGARO) {
239 m_sampleAngleName = "CollAngle.actual_coll_angle";
240 m_offsetFrom = "CollAngle";
241 // FIGARO: find out which of the four choppers are used
242 NXInt firstChopper = entry.openNXInt("instrument/ChopperSetting/firstChopper");
243 firstChopper.load();
244 NXInt secondChopper = entry.openNXInt("instrument/ChopperSetting/secondChopper");
245 secondChopper.load();
246 m_chopper1Name = "chopper" + std::to_string(firstChopper[0]);
247 m_chopper2Name = "chopper" + std::to_string(secondChopper[0]);
248 }
249 // get acquisition mode
250 NXInt acqMode = entry.openNXInt("acquisition_mode");
251 acqMode.load();
252 m_acqMode = acqMode[0];
253 m_acqMode ? g_log.debug("TOF mode") : g_log.debug("Monochromatic Mode");
254}
255
262 if (m_acqMode && (getPropertyValue("XUnit") == "Wavelength")) {
263 auto convertToWavelength = createChildAlgorithm("ConvertUnits", -1, -1, true);
264 convertToWavelength->initialize();
265 convertToWavelength->setProperty<MatrixWorkspace_sptr>("InputWorkspace", m_localWorkspace);
266 convertToWavelength->setProperty<MatrixWorkspace_sptr>("OutputWorkspace", m_localWorkspace);
267 convertToWavelength->setPropertyValue("Target", "Wavelength");
268 convertToWavelength->executeAsChildAlg();
269 }
270}
271
278void LoadILLReflectometry::initWorkspace(const std::vector<std::string> &monitorNames) {
279
280 g_log.debug() << "Number of monitors: " << monitorNames.size() << '\n';
281 for (size_t i = 0; i < monitorNames.size(); ++i) {
282 if (monitorNames[i].size() != m_numberOfChannels)
283 g_log.debug() << "Data size of monitor ID " << i << " is " << monitorNames[i].size() << '\n';
284 }
285 // create the workspace
286 m_localWorkspace = DataObjects::create<DataObjects::Workspace2D>(m_numberOfHistograms + monitorNames.size(),
287 HistogramData::BinEdges(m_numberOfChannels + 1));
288
289 if (m_acqMode)
290 m_localWorkspace->getAxis(0)->unit() = UnitFactory::Instance().create("TOF");
291 m_localWorkspace->setYUnitLabel("Counts");
292
293 // the start time is needed in the workspace when loading the parameter file
294 m_localWorkspace->mutableRun().addProperty<std::string>("start_time", m_startTime.toISO8601String());
295}
296
303 // PSD data D17 256 x 1 x 1000
304 // PSD data FIGARO 1 x 256 x 1000
305 m_startTime = DateAndTime(LoadHelper::dateTimeInIsoFormat(entry.getString("start_time")));
306 if (m_acqMode) {
307 NXFloat timeOfFlight = entry.openNXFloat("instrument/PSD/time_of_flight");
308 timeOfFlight.load();
309 m_channelWidth = static_cast<double>(timeOfFlight[0]);
310 m_numberOfChannels = size_t(timeOfFlight[1]);
311 m_tofDelay = timeOfFlight[2];
312 } else { // monochromatic mode
314 }
315 NXInt nChannels = entry.openNXInt("instrument/PSD/detsize");
316 nChannels.load();
317 m_numberOfHistograms = nChannels[0];
318}
319
327double LoadILLReflectometry::doubleFromRun(const std::string &entryName) const {
328 if (m_localWorkspace->run().hasProperty(entryName)) {
329 return m_localWorkspace->run().getPropertyValueAsType<double>(entryName);
330 } else {
331 throw std::runtime_error("The log with the given name does not exist " + entryName);
332 }
333}
334
340std::vector<std::string> LoadILLReflectometry::getMonitorNames() {
341 // vector of addresses to monitor data
342 const std::vector<std::string> monitors{"monitor1/data", "monitor2/data"};
343 return monitors;
344}
345
351std::vector<double> LoadILLReflectometry::getXValues() {
352 const auto &instrument = m_localWorkspace->getInstrument();
353 const auto &run = m_localWorkspace->run();
354 std::vector<double> xVals; // no initialisation
355 xVals.reserve(m_numberOfChannels + 1); // reserve memory
356 if (m_acqMode) {
358 if (run.hasProperty("Distance.edelay_delay"))
359 m_tofDelay += doubleFromRun("Distance.edelay_delay");
360 else if (run.hasProperty("Theta.edelay_delay"))
361 m_tofDelay += doubleFromRun("Theta.edelay_delay");
362 else if (run.hasProperty("MainParameters.edelay_delay")) {
363 m_tofDelay += doubleFromRun("MainParameters.edelay_delay");
364 } else {
365 g_log.warning() << "Unable to find edelay_delay from the file\n";
366 }
367 }
368 g_log.debug() << "TOF delay: " << m_tofDelay << '\n';
369 std::string chopper{"Chopper"};
370 double chop1Speed{0.0}, chop1Phase{0.0}, chop2Speed{0.0}, chop2Phase{0.0};
372 const auto duration = doubleFromRun("duration");
373 std::string chop1SpeedName, chop1PhaseName, chop2SpeedName, chop2PhaseName;
374 if (duration > 30.0) {
375 chop1SpeedName = instrument->getStringParameter("chopper1_speed")[0];
376 chop1PhaseName = instrument->getStringParameter("chopper1_phase")[0];
377 chop2SpeedName = instrument->getStringParameter("chopper2_speed")[0];
378 chop2PhaseName = instrument->getStringParameter("chopper2_phase")[0];
379 } else {
380 chop1SpeedName = instrument->getStringParameter("chopper1_speed_alt")[0];
381 chop1PhaseName = instrument->getStringParameter("chopper1_phase_alt")[0];
382 chop2SpeedName = instrument->getStringParameter("chopper2_speed_alt")[0];
383 chop2PhaseName = instrument->getStringParameter("chopper2_phase_alt")[0];
384 }
385 chop1Speed = doubleFromRun(chop1SpeedName);
386 chop1Phase = doubleFromRun(chop1PhaseName);
387 chop2Speed = doubleFromRun(chop2SpeedName);
388 chop2Phase = doubleFromRun(chop2PhaseName);
389 if (chop1Phase > 360.) {
390 // Pre-2018 D17 files which have chopper 1 phase and chopper 2 speed
391 // swapped.
392 std::swap(chop1Phase, chop2Speed);
393 }
394 } else if (m_instrument == Supported::FIGARO) {
395 chop1Phase = doubleFromRun(m_chopper1Name + ".phase");
396 // Chopper 1 phase on FIGARO is set to an arbitrary value (999.9)
397 if (chop1Phase > 360.0)
398 chop1Phase = 0.0;
399 }
400 double POFF;
401 if (run.hasProperty(m_offsetFrom + ".poff")) {
402 POFF = doubleFromRun(m_offsetFrom + ".poff");
403 } else if (run.hasProperty(m_offsetFrom + ".pickup_offset")) {
404 POFF = doubleFromRun(m_offsetFrom + ".pickup_offset");
405 } else {
406 throw std::runtime_error("Unable to find chopper pickup offset");
407 }
408 double openOffset;
409 if (run.hasProperty(m_offsetFrom + ".open_offset")) {
410 openOffset = doubleFromRun(m_offsetFrom + ".open_offset");
411 } else if (run.hasProperty(m_offsetFrom + ".openOffset")) {
412 openOffset = doubleFromRun(m_offsetFrom + ".openOffset");
413 } else {
414 throw std::runtime_error("Unable to find chopper open offset");
415 }
416 if (m_instrument == Supported::D17 && chop1Speed != 0.0 && chop2Speed != 0.0 && chop2Phase != 0.0) {
417 // virtual chopper entries are valid
418 chopper = "Virtual chopper";
419 } else {
420 // use chopper values
421 chop1Speed = doubleFromRun(m_chopper1Name + ".rotation_speed");
422 chop2Speed = doubleFromRun(m_chopper2Name + ".rotation_speed");
423 chop2Phase = doubleFromRun(m_chopper2Name + ".phase");
424 }
425 // logging
426 g_log.debug() << "Poff: " << POFF << '\n';
427 g_log.debug() << "Open offset: " << openOffset << '\n';
428 g_log.debug() << "Chopper 1 phase: " << chop1Phase << '\n';
429 g_log.debug() << chopper << " 1 speed: " << chop1Speed << '\n';
430 g_log.debug() << chopper << " 2 phase: " << chop2Phase << '\n';
431 g_log.debug() << chopper << " 2 speed: " << chop2Speed << '\n';
432
433 if (chop1Speed <= 0.0) {
434 g_log.error() << "First chopper velocity " << chop1Speed << ". Check you NeXus file.\n";
435 }
436
437 const double chopWindow = instrument->getNumberParameter("chopper_window_opening")[0];
438 m_localWorkspace->mutableRun().addProperty("ChopperWindow", chopWindow, "degree", true);
439 g_log.debug() << "Chopper Opening Window [degrees]" << chopWindow << '\n';
440
441 const double t_TOF2 = m_tofDelay - 1.e+6 * 60.0 * (POFF - chopWindow + chop2Phase - chop1Phase + openOffset) /
442 (2.0 * 360 * chop1Speed);
443 g_log.debug() << "t_TOF2: " << t_TOF2 << '\n';
444 // compute tof values
445 for (int channelIndex = 0; channelIndex < static_cast<int>(m_numberOfChannels) + 1; ++channelIndex) {
446 const double t_TOF1 = channelIndex * m_channelWidth;
447 xVals.emplace_back(t_TOF1 + t_TOF2);
448 }
449 } else {
450 g_log.debug("Time channel index for axis description \n");
451 for (size_t t = 0; t <= m_numberOfChannels; ++t)
452 xVals.emplace_back(static_cast<double>(t));
453 }
454
455 return xVals;
456}
457
465void LoadILLReflectometry::loadData(const Nexus::NXEntry &entry, const std::vector<std::string> &monitorNames,
466 const std::vector<double> &xVals) {
467 auto data = LoadHelper::getIntDataset(entry, "data");
468 data.load();
469 const int nb_monitors = static_cast<int>(monitorNames.size());
470 Progress progress(this, 0, 1, m_numberOfHistograms + nb_monitors);
471 if (!xVals.empty()) {
472 // first, load data
474 progress.report();
475 // then, the monitor data
476 for (auto im = 0; im < nb_monitors; ++im) {
477 const std::string monitorDataSetName("monitor" + std::to_string(im + 1) + "/data");
478 auto monitorData = LoadHelper::getIntDataset(entry, monitorDataSetName);
479 monitorData.load();
481 static_cast<int>(m_numberOfHistograms) + im);
482 progress.report();
483 }
484 }
485}
486
492 const std::string filename{getPropertyValue("Filename")};
493 API::Run &runDetails = m_localWorkspace->mutableRun();
494
495 try {
496 Nexus::File nxfileID(filename, NXaccess::READ);
497 LoadHelper::addNexusFieldsToWsRun(nxfileID, runDetails);
498 } catch (Nexus::Exception const &) {
499 throw Kernel::Exception::FileError("Unable to open File:", filename);
500 }
501
503 auto const bgs3 = m_localWorkspace->mutableRun().getLogAsSingleValue("BGS3.value");
504 // log data below should be a boolean, but boolean is broken in NeXus so it cannot be saved properly
505 // when exporting data.
506 m_localWorkspace->mutableRun().addLogData(
507 new Kernel::PropertyWithValue<int>("refdown", static_cast<int>(bgs3 > 45)));
508 }
509 const PropertyManager_const_sptr logsToReplace = getProperty("LogsToReplace");
510 if (logsToReplace != nullptr && logsToReplace->propertyCount() > 0) {
511 for (auto *prop : logsToReplace->getProperties()) {
512 if (prop->type() == "number") { // logs are either treated as numbers and cast to double or are saved as strings
513 runDetails.addProperty(prop->name(), std::stod(prop->value()), true);
514 } else
515 runDetails.addProperty(prop->name(), prop->value(), true);
516 }
517 }
518}
519
527 if (!isDefault("ForegroundPeakCentre")) {
528 return getProperty("ForegroundPeakCentre");
529 }
530 const auto autoIndices = fitIntegrationWSIndexRange(*m_localWorkspace);
531 auto startIndex = autoIndices.first;
532 auto endIndex = autoIndices.second;
533 if (!isDefault("FitStartWorkspaceIndex")) {
534 startIndex = getProperty("FitStartWorkspaceIndex");
535 }
536 if (!isDefault("FitEndWorkspaceIndex")) {
537 endIndex = getProperty("FitEndWorkspaceIndex");
538 }
539 auto findLine = createChildAlgorithm("FindReflectometryLines", -1.0, -1.0, true, 3);
540 findLine->setProperty("InputWorkspace", m_localWorkspace);
541 findLine->setProperty("StartWorkspaceIndex", startIndex);
542 findLine->setProperty("EndWorkspaceIndex", endIndex);
543 findLine->setProperty("BackgroundType", "Linear");
544 findLine->setProperty("AcceptChangesInFunctionTooSmall", false);
545 findLine->setProperty("AcceptChangesInParameterTooSmall", false);
546 if (!isDefault("FitRangeLower")) {
547 findLine->setProperty("RangeLower",
548 wavelengthToTOF(getProperty("FitRangeLower"), m_sourceDistance, m_detectorDistance));
549 }
550 if (!isDefault("FitRangeUpper")) {
551 findLine->setProperty("RangeUpper",
552 wavelengthToTOF(getProperty("FitRangeUpper"), m_sourceDistance, m_detectorDistance));
553 }
554 findLine->execute();
555 return findLine->getProperty("LineCentre");
556}
557
562 const double peakCentre = reflectometryPeak();
563 m_localWorkspace->mutableRun().addProperty("reduction.line_position", peakCentre, true);
564 const double detectorCentre = getProperty("DetectorCentreFractionalIndex");
565 const std::string measurement = getPropertyValue("Measurement");
566 double two_theta = offsetAngle(peakCentre, detectorCentre, m_detectorDistance);
567 if (measurement == "ReflectedBeam") {
568 if (isDefault("BraggAngle")) {
569 if (m_sampleAngle == 0.) {
570 g_log.warning("Sample angle is either 0 or doesn't exist in the file. "
571 "Please specify BraggAngle manually for reflected beams.");
572 }
573 }
574 two_theta += 2 * (isDefault("BraggAngle") ? m_sampleAngle : getProperty("BraggAngle"));
575 }
576 return two_theta;
577}
578
590 std::string entryName;
592 if (entry.isValid("instrument/SAN/value")) {
593 entryName = "instrument/SAN/value";
594 } else if (entry.isValid("instrument/san/value")) {
595 entryName = "instrument/san/value";
596 }
597 } else {
598 if (entry.isValid("instrument/Theta/wanted_theta")) {
599 entryName = "instrument/Theta/wanted_theta";
600 }
601 }
602 if (!entryName.empty()) {
603 NXFloat angle = entry.openNXFloat(entryName);
604 angle.load();
605 m_sampleAngle = angle[0];
606 }
607}
608
611 const auto &componentInfo = m_localWorkspace->componentInfo();
612 if (!componentInfo.uniqueName("detector")) {
613 throw std::runtime_error("IDF should have a single 'detector' component.");
614 }
615 const size_t detectorIndex = componentInfo.indexOfAny("detector");
616 const auto grid = componentInfo.pixelGridComponent(detectorIndex);
618 m_pixelWidth = std::abs(grid.xStep);
619 } else {
620 m_pixelWidth = std::abs(grid.yStep);
621 }
622}
623
627 m_localWorkspace->mutableRun().addProperty<double>("L2", m_detectorDistance, true);
628 const auto detectorRotationAngle = detectorRotation();
629 const std::string componentName = "detector";
630 const RotationPlane rotPlane = m_instrument == Supported::D17 ? RotationPlane::horizontal : RotationPlane::vertical;
631 const auto newpos = detectorPosition(rotPlane, m_detectorDistance, detectorRotationAngle);
632 LoadHelper::moveComponent(m_localWorkspace, componentName, newpos);
633 // apply a local rotation to stay perpendicular to the beam
634 const auto rotation = detectorFaceRotation(rotPlane, detectorRotationAngle);
636}
637
640 double slit1ToSample{0.0};
641 double slit2ToSample{0.0};
642 const auto &run = m_localWorkspace->run();
644 const double deflectionAngle = doubleFromRun(m_sampleAngleName);
645 const double offset = m_sampleZOffset / std::cos(degToRad(deflectionAngle));
646 if (run.hasProperty("Distance.S2_Sample")) {
647 slit1ToSample = mmToMeter(doubleFromRun("Distance.S2_Sample"));
648 } else {
649 throw std::runtime_error("Unable to find slit 2 to sample distance");
650 }
651 if (run.hasProperty("Distance.S3_Sample")) {
652 slit2ToSample = mmToMeter(doubleFromRun("Distance.S3_Sample"));
653 } else {
654 throw std::runtime_error("Unable to find slit 3 to sample distance");
655 }
656 slit2ToSample += offset;
657 slit1ToSample += offset;
658 } else {
659 if (run.hasProperty("Distance.S2toSample")) {
660 slit1ToSample = mmToMeter(doubleFromRun("Distance.S2toSample"));
661 } else if (run.hasProperty("Distance.S2_Sample")) {
662 slit1ToSample = mmToMeter(doubleFromRun("Distance.S2_Sample"));
663 } else {
664 throw std::runtime_error("Unable to find slit 2 to sample distance");
665 }
666 if (run.hasProperty("Distance.S3toSample")) {
667 slit2ToSample = mmToMeter(doubleFromRun("Distance.S3toSample"));
668 } else if (run.hasProperty("Distance.S3_Sample")) {
669 slit2ToSample = mmToMeter(doubleFromRun("Distance.S3_Sample"));
670 } else {
671 throw std::runtime_error("Unable to find slit 3 to sample distance");
672 }
673 }
674 V3D pos{0.0, 0.0, -slit1ToSample};
676 pos = {0.0, 0.0, -slit2ToSample};
678}
679
683 const std::string source = "chopper1";
684 const V3D newPos{0.0, 0.0, -m_sourceDistance};
686}
687
692
699double LoadILLReflectometry::offsetAngle(const double peakCentre, const double detectorCentre,
700 const double detectorDistance) const {
701 const double offsetWidth = (detectorCentre - peakCentre) * m_pixelWidth;
702 // Sign depends on the definition of detector angle and which way
703 // spectrum numbers increase. Negative convention is used for D17 and positive for FIGARO.
704 auto const sign = m_instrument == Supported::FIGARO ? 1 : -1;
705 return sign * radToDeg(std::atan2(offsetWidth, detectorDistance));
706}
707
712 std::string distanceEntry;
714 distanceEntry = "det.value";
715 } else {
716 distanceEntry = "Distance.Sample_CenterOfDetector_distance";
717 }
718 return mmToMeter(doubleFromRun(distanceEntry));
719}
720
724 std::string offsetEntry;
725 const auto &run = m_localWorkspace->run();
726 if (run.hasProperty("Theta.sampleHorizontalOffset"))
727 offsetEntry = "Theta.sampleHorizontalOffset";
728 else if (run.hasProperty("Distance.sampleHorizontalOffset")) {
729 offsetEntry = "Distance.sampleHorizontalOffset";
730 } else if (run.hasProperty("Distance.sample_changer_horizontal_offset")) {
731 offsetEntry = "Distance.sample_changer_horizontal_offset";
732 } else if (run.hasProperty("Theta.sample_horizontal_offset")) {
733 offsetEntry = "Theta.sample_horizontal_offset";
734 } else {
735 throw std::runtime_error("Unable to find sample horizontal offset in the file");
736 }
737 m_sampleZOffset = mmToMeter(doubleFromRun(offsetEntry));
738 }
739}
740
745 const auto &run = m_localWorkspace->run();
747 const std::string chopperGapUnit = m_localWorkspace->getInstrument()->getStringParameter("chopper_gap_unit")[0];
748 const double scale = (chopperGapUnit == "cm") ? 0.01 : (chopperGapUnit == "mm") ? 0.001 : 1.;
749 double pairCentre;
750 double pairSeparation;
751 if (run.hasProperty("VirtualChopper.dist_chop_samp")) {
752 // This is valid up to cycle 191 included
753 pairCentre = doubleFromRun("VirtualChopper.dist_chop_samp"); // in [m]
754 // It is in meter, just restate its unit
755 m_localWorkspace->mutableRun().addProperty("VirtualChopper.dist_chop_samp", pairCentre, "meter", true);
756 pairSeparation = doubleFromRun("Distance.ChopperGap") * scale; // [cm] to [m]
757 // Here it's the first chopper to sample, so we need to subtract half of
758 // the gap
759 pairCentre -= 0.5 * pairSeparation;
760 } else if (run.hasProperty("VirtualChopper.MidChopper_Sample_distance")) {
761 // Valid from cycle 192 onwards, here it's directly the mid-chopper to
762 // sample, but in mm
763 pairCentre = mmToMeter(doubleFromRun("VirtualChopper.MidChopper_Sample_distance")); // [mm] to [m]
764 pairSeparation = doubleFromRun("Distance.ChopperGap") * scale; // in [m]
765 m_localWorkspace->mutableRun().addProperty("VirtualChopper.MidChopper_Sample_distance", pairCentre, "meter",
766 true);
767 } else if (run.hasProperty("Distance.Chopper1_Sample")) {
768 // Valid from cycle 212 onwards
769 pairCentre = mmToMeter(doubleFromRun("Distance.MidChopper_Sample")); // [mm] to [m]
770 pairSeparation = doubleFromRun("Distance.ChopperGap") * scale; // in [m]
771 m_localWorkspace->mutableRun().addProperty("VirtualChopper.MidChopper_Sample_distance", pairCentre, "meter",
772 true);
773 } else {
774 throw std::runtime_error("Unable to extract chopper to sample distance");
775 }
776 // in any case we overwrite the chopper gap now in meters, so that the
777 // reduction code works universally
778 m_localWorkspace->mutableRun().addProperty("Distance.ChopperGap", pairSeparation, "meter", true);
779 return pairCentre;
780 } else {
781 if (run.hasProperty("ChopperSetting.chopperpair_sample_distance")) { // until cycle 231
782 const double chopperDist = mmToMeter(doubleFromRun("ChopperSetting.chopperpair_sample_distance"));
783 std::string entryName = "correct_chopper_sample_distance";
784 bool correctChopperSampleDistance = m_localWorkspace->getInstrument()->getBoolParameter(entryName)[0];
785 auto offset = 0.0;
786 if (correctChopperSampleDistance) {
787 const double deflectionAngle = doubleFromRun(m_sampleAngleName);
788 offset = m_sampleZOffset / std::cos(degToRad(deflectionAngle));
789 }
790 return chopperDist + offset;
791 } else if (run.hasProperty("Distance.MidChopper_Sample")) { // since cycle 231
792 return mmToMeter(doubleFromRun("Distance.MidChopper_Sample"));
793 } else {
794 throw std::runtime_error("Unable to extract chopper to sample distance");
795 }
796 }
797}
798
799} // namespace Mantid::DataHandling
const std::vector< double > * lambda
IntArray detectorIndex
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:423
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:90
Base MatrixWorkspace Abstract Class.
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:36
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