File indexing completed on 2026-06-03 08:44:45
0001
0002
0003
0004
0005
0006
0007
0008
0009 #ifndef BVH_NAVIGATOR_H
0010 #define BVH_NAVIGATOR_H
0011
0012 #include <VecGeom/base/Global.h>
0013 #include <VecGeom/base/Vector3D.h>
0014 #include <VecGeom/navigation/NavigationState.h>
0015 #include <VecGeom/volumes/LogicalVolume.h>
0016 #include <VecGeom/management/BVHManager.h>
0017 #include <VecGeom/management/GeoManager.h>
0018
0019 #ifdef VECGEOM_ENABLE_CUDA
0020 #include <VecGeom/backend/cuda/Interface.h>
0021 #endif
0022
0023 namespace vecgeom {
0024
0025 class BVHNavigator {
0026
0027
0028
0029 #ifdef VECGEOM_BVH_SINGLE
0030 using Real_t = float;
0031 #else
0032 using Real_t = double;
0033 #endif
0034
0035 public:
0036 static constexpr Precision kBoundaryPush = 10 * vecgeom::kTolerance;
0037
0038
0039
0040
0041
0042
0043 VECCORE_ATT_HOST_DEVICE
0044 static VECGEOM_FORCE_INLINE Daughter GetPlacedVolume(int aLVIndex, int index)
0045 {
0046 #ifdef VECCORE_CUDA_DEVICE_COMPILATION
0047 VECGEOM_VALIDATE(vecgeom::globaldevicegeomdata::gDeviceLogicalVolumes != nullptr,
0048 << "Logical volumes not copied to device");
0049 return vecgeom::globaldevicegeomdata::gDeviceLogicalVolumes[aLVIndex].GetDaughters()[index];
0050 #else
0051 #ifndef VECCORE_CUDA
0052 return vecgeom::GeoManager::Instance().GetLogicalVolume(aLVIndex)->GetDaughters()[index];
0053 #else
0054
0055 VECGEOM_VALIDATE(false, << "reached unimplement code");
0056 (void)index;
0057 (void)aLVIndex;
0058 return nullptr;
0059 #endif
0060 #endif
0061 }
0062
0063
0064
0065
0066
0067 VECCORE_ATT_HOST_DEVICE
0068 static VECGEOM_FORCE_INLINE vecgeom::VPlacedVolume *GetPlacedVolume(int global_index)
0069 {
0070 #ifdef VECCORE_CUDA_DEVICE_COMPILATION
0071 VECGEOM_VALIDATE(vecgeom::globaldevicegeomdata::gCompactPlacedVolBuffer != nullptr,
0072 << "Placed volumes not copied to device");
0073 return &vecgeom::globaldevicegeomdata::gCompactPlacedVolBuffer[global_index];
0074 #else
0075 #ifndef VECCORE_CUDA
0076 return vecgeom::GeoManager::Instance().GetPlacedVolume(global_index);
0077 #else
0078
0079 VECGEOM_VALIDATE(false, << "reached unimplement code");
0080 (void)global_index;
0081 return nullptr;
0082 #endif
0083 #endif
0084 }
0085
0086
0087
0088
0089
0090
0091
0092 VECCORE_ATT_HOST_DEVICE
0093 static Precision CandidateSafetyToIn(int aLVIndex, int index, Vector3D<Precision> localpoint)
0094 {
0095 return GetPlacedVolume(aLVIndex, index)->SafetyToIn(localpoint);
0096 };
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107 VECCORE_ATT_HOST_DEVICE
0108 static Precision CandidateDistanceToIn(int aLVIndex, int index, Vector3D<Precision> localpoint,
0109 Vector3D<Precision> localdir, Precision step)
0110 {
0111 Daughter vol = GetPlacedVolume(aLVIndex, index);
0112 return vol->DistanceToIn(localpoint, localdir, step);
0113 };
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123 VECCORE_ATT_HOST_DEVICE
0124 static vecgeom::Inside_t CandidateInside(int aLVIndex, int index, Vector3D<Precision> const &localpoint,
0125 Vector3D<Precision> &daughterlocalpoint)
0126 {
0127 auto daughter = GetPlacedVolume(aLVIndex, index);
0128 daughterlocalpoint = daughter->GetTransformation()->Transform<Precision>(localpoint);
0129 return daughter->GetUnplacedVolume()->Inside(daughterlocalpoint);
0130 };
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140 VECCORE_ATT_HOST_DEVICE
0141 static bool CandidateContains(int aLVIndex, int index, Vector3D<Precision> const &localpoint,
0142 Vector3D<Precision> &daughterlocalpoint)
0143 {
0144 auto inside = CandidateInside(aLVIndex, index, localpoint, daughterlocalpoint);
0145 return inside != EnumInside::kOutside;
0146 };
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156 VECCORE_ATT_HOST_DEVICE
0157 static Precision CandidateApproachSolid(int aLVIndex, int index, Vector3D<Precision> localpoint,
0158 Vector3D<Precision> localdir)
0159 {
0160 auto vol = GetPlacedVolume(aLVIndex, index);
0161 vecgeom::Transformation3D const *tr = vol->GetTransformation();
0162 Vector3D<Precision> pv_localpoint = tr->Transform(localpoint);
0163 Vector3D<Precision> pv_localdir = tr->TransformDirection(localdir);
0164 Vector3D<Precision> pv_invlocaldir(1.0 / vecgeom::NonZero(pv_localdir[0]), 1.0 / vecgeom::NonZero(pv_localdir[1]),
0165 1.0 / vecgeom::NonZero(pv_localdir[2]));
0166 return vol->GetUnplacedVolume()->ApproachSolid(pv_localpoint, pv_invlocaldir);
0167 };
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177 VECCORE_ATT_HOST_DEVICE
0178 static VECGEOM_FORCE_INLINE bool SkipItem(int aLVIndex, int index, long const global_id)
0179 {
0180 return (global_id == GetPlacedVolume(aLVIndex, index)->id());
0181 }
0182
0183 VECCORE_ATT_HOST_DEVICE
0184 static long TestBVHCheckDaughterIntersections(const vecgeom::BVH<Real_t> &bvh, Vector3D<Precision> &localpoint,
0185 Vector3D<Precision> &localdir, Precision &bvhstep)
0186 {
0187 long hitcandidate_index = -1;
0188 long last_exited_id = -1;
0189 bvh.CheckDaughterIntersections<BVHNavigator, Precision>(localpoint, localdir, bvhstep, last_exited_id,
0190 hitcandidate_index);
0191 return hitcandidate_index;
0192 }
0193
0194
0195
0196
0197
0198
0199 VECCORE_ATT_HOST_DEVICE
0200 static uint ItemId(int aLVIndex, int index) { return GetPlacedVolume(aLVIndex, index)->id(); }
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226 VECCORE_ATT_HOST_DEVICE
0227 static Daughter LocatePointIn(vecgeom::VPlacedVolume const *vol, Vector3D<Precision> const &point,
0228 vecgeom::NavigationState &path, bool top,
0229 vecgeom::VPlacedVolume const *exclude = nullptr)
0230 {
0231
0232
0233 if (top) {
0234
0235 VECGEOM_ASSERT(vol != nullptr);
0236 auto inside = vol->Inside(point);
0237 if (inside == kOutside) return nullptr;
0238
0239 if (inside == kSurface) path.SetBoundaryState(true);
0240 }
0241
0242 path.Push(vol);
0243
0244
0245 Vector3D<Precision> currentpoint(vol->GetTransformation()->Transform<Precision>(point));
0246 Vector3D<Precision> daughterlocalpoint;
0247 long exclude_id = -1;
0248 long vol_id = -1;
0249
0250 for (auto v = vol; v->GetDaughters().size() > 0;) {
0251 auto bvh = vecgeom::BVHManager::GetBVH(v->GetLogicalVolume()->id());
0252
0253 exclude_id = -1;
0254 if (exclude != nullptr) {
0255 exclude_id = exclude->id();
0256 }
0257 vol_id = -1;
0258
0259 auto inside = bvh->LevelInside<BVHNavigator>(exclude_id, currentpoint, vol_id, daughterlocalpoint);
0260 if (inside == kOutside) break;
0261 if (inside == kSurface) path.SetBoundaryState(true);
0262
0263 currentpoint = daughterlocalpoint;
0264
0265 v = GetPlacedVolume(vol_id);
0266 path.Push(v);
0267
0268
0269
0270 exclude = nullptr;
0271 }
0272
0273 return path.Top();
0274 }
0275
0276
0277
0278
0279
0280
0281
0282
0283
0284 VECCORE_ATT_HOST_DEVICE
0285 static Daughter RelocatePoint(Vector3D<Precision> const &localpoint, vecgeom::NavigationState &path)
0286 {
0287 vecgeom::VPlacedVolume const *currentmother = path.Top();
0288 Daughter skip = nullptr;
0289 Vector3D<Precision> transformed = localpoint;
0290
0291 do {
0292 skip = currentmother;
0293 path.Pop();
0294 transformed = currentmother->GetTransformation()->InverseTransform(transformed);
0295 currentmother = path.Top();
0296 } while (currentmother && (currentmother->IsAssembly() || !currentmother->UnplacedContains(transformed)));
0297
0298
0299
0300 if (currentmother) {
0301 return LocatePointInNavState(transformed, path, false, skip);
0302 }
0303 return currentmother;
0304 }
0305
0306
0307
0308
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319 VECCORE_ATT_HOST_DEVICE
0320 static Daughter LocatePointInNavState(Vector3D<Precision> const &localpoint, vecgeom::NavigationState &path, bool top,
0321 vecgeom::VPlacedVolume const *exclude = nullptr)
0322 {
0323
0324 VECGEOM_ASSERT(path.Top() != nullptr);
0325
0326 auto currentLogical = path.Top()->GetLogicalVolume();
0327 VECGEOM_ASSERT(currentLogical != nullptr);
0328
0329
0330 if (top) {
0331 auto inside = currentLogical->GetUnplacedVolume()->Inside(localpoint);
0332 if (inside == kOutside) return nullptr;
0333
0334 if (inside == kSurface) path.SetBoundaryState(true);
0335 }
0336
0337 Vector3D<Precision> currentpoint(localpoint);
0338 Vector3D<Precision> daughterlocalpoint;
0339
0340 long exclude_id = exclude ? static_cast<long>(exclude->id()) : -1;
0341 long pvol_id = -1;
0342
0343 while (currentLogical->GetDaughters().size() > 0) {
0344 auto *bvh = vecgeom::BVHManager::GetBVH(currentLogical->id());
0345
0346 auto inside = bvh->LevelInside<BVHNavigator>(exclude_id, currentpoint, pvol_id, daughterlocalpoint);
0347
0348 if (inside == kOutside) break;
0349 if (inside == kSurface) path.SetBoundaryState(true);
0350
0351 auto *placed = GetPlacedVolume(pvol_id);
0352 path.Push(placed);
0353
0354
0355 currentpoint = daughterlocalpoint;
0356 currentLogical = placed->GetLogicalVolume();
0357
0358
0359
0360 exclude_id = -1;
0361 exclude = nullptr;
0362 }
0363
0364 return path.Top();
0365 }
0366
0367 private:
0368
0369
0370
0371
0372 VECCORE_ATT_HOST_DEVICE
0373 static Precision ComputeStepAndHit(Vector3D<Precision> const &localpoint, Vector3D<Precision> const &localdir,
0374 Precision step_limit, vecgeom::NavigationState const &in_state,
0375 vecgeom::NavigationState &out_state, Daughter &hitcandidate)
0376 {
0377 in_state.CopyTo(&out_state);
0378
0379 if (step_limit <= 0) return 0.;
0380
0381 Precision step = step_limit;
0382 Daughter pvol = in_state.Top();
0383
0384 long hitcandidate_index = -1;
0385 long last_exited_id = -1;
0386
0387
0388 step = pvol->DistanceToOut(localpoint, localdir, step_limit);
0389
0390 if (step == vecgeom::kInfLength) step = 0.;
0391
0392 step = Min(step, step_limit);
0393
0394 if (step < kTolerance) {
0395 step = Max(0., step);
0396
0397 out_state.SetBoundaryState(true);
0398 return step;
0399 }
0400
0401
0402 if (pvol->GetDaughters().size() > 0) {
0403 auto bvh = vecgeom::BVHManager::GetBVH(pvol->GetLogicalVolume()->id());
0404
0405 hitcandidate_index = -1;
0406
0407
0408 last_exited_id = -1;
0409
0410
0411 bvh->CheckDaughterIntersections<BVHNavigator, Precision>(localpoint, localdir, step, last_exited_id,
0412 hitcandidate_index);
0413
0414 if (hitcandidate_index >= 0) {
0415
0416 step = Max(0., step);
0417 hitcandidate = pvol->GetLogicalVolume()->GetDaughters()[hitcandidate_index];
0418 out_state.SetBoundaryState(true);
0419 return step;
0420 }
0421 }
0422
0423
0424 if (step >= step_limit) {
0425
0426 out_state.SetBoundaryState(false);
0427 return step_limit;
0428 }
0429
0430
0431 out_state.SetBoundaryState(true);
0432 return step;
0433 }
0434
0435
0436
0437 VECCORE_ATT_HOST_DEVICE
0438 static Precision ApproachNextVolume(Vector3D<Precision> const &localpoint, Vector3D<Precision> const &localdir,
0439 Precision step_limit, vecgeom::NavigationState const &in_state)
0440 {
0441 Precision step = step_limit;
0442 Daughter pvol = in_state.Top();
0443
0444
0445 if (pvol->GetDaughters().size() > 0) {
0446 auto bvh = vecgeom::BVHManager::GetBVH(pvol->GetLogicalVolume()->id());
0447
0448
0449
0450 long last_exited_id = -1;
0451
0452
0453 bvh->ApproachNextDaughter<BVHNavigator>(localpoint, localdir, step, last_exited_id);
0454
0455 step -= 10 * vecgeom::kTolerance;
0456 }
0457
0458 if (step == vecgeom::kInfLength && step_limit > 0) return 0;
0459
0460
0461 if (step > step_limit) {
0462
0463 return step_limit;
0464 }
0465
0466 if (step < 0) {
0467 step = 0;
0468 }
0469
0470 return step;
0471 }
0472
0473 public:
0474
0475 VECCORE_ATT_HOST_DEVICE
0476 static Precision ComputeSafety(Vector3D<Precision> const &globalpoint, vecgeom::NavigationState const &state,
0477 Precision limit = InfinityLength<Precision>())
0478 {
0479 Daughter pvol = state.Top();
0480 if (pvol == nullptr) return kInfLength;
0481 vecgeom::Transformation3D m;
0482 state.TopMatrix(m);
0483 Vector3D<Precision> localpoint = m.Transform(globalpoint);
0484
0485
0486 Precision safety = pvol->SafetyToOut(localpoint);
0487 limit = Min(safety, limit);
0488
0489 if (safety > 0 && pvol->GetDaughters().size() > 0) {
0490 auto bvh = vecgeom::BVHManager::GetBVH(pvol->GetLogicalVolume()->id());
0491 safety = bvh->ComputeSafety<BVHNavigator>(localpoint, safety, limit);
0492 }
0493
0494 return safety;
0495 }
0496
0497
0498
0499
0500
0501 VECCORE_ATT_HOST_DEVICE
0502 static Precision ComputeStepAndPropagatedState(Vector3D<Precision> const &globalpoint,
0503 Vector3D<Precision> const &globaldir, Precision step_limit,
0504 vecgeom::NavigationState const &in_state,
0505 vecgeom::NavigationState &out_state, Precision push = 0)
0506 {
0507 if (in_state.Top() == nullptr) return kInfLength;
0508
0509 if (in_state.IsOnBoundary()) {
0510 push += kBoundaryPush;
0511 }
0512 if (step_limit < push) {
0513
0514
0515 in_state.CopyTo(&out_state);
0516 out_state.SetBoundaryState(false);
0517 return step_limit;
0518 }
0519 step_limit -= push;
0520
0521
0522 Vector3D<Precision> localpoint;
0523 Vector3D<Precision> localdir;
0524
0525 vecgeom::Transformation3D m;
0526 in_state.TopMatrix(m);
0527 localpoint = m.Transform(globalpoint);
0528 localdir = m.TransformDirection(globaldir);
0529
0530 localpoint += push * localdir;
0531
0532 Daughter hitcandidate = nullptr;
0533 Precision step = ComputeStepAndHit(localpoint, localdir, step_limit, in_state, out_state, hitcandidate);
0534 step += push;
0535
0536 if (out_state.IsOnBoundary()) {
0537
0538 localpoint += (step + kBoundaryPush) * localdir;
0539
0540 if (!hitcandidate) {
0541
0542 RelocatePoint(localpoint, out_state);
0543 } else {
0544
0545 localpoint = hitcandidate->GetTransformation()->Transform(localpoint);
0546 LocatePointIn(hitcandidate, localpoint, out_state, false);
0547 }
0548
0549 if (out_state.Top() != nullptr) {
0550 while (out_state.Top()->IsAssembly() || out_state.HasSamePathAsOther(in_state)) {
0551 out_state.Pop();
0552 }
0553 VECGEOM_ASSERT(!out_state.Top()->GetLogicalVolume()->GetUnplacedVolume()->IsAssembly());
0554 }
0555 }
0556
0557 return step;
0558 }
0559
0560
0561
0562
0563
0564
0565
0566
0567 VECCORE_ATT_HOST_DEVICE
0568 static Precision ComputeStepAndNextVolume(Vector3D<Precision> const &globalpoint,
0569 Vector3D<Precision> const &globaldir, Precision step_limit,
0570 vecgeom::NavigationState const &in_state,
0571 vecgeom::NavigationState &out_state, Precision push = 0)
0572 {
0573 if (in_state.Top() == nullptr) return kInfLength;
0574
0575 if (in_state.IsOnBoundary()) {
0576 push += kBoundaryPush;
0577 }
0578 if (step_limit < push) {
0579
0580
0581 in_state.CopyTo(&out_state);
0582 if (step_limit > kTolerance) out_state.SetBoundaryState(false);
0583 return step_limit;
0584 }
0585 step_limit -= push;
0586
0587
0588 Vector3D<Precision> localpoint;
0589 Vector3D<Precision> localdir;
0590
0591 vecgeom::Transformation3D m;
0592 in_state.TopMatrix(m);
0593 localpoint = m.Transform(globalpoint);
0594 localdir = m.TransformDirection(globaldir);
0595
0596 Daughter hitcandidate = nullptr;
0597
0598 Precision step =
0599 ComputeStepAndHit(localpoint + push * localdir, localdir, step_limit, in_state, out_state, hitcandidate);
0600
0601 step += (step > 0.) * push;
0602
0603 if (out_state.IsOnBoundary()) {
0604 if (!hitcandidate) {
0605 vecgeom::VPlacedVolume const *currentmother = out_state.Top();
0606 Vector3D<Precision> transformed = localpoint;
0607
0608 transformed += (step + kBoundaryPush) * localdir;
0609 do {
0610 out_state.SetLastExited();
0611 out_state.Pop();
0612 transformed = currentmother->GetTransformation()->InverseTransform(transformed);
0613 currentmother = out_state.Top();
0614 } while (currentmother &&
0615 (currentmother->IsAssembly() || currentmother->GetUnplacedVolume()->Inside(transformed) != kInside));
0616 } else {
0617 out_state.Push(hitcandidate);
0618 }
0619 }
0620
0621 return step;
0622 }
0623
0624
0625
0626 VECCORE_ATT_HOST_DEVICE
0627 static Precision ComputeStepToApproachNextVolume(Vector3D<Precision> const &globalpoint,
0628 Vector3D<Precision> const &globaldir, Precision step_limit,
0629 vecgeom::NavigationState const &in_state)
0630 {
0631 if (in_state.Top() == nullptr) return kInfLength;
0632
0633 Vector3D<Precision> localpoint;
0634 Vector3D<Precision> localdir;
0635
0636 vecgeom::Transformation3D m;
0637 in_state.TopMatrix(m);
0638 localpoint = m.Transform(globalpoint);
0639 localdir = m.TransformDirection(globaldir);
0640
0641 Precision step = ApproachNextVolume(localpoint, localdir, step_limit, in_state);
0642
0643 return step;
0644 }
0645
0646
0647
0648 VECCORE_ATT_HOST_DEVICE
0649 static void RelocateToNextVolume(Vector3D<Precision> const &globalpoint, Vector3D<Precision> const &globaldir,
0650 vecgeom::NavigationState &state)
0651 {
0652
0653 if (state.IsOutside()) return;
0654
0655
0656
0657 Vector3D<Precision> pushed = globalpoint ;
0658
0659
0660 vecgeom::Transformation3D m;
0661 state.TopMatrix(m);
0662 Vector3D<Precision> localpoint = m.Transform(pushed);
0663
0664
0665 LocatePointInNavState(localpoint, state, false, state.GetLastExited());
0666
0667 if (state.Top() != nullptr) {
0668 while (state.Top()->IsAssembly()) {
0669 state.Pop();
0670 }
0671 VECGEOM_ASSERT(!state.Top()->GetLogicalVolume()->GetUnplacedVolume()->IsAssembly());
0672 }
0673 }
0674 };
0675
0676 }
0677
0678 #endif