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)) {
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 }
158 const bool boundaryLimited = vgStep < limit;
159 G4double step = std::max(vgStep, 0.) * kVGToG4;
160 const bool entering = mStepState.GetCurrentLevel() > mCurState.GetCurrentLevel();
161
162 // A track that is not moving, handled as G4Navigator does: after ten zero steps the step is
163 // lengthened by 100 kCarTolerance, after twenty-five the event is aborted.
164 if (step < 0.05 * kCarTolerance) {
165 ++mZeroStepCount;
166 if (++mNzeroSteps >= kActionThresholdNoZeroSteps) {
167 ++mStuckPushCount;
168 step += 100. * kCarTolerance;
169 if (mNzeroSteps >= kAbandonThresholdNoZeroSteps) {
170 ++mAbandonCount;
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.";
175 mNzeroSteps = 0;
176 G4Exception("VecGeomG4Navigator::ComputeStep()", "GeomNav0003", EventMustBeAborted, msg.str().c_str());
177 }
178 }
179 } else {
180 mNzeroSteps = 0;
181 }
182
183 if (boundaryLimited) {
184 mWouldEnter = entering;
185 mWouldExit = !entering;
186 mNextPoint = globalPoint + step * direction;
187 if (entering) {
188 mNextState = mStepState;
189 }
190 // The surface this step ends on, for the exit normal: the entered daughter's or our own.
191 mNormalState = entering ? mNextState : mCurState;
192 mNormalEnter = entering;
193 mNormalPoint = mNextPoint;
194 mNormalValid = true;
195 } else {
196 step = kInfinity;
197 mNormalValid = false;
198 }
199 return step;
200}
201
209void VecGeomG4Navigator::leaveFlushVolumes(const V3& point, const V3& dir, int minLevel,
210 vecgeom::VPlacedVolume const* avoid)
211{
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.) {
218 break;
219 }
220 left = mCurState.Top();
221 mCurState.SetLastExited();
222 setExited(mCurState, false);
223 if (mCurState.GetCurrentLevel() <= 1) {
224 mCurState.Clear(); // nothing to travel in even in the world: the track left it
225 return;
226 }
227 mCurState.Pop();
228 }
229 if (left == nullptr) {
230 return;
231 }
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();
237 mCurState.Pop();
238 vecgeom::GlobalLocator::LocateGlobalPointExclVolume(mother, left, m.Transform(point), mCurState, false);
239 mCurState.SetLastExited(blocked);
240 if (mCurState.GetCurrentLevel() == level) {
241 return; // no daughter holds the point
242 }
243 if (avoid != nullptr && mCurState.GetCurrentLevel() > level) {
244 // Undo a descent into the volume the crossing exited, at whatever depth the search put it.
245 mPathScratch = mCurState;
246 while (mPathScratch.GetCurrentLevel() > level && mPathScratch.Top() != avoid) {
247 mPathScratch.Pop();
248 }
249 if (mPathScratch.GetCurrentLevel() > level) {
250 while (mCurState.GetCurrentLevel() > level) {
251 mCurState.Pop();
252 }
253 return;
254 }
255 }
256 }
257}
258
262void VecGeomG4Navigator::updateCrossingFlags(bool entering)
263{
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;
269 while (la > lb) {
270 mPathScratch.Pop();
271 --la;
272 }
273 while (lb > la) {
274 mStepState.Pop();
275 --lb;
276 }
277 while (la > 0 && !mPathScratch.HasSamePathAsOther(mStepState)) {
278 mPathScratch.Pop();
279 mStepState.Pop();
280 --la;
281 }
282 fExitedMother = !entering || la < preLevel;
283 fEnteredDaughter = entering || la < curLevel;
284}
285
286G4VPhysicalVolume* VecGeomG4Navigator::LocateGlobalPointAndSetup(const G4ThreeVector& point,
287 const G4ThreeVector* direction,
288 const G4bool relativeSearch, const G4bool)
289{
290 // A boundary is being crossed when Geant4 says the last step was limited by the geometry (or the
291 // point is where the last ComputeStep put the boundary) and that step found one.
292 const bool onBoundary = relativeSearch && (fWasLimitedByGeometry || samePoint(point, mNextPoint));
293 const bool crossing = onBoundary && (mWouldEnter || mWouldExit) && direction != nullptr;
294 const bool entering = crossing && mWouldEnter;
295 const V3 p = toVG(point);
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;
299
300 fWasLimitedByGeometry = false;
301 fEnteredDaughter = false;
302 fExitedMother = false;
303 mLocatedOnBoundary = false;
304 mLastLocatedPoint = point;
305 mSafetyOrig = kNoPoint;
306 clearLastExited();
307
308 if (!relativeSearch) {
309 mNzeroSteps = 0; // a new track: nothing of the previous one applies
310 locateFromWorld(p);
311 } else if (topOf(mCurState) == nullptr) {
312 locateFromWorld(p);
313 } else if (entering) {
314 // Into the daughter the step hit, then down inside it.
315 mReloScratch = mCurState;
316 mCurState = mNextState;
317 auto const* daughter = mCurState.Top();
318 mCurState.Pop();
319 vecgeom::Transformation3D m;
320 mCurState.TopMatrix(m);
321 vecgeom::GlobalLocator::LocateGlobalPoint(daughter, daughter->GetTransformation()->Transform(m.Transform(q)),
322 mCurState, false);
323 mLocatedOnBoundary = true;
324 } else if (crossing) {
325 // Out of the current volume: up until the point is contained, then down, never back into the
326 // volume just left; that volume is blocked at zero distance in the next ComputeStep while the
327 // direction points away from it, as G4Navigator's fBlockedPhysicalVolume.
328 mReloScratch = mCurState;
329 if (mCurState.GetCurrentLevel() <= 1) {
330 mCurState.Clear(); // left the world
331 } else {
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);
338 }
339 mLocatedOnBoundary = true;
340 } else {
341 // Anywhere else: from the current path, up until contained, then down.
342 vecgeom::Transformation3D m;
343 mCurState.TopMatrix(m);
344 vecgeom::GlobalLocator::RelocatePointFromPath(m.Transform(q), mCurState);
345 if (topOf(mCurState) == nullptr) {
346 locateFromWorld(p);
347 }
348 mLocatedOnBoundary = onBoundary;
349 }
350 if (crossing) {
351 leaveFlushVolumes(q, dir, entering ? static_cast<int>(mNextState.GetCurrentLevel()) : 0,
352 entering ? nullptr : topOf(mReloScratch));
353 updateCrossingFlags(entering);
354 }
355 mWouldEnter = false;
356 mWouldExit = false;
357 return updateG4History();
358}
359
360G4VPhysicalVolume* VecGeomG4Navigator::ResetHierarchyAndLocate(const G4ThreeVector& point, const G4ThreeVector&,
361 const G4TouchableHistory& history)
362{
363 // A track resumes from a stored touchable, usually a secondary starting where its parent's step
364 // ended. The touchable names its volume; it is kept unless it does not hold the point.
365 fWasLimitedByGeometry = false;
366 fEnteredDaughter = false;
367 fExitedMother = false;
368 mWouldEnter = false;
369 mWouldExit = false;
370 mLocatedOnBoundary = false;
371 mLastLocatedPoint = point;
372 mSafetyOrig = kNoPoint;
373 mNzeroSteps = 0;
374 fHistory = *history.GetHistory();
375 const V3 p = toVG(point);
376 bool kept = false;
377 if (stateFromHistory(mCurState)) {
378 vecgeom::Transformation3D m;
379 mCurState.TopMatrix(m);
380 if (mCurState.Top()->GetUnplacedVolume()->Contains(m.Transform(p))) {
381 kept = true;
382 } else {
383 ++mRelocatedResumeCount;
384 vecgeom::GlobalLocator::RelocatePointFromPath(m.Transform(p), mCurState);
385 if (topOf(mCurState) == nullptr) {
386 locateFromWorld(p);
387 }
388 }
389 } else {
390 if (++mUnmappableHistoryCount <= 10) {
391 LOG(warning) << "VecGeom navigation: a touchable matches no VecGeom path; locating from the world";
392 }
393 locateFromWorld(p);
394 }
395 clearLastExited();
396 if (kept) {
397 // The touchable's history is the path of the state: keep it rather than rebuild it.
398 mHistoryState = mCurState;
399 mHistoryValid = true;
400 return fHistory.GetTopVolume();
401 }
402 mHistoryValid = false;
403 return updateG4History();
404}
405
406void VecGeomG4Navigator::LocateGlobalPointWithinVolume(const G4ThreeVector& position)
407{
408 // The caller guarantees the point is in the current volume, so the state stays; only what
409 // described the last crossing is dropped, as in G4Navigator.
410 mLastLocatedPoint = position;
411 mLocatedOnBoundary = false;
412 mWouldEnter = false;
413 mWouldExit = false;
414 fEnteredDaughter = false;
415 fExitedMother = false;
416 clearLastExited();
417}
418
419G4double VecGeomG4Navigator::ComputeSafety(const G4ThreeVector& globalPoint, const G4double, const G4bool)
420{
421 if (mZeroSafety) {
422 return 0.;
423 }
424 if (mLocatedOnBoundary && samePoint(globalPoint, mLastLocatedPoint)) {
425 return 0.;
426 }
427 if ((mWouldEnter || mWouldExit) && samePoint(globalPoint, mNextPoint)) {
428 return 0.;
429 }
430 if (samePoint(globalPoint, mSafetyOrig)) {
431 return mLastSafety;
432 }
433 auto const* top = topOf(mCurState);
434 if (top == nullptr) {
435 return 0.;
436 }
437 auto const* estimator = top->GetLogicalVolume()->GetSafetyEstimator();
438 if (estimator == nullptr) {
439 return 0.;
440 }
441 double safety = estimator->ComputeSafety(toVG(globalPoint), mCurState);
442 if (safety < 0.) {
443 ++mNegativeSafetyCount;
444 safety = 0.;
445 }
446 mSafetyOrig = globalPoint;
447 mLastSafety = safety * kVGToG4;
448 return mLastSafety;
449}
450
457bool VecGeomG4Navigator::computeExitNormal(const G4ThreeVector& point, V3& globalNormal) const
458{
459 if (!mNormalValid || topOf(mNormalState) == nullptr) {
460 return false;
461 }
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();
468 if (!(mag > 0.5)) {
469 return false;
470 }
471 globalNormal /= mag;
472 if (mNormalEnter) {
473 globalNormal = -globalNormal;
474 }
475 return true;
476}
477
479{
480 V3 n;
481 if (!computeExitNormal(mNormalPoint, n) || topOf(mCurState) == nullptr) {
482 ++mNoNormalCount;
483 if (valid != nullptr) {
484 *valid = false;
485 }
486 return G4ThreeVector();
487 }
488 vecgeom::Transformation3D m;
489 mCurState.TopMatrix(m);
490 const auto local = m.TransformDirection(n);
491 if (valid != nullptr) {
492 *valid = true;
493 }
494 return G4ThreeVector(local[0], local[1], local[2]);
495}
496
497G4ThreeVector VecGeomG4Navigator::GetGlobalExitNormal(const G4ThreeVector& point, G4bool* valid)
498{
499 V3 n;
500 if (!computeExitNormal(point, n)) {
501 ++mNoNormalCount;
502 if (valid != nullptr) {
503 *valid = false;
504 }
505 return G4ThreeVector();
506 }
507 if (valid != nullptr) {
508 *valid = true;
509 }
510 return G4ThreeVector(n[0], n[1], n[2]);
511}
512
513} // namespace o2::simsetup
bool valid
uint32_t c
Definition RawData.h:2
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
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