Mantid
Loading...
Searching...
No Matches
SNSAppendGeometryToNexus.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 +
15#include "MantidNexus/NexusFile.h"
16
17#include <Poco/Exception.h>
18#include <filesystem>
19using namespace Mantid::Kernel;
20using namespace Mantid::API;
21using namespace Mantid::Nexus;
22
23namespace Mantid::DataHandling {
24
25// Register the algorithm into the AlgorithmFactory
27
28//----------------------------------------------------------------------------------------------
32 : m_makeNexusCopy(false), m_instrumentLoadedCorrectly(false), m_logsLoadedCorrectly(false) {
33 // inform deprecation alias status
34 setDeprecationDate("2021-09-14");
35}
36
37//----------------------------------------------------------------------------------------------
43
44//----------------------------------------------------------------------------------------------
46const std::string SNSAppendGeometryToNexus::name() const { return "SNSAppendGeometryToNexus"; }
47
49int SNSAppendGeometryToNexus::version() const { return 1; }
50
52const std::string SNSAppendGeometryToNexus::category() const { return "DataHandling\\DataAcquisition"; }
53
54//----------------------------------------------------------------------------------------------
55
56//----------------------------------------------------------------------------------------------
60 // Declare potential extensions for input NeXus file
61 std::vector<std::string> extensions{".nxs", ".h5"};
62
63 declareProperty(std::make_unique<API::FileProperty>("Filename", "", API::FileProperty::Load, extensions),
64 "The name of the NeXus file to append geometry to.");
65
66 // TODO: change MakeCopy default to False when comfortable. Otherwise need to
67 // remove the extra copy once in production.
68 declareProperty(std::make_unique<PropertyWithValue<bool>>("MakeCopy", true, Direction::Input),
69 "Copy the NeXus file first before appending (optional, default True).");
70}
71
72//----------------------------------------------------------------------------------------------
76 // TODO: rename the created arrays before moving to production
77 g_log.warning() << "This is intended as a proof of principle and not a long "
78 "term implementation.\n";
79 g_log.warning() << "(the created arrays in the NeXus file will have the '_new' suffix)\n";
80
81 // Retrieve filename from the properties
82 m_filename = getPropertyValue("Filename");
83
84 // Are we going to make a copy of the file ?
85 m_makeNexusCopy = getProperty("MakeCopy");
86
87 if (m_makeNexusCopy) {
88 std::filesystem::path originalFile(m_filename);
89
90 if (std::filesystem::exists(originalFile)) {
91 std::filesystem::path destinationFile = std::filesystem::temp_directory_path() / originalFile.filename();
92
93 try {
94 std::filesystem::copy_file(originalFile, destinationFile);
95 g_log.notice() << "Copied " << m_filename << " to " << destinationFile << ".\n";
96 m_filename = destinationFile.string();
97 } catch (Poco::FileAccessDeniedException &) {
98 throw std::runtime_error("A Problem occurred in making a copy of the "
99 "NeXus file. Failed to copy " +
100 originalFile.string() + " to " + destinationFile.string() +
101 ". Please check file permissions.");
102 }
103 } else {
104 g_log.error() << "Cannot copy a file that doesn't exist! (" << originalFile << ").\n";
105 }
106 }
107
108 // Let's check to see if we can write to the NeXus file.
109 const auto perms = std::filesystem::status(std::filesystem::path(m_filename)).permissions();
110 if ((perms & std::filesystem::perms::owner_write) == std::filesystem::perms::none) {
111 throw std::runtime_error("The specified NeXus file (" + m_filename + ") is not writable.");
112 }
113
114 // Let's look for the instrument name
116
117 if (m_instrument.length() == 0) {
118
119 throw std::runtime_error("Failed to get instrument name from " + m_filename +
120 ". Can't identify instrument definition file.");
121 }
122
123 // Temp workspace name to load the instrument into
124 // std::string workspaceName = "__" + m_instrument + "_geometry_ws";
125
126 // Now what is the instrument definition filename ?
127 // TODO: Modify to use /entry/instrument/instrument_xml/data after
128 // establishing a way to maintain ADARA Geometry Packet
130 g_log.debug() << "Loading instrument definition from " << m_idf_filename << ".\n";
131
132 // Modified to call LoadInstrument directly as a Child Algorithm
133 ws = WorkspaceFactory::Instance().create("Workspace2D", 1, 2, 1);
134
135 // Load NeXus logs for HYSPEC, HYSPECA(testing), and SNAP
136 if (m_instrument == "HYSPEC" || m_instrument == "HYSPECA" || m_instrument == "SNAP") {
137 g_log.debug() << "Run LoadNexusLogs Child Algorithm.\n";
139
141 throw std::runtime_error("Failed to run LoadNexusLogs Child Algorithm.");
142 }
143
144 g_log.debug() << "Run LoadInstrument Child Algorithm.\n";
146
148 throw std::runtime_error("Failed to run LoadInstrument Child Algorithm.");
149
150 // Get the number of detectors (just for progress reporting)
151 // Get the number of histograms/detectors
152 const size_t numDetectors = ws->getInstrument()->getDetectorIDs().size();
153
154 API::Progress progress(this, 0.0, 1.0, numDetectors);
155
156 // Get the instrument
157 Geometry::Instrument_const_sptr instrument = ws->getInstrument();
158
159 // Get the sample (needed to calculate distances)
160 Geometry::IComponent_const_sptr sample = instrument->getSample();
161 // Get the source (moderator)
162 Geometry::IComponent_const_sptr source = instrument->getSource();
163
164 // Open the NeXus file
165 Nexus::File nxfile(m_filename, NXaccess::RDWR);
166
167 // using string_map_t = std::map<std::string,std::string>;
168 std::map<std::string, std::string>::const_iterator root_iter;
169 std::map<std::string, std::string> entries = nxfile.getEntries();
170
171 for (root_iter = entries.begin(); root_iter != entries.end(); ++root_iter) {
172 // Open all NXentry
173 if (root_iter->second == "NXentry") {
174 nxfile.openGroup(root_iter->first, "NXentry");
175
176 // Get a list of items within the entry.
177 std::map<std::string, std::string> entry_items = nxfile.getEntries();
178 // Create an iterator for this
179 std::map<std::string, std::string>::const_iterator entry_iter;
180
181 for (entry_iter = entry_items.begin(); entry_iter != entry_items.end(); ++entry_iter) {
182 // Look for an instrument
183 if (entry_iter->second == "NXinstrument") {
184 // Open the instrument
185 nxfile.openGroup(entry_iter->first, "NXinstrument");
186 std::map<std::string, std::string> instr_items = nxfile.getEntries();
187 std::map<std::string, std::string>::const_iterator instr_iter;
188 for (instr_iter = instr_items.begin(); instr_iter != instr_items.end(); ++instr_iter) {
189 // Look for NXdetectors
190 if (instr_iter->second == "NXdetector") {
191 g_log.debug() << "Detector called '" << instr_iter->first << "' found.\n";
192 std::string bankName = instr_iter->first;
193 std::vector<Geometry::IDetector_const_sptr> dets;
194 ws->getInstrument()->getDetectorsInBank(dets, bankName);
195
196 if (!dets.empty()) {
197 nxfile.openGroup(bankName, "NXdetector");
198
199 // Let's create some vectors for the parameters to write
200 // Pixel IDs
201 std::vector<int> pixel_id;
202 std::vector<double> distance;
203 std::vector<double> polar_angle;
204 std::vector<double> azimuthal_angle;
205
206 pixel_id.reserve(dets.size());
207 distance.reserve(dets.size());
208 polar_angle.reserve(dets.size());
209 azimuthal_angle.reserve(dets.size());
210
211 for (auto &det : dets) {
212 pixel_id.emplace_back(det->getID());
213 distance.emplace_back(det->getDistance(*sample));
214 azimuthal_angle.emplace_back(det->getPhi());
215 polar_angle.emplace_back(ws->detectorTwoTheta(*det));
216 }
217
218 // Write Pixel ID to file
219 nxfile.writeData("pixel_id_new", pixel_id);
220
221 // Write Secondary Flight Path to file
222 nxfile.writeData("distance_new", distance);
223 nxfile.openData("distance_new");
224 nxfile.putAttr("units", "metre");
225 nxfile.closeData();
226
227 // Write Polar Angle (2theta) to file
228 nxfile.writeData("polar_angle_new", polar_angle);
229 nxfile.openData("polar_angle_new");
230 nxfile.putAttr("units", "radian");
231 nxfile.closeData();
232
233 // Write Azimuthal Angle (Phi) to file
234 nxfile.writeData("azimuthal_angle_new", azimuthal_angle);
235 nxfile.openData("azimuthal_angle_new");
236 nxfile.putAttr("units", "radian");
237 nxfile.closeData();
238
239 nxfile.closeGroup(); // close NXdetector
240
241 progress.report(dets.size());
242 } else {
243 throw std::runtime_error("Could not find any detectors for the bank named " + bankName +
244 " that is listed in the NeXus file."
245 "Check that it exists in the Instrument Definition File.");
246 }
247 }
248 }
249
250 nxfile.closeGroup(); // NXinstrument
251
252 }
253 // Look for monitors
254 else if (entry_iter->second == "NXmonitor") {
255 g_log.debug() << "Monitor called '" << entry_iter->first << "' found.\n";
256 nxfile.openGroup(entry_iter->first, "NXmonitor");
257
258 Geometry::IComponent_const_sptr monitor = instrument->getComponentByName(entry_iter->first);
259
260 // Write Pixel ID to file
261 // nxfile.writeData("pixel_id_new", monitor->get);
262
263 double source_monitor = source->getDistance(*monitor);
264 double source_sample = source->getDistance(*sample);
265
266 g_log.debug() << "source->monitor=" << source_monitor << '\n';
267 g_log.debug() << "source->sample=" << source_sample << '\n';
268 g_log.debug() << "sample->monitor=" << (source_monitor - source_sample) << '\n';
269
270 // Distance
271 nxfile.writeData("distance_new", (source_monitor - source_sample));
272 nxfile.openData("distance_new");
273 nxfile.putAttr("units", "metre");
274 nxfile.closeData();
275
276 nxfile.closeGroup(); // NXmonitor
277 }
278 }
279
280 } else {
281 g_log.error() << "There are no NXentry nodes in the specified NeXus file.\n";
282 }
283 }
284}
285
286//----------------------------------------------------------------------------------------------
292std::string SNSAppendGeometryToNexus::getInstrumentName(const std::string &nxfilename) {
293 std::string instrument;
294
295 // Open the NeXus file
296 Nexus::File nxfile(nxfilename);
297 // What is the first entry ?
298 std::map<std::string, std::string> entries = nxfile.getEntries();
299
300 // For now, let's just open the first entry
301 nxfile.openGroup(entries.begin()->first, "NXentry");
302 g_log.debug() << "Using entry '" << entries.begin()->first << "' to determine instrument name.\n";
303
304 nxfile.openGroup("instrument", "NXinstrument");
305 try {
306 nxfile.openData("name");
307 instrument = nxfile.getStrData();
308 } catch (Nexus::Exception const &) {
309 // TODO: try and get the instrument name from the filename instead.
310 // Note in filename we have instrument short name yet
311 // ExperimentiInfo.getInstrumentFilename() expects instrument long name
312 instrument = "";
313 }
314
315 g_log.debug() << " Instrument name read from NeXus file is " << instrument << '\n';
316
317 return instrument;
318}
319
320//----------------------------------------------------------------------------------------------
330bool SNSAppendGeometryToNexus::runLoadInstrument(const std::string &idf_filename,
331 const API::MatrixWorkspace_sptr &localWorkspace, Algorithm *alg) {
332 auto loadInst = createChildAlgorithm("LoadInstrument", 0, 1, true);
333
334 // Execute the Child Algorithm.
335 bool executionSuccessful(true);
336 try {
337 loadInst->setPropertyValue("Filename", idf_filename);
338 loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", localWorkspace);
339 loadInst->setProperty("RewriteSpectraMap", OptionalBool(false));
340 loadInst->execute();
341 } catch (std::invalid_argument &e) {
342 alg->getLogger().information("Invalid argument to LoadInstrument Child Algorithm");
343 alg->getLogger().information(e.what());
344 executionSuccessful = false;
345 } catch (std::runtime_error &e) {
346 alg->getLogger().information("Failed to run LoadInstrument Child Algorithm");
347 alg->getLogger().information(e.what());
348 executionSuccessful = false;
349 }
350
351 // Throwing an error if failed
352 if (!executionSuccessful) {
353 alg->getLogger().error("Error loading instrument\n");
354 }
355 return executionSuccessful;
356}
357
358//-----------------------------------------------------------------------------
366bool SNSAppendGeometryToNexus::runLoadNexusLogs(const std::string &nexusFileName,
367 const API::MatrixWorkspace_sptr &localWorkspace, Algorithm *alg) {
368 auto loadLogs = alg->createChildAlgorithm("LoadNexusLogs", 0, 1, true);
369
370 // Execute the Child Algorithm, catching errors without stopping.
371 bool executionSuccessful(true);
372 try {
373 alg->getLogger().information() << "Loading logs from the NeXus file...\n";
374 loadLogs->setPropertyValue("Filename", nexusFileName);
375 loadLogs->setProperty<MatrixWorkspace_sptr>("Workspace", localWorkspace);
376 loadLogs->executeAsChildAlg();
377 } catch (std::invalid_argument &e) {
378 alg->getLogger().information("Invalid argument to LoadNexusLogs Child Algorithm");
379 alg->getLogger().information(e.what());
380 executionSuccessful = false;
381 } catch (std::runtime_error &) {
382 alg->getLogger().information("Unable to successfully run runLoadNexusLogs Child Algorithm./n");
383 executionSuccessful = false;
384 }
385
386 return executionSuccessful;
387}
388} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
Base class from which all concrete algorithm classes should be derived.
Definition Algorithm.h:76
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
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Kernel::Logger & getLogger() const
Returns a reference to the logger.
@ Load
allowed here which will be passed to the algorithm
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
SNSAppendGeometryToNexus : Appends geometry information to a NeXus file.
std::string m_filename
The filename of the NeXus file to append geometry info to.
API::MatrixWorkspace_sptr ws
The workspace to load instrument and logs.
std::string getInstrumentName(const std::string &nxfilename)
Get the instrument name from the NeXus file.
int version() const override
Algorithm's version for identification.
bool runLoadInstrument(const std::string &idf_filename, const API::MatrixWorkspace_sptr &localWorkspace, Algorithm *alg)
Run LoadInstrument as a Child Algorithm.
const std::string category() const override
Algorithm's category for identification.
bool m_makeNexusCopy
Are we going to make a copy of the NeXus file to operate on ?
void init() override
Initialize the algorithm's properties.
const std::string name() const override
Algorithm's name for identification.
static bool runLoadNexusLogs(const std::string &nexusFileName, const API::MatrixWorkspace_sptr &localWorkspace, Algorithm *alg)
Load logs from the NeXus file.
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 warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
OptionalBool : Tri-state bool.
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Class that provides for a standard Nexus exception.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition IComponent.h:167
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
Header for a base Nexus::Exception.
@ Input
An input workspace.
Definition Property.h:53