Project
Loading...
Searching...
No Matches
VecGeomG4Navigator.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include "VecGeomG4Navigator.h"
13
14#include "G4Exception.hh"
15#include "G4TouchableHistory.hh"
16#include "G4VPhysicalVolume.hh"
17
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>
25
26#include <fairlogger/Logger.h>
27
28#include <algorithm>
29#include <cmath>
30#include <limits>
31#include <sstream>
32
33namespace
34{
35const G4ThreeVector kNoPoint(-1e8, -1e8, -1e8);
36
37bool samePoint(const G4ThreeVector& a, const G4ThreeVector& b) { return a.diff2(b) < 1e-20; }
38
39vecgeom::VPlacedVolume const* topOf(vecgeom::NavigationState const& st)
40{
41 return st.IsOutside() ? nullptr : st.Top();
42}
43} // namespace
44
45namespace o2::simsetup
46{
47
48VecGeomG4Navigator::VecGeomG4Navigator(VecGeomG4Map const& map, double pushDepth, bool zeroSafety)
49 : VecGeomG4NavigatorBase(map), mPushDepth(pushDepth), mZeroSafety(zeroSafety)
50{
51 mEmptyState.Clear();
52}
53
55{
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;
60}
61
62G4VPhysicalVolume* VecGeomG4Navigator::updateG4History()
63{
64 // The history is a function of the VecGeom state alone, so it is rebuilt only when that changed.
65 if (mHistoryValid && mCurState.HasSamePathAsOther(mHistoryState)) {
66 return fHistory.GetTopVolume();
67 }
68 mHistoryState = mCurState;
69 mHistoryValid = true;
70 return historyFromState(mCurState);
71}
72
73void VecGeomG4Navigator::locateFromWorld(const V3& point)
74{
75 mCurState.Clear();
76 vecgeom::GlobalLocator::LocateGlobalPoint(vecgeom::GeoManager::Instance().GetWorld(), point, mCurState, true);
77}
78
83double VecGeomG4Navigator::boundaryPush(const V3& point, const V3& dir)
84{
85 constexpr double kMaxPush = 1.e-4; // cm along the direction
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();
88
89 // The face just crossed: the entered daughter's, or the current volume's own.
90 vecgeom::NavigationState const& st = mWouldEnter ? mNextState : mCurState;
91 double cosn = 1.;
92 mPushNormalValid = false;
93 if (!st.IsOutside() && st.Top() != nullptr) {
94 vecgeom::Transformation3D m;
95 st.TopMatrix(m);
96 V3 n;
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) {
100 cosn = c;
101 }
102 if (n.Mag2() > 0.5) {
103 mPushNormal = m.InverseTransformDirection(n);
104 mPushNormalValid = true;
105 }
106 }
107 return std::max(rounding, std::min(mPushDepth / cosn, kMaxPush));
108}
109
110G4double VecGeomG4Navigator::ComputeStep(const G4ThreeVector& globalPoint, const G4ThreeVector& direction,
111 const G4double proposedStepLength, G4double& newSafety)
112{
113 newSafety = 0.;
114 mWouldEnter = false;
115 mWouldExit = false;
116
117 auto const* top = topOf(mCurState);
118 if (top == nullptr) { // the track is outside the world
119 return kInfinity;
120 }
121 auto const* navigator = top->GetLogicalVolume()->GetNavigator();
122
123 // On the point a boundary locate left the track on, the safety is zero; a point seen before
124 // reuses its safety. Otherwise the navigator computes it with the step.
125 bool calcSafety = !mZeroSafety && !(mLocatedOnBoundary && samePoint(globalPoint, mLastLocatedPoint));
126 if (calcSafety && samePoint(globalPoint, mSafetyOrig) && mLastSafety < mLastSafetyLimit) {
127 calcSafety = false;
128 newSafety = mLastSafety;
129 }
130
131 // The step is computed on a copy. The state stays as the locate left it for every call until the
132 // next locate, which is what the field propagator relies on when it calls this from trial points
133 // along the curve. The volume the last crossing left is blocked in the first call only, and only
134 // while the direction points away from it; a track turning back into it must see its boundary.
135 mStepState = mCurState;
136 if (mExitBlockPending) {
137 const bool block = samePoint(globalPoint, mLastLocatedPoint) &&
138 (mExitNormalFromPush ? directionLeaves(mPushNormal, toDir(direction))
139 : directionLeaves(mExitedState, toVG(globalPoint), toDir(direction)));
140 if (!block) {
141 mStepState.SetLastExited(mEmptyState.GetLastExitedState());
142 }
143 clearLastExited();
144 }
145 const double limit = std::min(proposedStepLength * kG4ToVG, static_cast<double>(vecgeom::kInfLength));
146 double safety = 0.;
147 double vgStep = navigator->ComputeStepAndSafety(toVG(globalPoint), toDir(direction), limit, mStepState, calcSafety,
148 safety, true);
149 if (calcSafety) {
150 if (safety < 0.) {
151 ++mNegativeSafetyCount;
152 safety = 0.;
153 }
154 newSafety = safety * kVGToG4;
155 mSafetyOrig = globalPoint;
156 mLastSafety = newSafety;
157 mLastSafetyLimit = kInfinity;
158 }
159 const bool boundaryLimited = vgStep < limit;
160 G4double step = std::max(vgStep, 0.) * kVGToG4;
161 const bool entering = mStepState.GetCurrentLevel() > mCurState.GetCurrentLevel();
162
163 // A track that is not moving, handled as G4Navigator does: after ten zero steps the step is
164 // lengthened by 100 kCarTolerance, after twenty-five the event is aborted.
165 if (step < 0.05 * kCarTolerance) {
166 ++mZeroStepCount;
167 if (++mNzeroSteps >= kActionThresholdNoZeroSteps) {
168 ++mStuckPushCount;
169 step += 100. * kCarTolerance;
170 if (mNzeroSteps >= kAbandonThresholdNoZeroSteps) {
171 ++mAbandonCount;
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.";
176 mNzeroSteps = 0;
177 G4Exception("VecGeomG4Navigator::ComputeStep()", "GeomNav0003", EventMustBeAborted, msg.str().c_str());
178 }
179 }
180 } else {
181 mNzeroSteps = 0;
182 }
183
184 if (boundaryLimited) {
185 mWouldEnter = entering;
186 mWouldExit = !entering;
187 mNextPoint = globalPoint + step * direction;
188 if (entering) {
189 mNextState = mStepState;
190 }
191 // The surface this step ends on, for the exit normal: the entered daughter's or our own.
192 mNormalState = entering ? mNextState : mCurState;
193 mNormalEnter = entering;
194 mNormalPoint = mNextPoint;
195 mNormalValid = true;
196 } else {
197 step = kInfinity;
198 mNormalValid = false;
199 }
200 return step;
201}
202
210void VecGeomG4Navigator::leaveFlushVolumes(const V3& point, const V3& dir, int minLevel,
211 vecgeom::VPlacedVolume const* avoid)
212{
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.) {
219 break;
220 }
221 left = mCurState.Top();
222 mCurState.SetLastExited();
223 setExited(mCurState, false);
224 if (mCurState.GetCurrentLevel() <= 1) {
225 mCurState.Clear(); // nothing to travel in even in the world: the track left it
226 return;
227 }
228 mCurState.Pop();
229 }
230 if (left == nullptr) {
231 return;
232 }
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();
238 mCurState.Pop();
239 vecgeom::GlobalLocator::LocateGlobalPointExclVolume(mother, left, m.Transform(point), mCurState, false);
240 mCurState.SetLastExited(blocked);
241 if (mCurState.GetCurrentLevel() == level) {
242 return; // no daughter holds the point
243 }
244 if (avoid != nullptr && mCurState.GetCurrentLevel() > level) {
245 // Undo a descent into the volume the crossing exited, at whatever depth the search put it.
246 mPathScratch = mCurState;
247 while (mPathScratch.GetCurrentLevel() > level && mPathScratch.Top() != avoid) {
248 mPathScratch.Pop();
249 }
250 if (mPathScratch.GetCurrentLevel() > level) {
251 while (mCurState.GetCurrentLevel() > level) {
252 mCurState.Pop();
253 }
254 return;
255 }
256 }
257 }
258}
259
263void VecGeomG4Navigator::updateCrossingFlags(bool entering)
264{
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;
270 while (la > lb) {
271 mPathScratch.Pop();
272 --la;
273 }
274 while (lb > la) {
275 mStepState.Pop();
276 --lb;
277 }
278 while (la > 0 && !mPathScratch.HasSamePathAsOther(mStepState)) {
279 mPathScratch.Pop();
280 mStepState.Pop();
281 --la;
282 }
283 fExitedMother = !entering || la < preLevel;
284 fEnteredDaughter = entering || la < curLevel;
285}
286
287G4VPhysicalVolume* VecGeomG4Navigator::LocateGlobalPointAndSetup(const G4ThreeVector& point,
288 const G4ThreeVector* direction,
289 const G4bool relativeSearch, const G4bool)
290{
291 // A boundary is being crossed when Geant4 says the last step was limited by the geometry (or the
292 // point is where the last ComputeStep put the boundary) and that step found one.
293 const bool onBoundary = relativeSearch && (fWasLimitedByGeometry || samePoint(point, mNextPoint));
294 const bool crossing = onBoundary && (mWouldEnter || mWouldExit) && direction != nullptr;
295 const bool entering = crossing && mWouldEnter;
296 const V3 p = toVG(point);
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;
300
301 fWasLimitedByGeometry = false;
302 fEnteredDaughter = false;
303 fExitedMother = false;
304 mLocatedOnBoundary = false;
305 mLastLocatedPoint = point;
306 mSafetyOrig = kNoPoint;
307 clearLastExited();
308
309 if (!relativeSearch) {
310 mNzeroSteps = 0; // a new track: nothing of the previous one applies
311 locateFromWorld(p);
312 } else if (topOf(mCurState) == nullptr) {
313 locateFromWorld(p);
314 } else if (entering) {
315 // Into the daughter the step hit, then down inside it.
316 mReloScratch = mCurState;
317 mCurState = mNextState;
318 // mNextState is a copy of the step state and can still carry the block of the volume the previous
319 // crossing left, which was meant for the first step after that exit only.
320 mCurState.SetLastExited(mEmptyState.GetLastExitedState());
321 auto const* daughter = mCurState.Top();
322 mCurState.Pop();
323 vecgeom::Transformation3D m;
324 mCurState.TopMatrix(m);
325 vecgeom::GlobalLocator::LocateGlobalPoint(daughter, daughter->GetTransformation()->Transform(m.Transform(q)),
326 mCurState, false);
327 mLocatedOnBoundary = true;
328 } else if (crossing) {
329 // Out of the current volume: up until the point is contained, then down, never back into the
330 // volume just left; that volume is blocked at zero distance in the next ComputeStep while the
331 // direction points away from it, as G4Navigator's fBlockedPhysicalVolume.
332 mReloScratch = mCurState;
333 if (mCurState.GetCurrentLevel() <= 1) {
334 mCurState.Clear(); // left the world
335 } else {
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);
342 }
343 mLocatedOnBoundary = true;
344 } else {
345 // Anywhere else: from the current path, up until contained, then down.
346 vecgeom::Transformation3D m;
347 mCurState.TopMatrix(m);
348 vecgeom::GlobalLocator::RelocatePointFromPath(m.Transform(q), mCurState);
349 if (topOf(mCurState) == nullptr) {
350 locateFromWorld(p);
351 }
352 mLocatedOnBoundary = onBoundary;
353 }
354 if (crossing) {
355 leaveFlushVolumes(q, dir, entering ? static_cast<int>(mNextState.GetCurrentLevel()) : 0,
356 entering ? nullptr : topOf(mReloScratch));
357 updateCrossingFlags(entering);
358 }
359 mWouldEnter = false;
360 mWouldExit = false;
361 return updateG4History();
362}
363
364G4VPhysicalVolume* VecGeomG4Navigator::ResetHierarchyAndLocate(const G4ThreeVector& point, const G4ThreeVector&,
365 const G4TouchableHistory& history)
366{
367 // A track resumes from a stored touchable, usually a secondary starting where its parent's step
368 // ended. The touchable names its volume; it is kept unless it does not hold the point.
369 fWasLimitedByGeometry = false;
370 fEnteredDaughter = false;
371 fExitedMother = false;
372 mWouldEnter = false;
373 mWouldExit = false;
374 mLocatedOnBoundary = false;
375 mLastLocatedPoint = point;
376 mSafetyOrig = kNoPoint;
377 mNzeroSteps = 0;
378 fHistory = *history.GetHistory();
379 const V3 p = toVG(point);
380 bool kept = false;
381 if (stateFromHistory(mCurState)) {
382 vecgeom::Transformation3D m;
383 mCurState.TopMatrix(m);
384 if (mCurState.Top()->GetUnplacedVolume()->Contains(m.Transform(p))) {
385 kept = true;
386 } else {
387 ++mRelocatedResumeCount;
388 vecgeom::GlobalLocator::RelocatePointFromPath(m.Transform(p), mCurState);
389 if (topOf(mCurState) == nullptr) {
390 locateFromWorld(p);
391 }
392 }
393 } else {
394 if (++mUnmappableHistoryCount <= 10) {
395 LOG(warning) << "VecGeom navigation: a touchable matches no VecGeom path; locating from the world";
396 }
397 locateFromWorld(p);
398 }
399 clearLastExited();
400 if (kept) {
401 // The touchable's history is the path of the state: keep it rather than rebuild it.
402 mHistoryState = mCurState;
403 mHistoryValid = true;
404 return fHistory.GetTopVolume();
405 }
406 mHistoryValid = false;
407 return updateG4History();
408}
409
411{
412 // The caller guarantees the point is in the current volume, so the state stays; only what
413 // described the last crossing is dropped, as in G4Navigator.
414 mLastLocatedPoint = position;
415 mLocatedOnBoundary = false;
416 mWouldEnter = false;
417 mWouldExit = false;
418 fEnteredDaughter = false;
419 fExitedMother = false;
420 clearLastExited();
421}
422
423G4double VecGeomG4Navigator::ComputeSafety(const G4ThreeVector& globalPoint, const G4double proposedMaxLength,
424 const G4bool)
425{
426 if (mZeroSafety) {
427 return 0.;
428 }
429 if (mLocatedOnBoundary && samePoint(globalPoint, mLastLocatedPoint)) {
430 return 0.;
431 }
432 if ((mWouldEnter || mWouldExit) && samePoint(globalPoint, mNextPoint)) {
433 return 0.;
434 }
435 if (samePoint(globalPoint, mSafetyOrig) && (mLastSafety < mLastSafetyLimit || mLastSafety >= proposedMaxLength)) {
436 return mLastSafety;
437 }
438 if (topOf(mCurState) == nullptr) {
439 return 0.;
440 }
441 // Every point closer to the last safety origin than its safety is in the same volume, so s0 - d is a
442 // valid safety there; it is used when it covers the caller's bound. Every locate drops the cache.
443 const double rest = mLastSafety - std::sqrt(globalPoint.diff2(mSafetyOrig));
444 if (rest >= proposedMaxLength) {
445 return rest;
446 }
447 double safety = boundedSafety(mCurState, globalPoint, proposedMaxLength);
448 if (safety < 0.) {
449 ++mNegativeSafetyCount;
450 safety = 0.;
451 }
452 mSafetyOrig = globalPoint;
453 mLastSafety = safety;
454 mLastSafetyLimit = proposedMaxLength;
455 return mLastSafety;
456}
457
464bool VecGeomG4Navigator::computeExitNormal(const G4ThreeVector& point, V3& globalNormal) const
465{
466 if (!mNormalValid || topOf(mNormalState) == nullptr) {
467 return false;
468 }
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();
475 if (!(mag > 0.5)) {
476 return false;
477 }
478 globalNormal /= mag;
479 if (mNormalEnter) {
480 globalNormal = -globalNormal;
481 }
482 return true;
483}
484
486{
487 V3 n;
488 if (!computeExitNormal(mNormalPoint, n) || topOf(mCurState) == nullptr) {
489 ++mNoNormalCount;
490 if (valid != nullptr) {
491 *valid = false;
492 }
493 return G4ThreeVector();
494 }
495 vecgeom::Transformation3D m;
496 mCurState.TopMatrix(m);
497 const auto local = m.TransformDirection(n);
498 if (valid != nullptr) {
499 *valid = true;
500 }
501 return G4ThreeVector(local[0], local[1], local[2]);
502}
503
504G4ThreeVector VecGeomG4Navigator::GetGlobalExitNormal(const G4ThreeVector& point, G4bool* valid)
505{
506 V3 n;
507 if (!computeExitNormal(point, n)) {
508 ++mNoNormalCount;
509 if (valid != nullptr) {
510 *valid = false;
511 }
512 return G4ThreeVector();
513 }
514 if (valid != nullptr) {
515 *valid = true;
516 }
517 return G4ThreeVector(n[0], n[1], n[2]);
518}
519
520} // namespace o2::simsetup
bool valid
uint32_t c
Definition RawData.h:2
Acts::Vector3 position
vecgeom::Vector3D< double > V3
benchmark::State & st
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)
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(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 n
Definition glcorearb.h:1982
const GLfloat * m
Definition glcorearb.h:4066
GLdouble GLdouble GLdouble GLdouble top
Definition glcorearb.h:4077
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint left
Definition glcorearb.h:1979
GLint level
Definition glcorearb.h:275
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
uint64_t const void const *restrict const msg
Definition x9.h:153