-
Notifications
You must be signed in to change notification settings - Fork 122
/
LoadPLN.cpp
985 lines (810 loc) · 34.5 KB
/
LoadPLN.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2020 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source,
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidDataHandling/LoadPLN.h"
#include "MantidAPI/Axis.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/LogManager.h"
#include "MantidAPI/RegisterFileLoader.h"
#include "MantidAPI/Run.h"
#include "MantidDataHandling/LoadANSTOEventFile.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidGeometry/Instrument.h"
#include "MantidGeometry/Instrument/DetectorInfo.h"
#include "MantidKernel/MandatoryValidator.h"
#include "MantidKernel/OptionalBool.h"
#include "MantidKernel/PhysicalConstants.h"
#include "MantidKernel/UnitFactory.h"
#include "MantidNexus/NexusClasses.h"
#include <boost/filesystem.hpp>
#include <algorithm>
#include <cmath>
#include <fstream>
namespace Mantid {
namespace DataHandling {
using namespace Kernel;
namespace {
// number of physical detectors
constexpr size_t MONITORS = 8;
constexpr size_t DETECTOR_TUBES = 200;
constexpr size_t HISTO_BINS_X = DETECTOR_TUBES + MONITORS;
constexpr size_t HISTO_BINS_Y_DENUMERATOR = 16;
constexpr size_t PIXELS_PER_TUBE = 1024 / HISTO_BINS_Y_DENUMERATOR;
constexpr size_t DETECTOR_SPECTRA = DETECTOR_TUBES * PIXELS_PER_TUBE;
constexpr size_t HISTOGRAMS = DETECTOR_SPECTRA + MONITORS;
// File loading progress boundaries
constexpr size_t Progress_LoadBinFile = 48;
constexpr size_t Progress_ReserveMemory = 4;
constexpr size_t Progress_Total =
2 * Progress_LoadBinFile + Progress_ReserveMemory;
// Algorithm parameter names
constexpr char FilenameStr[] = "Filename";
constexpr char MaskStr[] = "Mask";
constexpr char SelectDetectorTubesStr[] = "SelectDetectorTubes";
constexpr char SelectDatasetStr[] = "SelectDataset";
constexpr char FilterByTimeStartStr[] = "FilterByTimeStart";
constexpr char FilterByTimeStopStr[] = "FilterByTimeStop";
constexpr char PathToBinaryStr[] = "BinaryEventPath";
constexpr char TOFBiasStr[] = "TimeOfFlightBias";
constexpr char CalibrateTOFStr[] = "CalibrateTOFBias";
constexpr char LambdaOnTwoStr[] = "LambdaOnTwoMode";
// Common pairing of limits
using TimeLimits = std::pair<double, double>;
template <typename Type>
void AddSinglePointTimeSeriesProperty(API::LogManager &logManager,
const std::string &time,
const std::string &name,
const Type value) {
// create time series property and add single value
auto p = new Kernel::TimeSeriesProperty<Type>(name);
p->addValue(time, value);
// add to log manager
logManager.addProperty(p);
}
// Utility functions for loading values with defaults
// Single value properties only support int, double, string and bool
template <typename Type>
Type GetNeXusValue(NeXus::NXEntry &entry, const std::string &path,
const Type &defval, int32_t index) {
try {
NeXus::NXDataSetTyped<Type> dataSet = entry.openNXDataSet<Type>(path);
dataSet.load();
return dataSet()[index];
} catch (std::runtime_error &) {
return defval;
}
}
// string and double are special cases
template <>
double GetNeXusValue<double>(NeXus::NXEntry &entry, const std::string &path,
const double &defval, int32_t index) {
try {
NeXus::NXDataSetTyped<float> dataSet = entry.openNXDataSet<float>(path);
dataSet.load();
return dataSet()[index];
} catch (std::runtime_error &) {
return defval;
}
}
template <>
std::string
GetNeXusValue<std::string>(NeXus::NXEntry &entry, const std::string &path,
const std::string &defval, int32_t /*unused*/) {
try {
NeXus::NXChar dataSet = entry.openNXChar(path);
dataSet.load();
return std::string(dataSet(), dataSet.dim0());
} catch (std::runtime_error &) {
return defval;
}
}
template <typename T>
void MapNeXusToProperty(NeXus::NXEntry &entry, const std::string &path,
const T &defval, API::LogManager &logManager,
const std::string &name, const T &factor,
int32_t index) {
T value = GetNeXusValue<T>(entry, path, defval, index);
logManager.addProperty<T>(name, value * factor);
}
// sting is a special case
template <>
void MapNeXusToProperty<std::string>(
NeXus::NXEntry &entry, const std::string &path, const std::string &defval,
API::LogManager &logManager, const std::string &name,
const std::string & /*unused*/, int32_t index) {
std::string value = GetNeXusValue<std::string>(entry, path, defval, index);
logManager.addProperty<std::string>(name, value);
}
template <typename T>
void MapNeXusToSeries(NeXus::NXEntry &entry, const std::string &path,
const T &defval, API::LogManager &logManager,
const std::string &time, const std::string &name,
const T &factor, int32_t index) {
auto value = GetNeXusValue<T>(entry, path, defval, index);
AddSinglePointTimeSeriesProperty<T>(logManager, time, name, value * factor);
}
// map the comma separated range of indexes to the vector via a lambda function
// throws an exception if it is outside the vector range
//
template <typename T, typename F>
void mapRangeToIndex(const std::string &line, std::vector<T> &result,
const F &fn) {
std::stringstream ss(line);
std::string item;
size_t index = 0;
while (std::getline(ss, item, ',')) {
auto const k = item.find('-');
size_t p0, p1;
if (k != std::string::npos) {
p0 = boost::lexical_cast<size_t>(item.substr(0, k));
p1 = boost::lexical_cast<size_t>(item.substr(k + 1, item.size() - k - 1));
} else {
p0 = boost::lexical_cast<size_t>(item);
p1 = p0;
}
if (p1 < result.size() && p0 <= p1) {
while (p0 <= p1) {
result[p0++] = fn(index);
index++;
}
} else if (p0 < result.size() && p1 < p0) {
do {
result[p0] = fn(index);
index++;
} while (p1 < p0--);
} else
throw std::invalid_argument("invalid range specification");
}
}
// Simple reader that is compatible with the ASNTO event file loader
class FileLoader {
std::ifstream _ifs;
size_t _size;
public:
explicit FileLoader(const char *filename)
: _ifs(filename, std::ios::binary | std::ios::in) {
if (!_ifs.is_open() || _ifs.fail())
throw std::runtime_error("unable to open file");
_ifs.seekg(0, _ifs.end);
_size = _ifs.tellg();
_ifs.seekg(0, _ifs.beg);
}
bool read(char *s, std::streamsize n) {
return static_cast<bool>(_ifs.read(s, n));
}
size_t size() { return _size; }
size_t position() { return _ifs.tellg(); }
size_t selected_position() { return _ifs.tellg(); }
};
} // anonymous namespace
namespace PLN {
//
// In the future the ANSTO helper and event file loader will be generalized to
// handle the instruments consistently.
// Simple 1D histogram class
class SimpleHist {
std::vector<size_t> m_hist;
double m_M;
double m_B;
size_t m_peak;
size_t m_count;
public:
SimpleHist(size_t N, double minVal, double maxVal) : m_hist(N, 0) {
m_M = (static_cast<double>(N) / (maxVal - minVal));
m_B = -m_M * minVal;
m_peak = 0;
m_count = 0;
}
inline double ival(double val) const { return m_M * val + m_B; }
inline double xval(double ix) const { return (ix - m_B) / m_M; }
inline void add(double val) {
auto ix = static_cast<size_t>(std::floor(ival(val)));
if (ix < m_hist.size()) {
m_hist[ix]++;
m_count++;
if (m_hist[ix] > m_peak)
m_peak = m_hist[ix];
}
}
const std::vector<size_t> &histogram() const { return m_hist; }
inline size_t peak() const { return m_peak; }
inline size_t count() const { return m_count; }
};
class EventProcessor {
protected:
// fields
const std::vector<bool> &m_roi;
const std::vector<size_t> &m_mapIndex;
const double m_framePeriod;
const double m_gatePeriod;
// number of frames
size_t m_frames;
size_t m_framesValid;
// time boundaries
const TimeLimits m_timeBoundary; // seconds
virtual void addEventImpl(size_t id, size_t x, size_t y, double tof) = 0;
public:
EventProcessor(const std::vector<bool> &roi,
const std::vector<size_t> &mapIndex, const double framePeriod,
const double gatePeriod, const TimeLimits &timeBoundary)
: m_roi(roi), m_mapIndex(mapIndex), m_framePeriod(framePeriod),
m_gatePeriod(gatePeriod), m_frames(0), m_framesValid(0),
m_timeBoundary(timeBoundary) {}
void newFrame() {
m_frames++;
if (validFrame())
m_framesValid++;
}
inline bool validFrame() const {
double frameTime = m_framePeriod * static_cast<double>(m_frames) * 1.0e-6;
return (frameTime >= m_timeBoundary.first &&
frameTime <= m_timeBoundary.second);
}
double duration() const {
// length test in seconds
return m_framePeriod * static_cast<double>(m_frames) * 1.0e-6;
}
inline int64_t frameStart() const {
// returns time in nanoseconds from start of test
auto start = m_framePeriod * static_cast<double>(m_frames);
return static_cast<int64_t>(start * 1.0e3);
}
void addEvent(size_t x, size_t p, double tof, double /*taux*/) {
// check if in time boundaries
if (!validFrame())
return;
// group pixels
auto y = static_cast<size_t>(p / HISTO_BINS_Y_DENUMERATOR);
// determine detector id and check limits
if (x >= HISTO_BINS_X || y >= PIXELS_PER_TUBE)
return;
// map the raw detector index to the physical model
size_t xid = m_mapIndex[x];
// take the modules of the tof time to account for the
// longer background chopper rate
double mtof = tof < 0.0 ? fmod(tof + m_gatePeriod, m_gatePeriod)
: fmod(tof, m_gatePeriod);
size_t id = xid < DETECTOR_TUBES ? PIXELS_PER_TUBE * xid + y
: DETECTOR_SPECTRA + xid;
if (id >= m_roi.size())
return;
// check if neutron is in region of interest
if (!m_roi[id])
return;
// finally pass to specific handler
addEventImpl(id, xid, y, mtof);
}
};
// The class determines the number of counts linked to the detectors and the
// tof correction.
class EventCounter : public EventProcessor {
protected:
// fields
std::vector<size_t> &m_eventCounts;
double m_L1;
double m_V0;
const std::vector<double> &m_L2;
SimpleHist m_histogram;
void addEventImpl(size_t id, size_t /*x*/, size_t /*y*/,
double tobs) override {
m_eventCounts[id]++;
// the maximum occurs at the elastic peak
double deltaT = 1.0e6 * (m_L1 + m_L2[id]) / m_V0 - tobs;
m_histogram.add(deltaT);
}
public:
// construction
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)
: EventProcessor(roi, mapIndex, framePeriod, gatePeriod, timeBoundary),
m_eventCounts(eventCounts), m_L1(L1), m_V0(V0), m_L2(vecL2),
m_histogram(5000, -2500.0, 2500.0) {}
size_t numFrames() const { return m_framesValid; }
size_t numEvents() const {
size_t sum(0);
return std::accumulate(m_eventCounts.begin(), m_eventCounts.end(), sum);
}
// clips the histogram above 25% and takes the mean of the values
double tofCorrection() {
// determine the points above the 25% threshold
auto minLevel = static_cast<size_t>(m_histogram.peak() / 4);
auto hvec = m_histogram.histogram();
double sum = 0.0;
size_t count = 0;
for (size_t i = 0; i < hvec.size(); i++) {
if (hvec[i] >= minLevel) {
auto ix = static_cast<double>(i);
sum += static_cast<double>(hvec[i]) * m_histogram.xval(ix + 0.5);
count += hvec[i];
}
}
return (count > 0 ? sum / static_cast<double>(count) : 0.0);
}
};
class EventAssigner : public EventProcessor {
protected:
// fields
std::vector<EventVector_pt> &m_eventVectors;
double m_tofMin;
double m_tofMax;
int64_t m_startTime;
double m_tofCorrection;
double m_sampleTime;
void addEventImpl(size_t id, size_t /*x*/, size_t /*y*/,
double tobs) override {
// get the absolute time for the start of the frame
auto const offset = m_startTime + frameStart();
// adjust the the tof to account for the correction and allocate events
// that occur before the sample time as slow events from the previous pulse
double tof = tobs + m_tofCorrection - m_sampleTime;
if (tof < 0.0)
tof = fmod(tof + m_gatePeriod, m_gatePeriod);
tof += m_sampleTime;
if (m_tofMin > tof)
m_tofMin = tof;
if (m_tofMax < tof)
m_tofMax = tof;
auto ev = Types::Event::TofEvent(tof, Types::Core::DateAndTime(offset));
m_eventVectors[id]->emplace_back(ev);
}
public:
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)
: EventProcessor(roi, mapIndex, framePeriod, gatePeriod, timeBoundary),
m_eventVectors(eventVectors),
m_tofMin(std::numeric_limits<double>::max()),
m_tofMax(std::numeric_limits<double>::min()), m_startTime(startTime),
m_tofCorrection(tofCorrection), m_sampleTime(sampleTime) {}
double tofMin() const { return m_tofMin <= m_tofMax ? m_tofMin : 0.0; }
double tofMax() const { return m_tofMin <= m_tofMax ? m_tofMax : 0.0; }
};
template <typename EP>
void loadEvents(API::Progress &prog, const char *progMsg,
const std::string &eventFile, EP &eventProcessor) {
using namespace ANSTO;
prog.doReport(progMsg);
FileLoader loader(eventFile.c_str());
// for progress notifications
ANSTO::ProgressTracker progTracker(prog, progMsg, loader.size(),
Progress_LoadBinFile);
ReadEventFile(loader, eventProcessor, progTracker, 100, false);
}
} // namespace PLN
/// Initialise the algorithm and declare the properties for the
/// nexus descriptor.
void LoadPLN::init() {
// Specify file extensions which can be associated with a specific file.
std::vector<std::string> exts;
// Declare the Filename algorithm property. Mandatory. Sets the path to the
// file to load.
exts.clear();
exts.emplace_back(".hdf");
declareProperty(std::make_unique<API::FileProperty>(
FilenameStr, "", API::FileProperty::Load, exts),
"The input filename of the stored data");
declareProperty(PathToBinaryStr, "",
std::make_shared<Kernel::MandatoryValidator<std::string>>(),
"Relative or absolute path to the compressed binary\n"
"event file linked to the HDF file, eg /storage/data/");
// mask
exts.clear();
exts.emplace_back(".xml");
declareProperty(std::make_unique<API::FileProperty>(
MaskStr, "", API::FileProperty::OptionalLoad, exts),
"The input filename of the mask data");
declareProperty(SelectDetectorTubesStr, "",
"Comma separated range of detectors tubes to be loaded,\n"
" eg 16,19-45,47");
declareProperty(
std::make_unique<API::WorkspaceProperty<API::IEventWorkspace>>(
"OutputWorkspace", "", Kernel::Direction::Output));
declareProperty(SelectDatasetStr, 0,
"Select the index for the dataset to be loaded.");
declareProperty(TOFBiasStr, 0.0, "Time of flight correction in micro-sec.");
declareProperty(CalibrateTOFStr, false,
"Calibrate the TOF correction from the elastic pulse.");
declareProperty(LambdaOnTwoStr, false,
"Instrument is operating in Lambda on Two mode.");
declareProperty(FilterByTimeStartStr, 0.0,
"Only include events after the provided start time, in "
"seconds (relative to the start of the run).");
declareProperty(FilterByTimeStopStr, EMPTY_DBL(),
"Only include events before the provided stop time, in "
"seconds (relative to the start of the run).");
std::string grpOptional = "Filters";
setPropertyGroup(FilterByTimeStartStr, grpOptional);
setPropertyGroup(FilterByTimeStopStr, grpOptional);
}
/// Creates an event workspace and sets the \p title.
void LoadPLN::createWorkspace(const std::string &title) {
// Create the workspace
m_localWorkspace = std::make_shared<DataObjects::EventWorkspace>();
m_localWorkspace->initialize(HISTOGRAMS, 2, 1);
// set the units
m_localWorkspace->getAxis(0)->unit() =
Kernel::UnitFactory::Instance().create("TOF");
m_localWorkspace->setYUnit("Counts");
// set title
m_localWorkspace->setTitle(title);
}
/// Execute the algorithm using the \p hdfFile and \p eventFile.
/// The steps involved are:
/// Create the workspace
/// Get the instrument properties and load options
/// Load the instrument from the IDF
/// Load the data values and adjust TOF
/// Set up the masks
void LoadPLN::exec(const std::string &hdfFile, const std::string &eventFile) {
namespace fs = boost::filesystem;
// Create workspace
// ----------------
fs::path p = hdfFile;
for (; !p.extension().empty();)
p = p.stem();
std::string title = p.generic_string();
createWorkspace(title);
API::LogManager &logManager = m_localWorkspace->mutableRun();
API::Progress prog(this, 0.0, 1.0, Progress_Total);
// Load instrument and workspace properties
logManager.addProperty(SelectDatasetStr, m_datasetIndex);
loadParameters(hdfFile, logManager);
prog.doReport("creating instrument");
loadInstrument();
// Get the region of interest and filters and save to log
std::string const maskfile = getPropertyValue(MaskStr);
std::string const seltubes = getPropertyValue(SelectDetectorTubesStr);
logManager.addProperty(SelectDetectorTubesStr, seltubes);
logManager.addProperty(MaskStr, maskfile);
std::vector<bool> roi = createRoiVector(seltubes, maskfile);
double timeMaxBoundary = getProperty(FilterByTimeStopStr);
if (isEmpty(timeMaxBoundary))
timeMaxBoundary = std::numeric_limits<double>::infinity();
TimeLimits timeBoundary(getProperty(FilterByTimeStartStr), timeMaxBoundary);
// get the detector map from raw input to a physical detector
auto instr = m_localWorkspace->getInstrument();
std::string dmapStr = instr->getParameterAsString("DetectorMap");
std::vector<size_t> detMapIndex = std::vector<size_t>(HISTO_BINS_X, 0);
mapRangeToIndex(dmapStr, detMapIndex, [](size_t n) { return n; });
// Load the events file. First count the number of events to reserve
// memory and then assign the events to the detectors
size_t numberHistograms = m_localWorkspace->getNumberHistograms();
std::vector<EventVector_pt> eventVectors(numberHistograms, nullptr);
std::vector<size_t> eventCounts(numberHistograms, 0);
double masterRpm =
fabs(logManager.getTimeSeriesProperty<double>("FermiChopperFreq")
->firstValue());
double slaveRpm =
fabs(logManager.getTimeSeriesProperty<double>("OverlapChopperFreq")
->firstValue());
double framePeriod = 1.0e6 / masterRpm;
// if fermi chopper freq equals the overlap freq then the gate period is
// half the frame period
double gatePeriod =
(std::round(masterRpm / slaveRpm) == 1.0 ? 0.5 * framePeriod
: framePeriod);
AddSinglePointTimeSeriesProperty<double>(logManager, m_startRun, "GatePeriod",
gatePeriod);
// count total events per pixel and reserve necessary memory
loadDetectorL2Values();
double sourceSample = fabs(instr->getSource()->getPos().Z());
double wavelength =
logManager.getTimeSeriesProperty<double>("Wavelength")->firstValue();
double velocity = PhysicalConstants::h /
(PhysicalConstants::NeutronMass * wavelength * 1e-10);
double sampleTime = 1.0e6 * sourceSample / velocity;
PLN::EventCounter eventCounter(roi, detMapIndex, framePeriod, gatePeriod,
timeBoundary, eventCounts, sourceSample,
velocity, m_detectorL2);
PLN::loadEvents(prog, "loading neutron counts", eventFile, eventCounter);
ANSTO::ProgressTracker progTracker(prog, "creating neutron event lists",
numberHistograms, Progress_ReserveMemory);
prepareEventStorage(progTracker, eventCounts, eventVectors);
// log a message if the number of events in the event file does not match
// the total counts in the hdf
size_t hdfCounts = static_cast<size_t>(
logManager.getTimeSeriesProperty<int>("TotalCounts")->firstValue());
if (hdfCounts != eventCounter.numEvents()) {
g_log.error("HDF and event counts differ: " + std::to_string(hdfCounts) +
", " + std::to_string(eventCounter.numEvents()));
}
// now perform the actual event collection and TOF convert if necessary
// if a phase calibration is required then load it as raw doppler time
// perform the calibration and then convert to TOF
Types::Core::DateAndTime startTime(m_startRun);
auto const start_nanosec = startTime.totalNanoseconds();
bool const calibrateTOF = getProperty(CalibrateTOFStr);
double tofCorrection = getProperty(TOFBiasStr);
if (calibrateTOF) {
tofCorrection = eventCounter.tofCorrection();
}
logManager.addProperty("CalibrateTOF", (calibrateTOF ? 1 : 0));
AddSinglePointTimeSeriesProperty<double>(logManager, m_startRun,
"TOFCorrection", tofCorrection);
PLN::EventAssigner eventAssigner(roi, detMapIndex, framePeriod, gatePeriod,
timeBoundary, eventVectors, start_nanosec,
tofCorrection, sampleTime);
PLN::loadEvents(prog, "loading neutron events (TOF)", eventFile,
eventAssigner);
// perform a calibration and then TOF conversion if necessary
// and update the tof limits
auto minTOF = eventAssigner.tofMin();
auto maxTOF = eventAssigner.tofMax();
// just to make sure the bins hold it all and setup the detector masks
m_localWorkspace->setAllX(
HistogramData::BinEdges{std::max(0.0, floor(minTOF)), maxTOF + 1});
setupDetectorMasks(roi);
// set log values
auto frame_count = static_cast<int>(eventCounter.numFrames());
AddSinglePointTimeSeriesProperty<int>(logManager, m_startRun, "frame_count",
frame_count);
std::string filename = getPropertyValue(FilenameStr);
logManager.addProperty("filename", filename);
Types::Core::time_duration duration = boost::posix_time::microseconds(
static_cast<boost::int64_t>(eventCounter.duration() * 1.0e6));
Types::Core::DateAndTime endTime(startTime + duration);
logManager.addProperty("start_time", startTime.toISO8601String());
logManager.addProperty("end_time", endTime.toISO8601String());
logManager.addProperty<double>("dur", eventCounter.duration());
// Finally add the time-series evironment parameters explicitly
loadEnvironParameters(hdfFile, logManager);
setProperty("OutputWorkspace", m_localWorkspace);
}
/// Recovers the L2 neutronic distance for each detector.
void LoadPLN::loadDetectorL2Values() {
m_detectorL2 = std::vector<double>(HISTOGRAMS);
const auto &detectorInfo = m_localWorkspace->detectorInfo();
auto detIDs = detectorInfo.detectorIDs();
for (const auto detID : detIDs) {
auto ix = detectorInfo.indexOf(detID);
double l2 = detectorInfo.l2(ix);
m_detectorL2[detID] = l2;
}
}
/// Set up the detector masks to the region of interest \p roi.
void LoadPLN::setupDetectorMasks(std::vector<bool> &roi) {
// count total number of masked bins
size_t maskedBins = 0;
for (size_t i = 0; i != roi.size(); i++)
if (!roi[i])
maskedBins++;
if (maskedBins > 0) {
// create list of masked bins
std::vector<size_t> maskIndexList(maskedBins);
size_t maskIndex = 0;
for (size_t i = 0; i != roi.size(); i++)
if (!roi[i])
maskIndexList[maskIndex++] = i;
API::IAlgorithm_sptr maskingAlg = createChildAlgorithm("MaskDetectors");
maskingAlg->setProperty("Workspace", m_localWorkspace);
maskingAlg->setProperty("WorkspaceIndexList", maskIndexList);
maskingAlg->executeAsChildAlg();
}
}
/// Allocate space for the event storage in \p eventVectors after the
/// \p eventCounts have been determined.
void LoadPLN::prepareEventStorage(ANSTO::ProgressTracker &progTracker,
std::vector<size_t> &eventCounts,
std::vector<EventVector_pt> &eventVectors) {
size_t numberHistograms = eventCounts.size();
for (size_t i = 0; i != numberHistograms; ++i) {
DataObjects::EventList &eventList = m_localWorkspace->getSpectrum(i);
eventList.setSortOrder(DataObjects::PULSETIME_SORT);
eventList.reserve(eventCounts[i]);
eventList.setDetectorID(static_cast<detid_t>(i));
eventList.setSpectrumNo(static_cast<detid_t>(i));
DataObjects::getEventsFrom(eventList, eventVectors[i]);
progTracker.update(i);
}
progTracker.complete();
}
/// Region of interest is defined by the \p selected detectors and the
/// \p maskfile.
std::vector<bool> LoadPLN::createRoiVector(const std::string &selected,
const std::string &maskfile) {
std::vector<bool> result(HISTOGRAMS, true);
// turn off pixels linked to missing tubes
if (!selected.empty()) {
std::vector<bool> tubes(HISTO_BINS_X, false);
mapRangeToIndex(selected, tubes, [](size_t) { return true; });
for (size_t i = 0; i < DETECTOR_TUBES; i++) {
if (tubes[i] == false) {
for (size_t j = 0; j < PIXELS_PER_TUBE; j++) {
result[i * PIXELS_PER_TUBE + j] = false;
}
}
}
for (size_t i = 0; i < MONITORS; i++) {
result[DETECTOR_SPECTRA + i] = tubes[DETECTOR_TUBES + i];
}
}
if (maskfile.length() == 0)
return result;
std::ifstream input(maskfile.c_str());
if (!input.good())
throw std::invalid_argument("invalid mask file");
std::string line;
while (std::getline(input, line)) {
auto i0 = line.find("<detids>");
auto iN = line.find("</detids>");
if ((i0 != std::string::npos) && (iN != std::string::npos) && (i0 < iN)) {
line = line.substr(i0 + 8, iN - i0 - 8); // 8 = len("<detids>")
mapRangeToIndex(line, result, [](size_t) { return false; });
}
}
return result;
}
/// Load parameters from input \p hdfFile and save to the log manager, \p logm.
void LoadPLN::loadParameters(const std::string &hdfFile,
API::LogManager &logm) {
NeXus::NXRoot root(hdfFile);
NeXus::NXEntry entry = root.openFirstEntry();
MapNeXusToProperty<std::string>(entry, "sample/name", "unknown", logm,
"SampleName", "", 0);
MapNeXusToProperty<std::string>(entry, "sample/description", "unknown", logm,
"SampleDescription", "", 0);
// if dataset index > 0 need to add an offset to the start time
Types::Core::DateAndTime startTime(GetNeXusValue<std::string>(
entry, "start_time", "2000-01-01T00:00:00", 0));
if (m_datasetIndex > 0) {
auto baseTime =
GetNeXusValue<int32_t>(entry, "instrument/detector/start_time", 0, 0);
auto nthTime = GetNeXusValue<int32_t>(
entry, "instrument/detector/start_time", 0, m_datasetIndex);
Types::Core::time_duration duration = boost::posix_time::microseconds(
static_cast<boost::int64_t>((nthTime - baseTime) * 1.0e6));
Types::Core::DateAndTime startDataset(startTime + duration);
m_startRun = startDataset.toISO8601String();
} else {
m_startRun = startTime.toISO8601String();
}
// Add support for instrument running in lambda on two mode.
// Added as UI option as the available instrument parameters
// cannot be reliably interpreted to predict the mode (as
// advised by the instrument scientist).
bool const lambdaOnTwoMode = getProperty(LambdaOnTwoStr);
double lambdaFactor = (lambdaOnTwoMode ? 0.5 : 1.0);
logm.addProperty("LambdaOnTwoMode", (lambdaOnTwoMode ? 1 : 0));
MapNeXusToSeries<double>(entry, "instrument/fermi_chopper/mchs", 0.0, logm,
m_startRun, "FermiChopperFreq", 1.0 / 60,
m_datasetIndex);
MapNeXusToSeries<double>(entry, "instrument/fermi_chopper/schs", 0.0, logm,
m_startRun, "OverlapChopperFreq", 1.0 / 60,
m_datasetIndex);
MapNeXusToSeries<double>(entry, "instrument/crystal/wavelength", 0.0, logm,
m_startRun, "Wavelength", lambdaFactor,
m_datasetIndex);
MapNeXusToSeries<double>(entry, "instrument/detector/stth", 0.0, logm,
m_startRun, "DetectorTankAngle", 1.0,
m_datasetIndex);
MapNeXusToSeries<int32_t>(entry, "monitor/bm1_counts", 0, logm, m_startRun,
"MonitorCounts", 1, m_datasetIndex);
MapNeXusToSeries<int32_t>(entry, "data/total_counts", 0, logm, m_startRun,
"TotalCounts", 1, m_datasetIndex);
MapNeXusToSeries<double>(entry, "data/tofw", 5.0, logm, m_startRun,
"ChannelWidth", 1, m_datasetIndex);
MapNeXusToSeries<double>(entry, "sample/mscor", 0.0, logm, m_startRun,
"SampleRotation", 1, m_datasetIndex);
}
/// Load the environment variables from the \p hdfFile and save as
/// time series to the log manager, \p logm.
void LoadPLN::loadEnvironParameters(const std::string &hdfFile,
API::LogManager &logm) {
NeXus::NXRoot root(hdfFile);
NeXus::NXEntry entry = root.openFirstEntry();
auto time_str = logm.getPropertyValueAsType<std::string>("end_time");
// load the environment variables for the dataset loaded
std::vector<std::string> tags = {"P01PS05", "P01PSP05", "T01S00", "T01S06",
"T01S07", "T01S08", "T01SP00", "T01SP06",
"T01SP07", "T01SP08", "T2S1", "T2S2",
"T2S3", "T2S4", "T2SP1", "T2SP2"};
for (const auto &tag : tags) {
MapNeXusToSeries<double>(entry, "data/" + tag, 0.0, logm, time_str,
"env_" + tag, 1.0, m_datasetIndex);
}
}
/// Load the instrument definition.
void LoadPLN::loadInstrument() {
// loads the IDF and parameter file
API::IAlgorithm_sptr loadInstrumentAlg =
createChildAlgorithm("LoadInstrument");
loadInstrumentAlg->setProperty("Workspace", m_localWorkspace);
loadInstrumentAlg->setPropertyValue("InstrumentName", "PELICAN");
loadInstrumentAlg->setProperty("RewriteSpectraMap",
Mantid::Kernel::OptionalBool(false));
loadInstrumentAlg->executeAsChildAlg();
}
/// Algorithm's version for identification. @see Algorithm::version
int LoadPLN::version() const { return 1; }
/// Similar algorithms. @see Algorithm::seeAlso
const std::vector<std::string> LoadPLN::seeAlso() const {
return {"Load", "LoadEMU"};
}
/// Algorithm's category for identification. @see Algorithm::category
const std::string LoadPLN::category() const { return "DataHandling\\ANSTO"; }
/// Algorithms name for identification. @see Algorithm::name
const std::string LoadPLN::name() const { return "LoadPLN"; }
/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
const std::string LoadPLN::summary() const {
return "Loads a PLN Hdf and linked event file into a workspace.";
}
/// Return the confidence as an integer value that this algorithm can
/// load the file \p descriptor.
int LoadPLN::confidence(Kernel::NexusDescriptor &descriptor) const {
if (descriptor.extension() != ".hdf")
return 0;
if (descriptor.pathExists("/entry1/site_name") &&
descriptor.pathExists("/entry1/instrument/fermi_chopper") &&
descriptor.pathExists("/entry1/instrument/aperture/sh1") &&
descriptor.pathExists("/entry1/instrument/ag1010/MEAS/Temperature") &&
descriptor.pathExists("/entry1/instrument/detector/daq_dirname") &&
descriptor.pathExists("/entry1/instrument/detector/dataset_number") &&
descriptor.pathExists("/entry1/data/hmm") &&
descriptor.pathExists("/entry1/data/time_of_flight") &&
descriptor.pathExists("/entry1/data/total_counts")) {
return 80;
} else {
return 0;
}
}
/// Execute the algorithm. Establishes the filepath to the event file
/// from the HDF link and the path provided and invokes the common
// exec() function that works with the two files.
void LoadPLN::exec() {
namespace fs = boost::filesystem;
// Open the hdf file and find the dirname and dataset number
std::string hdfFile = getPropertyValue(FilenameStr);
std::string evtPath = getPropertyValue(PathToBinaryStr);
if (evtPath.empty())
evtPath = "./";
// if relative ./ or ../ then append to the directory for the hdf file
if (evtPath.rfind("./") == 0 || evtPath.rfind("../") == 0) {
fs::path hp = hdfFile;
evtPath = fs::canonical(evtPath, hp.parent_path()).generic_string();
}
// dataset index to be loaded
m_datasetIndex = getProperty(SelectDatasetStr);
// if path provided build the file path
if (fs::is_directory(evtPath)) {
NeXus::NXRoot root(hdfFile);
NeXus::NXEntry entry = root.openFirstEntry();
auto eventDir = GetNeXusValue<std::string>(
entry, "instrument/detector/daq_dirname", "./", 0);
auto dataset = GetNeXusValue<int32_t>(
entry, "instrument/detector/dataset_number", 0, m_datasetIndex);
if (dataset < 0) {
std::string message(
"Negative dataset index recorded in HDF, reset to zero!");
g_log.error(message);
dataset = 0;
}
// build the path to the event file as if a relative or absolute
// path is passed:
// 'relpath/[daq_dirname]/DATASET_[n]/EOS.bin' or the
char buffer[255] = {};
snprintf(buffer, sizeof(buffer), "%s/DATASET_%d/EOS.bin", eventDir.c_str(),
dataset);
fs::path path = evtPath;
path /= buffer;
path = fs::absolute(path);
evtPath = path.generic_string();
}
// finally check that the event file exists
if (!fs::is_regular_file(evtPath)) {
std::string msg = "Check path, cannot open binary event file: " + evtPath;
throw std::runtime_error(msg);
}
exec(hdfFile, evtPath);
}
// register the algorithms into the AlgorithmFactory
DECLARE_NEXUS_FILELOADER_ALGORITHM(LoadPLN)
} // namespace DataHandling
} // namespace Mantid