"qt/git@code.ornl.gov:mantidproject/mantid.git" did not exist on "ae9d0495e38ddd20c3b62eed6c06b933c5df0cfd"
Newer
Older
#include "MantidDataHandling/LoadEventNexus.h"
#include "MantidDataHandling/EventWorkspaceCollection.h"
#include "MantidDataHandling/ProcessBankData.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/Axis.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/RegisterFileLoader.h"
#include "MantidAPI/Run.h"
#include "MantidAPI/Sample.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/SpectrumDetectorMapping.h"
Federico Montesino Pouzols
committed
#include "MantidGeometry/Instrument.h"
#include "MantidGeometry/Instrument/Goniometer.h"
Federico Montesino Pouzols
committed
#include "MantidGeometry/Instrument/RectangularDetector.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/DateAndTimeHelpers.h"
#include "MantidKernel/MultiThreaded.h"
Federico Montesino Pouzols
committed
#include "MantidKernel/ThreadPool.h"
#include "MantidKernel/ThreadSchedulerMutexes.h"
#include "MantidKernel/TimeSeriesProperty.h"
Federico Montesino Pouzols
committed
#include "MantidKernel/UnitFactory.h"
#include "MantidKernel/VisibleWhenProperty.h"
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/shared_array.hpp>
Federico Montesino Pouzols
committed
#include <functional>
using Mantid::Types::Core::DateAndTime;
using Mantid::Types::Event::TofEvent;
using std::map;
using std::string;
using std::vector;
using namespace ::NeXus;
namespace Mantid {
namespace DataHandling {
DECLARE_NEXUS_FILELOADER_ALGORITHM(LoadEventNexus)
using namespace DateAndTimeHelpers;
using namespace Geometry;
using namespace API;
using namespace DataObjects;
using Types::Core::DateAndTime;
using Types::Event::TofEvent;
namespace {
/**
* Copy all logData properties from the 'from' workspace to the 'to'
* workspace. Does not use CopyLogs as a child algorithm (this is a
* simple copy and the workspace is not yet in the ADS).
*
* @param from source of log entries
* @param to workspace where to add the log entries
*/
void copyLogs(const Mantid::DataHandling::EventWorkspaceCollection_sptr &from,
EventWorkspace_sptr &to) {
// from the logs, get all the properties that don't overwrite any
// prop. already set in the sink workspace (like 'filename').
auto props = from->mutableRun().getLogData();
for (auto &prop : props) {
if (!to->mutableRun().hasProperty(prop->name())) {
to->mutableRun().addLogData(prop->clone());
//==============================================================================================
// Class LoadBankFromDiskTask
//==============================================================================================
/** This task does the disk IO from loading the NXS file,
* and so will be on a disk IO mutex */
class LoadBankFromDiskTask : public Task {
public:
//---------------------------------------------------------------------------------------------------
/** Constructor
*
* @param alg :: Handle to the main algorithm
* @param entry_name :: The pathname of the bank to load
* @param entry_type :: The classtype of the entry to load
* @param numEvents :: The number of events in the bank.
* @param oldNeXusFileNames :: Identify if file is of old variety.
* @param prog :: an optional Progress object
* @param ioMutex :: a mutex shared for all Disk I-O tasks
* @param scheduler :: the ThreadScheduler that runs this task.
* @param framePeriodNumbers :: Period numbers corresponding to each frame
*/
LoadBankFromDiskTask(LoadEventNexus *alg, const std::string &entry_name,
const std::string &entry_type,
const std::size_t numEvents,
const bool oldNeXusFileNames, Progress *prog,
boost::shared_ptr<std::mutex> ioMutex,
ThreadScheduler *scheduler,
const std::vector<int> &framePeriodNumbers)
: Task(), alg(alg), entry_name(entry_name), entry_type(entry_type),
// prog(prog), scheduler(scheduler), thisBankPulseTimes(NULL),
// m_loadError(false),
prog(prog), scheduler(scheduler), m_loadError(false),
m_oldNexusFileNames(oldNeXusFileNames), m_loadStart(), m_loadSize(),
m_event_id(nullptr), m_event_time_of_flight(nullptr),
m_have_weight(false), m_event_weight(nullptr),
m_framePeriodNumbers(framePeriodNumbers) {
setMutex(ioMutex);
m_cost = static_cast<double>(numEvents);
m_min_id = std::numeric_limits<uint32_t>::max();
m_max_id = 0;
}
//---------------------------------------------------------------------------------------------------
/** Load the pulse times, if needed. This sets
* thisBankPulseTimes to the right pointer.
* */
void loadPulseTimes(::NeXus::File &file) {
try {
// First, get info about the event_time_zero field in this bank
file.openData("event_time_zero");
} catch (::NeXus::Exception &) {
// Field not found error is most likely.
// Use the "proton_charge" das logs.
thisBankPulseTimes = alg->m_allBanksPulseTimes;
return;
}
std::string thisStartTime;
size_t thisNumPulses = 0;
file.getAttr("offset", thisStartTime);
if (!file.getInfo().dims.empty())
thisNumPulses = file.getInfo().dims[0];
file.closeData();
// Now, we look through existing ones to see if it is already loaded
// thisBankPulseTimes = NULL;
for (auto &bankPulseTime : alg->m_bankPulseTimes) {
if (bankPulseTime->equals(thisNumPulses, thisStartTime)) {
thisBankPulseTimes = bankPulseTime;
// Not found? Need to load and add it
thisBankPulseTimes = boost::make_shared<BankPulseTimes>(
boost::ref(file), m_framePeriodNumbers);
alg->m_bankPulseTimes.push_back(thisBankPulseTimes);
}
//---------------------------------------------------------------------------------------------------
/** Load the event_index field
(a list of size of # of pulses giving the index in the event list for that
pulse)
* @param file :: File handle for the NeXus file
* @param event_index :: ref to the vector
*/
void loadEventIndex(::NeXus::File &file, std::vector<uint64_t> &event_index) {
// Get the event_index (a list of size of # of pulses giving the index in
// the event list for that pulse)
file.openData("event_index");
// Must be uint64
if (file.getInfo().type == ::NeXus::UINT64)
file.getData(event_index);
else {
alg->getLogger().warning()
<< "Entry " << entry_name
<< "'s event_index field is not UINT64! It will be skipped.\n";
m_loadError = true;
}
file.closeData();
// Look for the sign that the bank is empty
if (event_index.size() == 1) {
if (event_index[0] == 0) {
// One entry, only zero. This means NO events in this bank.
m_loadError = true;
alg->getLogger().debug() << "Bank " << entry_name << " is empty.\n";
}
}
}
//---------------------------------------------------------------------------------------------------
/** Open the event_id field and validate the contents
*
* @param file :: File handle for the NeXus file
* @param start_event :: set to the index of the first event
* @param stop_event :: set to the index of the last event + 1
* @param event_index :: (a list of size of # of pulses giving the index in
*the event list for that pulse)
*/
void prepareEventId(::NeXus::File &file, size_t &start_event,
size_t &stop_event, std::vector<uint64_t> &event_index) {
// Get the list of pixel ID's
if (m_oldNexusFileNames)
file.openData("event_pixel_id");
else
file.openData("event_id");
// By default, use all available indices
start_event = 0;
::NeXus::Info id_info = file.getInfo();
// dims[0] can be negative in ISIS meaning 2^32 + dims[0]. Take that into
// account
int64_t dim0 = recalculateDataSize(id_info.dims[0]);
stop_event = static_cast<size_t>(dim0);
// Handle the time filtering by changing the start/end offsets.
for (size_t i = 0; i < thisBankPulseTimes->numPulses; i++) {
if (thisBankPulseTimes->pulseTimes[i] >= alg->filter_time_start) {
start_event = event_index[i];
break; // stop looking
if (start_event > static_cast<size_t>(dim0)) {
// If the frame indexes are bad then we can't construct the times of the
// events properly and filtering by time
// will not work on this data
alg->getLogger().warning()
<< this->entry_name
<< "'s field 'event_index' seems to be invalid (start_index > than "
"the number of events in the bank)."
<< "All events will appear in the same frame and filtering by time "
"will not be possible on this data.\n";
start_event = 0;
stop_event = static_cast<size_t>(dim0);
} else {
for (size_t i = 0; i < thisBankPulseTimes->numPulses; i++) {
if (thisBankPulseTimes->pulseTimes[i] > alg->filter_time_stop) {
stop_event = event_index[i];
break;
}
}
// We are loading part - work out the event number range
if (alg->chunk != EMPTY_INT()) {
start_event = (alg->chunk - alg->firstChunkForBank) * alg->eventsPerChunk;
// Don't change stop_event for the final chunk
if (start_event + alg->eventsPerChunk < stop_event)
stop_event = start_event + alg->eventsPerChunk;
}
// Make sure it is within range
if (stop_event > static_cast<size_t>(dim0))
stop_event = dim0;
alg->getLogger().debug() << entry_name << ": start_event " << start_event
<< " stop_event " << stop_event << "\n";
}
//---------------------------------------------------------------------------------------------------
/** Load the event_id field, which has been open
void loadEventId(::NeXus::File &file) {
// This is the data size
::NeXus::Info id_info = file.getInfo();
int64_t dim0 = recalculateDataSize(id_info.dims[0]);
// Now we allocate the required arrays
m_event_id = new uint32_t[m_loadSize[0]];
// Check that the required space is there in the file.
if (dim0 < m_loadSize[0] + m_loadStart[0]) {
alg->getLogger().warning() << "Entry " << entry_name
<< "'s event_id field is too small (" << dim0
<< ") to load the desired data size ("
<< m_loadSize[0] + m_loadStart[0] << ").\n";
if (alg->getCancel())
m_loadError = true; // To allow cancelling the algorithm
if (!m_loadError) {
// Must be uint32
if (id_info.type == ::NeXus::UINT32)
file.getSlab(m_event_id, m_loadStart, m_loadSize);
else {
alg->getLogger().warning()
<< "Entry " << entry_name
<< "'s event_id field is not UINT32! It will be skipped.\n";
m_loadError = true;
}
file.closeData();
// determine the range of pixel ids
for (auto i = 0; i < m_loadSize[0]; ++i) {
if (temp < m_min_id)
m_min_id = temp;
if (temp > m_max_id)
m_max_id = temp;
}
if (m_min_id > static_cast<uint32_t>(alg->eventid_max)) {
// All the detector IDs in the bank are higher than the highest 'known'
// (from the IDF)
// ID. Setting this will abort the loading of the bank.
m_loadError = true;
}
// fixup the minimum pixel id in the case that it's lower than the lowest
// 'known' id. We test this by checking that when we add the offset we
// would not get a negative index into the vector. Note that m_min_id is
// a uint so we have to be cautious about adding it to an int which may be
// negative.
if (static_cast<int32_t>(m_min_id) + alg->pixelID_to_wi_offset < 0) {
m_min_id = static_cast<uint32_t>(abs(alg->pixelID_to_wi_offset));
// fixup the maximum pixel id in the case that it's higher than the
// highest 'known' id
if (m_max_id > static_cast<uint32_t>(alg->eventid_max))
m_max_id = static_cast<uint32_t>(alg->eventid_max);
}
}
//---------------------------------------------------------------------------------------------------
/** Open and load the times-of-flight data
void loadTof(::NeXus::File &file) {
// Allocate the array
auto temp = new float[m_loadSize[0]];
delete[] m_event_time_of_flight;
m_event_time_of_flight = temp;
// Get the list of event_time_of_flight's
if (!m_oldNexusFileNames)
file.openData("event_time_offset");
else
file.openData("event_time_of_flight");
// Check that the required space is there in the file.
::NeXus::Info tof_info = file.getInfo();
int64_t tof_dim0 = recalculateDataSize(tof_info.dims[0]);
if (tof_dim0 < m_loadSize[0] + m_loadStart[0]) {
alg->getLogger().warning() << "Entry " << entry_name
<< "'s event_time_offset field is too small "
"to load the desired data.\n";
m_loadError = true;
}
// Check that the type is what it is supposed to be
if (tof_info.type == ::NeXus::FLOAT32)
file.getSlab(m_event_time_of_flight, m_loadStart, m_loadSize);
else {
alg->getLogger().warning()
<< "Entry " << entry_name
<< "'s event_time_offset field is not FLOAT32! It will be skipped.\n";
m_loadError = true;
}
if (!m_loadError) {
std::string units;
file.getAttr("units", units);
if (units != "microsecond") {
alg->getLogger().warning() << "Entry " << entry_name
<< "'s event_time_offset field's units are "
"not microsecond. It will be skipped.\n";
m_loadError = true;
file.closeData();
} // no error
}
//----------------------------------------------------------------------------------------------
/** Load weight of weigthed events
void loadEventWeights(::NeXus::File &file) {
try {
// First, get info about the event_weight field in this bank
file.openData("event_weight");
} catch (::NeXus::Exception &) {
// Field not found error is most likely.
m_have_weight = false;
return;
}
// OK, we've got them
m_have_weight = true;
// Allocate the array
auto temp = new float[m_loadSize[0]];
delete[] m_event_weight;
m_event_weight = temp;
::NeXus::Info weight_info = file.getInfo();
int64_t weight_dim0 = recalculateDataSize(weight_info.dims[0]);
if (weight_dim0 < m_loadSize[0] + m_loadStart[0]) {
alg->getLogger().warning()
<< "Entry " << entry_name
<< "'s event_weight field is too small to load the desired data.\n";
m_loadError = true;
}
// Check that the type is what it is supposed to be
if (weight_info.type == ::NeXus::FLOAT32)
file.getSlab(m_event_weight, m_loadStart, m_loadSize);
else {
alg->getLogger().warning()
<< "Entry " << entry_name
<< "'s event_weight field is not FLOAT32! It will be skipped.\n";
m_loadError = true;
}
if (!m_loadError) {
file.closeData();
}
}
//---------------------------------------------------------------------------------------------------
void run() override {
// The vectors we will be filling
auto event_index_ptr = new std::vector<uint64_t>();
std::vector<uint64_t> &event_index = *event_index_ptr;
// These give the limits in each file as to which events we actually load
// (when filtering by time).
m_loadStart.resize(1, 0);
m_loadSize.resize(1, 0);
m_event_id = nullptr;
m_event_time_of_flight = nullptr;
m_event_weight = nullptr;
m_loadError = false;
m_have_weight = alg->m_haveWeights;
prog->report(entry_name + ": load from disk");
// Open the file
::NeXus::File file(alg->m_filename);
try {
// Navigate into the file
file.openGroup(alg->m_top_entry_name, "NXentry");
// Open the bankN_event group
file.openGroup(entry_name, entry_type);
// Load the event_index field.
this->loadEventIndex(file, event_index);
if (!m_loadError) {
// Load and validate the pulse times
this->loadPulseTimes(file);
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
// The event_index should be the same length as the pulse times from DAS
// logs.
if (event_index.size() != thisBankPulseTimes->numPulses)
alg->getLogger().warning()
<< "Bank " << entry_name
<< " has a mismatch between the number of event_index entries "
"and the number of pulse times in event_time_zero.\n";
// Open and validate event_id field.
size_t start_event = 0;
size_t stop_event = 0;
this->prepareEventId(file, start_event, stop_event, event_index);
// These are the arguments to getSlab()
m_loadStart[0] = static_cast<int>(start_event);
m_loadSize[0] = static_cast<int>(stop_event - start_event);
if ((m_loadSize[0] > 0) && (m_loadStart[0] >= 0)) {
// Load pixel IDs
this->loadEventId(file);
if (alg->getCancel())
m_loadError = true; // To allow cancelling the algorithm
// And TOF.
if (!m_loadError) {
this->loadTof(file);
if (m_have_weight) {
this->loadEventWeights(file);
}
}
} // Size is at least 1
else {
// Found a size that was 0 or less; stop processing
m_loadError = true;
}
} // try block
catch (std::exception &e) {
alg->getLogger().error() << "Error while loading bank " << entry_name
<< ":\n";
alg->getLogger().error() << e.what() << '\n';
m_loadError = true;
} catch (...) {
alg->getLogger().error() << "Unspecified error while loading bank "
<< entry_name << '\n';
// Close up the file even if errors occured.
file.closeGroup();
file.close();
// Abort if anything failed
if (m_loadError) {
delete[] m_event_id;
delete[] m_event_time_of_flight;
if (m_have_weight) {
delete[] m_event_weight;
const auto bank_size = m_max_id - m_min_id;
const uint32_t minSpectraToLoad = static_cast<uint32_t>(alg->m_specMin);
const uint32_t maxSpectraToLoad = static_cast<uint32_t>(alg->m_specMax);
const uint32_t emptyInt = static_cast<uint32_t>(EMPTY_INT());
// check that if a range of spectra were requested that these fit within
// this bank
if (minSpectraToLoad != emptyInt && m_min_id < minSpectraToLoad) {
if (minSpectraToLoad > m_max_id) { // the minimum spectra to load is more
return;
}
// the min spectra to load is higher than the min for this bank
if (maxSpectraToLoad != emptyInt && m_max_id > maxSpectraToLoad) {
if (maxSpectraToLoad < m_min_id) {
// the maximum spectra to load is less than the minimum of this bank
return;
}
// the max spectra to load is lower than the max for this bank
}
if (m_min_id > m_max_id) {
// the min is now larger than the max, this means the entire block of
// spectra to load is outside this bank
return;
}
// schedule the job to generate the event lists
auto mid_id = m_max_id;
if (alg->splitProcessing && m_max_id > (m_min_id + (bank_size / 4)))
// only split if told to and the section to load is at least 1/4 the size
// of the whole bank
mid_id = (m_max_id + m_min_id) / 2;
// No error? Launch a new task to process that data.
size_t numEvents = m_loadSize[0];
size_t startAt = m_loadStart[0];
// convert things to shared_arrays
boost::shared_array<uint32_t> event_id_shrd(m_event_id);
boost::shared_array<float> event_time_of_flight_shrd(
m_event_time_of_flight);
boost::shared_array<float> event_weight_shrd(m_event_weight);
boost::shared_ptr<std::vector<uint64_t>> event_index_shrd(event_index_ptr);
ProcessBankData *newTask1 = new ProcessBankData(
alg, entry_name, prog, event_id_shrd, event_time_of_flight_shrd,
numEvents, startAt, event_index_shrd, thisBankPulseTimes, m_have_weight,
event_weight_shrd, m_min_id, mid_id);
scheduler->push(newTask1);
ProcessBankData *newTask2 = new ProcessBankData(
alg, entry_name, prog, event_id_shrd, event_time_of_flight_shrd,
numEvents, startAt, event_index_shrd, thisBankPulseTimes,
m_have_weight, event_weight_shrd, (mid_id + 1), m_max_id);
scheduler->push(newTask2);
}
}
//---------------------------------------------------------------------------------------------------
/**
* Interpret the value describing the number of events. If the number is
* positive return it unchanged.
* If the value is negative (can happen at ISIS) add 2^32 to it.
* @param size :: The size of events value.
*/
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
int64_t recalculateDataSize(const int64_t &size) {
if (size < 0) {
const int64_t shift = int64_t(1) << 32;
return shift + size;
}
return size;
}
private:
/// Algorithm being run
LoadEventNexus *alg;
/// NXS path to bank
std::string entry_name;
/// NXS type
std::string entry_type;
/// Progress reporting
Progress *prog;
/// ThreadScheduler running this task
ThreadScheduler *scheduler;
/// Object with the pulse times for this bank
boost::shared_ptr<BankPulseTimes> thisBankPulseTimes;
/// Did we get an error in loading
bool m_loadError;
/// Old names in the file?
bool m_oldNexusFileNames;
/// Index to load start at in the file
std::vector<int> m_loadStart;
/// How much to load in the file
std::vector<int> m_loadSize;
/// Event pixel ID data
uint32_t *m_event_id;
/// Minimum pixel ID in this data
uint32_t m_min_id;
/// Maximum pixel ID in this data
uint32_t m_max_id;
/// TOF data
float *m_event_time_of_flight;
/// Flag for simulated data
bool m_have_weight;
/// Event weights
float *m_event_weight;
/// Frame period numbers
const std::vector<int> m_framePeriodNumbers;
}; // END-DEF-CLASS LoadBankFromDiskTask
//===============================================================================================
// LoadEventNexus
//===============================================================================================
//----------------------------------------------------------------------------------------------
/** Empty default constructor
LoadEventNexus::LoadEventNexus()
: IFileLoader<Kernel::NexusDescriptor>(), m_filename(), filter_tof_min(0),
filter_tof_max(0), m_specList(), m_specMin(0), m_specMax(0),
filter_time_start(), filter_time_stop(), chunk(0), totalChunks(0),
firstChunkForBank(0), eventsPerChunk(0), m_tofMutex(), longest_tof(0),
shortest_tof(0), bad_tofs(0), discarded_events(0), precount(0),
compressTolerance(0), eventVectors(), m_eventVectorMutex(),
eventid_max(0), pixelID_to_wi_vector(), pixelID_to_wi_offset(),
m_bankPulseTimes(), m_allBanksPulseTimes(), m_top_entry_name(),
m_file(nullptr), splitProcessing(false), m_haveWeights(false),
weightedEventVectors(), m_instrument_loaded_correctly(false),
loadlogs(false), m_logs_loaded_correctly(false), event_id_is_spec(false) {
//----------------------------------------------------------------------------------------------
/** Destructor */
LoadEventNexus::~LoadEventNexus() {
if (m_file)
delete m_file;
}
//----------------------------------------------------------------------------------------------
/**
* Return the confidence with with this algorithm can load the file
* @param descriptor A descriptor for the file
* @returns An integer specifying the confidence level. 0 indicates it will not
* be used
*/
int LoadEventNexus::confidence(Kernel::NexusDescriptor &descriptor) const {
int confidence(0);
if (descriptor.classTypeExists("NXevent_data")) {
if (descriptor.pathOfTypeExists("/entry", "NXentry") ||
descriptor.pathOfTypeExists("/raw_data_1", "NXentry")) {
confidence = 80;
}
}
return confidence;
}
//----------------------------------------------------------------------------------------------
/** Initialisation method.
const std::vector<std::string> exts{"_event.nxs", ".nxs.h5", ".nxs"};
Kernel::make_unique<FileProperty>("Filename", "", FileProperty::Load,
exts),
"The name of the Event NeXus file to read, including its full or "
"relative path. "
"The file name is typically of the form INST_####_event.nxs (N.B. case "
"sensitive if running on Linux).");
make_unique<WorkspaceProperty<Workspace>>("OutputWorkspace", "",
Direction::Output),
"The name of the output EventWorkspace or WorkspaceGroup in which to "
"load the EventNexus file.");
declareProperty(
make_unique<PropertyWithValue<string>>("NXentryName", "",
Direction::Input),
"Optional: Name of the NXentry to load if it's not the default.");
declareProperty(make_unique<PropertyWithValue<double>>(
"FilterByTofMin", EMPTY_DBL(), Direction::Input),
"Optional: To exclude events that do not fall within a range "
"of times-of-flight. "
"This is the minimum accepted value in microseconds. Keep "
"blank to load all events.");
declareProperty(make_unique<PropertyWithValue<double>>(
"FilterByTofMax", EMPTY_DBL(), Direction::Input),
"Optional: To exclude events that do not fall within a range "
"of times-of-flight. "
"This is the maximum accepted value in microseconds. Keep "
"blank to load all events.");
declareProperty(make_unique<PropertyWithValue<double>>(
"FilterByTimeStart", EMPTY_DBL(), Direction::Input),
"Optional: To only include events after the provided start "
"time, in seconds (relative to the start of the run).");
declareProperty(make_unique<PropertyWithValue<double>>(
"FilterByTimeStop", EMPTY_DBL(), Direction::Input),
"Optional: To only include events before the provided stop "
"time, in seconds (relative to the start of the run).");
std::string grp1 = "Filter Events";
setPropertyGroup("FilterByTofMin", grp1);
setPropertyGroup("FilterByTofMax", grp1);
setPropertyGroup("FilterByTimeStart", grp1);
setPropertyGroup("FilterByTimeStop", grp1);
declareProperty(
make_unique<ArrayProperty<string>>("BankName", Direction::Input),
"Optional: To only include events from one bank. Any bank "
"whose name does not match the given string will have no "
"events.");
declareProperty(make_unique<PropertyWithValue<bool>>("SingleBankPixelsOnly",
true, Direction::Input),
"Optional: Only applies if you specified a single bank to "
"load with BankName. "
"Only pixels in the specified bank will be created if true; "
"all of the instrument's pixels will be created otherwise.");
setPropertySettings("SingleBankPixelsOnly", make_unique<VisibleWhenProperty>(
"BankName", IS_NOT_DEFAULT));
std::string grp2 = "Loading a Single Bank";
setPropertyGroup("BankName", grp2);
setPropertyGroup("SingleBankPixelsOnly", grp2);
declareProperty(
make_unique<PropertyWithValue<bool>>("Precount", true, Direction::Input),
"Pre-count the number of events in each pixel before allocating memory "
"This can significantly reduce memory use and memory fragmentation; it "
"may also speed up loading.");
declareProperty(make_unique<PropertyWithValue<double>>(
"CompressTolerance", -1.0, Direction::Input),
"Run CompressEvents while loading (optional, leave blank or "
"negative to not do). "
"This specified the tolerance to use (in microseconds) when "
"compressing.");
auto mustBePositive = boost::make_shared<BoundedValidator<int>>();
mustBePositive->setLower(1);
declareProperty("ChunkNumber", EMPTY_INT(), mustBePositive,
"If loading the file by sections ('chunks'), this is the "
"section number of this execution of the algorithm.");
declareProperty("TotalChunks", EMPTY_INT(), mustBePositive,
"If loading the file by sections ('chunks'), this is the "
"total number of sections.");
// TotalChunks is only meaningful if ChunkNumber is set
// Would be nice to be able to restrict ChunkNumber to be <= TotalChunks at
// validation
setPropertySettings("TotalChunks", make_unique<VisibleWhenProperty>(
"ChunkNumber", IS_NOT_DEFAULT));
std::string grp3 = "Reduce Memory Use";
setPropertyGroup("Precount", grp3);
setPropertyGroup("CompressTolerance", grp3);
setPropertyGroup("ChunkNumber", grp3);
setPropertyGroup("TotalChunks", grp3);
declareProperty(make_unique<PropertyWithValue<bool>>("LoadMonitors", false,
Direction::Input),
"Load the monitors from the file (optional, default False).");
make_unique<PropertyWithValue<bool>>("MonitorsAsEvents", false,
Direction::Input),
"If present, load the monitors as events. '''WARNING:''' WILL "
"SIGNIFICANTLY INCREASE MEMORY USAGE (optional, default False). ");
declareProperty("LoadEventMonitors", true,
"Load event monitor in NeXus file both event monitor and "
"histogram monitor found in NeXus file."
"If both of LoadEventMonitor and LoadHistoMonitor are true, "
"or both of them are false,"
"then it is in the auto mode such that any existing monitor "
"will be loaded.");
declareProperty("LoadHistoMonitors", true,
"Load histogram monitor in NeXus file both event monitor and "
"histogram monitor found in NeXus file."
"If both of LoadEventMonitor and LoadHistoMonitor are true, "
"or both of them are false,"
"then it is in the auto mode such that any existing monitor "
"will be loaded.");
declareProperty(make_unique<PropertyWithValue<double>>(
"FilterMonByTofMin", EMPTY_DBL(), Direction::Input),
"Optional: To exclude events from monitors that do not fall "
"within a range of times-of-flight. "
"This is the minimum accepted value in microseconds.");
declareProperty(make_unique<PropertyWithValue<double>>(
"FilterMonByTofMax", EMPTY_DBL(), Direction::Input),
"Optional: To exclude events from monitors that do not fall "
"within a range of times-of-flight. "
"This is the maximum accepted value in microseconds.");
declareProperty(make_unique<PropertyWithValue<double>>(
"FilterMonByTimeStart", EMPTY_DBL(), Direction::Input),
"Optional: To only include events from monitors after the "
"provided start time, in seconds (relative to the start of "
"the run).");
declareProperty(make_unique<PropertyWithValue<double>>(
"FilterMonByTimeStop", EMPTY_DBL(), Direction::Input),
"Optional: To only include events from monitors before the "
"provided stop time, in seconds (relative to the start of "
"the run).");
setPropertySettings(
"MonitorsAsEvents",
make_unique<VisibleWhenProperty>("LoadMonitors", IS_EQUAL_TO, "1"));
"LoadEventMonitors",
make_unique<VisibleWhenProperty>("LoadMonitors", IS_EQUAL_TO, "1"));
"LoadHistoMonitors",
make_unique<VisibleWhenProperty>("LoadMonitors", IS_EQUAL_TO, "1"));
auto asEventsIsOn = [] {
std::unique_ptr<IPropertySettings> prop =
make_unique<VisibleWhenProperty>("MonitorsAsEvents", IS_EQUAL_TO, "1");
return prop;
};
setPropertySettings("FilterMonByTofMin", asEventsIsOn());
setPropertySettings("FilterMonByTofMax", asEventsIsOn());
setPropertySettings("FilterMonByTimeStart", asEventsIsOn());
setPropertySettings("FilterMonByTimeStop", asEventsIsOn());
std::string grp4 = "Monitors";
setPropertyGroup("LoadMonitors", grp4);
setPropertyGroup("MonitorsAsEvents", grp4);
setPropertyGroup("LoadEventMonitors", grp4);
setPropertyGroup("LoadHistoMonitors", grp4);
setPropertyGroup("FilterMonByTofMin", grp4);
setPropertyGroup("FilterMonByTofMax", grp4);
setPropertyGroup("FilterMonByTimeStart", grp4);
setPropertyGroup("FilterMonByTimeStop", grp4);
declareProperty("SpectrumMin", EMPTY_INT(), mustBePositive,
"The number of the first spectrum to read.");
declareProperty("SpectrumMax", EMPTY_INT(), mustBePositive,
"The number of the last spectrum to read.");
declareProperty(make_unique<ArrayProperty<int32_t>>("SpectrumList"),
"A comma-separated list of individual spectra to read.");
declareProperty(
make_unique<PropertyWithValue<bool>>("MetaDataOnly", false,
Direction::Input),
"If true, only the meta data and sample logs will be loaded.");
declareProperty(
make_unique<PropertyWithValue<bool>>("LoadLogs", true, Direction::Input),
"Load the Sample/DAS logs from the file (default True).");
}
//----------------------------------------------------------------------------------------------
/** set the name of the top level NXentry m_top_entry_name
void LoadEventNexus::setTopEntryName() {
std::string nxentryProperty = getProperty("NXentryName");
if (!nxentryProperty.empty()) {
m_top_entry_name = nxentryProperty;
return;
}
typedef std::map<std::string, std::string> string_map_t;
try {
string_map_t::const_iterator it;
// assume we're at the top, otherwise: m_file->openPath("/");
string_map_t entries = m_file->getEntries();
// Choose the first entry as the default
m_top_entry_name = entries.begin()->first;
for (it = entries.begin(); it != entries.end(); ++it) {
if (((it->first == "entry") || (it->first == "raw_data_1")) &&
(it->second == "NXentry")) {
m_top_entry_name = it->first;
break;
}
} catch (const std::exception &) {
g_log.error() << "Unable to determine name of top level NXentry - assuming "
"\"entry\".\n";
m_top_entry_name = "entry";
}
}
template <typename T> void LoadEventNexus::filterDuringPause(T workspace) {
try {
if ((!ConfigService::Instance().hasProperty(
"loadeventnexus.keeppausedevents")) &&
(m_ws->run().getLogData("pause")->size() > 1)) {
g_log.notice("Filtering out events when the run was marked as paused. "
"Set the loadeventnexus.keeppausedevents configuration "
"property to override this.");
auto filter = createChildAlgorithm("FilterByLogValue");
filter->setProperty("InputWorkspace", workspace);
filter->setProperty("OutputWorkspace", workspace);
filter->setProperty("LogName", "pause");
// The log value is set to 1 when the run is paused, 0 otherwise.
filter->setProperty("MinimumValue", 0.0);
filter->setProperty("MaximumValue", 0.0);
filter->setProperty("LogBoundary", "Left");
filter->execute();
}
} catch (Exception::NotFoundError &) {
// No "pause" log, just carry on
}
}
template <>
void LoadEventNexus::filterDuringPause<EventWorkspaceCollection_sptr>(
EventWorkspaceCollection_sptr workspace) {
// We provide a function pointer to the filter method of the object
boost::function<void(MatrixWorkspace_sptr)> func = std::bind1st(
std::mem_fun(&LoadEventNexus::filterDuringPause<MatrixWorkspace_sptr>),
this);
workspace->applyFilter(func);
}
//------------------------------------------------------------------------------------------------
/** Executes the algorithm. Reading in the file and creating and populating
* the output workspace
*/
void LoadEventNexus::exec() {
// Retrieve the filename from the properties
m_filename = getPropertyValue("Filename");
precount = getProperty("Precount");
compressTolerance = getProperty("CompressTolerance");
loadlogs = getProperty("LoadLogs");
// Check to see if the monitors need to be loaded later
bool load_monitors = this->getProperty("LoadMonitors");
// this must make absolutely sure that m_file is a valid (and open)
// NeXus::File object
safeOpenFile(m_filename);
setTopEntryName();
// Initialize progress reporting.
int reports = 3;
if (load_monitors)
reports++;
Progress prog(this, 0.0, 0.3, reports);
// Load the detector events
m_ws = boost::make_shared<EventWorkspaceCollection>(); // Algorithm currently
// relies on an
// object-level workspace ptr
loadEvents(&prog, false); // Do not load monitor blocks
if (discarded_events > 0) {
g_log.information() << discarded_events
<< " events were encountered coming from pixels which "
"are not in the Instrument Definition File."
"These events were discarded.\n";
}
// If the run was paused at any point, filter out those events (SNS only, I
// think)
filterDuringPause(m_ws->getSingleHeldWorkspace());
m_ws->mutableRun().addProperty("Filename", m_filename);
this->setProperty("OutputWorkspace", m_ws->combinedWorkspace());
// Load the monitors
if (load_monitors) {
prog.report("Loading monitors");
const bool monitorsAsEvents = getProperty("MonitorsAsEvents");
if (monitorsAsEvents && !this->hasEventMonitors()) {
g_log.warning()
<< "The property MonitorsAsEvents has been enabled but "
"this file does not seem to have monitors with events.\n";