Mantid
Loading...
Searching...
No Matches
LoadEMU.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"
25
26#include <boost/math/special_functions/round.hpp>
27#include <boost/math/tools/minima.hpp>
28
29#include <Poco/AutoPtr.h>
30#include <Poco/TemporaryFile.h>
31#include <Poco/Util/PropertyFileConfiguration.h>
32
33#include <algorithm>
34#include <cmath>
35#include <cstdio>
36#include <filesystem>
37#include <fstream>
38#include <utility>
39
42namespace {
43
44// number of physical detectors
45constexpr size_t HORIZONTAL_TUBES = 16;
46constexpr size_t VERTICAL_TUBES = 35;
47constexpr size_t DETECTOR_TUBES = HORIZONTAL_TUBES + VERTICAL_TUBES;
48// analysed and direct detectors
49constexpr size_t HISTO_BINS_X = DETECTOR_TUBES * 2;
50constexpr size_t HISTO_BINS_Y = 1024;
51constexpr size_t HISTO_BINS_Y_DENUMERATOR = 16;
52constexpr size_t PIXELS_PER_TUBE = HISTO_BINS_Y / HISTO_BINS_Y_DENUMERATOR;
53
54constexpr size_t BM_HISTOGRAMS = HISTO_BINS_X * PIXELS_PER_TUBE;
55constexpr size_t HISTOGRAMS = BM_HISTOGRAMS + PIXELS_PER_TUBE;
56constexpr size_t BEAM_MONITOR_BINS = 100;
57constexpr size_t PSEUDO_BM_TUBE = 55;
58// the half window for the running average matches the plateau width for the peak
59constexpr size_t BM_HALF_WINDOW = 5;
60
61// File loading progress boundaries
62constexpr size_t Progress_LoadBinFile = 48;
63constexpr size_t Progress_ReserveMemory = 4;
66// Algorithm parameter names
67constexpr char FilenameStr[] = "Filename";
68constexpr char MaskStr[] = "Mask";
69constexpr char SelectDetectorTubesStr[] = "SelectDetectorTubes";
70constexpr char SelectDatasetStr[] = "SelectDataset";
71constexpr char OverrideDopplerFreqStr[] = "OverrideDopplerFrequency";
72constexpr char OverrideDopplerPhaseStr[] = "OverrideDopplerPhase";
73constexpr char FilterByTimeStartStr[] = "FilterByTimeStart";
74constexpr char FilterByTimeStopStr[] = "FilterByTimeStop";
75constexpr char RawDopplerTimeStr[] = "LoadAsRawDopplerTime";
76constexpr char IncludePseudoBMStr[] = "IncludeBeamMonitor";
77constexpr char CalibrateDopplerPhaseStr[] = "CalibrateDopplerPhase";
78constexpr char PathToBinaryStr[] = "BinaryEventPath";
79
80// Common pairing of limits
81using TimeLimits = std::pair<double, double>;
82
83template <typename Type>
84void AddSinglePointTimeSeriesProperty(API::LogManager &logManager, const std::string &time, const std::string &name,
85 const Type value) {
86 // create time series property and add single value
88 p->addValue(time, value);
89
90 // add to log manager
91 logManager.addProperty(p);
92}
93
94// Utility functions for loading values with defaults
95// Single value properties only support int, double, string and bool
96template <typename Type>
97Type GetNeXusValue(const Nexus::NXEntry &entry, const std::string &address, const Type &defval, int32_t index) {
98 try {
99 Nexus::NXDataSetTyped<Type> dataSet = entry.openNXDataSet<Type>(address);
100 dataSet.load();
101
102 return dataSet()[index];
103 } catch (std::runtime_error &) {
104 return defval;
105 }
106}
107// string and double are special cases
108template <>
109double GetNeXusValue<double>(const Nexus::NXEntry &entry, const std::string &address, const double &defval,
110 int32_t index) {
111 try {
112 Nexus::NXFloat dataSet = entry.openNXFloat(address);
113 dataSet.load();
114
115 return dataSet()[index];
116 } catch (std::runtime_error &) {
117 return defval;
118 }
119}
120template <>
121std::string GetNeXusValue<std::string>(const Nexus::NXEntry &entry, const std::string &address,
122 const std::string &defval, int32_t /*unused*/) {
123
124 try {
125 return entry.getString(address);
126 } catch (std::runtime_error &) {
127 return defval;
128 }
129}
130
131template <typename T>
132void MapNeXusToProperty(const Nexus::NXEntry &entry, const std::string &address, const T &defval,
133 API::LogManager &logManager, const std::string &name, const T &factor, int32_t index) {
134
135 T value = GetNeXusValue<T>(entry, address, defval, index);
136 logManager.addProperty<T>(name, value * factor);
137}
138
139// sting is a special case
140template <>
141void MapNeXusToProperty<std::string>(Nexus::NXEntry const &entry, const std::string &address, const std::string &defval,
142 API::LogManager &logManager, const std::string &name,
143 const std::string & /*unused*/, int32_t index) {
144
145 std::string const value = GetNeXusValue<std::string>(entry, address, defval, index);
146 logManager.addProperty<std::string>(name, value);
147}
148
149template <typename T>
150void MapNeXusToSeries(Nexus::NXEntry &entry, const std::string &address, const T &defval, API::LogManager &logManager,
151 const std::string &time, const std::string &name, const T &factor, int32_t index) {
152
153 auto value = GetNeXusValue<T>(entry, address, defval, index);
154 AddSinglePointTimeSeriesProperty<T>(logManager, time, name, value * factor);
155}
156
157// map the comma separated range of indexes to the vector via a lambda function
158// throws an exception if it is outside the vector range
159//
160template <typename T, typename F> void mapRangeToIndex(const std::string &line, std::vector<T> &result, const F &fn) {
161
162 std::stringstream ss(line);
163 std::string item;
164 size_t index = 0;
165 while (std::getline(ss, item, ',')) {
166 auto k = item.find('-');
167
168 size_t p0, p1;
169 if (k != std::string::npos) {
170 p0 = boost::lexical_cast<size_t>(item.substr(0, k));
171 p1 = boost::lexical_cast<size_t>(item.substr(k + 1, item.size() - k - 1));
172 } else {
173 p0 = boost::lexical_cast<size_t>(item);
174 p1 = p0;
175 }
176
177 if (p1 < result.size() && p0 <= p1) {
178 while (p0 <= p1) {
179 result[p0++] = fn(index);
180 index++;
181 }
182 } else if (p0 < result.size() && p1 < p0) {
183 do {
184 result[p0] = fn(index);
185 index++;
186 } while (p1 < p0--);
187 } else
188 throw std::invalid_argument("invalid range specification");
189 }
190}
191
192// secant invert fucntion
193//
194template <typename F> double invert(double y, const F &f, double x0 = 0.0, const double eps = 1e-16) {
195 // secant method
196 double e0 = f(x0) - y;
197
198 double x1 = x0 + eps;
199 double e1 = f(x1) - y;
200 int loop = 16;
201 while (fabs(e0) > eps && loop-- > 0) {
202 double x = (x1 * e0 - x0 * e1) / (e0 - e1);
203
204 x1 = x0;
205 e1 = e0;
206
207 x0 = x;
208 e0 = f(x0) - y;
209 }
210
211 return x0;
212}
213
214// Need to convert two different methods for direct and analysed data
215// provide two distinct methods that may be called but the distances between
216// the detectors is not constant so it needs to store the distance for each
217// Note that observation time and TOF are in usec
218//
219using TofData = std::tuple<double, double>;
220
221class ConvertTOF {
222 const double m_w;
223 const double m_phi;
224 const double m_L0;
225 const double m_v2;
226 const double m_A;
227 const std::vector<double> &m_L2;
228
229 inline double L1(double t) const { return m_L0 + m_A * sin(m_w * t + m_phi); }
230
231 inline double v1(double t) const { return m_v2 - m_A * m_w * cos(m_w * t + m_phi); }
232
233public:
234 ConvertTOF(double Amp, double freq, double phase, double L1, double v2, const std::vector<double> &L2)
235 : m_w(2 * M_PI * freq), m_phi(M_PI * phase / 180.0), m_L0(L1), m_v2(v2), m_A(Amp), m_L2(L2) {}
236
237 TofData directTOF(size_t detID, double tobs) const {
238
239 // observation time and tof are in usec
240 auto tn = [=](double t) { return t + (L1(t) + m_L2[detID]) / v1(t); };
241
242 double tsec = tobs * 1.0e-6;
243 double t0 = tsec - (m_L0 + m_L2[detID]) / m_v2;
244 double tinv = invert(tsec, tn, t0);
245 double tof = (m_L0 + m_L2[detID]) / v1(tinv);
246
247 return TofData(tinv * 1.0e6, tof * 1.0e6);
248 }
249
250 TofData analysedTOF(size_t detID, double tobs) const {
251 // observation time and tof are in usec
252 auto tn = [=](double t) { return t + L1(t) / v1(t) + m_L2[detID] / m_v2; };
253
254 double tsec = tobs * 1.0e-6;
255 double t0 = tsec - (m_L0 + m_L2[detID]) / m_v2;
256 double t = invert(tsec, tn, t0);
257 double tof = m_L0 / v1(t) + m_L2[detID] / m_v2;
258
259 return TofData(t * 1.0e6, tof * 1.0e6);
260 }
261};
262
263// calculate mean of a subset of the vector
264double maskedMean(const std::vector<double> &vec, const std::vector<bool> &mask) {
265 if (vec.size() == 0 || vec.size() != mask.size())
266 throw std::runtime_error("masked mean of empty or mismatched vectors");
267 double sum = 0.0;
268 size_t count = 0;
269 for (size_t i = 0; i != vec.size(); i++) {
270 if (!mask[i])
271 continue;
272 sum += vec[i];
273 count++;
274 }
275 if (count == 0)
276 throw std::runtime_error("mean of empty vector");
277 return sum / static_cast<double>(count);
278}
279
280// calculate stdev for a subset of the vector
281double maskedStdev(const std::vector<double> &vec, const std::vector<bool> &mask) {
282
283 auto avg = maskedMean(vec, mask);
284 size_t count = 0;
285 double sum = 0.0;
286 for (size_t i = 0; i != vec.size(); i++) {
287 if (!mask[i])
288 continue;
289 sum += (vec[i] - avg) * (vec[i] - avg);
290 count++;
291 }
292 return std::sqrt(sum / static_cast<double>(count));
293}
294
295// Calculates a running average for a given half window size assuming the data is wrapped.
296// As the data are integer values, it is possible to maintain an exact sum without any
297// loss of accuracy where the next filtered value is calculated by adding the leading
298// point and subtracting the trailing point from the sum. As the data is wrapped the edge
299// points are handled by the modulus of the array index.
300//
301// |...........[.....x.....].............|
302// 0 |totalWindow| |N
303//
304std::vector<double> runningAverage(const std::vector<size_t> &data, size_t halfWindow) {
305 const auto N = data.size();
306 const size_t totalWindow = 2 * halfWindow + 1;
307
308 // Step 1 is to calculate the sum for the first point where startIndex is the
309 // first point in the total window. The start index wraps from the end of the data.
310 size_t sum{0};
311 size_t startIndex = N - halfWindow;
312 for (size_t i = 0; i < totalWindow; i++) {
313 size_t ix = (startIndex + i) % N;
314 sum += data[ix];
315 }
316
317 // Save the average for the current point, drop the first point from the total window
318 // sum, add the next point to the sum and shift the start index for the window.
319 std::vector<double> filtered(N, 0);
320 for (size_t i = 0; i < N; i++) {
321 // save previous and then move to next
322 filtered[i] = static_cast<double>(sum) / static_cast<double>(totalWindow);
323
324 sum -= data[startIndex];
325 sum += data[(startIndex + totalWindow) % N];
326 startIndex = (startIndex + 1) % N;
327 }
328 return filtered;
329}
330
331// Simple reader that is compatible with the ASNTO event file loader
332class FileLoader {
333 std::ifstream _ifs;
334 size_t _size;
335
336public:
337 explicit FileLoader(const char *filename) : _ifs(filename, std::ios::binary | std::ios::in) {
338 if (!_ifs.is_open() || _ifs.fail())
339 throw std::runtime_error("unable to open file");
340
341 _ifs.seekg(0, _ifs.end);
342 _size = _ifs.tellg();
343 _ifs.seekg(0, _ifs.beg);
344 }
345
346 bool read(char *s, std::streamsize n) { return static_cast<bool>(_ifs.read(s, n)); }
347
348 size_t size() { return _size; }
349
350 size_t position() { return _ifs.tellg(); }
351
352 size_t selected_position() { return _ifs.tellg(); }
353};
354
355} // anonymous namespace
356
357namespace EMU {
358
359// Implement emu specific handlers for the more general EMU events. The main
360// differences with the current impementation of handlers:
361// - tof needs to be derived from the observed time because of the doppler drive
362// - emu includes direct and indirect virtual detectors that account for
363// alternate paths
364// - the loader returns the doppler and auxillary time along with the
365// absolute time rather than just the primary observed time that is
366// equivalent to tof
367//
368// In the future the ANSTO helper and event file loader will be generalized to
369// handle the instruments consistently.
370
372protected:
373 // fields
374 const std::vector<bool> &m_roi;
375 const std::vector<size_t> &m_mapIndex;
376 const size_t m_stride;
377 const double m_framePeriod;
378 const double m_gatePeriod;
379
380 // number of frames
381 size_t m_frames;
383
384 // time boundaries
385 const TimeLimits m_timeBoundary; // seconds
386 const TimeLimits m_directTaux; // microsec
387 const TimeLimits m_analysedTaux; // microsec
388
389 const bool m_includeBM;
390
391 virtual void addEventImpl(size_t id, size_t x, size_t y, double tof) = 0;
392 virtual void addPseudoBMEventImpl(size_t id, double tobs) = 0;
393
394public:
395 EventProcessor(const std::vector<bool> &roi, const std::vector<size_t> &mapIndex, const size_t stride,
396 const double framePeriod, const double gatePeriod, TimeLimits timeBoundary, TimeLimits directLimits,
397 TimeLimits analysedLimits, bool includeBM)
398 : m_roi(roi), m_mapIndex(mapIndex), m_stride(stride), m_framePeriod(framePeriod), m_gatePeriod(gatePeriod),
399 m_frames(0), m_framesValid(0), m_timeBoundary(std::move(timeBoundary)), m_directTaux(std::move(directLimits)),
400 m_analysedTaux(std::move(analysedLimits)), m_includeBM(includeBM) {}
401
402 void newFrame() {
403 m_frames++;
404 if (validFrame())
406 }
407
408 inline bool validFrame() const {
409 double frameTime = m_framePeriod * static_cast<double>(m_frames) * 1.0e-6;
410 return (frameTime >= m_timeBoundary.first && frameTime <= m_timeBoundary.second);
411 }
412
413 double duration() const {
414 // test length in seconds
415 return m_framePeriod * static_cast<double>(m_frames) * 1.0e-6;
416 }
417
418 inline int64_t frameStart() const {
419 // returns time in nanoseconds from start of test
420 auto start = m_framePeriod * static_cast<double>(m_frames);
421 return static_cast<int64_t>(start * 1.0e3);
422 }
423
424 void addEvent(size_t x, size_t p, double tdop, double taux) {
425
426 // check if in time boundaries
427 if (!validFrame())
428 return;
429
430 // group pixels
431 auto y = static_cast<size_t>(p / HISTO_BINS_Y_DENUMERATOR);
432
433 // check for beam monitor tube
434 if (x == PSEUDO_BM_TUBE && y < m_stride) {
435 size_t id = BM_HISTOGRAMS + y;
436 double ptaux = fmod(taux, m_gatePeriod);
437 if (ptaux < 0)
438 ptaux = ptaux + m_gatePeriod;
439 addPseudoBMEventImpl(id, ptaux);
440 return;
441 }
442
443 // determine detector id and check limits
444 if (x >= DETECTOR_TUBES || y >= m_stride)
445 return;
446
447 // map the raw detector index to the physical model
448 size_t xid = m_mapIndex[x];
449
450 // take the modulus of the taux time to account for the
451 // longer background chopper rate
452 double ptaux = fmod(taux, m_gatePeriod);
453 if (ptaux >= m_directTaux.first && ptaux <= m_directTaux.second)
454 xid = xid + DETECTOR_TUBES;
455 else if (!(ptaux >= m_analysedTaux.first && ptaux <= m_analysedTaux.second))
456 return;
457
458 size_t id = m_stride * xid + y;
459 if (id >= m_roi.size())
460 return;
461
462 // check if neutron is in region of interest
463 if (!m_roi[id])
464 return;
465
466 // finally pass to specific handler
467 addEventImpl(id, xid, y, tdop);
468 }
469};
470
472protected:
473 // fields
474 std::vector<size_t> &m_eventCounts;
475
476 void addEventImpl(size_t id, size_t /*x*/, size_t /*y*/, double /*tof*/) override { m_eventCounts[id]++; }
477 void addPseudoBMEventImpl(size_t id, double) override {
478 if (m_includeBM) {
479 m_eventCounts[id]++;
480 }
481 }
482
483public:
484 // construction
485 EventCounter(const std::vector<bool> &roi, const std::vector<size_t> &mapIndex, const size_t stride,
486 const double framePeriod, const double gatePeriod, const TimeLimits &timeBoundary,
487 const TimeLimits &directLimits, const TimeLimits &analysedLimits, std::vector<size_t> &eventCounts,
488 bool includeBM)
489 : EventProcessor(roi, mapIndex, stride, framePeriod, gatePeriod, timeBoundary, directLimits, analysedLimits,
490 includeBM),
491 m_eventCounts(eventCounts) {}
492
493 size_t numFrames() const { return m_framesValid; }
494};
495
497protected:
498 // fields
499 std::vector<EventVector_pt> &m_eventVectors;
500 const ConvertTOF &m_convertTOF;
501 double m_tofMin;
502 double m_tofMax;
503 int64_t m_startTime;
505 const double m_binSize;
506 std::vector<size_t> m_bmCounts;
507
508 void addEventImpl(size_t id, size_t x, size_t /*y*/, double tobs) override {
509
510 // get the absolute time for the start of the frame
511 auto offset = m_startTime + frameStart();
512
513 // convert observation time to tof and set the pulse time
514 // relative to the start of the doppler cycle
515 double tof = tobs;
516
517 if (m_saveAsTOF) {
518 double pulse;
519 if (x < DETECTOR_TUBES)
520 std::tie(pulse, tof) = m_convertTOF.analysedTOF(id, tobs);
521 else
522 std::tie(pulse, tof) = m_convertTOF.directTOF(id, tobs);
523 offset += static_cast<int64_t>(pulse * 1e3);
524 }
525
526 if (m_tofMin > tof)
527 m_tofMin = tof;
528 if (m_tofMax < tof)
529 m_tofMax = tof;
530
531 auto ev = Types::Event::TofEvent(tof, Types::Core::DateAndTime(offset));
532 m_eventVectors[id]->emplace_back(ev);
533 }
534
535 void addPseudoBMEventImpl(size_t id, double tobs) override {
536 // get the absolute time for the start of the frame
537 if (m_includeBM) {
538 auto offset = m_startTime + frameStart();
539 auto ev = Types::Event::TofEvent(tobs, Types::Core::DateAndTime(offset));
540 m_eventVectors[id]->emplace_back(ev);
541 }
542
543 // add to the binned counts
544 auto index = static_cast<size_t>(tobs / m_binSize);
545 m_bmCounts[index] += 1;
546 }
547
548public:
549 EventAssigner(const std::vector<bool> &roi, const std::vector<size_t> &mapIndex, const size_t stride,
550 const double framePeriod, const double gatePeriod, const TimeLimits &timeBoundary,
551 const TimeLimits &directLimits, const TimeLimits &analysedLimits, ConvertTOF &convert,
552 std::vector<EventVector_pt> &eventVectors, int64_t startTime, bool saveAsTOF, bool includeBM)
553 : EventProcessor(roi, mapIndex, stride, framePeriod, gatePeriod, timeBoundary, directLimits, analysedLimits,
554 includeBM),
555 m_eventVectors(eventVectors), m_convertTOF(convert), m_tofMin(std::numeric_limits<double>::max()),
556 m_tofMax(std::numeric_limits<double>::min()), m_startTime(startTime), m_saveAsTOF(saveAsTOF),
557 m_binSize(gatePeriod / BEAM_MONITOR_BINS), m_bmCounts(BEAM_MONITOR_BINS, 0) {}
558
559 double tofMin() const { return m_tofMin <= m_tofMax ? m_tofMin : 0.0; }
560 double tofMax() const { return m_tofMin <= m_tofMax ? m_tofMax : 0.0; }
561 const std::vector<size_t> &beamMonitorCounts() const { return m_bmCounts; }
562 double binSize() const { return m_binSize; }
563 size_t numBins() const { return BEAM_MONITOR_BINS; }
564 size_t bmCounts() const { return std::accumulate(m_bmCounts.begin(), m_bmCounts.end(), (size_t)0); }
565};
566
567template <typename EP>
568void loadEvents(API::Progress &prog, const char *progMsg, const std::string &eventFile, EP &eventProcessor) {
569
570 using namespace ANSTO;
571
572 prog.doReport(progMsg);
573
574 FileLoader loader(eventFile.c_str());
575
576 // for progress notifications
577 ANSTO::ProgressTracker progTracker(prog, progMsg, loader.size(), Progress_LoadBinFile);
578
579 ReadEventFile(loader, eventProcessor, progTracker, 100, false);
580}
581
582} // namespace EMU
583
586template <typename FD> void LoadEMU<FD>::init(bool hdfLoader) {
587
588 // Specify file extensions which can be associated with a specific file.
589 std::vector<std::string> exts;
590
591 // Declare the Filename algorithm property. Mandatory. Sets the path to the
592 // file to load.
593 exts.clear();
594 if (hdfLoader)
595 exts.emplace_back(".hdf");
596 else
597 exts.emplace_back(".tar");
598 Base::declareProperty(std::make_unique<API::FileProperty>(FilenameStr, "", API::FileProperty::Load, exts),
599 "The input filename of the stored data");
600
601 if (hdfLoader) {
602 Base::declareProperty(PathToBinaryStr, "",
603 "Relative or absolute path to the compressed binary\n"
604 "event file linked to the HDF file, eg /storage/data/");
605 }
606
607 // mask
608 exts.clear();
609 exts.emplace_back(".xml");
610 Base::declareProperty(std::make_unique<API::FileProperty>(MaskStr, "", API::FileProperty::OptionalLoad, exts),
611 "The input filename of the mask data");
612
613 Base::declareProperty(SelectDetectorTubesStr, "",
614 "Comma separated range of detectors tubes to be loaded,\n"
615 " eg 16,19-45,47");
616
617 Base::declareProperty(
618 std::make_unique<API::WorkspaceProperty<API::IEventWorkspace>>("OutputWorkspace", "", Kernel::Direction::Output));
619
620 if (hdfLoader) {
621 Base::declareProperty(SelectDatasetStr, 0, "Select the index for the dataset to be loaded.");
622 }
623
624 Base::declareProperty(OverrideDopplerFreqStr, EMPTY_DBL(), "Override the Doppler frequency, in Hertz.");
625
626 Base::declareProperty(OverrideDopplerPhaseStr, EMPTY_DBL(), "Override the Doppler phase, in degrees.");
627
628 Base::declareProperty(CalibrateDopplerPhaseStr, false,
629 "Calibrate the Doppler phase prior to TOF conversion,\n"
630 "ignored if imported as Doppler time or phase entered");
631
632 Base::declareProperty(RawDopplerTimeStr, false,
633 "Import file as observed time relative the Doppler\n"
634 "drive, in microsecs.");
635
636 Base::declareProperty(IncludePseudoBMStr, false, "Include the individual beam monitor events as spectra.");
637
638 Base::declareProperty(FilterByTimeStartStr, 0.0,
639 "Only include events after the provided start time, in "
640 "seconds (relative to the start of the run).");
641
642 Base::declareProperty(FilterByTimeStopStr, EMPTY_DBL(),
643 "Only include events before the provided stop time, in "
644 "seconds (relative to the start of the run).");
645
646 std::string grpOptional = "Filters";
647 Base::setPropertyGroup(FilterByTimeStartStr, grpOptional);
648 Base::setPropertyGroup(FilterByTimeStopStr, grpOptional);
649}
650
652template <typename FD> void LoadEMU<FD>::createWorkspace(const std::string &title) {
653
654 // Create the workspace
655 m_localWorkspace = std::make_shared<DataObjects::EventWorkspace>();
656 m_localWorkspace->initialize(HISTOGRAMS, 2, 1);
657
658 // set the units
659 m_localWorkspace->getAxis(0)->unit() = Kernel::UnitFactory::Instance().create("TOF");
660 m_localWorkspace->setYUnit("Counts");
661
662 // set title
663 m_localWorkspace->setTitle(title);
664}
665
674template <typename FD> void LoadEMU<FD>::exec(const std::string &hdfFile, const std::string &eventFile) {
675
676 namespace fs = std::filesystem;
677
678 // Create workspace
679 // ----------------
680 fs::path p = hdfFile;
681 for (; !p.extension().empty();)
682 p = p.stem();
683 std::string title = p.generic_string();
684 createWorkspace(title);
685 API::LogManager &logManager = m_localWorkspace->mutableRun();
686 API::Progress prog(this, 0.0, 1.0, Progress_Total);
687
688 // Load instrument and workspace properties
689 // ----------------------------------------
690 logManager.addProperty(SelectDatasetStr, m_datasetIndex);
691 loadParameters(hdfFile, logManager);
692 prog.doReport("creating instrument");
693 loadInstrument();
694
695 // Get the region of interest and filters and save to log
696 //
697 std::string maskfile = Base::getPropertyValue(MaskStr);
698 std::string seltubes = Base::getPropertyValue(SelectDetectorTubesStr);
699 logManager.addProperty(SelectDetectorTubesStr, seltubes);
700 logManager.addProperty(MaskStr, maskfile);
701
702 std::vector<bool> roi = createRoiVector(seltubes, maskfile);
703 double timeMaxBoundary = Base::getProperty(FilterByTimeStopStr);
704 if (Base::isEmpty(timeMaxBoundary))
705 timeMaxBoundary = std::numeric_limits<double>::infinity();
706 TimeLimits timeBoundary(Base::getProperty(FilterByTimeStartStr), timeMaxBoundary);
707
708 // lambda to simplify loading instrument parameters
709 auto instr = m_localWorkspace->getInstrument();
710 auto iparam = [&instr](const std::string &tag) { return instr->getNumberParameter(tag)[0]; };
711
712 // Update the neutronic positions for the indirect detectors
713 // noting that the vertical and horizontal are a continuous
714 // sequence starting from 0
715 //
716 double sampleAnalyser = iparam("SampleAnalyser");
717 auto endID = static_cast<detid_t>(DETECTOR_TUBES * PIXELS_PER_TUBE);
718 for (detid_t detID = 0; detID < endID; ++detID)
719 updateNeutronicPostions(detID, sampleAnalyser);
720
721 // get the detector map from raw input to a physical detector
722 //
723 std::string dmapStr = instr->getParameterAsString("DetectorMap");
724 std::vector<size_t> detMapIndex = std::vector<size_t>(DETECTOR_TUBES, 0);
725 mapRangeToIndex(dmapStr, detMapIndex, [](size_t n) { return n; });
726
727 // Collect the L2 distances, Doppler characteristics and
728 // initiate the TOF converter
729 //
730 loadDetectorL2Values();
731 loadDopplerParameters(logManager);
732 double v2 = iparam("AnalysedV2"); // analysed velocity in metres per sec
733 double framePeriod = 1.0e6 / m_dopplerFreq; // period and max direct as microsec
734 double sourceSample = iparam("SourceSample");
735 ConvertTOF convertTOF(m_dopplerAmpl * m_dopplerRun, m_dopplerFreq, m_dopplerPhase, sourceSample, v2, m_detectorL2);
736
737 // Load the events file
738 // --------------------
739 // First count the number of events to reserve memory and then assign the
740 // events to the detectors
741
742 // load events
743 size_t numberHistograms = m_localWorkspace->getNumberHistograms();
744 std::vector<EventVector_pt> eventVectors(numberHistograms, nullptr);
745 std::vector<size_t> eventCounts(numberHistograms, 0);
746
747 // Discriminating between direct and analysed is based on the auxillary time
748 // and is determined by the graphite chopper frequency and v2 which are stable
749 // so these limits are kept in the instrument parameter file. Convert from
750 // milsec to microsec.
751 TimeLimits directLimits(1000.0 * iparam("DirectTauxMin"), 1000.0 * iparam("DirectTauxMax"));
752 TimeLimits analysedLimits(1000.0 * iparam("AnalysedTauxMin"), 1000.0 * iparam("AnalysedTauxMax"));
753
754 // fabs because the value can be negative
755 double gatePeriod = 1.0e6 / fabs(logManager.getTimeSeriesProperty<double>("GraphiteChopperFrequency")->firstValue());
756
757 // count total events per pixel and reserve necessary memory
758 bool includeBM = Base::getProperty(IncludePseudoBMStr);
759 EMU::EventCounter eventCounter(roi, detMapIndex, PIXELS_PER_TUBE, framePeriod, gatePeriod, timeBoundary, directLimits,
760 analysedLimits, eventCounts, includeBM);
761 EMU::loadEvents(prog, "loading neutron counts", eventFile, eventCounter);
762 ANSTO::ProgressTracker progTracker(prog, "creating neutron event lists", numberHistograms, Progress_ReserveMemory);
763 prepareEventStorage(progTracker, eventCounts, eventVectors);
764
765 // now perform the actual event collection and TOF convert if necessary
766 // if a phase calibration is required then load it as raw doppler time
767 // perform the calibration and then convert to TOF
768 Types::Core::DateAndTime startTime(m_startRun);
769 auto start_nanosec = startTime.totalNanoseconds();
770 bool saveAsTOF = !Base::getProperty(RawDopplerTimeStr);
771 bool loadAsTOF = !m_calibrateDoppler && saveAsTOF;
772 EMU::EventAssigner eventAssigner(roi, detMapIndex, PIXELS_PER_TUBE, framePeriod, gatePeriod, timeBoundary,
773 directLimits, analysedLimits, convertTOF, eventVectors, start_nanosec, loadAsTOF,
774 includeBM);
775 EMU::loadEvents(prog, "loading neutron events (TOF)", eventFile, eventAssigner);
776
777 // determine the minimum and maximum beam rate per sec
778 auto filteredBM = runningAverage(eventAssigner.beamMonitorCounts(), BM_HALF_WINDOW);
779 auto res = std::minmax_element(filteredBM.begin(), filteredBM.end());
780 auto ratePerSec = static_cast<double>(eventAssigner.numBins()) / eventCounter.duration();
781 auto minBM = *res.first * ratePerSec;
782 auto maxBM = *res.second * ratePerSec;
783 AddSinglePointTimeSeriesProperty<double>(logManager, m_startRun, "BeamMonitorBkgRate", minBM);
784 AddSinglePointTimeSeriesProperty<double>(logManager, m_startRun, "BeamMonitorRate", maxBM);
785 AddSinglePointTimeSeriesProperty<int>(logManager, m_startRun, "MonitorCounts",
786 static_cast<int>(eventAssigner.bmCounts()));
787
788 // perform a calibration and then TOF conversion if necessary
789 // and update the tof limits
790 auto minTOF = eventAssigner.tofMin();
791 auto maxTOF = eventAssigner.tofMax();
792 if (m_calibrateDoppler) {
793 calibrateDopplerPhase(eventCounts, eventVectors);
794 if (saveAsTOF) {
795 dopplerTimeToTOF(eventVectors, minTOF, maxTOF);
796 }
797 }
798 AddSinglePointTimeSeriesProperty<double>(logManager, m_startRun, "DopplerPhase", m_dopplerPhase);
799
800 // just to make sure the bins hold it all and setup the detector masks
801 m_localWorkspace->setAllX(HistogramData::BinEdges{std::max(0.0, floor(minTOF)), maxTOF + 1});
802 setupDetectorMasks(roi);
803
804 // set log values
805 auto frame_count = eventCounter.numFrames();
806 AddSinglePointTimeSeriesProperty<int>(logManager, m_startRun, "frame_count", static_cast<int>(frame_count));
807
808 // add the scan period in secs to the log
809 auto scan_period = static_cast<double>(frame_count + 1) / m_dopplerFreq;
810 AddSinglePointTimeSeriesProperty<double>(logManager, m_startRun, "ScanPeriod", scan_period);
811
812 std::string filename = Base::getPropertyValue(FilenameStr);
813 logManager.addProperty("filename", filename);
814
815 Types::Core::time_duration duration =
816 boost::posix_time::microseconds(static_cast<boost::int64_t>(eventCounter.duration() * 1.0e6));
817 Types::Core::DateAndTime endTime(startTime + duration);
818 logManager.addProperty("end_time", endTime.toISO8601String());
819 logManager.addProperty<double>("dur", eventCounter.duration());
820
821 // Finally add the time-series parameter explicitly
822 loadEnvironParameters(hdfFile, logManager);
823
824 Base::setProperty("OutputWorkspace", m_localWorkspace);
825}
826
828template <typename FD> void LoadEMU<FD>::setupDetectorMasks(const std::vector<bool> &roi) {
829
830 // count total number of masked bins
831 size_t maskedBins = std::count_if(roi.begin(), roi.end(), [](bool v) { return !v; });
832
833 if (maskedBins > 0) {
834 // create list of masked bins
835 std::vector<size_t> maskIndexList(maskedBins);
836 size_t maskIndex = 0;
837
838 for (size_t i = 0; i != roi.size(); i++)
839 if (!roi[i])
840 maskIndexList[maskIndex++] = i;
841
842 auto maskingAlg = Base::createChildAlgorithm("MaskDetectors");
843 maskingAlg->setProperty("Workspace", m_localWorkspace);
844 maskingAlg->setProperty("WorkspaceIndexList", maskIndexList);
845 maskingAlg->executeAsChildAlg();
846 }
847}
848
851template <typename FD>
852void LoadEMU<FD>::prepareEventStorage(ANSTO::ProgressTracker &progTracker, const std::vector<size_t> &eventCounts,
853 std::vector<EventVector_pt> &eventVectors) {
854
855 size_t numberHistograms = eventCounts.size();
856 for (size_t i = 0; i != numberHistograms; ++i) {
857 DataObjects::EventList &eventList = m_localWorkspace->getSpectrum(i);
858
860 eventList.reserve(eventCounts[i]);
861
862 eventList.setDetectorID(static_cast<detid_t>(i));
863 eventList.setSpectrumNo(static_cast<detid_t>(i));
864
865 DataObjects::getEventsFrom(eventList, eventVectors[i]);
866
867 progTracker.update(i);
868 }
869 progTracker.complete();
870}
871
873template <typename FD> void LoadEMU<FD>::loadDopplerParameters(API::LogManager &logm) {
874
875 auto instr = m_localWorkspace->getInstrument();
876
877 // use nominal frequency based on amp and velocity unless overridden
878 m_dopplerAmpl = logm.getTimeSeriesProperty<double>("DopplerAmplitude")->firstValue();
879 m_dopplerRun = logm.getTimeSeriesProperty<int32_t>("DopplerRun")->firstValue();
880 m_dopplerFreq = Base::getProperty(OverrideDopplerFreqStr);
881 if (Base::isEmpty(m_dopplerFreq)) {
882 auto doppVel = logm.getTimeSeriesProperty<double>("DopplerVelocity")->firstValue();
883 m_dopplerFreq = 0.5 * doppVel / (M_PI * m_dopplerAmpl);
884 }
885 AddSinglePointTimeSeriesProperty<double>(logm, m_startRun, "DopplerFrequency", m_dopplerFreq);
886
887 m_dopplerPhase = Base::getProperty(OverrideDopplerPhaseStr);
888 m_calibrateDoppler = Base::getProperty(CalibrateDopplerPhaseStr) && Base::isEmpty(m_dopplerPhase);
889 if (Base::isEmpty(m_dopplerPhase)) {
890 // sinusoidal motion crossing a threshold with a delay
891 double doppThreshold = instr->getNumberParameter("DopplerReferenceThreshold")[0];
892 double doppDelay = instr->getNumberParameter("DopplerReferenceDelay")[0];
893 m_dopplerPhase = 180.0 - asin(0.001 * doppThreshold / m_dopplerAmpl) * 180.0 / M_PI + doppDelay * m_dopplerFreq;
894 }
895
896 // problem adding 'bool' to log
897 int32_t calPhase = (m_calibrateDoppler ? 1 : 0);
898 logm.addProperty("CalibratePhase", calPhase);
899}
900
903template <typename FD>
904void LoadEMU<FD>::calibrateDopplerPhase(const std::vector<size_t> &eventCounts,
905 const std::vector<EventVector_pt> &eventVectors) {
906
907 // get the doppler parameters
908 auto instr = m_localWorkspace->getInstrument();
909 double v2 = instr->getNumberParameter("AnalysedV2")[0];
910 double l1 = instr->getNumberParameter("SourceSample")[0];
911
912 // get the number of analysed events and initial doppler time
913 auto startID = static_cast<size_t>(HORIZONTAL_TUBES * PIXELS_PER_TUBE);
914 auto endID = static_cast<size_t>(DETECTOR_TUBES * PIXELS_PER_TUBE);
915 size_t numEvents = std::accumulate(eventCounts.begin() + startID, eventCounts.begin() + endID, size_t{0});
916 if (numEvents == 0)
917 throw std::runtime_error("no analysed events for phase calibration");
918 std::vector<double> nVel(numEvents);
919 std::vector<size_t> nMap(numEvents);
920 std::vector<bool> nCnd(numEvents);
921 constexpr size_t NHIST = 100;
922 std::vector<int> histogram(NHIST + 1, 0);
923
924 // define the cost function to optimize phase
925 auto costFn = [&, this](double phase) {
926 ConvertTOF convTOF(m_dopplerAmpl * m_dopplerRun, m_dopplerFreq, phase, l1, v2, m_detectorL2);
927
928 // convert each analysed event to source velocity
929 size_t ix = 0;
930 double tof, pulse;
931 for (size_t i = startID; i < endID; i++) {
932 for (auto const &x : *eventVectors[i]) {
933 std::tie(pulse, tof) = convTOF.analysedTOF(i, x.tof());
934 auto tof1 = 1e-6 * tof - m_detectorL2[i] / v2;
935 nVel[ix++] = l1 / tof1;
936 }
937 }
938
939 // now histogram the data and create the map from velocity to hist
940 auto ixlim = std::minmax_element(nVel.begin(), nVel.end());
941 auto vmin = nVel[ixlim.first - nVel.begin()];
942 auto vmax = nVel[ixlim.second - nVel.begin()];
943 int maxHist = 0;
944 std::fill(histogram.begin(), histogram.end(), 0);
945 auto delta = (vmax - vmin) / NHIST;
946 for (size_t i = 0; i < numEvents; i++) {
947 auto v = nVel[i];
948 auto j = static_cast<size_t>(std::floor((v - vmin) / delta));
949 histogram[j]++;
950 if (histogram[j] > maxHist)
951 maxHist = histogram[j];
952 nMap[i] = j;
953 }
954
955 // determine the points above the 25% threshold
956 auto minLevel = static_cast<int>(maxHist / 4);
957 for (size_t i = 0; i < numEvents; i++) {
958 nCnd[i] = (histogram[nMap[i]] >= minLevel ? true : false);
959 }
960
961 // calculate the standard deviation for the points above the threshold
962 auto cost = maskedStdev(nVel, nCnd);
963 return cost;
964 };
965
966 // call the optimizer and update the doppler phase value
967 // limit the optimization to 30 iterations
968 int bits = std::numeric_limits<double>::digits;
969 boost::uintmax_t itn = 30;
970 using boost::math::tools::brent_find_minima;
971 auto minPhase = m_dopplerPhase - 5.0;
972 auto maxPhase = m_dopplerPhase + 5.0;
973 auto r = brent_find_minima(costFn, minPhase, maxPhase, bits, itn);
974 m_dopplerPhase = r.first;
975}
976
979template <typename FD>
980void LoadEMU<FD>::dopplerTimeToTOF(std::vector<EventVector_pt> const &eventVectors, double &minTOF, double &maxTOF) {
981
982 // get the doppler parameters and initialise TOD converter
983 auto const instr = m_localWorkspace->getInstrument();
984 double v2 = instr->getNumberParameter("AnalysedV2")[0];
985 double l1 = instr->getNumberParameter("SourceSample")[0];
986 ConvertTOF convTOF(m_dopplerAmpl * m_dopplerRun, m_dopplerFreq, m_dopplerPhase, l1, v2, m_detectorL2);
987
988 // run through all the events noting that analysed event are in
989 // the bottom half of the detector ids
990 auto start = true;
991 auto directID = static_cast<size_t>(DETECTOR_TUBES * PIXELS_PER_TUBE);
992 for (size_t id = 0; id < eventVectors.size(); id++) {
993 for (auto &x : *eventVectors[id]) {
994 double tof, pulse;
995 if (id < directID)
996 std::tie(pulse, tof) = convTOF.analysedTOF(id, x.tof());
997 else
998 std::tie(pulse, tof) = convTOF.directTOF(id, x.tof());
999
1000 // update the pulse time and tof
1001 int64_t pulseTime = x.pulseTime().totalNanoseconds();
1002 pulseTime += static_cast<int64_t>(pulse * 1000);
1003 x = Types::Event::TofEvent(tof, Types::Core::DateAndTime(pulseTime));
1004
1005 if (start) {
1006 minTOF = maxTOF = x.tof();
1007 start = false;
1008 } else {
1009 minTOF = std::min(minTOF, x.tof());
1010 maxTOF = std::max(maxTOF, x.tof());
1011 }
1012 }
1013 }
1014}
1015
1017template <typename FD> void LoadEMU<FD>::loadDetectorL2Values() {
1018
1019 m_detectorL2 = std::vector<double>(HISTOGRAMS);
1020 const auto &detectorInfo = m_localWorkspace->detectorInfo();
1021 auto detIDs = detectorInfo.detectorIDs();
1022 for (const auto detID : detIDs) {
1023 auto ix = detectorInfo.indexOf(detID);
1024 double l2 = detectorInfo.l2(ix);
1025 m_detectorL2[detID] = l2;
1026 }
1027}
1028
1031template <typename FD> void LoadEMU<FD>::updateNeutronicPostions(detid_t detID, double sampleAnalyser) {
1032
1033 Geometry::Instrument_const_sptr instrument = m_localWorkspace->getInstrument();
1034 auto &compInfo = m_localWorkspace->mutableComponentInfo();
1035
1036 try {
1037 auto component = instrument->getDetector(detID);
1038 double rho, theta, phi;
1039 V3D position = component->getPos();
1040 position.getSpherical(rho, theta, phi);
1041
1042 double scale = -(2 * sampleAnalyser + rho) / rho;
1043 position *= scale;
1044
1045 const auto componentIndex = compInfo.indexOf(component->getComponentID());
1046 compInfo.setPosition(componentIndex, position);
1047 } catch (const std::runtime_error &) {
1048 // just continue with the remainder
1049 }
1050}
1051
1054template <typename FD>
1055std::vector<bool> LoadEMU<FD>::createRoiVector(const std::string &selected, const std::string &maskfile) {
1056
1057 std::vector<bool> result(HISTOGRAMS, true);
1058
1059 // turn off pixels linked to missing tubes
1060 if (!selected.empty()) {
1061 std::vector<bool> tubes(HISTO_BINS_X, false);
1062 mapRangeToIndex(selected, tubes, [](size_t) { return true; });
1063 for (size_t i = 0; i < HISTO_BINS_X; i++) {
1064 if (tubes[i] == false) {
1065 for (size_t j = 0; j < PIXELS_PER_TUBE; j++) {
1066 result[i * PIXELS_PER_TUBE + j] = false;
1067 }
1068 }
1069 }
1070 }
1071
1072 if (maskfile.length() == 0)
1073 return result;
1074
1075 std::ifstream input(maskfile.c_str());
1076 if (!input.good())
1077 throw std::invalid_argument("invalid mask file");
1078
1079 std::string line;
1080 while (std::getline(input, line)) {
1081 auto i0 = line.find("<detids>");
1082 auto iN = line.find("</detids>");
1083
1084 if ((i0 != std::string::npos) && (iN != std::string::npos) && (i0 < iN)) {
1085 line = line.substr(i0 + 8, iN - i0 - 8); // 8 = len("<detids>")
1086 mapRangeToIndex(line, result, [](size_t) { return false; });
1087 }
1088 }
1089
1090 return result;
1091}
1092
1094template <typename FD> void LoadEMU<FD>::loadParameters(const std::string &hdfFile, API::LogManager &logm) {
1095
1096 Nexus::NXRoot root(hdfFile);
1097 Nexus::NXEntry entry = root.openFirstEntry();
1098
1099 MapNeXusToProperty<std::string>(entry, "sample/name", "unknown", logm, "SampleName", "", 0);
1100 MapNeXusToProperty<std::string>(entry, "sample/description", "unknown", logm, "SampleDescription", "", 0);
1101
1102 // if dataset index > 0 need to add an offset to the start time
1103 Types::Core::DateAndTime startTime(GetNeXusValue<std::string>(entry, "start_time", "2000-01-01T00:00:00", 0));
1104 if (m_datasetIndex > 0) {
1105 auto baseTime = GetNeXusValue<int32_t>(entry, "instrument/detector/start_time", 0, 0);
1106 auto nthTime = GetNeXusValue<int32_t>(entry, "instrument/detector/start_time", 0, m_datasetIndex);
1107
1108 Types::Core::time_duration duration =
1109 boost::posix_time::microseconds(static_cast<boost::int64_t>((nthTime - baseTime) * 1.0e6));
1110 Types::Core::DateAndTime startDataset(startTime + duration);
1111 m_startRun = startDataset.toISO8601String();
1112 } else {
1113 m_startRun = startTime.toISO8601String();
1114 }
1115 logm.addProperty("start_time", m_startRun);
1116
1117 MapNeXusToSeries<double>(entry, "instrument/doppler/ctrl/amplitude", 75.0, logm, m_startRun, "DopplerAmplitude",
1118 0.001, m_datasetIndex);
1119 MapNeXusToSeries<double>(entry, "instrument/doppler/ctrl/velocity", 4.7, logm, m_startRun, "DopplerVelocity", 1,
1120 m_datasetIndex);
1121 MapNeXusToSeries<int>(entry, "instrument/doppler/ctrl/run_cmd", 1, logm, m_startRun, "DopplerRun", 1, m_datasetIndex);
1122
1123 MapNeXusToSeries<double>(entry, "instrument/chpr/background/actspeed", 1272.8, logm, m_startRun,
1124 "BackgroundChopperFrequency", 1.0 / 60, 0);
1125 MapNeXusToSeries<double>(entry, "instrument/chpr/graphite/actspeed", 2545.6, logm, m_startRun,
1126 "GraphiteChopperFrequency", 1.0 / 60, 0);
1127 // hz tube gap or equivalent to be added later - reverts to default
1128 MapNeXusToSeries<double>(entry, "instrument/hztubegap", 0.02, logm, m_startRun, "horizontal_tubes_gap", 1.0, 0);
1129 // add the reactor power to the log
1130 MapNeXusToSeries<double>(entry, "instrument/source/power", 20.0, logm, m_startRun, "ReactorPower", 1.0,
1131 m_datasetIndex);
1132 // fix for source position when loading IDF
1133 MapNeXusToProperty<double>(entry, "instrument/doppler/tosource", 2.035, logm, "SourceSample", 1.0, 0);
1134}
1135
1138template <typename FD> void LoadEMU<FD>::loadEnvironParameters(const std::string &hdfFile, API::LogManager &logm) {
1139
1140 Nexus::NXRoot root(hdfFile);
1141 Nexus::NXEntry entry = root.openFirstEntry();
1142 auto time_str = logm.getPropertyValueAsType<std::string>("end_time");
1143
1144 // load the environment variables for the dataset loaded
1145 auto tags = ANSTO::filterDatasets(entry, "data/", "^[A-Z]{1,3}[0-9]{1,3}[A-Z]{1,3}[0-9]{1,3}$");
1146 for (const auto &tag : tags) {
1147 MapNeXusToSeries<double>(entry, "data/" + tag, 0.0, logm, time_str, "env_" + tag, 1.0, m_datasetIndex);
1148 }
1149}
1150
1152template <typename FD> void LoadEMU<FD>::loadInstrument() {
1153
1154 // loads the IDF and parameter file
1155 auto loadInstrumentAlg = Base::createChildAlgorithm("LoadInstrument");
1156 loadInstrumentAlg->setProperty("Workspace", m_localWorkspace);
1157 loadInstrumentAlg->setPropertyValue("InstrumentName", "EMUau");
1158 loadInstrumentAlg->setProperty("RewriteSpectraMap", Mantid::Kernel::OptionalBool(false));
1159 loadInstrumentAlg->executeAsChildAlg();
1160}
1161
1162//--------- explicit template instantiation -----------
1163
1164// Instantiate base class for LoadEMU's
1165template class LoadEMU<Kernel::FileDescriptor>;
1166template class LoadEMU<Nexus::NexusDescriptor>;
1167
1168// -------- EMU Hdf loader -----------------------
1169
1171int LoadEMUHdf::version() const { return 1; }
1172
1174const std::vector<std::string> LoadEMUHdf::seeAlso() const { return {"Load", "LoadQKK"}; }
1176const std::string LoadEMUHdf::category() const { return "DataHandling\\ANSTO"; }
1177
1179const std::string LoadEMUHdf::name() const { return "LoadEMUHdf"; }
1180
1182const std::string LoadEMUHdf::summary() const { return "Loads an EMU Hdf and linked event file into a workspace."; }
1183
1187 if (descriptor.extension() != ".hdf")
1188 return 0;
1189
1190 if (descriptor.isEntry("/entry1/site_name") && descriptor.isEntry("/entry1/instrument/doppler/ctrl/velocity") &&
1191 descriptor.isEntry("/entry1/instrument/doppler/ctrl/amplitude") &&
1192 descriptor.isEntry("/entry1/instrument/detector/daq_dirname") &&
1193 descriptor.isEntry("/entry1/instrument/detector/dataset_number") &&
1194 descriptor.isEntry("/entry1/data/hmm_total_t_ds0") && descriptor.isEntry("/entry1/data/hmm_total_t_ds1") &&
1195 descriptor.isEntry("/entry1/data/hmm_total_xt_ds0") && descriptor.isEntry("/entry1/data/hmm_total_xt_ds1")) {
1196 return 80;
1197 } else {
1198 return 0;
1199 }
1200}
1201
1205
1208// exec() function that works with the two files.
1210
1211 namespace fs = std::filesystem;
1212
1213 // Open the hdf file and find the dirname and dataset number
1214 std::string hdfFile = Base::getPropertyValue(FilenameStr);
1215 std::string evtPath = Base::getPropertyValue(PathToBinaryStr);
1216 if (evtPath.empty())
1217 evtPath = "./";
1218
1219 // if relative ./ or ../ then append to the directory for the hdf file
1220 if (evtPath.rfind("./") == 0 || evtPath.rfind("../") == 0) {
1221 fs::path hp(hdfFile);
1222 evtPath = fs::canonical(hp.parent_path() / evtPath).string();
1223 }
1224
1225 // dataset index to be loaded
1226 m_datasetIndex = Base::getProperty(SelectDatasetStr);
1227
1228 // if path provided build the file path from the directory name and dataset
1229 // number from the hdf file, however if this is not a valid path then try
1230 // the basename with a '.bin' extension
1231 if (fs::is_directory(evtPath)) {
1232 Nexus::NXRoot root(hdfFile);
1233 Nexus::NXEntry entry = root.openFirstEntry();
1234 auto eventDir = GetNeXusValue<std::string>(entry, "instrument/detector/daq_dirname", "./", 0);
1235 auto dataset = GetNeXusValue<int32_t>(entry, "instrument/detector/dataset_number", 0, m_datasetIndex);
1236 if (dataset < 0) {
1237 g_log.warning("Negative dataset index recorded in HDF, reset to zero!");
1238 dataset = 0;
1239 }
1240
1241 // build the path to the event file using the standard storage convention at ansto:
1242 // 'relpath/[daq_dirname]/DATASET_[n]/EOS.bin'
1243 // but if the file is missing, try relpath/{source}.bin
1244 fs::path filePath = fs::absolute(fs::path(evtPath) / eventDir / ("DATASET_" + std::to_string(dataset)) / "EOS.bin");
1245 if (!fs::is_regular_file(filePath)) {
1246 filePath = fs::absolute(fs::path(hdfFile).replace_extension(".bin"));
1247 }
1248 evtPath = filePath.generic_string();
1249 }
1250
1251 // finally check that the event file exists
1252 if (!fs::is_regular_file(evtPath)) {
1253 std::string msg = "Check path, cannot open binary event file: " + evtPath;
1254 throw std::runtime_error(msg);
1255 }
1256
1257 LoadEMU<Nexus::NexusDescriptor>::exec(hdfFile, evtPath);
1258}
1259
1260// -------- EMU Tar loader -----------------------
1261
1263int LoadEMUTar::version() const { return 1; }
1264
1266const std::vector<std::string> LoadEMUTar::seeAlso() const { return {"Load", "LoadQKK"}; }
1268const std::string LoadEMUTar::category() const { return "DataHandling\\ANSTO"; }
1269
1271const std::string LoadEMUTar::name() const { return "LoadEMU"; }
1272
1274const std::string LoadEMUTar::summary() const {
1275 return "Loads an EMU tar file, containing the Hdf and event file, into a "
1276 "workspace.";
1277}
1278
1282 if (descriptor.extension() != ".tar")
1283 return 0;
1284
1285 ANSTO::Tar::File file(descriptor.filename());
1286 if (!file.good())
1287 return 0;
1288
1289 size_t hdfFiles = 0;
1290 size_t binFiles = 0;
1291 const std::vector<std::string> &subFiles = file.files();
1292 for (const auto &subFile : subFiles) {
1293 auto len = subFile.length();
1294 if ((len > 4) && (subFile.find_first_of("\\/", 0, 2) == std::string::npos)) {
1295 if ((subFile.rfind(".hdf") == len - 4) && (subFile.compare(0, 3, "EMU") == 0))
1296 hdfFiles++;
1297 else if (subFile.rfind(".bin") == len - 4)
1298 binFiles++;
1299 }
1300 }
1301
1302 return (hdfFiles == 1) && (binFiles == 1) ? 50 : 0;
1303}
1304
1308
1313
1314 // Opens the tar and extracts the hdf and event data to temporary files
1315 std::string filename = Base::getPropertyValue(FilenameStr);
1316 ANSTO::Tar::File tarFile(filename);
1317 if (!tarFile.good())
1318 throw std::invalid_argument("invalid EMU tar file");
1319
1320 // dataset selection not supported in tar version - order is not guaranteed
1321 m_datasetIndex = 0;
1322
1323 // lambda functions to find the first file of extension and to extract
1324 // the file
1325 const std::vector<std::string> &files = tarFile.files();
1326 auto selectFile = [&](const std::string &ext) {
1327 auto itf = std::find_if(files.cbegin(), files.cend(),
1328 [&ext](const std::string &file) { return file.rfind(ext) == file.length() - 4; });
1329 if (itf == files.end())
1330 throw std::runtime_error("missing tar file data");
1331 else
1332 tarFile.select(itf->c_str());
1333 };
1334 auto extractFile = [&](Poco::TemporaryFile &tfile) {
1335 std::shared_ptr<FILE> handle(fopen(tfile.path().c_str(), "wb"), fclose);
1336 if (handle) {
1337 // copy content
1338 char buffer[4096];
1339 size_t bytesRead;
1340 while (0 != (bytesRead = tarFile.read(buffer, sizeof(buffer))))
1341 fwrite(buffer, bytesRead, 1, handle.get());
1342 handle.reset();
1343 }
1344 };
1345
1346 // extract hdf file into tmp file
1347 selectFile(".hdf");
1348 Poco::TemporaryFile hdfFile;
1349 extractFile(hdfFile);
1350
1351 // extract the event file
1352 selectFile(".bin");
1353 Poco::TemporaryFile eventFile;
1354 extractFile(eventFile);
1355
1356 // call the common loader
1357 LoadEMU<Kernel::FileDescriptor>::exec(hdfFile.path(), eventFile.path());
1358}
1359
1360// register the algorithms into the AlgorithmFactory
1363
1364} // namespace Mantid::DataHandling
std::string name
Definition Run.cpp:60
double value
The value of the point.
Definition FitMW.cpp:51
double position
Definition GetAllEi.cpp:154
std::map< DeltaEMode::Type, std::string > index
const double m_L0
Definition LoadEMU.cpp:224
size_t _size
Definition LoadEMU.cpp:334
const double m_A
Definition LoadEMU.cpp:226
std::ifstream _ifs
Definition LoadEMU.cpp:333
const double m_phi
Definition LoadEMU.cpp:223
const double m_w
Definition LoadEMU.cpp:222
const double m_v2
Definition LoadEMU.cpp:225
const std::vector< double > & m_L2
Definition LoadEMU.cpp:227
#define fabs(x)
Definition Matrix.cpp:22
int count
counter
Definition Matrix.cpp:37
std::vector< T > const * vec
#define DECLARE_NEXUS_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro wh...
#define DECLARE_FILELOADER_ALGORITHM(classname)
DECLARE_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro when wri...
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 void init()=0
Virtual method - must be overridden by concrete algorithm.
virtual void exec()=0
Virtual method - must be overridden by concrete algorithm.
@ 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:44
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition LogManager.h:91
HeldType getPropertyValueAsType(const std::string &name) const
Get the value of a property as the given TYPE.
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.
helper class to keep track of progress
size_t read(void *dst, size_t size)
const std::vector< std::string > & files() const
std::vector< EventVector_pt > & m_eventVectors
Definition LoadEMU.cpp:499
const std::vector< size_t > & beamMonitorCounts() const
Definition LoadEMU.cpp:561
EventAssigner(const std::vector< bool > &roi, const std::vector< size_t > &mapIndex, const size_t stride, const double framePeriod, const double gatePeriod, const TimeLimits &timeBoundary, const TimeLimits &directLimits, const TimeLimits &analysedLimits, ConvertTOF &convert, std::vector< EventVector_pt > &eventVectors, int64_t startTime, bool saveAsTOF, bool includeBM)
Definition LoadEMU.cpp:549
void addEventImpl(size_t id, size_t x, size_t, double tobs) override
Definition LoadEMU.cpp:508
void addPseudoBMEventImpl(size_t id, double tobs) override
Definition LoadEMU.cpp:535
EventCounter(const std::vector< bool > &roi, const std::vector< size_t > &mapIndex, const size_t stride, const double framePeriod, const double gatePeriod, const TimeLimits &timeBoundary, const TimeLimits &directLimits, const TimeLimits &analysedLimits, std::vector< size_t > &eventCounts, bool includeBM)
Definition LoadEMU.cpp:485
void addEventImpl(size_t id, size_t, size_t, double) override
Definition LoadEMU.cpp:476
void addPseudoBMEventImpl(size_t id, double) override
Definition LoadEMU.cpp:477
std::vector< size_t > & m_eventCounts
Definition LoadEMU.cpp:474
const std::vector< bool > & m_roi
Definition LoadEMU.cpp:374
void addEvent(size_t x, size_t p, double tdop, double taux)
Definition LoadEMU.cpp:424
const std::vector< size_t > & m_mapIndex
Definition LoadEMU.cpp:375
virtual void addEventImpl(size_t id, size_t x, size_t y, double tof)=0
EventProcessor(const std::vector< bool > &roi, const std::vector< size_t > &mapIndex, const size_t stride, const double framePeriod, const double gatePeriod, TimeLimits timeBoundary, TimeLimits directLimits, TimeLimits analysedLimits, bool includeBM)
Definition LoadEMU.cpp:395
virtual void addPseudoBMEventImpl(size_t id, double tobs)=0
LoadEMUHdf : Loads an ANSTO EMU Hdf and linked event file into a workspace.
Definition LoadEMU.h:156
const std::vector< std::string > seeAlso() const override
Similar algorithms.
Definition LoadEMU.cpp:1174
void exec() override
Execute the algorithm.
Definition LoadEMU.cpp:1209
int version() const override
Algorithm's version for identification.
Definition LoadEMU.cpp:1171
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
Definition LoadEMU.cpp:1182
const std::string name() const override
Algorithms name for identification.
Definition LoadEMU.cpp:1179
const std::string category() const override
Algorithm's category for identification.
Definition LoadEMU.cpp:1176
void init() override
Initialise the algorithm and declare the properties for the nexus descriptor.
Definition LoadEMU.cpp:1204
int confidence(Nexus::NexusDescriptor &descriptor) const override
Return the confidence as an integer value that this algorithm can load the file descriptor.
Definition LoadEMU.cpp:1186
LoadEMUTar : Loads a merged ANSTO EMU Hdf and event file into a workspace.
Definition LoadEMU.h:119
int version() const override
Algorithm's version for identification.
Definition LoadEMU.cpp:1263
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
Definition LoadEMU.cpp:1274
const std::string name() const override
Algorithms name for identification.
Definition LoadEMU.cpp:1271
int confidence(Kernel::FileDescriptor &descriptor) const override
Return the confidence as an integer value that this algorithm can load the file descriptor.
Definition LoadEMU.cpp:1281
const std::string category() const override
Algorithm's category for identification.
Definition LoadEMU.cpp:1268
void exec() override
Execute the algorithm.
Definition LoadEMU.cpp:1312
const std::vector< std::string > seeAlso() const override
Similar algorithms.
Definition LoadEMU.cpp:1266
void init() override
Initialise the algorithm and declare the standard properties for the general file descriptor.
Definition LoadEMU.cpp:1307
void prepareEventStorage(ANSTO::ProgressTracker &prog, const std::vector< size_t > &eventCounts, std::vector< EventVector_pt > &eventVectors)
Allocate space for the event storage in eventVectors after the eventCounts have been determined.
Definition LoadEMU.cpp:852
void calibrateDopplerPhase(const std::vector< size_t > &eventCounts, const std::vector< EventVector_pt > &eventVectors)
Calibrate the doppler phase based on the analysed events using the eventCounts and eventVectors.
Definition LoadEMU.cpp:904
std::vector< bool > createRoiVector(const std::string &seltubes, const std::string &maskfile)
Region of interest is defined by the selected detectors and the maskfile.
Definition LoadEMU.cpp:1055
void loadInstrument()
Load the instrument definition.
Definition LoadEMU.cpp:1152
void loadParameters(const std::string &hdfFile, API::LogManager &logm)
Load parameters from input hdfFile and save to the log manager, logm.
Definition LoadEMU.cpp:1094
void loadDetectorL2Values()
Recovers the L2 neutronic distance for each detector.
Definition LoadEMU.cpp:1017
void setupDetectorMasks(const std::vector< bool > &roi)
Set up the detector masks to the region of interest roi.
Definition LoadEMU.cpp:828
void updateNeutronicPostions(detid_t detID, double sampleAnalyser)
Update the neutronic position for the detID using the distance from the source and the sample to anal...
Definition LoadEMU.cpp:1031
void loadDopplerParameters(API::LogManager &logm)
Get the Doppler parameters and record to the log manager, logm.
Definition LoadEMU.cpp:873
void createWorkspace(const std::string &title)
Creates an event workspace and sets the title.
Definition LoadEMU.cpp:652
void loadEnvironParameters(const std::string &hdfFile, API::LogManager &logm)
Load the environment variables from the hdfFile and save as time series to the log manager,...
Definition LoadEMU.cpp:1138
void dopplerTimeToTOF(const std::vector< EventVector_pt > &eventVectors, double &minTOF, double &maxTOF)
Convert the doppler time to TOF for all the events in eventVectors and time of flight range as minTOF...
Definition LoadEMU.cpp:980
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.
Defines a wrapper around an open file.
const std::string & filename() const
Access the filename.
const std::string & extension() const
Access the file extension.
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
OptionalBool : Tri-state bool.
A specialised Property class for holding a series of time-value pairs.
Class for 3D vectors.
Definition V3D.h:34
void getSpherical(double &R, double &theta, double &phi) const noexcept
Return the vector's position in spherical coordinates.
Definition V3D.cpp:116
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.
bool isEntry(const std::string &entryName, const std::string &groupClass) const noexcept
Checks if a full-address entry exists for a particular groupClass in a Nexus dataset.
const std::string & extension() const
Access the file extension.
std::shared_ptr< T > createWorkspace(InitArgs... args)
Kernel::Logger g_log("ExperimentInfo")
static logger object
void ReadEventFile(IReader &loader, IEventHandler &handler, IProgress &progress, int32_t def_clock_scale, bool use_tx_chopper)
std::vector< std::string > filterDatasets(const Nexus::NXEntry &entry, const std::string &groupAddress, const std::string &regexFilter)
extract datasets from a group that match a regex filter
void loadEvents(API::Progress &prog, const char *progMsg, const std::string &eventFile, EP &eventProcessor)
Definition LoadEMU.cpp:568
static char const *const FilenameStr
Definition LoadBBY.cpp:55
static const size_t HISTO_BINS_X
Definition LoadBBY.cpp:48
static const size_t HISTO_BINS_Y
Definition LoadBBY.cpp:49
static const size_t Progress_LoadBinFile
Definition LoadBBY.cpp:51
void AddSinglePointTimeSeriesProperty(API::LogManager &logManager, const std::string &time, const std::string &name, const TYPE value)
Definition LoadBBY.cpp:67
static char const *const MaskStr
Definition LoadBBY.cpp:56
static char const *const FilterByTimeStartStr
Definition LoadBBY.cpp:61
static const size_t Progress_Total
Definition LoadBBY.cpp:53
std::size_t numEvents(Nexus::File &file, bool &hasTotalCounts, bool &oldNeXusFileNames, const std::string &prefix, const Nexus::NexusDescriptor &descriptor)
Get the number of events in the currently opened group.
static char const *const FilterByTimeStopStr
Definition LoadBBY.cpp:62
static const size_t Progress_ReserveMemory
Definition LoadBBY.cpp:52
DLLExport void getEventsFrom(EventList &el, std::vector< Types::Event::TofEvent > *&events)
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
NXDataSetTyped< float > NXFloat
The float dataset type.
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.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Output
An output workspace.
Definition Property.h:54