Mantid
Loading...
Searching...
No Matches
LoadInstrument.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 +
12#include "MantidAPI/Progress.h"
21#include "MantidNexusGeometry/NexusGeometryParser.h"
22
23#include <boost/algorithm/string.hpp>
24
25namespace Mantid::DataHandling {
26
27DECLARE_ALGORITHM(LoadInstrument)
28
29using namespace Kernel;
30using namespace API;
31using namespace Geometry;
32
33std::recursive_mutex LoadInstrument::m_mutex;
34
35// This enum class is used to remember easily which instrument loader should be
36// used. There are 3 different loaders: from XML string, from an IDF file and
37// from a Nexus file. Some parts of the exec() function are common to Xml and
38// Idf, some parts are common to Idf and Nxs, and some parts are common to all
39// three. Assigning numbers to the 3 types allows us to write statements like
40// if (loader_type < LoaderType::Nxs) then do all things common to Xml and Idf.
41enum class LoaderType { Xml = 1, Idf = 2, Nxs = 3 };
42
45 // When used as a Child Algorithm the workspace name is not used - hence the
46 // "Anonymous" to satisfy the validator
47 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("Workspace", "Anonymous", Direction::InOut),
48 "The name of the workspace to load the instrument definition "
49 "into. Any existing instrument will be replaced.");
51 std::make_unique<FileProperty>("Filename", "", FileProperty::OptionalLoad, LoadGeometry::validExtensions()),
52 "The filename (including its full or relative path) of an instrument "
53 "definition file. The file extension must either be .xml or .XML when "
54 "specifying an instrument definition file. Files can also be .hdf5 or "
55 ".nxs for usage with NeXus Geometry files. Note Filename or "
56 "InstrumentName must be specified but not both.");
57 declareProperty(std::make_unique<ArrayProperty<detid_t>>("MonitorList", Direction::Output),
58 "Will be filled with a list of the detector ids of any "
59 "monitors loaded in to the workspace.");
60 declareProperty("InstrumentName", "",
61 "Name of instrument. Can be used instead of Filename to specify an"
62 "instrument definition.");
63 declareProperty("InstrumentXML", "", "The full XML instrument definition as a string.");
65 "RewriteSpectraMap", OptionalBool::Unset, std::make_shared<MandatoryValidator<OptionalBool>>()),
66 "If set to True then a 1:1 map between the spectrum numbers and "
67 "detector/monitor IDs is set up such that the detector/monitor IDs in "
68 "the IDF are ordered from smallest to largest number and then assigned "
69 "in that order to the spectra in the workspace. For example if the IDF "
70 "has defined detectors/monitors with IDs 1, 5, 10 and the workspace "
71 "contains 3 spectra with numbers 1, 2, 3 (and workspace indices 0, 1, 2) "
72 "then spectrum number 1 is associated with detector ID 1, spectrum "
73 "number 2 with detector ID 5 and spectrum number 3 with detector ID 10."
74 "If the number of spectra and detectors do not match then the operation "
75 "is performed until the maximum number of either is reached. For example "
76 "if there are 12 spectra and 50 detectors then the first 12 detectors "
77 "are assigned to the 12 spectra in the workspace."
78 "If set to False then the spectrum numbers and detector IDs of the "
79 "workspace are not modified."
80 "This property must be set to either True or False.");
81}
82
83//------------------------------------------------------------------------------------------------------------------------------
92 // Get the input workspace
93 std::shared_ptr<API::MatrixWorkspace> ws = getProperty("Workspace");
94 std::string filename = getPropertyValue("Filename");
95 std::string instname = getPropertyValue("InstrumentName");
96 // std::pair<std::string, std::string> loader_type;
97 LoaderType loader_type;
98
99 // If instrumentXML is not default (i.e. it has been defined), then use that
100 // Note: this is part of the IDF loader
101 const Property *const InstrumentXML = getProperty("InstrumentXML");
102 if (!InstrumentXML->isDefault()) {
103 // We need the instrument name to be set as well because, for whatever
104 // reason, this isn't pulled out of the XML.
105 if (instname.empty())
106 throw std::runtime_error("The InstrumentName property must be set when "
107 "using the InstrumentXML property.");
108 // If the Filename property is not set, set it to the same as the instrument
109 // name
110 if (filename.empty())
111 filename = instname;
112
113 // Assign the loader type to Xml
114 loader_type = LoaderType::Xml;
115
116 } else {
117 // This part of the loader searches through the instrument directories for
118 // a valid IDF or Nexus geometry file
119
120 // The first step is to define a valid filename
121
122 // If the filename is empty, try to find a file from the instrument name
123 if (filename.empty()) {
124 // look to see if an Instrument name provided in which case create
125 // filename on the fly
126 if (instname.empty()) {
127 g_log.error("Either the InstrumentName or Filename property of "
128 "LoadInstrument most be specified");
129 throw Kernel::Exception::FileError("Either the InstrumentName or Filename property of LoadInstrument "
130 "must be specified to load an instrument",
131 filename);
132 } else {
133 filename = InstrumentFileFinder::getInstrumentFilename(instname, ws->getWorkspaceStartDate());
134 setPropertyValue("Filename", filename);
135 }
136 }
137 if (filename.empty()) {
138 throw Exception::NotFoundError("Unable to find an Instrument File for instrument: ", instname);
139 }
140
141 // Remove the path from the filename for use with the InstrumentDataService
142 const std::string::size_type stripPath = filename.find_last_of("\\/");
143 std::string instrumentFile = filename.substr(stripPath + 1, filename.size());
144
145 // Strip off "_Definition.xml"
146 auto definitionRange = boost::ifind_first(instrumentFile, "_Def");
147 if (definitionRange) {
148 instname = instrumentFile.substr(0, std::distance(instrumentFile.begin(), definitionRange.begin()));
149 } else {
150 g_log.warning("The instrument definition filename does not contain "
151 "_Definition. Your instrument name will be set to: " +
152 instrumentFile);
153 instname = instrumentFile;
154 }
155
156 // Now that we have a file name, decide whether to use Nexus or IDF loading
157 if (LoadGeometry::isIDF(filename)) {
158 // Assign the loader type to Idf
159 loader_type = LoaderType::Idf;
160 } else if (LoadGeometry::isNexus(filename)) {
161 // Assign the loader type to Nxs
162 loader_type = LoaderType::Nxs;
163 } else {
164 throw Kernel::Exception::FileError("No valid loader found for instrument file ", filename);
165 }
166 }
167
169 std::string instrumentNameMangled;
170 Instrument_sptr instrument;
171
172 // Define a parser if using IDFs
173 if (loader_type == LoaderType::Xml)
174 parser = InstrumentDefinitionParser(filename, instname, InstrumentXML->value());
175 else if (loader_type == LoaderType::Idf)
176 parser = InstrumentDefinitionParser(filename, instname, Strings::loadFile(filename));
177
178 // Find the mangled instrument name that includes the modified date
179 if (loader_type < LoaderType::Nxs)
180 instrumentNameMangled = parser.getMangledName();
181 else if (loader_type == LoaderType::Nxs)
182 instrumentNameMangled = NexusGeometry::NexusGeometryParser::getMangledName(filename, instname);
183 else
184 throw std::runtime_error("Unknown instrument LoaderType");
185
186 {
187 // Make InstrumentService access thread-safe
188 std::lock_guard<std::recursive_mutex> lock(m_mutex);
189
190 // Check whether the instrument is already in the InstrumentDataService
191 if (InstrumentDataService::Instance().doesExist(instrumentNameMangled)) {
192 // If it does, just use the one from the one stored there
193 instrument = InstrumentDataService::Instance().retrieve(instrumentNameMangled);
194 } else {
195
196 if (loader_type < LoaderType::Nxs) {
197 // Really create the instrument
198 Progress prog(this, 0.0, 1.0, 100);
199 instrument = parser.parseXML(&prog);
200 // Parse the instrument tree (internally create ComponentInfo and
201 // DetectorInfo). This is an optimization that avoids duplicate parsing
202 // of the instrument tree when loading multiple workspaces with the same
203 // instrument. As a consequence less time is spent and less memory is
204 // used. Note that this is only possible since the tree in `instrument`
205 // will not be modified once we add it to the IDS.
206 instrument->parseTreeAndCacheBeamline();
207 } else {
209 NexusGeometry::NexusGeometryParser::createInstrument(filename, NexusGeometry::makeLogger(&m_log));
210 instrument = std::const_pointer_cast<Instrument>(ins);
211 }
212 // Add to data service for later retrieval
213 InstrumentDataService::Instance().add(instrumentNameMangled, instrument);
214 }
215 ws->setInstrument(instrument);
216
217 // populate parameter map of workspace
218 ws->populateInstrumentParameters();
219
220 // LoadParameterFile modifies the base instrument stored in the IDS so this
221 // must also be protected by the lock until LoadParameterFile is fixed.
222 // check if default parameter file is also present, unless loading from
223 if (!filename.empty() && (loader_type < LoaderType::Nxs))
224 runLoadParameterFile(ws, filename);
225 } // end of mutex scope
226
227 // Set the monitors output property
228 setProperty("MonitorList", (ws->getInstrument())->getMonitors());
229
230 // Rebuild the spectra map for this workspace so that it matches the
231 // instrument, if required
232 const OptionalBool RewriteSpectraMap = getProperty("RewriteSpectraMap");
233 if (RewriteSpectraMap == OptionalBool::True)
234 ws->rebuildSpectraMapping();
235}
236
237//-----------------------------------------------------------------------------------------------------------------------
239void LoadInstrument::runLoadParameterFile(const std::shared_ptr<API::MatrixWorkspace> &ws,
240 const std::string &filename) {
241 g_log.debug("Loading the parameter definition...");
242
243 // First search for XML parameter file in same folder as IDF file
244 const std::string::size_type dir_end = filename.find_last_of("\\/");
245 std::string directoryName = filename.substr(0, dir_end + 1); // include final '/'.
246 std::string fullPathParamIDF = InstrumentFileFinder::getParameterPath(filename, directoryName);
247
248 if (!fullPathParamIDF.empty()) {
249
250 g_log.debug() << "Parameter file: " << fullPathParamIDF << '\n';
251 // Now execute the Child Algorithm. Catch and log any error, but don't stop.
252 try {
253 // To allow the use of ExperimentInfo instead of workspace, we call it
254 // manually
255 Algorithm_sptr loadParamAlg = createChildAlgorithm("LoadParameterFile");
256 loadParamAlg->setProperty("Filename", fullPathParamIDF);
257 loadParamAlg->setProperty("Workspace", ws);
258 loadParamAlg->execute();
259 g_log.debug("Parameters loaded successfully.");
260 } catch (std::invalid_argument &e) {
261 g_log.information("LoadParameterFile: No parameter file found for this instrument");
262 g_log.information(e.what());
263 } catch (std::runtime_error &e) {
264 g_log.information("Unable to successfully run LoadParameterFile Child Algorithm");
265 g_log.information(e.what());
266 }
267 } else {
268 g_log.information("No parameter file found for this instrument");
269 }
270}
271
272} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Definition: Algorithm.cpp:842
Kernel::Logger & g_log
Definition: Algorithm.h:451
void setPropertyValue(const std::string &name, const std::string &value) override
Set the value of a property by string N.B.
Definition: Algorithm.cpp:1975
Kernel::Logger m_log
Logger for this algorithm.
Definition: Algorithm.h:450
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
Definition: FileProperty.h:53
static std::string getParameterPath(const std::string &instName, const std::string &dirHint="")
Search instrument directories for Parameter file, return full path name if found, else "".
static std::string getInstrumentFilename(const std::string &instrumentName, const std::string &date="")
Get the IDF using the instrument name and date.
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
A property class for workspaces.
static std::recursive_mutex m_mutex
Mutex to avoid simultaneous access.
void init() override
Initialisation method.
void runLoadParameterFile(const std::shared_ptr< API::MatrixWorkspace > &ws, const std::string &filename)
Run the Child Algorithm LoadParameters.
void exec() override
Executes the algorithm.
Creates an instrument data from a XML instrument description file.
std::shared_ptr< Instrument > parseXML(Kernel::ProgressBase *progressReporter)
Parse XML contents.
std::string getMangledName()
Handle used in the singleton constructor for instrument file should append the value file sha-1 check...
Support for a property that holds an array of values.
Definition: ArrayProperty.h:28
Records the filename and the description of failure.
Definition: Exception.h:98
Exception for when an item is not found in a collection.
Definition: Exception.h:145
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
Validator to check that a property is not left empty.
OptionalBool : Tri-state bool.
Definition: OptionalBool.h:25
The concrete, templated class for properties.
Base class for properties.
Definition: Property.h:94
virtual bool isDefault() const =0
Overriden function that returns if property has the same value that it was initialised with,...
virtual std::string value() const =0
Returns the value of the property as a string.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition: Algorithm.h:61
bool isIDF(const std::string &filename)
Determine if the Geometry file type is IDF.
const std::vector< std::string > validExtensions()
List allowed file extensions for geometry.
bool isNexus(const std::string &filename)
Determine if the Geometry file type is Nexus.
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
std::shared_ptr< Instrument > Instrument_sptr
Shared pointer to an instrument object.
MANTID_KERNEL_DLL std::string loadFile(const std::string &filename)
Loads the entire contents of a text file into a string.
Definition: Strings.cpp:28
@ InOut
Both an input & output workspace.
Definition: Property.h:55
@ Output
An output workspace.
Definition: Property.h:54