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