Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TGeoParallelWorld.cxx
Go to the documentation of this file.
1// Author: Andrei Gheata 17/02/04
2
3/*************************************************************************
4 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11/** \class TGeoParallelWorld
12\ingroup Geometry_classes
13Base class for a flat parallel geometry.
14
15 The parallel geometry can be composed by both normal volumes added
16using the AddNode interface (not implemented yet) or by physical nodes
17which will use as position their actual global matrix with respect to the top
18volume of the main geometry.
19
20 All these nodes are added as daughters to the "top" volume of
21the parallel world which acts as a navigation helper in this parallel
22world. The parallel world has to be closed before calling any navigation
23method.
24*/
25
26#include "TGeoParallelWorld.h"
27#include "TObjString.h"
28#include "TGeoManager.h"
29#include "TGeoVolume.h"
30#include "TGeoVoxelFinder.h"
31#include "TGeoMatrix.h"
32#include "TGeoPhysicalNode.h"
33#include "TGeoNavigator.h"
34#include "TGeoBBox.h"
35#include "TGeoVoxelGrid.h"
36#include "TStopwatch.h"
37#include <iostream>
38#include <queue>
39#include <functional>
40#include <mutex>
41
42// this is for the bvh acceleration stuff
43#include <bvh2_third_party.h>
44#include <bvh2_extra_kernels.h>
45
46////////////////////////////////////////////////////////////////////////////////
47/// Default constructor
48
50 : TNamed(name, ""),
51 fGeoManager(mgr),
52 fPaths(new TObjArray(256)),
53 fUseOverlaps(kFALSE),
54 fIsClosed(kFALSE),
55 fVolume(nullptr),
56 fLastState(nullptr),
57 fPhysical(new TObjArray(256))
58{
59}
60
61////////////////////////////////////////////////////////////////////////////////
62/// Destructor
63
65{
66 if (fPhysical) {
68 delete fPhysical;
69 }
70 if (fPaths) {
71 fPaths->Delete();
72 delete fPaths;
73 }
74 delete fVolume;
75}
76
77////////////////////////////////////////////////////////////////////////////////
78/// Add a node normally to this world. Overlapping nodes not allowed
79
80void TGeoParallelWorld::AddNode(const char *path)
81{
82 if (fIsClosed)
83 Fatal("AddNode", "Cannot add nodes to a closed parallel geometry");
84 if (!fGeoManager->CheckPath(path)) {
85 Error("AddNode", "Path %s not valid.\nCannot add to parallel world!", path);
86 return;
87 }
88 fPaths->Add(new TObjString(path));
89}
90
91////////////////////////////////////////////////////////////////////////////////
92/// To use this optimization, the user should declare the full list of volumes
93/// which may overlap with any of the physical nodes of the parallel world. Better
94/// be done before misalignment
95
97{
98 if (activate)
101}
102
103////////////////////////////////////////////////////////////////////////////////
104/// To use this optimization, the user should declare the full list of volumes
105/// which may overlap with any of the physical nodes of the parallel world. Better
106/// be done before misalignment
107
109{
110 if (activate)
113 TGeoVolume *vol;
114 while ((vol = (TGeoVolume *)next())) {
115 if (!strcmp(vol->GetName(), volname))
117 }
118}
119
120////////////////////////////////////////////////////////////////////////////////
121/// Print the overlaps which were detected during real tracking
122
124{
126 TGeoVolume *vol;
127 Int_t noverlaps = 0;
128 while ((vol = (TGeoVolume *)next())) {
129 if (vol->IsOverlappingCandidate()) {
130 if (noverlaps == 0)
131 Info("PrintDetectedOverlaps", "List of detected volumes overlapping with the PW");
132 noverlaps++;
133 printf("volume: %s at index: %d\n", vol->GetName(), vol->GetNumber());
134 }
135 }
136 return noverlaps;
137}
138
139////////////////////////////////////////////////////////////////////////////////
140/// Reset overlapflag for all volumes in geometry
141
143{
145 TGeoVolume *vol;
146 while ((vol = (TGeoVolume *)next()))
148}
149
150////////////////////////////////////////////////////////////////////////////////
151/// The main geometry must be closed.
152
154{
155 if (fIsClosed)
156 return kTRUE;
157 if (!fGeoManager->IsClosed())
158 Fatal("CloseGeometry", "Main geometry must be closed first");
159 if (!fPaths || !fPaths->GetEntriesFast()) {
160 Error("CloseGeometry", "List of paths is empty");
161 return kFALSE;
162 }
165 Info("CloseGeometry", "Parallel world %s contains %d prioritised objects", GetName(), fPaths->GetEntriesFast());
166 Int_t novlp = 0;
168 TGeoVolume *vol;
169 while ((vol = (TGeoVolume *)next()))
170 if (vol->IsOverlappingCandidate())
171 novlp++;
172 Info("CloseGeometry", "Number of declared overlaps: %d", novlp);
173 if (fUseOverlaps)
174 Info("CloseGeometry", "Parallel world will use declared overlaps");
175 else
176 Info("CloseGeometry", "Parallel world will detect overlaps with other volumes");
177
178 Info("CloseGeometry", "Parallel world has %d volumes", fVolume->GetNdaughters());
179 return kTRUE;
180}
181
182////////////////////////////////////////////////////////////////////////////////
183/// Refresh the node pointers and re-voxelize. To be called mandatory in case
184/// re-alignment happened.
185
187{
188 delete fVolume;
191 // Loop physical nodes and add them to the navigation helper volume
192 if (fPhysical) {
193 fPhysical->Delete();
194 delete fPhysical;
195 }
199 TIter next(fPaths);
200 Int_t copy = 0;
201 while ((objs = (TObjString *)next())) {
202 pnode = new TGeoPhysicalNode(objs->GetName());
203 fPhysical->AddAt(pnode, copy);
204 fVolume->AddNode(pnode->GetVolume(), copy++, new TGeoHMatrix(*pnode->GetMatrix()));
205 }
206 // Voxelize the volume
209 timer.Start();
212 this->BuildBVH();
213 timer.Stop();
214 if (verboselevel > 2) {
215 Info("RefreshPhysicalNodes", "Initializing BVH took %f seconds", timer.RealTime());
216 }
217 }
219 timer.Start();
220 fVolume->Voxelize("ALL");
221 timer.Stop();
222 if (verboselevel > 2) {
223 Info("RefreshPhysicalNodes", "Voxelization took %f seconds", timer.RealTime());
224 }
225 }
226}
227
228////////////////////////////////////////////////////////////////////////////////
229/// Finds physical node containing the point.
230/// Uses BVH to do so. (Not the best algorithm since not O(1) but good enough.)
231/// An improved version could be implemented based on TGeoVoxelGrid caching.
232
234{
235 using namespace bvh::v2::extra;
236
237 if (!fIsClosed) {
238 Fatal("FindNode", "Parallel geometry must be closed first");
239 }
240
241 using Scalar = float;
244 using Bvh = bvh::v2::Bvh<Node>;
245
246 // let's fetch the bvh
247 auto mybvh = (Bvh *)fBVH;
248 assert(mybvh);
249
250 Vec3 testpoint(point[0], point[1], point[2]);
251
252 TGeoPhysicalNode *nextnode = nullptr;
253
254 // This index stores the smallest object_id that contains the point
255 // only relevant if there are overlaps within the parallel world.
256 // We introduce this to make sure that the BVH traversal here, gives the same
257 // result as a simple loop iteration in increasing object_id order.
258 size_t min_contained_object_id = std::numeric_limits<size_t>::max();
259
260 auto contains = [](const Node &node, const Vec3 &p) {
261 auto box = node.get_bbox();
262 auto min = box.min;
263 auto max = box.max;
264 return (p[0] >= min[0] && p[0] <= max[0]) && (p[1] >= min[1] && p[1] <= max[1]) &&
265 (p[2] >= min[2] && p[2] <= max[2]);
266 };
267
268 auto leaf_fn = [&](size_t begin, size_t end) {
269 for (size_t prim_id = begin; prim_id < end; ++prim_id) {
270 auto objectid = mybvh->prim_ids[prim_id];
271 if (min_contained_object_id == std::numeric_limits<size_t>::max() || objectid < min_contained_object_id) {
272 auto object = fVolume->GetNode(objectid);
273 Double_t lpoint[3] = {0};
274 object->MasterToLocal(point, lpoint);
275 if (object->GetVolume()->Contains(lpoint)) {
278 }
279 }
280 }
281 return false; // false == go on with search even after this leaf
282 };
283
284 auto root = mybvh->nodes[0];
285 // quick check against the root node
286 if (!contains(root, testpoint)) {
287 nextnode = nullptr;
288 } else {
290 constexpr bool earlyExit = false; // needed in overlapping cases, in which we prioritize smaller object indices
291 mybvh->traverse_top_down<earlyExit>(root.index, stack, leaf_fn, [&](const Node &left, const Node &right) {
292 bool follow_left = contains(left, testpoint);
293 bool follow_right = contains(right, testpoint);
294 return std::make_tuple(follow_left, follow_right, false);
295 });
296 }
297
298 if (nextnode) {
300 }
301 return nextnode;
302}
303
304////////////////////////////////////////////////////////////////////////////////
305/// Finds physical node containing the point
306/// (original version based on TGeoVoxelFinder)
307
309{
310 if (!fIsClosed)
311 Fatal("FindNode", "Parallel geometry must be closed first");
313 // Fast return if not in an overlapping candidate
315 Int_t id;
316 Int_t ncheck = 0;
318 // get the list of nodes passing thorough the current voxel
319 TGeoNodeCache *cache = nav->GetCache();
320 TGeoStateInfo &info = *cache->GetMakePWInfo(nd);
321 Int_t *check_list = voxels->GetCheckList(point, ncheck, info);
322 // cache->ReleaseInfo(); // no hierarchical use
323 if (!check_list)
324 return nullptr;
325 // loop all nodes in voxel
326 TGeoNode *node;
327 Double_t local[3];
328 for (id = 0; id < ncheck; id++) {
329 node = fVolume->GetNode(check_list[id]);
330 node->MasterToLocal(point, local);
331 if (node->GetVolume()->Contains(local)) {
332 // We found a node containing the point
334 return fLastState;
335 }
336 }
337 return nullptr;
338}
339
340///////////////////////////////////////////////////////////////////////////////////
341/// Finds physical node containing the point using simple algorithm (for debugging)
342
344{
345 if (!fIsClosed)
346 Fatal("FindNode", "Parallel geometry must be closed first");
348 for (int id = 0; id < nd; id++) {
349 Double_t local[3] = {0};
350 auto node = fVolume->GetNode(id);
351 node->MasterToLocal(point, local);
352 if (node->GetVolume()->Contains(local)) {
353 // We found a node containing the point
354 fLastState = (TGeoPhysicalNode *)fPhysical->At(node->GetNumber());
355 return fLastState;
356 }
357 }
358 return nullptr;
359}
360
361////////////////////////////////////////////////////////////////////////////////
362/// Prints the BVH
363
365{
366 using Scalar = float;
368 using Bvh = bvh::v2::Bvh<Node>;
369
370 // let's fetch the bvh
371 auto mybvh = (Bvh *)fBVH;
372
373 for (size_t i = 0; i < mybvh->nodes.size(); ++i) {
374 const auto &n = mybvh->nodes[i];
375 auto bbox = n.get_bbox();
376 auto min = bbox.min;
377 auto max = bbox.max;
378 long objectid = -1;
379 if (n.index.prim_count() > 0) {
380 objectid = mybvh->prim_ids[n.index.first_id()];
381 }
382 std::cout << "NODE id" << i << " "
383 << " prim_count: " << n.index.prim_count() << " first_id " << n.index.first_id() << " object_id "
384 << objectid << " ( " << min[0] << " , " << min[1] << " , " << min[2] << ")"
385 << " ( " << max[0] << " , " << max[1] << " , " << max[2] << ")"
386 << "\n";
387 }
388}
389
390////////////////////////////////////////////////////////////////////////////////
391/// Same functionality as TGeoNavigator::FindNextDaughterBoundary for the
392/// parallel world. Uses BVH to do so.
393
396{
397 if (!fIsClosed) {
398 Fatal("FindNextBoundary", "Parallel geometry must be closed first");
399 }
400
402 // Fast return if not in an overlapping candidate
403 if (fUseOverlaps && !nav->GetCurrentVolume()->IsOverlappingCandidate()) {
404 return nullptr;
405 }
406 // last touched physical node in the parallel geometry
408 return nullptr;
409 }
410
411 double local_step = stepmax; // we need this otherwise the lambda get's confused
412
413 using Scalar = float;
416 using Bvh = bvh::v2::Bvh<Node>;
418
419 // let's fetch the bvh
420 auto mybvh = (Bvh *)fBVH;
421 if (!mybvh) {
422 Error("FindNextBoundary", "Cannot perform safety; No BVH initialized");
423 return nullptr;
424 }
425
426 auto truncate_roundup = [](double orig) {
427 float epsilon = std::numeric_limits<float>::epsilon() * std::fabs(orig);
428 // Add the bias to x before assigning it to y
429 return static_cast<float>(orig + epsilon);
430 };
431
432 // let's do very quick checks against the top node
433 const auto topnode_bbox = mybvh->get_root().get_bbox();
434 if ((-point[0] + topnode_bbox.min[0]) > stepmax) {
435 step = stepmax;
436 return nullptr;
437 }
438 if ((-point[1] + topnode_bbox.min[1]) > stepmax) {
439 step = stepmax;
440 return nullptr;
441 }
442 if ((-point[2] + topnode_bbox.min[2]) > stepmax) {
443 step = stepmax;
444 return nullptr;
445 }
446 if ((point[0] - topnode_bbox.max[0]) > stepmax) {
447 step = stepmax;
448 return nullptr;
449 }
450 if ((point[1] - topnode_bbox.max[1]) > stepmax) {
451 step = stepmax;
452 return nullptr;
453 }
454 if ((point[2] - topnode_bbox.max[2]) > stepmax) {
455 step = stepmax;
456 return nullptr;
457 }
458
459 // the ray used for bvh interaction
460 Ray ray(Vec3(point[0], point[1], point[2]), // origin
461 Vec3(dir[0], dir[1], dir[2]), // direction
462 0.0f, // minimum distance
464
465 TGeoPhysicalNode *nextnode = nullptr;
466
467 static constexpr bool use_robust_traversal = true;
468
469 // Traverse the BVH and apply concrecte object intersection in BVH leafs
471 mybvh->intersect<false, use_robust_traversal>(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) {
472 for (size_t prim_id = begin; prim_id < end; ++prim_id) {
473 auto objectid = mybvh->prim_ids[prim_id];
474 auto object = fVolume->GetNode(objectid);
475
477 if (pnode->IsMatchingState(nav)) {
478 // Info("FOO", "Matching state return");
479 step = TGeoShape::Big();
480 nextnode = nullptr;
481 return true;
482 }
483 Double_t lpoint[3], ldir[3];
484 object->MasterToLocal(point, lpoint);
485 object->MasterToLocalVect(dir, ldir);
486 auto thisstep = object->GetVolume()->GetShape()->DistFromOutside(lpoint, ldir, 3, local_step);
487 if (thisstep < local_step) {
489 nextnode = pnode;
490 }
491 }
492 return false; // go on after this
493 });
494
495 // nothing hit
496 if (!nextnode) {
498 }
499 step = local_step;
500 return nextnode;
501}
502
503////////////////////////////////////////////////////////////////////////////////
504/// Same functionality as TGeoNavigator::FindNextDaughterBoundary for the
505/// parallel world.
506/// (original version based on TGeoVoxelFinder)
507
510{
511 if (!fIsClosed)
512 Fatal("FindNextBoundary", "Parallel geometry must be closed first");
513 TGeoPhysicalNode *pnode = nullptr;
515 // Fast return if not in an overlapping candidate
516 if (fUseOverlaps && !nav->GetCurrentVolume()->IsOverlappingCandidate())
517 return nullptr;
518 // TIter next(fPhysical);
519 // Ignore the request if the current state in the main geometry matches the
520 // last touched physical node in the parallel geometry
522 return nullptr;
523 // while ((pnode = (TGeoPhysicalNode*)next())) {
524 // if (pnode->IsMatchingState(nav)) return 0;
525 // }
526 step = stepmax;
528 Int_t idaughter = -1; // nothing crossed
530 Int_t i;
531 TGeoNode *current;
532 Double_t lpoint[3], ldir[3];
533 // const Double_t tolerance = TGeoShape::Tolerance();
534 if (nd < 5) {
535 // loop over daughters
536 for (i = 0; i < nd; i++) {
537 current = fVolume->GetNode(i);
539 if (pnode->IsMatchingState(nav)) {
540 step = TGeoShape::Big();
541 return nullptr;
542 }
543 // validate only within stepmax
544 if (voxels->IsSafeVoxel(point, i, stepmax))
545 continue;
546 current->MasterToLocal(point, lpoint);
547 current->MasterToLocalVect(dir, ldir);
548 Double_t snext = current->GetVolume()->GetShape()->DistFromOutside(lpoint, ldir, 3, step);
549 if (snext < step) {
550 step = snext;
551 idaughter = i;
552 }
553 }
554 if (idaughter >= 0) {
556 return pnode;
557 }
558 step = TGeoShape::Big();
559 return nullptr;
560 }
561 // Get current voxel
562 Int_t ncheck = 0;
563 Int_t sumchecked = 0;
564 Int_t *vlist = nullptr;
565 TGeoNodeCache *cache = nav->GetCache();
566 TGeoStateInfo &info = *cache->GetMakePWInfo(nd);
567 // TGeoStateInfo &info = *cache->GetInfo();
568 // cache->ReleaseInfo(); // no hierarchical use
569 voxels->SortCrossedVoxels(point, dir, info);
570 while ((sumchecked < nd) && (vlist = voxels->GetNextVoxel(point, dir, ncheck, info))) {
571 for (i = 0; i < ncheck; i++) {
573 if (pnode->IsMatchingState(nav)) {
574 step = TGeoShape::Big();
575 return nullptr;
576 }
577 current = fVolume->GetNode(vlist[i]);
578 current->MasterToLocal(point, lpoint);
579 current->MasterToLocalVect(dir, ldir);
580 Double_t snext = current->GetVolume()->GetShape()->DistFromOutside(lpoint, ldir, 3, step);
581 if (snext < step - 1.E-8) {
582 step = snext;
583 idaughter = vlist[i];
584 }
585 }
586 if (idaughter >= 0) {
588 // mark the overlap
589 if (!fUseOverlaps && !nav->GetCurrentVolume()->IsOverlappingCandidate()) {
590 AddOverlap(nav->GetCurrentVolume(), kFALSE);
591 // printf("object %s overlapping with pn: %s\n", fGeoManager->GetPath(), pnode->GetName());
592 }
593 return pnode;
594 }
595 }
596 step = TGeoShape::Big();
597 return nullptr;
598}
599
600////////////////////////////////////////////////////////////////////////////////
601/// Same functionality as TGeoNavigator::FindNextDaughterBoundary for the
602/// parallel world in a trivial loop version (for debugging)
603
606{
607 if (!fIsClosed) {
608 Fatal("FindNextBoundary", "Parallel geometry must be closed first");
609 }
610
612 // Fast return if not in an overlapping candidate
613 if (fUseOverlaps && !nav->GetCurrentVolume()->IsOverlappingCandidate()) {
614 return nullptr;
615 }
616 // last touched physical node in the parallel geometry
618 return nullptr;
619 }
620
621 step = stepmax;
622 int nd = fVolume->GetNdaughters();
623 TGeoPhysicalNode *nextnode = nullptr;
624 for (int i = 0; i < nd; ++i) {
625 auto object = fVolume->GetNode(i);
626 // check actual distance/safety to object
627 Double_t lpoint[3], ldir[3];
628
629 object->MasterToLocal(point, lpoint);
630 object->MasterToLocalVect(dir, ldir);
631 auto thisstep = object->GetVolume()->GetShape()->DistFromOutside(lpoint, ldir, 3, step);
632 if (thisstep < step) {
633 step = thisstep;
635 }
636 }
637 // nothing hit
638 if (!nextnode) {
639 step = TGeoShape::Big();
640 }
641 return nextnode;
642}
643
644namespace {
645
646// Helper classes/structs used for priority queue - BVH traversal
647// structure keeping cost (value) for a BVH index
648struct BVHPrioElement {
649 size_t bvh_node_id;
650 float value;
651};
652
653// A priority queue for BVHPrioElement with an additional clear method
654// for quick reset
655template <typename Comparator>
656class BVHPrioQueue : public std::priority_queue<BVHPrioElement, std::vector<BVHPrioElement>, Comparator> {
657public:
658 using std::priority_queue<BVHPrioElement, std::vector<BVHPrioElement>,
659 Comparator>::priority_queue; // constructor inclusion
660
661 // convenience method to quickly clear/reset the queue (instead of having to pop one by one)
662 void clear() { this->c.clear(); }
663};
664
665} // namespace
666
667////////////////////////////////////////////////////////////////////////////////////////
668/// Method to find potentially relevant candidate bounding boxes for safety calculation
669/// given a point. Uses trivial algorithm to do so.
670
671std::pair<double, double>
672TGeoParallelWorld::GetLoopSafetyCandidates(double point[3], std::vector<int> &candidates, double margin) const
673{
674 using namespace bvh::v2::extra;
675 // Given a 3D point, returns the
676 // a) the min radius R such that there is at least one leaf bounding box fully enclosed
677 // in the sphere of radius R around point + the smallest squared safety
678 // b) the set of leaf bounding boxes who partially lie within radius + margin
679
680 using Scalar = float;
683 const auto bboxes_ptr = (std::vector<BBox> *)fBoundingBoxes;
684 auto &bboxes = (*bboxes_ptr);
685
686 auto cmp = [](BVHPrioElement a, BVHPrioElement b) { return a.value > b.value; };
687 static thread_local BVHPrioQueue<decltype(cmp)> queue(cmp);
688 queue.clear();
689
690 // testpoint object in float for quick BVH interaction
691 Vec3 testpoint(point[0], point[1], point[2]);
692 float best_enclosing_R_sq = std::numeric_limits<float>::max();
693 for (size_t i = 0; i < bboxes.size(); ++i) {
694 const auto &thisbox = bboxes[i];
695 auto safety_sq = SafetySqToNode(thisbox, testpoint);
696 const auto this_R_max_sq = RmaxSqToNode(thisbox, testpoint);
697 const auto inside = contains(thisbox, testpoint);
698 safety_sq = inside ? -1.f : safety_sq;
700 queue.emplace(BVHPrioElement{i, safety_sq});
701 }
702
703 // now we know the best enclosing R
704 // **and** we can fill the candidate set from the priority queue easily
705 float safety_sq_at_least = -1.f;
706
707 // final transform in order to take into account margin
708 if (margin != 0.) {
709 float best_enclosing_R = std::sqrt(best_enclosing_R_sq) + margin;
711 }
712
713 if (queue.size() > 0) {
714 auto el = queue.top();
715 queue.pop();
716 safety_sq_at_least = el.value; // safety_sq;
717 while (el.value /*safety_sq*/ < best_enclosing_R_sq) {
718 candidates.emplace_back(el.bvh_node_id);
719 if (queue.size() > 0) {
720 el = queue.top();
721 queue.pop();
722 } else {
723 break;
724 }
725 }
726 }
727 return std::make_pair<double, double>(best_enclosing_R_sq, safety_sq_at_least);
728}
729
730////////////////////////////////////////////////////////////////////////////////////////
731/// Method to find potentially relevant candidate bounding boxes for safety calculation
732/// given a point. Uses BVH to do so.
733
734std::pair<double, double>
735TGeoParallelWorld::GetBVHSafetyCandidates(double point[3], std::vector<int> &candidates, double margin) const
736{
737 // Given a 3D point, returns the
738 // a) the min radius R such that there is at least one leaf bounding box fully enclosed
739 // in the sphere of radius R around point
740 // b) the set of leaf bounding boxes who partially lie within radius + margin
741
742 using namespace bvh::v2::extra;
743 using Scalar = float;
746 using Bvh = bvh::v2::Bvh<Node>;
748 // let's fetch the primitive bounding boxes
749 const auto bboxes = (std::vector<BBox> *)fBoundingBoxes;
750 // let's fetch the bvh
751 auto mybvh = (Bvh *)fBVH;
752
753 // testpoint object in float for quick BVH interaction
754 Vec3 testpoint(point[0], point[1], point[2]);
755
756 // comparator bringing out "smallest" value on top
757 auto cmp = [](BVHPrioElement a, BVHPrioElement b) { return a.value > b.value; };
758 static thread_local BVHPrioQueue<decltype(cmp)> queue(cmp);
759 queue.clear();
760 static thread_local BVHPrioQueue<decltype(cmp)> leaf_queue(cmp);
761 leaf_queue.clear();
762
763 auto currnode = mybvh->nodes[0]; // we start from the top BVH node
764 // algorithm is based on standard iterative tree traversal with priority queues
765 float best_enclosing_R_sq = std::numeric_limits<float>::max();
766 float best_enclosing_R_with_margin_sq = std::numeric_limits<float>::max();
767 float current_safety_sq = 0.f;
768 do {
769 if (currnode.is_leaf()) {
770 // we are in a leaf node and actually talk to primitives
771 const auto begin_prim_id = currnode.index.first_id();
772 const auto end_prim_id = begin_prim_id + currnode.index.prim_count();
773 for (auto p_id = begin_prim_id; p_id < end_prim_id; p_id++) {
774 const auto object_id = mybvh->prim_ids[p_id];
775 //
776 // fetch leaf_bounding box
777 const auto &leaf_bounding_box = (*bboxes)[object_id];
778 auto this_Rmax_sq = RmaxSqToNode(leaf_bounding_box, testpoint);
779 const bool inside = contains(leaf_bounding_box, testpoint);
780 const auto safety_sq = inside ? -1.f : SafetySqToNode(leaf_bounding_box, testpoint);
781
782 // update best Rmin
785 const auto this_Rmax = std::sqrt(this_Rmax_sq);
786 best_enclosing_R_with_margin_sq = (this_Rmax + margin) * (this_Rmax + margin);
787 }
788
789 // best_enclosing_R_sq = std::min(best_enclosing_R_sq, this_Rmax_sq);
791 // update the priority queue of leaf bounding boxes
792 leaf_queue.emplace(BVHPrioElement{object_id, safety_sq});
793 }
794 }
795 } else {
796 // not a leave node ... for further traversal,
797 // we inject the children into priority queue based on distance to it's bounding box
798 const auto leftchild_id = currnode.index.first_id();
799 const auto rightchild_id = leftchild_id + 1;
800
801 for (size_t childid : {leftchild_id, rightchild_id}) {
802 if (childid >= mybvh->nodes.size()) {
803 continue;
804 }
805 const auto &node = mybvh->nodes[childid];
806 const auto &thisbbox = node.get_bbox();
807 auto inside = contains(thisbbox, testpoint);
808 const auto this_safety_sq = inside ? -1.f : SafetySqToNode(thisbbox, testpoint);
810 // this should be further considered
811 queue.push(BVHPrioElement{childid, this_safety_sq});
812 }
813 }
814 }
815
816 if (queue.size() > 0) {
817 auto currElement = queue.top();
818 currnode = mybvh->nodes[currElement.bvh_node_id];
820 queue.pop();
821 } else {
822 break;
823 }
825
826 // now we know the best enclosing R
827 // **and** we can fill the candidate set from the leaf priority queue easily
828 float safety_sq_at_least = -1.f;
829 if (leaf_queue.size() > 0) {
830 auto el = leaf_queue.top();
831 leaf_queue.pop();
832 safety_sq_at_least = el.value;
833 while (el.value < best_enclosing_R_with_margin_sq) {
834 candidates.emplace_back(el.bvh_node_id);
835 if (leaf_queue.size() > 0) {
836 el = leaf_queue.top();
837 leaf_queue.pop();
838 } else {
839 break;
840 }
841 }
842 }
843 return std::make_pair<double, double>(best_enclosing_R_sq, safety_sq_at_least);
844}
845
846////////////////////////////////////////////////////////////////////////////////
847/// Method to initialize the safety voxel at a specific 3D voxel (grid) index
848///
849
851{
852 static std::mutex g_mutex;
853 // this function modifies cache state ---> make writing thread-safe
854 const std::lock_guard<std::mutex> lock(g_mutex);
855
856 // determine voxel midpoint
857 const auto [mpx, mpy, mpz] = fSafetyVoxelCache->getVoxelMidpoint(vi);
858 static std::vector<int> candidates;
859 candidates.clear();
860 double point[3] = {mpx, mpy, mpz};
862 GetBVHSafetyCandidates(point, candidates, fSafetyVoxelCache->getDiagonalLength());
863
864 // cache information
865 auto voxelinfo = fSafetyVoxelCache->at(vi);
866 voxelinfo->min_safety = std::sqrt(min_safety_sq);
867 voxelinfo->idx_start = fSafetyCandidateStore.size();
868 voxelinfo->num_candidates = candidates.size();
869
870 // update flat candidate store
871 std::copy(candidates.begin(), candidates.end(), std::back_inserter(fSafetyCandidateStore));
872}
873
874////////////////////////////////////////////////////////////////////////////////
875/// Compute safety for the parallel world
876/// used BVH structure with addiditional on-the-fly 3D grid/voxel caching ---> essentially an O(1) algorithm !)
877
878double TGeoParallelWorld::VoxelSafety(double point[3], double safe_max)
879{
880 // (a): Very fast checks against top box and global caching
881 // (b): Use voxel cached best safety
882 // (c): Use voxel candidates (fetch them if they are not yet initialized)
883
885 // Fast return if the state matches the last one recorded
886
888 return TGeoShape::Big();
889 }
890
891 // Fast return if not in an overlapping candidate
892 if (fUseOverlaps && !nav->GetCurrentVolume()->IsOverlappingCandidate()) {
893 return TGeoShape::Big();
894 }
895
896 // let's determine the voxel indices
897 TGeoVoxelGridIndex vi = fSafetyVoxelCache->pointToVoxelIndex((float)point[0], (float)point[1], (float)point[2]);
898 if (!vi.isValid()) {
899 return SafetyBVH(point, safe_max);
900 }
901
902 auto &voxelinfo = fSafetyVoxelCache->fGrid[vi.idx];
903 double bestsafety = safe_max;
904
905 if (!voxelinfo.isInitialized()) {
906 // initialize the cache at this voxel
908 }
909
910 if (voxelinfo.min_safety > 0) {
911 // Nothing to do if this is already much further away than safe_max (within the margin limits of the voxel)
912 if (voxelinfo.min_safety - fSafetyVoxelCache->getDiagonalLength() > safe_max) {
913 return safe_max;
914 }
915
916 // see if the precalculated (mid-point) safety value can be used
917 auto midpoint = fSafetyVoxelCache->getVoxelMidpoint(vi);
918 double r_sq = 0;
919 for (int i = 0; i < 3; ++i) {
920 const auto d = (point[i] - midpoint[i]);
921 r_sq += d * d;
922 }
923 if (r_sq < voxelinfo.min_safety * voxelinfo.min_safety) {
924 // std::cout << " Still within cached safety ... remaining safety would be " << ls_eval - std::sqrt(r) << "\n";
925 return voxelinfo.min_safety - std::sqrt(r_sq);
926 }
927 }
928
929 using namespace bvh::v2::extra;
930 using Scalar = float;
933 const auto bboxes_ptr = (std::vector<BBox> *)fBoundingBoxes;
934 auto &bboxes = (*bboxes_ptr);
935 Vec3 testpoint(point[0], point[1], point[2]);
936 // do the full calculation using all candidates here
937 for (size_t store_id = voxelinfo.idx_start; store_id < voxelinfo.idx_start + voxelinfo.num_candidates; ++store_id) {
938
940
941 // check against bounding box
942 const auto &bbox = bboxes[cand_id];
943 const auto bbox_safe_sq = SafetySqToNode(bbox, testpoint);
945 continue;
946 }
947
948 // check against actual geometry primitive-safety
949 const auto primitive_node = fVolume->GetNode(cand_id);
950 const auto thissafety = primitive_node->Safety(point, false);
951 if (thissafety < bestsafety) {
952 bestsafety = std::max(0., thissafety);
953 }
954 }
955 return bestsafety;
956}
957
958////////////////////////////////////////////////////////////////////////////////
959/// Compute safety for the parallel world
960/// (using pure BVH traversal, mainly for debugging/fallback since VoxelSafety should be faster)
961
962double TGeoParallelWorld::SafetyBVH(double point[3], double safe_max)
963{
965 // Fast return if the state matches the last one recorded
967 return TGeoShape::Big();
968 }
969 // Fast return if not in an overlapping candidate
970 if (fUseOverlaps && !nav->GetCurrentVolume()->IsOverlappingCandidate()) {
971 return TGeoShape::Big();
972 }
973
976
977 using namespace bvh::v2::extra;
978 using Scalar = float;
981 using Bvh = bvh::v2::Bvh<Node>;
982
983 // let's fetch the bvh
984 auto mybvh = (Bvh *)fBVH;
985
986 // testpoint object in float for quick BVH interaction
987 Vec3 testpoint(point[0], point[1], point[2]);
988
989 auto currnode = mybvh->nodes[0]; // we start from the top BVH node
990 // we do a quick check on the top node
991 bool outside_top = !contains(currnode.get_bbox(), testpoint);
992 const auto safety_sq_to_top = SafetySqToNode(currnode.get_bbox(), testpoint);
994 // the top node is already further away than our limit, so we can simply return the limit
995 return smallest_safety;
996 }
997
998 // comparator bringing out "smallest" value on top
999 auto cmp = [](BVHPrioElement a, BVHPrioElement b) { return a.value > b.value; };
1000 static thread_local BVHPrioQueue<decltype(cmp)> queue(cmp);
1001 queue.clear();
1002
1003 // algorithm is based on standard iterative tree traversal with priority queues
1005 do {
1006 if (currnode.is_leaf()) {
1007 // we are in a leaf node and actually talk to TGeo primitives
1008 const auto begin_prim_id = currnode.index.first_id();
1009 const auto end_prim_id = begin_prim_id + currnode.index.prim_count();
1010
1011 for (auto p_id = begin_prim_id; p_id < end_prim_id; p_id++) {
1012 const auto object_id = mybvh->prim_ids[p_id];
1013
1015 // Return if inside the current node
1016 if (pnode->IsMatchingState(nav)) {
1017 return TGeoShape::Big();
1018 }
1019
1020 auto object = fVolume->GetNode(object_id);
1021 // check actual distance/safety to object
1022 auto thissafety = object->Safety(point, false);
1023
1024 // this value (if not zero or negative) may be approximative but we know that it can actually not be smaller
1025 // than the relevant distance to the bounding box of the node!! Hence we can correct it.
1026
1027 // if (thissafety > 1E-10 && thissafety * thissafety < current_safety_to_node_sq) {
1028 // thissafety = std::max(thissafety, double(std::sqrt(current_safety_to_node_sq)));
1029 // }
1030
1032 smallest_safety = std::max(0., thissafety);
1034 }
1035 }
1036 } else {
1037 // not a leave node ... for further traversal,
1038 // we inject the children into priority queue based on distance to it's bounding box
1039 const auto leftchild_id = currnode.index.first_id();
1040 const auto rightchild_id = leftchild_id + 1;
1041
1042 for (size_t childid : {leftchild_id, rightchild_id}) {
1043 if (childid >= mybvh->nodes.size()) {
1044 continue;
1045 }
1046
1047 const auto &node = mybvh->nodes[childid];
1048 const auto inside = contains(node.get_bbox(), testpoint);
1049
1050 if (inside) {
1051 // this must be further considered because we are inside the bounding box
1052 queue.push(BVHPrioElement{childid, -1.});
1053 } else {
1054 auto safety_to_node_square = SafetySqToNode(node.get_bbox(), testpoint);
1056 // this should be further considered
1057 queue.push(BVHPrioElement{childid, safety_to_node_square});
1058 }
1059 }
1060 }
1061 }
1062
1063 if (queue.size() > 0) {
1064 auto currElement = queue.top();
1065 currnode = mybvh->nodes[currElement.bvh_node_id];
1067 queue.pop();
1068 } else {
1069 break;
1070 }
1072
1073 const auto s = std::max(0., double(smallest_safety));
1074 return s;
1075}
1076
1077////////////////////////////////////////////////////////////////////////////////
1078/// Compute safety for the parallel world
1079/// (original version based on TGeoVoxelFinder)
1080
1082{
1084 // Fast return if the state matches the last one recorded
1086 return TGeoShape::Big();
1087 // Fast return if not in an overlapping candidate
1088 if (fUseOverlaps && !nav->GetCurrentVolume()->IsOverlappingCandidate())
1089 return TGeoShape::Big();
1090 Double_t local[3];
1093 TGeoPhysicalNode *pnode = nullptr;
1095 Int_t nd = fVolume->GetNdaughters();
1096 TGeoNode *current;
1098 //---> check fast unsafe voxels
1099 Double_t *boxes = voxels->GetBoxes();
1100 for (Int_t id = 0; id < nd; id++) {
1101 Int_t ist = 6 * id;
1102 Double_t dxyz = 0.;
1103 Double_t dxyz0 = std::abs(point[0] - boxes[ist + 3]) - boxes[ist];
1104 if (dxyz0 > safe)
1105 continue;
1106 Double_t dxyz1 = std::abs(point[1] - boxes[ist + 4]) - boxes[ist + 1];
1107 if (dxyz1 > safe)
1108 continue;
1109 Double_t dxyz2 = std::abs(point[2] - boxes[ist + 5]) - boxes[ist + 2];
1110 if (dxyz2 > safe)
1111 continue;
1112 if (dxyz0 > 0)
1113 dxyz += dxyz0 * dxyz0;
1114 if (dxyz1 > 0)
1115 dxyz += dxyz1 * dxyz1;
1116 if (dxyz2 > 0)
1117 dxyz += dxyz2 * dxyz2;
1118 if (dxyz >= safe * safe)
1119 continue;
1120
1122 // Return if inside the current node
1123 if (pnode->IsMatchingState(nav)) {
1124 return TGeoShape::Big();
1125 }
1126
1127 current = fVolume->GetNode(id);
1128 current->MasterToLocal(point, local);
1129 // Safety to current node
1130 safnext = current->GetVolume()->GetShape()->Safety(local, kFALSE);
1131 if (safnext < tolerance)
1132 return 0.;
1133 if (safnext < safe)
1134 safe = safnext;
1135 }
1136 return safe;
1137}
1138
1139////////////////////////////////////////////////////////////////////////////////
1140/// Compute safety for the parallel world
1141/// (trivial loop version for comparison/debugging)
1142
1144{
1146 // Fast return if the state matches the last one recorded
1148 return TGeoShape::Big();
1149 // Fast return if not in an overlapping candidate
1150 if (fUseOverlaps && !nav->GetCurrentVolume()->IsOverlappingCandidate())
1151 return TGeoShape::Big();
1152
1153 Double_t local[3];
1157 Int_t nd = fVolume->GetNdaughters();
1158
1159 for (Int_t id = 0; id < nd; id++) {
1160 auto current = fVolume->GetNode(id);
1161 current->MasterToLocal(point, local);
1162 if (current->GetVolume()->GetShape()->Contains(local)) {
1163 safnext = 0.;
1164 } else {
1165 // Safety to current node
1166 safnext = current->GetVolume()->GetShape()->Safety(local, kFALSE);
1167 }
1168 if (safnext < tolerance) {
1169 return 0.;
1170 }
1171 if (safnext < safe) {
1172 safe = safnext;
1173 }
1174 }
1175 return safe;
1176}
1177
1178////////////////////////////////////////////////////////////////////////////////
1179/// Check overlaps within a tolerance value.
1180
1185
1186////////////////////////////////////////////////////////////////////////////////
1187/// Draw the parallel world
1188
1193
1194////////////////////////////////////////////////////////////////////////////////
1195/// Check/validate the BVH acceleration structure.
1196
1198{
1199 using Scalar = float;
1201 using Bvh = bvh::v2::Bvh<Node>;
1202 auto mybvh = (Bvh *)bvh;
1203
1204 size_t leaf_count = 0;
1205 std::function<bool(Node const &)> checkNode = [&](Node const &nde) -> bool {
1206 if (nde.is_leaf()) {
1207 leaf_count += nde.index.prim_count();
1208 return nde.index.prim_count() > 0;
1209 }
1210
1211 // we do it recursively
1212 auto thisbb = nde.get_bbox();
1213
1214 auto leftindex = nde.index.first_id();
1215 auto rightindex = leftindex + 1;
1216
1217 auto leftnode = mybvh->nodes[leftindex];
1218 auto rightnode = mybvh->nodes[rightindex];
1219
1220 auto leftbb = leftnode.get_bbox();
1221 auto rightbb = rightnode.get_bbox();
1222
1223 // both of these boxes must be contained in the bounding box
1224 // of the outer node
1225 auto tmi = thisbb.min;
1226 auto lmi = leftbb.min;
1227 auto rmi = rightbb.min;
1228
1229 auto check1 = lmi[0] >= tmi[0] && lmi[1] >= tmi[1] && lmi[2] >= tmi[2];
1230 auto check2 = rmi[0] >= tmi[0] && rmi[1] >= tmi[1] && rmi[2] >= tmi[2];
1231
1232 auto tma = thisbb.max;
1233 auto lma = leftbb.max;
1234 auto rma = rightbb.max;
1235
1236 auto check3 = lma[0] <= tma[0] && lma[1] <= tma[1] && lma[2] <= tma[2];
1237 auto check4 = rma[0] <= tma[0] && rma[1] <= tma[1] && rma[2] <= tma[2];
1238
1239 auto check = check1 && check2 && check3 && check4;
1240
1242 };
1243
1244 auto check = checkNode(mybvh->nodes[0]);
1246}
1247
1248////////////////////////////////////////////////////////////////////////////////
1249/// Build the BVH acceleration structure.
1250
1252{
1253 using Scalar = float;
1257 using Bvh = bvh::v2::Bvh<Node>;
1258
1259 auto DaughterToMother = [](TGeoNode const *node, const Double_t *local, Double_t *master) {
1260 TGeoMatrix *mat = node->GetMatrix();
1261 if (!mat) {
1262 memcpy(master, local, 3 * sizeof(Double_t));
1263 } else {
1264 mat->LocalToMaster(local, master);
1265 }
1266 };
1267
1268 // helper determining axis aligned bounding box from TGeoNode; code copied from the TGeoVoxelFinder
1269 auto GetBoundingBoxInMother = [DaughterToMother](TGeoNode const *node) {
1270 Double_t vert[24] = {0};
1271 Double_t pt[3] = {0};
1272 Double_t xyz[6] = {0};
1273 TGeoBBox *box = (TGeoBBox *)node->GetVolume()->GetShape();
1274 box->SetBoxPoints(&vert[0]);
1275 for (Int_t point = 0; point < 8; point++) {
1276 DaughterToMother(node, &vert[3 * point], &pt[0]);
1277 if (!point) {
1278 xyz[0] = xyz[1] = pt[0];
1279 xyz[2] = xyz[3] = pt[1];
1280 xyz[4] = xyz[5] = pt[2];
1281 continue;
1282 }
1283 for (Int_t j = 0; j < 3; j++) {
1284 if (pt[j] < xyz[2 * j]) {
1285 xyz[2 * j] = pt[j];
1286 }
1287 if (pt[j] > xyz[2 * j + 1]) {
1288 xyz[2 * j + 1] = pt[j];
1289 }
1290 }
1291 }
1292 BBox bbox;
1293 bbox.min[0] = std::min(xyz[1], xyz[0]) - 0.001f;
1294 bbox.min[1] = std::min(xyz[3], xyz[2]) - 0.001f;
1295 bbox.min[2] = std::min(xyz[5], xyz[4]) - 0.001f;
1296 bbox.max[0] = std::max(xyz[0], xyz[1]) + 0.001f;
1297 bbox.max[1] = std::max(xyz[2], xyz[3]) + 0.001f;
1298 bbox.max[2] = std::max(xyz[4], xyz[5]) + 0.001f;
1299 return bbox;
1300 };
1301
1302 // we need bounding boxes enclosing the primitives and centers of primitives
1303 // (replaced here by centers of bounding boxes) to build the bvh
1304 auto bboxes_ptr = new std::vector<BBox>();
1305 fBoundingBoxes = (void *)bboxes_ptr;
1306 auto &bboxes = *bboxes_ptr;
1307 std::vector<Vec3> centers;
1308
1309 int nd = fVolume->GetNdaughters();
1310 for (int i = 0; i < nd; ++i) {
1311 auto node = fVolume->GetNode(i);
1312 // fetch the bounding box of this node and add to the vector of bounding boxes
1313 (bboxes).push_back(GetBoundingBoxInMother(node));
1314 centers.emplace_back((bboxes).back().get_center());
1315 }
1316
1317 // check if some previous object is registered and delete if necessary
1318 if (fBVH) {
1319 delete (Bvh *)fBVH;
1320 fBVH = nullptr;
1321 }
1322
1323 // create the bvh
1326 auto bvh = bvh::v2::DefaultBuilder<Node>::build(bboxes, centers, config);
1327 auto bvhptr = new Bvh;
1328 *bvhptr = std::move(bvh); // copy structure
1329 fBVH = (void *)(bvhptr);
1330
1331 auto check = CheckBVH(fBVH, nd);
1332 if (!check) {
1333 Error("BuildBVH", "BVH corrupted\n");
1334 } else {
1335 Info("BuildBVH", "BVH good\n");
1336 }
1337
1338 // now instantiate the 3D voxel grid for caching the safety candidates
1339 // (note that the structure is merely reserved ... actual filling will happen on-the-fly later on)
1340 const auto &topBB = bvhptr->get_root().get_bbox();
1341 int N = std::cbrt(bboxes.size()) + 1;
1342 // std::cout << "3D Safety GRID cache: Determined N to be " << N << "\n";
1343 double Lx = (topBB.max[0] - topBB.min[0]) / N;
1344 double Ly = (topBB.max[1] - topBB.min[1]) / N;
1345 double Lz = (topBB.max[2] - topBB.min[2]) / N;
1346 // TODO: Instead of equal number of voxels in each direction, we
1347 // could impose equal "cubic" voxel size
1348
1349 if (fSafetyVoxelCache) {
1350 delete fSafetyVoxelCache;
1351 fSafetyCandidateStore.clear();
1352 }
1353
1354 fSafetyVoxelCache = new TGeoVoxelGrid<SafetyVoxelInfo>(topBB.min[0], topBB.min[1], topBB.min[2], topBB.max[0],
1355 topBB.max[1], topBB.max[2], Lx, Ly, Lz);
1356
1357 // TestVoxelGrid();
1358 return;
1359}
1360
1362{
1363 static bool done = false;
1364 if (done) {
1365 return;
1366 }
1367 done = true;
1368
1369 auto NX = fSafetyVoxelCache->getVoxelCountX();
1370 auto NY = fSafetyVoxelCache->getVoxelCountY();
1371 auto NZ = fSafetyVoxelCache->getVoxelCountZ();
1372
1373 std::vector<int> candidates1;
1374 std::vector<int> candidates2;
1375
1376 for (int i = 0; i < NX; ++i) {
1377 for (int j = 0; j < NY; ++j) {
1378 for (int k = 0; k < NZ; ++k) {
1379 size_t idx = fSafetyVoxelCache->index(i, j, k);
1380 TGeoVoxelGridIndex vi{i, j, k, idx};
1381
1382 // midpoint
1383 auto mp = fSafetyVoxelCache->getVoxelMidpoint(vi);
1384
1385 // let's do some tests
1386 candidates1.clear();
1387 candidates2.clear();
1388 double point[3] = {mp[0], mp[1], mp[2]};
1390 GetBVHSafetyCandidates(point, candidates1, fSafetyVoxelCache->getDiagonalLength());
1392 GetLoopSafetyCandidates(point, candidates2, fSafetyVoxelCache->getDiagonalLength());
1393 if (candidates1.size() != candidates2.size()) {
1394 std::cerr << " i " << i << " " << j << " " << k << " RMAX 2 (BVH) " << encl_Rmax_sq_1 << " CANDSIZE "
1395 << candidates1.size() << " RMAX (LOOP) " << encl_Rmax_sq_2 << " CANDSIZE "
1396 << candidates2.size() << "\n";
1397 }
1398
1399 // the candidate indices have to be the same
1400 bool same_test1 = true;
1401 for (auto &id : candidates1) {
1402 auto ok = std::find(candidates2.begin(), candidates2.end(), id) != candidates2.end();
1403 if (!ok) {
1404 same_test1 = false;
1405 break;
1406 }
1407 }
1408 bool same_test2 = true;
1409 for (auto &id : candidates2) {
1410 auto ok = std::find(candidates1.begin(), candidates1.end(), id) != candidates1.end();
1411 if (!ok) {
1412 same_test2 = false;
1413 break;
1414 }
1415 }
1416 if (!(same_test1 && same_test2)) {
1417 Error("VoxelTest", "Same test fails %d %d", same_test1, same_test2);
1418 }
1419 }
1420 }
1421 }
1422}
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
constexpr Bool_t kFALSE
Definition RtypesCore.h:108
constexpr Bool_t kTRUE
Definition RtypesCore.h:107
const char Option_t
Option string (const char)
Definition RtypesCore.h:80
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define N
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize id
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
char name[80]
Definition TGX11.cxx:110
const_iterator begin() const
const_iterator end() const
Box class.
Definition TGeoBBox.h:18
Matrix class used for computing global transformations Should NOT be used for node definition.
Definition TGeoMatrix.h:459
The manager class for any TGeo geometry.
Definition TGeoManager.h:46
TObjArray * GetListOfVolumes() const
TGeoNavigator * GetCurrentNavigator() const
Returns current navigator for the calling thread.
Bool_t IsClosed() const
Bool_t CheckPath(const char *path) const
Check if a geometry path is valid without changing the state of the current navigator.
static Int_t GetVerboseLevel()
Set verbosity level (static function).
Geometrical transformation package.
Definition TGeoMatrix.h:39
Class providing navigation API for TGeo geometries.
Special pool of reusable nodes.
Definition TGeoCache.h:56
TGeoStateInfo * GetMakePWInfo(Int_t nd)
Get the PW info, if none create one.
A node represent a volume positioned inside another.They store links to both volumes and to the TGeoM...
Definition TGeoNode.h:39
TGeoVolume * GetVolume() const
Definition TGeoNode.h:100
virtual TGeoMatrix * GetMatrix() const =0
Int_t GetNumber() const
Definition TGeoNode.h:94
virtual void MasterToLocal(const Double_t *master, Double_t *local) const
Convert the point coordinates from mother reference to local reference system.
Definition TGeoNode.cxx:696
virtual void MasterToLocalVect(const Double_t *master, Double_t *local) const
Convert a vector from mother reference to local reference system.
Definition TGeoNode.cxx:704
TGeoManager * fGeoManager
Double_t SafetyBVH(Double_t point[3], Double_t safmax=1.E30)
Compute safety for the parallel world (using pure BVH traversal, mainly for debugging/fallback since ...
std::pair< double, double > GetBVHSafetyCandidates(double point[3], std::vector< int > &candidates, double margin=0.) const
Method to find potentially relevant candidate bounding boxes for safety calculation given a point.
void Draw(Option_t *option) override
Draw the parallel world.
TObjArray * fPhysical
! array of physical nodes
TGeoPhysicalNode * FindNodeOrig(Double_t point[3])
Finds physical node containing the point (original version based on TGeoVoxelFinder)
Bool_t CloseGeometry()
The main geometry must be closed.
void AddNode(const char *path)
Add a node normally to this world. Overlapping nodes not allowed.
void ResetOverlaps() const
Reset overlapflag for all volumes in geometry.
std::vector< unsigned int > fSafetyCandidateStore
! stores bounding boxes serving a quick safety candidates (to be ! used with the VoxelGrid and Safety...
TGeoPhysicalNode * FindNodeBVH(Double_t point[3])
Finds physical node containing the point.
void * fBoundingBoxes
! to keep the vector of primitive axis aligned bounding boxes
TGeoPhysicalNode * FindNextBoundaryLoop(Double_t point[3], Double_t dir[3], Double_t &step, Double_t stepmax=1.E30)
Same functionality as TGeoNavigator::FindNextDaughterBoundary for the parallel world in a trivial loo...
TGeoVoxelGrid< SafetyVoxelInfo > * fSafetyVoxelCache
! A regular 3D cache layer for fast point-based safety lookups
bool CheckBVH(void *, size_t) const
Check/validate the BVH acceleration structure.
~TGeoParallelWorld() override
Destructor.
TGeoVolume * fVolume
! helper volume
Double_t SafetyLoop(Double_t point[3], Double_t safmax=1.E30)
Compute safety for the parallel world (trivial loop version for comparison/debugging)
Int_t PrintDetectedOverlaps() const
Print the overlaps which were detected during real tracking.
TGeoPhysicalNode * FindNextBoundaryBVH(Double_t point[3], Double_t dir[3], Double_t &step, Double_t stepmax=1.E30)
Same functionality as TGeoNavigator::FindNextDaughterBoundary for the parallel world.
void CheckOverlaps(Double_t ovlp=0.001)
Check overlaps within a tolerance value.
std::pair< double, double > GetLoopSafetyCandidates(double point[3], std::vector< int > &candidates, double margin=0.) const
Method to find potentially relevant candidate bounding boxes for safety calculation given a point.
TGeoPhysicalNode * fLastState
! Last PN touched
Double_t SafetyOrig(Double_t point[3], Double_t safmax=1.E30)
Compute safety for the parallel world (original version based on TGeoVoxelFinder)
void AddOverlap(TGeoVolume *vol, Bool_t activate=kTRUE)
To use this optimization, the user should declare the full list of volumes which may overlap with any...
void RefreshPhysicalNodes()
Refresh the node pointers and re-voxelize.
TGeoPhysicalNode * FindNodeLoop(Double_t point[3])
Finds physical node containing the point using simple algorithm (for debugging)
Bool_t fIsClosed
! Closed flag
AccelerationMode fAccMode
! switch between different algorithm implementations
void InitSafetyVoxel(TGeoVoxelGridIndex const &)
Method to initialize the safety voxel at a specific 3D voxel (grid) index.
void * fBVH
! BVH helper structure for safety and navigation
void BuildBVH()
Build the BVH acceleration structure.
Double_t VoxelSafety(Double_t point[3], Double_t safmax=1.E30)
Compute safety for the parallel world used BVH structure with addiditional on-the-fly 3D grid/voxel c...
TGeoPhysicalNode * FindNextBoundaryOrig(Double_t point[3], Double_t dir[3], Double_t &step, Double_t stepmax=1.E30)
Same functionality as TGeoNavigator::FindNextDaughterBoundary for the parallel world.
void PrintBVH() const
Prints the BVH.
Physical nodes are the actual 'touchable' objects in the geometry, representing a path of positioned ...
Bool_t IsMatchingState(TGeoNavigator *nav) const
Checks if a given navigator state matches this physical node.
static Double_t Big()
Definition TGeoShape.h:95
virtual Double_t Safety(const Double_t *point, Bool_t in=kTRUE) const =0
virtual Double_t DistFromOutside(const Double_t *point, const Double_t *dir, Int_t iact=1, Double_t step=TGeoShape::Big(), Double_t *safe=nullptr) const =0
virtual void ComputeBBox()=0
static Double_t Tolerance()
Definition TGeoShape.h:98
Volume assemblies.
Definition TGeoVolume.h:317
TGeoVolume, TGeoVolumeMulti, TGeoVolumeAssembly are the volume classes.
Definition TGeoVolume.h:43
void Voxelize(Option_t *option)
build the voxels for this volume
Bool_t Contains(const Double_t *point) const
Definition TGeoVolume.h:105
virtual TGeoNode * AddNode(TGeoVolume *vol, Int_t copy_no, TGeoMatrix *mat=nullptr, Option_t *option="")
Add a TGeoNode to the list of nodes.
void Draw(Option_t *option="") override
draw top volume according to option
Int_t GetNdaughters() const
Definition TGeoVolume.h:363
TGeoNode * GetNode(const char *name) const
get the pointer to a daughter node
TGeoVoxelFinder * GetVoxels() const
Getter for optimization structure.
Int_t GetNumber() const
Definition TGeoVolume.h:185
TGeoShape * GetShape() const
Definition TGeoVolume.h:191
void CheckOverlaps(Double_t ovlp=0.1, Option_t *option="")
Overlap checking tool. Check for illegal overlaps within a limit OVLP.
void SetOverlappingCandidate(Bool_t flag)
Definition TGeoVolume.h:229
Bool_t IsOverlappingCandidate() const
Definition TGeoVolume.h:149
Finder class handling voxels.
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
An array of TObjects.
Definition TObjArray.h:31
Int_t GetEntriesFast() const
Definition TObjArray.h:58
void AddAt(TObject *obj, Int_t idx) override
Add object at position ids.
void Delete(Option_t *option="") override
Remove all objects from the array AND delete all heap based objects.
TObject * At(Int_t idx) const override
Definition TObjArray.h:170
TObject * UncheckedAt(Int_t i) const
Definition TObjArray.h:90
TObject * Remove(TObject *obj) override
Remove object from array.
void Add(TObject *obj) override
Definition TObjArray.h:68
Collectable string class.
Definition TObjString.h:28
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1095
virtual void Fatal(const char *method, const char *msgfmt,...) const
Issue fatal error message.
Definition TObject.cxx:1123
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1069
Stopwatch class.
Definition TStopwatch.h:28
void Stop()
Stop the stopwatch.
This builder is only a wrapper around all the other builders, which selects the best builder dependin...
static BVH_ALWAYS_INLINE Bvh< Node > build(ThreadPool &thread_pool, std::span< const BBox > bboxes, std::span< const Vec > centers, const Config &config={})
Build a BVH in parallel using the given thread pool.
TPaveText * pt
void box(Int_t pat, Double_t x1, Double_t y1, Double_t x2, Double_t y2)
Definition fillpatterns.C:1
const Int_t n
Definition legend1.C:16
Definition bbox.h:9
Statefull info for the current geometry level.
Vec< T, N > max
Definition bbox.h:13
Vec< T, N > min
Definition bbox.h:13
Quality quality
The quality of the BVH produced by the builder.
Growing stack that can be used for BVH traversal.
Definition stack.h:34
Binary BVH node, containing its bounds and an index into its children or the primitives it contains.
Definition node.h:23
BVH_ALWAYS_INLINE BBox< T, Dim > get_bbox() const
Definition node.h:52