-
Notifications
You must be signed in to change notification settings - Fork 122
/
MDGridBox.cpp
1737 lines (1579 loc) · 65.2 KB
/
MDGridBox.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "MantidKernel/Task.h"
#include "MantidKernel/Utils.h"
#include "MantidKernel/FunctionTask.h"
#include "MantidKernel/Timer.h"
#include "MantidKernel/ThreadPool.h"
#include "MantidKernel/ThreadScheduler.h"
#include "MantidKernel/ThreadSchedulerMutexes.h"
#include "MantidKernel/WarningSuppressions.h"
#include "MantidMDEvents/MDBox.h"
#include "MantidMDEvents/MDEvent.h"
#include "MantidMDEvents/MDGridBox.h"
#include <boost/math/special_functions/round.hpp>
#include <ostream>
#include "MantidKernel/Strings.h"
using namespace Mantid;
using namespace Mantid::Kernel;
using namespace Mantid::API;
// These pragmas ignores the warning in the ctor where "d<nd-1" for nd=1.
// This is okay (though would be better if it were for only that function
#if (defined(__INTEL_COMPILER))
#pragma warning disable 186
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wtype-limits"
#endif
namespace Mantid {
namespace MDEvents {
////===============================================================================================
////===============================================================================================
//-----------------------------------------------------------------------------------------------
/** Constructor with a box controller.
* @param bc :: poineter to the BoxController, owned by workspace
* @param depth :: recursive split depth
* @param extentsVector :: size of the box
*/
TMDE(MDGridBox)::MDGridBox(
BoxController *const bc, const uint32_t depth,
const std::vector<
Mantid::Geometry::MDDimensionExtents<coord_t>> &extentsVector)
: MDBoxBase<MDE, nd>(bc, depth, UNDEF_SIZET, extentsVector), numBoxes(0),
nPoints(0) {
initGridBox();
}
/** convenience Constructor, taking the shared pointer and extracting const
* pointer from it
* @param bc :: shared poineter to the BoxController, owned by workspace
* @param depth :: recursive split depth
* @param extentsVector :: size of the box
*/
TMDE(MDGridBox)::MDGridBox(
boost::shared_ptr<API::BoxController> &bc, const uint32_t depth,
const std::vector<
Mantid::Geometry::MDDimensionExtents<coord_t>> &extentsVector)
: MDBoxBase<MDE, nd>(bc.get(), depth, UNDEF_SIZET, extentsVector),
numBoxes(0), nPoints(0) {
initGridBox();
}
/// common part of MDGridBox contstructor;
template <typename MDE, size_t nd> void MDGridBox<MDE, nd>::initGridBox() {
if (!this->m_BoxController)
throw std::runtime_error(
"MDGridBox::ctor(): No BoxController specified in box.");
// How many is it split?
for (size_t d = 0; d < nd; d++)
split[d] = this->m_BoxController->getSplitInto(d);
// Compute sizes etc.
size_t tot = computeSizesFromSplit();
if (tot == 0)
throw std::runtime_error(
"MDGridBox::ctor(): Invalid splitting criterion (one was zero).");
}
//-----------------------------------------------------------------------------------------------
/** Constructor
* @param box :: MDBox containing the events to split
*/
TMDE(MDGridBox)::MDGridBox(MDBox<MDE, nd> *box)
: MDBoxBase<MDE, nd>(*box, box->getBoxController()), nPoints(0) {
if (!this->m_BoxController)
throw std::runtime_error("MDGridBox::ctor(): constructing from box:: No "
"BoxController specified in box.");
// std::cout << "Splitting MDBox ID " << box->getId() << " with " <<
// box->getNPoints() << " events into MDGridBox" << std::endl;
// How many is it split?
for (size_t d = 0; d < nd; d++)
split[d] = this->m_BoxController->getSplitInto(d);
// Compute sizes etc.
size_t tot = computeSizesFromSplit();
if (tot == 0)
throw std::runtime_error("MDGridBox::ctor(): constructing from "
"box::Invalid splitting criterion (one was "
"zero).");
double ChildVol(1);
for (size_t d = 0; d < nd; d++)
ChildVol *= m_SubBoxSize[d];
// Splitting an input MDBox requires creating a bunch of children
fillBoxShell(tot, coord_t(1. / ChildVol));
// Prepare to distribute the events that were in the box before, this will
// load missing events from HDD in file based ws if there are some.
const std::vector<MDE> &events = box->getConstEvents();
typename std::vector<MDE>::const_iterator it = events.begin();
typename std::vector<MDE>::const_iterator it_end = events.end();
// just add event to the existing internal box
for (; it != it_end; ++it)
addEvent(*it);
// Copy the cached numbers from the incoming box. This is quick - don't need
// to refresh cache
this->nPoints = box->getNPoints();
// Clear the old box and delete it from disk buffer if one is used.
box->clear();
}
/**Internal function to do main job of filling in a GridBox contents (part of
* the constructor) */
template <typename MDE, size_t nd>
void MDGridBox<MDE, nd>::fillBoxShell(const size_t tot,
const coord_t ChildInverseVolume) {
// Create the array of MDBox contents.
this->m_Children.clear();
this->m_Children.reserve(tot);
this->numBoxes = tot;
size_t indices[nd];
for (size_t d = 0; d < nd; d++)
indices[d] = 0;
// get inital free ID for the boxes, which would be created by this command
// Splitting an input MDBox requires creating a bunch of children
// But the IDs of these children MUST be sequential. Hence the critical block
// within claimIDRange,
// which would produce sequental ranges in multithreaded environment
size_t ID0 = this->m_BoxController->claimIDRange(tot);
for (size_t i = 0; i < tot; i++) {
// Create the box
// (Increase the depth of this box to one more than the parent (this))
MDBox<MDE, nd> *splitBox = new MDBox<MDE, nd>(
this->m_BoxController, this->m_depth + 1, UNDEF_SIZET, size_t(ID0 + i));
// This MDGridBox is the parent of the new child.
splitBox->setParent(this);
// Set the extents of this box.
for (size_t d = 0; d < nd; d++) {
double min = double(this->extents[d].getMin()) +
double(indices[d]) * m_SubBoxSize[d];
double max = min + m_SubBoxSize[d];
splitBox->setExtents(d, min, max);
}
splitBox->setInverseVolume(
ChildInverseVolume); // Set the cached inverse volume
m_Children.push_back(splitBox);
// Increment the indices, rolling back as needed
indices[0]++;
for (
size_t d = 0; d < nd - 1;
d++) // This is not run if nd=1; that's okay, you can ignore the warning
{
if (indices[d] >= split[d]) {
indices[d] = 0;
indices[d + 1]++;
}
}
} // for each box
}
//-----------------------------------------------------------------------------------------------
/** Copy constructor
* @param other :: MDGridBox to copy
* @param otherBC :: mandatory pointer to other box controller, which will split
this box.
if it the same BC, as the one for the copied box, it needs
to be taken explicitly from the
copied box.
*/
TMDE(MDGridBox)::MDGridBox(const MDGridBox<MDE, nd> &other,
Mantid::API::BoxController *const otherBC)
: MDBoxBase<MDE, nd>(other, otherBC), numBoxes(other.numBoxes),
diagonalSquared(other.diagonalSquared), nPoints(other.nPoints) {
for (size_t d = 0; d < nd; d++) {
split[d] = other.split[d];
splitCumul[d] = other.splitCumul[d];
m_SubBoxSize[d] = other.m_SubBoxSize[d];
}
// Copy all the boxes
m_Children.clear();
m_Children.reserve(numBoxes);
for (size_t i = 0; i < other.m_Children.size(); i++) {
API::IMDNode *otherBox = other.m_Children[i];
const MDBox<MDE, nd> *otherMDBox =
dynamic_cast<const MDBox<MDE, nd> *>(otherBox);
const MDGridBox<MDE, nd> *otherMDGridBox =
dynamic_cast<const MDGridBox<MDE, nd> *>(otherBox);
if (otherMDBox) {
MDBox<MDE, nd> *newBox = new MDBox<MDE, nd>(*otherMDBox, otherBC);
newBox->setParent(this);
m_Children.push_back(newBox);
} else if (otherMDGridBox) {
MDGridBox<MDE, nd> *newBox =
new MDGridBox<MDE, nd>(*otherMDGridBox, otherBC);
newBox->setParent(this);
m_Children.push_back(newBox);
} else {
throw std::runtime_error(
"MDGridBox::copy_ctor(): an unexpected child box type was found.");
}
}
}
//-----------------------------------------------------------------------------------------------
/** Transform the dimensions contained in this box
* x' = x*scaling + offset.
* NON-RECURSIVE!
*
* @param scaling :: multiply each coordinate by this value.
* @param offset :: after multiplying, add this offset.
*/
TMDE(void MDGridBox)::transformDimensions(std::vector<double> &scaling,
std::vector<double> &offset) {
MDBoxBase<MDE, nd>::transformDimensions(scaling, offset);
this->computeSizesFromSplit();
}
//-----------------------------------------------------------------------------------------------
/** Compute some data from the split[] array and the extents.
*
* @return :: the total number of boxes */
TMDE(size_t MDGridBox)::computeSizesFromSplit() {
// Do some computation based on how many splits per each dim.
size_t tot = 1;
double diagSum(0);
for (size_t d = 0; d < nd; d++) {
// Cumulative multiplier, for indexing
splitCumul[d] = tot;
tot *= split[d];
// Length of the side of a box in this dimension
m_SubBoxSize[d] =
double(this->extents[d].getSize()) / static_cast<double>(split[d]);
// Accumulate the squared diagonal length.
diagSum += m_SubBoxSize[d] * m_SubBoxSize[d];
}
diagonalSquared = static_cast<coord_t>(diagSum);
return tot;
}
//-----------------------------------------------------------------------------------------------
/// Destructor
TMDE(MDGridBox)::~MDGridBox() {
// Delete all contained boxes (this should fire the MDGridBox destructors
// recursively).
auto it = m_Children.begin();
for (; it != m_Children.end(); ++it)
delete *it;
m_Children.clear();
}
//-----------------------------------------------------------------------------------------------
/** Clear any points contained. */
TMDE(void MDGridBox)::clear() {
this->m_signal = 0.0;
this->m_errorSquared = 0.0;
auto it = m_Children.begin();
for (; it != m_Children.end(); ++it) {
(*it)->clear();
}
}
//-----------------------------------------------------------------------------------------------
/** Returns the number of dimensions in this box */
TMDE(size_t MDGridBox)::getNumDims() const { return nd; }
//-----------------------------------------------------------------------------------------------
/// Recursiveluy calculates the amount of the data located in memory. Slow
TMDE(size_t MDGridBox)::getDataInMemorySize() const {
size_t nPoints(0);
for (size_t i = 0; i < numBoxes; i++)
nPoints += m_Children[i]->getDataInMemorySize();
return nPoints;
}
//-----------------------------------------------------------------------------------------------
/** Returns the number of un-split MDBoxes in this box (recursively including
* all children)
* @return :: the total # of MDBoxes in all children */
TMDE(size_t MDGridBox)::getNumMDBoxes() const {
size_t total = 0;
auto it = m_Children.begin();
for (; it != m_Children.end(); ++it) {
total += (*it)->getNumMDBoxes();
}
return total;
}
//-----------------------------------------------------------------------------------------------
/** @return The number of children of the MDGridBox, not recursively
*/
TMDE(size_t MDGridBox)::getNumChildren() const { return numBoxes; }
//-----------------------------------------------------------------------------------------------
/** Get a child box
* @param index :: index into the array, within range 0..getNumChildren()-1
* @return the child MDBoxBase pointer.
*/
TMDE(API::IMDNode *MDGridBox)::getChild(size_t index) {
return m_Children[index];
}
//-----------------------------------------------------------------------------------------------
/** Directly set the children of the MDGridBox. Used in file loading.
* Should not be called on a box with children; the existing children are NOT
*deleted.
*
* @param otherBoxes:: reference to a vector of boxes containing the children
* @param indexStart :: start point in the vector
* @param indexEnd :: end point in the vector, not-inclusive
*/
TMDE(void MDGridBox)::setChildren(const std::vector<API::IMDNode *> &otherBoxes,
const size_t indexStart,
const size_t indexEnd) {
m_Children.clear();
m_Children.reserve(indexEnd - indexStart + 1);
auto it = otherBoxes.begin() + indexStart;
auto it_end = otherBoxes.begin() + indexEnd;
// Set the parent of each new child box.
for (; it != it_end; it++) {
m_Children.push_back(dynamic_cast<MDBoxBase<MDE, nd> *>(*it));
m_Children.back()->setParent(this);
}
numBoxes = m_Children.size();
}
//-----------------------------------------------------------------------------------------------
/** Helper function to get the index into the linear array given
* an array of indices for each dimension (0 to nd)
* @param indices :: array of size[nd]
* @return size_t index into m_Children[].
*/
TMDE(inline size_t MDGridBox)::getLinearIndex(size_t *indices) const {
size_t out_linear_index = 0;
for (size_t d = 0; d < nd; d++)
out_linear_index += (indices[d] * splitCumul[d]);
return out_linear_index;
}
//-----------------------------------------------------------------------------------------------
/** Refresh the cache of nPoints, signal and error,
* by adding up all boxes (recursively).
* MDBoxes' totals are used directly.
*
* @param ts :: ThreadScheduler pointer to perform the caching
* in parallel. If NULL, it will be performed in series.
*/
TMDE(void MDGridBox)::refreshCache(ThreadScheduler *ts) {
// Clear your total
nPoints = 0;
this->m_signal = 0;
this->m_errorSquared = 0;
this->m_totalWeight = 0;
typename boxVector_t::iterator it;
typename boxVector_t::iterator it_end = m_Children.end();
if (!ts) {
//--------- Serial -----------
for (it = m_Children.begin(); it != it_end; ++it) {
MDBoxBase<MDE, nd> *ibox = *it;
// Refresh the cache (does nothing for MDBox)
ibox->refreshCache();
// Add up what's in there
nPoints += ibox->getNPoints();
this->m_signal += ibox->getSignal();
this->m_errorSquared += ibox->getErrorSquared();
this->m_totalWeight += ibox->getTotalWeight();
}
} else {
//---------- Parallel refresh --------------
throw std::runtime_error("Not implemented");
}
}
//-----------------------------------------------------------------------------------------------
/** Allocate and return a vector with a copy of all events contained
*/
TMDE(std::vector<MDE> *MDGridBox)::getEventsCopy() {
std::vector<MDE> *out = new std::vector<MDE>();
// Make the copy
// out->insert(out->begin(), data.begin(), data.end());
return out;
}
//-----------------------------------------------------------------------------------------------
/** Return all boxes contained within.
*
* @param outBoxes :: vector to fill
* @param maxDepth :: max depth value of the returned boxes.
* @param leafOnly :: if true, only add the boxes that are no more subdivided
*(leaves on the tree)
*/
TMDE(void MDGridBox)::getBoxes(std::vector<API::IMDNode *> &outBoxes,
size_t maxDepth, bool leafOnly) {
// Add this box, unless we only want the leaves
if (!leafOnly)
outBoxes.push_back(this);
if (this->getDepth() + 1 <= maxDepth) {
for (size_t i = 0; i < numBoxes; i++) {
// Recursively go deeper, if needed
m_Children[i]->getBoxes(outBoxes, maxDepth, leafOnly);
}
} else {
// Oh, we reached the max depth and want only leaves.
// ... so we consider this box to be a leaf too.
if (leafOnly)
outBoxes.push_back(this);
}
}
//-----------------------------------------------------------------------------------------------
/** Return all boxes contained within, limited by an implicit function.
*
* This method evaluates each vertex to see how it is contained by the implicit
*function.
* For example, if there are 4x4 boxes, there are 5x5 vertices to evaluate.
*
* All boxes that might be touching the implicit function are returned
*(including ones that
* overlap without any point actually in the function).
*
* @param outBoxes :: vector to fill
* @param maxDepth :: max depth value of the returned boxes.
* @param leafOnly :: if true, only add the boxes that are no more subdivided
*(leaves on the tree)
* @param function :: implicitFunction pointer
*/
TMDE(void MDGridBox)::getBoxes(std::vector<API::IMDNode *> &outBoxes,
size_t maxDepth, bool leafOnly,
Mantid::Geometry::MDImplicitFunction *function) {
// Add this box, unless we only want the leaves
if (!leafOnly)
outBoxes.push_back(this);
if (this->getDepth() + 1 <= maxDepth) {
// OK, let's look for children that are either touching or completely
// contained by the implicit function.
// The number of vertices in each dimension is the # split[d] + 1
size_t vertices_max[nd];
Utils::NestedForLoop::SetUp(nd, vertices_max, 0);
// Total number of vertices for all the boxes
size_t numVertices = 1;
for (size_t d = 0; d < nd; ++d) {
vertices_max[d] = split[d] + 1;
numVertices *= vertices_max[d];
}
// The function is limited by this many planes
size_t numPlanes = function->getNumPlanes();
// This array will hold whether each vertex is contained by each plane.
bool *vertexContained = new bool[numVertices * numPlanes];
// The index to the vertex in each dimension
size_t vertexIndex[nd];
Utils::NestedForLoop::SetUp(nd, vertexIndex, 0);
// To get indexes in the array of vertexes
size_t vertexIndexMaker[nd];
Utils::NestedForLoop::SetUpIndexMaker(nd, vertexIndexMaker, vertices_max);
// To get indexes in the array of BOXES
size_t boxIndexMaker[nd];
Utils::NestedForLoop::SetUpIndexMaker(nd, boxIndexMaker, split);
size_t linearVertexIndex = 0;
for (linearVertexIndex = 0; linearVertexIndex < numVertices;
linearVertexIndex++) {
// Get the nd-dimensional index
Utils::NestedForLoop::GetIndicesFromLinearIndex(
nd, linearVertexIndex, vertexIndexMaker, vertices_max, vertexIndex);
// Coordinates of this vertex
coord_t vertexCoord[nd];
for (size_t d = 0; d < nd; ++d)
vertexCoord[d] = this->extents[d].getMin() +
coord_t(double(vertexIndex[d]) * m_SubBoxSize[d]);
// Now check each plane to see if the vertex is bounded by it
for (size_t p = 0; p < numPlanes; p++) {
// Save whether this vertex is contained by this plane
vertexContained[p * numVertices + linearVertexIndex] =
function->getPlane(p).isPointBounded(vertexCoord);
}
}
// OK, now we have an array saying which vertex is contained by which plane.
// This is the number of vertices for each box, e.g. 8 in 3D
size_t verticesPerBox = 1 << nd;
/* There is a fixed relationship betwen a vertex (in a linear index) and its
* neighbors for a given box. This array calculates this: */
size_t *vertexNeighborsOffsets = new size_t[verticesPerBox];
for (size_t i = 0; i < verticesPerBox; i++) {
// Index (in n-dimensions) of this neighbor)
size_t vertIndex[nd];
for (size_t d = 0; d < nd; d++) {
vertIndex[d] = 0;
// Use a bit mask to iterate through the 2^nd neighbor options
size_t mask = 1 << d;
if (i & mask)
vertIndex[d] = 1;
}
size_t linIndex =
Utils::NestedForLoop::GetLinearIndex(nd, vertIndex, vertexIndexMaker);
vertexNeighborsOffsets[i] = linIndex;
}
// Go through all the boxes
size_t boxIndex[nd];
Utils::NestedForLoop::SetUp(nd, boxIndex, 0);
bool allDone = false;
while (!allDone) {
// Find the linear index into the BOXES array.
size_t boxLinearIndex =
Utils::NestedForLoop::GetLinearIndex(nd, boxIndex, boxIndexMaker);
API::IMDNode *box = m_Children[boxLinearIndex];
// std::cout << "Box at " << Strings::join(boxIndex, boxIndex+nd,
// ", ")
// << " (" << box->getExtentsStr() << ") ";
// Find the linear index of the upper left vertex of the box.
// (note that we're using the VERTEX index maker to find the linear index
// in that LARGER array)
size_t vertLinearIndex =
Utils::NestedForLoop::GetLinearIndex(nd, boxIndex, vertexIndexMaker);
// OK, now its time to see if the box is touching or contained or out of
// it.
// Recall that:
// - if a plane has NO vertices, then the box DOES NOT TOUCH
// - if EVERY plane has EVERY vertex, then the box is CONTAINED
// - if EVERY plane has at least one vertex, then the box is TOUCHING
size_t numPlanesWithAllVertexes = 0;
bool boxIsNotTouching = false;
// Go plane by plane
for (size_t p = 0; p < numPlanes; p++) {
size_t numVertexesInThisPlane = 0;
// Evaluate the 2^nd vertexes for this box.
for (size_t i = 0; i < verticesPerBox; i++) {
// (the index of the vertex is) = vertLinearIndex +
// vertexNeighborsOffsets[i]
if (vertexContained[p * numVertices + vertLinearIndex +
vertexNeighborsOffsets[i]])
numVertexesInThisPlane++;
}
// Plane with no vertexes = NOT TOUCHING. You can exit now
if (numVertexesInThisPlane == 0) {
boxIsNotTouching = true;
break;
}
// Plane has all the vertexes
if (numVertexesInThisPlane == verticesPerBox)
numPlanesWithAllVertexes++;
} // (for each plane)
// Is there a chance that the box is contained?
if (!boxIsNotTouching) {
if (numPlanesWithAllVertexes == numPlanes) {
// All planes have all vertexes
// The box is FULLY CONTAINED
// So we can get ALL children and don't need to check the implicit
// function
box->getBoxes(outBoxes, maxDepth, leafOnly);
} else {
// There is a chance the box is touching. Keep checking with implicit
// functions
box->getBoxes(outBoxes, maxDepth, leafOnly, function);
}
} else {
// std::cout << " is not touching at all." << std::endl;
}
// Move on to the next box in the list
allDone = Utils::NestedForLoop::Increment(nd, boxIndex, split);
}
// Clean up.
delete[] vertexContained;
delete[] vertexNeighborsOffsets;
} // Not at max depth
else {
// Oh, we reached the max depth and want only leaves.
// ... so we consider this box to be a leaf too.
if (leafOnly)
outBoxes.push_back(this);
}
}
//-----------------------------------------------------------------------------------------------
/** Returns the lowest-level box at the given coordinates
* @param coords :: nd-sized array of the coordinate of the point to look at
* @return MDBoxBase pointer.
*/
template <typename MDE, size_t nd>
const API::IMDNode *MDGridBox<MDE, nd>::getBoxAtCoord(const coord_t *coords) {
size_t index = 0;
for (size_t d = 0; d < nd; d++) {
coord_t x = coords[d];
int i = int((x - this->extents[d].getMin()) / m_SubBoxSize[d]);
// NOTE: No bounds checking is done (for performance).
// Accumulate the index
index += (i * splitCumul[d]);
}
// Add it to the contained box
if (index < numBoxes) // avoid segfaults for floating point round-off errors.
return m_Children[index]->getBoxAtCoord(coords);
else
return NULL;
}
//-----------------------------------------------------------------------------------------------
/** Split a box that is contained in the GridBox, at the given index,
* into a MDGridBox.
*
* Thread-safe as long as 'index' is different for all threads.
*
* @param index :: index into the boxes vector.
* Warning: No bounds check is made, don't give stupid values!
* @param ts :: optional ThreadScheduler * that will be used to parallelize
* recursive splitting. Set to NULL for no recursive splitting.
*/
TMDE(void MDGridBox)::splitContents(size_t index, ThreadScheduler *ts) {
// You can only split it if it is a MDBox (not MDGridBox).
MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(m_Children[index]);
if (!box)
return;
// Track how many MDBoxes there are in the overall workspace
this->m_BoxController->trackNumBoxes(box->getDepth());
// Construct the grid box. This should take the object out of the disk MRU
MDGridBox<MDE, nd> *gridbox = new MDGridBox<MDE, nd>(box);
// Delete the old ungridded box
delete m_Children[index];
// And now we have a gridded box instead of a boring old regular box.
m_Children[index] = gridbox;
if (ts) {
// Create a task to split the newly created MDGridBox.
ts->push(new FunctionTask(
boost::bind(&MDGridBox<MDE, nd>::splitAllIfNeeded, &*gridbox, ts)));
} else {
gridbox->splitAllIfNeeded(NULL);
}
}
//-----------------------------------------------------------------------------------------------
/** Get the child index from its ID
*
* @param childId :: ID of the child you want
* @return the index into the children of this grid box; size_t(-1) if NOT
*found.
*/
TMDE(size_t MDGridBox)::getChildIndexFromID(size_t childId) const {
for (size_t index = 0; index < numBoxes; index++) {
if (m_Children[index]->getID() == childId)
return index;
}
return UNDEF_SIZET;
}
//-----------------------------------------------------------------------------------------------
/** Goes through all the sub-boxes and splits them if they contain
* enough events to be worth it.
*
* @param ts :: optional ThreadScheduler * that will be used to parallelize
* recursive splitting. Set to NULL to do it serially.
*/
TMDE(void MDGridBox)::splitAllIfNeeded(ThreadScheduler *ts) {
for (size_t i = 0; i < numBoxes; ++i) {
MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(m_Children[i]);
if (box) {
// Plain MD-Box. Does it need to be split?
if (this->m_BoxController->willSplit(box->getNPoints(),
box->getDepth())) {
// The MDBox needs to split into a grid box.
if (!ts) {
// ------ Perform split serially (no ThreadPool) ------
MDGridBox<MDE, nd> *gridBox = new MDGridBox<MDE, nd>(box);
// Track how many MDBoxes there are in the overall workspace
this->m_BoxController->trackNumBoxes(box->getDepth());
// Replace in the array
m_Children[i] = gridBox;
// Delete the old box
delete box;
// Now recursively check if this NEW grid box's contents should be
// split too
gridBox->splitAllIfNeeded(NULL);
} else {
// ------ Perform split in parallel (using ThreadPool) ------
// So we create a task to split this MDBox,
// Task is : this->splitContents(i, ts);
ts->push(new FunctionTask(
boost::bind(&MDGridBox<MDE, nd>::splitContents, &*this, i, ts)));
}
} else {
// This box does NOT have enough events to be worth splitting, if it do
// have at least something in memory then,
Kernel::ISaveable *const pSaver(box->getISaveable());
if (pSaver && box->getDataInMemorySize() > 0) {
// Mark the box as "to-write" in DiskBuffer. If the buffer is full,
// the boxes will be dropped on disk
this->m_BoxController->getFileIO()->toWrite(pSaver);
}
}
} else {
// It should be a MDGridBox
MDGridBox<MDE, nd> *gridBox =
dynamic_cast<MDGridBox<MDE, nd> *>(m_Children[i]);
if (gridBox) {
// Now recursively check if this old grid box's contents should be split
// too
if (!ts || (this->nPoints <
this->m_BoxController->getAddingEvents_eventsPerTask()))
// Go serially if there are only a few points contained (less
// overhead).
gridBox->splitAllIfNeeded(ts);
else
// Go parallel if this is a big enough gridbox.
// Task is : gridBox->splitAllIfNeeded(ts);
ts->push(new FunctionTask(boost::bind(
&MDGridBox<MDE, nd>::splitAllIfNeeded, &*gridBox, ts)));
}
}
}
}
//-----------------------------------------------------------------------------------------------
/** Perform centerpoint binning of events, with bins defined
* in axes perpendicular to the axes of the workspace.
*
* @param bin :: MDBin object giving the limits of events to accept.
* @param fullyContained :: optional bool array sized [nd] of which dimensions
*are known to be fully contained (for MDSplitBox)
*/
TMDE(void MDGridBox)::centerpointBin(MDBin<MDE, nd> &bin,
bool *fullyContained) const {
// The MDBin ranges from index_min to index_max (inclusively) if each
// dimension. So
// we'll need to make nested loops from index_min[0] to index_max[0]; from
// index_min[1] to index_max[1]; etc.
int index_min[nd];
int index_max[nd];
// For running the nested loop, counters of each dimension. These are bounded
// by 0..split[d]
size_t counters_min[nd];
size_t counters_max[nd];
for (size_t d = 0; d < nd; d++) {
int min, max;
// The min index in this dimension (we round down - we'll include this edge)
if (bin.m_min[d] >= this->extents[d].getMin()) {
min = int((bin.m_min[d] - this->extents[d].getMin()) / m_SubBoxSize[d]);
counters_min[d] = min;
} else {
min = -1; // Goes past the edge
counters_min[d] = 0;
}
// If the minimum is bigger than the number of blocks in that dimension,
// then the bin is off completely in
// that dimension. There is nothing to integrate.
if (min >= static_cast<int>(split[d]))
return;
index_min[d] = min;
// The max index in this dimension (we round UP, but when we iterate we'll
// NOT include this edge)
if (bin.m_max[d] < this->extents[d].getMax()) {
max = int(ceil((bin.m_max[d] - this->extents[d].getMin()) /
m_SubBoxSize[d])) -
1;
counters_max[d] =
max + 1; // (the counter looping will NOT include counters_max[d])
} else {
max = int(split[d]); // Goes past THAT edge
counters_max[d] = max; // (the counter looping will NOT include max)
}
// If the max value is before the min, that means NOTHING is in the bin, and
// we can return
if ((max < min) || (max < 0))
return;
index_max[d] = max;
// std::cout << d << " from " << std::setw(5) << index_min[d] << " to " <<
// std::setw(5) << index_max[d] << "inc" << std::endl;
}
// If you reach here, than at least some of bin is overlapping this box
size_t counters[nd];
for (size_t d = 0; d < nd; d++)
counters[d] = counters_min[d];
bool allDone = false;
while (!allDone) {
size_t index = getLinearIndex(counters);
// std::cout << index << ": " << counters[0] << ", " << counters[1] <<
// std::endl;
// Find if the box is COMPLETELY held in the bin.
bool completelyWithin = true;
for (size_t dim = 0; dim < nd; dim++)
if ((static_cast<int>(counters[dim]) <= index_min[dim]) ||
(static_cast<int>(counters[dim]) >= index_max[dim])) {
// The index we are at is at the edge of the integrated area (index_min
// or index_max-1)
// That means that the bin only PARTIALLY covers this MDBox
completelyWithin = false;
break;
}
if (completelyWithin) {
// Box is completely in the bin.
// std::cout << "Box at index " << counters[0] << ", " << counters[1] << "
// is entirely contained.\n";
// Use the aggregated signal and error
bin.m_signal += m_Children[index]->getSignal();
bin.m_errorSquared += m_Children[index]->getErrorSquared();
} else {
// Perform the binning
m_Children[index]->centerpointBin(bin, fullyContained);
}
// Increment the counter(s) in the nested for loops.
allDone = Utils::NestedForLoop::Increment(nd, counters, counters_max,
counters_min);
}
}
// TMDE(
// void MDGridBox)::generalBin(MDBin<MDE,nd> & bin,
// Mantid::API::ImplicitFunction & function) const
// {
// // The MDBin ranges from index_min to index_max (inclusively) if each
// dimension. So
// // we'll need to make nested loops from index_min[0] to index_max[0]; from
// index_min[1] to index_max[1]; etc.
// int index_min[nd];
// int index_max[nd];
// // For running the nested loop, counters of each dimension. These are
// bounded by 0..split[d]
// size_t counters_min[nd];
// size_t counters_max[nd];
//
// for (size_t d=0; d<nd; d++)
// {
// int min,max;
//
// // The min index in this dimension (we round down - we'll include this
// edge)
// if (bin.m_min[d] >= this->extents[d].getMin())
// {
// min = int((bin.m_min[d] - this->extents[d].getMin()) / boxSize[d]);
// counters_min[d] = min;
// }
// else
// {
// min = -1; // Goes past the edge
// counters_min[d] = 0;
// }
//
// // If the minimum is bigger than the number of blocks in that dimension,
// then the bin is off completely in
// // that dimension. There is nothing to integrate.
// if (min >= static_cast<int>(split[d]))
// return;
// index_min[d] = min;
//
// // The max index in this dimension (we round UP, but when we iterate
// we'll NOT include this edge)
// if (bin.m_max[d] < this->extents[d].max)
// {
// max = int(ceil((bin.m_max[d] - this->extents[d].getMin()) /
// boxSize[d])) - 1;
// counters_max[d] = max+1; // (the counter looping will NOT include
// counters_max[d])
// }
// else
// {
// max = int(split[d]); // Goes past THAT edge
// counters_max[d] = max; // (the counter looping will NOT include max)
// }
//
// // If the max value is before the min, that means NOTHING is in the bin,
// and we can return
// if ((max < min) || (max < 0))
// return;
// index_max[d] = max;
//
// //std::cout << d << " from " << std::setw(5) << index_min[d] << " to "
// << std::setw(5) << index_max[d] << "inc" << std::endl;
// }
//
// // If you reach here, than at least some of bin is overlapping this box
//
//
// // We start by looking at the vertices at every corner of every box
// contained,
// // to see which boxes are partially contained/fully contained.
//
// // One entry with the # of vertices in this box contained; start at 0.
// size_t * verticesContained = new size_t[numBoxes];
// memset( verticesContained, 0, numBoxes * sizeof(size_t) );
//
// // Set to true if there is a possibility of the box at least partly
// touching the integration volume.
// bool * boxMightTouch = new bool[numBoxes];
// memset( boxMightTouch, 0, numBoxes * sizeof(bool) );
//
// // How many vertices does one box have? 2^nd, or bitwise shift left 1 by
// nd bits
// size_t maxVertices = 1 << nd;
//
// // The index to the vertex in each dimension
// size_t * vertexIndex = Utils::NestedForLoop::SetUp(nd, 0);
//
// // This is the index in each dimension at which we start looking at
// vertices
// size_t * vertices_min = Utils::NestedForLoop::SetUp(nd, 0);
// for (size_t d=0; d<nd; ++d)
// {
// vertices_min[d] = counters_min[d];
// vertexIndex[d] = vertices_min[d]; // This is where we start
// }
//
// // There is one more vertex in each dimension than there are boxes we are
// considering
// size_t * vertices_max = Utils::NestedForLoop::SetUp(nd, 0);
// for (size_t d=0; d<nd; ++d)
// vertices_max[d] = counters_max[d]+1;
//
// size_t * boxIndex = Utils::NestedForLoop::SetUp(nd, 0);
// size_t * indexMaker = Utils::NestedForLoop::SetUpIndexMaker(nd, split);
//
// bool allDone = false;
// while (!allDone)
// {
// // Coordinates of this vertex
// coord_t vertexCoord[nd];
// bool masks[nd];
// for (size_t d=0; d<nd; ++d)
// {
// vertexCoord[d] = double(vertexIndex[d]) * boxSize[d] +
// this->extents[d].getMin();
// masks[d] = false; //HACK ... assumes that all vertexes are used.
// }
// // Is this vertex contained?
// if (function.evaluate(vertexCoord, masks, nd))
// {
// // Yes, this vertex is contained within the integration volume!
//// std::cout << "vertex at " << vertexCoord[0] << ", " <<
/// vertexCoord[1] << ", " << vertexCoord[2] << " is contained\n";
//
// // This vertex is shared by up to 2^nd adjacent boxes (left-right
// along each dimension).
// for (size_t neighb=0; neighb<maxVertices; ++neighb)
// {
// // The index of the box is the same as the vertex, but maybe - 1 in
// each possible combination of dimensions
// bool badIndex = false;