Skip to content

Commit 2050d87

Browse files
sawenzelclaude
andcommitted
Honour the safety bound and reuse the last safety within its sphere
This makes the stand-alone ComputeSafety of both VecGeom navigators cheaper, as Geant4's own navigator does. - The caller's proposedMaxLength now limits the BVH search: the value is exact below it and a valid lower bound above it, as in G4VoxelSafety. - A query at distance d from the last safety origin returns s0 - d when that covers the bound, without a computation. Every locate drops this cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 1354f47 commit 2050d87

6 files changed

Lines changed: 69 additions & 16 deletions

‎Detectors/gconfig/src/VecGeomG4Navigator.cxx‎

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ G4double VecGeomG4Navigator::ComputeStep(const G4ThreeVector& globalPoint, const
123123
// On the point a boundary locate left the track on, the safety is zero; a point seen before
124124
// reuses its safety. Otherwise the navigator computes it with the step.
125125
bool calcSafety = !mZeroSafety && !(mLocatedOnBoundary && samePoint(globalPoint, mLastLocatedPoint));
126-
if (calcSafety && samePoint(globalPoint, mSafetyOrig)) {
126+
if (calcSafety && samePoint(globalPoint, mSafetyOrig) && mLastSafety < mLastSafetyLimit) {
127127
calcSafety = false;
128128
newSafety = mLastSafety;
129129
}
@@ -154,6 +154,7 @@ G4double VecGeomG4Navigator::ComputeStep(const G4ThreeVector& globalPoint, const
154154
newSafety = safety * kVGToG4;
155155
mSafetyOrig = globalPoint;
156156
mLastSafety = newSafety;
157+
mLastSafetyLimit = kInfinity;
157158
}
158159
const bool boundaryLimited = vgStep < limit;
159160
G4double step = std::max(vgStep, 0.) * kVGToG4;
@@ -416,7 +417,8 @@ void VecGeomG4Navigator::LocateGlobalPointWithinVolume(const G4ThreeVector& posi
416417
clearLastExited();
417418
}
418419

419-
G4double VecGeomG4Navigator::ComputeSafety(const G4ThreeVector& globalPoint, const G4double, const G4bool)
420+
G4double VecGeomG4Navigator::ComputeSafety(const G4ThreeVector& globalPoint, const G4double proposedMaxLength,
421+
const G4bool)
420422
{
421423
if (mZeroSafety) {
422424
return 0.;
@@ -427,24 +429,26 @@ G4double VecGeomG4Navigator::ComputeSafety(const G4ThreeVector& globalPoint, con
427429
if ((mWouldEnter || mWouldExit) && samePoint(globalPoint, mNextPoint)) {
428430
return 0.;
429431
}
430-
if (samePoint(globalPoint, mSafetyOrig)) {
432+
if (samePoint(globalPoint, mSafetyOrig) && (mLastSafety < mLastSafetyLimit || mLastSafety >= proposedMaxLength)) {
431433
return mLastSafety;
432434
}
433-
auto const* top = topOf(mCurState);
434-
if (top == nullptr) {
435+
if (topOf(mCurState) == nullptr) {
435436
return 0.;
436437
}
437-
auto const* estimator = top->GetLogicalVolume()->GetSafetyEstimator();
438-
if (estimator == nullptr) {
439-
return 0.;
438+
// Every point closer to the last safety origin than its safety is in the same volume, so s0 - d is a
439+
// valid safety there; it is used when it covers the caller's bound. Every locate drops the cache.
440+
const double rest = mLastSafety - std::sqrt(globalPoint.diff2(mSafetyOrig));
441+
if (rest >= proposedMaxLength) {
442+
return rest;
440443
}
441-
double safety = estimator->ComputeSafety(toVG(globalPoint), mCurState);
444+
double safety = boundedSafety(mCurState, globalPoint, proposedMaxLength);
442445
if (safety < 0.) {
443446
++mNegativeSafetyCount;
444447
safety = 0.;
445448
}
446449
mSafetyOrig = globalPoint;
447-
mLastSafety = safety * kVGToG4;
450+
mLastSafety = safety;
451+
mLastSafetyLimit = proposedMaxLength;
448452
return mLastSafety;
449453
}
450454

‎Detectors/gconfig/src/VecGeomG4Navigator.h‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ namespace o2::simsetup
2828
/// The point is first pushed across the face by a small depth, and afterwards leaves every volume
2929
/// it is flush with and heading out of. As in Geant4, the volume left is blocked only in the first
3030
/// ComputeStep after the exit, and only while the direction points away from it.
31-
/// - Safety is zero only at the boundary point itself.
31+
/// - Safety is zero only at the boundary point itself. A bounded query is answered from the last safety
32+
/// computed in the same volume when that covers the bound.
3233
///
3334
/// Unlike TG4VecGeomNavigator, the VecGeom geometry is converted from TGeo, not from Geant4, so one
3435
/// VecGeom placement can stand for a chain of g4root volumes (VecGeomG4Map).
@@ -110,6 +111,7 @@ class VecGeomG4Navigator : public VecGeomG4NavigatorBase
110111
bool mLocatedOnBoundary = false;
111112
G4ThreeVector mSafetyOrig{-1e8, -1e8, -1e8}; ///< the last point a safety was computed for
112113
double mLastSafety = 0.; ///< mm
114+
double mLastSafetyLimit = kInfinity; ///< mm; mLastSafety is exact below it, a lower bound above
113115
bool mNormalEnter = false;
114116
bool mNormalValid = false;
115117
G4ThreeVector mNormalPoint{-1e8, -1e8, -1e8};

‎Detectors/gconfig/src/VecGeomG4NavigatorBase.cxx‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@
1313

1414
#include "G4VPhysicalVolume.hh"
1515

16+
#include <VecGeom/management/BVHManager.h>
17+
#include <VecGeom/navigation/BVHNavigator.h>
18+
#include <VecGeom/navigation/BVHSafetyEstimator.h>
19+
#include <VecGeom/navigation/VSafetyEstimator.h>
20+
#include <VecGeom/volumes/LogicalVolume.h>
1621
#include <VecGeom/volumes/PlacedVolume.h>
1722

1823
#include <fairlogger/Logger.h>
1924

25+
#include <algorithm>
26+
2027
namespace
2128
{
2229
/// Longest run of Geant4 levels one flattened VecGeom placement can stand for.
@@ -28,6 +35,30 @@ constexpr int kMaxDepth = 64;
2835
namespace o2::simsetup
2936
{
3037

38+
double VecGeomG4NavigatorBase::boundedSafety(vecgeom::NavigationState const& state, const G4ThreeVector& point,
39+
double limit)
40+
{
41+
auto const* pvol = state.Top();
42+
auto const* lvol = pvol->GetLogicalVolume();
43+
auto const* estimator = lvol->GetSafetyEstimator();
44+
if (estimator == nullptr) {
45+
return 0.;
46+
}
47+
if (limit < kInfinity && estimator == vecgeom::BVHSafetyEstimator::Instance() && lvol->GetDaughters().size() > 0) {
48+
// The BVH estimator's own computation, with the search limited.
49+
vecgeom::Transformation3D m;
50+
state.TopMatrix(m);
51+
const V3 local = m.Transform(toVG(point));
52+
double safety = pvol->SafetyToOut(local);
53+
if (safety > 0.) {
54+
safety = vecgeom::BVHNavigator::ComputeBVHSafety<vecgeom::BVHSafetyEstimator>(
55+
*vecgeom::BVHManager::GetBVH(lvol), local, safety, std::min(safety, limit * kG4ToVG));
56+
}
57+
return safety * kVGToG4;
58+
}
59+
return estimator->ComputeSafety(toVG(point), state) * kVGToG4;
60+
}
61+
3162
G4VPhysicalVolume* VecGeomG4NavigatorBase::historyFromState(vecgeom::NavigationState const& state)
3263
{
3364
// Collect the Geant4 volumes the state stands for, then keep the levels the history already has

‎Detectors/gconfig/src/VecGeomG4NavigatorBase.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ class VecGeomG4NavigatorBase : public G4Navigator
5050
/// False if a level matches no VecGeom placement; \a state is then empty.
5151
bool stateFromHistory(vecgeom::NavigationState& state) const;
5252

53+
/// The safety (mm) at \a point in the top volume of \a state, exact below \a limit (mm) and a valid
54+
/// lower bound above it: daughters beyond the limit are bounded by their boxes, as in G4VoxelSafety.
55+
static double boundedSafety(vecgeom::NavigationState const& state, const G4ThreeVector& point, double limit);
56+
5357
/// Geant4's rule for the volume a track just left (G4NormalNavigation, G4VoxelNavigation): it is
5458
/// blocked only while the direction points away from it, along its outward normal \a n (global).
5559
[[gnu::always_inline]] static bool directionLeaves(const V3& n, const V3& dir)

‎Detectors/gconfig/src/VecGeomG4PropagatingNavigator.cxx‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include <fairlogger/Logger.h>
2626

27+
#include <algorithm>
2728
#include <cmath>
2829

2930
namespace
@@ -133,6 +134,7 @@ G4VPhysicalVolume* VecGeomG4PropagatingNavigator::ResetHierarchyAndLocate(const
133134
mWouldExit = false;
134135
mOnBoundary = false;
135136
mHaveNextState = false;
137+
mLastSafety = -1.;
136138
fHistory = *history.GetHistory();
137139
if (!stateFromHistory(mCurState) && fHistory.GetVolume(0) != nullptr) {
138140
LOG(fatal) << "Geant4 handed back a touchable that matches no VecGeom path";
@@ -153,6 +155,7 @@ G4VPhysicalVolume* VecGeomG4PropagatingNavigator::LocateGlobalPointAndSetup(cons
153155
mPrevState = mCurState;
154156
mLocatedPoint = point;
155157
mExitBlockPending = false;
158+
mLastSafety = -1.;
156159

157160
if (!mForceReInit && relativeSearch && onBoundary && mHaveNextState) {
158161
// The state on the far side of the boundary was already worked out, and relocated, by the step
@@ -190,17 +193,24 @@ void VecGeomG4PropagatingNavigator::LocateGlobalPointWithinVolume(const G4ThreeV
190193
fExitedMother = false;
191194
}
192195

193-
G4double VecGeomG4PropagatingNavigator::ComputeSafety(const G4ThreeVector& globalPoint, const G4double, const G4bool)
196+
G4double VecGeomG4PropagatingNavigator::ComputeSafety(const G4ThreeVector& globalPoint, const G4double proposedMaxLength,
197+
const G4bool)
194198
{
195199
if (mZeroSafety || mOnBoundary || mCrossed || fEnteredDaughter || fExitedMother || mWouldEnter || mWouldExit) {
196200
return 0.;
197201
}
198-
auto const* top = mCurState.Top();
199-
if (top == nullptr) {
202+
if (mCurState.Top() == nullptr) {
200203
return 0.;
201204
}
202-
const double safety = top->GetLogicalVolume()->GetSafetyEstimator()->ComputeSafety(toVG(globalPoint), mCurState);
203-
return (safety > 0.) ? safety * kVGToG4 : 0.;
205+
// Every point closer to the last safety origin than its safety is in the same volume, so s0 - d is a
206+
// valid safety there; it is used when it covers the caller's bound. Every locate drops the cache.
207+
const double rest = mLastSafety - std::sqrt(globalPoint.diff2(mSafetyOrig));
208+
if (rest >= proposedMaxLength) {
209+
return rest;
210+
}
211+
mSafetyOrig = globalPoint;
212+
mLastSafety = std::max(boundedSafety(mCurState, globalPoint, proposedMaxLength), 0.);
213+
return mLastSafety;
204214
}
205215

206216
G4ThreeVector VecGeomG4PropagatingNavigator::GetGlobalExitNormal(const G4ThreeVector& point, G4bool* valid)

‎Detectors/gconfig/src/VecGeomG4PropagatingNavigator.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ class VecGeomG4PropagatingNavigator : public VecGeomG4NavigatorBase
6565
bool mCrossed = false; ///< the last locate acted on a boundary crossing
6666
bool mExitBlockPending = false; ///< the next step is the first after leaving mPrevState's volume
6767
G4ThreeVector mLocatedPoint{-1e8, -1e8, -1e8}; ///< where the last locate put the track
68+
G4ThreeVector mSafetyOrig{-1e8, -1e8, -1e8}; ///< the last point a safety was computed for
69+
double mLastSafety = -1.; ///< mm; negative when there is none
6870

6971
int mZeroSteps = 0;
7072
long mNudgedSteps = 0;

0 commit comments

Comments
 (0)