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 +
16#include "MantidNexus/NexusFile.h"
17
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 (std::filesystem::filesystem_error &) {
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 const auto &detectorInfo = ws->detectorInfo();
212 for (auto &det : dets) {
213 pixel_id.emplace_back(det->getID());
214 distance.emplace_back(det->getDistance(*sample));
215 azimuthal_angle.emplace_back(detectorInfo.phi(detectorInfo.indexOf(det->getID())));
216 polar_angle.emplace_back(ws->detectorTwoTheta(*det));
217 }
218
219 // Write Pixel ID to file
220 nxfile.writeData("pixel_id_new", pixel_id);
221
222 // Write Secondary Flight Path to file
223 nxfile.writeData("distance_new", distance);
224 nxfile.openData("distance_new");
225 nxfile.putAttr("units", "metre");
226 nxfile.closeData();
227
228 // Write Polar Angle (2theta) to file
229 nxfile.writeData("polar_angle_new", polar_angle);
230 nxfile.openData("polar_angle_new");
231 nxfile.putAttr("units", "radian");
232 nxfile.closeData();
233
234 // Write Azimuthal Angle (Phi) to file
235 nxfile.writeData("azimuthal_angle_new", azimuthal_angle);
236 nxfile.openData("azimuthal_angle_new");
237 nxfile.putAttr("units", "radian");
238 nxfile.closeData();
239
240 nxfile.closeGroup(); // close NXdetector
241
242 progress.report(dets.size());
243 } else {
244 throw std::runtime_error("Could not find any detectors for the bank named " + bankName +
245 " that is listed in the NeXus file."
246 "Check that it exists in the Instrument Definition File.");
247 }
248 }
249 }
250
251 nxfile.closeGroup(); // NXinstrument
252
253 }
254 // Look for monitors
255 else if (entry_iter->second == "NXmonitor") {
256 g_log.debug() << "Monitor called '" << entry_iter->first << "' found.\n";
257 nxfile.openGroup(entry_iter->first, "NXmonitor");
258
259 Geometry::IComponent_const_sptr monitor = instrument->getComponentByName(entry_iter->first);
260
261 // Write Pixel ID to file
262 // nxfile.writeData("pixel_id_new", monitor->get);
263
264 double source_monitor = source->getDistance(*monitor);
265 double source_sample = source->getDistance(*sample);
266
267 g_log.debug() << "source->monitor=" << source_monitor << '\n';
268 g_log.debug() << "source->sample=" << source_sample << '\n';
269 g_log.debug() << "sample->monitor=" << (source_monitor - source_sample) << '\n';
270
271 // Distance
272 nxfile.writeData("distance_new", (source_monitor - source_sample));
273 nxfile.openData("distance_new");
274 nxfile.putAttr("units", "metre");
275 nxfile.closeData();
276
277 nxfile.closeGroup(); // NXmonitor
278 }
279 }
280
281 } else {
282 g_log.error() << "There are no NXentry nodes in the specified NeXus file.\n";
283 }
284 }
285}
286
287//----------------------------------------------------------------------------------------------
293std::string SNSAppendGeometryToNexus::getInstrumentName(const std::string &nxfilename) {
294 std::string instrument;
295
296 // Open the NeXus file
297 Nexus::File nxfile(nxfilename);
298 // What is the first entry ?
299 std::map<std::string, std::string> entries = nxfile.getEntries();
300
301 // For now, let's just open the first entry
302 nxfile.openGroup(entries.begin()->first, "NXentry");
303 g_log.debug() << "Using entry '" << entries.begin()->first << "' to determine instrument name.\n";
304
305 nxfile.openGroup("instrument", "NXinstrument");
306 try {
307 nxfile.openData("name");
308 instrument = nxfile.getStrData();
309 } catch (Nexus::Exception const &) {
310 // TODO: try and get the instrument name from the filename instead.
311 // Note in filename we have instrument short name yet
312 // ExperimentiInfo.getInstrumentFilename() expects instrument long name
313 instrument = "";
314 }
315
316 g_log.debug() << " Instrument name read from NeXus file is " << instrument << '\n';
317
318 return instrument;
319}
320
321//----------------------------------------------------------------------------------------------
331bool SNSAppendGeometryToNexus::runLoadInstrument(const std::string &idf_filename,
332 const API::MatrixWorkspace_sptr &localWorkspace, Algorithm *alg) {
333 auto loadInst = createChildAlgorithm("LoadInstrument", 0, 1, true);
334
335 // Execute the Child Algorithm.
336 bool executionSuccessful(true);
337 try {
338 loadInst->setPropertyValue("Filename", idf_filename);
339 loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", localWorkspace);
340 loadInst->setProperty("RewriteSpectraMap", OptionalBool(false));
341 loadInst->execute();
342 } catch (std::invalid_argument &e) {
343 alg->getLogger().information("Invalid argument to LoadInstrument Child Algorithm");
344 alg->getLogger().information(e.what());
345 executionSuccessful = false;
346 } catch (std::runtime_error &e) {
347 alg->getLogger().information("Failed to run LoadInstrument Child Algorithm");
348 alg->getLogger().information(e.what());
349 executionSuccessful = false;
350 }
351
352 // Throwing an error if failed
353 if (!executionSuccessful) {
354 alg->getLogger().error("Error loading instrument\n");
355 }
356 return executionSuccessful;
357}
358
359//-----------------------------------------------------------------------------
367bool SNSAppendGeometryToNexus::runLoadNexusLogs(const std::string &nexusFileName,
368 const API::MatrixWorkspace_sptr &localWorkspace, Algorithm *alg) {
369 auto loadLogs = alg->createChildAlgorithm("LoadNexusLogs", 0, 1, true);
370
371 // Execute the Child Algorithm, catching errors without stopping.
372 bool executionSuccessful(true);
373 try {
374 alg->getLogger().information() << "Loading logs from the NeXus file...\n";
375 loadLogs->setPropertyValue("Filename", nexusFileName);
376 loadLogs->setProperty<MatrixWorkspace_sptr>("Workspace", localWorkspace);
377 loadLogs->executeAsChildAlg();
378 } catch (std::invalid_argument &e) {
379 alg->getLogger().information("Invalid argument to LoadNexusLogs Child Algorithm");
380 alg->getLogger().information(e.what());
381 executionSuccessful = false;
382 } catch (std::runtime_error &) {
383 alg->getLogger().information("Unable to successfully run runLoadNexusLogs Child Algorithm./n");
384 executionSuccessful = false;
385 }
386
387 return executionSuccessful;
388}
389} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:547
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:426
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:165
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:54