Mantid
Loading...
Searching...
No Matches
LoadILLTOF3.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2025 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"
13#include "MantidAPI/Run.h"
19#include "MantidNexus/NexusFile.h"
20
21namespace {
23const std::array<std::string, 5> SUPPORTED_INSTRUMENTS = {{"IN4", "IN5", "IN6", "PANTHER", "SHARP"}};
24} // namespace
25
26namespace Mantid::DataHandling {
27
28using namespace Kernel;
29using namespace API;
30using namespace Nexus;
31using namespace HistogramData;
32
34
35
43int LoadILLTOF3::confidence(Nexus::NexusDescriptor &descriptor) const {
44
45 // fields existent only at the ILL
46 if ((descriptor.isEntry("/entry0/wavelength") && descriptor.isEntry("/entry0/experiment_identifier") &&
47 descriptor.isEntry("/entry0/mode") && !descriptor.isEntry("/entry0/dataSD") // This one is for
48 // LoadILLIndirect
49 && !descriptor.isEntry("/entry0/instrument/VirtualChopper") // This one is for
50 // LoadILLReflectometry
51 && !descriptor.isEntry("/entry0/instrument/Tx")) // This eliminates SALSA data
52 || (descriptor.isEntry("/entry0/data_scan") &&
53 descriptor.isEntry("/entry0/instrument/Detector")) // The last one is scan mode of PANTHER and SHARP
54 ) {
55 return 80;
56 } else {
57 return 0;
58 }
59}
60
62
67 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, ".nxs"),
68 "File path of the Data file to load");
69
70 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
71 "The name to use for the output workspace");
72 declareProperty("ConvertToTOF", false, "Convert the bin edges to time-of-flight", Direction::Input);
73}
74
79 // Retrieve filename
80 const std::string filenameData = getPropertyValue("Filename");
81 bool convertToTOF = getProperty("convertToTOF");
82
83 // open the root node
84 Nexus::NXRoot dataRoot(filenameData);
85 NXEntry dataFirstEntry = dataRoot.openFirstEntry();
86 m_isScan = dataFirstEntry.containsGroup("data_scan");
87
88 loadInstrumentDetails(dataFirstEntry);
89 loadTimeDetails(dataFirstEntry);
90
91 const auto monitorList = getMonitorInfo(dataFirstEntry);
92 initWorkspace(dataFirstEntry);
93
96 // load the instrument from the IDF if it exists
98
99 if (m_isScan) {
100 fillScanWorkspace(dataFirstEntry, monitorList);
101 } else {
102 fillStaticWorkspace(dataFirstEntry, monitorList, convertToTOF);
103 }
106
107 // Set the output workspace property
108 setProperty("OutputWorkspace", m_localWorkspace);
109}
110
118std::vector<std::string> LoadILLTOF3::getMonitorInfo(const Nexus::NXEntry &firstEntry) {
119 std::vector<std::string> monitorList;
120 if (m_isScan) {
121 // in case of a scan, there is only one monitor and its data are stored per scan step
122 // in "data_scan/scanned_variables/data", if that changes, a search for the "monitor" name
123 // may be required in the "data_scan/scanned_variables/variables_names"
124 monitorList.push_back("data_scan/scanned_variables/data");
125 } else {
126 for (std::vector<NXClassInfo>::const_iterator it = firstEntry.groups().begin(); it != firstEntry.groups().end();
127 ++it) {
128 if (it->nxclass == "NXmonitor" || it->nxname.starts_with("monitor")) {
129 monitorList.push_back(it->nxname + "/data");
130 }
131 }
132 }
133 m_numberOfMonitors = monitorList.size();
134 return monitorList;
135}
136
143
145
146 if (m_instrumentAddress.empty()) {
147 throw std::runtime_error("Cannot set the instrument name from the Nexus file!");
148 }
149
151
152 if (std::find(SUPPORTED_INSTRUMENTS.begin(), SUPPORTED_INSTRUMENTS.end(), m_instrumentName) ==
153 SUPPORTED_INSTRUMENTS.end()) {
154 std::string message = "The instrument " + m_instrumentName + " is not valid for this loader!";
155 throw std::runtime_error(message);
156 }
157
158 // Monitor can be monitor (IN5, PANTHER) or monitor1 (IN6)
159 if (firstEntry.containsGroup("monitor"))
160 m_monitorName = "monitor";
161 else if (firstEntry.containsGroup("monitor1"))
162 m_monitorName = "monitor1";
163 else {
164 std::string message("Cannot find monitor[1] in the Nexus file!");
165 throw std::runtime_error(message);
166 }
167
168 g_log.debug() << "Instrument name set to: " + m_instrumentName << '\n';
169}
170
178
179 // read in the data
180 const std::string dataName = m_isScan ? "data_scan/detector_data/data" : "data";
181 auto data = LoadHelper::getIntDataset(entry, dataName);
182
183 // default order is: tubes - pixels - channels, but for scans it is scans - tubes - pixels
184 m_numberOfTubes = static_cast<size_t>(m_isScan ? data.dim1() : data.dim0());
185 m_numberOfPixelsPerTube = static_cast<size_t>(m_isScan ? data.dim2() : data.dim1());
186 m_numberOfChannels = static_cast<size_t>(m_isScan ? data.dim0() : data.dim2());
187
191 size_t numberOfTubesInRosace = 0;
192 if (m_instrumentName == "IN4") {
193 auto dataRosace = LoadHelper::getIntDataset(entry, "instrument/Detector_Rosace/data");
194 numberOfTubesInRosace += static_cast<size_t>(dataRosace.dim0());
195 }
196
197 // dim0 * m_numberOfPixelsPerTube is the total number of detectors
199
200 g_log.debug() << "NumberOfTubes: " << m_numberOfTubes << '\n';
201 g_log.debug() << "NumberOfPixelsPerTube: " << m_numberOfPixelsPerTube << '\n';
202 g_log.debug() << "NumberOfChannels: " << m_numberOfChannels << '\n';
203
204 // Now create the output workspace
205 // total number of spectra + number of monitors,
206 // bin boundaries = m_numberOfChannels + 1 if diffraction or TOF mode, m_numberOfChannels for scans
207 // Z/time dimension
208 const auto numberOfChannels = m_isScan ? m_numberOfChannels : m_numberOfChannels + 1;
210 numberOfChannels, m_numberOfChannels);
211 if (m_isScan) {
212 m_localWorkspace->setYUnitLabel("Counts");
213 } else {
214 NXClass monitor = entry.openNXGroup(m_monitorName);
215 if (monitor.containsDataSet("time_of_flight")) {
216 m_localWorkspace->getAxis(0)->unit() = UnitFactory::Instance().create("TOF");
217 m_localWorkspace->setYUnitLabel("Counts");
218 } else {
219 g_log.debug("PANTHER diffraction mode");
220 m_localWorkspace->getAxis(0)->unit() = UnitFactory::Instance().create("Wavelength");
221 m_localWorkspace->setYUnitLabel("Counts");
222 }
223 }
224}
225
232
233 m_wavelength = entry.getFloat("wavelength");
234
235 Nexus::NXClass monitorEntry = entry.openNXGroup(m_monitorName);
236
237 if (monitorEntry.containsDataSet("time_of_flight")) {
238
239 NXFloat time_of_flight_data = entry.openNXFloat(m_monitorName + "/time_of_flight");
240 time_of_flight_data.load();
241
242 // The entry "monitor/time_of_flight", has 3 fields:
243 // channel width , number of channels, Time of flight delay
244 m_channelWidth = time_of_flight_data[0];
245 m_timeOfFlightDelay = time_of_flight_data[2];
246
247 g_log.debug("Nexus Data:");
248 g_log.debug() << " ChannelWidth: " << m_channelWidth << '\n';
249 g_log.debug() << " TimeOfFlightDelay: " << m_timeOfFlightDelay << '\n';
250 g_log.debug() << " Wavelength: " << m_wavelength << '\n';
251 } // the other case is the diffraction mode for PANTHER, where nothing is
252 // needed here
253}
254
261void LoadILLTOF3::addAllNexusFieldsAsProperties(const std::string &filename) {
262
263 API::Run &runDetails = m_localWorkspace->mutableRun();
264
265 // Open NeXus file
266 try {
267 Nexus::File nxfileID(filename, NXaccess::READ);
268 LoadHelper::addNexusFieldsToWsRun(nxfileID, runDetails);
269 } catch (Nexus::Exception const &) {
270 g_log.debug() << "convertNexusToProperties: Error loading " << filename;
271 throw Kernel::Exception::FileError("Unable to open File:", filename);
272 }
273
274 runDetails.addProperty("run_list", runDetails.getPropertyValueAsType<int>("run_number"));
275 g_log.debug() << "End parsing properties from : " << filename << '\n';
276}
277
283
284 API::Run &runDetails = m_localWorkspace->mutableRun();
285 const double ei = LoadHelper::calculateEnergy(m_wavelength);
286 runDetails.addProperty<double>("Ei", ei, true); // overwrite
287}
288
293 API::Run &runDetails = m_localWorkspace->mutableRun();
294 runDetails.addProperty("Facility", std::string("ILL"));
295}
296
301 API::Run &runDetails = m_localWorkspace->mutableRun();
302 double n_pulses = -1;
303 double fermiChopperSpeed = -1;
304
305 if (m_instrumentName == "IN4") {
306 fermiChopperSpeed = runDetails.getPropertyAsSingleValue("FC.rotation_speed");
307 const double bkgChopper1Speed = runDetails.getPropertyAsSingleValue("BC1.rotation_speed");
308 const double bkgChopper2Speed = runDetails.getPropertyAsSingleValue("BC2.rotation_speed");
309
310 if (std::abs(bkgChopper1Speed - bkgChopper2Speed) > 1) {
311 throw std::invalid_argument("Background choppers 1 and 2 have different speeds");
312 }
313
314 n_pulses = fermiChopperSpeed / bkgChopper1Speed / 4;
315 } else if (m_instrumentName == "IN6") {
316 fermiChopperSpeed = runDetails.getPropertyAsSingleValue("Fermi.rotation_speed");
317 const double suppressorSpeed = runDetails.getPropertyAsSingleValue("Suppressor.rotation_speed");
318
319 n_pulses = fermiChopperSpeed / suppressorSpeed;
320 } else {
321 return;
322 }
323
324 const double pulseInterval = 60.0 / (2 * fermiChopperSpeed) * n_pulses;
325 runDetails.addProperty<double>("pulse_interval", pulseInterval);
326}
327
334std::vector<double> LoadILLTOF3::prepareAxis(const Nexus::NXEntry &entry, bool convertToTOF) {
335
336 std::vector<double> xAxis(m_localWorkspace->readX(0).size());
337 if (m_isScan) {
338 // read which variable is going to be the axis
339 NXInt scannedAxis = entry.openNXInt("data_scan/scanned_variables/variables_names/axis");
340 scannedAxis.load();
341 std::size_t scannedVarId = 0;
342 for (std::size_t index = 0; index < scannedAxis.dim0(); index++) {
343 if (scannedAxis[index] == 1) {
344 scannedVarId = index;
345 break;
346 }
347 }
348 auto axis = LoadHelper::getDoubleDataset(entry, "data_scan/scanned_variables/data");
349 axis.load();
350 for (std::size_t index = 0; index < axis.dim1(); index++) {
351 xAxis[index] = axis(scannedVarId, index);
352 }
353 } else {
354 NXClass moni = entry.openNXGroup(m_monitorName);
355 if (moni.containsDataSet("time_of_flight")) {
356 if (convertToTOF) {
357 for (size_t i = 0; i < m_numberOfChannels + 1; ++i) {
358 xAxis[i] = m_timeOfFlightDelay + m_channelWidth * static_cast<double>(i) +
359 m_channelWidth / 2; // to make sure the bin centre is positive
360 }
361 } else {
362 for (size_t i = 0; i < m_numberOfChannels + 1; ++i) {
363 xAxis[i] = static_cast<double>(i); // just take the channel index
364 }
365 }
366 } else {
367 // Diffraction PANTHER
368 xAxis[0] = m_wavelength * 0.9;
369 xAxis[1] = m_wavelength * 1.1;
370 }
371 }
372 return xAxis;
373}
374
383void LoadILLTOF3::fillStaticWorkspace(const Nexus::NXEntry &entry, const std::vector<std::string> &monitorList,
384 bool convertToTOF) {
385
386 g_log.debug() << "Loading data into the workspace...\n";
387
388 // Prepare X-axis array
389 auto xAxis = prepareAxis(entry, convertToTOF);
390
391 // The binning for monitors is considered the same as for detectors
392 int64_t spec = 0;
393 std::vector<int> detectorIDs = m_localWorkspace->getInstrument()->getDetectorIDs(false);
394
395 auto data = LoadHelper::getIntDataset(entry, "data");
396 data.load();
397
398 LoadHelper::fillStaticWorkspace(m_localWorkspace, data, xAxis, spec, false, detectorIDs);
399 spec = static_cast<int>(m_numberOfTubes * m_numberOfPixelsPerTube);
400
401 // IN4 Rosace detectors are in a different NeXus entry
402 if (m_instrumentName == "IN4") {
403 g_log.debug() << "Loading data into the workspace: IN4 Rosace!\n";
404 // read in the data
405 // load the counts from the file into memory
406 auto dataRosace = LoadHelper::getIntDataset(entry, "instrument/Detector_Rosace/data");
407 dataRosace.load();
408 LoadHelper::fillStaticWorkspace(m_localWorkspace, dataRosace, xAxis, spec, false, detectorIDs);
409 spec += dataRosace.dim0();
410 }
411
412 for (const auto &monitorName : monitorList) {
413 detectorIDs[spec] = static_cast<int>(spec) + 1;
414 auto monitorData = LoadHelper::getIntDataset(entry, monitorName);
415 monitorData.load();
416 LoadHelper::fillStaticWorkspace(m_localWorkspace, monitorData, xAxis, spec, false, detectorIDs);
417 spec++;
418 }
419}
420
426void LoadILLTOF3::fillScanWorkspace(const Nexus::NXEntry &entry, const std::vector<std::string> &monitorList) {
427 // Prepare X-axis array
428 auto xAxis = prepareAxis(entry, false);
429 auto data = LoadHelper::getIntDataset(entry, "data_scan/detector_data/data");
430 data.load();
431
432 // Load scan data
433 const std::vector<int> detectorIDs = m_localWorkspace->getInstrument()->getDetectorIDs(false);
434 const std::tuple<int, int, int> dimOrder{1, 2, 0};
435 LoadHelper::fillStaticWorkspace(m_localWorkspace, data, xAxis, 0, true, detectorIDs, std::set<detid_t>(), dimOrder);
436
437 // Load monitor data, there is only one monitor
438 const std::vector<int> monitorIDs = m_localWorkspace->getInstrument()->getMonitors();
439 const auto spectrumNo = data.dim1() * data.dim2();
440 auto monitorData = LoadHelper::getDoubleDataset(entry, monitorList[0]);
441 monitorData.load();
442 for (std::size_t index = 0; index < monitorData.dim1(); index++) {
443 // monitor is always the 4th row, if that ever changes, a name search for 'monitor1' would be necessary among
444 // scanned_variables
445 const auto counts = monitorData(3, index);
446 m_localWorkspace->mutableY(spectrumNo)[index] = counts;
447 m_localWorkspace->mutableE(spectrumNo)[index] = sqrt(counts);
448 m_localWorkspace->mutableX(spectrumNo)[index] = xAxis[index];
449 }
450 // finally, we need to set the correct detector ID for the monitor
451 m_localWorkspace->getSpectrum(spectrumNo).setDetectorID(monitorIDs[0]);
452}
453
454} // namespace Mantid::DataHandling
std::map< DeltaEMode::Type, std::string > index
#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.
Kernel::Logger & g_log
Definition Algorithm.h:422
@ Load
allowed here which will be passed to the algorithm
Defines an interface to an algorithm that loads a file so that it can take part in the automatic sele...
Definition IFileLoader.h:19
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition LogManager.h:91
double getPropertyAsSingleValue(const std::string &name, Kernel::Math::StatisticType statistic=Kernel::Math::Mean) const
Returns a property as a single double value from its name.
HeldType getPropertyValueAsType(const std::string &name) const
Get the value of a property as the given TYPE.
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
A property class for workspaces.
Loads an ILL IN4/5/6/Panther NeXus file into a Mantid workspace.
Definition LoadILLTOF3.h:20
void exec() override
Executes the algorithm.
void fillStaticWorkspace(const Nexus::NXEntry &entry, const std::vector< std::string > &monitorList, bool convertToTOF)
Fills the non-scan measurement data into the workspace, including that from the monitor.
void initWorkspace(const Nexus::NXEntry &entry)
Creates the workspace and initialises member variables with the corresponding values.
std::vector< std::string > getMonitorInfo(const Nexus::NXEntry &firstEntry)
Finds monitor data names and stores them in a vector.
void addFacility()
Adds facility info to the sample logs.
std::string m_instrumentName
Name of the instrument.
Definition LoadILLTOF3.h:62
void fillScanWorkspace(const Nexus::NXEntry &entry, const std::vector< std::string > &monitorList)
Fills scan workspace with data and monitor data counts.
void loadTimeDetails(const Nexus::NXEntry &entry)
Load the time details from the nexus file.
void addAllNexusFieldsAsProperties(const std::string &filename)
Goes through all the fields of the NeXus file and adds them as parameters in the workspace.
std::string m_instrumentAddress
Name of the instrument address.
Definition LoadILLTOF3.h:63
void addEnergyToRun()
Calculates the incident energy from the wavelength and adds it as sample log 'Ei'.
void loadInstrumentDetails(const Nexus::NXEntry &)
Sets the instrument name along with its address in the nexus file.
void init() override
Initialises the algorithm.
void addPulseInterval()
Calculates and adds the pulse intervals for the run.
std::vector< double > prepareAxis(const Nexus::NXEntry &entry, bool convertToTOF)
Prepares X axis for the workspace being loaded.
API::MatrixWorkspace_sptr m_localWorkspace
Definition LoadILLTOF3.h:60
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
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Class that provides for a standard Nexus exception.
The base class for a Nexus class (group).
float getFloat(const std::string &name) const
Returns a float.
bool containsGroup(const std::string &query) const
Returns whether an individual group (or group) is present.
NXFloat openNXFloat(const std::string &name) const
Creates and opens a float dataset.
std::vector< NXClassInfo > & groups() const
Returns a list of all classes (or groups) in this NXClass.
NXClass openNXGroup(const std::string &name) const
Creates and opens an arbitrary (non-standard) class (group).
NXInt openNXInt(const std::string &name) const
Creates and opens an integer dataset.
bool containsDataSet(const std::string &query) const
Returns whether an individual dataset is present.
Templated class implementation of NXDataSet.
void load()
Read all of the datablock in.
dimsize_t dim0() const
Returns the number of elements along the first dimension.
Implements NXentry Nexus class.
Implements NXroot Nexus class.
NXEntry openFirstEntry()
Open the first NXentry in the file.
std::string getStringFromNexusAddress(const Mantid::Nexus::NXEntry &, const std::string &)
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.
double calculateEnergy(double)
Calculate Neutron Energy from wavelength: .
Nexus::NXInt getIntDataset(const Nexus::NXEntry &, const std::string &)
Fetches NXInt data from the requested group name in the entry provided.
Nexus::NXDouble getDoubleDataset(const Nexus::NXEntry &, const std::string &)
Fetches NXDouble data from the requested group name in the entry provided.
void fillStaticWorkspace(const API::MatrixWorkspace_sptr &, const Mantid::Nexus::NXInt &, const std::vector< double > &xAxis, int64_t initialSpectrum=0, bool pointData=false, const std::vector< detid_t > &detectorIDs=std::vector< int >(), const std::set< detid_t > &acceptedID=std::set< int >(), const std::tuple< short, short, short > &axisOrder=std::tuple< short, short, short >(0, 1, 2))
Fills workspace with histogram data from provided data structure.
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...
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54