EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Navigator.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file Navigator.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2016-2020 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
12 #include "Acts/Geometry/Layer.hpp"
19 #include "Acts/Utilities/Units.hpp"
20 
21 #include <iomanip>
22 #include <iterator>
23 #include <sstream>
24 #include <string>
25 
26 #include <boost/algorithm/string.hpp>
27 
28 namespace Acts {
29 
30 using namespace Acts::UnitLiterals;
31 
37 template <typename object_t>
41 
44 
45  // How to resolve the geometry
47  bool resolveSensitive = true;
49  bool resolveMaterial = true;
51  bool resolvePassive = false;
52 
54  const object_t* startObject = nullptr;
56  const object_t* endObject = nullptr;
57 
59  const Surface* targetSurface = nullptr;
60 
62  double pathLimit = std::numeric_limits<double>::max();
63 
67  double overstepLimit = -1_um;
68 
77  bool resolves = true, bool resolvem = true,
78  bool resolvep = false, const object_t* sobject = nullptr,
79  const object_t* eobject = nullptr)
80  : navDir(ndir),
81  boundaryCheck(std::move(bcheck)),
82  resolveSensitive(resolves),
83  resolveMaterial(resolvem),
84  resolvePassive(resolvep),
85  startObject(sobject),
86  endObject(eobject),
87  pathLimit(ndir * std::numeric_limits<double>::max()),
88  overstepLimit(-1_um) {}
89 };
90 
114 class Navigator {
115  public:
116  using Surfaces = std::vector<const Surface*>;
117  using SurfaceIter = std::vector<const Surface*>::iterator;
118 
119  using NavigationSurfaces = std::vector<SurfaceIntersection>;
120  using NavigationSurfaceIter = NavigationSurfaces::iterator;
121 
122  using NavigationLayers = std::vector<LayerIntersection>;
123  using NavigationLayerIter = NavigationLayers::iterator;
124 
125  using NavigationBoundaries = std::vector<BoundaryIntersection>;
126  using NavigationBoundaryIter = NavigationBoundaries::iterator;
127 
128  using ExternalSurfaces = std::multimap<const Layer*, const Surface*>;
129 
131  enum struct Stage : int {
132  undefined = 0,
133  surfaceTarget = 1,
134  layerTarget = 2,
135  boundaryTarget = 3
136  };
137 
141  Navigator(std::shared_ptr<const TrackingGeometry> tGeometry = nullptr)
142  : trackingGeometry(std::move(tGeometry)) {}
143 
145  std::shared_ptr<const TrackingGeometry> trackingGeometry;
146 
149 
152  bool resolveSensitive = true;
154  bool resolveMaterial = true;
156  bool resolvePassive = false;
157 
163  struct State {
164  // Navigation on surface level
166  NavigationSurfaces navSurfaces = {};
168  NavigationSurfaceIter navSurfaceIter = navSurfaces.end();
169 
170  // Navigation on layer level
172  NavigationLayers navLayers = {};
174  NavigationLayerIter navLayerIter = navLayers.end();
175 
176  // Navigation on volume level
178  NavigationBoundaries navBoundaries = {};
180  NavigationBoundaryIter navBoundaryIter = navBoundaries.end();
181 
183  ExternalSurfaces externalSurfaces = {};
184 
186  const TrackingVolume* worldVolume = nullptr;
187 
189  const TrackingVolume* startVolume = nullptr;
191  const Layer* startLayer = nullptr;
193  const Surface* startSurface = nullptr;
195  const Surface* currentSurface = nullptr;
197  const TrackingVolume* currentVolume = nullptr;
199  const TrackingVolume* targetVolume = nullptr;
201  const Layer* targetLayer = nullptr;
203  const Surface* targetSurface = nullptr;
204 
206  bool startLayerResolved = false;
208  bool targetReached = false;
210  bool navigationBreak = false;
211  // The navigation stage (@todo: integrate break, target)
212  Stage navigationStage = Stage::undefined;
213  };
214 
234  template <typename propagator_state_t, typename stepper_t>
235  void status(propagator_state_t& state, const stepper_t& stepper) const {
236  const auto& logger = state.options.logger;
237 
238  // Check if the navigator is inactive
239  if (inactive(state, stepper)) {
240  return;
241  }
242 
243  // Set the navigation stage
244  state.navigation.navigationStage = Stage::undefined;
245 
246  // Call the navigation helper prior to actual navigation
247  ACTS_VERBOSE(volInfo(state) << "Entering navigator::status.");
248 
249  // (a) Pre-stepping call from propgator
250  if (not state.navigation.startVolume or not state.navigation.startSurface) {
251  // Initialize and return
252  initialize(state, stepper);
253  return;
254  }
255 
256  // Navigator status always starts without current surface
257  state.navigation.currentSurface = nullptr;
258 
259  // (b) Status call within propagation loop
260  // Try finding status of surfaces
261  if (status(state, stepper, state.navigation.navSurfaces,
262  state.navigation.navSurfaceIter)) {
263  ACTS_VERBOSE(volInfo(state) << "Status: in surface handling.");
264  if (state.navigation.currentSurface) {
265  ACTS_VERBOSE(volInfo(state)
266  << "On surface: switch forward or release.");
267  if (++state.navigation.navSurfaceIter ==
268  state.navigation.navSurfaces.end()) {
269  // this was the last surface, check if we have layers
270  if (!state.navigation.navLayers.empty()) {
271  ++state.navigation.navLayerIter;
272  } else {
273  // no layers, go to boundary
274  state.navigation.navigationStage = Stage::boundaryTarget;
275  return;
276  }
277  }
278  }
279  // Set the navigation stage to surface target
280  state.navigation.navigationStage = Stage::surfaceTarget;
281  ACTS_VERBOSE(volInfo(state) << "Staying focussed on surface.");
282  // Try finding status of layer
283  } else if (status(state, stepper, state.navigation.navLayers,
284  state.navigation.navLayerIter)) {
285  ACTS_VERBOSE(volInfo(state) << "Status: in layer handling.");
286  if (state.navigation.currentSurface != nullptr) {
287  ACTS_VERBOSE(volInfo(state) << "On layer: update layer information.");
288  if (resolveSurfaces(state, stepper)) {
289  // Set the navigation stage back to surface handling
290  state.navigation.navigationStage = Stage::surfaceTarget;
291  return;
292  }
293  } else {
294  // Set the navigation stage to layer target
295  state.navigation.navigationStage = Stage::layerTarget;
296  ACTS_VERBOSE(volInfo(state) << "Staying focussed on layer.");
297  }
298  // Try finding status of boundaries
299  } else if (status(state, stepper, state.navigation.navBoundaries,
300  state.navigation.navBoundaryIter)) {
301  ACTS_VERBOSE(volInfo(state) << "Status: in boundary handling.");
302 
303  // Are we on the boundary - then overwrite the stage
304  if (state.navigation.currentSurface != nullptr) {
305  // Set the navigation stage back to surface handling
306  ACTS_VERBOSE(volInfo(state)
307  << "On boundary: update volume information.");
308  // We are on a boundary, reset all information
309  state.navigation.navSurfaces.clear();
310  state.navigation.navSurfaceIter = state.navigation.navSurfaces.end();
311  state.navigation.navLayers.clear();
312  state.navigation.navLayerIter = state.navigation.navLayers.end();
313  // Update volume information
314  // get the attached volume information
315  auto boundary = state.navigation.navBoundaryIter->object;
316  state.navigation.currentVolume = boundary->attachedVolume(
317  state.geoContext, stepper.position(state.stepping),
318  stepper.direction(state.stepping), state.stepping.navDir);
319  // No volume anymore : end of known world
320  if (!state.navigation.currentVolume) {
321  ACTS_VERBOSE(
322  volInfo(state)
323  << "No more volume to progress to, stopping navigation.");
324  // Navigation break & release navigation stepping
325  state.navigation.navigationBreak = true;
326  stepper.releaseStepSize(state.stepping);
327  return;
328  } else {
329  ACTS_VERBOSE(volInfo(state) << "Volume updated.");
330  // Forget the bounday information
331  state.navigation.navBoundaries.clear();
332  state.navigation.navBoundaryIter =
333  state.navigation.navBoundaries.end();
334  }
335  } else {
336  // Set the navigation stage back to boundary target
337  state.navigation.navigationStage = Stage::boundaryTarget;
338  ACTS_VERBOSE(volInfo(state) << "Staying focussed on boundary.");
339  }
340  } else if (state.navigation.currentVolume ==
341  state.navigation.targetVolume) {
342  ACTS_VERBOSE(volInfo(state)
343  << "No further navigation action, proceed to target.");
344  // Set navigation break and release the navigation step size
345  state.navigation.navigationBreak = true;
346  stepper.releaseStepSize(state.stepping);
347  } else {
348  ACTS_VERBOSE(volInfo(state)
349  << "Status could not be determined - good luck.");
350  }
351  return;
352  }
353 
366  template <typename propagator_state_t, typename stepper_t>
367  void target(propagator_state_t& state, const stepper_t& stepper) const {
368  const auto& logger = state.options.logger;
369  // Check if the navigator is inactive
370  if (inactive(state, stepper)) {
371  return;
372  }
373 
374  // Call the navigation helper prior to actual navigation
375  ACTS_VERBOSE(volInfo(state) << "Entering navigator::target.");
376 
377  // Initialize the target and target volume
378  if (state.navigation.targetSurface and not state.navigation.targetVolume) {
379  // Find out about the target as much as you can
380  initializeTarget(state, stepper);
381  }
382 
383  // Try targeting the surfaces - then layers - then boundaries
384  if (state.navigation.navigationStage <= Stage::surfaceTarget and
385  targetSurfaces(state, stepper)) {
386  ACTS_VERBOSE(volInfo(state) << "Target set to next surface.");
387  } else if (state.navigation.navigationStage <= Stage::layerTarget and
388  targetLayers(state, stepper)) {
389  ACTS_VERBOSE(volInfo(state) << "Target set to next layer.");
390  } else if (targetBoundaries(state, stepper)) {
391  ACTS_VERBOSE(volInfo(state) << "Target set to next boundary.");
392  } else {
393  ACTS_VERBOSE(volInfo(state)
394  << "No further navigation action, proceed to target.");
395  // Set navigation break and release the navigation step size
396  state.navigation.navigationBreak = true;
397  stepper.releaseStepSize(state.stepping);
398  }
399 
400  // Navigator target always resets the current surface
401  state.navigation.currentSurface = nullptr;
402 
403  // Return to the propagator
404  return;
405  }
406 
407  private:
418  template <typename propagator_state_t, typename stepper_t>
419  void initialize(propagator_state_t& state, const stepper_t& stepper) const {
420  const auto& logger = state.options.logger;
421 
422  // Call the navigation helper prior to actual navigation
423  ACTS_VERBOSE(volInfo(state) << "Initialization.");
424 
425  // Set the world volume if it is not set
426  if (not state.navigation.worldVolume) {
427  state.navigation.worldVolume = trackingGeometry->highestTrackingVolume();
428  }
429 
430  // We set the current surface to the start surface
431  // for eventual post-update action, e.g. material integration
432  // or collection when leaving a surface at the start of
433  // an extrapolation process
434  state.navigation.currentSurface = state.navigation.startSurface;
435  if (state.navigation.currentSurface) {
436  ACTS_VERBOSE(volInfo(state)
437  << "Current surface set to start surface "
438  << state.navigation.currentSurface->geometryId());
439  }
440  // Fast Navigation initialization for start condition:
441  // - short-cut through object association, saves navigation in the
442  // - geometry and volume tree search for the lowest volume
443  if (state.navigation.startSurface &&
444  state.navigation.startSurface->associatedLayer()) {
445  ACTS_VERBOSE(
446  volInfo(state)
447  << "Fast start initialization through association from Surface.");
448  // assign the current layer and volume by association
449  state.navigation.startLayer =
450  state.navigation.startSurface->associatedLayer();
451  state.navigation.startVolume =
452  state.navigation.startLayer->trackingVolume();
453  // Set the start volume as current volume
454  state.navigation.currentVolume = state.navigation.startVolume;
455  } else {
456  if (state.navigation.startVolume) {
457  ACTS_VERBOSE(
458  volInfo(state)
459  << "Fast start initialization through association from Volume.");
460  state.navigation.startLayer =
461  state.navigation.startVolume->associatedLayer(
462  state.geoContext, stepper.position(state.stepping));
463  // Set the start volume as current volume
464  state.navigation.currentVolume = state.navigation.startVolume;
465  } else {
466  ACTS_VERBOSE(volInfo(state)
467  << "Slow start initialization through search.");
468  // current volume and layer search through global search
469  ACTS_VERBOSE(volInfo(state)
470  << "Starting from position "
471  << toString(stepper.position(state.stepping))
472  << " and direction "
473  << toString(stepper.direction(state.stepping)));
474  state.navigation.startVolume = trackingGeometry->lowestTrackingVolume(
475  state.geoContext, stepper.position(state.stepping));
476  state.navigation.startLayer =
477  state.navigation.startVolume
478  ? state.navigation.startVolume->associatedLayer(
479  state.geoContext, stepper.position(state.stepping))
480  : nullptr;
481  // Set the start volume as current volume
482  state.navigation.currentVolume = state.navigation.startVolume;
483  if (state.navigation.startVolume) {
484  ACTS_VERBOSE(volInfo(state) << "Start volume resolved.");
485  }
486  }
487  }
488  return;
489  }
490 
508  template <typename propagator_state_t, typename stepper_t,
509  typename navigation_surfaces_t, typename navigation_iter_t>
510  bool status(propagator_state_t& state, const stepper_t& stepper,
511  navigation_surfaces_t& navSurfaces,
512  const navigation_iter_t& navIter) const {
513  const auto& logger = state.options.logger;
514 
515  // No surfaces, status check will be done on layer
516  if (navSurfaces.empty() or navIter == navSurfaces.end()) {
517  return false;
518  }
519  // Take the current surface
520  auto surface = navIter->representation;
521  // Check if we are at a surface
522  // If we are on the surface pointed at by the iterator, we can make
523  // it the current one to pass it to the other actors
524  auto surfaceStatus =
525  stepper.updateSurfaceStatus(state.stepping, *surface, true);
526  if (surfaceStatus == Intersection3D::Status::onSurface) {
527  ACTS_VERBOSE(volInfo(state)
528  << "Status Surface successfully hit, storing it.");
529  // Set in navigation state, so actors and aborters can access it
530  state.navigation.currentSurface = surface;
531  if (state.navigation.currentSurface) {
532  ACTS_VERBOSE(volInfo(state)
533  << "Current surface set to surface "
534  << state.navigation.currentSurface->geometryId());
535  }
536  }
537  // Return a positive status: either on it, or on the way
538  return true;
539  }
540 
553  template <typename propagator_state_t, typename stepper_t>
554  bool targetSurfaces(propagator_state_t& state,
555  const stepper_t& stepper) const {
556  const auto& logger = state.options.logger;
557 
558  if (state.navigation.navigationBreak) {
559  return false;
560  }
561 
562  // Make sure resolve Surfaces is called on the start layer
563  if (state.navigation.startLayer and
564  not state.navigation.startLayerResolved) {
565  ACTS_VERBOSE(volInfo(state) << "Start layer to be resolved.");
566  // We provide the layer to the resolve surface method in this case
567  state.navigation.startLayerResolved = true;
568  bool startResolved =
569  resolveSurfaces(state, stepper, state.navigation.startLayer);
570  if (not startResolved and
571  state.navigation.startLayer == state.navigation.targetLayer) {
572  ACTS_VERBOSE(volInfo(state)
573  << "Start is target layer, nothing left to do.");
574  // set the navigation break
575  state.navigation.navigationBreak = true;
576  stepper.releaseStepSize(state.stepping);
577  }
578  return startResolved;
579  }
580 
581  // The call that we are on a layer and have not yet resolved the surfaces
582  // No surfaces, do not return to stepper
583  if (state.navigation.navSurfaces.empty() or
584  state.navigation.navSurfaceIter == state.navigation.navSurfaces.end()) {
585  ACTS_VERBOSE(volInfo(state)
586  << "No surfaces present, target at layer first.");
587  return false;
588  }
589  // Loop over the remaining navigation surfaces
590  while (state.navigation.navSurfaceIter !=
591  state.navigation.navSurfaces.end()) {
592  // Screen output how much is left to try
593  ACTS_VERBOSE(volInfo(state)
594  << std::distance(state.navigation.navSurfaceIter,
595  state.navigation.navSurfaces.end())
596  << " out of " << state.navigation.navSurfaces.size()
597  << " surfaces remain to try.");
598  // Take the surface
599  auto surface = state.navigation.navSurfaceIter->object;
600  // Screen output which surface you are on
601  ACTS_VERBOSE(volInfo(state) << "Next surface candidate will be "
602  << surface->geometryId());
603  // Estimate the surface status
604  auto surfaceStatus =
605  stepper.updateSurfaceStatus(state.stepping, *surface, true);
606  if (surfaceStatus == Intersection3D::Status::reachable) {
607  ACTS_VERBOSE(volInfo(state)
608  << "Surface reachable, step size updated to "
609  << stepper.outputStepSize(state.stepping));
610  return true;
611  }
612  ++state.navigation.navSurfaceIter;
613  continue;
614  }
615 
616  // Reached the end of the surface iteration
617  if (state.navigation.navSurfaceIter == state.navigation.navSurfaces.end()) {
618  ACTS_VERBOSE(volInfo(state)
619  << "Last surface on layer reached, switching layer.");
620  // first clear the surface cache
621  state.navigation.navSurfaces.clear();
622  state.navigation.navSurfaceIter = state.navigation.navSurfaces.end();
623  // now switch to the next layer
624  ++state.navigation.navLayerIter;
625  }
626  // Do not return to the propagator
627  return false;
628  }
629 
648  template <typename propagator_state_t, typename stepper_t>
649  bool targetLayers(propagator_state_t& state, const stepper_t& stepper) const {
650  const auto& logger = state.options.logger;
651 
652  if (state.navigation.navigationBreak) {
653  return false;
654  }
655 
656  // if there are no layers, go back to the navigator (not stepper yet)
657  if (state.navigation.navLayers.empty()) {
658  ACTS_VERBOSE(volInfo(state)
659  << "No layers present, resolve volume first.");
660 
661  // check if current volume has BVH, or layers
662  if (state.navigation.currentVolume->hasBoundingVolumeHierarchy()) {
663  // has hierarchy, use that, skip layer resolution
665  state.stepping.navDir, true, resolveSensitive, resolveMaterial,
666  resolvePassive, nullptr, state.navigation.targetSurface);
667  navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
668  double opening_angle = 0;
669 
670  // Preliminary version of the frustum opening angle estimation.
671  // Currently not used (only rays), but will be.
672 
673  /*
674  Vector3D pos = stepper.position(state.stepping);
675  double mom = stepper.momentum(state.stepping) / UnitConstants::GeV;
676  double q = stepper.charge(state.stepping);
677  Vector3D dir = stepper.direction(state.stepping);
678  Vector3D B = stepper.getField(state.stepping, pos);
679  if (B.squaredNorm() > 1e-9) {
680  // ~ non-zero field
681  double ir = (dir.cross(B).norm()) * q / mom;
682  double s;
683  if (state.stepping.navDir == forward) {
684  s = state.stepping.stepSize.max();
685  } else {
686  s = state.stepping.stepSize.min();
687  }
688  opening_angle = std::atan((1 - std::cos(s * ir)) / std::sin(s * ir));
689  }
690 
691  ACTS_VERBOSE(volInfo(state) << "Estimating opening angle for frustum
692  nav:"); ACTS_VERBOSE(volInfo(state) << "pos: " << pos.transpose());
693  ACTS_VERBOSE(volInfo(state) << "dir: " << dir.transpose());
694  ACTS_VERBOSE(volInfo(state) << "B: " << B.transpose() << " |B|: " <<
695  B.norm()); ACTS_VERBOSE(volInfo(state) << "step mom: " <<
696  stepper.momentum(state.stepping)); ACTS_VERBOSE(volInfo(state) << "=>
697  opening angle: " << opening_angle);
698  */
699 
700  auto protoNavSurfaces =
701  state.navigation.currentVolume->compatibleSurfacesFromHierarchy(
702  state.geoContext, stepper.position(state.stepping),
703  stepper.direction(state.stepping), opening_angle, navOpts);
704  if (!protoNavSurfaces.empty()) {
705  // did we find any surfaces?
706 
707  // Check: are we on the first surface?
708  if (state.navigation.currentSurface == nullptr ||
709  protoNavSurfaces.front().intersection.pathLength > 1_um) {
710  // we are not, go on
711  state.navigation.navSurfaces = std::move(protoNavSurfaces);
712 
713  state.navigation.navSurfaceIter =
714  state.navigation.navSurfaces.begin();
715  state.navigation.navLayers = {};
716  state.navigation.navLayerIter = state.navigation.navLayers.end();
717  // The stepper updates the step size ( single / multi component)
718  stepper.updateStepSize(state.stepping,
719  *state.navigation.navSurfaceIter, true);
720  ACTS_VERBOSE(volInfo(state)
721  << "Navigation stepSize updated to "
722  << stepper.outputStepSize(state.stepping));
723  return true;
724  }
725  }
726  }
727 
728  if (resolveLayers(state, stepper)) {
729  // The layer resolving worked
730  return true;
731  }
732  }
733  // loop over the available navigation layer candiates
734  while (state.navigation.navLayerIter != state.navigation.navLayers.end()) {
735  // The layer surface
736  auto layerSurface = state.navigation.navLayerIter->representation;
737  // We are on the layer
738  if (state.navigation.currentSurface == layerSurface) {
739  ACTS_VERBOSE(volInfo(state) << "We are on a layer, resolve Surfaces.");
740  // If you found surfaces return to the propagator
741  if (resolveSurfaces(state, stepper)) {
742  return true;
743  } else {
744  // Try the next one
745  ++state.navigation.navLayerIter;
746  continue;
747  }
748  }
749  // Try to step towards it
750  auto layerStatus =
751  stepper.updateSurfaceStatus(state.stepping, *layerSurface, true);
752  if (layerStatus == Intersection3D::Status::reachable) {
753  ACTS_VERBOSE(volInfo(state) << "Layer reachable, step size updated to "
754  << stepper.outputStepSize(state.stepping));
755  return true;
756  }
757  ACTS_VERBOSE(volInfo(state)
758  << "Layer intersection not valid, skipping it.");
759  ++state.navigation.navLayerIter;
760  }
761 
762  // Re-initialize target at last layer, only in case it is the target volume
763  // This avoids a wrong target volume estimation
764  if (state.navigation.currentVolume == state.navigation.targetVolume) {
765  initializeTarget(state, stepper);
766  }
767  // Screen output
768  logger().log(Logging::VERBOSE, [&](auto dstream) {
769  dstream << "Last layer";
770  if (state.navigation.currentVolume == state.navigation.targetVolume) {
771  dstream << " (final volume) done, proceed to target.";
772  } else {
773  dstream << " done, target volume boundary.";
774  }
775  });
776  // Set the navigation break if necessary
777  state.navigation.navigationBreak =
778  (state.navigation.currentVolume == state.navigation.targetVolume);
779  return false;
780  }
781 
808  template <typename propagator_state_t, typename stepper_t>
809  bool targetBoundaries(propagator_state_t& state,
810  const stepper_t& stepper) const {
811  const auto& logger = state.options.logger;
812 
813  if (state.navigation.navigationBreak) {
814  return false;
815  }
816 
817  if (!state.navigation.currentVolume) {
818  ACTS_VERBOSE(volInfo(state)
819  << "No sufficient information to resolve boundary, "
820  "stopping navigation.");
821  stepper.releaseStepSize(state.stepping);
822  return false;
823  } else if (state.navigation.currentVolume ==
824  state.navigation.targetVolume) {
825  ACTS_VERBOSE(volInfo(state)
826  << "In target volume: no need to resolve boundary, "
827  "stopping navigation.");
828  state.navigation.navigationBreak = true;
829  stepper.releaseStepSize(state.stepping);
830  return true;
831  }
832 
833  // Re-initialize target at volume boundary
834  initializeTarget(state, stepper);
835 
836  // Helper function to find boundaries
837  auto findBoundaries = [&]() -> bool {
838  // The navigation options
839  NavigationOptions<Surface> navOpts(state.stepping.navDir, true);
840  navOpts.pathLimit =
841  state.stepping.stepSize.value(ConstrainedStep::aborter);
842  navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
843 
844  // Exclude the current surface in case it's a boundary
845  navOpts.startObject = state.navigation.currentSurface;
846  ACTS_VERBOSE(volInfo(state)
847  << "Try to find boundaries, we are at: "
848  << stepper.position(state.stepping).transpose() << ", dir: "
849  << stepper.direction(state.stepping).transpose());
850 
851  // Evaluate the boundary surfaces
852  state.navigation.navBoundaries =
853  state.navigation.currentVolume->compatibleBoundaries(
854  state.geoContext, stepper.position(state.stepping),
855  stepper.direction(state.stepping), navOpts,
856  LoggerWrapper{logger()});
857  // The number of boundary candidates
858  if (logger().doPrint(Logging::VERBOSE)) {
859  auto dstream = logger().log(Logging::VERBOSE);
860  dstream << state.navigation.navBoundaries.size();
861  dstream << " boundary candidates found at path(s): ";
862  for (auto& bc : state.navigation.navBoundaries) {
863  dstream << bc.intersection.pathLength << " ";
864  }
865  }
866  // Set the begin iterator
867  state.navigation.navBoundaryIter = state.navigation.navBoundaries.begin();
868  if (not state.navigation.navBoundaries.empty()) {
869  // Set to the first and return to the stepper
870  stepper.updateStepSize(state.stepping,
871  *state.navigation.navBoundaryIter, true);
872  ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to "
873  << stepper.outputStepSize(state.stepping));
874  return true;
875  }
876  return false;
877  };
878 
879  // No boundaries are assigned yet, find them
880  if (state.navigation.navBoundaries.empty() and findBoundaries()) {
881  return true;
882  }
883 
884  // Loop over the boundary surface
885  while (state.navigation.navBoundaryIter !=
886  state.navigation.navBoundaries.end()) {
887  // That is the current boundary surface
888  auto boundarySurface = state.navigation.navBoundaryIter->representation;
889  // Step towards the boundary surfrace
890  auto boundaryStatus =
891  stepper.updateSurfaceStatus(state.stepping, *boundarySurface, true);
892  if (boundaryStatus == Intersection3D::Status::reachable) {
893  ACTS_VERBOSE(volInfo(state)
894  << "Boundary reachable, step size updated to "
895  << stepper.outputStepSize(state.stepping));
896  return true;
897  } else {
898  if (logger().doPrint(Logging::VERBOSE)) {
899  auto dstream = logger().log(Logging::VERBOSE);
900  dstream << "Boundary ";
901  dstream << std::distance(state.navigation.navBoundaryIter,
902  state.navigation.navBoundaries.end());
903  dstream << " out of " << state.navigation.navBoundaries.size();
904  dstream << " not reachable anymore, switching to next.";
905  }
906  }
907  // Increase the iterator to the next one
908  ++state.navigation.navBoundaryIter;
909  }
910  // We have to leave the volume somehow, so try again
911  state.navigation.navBoundaries.clear();
912  ACTS_VERBOSE(volInfo(state) << "Boundary navigation lost, re-targetting.");
913  if (findBoundaries()) {
914  return true;
915  }
916 
917  // Tried our best, but couldn't do anything
918  return false;
919  }
920 
941  template <typename propagator_state_t, typename stepper_t>
942  void initializeTarget(propagator_state_t& state,
943  const stepper_t& stepper) const {
944  const auto& logger = state.options.logger;
945 
946  if (state.navigation.targetVolume and
947  state.stepping.pathAccumulated == 0.) {
948  ACTS_VERBOSE(volInfo(state)
949  << "Re-initialzing cancelled as it is the first step.");
950  return;
951  }
952  // Fast Navigation initialization for target:
953  if (state.navigation.targetSurface &&
954  state.navigation.targetSurface->associatedLayer() &&
955  !state.navigation.targetVolume) {
956  ACTS_VERBOSE(volInfo(state)
957  << "Fast target initialization through association.");
958  ACTS_VERBOSE(volInfo(state)
959  << "Target surface set to "
960  << state.navigation.targetSurface->geometryId());
961  state.navigation.targetLayer =
962  state.navigation.targetSurface->associatedLayer();
963  state.navigation.targetVolume =
964  state.navigation.targetLayer->trackingVolume();
965  } else if (state.navigation.targetSurface) {
966  // screen output that you do a re-initialization
967  if (state.navigation.targetVolume) {
968  ACTS_VERBOSE(volInfo(state)
969  << "Re-initialization of target volume triggered.");
970  }
971  // Slow navigation initialization for target:
972  // target volume and layer search through global search
973  auto targetIntersection = state.navigation.targetSurface->intersect(
974  state.geoContext, stepper.position(state.stepping),
975  state.stepping.navDir * stepper.direction(state.stepping), false);
976  if (targetIntersection) {
977  ACTS_VERBOSE(volInfo(state)
978  << "Target estimate position ("
979  << targetIntersection.intersection.position.x() << ", "
980  << targetIntersection.intersection.position.y() << ", "
981  << targetIntersection.intersection.position.z() << ")");
983  auto tPosition = targetIntersection.intersection.position;
984  state.navigation.targetVolume =
985  trackingGeometry->lowestTrackingVolume(state.geoContext, tPosition);
986  state.navigation.targetLayer =
987  state.navigation.targetVolume
988  ? state.navigation.targetVolume->associatedLayer(
989  state.geoContext, tPosition)
990  : nullptr;
991  if (state.navigation.targetVolume) {
992  ACTS_VERBOSE(volInfo(state)
993  << "Target volume estimated : "
994  << state.navigation.targetVolume->volumeName());
995  }
996  }
997  }
998  }
999 
1010  template <typename propagator_state_t, typename stepper_t>
1011  bool resolveSurfaces(propagator_state_t& state, const stepper_t& stepper,
1012  const Layer* cLayer = nullptr) const {
1013  const auto& logger = state.options.logger;
1014  // get the layer and layer surface
1015  auto layerSurface = cLayer ? state.navigation.startSurface
1016  : state.navigation.navLayerIter->representation;
1017  auto navLayer = cLayer ? cLayer : state.navigation.navLayerIter->object;
1018  // are we on the start layer
1019  bool onStart = (navLayer == state.navigation.startLayer);
1020  auto startSurface = onStart ? state.navigation.startSurface : layerSurface;
1021  // Use navigation parameters and NavigationOptions
1023  state.stepping.navDir, true, resolveSensitive, resolveMaterial,
1024  resolvePassive, startSurface, state.navigation.targetSurface);
1025  // Check the limit
1026  navOpts.pathLimit = state.stepping.stepSize.value(ConstrainedStep::aborter);
1027  // No overstepping on start layer, otherwise ask the stepper
1028  navOpts.overstepLimit = (cLayer != nullptr)
1030  : stepper.overstepLimit(state.stepping);
1031 
1032  // get the surfaces
1033  state.navigation.navSurfaces = navLayer->compatibleSurfaces(
1034  state.geoContext, stepper.position(state.stepping),
1035  stepper.direction(state.stepping), navOpts);
1036  // the number of layer candidates
1037  if (!state.navigation.navSurfaces.empty()) {
1038  logger().log(Logging::VERBOSE, [&](auto dstream) {
1039  dstream << state.navigation.navSurfaces.size();
1040  dstream << " surface candidates found at path(s): ";
1041  for (auto& sfc : state.navigation.navSurfaces) {
1042  dstream << sfc.intersection.pathLength << " ";
1043  }
1044  });
1045 
1046  // set the iterator
1047  state.navigation.navSurfaceIter = state.navigation.navSurfaces.begin();
1048  // The stepper updates the step size ( single / multi component)
1049  stepper.updateStepSize(state.stepping, *state.navigation.navSurfaceIter,
1050  true);
1051  ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to "
1052  << stepper.outputStepSize(state.stepping));
1053  return true;
1054  }
1055  state.navigation.navSurfaceIter = state.navigation.navSurfaces.end();
1056  ACTS_VERBOSE(volInfo(state) << "No surface candidates found.");
1057  return false;
1058  }
1059 
1075  template <typename propagator_state_t, typename stepper_t>
1076  bool resolveLayers(propagator_state_t& state,
1077  const stepper_t& stepper) const {
1078  const auto& logger = state.options.logger;
1079  ACTS_VERBOSE(volInfo(state) << "Searching for compatible layers.");
1080 
1081  // Check if we are in the start volume
1082  auto startLayer =
1083  (state.navigation.currentVolume == state.navigation.startVolume)
1084  ? state.navigation.startLayer
1085  : nullptr;
1086  // Create the navigation options
1087  // - and get the compatible layers, start layer will be excluded
1088  NavigationOptions<Layer> navOpts(state.stepping.navDir, true,
1089  resolveSensitive, resolveMaterial,
1090  resolvePassive, startLayer, nullptr);
1091  // Set also the target surface
1092  navOpts.targetSurface = state.navigation.targetSurface;
1093  navOpts.pathLimit = state.stepping.stepSize.value(ConstrainedStep::aborter);
1094  navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
1095  // Request the compatible layers
1096  state.navigation.navLayers =
1097  state.navigation.currentVolume->compatibleLayers(
1098  state.geoContext, stepper.position(state.stepping),
1099  stepper.direction(state.stepping), navOpts);
1100 
1101  // Layer candidates have been found
1102  if (!state.navigation.navLayers.empty()) {
1103  // Screen output where they are
1104  logger().log(Logging::VERBOSE, [&](auto dstream) {
1105  dstream << state.navigation.navLayers.size();
1106  dstream << " layer candidates found at path(s): ";
1107  for (auto& lc : state.navigation.navLayers) {
1108  dstream << lc.intersection.pathLength << " ";
1109  }
1110  });
1111  // Set the iterator to the first
1112  state.navigation.navLayerIter = state.navigation.navLayers.begin();
1113  // Setting the step size towards first
1114  if (state.navigation.startLayer &&
1115  state.navigation.navLayerIter->object !=
1116  state.navigation.startLayer) {
1117  ACTS_VERBOSE(volInfo(state) << "Target at layer.");
1118  // The stepper updates the step size ( single / multi component)
1119  stepper.updateStepSize(state.stepping, *state.navigation.navLayerIter,
1120  true);
1121  ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to "
1122  << stepper.outputStepSize(state.stepping));
1123  return true;
1124  }
1125  }
1126 
1127  // Set the iterator to the end of the list
1128  state.navigation.navLayerIter = state.navigation.navLayers.end();
1129 
1130  // Screen output - no layer candidates found
1131  ACTS_VERBOSE(volInfo(state) << "No compatible layer candidates found.");
1132  // Release the step size
1133  stepper.releaseStepSize(state.stepping);
1134  return false;
1135  }
1136 
1150  template <typename propagator_state_t, typename stepper_t>
1151  bool inactive(propagator_state_t& state, const stepper_t& stepper) const {
1152  const auto& logger = state.options.logger;
1153 
1154  // Void behavior in case no tracking geometry is present
1155  if (!trackingGeometry) {
1156  return true;
1157  }
1158  // turn the navigator into void when you are intructed to do nothing
1159  if (!resolveSensitive && !resolveMaterial && !resolvePassive) {
1160  return true;
1161  }
1162 
1163  // --------------------------------------------------------------------
1164  // Navigation break handling
1165  // This checks if a navigation break had been triggered:
1166  // - If so & the target exists or was hit - it simply returns
1167  // - If a target exists and was not yet hit, it checks for it
1168  // -> return is always to the stepper
1169  if (state.navigation.navigationBreak) {
1170  // target exists and reached, or no target exists
1171  if (state.navigation.targetReached || !state.navigation.targetSurface) {
1172  return true;
1173  }
1174  auto targetStatus = stepper.updateSurfaceStatus(
1175  state.stepping, *state.navigation.targetSurface, true);
1176  // the only advance could have been to the target
1177  if (targetStatus == Intersection3D::Status::onSurface) {
1178  // set the target surface
1179  state.navigation.currentSurface = state.navigation.targetSurface;
1180  ACTS_VERBOSE(volInfo(state)
1181  << volInfo(state)
1182  << "Current surface set to target surface "
1183  << state.navigation.currentSurface->geometryId());
1184  return true;
1185  }
1186  }
1187  return false;
1188  }
1189 
1190  private:
1191  template <typename propagator_state_t>
1192  std::string volInfo(const propagator_state_t& state) const {
1193  return (state.navigation.currentVolume
1194  ? state.navigation.currentVolume->volumeName()
1195  : "No Volume") +
1196  " | ";
1197  }
1198 };
1199 
1200 } // namespace Acts