Mantid
Loading...
Searching...
No Matches
LoadEmptyInstrument.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 +
16#include "MantidIndexing/IndexInfo.h"
21
22#include <filesystem>
23
24namespace Mantid::DataHandling {
25// Register the algorithm into the algorithm factory as a file loading algorithm
26DECLARE_FILELOADER_ALGORITHM(LoadEmptyInstrument)
27
28using namespace Kernel;
29using namespace API;
30using namespace Geometry;
31using namespace DataObjects;
32using namespace HistogramData;
33
34namespace {
35const std::vector<std::string> validFilenameExtensions{".xml", ".hdf5", ".nxs", ".nxs.h5"};
36enum class FilenameExtensionEnum { XML, HDF5, NXS, NXS_H5, enum_count };
37typedef EnumeratedString<FilenameExtensionEnum, &validFilenameExtensions, &compareStringsCaseInsensitive>
38 FilenameExtension;
39} // namespace
40
41// This method attempts to retrieve a valid, i.e. enumerated, instrument file name extension.
42// Possible double and single extensions are considered. If no valid file name extension could
43// be retrieved, an std::runtime_error will be thrown. For example, a file name might have a double
44// extension such as ".nxs.h5", which is valid. It may also have a double extension such as .lite.nxs,
45// which is invalid. In the latter case a single extension, .nxs, which is valid, should be retrieved.
46std::string LoadEmptyInstrument::retrieveValidInstrumentFilenameExtension(const std::string &filename) {
47 std::string ext{std::filesystem::path(filename).extension().string()};
48 std::string stem{std::filesystem::path(filename).stem().string()};
49 std::string pre_ext{std::filesystem::path(stem).extension().string()};
50
51 // Test a possible double extension
52 if (!pre_ext.empty()) {
53 try {
54 const std::string double_ext{pre_ext + ext};
55 FilenameExtension fne(double_ext);
56 return fne.c_str();
57 } catch (const std::runtime_error &) {
58 }
59 }
60
61 // Test a possible single extension
62 try {
63 FilenameExtension fne(ext);
64 return fne.c_str();
65 } catch (std::runtime_error &) {
66 }
67
68 std::ostringstream os;
69 os << "Instrument file name \"" << filename << "\" has an invalid extension.";
70 throw std::runtime_error(os.str());
71}
72
73// Return all valid instrument file name extensions
75 return validFilenameExtensions;
76}
77
85 const std::string &filePath = descriptor.filename();
86
87 int confidence(0);
88 if (descriptor.isAscii()) // Only consider an Ascii file
89 {
90 // Filename must contain "Definition"
91 std::string::size_type stripPath = filePath.find_last_of("\\/");
92 if (stripPath == std::string::npos)
93 stripPath = 0;
94 if (filePath.find("Definition", stripPath) != std::string::npos) {
95 // We have some confidence and it depends on the filetype.
96 if (descriptor.extension() == "xml") {
97 confidence = 80;
98 } else {
99 confidence = 20;
100 }
101 } // Has "Definition"
102 } // Ascii file
103 return confidence;
104}
105
108
109 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::OptionalLoad, validFilenameExtensions),
110 "Path to a file (full or relative) defining the instrument. The file could be an"
111 " IDF or a NeXus Geometry file. Note, Filename or InstrumentName must be specified, but not both.");
112 declareProperty("InstrumentName", "",
113 "Name of instrument. Can be used instead of Filename to "
114 "specify an IDF");
115 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("OutputWorkspace", "", Direction::Output),
116 "The name of the workspace in which to store the imported instrument");
117
118 auto mustBePositive = std::make_shared<BoundedValidator<double>>();
119 mustBePositive->setLower(0.0);
120 declareProperty("DetectorValue", 1.0, mustBePositive,
121 "This value affects the colour of the detectors in the instrument\n"
122 "display window (default 1)");
123 declareProperty("MonitorValue", 2.0, mustBePositive,
124 "This value affects the colour of the monitors in the instrument\n"
125 "display window (default 2)");
126
127 declareProperty(std::make_unique<PropertyWithValue<bool>>("MakeEventWorkspace", false),
128 "Set to True to create an EventWorkspace (with no events) "
129 "instead of a Workspace2D.");
130}
131
143 Progress prog(this, 0.0, 1.0, 10);
144
145 // load the instrument into this workspace
147 const std::string instrumentName = getPropertyValue("InstrumentName");
148 const std::string filename = getPropertyValue("Filename");
149 if (!instrumentName.empty())
150 ws = this->runLoadInstrument(filename, instrumentName);
151 else {
152 FilenameExtension enFilenameExtension{retrieveValidInstrumentFilenameExtension(filename)};
153 switch (enFilenameExtension) {
154 case FilenameExtensionEnum::XML:
155 case FilenameExtensionEnum::HDF5:
156 ws = this->runLoadInstrument(filename, instrumentName);
157 break;
158 case FilenameExtensionEnum::NXS:
159 case FilenameExtensionEnum::NXS_H5:
160 ws = this->runLoadIDFFromNexus(filename);
161 break;
162 default:
163 std::ostringstream os;
164 os << "Instrument file name has an invalid extension: "
165 << "\"" << enFilenameExtension.c_str() << "\"";
166 throw std::runtime_error(os.str());
167 }
168 }
169
170 auto timerStart = std::chrono::high_resolution_clock::now();
171 Instrument_const_sptr instrument = ws->getInstrument();
172 addTimer("getInstrument", timerStart, std::chrono::high_resolution_clock::now());
173
174 // Get number of detectors stored in instrument
175 timerStart = std::chrono::high_resolution_clock::now();
176 const size_t number_spectra = instrument->getNumberDetectors();
177 addTimer("getNumberDetectors", timerStart, std::chrono::high_resolution_clock::now());
178
179 // Check that we have some spectra for the workspace
180 if (number_spectra == 0) {
181 g_log.error("Instrument has no detectors, unable to create workspace for it");
182 throw Kernel::Exception::InstrumentDefinitionError("No detectors found in instrument");
183 }
184
185 timerStart = std::chrono::high_resolution_clock::now();
186 Indexing::IndexInfo indexInfo(number_spectra);
187 addTimer("createIndexInfo", timerStart, std::chrono::high_resolution_clock::now());
188 bool MakeEventWorkspace = getProperty("MakeEventWorkspace");
189 prog.reportIncrement(5, "Creating Data");
190 if (MakeEventWorkspace) {
191 setProperty("OutputWorkspace",
192 create<EventWorkspace>(instrument, indexInfo, BinEdges{0.0, std::numeric_limits<double>::min()}));
193 } else {
194 timerStart = std::chrono::high_resolution_clock::now();
195 const double detector_value = getProperty("DetectorValue");
196 const double monitor_value = getProperty("MonitorValue");
197 auto ws2D = create<Workspace2D>(
198 instrument, indexInfo,
199 Histogram(BinEdges{0.0, 1.0}, Counts(1, detector_value), CountStandardDeviations(1, detector_value)));
200 addTimer("makeWorkspace2D", timerStart, std::chrono::high_resolution_clock::now());
201
202 if (monitor_value != detector_value) {
203 timerStart = std::chrono::high_resolution_clock::now();
204 Counts v_monitor_y(1, monitor_value);
205 CountStandardDeviations v_monitor_e(1, monitor_value);
206
207 const auto &spectrumInfo = ws2D->spectrumInfo();
208 const auto size = static_cast<int64_t>(spectrumInfo.size());
209#pragma omp parallel for
210 for (int64_t i = 0; i < size; i++) {
211 if (spectrumInfo.isMonitor(i)) {
212 ws2D->setCounts(i, v_monitor_y);
213 ws2D->setCountStandardDeviations(i, v_monitor_e);
214 }
215 }
216 addTimer("setMonitors", timerStart, std::chrono::high_resolution_clock::now());
217 }
218 setProperty("OutputWorkspace", std::move(ws2D));
219 }
220}
221
222// Call LoadInstrument as a child algorithm
224 const std::string &instrumentname) {
225 auto ws = WorkspaceFactory::Instance().create("Workspace2D", 1, 2, 1);
226
227 auto loadInst = createChildAlgorithm("LoadInstrument", 0, 0.5);
228 loadInst->setPropertyValue("Filename", filename);
229 loadInst->setPropertyValue("InstrumentName", instrumentname);
230 loadInst->setProperty("RewriteSpectraMap", OptionalBool(true));
231 loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", ws);
232
233 loadInst->execute();
234
235 return ws;
236}
237
238// Call LoadIDFFromNexus as a child algorithm
240 const std::string instrumentEntryName{"/instrument/instrument_xml"};
241 // get the top-level entry name
242 bool foundIDF{false};
243 std::string instrumentParentEntryName;
244 {
245 // since we only need to evaluate if a single entry is present, use a lazy descriptor
246 Nexus::NexusDescriptorLazy nxsfile(filename);
247 instrumentParentEntryName = "/" + nxsfile.firstEntryNameType().first;
248 foundIDF = nxsfile.isEntry(instrumentParentEntryName + instrumentEntryName);
249 }
250
251 if (!foundIDF) {
252 throw std::runtime_error("No instrument XML definition found in " + filename + " at " + instrumentParentEntryName +
253 instrumentEntryName);
254 }
255
256 auto loadInst = createChildAlgorithm("LoadIDFFromNexus");
257
258 // Execute the child algorithm. Catch and log any error.
259 auto ws = WorkspaceFactory::Instance().create("Workspace2D", 1, 2, 1);
260 try {
261 loadInst->setPropertyValue("Filename", filename);
262 loadInst->setProperty<Mantid::API::MatrixWorkspace_sptr>("Workspace", ws);
263 loadInst->setPropertyValue("InstrumentParentPath", instrumentParentEntryName);
264 loadInst->execute();
265 } catch (std::invalid_argument &) {
266 getLogger().error("Invalid argument to LoadIDFFromNexus Child Algorithm ");
267 } catch (std::runtime_error &) {
268 getLogger().debug("No instrument definition found by LoadIDFFromNexus in " + filename + " at " +
269 instrumentParentEntryName + instrumentEntryName);
270 }
271
272 if (!loadInst->isExecuted())
273 getLogger().information("No IDF loaded from the Nexus file " + filename);
274
275 return ws;
276}
277} // namespace Mantid::DataHandling
#define DECLARE_FILELOADER_ALGORITHM(classname)
DECLARE_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro when wri...
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
Kernel::Logger & getLogger() const
Returns a reference to the logger.
void addTimer(const std::string &name, const Kernel::time_point_ns &begin, const Kernel::time_point_ns &end)
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
Helper class for reporting progress from algorithms.
Definition Progress.h:25
A property class for workspaces.
void init() override
Overwrites Algorithm method.
int confidence(Kernel::FileDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
static const std::vector< std::string > & getValidInstrumentFilenameExtensions()
API::MatrixWorkspace_sptr runLoadInstrument(const std::string &filename, const std::string &instrumentname)
void exec() override
Overwrites Algorithm method.
API::MatrixWorkspace_sptr runLoadIDFFromNexus(const std::string &filename)
static std::string retrieveValidInstrumentFilenameExtension(const std::string &filename)
Exception for errors associated with the instrument definition.
Definition Exception.h:220
Defines a wrapper around an open file.
const std::string & filename() const
Access the filename.
static bool isAscii(const std::string &filename, const size_t nbytes=256)
Returns true if the file is considered ascii.
const std::string & extension() const
Access the file extension.
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 information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
OptionalBool : Tri-state bool.
void reportIncrement(int inc, const std::string &msg="")
Sends the progress notification and increment the loop counter by more than one.
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
bool isEntry(std::string const &entryName, std::string const &groupClass) const
Checks if a full-address entry exists for a particular groupClass in a Nexus dataset.
std::pair< std::string, std::string > const & firstEntryNameType() const noexcept
Returns the name & type of the first entry in the file.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
@ Output
An output workspace.
Definition Property.h:54