Mantid
Loading...
Searching...
No Matches
PulseIndexer.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2024 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 <sstream>
11#include <utility>
12
13namespace Mantid::DataHandling {
14PulseIndexer::PulseIndexer(std::shared_ptr<std::vector<uint64_t> const> const &event_index,
15 const std::size_t firstEventIndex, const std::size_t numEvents,
16 const std::string &entry_name, const std::vector<size_t> &pulse_roi)
17 : m_event_index(std::move(event_index)), m_firstEventIndex(firstEventIndex), m_numEvents(numEvents),
18 m_roi_complex(false), m_entry_name(entry_name) {
19 // cache the number of pulses
20 m_numPulses = m_event_index->size();
21
22 // determine first useful pulse index
23 m_roi.push_back(this->determineFirstPulseIndex());
24 // for now, use all pulses up to the end
25 m_roi.push_back(this->determineLastPulseIndex());
26
27 // update based on the information in the pulse_roi
28 if (!pulse_roi.empty()) {
29 if (pulse_roi.size() % 2 != 0)
30 throw std::runtime_error("Invalid size for pulsetime roi, must be even or empty");
31
32 // new roi is the intersection of these two
33 auto roi_combined = Mantid::Kernel::ROI::calculate_intersection(m_roi, pulse_roi);
34 m_roi.clear();
35 if (roi_combined.empty()) {
36 // if roi_combined is empty then no pulses should be included, just set range to 0
37 m_roi.push_back(0);
38 m_roi.push_back(0);
39 m_roi_complex = false;
40 return;
41 } else
42 m_roi.assign(roi_combined.cbegin(), roi_combined.cend());
43 m_roi_complex = bool(m_roi.size() > 2);
44 }
45
46 // determine if should trim the front end to remove empty pulses
47 auto firstPulseIndex = m_roi.front();
48 auto eventRange = this->getEventIndexRange(firstPulseIndex);
49 while (eventRange.first == eventRange.second && eventRange.first < m_numEvents) {
50 ++firstPulseIndex;
51 eventRange = this->getEventIndexRange(firstPulseIndex);
52 }
53
54 // determine if should trim the back end to remove empty pulses
55 auto lastPulseIndex = m_roi.back();
56 if (lastPulseIndex > 0) {
57 eventRange = this->getEventIndexRange(lastPulseIndex - 1);
58 while (eventRange.first == eventRange.second && eventRange.second > 0 && lastPulseIndex > 1) {
59 --lastPulseIndex;
60 eventRange = this->getEventIndexRange(lastPulseIndex - 1);
61 }
62 }
63
64 // update the value if it has changed
65 if ((firstPulseIndex != m_roi.front()) || (lastPulseIndex != m_roi.back())) {
66 auto roi_combined = Mantid::Kernel::ROI::calculate_intersection(m_roi, {firstPulseIndex, lastPulseIndex});
67 m_roi.clear();
68 if (roi_combined.empty()) {
69 // if roi_combined is empty then no pulses should be included, just set range to 0
70 m_roi.push_back(0);
71 m_roi.push_back(0);
72 } else
73 m_roi.assign(roi_combined.cbegin(), roi_combined.cend());
74 }
75
76 // after the updates, recalculate if the roi is more than a single region
77 m_roi_complex = bool(m_roi.size() > 2);
78}
79
85 // return early if the number of event indices is too small
86 if (1 >= m_event_index->size())
87 return 1;
88
89 size_t firstPulseIndex = 0;
90
91 // special case to stop from setting up temporary objects because the first event is in the first pulse
92 if (m_firstEventIndex != 0) {
93 // linear search is used because it is more likely that the pulse index is earlier in the array.
94 // a bisecting search would win if most of the time the first event_index is much after the first quarter of the
95 // pulse_index array.
96 const auto event_index_end = m_event_index->cend();
97 auto event_index_iter = m_event_index->cbegin();
98
99 while ((m_firstEventIndex < *event_index_iter) || (m_firstEventIndex >= *(event_index_iter + 1))) {
100 event_index_iter++;
101
102 // make sure not to go past the end
103 if (event_index_iter + 1 == event_index_end)
104 break;
105 }
106 firstPulseIndex = static_cast<size_t>(std::distance(m_event_index->cbegin(), event_index_iter));
107 }
108
109 // verify that there isn't a repeat right after the found value
110 if (firstPulseIndex + 1 != m_event_index->size()) {
111 for (; firstPulseIndex < m_event_index->size() - 1; ++firstPulseIndex) {
112 if ((*m_event_index)[firstPulseIndex] != (*m_event_index)[firstPulseIndex + 1]) {
113 break;
114 }
115 }
116 }
117
118 return firstPulseIndex;
119}
120
126 return m_event_index->size();
127
128 const auto eventIndexValue = m_firstEventIndex + m_numEvents;
129 // linear search is used because it is more likely that the pulse index is closer to the end of the array.
130 // a bisecting search would win if most of the time the first event_index is much after the first quarter of the
131 // pulse_index array.
132 const auto event_index_end = m_event_index->crend();
133 auto event_index_iter = m_event_index->crbegin();
134 while ((eventIndexValue < *event_index_iter)) {
135 event_index_iter++;
136
137 // make sure not to go past the end
138 if (event_index_iter + 1 == event_index_end)
139 break;
140 }
141
142 return m_event_index->size() - static_cast<size_t>(std::distance(m_event_index->crbegin(), event_index_iter));
143}
144
145size_t PulseIndexer::getFirstPulseIndex() const { return m_roi.front(); }
146size_t PulseIndexer::getLastPulseIndex() const { return m_roi.back(); }
147
148std::pair<size_t, size_t> PulseIndexer::getEventIndexRange(const size_t pulseIndex) const {
149 const auto start = this->getStartEventIndex(pulseIndex);
150 // return early if the start is too big
151 if (start >= m_numEvents)
152 return std::make_pair(start, m_numEvents);
153
154 // get the end index
155 const auto stop = this->getStopEventIndex(pulseIndex);
156 if (start > stop) {
157 std::stringstream msg;
158 msg << "Something went really wrong with pulseIndex=" << pulseIndex << ": " << start << " > " << stop << "| "
159 << m_entry_name << " startAt=" << m_firstEventIndex << " numEvents=" << m_event_index->size() << " RAWINDICES=["
160 << start + m_firstEventIndex << ",?)"
161 << " pulseIndex=" << pulseIndex << " of " << m_event_index->size();
162 throw std::runtime_error(msg.str());
163 }
164
165 return std::make_pair(start, stop);
166}
167
168size_t PulseIndexer::getStartEventIndex(const size_t pulseIndex) const {
169 // return the start index to signify not using
170 if (pulseIndex >= m_roi.back())
171 return this->getStopEventIndex(pulseIndex);
172
173 // determine the correct start index
174 size_t eventIndex;
175 if (pulseIndex <= m_roi.front()) {
176 eventIndex = (*m_event_index)[m_roi.front()];
177 } else {
178 eventIndex = (*m_event_index)[pulseIndex];
179 }
180
181 // return the index with the offset subtracted
182 if (eventIndex >= m_firstEventIndex) {
183 return eventIndex - m_firstEventIndex;
184 } else {
185 return 0;
186 }
187}
188
189bool PulseIndexer::includedPulse(const size_t pulseIndex) const {
190 if (pulseIndex >= m_roi.back()) {
191 return false; // case is already handled in getStopEventIndex and shouldn't be called
192 } else if (pulseIndex < m_roi.front()) {
193 return false;
194 } else {
195 if (m_roi_complex) {
196 // get the first ROI time boundary greater than the input time. Note that an ROI is a series of alternating
197 // ROI_USE and ROI_IGNORE values.
198 const auto iterUpper = std::upper_bound(m_roi.cbegin(), m_roi.cend(), pulseIndex);
199
200 return (std::distance(m_roi.cbegin(), iterUpper) % 2 != 0);
201 } else {
202 return true; // value is past m_roi.front()
203 }
204 }
205}
206
207size_t PulseIndexer::getStopEventIndex(const size_t pulseIndex) const {
208 if (pulseIndex >= m_roi.back()) // indicates everything has already been read
209 return m_numEvents;
210
211 // return the start index to signify not using
212 if (!includedPulse(pulseIndex))
213 return this->getStartEventIndex(pulseIndex);
214
215 const auto pulseIndexEnd = pulseIndex + 1;
216
217 // check if the requests have gone past the end - order of if/else matters
218 size_t eventIndex = (pulseIndexEnd >= m_event_index->size()) ? m_numEvents : (*m_event_index)[pulseIndexEnd];
219 if (pulseIndexEnd == m_roi.back()) {
220 eventIndex = std::min(m_numEvents, eventIndex);
221 if (pulseIndexEnd == m_event_index->size())
222 eventIndex = m_numEvents;
223 }
224
225 if (eventIndex > m_firstEventIndex)
226 return eventIndex - m_firstEventIndex;
227 else
228 return m_numEvents;
229}
230
231// ----------------------------------------- range for iteration
235
239
241
243
244// ----------------------------------------- input iterator implementation
245
248 const auto eventRange = m_indexer->getEventIndexRange(m_value.pulseIndex);
249 m_value.eventIndexStart = eventRange.first;
250 m_value.eventIndexStop = eventRange.second;
251
253}
254
256
258 ++m_value.pulseIndex;
259 // cache the final pulse index to use
260 const auto lastPulseIndex = m_indexer->m_roi.back();
261
262 // advance to the next included pulse
263 while ((m_value.pulseIndex < lastPulseIndex) && (!m_indexer->includedPulse(m_value.pulseIndex)))
264 ++m_value.pulseIndex;
265
266 // return early if this has advanced to the end
267 if (m_value.pulseIndex >= lastPulseIndex)
268 return *this;
269
270 while (this->calculateEventRange() && (m_value.pulseIndex < lastPulseIndex)) {
271 ++m_value.pulseIndex; // move forward a pulse while there is
272 }
273
274 return *this;
275}
276
278 if (this->m_indexer != other.m_indexer)
279 return false;
280 else
281 return this->m_value.pulseIndex == other.m_value.pulseIndex;
282}
283
284bool PulseIndexer::Iterator::operator!=(const PulseIndexer::Iterator &other) const { return !(*this == other); }
285
286} // namespace Mantid::DataHandling
const std::string & m_value
Definition Algorithm.cpp:71
size_t getFirstPulseIndex() const
Which element in the event_index array is the first one to use.
std::size_t m_firstEventIndex
How far into the array of events the tof/detid are already.
size_t getStopEventIndex(const size_t pulseIndex) const
The one past the last event(tof,detid) index to read for this pulse.
size_t determineFirstPulseIndex() const
This performs a linear search because it is much more likely that the index to look for is at the beg...
std::vector< std::size_t > m_roi
Alternating values describe ranges of [use, don't) of pulse index ranges.
const std::shared_ptr< std::vector< uint64_t > const > m_event_index
vector of indices (length of # of pulses) into the event arrays
bool includedPulse(const size_t pulseIndex) const
returns true when the roi says the pulse should be used
bool m_roi_complex
true when there is more to check than the pulse being between the ends
size_t determineLastPulseIndex() const
This looks at the event_indexes and number of events to read then determines what is the maximum puls...
size_t getStartEventIndex(const size_t pulseIndex) const
The first event(tof,detid) index to read for this pulse.
std::size_t m_numEvents
Total number of events tof/detid that should be processed.
const std::string m_entry_name
Name of the NXentry to be used in exceptions.
std::size_t m_numPulses
Total number of pulsetime/pulseindex.
size_t getLastPulseIndex() const
Which element in the event_index array is the last one to use.
std::pair< size_t, size_t > getEventIndexRange(const size_t pulseIndex) const
The range of event(tof,detid) indices to read for this pulse.
std::size_t numEvents(Nexus::File &file, bool &hasTotalCounts, bool &oldNeXusFileNames, const std::string &prefix)
Get the number of events in the currently opened group.
std::vector< TYPE > calculate_intersection(const std::vector< TYPE > &left, const std::vector< TYPE > &right)
This calculates the intersection of two sorted vectors that represent regions of interest (ROI).
Definition TimeROI.cpp:449
STL namespace.
bool calculateEventRange()
returns true if the range is empty
bool operator!=(const PulseIndexer::Iterator &other) const
bool operator==(const PulseIndexer::Iterator &other) const
const IteratorValue & operator*() const