Mantid
Loading...
Searching...
No Matches
LoadIDFFromNexus.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 +
14#include "MantidNexus/NexusFile.h"
15
16#include <Poco/DOM/DOMParser.h>
17#include <Poco/DOM/Document.h>
18#include <Poco/DOM/Element.h>
19#include <Poco/DOM/NodeIterator.h>
20#include <Poco/DOM/NodeList.h>
21#include <filesystem>
22using Poco::XML::Document;
23using Poco::XML::DOMParser;
24using Poco::XML::Element;
25using Poco::XML::NodeList;
26
27namespace Mantid::DataHandling {
28
29DECLARE_ALGORITHM(LoadIDFFromNexus)
30
31using namespace Kernel;
32using namespace API;
33using Types::Core::DateAndTime;
34
37
40 // When used as a Child Algorithm the workspace name is not used - hence the
41 // "Anonymous" to satisfy the validator
42 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("Workspace", "Anonymous", Direction::InOut),
43 "The name of the workspace in which to attach the imported instrument");
44
45 const std::vector<std::string> exts{".nxs", ".nxs.h5"};
47 std::make_unique<FileProperty>("Filename", "", FileProperty::Load, exts),
48 "The name (including its full or relative path) of the Nexus file to attempt to load the instrument from.");
49
51 "InstrumentParentPath", std::string(""),
52 "Path name within the Nexus tree of the folder containing the instrument folder. "
53 "For example it is 'raw_data_1' for an ISIS raw Nexus file and 'mantid_workspace_1' for a processed nexus file. "
54 "Only a one level path is curently supported",
56 declareProperty("ParameterCorrectionFilePath", std::string(""),
57 "Full path name of Parameter Correction file. "
58 "This should only be used in a situation,where the default full file path is inconvenient.",
60}
61
68 // Retrieve the filename from the properties
69 const std::string filename = getPropertyValue("Filename");
70
71 // Get the input workspace
72 const MatrixWorkspace_sptr localWorkspace = getProperty("Workspace");
73
74 // Get the instrument address
75 std::string instrumentParentAddress = getPropertyValue("InstrumentParentPath");
76
77 // Get the instrument group in the Nexus file
78 Nexus::File nxfile(filename);
79 // Assume one level in instrument path
80 nxfile.openAddress(instrumentParentAddress);
81
82 // Take instrument info from nexus file.
83 localWorkspace->loadInstrumentInfoNexus(filename, &nxfile);
84
85 // Look for parameter correction file
86 std::string parameterCorrectionFile = getPropertyValue("ParameterCorrectionFilePath");
87 if (parameterCorrectionFile.empty()) {
88 parameterCorrectionFile = getParameterCorrectionFile(localWorkspace->getInstrument()->getName());
89 }
90 g_log.debug() << "Parameter correction file: " << parameterCorrectionFile << "\n";
91
92 // Read parameter correction file, if found
93 std::string correctionParameterFile;
94 bool append = false;
95 if (!parameterCorrectionFile.empty()) {
96 // Read parameter correction file
97 // to find out which parameter file to use
98 // and whether it is appended to default parameters.
99 g_log.notice() << "Using parameter correction file: " << parameterCorrectionFile << ".\n";
100 readParameterCorrectionFile(parameterCorrectionFile, localWorkspace->getAvailableWorkspaceStartDate(),
101 correctionParameterFile, append);
102 }
103
104 // Load default parameters if either there is no correction parameter file or
105 // it is to be appended.
106 if (correctionParameterFile.empty() || append) {
107 LoadParameters(&nxfile, localWorkspace);
108 } else { // Else clear the parameters
109 g_log.notice() << "Parameters to be replaced are cleared.\n";
110 localWorkspace->getInstrument()->getParameterMap()->clear();
111 }
112
113 // Load parameters from correction parameter file, if it exists
114 if (!correctionParameterFile.empty()) {
115 std::filesystem::path corrFilePath(parameterCorrectionFile);
116 g_log.debug() << "Correction file path: " << corrFilePath.string() << "\n";
117 std::filesystem::path corrDirPath = corrFilePath.parent_path();
118 g_log.debug() << "Correction directory path: " << corrDirPath.string() << "\n";
119 std::filesystem::path corrParamFile(corrDirPath / correctionParameterFile);
120 if (append) {
121 g_log.notice() << "Using correction parameter file: " << corrParamFile.string() << " to append parameters.\n";
122 } else {
123 g_log.notice() << "Using correction parameter file: " << corrParamFile.string() << " to replace parameters.\n";
124 }
125 runLoadParameterFile(corrParamFile.string(), localWorkspace);
126 } else {
127 g_log.notice() << "No correction parameter file applies to the date for "
128 "correction file.\n";
129 }
130}
131
132/* Gets the full pathname of the parameter correction file, if it exists
133 * @param instName :: short name of instrument as it appears in IDF filename
134 * etc.
135 * @returns full path name of correction file if found else ""
136 */
137std::string LoadIDFFromNexus::getParameterCorrectionFile(const std::string &instName) {
138
139 std::vector<std::string> directoryNames = ConfigService::Instance().getInstrumentDirectories();
140 for (auto &directoryName : directoryNames) {
141 // This will iterate around the directories from user ->etc ->install, and
142 // find the first appropriate file
143 std::filesystem::path iPath =
144 std::filesystem::path(directoryName) / "embedded_instrument_corrections"; // Go to correction file subfolder
145 // First see if the directory exists
146 if (std::filesystem::exists(iPath) && std::filesystem::is_directory(iPath)) {
147 std::filesystem::path ipFile = iPath / (instName + "_Parameter_Corrections.xml"); // Append file name to pathname
148 if (std::filesystem::exists(ipFile) && std::filesystem::is_regular_file(ipFile)) {
149 return ipFile.string(); // Return first found
150 }
151 } // Directory
152 } // Loop
153 return ""; // No file found
154}
155
156/* Reads the parameter correction file and if a correction is needed output the
157 *parameterfile needed
158 * and whether it is to be appended.
159 * @param correction_file :: path nsame of correction file as returned by
160 *getParameterCorrectionFile()
161 * @param date :: IS8601 date string applicable: Must be full timestamp
162 *(timezone optional)
163 * @param parameter_file :: output parameter file to use or "" if none
164 * @param append :: output whether the parameters from parameter_file should be
165 *appended.
166 *
167 * @throw FileError Thrown if unable to parse XML file
168 */
169void LoadIDFFromNexus::readParameterCorrectionFile(const std::string &correction_file, const std::string &date,
170 std::string &parameter_file, bool &append) {
171 using namespace Poco::XML;
172 // Set output arguments to default
173 parameter_file = "";
174 append = false;
175
176 // Check the date.
177 if (date.empty()) {
178 g_log.notice() << "No date is supplied for parameter correction file " << correction_file
179 << ". Correction file is ignored.\n";
180 return;
181 }
182
183 // Get contents of correction file
184 const std::string xmlText = Kernel::Strings::loadFile(correction_file);
185
186 // Set up the DOM parser and parse xml file
187 DOMParser pParser;
188 Poco::AutoPtr<Document> pDoc;
189 try {
190 pDoc = pParser.parseString(xmlText);
191 } catch (Poco::Exception &exc) {
192 throw Kernel::Exception::FileError(exc.displayText() + ". Unable to parse parameter correction file:",
193 correction_file);
194 } catch (...) {
195 throw Kernel::Exception::FileError("Unable to parse parameter correction file:", correction_file);
196 }
197 // Get pointer to root element
198 Element *pRootElem = pDoc->documentElement();
199 if (!pRootElem->hasChildNodes()) {
200 g_log.error("Parameter correction file: " + correction_file + "contains no XML root element.");
201 throw Kernel::Exception::InstrumentDefinitionError("No root element in XML parameter correction file",
202 correction_file);
203 }
204
205 // Convert date to Mantid object
206 g_log.notice() << "Date for correction file " << date << "\n";
207 DateAndTime externalDate(date);
208
209 // Examine the XML structure obtained by parsing
210 Poco::AutoPtr<NodeList> correctionNodeList = pRootElem->getElementsByTagName("correction");
211 for (unsigned long i = 0; i < correctionNodeList->length(); ++i) {
212 // For each correction element
213 auto *corr = dynamic_cast<Element *>(correctionNodeList->item(i));
214 if (corr) {
215 DateAndTime start(corr->getAttribute("valid-from"));
216 DateAndTime end(corr->getAttribute("valid-to"));
217 if (start <= externalDate && externalDate <= end) {
218 parameter_file = corr->getAttribute("file");
219 append = (corr->getAttribute("append") == "true");
220 break;
221 }
222 } else {
223 g_log.error("Parameter correction file: " + correction_file + "contains an invalid correction element.");
224 throw Kernel::Exception::InstrumentDefinitionError("Invalid element in XML parameter correction file",
225 correction_file);
226 }
227 }
228}
229
238void LoadIDFFromNexus::LoadParameters(Nexus::File *nxfile, const MatrixWorkspace_sptr &localWorkspace) {
239
240 std::string parameterString;
241
242 // First attempt to load parameters from nexus file.
243 nxfile->openGroup("instrument", "NXinstrument");
244 localWorkspace->loadInstrumentParametersNexus(nxfile, parameterString);
245 nxfile->closeGroup();
246
247 // loadInstrumentParametersNexus does not populate any instrument params
248 // so we do it here.
249 localWorkspace->populateInstrumentParameters();
250
251 if (parameterString.empty()) {
252 loadParameterFile(localWorkspace);
253 } else { // We do have parameters from the Nexus file
254 g_log.notice() << "Found Instrument parameter map entry in Nexus file, "
255 "which is loaded.\n\n";
256 // process parameterString into parameters in workspace
257 localWorkspace->readParameterMap(parameterString);
258 }
259}
260
261// Private function to load best parameter file associated with a workspace
262void LoadIDFFromNexus::loadParameterFile(const std::shared_ptr<API::MatrixWorkspace> &ws) {
263 g_log.debug("Loading the parameter definition...");
264
265 // Search for XML parameter file which matches instrument and date
266 const std::string instName = ws->getInstrument()->getName();
267 const std::string runDate = ws->getWorkspaceStartDate();
268 std::string fullPathParamIDF = InstrumentFileFinder::getParameterFilename(instName, runDate);
269 if (!fullPathParamIDF.empty()) {
270 g_log.debug() << "Parameter file: " << fullPathParamIDF << '\n';
271 bool paramLoaded = runLoadParameterFile(fullPathParamIDF, ws);
272 if (!paramLoaded) {
273 g_log.notice() << "No Instrument parameter map entry found.\n\n";
274 }
275 }
276}
277
278// Private function to execute LoadParameterFile specified by a full path name into
279// given workspace, returning success.
280bool LoadIDFFromNexus::runLoadParameterFile(const std::string &fullPathName,
281 const MatrixWorkspace_sptr &localWorkspace) {
282 try {
283 // load and also populate instrument parameters from this 'fallback'
284 // parameter file
285 Algorithm_sptr loadParamAlg = createChildAlgorithm("LoadParameterFile");
286 loadParamAlg->setProperty("Filename", fullPathName);
287 loadParamAlg->setProperty("Workspace", localWorkspace);
288 loadParamAlg->execute();
289 g_log.notice() << "Instrument parameter file: " << fullPathName << " has been loaded.\n\n";
290 return true; // Success
291 } catch (std::invalid_argument &e) {
292 g_log.information("LoadParameterFile: No parameter file found for this instrument");
293 g_log.information(e.what());
294 } catch (std::runtime_error &e) {
295 g_log.information("Unable to successfully run LoadParameterFile Child Algorithm");
296 g_log.information(e.what());
297 }
298 return false;
299}
300
301} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
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
@ Load
allowed here which will be passed to the algorithm
static std::string getParameterFilename(const std::string &instrumentName, const std::string &date="", const std::string &dirHint="")
Get the Parameter file using the instrument name and date, optionally searching dirHint first before ...
A property class for workspaces.
std::string getParameterCorrectionFile(const std::string &instName)
Get the parameter correction file, if it exists else return "".
void exec() override
Overwrites Algorithm method.
void readParameterCorrectionFile(const std::string &correction_file, const std::string &date, std::string &parameter_file, bool &append)
Read parameter correction file, return applicabel parameter file and whether to append.
void LoadParameters(Nexus::File *nxfile, const API::MatrixWorkspace_sptr &localWorkspace)
Load the parameters from Nexus file if possible, else from parameter file, into workspace.
bool runLoadParameterFile(const std::string &fullPathName, const API::MatrixWorkspace_sptr &localWorkspace)
Load Parameter File specified by full pathname into given workspace, return success.
void init() override
Overwrites Algorithm method. Does nothing at present.
LoadIDFFromNexus()
Default constructor.
void loadParameterFile(const std::shared_ptr< API::MatrixWorkspace > &ws)
Search for and then load parameter file.
Records the filename and the description of failure.
Definition Exception.h:98
Exception for errors associated with the instrument definition.
Definition Exception.h:220
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
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
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition Algorithm.h:52
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
MANTID_KERNEL_DLL std::string loadFile(const std::string &filename)
Loads the entire contents of a text file into a string.
Definition Strings.cpp:26
@ InOut
Both an input & output workspace.
Definition Property.h:55
@ Input
An input workspace.
Definition Property.h:53