Mantid
Loading...
Searching...
No Matches
LoadPLNnxs.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2020 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 +
7
10#include "MantidAPI/Axis.h"
14#include "MantidAPI/Run.h"
24
25#include <algorithm>
26#include <cmath>
27#include <filesystem>
28#include <fstream>
29#include <utility>
30
31namespace Mantid::DataHandling {
32
33using namespace Kernel;
34using namespace API;
35using namespace Nexus;
36
37using namespace ANSTO;
39
40// algorithm parameter names
41constexpr char FilenameStr[] = "Filename";
42constexpr char MaskStr[] = "Mask";
43constexpr char SelectDetectorTubesStr[] = "SelectDetectorTubes";
44constexpr char SelectDatasetStr[] = "SelectDataset";
45constexpr char FilterByTimeStartStr[] = "FilterByTimeStart";
46constexpr char FilterByTimeStopStr[] = "FilterByTimeStop";
47constexpr char TOFBiasStr[] = "TimeOfFlightBias";
48constexpr char CalibrateTOFStr[] = "CalibrateTOFBias";
49constexpr char LambdaOnTwoStr[] = "LambdaOnTwoMode";
50constexpr char UseHMScanTimeStr[] = "UseHMScanTime";
51
52// register the algorithm
54
55static const std::map<std::string, Anxs::ScanLog> ScanLogMap = {
56 {"end", Anxs::ScanLog::End}, {"mean", Anxs::ScanLog::Mean}, {"start", Anxs::ScanLog::Start}};
57
58// number of physical detectors
59constexpr size_t MONITORS = 8;
60constexpr size_t DETECTOR_TUBES = 200;
61constexpr size_t HISTO_BINS_X = DETECTOR_TUBES + MONITORS;
62constexpr size_t HISTO_BINS_Y_DENUMERATOR = 16;
63constexpr size_t TUBE_DETECTOR_RESOLUTION = 1024;
67
68// File loading progress boundaries
69constexpr size_t Progress_LoadBinFile = 48;
70constexpr size_t Progress_ReserveMemory = 4;
72
73// Common pairing of limits
74using TimeLimits = std::pair<double, double>;
75
77 DatasetTime(int32_t index, int64_t start, int64_t end) : index(index), start(start), end(end) {}
78
79 int32_t index;
80 int64_t start;
81 int64_t end;
82};
83
84template <typename Type>
85void AddSinglePointTimeSeriesProperty(API::LogManager &logManager, const std::string &time, const std::string &name,
86 const Type value) {
87 // create time series property and add single value
89 p->addValue(time, value);
90
91 // add to log manager
92 logManager.addProperty(p);
93}
94
95template <typename T>
96void AddSinglePointTimeSeriesProperty(API::LogManager &logManager, std::int64_t eventTime, const std::string &name,
97 const T value) {
98 std::string strEventTime = Types::Core::DateAndTime(Anxs::epochRelDateTimeBase(eventTime)).toISO8601String();
99 AddSinglePointTimeSeriesProperty<T>(logManager, strEventTime, name, value);
100}
101
102// Utility functions for loading values with defaults
103// Single value properties only support int, double, string and bool
104template <typename Type>
105Type GetNeXusValue(const Nexus::NXEntry &entry, const std::string &address, const Type &defval, int32_t index) {
106 try {
107 Nexus::NXDataSetTyped<Type> dataSet = entry.openNXDataSet<Type>(address);
108 dataSet.load();
109
110 return dataSet()[index];
111 } catch (std::runtime_error &) {
112 return defval;
113 }
114}
115
116// string and double are special cases
117template <>
118double GetNeXusValue<double>(const Nexus::NXEntry &entry, const std::string &address, const double &defval,
119 int32_t index) {
120 try {
121 Nexus::NXFloat dataSet = entry.openNXDataSet<float>(address);
122 dataSet.load();
123
124 return dataSet()[index];
125 } catch (std::runtime_error &) {
126 return defval;
127 }
128}
129
130template <>
131std::string GetNeXusValue<std::string>(const Nexus::NXEntry &entry, const std::string &address,
132 const std::string &defval, int32_t /*unused*/) {
133
134 try {
135 return entry.getString(address);
136 } catch (std::runtime_error &) {
137 return defval;
138 }
139}
140
141template <typename T>
142void MapNeXusToProperty(const Nexus::NXEntry &entry, const std::string &address, const T &defval,
143 API::LogManager &logManager, const std::string &name, const T &factor, int32_t index) {
144
145 T value = GetNeXusValue<T>(entry, address, defval, index);
146 logManager.addProperty<T>(name, value * factor);
147}
148
149// string is a special case
150template <>
151void MapNeXusToProperty<std::string>(const Nexus::NXEntry &entry, const std::string &address, const std::string &defval,
152 API::LogManager &logManager, const std::string &name,
153 const std::string & /*unused*/, int32_t index) {
154
155 std::string value = GetNeXusValue<std::string>(entry, address, defval, index);
156 logManager.addProperty<std::string>(name, value);
157}
158
159template <typename T>
160void MapNeXusToSeries(const Nexus::NXEntry &entry, const std::string &nxsPath, uint64_t startTime, uint64_t endTime,
161 Anxs::ScanLog valueOption, const T &defval, API::LogManager &logManager,
162 const std::string &logName, const T &factor) {
163
164 std::string units;
165 uint64_t eventTime{0};
166 T eventValue{0};
167 if (!Anxs::extractTimedDataSet(entry, nxsPath, startTime, endTime, valueOption, eventTime, eventValue, units)) {
168 eventValue = defval;
169 eventTime = startTime;
170 }
171 std::string strEventTime = Types::Core::DateAndTime(Anxs::epochRelDateTimeBase(eventTime)).toISO8601String();
172 AddSinglePointTimeSeriesProperty<T>(logManager, strEventTime, logName, eventValue * factor);
173}
174
175// map the comma separated range of indexes to the vector via a lambda function
176// throws an exception if it is outside the vector range
177//
178template <typename T, typename F> void mapRangeToIndex(const std::string &line, std::vector<T> &result, const F &fn) {
179
180 std::stringstream ss(line);
181 std::string item;
182 size_t index = 0;
183 while (std::getline(ss, item, ',')) {
184 auto const k = item.find('-');
185
186 size_t p0, p1;
187 if (k != std::string::npos) {
188 p0 = boost::lexical_cast<size_t>(item.substr(0, k));
189 p1 = boost::lexical_cast<size_t>(item.substr(k + 1, item.size() - k - 1));
190 } else {
191 p0 = boost::lexical_cast<size_t>(item);
192 p1 = p0;
193 }
194
195 if (p1 < result.size() && p0 <= p1) {
196 while (p0 <= p1) {
197 result[p0++] = fn(index);
198 index++;
199 }
200 } else if (p0 < result.size() && p1 < p0) {
201 do {
202 result[p0] = fn(index);
203 index++;
204 } while (p1 < p0--);
205 } else
206 throw std::invalid_argument("invalid range specification");
207 }
208}
209
210void loadEvents(API::Progress &prog, const char *progMsg, ANSTO::BaseEventProcessor *eventProcessor,
211 const Nexus::NXEntry &entry, uint64_t start_nsec, uint64_t end_nsec) {
212
213 prog.doReport(progMsg);
214
215 // for progress notifications
217
218 const std::string neutronPath{"instrument/detector_events"};
219 Anxs::ReadEventData(progTracker, entry, eventProcessor, start_nsec, end_nsec, neutronPath, TUBE_DETECTOR_RESOLUTION);
220}
221
222namespace PLN2 {
223
224// Simple reader that is compatible with the ASNTO event file loader
226 std::ifstream _ifs;
227 size_t _size;
228
229public:
230 explicit FileLoader(const char *filename) : _ifs(filename, std::ios::binary | std::ios::in) {
231 if (!_ifs.is_open() || _ifs.fail())
232 throw std::runtime_error("unable to open file");
233
234 _ifs.seekg(0, _ifs.end);
235 _size = _ifs.tellg();
236 _ifs.seekg(0, _ifs.beg);
237 }
238
239 bool read(char *s, std::streamsize n) { return static_cast<bool>(_ifs.read(s, n)); }
240
241 size_t size() const { return _size; }
242
243 size_t position() { return _ifs.tellg(); }
244
245 size_t selected_position() { return _ifs.tellg(); }
246};
247
248//
249// In the future the ANSTO helper and event file loader will be generalized to
250// handle the instruments consistently.
251
252// Simple 1D histogram class
254 std::vector<size_t> m_hist;
255 double m_M;
256 double m_B;
257 size_t m_peak;
258 size_t m_count;
259
260public:
261 SimpleHist(size_t N, double minVal, double maxVal) : m_hist(N, 0) {
262 m_M = (static_cast<double>(N) / (maxVal - minVal));
263 m_B = -m_M * minVal;
264 m_peak = 0;
265 m_count = 0;
266 }
267
268 inline double ival(double val) const { return m_M * val + m_B; }
269
270 inline double xval(double ix) const { return (ix - m_B) / m_M; }
271
272 inline void add(double val) {
273 auto ix = static_cast<size_t>(std::floor(ival(val)));
274 if (ix < m_hist.size()) {
275 m_hist[ix]++;
276 m_count++;
277 if (m_hist[ix] > m_peak)
278 m_peak = m_hist[ix];
279 }
280 }
281
282 const std::vector<size_t> &histogram() const { return m_hist; }
283
284 inline size_t peak() const { return m_peak; }
285 inline size_t count() const { return m_count; }
286};
287
289protected:
290 // fields
291 const std::vector<bool> &m_roi;
292 const std::vector<size_t> &m_mapIndex;
293 const double m_framePeriod;
294 const double m_gatePeriod;
295
296 // number of frames
297 size_t m_frames;
299
300 // number of events
304
305 // time boundaries
306 const TimeLimits m_timeBoundary; // seconds
307
308 virtual void addEventImpl(size_t id, size_t x, size_t y, double tof) = 0;
309
310public:
311 EventProcessor(const std::vector<bool> &roi, const std::vector<size_t> &mapIndex, const double framePeriod,
312 const double gatePeriod, const TimeLimits &timeBoundary, size_t maxEvents)
313 : m_roi(roi), m_mapIndex(mapIndex), m_framePeriod(framePeriod), m_gatePeriod(gatePeriod), m_frames(0),
315 m_timeBoundary(timeBoundary) {}
316
317 void newFrame() override {
319 m_frames++;
320 if (validFrame())
322 }
323 }
324
325 inline bool validFrame() const {
326 double frameTime = m_framePeriod * static_cast<double>(m_frames) * 1.0e-6;
327 return (frameTime >= m_timeBoundary.first && frameTime <= m_timeBoundary.second);
328 }
329
330 double duration() const {
331 // length test in seconds
332 return m_framePeriod * static_cast<double>(m_frames) * 1.0e-6;
333 }
334
335 inline int64_t frameStart() const {
336 // returns time in nanoseconds from start of test
337 auto start = m_framePeriod * static_cast<double>(m_frames);
338 return static_cast<int64_t>(start * 1.0e3);
339 }
340
341 size_t numFrames() const { return m_framesValid; }
342
344
345 size_t processedEvents() const { return m_processedEvents; }
346
347 void addEvent(size_t x, size_t p, double tof) override {
348
349 // check if in time boundaries
350 if (!validFrame())
351 return;
352
353 // group pixels
354 auto y = static_cast<size_t>(p / HISTO_BINS_Y_DENUMERATOR);
355
356 // determine detector id and check limits
357 if (x >= HISTO_BINS_X || y >= PIXELS_PER_TUBE)
358 return;
359
360 // map the raw detector index to the physical model
361 size_t xid = m_mapIndex[x];
362
363 size_t id = xid < DETECTOR_TUBES ? PIXELS_PER_TUBE * xid + y : DETECTOR_SPECTRA + xid;
364 if (id >= m_roi.size())
365 return;
366
367 // check if neutron is in region of interest
368 if (!m_roi[id])
369 return;
370
371 // finally pass to specific handler
373 // take the modulus of the tof time to account for the
374 // longer background chopper rate
375 double mtof = tof < 0.0 ? fmod(tof + m_gatePeriod, m_gatePeriod) : fmod(tof, m_gatePeriod);
376
377 addEventImpl(id, xid, y, mtof);
379 } else {
381 }
382 }
383};
384
385// The class determines the number of counts linked to the detectors and the
386// tof correction.
388protected:
389 // fields
390 std::vector<size_t> &m_eventCounts;
391 double m_L1;
392 double m_V0;
393 const std::vector<double> &m_L2;
395
396 void addEventImpl(size_t id, size_t /*x*/, size_t /*y*/, double tobs) override {
397 m_eventCounts[id]++;
398 // the maximum occurs at the elastic peak
399 double deltaT = 1.0e6 * (m_L1 + m_L2[id]) / m_V0 - tobs;
400 m_histogram.add(deltaT);
401 }
402
403public:
404 // construction
405 EventCounter(const std::vector<bool> &roi, const std::vector<size_t> &mapIndex, const double framePeriod,
406 const double gatePeriod, const TimeLimits &timeBoundary, std::vector<size_t> &eventCounts,
407 const double L1, const double V0, const std::vector<double> &vecL2, size_t maxEvents)
408 : EventProcessor(roi, mapIndex, framePeriod, gatePeriod, timeBoundary, maxEvents), m_eventCounts(eventCounts),
409 m_L1(L1), m_V0(V0), m_L2(vecL2), m_histogram(5000, -2500.0, 2500.0) {}
410
411 // clips the histogram above 25% and takes the mean of the values
412 double tofCorrection() {
413
414 // determine the points above the 25% threshold
415 auto minLevel = static_cast<size_t>(m_histogram.peak() / 4);
416 auto hvec = m_histogram.histogram();
417 double sum = 0.0;
418 size_t count = 0;
419 for (size_t i = 0; i < hvec.size(); i++) {
420 if (hvec[i] >= minLevel) {
421 auto ix = static_cast<double>(i);
422 sum += static_cast<double>(hvec[i]) * m_histogram.xval(ix + 0.5);
423 count += hvec[i];
424 }
425 }
426
427 return (count > 0 ? sum / static_cast<double>(count) : 0.0);
428 }
429};
430
432protected:
433 // fields
434 std::vector<EventVector_pt> &m_eventVectors;
435 double m_tofMin;
436 double m_tofMax;
437 int64_t m_startTime;
440
441 void addEventImpl(size_t id, size_t /*x*/, size_t /*y*/, double tobs) override {
442
443 // get the absolute time for the start of the frame
444 auto const offset = m_startTime + frameStart();
445
446 // adjust the tof to account for the correction and allocate events
447 // that occur before the sample time as slow events from the previous pulse
448 double tof = tobs + m_tofCorrection - m_sampleTime;
449 if (tof < 0.0)
450 tof = fmod(tof + m_gatePeriod, m_gatePeriod);
451 tof += m_sampleTime;
452 if (m_tofMin > tof)
453 m_tofMin = tof;
454 if (m_tofMax < tof)
455 m_tofMax = tof;
456
457 auto ev = Types::Event::TofEvent(tof, Types::Core::DateAndTime(offset));
458 m_eventVectors[id]->emplace_back(ev);
459 }
460
461public:
462 EventAssigner(const std::vector<bool> &roi, const std::vector<size_t> &mapIndex, const double framePeriod,
463 const double gatePeriod, const TimeLimits &timeBoundary, std::vector<EventVector_pt> &eventVectors,
464 int64_t startTime, double tofCorrection, double sampleTime, size_t maxEvents)
465 : EventProcessor(roi, mapIndex, framePeriod, gatePeriod, timeBoundary, maxEvents), m_eventVectors(eventVectors),
466 m_tofMin(std::numeric_limits<double>::max()), m_tofMax(std::numeric_limits<double>::min()),
467 m_startTime(startTime), m_tofCorrection(tofCorrection), m_sampleTime(sampleTime) {}
468
469 double tofMin() const { return m_tofMin <= m_tofMax ? m_tofMin : 0.0; }
470 double tofMax() const { return m_tofMin <= m_tofMax ? m_tofMax : 0.0; }
471};
472
473} // namespace PLN2
474
478
479 // Specify file extensions which can be associated with a specific file.
480 std::vector<std::string> exts;
481
482 // Declare the Filename algorithm property. Mandatory. Sets the path to the
483 // file to load.
484 exts.clear();
485 exts.emplace_back(".nxs");
486 declareProperty(std::make_unique<API::FileProperty>(FilenameStr, "", API::FileProperty::Load, exts),
487 "The input filename of the stored data");
488
489 // mask
490 exts.clear();
491 exts.emplace_back(".xml");
492 declareProperty(std::make_unique<API::FileProperty>(MaskStr, "", API::FileProperty::OptionalLoad, exts),
493 "The input filename of the mask data");
494
496 "Comma separated range of detectors tubes to be loaded,\n"
497 " eg 16,19-45,47");
498
500 std::make_unique<API::WorkspaceProperty<API::IEventWorkspace>>("OutputWorkspace", "", Kernel::Direction::Output));
501
502 declareProperty(SelectDatasetStr, 0, "Select the index for the dataset to be loaded.");
503
504 declareProperty(TOFBiasStr, 0.0, "Time of flight correction in micro-sec.");
505
506 declareProperty(CalibrateTOFStr, false, "Calibrate the TOF correction from the elastic pulse.");
507
508 declareProperty(LambdaOnTwoStr, false, "Instrument is operating in Lambda on Two mode.");
509
511 "Only include events after the provided start time, in "
512 "seconds (relative to the start of the run).");
513
515 "Only include events before the provided stop time, in "
516 "seconds (relative to the start of the run).");
517
518 declareProperty(UseHMScanTimeStr, false, "Use hmscan time rather than scan_dataset.");
519
520 std::string grpOptional = "Filters";
523}
524
526void LoadPLNnxs::createWorkspace(const std::string &title) {
527
528 // Create the workspace
529 m_localWorkspace = std::make_shared<DataObjects::EventWorkspace>();
530 m_localWorkspace->initialize(HISTOGRAMS, 2, 1);
531
532 // set the units
533 m_localWorkspace->getAxis(0)->unit() = Kernel::UnitFactory::Instance().create("TOF");
534 m_localWorkspace->setYUnit("Counts");
535
536 // set title
537 m_localWorkspace->setTitle(title);
538}
539
542// exec() function that works with the two files.
544
545 namespace fs = std::filesystem;
546
547 // Delete the output workspace name if it existed
548 std::string outName = getPropertyValue("OutputWorkspace");
549 if (API::AnalysisDataService::Instance().doesExist(outName))
550 API::AnalysisDataService::Instance().remove(outName);
551
552 std::string nxsFile = getPropertyValue(FilenameStr);
553
554 // choose sics or hms dataset timing
555 bool const useHMScanTime = getProperty(UseHMScanTimeStr);
556
557 // dataset index to be loaded
559
560 // get the root entry and time period
561 Nexus::NXRoot root(nxsFile);
562 Nexus::NXEntry nxsEntry = root.openFirstEntry();
563 uint64_t startTime, endTime;
564 if (useHMScanTime)
565 std::tie(startTime, endTime) = Anxs::getHMScanLimits(nxsEntry, m_datasetIndex);
566 else
567 std::tie(startTime, endTime) = Anxs::getTimeScanLimits(nxsEntry, m_datasetIndex);
568 if (startTime >= endTime) {
569 g_log.error() << "Invalid time window from hmscan" << "\n";
570 throw std::runtime_error("LoadPLNnxs: invalid or missing scan time range.");
571 }
572 m_startRun = Types::Core::DateAndTime(Anxs::epochRelDateTimeBase(startTime)).toISO8601String();
573
574 // Create workspace and initiate progress bar
576 API::LogManager &logManager = m_localWorkspace->mutableRun();
577 API::Progress prog(this, 0.0, 1.0, Progress_Total);
578
579 // Load instrument and workspace properties
581 loadParameters(nxsEntry, startTime, endTime, logManager);
582 prog.doReport("creating instrument");
584
585 // Get the region of interest and filters and save to log
586 std::string const maskfile = getPropertyValue(MaskStr);
587 std::string const seltubes = getPropertyValue(SelectDetectorTubesStr);
588 logManager.addProperty(SelectDetectorTubesStr, seltubes);
589 logManager.addProperty(MaskStr, maskfile);
590
591 std::vector<bool> roi = createRoiVector(seltubes, maskfile);
592 double timeMaxBoundary = getProperty(FilterByTimeStopStr);
593 if (isEmpty(timeMaxBoundary))
594 timeMaxBoundary = std::numeric_limits<double>::infinity();
595 TimeLimits timeBoundary(getProperty(FilterByTimeStartStr), timeMaxBoundary);
596
597 // get the detector map from raw input to a physical detector
598 auto instr = m_localWorkspace->getInstrument();
599 std::string dmapStr = instr->getParameterAsString("DetectorMap");
600 std::vector<size_t> detMapIndex = std::vector<size_t>(HISTO_BINS_X, 0);
601 mapRangeToIndex(dmapStr, detMapIndex, [](size_t n) { return n; });
602
603 // Load the events file. First count the number of events to reserve
604 // memory and then assign the events to the detectors
605 size_t numberHistograms = m_localWorkspace->getNumberHistograms();
606 std::vector<EventVector_pt> eventVectors(numberHistograms, nullptr);
607 std::vector<size_t> eventCounts(numberHistograms, 0);
608
609 double masterRpm = fabs(logManager.getTimeSeriesProperty<double>("FermiChopperFreq")->firstValue());
610 double slaveRpm = fabs(logManager.getTimeSeriesProperty<double>("OverlapChopperFreq")->firstValue());
611 double framePeriod = 1.0e6 / masterRpm;
612
613 // if fermi chopper freq equals the overlap freq then the gate period is
614 // half the frame period
615 double gatePeriod = (std::round(masterRpm / slaveRpm) == 1.0 ? 0.5 * framePeriod : framePeriod);
616 AddSinglePointTimeSeriesProperty<double>(logManager, m_startRun, "GatePeriod", gatePeriod);
617
618 // count total events per pixel and reserve necessary memory
619 // size_t hdfCounts = static_cast<size_t>(logManager.getTimeSeriesProperty<int64_t>("TotalCounts")->firstValue());
621 double sourceSample = fabs(instr->getSource()->getPos().Z());
622 double wavelength = logManager.getTimeSeriesProperty<double>("Wavelength")->firstValue();
623 double velocity = PhysicalConstants::h / (PhysicalConstants::NeutronMass * wavelength * 1e-10);
624 double sampleTime = 1.0e6 * sourceSample / velocity;
625 PLN2::EventCounter eventCounter(roi, detMapIndex, framePeriod, gatePeriod, timeBoundary, eventCounts, sourceSample,
626 velocity, m_detectorL2, 0);
627 loadEvents(prog, "loading neutron counts", &eventCounter, nxsEntry, startTime, endTime);
628 ANSTO::ProgressTracker progTracker(prog, "creating neutron event lists", numberHistograms, Progress_ReserveMemory);
629 prepareEventStorage(progTracker, eventCounts, eventVectors);
630
631 AddSinglePointTimeSeriesProperty<int64_t>(logManager, m_startRun, "TotalCounts", eventCounter.availableEvents());
632
633 // perform the actual event collection and TOF convert if necessary
634 // if a phase calibration is required then load it as raw doppler time
635 // perform the calibration and then convert to TOF
636 Types::Core::DateAndTime startDateTime(m_startRun);
637 auto const start_nanosec = startDateTime.totalNanoseconds();
638 bool const calibrateTOF = getProperty(CalibrateTOFStr);
639 double tofCorrection = getProperty(TOFBiasStr);
640 if (calibrateTOF) {
641 tofCorrection = eventCounter.tofCorrection();
642 }
643 logManager.addProperty("CalibrateTOF", (calibrateTOF ? 1 : 0));
644 AddSinglePointTimeSeriesProperty<double>(logManager, m_startRun, "TOFCorrection", tofCorrection);
645 PLN2::EventAssigner eventAssigner(roi, detMapIndex, framePeriod, gatePeriod, timeBoundary, eventVectors,
646 start_nanosec, tofCorrection, sampleTime, 0);
647 loadEvents(prog, "loading neutron events (TOF)", &eventAssigner, nxsEntry, startTime, endTime);
648
649 // perform a calibration and then TOF conversion if necessary
650 // and update the tof limits
651 auto minTOF = eventAssigner.tofMin();
652 auto maxTOF = eventAssigner.tofMax();
653
654 // just to make sure the bins hold it all and setup the detector masks
655 m_localWorkspace->setAllX(HistogramData::BinEdges{std::max(0.0, floor(minTOF)), maxTOF + 1});
657
658 // set log values
659 auto frame_count = static_cast<int>(eventCounter.numFrames());
660 AddSinglePointTimeSeriesProperty<int>(logManager, m_startRun, "frame_count", frame_count);
661
662 std::string filename = getPropertyValue(FilenameStr);
663 logManager.addProperty("filename", filename);
664
665 Types::Core::time_duration duration =
666 boost::posix_time::microseconds(static_cast<boost::int64_t>(eventCounter.duration() * 1.0e6));
667 Types::Core::DateAndTime endDateTime(startDateTime + duration);
668 logManager.addProperty("start_time", startDateTime.toISO8601String());
669 logManager.addProperty("end_time", endDateTime.toISO8601String());
670 logManager.addProperty<double>("dur", eventCounter.duration());
671
672 // Finally add the time-series evironment parameters explicitly
673 loadEnvironParameters(nxsEntry, startTime, endTime, logManager);
674
675 setProperty("OutputWorkspace", m_localWorkspace);
676}
677
680
681 m_detectorL2.resize(HISTOGRAMS, 0.0);
682 const auto &detectorInfo = m_localWorkspace->detectorInfo();
683 auto detIDs = detectorInfo.detectorIDs();
684 for (const auto detID : detIDs) {
685 auto ix = detectorInfo.indexOf(detID);
686 double l2 = detectorInfo.l2(ix);
687 m_detectorL2[detID] = l2;
688 }
689}
690
692void LoadPLNnxs::setupDetectorMasks(const std::vector<bool> &roi) {
693
694 // count total number of masked bins
695 size_t maskedBins = std::count(roi.begin(), roi.end(), false);
696
697 if (maskedBins > 0) {
698 // create list of masked bins
699 std::vector<size_t> maskIndexList(maskedBins);
700 size_t maskIndex = 0;
701
702 for (size_t i = 0; i != roi.size(); i++)
703 if (!roi[i])
704 maskIndexList[maskIndex++] = i;
705
706 auto maskingAlg = createChildAlgorithm("MaskDetectors");
707 maskingAlg->setProperty("Workspace", m_localWorkspace);
708 maskingAlg->setProperty("WorkspaceIndexList", maskIndexList);
709 maskingAlg->executeAsChildAlg();
710 }
711}
712
715void LoadPLNnxs::prepareEventStorage(ANSTO::ProgressTracker &progTracker, std::vector<size_t> &eventCounts,
716 std::vector<EventVector_pt> &eventVectors) {
717
718 size_t numberHistograms = eventCounts.size();
719 for (size_t i = 0; i != numberHistograms; ++i) {
720 DataObjects::EventList &eventList = m_localWorkspace->getSpectrum(i);
721
723 eventList.reserve(eventCounts[i]);
724
725 eventList.setDetectorID(static_cast<detid_t>(i));
726 eventList.setSpectrumNo(static_cast<detid_t>(i));
727
728 DataObjects::getEventsFrom(eventList, eventVectors[i]);
729
730 progTracker.update(i);
731 }
732 progTracker.complete();
733}
734
737std::vector<bool> LoadPLNnxs::createRoiVector(const std::string &selected, const std::string &maskfile) {
738
739 std::vector<bool> result(HISTOGRAMS, true);
740
741 // turn off pixels linked to missing tubes
742 if (!selected.empty()) {
743 std::vector<bool> tubes(HISTO_BINS_X, false);
744 mapRangeToIndex(selected, tubes, [](size_t) { return true; });
745 for (size_t i = 0; i < DETECTOR_TUBES; i++) {
746 if (tubes[i] == false) {
747 for (size_t j = 0; j < PIXELS_PER_TUBE; j++) {
748 result[i * PIXELS_PER_TUBE + j] = false;
749 }
750 }
751 }
752 for (size_t i = 0; i < MONITORS; i++) {
753 result[DETECTOR_SPECTRA + i] = tubes[DETECTOR_TUBES + i];
754 }
755 }
756
757 if (maskfile.length() == 0)
758 return result;
759
760 std::ifstream input(maskfile.c_str());
761 if (!input.good())
762 throw std::invalid_argument("invalid mask file");
763
764 std::string line;
765 while (std::getline(input, line)) {
766 auto i0 = line.find("<detids>");
767 auto iN = line.find("</detids>");
768
769 if ((i0 != std::string::npos) && (iN != std::string::npos) && (i0 < iN)) {
770 line = line.substr(i0 + 8, iN - i0 - 8); // 8 = len("<detids>")
771 mapRangeToIndex(line, result, [](size_t) { return false; });
772 }
773 }
774
775 return result;
776}
777
779void LoadPLNnxs::loadParameters(const Nexus::NXEntry &entry, uint64_t startTime, uint64_t endTime,
780 API::LogManager &logm) {
781
782 MapNeXusToProperty<std::string>(entry, "sample/name", "unknown", logm, "SampleName", "", 0);
783 MapNeXusToProperty<std::string>(entry, "sample/description", "unknown", logm, "SampleDescription", "", 0);
784
785 // Add support for instrument running in lambda on two mode.
786 // Added as UI option as the available instrument parameters
787 // cannot be reliably interpreted to predict the mode (as
788 // advised by the instrument scientist).
789 bool const lambdaOnTwoMode = getProperty(LambdaOnTwoStr);
790 double lambdaFactor = (lambdaOnTwoMode ? 0.5 : 1.0);
791 logm.addProperty("LambdaOnTwoMode", (lambdaOnTwoMode ? 1 : 0));
792
793 MapNeXusToSeries<double>(entry, "instrument/fermi_chopper/mchs", startTime, endTime, Anxs::ScanLog::End, 0.0, logm,
794 "FermiChopperFreq", 1.0 / 60);
795 MapNeXusToSeries<double>(entry, "instrument/fermi_chopper/schs", startTime, endTime, Anxs::ScanLog::End, 0.0, logm,
796 "OverlapChopperFreq", 1.0 / 60);
797 MapNeXusToSeries<double>(entry, "instrument/crystal/wavelength", startTime, endTime, Anxs::ScanLog::End, 0.0, logm,
798 "Wavelength", lambdaFactor);
799 MapNeXusToSeries<double>(entry, "instrument/detector/stth", startTime, endTime, Anxs::ScanLog::End, 0.0, logm,
800 "DetectorTankAngle", 1.0);
801 MapNeXusToSeries<int64_t>(entry, "monitor/bm1_counts", startTime, endTime, Anxs::ScanLog::End, 0, logm,
802 "MonitorCounts", 1);
803 MapNeXusToSeries<double>(entry, "instrument/detector/tofw", startTime, endTime, Anxs::ScanLog::End, 5.0, logm,
804 "ChannelWidth", 1);
805 MapNeXusToSeries<double>(entry, "sample/mscor", startTime, endTime, Anxs::ScanLog::End, 0.0, logm, "SampleRotation",
806 1);
807}
808
811void LoadPLNnxs::loadEnvironParameters(const Nexus::NXEntry &entry, uint64_t startTime, uint64_t endTime,
812 API::LogManager &logm) {
813
814 // load the environment variables for the dataset loaded
815 auto tags = ANSTO::filterGroups(entry, "control/", "^[A-Z]{1,3}[0-9]{1,3}[A-Za-z]{1,9}[0-9]{0,3}$");
816 for (const auto &tag : tags) {
817 MapNeXusToSeries<double>(entry, "control/" + tag, startTime, endTime, Anxs::ScanLog::End, 0.0, logm, "env_" + tag,
818 1.0);
819 }
820}
821
824
825 // loads the IDF and parameter file
826 auto loadInstrumentAlg = createChildAlgorithm("LoadInstrument");
827 loadInstrumentAlg->setProperty("Workspace", m_localWorkspace);
828 loadInstrumentAlg->setPropertyValue("InstrumentName", "PELICAN");
829 loadInstrumentAlg->setProperty("RewriteSpectraMap", Mantid::Kernel::OptionalBool(false));
830 loadInstrumentAlg->executeAsChildAlg();
831}
832
834int LoadPLNnxs::version() const { return 1; }
835
837const std::vector<std::string> LoadPLNnxs::seeAlso() const { return {"Load", "LoadEMU"}; }
839const std::string LoadPLNnxs::category() const { return "DataHandling\\ANSTO"; }
840
842const std::string LoadPLNnxs::name() const { return "LoadPLNnxs"; }
843
845const std::string LoadPLNnxs::summary() const { return "Loads a PLN2 Hdf and linked event file into a workspace."; }
846
850 if (descriptor.extension() != ".nxs")
851 return 0;
852
853 if (descriptor.isEntry("/entry1/instrument/fermi_chopper/schs/value") &&
854 descriptor.isEntry("/entry1/instrument/aperture/sh1/value") &&
855 descriptor.isEntry("/entry1/instrument/ag1010/MEAS/Temperature/value") &&
856 descriptor.isEntry("/entry1/instrument/detector/stth/value") &&
857 descriptor.isEntry("/entry1/instrument/detector_events/event_id") &&
858 descriptor.isEntry("/entry1/scan_dataset/time") && descriptor.isEntry("/entry1/scan_dataset/value")) {
859 return 80;
860 } else {
861 return 0;
862 }
863}
864
865} // namespace Mantid::DataHandling
std::string name
Definition Run.cpp:60
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
#define fabs(x)
Definition Matrix.cpp:22
int count
counter
Definition Matrix.cpp:37
#define DECLARE_NEXUS_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro wh...
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.
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.
Kernel::Logger & g_log
Definition Algorithm.h:423
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
@ Load
allowed here which will be passed to the algorithm
void setDetectorID(const detid_t detID)
Clear the list of detector IDs, then add one.
Definition ISpectrum.cpp:84
void setSpectrumNo(specnum_t num)
Sets the spectrum number of this spectrum.
This class contains the information about the log entries.
Definition LogManager.h:43
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition LogManager.h:90
Kernel::TimeSeriesProperty< T > * getTimeSeriesProperty(const std::string &name) const
Returns a property as a time series property.
Helper class for reporting progress from algorithms.
Definition Progress.h:25
void doReport(const std::string &msg="") override
Actually do the reporting, without changing the loop counter.
Definition Progress.cpp:70
A property class for workspaces.
abstract base class for processing events as they are read from the file
helper class to keep track of progress
LoadPLNnxs : Loads an ANSTO PLN nxs and linked event file into a workspace.
Definition LoadPLNnxs.h:58
int confidence(Nexus::NexusDescriptorLazy &descriptor) const override
Return the confidence as an integer value that this algorithm can load the file descriptor.
void loadInstrument()
Load the instrument definition.
void prepareEventStorage(ANSTO::ProgressTracker &prog, std::vector< size_t > &eventCounts, std::vector< EventVector_pt > &eventVectors)
Allocate space for the event storage in eventVectors after the eventCounts have been determined.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
const std::vector< std::string > seeAlso() const override
Similar algorithms.
void loadDetectorL2Values()
Recovers the L2 neutronic distance for each detector.
void createWorkspace(const std::string &title)
Creates an event workspace and sets the title.
void loadEnvironParameters(const Nexus::NXEntry &entry, uint64_t startTime, uint64_t endTime, API::LogManager &logm)
Load the environment variables from the nxsFile and save as time series to the log manager,...
void setupDetectorMasks(const std::vector< bool > &roi)
Set up the detector masks to the region of interest roi.
void exec() override
Execute the algorithm.
int version() const override
Algorithm's version for identification.
const std::string category() const override
Algorithm's category for identification.
const std::string name() const override
Algorithms name for identification.
DataObjects::EventWorkspace_sptr m_localWorkspace
Definition LoadPLNnxs.h:98
void loadParameters(const Nexus::NXEntry &entry, uint64_t startTime, uint64_t endTime, API::LogManager &logm)
Load parameters from input nxsFile and save to the log manager, logm.
std::vector< bool > createRoiVector(const std::string &seltubes, const std::string &maskfile)
Region of interest is defined by the selected detectors and the maskfile.
std::vector< double > m_detectorL2
Definition LoadPLNnxs.h:101
void init() override
Initialise the algorithm and declare the properties for the nexus descriptor.
void addEventImpl(size_t id, size_t, size_t, double tobs) override
EventAssigner(const std::vector< bool > &roi, const std::vector< size_t > &mapIndex, const double framePeriod, const double gatePeriod, const TimeLimits &timeBoundary, std::vector< EventVector_pt > &eventVectors, int64_t startTime, double tofCorrection, double sampleTime, size_t maxEvents)
std::vector< EventVector_pt > & m_eventVectors
void addEventImpl(size_t id, size_t, size_t, double tobs) override
EventCounter(const std::vector< bool > &roi, const std::vector< size_t > &mapIndex, const double framePeriod, const double gatePeriod, const TimeLimits &timeBoundary, std::vector< size_t > &eventCounts, const double L1, const double V0, const std::vector< double > &vecL2, size_t maxEvents)
const std::vector< double > & m_L2
EventProcessor(const std::vector< bool > &roi, const std::vector< size_t > &mapIndex, const double framePeriod, const double gatePeriod, const TimeLimits &timeBoundary, size_t maxEvents)
virtual void addEventImpl(size_t id, size_t x, size_t y, double tof)=0
const std::vector< size_t > & m_mapIndex
void addEvent(size_t x, size_t p, double tof) override
bool read(char *s, std::streamsize n)
const std::vector< size_t > & histogram() const
SimpleHist(size_t N, double minVal, double maxVal)
A class for holding :
Definition EventList.h:57
void setSortOrder(const EventSortType order) const
Manually set the event list sort order value.
void reserve(size_t num) override
Reserve a certain number of entries in event list of the specified eventType.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertyGroup(const std::string &name, const std::string &group)
Set the group for a given property.
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
OptionalBool : Tri-state bool.
A specialised Property class for holding a series of time-value pairs.
std::string getString(const std::string &name) const
Returns a string.
NXDataSetTyped< T > openNXDataSet(const std::string &name) const
Templated method for creating datasets.
Templated class implementation of NXDataSet.
void load()
Read all of the datablock in.
Implements NXentry Nexus class.
Implements NXroot Nexus class.
NXEntry openFirstEntry()
Open the first NXentry in the file.
std::string const & extension() const noexcept
Access the file extension.
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::string extractWorkspaceTitle(const std::string &nxsFile)
int64_t epochRelDateTimeBase(int64_t epochInNanoSeconds)
std::pair< uint64_t, uint64_t > getHMScanLimits(const Nexus::NXEntry &entry, int datasetIx)
uint64_t extractTimedDataSet(const Nexus::NXEntry &entry, const std::string &path, uint64_t startTime, uint64_t endTime, std::vector< uint64_t > &times, std::vector< T > &events, std::string &units)
void ReadEventData(ProgressTracker &prog, const Nexus::NXEntry &entry, BaseEventProcessor *handler, uint64_t start_nsec, uint64_t end_nsec, const std::string &neutron_path, int tube_resolution=1024)
std::pair< uint64_t, uint64_t > getTimeScanLimits(const Nexus::NXEntry &entry, int datasetIx)
std::vector< Types::Event::TofEvent > * EventVector_pt
pointer to the vector of events
std::vector< std::string > filterGroups(const Nexus::NXEntry &entry, const std::string &groupAddress, const std::string &regexFilter)
extract groups from a group that match a regex filter
constexpr size_t TUBE_DETECTOR_RESOLUTION
void MapNeXusToProperty(const Nexus::NXEntry &entry, const std::string &address, const T &defval, API::LogManager &logManager, const std::string &name, const T &factor, int32_t index)
constexpr size_t DETECTOR_TUBES
Type GetNeXusValue(const Nexus::NXEntry &entry, const std::string &address, const Type &defval, int32_t index)
constexpr char SelectDetectorTubesStr[]
constexpr size_t HISTO_BINS_Y_DENUMERATOR
static const size_t Progress_LoadBinFile
Definition LoadBBY.cpp:51
std::string GetNeXusValue< std::string >(const Nexus::NXEntry &entry, const std::string &address, const std::string &defval, int32_t)
constexpr size_t DETECTOR_SPECTRA
static char const *const FilterByTimeStopStr
Definition LoadBBY.cpp:62
static char const *const MaskStr
Definition LoadBBY.cpp:56
static char const *const FilenameStr
Definition LoadBBY.cpp:55
constexpr char CalibrateTOFStr[]
void loadEvents(API::Progress &prog, const char *progMsg, EP &eventProcessor, const Nexus::NXEntry &entry, uint64_t start_nsec, uint64_t end_nsec)
Definition LoadBBY2.cpp:120
constexpr char SelectDatasetStr[]
static const size_t HISTO_BINS_X
Definition LoadBBY.cpp:48
void AddSinglePointTimeSeriesProperty(API::LogManager &logManager, const std::string &time, const std::string &name, const TYPE value)
Definition LoadBBY.cpp:67
constexpr char LambdaOnTwoStr[]
static char const *const UseHMScanTimeStr
Definition LoadBBY2.cpp:73
static char const *const FilterByTimeStartStr
Definition LoadBBY.cpp:61
void MapNeXusToSeries(const Nexus::NXEntry &entry, const std::string &nxsPath, uint64_t startTime, uint64_t endTime, Anxs::ScanLog valueOption, const T &defval, API::LogManager &logManager, const std::string &logName, const T &factor)
void MapNeXusToProperty< std::string >(const Nexus::NXEntry &entry, const std::string &address, const std::string &defval, API::LogManager &logManager, const std::string &name, const std::string &, int32_t index)
static const size_t Progress_ReserveMemory
Definition LoadBBY.cpp:52
constexpr size_t MONITORS
constexpr size_t PIXELS_PER_TUBE
static const std::map< std::string, Anxs::ScanLog > ScanLogMap
Definition LoadBBY2.cpp:78
constexpr size_t HISTOGRAMS
static const size_t Progress_Total
Definition LoadBBY.cpp:53
std::pair< double, double > TimeLimits
double GetNeXusValue< double >(const Nexus::NXEntry &entry, const std::string &address, const double &defval, int32_t index)
constexpr char TOFBiasStr[]
void mapRangeToIndex(const std::string &line, std::vector< T > &result, const F &fn)
DLLExport void getEventsFrom(EventList &el, std::vector< Types::Event::TofEvent > *&events)
static constexpr double NeutronMass
Mass of the neutron in kg.
static constexpr double h
Planck constant in J*s.
int32_t detid_t
Typedef for a detector ID.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
STL namespace.
DatasetTime(int32_t index, int64_t start, int64_t end)
@ Output
An output workspace.
Definition Property.h:54