Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:09:19

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2016-2020 CERN for the benefit of the Acts project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 #include "Acts/Definitions/Units.hpp"
0012 #include "Acts/Geometry/BoundarySurfaceT.hpp"
0013 #include "Acts/Geometry/GeometryIdentifier.hpp"
0014 #include "Acts/Geometry/Layer.hpp"
0015 #include "Acts/Geometry/TrackingGeometry.hpp"
0016 #include "Acts/Geometry/TrackingVolume.hpp"
0017 #include "Acts/Propagator/ConstrainedStep.hpp"
0018 #include "Acts/Surfaces/Surface.hpp"
0019 #include "Acts/Utilities/Logger.hpp"
0020 #include "Acts/Utilities/StringHelpers.hpp"
0021 
0022 #include <iomanip>
0023 #include <iterator>
0024 #include <sstream>
0025 #include <string>
0026 
0027 #include <boost/container/small_vector.hpp>
0028 
0029 namespace Acts {
0030 
0031 /// @brief struct for the Navigation options that are forwarded to
0032 ///        the geometry
0033 ///
0034 /// @tparam propagator_state_t Type of the object for navigation state
0035 /// @tparam object_t Type of the object for navigation to check against
0036 template <typename object_t>
0037 struct NavigationOptions {
0038   /// The boundary check directive
0039   BoundaryCheck boundaryCheck = BoundaryCheck(true);
0040 
0041   // How to resolve the geometry
0042   /// Always look for sensitive
0043   bool resolveSensitive = true;
0044   /// Always look for material
0045   bool resolveMaterial = true;
0046   /// always look for passive
0047   bool resolvePassive = false;
0048 
0049   /// object to check against: at start
0050   const object_t* startObject = nullptr;
0051   /// object to check against: at end
0052   const object_t* endObject = nullptr;
0053 
0054   /// External surface identifier for which the boundary check is ignored
0055   std::vector<GeometryIdentifier> externalSurfaces = {};
0056 
0057   /// The minimum distance for a surface to be considered
0058   double nearLimit = 0;
0059   /// The maximum distance for a surface to be considered
0060   double farLimit = std::numeric_limits<double>::max();
0061 
0062   /// Force intersection with boundaries
0063   bool forceIntersectBoundaries = false;
0064 };
0065 
0066 /// @brief Steers the propagation through the geometry by adjusting the step
0067 ///        size and providing the next surface to be targeted.
0068 ///
0069 /// The Navigator is part of the propagation and responsible for steering
0070 /// the step size in order to encounter all the relevant surfaces which are
0071 /// intersected by the trajectory.
0072 ///
0073 /// The current navigation stage is cached in the state struct and updated
0074 /// when necessary. If any surface in the extrapolation flow is hit, it is
0075 /// set to the navigation state, such that other actors can deal with it.
0076 ///
0077 /// The current target surface is referenced by an index which points into
0078 /// the navigation candidates. The navigation candidates are ordered by the
0079 /// path length to the surface. If a surface is hit, the
0080 /// `state.navigation.currentSurface` pointer is set. This actors to observe
0081 /// that we are on a surface.
0082 ///
0083 class Navigator {
0084  public:
0085   using Surfaces = std::vector<const Surface*>;
0086 
0087   using NavigationSurfaces =
0088       boost::container::small_vector<SurfaceIntersection, 10>;
0089 
0090   using NavigationLayers =
0091       boost::container::small_vector<LayerIntersection, 10>;
0092 
0093   using NavigationBoundaries =
0094       boost::container::small_vector<BoundaryIntersection, 4>;
0095 
0096   using ExternalSurfaces = std::multimap<uint64_t, GeometryIdentifier>;
0097 
0098   /// The navigation stage
0099   enum struct Stage : int {
0100     undefined = 0,
0101     surfaceTarget = 1,
0102     layerTarget = 2,
0103     boundaryTarget = 3
0104   };
0105 
0106   struct Config {
0107     /// Tracking Geometry for this Navigator
0108     std::shared_ptr<const TrackingGeometry> trackingGeometry{nullptr};
0109 
0110     /// stop at every sensitive surface (whether it has material or not)
0111     bool resolveSensitive = true;
0112     /// stop at every material surface (whether it is passive or not)
0113     bool resolveMaterial = true;
0114     /// stop at every surface regardless what it is
0115     bool resolvePassive = false;
0116   };
0117 
0118   /// @brief Nested State struct
0119   ///
0120   /// It acts as an internal state which is created for every propagation and
0121   /// meant to keep thread-local navigation information.
0122   struct State {
0123     // Navigation on surface level
0124     /// the vector of navigation surfaces to work through
0125     NavigationSurfaces navSurfaces = {};
0126     /// the current surface index of the navigation state
0127     std::size_t navSurfaceIndex = navSurfaces.size();
0128 
0129     // Navigation on layer level
0130     /// the vector of navigation layers to work through
0131     NavigationLayers navLayers = {};
0132     /// the current layer index of the navigation state
0133     std::size_t navLayerIndex = navLayers.size();
0134 
0135     // Navigation on volume level
0136     /// the vector of boundary surfaces to work through
0137     NavigationBoundaries navBoundaries = {};
0138     /// the current boundary index of the navigation state
0139     std::size_t navBoundaryIndex = navBoundaries.size();
0140 
0141     auto navSurface() const { return navSurfaces.at(navSurfaceIndex); }
0142     auto navLayer() const { return navLayers.at(navLayerIndex); }
0143     auto navBoundary() const { return navBoundaries.at(navBoundaryIndex); }
0144 
0145     /// Externally provided surfaces - these are tried to be hit
0146     ExternalSurfaces externalSurfaces = {};
0147 
0148     /// Navigation state: the world volume
0149     const TrackingVolume* worldVolume = nullptr;
0150 
0151     /// Navigation state: the start volume
0152     const TrackingVolume* startVolume = nullptr;
0153     /// Navigation state: the start layer
0154     const Layer* startLayer = nullptr;
0155     /// Navigation state: the start surface
0156     const Surface* startSurface = nullptr;
0157     /// Navigation state - external state: the current surface
0158     const Surface* currentSurface = nullptr;
0159     /// Navigation state: the current volume
0160     const TrackingVolume* currentVolume = nullptr;
0161     /// Navigation state: the target volume
0162     const TrackingVolume* targetVolume = nullptr;
0163     /// Navigation state: the target layer
0164     const Layer* targetLayer = nullptr;
0165     /// Navigation state: the target surface
0166     const Surface* targetSurface = nullptr;
0167 
0168     /// Indicator for start layer treatment
0169     bool startLayerResolved = false;
0170     /// Indicator if the target is reached
0171     bool targetReached = false;
0172     /// Indicator that the last VolumeHierarchy surface was reached
0173     /// skip the next layer targeting to the next boundary/volume
0174     bool lastHierarchySurfaceReached = false;
0175     /// Navigation state : a break has been detected
0176     bool navigationBreak = false;
0177     // The navigation stage (@todo: integrate break, target)
0178     Stage navigationStage = Stage::undefined;
0179     /// Force intersection with boundaries
0180     bool forceIntersectBoundaries = false;
0181   };
0182 
0183   /// Constructor with configuration object
0184   ///
0185   /// @param cfg The navigator configuration
0186   /// @param _logger a logger instance
0187   explicit Navigator(Config cfg,
0188                      std::shared_ptr<const Logger> _logger =
0189                          getDefaultLogger("Navigator", Logging::Level::INFO))
0190       : m_cfg{std::move(cfg)}, m_logger{std::move(_logger)} {}
0191 
0192   State makeState(const Surface* startSurface,
0193                   const Surface* targetSurface) const {
0194     State result;
0195     result.startSurface = startSurface;
0196     result.targetSurface = targetSurface;
0197     return result;
0198   }
0199 
0200   const Surface* currentSurface(const State& state) const {
0201     return state.currentSurface;
0202   }
0203 
0204   const TrackingVolume* currentVolume(const State& state) const {
0205     return state.currentVolume;
0206   }
0207 
0208   const IVolumeMaterial* currentVolumeMaterial(const State& state) const {
0209     if (state.currentVolume == nullptr) {
0210       return nullptr;
0211     }
0212     return state.currentVolume->volumeMaterial();
0213   }
0214 
0215   const Surface* startSurface(const State& state) const {
0216     return state.startSurface;
0217   }
0218 
0219   const Surface* targetSurface(const State& state) const {
0220     return state.targetSurface;
0221   }
0222 
0223   bool targetReached(const State& state) const { return state.targetReached; }
0224 
0225   bool endOfWorldReached(const State& state) const {
0226     return state.currentVolume == nullptr;
0227   }
0228 
0229   bool navigationBreak(const State& state) const {
0230     return state.navigationBreak;
0231   }
0232 
0233   void currentSurface(State& state, const Surface* surface) const {
0234     state.currentSurface = surface;
0235   }
0236 
0237   void targetReached(State& state, bool targetReached) const {
0238     state.targetReached = targetReached;
0239   }
0240 
0241   void navigationBreak(State& state, bool navigationBreak) const {
0242     state.navigationBreak = navigationBreak;
0243   }
0244 
0245   void insertExternalSurface(State& state, GeometryIdentifier geoid) const {
0246     state.externalSurfaces.insert(
0247         std::pair<uint64_t, GeometryIdentifier>(geoid.layer(), geoid));
0248   }
0249 
0250   /// @brief Initialize call - start of navigation
0251   ///
0252   /// @tparam propagator_state_t The state type of the propagator
0253   /// @tparam stepper_t The type of stepper used for the propagation
0254   ///
0255   /// @param [in,out] state is the propagation state object
0256   /// @param [in] stepper Stepper in use
0257   template <typename propagator_state_t, typename stepper_t>
0258   void initialize(propagator_state_t& state, const stepper_t& stepper) const {
0259     // Call the navigation helper prior to actual navigation
0260     ACTS_VERBOSE(volInfo(state) << "Initialization.");
0261 
0262     // Set the world volume if it is not set
0263     if (!state.navigation.worldVolume) {
0264       state.navigation.worldVolume =
0265           m_cfg.trackingGeometry->highestTrackingVolume();
0266     }
0267 
0268     // We set the current surface to the start surface
0269     // for eventual post-update action, e.g. material integration
0270     // or collection when leaving a surface at the start of
0271     // an extrapolation process
0272     state.navigation.currentSurface = state.navigation.startSurface;
0273     if (state.navigation.currentSurface) {
0274       ACTS_VERBOSE(volInfo(state)
0275                    << "Current surface set to start surface "
0276                    << state.navigation.currentSurface->geometryId());
0277     }
0278 
0279     // Fast Navigation initialization for start condition:
0280     // - short-cut through object association, saves navigation in the
0281     // - geometry and volume tree search for the lowest volume
0282     if (state.navigation.startSurface &&
0283         state.navigation.startSurface->associatedLayer()) {
0284       ACTS_VERBOSE(
0285           volInfo(state)
0286           << "Fast start initialization through association from Surface.");
0287       // assign the current layer and volume by association
0288       state.navigation.startLayer =
0289           state.navigation.startSurface->associatedLayer();
0290       state.navigation.startVolume =
0291           state.navigation.startLayer->trackingVolume();
0292       // Set the start volume as current volume
0293       state.navigation.currentVolume = state.navigation.startVolume;
0294     } else if (state.navigation.startVolume) {
0295       ACTS_VERBOSE(
0296           volInfo(state)
0297           << "Fast start initialization through association from Volume.");
0298       state.navigation.startLayer =
0299           state.navigation.startVolume->associatedLayer(
0300               state.geoContext, stepper.position(state.stepping));
0301       // Set the start volume as current volume
0302       state.navigation.currentVolume = state.navigation.startVolume;
0303     } else {
0304       ACTS_VERBOSE(volInfo(state)
0305                    << "Slow start initialization through search.");
0306       // current volume and layer search through global search
0307       ACTS_VERBOSE(volInfo(state)
0308                    << "Starting from position "
0309                    << toString(stepper.position(state.stepping))
0310                    << " and direction "
0311                    << toString(stepper.direction(state.stepping)));
0312       state.navigation.startVolume =
0313           m_cfg.trackingGeometry->lowestTrackingVolume(
0314               state.geoContext, stepper.position(state.stepping));
0315       state.navigation.startLayer =
0316           state.navigation.startVolume
0317               ? state.navigation.startVolume->associatedLayer(
0318                     state.geoContext, stepper.position(state.stepping))
0319               : nullptr;
0320       // Set the start volume as current volume
0321       state.navigation.currentVolume = state.navigation.startVolume;
0322       if (state.navigation.startVolume) {
0323         ACTS_VERBOSE(volInfo(state) << "Start volume resolved.");
0324       }
0325     }
0326   }
0327 
0328   /// @brief Navigator pre step call
0329   ///
0330   /// Call options
0331   /// (a) there are still surfaces to be resolved: handle those
0332   /// (b) there no surfaces but still layers to be resolved, handle those
0333   /// (c) there are no surfaces nor layers to be resolved, handle boundary
0334   ///
0335   /// @tparam propagator_state_t is the type of Propagatgor state
0336   /// @tparam stepper_t is the used type of the Stepper by the Propagator
0337   ///
0338   /// @param [in,out] state is the mutable propagator state object
0339   /// @param [in] stepper Stepper in use
0340   template <typename propagator_state_t, typename stepper_t>
0341   void preStep(propagator_state_t& state, const stepper_t& stepper) const {
0342     // Check if the navigator is inactive
0343     if (inactive(state, stepper)) {
0344       return;
0345     }
0346 
0347     // Call the navigation helper prior to actual navigation
0348     ACTS_VERBOSE(volInfo(state) << "Entering navigator::preStep.");
0349 
0350     // Initialize the target and target volume
0351     if (state.navigation.targetSurface && !state.navigation.targetVolume) {
0352       // Find out about the target as much as you can
0353       initializeTarget(state, stepper);
0354     }
0355     // Try targeting the surfaces - then layers - then boundaries
0356     if (state.navigation.navigationStage <= Stage::surfaceTarget &&
0357         targetSurfaces(state, stepper)) {
0358       ACTS_VERBOSE(volInfo(state) << "Target set to next surface.");
0359     } else if (state.navigation.navigationStage <= Stage::layerTarget &&
0360                targetLayers(state, stepper)) {
0361       ACTS_VERBOSE(volInfo(state) << "Target set to next layer.");
0362     } else if (targetBoundaries(state, stepper)) {
0363       ACTS_VERBOSE(volInfo(state) << "Target set to next boundary.");
0364     } else {
0365       ACTS_VERBOSE(volInfo(state)
0366                    << "No further navigation action, proceed to target.");
0367       // Set navigation break and release the navigation step size
0368       state.navigation.navigationBreak = true;
0369       stepper.releaseStepSize(state.stepping, ConstrainedStep::actor);
0370     }
0371 
0372     // Navigator target always resets the current surface
0373     state.navigation.currentSurface = nullptr;
0374   }
0375 
0376   /// @brief Navigator post step call
0377   ///
0378   /// (a) It initializes the Navigation stream if start volume is
0379   ///     not yet defined:
0380   ///  - initialize the volume
0381   ///  - establish the start layer and start volume
0382   ///  - set the current surface to the start surface
0383   ///
0384   /// (b) It establishes the currentSurface status during
0385   ///     the propagation flow, currentSurface can be
0386   ///  - surfaces still to be handled within a layer
0387   ///  - layers still to be handled within a volume
0388   ///  - boundaries still to be handled to exit a volume
0389   ///
0390   /// @tparam propagator_state_t is the type of Propagatgor state
0391   /// @tparam stepper_t is the used type of the Stepper by the Propagator
0392   ///
0393   /// @param [in,out] state is the mutable propagator state object
0394   /// @param [in] stepper Stepper in use
0395   template <typename propagator_state_t, typename stepper_t>
0396   void postStep(propagator_state_t& state, const stepper_t& stepper) const {
0397     // Check if the navigator is inactive
0398     if (inactive(state, stepper)) {
0399       return;
0400     }
0401 
0402     // Set the navigation stage
0403     state.navigation.navigationStage = Stage::undefined;
0404 
0405     // Call the navigation helper prior to actual navigation
0406     ACTS_VERBOSE(volInfo(state) << "Entering navigator::postStep.");
0407 
0408     // Navigator post step always starts without current surface
0409     state.navigation.currentSurface = nullptr;
0410 
0411     // (b) Status call within propagation loop
0412     // Try finding status of surfaces
0413     if (surfaceStatus(state, stepper, state.navigation.navSurfaces,
0414                       state.navigation.navSurfaceIndex)) {
0415       ACTS_VERBOSE(volInfo(state) << "Post step: in surface handling.");
0416       if (state.navigation.currentSurface) {
0417         ACTS_VERBOSE(volInfo(state)
0418                      << "On surface: switch forward or release.");
0419         if (++state.navigation.navSurfaceIndex ==
0420             state.navigation.navSurfaces.size()) {
0421           // this was the last surface, check if we have layers
0422           if (!state.navigation.navLayers.empty()) {
0423             ++state.navigation.navLayerIndex;
0424           } else if (state.navigation.startLayer != nullptr &&
0425                      state.navigation.currentSurface->associatedLayer() ==
0426                          state.navigation.startLayer) {
0427             // this was the start layer, switch to layer target next
0428             state.navigation.navigationStage = Stage::layerTarget;
0429             return;
0430           } else {
0431             // no layers, go to boundary
0432             state.navigation.navigationStage = Stage::boundaryTarget;
0433             return;
0434           }
0435         }
0436       }
0437       // Set the navigation stage to surface target
0438       state.navigation.navigationStage = Stage::surfaceTarget;
0439       ACTS_VERBOSE(volInfo(state) << "Staying focussed on surface.");
0440       // Try finding status of layer
0441     } else if (surfaceStatus(state, stepper, state.navigation.navLayers,
0442                              state.navigation.navLayerIndex)) {
0443       ACTS_VERBOSE(volInfo(state) << "Post step: in layer handling.");
0444       if (state.navigation.currentSurface != nullptr) {
0445         ACTS_VERBOSE(volInfo(state) << "On layer: update layer information.");
0446         if (resolveSurfaces(state, stepper)) {
0447           // Set the navigation stage back to surface handling
0448           state.navigation.navigationStage = Stage::surfaceTarget;
0449           return;
0450         }
0451       } else {
0452         // Set the navigation stage to layer target
0453         state.navigation.navigationStage = Stage::layerTarget;
0454         ACTS_VERBOSE(volInfo(state) << "Staying focussed on layer.");
0455       }
0456       // Try finding status of boundaries
0457     } else if (surfaceStatus(state, stepper, state.navigation.navBoundaries,
0458                              state.navigation.navBoundaryIndex)) {
0459       ACTS_VERBOSE(volInfo(state) << "Post step: in boundary handling.");
0460 
0461       // Are we on the boundary - then overwrite the stage
0462       if (state.navigation.currentSurface != nullptr) {
0463         // Set the navigation stage back to surface handling
0464         ACTS_VERBOSE(volInfo(state)
0465                      << "On boundary: update volume information.");
0466         // We are on a boundary, reset all information
0467         state.navigation.navSurfaces.clear();
0468         state.navigation.navSurfaceIndex = state.navigation.navSurfaces.size();
0469         state.navigation.navLayers.clear();
0470         state.navigation.navLayerIndex = state.navigation.navLayers.size();
0471         state.navigation.lastHierarchySurfaceReached = false;
0472         // Update volume information
0473         // get the attached volume information
0474         auto boundary = state.navigation.navBoundary().second;
0475         state.navigation.currentVolume = boundary->attachedVolume(
0476             state.geoContext, stepper.position(state.stepping),
0477             state.options.direction * stepper.direction(state.stepping));
0478         // No volume anymore : end of known world
0479         if (!state.navigation.currentVolume) {
0480           ACTS_VERBOSE(
0481               volInfo(state)
0482               << "No more volume to progress to, stopping navigation.");
0483           // Navigation break & release navigation stepping
0484           state.navigation.navigationBreak = true;
0485           return;
0486         } else {
0487           ACTS_VERBOSE(volInfo(state) << "Volume updated.");
0488           // Forget the boundary information
0489           state.navigation.navBoundaries.clear();
0490           state.navigation.navBoundaryIndex =
0491               state.navigation.navBoundaries.size();
0492         }
0493       } else {
0494         // Set the navigation stage back to boundary target
0495         state.navigation.navigationStage = Stage::boundaryTarget;
0496         ACTS_VERBOSE(volInfo(state) << "Staying focussed on boundary.");
0497       }
0498     } else if (state.navigation.currentVolume ==
0499                state.navigation.targetVolume) {
0500       if (state.navigation.targetSurface == nullptr) {
0501         ACTS_WARNING(volInfo(state)
0502                      << "No further navigation action, proceed to "
0503                         "target. This is very likely an error");
0504       } else {
0505         ACTS_VERBOSE(volInfo(state)
0506                      << "No further navigation action, proceed to target.");
0507       }
0508       // Set navigation break and release the navigation step size
0509       state.navigation.navigationBreak = true;
0510     } else {
0511       ACTS_VERBOSE(volInfo(state)
0512                    << "Status could not be determined - good luck.");
0513     }
0514   }
0515 
0516  private:
0517   const SurfaceIntersection& candidateIntersection(
0518       const NavigationSurfaces& surfaces, std::size_t index) const {
0519     return surfaces.at(index);
0520   }
0521   const SurfaceIntersection& candidateIntersection(
0522       const NavigationLayers& surfaces, std::size_t index) const {
0523     return surfaces.at(index).first;
0524   }
0525   const SurfaceIntersection& candidateIntersection(
0526       const NavigationBoundaries& surfaces, std::size_t index) const {
0527     return surfaces.at(index).first;
0528   }
0529 
0530   /// @brief Status call for test surfaces (surfaces, layers, boundaries)
0531   ///
0532   /// If there are surfaces to be handled, check if the current
0533   /// state is on the surface
0534   ///
0535   /// @tparam propagator_state_t The state type of the propagator
0536   /// @tparam stepper_t The type of stepper used for the propagation
0537   /// @tparam navigation_surfaces_t Type of the propagator
0538   ///
0539   /// @param [in,out] state is the propagation state object
0540   /// @param [in] stepper Stepper in use
0541   /// @param [in] navSurfaces is the navigation status objects
0542   /// @param [in] navIndex test surface fore the status test
0543   ///
0544   /// @return boolean return triggers exit to stepper
0545   template <typename propagator_state_t, typename stepper_t,
0546             typename navigation_surfaces_t>
0547   bool surfaceStatus(propagator_state_t& state, const stepper_t& stepper,
0548                      const navigation_surfaces_t& navSurfaces,
0549                      std::size_t navIndex) const {
0550     // No surfaces, status check will be done on layer
0551     if (navSurfaces.empty() || navIndex == navSurfaces.size()) {
0552       return false;
0553     }
0554     const auto& intersection = candidateIntersection(navSurfaces, navIndex);
0555     // Take the current surface
0556     const auto* surface = intersection.object();
0557     // Check if we are at a surface
0558     // If we are on the surface pointed at by the index, we can make
0559     // it the current one to pass it to the other actors
0560     auto surfaceStatus = stepper.updateSurfaceStatus(
0561         state.stepping, *surface, intersection.index(), state.options.direction,
0562         BoundaryCheck(true), state.options.surfaceTolerance, logger());
0563     if (surfaceStatus == Intersection3D::Status::onSurface) {
0564       ACTS_VERBOSE(volInfo(state)
0565                    << "Status Surface successfully hit, storing it.");
0566       // Set in navigation state, so actors and aborters can access it
0567       state.navigation.currentSurface = surface;
0568       if (state.navigation.currentSurface) {
0569         ACTS_VERBOSE(volInfo(state)
0570                      << "Current surface set to surface "
0571                      << state.navigation.currentSurface->geometryId());
0572       }
0573     }
0574     // Return a positive status: either on it, or on the way
0575     return true;
0576   }
0577 
0578   /// Loop over surface candidates here:
0579   ///  - if an intersect is  valid but not yet reached
0580   ///    then return with updated step size
0581   ///  - if an intersect is not valid, switch to next
0582   ///
0583   /// @tparam propagator_state_t The state type of the propagator
0584   /// @tparam stepper_t The type of stepper used for the propagation
0585   ///
0586   /// @param [in,out] state is the propagation state object
0587   /// @param [in] stepper Stepper in use
0588   ///
0589   /// boolean return triggers exit to stepper
0590   template <typename propagator_state_t, typename stepper_t>
0591   bool targetSurfaces(propagator_state_t& state,
0592                       const stepper_t& stepper) const {
0593     if (state.navigation.navigationBreak) {
0594       return false;
0595     }
0596     // Make sure resolve Surfaces is called on the start layer
0597     if (state.navigation.startLayer && !state.navigation.startLayerResolved) {
0598       ACTS_VERBOSE(volInfo(state) << "Start layer to be resolved.");
0599       // We provide the layer to the resolve surface method in this case
0600       state.navigation.startLayerResolved = true;
0601       bool startResolved =
0602           resolveSurfaces(state, stepper, state.navigation.startLayer);
0603       if (!startResolved &&
0604           state.navigation.startLayer == state.navigation.targetLayer) {
0605         ACTS_VERBOSE(volInfo(state)
0606                      << "Start is target layer, nothing left to do.");
0607         // set the navigation break
0608         state.navigation.navigationBreak = true;
0609         stepper.releaseStepSize(state.stepping, ConstrainedStep::actor);
0610       }
0611       return startResolved;
0612     }
0613 
0614     // The call that we are on a layer and have not yet resolved the surfaces
0615     // No surfaces, do not return to stepper
0616     if (state.navigation.navSurfaces.empty() ||
0617         state.navigation.navSurfaceIndex ==
0618             state.navigation.navSurfaces.size()) {
0619       ACTS_VERBOSE(volInfo(state)
0620                    << "No surfaces present, target at layer first.");
0621       return false;
0622     }
0623     auto layerID = state.navigation.navSurface().object()->geometryId().layer();
0624     std::pair<ExternalSurfaces::iterator, ExternalSurfaces::iterator>
0625         externalSurfaceRange =
0626             state.navigation.externalSurfaces.equal_range(layerID);
0627     // Loop over the remaining navigation surfaces
0628     while (state.navigation.navSurfaceIndex !=
0629            state.navigation.navSurfaces.size()) {
0630       // Screen output how much is left to try
0631       ACTS_VERBOSE(volInfo(state)
0632                    << (state.navigation.navSurfaces.size() -
0633                        state.navigation.navSurfaceIndex)
0634                    << " out of " << state.navigation.navSurfaces.size()
0635                    << " surfaces remain to try.");
0636       const auto& intersection = state.navigation.navSurface();
0637       // Take the surface
0638       const auto* surface = intersection.object();
0639       // Screen output which surface you are on
0640       ACTS_VERBOSE(volInfo(state) << "Next surface candidate will be "
0641                                   << surface->geometryId());
0642       // Estimate the surface status
0643       bool boundaryCheck = true;
0644       for (auto it = externalSurfaceRange.first;
0645            it != externalSurfaceRange.second; it++) {
0646         if (surface->geometryId() == it->second) {
0647           boundaryCheck = false;
0648           break;
0649         }
0650       }
0651       auto surfaceStatus = stepper.updateSurfaceStatus(
0652           state.stepping, *surface, intersection.index(),
0653           state.options.direction, BoundaryCheck(boundaryCheck),
0654           state.options.surfaceTolerance, logger());
0655       if (surfaceStatus == Intersection3D::Status::reachable) {
0656         ACTS_VERBOSE(volInfo(state)
0657                      << "Surface reachable, step size updated to "
0658                      << stepper.outputStepSize(state.stepping));
0659         return true;
0660       }
0661       ++state.navigation.navSurfaceIndex;
0662       continue;
0663     }
0664 
0665     // Reached the end of the surface iteration
0666     if (state.navigation.navSurfaceIndex ==
0667         state.navigation.navSurfaces.size()) {
0668       // first clear the surface cache
0669       state.navigation.navSurfaces.clear();
0670       state.navigation.navSurfaceIndex = state.navigation.navSurfaces.size();
0671 
0672       if (state.navigation.navLayerIndex != state.navigation.navLayers.size()) {
0673         ACTS_VERBOSE(volInfo(state)
0674                      << "Last surface on layer reached, switching layer.");
0675         // now switch to the next layer
0676         ++state.navigation.navLayerIndex;
0677       } else {
0678         ACTS_VERBOSE(volInfo(state)
0679                      << "Last surface on layer reached, and no layer.");
0680         // first clear the surface cache
0681         state.navigation.lastHierarchySurfaceReached = true;
0682         state.navigation.navigationBreak =
0683             (state.navigation.currentVolume == state.navigation.targetVolume);
0684       }
0685     }
0686     // Do not return to the propagator
0687     return false;
0688   }
0689 
0690   /// @brief Target layer candidates.
0691   ///
0692   /// We are now trying to advance to the next layer (with surfaces)
0693   /// Check if we are on the representing surface of the layer pointed
0694   /// at by navLayerIndex. If so, we unpack the compatible surfaces
0695   /// (determined by straight line intersect), and set up the index
0696   /// so that the next postStep() call will enter the surface
0697   /// check mode above. If no surfaces are found, we skip the layer.
0698   /// If we unpack a surface, the step size is set to the path length
0699   /// to the first surface, as determined by straight line intersect.
0700   ///
0701   /// @tparam propagator_state_t The state type of the propagator
0702   /// @tparam stepper_t The type of stepper used for the propagation
0703   ///
0704   /// @param [in,out] state is the propagation state object
0705   /// @param [in] stepper Stepper in use
0706   ///
0707   /// @return boolean return triggers exit to stepper
0708   template <typename propagator_state_t, typename stepper_t>
0709   bool targetLayers(propagator_state_t& state, const stepper_t& stepper) const {
0710     using namespace UnitLiterals;
0711 
0712     if (state.navigation.navigationBreak ||
0713         state.navigation.lastHierarchySurfaceReached) {
0714       return false;
0715     }
0716 
0717     // if there are no layers, go back to the navigator (not stepper yet)
0718     if (state.navigation.navLayers.empty()) {
0719       ACTS_VERBOSE(volInfo(state)
0720                    << "No layers present, resolve volume first.");
0721 
0722       if (resolveLayers(state, stepper)) {
0723         // The layer resolving worked
0724         return true;
0725       }
0726     }
0727     // loop over the available navigation layer candidates
0728     while (state.navigation.navLayerIndex !=
0729            state.navigation.navLayers.size()) {
0730       const auto& intersection = state.navigation.navLayer().first;
0731       // The layer surface
0732       const auto* layerSurface = intersection.object();
0733       // We are on the layer
0734       if (state.navigation.currentSurface == layerSurface) {
0735         ACTS_VERBOSE(volInfo(state) << "We are on a layer, resolve Surfaces.");
0736         // If you found surfaces return to the propagator
0737         if (resolveSurfaces(state, stepper)) {
0738           return true;
0739         } else {
0740           // Try the next one
0741           ++state.navigation.navLayerIndex;
0742           continue;
0743         }
0744       }
0745       // Try to step towards it
0746       auto layerStatus = stepper.updateSurfaceStatus(
0747           state.stepping, *layerSurface, intersection.index(),
0748           state.options.direction, BoundaryCheck(true),
0749           state.options.surfaceTolerance, logger());
0750       if (layerStatus == Intersection3D::Status::reachable) {
0751         ACTS_VERBOSE(volInfo(state) << "Layer reachable, step size updated to "
0752                                     << stepper.outputStepSize(state.stepping));
0753         return true;
0754       }
0755       ACTS_VERBOSE(volInfo(state)
0756                    << "Layer intersection not valid, skipping it.");
0757       ++state.navigation.navLayerIndex;
0758     }
0759 
0760     // Re-initialize target at last layer, only in case it is the target volume
0761     // This avoids a wrong target volume estimation
0762     if (state.navigation.currentVolume == state.navigation.targetVolume) {
0763       initializeTarget(state, stepper);
0764     }
0765     // Screen output
0766     if (logger().doPrint(Logging::VERBOSE)) {
0767       std::ostringstream os;
0768       os << "Last layer";
0769       if (state.navigation.currentVolume == state.navigation.targetVolume) {
0770         os << " (final volume) done, proceed to target.";
0771       } else {
0772         os << " done, target volume boundary.";
0773       }
0774       logger().log(Logging::VERBOSE, os.str());
0775     }
0776     // Set the navigation break if necessary
0777     state.navigation.navigationBreak =
0778         (state.navigation.currentVolume == state.navigation.targetVolume);
0779     return false;
0780   }
0781 
0782   /// @brief Navigation through volumes
0783   ///
0784   /// This is the boundary check routine. If the code above set up the
0785   /// boundary surface index, we advance through them here. If we are on
0786   /// the boundary surface, we set the current surface to the boundary
0787   /// surface, and get the volume pointed at by the boundary surface.  Next
0788   /// we unpack the layers from that volume. If the volume contains layers
0789   /// we set the step size to the straight line path length to the first
0790   /// layer.  If we don't find a next volume, the navigationBreak
0791   /// indicator is set.  This ends the navigation. Finally, the boundary
0792   /// index is cleared, so that the subsequent call goes back to
0793   /// the layer iteration logic.
0794   ///
0795   /// If we are not on the current boundary surface, we try the next one.
0796   /// The index is advanced and the step size is set. If no straight
0797   /// line intersect is found, the boundary surface is skipped.
0798   /// If we are out of boundary surfaces, the navigation is terminated.
0799   ///
0800   /// @tparam propagator_state_t The state type of the propagator
0801   /// @tparam stepper_t The type of stepper used for the propagation
0802   ///
0803   /// @param [in,out] state is the propagation state object
0804   /// @param [in] stepper Stepper in use
0805   ///
0806   /// boolean return triggers exit to stepper
0807   template <typename propagator_state_t, typename stepper_t>
0808   bool targetBoundaries(propagator_state_t& state,
0809                         const stepper_t& stepper) const {
0810     if (state.navigation.navigationBreak) {
0811       return false;
0812     }
0813 
0814     if (!state.navigation.currentVolume) {
0815       ACTS_VERBOSE(volInfo(state)
0816                    << "No sufficient information to resolve boundary, "
0817                       "stopping navigation.");
0818       stepper.releaseStepSize(state.stepping, ConstrainedStep::actor);
0819       return false;
0820     } else if (state.navigation.currentVolume ==
0821                state.navigation.targetVolume) {
0822       ACTS_VERBOSE(volInfo(state)
0823                    << "In target volume: no need to resolve boundary, "
0824                       "stopping navigation.");
0825       state.navigation.navigationBreak = true;
0826       stepper.releaseStepSize(state.stepping, ConstrainedStep::actor);
0827       return true;
0828     }
0829 
0830     // Re-initialize target at volume boundary
0831     initializeTarget(state, stepper);
0832 
0833     // Helper function to find boundaries
0834     auto findBoundaries = [&]() -> bool {
0835       // The navigation options
0836       NavigationOptions<Surface> navOpts;
0837       // Exclude the current surface in case it's a boundary
0838       navOpts.startObject = state.navigation.currentSurface;
0839       navOpts.nearLimit = stepper.overstepLimit(state.stepping);
0840       navOpts.farLimit =
0841           stepper.getStepSize(state.stepping, ConstrainedStep::aborter);
0842       navOpts.forceIntersectBoundaries =
0843           state.navigation.forceIntersectBoundaries;
0844 
0845       ACTS_VERBOSE(volInfo(state)
0846                    << "Try to find boundaries, we are at: "
0847                    << stepper.position(state.stepping).transpose() << ", dir: "
0848                    << stepper.direction(state.stepping).transpose());
0849 
0850       // Evaluate the boundary surfaces
0851       state.navigation.navBoundaries =
0852           state.navigation.currentVolume->compatibleBoundaries(
0853               state.geoContext, stepper.position(state.stepping),
0854               state.options.direction * stepper.direction(state.stepping),
0855               navOpts, logger());
0856       // The number of boundary candidates
0857       if (logger().doPrint(Logging::VERBOSE)) {
0858         std::ostringstream os;
0859         os << state.navigation.navBoundaries.size();
0860         os << " boundary candidates found at path(s): ";
0861         for (auto& bc : state.navigation.navBoundaries) {
0862           os << bc.first.pathLength() << "  ";
0863         }
0864         logger().log(Logging::VERBOSE, os.str());
0865       }
0866       // Set the begin index
0867       state.navigation.navBoundaryIndex = 0;
0868       if (!state.navigation.navBoundaries.empty()) {
0869         // Set to the first and return to the stepper
0870         stepper.updateStepSize(state.stepping,
0871                                state.navigation.navBoundary().first,
0872                                state.options.direction, true);
0873         ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to "
0874                                     << stepper.outputStepSize(state.stepping));
0875         return true;
0876       }
0877       return false;
0878     };
0879 
0880     // No boundaries are assigned yet, find them
0881     if (state.navigation.navBoundaries.empty() && findBoundaries()) {
0882       return true;
0883     }
0884 
0885     // Loop over the boundary surface
0886     while (state.navigation.navBoundaryIndex !=
0887            state.navigation.navBoundaries.size()) {
0888       const auto& intersection = state.navigation.navBoundary().first;
0889       // That is the current boundary surface
0890       const auto* boundarySurface = intersection.object();
0891       // Step towards the boundary surfrace
0892       auto boundaryStatus = stepper.updateSurfaceStatus(
0893           state.stepping, *boundarySurface, intersection.index(),
0894           state.options.direction, BoundaryCheck(true),
0895           state.options.surfaceTolerance, logger());
0896       if (boundaryStatus == Intersection3D::Status::reachable) {
0897         ACTS_VERBOSE(volInfo(state)
0898                      << "Boundary reachable, step size updated to "
0899                      << stepper.outputStepSize(state.stepping));
0900         return true;
0901       } else {
0902         ACTS_VERBOSE("Boundary "
0903                      << (state.navigation.navBoundaries.size() -
0904                          state.navigation.navBoundaryIndex)
0905                      << " out of " << state.navigation.navBoundaries.size()
0906                      << " not reachable anymore, switching to next.");
0907         ACTS_VERBOSE("Targeted boundary surface was: \n"
0908                      << std::tie(*boundarySurface, state.geoContext));
0909       }
0910       // Increase the index to the next one
0911       ++state.navigation.navBoundaryIndex;
0912     }
0913     // We have to leave the volume somehow, so try again
0914     state.navigation.navBoundaries.clear();
0915     ACTS_VERBOSE(volInfo(state) << "Boundary navigation lost, re-targetting.");
0916     state.navigation.forceIntersectBoundaries = true;
0917     if (findBoundaries()) {
0918       // Resetting intersection check for boundary surfaces
0919       state.navigation.forceIntersectBoundaries = false;
0920       return true;
0921     }
0922 
0923     // Tried our best, but couldn't do anything
0924     return false;
0925   }
0926 
0927   /// @brief Navigation (re-)initialisation for the target
0928   ///
0929   /// @note This is only called a few times every propagation/extrapolation
0930   ///
0931   /// As a straight line estimate can lead you to the wrong destination
0932   /// Volume, this will be called at:
0933   /// - initialization
0934   /// - attempted volume switch
0935   /// Target finding by association will not be done again
0936   ///
0937   /// @tparam propagator_state_t The state type of the propagator
0938   /// @tparam stepper_t The type of stepper used for the propagation
0939   ///
0940   /// @param [in,out] state is the propagation state object
0941   /// @param [in] stepper Stepper in use
0942   ///
0943   /// boolean return triggers exit to stepper
0944   template <typename propagator_state_t, typename stepper_t>
0945   void initializeTarget(propagator_state_t& state,
0946                         const stepper_t& stepper) const {
0947     if (state.navigation.targetVolume && state.stepping.pathAccumulated == 0.) {
0948       ACTS_VERBOSE(volInfo(state)
0949                    << "Re-initialzing cancelled as it is the first step.");
0950       return;
0951     }
0952     // Fast Navigation initialization for target:
0953     if (state.navigation.targetSurface &&
0954         state.navigation.targetSurface->associatedLayer() &&
0955         !state.navigation.targetVolume) {
0956       ACTS_VERBOSE(volInfo(state)
0957                    << "Fast target initialization through association.");
0958       ACTS_VERBOSE(volInfo(state)
0959                    << "Target surface set to "
0960                    << state.navigation.targetSurface->geometryId());
0961       state.navigation.targetLayer =
0962           state.navigation.targetSurface->associatedLayer();
0963       state.navigation.targetVolume =
0964           state.navigation.targetLayer->trackingVolume();
0965     } else if (state.navigation.targetSurface) {
0966       // screen output that you do a re-initialization
0967       if (state.navigation.targetVolume) {
0968         ACTS_VERBOSE(volInfo(state)
0969                      << "Re-initialization of target volume triggered.");
0970       }
0971       // Slow navigation initialization for target:
0972       // target volume and layer search through global search
0973       auto targetIntersection =
0974           state.navigation.targetSurface
0975               ->intersect(
0976                   state.geoContext, stepper.position(state.stepping),
0977                   state.options.direction * stepper.direction(state.stepping),
0978                   BoundaryCheck(false), state.options.surfaceTolerance)
0979               .closest();
0980       if (targetIntersection) {
0981         ACTS_VERBOSE(volInfo(state)
0982                      << "Target estimate position ("
0983                      << targetIntersection.position().x() << ", "
0984                      << targetIntersection.position().y() << ", "
0985                      << targetIntersection.position().z() << ")");
0986         /// get the target volume from the intersection
0987         auto tPosition = targetIntersection.position();
0988         state.navigation.targetVolume =
0989             m_cfg.trackingGeometry->lowestTrackingVolume(state.geoContext,
0990                                                          tPosition);
0991         state.navigation.targetLayer =
0992             state.navigation.targetVolume
0993                 ? state.navigation.targetVolume->associatedLayer(
0994                       state.geoContext, tPosition)
0995                 : nullptr;
0996         if (state.navigation.targetVolume) {
0997           ACTS_VERBOSE(volInfo(state)
0998                        << "Target volume estimated : "
0999                        << state.navigation.targetVolume->volumeName());
1000         }
1001       }
1002     }
1003   }
1004 
1005   /// @brief Resolve the surfaces of this layer
1006   ///
1007   /// @tparam propagator_state_t The state type of the propagator
1008   /// @tparam stepper_t The type of stepper used for the propagation
1009   ///
1010   /// @param [in,out] state is the propagation state object
1011   /// @param [in] stepper Stepper in use
1012   /// @param [in] cLayer is the already assigned current layer, e.g. start layer
1013   ///
1014   /// boolean return triggers exit to stepper
1015   template <typename propagator_state_t, typename stepper_t>
1016   bool resolveSurfaces(propagator_state_t& state, const stepper_t& stepper,
1017                        const Layer* cLayer = nullptr) const {
1018     // get the layer and layer surface
1019     auto layerSurface = cLayer ? state.navigation.startSurface
1020                                : state.navigation.navLayer().first.object();
1021     auto navLayer = cLayer ? cLayer : state.navigation.navLayer().second;
1022     // are we on the start layer
1023     bool onStart = (navLayer == state.navigation.startLayer);
1024     auto startSurface = onStart ? state.navigation.startSurface : layerSurface;
1025     // Use navigation parameters and NavigationOptions
1026     NavigationOptions<Surface> navOpts;
1027     navOpts.resolveSensitive = m_cfg.resolveSensitive;
1028     navOpts.resolveMaterial = m_cfg.resolveMaterial;
1029     navOpts.resolvePassive = m_cfg.resolvePassive;
1030     navOpts.startObject = startSurface;
1031     navOpts.endObject = state.navigation.targetSurface;
1032 
1033     std::vector<GeometryIdentifier> externalSurfaces;
1034     if (!state.navigation.externalSurfaces.empty()) {
1035       auto layerID = layerSurface->geometryId().layer();
1036       auto externalSurfaceRange =
1037           state.navigation.externalSurfaces.equal_range(layerID);
1038       navOpts.externalSurfaces.reserve(
1039           state.navigation.externalSurfaces.count(layerID));
1040       for (auto itSurface = externalSurfaceRange.first;
1041            itSurface != externalSurfaceRange.second; itSurface++) {
1042         navOpts.externalSurfaces.push_back(itSurface->second);
1043       }
1044     }
1045     // No overstepping on start layer, otherwise ask the stepper
1046     navOpts.nearLimit = (cLayer != nullptr)
1047                             ? state.options.surfaceTolerance
1048                             : stepper.overstepLimit(state.stepping);
1049     // Check the limit
1050     navOpts.farLimit =
1051         stepper.getStepSize(state.stepping, ConstrainedStep::aborter);
1052 
1053     // get the surfaces
1054     state.navigation.navSurfaces = navLayer->compatibleSurfaces(
1055         state.geoContext, stepper.position(state.stepping),
1056         state.options.direction * stepper.direction(state.stepping), navOpts);
1057     // the number of layer candidates
1058     if (!state.navigation.navSurfaces.empty()) {
1059       if (logger().doPrint(Logging::VERBOSE)) {
1060         std::ostringstream os;
1061         os << state.navigation.navSurfaces.size();
1062         os << " surface candidates found at path(s): ";
1063         for (auto& sfc : state.navigation.navSurfaces) {
1064           os << sfc.pathLength() << "  ";
1065         }
1066         logger().log(Logging::VERBOSE, os.str());
1067       }
1068 
1069       // set the index
1070       state.navigation.navSurfaceIndex = 0;
1071       // The stepper updates the step size ( single / multi component)
1072       stepper.updateStepSize(state.stepping, state.navigation.navSurface(),
1073                              state.options.direction, true);
1074       ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to "
1075                                   << stepper.outputStepSize(state.stepping));
1076       return true;
1077     }
1078     state.navigation.navSurfaceIndex = state.navigation.navSurfaces.size();
1079     ACTS_VERBOSE(volInfo(state) << "No surface candidates found.");
1080     return false;
1081   }
1082 
1083   /// @brief Navigation through layers
1084   ///
1085   /// Resolve layers.
1086   ///
1087   /// This initializes the layer candidates when starting
1088   /// or when entering a new volume
1089   ///
1090   /// @tparam propagator_state_t The state type of the propagator
1091   /// @tparam stepper_t The type of stepper used for the propagation
1092   ///
1093   /// @param [in,out] state is the propagation state object
1094   /// @param [in] stepper Stepper in use
1095   ///
1096   /// @return boolean return triggers exit to stepper
1097   template <typename propagator_state_t, typename stepper_t>
1098   bool resolveLayers(propagator_state_t& state,
1099                      const stepper_t& stepper) const {
1100     ACTS_VERBOSE(volInfo(state) << "Searching for compatible layers.");
1101 
1102     // Check if we are in the start volume
1103     auto startLayer =
1104         (state.navigation.currentVolume == state.navigation.startVolume)
1105             ? state.navigation.startLayer
1106             : nullptr;
1107     // Create the navigation options
1108     // - and get the compatible layers, start layer will be excluded
1109     NavigationOptions<Layer> navOpts;
1110     navOpts.resolveSensitive = m_cfg.resolveSensitive;
1111     navOpts.resolveMaterial = m_cfg.resolveMaterial;
1112     navOpts.resolvePassive = m_cfg.resolvePassive;
1113     navOpts.startObject = startLayer;
1114     navOpts.nearLimit = stepper.overstepLimit(state.stepping);
1115     navOpts.farLimit =
1116         stepper.getStepSize(state.stepping, ConstrainedStep::aborter);
1117     // Request the compatible layers
1118     state.navigation.navLayers =
1119         state.navigation.currentVolume->compatibleLayers(
1120             state.geoContext, stepper.position(state.stepping),
1121             state.options.direction * stepper.direction(state.stepping),
1122             navOpts);
1123 
1124     // Layer candidates have been found
1125     if (!state.navigation.navLayers.empty()) {
1126       // Screen output where they are
1127       if (logger().doPrint(Logging::VERBOSE)) {
1128         std::ostringstream os;
1129         os << state.navigation.navLayers.size();
1130         os << " layer candidates found at path(s): ";
1131         for (auto& lc : state.navigation.navLayers) {
1132           os << lc.first.pathLength() << "  ";
1133         }
1134         logger().log(Logging::VERBOSE, os.str());
1135       }
1136       // Set the index to the first
1137       state.navigation.navLayerIndex = 0;
1138       // Setting the step size towards first
1139       if (state.navigation.startLayer &&
1140           state.navigation.navLayer().second != state.navigation.startLayer) {
1141         ACTS_VERBOSE(volInfo(state) << "Target at layer.");
1142         // The stepper updates the step size ( single / multi component)
1143         stepper.updateStepSize(state.stepping,
1144                                state.navigation.navLayer().first,
1145                                state.options.direction, true);
1146         ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to "
1147                                     << stepper.outputStepSize(state.stepping));
1148         return true;
1149       }
1150     }
1151 
1152     // Set the index to the end of the list
1153     state.navigation.navLayerIndex = state.navigation.navLayers.size();
1154 
1155     // Screen output - no layer candidates found
1156     ACTS_VERBOSE(volInfo(state) << "No compatible layer candidates found.");
1157     // Release the step size
1158     stepper.releaseStepSize(state.stepping, ConstrainedStep::actor);
1159     return false;
1160   }
1161 
1162   /// Inactive
1163   ///
1164   /// This checks if a navigation break had been triggered or navigator
1165   /// is misconfigured
1166   ///
1167   /// @tparam propagator_state_t The state type of the propagator
1168   /// @tparam stepper_t The type of stepper used for the propagation
1169   ///
1170   /// @param [in,out] state is the propagation state object
1171   /// @param [in] stepper Stepper in use
1172   ///
1173   /// boolean return triggers exit to stepper
1174   template <typename propagator_state_t, typename stepper_t>
1175   bool inactive(propagator_state_t& state, const stepper_t& stepper) const {
1176     // Void behavior in case no tracking geometry is present
1177     if (!m_cfg.trackingGeometry) {
1178       return true;
1179     }
1180     // turn the navigator into void when you are instructed to do nothing
1181     if (!m_cfg.resolveSensitive && !m_cfg.resolveMaterial &&
1182         !m_cfg.resolvePassive) {
1183       return true;
1184     }
1185 
1186     // Navigation break handling
1187     // This checks if a navigation break had been triggered:
1188     // - If so & the target exists or was hit - it simply returns
1189     // - If a target exists and was not yet hit, it checks for it
1190     // -> return is always to the stepper
1191     if (state.navigation.navigationBreak) {
1192       // target exists and reached, or no target exists
1193       if (state.navigation.targetReached || !state.navigation.targetSurface) {
1194         return true;
1195       }
1196       // TODO we do not know the intersection index - passing 0
1197       auto targetStatus = stepper.updateSurfaceStatus(
1198           state.stepping, *state.navigation.targetSurface, 0,
1199           state.options.direction, BoundaryCheck(true),
1200           state.options.surfaceTolerance, logger());
1201       // the only advance could have been to the target
1202       if (targetStatus == Intersection3D::Status::onSurface) {
1203         // set the target surface
1204         state.navigation.currentSurface = state.navigation.targetSurface;
1205         ACTS_VERBOSE(volInfo(state)
1206                      << volInfo(state)
1207                      << "Current surface set to target surface "
1208                      << state.navigation.currentSurface->geometryId());
1209         return true;
1210       }
1211     }
1212     return false;
1213   }
1214 
1215  private:
1216   template <typename propagator_state_t>
1217   std::string volInfo(const propagator_state_t& state) const {
1218     return (state.navigation.currentVolume
1219                 ? state.navigation.currentVolume->volumeName()
1220                 : "No Volume") +
1221            " | ";
1222   }
1223 
1224   const Logger& logger() const { return *m_logger; }
1225 
1226   Config m_cfg;
1227 
1228   std::shared_ptr<const Logger> m_logger;
1229 };
1230 
1231 }  // namespace Acts