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 m_SubBoxSizeInv[d] = other.m_SubBoxSizeInv[d];
184 }
185 // Copy all the boxes
186 m_Children.clear();
187 m_Children.reserve(numBoxes);
188 for (size_t i = 0; i < other.m_Children.size(); i++) {
189 API::IMDNode *otherBox = other.m_Children[i];
190 const MDBox<MDE, nd> *otherMDBox = dynamic_cast<const MDBox<MDE, nd> *>(otherBox);
191 const MDGridBox<MDE, nd> *otherMDGridBox = dynamic_cast<const MDGridBox<MDE, nd> *>(otherBox);
192 if (otherMDBox) {
193 auto newBox = new MDBox<MDE, nd>(*otherMDBox, otherBC);
194 newBox->setParent(this);
195 m_Children.emplace_back(newBox);
196 } else if (otherMDGridBox) {
197 auto newBox = new MDGridBox<MDE, nd>(*otherMDGridBox, otherBC);
198 newBox->setParent(this);
199 m_Children.emplace_back(newBox);
200 } else {
201 throw std::runtime_error("MDGridBox::copy_ctor(): an unexpected child box type was found.");
202 }
203 }
204}
205
206//-----------------------------------------------------------------------------------------------
214TMDE(void MDGridBox)::transformDimensions(std::vector<double> &scaling, std::vector<double> &offset) {
216 this->computeSizesFromSplit();
217}
218
219//-----------------------------------------------------------------------------------------------
223TMDE(size_t MDGridBox)::computeSizesFromSplit() {
224 // Do some computation based on how many splits per each dim.
225 size_t tot = 1;
226 double diagSum(0);
227 for (size_t d = 0; d < nd; d++) {
228 // Cumulative multiplier, for indexing
229 splitCumul[d] = tot;
230 tot *= split[d];
231 // Length of the side of a box in this dimension
232 m_SubBoxSize[d] = static_cast<double>(this->extents[d].getSize()) / static_cast<double>(split[d]);
233 m_SubBoxSizeInv[d] = 1.0 / m_SubBoxSize[d];
234 // Accumulate the squared diagonal length.
235 diagSum += m_SubBoxSize[d] * m_SubBoxSize[d];
236 }
237 diagonalSquared = static_cast<coord_t>(diagSum);
238
239 return tot;
240}
241
242//-----------------------------------------------------------------------------------------------
245 // Delete all contained boxes (this should fire the MDGridBox destructors
246 // recursively).
247 auto it = m_Children.begin();
248 for (; it != m_Children.end(); ++it)
249 delete *it;
250 m_Children.clear();
251}
252
253//-----------------------------------------------------------------------------------------------
255TMDE(void MDGridBox)::clear() {
256 this->m_signal = 0.0;
257 this->m_errorSquared = 0.0;
258 auto it = m_Children.begin();
259 for (; it != m_Children.end(); ++it) {
260 (*it)->clear();
261 }
262}
263
264//-----------------------------------------------------------------------------------------------
266TMDE(size_t MDGridBox)::getNumDims() const { return nd; }
267
268//-----------------------------------------------------------------------------------------------
270TMDE(size_t MDGridBox)::getDataInMemorySize() const {
271 size_t nPoints(0);
272 for (size_t i = 0; i < numBoxes; i++)
273 nPoints += m_Children[i]->getDataInMemorySize();
274 return nPoints;
275}
276
277//-----------------------------------------------------------------------------------------------
281TMDE(size_t MDGridBox)::getNumMDBoxes() const {
282 size_t total = 0;
283 auto it = m_Children.begin();
284 for (; it != m_Children.end(); ++it) {
285 total += (*it)->getNumMDBoxes();
286 }
287 return total;
288}
289
290//-----------------------------------------------------------------------------------------------
293TMDE(size_t MDGridBox)::getNumChildren() const { return numBoxes; }
294
295//-----------------------------------------------------------------------------------------------
300TMDE(API::IMDNode *MDGridBox)::getChild(size_t index) { return m_Children[index]; }
301
302//-----------------------------------------------------------------------------------------------
311TMDE(void MDGridBox)::setChildren(const std::vector<API::IMDNode *> &otherBoxes, const size_t indexStart,
312 const size_t indexEnd) {
313 m_Children.clear();
314 m_Children.reserve(indexEnd - indexStart + 1);
315 auto it = otherBoxes.begin() + indexStart;
316 auto it_end = otherBoxes.begin() + indexEnd;
317 // Set the parent of each new child box.
318 for (; it != it_end; it++) {
319 m_Children.emplace_back(dynamic_cast<MDBoxBase<MDE, nd> *>(*it));
320 m_Children.back()->setParent(this);
321 }
322 numBoxes = m_Children.size();
323}
324
325//-----------------------------------------------------------------------------------------------
331TMDE(inline size_t MDGridBox)::getLinearIndex(size_t *indices) const {
332 size_t out_linear_index = 0;
333 for (size_t d = 0; d < nd; d++)
334 out_linear_index += (indices[d] * splitCumul[d]);
335 return out_linear_index;
336}
337
338//-----------------------------------------------------------------------------------------------
346TMDE(void MDGridBox)::refreshCache(Kernel::ThreadScheduler *ts) {
347 // Clear your total
348 nPoints = 0;
349 this->m_signal = 0;
350 this->m_errorSquared = 0;
351 this->m_totalWeight = 0;
352
353 if (!ts) {
354 //--------- Serial -----------
355 for (MDBoxBase<MDE, nd> *ibox : m_Children) {
356
357 // Refresh the cache (does nothing for MDBox)
358 ibox->refreshCache();
359
360 // Add up what's in there
361 nPoints += ibox->getNPoints();
362 this->m_signal += ibox->getSignal();
363 this->m_errorSquared += ibox->getErrorSquared();
364 this->m_totalWeight += ibox->getTotalWeight();
365 }
366 } else {
367 //---------- Parallel refresh --------------
368 throw std::runtime_error("Not implemented");
369 }
370}
371//-----------------------------------------------------------------------------------------------
378TMDE(void MDGridBox)::calculateGridCaches() {
379 // Clear your total
380 nPoints = 0;
381 this->m_signal = 0;
382 this->m_errorSquared = 0;
383 this->m_totalWeight = 0;
384
385 for (MDBoxBase<MDE, nd> *ibox : m_Children) {
386
387 // does nothing for MDBox
388 ibox->calculateGridCaches();
389
390 // Add up what's in there
391 nPoints += ibox->getNPoints();
392 this->m_signal += ibox->getSignal();
393 this->m_errorSquared += ibox->getErrorSquared();
394 this->m_totalWeight += ibox->getTotalWeight();
395 }
396}
397//-----------------------------------------------------------------------------------------------
400TMDE(std::vector<MDE> *MDGridBox)::getEventsCopy() {
401 auto out = new std::vector<MDE>();
402 // Make the copy
403 // out->insert(out->begin(), data.begin(), data.end());
404 return out;
405}
406
407//-----------------------------------------------------------------------------------------------
415TMDE(void MDGridBox)::getBoxes(std::vector<API::IMDNode *> &outBoxes, size_t maxDepth, bool leafOnly) {
416 // Add this box, unless we only want the leaves
417 if (!leafOnly)
418 outBoxes.emplace_back(this);
419
420 if (this->getDepth() < maxDepth) {
421 for (API::IMDNode *child : m_Children) {
422 // Recursively go deeper, if needed
423 child->getBoxes(outBoxes, maxDepth, leafOnly);
424 }
425 } else {
426 // Oh, we reached the max depth and want only leaves.
427 // ... so we consider this box to be a leaf too.
428 if (leafOnly)
429 outBoxes.emplace_back(this);
430 }
431}
432
433GNU_DIAG_OFF("maybe-uninitialized")
434//-----------------------------------------------------------------------------------------------
451TMDE(void MDGridBox)::getBoxes(std::vector<API::IMDNode *> &outBoxes, size_t maxDepth, bool leafOnly,
452 Mantid::Geometry::MDImplicitFunction *function) {
453 // Add this box, unless we only want the leaves
454 if (!leafOnly)
455 outBoxes.emplace_back(this);
456
457 if (this->getDepth() < maxDepth) {
458 // OK, let's look for children that are either touching or completely
459 // contained by the implicit function.
460
461 // The number of vertices in each dimension is the # split[d] + 1
462 size_t vertices_max[nd];
463 Kernel::Utils::NestedForLoop::SetUp(nd, vertices_max, 0);
464
465 // Total number of vertices for all the boxes
466 size_t numVertices = 1;
467 for (size_t d = 0; d < nd; ++d) {
468 vertices_max[d] = split[d] + 1;
469 numVertices *= vertices_max[d];
470 }
471
472 // The function is limited by this many planes
473 size_t numPlanes = function->getNumPlanes();
474
475 // This array will hold whether each vertex is contained by each plane.
476 auto vertexContained = new bool[numVertices * numPlanes];
477
478 // The index to the vertex in each dimension
479 size_t vertexIndex[nd];
480 Kernel::Utils::NestedForLoop::SetUp(nd, vertexIndex, 0);
481 // To get indexes in the array of vertexes
482 size_t vertexIndexMaker[nd];
483 Kernel::Utils::NestedForLoop::SetUpIndexMaker(nd, vertexIndexMaker, vertices_max);
484 // To get indexes in the array of BOXES
485 size_t boxIndexMaker[nd];
486 Kernel::Utils::NestedForLoop::SetUpIndexMaker(nd, boxIndexMaker, split);
487
488 size_t linearVertexIndex = 0;
489 for (linearVertexIndex = 0; linearVertexIndex < numVertices; linearVertexIndex++) {
490 // Get the nd-dimensional index
491 Kernel::Utils::NestedForLoop::GetIndicesFromLinearIndex(nd, linearVertexIndex, vertexIndexMaker, vertices_max,
492 vertexIndex);
493
494 // Coordinates of this vertex
495 coord_t vertexCoord[nd];
496 for (size_t d = 0; d < nd; ++d)
497 vertexCoord[d] = this->extents[d].getMin() + coord_t(double(vertexIndex[d]) * m_SubBoxSize[d]);
498
499 // Now check each plane to see if the vertex is bounded by it
500 for (size_t p = 0; p < numPlanes; p++) {
501 // Save whether this vertex is contained by this plane
502 vertexContained[p * numVertices + linearVertexIndex] = function->getPlane(p).isPointInside(vertexCoord);
503 }
504 }
505
506 // OK, now we have an array saying which vertex is contained by which plane.
507
508 // This is the number of vertices for each box, e.g. 8 in 3D
509 size_t verticesPerBox = 1 << nd;
510
511 /* There is a fixed relationship betwen a vertex (in a linear index) and its
512 * neighbors for a given box. This array calculates this: */
513 auto vertexNeighborsOffsets = new size_t[verticesPerBox];
514
515 for (size_t i = 0; i < verticesPerBox; i++) {
516 // Index (in n-dimensions) of this neighbor)
517 size_t vertIndex[nd];
518 for (size_t d = 0; d < nd; d++) {
519 vertIndex[d] = 0;
520 // Use a bit mask to iterate through the 2^nd neighbor options
521 size_t mask = size_t{1} << d;
522 if (i & mask)
523 vertIndex[d] = 1;
524 }
525 size_t linIndex = Kernel::Utils::NestedForLoop::GetLinearIndex(nd, vertIndex, vertexIndexMaker);
526 vertexNeighborsOffsets[i] = linIndex;
527 }
528
529 // Go through all the boxes
530 size_t boxIndex[nd];
531 Kernel::Utils::NestedForLoop::SetUp(nd, boxIndex, 0);
532
533 bool allDone = false;
534 while (!allDone) {
535 // Find the linear index into the BOXES array.
536 size_t boxLinearIndex = Kernel::Utils::NestedForLoop::GetLinearIndex(nd, boxIndex, boxIndexMaker);
537 API::IMDNode *box = m_Children[boxLinearIndex];
538
539 // std::cout << "Box at " << Strings::join(boxIndex, boxIndex+nd,
540 // ", ")
541 // << " (" << box->getExtentsStr() << ") ";
542
543 // Find the linear index of the upper left vertex of the box.
544 // (note that we're using the VERTEX index maker to find the linear index
545 // in that LARGER array)
546 size_t vertLinearIndex = Kernel::Utils::NestedForLoop::GetLinearIndex(nd, boxIndex, vertexIndexMaker);
547
548 // OK, now its time to see if the box is touching or contained or out of
549 // it.
550 // Recall that:
551 // - if a plane has NO vertices, then the box DOES NOT TOUCH
552 // - if EVERY plane has EVERY vertex, then the box is CONTAINED
553 // - if EVERY plane has at least one vertex, then the box is TOUCHING
554
555 size_t numPlanesWithAllVertexes = 0;
556
557 bool boxIsNotTouching = false;
558
559 // Go plane by plane
560 for (size_t p = 0; p < numPlanes; p++) {
561 size_t numVertexesInThisPlane = 0;
562 // Evaluate the 2^nd vertexes for this box.
563 for (size_t i = 0; i < verticesPerBox; i++) {
564 // (the index of the vertex is) = vertLinearIndex +
565 // vertexNeighborsOffsets[i]
566 if (vertexContained[p * numVertices + vertLinearIndex + vertexNeighborsOffsets[i]])
567 numVertexesInThisPlane++;
568 }
569
570 // Plane with no vertexes = NOT TOUCHING. You can exit now
571 if (numVertexesInThisPlane == 0) {
572 boxIsNotTouching = true;
573 break;
574 }
575
576 // Plane has all the vertexes
577 if (numVertexesInThisPlane == verticesPerBox)
578 numPlanesWithAllVertexes++;
579 } // (for each plane)
580
581 // Is there a chance that the box is contained?
582 if (!boxIsNotTouching) {
583
584 if (numPlanesWithAllVertexes == numPlanes) {
585 // All planes have all vertexes
586 // The box is FULLY CONTAINED
587 // So we can get ALL children and don't need to check the implicit
588 // function
589 box->getBoxes(outBoxes, maxDepth, leafOnly);
590 } else {
591 // There is a chance the box is touching. Keep checking with implicit
592 // functions
593 box->getBoxes(outBoxes, maxDepth, leafOnly, function);
594 }
595 } else {
596 // std::cout << " is not touching at all.\n";
597 }
598
599 // Move on to the next box in the list
600 allDone = Kernel::Utils::NestedForLoop::Increment(nd, boxIndex, split);
601 }
602
603 // Clean up.
604 delete[] vertexContained;
605 delete[] vertexNeighborsOffsets;
606
607 } // Not at max depth
608 else {
609 // Oh, we reached the max depth and want only leaves.
610 // ... so we consider this box to be a leaf too.
611 if (leafOnly)
612 outBoxes.emplace_back(this);
613 }
614}
615GNU_DIAG_ON("maybe-uninitialized")
616//-----------------------------------------------------------------------------------------------
623TMDE(void MDGridBox)::getBoxes(std::vector<API::IMDNode *> &outBoxes, const std::function<bool(API::IMDNode *)> &cond) {
624 if (cond(this))
625 outBoxes.emplace_back(this);
626 for (API::IMDNode *child : m_Children) {
627 child->getBoxes(outBoxes, cond);
628 }
629}
630//-----------------------------------------------------------------------------------------------
635template <typename MDE, size_t nd> const API::IMDNode *MDGridBox<MDE, nd>::getBoxAtCoord(const coord_t *coords) {
636 size_t index = 0;
637 for (size_t d = 0; d < nd; d++) {
638 coord_t x = coords[d];
639 int i = int((x - this->extents[d].getMin()) / m_SubBoxSize[d]);
640 // NOTE: No bounds checking is done (for performance).
641 // Accumulate the index
642 index += (i * splitCumul[d]);
643 }
644
645 // Add it to the contained box
646 if (index < numBoxes) // avoid segfaults for floating point round-off errors.
647 return m_Children[index]->getBoxAtCoord(coords);
648 else
649 return nullptr;
650}
651
652//-----------------------------------------------------------------------------------------------
663TMDE(void MDGridBox)::splitContents(size_t index, Kernel::ThreadScheduler *ts) {
664 // You can only split it if it is a MDBox (not MDGridBox).
665 MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(m_Children[index]);
666 if (!box)
667 return;
668 // Track how many MDBoxes there are in the overall workspace
669 this->m_BoxController->trackNumBoxes(box->getDepth());
670 // Construct the grid box. This should take the object out of the disk MRU
671 auto gridbox = new MDGridBox<MDE, nd>(box);
672
673 // Delete the old ungridded box
674 delete m_Children[index];
675 // And now we have a gridded box instead of a boring old regular box.
676 m_Children[index] = gridbox;
677
678 if (ts) {
679 // Create a task to split the newly created MDGridBox.
680 ts->push(std::make_shared<Kernel::FunctionTask>(std::bind(&MDGridBox<MDE, nd>::splitAllIfNeeded, &*gridbox, ts)));
681 } else {
682 gridbox->splitAllIfNeeded(nullptr);
683 }
684}
685
686//-----------------------------------------------------------------------------------------------
693TMDE(size_t MDGridBox)::getChildIndexFromID(size_t childId) const {
694 for (size_t index = 0; index < numBoxes; index++) {
695 if (m_Children[index]->getID() == childId)
696 return index;
697 }
698 return UNDEF_SIZET;
699}
700
701//-----------------------------------------------------------------------------------------------
708TMDE(void MDGridBox)::splitAllIfNeeded(Kernel::ThreadScheduler *ts) {
709 for (size_t i = 0; i < numBoxes; ++i) {
710 MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(m_Children[i]);
711 if (box) {
712 // Plain MD-Box. Does it need to be split?
713 if (this->m_BoxController->willSplit(box->getNPoints(), box->getDepth())) {
714 // The MDBox needs to split into a grid box.
715 if (!ts) {
716 // ------ Perform split serially (no ThreadPool) ------
717 auto gridBox = new MDGridBox<MDE, nd>(box);
718 // Track how many MDBoxes there are in the overall workspace
719 this->m_BoxController->trackNumBoxes(box->getDepth());
720 // Replace in the array
721 m_Children[i] = gridBox;
722 // Delete the old box
723 delete box;
724 // Now recursively check if this NEW grid box's contents should be
725 // split too
726 gridBox->splitAllIfNeeded(nullptr);
727 } else {
728 // ------ Perform split in parallel (using ThreadPool) ------
729 // So we create a task to split this MDBox,
730 // Task is : this->splitContents(i, ts);
731 ts->push(
732 std::make_shared<Kernel::FunctionTask>(std::bind(&MDGridBox<MDE, nd>::splitContents, &*this, i, ts)));
733 }
734 } else {
735 // This box does NOT have enough events to be worth splitting, if it do
736 // have at least something in memory then,
737 Kernel::ISaveable *const pSaver(box->getISaveable());
738 if (pSaver && box->getDataInMemorySize() > 0) {
739 // Mark the box as "to-write" in DiskBuffer. If the buffer is full,
740 // the boxes will be dropped on disk
741
742 this->m_BoxController->getFileIO()->toWrite(pSaver);
743 }
744 }
745 } else {
746 // It should be a MDGridBox
747 MDGridBox<MDE, nd> *gridBox = dynamic_cast<MDGridBox<MDE, nd> *>(m_Children[i]);
748 if (gridBox) {
749 // Now recursively check if this old grid box's contents should be split
750 // too
751 if (!ts || (this->nPoints < this->m_BoxController->getAddingEvents_eventsPerTask()))
752 // Go serially if there are only a few points contained (less
753 // overhead).
754 gridBox->splitAllIfNeeded(ts);
755 else
756 // Go parallel if this is a big enough gridbox.
757 // Task is : gridBox->splitAllIfNeeded(ts);
758 ts->push(
759 std::make_shared<Kernel::FunctionTask>(std::bind(&MDGridBox<MDE, nd>::splitAllIfNeeded, &*gridBox, ts)));
760 }
761 }
762 }
763}
764
765//-----------------------------------------------------------------------------------------------
773TMDE(void MDGridBox)::centerpointBin(MDBin<MDE, nd> &bin, bool *fullyContained) const {
774
775 // The MDBin ranges from index_min to index_max (inclusively) if each
776 // dimension. So
777 // we'll need to make nested loops from index_min[0] to index_max[0]; from
778 // index_min[1] to index_max[1]; etc.
779 int index_min[nd];
780 int index_max[nd];
781 // For running the nested loop, counters of each dimension. These are bounded
782 // by 0..split[d]
783 size_t counters_min[nd];
784 size_t counters_max[nd];
785
786 for (size_t d = 0; d < nd; d++) {
787 int min, max;
788
789 // The min index in this dimension (we round down - we'll include this edge)
790 if (bin.m_min[d] >= this->extents[d].getMin()) {
791 min = int((bin.m_min[d] - this->extents[d].getMin()) / m_SubBoxSize[d]);
792 counters_min[d] = min;
793 } else {
794 min = -1; // Goes past the edge
795 counters_min[d] = 0;
796 }
797
798 // If the minimum is bigger than the number of blocks in that dimension,
799 // then the bin is off completely in
800 // that dimension. There is nothing to integrate.
801 if (min >= static_cast<int>(split[d]))
802 return;
803 index_min[d] = min;
804
805 // The max index in this dimension (we round UP, but when we iterate we'll
806 // NOT include this edge)
807 if (bin.m_max[d] < this->extents[d].getMax()) {
808 max = int(ceil((bin.m_max[d] - this->extents[d].getMin()) / m_SubBoxSize[d])) - 1;
809 counters_max[d] = max + 1; // (the counter looping will NOT include counters_max[d])
810 } else {
811 max = int(split[d]); // Goes past THAT edge
812 counters_max[d] = max; // (the counter looping will NOT include max)
813 }
814
815 // If the max value is before the min, that means NOTHING is in the bin, and
816 // we can return
817 if ((max < min) || (max < 0))
818 return;
819 index_max[d] = max;
820
821 // std::cout << d << " from " << std::setw(5) << index_min[d] << " to " <<
822 // std::setw(5) << index_max[d] << "inc\n";
823 }
824
825 // If you reach here, than at least some of bin is overlapping this box
826 size_t counters[nd];
827 for (size_t d = 0; d < nd; d++)
828 counters[d] = counters_min[d];
829
830 bool allDone = false;
831 while (!allDone) {
832 size_t index = getLinearIndex(counters);
833 // std::cout << index << ": " << counters[0] << ", " << counters[1] <<
834 // '\n';
835
836 // Find if the box is COMPLETELY held in the bin.
837 bool completelyWithin = true;
838 for (size_t dim = 0; dim < nd; dim++)
839 if ((static_cast<int>(counters[dim]) <= index_min[dim]) || (static_cast<int>(counters[dim]) >= index_max[dim])) {
840 // The index we are at is at the edge of the integrated area (index_min
841 // or index_max-1)
842 // That means that the bin only PARTIALLY covers this MDBox
843 completelyWithin = false;
844 break;
845 }
846
847 if (completelyWithin) {
848 // Box is completely in the bin.
849 // std::cout << "Box at index " << counters[0] << ", " << counters[1] << "
850 // is entirely contained.\n";
851 // Use the aggregated signal and error
852 bin.m_signal += m_Children[index]->getSignal();
853 bin.m_errorSquared += m_Children[index]->getErrorSquared();
854 } else {
855 // Perform the binning
856 m_Children[index]->centerpointBin(bin, fullyContained);
857 }
858
859 // Increment the counter(s) in the nested for loops.
860 allDone = Kernel::Utils::NestedForLoop::Increment(nd, counters, counters_max, counters_min);
861 }
862}
863
864// TMDE(
865// void MDGridBox)::generalBin(MDBin<MDE,nd> & bin,
866// Mantid::API::ImplicitFunction & function) const
867// {
868// // The MDBin ranges from index_min to index_max (inclusively) if each
869// dimension. So
870// // we'll need to make nested loops from index_min[0] to index_max[0]; from
871// index_min[1] to index_max[1]; etc.
872// int index_min[nd];
873// int index_max[nd];
874// // For running the nested loop, counters of each dimension. These are
875// bounded by 0..split[d]
876// size_t counters_min[nd];
877// size_t counters_max[nd];
878//
879// for (size_t d=0; d<nd; d++)
880// {
881// int min,max;
882//
883// // The min index in this dimension (we round down - we'll include this
884// edge)
885// if (bin.m_min[d] >= this->extents[d].getMin())
886// {
887// min = int((bin.m_min[d] - this->extents[d].getMin()) / boxSize[d]);
888// counters_min[d] = min;
889// }
890// else
891// {
892// min = -1; // Goes past the edge
893// counters_min[d] = 0;
894// }
895//
896// // If the minimum is bigger than the number of blocks in that dimension,
897// then the bin is off completely in
898// // that dimension. There is nothing to integrate.
899// if (min >= static_cast<int>(split[d]))
900// return;
901// index_min[d] = min;
902//
903// // The max index in this dimension (we round UP, but when we iterate
904// we'll NOT include this edge)
905// if (bin.m_max[d] < this->extents[d].max)
906// {
907// max = int(ceil((bin.m_max[d] - this->extents[d].getMin()) /
908// boxSize[d])) - 1;
909// counters_max[d] = max+1; // (the counter looping will NOT include
910// counters_max[d])
911// }
912// else
913// {
914// max = int(split[d]); // Goes past THAT edge
915// counters_max[d] = max; // (the counter looping will NOT include max)
916// }
917//
918// // If the max value is before the min, that means NOTHING is in the bin,
919// and we can return
920// if ((max < min) || (max < 0))
921// return;
922// index_max[d] = max;
923//
924// //std::cout << d << " from " << std::setw(5) << index_min[d] << " to "
925// << std::setw(5) << index_max[d] << "inc\n";
926// }
927//
928// // If you reach here, than at least some of bin is overlapping this box
929//
930//
931// // We start by looking at the vertices at every corner of every box
932// contained,
933// // to see which boxes are partially contained/fully contained.
934//
935// // One entry with the # of vertices in this box contained; start at 0.
936// size_t * verticesContained = new size_t[numBoxes];
937// memset( verticesContained, 0, numBoxes * sizeof(size_t) );
938//
939// // Set to true if there is a possibility of the box at least partly
940// touching the integration volume.
941// bool * boxMightTouch = new bool[numBoxes];
942// memset( boxMightTouch, 0, numBoxes * sizeof(bool) );
943//
944// // How many vertices does one box have? 2^nd, or bitwise shift left 1 by
945// nd bits
946// size_t maxVertices = 1 << nd;
947//
948// // The index to the vertex in each dimension
949// size_t * vertexIndex = Utils::NestedForLoop::SetUp(nd, 0);
950//
951// // This is the index in each dimension at which we start looking at
952// vertices
953// size_t * vertices_min = Utils::NestedForLoop::SetUp(nd, 0);
954// for (size_t d=0; d<nd; ++d)
955// {
956// vertices_min[d] = counters_min[d];
957// vertexIndex[d] = vertices_min[d]; // This is where we start
958// }
959//
960// // There is one more vertex in each dimension than there are boxes we are
961// considering
962// size_t * vertices_max = Utils::NestedForLoop::SetUp(nd, 0);
963// for (size_t d=0; d<nd; ++d)
964// vertices_max[d] = counters_max[d]+1;
965//
966// size_t * boxIndex = Utils::NestedForLoop::SetUp(nd, 0);
967// size_t * indexMaker = Utils::NestedForLoop::SetUpIndexMaker(nd, split);
968//
969// bool allDone = false;
970// while (!allDone)
971// {
972// // Coordinates of this vertex
973// coord_t vertexCoord[nd];
974// bool masks[nd];
975// for (size_t d=0; d<nd; ++d)
976// {
977// vertexCoord[d] = double(vertexIndex[d]) * boxSize[d] +
978// this->extents[d].getMin();
979// masks[d] = false; //HACK ... assumes that all vertexes are used.
980// }
981// // Is this vertex contained?
982// if (function.evaluate(vertexCoord, masks, nd))
983// {
984// // Yes, this vertex is contained within the integration volume!
987//
988// // This vertex is shared by up to 2^nd adjacent boxes (left-right
989// along each dimension).
990// for (size_t neighb=0; neighb<maxVertices; ++neighb)
991// {
992// // The index of the box is the same as the vertex, but maybe - 1 in
993// each possible combination of dimensions
994// bool badIndex = false;
995// // Build the index of the neighbor
996// for (size_t d=0; d<nd;d++)
997// {
998// boxIndex[d] = vertexIndex[d] - ((neighb & (1 << d)) >> d); //(this
999// does a bitwise and mask, shifted back to 1 to subtract 1 to the
1000// dimension)
1001// // Taking advantage of the fact that unsigned(0)-1 = some large
1002// POSITIVE number.
1003// if (boxIndex[d] >= split[d])
1004// {
1005// badIndex = true;
1006// break;
1007// }
1008// }
1009// if (!badIndex)
1010// {
1011// // Convert to linear index
1012// size_t linearIndex = Utils::NestedForLoop::GetLinearIndex(nd,
1013// boxIndex, indexMaker);
1014// // So we have one more vertex touching this box that is contained
1015// in the integration volume. Whew!
1016// verticesContained[linearIndex]++;
1019// }
1020// }
1021// }
1022//
1023// // Increment the counter(s) in the nested for loops.
1024// allDone = Utils::NestedForLoop::Increment(nd, vertexIndex, vertices_max,
1025// vertices_min);
1026// }
1027//
1028// // OK, we've done all the vertices. Now we go through and check each box.
1029// size_t numFullyContained = 0;
1030// //size_t numPartiallyContained = 0;
1031//
1032// // We'll iterate only through the boxes with (bin)
1033// size_t counters[nd];
1034// for (size_t d=0; d<nd; d++)
1035// counters[d] = counters_min[d];
1036//
1037// allDone = false;
1038// while (!allDone)
1039// {
1040// size_t index = getLinearIndex(counters);
1041// MDBoxBase<MDE,nd> * box = boxes[index];
1042//
1043// // Is this box fully contained?
1044// if (verticesContained[index] >= maxVertices)
1045// {
1046// // Use the integrated sum of signal in the box
1047// bin.m_signal += box->getSignal();
1048// bin.m_errorSquared += box->getErrorSquared();
1049// numFullyContained++;
1050// }
1051// else
1052// {
1053// // The box MAY be contained. Need to evaluate every event
1054//
1055// // box->generalBin(bin,function);
1056// }
1057//
1058// // Increment the counter(s) in the nested for loops.
1059// allDone = Utils::NestedForLoop::Increment(nd, counters, counters_max,
1060// counters_min);
1061// }
1062//
1066//
1067// delete [] verticesContained;
1068// delete [] boxMightTouch;
1069// delete [] vertexIndex;
1070// delete [] vertices_max;
1071// delete [] boxIndex;
1072// delete [] indexMaker;
1073//
1074// }
1075//
1076//
1077
1078//-----------------------------------------------------------------------------------------------
1094TMDE(void MDGridBox)::integrateSphere(API::CoordTransform &radiusTransform, const coord_t radiusSquared,
1095 signal_t &signal, signal_t &errorSquared, const coord_t innerRadiusSquared,
1096 const bool useOnePercentBackgroundCorrection) const {
1097 // We start by looking at the vertices at every corner of every box contained,
1098 // to see which boxes are partially contained/fully contained.
1099
1100 // One entry with the # of vertices in this box contained; start at 0.
1101 std::vector<size_t> verticesContained(numBoxes, 0);
1102
1103 // Set to true if there is a possibility of the box at least partly touching
1104 // the integration volume.
1105 std::vector<bool> boxMightTouch(numBoxes, 0);
1106
1107 // How many vertices does one box have? 2^nd, or bitwise shift left 1 by nd
1108 // bits
1109 size_t maxVertices = 1 << nd;
1110
1111 // set up caches for box sizes and min box values
1112 coord_t boxSize[nd];
1113 coord_t minBoxVal[nd];
1114
1115 // The number of vertices in each dimension is the # split[d] + 1
1116 size_t vertices_max[nd];
1117 Kernel::Utils::NestedForLoop::SetUp(nd, vertices_max, 0);
1118 for (size_t d = 0; d < nd; ++d) {
1119 vertices_max[d] = split[d] + 1;
1120 // cache box sizes and min box valyes for performance
1121 boxSize[d] = static_cast<coord_t>(m_SubBoxSize[d]);
1122 minBoxVal[d] = static_cast<coord_t>(this->extents[d].getMin());
1123 }
1124
1125 // The index to the vertex in each dimension
1126 size_t vertexIndex[nd];
1127 Kernel::Utils::NestedForLoop::SetUp(nd, vertexIndex, 0);
1128 size_t boxIndex[nd];
1129 Kernel::Utils::NestedForLoop::SetUp(nd, boxIndex, 0);
1130 size_t indexMaker[nd];
1131 Kernel::Utils::NestedForLoop::SetUpIndexMaker(nd, indexMaker, split);
1132
1133 bool allDone = false;
1134 while (!allDone) {
1135 // Coordinates of this vertex
1136 coord_t vertexCoord[nd];
1137 for (size_t d = 0; d < nd; ++d)
1138 vertexCoord[d] = static_cast<coord_t>(vertexIndex[d]) * boxSize[d] + minBoxVal[d];
1139 // static_cast<coord_t>(vertexIndex[d]) * boxSize[d] + this->extents[d].min
1140
1141 // Is this vertex contained?
1142 coord_t out[nd];
1143 radiusTransform.apply(vertexCoord, out);
1144 if (out[0] < radiusSquared && out[0] > innerRadiusSquared) {
1145 // Yes, this vertex is contained within the integration volume!
1146 // std::cout << "vertex at " << vertexCoord[0] << ", " <<
1147 // vertexCoord[1] << ", " << vertexCoord[2] << " is contained\n";
1148
1149 // This vertex is shared by up to 2^nd adjacent boxes (left-right along
1150 // each dimension).
1151 for (size_t neighb = 0; neighb < maxVertices; ++neighb) {
1152 // The index of the box is the same as the vertex, but maybe - 1 in each
1153 // possible combination of dimensions
1154 bool badIndex = false;
1155 // Build the index of the neighbor
1156 for (size_t d = 0; d < nd; d++) {
1157 boxIndex[d] = vertexIndex[d] - ((neighb & ((size_t)1 << d)) >> d); //(this does a bitwise and mask,
1158 // shifted back to 1 to subtract 1
1159 // to the dimension)
1160 // Taking advantage of the fact that unsigned(0)-1 = some large
1161 // POSITIVE number.
1162 if (boxIndex[d] >= split[d]) {
1163 badIndex = true;
1164 break;
1165 }
1166 }
1167 if (!badIndex) {
1168 // Convert to linear index
1169 size_t linearIndex = Kernel::Utils::NestedForLoop::GetLinearIndex(nd, boxIndex, indexMaker);
1170 // So we have one more vertex touching this box that is contained in
1171 // the integration volume. Whew!
1172 verticesContained[linearIndex]++;
1173 // std::cout << "... added 1 vertex to box " <<
1174 // boxes[linearIndex]->getExtentsStr() << "\n";
1175 }
1176 }
1177 }
1178
1179 // Increment the counter(s) in the nested for loops.
1180 allDone = Kernel::Utils::NestedForLoop::Increment(nd, vertexIndex, vertices_max);
1181 }
1182
1183 // NOTE:
1184 // The following section is just trying to uncover the peak center so that
1185 // we can compute the distance bewteen peak center and box center correctly
1186 // without worrying about the skew coming from coordTransformDistnace.
1187 auto tmpRadiusTransform = dynamic_cast<CoordTransformDistance *>(&radiusTransform);
1188 if (tmpRadiusTransform == nullptr) {
1189 throw std::runtime_error("radiusTransform has to be CoordTransformDistance");
1190 }
1191 auto peakCenter = tmpRadiusTransform->getCenter();
1192 double peakRadius = std::sqrt(radiusSquared);
1193 double peakInnerRadius = std::sqrt(innerRadiusSquared);
1194
1195 // OK, we've done counting all the vertices.
1196 // Now let's go through and check each box.
1197 for (size_t bIndex = 0; bIndex < numBoxes; ++bIndex) {
1198 API::IMDNode *box = m_Children[bIndex];
1199
1200 // First, check if we have reached the base case where the box is completely
1201 // enveloped by the peak
1202 if (verticesContained[bIndex] >= maxVertices) {
1203 // Use the integrated sum of signal in the box
1204 signal += box->getSignal();
1205 errorSquared += box->getErrorSquared();
1206 continue; // move on to next
1207 }
1208
1209 // Second, check if there is at least one vertex in the integration volume,
1210 // and kick off recursive search until we reach the base case
1211 if (verticesContained[bIndex] > 0) {
1212 box->integrateSphere(radiusTransform, radiusSquared, signal, errorSquared, innerRadiusSquared,
1213 useOnePercentBackgroundCorrection);
1214 continue;
1215 }
1216
1217 // Last, no vertices in the integration volume
1218 // -- there are only two cases (see below) where we can
1219 // skip the box and its children, and it requires
1220 // the knowledge of how box and peak are spatially
1221 // positioned in Euclidian space.
1222 // NOTE:
1223 // For ellipsoid, we are using the long semi-axis, which
1224 // means we will inevitablly search some empty boxes, but
1225 // this is by design as we do not want to miss any box
1226 // that might contain events we need to collect.
1227 coord_t boxCenter[nd];
1228 box->getCenter(boxCenter);
1229 double distPeakCenterToBoxCenter = 0.0;
1230 for (size_t i = 0; i < nd; ++i) {
1231 distPeakCenterToBoxCenter += (boxCenter[i] - peakCenter[i]) * (boxCenter[i] - peakCenter[i]);
1232 }
1233 distPeakCenterToBoxCenter = std::sqrt(distPeakCenterToBoxCenter);
1234 double boxRadius = std::sqrt(diagonalSquared);
1235
1236 // - Box is completely isolated from peak
1237 // distPeakCenterToBoxCenter - peakRadius > boxRadius
1238 // -> stop search and move on
1239 if (distPeakCenterToBoxCenter - peakRadius > boxRadius) {
1240 // Debug output
1241 // std::ostringstream debugmsg;
1242 // debugmsg << "R_peak = " << peakRadius << "\n"
1243 // << "R_box = " << boxRadius << "\n"
1244 // << "Peak Center: ";
1245 // for (size_t i = 0; i < nd; ++i) {
1246 // debugmsg << peakCenter[i] << ",";
1247 // }
1248 // debugmsg << "\n"
1249 // << "Box center: ";
1250 // for (size_t i = 0; i < nd; ++i) {
1251 // debugmsg << boxCenter[i] << ",";
1252 // }
1253 // debugmsg << "\n"
1254 // << "distPeakCenterToBoxCenter = " << distPeakCenterToBoxCenter
1255 // << "\n";
1256 // std::cout << debugmsg.str();
1257 continue;
1258 }
1259 // - Tiny box falls into the donut hole
1260 // distPeakCenterToBoxCenter + boxRadius < peakInnerRadius
1261 // -> stop search and move on
1262 if (peakInnerRadius > 0 && distPeakCenterToBoxCenter + boxRadius < peakInnerRadius) {
1263 continue;
1264 }
1265 // - All other cases, we need to refine the box, i.e. recursively
1266 // search the child box
1267 box->integrateSphere(radiusTransform, radiusSquared, signal, errorSquared, innerRadiusSquared,
1268 useOnePercentBackgroundCorrection);
1269 } // (for each box)
1270}
1271
1272//-----------------------------------------------------------------------------------------------
1283TMDE(void MDGridBox)::centroidSphere(API::CoordTransform &radiusTransform, const coord_t radiusSquared,
1284 coord_t *centroid, signal_t &signal) const {
1285 for (size_t i = 0; i < numBoxes; ++i) {
1286 // Go through each contained box
1287 API::IMDNode *box = m_Children[i];
1288 coord_t boxCenter[nd];
1289 box->getCenter(boxCenter);
1290
1291 // Distance from center to the peak integration center
1292 coord_t out[nd];
1293 radiusTransform.apply(boxCenter, out);
1294
1295 if (out[0] < diagonalSquared * 0.72 + radiusSquared) {
1296 // If the center is closer than the size of the box, then it MIGHT be
1297 // touching.
1298 // (We multiply by 0.72 (about sqrt(2)) to look for half the diagonal).
1299 // NOTE! Watch out for non-spherical transforms!
1300
1301 // Go down one level to keep centroiding
1302 box->centroidSphere(radiusTransform, radiusSquared, centroid, signal);
1303 }
1304 } // (for each box)
1305}
1306//-----------------------------------------------------------------------------------------------
1307GNU_DIAG_OFF("array-bounds")
1323TMDE(void MDGridBox)::integrateCylinder(Mantid::API::CoordTransform &radiusTransform, const coord_t radius,
1324 const coord_t length, signal_t &signal, signal_t &errorSquared,
1325 std::vector<signal_t> &signal_fit) const {
1326 // We start by looking at the vertices at every corner of every box contained,
1327 // to see which boxes are partially contained/fully contained.
1328
1329 // One entry with the # of vertices in this box contained; start at 0.
1330 auto verticesContained = new size_t[numBoxes];
1331 memset(verticesContained, 0, numBoxes * sizeof(size_t));
1332
1333 // Set to true if there is a possibility of the box at least partly touching
1334 // the integration volume.
1335 auto boxMightTouch = new bool[numBoxes];
1336 memset(boxMightTouch, 0, numBoxes * sizeof(bool));
1337
1338 // How many vertices does one box have? 2^nd, or bitwise shift left 1 by nd
1339 // bits
1340 size_t maxVertices = 1 << nd;
1341
1342 // set up caches for box sizes and min box values
1343 coord_t boxSize[nd];
1344 coord_t minBoxVal[nd];
1345
1346 // The number of vertices in each dimension is the # split[d] + 1
1347 size_t vertices_max[nd];
1348 Kernel::Utils::NestedForLoop::SetUp(nd, vertices_max, 0);
1349 for (size_t d = 0; d < nd; ++d) {
1350 vertices_max[d] = split[d] + 1;
1351 // cache box sizes and min box valyes for performance
1352 boxSize[d] = static_cast<coord_t>(m_SubBoxSize[d]);
1353 minBoxVal[d] = static_cast<coord_t>(this->extents[d].getMin());
1354 }
1355
1356 // The index to the vertex in each dimension
1357 size_t vertexIndex[nd];
1358 Kernel::Utils::NestedForLoop::SetUp(nd, vertexIndex, 0);
1359 size_t boxIndex[nd];
1360 Kernel::Utils::NestedForLoop::SetUp(nd, boxIndex, 0);
1361 size_t indexMaker[nd];
1362 Kernel::Utils::NestedForLoop::SetUpIndexMaker(nd, indexMaker, split);
1363
1364 size_t numSteps = signal_fit.size();
1365 double deltaQ = length / static_cast<double>(numSteps - 1);
1366 bool allDone = false;
1367 while (!allDone) {
1368 // Coordinates of this vertex
1369 coord_t vertexCoord[nd];
1370 for (size_t d = 0; d < nd; ++d)
1371 vertexCoord[d] = static_cast<coord_t>(vertexIndex[d]) * boxSize[d] + minBoxVal[d];
1372 // static_cast<coord_t>(vertexIndex[d]) * boxSize[d] + this->extents[d].min
1373
1374 // Is this vertex contained?
1375 coord_t out[2]; // radius and length of cylinder
1376 radiusTransform.apply(vertexCoord, out);
1377 if (out[0] < radius && std::fabs(out[1]) < 0.5 * length) {
1378 // Yes, this vertex is contained within the integration volume!
1379 // std::cout << "vertex at " << vertexCoord[0] << ", " <<
1380 // vertexCoord[1] << ", " << vertexCoord[2] << " is contained\n";
1381
1382 // This vertex is shared by up to 2^nd adjacent boxes (left-right along
1383 // each dimension).
1384 for (size_t neighb = 0; neighb < maxVertices; ++neighb) {
1385 // The index of the box is the same as the vertex, but maybe - 1 in each
1386 // possible combination of dimensions
1387 bool badIndex = false;
1388 // Build the index of the neighbor
1389 for (size_t d = 0; d < nd; d++) {
1390 boxIndex[d] = vertexIndex[d] - ((neighb & ((size_t)1 << d)) >> d); //(this does a bitwise and mask,
1391 // shifted back to 1 to subtract 1
1392 // to the dimension)
1393 // Taking advantage of the fact that unsigned(0)-1 = some large
1394 // POSITIVE number.
1395 if (boxIndex[d] >= split[d]) {
1396 badIndex = true;
1397 break;
1398 }
1399 }
1400 if (!badIndex) {
1401 // Convert to linear index
1402 size_t linearIndex = Kernel::Utils::NestedForLoop::GetLinearIndex(nd, boxIndex, indexMaker);
1403 // So we have one more vertex touching this box that is contained in
1404 // the integration volume. Whew!
1405 verticesContained[linearIndex]++;
1406 // std::cout << "... added 1 vertex to box " <<
1407 // boxes[linearIndex]->getExtentsStr() << "\n";
1408 }
1409 }
1410 }
1411
1412 // Increment the counter(s) in the nested for loops.
1413 allDone = Kernel::Utils::NestedForLoop::Increment(nd, vertexIndex, vertices_max);
1414 }
1415
1416 // OK, we've done all the vertices. Now we go through and check each box.
1417 for (size_t i = 0; i < numBoxes; ++i) {
1418 API::IMDNode *box = m_Children[i];
1419 // Box partially contained?
1420 bool partialBox = false;
1421
1422 // Is this box fully contained?
1423 if (verticesContained[i] >= maxVertices) {
1424 std::vector<coord_t> coordTable;
1425 size_t nColumns;
1426 box->getEventsData(coordTable, nColumns);
1427 if (nColumns > 0 && nd > 1) {
1428 size_t nEvents = coordTable.size() / nColumns;
1429 size_t skipCol = 2; // lean events
1430 if (nColumns == 7)
1431 skipCol += 2; // events
1432 for (size_t k = 0; k < nEvents; k++) {
1433 coord_t eventCenter[nd];
1434 for (size_t l = 0; l < nd; l++)
1435 eventCenter[l] = coordTable[k * nColumns + skipCol + l];
1436 coord_t out[nd];
1437 radiusTransform.apply(eventCenter, out);
1438 // add event to appropriate y channel
1439 size_t xchannel = static_cast<size_t>(std::floor(out[1] / deltaQ)) + numSteps / 2;
1440
1441 if (xchannel < numSteps)
1442 signal_fit[xchannel] += coordTable[k * nColumns];
1443 }
1444 }
1445 // box->releaseEvents();
1446 // Use the integrated sum of signal in the box
1447 signal += box->getSignal();
1448 errorSquared += box->getErrorSquared();
1449
1450 // std::cout << "box at " << i << " (" << box->getExtentsStr() <<
1451 // ") is fully contained. Vertices = " << verticesContained[i] <<
1452 // "\n";
1453 // Go on to the next box
1454 continue;
1455 }
1456
1457 if (verticesContained[i] == 0) {
1458 // There is a chance that this part of the box is within integration
1459 // volume,
1460 // even if no vertex of it is.
1461 coord_t boxCenter[nd];
1462 box->getCenter(boxCenter);
1463
1464 // Distance from center to the peak integration center
1465 coord_t out[nd];
1466 radiusTransform.apply(boxCenter, out);
1467 if ((nd >= 1) && out[0] < std::sqrt(diagonalSquared * 0.72 + radius * radius) &&
1468 (nd >= 2 && std::fabs(out[1]) < std::sqrt(diagonalSquared * 0.72 + 0.25 * length * length))) {
1469 // If the center is closer than the size of the box, then it MIGHT be
1470 // touching.
1471 // (We multiply by 0.72 (about sqrt(2)) to look for half the diagonal).
1472 // NOTE! Watch out for non-spherical transforms!
1473 // std::cout << "box at " << i << " is maybe touching\n";
1474 partialBox = true;
1475 }
1476 } else {
1477 partialBox = true;
1478 // std::cout << "box at " << i << " has a vertex touching\n";
1479 }
1480
1481 // We couldn't rule out that the box might be partially contained.
1482 if (partialBox) {
1483 // Use the detailed integration method.
1484 box->integrateCylinder(radiusTransform, radius, length, signal, errorSquared, signal_fit);
1485 // std::cout << ".signal=" << signal << "\n";
1486 }
1487 } // (for each box)
1488
1489 // std::cout << "Depth " << this->getDepth() << " with " <<
1490 // numFullyContained << " fully contained; " << numPartiallyContained << "
1491 // partial. Signal = " << signal <<"\n";
1492
1493 delete[] verticesContained;
1494 delete[] boxMightTouch;
1495}
1496GNU_DIAG_ON("array-bounds")
1497
1498
1502TMDE(bool MDGridBox)::getIsMasked() const {
1503 bool isMasked = false;
1504 for (size_t i = 0; i < numBoxes; ++i) {
1505 // Go through each contained box
1506 API::IMDNode *box = m_Children[i];
1507 if (box->getIsMasked()) {
1508 isMasked = true;
1509 break;
1510 }
1511 }
1512 return isMasked;
1513}
1514
1516TMDE(void MDGridBox)::mask() {
1517 for (size_t i = 0; i < numBoxes; ++i) {
1518 // Go through each contained box
1519 API::IMDNode *box = m_Children[i];
1520 box->mask();
1521 }
1522}
1523
1525TMDE(void MDGridBox)::unmask() {
1526 for (size_t i = 0; i < numBoxes; ++i) {
1527 // Go through each contained box
1528 API::IMDNode *box = m_Children[i];
1529 box->unmask();
1530 }
1531}
1532//------------------------------------------------------------------------------------------------------------------------------------------------------------
1533//------------------------------------------------------------------------------------------------------------------------------------------------------------
1534//------------------------------------------------------------------------------------------------------------------------------------------------------------
1535/* Internal TMP class to simplify adding events to the box for events and lean
1536 * events using single interface. One would nead to overload the box class
1537 * otherwise*/
1538template <typename MDE, size_t nd> struct IF_EVENT {
1539public:
1540 // create generic events from array of events data and add them to the grid
1541 // box
1542 static inline void EXEC(MDGridBox<MDE, nd> *pBox, const std::vector<signal_t> &sigErrSq,
1543 const std::vector<coord_t> &Coord, const std::vector<uint16_t> &expInfoIndex,
1544 const std::vector<uint16_t> &goniometerIndex, const std::vector<uint32_t> &detectorId,
1545 size_t nEvents) {
1546 for (size_t i = 0; i < nEvents; i++)
1547 pBox->addEvent(MDEvent<nd>(sigErrSq[2 * i], sigErrSq[2 * i + 1], expInfoIndex[i], goniometerIndex[i],
1548 detectorId[i], &Coord[i * nd]));
1549 }
1550};
1551/* Specialize for the case of LeanEvent */
1552template <size_t nd> struct IF_EVENT<MDLeanEvent<nd>, nd> {
1553public:
1554 // create lean events from array of events data and add them to the grid box
1555 static inline void EXEC(MDGridBox<MDLeanEvent<nd>, nd> *pBox, const std::vector<signal_t> &sigErrSq,
1556 const std::vector<coord_t> &Coord, const std::vector<uint16_t> & /*expInfoIndex*/,
1557 const std::vector<uint16_t> & /*goniometerIndex*/,
1558 const std::vector<uint32_t> & /*detectorId*/, size_t nEvents) {
1559 for (size_t i = 0; i < nEvents; i++)
1560 pBox->addEvent(MDLeanEvent<nd>(sigErrSq[2 * i], sigErrSq[2 * i + 1], &Coord[i * nd]));
1561 }
1562};
1563
1577TMDE(size_t MDGridBox)::buildAndAddEvents(const std::vector<signal_t> &sigErrSq, const std::vector<coord_t> &Coord,
1578 const std::vector<uint16_t> &expInfoIndex,
1579 const std::vector<uint16_t> &goniometerIndex,
1580 const std::vector<uint32_t> &detectorId) {
1581
1582 size_t nEvents = sigErrSq.size() / 2;
1583 IF_EVENT<MDE, nd>::EXEC(this, sigErrSq, Coord, expInfoIndex, goniometerIndex, detectorId, nEvents);
1584
1585 return 0;
1586}
1587
1596TMDE(void MDGridBox)::buildAndAddEvent(const signal_t Signal, const signal_t errorSq, const std::vector<coord_t> &point,
1597 uint16_t expInfoIndex, uint16_t goniometerIndex, uint32_t detectorId) {
1598 this->addEvent(IF<MDE, nd>::BUILD_EVENT(Signal, errorSq, &point[0], expInfoIndex, goniometerIndex, detectorId));
1599}
1600
1601//-----------------------------------------------------------------------------------------------
1614TMDE(void MDGridBox)::buildAndAddEventUnsafe(const signal_t Signal, const signal_t errorSq,
1615 const std::vector<coord_t> &point, uint16_t expInfoIndex,
1616 uint16_t goniometerIndex, uint32_t detectorId) {
1617 this->addEventUnsafe(IF<MDE, nd>::BUILD_EVENT(Signal, errorSq, &point[0], expInfoIndex, goniometerIndex, detectorId));
1618}
1619
1620//-----------------------------------------------------------------------------------------------
1633TMDE(inline size_t MDGridBox)::addEvent(const MDE &event) {
1634 size_t cindex = calculateChildIndex(event);
1635
1636 // We can erroneously get cindex == numBoxes for events which fall on the
1637 // upper boundary of the last child box, so add these events to the last box
1638 if (cindex == numBoxes)
1639 cindex = numBoxes - 1;
1640
1641 if (cindex < numBoxes)
1642 return m_Children[cindex]->addEvent(event);
1643 else
1644 return 0;
1645}
1646
1647//-----------------------------------------------------------------------------------------------
1664TMDE(inline size_t MDGridBox)::addEventUnsafe(const MDE &event) {
1665 size_t cindex = calculateChildIndex(event);
1666
1667 // We can erroneously get cindex == numBoxes for events which fall on the
1668 // upper boundary of the last child box, so add these events to the last box
1669 if (cindex == numBoxes)
1670 cindex = numBoxes - 1;
1671
1672 if (cindex < numBoxes)
1673 return m_Children[cindex]->addEventUnsafe(event);
1674 else
1675 return 0;
1676}
1677
1684TMDE(inline void MDGridBox)::setChild(size_t index, MDGridBox<MDE, nd> *newChild) {
1685 // Delete the old box (supposetly ungridded);
1686 delete this->m_Children[index];
1687 // set new box, supposetly gridded
1688 this->m_Children[index] = newChild;
1689}
1692TMDE(void MDGridBox)::setFileBacked(const uint64_t /*fileLocation*/, const size_t /*fileSize*/,
1693 const bool /*markSaved*/) {
1694 throw(Kernel::Exception::NotImplementedError("Recursive file backed is not "
1695 "yet implemented (unclear how "
1696 "to set file location etc)"));
1697}
1700TMDE(void MDGridBox)::setFileBacked() {
1701 for (size_t i = 0; i < this->numBoxes; i++) {
1702 m_Children[i]->setFileBacked();
1703 }
1704}
1717TMDE(void MDGridBox)::clearFileBacked(bool loadDiskBackedData) {
1718 auto it = m_Children.begin();
1719 auto it_end = m_Children.end();
1720 for (; it != it_end; it++) {
1721 (*it)->clearFileBacked(loadDiskBackedData);
1722 }
1723}
1724
1728TMDE(size_t MDGridBox)::calculateChildIndex(const MDE &event) const {
1729 size_t cindex(0);
1730 for (size_t d = 0; d < nd; d++) {
1731 // Accumulate the index
1732 const auto coordinate = event.getCenter(d);
1733 const auto offset = coordinate - this->extents[d].getMin();
1734 const int splitD = static_cast<int>(split[d]);
1735 // clamp to splitD-1: FP rounding can place an in-box coordinate one slot past the last valid child
1736 auto childIndex = static_cast<int>(offset * m_SubBoxSizeInv[d]);
1737 if (childIndex == splitD && coordinate <= this->extents[d].getMax())
1738 childIndex = splitD - 1;
1739 cindex += childIndex * splitCumul[d];
1740 }
1741 return cindex;
1742}
1743} // namespace DataObjects
1744
1745} // 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:40
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:64
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)