Newer
Older
#include "MantidDataHandling/AppendGeometryToSNSNexus.h"
#include "MantidKernel/System.h"
#include "MantidAPI/FileProperty.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidDataObjects/Workspace2D.h"
Federico Montesino Pouzols
committed
#include "MantidGeometry/Instrument.h"
#include <nexus/NeXusFile.hpp>
#include <nexus/NeXusException.hpp>
#include <Poco/File.h>
#include <Poco/Path.h>
#include <Poco/Exception.h>
using namespace Mantid::Kernel;
using namespace Mantid::API;
using namespace ::NeXus;
namespace Mantid {
namespace DataHandling {
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(AppendGeometryToSNSNexus)
//----------------------------------------------------------------------------------------------
/** Constructor
*/
AppendGeometryToSNSNexus::AppendGeometryToSNSNexus()
: m_makeNexusCopy(false), m_instrumentLoadedCorrectly(false),
m_logsLoadedCorrectly(false) {}
//----------------------------------------------------------------------------------------------
/** Destructor
*/
AppendGeometryToSNSNexus::~AppendGeometryToSNSNexus() {
// delete workspace
}
//----------------------------------------------------------------------------------------------
/// Algorithm's name for identification. @see Algorithm::name
const std::string AppendGeometryToSNSNexus::name() const {
return "AppendGeometryToSNSNexus";
}
/// Algorithm's version for identification. @see Algorithm::version
int AppendGeometryToSNSNexus::version() const { return 1; }
/// Algorithm's category for identification. @see Algorithm::category
const std::string AppendGeometryToSNSNexus::category() const {
return "DataHandling\\DataAcquisition";
}
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
/** Initialize the algorithm's properties.
*/
void AppendGeometryToSNSNexus::init() {
// Declare potential extensions for input NeXus file
std::vector<std::string> extensions{".nxs", ".h5"};
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
declareProperty(new API::FileProperty("Filename", "", API::FileProperty::Load,
extensions),
"The name of the NeXus file to append geometry to.");
// TODO: change MakeCopy default to False when comfortable. Otherwise need to
// remove the extra copy once in production.
declareProperty(
new PropertyWithValue<bool>("MakeCopy", true, Direction::Input),
"Copy the NeXus file first before appending (optional, default True).");
}
//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
*/
void AppendGeometryToSNSNexus::exec() {
// TODO: rename the created arrays before moving to production
g_log.warning() << "This is intended as a proof of principle and not a long "
"term implementation." << std::endl;
g_log.warning()
<< "(the created arrays in the NeXus file will have the '_new' suffix)"
<< std::endl;
// Retrieve filename from the properties
m_filename = getPropertyValue("Filename");
// Are we going to make a copy of the file ?
m_makeNexusCopy = getProperty("MakeCopy");
if (m_makeNexusCopy) {
Poco::File originalFile(m_filename);
Poco::Path originalPath(m_filename);
if (originalFile.exists()) {
Poco::File destinationFile(
Poco::Path(Poco::Path::temp(), originalPath.getFileName()));
try {
originalFile.copyTo(destinationFile.path());
g_log.notice() << "Copied " << m_filename << " to "
<< destinationFile.path() << "." << std::endl;
m_filename = destinationFile.path();
} catch (Poco::FileAccessDeniedException &) {
throw std::runtime_error("A Problem occurred in making a copy of the "
"NeXus file. Failed to copy " +
originalFile.path() + " to " +
destinationFile.path() +
". Please check file permissions.");
}
} else {
g_log.error() << "Cannot copy a file that doesn't exist! ("
<< originalFile.path() << ")." << std::endl;
}
// Let's check to see if we can write to the NeXus file.
if (!(Poco::File(m_filename).canWrite())) {
throw std::runtime_error("The specified NeXus file (" + m_filename +
") is not writable.");
// Let's look for the instrument name
m_instrument = getInstrumentName(m_filename);
if (m_instrument.length() == 0) {
throw std::runtime_error("Failed to get instrument name from " +
m_filename +
". Can't identify instrument definition file.");
}
// Temp workspace name to load the instrument into
// std::string workspaceName = "__" + m_instrument + "_geometry_ws";
// Now what is the instrument definition filename ?
// TODO: Modify to use /entry/instrument/instrument_xml/data after
// establishing a way to maintain ADARA Geometry Packet
m_idf_filename = ExperimentInfo::getInstrumentFilename(m_instrument);
g_log.debug() << "Loading instrument definition from " << m_idf_filename
<< "." << std::endl;
// Modified to call LoadInstrument directly as a Child Algorithm
ws = WorkspaceFactory::Instance().create("Workspace2D", 1, 2, 1);
// Load NeXus logs for HYSPEC, HYSPECA(testing), and SNAP
if (m_instrument == "HYSPEC" || m_instrument == "HYSPECA" ||
m_instrument == "SNAP") {
g_log.debug() << "Run LoadNexusLogs Child Algorithm." << std::endl;
m_logsLoadedCorrectly = runLoadNexusLogs(m_filename, ws, this);
if (!m_logsLoadedCorrectly)
throw std::runtime_error("Failed to run LoadNexusLogs Child Algorithm.");
}
g_log.debug() << "Run LoadInstrument Child Algorithm." << std::endl;
m_instrumentLoadedCorrectly = runLoadInstrument(m_idf_filename, ws, this);
if (!m_instrumentLoadedCorrectly)
throw std::runtime_error("Failed to run LoadInstrument Child Algorithm.");
// Get the number of detectors (just for progress reporting)
// Get the number of histograms/detectors
const size_t numDetectors = ws->getInstrument()->getDetectorIDs().size();
API::Progress progress(this, 0.0, 1.0, numDetectors);
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// Get the instrument
Geometry::Instrument_const_sptr instrument = ws->getInstrument();
// Get the sample (needed to calculate distances)
Geometry::IComponent_const_sptr sample = instrument->getSample();
// Get the source (moderator)
Geometry::IComponent_const_sptr source = instrument->getSource();
// Open the NeXus file
::NeXus::File nxfile(m_filename, NXACC_RDWR);
// typedef std::map<std::string,std::string> string_map_t;
std::map<std::string, std::string>::const_iterator root_iter;
std::map<std::string, std::string> entries = nxfile.getEntries();
for (root_iter = entries.begin(); root_iter != entries.end(); ++root_iter) {
// Open all NXentry
if (root_iter->second == "NXentry") {
nxfile.openGroup(root_iter->first, "NXentry");
// Get a list of items within the entry.
std::map<std::string, std::string> entry_items = nxfile.getEntries();
// Create an iterator for this
std::map<std::string, std::string>::const_iterator entry_iter;
for (entry_iter = entry_items.begin(); entry_iter != entry_items.end();
++entry_iter) {
// Look for an instrument
if (entry_iter->second == "NXinstrument") {
// Open the instrument
nxfile.openGroup(entry_iter->first, "NXinstrument");
std::map<std::string, std::string> instr_items = nxfile.getEntries();
std::map<std::string, std::string>::const_iterator instr_iter;
for (instr_iter = instr_items.begin();
instr_iter != instr_items.end(); ++instr_iter) {
// Look for NXdetectors
if (instr_iter->second == "NXdetector") {
g_log.debug() << "Detector called '" << instr_iter->first
<< "' found." << std::endl;
std::string bankName = instr_iter->first;
std::vector<Geometry::IDetector_const_sptr> dets;
ws->getInstrument()->getDetectorsInBank(dets, bankName);
if (!dets.empty()) {
nxfile.openGroup(bankName, "NXdetector");
// Let's create some vectors for the parameters to write
// Pixel IDs
std::vector<int> pixel_id;
std::vector<double> distance;
std::vector<double> polar_angle;
std::vector<double> azimuthal_angle;
pixel_id.reserve(dets.size());
distance.reserve(dets.size());
polar_angle.reserve(dets.size());
azimuthal_angle.reserve(dets.size());
for (auto &det : dets) {
pixel_id.push_back(det->getID());
distance.push_back(det->getDistance(*sample));
azimuthal_angle.push_back(det->getPhi());
polar_angle.push_back(ws->detectorTwoTheta(det));
}
// Write Pixel ID to file
nxfile.writeData("pixel_id_new", pixel_id);
// Write Secondary Flight Path to file
nxfile.writeData("distance_new", distance);
nxfile.openData("distance_new");
nxfile.putAttr("units", "metre");
nxfile.closeData();
// Write Polar Angle (2theta) to file
nxfile.writeData("polar_angle_new", polar_angle);
nxfile.openData("polar_angle_new");
nxfile.putAttr("units", "radian");
nxfile.closeData();
// Write Azimuthal Angle (Phi) to file
nxfile.writeData("azimuthal_angle_new", azimuthal_angle);
nxfile.openData("azimuthal_angle_new");
nxfile.putAttr("units", "radian");
nxfile.closeData();
nxfile.closeGroup(); // close NXdetector
progress.report(dets.size());
} else {
throw std::runtime_error(
"Could not find any detectors for the bank named " +
bankName +
" that is listed in the NeXus file."
"Check that it exists in the Instrument Definition File.");
}
}
}
nxfile.closeGroup(); // NXinstrument
}
// Look for monitors
else if (entry_iter->second == "NXmonitor") {
g_log.debug() << "Monitor called '" << entry_iter->first << "' found."
<< std::endl;
nxfile.openGroup(entry_iter->first, "NXmonitor");
Geometry::IComponent_const_sptr monitor =
instrument->getComponentByName(entry_iter->first);
// Write Pixel ID to file
// nxfile.writeData("pixel_id_new", monitor->get);
double source_monitor = source->getDistance(*monitor);
double source_sample = source->getDistance(*sample);
g_log.debug() << "source->monitor=" << source_monitor << std::endl;
g_log.debug() << "source->sample=" << source_sample << std::endl;
g_log.debug() << "sample->monitor="
<< (source_monitor - source_sample) << std::endl;
// Distance
nxfile.writeData("distance_new", (source_monitor - source_sample));
nxfile.openData("distance_new");
nxfile.putAttr("units", "metre");
nxfile.closeData();
nxfile.closeGroup(); // NXmonitor
}
} else {
g_log.error() << "There are no NXentry nodes in the specified NeXus file."
<< std::endl;
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
}
}
//----------------------------------------------------------------------------------------------
/** Get the instrument name from the input NeXus file.
*
* @param nxfilename :: Input NeXus file.
* @return the instrument name, empty string if failed.
*/
std::string
AppendGeometryToSNSNexus::getInstrumentName(const std::string &nxfilename) {
std::string instrument;
// Open the NeXus file
::NeXus::File nxfile(nxfilename);
// What is the first entry ?
std::map<std::string, std::string> entries = nxfile.getEntries();
// For now, let's just open the first entry
nxfile.openGroup(entries.begin()->first, "NXentry");
g_log.debug() << "Using entry '" << entries.begin()->first
<< "' to determine instrument name." << std::endl;
nxfile.openGroup("instrument", "NXinstrument");
try {
nxfile.openData("name");
instrument = nxfile.getStrData();
} catch (::NeXus::Exception &) {
// TODO: try and get the instrument name from the filename instead.
// Note in filename we have instrument short name yet
// ExperimentiInfo.getInstrumentFilename() expects instrument long name
instrument = "";
}
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
g_log.debug() << " Instrument name read from NeXus file is " << instrument
<< std::endl;
return instrument;
}
//----------------------------------------------------------------------------------------------
/** Load the instrument using the input instrument definition file.
*
* @param idf_filename :: Input instrument definition file.
* @param localWorkspace :: MatrixWorkspace in which to put the instrument
*geometry
* @param alg :: Handle of an algorithm for logging access
* @return true if successful
*/
bool AppendGeometryToSNSNexus::runLoadInstrument(
const std::string &idf_filename, API::MatrixWorkspace_sptr localWorkspace,
Algorithm *alg) {
IAlgorithm_sptr loadInst = createChildAlgorithm("LoadInstrument", 0, 1, true);
// Execute the Child Algorithm.
bool executionSuccessful(true);
try {
loadInst->setPropertyValue("Filename", idf_filename);
loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", localWorkspace);
loadInst->setProperty("RewriteSpectraMap", OptionalBool(false));
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
loadInst->execute();
} catch (std::invalid_argument &e) {
alg->getLogger().information(
"Invalid argument to LoadInstrument Child Algorithm");
alg->getLogger().information(e.what());
executionSuccessful = false;
} catch (std::runtime_error &e) {
alg->getLogger().information(
"Failed to run LoadInstrument Child Algorithm");
alg->getLogger().information(e.what());
executionSuccessful = false;
}
// Throwing an error if failed
if (!executionSuccessful) {
alg->getLogger().error("Error loading instrument\n");
}
return executionSuccessful;
}
//-----------------------------------------------------------------------------
/** Load the logs from the input NeXus file.
*
* @param nexusFileName :: Name of the NeXus file to load logs from.
* @param localWorkspace :: MatrixWorkspace in which to put the logs.
* @param alg :: Handle of an algorithm for logging access.
* @return true if successful.
*/
bool AppendGeometryToSNSNexus::runLoadNexusLogs(
const std::string &nexusFileName, API::MatrixWorkspace_sptr localWorkspace,
Algorithm *alg) {
IAlgorithm_sptr loadLogs =
alg->createChildAlgorithm("LoadNexusLogs", 0, 1, true);
// Execute the Child Algorithm, catching errors without stopping.
bool executionSuccessful(true);
try {
alg->getLogger().information() << "Loading logs from the NeXus file..."
<< std::endl;
loadLogs->setPropertyValue("Filename", nexusFileName);
loadLogs->setProperty<MatrixWorkspace_sptr>("Workspace", localWorkspace);
loadLogs->executeAsChildAlg();
} catch (std::invalid_argument &e) {
alg->getLogger().information(
"Invalid argument to LoadNexusLogs Child Algorithm");
alg->getLogger().information(e.what());
executionSuccessful = false;
} catch (std::runtime_error &) {
alg->getLogger().information(
"Unable to successfully run runLoadNexusLogs Child Algorithm./n");
executionSuccessful = false;
return executionSuccessful;
}
} // namespace Mantid
} // namespace DataHandling