19#include "MantidNexus/NexusFile.h"
21#include <boost/algorithm/string/detail/classification.hpp>
22#include <boost/algorithm/string/split.hpp>
29using namespace Kernel;
31using namespace DataObjects;
32using HistogramData::BinEdges;
33using HistogramData::Counts;
34using HistogramData::CountStandardDeviations;
37 : m_numPixels(0), m_signalNo(0), pulseTimes(0), m_numBins(0), m_spec_min(0), m_spec_max(0), m_dataField(
""),
38 m_axisField(
""), m_xUnits(
""), m_fileMutex(), m_assumeOldFile(false) {}
43 "The name of the NeXus file to load");
45 "The name of the Workspace2D to create.");
47 "Number of the signal to load from the file. Default is 1 = "
49 "Some NXS files have multiple data fields giving binning in "
50 "other units (e.g. d-spacing or momentum).\n"
51 "Enter the right signal number for your desired field.");
52 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
53 mustBePositive->setLower(1);
55 "The index number of the first spectrum to read. Only used if\n"
56 "spectrum_max is set.");
58 "The number of the last spectrum to read. Only used if explicitly\n"
64const std::string ENTRY_NAME(
"/entry");
65const std::string ENTRY_STATE_NAME(
"/entry-state0");
66const std::string NXENTRY(
"NXentry");
67const std::string NXEVENT_DATA(
"NXevent_data");
68const std::string NX_DATA(
"NXdata");
79 if (descriptor.
isEntry(ENTRY_NAME, NXENTRY) || descriptor.
isEntry(ENTRY_STATE_NAME, NXENTRY)) {
85 if (hasData && hasEventData)
88 else if (hasData && !hasEventData)
106 std::vector<std::string> &bankNames) {
114 auto file = Nexus::File(nexusfilename);
117 file.openGroup(entry_name,
"NXentry");
119 file.openGroup(
"instrument",
"NXinstrument");
122 std::map<std::string, std::string> entries = file.getEntries();
123 std::map<std::string, std::string>::iterator it;
124 for (it = entries.begin(); it != entries.end(); ++it) {
125 std::string entryName = it->first;
126 if (entryName.size() > 4) {
127 if (entryName.substr(0, 4) ==
"bank") {
129 file.openGroup(entryName, it->second);
133 std::map<std::string, std::string> dataEntries = file.getEntries();
134 for (
auto dataEntryIt = dataEntries.begin(); dataEntryIt != dataEntries.end(); ++dataEntryIt) {
135 if (dataEntryIt->second ==
"SDS") {
136 file.openData(dataEntryIt->first);
137 if (file.hasAttr(
"signal")) {
139 file.getAttr(
"signal", signal);
146 if (!file.hasAttr(
"axes")) {
149 ", corresponds to the data field '" +
m_dataField +
150 "' has no 'axes' attribute specifying.");
153 axes =
"x_pixel_offset,y_pixel_offset,time_of_flight";
158 file.getAttr(
"axes", axes);
161 std::vector<std::string> allAxes;
162 boost::split(allAxes, axes, boost::algorithm::detail::is_any_ofF<char>(
","));
163 if (allAxes.size() != 3)
165 ", corresponds to the data field '" +
m_dataField +
"' which has only " +
186 ", was not found in any of the data fields of any "
187 "'bankX' group. Cannot load file.");
189 for (it = entries.begin(); it != entries.end(); ++it) {
190 std::string entryName = it->first;
191 if (entryName.size() > 4) {
192 if (entryName.substr(0, 4) ==
"bank") {
194 file.openGroup(entryName, it->second);
195 const auto bankEntries = file.getEntries();
197 if (bankEntries.find(
"pixel_id") != bankEntries.end()) {
198 bankNames.emplace_back(entryName);
201 file.openData(
"pixel_id");
206 const size_t newPixels = std::accumulate(dims.cbegin(), dims.cend(),
static_cast<size_t>(1),
207 [](
size_t product,
auto dim) { return product * dim; });
211 bankNames.emplace_back(entryName);
214 file.openData(
"x_pixel_offset");
218 file.openData(
"y_pixel_offset");
222 if (!xdim.empty() && !ydim.empty()) {
227 if (bankEntries.find(
m_axisField) != bankEntries.end()) {
232 if (file.hasAttr(
"units"))
266 return (wi + 1 < m_min || wi + 1 > m_max);
287 g_log.
debug() <<
"Loading bank " << bankName <<
'\n';
292 auto file = Nexus::File(nexusfilename);
293 file.openGroup(entry_name,
"NXentry");
294 file.openGroup(
"instrument",
"NXinstrument");
295 file.openGroup(bankName,
"NXdetector");
298 std::vector<uint32_t> pixel_id;
302 file.readData(
"pixel_id", pixel_id);
307 g_log.
warning() <<
"Invalid pixel_id data in " << bankName <<
'\n';
312 std::vector<float> xoffsets;
313 std::vector<float> yoffsets;
314 file.readData(
"x_pixel_offset", xoffsets);
315 file.readData(
"y_pixel_offset", yoffsets);
321 g_log.
warning() <<
"Invalid (x,y) offsets in " << bankName <<
'\n';
326 if (bankName.size() > 4) {
327 if (bankName.substr(0, 4) ==
"bank") {
328 bankNum = boost::lexical_cast<size_t>(bankName.substr(4));
333 g_log.
warning() <<
"Invalid bank number for " << bankName <<
'\n';
339 size_t numX = xoffsets.size();
340 size_t numY = yoffsets.size();
342 for (
size_t i = 0; i < numX; i++) {
343 for (
size_t j = 0; j < numY; j++) {
344 pixel_id.emplace_back(
static_cast<uint32_t
>(j + numY * (i + numX * bankNum)));
351 uint32_t ifirst = pixel_id[0];
353 auto newEnd = std::remove_if(pixel_id.begin(), pixel_id.end(), out_range);
354 pixel_id.erase(newEnd, pixel_id.end());
356 if (ifirst != pixel_id[0])
362 g_log.
warning() <<
"No pixels from " << bankName <<
'\n';
367 std::vector<float> tof;
370 if (tof.size() <= 1) {
377 BinEdges
X(tof.begin(), tof.end());
380 std::string errorsField;
381 std::vector<double> data;
383 file.getDataCoerce(data);
384 if (file.hasAttr(
"errors"))
385 file.getAttr(
"errors", errorsField);
389 bool hasErrors = !errorsField.empty();
390 std::vector<double> errors;
393 file.openData(errorsField);
394 file.getDataCoerce(errors);
397 g_log.
information() <<
"Error loading the errors field, '" << errorsField <<
"' for bank " << bankName
398 <<
". Will use sqrt(counts). \n";
407 for (
size_t i = iPart; i < iPart +
m_numPixels; i++) {
409 detid_t pixelID = pixel_id[i - iPart];
410 size_t wi = id_to_wi.find(pixelID)->second;
413 auto &spec = WS->getSpectrum(wi);
415 spec.setDetectorID(pixel_id[i - iPart]);
416 auto from = data.begin() + i *
m_numBins;
420 auto eFrom = errors.begin() + i *
m_numBins;
422 spec.setHistogram(
X, Counts(from, to), CountStandardDeviations(eFrom, eTo));
424 spec.setHistogram(
X, Counts(from, to));
433 std::string entry_name =
"entry";
434 auto file = Nexus::File(filename);
435 std::map<std::string, std::string> entries = file.getEntries();
439 throw std::runtime_error(
"No entries in the NXS file!");
443 if (entries.find(entry_name) == entries.end())
444 entry_name =
"entry-state0";
446 if (entries.find(entry_name) == entries.end())
447 entry_name = entries.begin()->first;
474 auto prog = std::make_unique<Progress>(
this, 0.0, 1.0, 10);
475 prog->doReport(
"Counting pixels");
476 std::vector<std::string> bankNames;
480 prog->setNumSteps(bankNames.size() + 5);
482 prog->doReport(
"Creating workspace");
487 prog->doReport(
"Loading DAS logs");
491 auto periodLog = std::make_unique<const TimeSeriesProperty<int>>(
"period_log");
492 LoadEventNexus::runLoadNexusLogs<MatrixWorkspace_sptr>(filename, WS, *
this,
false, nPeriods, periodLog);
495 prog->report(
"Loading instrument");
497 LoadEventNexus::runLoadInstrument<MatrixWorkspace_sptr>(filename, WS, entry_name,
this);
500 prog->report(
"Loading metadata");
505 }
catch (std::exception &e) {
506 g_log.
warning() <<
"Error while loading meta data: " << e.what() <<
'\n';
511 prog->report(
"Building Spectra Mapping");
512 g_log.
debug() <<
"Building Spectra Mapping\n";
513 WS->rebuildSpectraMapping(
false);
516 const auto id_to_wi = WS->getDetectorIDToWorkspaceIndexMap();
519 for (
const auto &bankName : bankNames) {
520 prog->report(
"Loading bank " + bankName);
521 g_log.
debug() <<
"Loading bank " << bankName <<
'\n';
522 loadBank(filename, entry_name, bankName, WS, id_to_wi);
527 WS->getAxis(0)->setUnit(
"dSpacing");
529 WS->getAxis(0)->setUnit(
"MomentumTransfer");
532 WS->getAxis(0)->setUnit(
"TOF");
533 WS->setYUnit(
"Counts");
detid2index_map m_id_to_wi
#define DECLARE_NEXUS_LAZY_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_LAZY_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM mac...
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.
@ Load
allowed here which will be passed to the algorithm
A property class for workspaces.
static void loadEntryMetadata(const std::string &nexusfilename, T WS, const std::string &entry_name)
Load the run number and other meta data from the given bank.
size_t m_numBins
Number of bins.
std::string m_dataField
Name of the 'data' field to load (depending on Signal)
int m_signalNo
Signal # to load. Default 1.
bool m_assumeOldFile
Flag for whether or not to assume the data is old SNS raw files;.
static std::string getEntryName(const std::string &filename)
std::mutex m_fileMutex
Mutex to avoid simultaneous file access.
void countPixels(const std::string &nexusfilename, const std::string &entry_name, std::vector< std::string > &bankNames)
Goes thoguh a histogram NXS file and counts the number of pixels.
LoadTOFRawNexus()
Default Constructor.
void exec() override
Executes the algorithm.
int confidence(Nexus::NexusDescriptorLazy &descriptor) const override
Returns a confidence value that this algorithm can load a file.
specnum_t m_spec_min
Interval of chunk.
size_t m_numPixels
Number of pixels.
void loadBank(const std::string &nexusfilename, const std::string &entry_name, const std::string &bankName, const API::MatrixWorkspace_sptr &WS, const detid2index_map &id_to_wi)
Load a single bank into the workspace.
void init() override
Initialisation method.
std::string m_axisField
Name of the 'axis' field to load (depending on Signal)
std::string m_xUnits
Units of the X axis found.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
void warning(const std::string &msg)
Logs at warning level.
void information(const std::string &msg)
Logs at information level.
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...
bool classTypeExistsChild(const std::string &parentPath, const std::string &classType) const
Query if a given type exists as a decendant of the supplied parentPath.
bool isEntry(std::string const &entryName, std::string const &groupClass) const
Checks if a full-address entry exists for a particular groupClass in a Nexus dataset.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::string toString(const T &value)
Convert a number to a string.
std::vector< dimsize_t > DimVector
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
int32_t detid_t
Typedef for a detector ID.
std::unordered_map< detid_t, size_t > detid2index_map
Map with key = detector ID, value = workspace index.
int32_t specnum_t
Typedef for a spectrum Number.
@ Output
An output workspace.