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