File indexing completed on 2026-09-19 09:40:14
0001
0002
0003
0004 #ifndef VECGEOM_BASE_BVH_H_
0005 #define VECGEOM_BASE_BVH_H_
0006
0007 #include "VecGeom/base/AABB.h"
0008 #include "VecGeom/base/Config.h"
0009 #include "VecGeom/base/Cuda.h"
0010 #include "VecGeom/navigation/NavStateIndex.h"
0011 #include "VecGeom/navigation/NavigationState.h"
0012 #include "VecGeom/volumes/LogicalVolume.h"
0013 #include "VecGeom/volumes/PlacedVolume.h"
0014
0015
0016 #include <vector>
0017
0018
0019 namespace vgbrep {
0020 template <typename Real_t>
0021 struct CPUsurfData;
0022 }
0023
0024 namespace vecgeom {
0025 namespace cuda {
0026 template <typename Real_t>
0027 class BVH;
0028 }
0029 VECGEOM_DEVICE_DECLARE_CONV_TEMPLATE(class, BVH, typename);
0030 inline namespace VECGEOM_IMPL_NAMESPACE {
0031
0032 class LogicalVolume;
0033 class VPlacedVolume;
0034
0035
0036
0037
0038
0039 template <typename Real_t>
0040 class BVH {
0041 private:
0042 uint fRootId = 0;
0043 int fRootNChild = 0;
0044 int fDepth = 0;
0045 int *fPrimId{nullptr};
0046 int *fOffset{nullptr};
0047 int *fNChild{nullptr};
0048 AABB<Real_t> *fNodes{nullptr};
0049 AABB<Real_t> *fAABBs{nullptr};
0050
0051 public:
0052
0053 BVH()
0054 : fRootId(0), fRootNChild(0), fDepth(0), fPrimId(nullptr), fOffset(nullptr), fNChild(nullptr), fNodes(nullptr),
0055 fAABBs(nullptr)
0056 {
0057 }
0058
0059 uint GetRootId() const { return fRootId; }
0060 int GetRootNChild() const { return fRootNChild; };
0061 int GetDepth() const { return fDepth; };
0062 const int *GetPrimId() const { return fPrimId; };
0063 const int *GetOffset() const { return fOffset; };
0064 const int *GetNChild() const { return fNChild; };
0065 const AABB<Real_t> *GetAABBs() const { return fAABBs; };
0066 const AABB<Real_t> *GetNodes() const { return fNodes; };
0067
0068
0069 static constexpr int BVH_MAX_DEPTH = 32;
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079 BVH(LogicalVolume const &volume, Vector3D<Precision> *ptrAABB, int nChild, int depth = 0);
0080
0081
0082 ~BVH() { Clear(); }
0083 void Clear();
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096 VECCORE_ATT_HOST_DEVICE
0097 void Set(int id, int nchild, int depth, int *dPrimId, vecgeom::AABB<Real_t> *dAABBs, int *dOffset, int *dNChild,
0098 vecgeom::AABB<Real_t> *dNodes)
0099 {
0100 fRootId = id;
0101 fRootNChild = nchild;
0102 fDepth = depth;
0103 SetPointers(dPrimId, dOffset, dNChild, dAABBs, dNodes);
0104 }
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114 VECCORE_ATT_HOST_DEVICE
0115 void SetPointers(int *dPrimId, int *dOffset, int *dNChild, AABB<Real_t> *dAABBs, AABB<Real_t> *dNodes)
0116 {
0117 fPrimId = dPrimId;
0118 fOffset = dOffset;
0119 fNChild = dNChild;
0120 fAABBs = dAABBs;
0121 fNodes = dNodes;
0122 }
0123
0124 #ifdef VECGEOM_ENABLE_CUDA
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135 VECCORE_ATT_DEVICE
0136 BVH(LogicalVolume const *volume, int depth, int *dPrimId, AABB<Real_t> *dAABBs, int *dOffset, int *NChild,
0137 AABB<Real_t> *dNodes);
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150 VECCORE_ATT_HOST_DEVICE
0151 BVH(int id, int nchild, int depth, int *dPrimId, vecgeom::AABB<Real_t> *dAABBs, int *dOffset, int *dNChild,
0152 vecgeom::AABB<Real_t> *dNodes)
0153 : fRootId(id), fRootNChild(nchild), fDepth(depth), fPrimId(dPrimId), fOffset(dOffset), fNChild(dNChild),
0154 fNodes(dNodes), fAABBs(dAABBs)
0155 {
0156 }
0157
0158 #endif
0159
0160 #ifdef VECGEOM_CUDA_INTERFACE
0161
0162 DevicePtr<cuda::BVH<Real_t>> CopyToGpu(void *addr) const;
0163 #endif
0164
0165
0166
0167
0168 VECCORE_ATT_HOST_DEVICE
0169 void Print(bool verbose = false) const;
0170
0171 uint GetAllocatedSize() const
0172 {
0173 uint nodes = (2 << fDepth) - 1;
0174 uint size{0};
0175
0176 size += fRootNChild * sizeof(vecgeom::AABB<Real_t>);
0177
0178 size += nodes * sizeof(vecgeom::AABB<Real_t>);
0179
0180 size += nodes * sizeof(int);
0181
0182 size += nodes * sizeof(int);
0183
0184 size += fRootNChild * sizeof(int);
0185 return size;
0186 }
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213 template <typename Navigator, typename Real_i>
0214 VECCORE_ATT_HOST_DEVICE void CheckDaughterIntersections(const Vector3D<Real_i> &localpoint,
0215 const Vector3D<Real_i> &localdir, Real_i &step,
0216 long const last_exited_id, long &hitcandidate_index) const
0217 {
0218 unsigned int stack[BVH_MAX_DEPTH], *ptr = &stack[1];
0219 stack[0] = 0;
0220
0221
0222 Vector3D<Real_t> binvdir(static_cast<Real_t>(1.0) / vecgeom::NonZero(localdir[0]),
0223 static_cast<Real_t>(1.0) / vecgeom::NonZero(localdir[1]),
0224 static_cast<Real_t>(1.0) / vecgeom::NonZero(localdir[2]));
0225 Vector3D<Real_t> blocalpoint(static_cast<Real_t>(localpoint[0]), static_cast<Real_t>(localpoint[1]),
0226 static_cast<Real_t>(localpoint[2]));
0227 Vector3D<Real_t> blocaldir(static_cast<Real_t>(localdir[0]), static_cast<Real_t>(localdir[1]),
0228 static_cast<Real_t>(localdir[2]));
0229 Real_t bstep = static_cast<Real_t>(step);
0230
0231 do {
0232 const unsigned int id = *--ptr;
0233
0234
0235 Real_t min{vecgeom::InfinityLength<Real_t>()}, max{-vecgeom::InfinityLength<Real_t>()};
0236 fNodes[id].ComputeIntersectionInvDir(blocalpoint, binvdir, min, max);
0237 if (min > max || max < Real_t{0.} || min >= step) {
0238 continue;
0239 }
0240
0241 if (fNChild[id] >= 0) {
0242
0243
0244 for (int i = 0; i < fNChild[id]; ++i) {
0245 const int prim = fPrimId[fOffset[id] + i];
0246 if (last_exited_id >= 0 && Navigator::SkipItem(fRootId, prim, last_exited_id)) continue;
0247
0248 Real_t approach;
0249 if (fAABBs[prim].IntersectInvDirApproach(blocalpoint, binvdir, bstep, approach)) {
0250 auto dist = Navigator::CandidateDistanceToIn(
0251 fRootId, prim, localpoint + static_cast<Real_i>(approach) * localdir, localdir, step);
0252
0253 dist += (dist > 0.) * static_cast<Real_i>(approach);
0254
0255 if (dist < step) {
0256 dist = vecCore::Max(dist, 0.);
0257 step = dist;
0258 bstep = static_cast<Real_t>(dist);
0259 hitcandidate_index = prim;
0260 }
0261 }
0262 }
0263 } else {
0264 const unsigned int childL = 2 * id + 1;
0265 const unsigned int childR = 2 * id + 2;
0266
0267
0268 Real_t tminL = vecgeom::InfinityLength<Real_t>(), tmaxL = -vecgeom::InfinityLength<Real_t>(),
0269 tminR = vecgeom::InfinityLength<Real_t>(), tmaxR = -vecgeom::InfinityLength<Real_t>();
0270
0271 fNodes[childL].ComputeIntersectionInvDir(blocalpoint, binvdir, tminL, tmaxL);
0272 fNodes[childR].ComputeIntersectionInvDir(blocalpoint, binvdir, tminR, tmaxR);
0273
0274 const bool traverseL = tminL <= tmaxL && tmaxL >= static_cast<Real_t>(0.0) && tminL < bstep;
0275 const bool traverseR = tminR <= tmaxR && tmaxR >= static_cast<Real_t>(0.0) && tminR < bstep;
0276
0277
0278
0279
0280
0281 if (tminR < tminL) {
0282 if (traverseL) *ptr++ = childL;
0283 if (traverseR) *ptr++ = childR;
0284 } else {
0285 if (traverseR) *ptr++ = childR;
0286 if (traverseL) *ptr++ = childL;
0287 }
0288 }
0289 } while (ptr > stack);
0290 }
0291
0292
0293
0294
0295
0296
0297
0298
0299
0300
0301
0302
0303 template <typename Navigator>
0304 VECCORE_ATT_HOST_DEVICE Precision ComputeSafety(Vector3D<Precision> localpoint, Precision safety,
0305 Precision limit = InfinityLength<Precision>()) const
0306 {
0307 unsigned int stack[BVH_MAX_DEPTH], *ptr = &stack[1];
0308 stack[0] = 0;
0309
0310 do {
0311 const unsigned int id = *--ptr;
0312
0313
0314 if (fNodes[id].Safety(localpoint) > safety) continue;
0315
0316 if (fNChild[id] >= 0) {
0317 for (int i = 0; i < fNChild[id]; ++i) {
0318 const int prim = fPrimId[fOffset[id] + i];
0319 if (fAABBs[prim].Safety(localpoint) < safety) {
0320 auto safety_leaf = fAABBs[prim].Safety(localpoint);
0321
0322 if (safety_leaf >= safety) continue;
0323
0324 if (safety_leaf > limit) {
0325 safety = safety_leaf;
0326 continue;
0327 }
0328 const Precision dist = Navigator::CandidateSafetyToIn(fRootId, prim, localpoint);
0329
0330
0331
0332 if (dist < safety) safety = dist;
0333
0334 }
0335 }
0336 } else {
0337 const unsigned int childL = 2 * id + 1;
0338 const unsigned int childR = 2 * id + 2;
0339
0340 const Real_t safetyL = fNodes[childL].Safety(localpoint);
0341 const Real_t safetyR = fNodes[childR].Safety(localpoint);
0342
0343 const bool traverseL = safetyL < safety;
0344 const bool traverseR = safetyR < safety;
0345
0346 if (safetyR < safetyL) {
0347 if (traverseR) *ptr++ = childR;
0348 if (traverseL) *ptr++ = childL;
0349 } else {
0350 if (traverseL) *ptr++ = childL;
0351 if (traverseR) *ptr++ = childR;
0352 }
0353 }
0354 } while (ptr > stack);
0355
0356 return safety;
0357 }
0358
0359
0360
0361
0362
0363
0364
0365
0366
0367 template <typename Navigator>
0368 VECCORE_ATT_HOST_DEVICE bool LevelLocate(int const exclude_item_id, Vector3D<Real_t> const &localpoint,
0369 int &container_id, vecgeom::NavigationState &path) const
0370 {
0371 unsigned int stack[BVH_MAX_DEPTH], *ptr = &stack[1];
0372 stack[0] = 0;
0373
0374 do {
0375 const unsigned int id = *--ptr;
0376
0377 if (fNChild[id] >= 0) {
0378 for (int i = 0; i < fNChild[id]; ++i) {
0379 const int prim = fPrimId[fOffset[id] + i];
0380 if (fAABBs[prim].Contains(localpoint)) {
0381 if (!Navigator::SkipItem(fRootId, prim, exclude_item_id) &&
0382 Navigator::CandidateContains(fRootId, prim, localpoint, path)) {
0383 container_id = Navigator::ItemId(fRootId, prim);
0384 return true;
0385 }
0386 }
0387 }
0388 } else {
0389 const unsigned int childL = 2 * id + 1;
0390 if (fNodes[childL].Contains(localpoint)) *ptr++ = childL;
0391
0392 const unsigned int childR = 2 * id + 2;
0393 if (fNodes[childR].Contains(localpoint)) *ptr++ = childR;
0394 }
0395 } while (ptr > stack);
0396
0397 return false;
0398 }
0399
0400
0401
0402
0403
0404
0405
0406
0407
0408 template <typename Navigator>
0409 VECCORE_ATT_HOST_DEVICE vecgeom::Inside_t LevelInside(long const exclude_item_id,
0410 Vector3D<Precision> const &localpoint, long &container_id,
0411 Vector3D<Precision> &daughterlocalpoint) const
0412 {
0413 unsigned int stack[BVH_MAX_DEPTH], *ptr = &stack[1];
0414 stack[0] = 0;
0415
0416 do {
0417 const unsigned int id = *--ptr;
0418
0419 if (fNChild[id] >= 0) {
0420 for (int i = 0; i < fNChild[id]; ++i) {
0421 const int prim = fPrimId[fOffset[id] + i];
0422 if (fAABBs[prim].Contains(localpoint)) {
0423 if (Navigator::SkipItem(fRootId, prim, exclude_item_id)) continue;
0424 auto inside = Navigator::CandidateInside(fRootId, prim, localpoint, daughterlocalpoint);
0425 if (inside != kOutside) {
0426 container_id = Navigator::ItemId(fRootId, prim);
0427 return inside;
0428 }
0429 }
0430 }
0431 } else {
0432 const unsigned int childL = 2 * id + 1;
0433 if (fNodes[childL].Contains(localpoint)) *ptr++ = childL;
0434
0435 const unsigned int childR = 2 * id + 2;
0436 if (fNodes[childR].Contains(localpoint)) *ptr++ = childR;
0437 }
0438 } while (ptr > stack);
0439
0440 return kOutside;
0441 }
0442
0443
0444
0445
0446
0447
0448
0449
0450
0451
0452
0453
0454
0455
0456
0457
0458 template <typename Navigator>
0459 VECCORE_ATT_HOST_DEVICE void ApproachNextDaughter(Vector3D<Precision> localpoint, Vector3D<Precision> localdir,
0460 Precision &step, long const last_exited_id) const
0461 {
0462 unsigned int stack[BVH_MAX_DEPTH] = {0}, *ptr = &stack[1];
0463
0464
0465 Vector3D<Real_t> invlocaldir(static_cast<Real_t>(1.0 / NonZero(localdir[0])),
0466 static_cast<Real_t>(1.0 / NonZero(localdir[1])),
0467 static_cast<Real_t>(1.0 / NonZero(localdir[2])));
0468
0469 do {
0470 unsigned int id = *--ptr;
0471
0472 if (fNChild[id] >= 0) {
0473
0474 for (int i = 0; i < fNChild[id]; ++i) {
0475 int prim = fPrimId[fOffset[id] + i];
0476
0477 if (fAABBs[prim].IntersectInvDir(localpoint, invlocaldir, step)) {
0478 const auto dist = Navigator::CandidateApproachSolid(fRootId, prim, localpoint, localdir);
0479
0480 if (dist < step && !(dist <= 0.0 && Navigator::SkipItem(fRootId, prim, last_exited_id))) step = dist;
0481 }
0482 }
0483 } else {
0484 unsigned int childL = 2 * id + 1;
0485 unsigned int childR = 2 * id + 2;
0486
0487
0488 Real_t tminL = vecgeom::InfinityLength<Real_t>(), tmaxL = -vecgeom::InfinityLength<Real_t>(),
0489 tminR = vecgeom::InfinityLength<Real_t>(), tmaxR = -vecgeom::InfinityLength<Real_t>();
0490
0491 fNodes[childL].ComputeIntersectionInvDir(localpoint, invlocaldir, tminL, tmaxL);
0492 fNodes[childR].ComputeIntersectionInvDir(localpoint, invlocaldir, tminR, tmaxR);
0493
0494 bool traverseL = tminL <= tmaxL && tmaxL >= 0.0 && tminL < step;
0495 bool traverseR = tminR <= tmaxR && tmaxR >= 0.0 && tminR < step;
0496
0497
0498
0499
0500
0501 if (tminR < tminL) {
0502 if (traverseR) *ptr++ = childR;
0503 if (traverseL) *ptr++ = childL;
0504 } else {
0505 if (traverseL) *ptr++ = childL;
0506 if (traverseR) *ptr++ = childR;
0507 }
0508 }
0509 } while (ptr > stack);
0510 }
0511
0512
0513
0514
0515
0516
0517
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527 template <typename Navigator, typename Real_i>
0528 VECCORE_ATT_HOST_DEVICE void CheckDaughterIntersectionsBenchmark(const Vector3D<Real_i> &localpoint,
0529 const Vector3D<Real_i> &localdir, Real_i &step,
0530 long const last_exited_id, long &hitcandidate_index,
0531 long &total_visited_children,
0532 long &total_visited_leaves, long &total_cut_nodes,
0533 long &total_stacked_nodes) const
0534 {
0535 total_visited_children = 0;
0536 total_visited_leaves = 0;
0537 total_cut_nodes = 0;
0538 total_stacked_nodes = 0;
0539
0540 unsigned int stack[BVH_MAX_DEPTH], *ptr = &stack[1];
0541 stack[0] = 0;
0542
0543
0544 Vector3D<Real_t> binvdir(static_cast<Real_t>(1.0) / vecgeom::NonZero(localdir[0]),
0545 static_cast<Real_t>(1.0) / vecgeom::NonZero(localdir[1]),
0546 static_cast<Real_t>(1.0) / vecgeom::NonZero(localdir[2]));
0547 Vector3D<Real_t> blocalpoint(static_cast<Real_t>(localpoint[0]), static_cast<Real_t>(localpoint[1]),
0548 static_cast<Real_t>(localpoint[2]));
0549 Vector3D<Real_t> blocaldir(static_cast<Real_t>(localdir[0]), static_cast<Real_t>(localdir[1]),
0550 static_cast<Real_t>(localdir[2]));
0551 Real_t bstep = static_cast<Real_t>(step);
0552
0553 do {
0554 const unsigned int id = *--ptr;
0555
0556
0557 Real_t min{vecgeom::InfinityLength<Real_t>()}, max{-vecgeom::InfinityLength<Real_t>()};
0558 fNodes[id].ComputeIntersectionInvDir(blocalpoint, binvdir, min, max);
0559 if (!(min <= max && max >= 0.0 && min < step)) {
0560 total_cut_nodes++;
0561 continue;
0562 }
0563
0564 if (fNChild[id] >= 0) {
0565 total_visited_leaves++;
0566
0567 for (int i = 0; i < fNChild[id]; ++i) {
0568 const int prim = fPrimId[fOffset[id] + i];
0569
0570 Real_t approach;
0571 if (fAABBs[prim].IntersectInvDirApproach(blocalpoint, binvdir, bstep, approach)) {
0572 auto dist = Navigator::CandidateDistanceToIn(
0573 fRootId, prim, localpoint + static_cast<Real_i>(approach) * localdir, localdir, step);
0574 dist += static_cast<Real_i>(approach);
0575
0576 if (dist < step &&
0577 !(dist <= vecgeom::kToleranceDist<Real_i> && Navigator::SkipItem(fRootId, prim, last_exited_id))) {
0578 step = dist;
0579 bstep = static_cast<Real_t>(dist);
0580 hitcandidate_index = prim;
0581 }
0582 }
0583 }
0584 } else {
0585 const unsigned int childL = 2 * id + 1;
0586 const unsigned int childR = 2 * id + 2;
0587
0588
0589 Real_t tminL = vecgeom::InfinityLength<Real_t>(), tmaxL = -vecgeom::InfinityLength<Real_t>(),
0590 tminR = vecgeom::InfinityLength<Real_t>(), tmaxR = -vecgeom::InfinityLength<Real_t>();
0591
0592 fNodes[childL].ComputeIntersectionInvDir(blocalpoint, binvdir, tminL, tmaxL);
0593 fNodes[childR].ComputeIntersectionInvDir(blocalpoint, binvdir, tminR, tmaxR);
0594
0595 const bool traverseL = tminL <= tmaxL && tmaxL >= 0.0 && tminL < step;
0596 const bool traverseR = tminR <= tmaxR && tmaxR >= 0.0 && tminR < step;
0597
0598
0599
0600
0601
0602 if (tminR < tminL) {
0603 if (traverseR) *ptr++ = childR;
0604 if (traverseL) *ptr++ = childL;
0605 } else {
0606 if (traverseL) *ptr++ = childL;
0607 if (traverseR) *ptr++ = childR;
0608 }
0609
0610 if (traverseR) total_stacked_nodes++;
0611 if (traverseL) total_stacked_nodes++;
0612 }
0613 } while (ptr > stack);
0614 }
0615
0616 private:
0617 enum class ConstructionAlgorithm : unsigned int;
0618
0619
0620
0621
0622
0623
0624
0625
0626
0627
0628
0629
0630
0631
0632 void ComputeNodes(unsigned int id, int *first, int *last, unsigned int nodes, ConstructionAlgorithm);
0633 };
0634
0635 }
0636 }
0637
0638 #endif