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