Mantid
Loading...
Searching...
No Matches
MDEventTreeBuilder.h
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 +
7#pragma once
8
9#include <algorithm>
10#include <queue>
11#include <tbb/parallel_sort.h>
12#include <tbb/task_arena.h>
13#include <thread>
14
15namespace Mantid {
16namespace MDAlgorithms {
17
29template <size_t ND, template <size_t> class MDEventType, typename EventIterator> class MDEventTreeBuilder {
30 using MDEvent = MDEventType<ND>;
31 using IntT = typename MDEvent::IntT;
32 using MortonT = typename MDEvent::MortonT;
37
38public:
40 using IndexCoordinateSwitcher = typename MDEvent::template AccessFor<EventDistributor>;
46 struct Task {
48 const EventIterator begin;
49 const EventIterator end;
50 const typename MDEventType<ND>::MortonT lowerBound;
51 const typename MDEventType<ND>::MortonT upperBound;
52 size_t maxDepth;
53 unsigned level;
54 };
55
56public:
57 MDEventTreeBuilder(const int numWorkers, const size_t threshold, const API::BoxController_sptr &bc,
62 };
68 TreeWithIndexError distribute(std::vector<MDEventType<ND>> &mdEvents);
69
70private:
71 morton_index::MDCoordinate<ND> convertToIndex(std::vector<MDEventType<ND>> &mdEvents,
73 void sortEvents(std::vector<MDEventType<ND>> &mdEvents);
74 BoxBase *doDistributeEvents(std::vector<MDEventType<ND>> &mdEvents);
75 void distributeEvents(Task &tsk, const WORKER_TYPE &wtp);
76 void pushTask(Task &&tsk);
77 std::unique_ptr<Task> popTask();
78 void waitAndLaunchSlave();
79
80private:
81 const int m_numWorkers;
82 const size_t m_eventsThreshold;
83 std::queue<Task> m_tasks;
84 std::mutex m_mutex;
85 std::atomic<bool> m_masterFinished;
86
88 std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> m_extents;
90
93};
94
95template <size_t ND, template <size_t> class MDEventType, typename EventIterator>
99 : m_numWorkers(numWorkers), m_eventsThreshold(threshold), m_masterFinished{false}, m_space{space}, m_bc{bc},
100 m_mortonMin{morton_index::calculateDefaultBound<ND, IntT, MortonT>(std::numeric_limits<IntT>::min())},
101 m_mortonMax{morton_index::calculateDefaultBound<ND, IntT, MortonT>(std::numeric_limits<IntT>::max())} {
102 for (size_t ax = 0; ax < ND; ++ax) {
103 m_extents.emplace_back();
104 m_extents.back().setExtents(space(ax, 0), space(ax, 1));
105 }
106}
107
108template <size_t ND, template <size_t> class MDEventType, typename EventIterator>
111 auto err = convertToIndex(mdEvents, m_space);
112 sortEvents(mdEvents);
113 auto root = doDistributeEvents(mdEvents);
114 return {root, err};
115}
116
117template <size_t ND, template <size_t> class MDEventType, typename EventIterator>
120 if (mdEvents.size() <= m_bc->getSplitThreshold()) {
121 m_bc->incBoxesCounter(0);
122 return new DataObjects::MDBox<MDEvent, ND>(m_bc.get(), 0, m_extents, mdEvents.begin(), mdEvents.end());
123 } else {
124 auto root = new DataObjects::MDGridBox<MDEvent, ND>(m_bc.get(), 0, m_extents);
125 Task tsk{root, mdEvents.begin(), mdEvents.end(), m_mortonMin, m_mortonMax, m_bc->getMaxDepth() + 1, 1};
126
127 if (m_numWorkers == 1)
128 distributeEvents(tsk, SLAVE);
129 else {
130 std::vector<std::thread> workers;
131 workers.emplace_back([this, &tsk]() {
132 distributeEvents(tsk, MASTER);
133 m_masterFinished = true;
134 waitAndLaunchSlave();
135 });
136 for (auto i = 1; i < m_numWorkers; ++i)
137 workers.emplace_back(&MDEventTreeBuilder::waitAndLaunchSlave, this);
138 for (auto &worker : workers)
139 worker.join();
140 }
141 return root;
142 }
143}
144
145template <size_t ND, template <size_t> class MDEventType, typename EventIterator>
148 const morton_index::MDSpaceBounds<ND> &space) {
149 std::vector<morton_index::MDCoordinate<ND>> perThread(m_numWorkers, morton_index::MDCoordinate<ND>(0));
150#pragma omp parallel for num_threads(m_numWorkers)
151 for (int64_t i = 0; i < static_cast<int64_t>(mdEvents.size()); ++i) {
152 morton_index::MDCoordinate<ND> oldCoord{mdEvents[i].getCenter()};
153 IndexCoordinateSwitcher::convertToIndex(mdEvents[i], space);
155 morton_index::indexToCoordinates<ND, IntT, MortonT>(IndexCoordinateSwitcher::getIndex(mdEvents[i]), space);
156 newCoord -= oldCoord;
158 for (size_t d = 0; d < ND; ++d)
159 threadErr[d] = std::max(threadErr[d], std::abs(newCoord[d]));
160 }
162 for (const auto &err : perThread)
163 for (size_t d = 0; d < ND; ++d)
164 maxErr[d] = std::max(maxErr[d], err[d]);
165 return maxErr;
166}
167
168template <size_t ND, template <size_t> class MDEventType, typename EventIterator>
169void MDEventTreeBuilder<ND, MDEventType, EventIterator>::sortEvents(std::vector<MDEventType<ND>> &mdEvents) {
170
171 tbb::task_arena limited_arena(m_numWorkers);
172 limited_arena.execute([&]() {
173 tbb::parallel_sort(mdEvents.begin(), mdEvents.end(), [](const MDEventType<ND> &a, const MDEventType<ND> &b) {
174 return IndexCoordinateSwitcher::getIndex(a) < IndexCoordinateSwitcher::getIndex(b);
175 });
176 });
177}
178
179template <size_t ND, template <size_t> class MDEventType, typename EventIterator>
182 std::lock_guard<std::mutex> g(m_mutex);
183 m_tasks.emplace(tsk);
184}
185
186template <size_t ND, template <size_t> class MDEventType, typename EventIterator>
187std::unique_ptr<typename MDEventTreeBuilder<ND, MDEventType, EventIterator>::Task>
189 std::lock_guard<std::mutex> g(m_mutex);
190 if (m_tasks.empty())
191 return {nullptr};
192 else {
193 auto task = std::make_unique<Task>(m_tasks.front());
194 m_tasks.pop();
195 return task;
196 }
197}
198
199template <size_t ND, template <size_t> class MDEventType, typename EventIterator>
201 while (true) {
202 auto pTsk = popTask();
203 if (pTsk)
204 distributeEvents(*pTsk.get(), SLAVE);
205 else if (m_masterFinished)
206 break;
207 else
208 std::this_thread::sleep_for(std::chrono::nanoseconds(100));
209 }
210}
211
216template <size_t ND, template <size_t> class MDEventType, typename EventIterator>
218 const size_t childBoxCount = m_bc->getNumSplit();
219 const size_t splitThreshold = m_bc->getSplitThreshold();
220
221 if (tsk.maxDepth-- == 1 || std::distance(tsk.begin, tsk.end) <= static_cast<int>(splitThreshold)) {
222 return;
223 }
224
225 /* Determine the "width" of this box in Morton number */
226 const MortonT thisBoxWidth = tsk.upperBound - tsk.lowerBound;
227
228 /* Determine the "width" of the child boxes in Morton number */
229 const MortonT childBoxWidth = thisBoxWidth / childBoxCount;
230
231 auto eventIt = tsk.begin;
232
233 struct RecursionHelper {
234 std::pair<EventIterator, EventIterator> eventRange;
235 std::pair<MortonT, MortonT> mortonBounds;
236 BoxBase *box;
237 };
238 std::vector<RecursionHelper> children;
239 children.reserve(childBoxCount);
240
241 /* For each new child box */
242 for (size_t i = 0; i < childBoxCount; ++i) {
243 /* Lower child box bound is parent box lower bound plus for each previous
244 * child box; box width plus offset by one (such that lower bound of box
245 * i+1 is one grater than upper bound of box i) */
246 const auto boxLower = tsk.lowerBound + ((childBoxWidth + 1) * i);
247
248 /* Upper child box bound is lower plus child box width */
249 const auto boxUpper = childBoxWidth + boxLower;
250
251 const auto boxEventStart = eventIt;
252
253 if (eventIt < tsk.end) {
254 if (morton_index::morton_contains<MortonT>(boxLower, boxUpper, IndexCoordinateSwitcher::getIndex(*eventIt)))
255 eventIt = std::upper_bound(
256 boxEventStart, tsk.end, boxUpper,
257 [](const MortonT &m, const typename std::iterator_traits<EventIterator>::value_type &event) {
258 return m < IndexCoordinateSwitcher::getIndex(event);
259 });
260 }
261
262 /* Add new child box. */
263 /* As we are adding as we iterate over Morton numbers and parent events
264 * child boxes are inserted in the correct sorted order. */
265 std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> extents(ND);
266 auto minCoord = morton_index::indexToCoordinates<ND, IntT, MortonT>(boxLower, m_space);
267 auto maxCoord = morton_index::indexToCoordinates<ND, IntT, MortonT>(boxUpper, m_space);
268 for (unsigned ax = 0; ax < ND; ++ax) {
269 extents[ax].setExtents(minCoord[ax], maxCoord[ax]);
270 }
271
272 BoxBase *newBox;
273 if (std::distance(boxEventStart, eventIt) <= static_cast<int64_t>(splitThreshold) || tsk.maxDepth == 1) {
274 for (auto it = boxEventStart; it < eventIt; ++it)
275 IndexCoordinateSwitcher::convertToCoordinates(*it, m_space);
276 m_bc->incBoxesCounter(tsk.level);
277 newBox = new Box(m_bc.get(), tsk.level, extents, boxEventStart, eventIt);
278 } else {
279 m_bc->incGridBoxesCounter(tsk.level);
280 newBox = new GridBox(m_bc.get(), tsk.level, extents);
281 }
282
283 children.emplace_back(RecursionHelper{{boxEventStart, eventIt}, {boxLower, boxUpper}, newBox});
284 }
285 // sorting is needed due to fast finding the proper box for given coordinate,
286 // during drawing, for splitInto != 2 Z-curve gives wrong order
287 std::sort(children.begin(), children.end(), [](RecursionHelper &a, RecursionHelper &b) {
288 unsigned i = ND;
289 while (i-- > 0) {
290 const auto &ac = a.box->getExtents(i).getMin();
291 const auto &bc = b.box->getExtents(i).getMin();
292 if (ac < bc)
293 return true;
294 if (ac > bc)
295 return false;
296 }
297 return true;
298 });
299 std::vector<API::IMDNode *> boxes;
300 boxes.reserve(childBoxCount);
301 std::transform(std::cbegin(children), std::cend(children), std::back_inserter(boxes),
302 [](const auto &ch) { return ch.box; });
303 tsk.root->setChildren(boxes, 0, boxes.size());
304
305 ++tsk.level;
306 for (auto &ch : children) {
307 Task newTask{ch.box,
308 ch.eventRange.first,
309 ch.eventRange.second,
310 ch.mortonBounds.first,
311 ch.mortonBounds.second,
312 tsk.maxDepth,
313 tsk.level};
314 if (wtp == MASTER && (size_t)std::distance(newTask.begin, newTask.end) < m_eventsThreshold)
315 pushTask(std::move(newTask));
316 else
317 distributeEvents(newTask, wtp);
318 }
319}
320
321} // namespace MDAlgorithms
322} // namespace Mantid
#define PARALLEL_THREAD_NUMBER
Templated super-class of a multi-dimensional event "box".
Definition: MDBoxBase.h:50
Templated class for a multi-dimensional event "box".
Definition: MDBox.h:45
Templated class for a GRIDDED multi-dimensional event "box".
Definition: MDGridBox.h:42
A Task is a unit of work to be scheduled and run by a ThreadPool.
Definition: Task.h:29
Class to create the box structure of MDWorkspace.
TreeWithIndexError distribute(std::vector< MDEventType< ND > > &mdEvents)
const morton_index::MDSpaceBounds< ND > & m_space
BoxBase * doDistributeEvents(std::vector< MDEventType< ND > > &mdEvents)
void distributeEvents(Task &tsk, const WORKER_TYPE &wtp)
Does actual work on creating tasks in MASTER mode and executing tasks in SLAVE mode.
morton_index::MDCoordinate< ND > convertToIndex(std::vector< MDEventType< ND > > &mdEvents, const morton_index::MDSpaceBounds< ND > &space)
MDEventTreeBuilder(const int numWorkers, const size_t threshold, const API::BoxController_sptr &bc, const morton_index::MDSpaceBounds< ND > &space)
std::vector< Mantid::Geometry::MDDimensionExtents< coord_t > > m_extents
typename MDEvent::template AccessFor< EventDistributor > IndexCoordinateSwitcher
void sortEvents(std::vector< MDEventType< ND > > &mdEvents)
const API::BoxController_sptr & m_bc
std::shared_ptr< BoxController > BoxController_sptr
Shared ptr to BoxController.
Helper class which provides the Collimation Length for SANS instruments.
Eigen::Array< float, static_cast< int >(ND), 1 > MDCoordinate
Definition: Types.h:29
Eigen::Array< float, static_cast< int >(ND), 2 > MDSpaceBounds
Definition: Types.h:31
STL namespace.
Structure to mark the classes, which can switch the "physical" meaning of the union used in MDLeanEve...
Definition: MDLeanEvent.h:58
Structure to store the subtask of creating subtree from the range of events.
const MDEventType< ND >::MortonT lowerBound
const MDEventType< ND >::MortonT upperBound