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
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate increase X h configStandalone Camera window renderer OpenGL display display_camera Orient the camera such that the y axis is always upwards Full Screen MultiSample Anti Downsampling Anti Enable Vertical Sync Target frame or if the backend cannot limit to the refresh Use OpenGL indirect draws to reduce number of draw calls Display h configStandalone Vulkan display display_vulkan Have uniform buffers in host accessible device memory h configStandalone Internal display display_internal Number of updates the Qt GUI may send ahead of the last one the display has before it waits for the display Opacity of the console background Number of console lines kept
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
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