Skip to content
Snippets Groups Projects
Unverified Commit 92bb93b0 authored by Gigg, Martyn Anthony's avatar Gigg, Martyn Anthony Committed by GitHub
Browse files

Merge pull request #26775 from mantidproject/24306_nGEM_data_loader

nGEM Data Loader Created
parents ab73ff3b 2c942f2a
No related branches found
No related tags found
No related merge requests found
......@@ -92,6 +92,7 @@ set(SRC_FILES
src/LoadNexusProcessed.cpp
src/LoadNexusProcessed2.cpp
src/LoadOff.cpp
src/LoadNGEM.cpp
src/LoadPDFgetNFile.cpp
src/LoadPLN.cpp
src/LoadPSIMuonBin.cpp
......@@ -294,6 +295,7 @@ set(INC_FILES
inc/MantidDataHandling/LoadNexusProcessed.h
inc/MantidDataHandling/LoadNexusProcessed2.h
inc/MantidDataHandling/LoadOff.h
inc/MantidDataHandling/LoadNGEM.h
inc/MantidDataHandling/LoadPDFgetNFile.h
inc/MantidDataHandling/LoadPLN.h
inc/MantidDataHandling/LoadPSIMuonBin.h
......@@ -484,6 +486,7 @@ set(TEST_FILES
LoadNexusProcessed2Test.h
LoadNexusProcessedTest.h
LoadNexusTest.h
LoadNGEMTest.h
LoadPDFgetNFileTest.h
LoadPLNTest.h
LoadPSIMuonBinTest.h
......@@ -628,6 +631,8 @@ target_link_libraries(DataHandling
${TCMALLOC_LIBRARIES_LINKTIME}
${MANTIDLIBS}
Nexus
HistogramData
DataObjects
${NEXUS_LIBRARIES}
${HDF5_LIBRARIES}
${HDF5_HL_LIBRARIES}
......
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2019 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#ifndef MANTID_DATAHANDLING_LOADNGEM_H_
#define MANTID_DATAHANDLING_LOADNGEM_H_
#include "MantidAPI/IFileLoader.h"
#include "MantidDataObjects/EventWorkspace.h"
namespace Mantid {
namespace DataHandling {
static constexpr uint64_t CONTIN_ID_VALUE = 0x4F;
/// Generic event to separate bits.
struct GenericEvent {
uint64_t t0id : 24; // T0 ID
uint64_t reserved2 : 32; // Reserved for non-generics
uint64_t contin : 8; // 0x4F Continuation Code
uint64_t reserved1 : 56; // Reserved for non-generics
uint64_t id : 8; // 0x4E Event ID
};
/// Indicate time 0, the start of a new frame.
struct T0FrameEvent {
uint64_t t0id : 24; // T0 ID
uint64_t eventCount : 32; // Event Count
uint64_t contin : 8; // 0x4F Continuation Code
uint64_t totalLoss : 24; // Total loss count
uint64_t eventLoss : 20; // Event loss count
uint64_t frameLoss : 12; // Frame loss count
uint64_t id : 8; // 0x4E Event ID
static constexpr int T0_IDENTIFIER = 0x4E;
bool check() const {
return id == T0_IDENTIFIER && contin == CONTIN_ID_VALUE;
}
};
/// A detected neutron.
struct CoincidenceEvent {
uint64_t t0id : 24; // T0 ID
uint64_t clusterTimeY : 10; // Integrated time of the cluster on the Y side
// (5ns pixel)
uint64_t timeDiffY : 6; // Time lag from first to last detection on Y (5ns)
uint64_t clusterTimeX : 10; // Integrated time of the cluster on the X side
// (5ns pixel)
uint64_t timeDiffX : 6; // Time lag from first to last detection on X (5ns)
uint64_t contin : 8; // 0x4F Continuation Code
uint64_t lastY : 7; // Y position of pixel detected last
uint64_t firstY : 7; // Y position of pixel detected first
uint64_t lastX : 7; // X position of pixel detected last
uint64_t firstX : 7; // X position of pixel detected first
uint64_t timeOfFlight : 28; // Difference between T0 and detection (1ns)
uint64_t id : 8; // 0x47 Event ID.
uint64_t avgX() const { return (firstX + lastX) / 2; }
uint64_t avgY() const { return (firstY + lastY) / 2; }
static constexpr int COINCIDENCE_IDENTIFIER = 0x47;
bool check() {
return id == COINCIDENCE_IDENTIFIER && contin == CONTIN_ID_VALUE;
}
uint64_t getPixel() const {
return avgX() + (avgY() << 7); // Increase Y significance by 7 bits to
// account for 128x128 grid.
}
};
/// Holds the 128 bit words from the detector.
struct DetectorWord {
uint64_t words[2]; // Array holding the word from the detector split in two.
};
/// Is able to hold all versions of the data words in the same memory location.
union EventUnion {
GenericEvent generic;
T0FrameEvent tZero;
CoincidenceEvent coincidence;
DetectorWord splitWord;
};
class DLLExport LoadNGEM : public API::IFileLoader<Kernel::FileDescriptor> {
public:
/// Algorithm's name for identification.
const std::string name() const override { return "LoadNGEM"; }
/// The purpose of the algorithm.
const std::string summary() const override {
return "Load a file or range of files created by the nGEM detector into a "
"workspace.";
};
/// Algorithm's Version for identification.
int version() const override { return 1; }
/// Algorithm's category for identification.
const std::string category() const override { return "DataHandling\\NGEM"; };
/// Should the loader load multiple files into one workspace.
bool loadMutipleAsOne() override { return true; }
/// The confidence that an algorithm is able to load the file.
int confidence(Kernel::FileDescriptor &descriptor) const override;
private:
/// Initialise the algorithm.
void init() override;
/// Execute the algorithm.
void exec() override;
/// Load a file into the event lists.
void loadSingleFile(const std::vector<std::string> &filePath,
int &eventCountInFrame, int &maxToF, int &minToF,
int &rawFrames, int &goodFrames, const int &minEventsReq,
const int &maxEventsReq, MantidVec &frameEventCounts,
std::vector<DataObjects::EventList> &events,
std::vector<DataObjects::EventList> &eventsInFrame,
const size_t &totalFilePaths, int &fileCount);
/// Add some text information to the sample logs.
void addToSampleLog(const std::string &logName, const std::string &logText,
DataObjects::EventWorkspace_sptr &ws);
/// Add some number information to the sample logs.
void addToSampleLog(const std::string &logName, const int &logNumber,
DataObjects::EventWorkspace_sptr &ws);
/// Check that a file to be loaded is in 128 bit words.
size_t verifyFileSize(std::ifstream &file);
/// Reports progress and checks cancel flag.
bool reportProgressAndCheckCancel(size_t &numProcessedEvents,
int &eventCountInFrame,
const size_t &totalNumEvents,
const size_t &totalFilePaths,
const int &fileCount);
/// Create a workspace to store the number of counts per frame.
void createCountWorkspace(const std::vector<double> &frameEventCounts);
/// Load the instrument and attach to the data workspace.
void loadInstrument(DataObjects::EventWorkspace_sptr &dataWorkspace);
/// Validate the imputs into the algorithm, overrides.
std::map<std::string, std::string> validateInputs() override;
};
} // namespace DataHandling
} // namespace Mantid
#endif // MANTID_DATAHANDLING_LOADNGEM_H_
......@@ -291,6 +291,7 @@ void Load::init() {
exts.emplace_back(".sqw");
exts.emplace_back(".fits");
exts.emplace_back(".bin");
exts.emplace_back(".edb");
declareProperty(
std::make_unique<MultipleFileProperty>("Filename", exts),
......
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright &copy; 2019 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidDataHandling/LoadNGEM.h"
#include "MantidAPI/Axis.h"
#include "MantidAPI/MultipleFileProperty.h"
#include "MantidAPI/RegisterFileLoader.h"
#include "MantidDataObjects/Workspace2D.h"
#include "MantidDataObjects/WorkspaceCreation.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/MultiThreaded.h"
#include "MantidKernel/OptionalBool.h"
#include "MantidKernel/Unit.h"
#include "MantidKernel/UnitFactory.h"
#include <boost/algorithm/string.hpp>
#include <fstream>
namespace Mantid {
namespace DataHandling {
DECLARE_FILELOADER_ALGORITHM(LoadNGEM)
// Constants and helper functions.
namespace {
constexpr int NUM_OF_SPECTRA = 16384;
/**
* @brief Byte swap a 64 bit word as the nGEM detector outputs in big endian
* format. So must be swapped to have correct values on x86 and x64
* architectures.
*
* @param word The 64 bit word to swap.
* @return uint64_t The swapped word.
*/
uint64_t swapUint64(uint64_t word) {
word = ((word << 8) & 0xFF00FF00FF00FF00ULL) |
((word >> 8) & 0x00FF00FF00FF00FFULL);
word = ((word << 16) & 0xFFFF0000FFFF0000ULL) |
((word >> 16) & 0x0000FFFF0000FFFFULL);
return (word << 32) | (word >> 32);
}
/**
* @brief Correct a big endian event to be compatible with x64 and x86
* architecture.
*
* @param bigEndian The big endian formatted event.
* @param smallEndian The resulting small endian formatted event.
*/
void correctForBigEndian(EventUnion *&bigEndian, EventUnion &smallEndian) {
smallEndian.splitWord.words[0] = swapUint64(bigEndian->splitWord.words[1]);
smallEndian.splitWord.words[1] = swapUint64(bigEndian->splitWord.words[0]);
}
/**
* @brief Add a frame to the main set of events.
*
* @param rawFrames The number of T0 Events detected so far.
* @param goodFrames The number of good frames detected so far.
* @param eventCountInFrame The number of events in the current frame.
* @param minEventsReq The number of events required to be a good frame.
* @param maxEventsReq The max events allowed to be a good frame.
* @param frameEventCounts A vector of the number of events in each good frame.
* @param events The main set of events for the data so far.
* @param eventsInFrame The set of events for the current frame.
*/
void addFrameToOutputWorkspace(
int &rawFrames, int &goodFrames, const int &eventCountInFrame,
const int &minEventsReq, const int &maxEventsReq,
MantidVec &frameEventCounts, std::vector<DataObjects::EventList> &events,
std::vector<DataObjects::EventList> &eventsInFrame) {
++rawFrames;
if (eventCountInFrame >= minEventsReq && eventCountInFrame <= maxEventsReq) {
// Add number of event counts to workspace.
frameEventCounts.emplace_back(eventCountInFrame);
++goodFrames;
PARALLEL_FOR_NO_WSP_CHECK()
// Add events that match parameters to workspace
for (auto i = 0; i < NUM_OF_SPECTRA; ++i) {
if (eventsInFrame[i].getNumberEvents() > 0) {
events[i] += eventsInFrame[i];
eventsInFrame[i].clear();
}
}
}
}
/**
* @brief Creates an event workspace and fills it with the data.
*
* @param maxToF The largest ToF seen so far.
* @param binWidth The width of each bin.
* @param events The main events data.
* @param dataWorkspace The workspace to add the data to.
*/
void createEventWorkspace(const int &maxToF, const double &binWidth,
std::vector<DataObjects::EventList> &events,
DataObjects::EventWorkspace_sptr &dataWorkspace) {
std::vector<double> xAxis;
// Round up number of bins needed and reserve the space in the vector.
xAxis.reserve(int(std::ceil(maxToF / binWidth)));
for (auto i = 0; i < (maxToF / binWidth); i++) {
xAxis.push_back(i * binWidth);
}
dataWorkspace = DataObjects::create<DataObjects::EventWorkspace>(
NUM_OF_SPECTRA, HistogramData::Histogram(HistogramData::BinEdges(xAxis)));
PARALLEL_FOR_NO_WSP_CHECK()
for (auto i = 0u; i < events.size(); ++i) {
dataWorkspace->getSpectrum(i) = events[i];
dataWorkspace->getSpectrum(i).setSpectrumNo(i + 1);
dataWorkspace->getSpectrum(i).setDetectorID(i + 1);
}
dataWorkspace->setAllX(HistogramData::BinEdges{xAxis});
dataWorkspace->getAxis(0)->unit() =
Kernel::UnitFactory::Instance().create("TOF");
dataWorkspace->setYUnit("Counts");
}
} // namespace
/**
* @brief The confidence that a file can be loaded.
*
* @param descriptor A description of the file.
* @return int The level of certainty that the file can be loaded with this
* loader.
*/
int LoadNGEM::confidence(Kernel::FileDescriptor &descriptor) const {
if (descriptor.extension() == ".edb") {
return 95;
} else {
return 0;
}
}
/**
* @brief Initialisation of the algorithm, setting up the properties.
*/
void LoadNGEM::init() {
// Filename property.
const std::vector<std::string> extentions{".edb"};
declareProperty(
std::make_unique<API::MultipleFileProperty>("Filename", extentions),
"The name of the nGEM file to load. Selecting multiple files will "
"combine them into one workspace.");
// Output workspace
declareProperty(std::make_unique<API::WorkspaceProperty<API::Workspace>>(
"OutputWorkspace", "", Kernel::Direction::Output),
"The output workspace");
auto mustBePositive = boost::make_shared<Kernel::BoundedValidator<int>>();
mustBePositive->setLower(0);
auto mustBePositiveDbl =
boost::make_shared<Kernel::BoundedValidator<double>>();
mustBePositiveDbl->setLower(0.0);
// Bin Width
declareProperty("BinWidth", 10.0, mustBePositiveDbl,
"The width of the time bins in the output.");
declareProperty("MinEventsPerFrame", 0, mustBePositive,
"The minimum number of events required in a frame before a "
"it is considered 'good'.");
declareProperty("MaxEventsPerFrame", EMPTY_INT(), mustBePositive,
"The maximum number of events allowed in a frame to be "
"considered 'good'.");
declareProperty(
std::make_unique<Kernel::PropertyWithValue<bool>>(
"GenerateEventsPerFrame", false, Kernel::Direction::Input),
"Generate a workspace to show the number of events captured by each "
"frame. (optional, default False).");
}
/**
* @brief Execute the algorithm.
*/
void LoadNGEM::exec() {
progress(0);
std::vector<std::vector<std::string>> filePaths = getProperty("Filename");
const int minEventsReq(getProperty("MinEventsPerFrame"));
const int maxEventsReq(getProperty("MaxEventsPerFrame"));
int maxToF = -1;
int minToF = 2147483647;
const double binWidth(getProperty("BinWidth"));
int rawFrames = 0;
int goodFrames = 0;
std::vector<double> frameEventCounts;
int eventCountInFrame = 0;
std::vector<DataObjects::EventList> events, eventsInFrame;
events.resize(NUM_OF_SPECTRA);
eventsInFrame.resize(NUM_OF_SPECTRA);
progress(0.04);
size_t totalFilePaths(filePaths.size());
int counter(1);
for (const auto &filePath : filePaths) {
loadSingleFile(filePath, eventCountInFrame, maxToF, minToF, rawFrames,
goodFrames, minEventsReq, maxEventsReq, frameEventCounts,
events, eventsInFrame, totalFilePaths, counter);
}
// Add the final frame of events (as they are not followed by a T0 event)
addFrameToOutputWorkspace(rawFrames, goodFrames, eventCountInFrame,
minEventsReq, maxEventsReq, frameEventCounts,
events, eventsInFrame);
progress(0.90);
DataObjects::EventWorkspace_sptr dataWorkspace;
createEventWorkspace(maxToF, binWidth, events, dataWorkspace);
addToSampleLog("raw_frames", rawFrames, dataWorkspace);
addToSampleLog("good_frames", goodFrames, dataWorkspace);
addToSampleLog("max_ToF", maxToF, dataWorkspace);
addToSampleLog("min_ToF", minToF, dataWorkspace);
loadInstrument(dataWorkspace);
setProperty("OutputWorkspace", dataWorkspace);
if (this->getProperty("GenerateEventsPerFrame")) {
createCountWorkspace(frameEventCounts);
}
progress(1.00);
}
/**
* @brief Load a single file into the event lists.
*
* @param filePath The path to the file.
* @param eventCountInFrame The number of events in the current frame.
* @param maxToF The highest detected ToF
* @param minToF The lowest detected ToF
* @param rawFrames The number of T0 Events detected so far.
* @param goodFrames The number of good frames detected so far.
* @param minEventsReq The number of events required to be a good frame.
* @param maxEventsReq The max events allowed to be a good frame.
* @param frameEventCounts A vector of the number of events in each good frame.
* @param events The main set of events for the data so far.
* @param eventsInFrame The set of events for the current frame.
* @param totalFilePaths The total number of file paths.
* @param fileCount The number of file paths processed.
*/
void LoadNGEM::loadSingleFile(
const std::vector<std::string> &filePath, int &eventCountInFrame,
int &maxToF, int &minToF, int &rawFrames, int &goodFrames,
const int &minEventsReq, const int &maxEventsReq,
MantidVec &frameEventCounts, std::vector<DataObjects::EventList> &events,
std::vector<DataObjects::EventList> &eventsInFrame,
const size_t &totalFilePaths, int &fileCount) {
// Create file reader
if (filePath.size() > 1) {
throw std::runtime_error("Invalid filename parameter.");
}
std::ifstream file(filePath[0].c_str(), std::ifstream::binary);
if (!file.is_open()) {
throw std::runtime_error("File could not be found.");
}
std::array<char, 16> buffer;
const size_t totalNumEvents = verifyFileSize(file) / 16;
size_t numProcessedEvents = 0;
while (!file.eof()) {
// Load an event into the variable.
file.read(buffer.data(), 16);
auto eventBigEndian = reinterpret_cast<EventUnion *>(buffer.data());
// Correct for the big endian format.
EventUnion event;
correctForBigEndian(eventBigEndian, event);
if (event.coincidence.check()) { // Check for coincidence event.
++eventCountInFrame;
uint64_t pixel = event.coincidence.getPixel();
int tof =
event.coincidence.timeOfFlight / 1000; // Convert to microseconds (us)
if (tof > maxToF) {
maxToF = tof;
} else if (tof < minToF) {
minToF = tof;
}
eventsInFrame[pixel].addEventQuickly(Types::Event::TofEvent(tof));
} else if (event.tZero.check()) { // Check for T0 event.
addFrameToOutputWorkspace(rawFrames, goodFrames, eventCountInFrame,
minEventsReq, maxEventsReq, frameEventCounts,
events, eventsInFrame);
if (reportProgressAndCheckCancel(numProcessedEvents, eventCountInFrame,
totalNumEvents, totalFilePaths,
fileCount)) {
return;
}
} else { // Catch all other events and notify.
g_log.warning() << "Unexpected event type loaded.\n";
}
}
g_log.information() << "Finished loading a file.\n";
++fileCount;
}
/**
* @brief Add a string value to the sample logs.
*
* @param logName The name of the log.
* @param logText The content of the log.
* @param ws The workspace to add the log to.
*/
void LoadNGEM::addToSampleLog(const std::string &logName,
const std::string &logText,
DataObjects::EventWorkspace_sptr &ws) {
API::Algorithm_sptr sampLogAlg = createChildAlgorithm("AddSampleLog");
sampLogAlg->setProperty("Workspace", ws);
sampLogAlg->setProperty("LogType", "String");
sampLogAlg->setProperty("LogName", logName);
sampLogAlg->setProperty("LogText", logText);
sampLogAlg->executeAsChildAlg();
}
/**
* @brief Add a number value to the sample logs.
*
* @param logName Name of the log.
* @param logNumber The value of the log.
* @param ws The workspace to add the log to.
*/
void LoadNGEM::addToSampleLog(const std::string &logName, const int &logNumber,
DataObjects::EventWorkspace_sptr &ws) {
API::Algorithm_sptr sampLogAlg = createChildAlgorithm("AddSampleLog");
sampLogAlg->setProperty("Workspace", ws);
sampLogAlg->setProperty("LogType", "Number");
sampLogAlg->setProperty("LogName", logName);
sampLogAlg->setProperty("LogText", std::to_string(logNumber));
sampLogAlg->executeAsChildAlg();
}
/**
* @brief Ensure that the file fits into 16, as the detector spits out 128 bit
* words (16 bytes)
*
* @param file The file to check.
* @return size_t The size of the file.
*/
size_t LoadNGEM::verifyFileSize(std::ifstream &file) {
// Check that the file fits into 16 byte sections.
file.seekg(0, file.end);
size_t size = file.tellg();
if (size % 16 != 0) {
g_log.warning()
<< "Invalid file size. File is size is " << size
<< " bytes which is not a multiple of 16. There may be some bytes "
"missing from the data. \n";
}
file.seekg(0);
return size;
}
/**
* @brief Report the progress of the loader through the current file.
*
* @param numProcessedEvents The number of events processed so far.
* @param eventCountInFrame The number of events in the current frame.
* @param totalNumEvents The total number of events in the file.
* @param totalFilePaths The total number of file paths to process.
* @param fileCount The number of files processed.
* @return true If user has cancelled the load.
* @return false If the user has not cancelled.
*/
bool LoadNGEM::reportProgressAndCheckCancel(size_t &numProcessedEvents,
int &eventCountInFrame,
const size_t &totalNumEvents,
const size_t &totalFilePaths,
const int &fileCount) {
numProcessedEvents += eventCountInFrame;
std::string message(std::to_string(fileCount) + "/" +
std::to_string(totalFilePaths));
progress(double(numProcessedEvents) / double(totalNumEvents) / 1.11111,
message);
eventCountInFrame = 0;
// Check for cancel flag.
return this->getCancel();
}
/**
* @brief Create a count workspace to allow for the selection of "good"
* frames.
*
* @param frameEventCounts A Vector of the number of events per frame.
*/
void LoadNGEM::createCountWorkspace(
const std::vector<double> &frameEventCounts) {
std::vector<double> xAxisCounts(frameEventCounts.size() + 1);
std::generate(xAxisCounts.begin(),
xAxisCounts.end(), [n = 0.0]() mutable { return ++n; });
DataObjects::Workspace2D_sptr countsWorkspace =
DataObjects::create<DataObjects::Workspace2D>(
1, HistogramData::Histogram(HistogramData::BinEdges(xAxisCounts)));
countsWorkspace->mutableY(0) = frameEventCounts;
std::string countsWorkspaceName(this->getProperty("OutputWorkspace"));
countsWorkspaceName.append("_event_counts");
countsWorkspace->setYUnit("Counts");
boost::shared_ptr<Kernel::Units::Label> XLabel =
boost::dynamic_pointer_cast<Kernel::Units::Label>(
Kernel::UnitFactory::Instance().create("Label"));
XLabel->setLabel("Frame");
countsWorkspace->getAxis(0)->unit() = XLabel;
this->declareProperty(
std::make_unique<API::WorkspaceProperty<API::Workspace>>(
"CountsWorkspace", countsWorkspaceName, Kernel::Direction::Output),
"Counts of events per frame.");
progress(1.00);
this->setProperty("CountsWorkspace", countsWorkspace);
}
/**
* @brief Load the nGEM instrument into a workspace.
*
* @param dataWorkspace The workspace to load into.
*/
void LoadNGEM::loadInstrument(DataObjects::EventWorkspace_sptr &dataWorkspace) {
auto loadInstrument = this->createChildAlgorithm("LoadInstrument");
loadInstrument->setPropertyValue("InstrumentName", "NGEM");
loadInstrument->setProperty<API::MatrixWorkspace_sptr>("Workspace",
dataWorkspace);
loadInstrument->setProperty("RewriteSpectraMap", Kernel::OptionalBool(false));
loadInstrument->execute();
}
/**
* @brief Validate inputs into the loader GUI.
*
* @return std::map<std::string, std::string> A map that is empty if there are
* no issues, and contains a key of the input field and a value of the error
* message otherwise.
*/
std::map<std::string, std::string> LoadNGEM::validateInputs() {
std::map<std::string, std::string> results;
int MinEventsPerFrame = getProperty("MinEventsPerFrame");
int MaxEventsPerFrame = getProperty("MaxEventsPerFrame");
if (MaxEventsPerFrame < MinEventsPerFrame) {
results["MaxEventsPerFrame"] =
"MaxEventsPerFrame is less than MinEvents per frame";
}
return results;
}
} // namespace DataHandling
} // namespace Mantid
\ No newline at end of file
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#ifndef LOADNGEMTEST_H_
#define LOADNGEMTEST_H_
#include <MantidAPI/AnalysisDataService.h>
#include <MantidAPI/FileFinder.h>
#include <MantidDataHandling/LoadNGEM.h>
#include <MantidDataObjects/EventWorkspace.h>
#include <cxxtest/TestSuite.h>
using namespace Mantid;
using namespace Mantid::DataHandling;
using namespace Mantid::API;
class LoadNGEMTest : public CxxTest::TestSuite {
public:
void test_init() {
LoadNGEM alg;
TS_ASSERT_THROWS_NOTHING(alg.initialize());
TS_ASSERT(alg.isInitialized());
TS_ASSERT_THROWS_NOTHING(alg.setProperty(
"Filename", getTestFilePath("GEM000005_00_000_short.edb")));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("OutputWorkspace", "ws"));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("BinWidth", 10.0));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("MinEventsPerFrame", 10));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("MaxEventsPerFrame", 20));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("GenerateEventsPerFrame", false));
}
void test_exec_loads_data_to_ads() {
LoadNGEM alg;
alg.initialize();
TS_ASSERT(alg.isInitialized());
TS_ASSERT_THROWS_NOTHING(alg.setProperty(
"Filename", getTestFilePath("GEM000005_00_000_short.edb")));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("OutputWorkspace", "ws"));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("GenerateEventsPerFrame", false));
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT_THROWS_NOTHING(AnalysisDataService::Instance().remove("ws"));
}
void test_exec_loads_event_counts_workspace() {
LoadNGEM alg;
alg.initialize();
TS_ASSERT(alg.isInitialized());
TS_ASSERT_THROWS_NOTHING(alg.setProperty(
"Filename", getTestFilePath("GEM000005_00_000_short.edb")));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("OutputWorkspace", "ws"));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("GenerateEventsPerFrame", true));
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT_THROWS_NOTHING(AnalysisDataService::Instance().remove("ws"));
TS_ASSERT_THROWS_NOTHING(
AnalysisDataService::Instance().remove("ws_event_counts"));
}
void test_exec_not_load_event_counts_workspace() {
LoadNGEM alg;
alg.initialize();
TS_ASSERT(alg.isInitialized());
TS_ASSERT_THROWS_NOTHING(alg.setProperty(
"Filename", getTestFilePath("GEM000005_00_000_short.edb")));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("OutputWorkspace", "ws"));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("GenerateEventsPerFrame", false));
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT_THROWS_NOTHING(AnalysisDataService::Instance().remove("ws"));
TS_ASSERT_THROWS_ANYTHING(
AnalysisDataService::Instance().retrieve("ws_event_counts"));
}
void test_init_fails_on_bad_binWidth() {
LoadNGEM alg;
alg.initialize();
TS_ASSERT(alg.isInitialized());
TS_ASSERT_THROWS_ANYTHING(alg.setProperty("BinWidth", -10.0));
}
void test_init_fails_on_bad_MaxEventsPerFrame() {
LoadNGEM alg;
alg.initialize();
TS_ASSERT(alg.isInitialized());
TS_ASSERT_THROWS_ANYTHING(alg.setProperty("MaxEventsPerFrame", -10));
}
void test_init_fails_on_bad_MinEventsPerFrame() {
LoadNGEM alg;
alg.initialize();
TS_ASSERT(alg.isInitialized());
TS_ASSERT_THROWS_ANYTHING(alg.setProperty("MinEventsPerFrame", -10));
}
void test_init_fails_on_MaxEvents_is_less_than_MinEvents() {
LoadNGEM alg;
alg.initialize();
TS_ASSERT(alg.isInitialized());
TS_ASSERT_THROWS_NOTHING(alg.setProperty(
"Filename", getTestFilePath("GEM000005_00_000_short.edb")));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("OutputWorkspace", "ws"));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("MinEventsPerFrame", 20));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("MaxEventsPerFrame", 10));
TS_ASSERT_THROWS(alg.execute(), std::runtime_error);
}
void test_MinEventsPerFrame_removes_low_values() {
LoadNGEM alg;
alg.initialize();
TS_ASSERT(alg.isInitialized());
TS_ASSERT_THROWS_NOTHING(alg.setProperty(
"Filename", getTestFilePath("GEM000005_00_000_short.edb")));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("OutputWorkspace", "ws"));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("MinEventsPerFrame", 0));
TS_ASSERT_THROWS_NOTHING(alg.execute());
DataObjects::EventWorkspace_sptr ws;
ws =
AnalysisDataService::Instance().retrieveWS<DataObjects::EventWorkspace>(
"ws");
TS_ASSERT(ws);
const size_t rawNumEvents = ws->getNumberEvents();
TS_ASSERT_THROWS_NOTHING(
alg.setProperty("MinEventsPerFrame", Mantid::EMPTY_INT()));
TS_ASSERT_THROWS_NOTHING(alg.execute());
ws =
AnalysisDataService::Instance().retrieveWS<DataObjects::EventWorkspace>(
"ws");
TS_ASSERT(rawNumEvents > ws->getNumberEvents());
}
void test_MaxEventsPerFrame_removes_high_values() {
LoadNGEM alg;
alg.initialize();
TS_ASSERT(alg.isInitialized());
TS_ASSERT_THROWS_NOTHING(alg.setProperty(
"Filename", getTestFilePath("GEM000005_00_000_short.edb")));
TS_ASSERT_THROWS_NOTHING(alg.setProperty("OutputWorkspace", "ws"));
TS_ASSERT_THROWS_NOTHING(alg.execute());
DataObjects::EventWorkspace_sptr ws;
ws =
AnalysisDataService::Instance().retrieveWS<DataObjects::EventWorkspace>(
"ws");
TS_ASSERT(ws);
const size_t rawNumEvents = ws->getNumberEvents();
TS_ASSERT_THROWS_NOTHING(alg.setProperty("MaxEventsPerFrame", 0));
TS_ASSERT_THROWS_NOTHING(alg.execute());
ws =
AnalysisDataService::Instance().retrieveWS<DataObjects::EventWorkspace>(
"ws");
TS_ASSERT(rawNumEvents > ws->getNumberEvents());
}
private:
// Helper function to get the path of a specified file.
std::string getTestFilePath(const std::string &filename) {
const std::string filepath =
Mantid::API::FileFinder::Instance().getFullPath(filename);
TS_ASSERT_DIFFERS(filepath, "");
return filepath;
}
};
#endif // LOADNGEMTEST_H_
\ No newline at end of file
1010588fa5f3933b43ace958c201f93f
.. algorithm::
.. summary::
.. relatedalgorithms::
.. properties::
Description
-----------
The LoadNGEM algorithm will read .edb files generated by the nGEM
detector.
The detector produces files that are up to 1GB in size, so if the
collection produces more than 1GB of data, it will create a new file
to write the rest of the data to. When all of the files are selected
they are loaded into a single workspace containing all of the events.
The loading process allows the option to include a count of the number
of events in each frame (begun by a T0 event), which is loaded as a
plottable workspace. This can then be used to determine what is considered
a "good frame" so that only good frames are included in the final data.
Child Algorithms used
#####################
The ChildAlgorithms used by LoadNGEM are:
* :ref:`algm-AddSampleLog-v1` - Adds data to the Sample Log of the workspace
* :ref:`algm-LoadInstrument-v1` - Loads the instrument associated with the data
.. categories::
.. sourcelink::
......@@ -14,6 +14,7 @@ Concepts
Algorithms
----------
* :ref:`LoadNGEM <algm-LoadNGEM>` added as a loader for the .edb files generated by the nGEM detector used for diagnostics. Generates an event workspace.
* :ref:`MaskAngle <algm-MaskAngle>` has an additional option of ``Angle='InPlane'``
* :ref:`FitIncidentSpectrum <algm-FitIncidentSpectrum>` will fit a curve to an incident spectrum returning the curve and it's first derivative.
* Whitespace is now ignored anywhere in the string when setting the Filename parameter in :ref:`Load <algm-Load>`.
......
......@@ -157,6 +157,14 @@
</livedata>
</instrument>
<instrument name="NGEM">
<technique>Neutron Diffraction</technique>
<zeropadding size="8"/>
<livedata>
<connection name="histo" address="NDXGEM:6789" listener="ISISHistoDataListener" />
</livedata>
</instrument>
<instrument name="OSIRIS" shortname="OSI" beamline="N6">
<zeropadding size="8" startRunNumber="99780" prefix="OSIRIS"/>
<technique>Neutron Diffraction</technique>
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- For help on the notation used to specify an Instrument Definition File
see http://www.mantidproject.org/IDF -->
<instrument xmlns="http://www.mantidproject.org/IDF/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.mantidproject.org/IDF/1.0 http://schema.mantidproject.org/IDF/1.0/IDFSchema.xsd"
name="NGEM" valid-from ="2019-05-30 00:00:00"
valid-to ="2100-10-17 09:29:59"
last-modified="2019-05-30 11:40:00">
<defaults>
<length unit="meter"/>
<angle unit="degree"/>
<reference-frame>
<!-- The z-axis is set parallel to and in the direction of the beam. the
y-axis points up and the coordinate system is right handed. -->
<along-beam axis="z"/>
<pointing-up axis="y"/>
<handedness val="right"/>
</reference-frame>
<default-view axis-view="z-"/>
</defaults>
<!-- LIST OF PHYSICAL COMPONENTS (which the instrument consists of) -->
<!-- source and sample-position components -->
<component type="source">
<location z="-40.0"> <facing val="none"/> </location>
</component>
<type name="source" is="Source" />
<component type="some-sample-holder">
<location><facing val="none"/> </location>
</component>
<type name="some-sample-holder" is="SamplePos">
<properties />
<sphere id="some-shape">
<centre x="0.0" y="0.0" z="0.0" />
<radius val="0.03" />
</sphere>
<algebra val="some-shape" />
</type>
<component type="main-detector-bank" idstart="1" idfillbyfirst="x" idstepbyrow="128">
<location z="1.5" name="main-detector-bank"/>
</component>
<type name="main-detector-bank" is="rectangular_detector" type="main-detector-pixel"
xpixels="128" xstart="-0.05" xstep="+0.000787402"
ypixels="128" ystart="-0.05" ystep="+0.000787402" >
</type>
<type name="main-detector-pixel" is="detector">
<cuboid id="shape">
<left-front-bottom-point x="0.000393701" y="-0.000393701" z="0.0" />
<left-front-top-point x="0.000393701" y="-0.000393701" z="0.00000005" />
<left-back-bottom-point x="-0.000393701" y="-0.000393701" z="0.0" />
<right-front-bottom-point x="0.000393701" y="0.000393701" z="0.0" />
</cuboid>
<algebra val="shape" />
</type>
</instrument>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment