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)) {
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;
158 const bool boundaryLimited = vgStep < limit;
159 G4double step = std::max(vgStep, 0.) *
kVGToG4;
160 const bool entering = mStepState.GetCurrentLevel() > mCurState.GetCurrentLevel();
164 if (step < 0.05 * kCarTolerance) {
166 if (++mNzeroSteps >= kActionThresholdNoZeroSteps) {
168 step += 100. * kCarTolerance;
169 if (mNzeroSteps >= kAbandonThresholdNoZeroSteps) {
171 std::ostringstream
msg;
172 msg <<
"Track stuck or not moving: " << mNzeroSteps <<
" zero steps in " <<
top->GetLabel() <<
" at ("
173 << globalPoint.x() <<
", " << globalPoint.y() <<
", " << globalPoint.z() <<
") mm. Event aborted, as "
174 <<
"G4Navigator does.";
176 G4Exception(
"VecGeomG4Navigator::ComputeStep()",
"GeomNav0003", EventMustBeAborted,
msg.str().c_str());
183 if (boundaryLimited) {
184 mWouldEnter = entering;
185 mWouldExit = !entering;
186 mNextPoint = globalPoint + step * direction;
188 mNextState = mStepState;
191 mNormalState = entering ? mNextState : mCurState;
192 mNormalEnter = entering;
193 mNormalPoint = mNextPoint;
197 mNormalValid =
false;
209void VecGeomG4Navigator::leaveFlushVolumes(
const V3& point,
const V3& dir,
int minLevel,
210 vecgeom::VPlacedVolume
const* avoid)
212 for (
int round = 0; round < 4; ++round) {
213 vecgeom::VPlacedVolume
const*
left =
nullptr;
214 while (topOf(mCurState) !=
nullptr &&
static_cast<int>(mCurState.GetCurrentLevel()) > minLevel) {
215 vecgeom::Transformation3D
m;
216 mCurState.TopMatrix(
m);
217 if (mCurState.Top()->GetUnplacedVolume()->DistanceToOut(
m.Transform(point),
m.TransformDirection(dir)) > 0.) {
220 left = mCurState.Top();
221 mCurState.SetLastExited();
222 setExited(mCurState,
false);
223 if (mCurState.GetCurrentLevel() <= 1) {
229 if (
left ==
nullptr) {
232 auto const* mother = mCurState.Top();
233 vecgeom::Transformation3D
m;
234 mCurState.TopMatrix(
m);
235 const auto level = mCurState.GetCurrentLevel();
236 const auto blocked = mCurState.GetLastExitedState();
238 vecgeom::GlobalLocator::LocateGlobalPointExclVolume(mother,
left,
m.Transform(point), mCurState,
false);
239 mCurState.SetLastExited(blocked);
240 if (mCurState.GetCurrentLevel() ==
level) {
243 if (avoid !=
nullptr && mCurState.GetCurrentLevel() >
level) {
245 mPathScratch = mCurState;
246 while (mPathScratch.GetCurrentLevel() >
level && mPathScratch.Top() != avoid) {
249 if (mPathScratch.GetCurrentLevel() >
level) {
250 while (mCurState.GetCurrentLevel() >
level) {
262void VecGeomG4Navigator::updateCrossingFlags(
bool entering)
264 const int preLevel = mReloScratch.IsOutside() ? 0 :
static_cast<int>(mReloScratch.GetCurrentLevel());
265 const int curLevel = mCurState.IsOutside() ? 0 :
static_cast<int>(mCurState.GetCurrentLevel());
266 mPathScratch = mReloScratch;
267 mStepState = mCurState;
268 int la = preLevel, lb = curLevel;
277 while (la > 0 && !mPathScratch.HasSamePathAsOther(mStepState)) {
282 fExitedMother = !entering || la < preLevel;
283 fEnteredDaughter = entering || la < curLevel;
287 const G4ThreeVector* direction,
288 const G4bool relativeSearch,
const G4bool)
292 const bool onBoundary = relativeSearch && (fWasLimitedByGeometry || samePoint(point, mNextPoint));
293 const bool crossing = onBoundary && (mWouldEnter || mWouldExit) && direction !=
nullptr;
294 const bool entering = crossing && mWouldEnter;
296 const V3 dir = direction !=
nullptr ?
toDir(*direction) :
V3(0., 0., 0.);
297 const double push = (onBoundary && direction !=
nullptr) ? boundaryPush(p, dir) : 0.;
298 const V3 q = p + push * dir;
300 fWasLimitedByGeometry =
false;
301 fEnteredDaughter =
false;
302 fExitedMother =
false;
303 mLocatedOnBoundary =
false;
304 mLastLocatedPoint = point;
305 mSafetyOrig = kNoPoint;
308 if (!relativeSearch) {
311 }
else if (topOf(mCurState) ==
nullptr) {
313 }
else if (entering) {
315 mReloScratch = mCurState;
316 mCurState = mNextState;
317 auto const* daughter = mCurState.Top();
319 vecgeom::Transformation3D
m;
320 mCurState.TopMatrix(
m);
321 vecgeom::GlobalLocator::LocateGlobalPoint(daughter, daughter->GetTransformation()->Transform(
m.Transform(q)),
323 mLocatedOnBoundary =
true;
324 }
else if (crossing) {
328 mReloScratch = mCurState;
329 if (mCurState.GetCurrentLevel() <= 1) {
332 vecgeom::Transformation3D
m;
333 mCurState.TopMatrix(
m);
334 vecgeom::GlobalLocator::RelocatePointFromPathForceDifferent(
m.Transform(q), mCurState);
335 mReloScratch.SetLastExited();
336 mCurState.SetLastExited(mReloScratch.GetLastExitedState());
337 setExited(mReloScratch,
true);
339 mLocatedOnBoundary =
true;
342 vecgeom::Transformation3D
m;
343 mCurState.TopMatrix(
m);
344 vecgeom::GlobalLocator::RelocatePointFromPath(
m.Transform(q), mCurState);
345 if (topOf(mCurState) ==
nullptr) {
348 mLocatedOnBoundary = onBoundary;
351 leaveFlushVolumes(q, dir, entering ?
static_cast<int>(mNextState.GetCurrentLevel()) : 0,
352 entering ? nullptr : topOf(mReloScratch));
353 updateCrossingFlags(entering);
357 return updateG4History();
361 const G4TouchableHistory& history)
365 fWasLimitedByGeometry =
false;
366 fEnteredDaughter =
false;
367 fExitedMother =
false;
370 mLocatedOnBoundary =
false;
371 mLastLocatedPoint = point;
372 mSafetyOrig = kNoPoint;
374 fHistory = *history.GetHistory();
378 vecgeom::Transformation3D
m;
379 mCurState.TopMatrix(
m);
380 if (mCurState.Top()->GetUnplacedVolume()->Contains(
m.Transform(p))) {
383 ++mRelocatedResumeCount;
384 vecgeom::GlobalLocator::RelocatePointFromPath(
m.Transform(p), mCurState);
385 if (topOf(mCurState) ==
nullptr) {
390 if (++mUnmappableHistoryCount <= 10) {
391 LOG(warning) <<
"VecGeom navigation: a touchable matches no VecGeom path; locating from the world";
398 mHistoryState = mCurState;
399 mHistoryValid =
true;
400 return fHistory.GetTopVolume();
402 mHistoryValid =
false;
403 return updateG4History();
410 mLastLocatedPoint = position;
411 mLocatedOnBoundary =
false;
414 fEnteredDaughter =
false;
415 fExitedMother =
false;
424 if (mLocatedOnBoundary && samePoint(globalPoint, mLastLocatedPoint)) {
427 if ((mWouldEnter || mWouldExit) && samePoint(globalPoint, mNextPoint)) {
430 if (samePoint(globalPoint, mSafetyOrig)) {
433 auto const*
top = topOf(mCurState);
434 if (
top ==
nullptr) {
437 auto const* estimator =
top->GetLogicalVolume()->GetSafetyEstimator();
438 if (estimator ==
nullptr) {
441 double safety = estimator->ComputeSafety(
toVG(globalPoint), mCurState);
443 ++mNegativeSafetyCount;
446 mSafetyOrig = globalPoint;
447 mLastSafety = safety *
kVGToG4;
457bool VecGeomG4Navigator::computeExitNormal(
const G4ThreeVector& point,
V3& globalNormal)
const
459 if (!mNormalValid || topOf(mNormalState) ==
nullptr) {
462 vecgeom::Transformation3D
m;
463 mNormalState.TopMatrix(
m);
464 V3 localNormal(0., 0., 0.);
465 mNormalState.Top()->GetUnplacedVolume()->Normal(
m.Transform(
toVG(point)), localNormal);
466 globalNormal =
m.InverseTransformDirection(localNormal);
467 const double mag = globalNormal.Mag();
473 globalNormal = -globalNormal;
481 if (!computeExitNormal(mNormalPoint,
n) || topOf(mCurState) ==
nullptr) {
483 if (
valid !=
nullptr) {
486 return G4ThreeVector();
488 vecgeom::Transformation3D
m;
489 mCurState.TopMatrix(
m);
490 const auto local =
m.TransformDirection(
n);
491 if (
valid !=
nullptr) {
494 return G4ThreeVector(local[0], local[1], local[2]);
500 if (!computeExitNormal(point,
n)) {
502 if (
valid !=
nullptr) {
505 return G4ThreeVector();
507 if (
valid !=
nullptr) {
510 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 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