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