Mantid
Loading...
Searching...
No Matches
LoadNGEM.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2019 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8#include "MantidAPI/Axis.h"
11#include "MantidAPI/Run.h"
17#include "MantidKernel/Unit.h"
19
20#include <boost/algorithm/string.hpp>
21#include <fstream>
22
23namespace Mantid::DataHandling {
24
26
27// Constants and helper functions.
28namespace {
29constexpr int NUM_OF_SPECTRA = 16384;
30
39uint64_t swapUint64(uint64_t word) {
40 word = ((word << 8) & 0xFF00FF00FF00FF00ULL) | ((word >> 8) & 0x00FF00FF00FF00FFULL);
41 word = ((word << 16) & 0xFFFF0000FFFF0000ULL) | ((word >> 16) & 0x0000FFFF0000FFFFULL);
42 return (word << 32) | (word >> 32);
43}
44
52void correctForBigEndian(const EventUnion &bigEndian, EventUnion &smallEndian) {
53 smallEndian.splitWord.words[0] = swapUint64(bigEndian.splitWord.words[1]);
54 smallEndian.splitWord.words[1] = swapUint64(bigEndian.splitWord.words[0]);
55}
56
69void addFrameToOutputWorkspace(int &rawFrames, int &goodFrames, const int &eventCountInFrame, const int &minEventsReq,
70 const int &maxEventsReq, MantidVec &frameEventCounts,
71 std::vector<DataObjects::EventList> &events,
72 std::vector<DataObjects::EventList> &eventsInFrame) {
73 ++rawFrames;
74 if (eventCountInFrame >= minEventsReq && eventCountInFrame <= maxEventsReq) {
75 // Add number of event counts to workspace.
76 frameEventCounts.emplace_back(eventCountInFrame);
77 ++goodFrames;
78
80 // Add events that match parameters to workspace
81 for (auto i = 0; i < NUM_OF_SPECTRA; ++i) {
82 if (eventsInFrame[i].getNumberEvents() > 0) {
83 events[i] += eventsInFrame[i];
84 eventsInFrame[i].clear();
85 }
86 }
87 }
88}
89
98void createEventWorkspace(const double &maxToF, const double &binWidth,
99 const std::vector<DataObjects::EventList> &events,
100 DataObjects::EventWorkspace_sptr &dataWorkspace) {
101 // Round up number of bins needed
102 std::vector<double> xAxis(int(std::ceil(maxToF / binWidth)));
103 std::generate(xAxis.begin(), xAxis.end(), [i = 0, &binWidth]() mutable { return binWidth * i++; });
104
105 dataWorkspace = DataObjects::create<DataObjects::EventWorkspace>(
106 NUM_OF_SPECTRA, HistogramData::Histogram(HistogramData::BinEdges(xAxis)));
108 for (int i = 0; i < NUM_OF_SPECTRA; ++i) {
109 dataWorkspace->getSpectrum(i) = events[i];
110 dataWorkspace->getSpectrum(i).setSpectrumNo(i + 1);
111 dataWorkspace->getSpectrum(i).setDetectorID(i + 1);
112 }
113 dataWorkspace->setAllX(HistogramData::BinEdges{xAxis});
114 dataWorkspace->getAxis(0)->unit() = Kernel::UnitFactory::Instance().create("TOF");
115 dataWorkspace->setYUnit("Counts");
116}
117
125template <typename ValueType>
126void addToSampleLog(const std::string &name, const ValueType &value, DataObjects::EventWorkspace &ws) {
127 ws.mutableRun().addProperty(name, value, false);
128}
129
130} // namespace
131
140 if (descriptor.extension() == ".edb") {
141 return 95;
142 } else {
143 return 0;
144 }
145}
146
151 // Filename property.
152 const std::vector<std::string> extentions{".edb"};
153 declareProperty(std::make_unique<API::MultipleFileProperty>("Filename", extentions),
154 "The name of the nGEM file to load. Selecting multiple files will "
155 "combine them into one workspace.");
156 // Output workspace
158 std::make_unique<API::WorkspaceProperty<API::Workspace>>("OutputWorkspace", "", Kernel::Direction::Output),
159 "The output workspace");
160
161 auto mustBePositive = std::make_shared<Kernel::BoundedValidator<int>>();
162 mustBePositive->setLower(0);
163
164 auto mustBePositiveDbl = std::make_shared<Kernel::BoundedValidator<double>>();
165 mustBePositiveDbl->setLower(0.0);
166
167 // Bin Width
168 declareProperty("BinWidth", 10.0, mustBePositiveDbl, "The width of the time bins in the output.");
169
170 declareProperty("MinEventsPerFrame", 0, mustBePositive,
171 "The minimum number of events required in a frame before a "
172 "it is considered 'good'.");
173 declareProperty("MaxEventsPerFrame", EMPTY_INT(), mustBePositive,
174 "The maximum number of events allowed in a frame to be "
175 "considered 'good'.");
177 std::make_unique<Kernel::PropertyWithValue<bool>>("GenerateEventsPerFrame", false, Kernel::Direction::Input),
178 "Generate a workspace to show the number of events captured by each "
179 "frame. (optional, default False).");
180}
181
186 progress(0);
187
188 std::vector<std::vector<std::string>> filePaths = getProperty("Filename");
189
190 const int minEventsReq(getProperty("MinEventsPerFrame"));
191 const int maxEventsReq(getProperty("MaxEventsPerFrame"));
192
193 double maxToF{-std::numeric_limits<double>::max()}, minToF{std::numeric_limits<double>::max()};
194 const double binWidth(getProperty("BinWidth"));
195
196 int rawFrames = 0;
197 int goodFrames = 0;
198 std::vector<double> frameEventCounts;
199 int eventCountInFrame = 0;
200
201 std::vector<DataObjects::EventList> events, eventsInFrame;
202 events.resize(NUM_OF_SPECTRA);
203 eventsInFrame.resize(NUM_OF_SPECTRA);
204 progress(0.04);
205
206 size_t totalFilePaths(filePaths.size());
207 int counter(1);
208 for (const auto &filePath : filePaths) {
209 loadSingleFile(filePath, eventCountInFrame, maxToF, minToF, rawFrames, goodFrames, minEventsReq, maxEventsReq,
210 frameEventCounts, events, eventsInFrame, totalFilePaths, counter);
211 }
212 // Add the final frame of events (as they are not followed by a T0 event)
213 addFrameToOutputWorkspace(rawFrames, goodFrames, eventCountInFrame, minEventsReq, maxEventsReq, frameEventCounts,
214 events, eventsInFrame);
215 progress(0.90);
216
218 createEventWorkspace(maxToF, binWidth, events, dataWorkspace);
219
220 addToSampleLog("raw_frames", rawFrames, *dataWorkspace);
221 addToSampleLog("good_frames", goodFrames, *dataWorkspace);
222 addToSampleLog("max_ToF", maxToF, *dataWorkspace);
223 addToSampleLog("min_ToF", minToF, *dataWorkspace);
224
225 loadInstrument(dataWorkspace);
226
227 setProperty("OutputWorkspace", dataWorkspace);
228 if (this->getProperty("GenerateEventsPerFrame")) {
229 createCountWorkspace(frameEventCounts);
230 }
231 progress(1.00);
232}
233
251void LoadNGEM::loadSingleFile(const std::vector<std::string> &filePath, int &eventCountInFrame, double &maxToF,
252 double &minToF, int &rawFrames, int &goodFrames, const int &minEventsReq,
253 const int &maxEventsReq, MantidVec &frameEventCounts,
254 std::vector<DataObjects::EventList> &events,
255 std::vector<DataObjects::EventList> &eventsInFrame, const size_t &totalFilePaths,
256 int &fileCount) {
257 // Create file reader
258 if (filePath.size() > 1) {
259 throw std::runtime_error("Invalid filename parameter.");
260 }
261 std::ifstream file(filePath[0].c_str(), std::ifstream::binary);
262 if (!file.is_open()) {
263 throw std::runtime_error("File could not be found.");
264 }
265
266 const size_t totalNumEvents = verifyFileSize(file) / 16;
267 constexpr size_t SKIP_WORD_SIZE = 4;
268 size_t numProcessedEvents = 0;
269 size_t numWordsSkipped = 0;
270
271 while (true) {
272 // Load an event into the variable.
273 // Occasionally we may get a file where the first event has been chopped,
274 // so we seek to the start of a valid event.
275 // Chopping only seems to occur on a 4 byte word, hence seekg() of 4
276 EventUnion event, eventBigEndian;
277 do {
278 file.read(reinterpret_cast<char *>(&eventBigEndian), sizeof(eventBigEndian));
279 // Correct for the big endian format of nGEM datafile.
280 correctForBigEndian(eventBigEndian, event);
281 } while (!event.generic.check() && !file.seekg(SKIP_WORD_SIZE, std::ios_base::cur).eof() && ++numWordsSkipped);
282 if (file.eof()) {
283 break; // we have either not read an event, or only read part of one
284 }
285 if (event.coincidence.check()) { // Check for coincidence event.
286 ++eventCountInFrame;
287 uint64_t pixel = event.coincidence.getPixel();
288 // Convert to microseconds (us)
289 const double tof = event.coincidence.timeOfFlight / 1000.0;
290
291 if (tof > maxToF) {
292 maxToF = tof;
293 } else if (tof < minToF) {
294 minToF = tof;
295 }
296 eventsInFrame[pixel].addEventQuickly(Types::Event::TofEvent(tof));
297
298 } else if (event.tZero.check()) { // Check for T0 event.
299 addFrameToOutputWorkspace(rawFrames, goodFrames, eventCountInFrame, minEventsReq, maxEventsReq, frameEventCounts,
300 events, eventsInFrame);
301
302 if (reportProgressAndCheckCancel(numProcessedEvents, eventCountInFrame, totalNumEvents, totalFilePaths,
303 fileCount)) {
304 return;
305 }
306 } else if (event.generic.check()) { // match all other events and notify.
307 g_log.warning() << "Unexpected event type ID=" << event.generic.id << " loaded.\n";
308 } else { // if we were to get to here, must be a corrupt event
309 g_log.warning() << "Corrupt event detected.\n";
310 }
311 }
312 if (numWordsSkipped > 0) {
313 g_log.warning() << SKIP_WORD_SIZE * numWordsSkipped
314 << " bytes of file data were skipped when locating valid events.\n";
315 }
316 g_log.information() << "Finished loading a file.\n";
317 ++fileCount;
318}
319
327size_t LoadNGEM::verifyFileSize(std::ifstream &file) {
328 // Check that the file fits into 16 byte sections.
329 file.seekg(0, file.end);
330 size_t size = file.tellg();
331 if (size % 16 != 0) {
332 g_log.warning() << "Invalid file size. File is size is " << size
333 << " bytes which is not a multiple of 16. There may be some bytes "
334 "missing from the data. \n";
335 }
336 file.seekg(0);
337 return size;
338}
339
351bool LoadNGEM::reportProgressAndCheckCancel(size_t &numProcessedEvents, int &eventCountInFrame,
352 const size_t &totalNumEvents, const size_t &totalFilePaths,
353 const int &fileCount) {
354 numProcessedEvents += eventCountInFrame;
355 std::string message(std::to_string(fileCount) + "/" + std::to_string(totalFilePaths));
356 progress(double(numProcessedEvents) / double(totalNumEvents) / 1.11111, message);
357 eventCountInFrame = 0;
358 // Check for cancel flag.
359 return this->getCancel();
360}
361
368void LoadNGEM::createCountWorkspace(const std::vector<double> &frameEventCounts) {
369 std::vector<double> xAxisCounts(frameEventCounts.size() + 1);
370 std::generate(xAxisCounts.begin(), xAxisCounts.end(), [n = 0.0]() mutable { return ++n; });
371
372 DataObjects::Workspace2D_sptr countsWorkspace =
373 DataObjects::create<DataObjects::Workspace2D>(1, HistogramData::Histogram(HistogramData::BinEdges(xAxisCounts)));
374
375 countsWorkspace->mutableY(0) = frameEventCounts;
376 std::string countsWorkspaceName(this->getProperty("OutputWorkspace"));
377 countsWorkspaceName.append("_event_counts");
378 countsWorkspace->setYUnit("Counts");
379 std::shared_ptr<Kernel::Units::Label> XLabel =
380 std::dynamic_pointer_cast<Kernel::Units::Label>(Kernel::UnitFactory::Instance().create("Label"));
381 XLabel->setLabel("Frame");
382 countsWorkspace->getAxis(0)->unit() = XLabel;
383
384 this->declareProperty(std::make_unique<API::WorkspaceProperty<API::Workspace>>("CountsWorkspace", countsWorkspaceName,
386 "Counts of events per frame.");
387 progress(1.00);
388 this->setProperty("CountsWorkspace", countsWorkspace);
389}
390
397 auto loadInstrument = this->createChildAlgorithm("LoadInstrument");
398 loadInstrument->setPropertyValue("InstrumentName", "NGEM");
399 loadInstrument->setProperty<API::MatrixWorkspace_sptr>("Workspace", dataWorkspace);
400 loadInstrument->setProperty("RewriteSpectraMap", Kernel::OptionalBool(false));
401 loadInstrument->execute();
402}
403
411std::map<std::string, std::string> LoadNGEM::validateInputs() {
412 std::map<std::string, std::string> results;
413
414 int MinEventsPerFrame = getProperty("MinEventsPerFrame");
415 int MaxEventsPerFrame = getProperty("MaxEventsPerFrame");
416
417 if (MaxEventsPerFrame < MinEventsPerFrame) {
418 results["MaxEventsPerFrame"] = "MaxEventsPerFrame is less than MinEvents per frame";
419 }
420 return results;
421}
422
423} // namespace Mantid::DataHandling
double value
The value of the point.
Definition: FitMW.cpp:51
#define PARALLEL_FOR_NO_WSP_CHECK()
#define DECLARE_FILELOADER_ALGORITHM(classname)
DECLARE_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro when wri...
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Definition: Algorithm.cpp:842
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
bool getCancel() const
Returns the cancellation state.
Definition: Algorithm.cpp:1657
A property class for workspaces.
int confidence(Kernel::FileDescriptor &descriptor) const override
The confidence that an algorithm is able to load the file.
Definition: LoadNGEM.cpp:139
void loadSingleFile(const std::vector< std::string > &filePath, int &eventCountInFrame, double &maxToF, double &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)
Load a file into the event lists.
Definition: LoadNGEM.cpp:251
void createCountWorkspace(const std::vector< double > &frameEventCounts)
Create a workspace to store the number of counts per frame.
Definition: LoadNGEM.cpp:368
void exec() override
Execute the algorithm.
Definition: LoadNGEM.cpp:185
void loadInstrument(DataObjects::EventWorkspace_sptr &dataWorkspace)
Load the instrument and attach to the data workspace.
Definition: LoadNGEM.cpp:396
bool reportProgressAndCheckCancel(size_t &numProcessedEvents, int &eventCountInFrame, const size_t &totalNumEvents, const size_t &totalFilePaths, const int &fileCount)
Reports progress and checks cancel flag.
Definition: LoadNGEM.cpp:351
std::map< std::string, std::string > validateInputs() override
Validate the imputs into the algorithm, overrides.
Definition: LoadNGEM.cpp:411
size_t verifyFileSize(std::ifstream &file)
Check that a file to be loaded is in 128 bit words.
Definition: LoadNGEM.cpp:327
void init() override
Initialise the algorithm.
Definition: LoadNGEM.cpp:150
Defines a wrapper around an open file.
const std::string & extension() const
Access the file extension.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
OptionalBool : Tri-state bool.
Definition: OptionalBool.h:25
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< Workspace2D > Workspace2D_sptr
shared pointer to Mantid::DataObjects::Workspace2D
std::shared_ptr< EventWorkspace > EventWorkspace_sptr
shared pointer to the EventWorkspace class
std::unique_ptr< T > create(const P &parent, const IndexArg &indexArg, const HistArg &histArg)
This is the create() method that all the other create() methods call.
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition: EmptyValues.h:25
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
Definition: cow_ptr.h:172
Mantid::DataObjects::EventWorkspace_sptr createEventWorkspace()
Create event workspace with: 500 pixels 1000 histogrammed bins.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54
Is able to hold all versions of the data words in the same memory location.
Definition: LoadNGEM.h:79
CoincidenceEvent coincidence
Definition: LoadNGEM.h:82