14#include "G4Exception.hh"
15#include "G4TouchableHistory.hh"
16#include "G4VPhysicalVolume.hh"
18#include <VecGeom/base/Transformation3D.h>
19#include <VecGeom/management/GeoManager.h>
20#include <VecGeom/navigation/GlobalLocator.h>
21#include <VecGeom/navigation/VNavigator.h>
22#include <VecGeom/navigation/VSafetyEstimator.h>
23#include <VecGeom/volumes/LogicalVolume.h>
24#include <VecGeom/volumes/PlacedVolume.h>
26#include <fairlogger/Logger.h>
35const G4ThreeVector kNoPoint(-1e8, -1e8, -1e8);
37bool samePoint(
const G4ThreeVector&
a,
const G4ThreeVector&
b) {
return a.diff2(
b) < 1e-20; }
39vecgeom::VPlacedVolume
const* topOf(vecgeom::NavigationState
const&
st)
41 return st.IsOutside() ? nullptr :
st.Top();
56 LOG(info) <<
"VecGeom navigation: zero steps " << mZeroStepCount <<
", stuck pushes " << mStuckPushCount
57 <<
", abandoned " << mAbandonCount <<
", negative safeties " << mNegativeSafetyCount
58 <<
", unmappable touchables " << mUnmappableHistoryCount <<
", relocated resumes "
59 << mRelocatedResumeCount <<
", missing exit normals " << mNoNormalCount;
62G4VPhysicalVolume* VecGeomG4Navigator::updateG4History()
65 if (mHistoryValid && mCurState.HasSamePathAsOther(mHistoryState)) {
66 return fHistory.GetTopVolume();
68 mHistoryState = mCurState;
73void VecGeomG4Navigator::locateFromWorld(
const V3& point)
76 vecgeom::GlobalLocator::LocateGlobalPoint(vecgeom::GeoManager::Instance().GetWorld(), point, mCurState,
true);
83double VecGeomG4Navigator::boundaryPush(
const V3& point,
const V3& dir)
85 constexpr double kMaxPush = 1.e-4;
86 const double big = std::max({std::abs(point.x()), std::abs(point.y()), std::abs(point.z())});
87 const double rounding = 1.e3 * big * std::numeric_limits<double>::epsilon();
90 vecgeom::NavigationState
const&
st = mWouldEnter ? mNextState : mCurState;
92 mPushNormalValid =
false;
93 if (!
st.IsOutside() &&
st.Top() !=
nullptr) {
94 vecgeom::Transformation3D
m;
97 st.Top()->GetUnplacedVolume()->Normal(
m.Transform(point),
n);
98 const double c = std::abs(
n.Dot(
m.TransformDirection(dir)));
99 if (
c > 0. &&
n.Mag2() > 0.5) {
102 if (
n.Mag2() > 0.5) {
103 mPushNormal =
m.InverseTransformDirection(
n);
104 mPushNormalValid =
true;
107 return std::max(rounding, std::min(mPushDepth / cosn, kMaxPush));
111 const G4double proposedStepLength, G4double& newSafety)
117 auto const*
top = topOf(mCurState);
118 if (
top ==
nullptr) {
121 auto const* navigator =
top->GetLogicalVolume()->GetNavigator();
125 bool calcSafety = !mZeroSafety && !(mLocatedOnBoundary && samePoint(globalPoint, mLastLocatedPoint));
126 if (calcSafety && samePoint(globalPoint, mSafetyOrig) && mLastSafety < mLastSafetyLimit) {
128 newSafety = mLastSafety;
135 mStepState = mCurState;
136 if (mExitBlockPending) {
137 const bool block = samePoint(globalPoint, mLastLocatedPoint) &&
141 mStepState.SetLastExited(mEmptyState.GetLastExitedState());
145 const double limit = std::min(proposedStepLength *
kG4ToVG,
static_cast<double>(vecgeom::kInfLength));
147 double vgStep = navigator->ComputeStepAndSafety(
toVG(globalPoint),
toDir(direction), limit, mStepState, calcSafety,
151 ++mNegativeSafetyCount;
155 mSafetyOrig = globalPoint;
156 mLastSafety = newSafety;
157 mLastSafetyLimit = kInfinity;
159 const bool boundaryLimited = vgStep < limit;
160 G4double step = std::max(vgStep, 0.) *
kVGToG4;
161 const bool entering = mStepState.GetCurrentLevel() > mCurState.GetCurrentLevel();
165 if (step < 0.05 * kCarTolerance) {
167 if (++mNzeroSteps >= kActionThresholdNoZeroSteps) {
169 step += 100. * kCarTolerance;
170 if (mNzeroSteps >= kAbandonThresholdNoZeroSteps) {
172 std::ostringstream
msg;
173 msg <<
"Track stuck or not moving: " << mNzeroSteps <<
" zero steps in " <<
top->GetLabel() <<
" at ("
174 << globalPoint.x() <<
", " << globalPoint.y() <<
", " << globalPoint.z() <<
") mm. Event aborted, as "
175 <<
"G4Navigator does.";
177 G4Exception(
"VecGeomG4Navigator::ComputeStep()",
"GeomNav0003", EventMustBeAborted,
msg.str().c_str());
184 if (boundaryLimited) {
185 mWouldEnter = entering;
186 mWouldExit = !entering;
187 mNextPoint = globalPoint + step * direction;
189 mNextState = mStepState;
192 mNormalState = entering ? mNextState : mCurState;
193 mNormalEnter = entering;
194 mNormalPoint = mNextPoint;
198 mNormalValid =
false;
210void VecGeomG4Navigator::leaveFlushVolumes(
const V3& point,
const V3& dir,
int minLevel,
211 vecgeom::VPlacedVolume
const* avoid)
213 for (
int round = 0; round < 4; ++round) {
214 vecgeom::VPlacedVolume
const*
left =
nullptr;
215 while (topOf(mCurState) !=
nullptr &&
static_cast<int>(mCurState.GetCurrentLevel()) > minLevel) {
216 vecgeom::Transformation3D
m;
217 mCurState.TopMatrix(
m);
218 if (mCurState.Top()->GetUnplacedVolume()->DistanceToOut(
m.Transform(point),
m.TransformDirection(dir)) > 0.) {
221 left = mCurState.Top();
222 mCurState.SetLastExited();
223 setExited(mCurState,
false);
224 if (mCurState.GetCurrentLevel() <= 1) {
230 if (
left ==
nullptr) {
233 auto const* mother = mCurState.Top();
234 vecgeom::Transformation3D
m;
235 mCurState.TopMatrix(
m);
236 const auto level = mCurState.GetCurrentLevel();
237 const auto blocked = mCurState.GetLastExitedState();
239 vecgeom::GlobalLocator::LocateGlobalPointExclVolume(mother,
left,
m.Transform(point), mCurState,
false);
240 mCurState.SetLastExited(blocked);
241 if (mCurState.GetCurrentLevel() ==
level) {
244 if (avoid !=
nullptr && mCurState.GetCurrentLevel() >
level) {
246 mPathScratch = mCurState;
247 while (mPathScratch.GetCurrentLevel() >
level && mPathScratch.Top() != avoid) {
250 if (mPathScratch.GetCurrentLevel() >
level) {
251 while (mCurState.GetCurrentLevel() >
level) {
263void VecGeomG4Navigator::updateCrossingFlags(
bool entering)
265 const int preLevel = mReloScratch.IsOutside() ? 0 :
static_cast<int>(mReloScratch.GetCurrentLevel());
266 const int curLevel = mCurState.IsOutside() ? 0 :
static_cast<int>(mCurState.GetCurrentLevel());
267 mPathScratch = mReloScratch;
268 mStepState = mCurState;
269 int la = preLevel, lb = curLevel;
278 while (la > 0 && !mPathScratch.HasSamePathAsOther(mStepState)) {
283 fExitedMother = !entering || la < preLevel;
284 fEnteredDaughter = entering || la < curLevel;
288 const G4ThreeVector* direction,
289 const G4bool relativeSearch,
const G4bool)
293 const bool onBoundary = relativeSearch && (fWasLimitedByGeometry || samePoint(point, mNextPoint));
294 const bool crossing = onBoundary && (mWouldEnter || mWouldExit) && direction !=
nullptr;
295 const bool entering = crossing && mWouldEnter;
297 const V3 dir = direction !=
nullptr ?
toDir(*direction) :
V3(0., 0., 0.);
298 const double push = (onBoundary && direction !=
nullptr) ? boundaryPush(p, dir) : 0.;
299 const V3 q = p + push * dir;
301 fWasLimitedByGeometry =
false;
302 fEnteredDaughter =
false;
303 fExitedMother =
false;
304 mLocatedOnBoundary =
false;
305 mLastLocatedPoint = point;
306 mSafetyOrig = kNoPoint;
309 if (!relativeSearch) {
312 }
else if (topOf(mCurState) ==
nullptr) {
314 }
else if (entering) {
316 mReloScratch = mCurState;
317 mCurState = mNextState;
320 mCurState.SetLastExited(mEmptyState.GetLastExitedState());
321 auto const* daughter = mCurState.Top();
323 vecgeom::Transformation3D
m;
324 mCurState.TopMatrix(
m);
325 vecgeom::GlobalLocator::LocateGlobalPoint(daughter, daughter->GetTransformation()->Transform(
m.Transform(q)),
327 mLocatedOnBoundary =
true;
328 }
else if (crossing) {
332 mReloScratch = mCurState;
333 if (mCurState.GetCurrentLevel() <= 1) {
336 vecgeom::Transformation3D
m;
337 mCurState.TopMatrix(
m);
338 vecgeom::GlobalLocator::RelocatePointFromPathForceDifferent(
m.Transform(q), mCurState);
339 mReloScratch.SetLastExited();
340 mCurState.SetLastExited(mReloScratch.GetLastExitedState());
341 setExited(mReloScratch,
true);
343 mLocatedOnBoundary =
true;
346 vecgeom::Transformation3D
m;
347 mCurState.TopMatrix(
m);
348 vecgeom::GlobalLocator::RelocatePointFromPath(
m.Transform(q), mCurState);
349 if (topOf(mCurState) ==
nullptr) {
352 mLocatedOnBoundary = onBoundary;
355 leaveFlushVolumes(q, dir, entering ?
static_cast<int>(mNextState.GetCurrentLevel()) : 0,
356 entering ? nullptr : topOf(mReloScratch));
357 updateCrossingFlags(entering);
361 return updateG4History();
365 const G4TouchableHistory& history)
369 fWasLimitedByGeometry =
false;
370 fEnteredDaughter =
false;
371 fExitedMother =
false;
374 mLocatedOnBoundary =
false;
375 mLastLocatedPoint = point;
376 mSafetyOrig = kNoPoint;
378 fHistory = *history.GetHistory();
382 vecgeom::Transformation3D
m;
383 mCurState.TopMatrix(
m);
384 if (mCurState.Top()->GetUnplacedVolume()->Contains(
m.Transform(p))) {
387 ++mRelocatedResumeCount;
388 vecgeom::GlobalLocator::RelocatePointFromPath(
m.Transform(p), mCurState);
389 if (topOf(mCurState) ==
nullptr) {
394 if (++mUnmappableHistoryCount <= 10) {
395 LOG(warning) <<
"VecGeom navigation: a touchable matches no VecGeom path; locating from the world";
402 mHistoryState = mCurState;
403 mHistoryValid =
true;
404 return fHistory.GetTopVolume();
406 mHistoryValid =
false;
407 return updateG4History();
415 mLocatedOnBoundary =
false;
418 fEnteredDaughter =
false;
419 fExitedMother =
false;
429 if (mLocatedOnBoundary && samePoint(globalPoint, mLastLocatedPoint)) {
432 if ((mWouldEnter || mWouldExit) && samePoint(globalPoint, mNextPoint)) {
435 if (samePoint(globalPoint, mSafetyOrig) && (mLastSafety < mLastSafetyLimit || mLastSafety >= proposedMaxLength)) {
438 if (topOf(mCurState) ==
nullptr) {
443 const double rest = mLastSafety - std::sqrt(globalPoint.diff2(mSafetyOrig));
444 if (rest >= proposedMaxLength) {
447 double safety =
boundedSafety(mCurState, globalPoint, proposedMaxLength);
449 ++mNegativeSafetyCount;
452 mSafetyOrig = globalPoint;
453 mLastSafety = safety;
454 mLastSafetyLimit = proposedMaxLength;
464bool VecGeomG4Navigator::computeExitNormal(
const G4ThreeVector& point,
V3& globalNormal)
const
466 if (!mNormalValid || topOf(mNormalState) ==
nullptr) {
469 vecgeom::Transformation3D
m;
470 mNormalState.TopMatrix(
m);
471 V3 localNormal(0., 0., 0.);
472 mNormalState.Top()->GetUnplacedVolume()->Normal(
m.Transform(
toVG(point)), localNormal);
473 globalNormal =
m.InverseTransformDirection(localNormal);
474 const double mag = globalNormal.Mag();
480 globalNormal = -globalNormal;
488 if (!computeExitNormal(mNormalPoint,
n) || topOf(mCurState) ==
nullptr) {
490 if (
valid !=
nullptr) {
493 return G4ThreeVector();
495 vecgeom::Transformation3D
m;
496 mCurState.TopMatrix(
m);
497 const auto local =
m.TransformDirection(
n);
498 if (
valid !=
nullptr) {
501 return G4ThreeVector(local[0], local[1], local[2]);
507 if (!computeExitNormal(point,
n)) {
509 if (
valid !=
nullptr) {
512 return G4ThreeVector();
514 if (
valid !=
nullptr) {
517 return G4ThreeVector(
n[0],
n[1],
n[2]);
vecgeom::Vector3D< double > V3
static bool directionLeaves(const V3 &n, const V3 &dir)
static V3 toDir(const G4ThreeVector &d)
G4VPhysicalVolume * historyFromState(vecgeom::NavigationState const &state)
static V3 toVG(const G4ThreeVector &p)
bool stateFromHistory(vecgeom::NavigationState &state) const
static double boundedSafety(vecgeom::NavigationState const &state, const G4ThreeVector &point, double limit)
static constexpr double kVGToG4
static constexpr double kG4ToVG
vecgeom::Vector3D< double > V3
G4ThreeVector GetGlobalExitNormal(const G4ThreeVector &point, G4bool *valid) override
G4double ComputeSafety(const G4ThreeVector &globalPoint, const G4double proposedMaxLength=DBL_MAX, const G4bool keepState=true) override
G4double ComputeStep(const G4ThreeVector &globalPoint, const G4ThreeVector &direction, const G4double proposedStepLength, G4double &newSafety) override
~VecGeomG4Navigator() override
VecGeomG4Navigator(VecGeomG4Map const &map, double pushDepth, bool zeroSafety)
G4VPhysicalVolume * ResetHierarchyAndLocate(const G4ThreeVector &point, const G4ThreeVector &direction, const G4TouchableHistory &history) override
G4VPhysicalVolume * LocateGlobalPointAndSetup(const G4ThreeVector &point, const G4ThreeVector *direction=nullptr, const G4bool relativeSearch=true, const G4bool ignoreDirection=true) override
void LocateGlobalPointWithinVolume(const G4ThreeVector &position) override
G4ThreeVector GetLocalExitNormal(G4bool *valid) override
GLdouble GLdouble GLdouble GLdouble top
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
uint64_t const void const *restrict const msg