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