Mantid
Loading...
Searching...
No Matches
MDGridBox.hxx
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 +
13#include "MantidKernel/Task.h"
17#include "MantidKernel/Timer.h"
18#include "MantidKernel/Utils.h"
20#include <boost/math/special_functions/round.hpp>
21#include <optional>
22#include <ostream>
23
24// These pragmas ignores the warning in the ctor where "d<nd-1" for nd=1.
25// This is okay (though would be better if it were for only that function
26#if (defined(__INTEL_COMPILER))
27#pragma warning disable 186
28#elif defined(__GNUC__)
29#pragma GCC diagnostic ignored "-Wtype-limits"
30#endif
31
32namespace Mantid {
33namespace DataObjects {
34
37
38//-----------------------------------------------------------------------------------------------
44TMDE(MDGridBox)::MDGridBox(API::BoxController *const bc, const uint32_t depth,
45 const std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> &extentsVector)
46 : MDBoxBase<MDE, nd>(bc, depth, UNDEF_SIZET, extentsVector), numBoxes(0), m_Children(), diagonalSquared(0.f),
47 nPoints(0) {
48 initGridBox();
49}
50
57TMDE(MDGridBox)::MDGridBox(std::shared_ptr<API::BoxController> &bc, const uint32_t depth,
58 const std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> &extentsVector)
59 : MDBoxBase<MDE, nd>(bc.get(), depth, UNDEF_SIZET, extentsVector), numBoxes(0), m_Children(), diagonalSquared(0.f),
60 nPoints(0) {
61 initGridBox();
62}
63
65template <typename MDE, size_t nd> size_t MDGridBox<MDE, nd>::initGridBox() {
66 if (!this->m_BoxController)
67 throw std::runtime_error("MDGridBox::ctor(): No BoxController specified in box.");
68
69 // How many is it split?
70 // If we are at the top level and we have a specific top level split, then set
71 // it.
72 std::optional<std::vector<size_t>> splitTopInto = this->m_BoxController->getSplitTopInto();
73 if (this->getDepth() == 0 && splitTopInto) {
74 for (size_t d = 0; d < nd; d++)
75 split[d] = splitTopInto.value()[d];
76 } else {
77 for (size_t d = 0; d < nd; d++)
78 split[d] = this->m_BoxController->getSplitInto(d);
79 }
80
81 // Compute sizes etc.
82 size_t tot = computeSizesFromSplit();
83 if (tot == 0)
84 throw std::runtime_error("MDGridBox::ctor(): Invalid splitting criterion (one was zero).");
85 return tot;
86}
87
88//-----------------------------------------------------------------------------------------------
92TMDE(MDGridBox)::MDGridBox(MDBox<MDE, nd> *box)
93 : MDBoxBase<MDE, nd>(*box, box->getBoxController()), split(), splitCumul(), m_SubBoxSize(), numBoxes(0),
94 m_Children(), diagonalSquared(0.f), nPoints(0) {
95 size_t totalSize = initGridBox();
96
97 double ChildVol(1);
98 for (size_t d = 0; d < nd; d++)
99 ChildVol *= m_SubBoxSize[d];
100
101 // Splitting an input MDBox requires creating a bunch of children
102 fillBoxShell(totalSize, coord_t(1. / ChildVol));
103
104 // Prepare to distribute the events that were in the box before, this will
105 // load missing events from HDD in file based ws if there are some.
106 const std::vector<MDE> &events = box->getConstEvents();
107 // just add event to the existing internal box
108 for (const auto &evnt : events)
109 addEvent(evnt);
110
111 // Copy the cached numbers from the incoming box. This is quick - don't need
112 // to refresh cache
113 this->nPoints = box->getNPoints();
114
115 // Clear the old box and delete it from disk buffer if one is used.
116 box->clear();
117}
120template <typename MDE, size_t nd>
121void MDGridBox<MDE, nd>::fillBoxShell(const size_t tot, const coord_t ChildInverseVolume) {
122 // Create the array of MDBox contents.
123 this->m_Children.clear();
124 this->m_Children.reserve(tot);
125 this->numBoxes = tot;
126
127 size_t indices[nd];
128 for (size_t d = 0; d < nd; d++)
129 indices[d] = 0;
130
131 // get inital free ID for the boxes, which would be created by this command
132 // Splitting an input MDBox requires creating a bunch of children
133 // But the IDs of these children MUST be sequential. Hence the critical block
134 // within claimIDRange,
135 // which would produce sequental ranges in multithreaded environment
136 size_t ID0 = this->m_BoxController->claimIDRange(tot);
137
138 for (size_t i = 0; i < tot; i++) {
139 // Create the box
140 // (Increase the depth of this box to one more than the parent (this))
141 auto splitBox = new MDBox<MDE, nd>(this->m_BoxController, this->m_depth + 1, UNDEF_SIZET, size_t(ID0 + i));
142 // This MDGridBox is the parent of the new child.
143 splitBox->setParent(this);
144
145 // Set the extents of this box.
146 for (size_t d = 0; d < nd; d++) {
147 double min = double(this->extents[d].getMin()) + double(indices[d]) * m_SubBoxSize[d];
148 double max = min + m_SubBoxSize[d];
149 splitBox->setExtents(d, min, max);
150 }
151 splitBox->setInverseVolume(ChildInverseVolume); // Set the cached inverse volume
152 m_Children.emplace_back(splitBox);
153
154 // Increment the indices, rolling back as needed
155 indices[0]++;
156 for (size_t d = 0; d < nd - 1; d++) // This is not run if nd=1; that's okay,
157 // you can ignore the warning
158 {
159 if (indices[d] >= split[d]) {
160 indices[d] = 0;
161 indices[d + 1]++;
162 }
163 }
164 } // for each box
165}
166
167//-----------------------------------------------------------------------------------------------
176TMDE(MDGridBox)::MDGridBox(const MDGridBox<MDE, nd> &other, Mantid::API::BoxController *const otherBC)
177 : MDBoxBase<MDE, nd>(other, otherBC), numBoxes(other.numBoxes), m_Children(),
178 diagonalSquared(other.diagonalSquared), nPoints(other.nPoints) {
179 for (size_t d = 0; d < nd; d++) {
180 split[d] = other.split[d];
181 splitCumul[d] = other.splitCumul[d];
182 m_SubBoxSize[d] = other.m_SubBoxSize[d];
183 }
184 // Copy all the boxes
185 m_Children.clear();
186 m_Children.reserve(numBoxes);
187 for (size_t i = 0; i < other.m_Children.size(); i++) {
188 API::IMDNode *otherBox = other.m_Children[i];
189 const MDBox<MDE, nd> *otherMDBox = dynamic_cast<const MDBox<MDE, nd> *>(otherBox);
190 const MDGridBox<MDE, nd> *otherMDGridBox = dynamic_cast<const MDGridBox<MDE, nd> *>(otherBox);
191 if (otherMDBox) {
192 auto newBox = new MDBox<MDE, nd>(*otherMDBox, otherBC);
193 newBox->setParent(this);
194 m_Children.emplace_back(newBox);
195 } else if (otherMDGridBox) {
196 auto newBox = new MDGridBox<MDE, nd>(*otherMDGridBox, otherBC);
197 newBox->setParent(this);
198 m_Children.emplace_back(newBox);
199 } else {
200 throw std::runtime_error("MDGridBox::copy_ctor(): an unexpected child box type was found.");
201 }
202 }
203}
204
205//-----------------------------------------------------------------------------------------------
213TMDE(void MDGridBox)::transformDimensions(std::vector<double> &scaling, std::vector<double> &offset) {
215 this->computeSizesFromSplit();
216}
217
218//-----------------------------------------------------------------------------------------------
222TMDE(size_t MDGridBox)::computeSizesFromSplit() {
223 // Do some computation based on how many splits per each dim.
224 size_t tot = 1;
225 double diagSum(0);
226 for (size_t d = 0; d < nd; d++) {
227 // Cumulative multiplier, for indexing
228 splitCumul[d] = tot;
229 tot *= split[d];
230 // Length of the side of a box in this dimension
231 m_SubBoxSize[d] = static_cast<double>(this->extents[d].getSize()) / static_cast<double>(split[d]);
232 // Accumulate the squared diagonal length.
233 diagSum += m_SubBoxSize[d] * m_SubBoxSize[d];
234 }
235 diagonalSquared = static_cast<coord_t>(diagSum);
236
237 return tot;
238}
239
240//-----------------------------------------------------------------------------------------------
243 // Delete all contained boxes (this should fire the MDGridBox destructors
244 // recursively).
245 auto it = m_Children.begin();
246 for (; it != m_Children.end(); ++it)
247 delete *it;
248 m_Children.clear();
249}
250
251//-----------------------------------------------------------------------------------------------
253TMDE(void MDGridBox)::clear() {
254 this->m_signal = 0.0;
255 this->m_errorSquared = 0.0;
256 auto it = m_Children.begin();
257 for (; it != m_Children.end(); ++it) {
258 (*it)->clear();
259 }
260}
261
262//-----------------------------------------------------------------------------------------------
264TMDE(size_t MDGridBox)::getNumDims() const { return nd; }
265
266//-----------------------------------------------------------------------------------------------
268TMDE(size_t MDGridBox)::getDataInMemorySize() const {
269 size_t nPoints(0);
270 for (size_t i = 0; i < numBoxes; i++)
271 nPoints += m_Children[i]->getDataInMemorySize();
272 return nPoints;
273}
274
275//-----------------------------------------------------------------------------------------------
279TMDE(size_t MDGridBox)::getNumMDBoxes() const {
280 size_t total = 0;
281 auto it = m_Children.begin();
282 for (; it != m_Children.end(); ++it) {
283 total += (*it)->getNumMDBoxes();
284 }
285 return total;
286}
287
288//-----------------------------------------------------------------------------------------------
291TMDE(size_t MDGridBox)::getNumChildren() const { return numBoxes; }
292
293//-----------------------------------------------------------------------------------------------
298TMDE(API::IMDNode *MDGridBox)::getChild(size_t index) { return m_Children[index]; }
299
300//-----------------------------------------------------------------------------------------------
309TMDE(void MDGridBox)::setChildren(const std::vector<API::IMDNode *> &otherBoxes, const size_t indexStart,
310 const size_t indexEnd) {
311 m_Children.clear();
312 m_Children.reserve(indexEnd - indexStart + 1);
313 auto it = otherBoxes.begin() + indexStart;
314 auto it_end = otherBoxes.begin() + indexEnd;
315 // Set the parent of each new child box.
316 for (; it != it_end; it++) {
317 m_Children.emplace_back(dynamic_cast<MDBoxBase<MDE, nd> *>(*it));
318 m_Children.back()->setParent(this);
319 }
320 numBoxes = m_Children.size();
321}
322
323//-----------------------------------------------------------------------------------------------
329TMDE(inline size_t MDGridBox)::getLinearIndex(size_t *indices) const {
330 size_t out_linear_index = 0;
331 for (size_t d = 0; d < nd; d++)
332 out_linear_index += (indices[d] * splitCumul[d]);
333 return out_linear_index;
334}
335
336//-----------------------------------------------------------------------------------------------
344TMDE(void MDGridBox)::refreshCache(Kernel::ThreadScheduler *ts) {
345 // Clear your total
346 nPoints = 0;
347 this->m_signal = 0;
348 this->m_errorSquared = 0;
349 this->m_totalWeight = 0;
350
351 if (!ts) {
352 //--------- Serial -----------
353 for (MDBoxBase<MDE, nd> *ibox : m_Children) {
354
355 // Refresh the cache (does nothing for MDBox)
356 ibox->refreshCache();
357
358 // Add up what's in there
359 nPoints += ibox->getNPoints();
360 this->m_signal += ibox->getSignal();
361 this->m_errorSquared += ibox->getErrorSquared();
362 this->m_totalWeight += ibox->getTotalWeight();
363 }
364 } else {
365 //---------- Parallel refresh --------------
366 throw std::runtime_error("Not implemented");
367 }
368}
369//-----------------------------------------------------------------------------------------------
376TMDE(void MDGridBox)::calculateGridCaches() {
377 // Clear your total
378 nPoints = 0;
379 this->m_signal = 0;
380 this->m_errorSquared = 0;
381 this->m_totalWeight = 0;
382
383 for (MDBoxBase<MDE, nd> *ibox : m_Children) {
384
385 // does nothing for MDBox
386 ibox->calculateGridCaches();
387
388 // Add up what's in there
389 nPoints += ibox->getNPoints();
390 this->m_signal += ibox->getSignal();
391 this->m_errorSquared += ibox->getErrorSquared();
392 this->m_totalWeight += ibox->getTotalWeight();
393 }
394}
395//-----------------------------------------------------------------------------------------------
398TMDE(std::vector<MDE> *MDGridBox)::getEventsCopy() {
399 auto out = new std::vector<MDE>();
400 // Make the copy
401 // out->insert(out->begin(), data.begin(), data.end());
402 return out;
403}
404
405//-----------------------------------------------------------------------------------------------
413TMDE(void MDGridBox)::getBoxes(std::vector<API::IMDNode *> &outBoxes, size_t maxDepth, bool leafOnly) {
414 // Add this box, unless we only want the leaves
415 if (!leafOnly)
416 outBoxes.emplace_back(this);
417
418 if (this->getDepth() < maxDepth) {
419 for (API::IMDNode *child : m_Children) {
420 // Recursively go deeper, if needed
421 child->getBoxes(outBoxes, maxDepth, leafOnly);
422 }
423 } else {
424 // Oh, we reached the max depth and want only leaves.
425 // ... so we consider this box to be a leaf too.
426 if (leafOnly)
427 outBoxes.emplace_back(this);
428 }
429}
430
431GNU_DIAG_OFF("maybe-uninitialized")
432//-----------------------------------------------------------------------------------------------
449TMDE(void MDGridBox)::getBoxes(std::vector<API::IMDNode *> &outBoxes, size_t maxDepth, bool leafOnly,
450 Mantid::Geometry::MDImplicitFunction *function) {
451 // Add this box, unless we only want the leaves
452 if (!leafOnly)
453 outBoxes.emplace_back(this);
454
455 if (this->getDepth() < maxDepth) {
456 // OK, let's look for children that are either touching or completely
457 // contained by the implicit function.
458
459 // The number of vertices in each dimension is the # split[d] + 1
460 size_t vertices_max[nd];
461 Kernel::Utils::NestedForLoop::SetUp(nd, vertices_max, 0);
462
463 // Total number of vertices for all the boxes
464 size_t numVertices = 1;
465 for (size_t d = 0; d < nd; ++d) {
466 vertices_max[d] = split[d] + 1;
467 numVertices *= vertices_max[d];
468 }
469
470 // The function is limited by this many planes
471 size_t numPlanes = function->getNumPlanes();
472
473 // This array will hold whether each vertex is contained by each plane.
474 auto vertexContained = new bool[numVertices * numPlanes];
475
476 // The index to the vertex in each dimension
477 size_t vertexIndex[nd];
478 Kernel::Utils::NestedForLoop::SetUp(nd, vertexIndex, 0);
479 // To get indexes in the array of vertexes
480 size_t vertexIndexMaker[nd];
481 Kernel::Utils::NestedForLoop::SetUpIndexMaker(nd, vertexIndexMaker, vertices_max);
482 // To get indexes in the array of BOXES
483 size_t boxIndexMaker[nd];
484 Kernel::Utils::NestedForLoop::SetUpIndexMaker(nd, boxIndexMaker, split);
485
486 size_t linearVertexIndex = 0;
487 for (linearVertexIndex = 0; linearVertexIndex < numVertices; linearVertexIndex++) {
488 // Get the nd-dimensional index
489 Kernel::Utils::NestedForLoop::GetIndicesFromLinearIndex(nd, linearVertexIndex, vertexIndexMaker, vertices_max,
490 vertexIndex);
491
492 // Coordinates of this vertex
493 coord_t vertexCoord[nd];
494 for (size_t d = 0; d < nd; ++d)
495 vertexCoord[d] = this->extents[d].getMin() + coord_t(double(vertexIndex[d]) * m_SubBoxSize[d]);
496
497 // Now check each plane to see if the vertex is bounded by it
498 for (size_t p = 0; p < numPlanes; p++) {
499 // Save whether this vertex is contained by this plane
500 vertexContained[p * numVertices + linearVertexIndex] = function->getPlane(p).isPointInside(vertexCoord);
501 }
502 }
503
504 // OK, now we have an array saying which vertex is contained by which plane.
505
506 // This is the number of vertices for each box, e.g. 8 in 3D
507 size_t verticesPerBox = 1 << nd;
508
509 /* There is a fixed relationship betwen a vertex (in a linear index) and its
510 * neighbors for a given box. This array calculates this: */
511 auto vertexNeighborsOffsets = new size_t[verticesPerBox];
512
513 for (size_t i = 0; i < verticesPerBox; i++) {
514 // Index (in n-dimensions) of this neighbor)
515 size_t vertIndex[nd];
516 for (size_t d = 0; d < nd; d++) {
517 vertIndex[d] = 0;
518 // Use a bit mask to iterate through the 2^nd neighbor options
519 size_t mask = size_t{1} << d;
520 if (i & mask)
521 vertIndex[d] = 1;
522 }
523 size_t linIndex = Kernel::Utils::NestedForLoop::GetLinearIndex(nd, vertIndex, vertexIndexMaker);
524 vertexNeighborsOffsets[i] = linIndex;
525 }
526
527 // Go through all the boxes
528 size_t boxIndex[nd];
529 Kernel::Utils::NestedForLoop::SetUp(nd, boxIndex, 0);
530
531 bool allDone = false;
532 while (!allDone) {
533 // Find the linear index into the BOXES array.
534 size_t boxLinearIndex = Kernel::Utils::NestedForLoop::GetLinearIndex(nd, boxIndex, boxIndexMaker);
535 API::IMDNode *box = m_Children[boxLinearIndex];
536
537 // std::cout << "Box at " << Strings::join(boxIndex, boxIndex+nd,
538 // ", ")
539 // << " (" << box->getExtentsStr() << ") ";
540
541 // Find the linear index of the upper left vertex of the box.
542 // (note that we're using the VERTEX index maker to find the linear index
543 // in that LARGER array)
544 size_t vertLinearIndex = Kernel::Utils::NestedForLoop::GetLinearIndex(nd, boxIndex, vertexIndexMaker);
545
546 // OK, now its time to see if the box is touching or contained or out of
547 // it.
548 // Recall that:
549 // - if a plane has NO vertices, then the box DOES NOT TOUCH
550 // - if EVERY plane has EVERY vertex, then the box is CONTAINED
551 // - if EVERY plane has at least one vertex, then the box is TOUCHING
552
553 size_t numPlanesWithAllVertexes = 0;
554
555 bool boxIsNotTouching = false;
556
557 // Go plane by plane
558 for (size_t p = 0; p < numPlanes; p++) {
559 size_t numVertexesInThisPlane = 0;
560 // Evaluate the 2^nd vertexes for this box.
561 for (size_t i = 0; i < verticesPerBox; i++) {
562 // (the index of the vertex is) = vertLinearIndex +
563 // vertexNeighborsOffsets[i]
564 if (vertexContained[p * numVertices + vertLinearIndex + vertexNeighborsOffsets[i]])
565 numVertexesInThisPlane++;
566 }
567
568 // Plane with no vertexes = NOT TOUCHING. You can exit now
569 if (numVertexesInThisPlane == 0) {
570 boxIsNotTouching = true;
571 break;
572 }
573
574 // Plane has all the vertexes
575 if (numVertexesInThisPlane == verticesPerBox)
576 numPlanesWithAllVertexes++;
577 } // (for each plane)
578
579 // Is there a chance that the box is contained?
580 if (!boxIsNotTouching) {
581
582 if (numPlanesWithAllVertexes == numPlanes) {
583 // All planes have all vertexes
584 // The box is FULLY CONTAINED
585 // So we can get ALL children and don't need to check the implicit
586 // function
587 box->getBoxes(outBoxes, maxDepth, leafOnly);
588 } else {
589 // There is a chance the box is touching. Keep checking with implicit
590 // functions
591 box->getBoxes(outBoxes, maxDepth, leafOnly, function);
592 }
593 } else {
594 // std::cout << " is not touching at all.\n";
595 }
596
597 // Move on to the next box in the list
598 allDone = Kernel::Utils::NestedForLoop::Increment(nd, boxIndex, split);
599 }
600
601 // Clean up.
602 delete[] vertexContained;
603 delete[] vertexNeighborsOffsets;
604
605 } // Not at max depth
606 else {
607 // Oh, we reached the max depth and want only leaves.
608 // ... so we consider this box to be a leaf too.
609 if (leafOnly)
610 outBoxes.emplace_back(this);
611 }
612}
613GNU_DIAG_ON("maybe-uninitialized")
614//-----------------------------------------------------------------------------------------------
621TMDE(void MDGridBox)::getBoxes(std::vector<API::IMDNode *> &outBoxes, const std::function<bool(API::IMDNode *)> &cond) {
622 if (cond(this))
623 outBoxes.emplace_back(this);
624 for (API::IMDNode *child : m_Children) {
625 child->getBoxes(outBoxes, cond);
626 }
627}
628//-----------------------------------------------------------------------------------------------
633template <typename MDE, size_t nd> const API::IMDNode *MDGridBox<MDE, nd>::getBoxAtCoord(const coord_t *coords) {
634 size_t index = 0;
635 for (size_t d = 0; d < nd; d++) {
636 coord_t x = coords[d];
637 int i = int((x - this->extents[d].getMin()) / m_SubBoxSize[d]);
638 // NOTE: No bounds checking is done (for performance).
639 // Accumulate the index
640 index += (i * splitCumul[d]);
641 }
642
643 // Add it to the contained box
644 if (index < numBoxes) // avoid segfaults for floating point round-off errors.
645 return m_Children[index]->getBoxAtCoord(coords);
646 else
647 return nullptr;
648}
649
650//-----------------------------------------------------------------------------------------------
661TMDE(void MDGridBox)::splitContents(size_t index, Kernel::ThreadScheduler *ts) {
662 // You can only split it if it is a MDBox (not MDGridBox).
663 MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(m_Children[index]);
664 if (!box)
665 return;
666 // Track how many MDBoxes there are in the overall workspace
667 this->m_BoxController->trackNumBoxes(box->getDepth());
668 // Construct the grid box. This should take the object out of the disk MRU
669 auto gridbox = new MDGridBox<MDE, nd>(box);
670
671 // Delete the old ungridded box
672 delete m_Children[index];
673 // And now we have a gridded box instead of a boring old regular box.
674 m_Children[index] = gridbox;
675
676 if (ts) {
677 // Create a task to split the newly created MDGridBox.
678 ts->push(std::make_shared<Kernel::FunctionTask>(std::bind(&MDGridBox<MDE, nd>::splitAllIfNeeded, &*gridbox, ts)));
679 } else {
680 gridbox->splitAllIfNeeded(nullptr);
681 }
682}
683
684//-----------------------------------------------------------------------------------------------
691TMDE(size_t MDGridBox)::getChildIndexFromID(size_t childId) const {
692 for (size_t index = 0; index < numBoxes; index++) {
693 if (m_Children[index]->getID() == childId)
694 return index;
695 }
696 return UNDEF_SIZET;
697}
698
699//-----------------------------------------------------------------------------------------------
706TMDE(void MDGridBox)::splitAllIfNeeded(Kernel::ThreadScheduler *ts) {
707 for (size_t i = 0; i < numBoxes; ++i) {
708 MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(m_Children[i]);
709 if (box) {
710 // Plain MD-Box. Does it need to be split?
711 if (this->m_BoxController->willSplit(box->getNPoints(), box->getDepth())) {
712 // The MDBox needs to split into a grid box.
713 if (!ts) {
714 // ------ Perform split serially (no ThreadPool) ------
715 auto gridBox = new MDGridBox<MDE, nd>(box);
716 // Track how many MDBoxes there are in the overall workspace
717 this->m_BoxController->trackNumBoxes(box->getDepth());
718 // Replace in the array
719 m_Children[i] = gridBox;
720 // Delete the old box
721 delete box;
722 // Now recursively check if this NEW grid box's contents should be
723 // split too
724 gridBox->splitAllIfNeeded(nullptr);
725 } else {
726 // ------ Perform split in parallel (using ThreadPool) ------
727 // So we create a task to split this MDBox,
728 // Task is : this->splitContents(i, ts);
729 ts->push(
730 std::make_shared<Kernel::FunctionTask>(std::bind(&MDGridBox<MDE, nd>::splitContents, &*this, i, ts)));
731 }
732 } else {
733 // This box does NOT have enough events to be worth splitting, if it do
734 // have at least something in memory then,
735 Kernel::ISaveable *const pSaver(box->getISaveable());
736 if (pSaver && box->getDataInMemorySize() > 0) {
737 // Mark the box as "to-write" in DiskBuffer. If the buffer is full,
738 // the boxes will be dropped on disk
739
740 this->m_BoxController->getFileIO()->toWrite(pSaver);
741 }
742 }
743 } else {
744 // It should be a MDGridBox
745 MDGridBox<MDE, nd> *gridBox = dynamic_cast<MDGridBox<MDE, nd> *>(m_Children[i]);
746 if (gridBox) {
747 // Now recursively check if this old grid box's contents should be split
748 // too
749 if (!ts || (this->nPoints < this->m_BoxController->getAddingEvents_eventsPerTask()))
750 // Go serially if there are only a few points contained (less
751 // overhead).
752 gridBox->splitAllIfNeeded(ts);
753 else
754 // Go parallel if this is a big enough gridbox.
755 // Task is : gridBox->splitAllIfNeeded(ts);
756 ts->push(
757 std::make_shared<Kernel::FunctionTask>(std::bind(&MDGridBox<MDE, nd>::splitAllIfNeeded, &*gridBox, ts)));
758 }
759 }
760 }
761}
762
763//-----------------------------------------------------------------------------------------------
771TMDE(void MDGridBox)::centerpointBin(MDBin<MDE, nd> &bin, bool *fullyContained) const {
772
773 // The MDBin ranges from index_min to index_max (inclusively) if each
774 // dimension. So
775 // we'll need to make nested loops from index_min[0] to index_max[0]; from
776 // index_min[1] to index_max[1]; etc.
777 int index_min[nd];
778 int index_max[nd];
779 // For running the nested loop, counters of each dimension. These are bounded
780 // by 0..split[d]
781 size_t counters_min[nd];
782 size_t counters_max[nd];
783
784 for (size_t d = 0; d < nd; d++) {
785 int min, max;
786
787 // The min index in this dimension (we round down - we'll include this edge)
788 if (bin.m_min[d] >= this->extents[d].getMin()) {
789 min = int((bin.m_min[d] - this->extents[d].getMin()) / m_SubBoxSize[d]);
790 counters_min[d] = min;
791 } else {
792 min = -1; // Goes past the edge
793 counters_min[d] = 0;
794 }
795
796 // If the minimum is bigger than the number of blocks in that dimension,
797 // then the bin is off completely in
798 // that dimension. There is nothing to integrate.
799 if (min >= static_cast<int>(split[d]))
800 return;
801 index_min[d] = min;
802
803 // The max index in this dimension (we round UP, but when we iterate we'll
804 // NOT include this edge)
805 if (bin.m_max[d] < this->extents[d].getMax()) {
806 max = int(ceil((bin.m_max[d] - this->extents[d].getMin()) / m_SubBoxSize[d])) - 1;
807 counters_max[d] = max + 1; // (the counter looping will NOT include counters_max[d])
808 } else {
809 max = int(split[d]); // Goes past THAT edge
810 counters_max[d] = max; // (the counter looping will NOT include max)
811 }
812
813 // If the max value is before the min, that means NOTHING is in the bin, and
814 // we can return
815 if ((max < min) || (max < 0))
816 return;
817 index_max[d] = max;
818
819 // std::cout << d << " from " << std::setw(5) << index_min[d] << " to " <<
820 // std::setw(5) << index_max[d] << "inc\n";
821 }
822
823 // If you reach here, than at least some of bin is overlapping this box
824 size_t counters[nd];
825 for (size_t d = 0; d < nd; d++)
826 counters[d] = counters_min[d];
827
828 bool allDone = false;
829 while (!allDone) {
830 size_t index = getLinearIndex(counters);
831 // std::cout << index << ": " << counters[0] << ", " << counters[1] <<
832 // '\n';
833
834 // Find if the box is COMPLETELY held in the bin.
835 bool completelyWithin = true;
836 for (size_t dim = 0; dim < nd; dim++)
837 if ((static_cast<int>(counters[dim]) <= index_min[dim]) || (static_cast<int>(counters[dim]) >= index_max[dim])) {
838 // The index we are at is at the edge of the integrated area (index_min
839 // or index_max-1)
840 // That means that the bin only PARTIALLY covers this MDBox
841 completelyWithin = false;
842 break;
843 }
844
845 if (completelyWithin) {
846 // Box is completely in the bin.
847 // std::cout << "Box at index " << counters[0] << ", " << counters[1] << "
848 // is entirely contained.\n";
849 // Use the aggregated signal and error
850 bin.m_signal += m_Children[index]->getSignal();
851 bin.m_errorSquared += m_Children[index]->getErrorSquared();
852 } else {
853 // Perform the binning
854 m_Children[index]->centerpointBin(bin, fullyContained);
855 }
856
857 // Increment the counter(s) in the nested for loops.
858 allDone = Kernel::Utils::NestedForLoop::Increment(nd, counters, counters_max, counters_min);
859 }
860}
861
862// TMDE(
863// void MDGridBox)::generalBin(MDBin<MDE,nd> & bin,
864// Mantid::API::ImplicitFunction & function) const
865// {
866// // The MDBin ranges from index_min to index_max (inclusively) if each
867// dimension. So
868// // we'll need to make nested loops from index_min[0] to index_max[0]; from
869// index_min[1] to index_max[1]; etc.
870// int index_min[nd];
871// int index_max[nd];
872// // For running the nested loop, counters of each dimension. These are
873// bounded by 0..split[d]
874// size_t counters_min[nd];
875// size_t counters_max[nd];
876//
877// for (size_t d=0; d<nd; d++)
878// {
879// int min,max;
880//
881// // The min index in this dimension (we round down - we'll include this
882// edge)
883// if (bin.m_min[d] >= this->extents[d].getMin())
884// {
885// min = int((bin.m_min[d] - this->extents[d].getMin()) / boxSize[d]);
886// counters_min[d] = min;
887// }
888// else
889// {
890// min = -1; // Goes past the edge
891// counters_min[d] = 0;
892// }
893//
894// // If the minimum is bigger than the number of blocks in that dimension,
895// then the bin is off completely in
896// // that dimension. There is nothing to integrate.
897// if (min >= static_cast<int>(split[d]))
898// return;
899// index_min[d] = min;
900//
901// // The max index in this dimension (we round UP, but when we iterate
902// we'll NOT include this edge)
903// if (bin.m_max[d] < this->extents[d].max)
904// {
905// max = int(ceil((bin.m_max[d] - this->extents[d].getMin()) /
906// boxSize[d])) - 1;
907// counters_max[d] = max+1; // (the counter looping will NOT include
908// counters_max[d])
909// }
910// else
911// {
912// max = int(split[d]); // Goes past THAT edge
913// counters_max[d] = max; // (the counter looping will NOT include max)
914// }
915//
916// // If the max value is before the min, that means NOTHING is in the bin,
917// and we can return
918// if ((max < min) || (max < 0))
919// return;
920// index_max[d] = max;
921//
922// //std::cout << d << " from " << std::setw(5) << index_min[d] << " to "
923// << std::setw(5) << index_max[d] << "inc\n";
924// }
925//
926// // If you reach here, than at least some of bin is overlapping this box
927//
928//
929// // We start by looking at the vertices at every corner of every box
930// contained,
931// // to see which boxes are partially contained/fully contained.
932//
933// // One entry with the # of vertices in this box contained; start at 0.
934// size_t * verticesContained = new size_t[numBoxes];
935// memset( verticesContained, 0, numBoxes * sizeof(size_t) );
936//
937// // Set to true if there is a possibility of the box at least partly
938// touching the integration volume.
939// bool * boxMightTouch = new bool[numBoxes];
940// memset( boxMightTouch, 0, numBoxes * sizeof(bool) );
941//
942// // How many vertices does one box have? 2^nd, or bitwise shift left 1 by
943// nd bits
944// size_t maxVertices = 1 << nd;
945//
946// // The index to the vertex in each dimension
947// size_t * vertexIndex = Utils::NestedForLoop::SetUp(nd, 0);
948//
949// // This is the index in each dimension at which we start looking at
950// vertices
951// size_t * vertices_min = Utils::NestedForLoop::SetUp(nd, 0);
952// for (size_t d=0; d<nd; ++d)
953// {
954// vertices_min[d] = counters_min[d];
955// vertexIndex[d] = vertices_min[d]; // This is where we start
956// }
957//
958// // There is one more vertex in each dimension than there are boxes we are
959// considering
960// size_t * vertices_max = Utils::NestedForLoop::SetUp(nd, 0);
961// for (size_t d=0; d<nd; ++d)
962// vertices_max[d] = counters_max[d]+1;
963//
964// size_t * boxIndex = Utils::NestedForLoop::SetUp(nd, 0);
965// size_t * indexMaker = Utils::NestedForLoop::SetUpIndexMaker(nd, split);
966//
967// bool allDone = false;
968// while (!allDone)
969// {
970// // Coordinates of this vertex
971// coord_t vertexCoord[nd];
972// bool masks[nd];
973// for (size_t d=0; d<nd; ++d)
974// {
975// vertexCoord[d] = double(vertexIndex[d]) * boxSize[d] +
976// this->extents[d].getMin();
977// masks[d] = false; //HACK ... assumes that all vertexes are used.
978// }
979// // Is this vertex contained?
980// if (function.evaluate(vertexCoord, masks, nd))
981// {
982// // Yes, this vertex is contained within the integration volume!
985//
986// // This vertex is shared by up to 2^nd adjacent boxes (left-right
987// along each dimension).
988// for (size_t neighb=0; neighb<maxVertices; ++neighb)
989// {
990// // The index of the box is the same as the vertex, but maybe - 1 in
991// each possible combination of dimensions
992// bool badIndex = false;
993// // Build the index of the neighbor
994// for (size_t d=0; d<nd;d++)
995// {
996// boxIndex[d] = vertexIndex[d] - ((neighb & (1 << d)) >> d); //(this
997// does a bitwise and mask, shifted back to 1 to subtract 1 to the
998// dimension)
999// // Taking advantage of the fact that unsigned(0)-1 = some large
1000// POSITIVE number.
1001// if (boxIndex[d] >= split[d])
1002// {
1003// badIndex = true;
1004// break;
1005// }
1006// }
1007// if (!badIndex)
1008// {
1009// // Convert to linear index
1010// size_t linearIndex = Utils::NestedForLoop::GetLinearIndex(nd,
1011// boxIndex, indexMaker);
1012// // So we have one more vertex touching this box that is contained
1013// in the integration volume. Whew!
1014// verticesContained[linearIndex]++;
1017// }
1018// }
1019// }
1020//
1021// // Increment the counter(s) in the nested for loops.
1022// allDone = Utils::NestedForLoop::Increment(nd, vertexIndex, vertices_max,
1023// vertices_min);
1024// }
1025//
1026// // OK, we've done all the vertices. Now we go through and check each box.
1027// size_t numFullyContained = 0;
1028// //size_t numPartiallyContained = 0;
1029//
1030// // We'll iterate only through the boxes with (bin)
1031// size_t counters[nd];
1032// for (size_t d=0; d<nd; d++)
1033// counters[d] = counters_min[d];
1034//
1035// allDone = false;
1036// while (!allDone)
1037// {
1038// size_t index = getLinearIndex(counters);
1039// MDBoxBase<MDE,nd> * box = boxes[index];
1040//
1041// // Is this box fully contained?
1042// if (verticesContained[index] >= maxVertices)
1043// {
1044// // Use the integrated sum of signal in the box
1045// bin.m_signal += box->getSignal();
1046// bin.m_errorSquared += box->getErrorSquared();
1047// numFullyContained++;
1048// }
1049// else
1050// {
1051// // The box MAY be contained. Need to evaluate every event
1052//
1053// // box->generalBin(bin,function);
1054// }
1055//
1056// // Increment the counter(s) in the nested for loops.
1057// allDone = Utils::NestedForLoop::Increment(nd, counters, counters_max,
1058// counters_min);
1059// }
1060//
1064//
1065// delete [] verticesContained;
1066// delete [] boxMightTouch;
1067// delete [] vertexIndex;
1068// delete [] vertices_max;
1069// delete [] boxIndex;
1070// delete [] indexMaker;
1071//
1072// }
1073//
1074//
1075
1076//-----------------------------------------------------------------------------------------------
1092TMDE(void MDGridBox)::integrateSphere(API::CoordTransform &radiusTransform, const coord_t radiusSquared,
1093 signal_t &signal, signal_t &errorSquared, const coord_t innerRadiusSquared,
1094 const bool useOnePercentBackgroundCorrection) const {
1095 // We start by looking at the vertices at every corner of every box contained,
1096 // to see which boxes are partially contained/fully contained.
1097
1098 // One entry with the # of vertices in this box contained; start at 0.
1099 std::vector<size_t> verticesContained(numBoxes, 0);
1100
1101 // Set to true if there is a possibility of the box at least partly touching
1102 // the integration volume.
1103 std::vector<bool> boxMightTouch(numBoxes, 0);
1104
1105 // How many vertices does one box have? 2^nd, or bitwise shift left 1 by nd
1106 // bits
1107 size_t maxVertices = 1 << nd;
1108
1109 // set up caches for box sizes and min box values
1110 coord_t boxSize[nd];
1111 coord_t minBoxVal[nd];
1112
1113 // The number of vertices in each dimension is the # split[d] + 1
1114 size_t vertices_max[nd];
1115 Kernel::Utils::NestedForLoop::SetUp(nd, vertices_max, 0);
1116 for (size_t d = 0; d < nd; ++d) {
1117 vertices_max[d] = split[d] + 1;
1118 // cache box sizes and min box valyes for performance
1119 boxSize[d] = static_cast<coord_t>(m_SubBoxSize[d]);
1120 minBoxVal[d] = static_cast<coord_t>(this->extents[d].getMin());
1121 }
1122
1123 // The index to the vertex in each dimension
1124 size_t vertexIndex[nd];
1125 Kernel::Utils::NestedForLoop::SetUp(nd, vertexIndex, 0);
1126 size_t boxIndex[nd];
1127 Kernel::Utils::NestedForLoop::SetUp(nd, boxIndex, 0);
1128 size_t indexMaker[nd];
1129 Kernel::Utils::NestedForLoop::SetUpIndexMaker(nd, indexMaker, split);
1130
1131 bool allDone = false;
1132 while (!allDone) {
1133 // Coordinates of this vertex
1134 coord_t vertexCoord[nd];
1135 for (size_t d = 0; d < nd; ++d)
1136 vertexCoord[d] = static_cast<coord_t>(vertexIndex[d]) * boxSize[d] + minBoxVal[d];
1137 // static_cast<coord_t>(vertexIndex[d]) * boxSize[d] + this->extents[d].min
1138
1139 // Is this vertex contained?
1140 coord_t out[nd];
1141 radiusTransform.apply(vertexCoord, out);
1142 if (out[0] < radiusSquared && out[0] > innerRadiusSquared) {
1143 // Yes, this vertex is contained within the integration volume!
1144 // std::cout << "vertex at " << vertexCoord[0] << ", " <<
1145 // vertexCoord[1] << ", " << vertexCoord[2] << " is contained\n";
1146
1147 // This vertex is shared by up to 2^nd adjacent boxes (left-right along
1148 // each dimension).
1149 for (size_t neighb = 0; neighb < maxVertices; ++neighb) {
1150 // The index of the box is the same as the vertex, but maybe - 1 in each
1151 // possible combination of dimensions
1152 bool badIndex = false;
1153 // Build the index of the neighbor
1154 for (size_t d = 0; d < nd; d++) {
1155 boxIndex[d] = vertexIndex[d] - ((neighb & ((size_t)1 << d)) >> d); //(this does a bitwise and mask,
1156 // shifted back to 1 to subtract 1
1157 // to the dimension)
1158 // Taking advantage of the fact that unsigned(0)-1 = some large
1159 // POSITIVE number.
1160 if (boxIndex[d] >= split[d]) {
1161 badIndex = true;
1162 break;
1163 }
1164 }
1165 if (!badIndex) {
1166 // Convert to linear index
1167 size_t linearIndex = Kernel::Utils::NestedForLoop::GetLinearIndex(nd, boxIndex, indexMaker);
1168 // So we have one more vertex touching this box that is contained in
1169 // the integration volume. Whew!
1170 verticesContained[linearIndex]++;
1171 // std::cout << "... added 1 vertex to box " <<
1172 // boxes[linearIndex]->getExtentsStr() << "\n";
1173 }
1174 }
1175 }
1176
1177 // Increment the counter(s) in the nested for loops.
1178 allDone = Kernel::Utils::NestedForLoop::Increment(nd, vertexIndex, vertices_max);
1179 }
1180
1181 // NOTE:
1182 // The following section is just trying to uncover the peak center so that
1183 // we can compute the distance bewteen peak center and box center correctly
1184 // without worrying about the skew coming from coordTransformDistnace.
1185 auto tmpRadiusTransform = dynamic_cast<CoordTransformDistance *>(&radiusTransform);
1186 if (tmpRadiusTransform == nullptr) {
1187 throw std::runtime_error("radiusTransform has to be CoordTransformDistance");
1188 }
1189 auto peakCenter = tmpRadiusTransform->getCenter();
1190 double peakRadius = std::sqrt(radiusSquared);
1191 double peakInnerRadius = std::sqrt(innerRadiusSquared);
1192
1193 // OK, we've done counting all the vertices.
1194 // Now let's go through and check each box.
1195 for (size_t bIndex = 0; bIndex < numBoxes; ++bIndex) {
1196 API::IMDNode *box = m_Children[bIndex];
1197
1198 // First, check if we have reached the base case where the box is completely
1199 // enveloped by the peak
1200 if (verticesContained[bIndex] >= maxVertices) {
1201 // Use the integrated sum of signal in the box
1202 signal += box->getSignal();
1203 errorSquared += box->getErrorSquared();
1204 continue; // move on to next
1205 }
1206
1207 // Second, check if there is at least one vertex in the integration volume,
1208 // and kick off recursive search until we reach the base case
1209 if (verticesContained[bIndex] > 0) {
1210 box->integrateSphere(radiusTransform, radiusSquared, signal, errorSquared, innerRadiusSquared,
1211 useOnePercentBackgroundCorrection);
1212 continue;
1213 }
1214
1215 // Last, no vertices in the integration volume
1216 // -- there are only two cases (see below) where we can
1217 // skip the box and its children, and it requires
1218 // the knowledge of how box and peak are spatially
1219 // positioned in Euclidian space.
1220 // NOTE:
1221 // For ellipsoid, we are using the long semi-axis, which
1222 // means we will inevitablly search some empty boxes, but
1223 // this is by design as we do not want to miss any box
1224 // that might contain events we need to collect.
1225 coord_t boxCenter[nd];
1226 box->getCenter(boxCenter);
1227 double distPeakCenterToBoxCenter = 0.0;
1228 for (size_t i = 0; i < nd; ++i) {
1229 distPeakCenterToBoxCenter += (boxCenter[i] - peakCenter[i]) * (boxCenter[i] - peakCenter[i]);
1230 }
1231 distPeakCenterToBoxCenter = std::sqrt(distPeakCenterToBoxCenter);
1232 double boxRadius = std::sqrt(diagonalSquared);
1233
1234 // - Box is completely isolated from peak
1235 // distPeakCenterToBoxCenter - peakRadius > boxRadius
1236 // -> stop search and move on
1237 if (distPeakCenterToBoxCenter - peakRadius > boxRadius) {
1238 // Debug output
1239 // std::ostringstream debugmsg;
1240 // debugmsg << "R_peak = " << peakRadius << "\n"
1241 // << "R_box = " << boxRadius << "\n"
1242 // << "Peak Center: ";
1243 // for (size_t i = 0; i < nd; ++i) {
1244 // debugmsg << peakCenter[i] << ",";
1245 // }
1246 // debugmsg << "\n"
1247 // << "Box center: ";
1248 // for (size_t i = 0; i < nd; ++i) {
1249 // debugmsg << boxCenter[i] << ",";
1250 // }
1251 // debugmsg << "\n"
1252 // << "distPeakCenterToBoxCenter = " << distPeakCenterToBoxCenter
1253 // << "\n";
1254 // std::cout << debugmsg.str();
1255 continue;
1256 }
1257 // - Tiny box falls into the donut hole
1258 // distPeakCenterToBoxCenter + boxRadius < peakInnerRadius
1259 // -> stop search and move on
1260 if (peakInnerRadius > 0 && distPeakCenterToBoxCenter + boxRadius < peakInnerRadius) {
1261 continue;
1262 }
1263 // - All other cases, we need to refine the box, i.e. recursively
1264 // search the child box
1265 box->integrateSphere(radiusTransform, radiusSquared, signal, errorSquared, innerRadiusSquared,
1266 useOnePercentBackgroundCorrection);
1267 } // (for each box)
1268}
1269
1270//-----------------------------------------------------------------------------------------------
1281TMDE(void MDGridBox)::centroidSphere(API::CoordTransform &radiusTransform, const coord_t radiusSquared,
1282 coord_t *centroid, signal_t &signal) const {
1283 for (size_t i = 0; i < numBoxes; ++i) {
1284 // Go through each contained box
1285 API::IMDNode *box = m_Children[i];
1286 coord_t boxCenter[nd];
1287 box->getCenter(boxCenter);
1288
1289 // Distance from center to the peak integration center
1290 coord_t out[nd];
1291 radiusTransform.apply(boxCenter, out);
1292
1293 if (out[0] < diagonalSquared * 0.72 + radiusSquared) {
1294 // If the center is closer than the size of the box, then it MIGHT be
1295 // touching.
1296 // (We multiply by 0.72 (about sqrt(2)) to look for half the diagonal).
1297 // NOTE! Watch out for non-spherical transforms!
1298
1299 // Go down one level to keep centroiding
1300 box->centroidSphere(radiusTransform, radiusSquared, centroid, signal);
1301 }
1302 } // (for each box)
1303}
1304//-----------------------------------------------------------------------------------------------
1305GNU_DIAG_OFF("array-bounds")
1321TMDE(void MDGridBox)::integrateCylinder(Mantid::API::CoordTransform &radiusTransform, const coord_t radius,
1322 const coord_t length, signal_t &signal, signal_t &errorSquared,
1323 std::vector<signal_t> &signal_fit) const {
1324 // We start by looking at the vertices at every corner of every box contained,
1325 // to see which boxes are partially contained/fully contained.
1326
1327 // One entry with the # of vertices in this box contained; start at 0.
1328 auto verticesContained = new size_t[numBoxes];
1329 memset(verticesContained, 0, numBoxes * sizeof(size_t));
1330
1331 // Set to true if there is a possibility of the box at least partly touching
1332 // the integration volume.
1333 auto boxMightTouch = new bool[numBoxes];
1334 memset(boxMightTouch, 0, numBoxes * sizeof(bool));
1335
1336 // How many vertices does one box have? 2^nd, or bitwise shift left 1 by nd
1337 // bits
1338 size_t maxVertices = 1 << nd;
1339
1340 // set up caches for box sizes and min box values
1341 coord_t boxSize[nd];
1342 coord_t minBoxVal[nd];
1343
1344 // The number of vertices in each dimension is the # split[d] + 1
1345 size_t vertices_max[nd];
1346 Kernel::Utils::NestedForLoop::SetUp(nd, vertices_max, 0);
1347 for (size_t d = 0; d < nd; ++d) {
1348 vertices_max[d] = split[d] + 1;
1349 // cache box sizes and min box valyes for performance
1350 boxSize[d] = static_cast<coord_t>(m_SubBoxSize[d]);
1351 minBoxVal[d] = static_cast<coord_t>(this->extents[d].getMin());
1352 }
1353
1354 // The index to the vertex in each dimension
1355 size_t vertexIndex[nd];
1356 Kernel::Utils::NestedForLoop::SetUp(nd, vertexIndex, 0);
1357 size_t boxIndex[nd];
1358 Kernel::Utils::NestedForLoop::SetUp(nd, boxIndex, 0);
1359 size_t indexMaker[nd];
1360 Kernel::Utils::NestedForLoop::SetUpIndexMaker(nd, indexMaker, split);
1361
1362 size_t numSteps = signal_fit.size();
1363 double deltaQ = length / static_cast<double>(numSteps - 1);
1364 bool allDone = false;
1365 while (!allDone) {
1366 // Coordinates of this vertex
1367 coord_t vertexCoord[nd];
1368 for (size_t d = 0; d < nd; ++d)
1369 vertexCoord[d] = static_cast<coord_t>(vertexIndex[d]) * boxSize[d] + minBoxVal[d];
1370 // static_cast<coord_t>(vertexIndex[d]) * boxSize[d] + this->extents[d].min
1371
1372 // Is this vertex contained?
1373 coord_t out[2]; // radius and length of cylinder
1374 radiusTransform.apply(vertexCoord, out);
1375 if (out[0] < radius && std::fabs(out[1]) < 0.5 * length) {
1376 // Yes, this vertex is contained within the integration volume!
1377 // std::cout << "vertex at " << vertexCoord[0] << ", " <<
1378 // vertexCoord[1] << ", " << vertexCoord[2] << " is contained\n";
1379
1380 // This vertex is shared by up to 2^nd adjacent boxes (left-right along
1381 // each dimension).
1382 for (size_t neighb = 0; neighb < maxVertices; ++neighb) {
1383 // The index of the box is the same as the vertex, but maybe - 1 in each
1384 // possible combination of dimensions
1385 bool badIndex = false;
1386 // Build the index of the neighbor
1387 for (size_t d = 0; d < nd; d++) {
1388 boxIndex[d] = vertexIndex[d] - ((neighb & ((size_t)1 << d)) >> d); //(this does a bitwise and mask,
1389 // shifted back to 1 to subtract 1
1390 // to the dimension)
1391 // Taking advantage of the fact that unsigned(0)-1 = some large
1392 // POSITIVE number.
1393 if (boxIndex[d] >= split[d]) {
1394 badIndex = true;
1395 break;
1396 }
1397 }
1398 if (!badIndex) {
1399 // Convert to linear index
1400 size_t linearIndex = Kernel::Utils::NestedForLoop::GetLinearIndex(nd, boxIndex, indexMaker);
1401 // So we have one more vertex touching this box that is contained in
1402 // the integration volume. Whew!
1403 verticesContained[linearIndex]++;
1404 // std::cout << "... added 1 vertex to box " <<
1405 // boxes[linearIndex]->getExtentsStr() << "\n";
1406 }
1407 }
1408 }
1409
1410 // Increment the counter(s) in the nested for loops.
1411 allDone = Kernel::Utils::NestedForLoop::Increment(nd, vertexIndex, vertices_max);
1412 }
1413
1414 // OK, we've done all the vertices. Now we go through and check each box.
1415 for (size_t i = 0; i < numBoxes; ++i) {
1416 API::IMDNode *box = m_Children[i];
1417 // Box partially contained?
1418 bool partialBox = false;
1419
1420 // Is this box fully contained?
1421 if (verticesContained[i] >= maxVertices) {
1422 std::vector<coord_t> coordTable;
1423 size_t nColumns;
1424 box->getEventsData(coordTable, nColumns);
1425 if (nColumns > 0 && nd > 1) {
1426 size_t nEvents = coordTable.size() / nColumns;
1427 size_t skipCol = 2; // lean events
1428 if (nColumns == 7)
1429 skipCol += 2; // events
1430 for (size_t k = 0; k < nEvents; k++) {
1431 coord_t eventCenter[nd];
1432 for (size_t l = 0; l < nd; l++)
1433 eventCenter[l] = coordTable[k * nColumns + skipCol + l];
1434 coord_t out[nd];
1435 radiusTransform.apply(eventCenter, out);
1436 // add event to appropriate y channel
1437 size_t xchannel = static_cast<size_t>(std::floor(out[1] / deltaQ)) + numSteps / 2;
1438
1439 if (xchannel < numSteps)
1440 signal_fit[xchannel] += coordTable[k * nColumns];
1441 }
1442 }
1443 // box->releaseEvents();
1444 // Use the integrated sum of signal in the box
1445 signal += box->getSignal();
1446 errorSquared += box->getErrorSquared();
1447
1448 // std::cout << "box at " << i << " (" << box->getExtentsStr() <<
1449 // ") is fully contained. Vertices = " << verticesContained[i] <<
1450 // "\n";
1451 // Go on to the next box
1452 continue;
1453 }
1454
1455 if (verticesContained[i] == 0) {
1456 // There is a chance that this part of the box is within integration
1457 // volume,
1458 // even if no vertex of it is.
1459 coord_t boxCenter[nd];
1460 box->getCenter(boxCenter);
1461
1462 // Distance from center to the peak integration center
1463 coord_t out[nd];
1464 radiusTransform.apply(boxCenter, out);
1465 if ((nd >= 1) && out[0] < std::sqrt(diagonalSquared * 0.72 + radius * radius) &&
1466 (nd >= 2 && std::fabs(out[1]) < std::sqrt(diagonalSquared * 0.72 + 0.25 * length * length))) {
1467 // If the center is closer than the size of the box, then it MIGHT be
1468 // touching.
1469 // (We multiply by 0.72 (about sqrt(2)) to look for half the diagonal).
1470 // NOTE! Watch out for non-spherical transforms!
1471 // std::cout << "box at " << i << " is maybe touching\n";
1472 partialBox = true;
1473 }
1474 } else {
1475 partialBox = true;
1476 // std::cout << "box at " << i << " has a vertex touching\n";
1477 }
1478
1479 // We couldn't rule out that the box might be partially contained.
1480 if (partialBox) {
1481 // Use the detailed integration method.
1482 box->integrateCylinder(radiusTransform, radius, length, signal, errorSquared, signal_fit);
1483 // std::cout << ".signal=" << signal << "\n";
1484 }
1485 } // (for each box)
1486
1487 // std::cout << "Depth " << this->getDepth() << " with " <<
1488 // numFullyContained << " fully contained; " << numPartiallyContained << "
1489 // partial. Signal = " << signal <<"\n";
1490
1491 delete[] verticesContained;
1492 delete[] boxMightTouch;
1493}
1494GNU_DIAG_ON("array-bounds")
1495
1496
1500TMDE(bool MDGridBox)::getIsMasked() const {
1501 bool isMasked = false;
1502 for (size_t i = 0; i < numBoxes; ++i) {
1503 // Go through each contained box
1504 API::IMDNode *box = m_Children[i];
1505 if (box->getIsMasked()) {
1506 isMasked = true;
1507 break;
1508 }
1509 }
1510 return isMasked;
1511}
1512
1514TMDE(void MDGridBox)::mask() {
1515 for (size_t i = 0; i < numBoxes; ++i) {
1516 // Go through each contained box
1517 API::IMDNode *box = m_Children[i];
1518 box->mask();
1519 }
1520}
1521
1523TMDE(void MDGridBox)::unmask() {
1524 for (size_t i = 0; i < numBoxes; ++i) {
1525 // Go through each contained box
1526 API::IMDNode *box = m_Children[i];
1527 box->unmask();
1528 }
1529}
1530//------------------------------------------------------------------------------------------------------------------------------------------------------------
1531//------------------------------------------------------------------------------------------------------------------------------------------------------------
1532//------------------------------------------------------------------------------------------------------------------------------------------------------------
1533/* Internal TMP class to simplify adding events to the box for events and lean
1534 * events using single interface. One would nead to overload the box class
1535 * otherwise*/
1536template <typename MDE, size_t nd> struct IF_EVENT {
1537public:
1538 // create generic events from array of events data and add them to the grid
1539 // box
1540 static inline void EXEC(MDGridBox<MDE, nd> *pBox, const std::vector<signal_t> &sigErrSq,
1541 const std::vector<coord_t> &Coord, const std::vector<uint16_t> &expInfoIndex,
1542 const std::vector<uint16_t> &goniometerIndex, const std::vector<uint32_t> &detectorId,
1543 size_t nEvents) {
1544 for (size_t i = 0; i < nEvents; i++)
1545 pBox->addEvent(MDEvent<nd>(sigErrSq[2 * i], sigErrSq[2 * i + 1], expInfoIndex[i], goniometerIndex[i],
1546 detectorId[i], &Coord[i * nd]));
1547 }
1548};
1549/* Specialize for the case of LeanEvent */
1550template <size_t nd> struct IF_EVENT<MDLeanEvent<nd>, nd> {
1551public:
1552 // create lean events from array of events data and add them to the grid box
1553 static inline void EXEC(MDGridBox<MDLeanEvent<nd>, nd> *pBox, const std::vector<signal_t> &sigErrSq,
1554 const std::vector<coord_t> &Coord, const std::vector<uint16_t> & /*expInfoIndex*/,
1555 const std::vector<uint16_t> & /*goniometerIndex*/,
1556 const std::vector<uint32_t> & /*detectorId*/, size_t nEvents) {
1557 for (size_t i = 0; i < nEvents; i++)
1558 pBox->addEvent(MDLeanEvent<nd>(sigErrSq[2 * i], sigErrSq[2 * i + 1], &Coord[i * nd]));
1559 }
1560};
1561
1575TMDE(size_t MDGridBox)::buildAndAddEvents(const std::vector<signal_t> &sigErrSq, const std::vector<coord_t> &Coord,
1576 const std::vector<uint16_t> &expInfoIndex,
1577 const std::vector<uint16_t> &goniometerIndex,
1578 const std::vector<uint32_t> &detectorId) {
1579
1580 size_t nEvents = sigErrSq.size() / 2;
1581 IF_EVENT<MDE, nd>::EXEC(this, sigErrSq, Coord, expInfoIndex, goniometerIndex, detectorId, nEvents);
1582
1583 return 0;
1584}
1585
1594TMDE(void MDGridBox)::buildAndAddEvent(const signal_t Signal, const signal_t errorSq, const std::vector<coord_t> &point,
1595 uint16_t expInfoIndex, uint16_t goniometerIndex, uint32_t detectorId) {
1596 this->addEvent(IF<MDE, nd>::BUILD_EVENT(Signal, errorSq, &point[0], expInfoIndex, goniometerIndex, detectorId));
1597}
1598
1599//-----------------------------------------------------------------------------------------------
1612TMDE(void MDGridBox)::buildAndAddEventUnsafe(const signal_t Signal, const signal_t errorSq,
1613 const std::vector<coord_t> &point, uint16_t expInfoIndex,
1614 uint16_t goniometerIndex, uint32_t detectorId) {
1615 this->addEventUnsafe(IF<MDE, nd>::BUILD_EVENT(Signal, errorSq, &point[0], expInfoIndex, goniometerIndex, detectorId));
1616}
1617
1618//-----------------------------------------------------------------------------------------------
1631TMDE(inline size_t MDGridBox)::addEvent(const MDE &event) {
1632 size_t cindex = calculateChildIndex(event);
1633
1634 // We can erroneously get cindex == numBoxes for events which fall on the
1635 // upper boundary of the last child box, so add these events to the last box
1636 if (cindex == numBoxes)
1637 cindex = numBoxes - 1;
1638
1639 if (cindex < numBoxes)
1640 return m_Children[cindex]->addEvent(event);
1641 else
1642 return 0;
1643}
1644
1645//-----------------------------------------------------------------------------------------------
1662TMDE(inline size_t MDGridBox)::addEventUnsafe(const MDE &event) {
1663 size_t cindex = calculateChildIndex(event);
1664
1665 // We can erroneously get cindex == numBoxes for events which fall on the
1666 // upper boundary of the last child box, so add these events to the last box
1667 if (cindex == numBoxes)
1668 cindex = numBoxes - 1;
1669
1670 if (cindex < numBoxes)
1671 return m_Children[cindex]->addEventUnsafe(event);
1672 else
1673 return 0;
1674}
1675
1682TMDE(inline void MDGridBox)::setChild(size_t index, MDGridBox<MDE, nd> *newChild) {
1683 // Delete the old box (supposetly ungridded);
1684 delete this->m_Children[index];
1685 // set new box, supposetly gridded
1686 this->m_Children[index] = newChild;
1687}
1690TMDE(void MDGridBox)::setFileBacked(const uint64_t /*fileLocation*/, const size_t /*fileSize*/,
1691 const bool /*markSaved*/) {
1692 throw(Kernel::Exception::NotImplementedError("Recursive file backed is not "
1693 "yet implemented (unclear how "
1694 "to set file location etc)"));
1695}
1698TMDE(void MDGridBox)::setFileBacked() {
1699 for (size_t i = 0; i < this->numBoxes; i++) {
1700 m_Children[i]->setFileBacked();
1701 }
1702}
1715TMDE(void MDGridBox)::clearFileBacked(bool loadDiskBackedData) {
1716 auto it = m_Children.begin();
1717 auto it_end = m_Children.end();
1718 for (; it != it_end; it++) {
1719 (*it)->clearFileBacked(loadDiskBackedData);
1720 }
1721}
1722
1726TMDE(size_t MDGridBox)::calculateChildIndex(const MDE &event) const {
1727 size_t cindex(0);
1728 for (size_t d = 0; d < nd; d++) {
1729 // Accumulate the index
1730 auto offset = event.getCenter(d) - this->extents[d].getMin();
1731 cindex += static_cast<int>(offset / (m_SubBoxSize[d])) * splitCumul[d];
1732 }
1733 return cindex;
1734}
1735} // namespace DataObjects
1736
1737} // namespace Mantid
std::map< DeltaEMode::Type, std::string > index
#define UNDEF_SIZET
Definition MDTypes.h:61
#define TMDE(decl)
Macro TMDE to make declaring template functions faster.
Definition MDTypes.h:52
#define GNU_DIAG_ON(x)
#define GNU_DIAG_OFF(x)
This is a collection of macros for turning compiler warnings off in a controlled manner.
This class is used by MDBox and MDGridBox in order to intelligently determine optimal behavior.
Unique SingleValueParameter Declaration for InputNDimensions.
virtual void mask()=0
Setter for masking the box.
virtual void integrateCylinder(Mantid::API::CoordTransform &radiusTransform, const coord_t radius, const coord_t length, signal_t &signal, signal_t &errorSquared, std::vector< signal_t > &signal_fit) const =0
Cylinder (peak) integration The CoordTransform object could be used for more cylinder reduces the dim...
virtual void getBoxes(std::vector< IMDNode * > &boxes, size_t maxDepth, bool leafOnly)=0
Fill a vector with all the boxes who are the childred of this one up to a certain depth.
virtual void centroidSphere(Mantid::API::CoordTransform &radiusTransform, const coord_t radiusSquared, coord_t *centroid, signal_t &signal) const =0
Find the centroid of all events contained within by doing a weighted average of their coordinates.
virtual bool getIsMasked() const =0
Getter for the masking.
virtual signal_t getErrorSquared() const =0
virtual void getEventsData(std::vector< coord_t > &coordTable, size_t &nColumns) const =0
The method to convert events in a box into a table of coodrinates/signal/errors casted into coord_t t...
virtual void getCenter(coord_t *const) const =0
virtual void unmask()=0
Setter for unmasking the box.
virtual signal_t getSignal() const =0
MDBin : Class describing a single bin in a dense, Multidimensional histogram.
Definition MDBin.h:32
Templated super-class of a multi-dimensional event "box".
Definition MDBoxBase.h:50
uint32_t getDepth() const override
For testing, mostly: return the recursion depth of this box.
Definition MDBoxBase.h:286
void transformDimensions(std::vector< double > &scaling, std::vector< double > &offset) override
Transform the dimensions contained in this box x' = x*scaling + offset.
Definition MDBoxBase.hxx:78
Templated class for a multi-dimensional event "box".
Definition MDBox.h:45
Kernel::ISaveable * getISaveable() override
Definition MDBox.hxx:160
void splitAllIfNeeded(Mantid::Kernel::ThreadScheduler *=nullptr) override
Definition MDBox.h:157
uint64_t getNPoints() const override
Returns the total number of points (events) in this box either they are all in memory,...
Definition MDBox.hxx:225
size_t getDataInMemorySize() const override
Definition MDBox.h:87
Templated class holding data about a neutron detection event in N-dimensions (for example,...
Definition MDEvent.h:36
Templated class for a GRIDDED multi-dimensional event "box".
Definition MDGridBox.h:42
void splitAllIfNeeded(Kernel::ThreadScheduler *ts=nullptr) override
Goes through all the sub-boxes and splits them if they contain enough events to be worth it.
size_t addEvent(const MDE &event) override
Add a single MDLeanEvent to the grid box.
void clear() override
Clear any points contained.
size_t initGridBox()
common part of MDGridBox contstructor;
Definition MDGridBox.hxx:65
void fillBoxShell(const size_t tot, const coord_t ChildInverseVolume)
Internal function to do main job of filling in a GridBox contents (part of the constructor)
Templated class holding data about a neutron detection event in N-dimensions (for example,...
Definition MDLeanEvent.h:60
Simple class that holds the extents (min/max) of a given dimension in a MD workspace or MDBox.
An "ImplicitFunction" defining a hyper-cuboid-shaped region in N dimensions.
Marks code as not implemented yet.
Definition Exception.h:138
An interface for objects that can be cached or saved to disk.
Definition ISaveable.h:28
The ThreadScheduler object defines how tasks are allocated to threads and in what order.
void split(const int A, int &S, int &V)
Split a number into the sign and positive value.
Definition Acomp.cpp:42
Helper class which provides the Collimation Length for SANS instruments.
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
Definition MDTypes.h:27
double signal_t
Typedef for the signal recorded in a MDBox, etc.
Definition MDTypes.h:36
STL namespace.
static void EXEC(MDGridBox< MDLeanEvent< nd >, nd > *pBox, const std::vector< signal_t > &sigErrSq, const std::vector< coord_t > &Coord, const std::vector< uint16_t > &, const std::vector< uint16_t > &, const std::vector< uint32_t > &, size_t nEvents)
static void EXEC(MDGridBox< MDE, nd > *pBox, const std::vector< signal_t > &sigErrSq, const std::vector< coord_t > &Coord, const std::vector< uint16_t > &expInfoIndex, const std::vector< uint16_t > &goniometerIndex, const std::vector< uint32_t > &detectorId, size_t nEvents)