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 eventRange = this->getEventIndexRange(lastPulseIndex - 1);
57 while (eventRange.first == eventRange.second && eventRange.second > 0) {
58 --lastPulseIndex;
59 eventRange = this->getEventIndexRange(lastPulseIndex - 1);
60 }
61
62 // update the value if it has changed
63 if ((firstPulseIndex != m_roi.front()) || (lastPulseIndex != m_roi.back())) {
64 auto roi_combined = Mantid::Kernel::ROI::calculate_intersection(m_roi, {firstPulseIndex, lastPulseIndex});
65 m_roi.clear();
66 if (roi_combined.empty()) {
67 // if roi_combined is empty then no pulses should be included, just set range to 0
68 m_roi.push_back(0);
69 m_roi.push_back(0);
70 } else
71 m_roi.assign(roi_combined.cbegin(), roi_combined.cend());
72 }
73
74 // after the updates, recalculate if the roi is more than a single region
75 m_roi_complex = bool(m_roi.size() > 2);
76}
77
83 // return early if the number of event indices is too small
84 if (1 >= m_event_index->size())
85 return 1;
86
87 size_t firstPulseIndex = 0;
88
89 // special case to stop from setting up temporary objects because the first event is in the first pulse
90 if (m_firstEventIndex != 0) {
91 // linear search is used because it is more likely that the pulse index is earlier in the array.
92 // a bisecting search would win if most of the time the first event_index is much after the first quarter of the
93 // pulse_index array.
94 const auto event_index_end = m_event_index->cend();
95 auto event_index_iter = m_event_index->cbegin();
96
97 while ((m_firstEventIndex < *event_index_iter) || (m_firstEventIndex >= *(event_index_iter + 1))) {
98 event_index_iter++;
99
100 // make sure not to go past the end
101 if (event_index_iter + 1 == event_index_end)
102 break;
103 }
104 firstPulseIndex = static_cast<size_t>(std::distance(m_event_index->cbegin(), event_index_iter));
105 }
106
107 // verify that there isn't a repeat right after the found value
108 if (firstPulseIndex + 1 != m_event_index->size()) {
109 for (; firstPulseIndex < m_event_index->size() - 1; ++firstPulseIndex) {
110 if ((*m_event_index)[firstPulseIndex] != (*m_event_index)[firstPulseIndex + 1]) {
111 break;
112 }
113 }
114 }
115
116 return firstPulseIndex;
117}
118
124 return m_event_index->size();
125
126 const auto eventIndexValue = m_firstEventIndex + m_numEvents;
127 // linear search is used because it is more likely that the pulse index is closer to the end of the array.
128 // a bisecting search would win if most of the time the first event_index is much after the first quarter of the
129 // pulse_index array.
130 const auto event_index_end = m_event_index->crend();
131 auto event_index_iter = m_event_index->crbegin();
132 while ((eventIndexValue < *event_index_iter)) {
133 event_index_iter++;
134
135 // make sure not to go past the end
136 if (event_index_iter + 1 == event_index_end)
137 break;
138 }
139
140 return m_event_index->size() - static_cast<size_t>(std::distance(m_event_index->crbegin(), event_index_iter));
141}
142
143size_t PulseIndexer::getFirstPulseIndex() const { return m_roi.front(); }
144size_t PulseIndexer::getLastPulseIndex() const { return m_roi.back(); }
145
146std::pair<size_t, size_t> PulseIndexer::getEventIndexRange(const size_t pulseIndex) const {
147 const auto start = this->getStartEventIndex(pulseIndex);
148 // return early if the start is too big
149 if (start >= m_numEvents)
150 return std::make_pair(start, m_numEvents);
151
152 // get the end index
153 const auto stop = this->getStopEventIndex(pulseIndex);
154 if (start > stop) {
155 std::stringstream msg;
156 msg << "Something went really wrong with pulseIndex=" << pulseIndex << ": " << start << " > " << stop << "| "
157 << m_entry_name << " startAt=" << m_firstEventIndex << " numEvents=" << m_event_index->size() << " RAWINDICES=["
158 << start + m_firstEventIndex << ",?)"
159 << " pulseIndex=" << pulseIndex << " of " << m_event_index->size();
160 throw std::runtime_error(msg.str());
161 }
162
163 return std::make_pair(start, stop);
164}
165
166size_t PulseIndexer::getStartEventIndex(const size_t pulseIndex) const {
167 // return the start index to signify not using
168 if (pulseIndex >= m_roi.back())
169 return this->getStopEventIndex(pulseIndex);
170
171 // determine the correct start index
172 size_t eventIndex;
173 if (pulseIndex <= m_roi.front()) {
174 eventIndex = (*m_event_index)[m_roi.front()];
175 } else {
176 eventIndex = (*m_event_index)[pulseIndex];
177 }
178
179 // return the index with the offset subtracted
180 if (eventIndex >= m_firstEventIndex) {
181 return eventIndex - m_firstEventIndex;
182 } else {
183 return 0;
184 }
185}
186
187bool PulseIndexer::includedPulse(const size_t pulseIndex) const {
188 if (pulseIndex >= m_roi.back()) {
189 return false; // case is already handled in getStopEventIndex and shouldn't be called
190 } else if (pulseIndex < m_roi.front()) {
191 return false;
192 } else {
193 if (m_roi_complex) {
194 // get the first ROI time boundary greater than the input time. Note that an ROI is a series of alternating
195 // ROI_USE and ROI_IGNORE values.
196 const auto iterUpper = std::upper_bound(m_roi.cbegin(), m_roi.cend(), pulseIndex);
197
198 return (std::distance(m_roi.cbegin(), iterUpper) % 2 != 0);
199 } else {
200 return true; // value is past m_roi.front()
201 }
202 }
203}
204
205size_t PulseIndexer::getStopEventIndex(const size_t pulseIndex) const {
206 if (pulseIndex >= m_roi.back()) // indicates everything has already been read
207 return m_numEvents;
208
209 // return the start index to signify not using
210 if (!includedPulse(pulseIndex))
211 return this->getStartEventIndex(pulseIndex);
212
213 const auto pulseIndexEnd = pulseIndex + 1;
214
215 // check if the requests have gone past the end - order of if/else matters
216 size_t eventIndex = (pulseIndexEnd >= m_event_index->size()) ? m_numEvents : (*m_event_index)[pulseIndexEnd];
217 if (pulseIndexEnd == m_roi.back()) {
218 eventIndex = std::min(m_numEvents, eventIndex);
219 if (pulseIndexEnd == m_event_index->size())
220 eventIndex = m_numEvents;
221 }
222
223 if (eventIndex > m_firstEventIndex)
224 return eventIndex - m_firstEventIndex;
225 else
226 return m_numEvents;
227}
228
229// ----------------------------------------- range for iteration
233
237
239
241
242// ----------------------------------------- input iterator implementation
243
246 const auto eventRange = m_indexer->getEventIndexRange(m_value.pulseIndex);
247 m_value.eventIndexStart = eventRange.first;
248 m_value.eventIndexStop = eventRange.second;
249
251}
252
254
256 ++m_value.pulseIndex;
257 // cache the final pulse index to use
258 const auto lastPulseIndex = m_indexer->m_roi.back();
259
260 // advance to the next included pulse
261 while ((m_value.pulseIndex < lastPulseIndex) && (!m_indexer->includedPulse(m_value.pulseIndex)))
262 ++m_value.pulseIndex;
263
264 // return early if this has advanced to the end
265 if (m_value.pulseIndex >= lastPulseIndex)
266 return *this;
267
268 while (this->calculateEventRange() && (m_value.pulseIndex < lastPulseIndex)) {
269 ++m_value.pulseIndex; // move forward a pulse while there is
270 }
271
272 return *this;
273}
274
276 if (this->m_indexer != other.m_indexer)
277 return false;
278 else
279 return this->m_value.pulseIndex == other.m_value.pulseIndex;
280}
281
282bool PulseIndexer::Iterator::operator!=(const PulseIndexer::Iterator &other) const { return !(*this == other); }
283
284} // 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, const Nexus::NexusDescriptor &descriptor)
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