Mantid
Loading...
Searching...
No Matches
ProcessBankCompressed.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
13#include "MantidKernel/Timer.h"
14
15#include "tbb/parallel_for.h"
16#include <iostream>
17
18namespace Mantid {
19namespace DataHandling {
21 API::Progress *prog, std::shared_ptr<std::vector<uint32_t>> event_detid,
22 std::shared_ptr<std::vector<float>> event_tof, size_t startAt,
23 std::shared_ptr<std::vector<uint64_t>> event_index,
24 std::shared_ptr<BankPulseTimes> bankPulseTimes, detid_t min_detid,
25 detid_t max_detid,
26 std::shared_ptr<std::vector<double>> histogram_bin_edges,
27 const double divisor)
28 : Task(), m_loader(m_loader), m_entry_name(entry_name), m_prog(prog), m_event_detid(std::move(event_detid)),
29 m_event_tof(std::move(event_tof)), m_firstEventIndex(startAt), m_event_index(std::move(event_index)),
30 m_bankPulseTimes(std::move(bankPulseTimes)), m_detid_min(min_detid), m_detid_max(max_detid),
31 m_tof_min(static_cast<float>(histogram_bin_edges->front())),
32 m_tof_max(static_cast<float>(histogram_bin_edges->back())) {
33
34 m_cost = static_cast<double>(m_event_detid->size());
35
36 // setup the vector with sorting information
37 const auto NUM_DETS = static_cast<size_t>(m_detid_max - m_detid_min) + 1;
38 m_sorting.clear();
39 m_sorting.resize(NUM_DETS, DataObjects::UNSORTED);
40
41 // create the spetcra accumulators
42 const auto bin_mode = (divisor >= 0) ? CompressBinningMode::LINEAR : CompressBinningMode::LOGARITHMIC;
43 const auto divisor_abs = abs(divisor);
44 m_factory = std::make_unique<CompressEventAccumulatorFactory>(histogram_bin_edges, divisor_abs, bin_mode);
45}
46
47namespace {
48size_t estimate_avg_events(const size_t num_events, const size_t num_dets, const size_t num_periods) {
49 double result = static_cast<double>(num_events) / static_cast<double>(num_dets) / static_cast<double>(num_periods);
50 return static_cast<size_t>(result);
51}
52} // namespace
53
55 const auto NUM_PERIODS = m_loader.m_ws.nPeriods();
56 const auto NUM_DETS = static_cast<size_t>(m_detid_max - m_detid_min) + 1;
57 const auto NUM_EVENTS_AVG = estimate_avg_events(m_event_detid->size(), NUM_DETS, NUM_PERIODS);
58
59 std::vector<size_t> counts;
60 if (precount) {
61 const auto detid_min = static_cast<size_t>(m_detid_min);
62 const auto detid_max = static_cast<size_t>(m_detid_max);
63 counts.assign(NUM_DETS + 1, 0);
64 for (const auto &detid : *m_event_detid) {
65 if (!(detid < detid_min || detid > detid_max)) // or allows for skipping out early
66 counts[detid - detid_min]++;
67 }
68 }
69
70 m_spectra_accum.resize(NUM_PERIODS);
71 for (size_t periodIndex = 0; periodIndex < NUM_PERIODS; ++periodIndex) {
72 m_spectra_accum[periodIndex].reserve(NUM_DETS);
73 for (size_t det_index = 0; det_index <= NUM_DETS; ++det_index) {
74 if (precount)
75 m_spectra_accum[periodIndex].push_back(m_factory->create(counts[det_index]));
76 else
77 m_spectra_accum[periodIndex].push_back(m_factory->create(NUM_EVENTS_AVG));
78 }
79 }
80
81 // the factory is no longer needed so drop the pointer
82 m_factory.reset();
83}
84
85void ProcessBankCompressed::addEvent(const size_t period_index, const size_t event_index) {
86 // comparing to integers is cheapest
87 const auto detid = static_cast<detid_t>(m_event_detid->operator[](event_index));
88 if ((detid < m_detid_min) || (detid > m_detid_max)) {
89 return;
90 }
91
92 // check if the tof is within range
93 const auto tof = m_event_tof->operator[](event_index);
94 if (((tof - m_tof_min) * (tof - m_tof_max) > 0.)) {
95 return;
96 }
97
98 // accumulators are zero indexed
99 const auto det_index = static_cast<size_t>(detid - m_detid_min);
100 m_spectra_accum[period_index][det_index]->addEvent(tof);
101}
102
104 Kernel::Timer timer;
105 const auto NUM_EVENTS = m_event_detid->size();
106
107 const auto *alg = m_loader.alg;
108
109 // iterate through all events in a single pulse
110 if (m_event_index || m_loader.m_ws.nPeriods() > 1 || alg->m_is_time_filtered || alg->filter_bad_pulses) {
111 // set up wall-clock filtering if it was requested
112 std::vector<size_t> pulseROI;
113 if (alg->m_is_time_filtered) {
114 pulseROI = m_bankPulseTimes->getPulseIndices(alg->filter_time_start, alg->filter_time_stop);
115 }
116
117 if (alg->filter_bad_pulses) {
119 pulseROI, m_bankPulseTimes->getPulseIndices(alg->bad_pulses_timeroi->toTimeIntervals()));
120 }
121
122 const PulseIndexer pulseIndexer(m_event_index, m_firstEventIndex, NUM_EVENTS, m_entry_name, pulseROI);
123 for (const auto &pulseIter : pulseIndexer) {
124 const int logPeriodNumber = m_bankPulseTimes->periodNumber(pulseIter.pulseIndex);
125 const auto periodIndex = static_cast<size_t>(logPeriodNumber - 1);
126
127 // add all events in this pulse
128 for (std::size_t eventIndex = pulseIter.eventIndexStart; eventIndex < pulseIter.eventIndexStop; ++eventIndex) {
129 this->addEvent(periodIndex, eventIndex);
130 }
131 }
132 } else {
133 // add all events in the list which are all in the first period
134 constexpr size_t periodIndex{0};
135 for (std::size_t eventIndex = 0; eventIndex < NUM_EVENTS; ++eventIndex) {
136 this->addEvent(periodIndex, eventIndex);
137 }
138 }
139
140 m_event_detid.reset();
141 m_event_tof.reset();
142 m_event_index.reset();
143 m_bankPulseTimes.reset();
144
145#ifndef _WIN32
146 if (m_loader.alg->getLogger().isDebug())
147 m_loader.alg->getLogger().debug() << "Time to collectEvents: " << m_entry_name << " " << timer << "\n";
148#endif
149}
150
151/*
152 * A side effect of this is that the CompressEventAccumulator is converted to a nullptr after the events have been added
153 * to the EventList.
154 */
155void ProcessBankCompressed::createWeightedEvents(const size_t period_index, const detid_t detid,
156 std::vector<Mantid::DataObjects::WeightedEventNoTime> *raw_events) {
157 if ((detid < m_detid_min) || detid > m_detid_max) {
158 std::stringstream msg;
159 msg << "Encountered invalid detid=" << detid;
160 throw std::runtime_error(msg.str());
161 }
162
163 const auto det_index = static_cast<size_t>(detid - m_detid_min);
164 m_spectra_accum[period_index][det_index]->createWeightedEvents(raw_events);
165
166 // let go of the unique_ptr to free up memory
167 m_spectra_accum[period_index][det_index].reset();
168}
169
170namespace { // anonymous
171// private class to allow TBB do a more controlled spreading out of event creation
172// since each detid/spectrum/EventList is already independent, these can be done at once
173class EventCreationTask {
174public:
175 EventCreationTask(std::vector<std::unique_ptr<DataHandling::CompressEventAccumulator>> *accumulators,
176 std::vector<std::vector<Mantid::DataObjects::WeightedEventNoTime> *> *eventlists,
177 const detid_t detid_min, std::vector<DataObjects::EventSortType> *sorting)
178 : m_accumulators(accumulators), m_eventlists(eventlists), m_sorting(sorting),
179 m_detid_min(static_cast<size_t>(detid_min)) {}
180
181 void operator()(const tbb::blocked_range<size_t> &range) const {
182 for (size_t index = range.begin(); index < range.end(); ++index) {
183 auto &accumulator = m_accumulators->operator[](index);
184 if (accumulator->totalWeight() > 0.) {
185 auto *raw_events = m_eventlists->operator[](index + m_detid_min);
186 // create the events on the correct event list
187 m_accumulators->operator[](index)->createWeightedEvents(raw_events);
188 // drop extra space if the capacity is more than 10% of what is needed
189 if (static_cast<double>(raw_events->capacity()) > 1.1 * static_cast<double>(raw_events->size()))
190 raw_events->shrink_to_fit();
191 }
192 // get the sorting type back
193 m_sorting->operator[](index) = m_accumulators->operator[](index)->getSortType();
194 // let go of the unique_ptr to free up memory
195 m_accumulators->operator[](index).reset();
196 }
197 }
198
199private:
200 std::vector<std::unique_ptr<DataHandling::CompressEventAccumulator>> *m_accumulators;
201 std::vector<std::vector<Mantid::DataObjects::WeightedEventNoTime> *> *m_eventlists;
202 std::vector<DataObjects::EventSortType> *m_sorting;
203 const size_t m_detid_min;
204};
205
206} // namespace
207
209 Kernel::Timer timer;
210 const auto num_periods = m_loader.m_ws.nPeriods();
211 const auto num_dets = static_cast<size_t>(m_detid_max - m_detid_min + 1);
212 // loop over periods
213 for (size_t period_index = 0; period_index < num_periods; ++period_index) {
214 // create the events and add them to the EventLists
215 EventCreationTask create_task(&(m_spectra_accum[period_index]), &m_loader.weightedNoTimeEventVectors[period_index],
217 // grainsize selected to balance overhead of creating threads with how much work is done in a thread
218 const size_t grainsize = std::min<size_t>(20, num_dets / 20);
219 tbb::parallel_for(tbb::blocked_range<size_t>(0, num_dets, grainsize), create_task);
220 }
221
222#ifndef _WIN32
223 if (m_loader.alg->getLogger().isDebug())
224 m_loader.alg->getLogger().debug() << "Time to addToEventLists.append: " << m_entry_name << " " << timer << "\n";
225#endif
226}
227
229 // timer for performance
230 Kernel::Timer timer;
231 auto *alg = m_loader.alg;
232
234 m_prog->report();
235
236 // parse the events
237 this->collectEvents();
238 m_prog->report(m_entry_name + ": accumulated events");
239
240 // create weighted events on the workspace
241 this->addToEventLists();
242 m_prog->report(m_entry_name + ": created events");
243
244 // TODO need to coordinate with accumulators to find out if they were sorted
245 // set sort order on all of the EventLists since they were sorted by TOF
246 const auto pixelID_to_wi_offset = m_loader.pixelID_to_wi_offset;
247 auto &outputWS = m_loader.m_ws;
248 const size_t numEventLists = m_loader.m_ws.getNumberHistograms();
249 const detid_t pixelIDtoWSVec_size = static_cast<detid_t>(m_loader.pixelID_to_wi_vector.size());
250 for (detid_t detid = m_detid_min; detid <= m_detid_max; ++detid) {
251 const detid_t detid_offset = detid + pixelID_to_wi_offset;
252 if (!(detid_offset < 0 || detid_offset >= pixelIDtoWSVec_size)) {
253 const auto wi = m_loader.pixelID_to_wi_vector[detid_offset];
254 if (wi < numEventLists) {
255 const auto sortOrder = m_sorting[static_cast<size_t>(detid - m_detid_min)];
256 outputWS.getSpectrum(wi).setSortOrder(sortOrder);
257 }
258 }
259 }
260
261#ifndef _WIN32
262 if (alg->getLogger().isDebug())
263 alg->getLogger().debug() << "Time to ProcessBankCompressed " << m_entry_name << " " << timer << "\n";
264#endif
265} // END-OF-RUN()
266
268 double totalWeightedEvents = std::accumulate(
269 m_spectra_accum.cbegin(), m_spectra_accum.cend(), 0., [](const auto &current, const auto &period_spectra) {
270 return current + std::accumulate(period_spectra.cbegin(), period_spectra.cend(), 0.,
271 [](const auto &current, const auto &spectra_accum) {
272 return current + spectra_accum->totalWeight();
273 });
274 });
275 return totalWeightedEvents;
276}
277
278} // namespace DataHandling
279} // namespace Mantid
std::map< DeltaEMode::Type, std::string > index
const size_t m_detid_min
std::vector< std::unique_ptr< DataHandling::CompressEventAccumulator > > * m_accumulators
std::vector< DataObjects::EventSortType > * m_sorting
std::vector< std::vector< Mantid::DataObjects::WeightedEventNoTime > * > * m_eventlists
Kernel::Logger & getLogger() const
Returns a reference to the logger.
Helper class for reporting progress from algorithms.
Definition Progress.h:25
Helper class for LoadEventNexus that is specific to the current default loading code for NXevent_data...
std::vector< std::vector< std::vector< Mantid::DataObjects::WeightedEventNoTime > * > > weightedNoTimeEventVectors
Vector where index = event_id; value = ptr to std::vector<WeightedEventNoTime> in the event list.
bool precount
Do we pre-count the # of events in each pixel ID?
std::vector< size_t > pixelID_to_wi_vector
Vector where (index = pixel ID+pixelID_to_wi_offset), value = workspace index)
detid_t pixelID_to_wi_offset
Offset in the pixelID_to_wi_vector to use.
std::vector< DataObjects::EventSortType > m_sorting
std::shared_ptr< BankPulseTimes > m_bankPulseTimes
Pulse times for this bank.
void addEvent(const size_t period_index, const size_t event_index)
std::unique_ptr< CompressEventAccumulatorFactory > m_factory
factory for creating accumulators
std::shared_ptr< std::vector< uint64_t > > m_event_index
vector of event index (length of # of pulses)
DefaultEventLoader & m_loader
Algorithm being run.
double totalWeight() const
method only intended for testing
const size_t m_firstEventIndex
index of the first event from event_index
std::shared_ptr< std::vector< uint32_t > > m_event_detid
event pixel ID array
API::Progress * m_prog
Progress reporting.
std::shared_ptr< std::vector< float > > m_event_tof
event TOF array
void createWeightedEvents(const size_t period_index, const detid_t detid, std::vector< Mantid::DataObjects::WeightedEventNoTime > *raw_events)
void run() override
Main method that performs the work for the task.
std::vector< std::vector< std::unique_ptr< DataHandling::CompressEventAccumulator > > > m_spectra_accum
Objects holding individual spectra.
PulseIndexer contains information for mapping from pulse index/number to event index.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
bool isDebug() const
Returns true if log level is at least debug.
Definition Logger.cpp:189
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
A Task is a unit of work to be scheduled and run by a ThreadPool.
Definition Task.h:29
double m_cost
Cached computational cost for the thread.
Definition Task.h:82
A simple class that provides a wall-clock (not processor time) timer.
Definition Timer.h:27
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
Helper class which provides the Collimation Length for SANS instruments.
int32_t detid_t
Typedef for a detector ID.
STL namespace.