Mantid
Loading...
Searching...
No Matches
BankPulseTimes.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8#include "MantidNexus/NexusFile.h"
9#include <numeric>
10
11using namespace Mantid::Kernel;
12
13//===============================================================================================
14// BankPulseTimes
15//===============================================================================================
16
17namespace {
18// these values are simliar to those in EventList::EventSortType
19enum PulseSorting { UNKNOWN, UNSORTED, PULSETIME_SORT };
20
21} // namespace
22
23namespace Mantid::DataHandling {
24
25const std::string BankPulseTimes::DEFAULT_START_TIME("1970-01-01T00:00:00Z");
26
28
29//----------------------------------------------------------------------------------------------
34BankPulseTimes::BankPulseTimes(const std::vector<Mantid::Types::Core::DateAndTime> &times)
35 : startTime(DEFAULT_START_TIME), pulseTimes(times), have_period_info(false), m_sorting_info(PulseSorting::UNKNOWN) {
36 this->updateStartTime();
38}
39
40BankPulseTimes::BankPulseTimes(const std::vector<Mantid::Types::Core::DateAndTime> &times,
41 const std::vector<int> &periodNumbers)
42 : startTime(DEFAULT_START_TIME), periodNumbers(periodNumbers), pulseTimes(times), have_period_info(true),
43 m_sorting_info(PulseSorting::UNKNOWN) {
44 this->updateStartTime();
46}
47
53BankPulseTimes::BankPulseTimes(Nexus::File &file, const std::vector<int> &periodNumbers)
54 : startTime(DEFAULT_START_TIME), periodNumbers(periodNumbers), have_period_info(true),
55 m_sorting_info(PulseSorting::UNKNOWN) {
56
57 // Some old data use "pulse_time" instead of "event_time_zero" as entry
58 try {
59 file.openData("event_time_zero");
60 } catch (const std::exception &) {
61 file.openData("pulse_time");
62 }
63 // Read the offset (time zero)
64
65 // Use the offset if it is present
66 if (file.hasAttr("offset"))
67 file.getAttr("offset", startTime);
68
69 Mantid::Types::Core::DateAndTime start(startTime);
70
71 // number of pulse times
72 const auto dataInfo = file.getInfo();
73 const uint64_t numValues =
74 std::accumulate(dataInfo.dims.cbegin(), dataInfo.dims.cend(), uint64_t{1}, std::multiplies<>());
75 if (numValues == 0)
76 throw std::runtime_error("event_time_zero field has no data!");
77
78 const auto heldTimeZeroType = dataInfo.type;
79
80 // Nexus only requires event_time_zero to be a NXNumber, we support two
81 // possilites
82 if (heldTimeZeroType == NXnumtype::FLOAT64) {
83 this->readData<double>(file, numValues, start);
84 } else if (heldTimeZeroType == NXnumtype::UINT64) {
85 this->readData<uint64_t>(file, numValues, start);
86 } else {
87 throw std::invalid_argument("Unsupported type for event_time_zero");
88 }
89 file.closeData();
90
92}
93
94template <typename ValueType>
95void BankPulseTimes::readData(Nexus::File &file, std::size_t numValues, Mantid::Types::Core::DateAndTime &start) {
96 Nexus::DimVector indexStart{0};
97 Nexus::DimVector indexStep{std::min(numValues, std::size_t(12 * 3600 * 60))}; // 12 hour at 60Hz
98
99 // getSlab needs the data allocated already
100 std::vector<ValueType> rawData(indexStep[0]);
101
102 // loop over chunks of data and transform each chunk
103 while ((indexStep[0] > 0) && (indexStart[0] < numValues)) {
104 file.getSlab(rawData.data(), indexStart, indexStep);
105
106 // Now create the pulseTimes
107 std::transform(rawData.cbegin(), rawData.cend(), std::back_inserter(pulseTimes),
108 [start](ValueType incremental_time) { return start + incremental_time; });
109
110 // increment the slab to get
111 indexStart[0] += indexStep[0];
112 indexStep[0] = std::min(indexStep[0], numValues - indexStart[0]);
113 // resize the vector
114 rawData.resize(indexStep[0]);
115 }
116}
117
119 if (!pulseTimes.empty()) {
120 const auto minimum = std::min_element(pulseTimes.cbegin(), pulseTimes.cend());
121 startTime = minimum->toISO8601String();
122 }
123 // otherwise the existing startTime stays
124}
125
127 if (pulseTimes.empty()) {
128 // set periods to empty vector
129 periodNumbers = std::vector<int>();
130 have_period_info = true;
131 } else if (pulseTimes.size() != periodNumbers.size()) {
132 // something went wrong, everything is first period
133 have_period_info = false;
134 }
135}
136
137//----------------------------------------------------------------------------------------------
138
139size_t BankPulseTimes::numberOfPulses() const { return pulseTimes.size(); }
140
142 if (m_sorting_info == PulseSorting::UNKNOWN) {
143 // only allow one thread to check sorting at a time
144 // others can get the value without interference
145 std::scoped_lock<std::mutex> _lock(m_sortingMutex);
146 if (std::is_sorted(pulseTimes.cbegin(), pulseTimes.cend()))
147 m_sorting_info = PulseSorting::PULSETIME_SORT;
148 else
149 m_sorting_info = PulseSorting::UNSORTED;
150 }
151 return m_sorting_info == PulseSorting::PULSETIME_SORT;
152}
153
154int BankPulseTimes::periodNumber(const size_t index) const {
156 return this->periodNumbers[index];
157 else
158 return FIRST_PERIOD;
159}
160
161const Mantid::Types::Core::DateAndTime &BankPulseTimes::pulseTime(const size_t index) const {
162 return this->pulseTimes[index];
163}
164
165//----------------------------------------------------------------------------------------------
166
167namespace {
168std::size_t getFirstIncludedIndex(const std::vector<Mantid::Types::Core::DateAndTime> &pulseTimes,
169 const std::size_t startIndex, const Mantid::Types::Core::DateAndTime &start,
170 const Mantid::Types::Core::DateAndTime &stop) {
171 const auto NUM_PULSES{pulseTimes.size()};
172 if (startIndex >= NUM_PULSES)
173 return NUM_PULSES;
174
175 for (size_t i = startIndex; i < NUM_PULSES; ++i) {
176 const auto pulseTime = pulseTimes[i];
177 if (pulseTime >= start && pulseTime < stop)
178 return i;
179 }
180 return NUM_PULSES; // default is the number of pulses
181}
182
183std::size_t getFirstExcludedIndex(const std::vector<Mantid::Types::Core::DateAndTime> &pulseTimes,
184 const std::size_t startIndex, const Mantid::Types::Core::DateAndTime &start,
185 const Mantid::Types::Core::DateAndTime &stop) {
186 const auto NUM_PULSES{pulseTimes.size()};
187 if (startIndex >= NUM_PULSES)
188 return NUM_PULSES;
189
190 for (size_t i = startIndex; i < NUM_PULSES; ++i) {
191 const auto pulseTime = pulseTimes[i];
192 if (pulseTime < start || pulseTime > stop)
193 return i;
194 }
195 return NUM_PULSES; // default is the number of pulses
196}
197} // namespace
198
199// use this to get timeof interest into pulseindexter
200std::vector<size_t> BankPulseTimes::getPulseIndices(const Mantid::Types::Core::DateAndTime &start,
201 const Mantid::Types::Core::DateAndTime &stop) const {
202 std::vector<size_t> roi;
203 if (this->arePulseTimesIncreasing()) {
204 // sorted pulse times don't have to go through the whole vector, just look at the ends
205 const bool includeStart = start <= this->pulseTimes.front();
206 const bool includeStop = stop >= this->pulseTimes.back();
207 if (!(includeStart && includeStop)) {
208 // get start index
209 if (includeStart) {
210 roi.push_back(0);
211 } else {
212 // do a linear search with the assumption that the index will be near the beginning
213 roi.push_back(getFirstIncludedIndex(this->pulseTimes, 0, start, stop));
214 }
215 // get stop index
216 if (includeStop) {
217 roi.push_back(this->pulseTimes.size());
218 } else {
219 const auto start_index = roi.front();
220 // do a linear search with the assumption that the index will be near the beginning
221 for (size_t index = this->pulseTimes.size() - 1; index > start_index; --index) {
222 if (this->pulseTime(index) <= stop) {
223 roi.push_back(index + 1); // include this pulse
224 break;
225 }
226 }
227 }
228 }
229 } else {
230 // loop through the entire vector of pulse times
231 const auto [min_ele, max_ele] = std::minmax_element(this->pulseTimes.cbegin(), this->pulseTimes.cend());
232 const bool includeStart = (start <= *min_ele);
233 const bool includeStop = (stop >= *max_ele);
234
235 // only put together range if one is needed
236 if (!(includeStart && includeStop)) {
237 const auto NUM_PULSES = this->pulseTimes.size();
238 std::size_t firstInclude = getFirstIncludedIndex(this->pulseTimes, 0, start, stop);
239 while (firstInclude < NUM_PULSES) {
240 auto firstExclude = getFirstExcludedIndex(this->pulseTimes, firstInclude + 1, start, stop);
241 if (firstInclude != firstExclude) {
242 roi.push_back(firstInclude);
243 roi.push_back(firstExclude);
244 firstInclude = getFirstIncludedIndex(this->pulseTimes, firstExclude + 1, start, stop);
245 }
246 }
247 }
248 }
249
250 if ((!roi.empty()) && (roi.size() % 2 != 0)) {
251 std::stringstream msg;
252 msg << "Invalid state for ROI. Has odd number of values: " << roi.size();
253 throw std::runtime_error(msg.str());
254 }
255
256 return roi;
257}
258
259std::vector<size_t> BankPulseTimes::getPulseIndices(const std::vector<Mantid::Kernel::TimeInterval> &splitters) const {
260 std::vector<size_t> roi;
261 size_t start_index{0};
262 for (const auto &splitter : splitters) {
263 // do a linear search using the previous index as the starting point
264 roi.push_back(start_index =
265 getFirstIncludedIndex(this->pulseTimes, start_index, splitter.start(), splitter.stop()));
266 // we need the one before the first excluded index so do -1
267 roi.push_back(start_index =
268 getFirstExcludedIndex(this->pulseTimes, start_index, splitter.start(), splitter.stop()) - 1);
269 }
270 return roi;
271}
272
273//----------------------------------------------------------------------------------------------
274
283bool BankPulseTimes::equals(size_t otherNumPulse, const std::string &otherStartTime) {
284 return ((this->startTime == otherStartTime) && (this->pulseTimes.size() == otherNumPulse));
285}
286
287} // namespace Mantid::DataHandling
std::map< DeltaEMode::Type, std::string > index
std::vector< size_t > getPulseIndices(const Mantid::Types::Core::DateAndTime &start, const Mantid::Types::Core::DateAndTime &stop) const
Return a vector of [include,exclude) indices into the pulse vectors that are between the start and st...
size_t numberOfPulses() const
The number of pulses being held.
void readData(Nexus::File &file, std::size_t numValues, Mantid::Types::Core::DateAndTime &start)
int periodNumber(const size_t index) const
The raw number from the period stored in the logs.
bool arePulseTimesIncreasing() const
Threadsafe method to access cached information about sorting.
std::string startTime
String describing the start time.
std::vector< Mantid::Types::Core::DateAndTime > pulseTimes
Array of the pulse times.
void updateStartTime()
This determines the start time by finding the minimum value in the array.
const Mantid::Types::Core::DateAndTime & pulseTime(const size_t index) const
The wall clock time of the pulse.
bool equals(size_t otherNumPulse, const std::string &otherStartTime)
Equals.
static const int FIRST_PERIOD
Starting number for assigning periods.
std::vector< int > periodNumbers
Vector of period numbers corresponding to each pulse.
BankPulseTimes(Nexus::File &file, const std::vector< int > &periodNumbers)
Constructor with Nexus::File.
static const std::string DEFAULT_START_TIME
Unix epoch used as default epoch when the file does not specify one.
void finalizePeriodNumbers()
Ensure that we always have a consistency between nPulses and periodNumbers containers.
static unsigned short constexpr UINT64
static unsigned short constexpr FLOAT64
std::vector< dimsize_t > DimVector