Analysis Software
Documentation for sPHENIX simulation 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 
14 #include "Acts/Geometry/Layer.hpp"
21 
22 #include <iomanip>
23 #include <iterator>
24 #include <sstream>
25 #include <string>
26 
27 #include <boost/container/small_vector.hpp>
28 
29 namespace Acts {
30 
36 template <typename object_t>
40 
41  // How to resolve the geometry
43  bool resolveSensitive = true;
45  bool resolveMaterial = true;
47  bool resolvePassive = false;
48 
50  const object_t* startObject = nullptr;
52  const object_t* endObject = nullptr;
53 
55  const Surface* targetSurface = nullptr;
57  std::vector<GeometryIdentifier> externalSurfaces = {};
58 
60  double pathLimit = std::numeric_limits<double>::max();
61 
63  double overstepLimit = 0;
64 
67 };
68 
91 class Navigator {
92  public:
93  using Surfaces = std::vector<const Surface*>;
94 
95  using NavigationSurfaces =
96  boost::container::small_vector<SurfaceIntersection, 10>;
97 
98  using NavigationLayers =
99  boost::container::small_vector<LayerIntersection, 10>;
100 
101  using NavigationBoundaries =
102  boost::container::small_vector<BoundaryIntersection, 4>;
103 
104  using ExternalSurfaces = std::multimap<uint64_t, GeometryIdentifier>;
105 
107  enum struct Stage : int {
108  undefined = 0,
109  surfaceTarget = 1,
110  layerTarget = 2,
111  boundaryTarget = 3
112  };
113 
114  struct Config {
116  std::shared_ptr<const TrackingGeometry> trackingGeometry{nullptr};
117 
120  bool resolveSensitive = true;
122  bool resolveMaterial = true;
124  bool resolvePassive = false;
125 
129  };
130 
136  struct State {
137  // Navigation on surface level
141  std::size_t navSurfaceIndex = navSurfaces.size();
142 
143  // Navigation on layer level
147  std::size_t navLayerIndex = navLayers.size();
148 
149  // Navigation on volume level
153  std::size_t navBoundaryIndex = navBoundaries.size();
154 
155  auto navSurface() const { return navSurfaces.at(navSurfaceIndex); }
156  auto navLayer() const { return navLayers.at(navLayerIndex); }
157  auto navBoundary() const { return navBoundaries.at(navBoundaryIndex); }
158 
161 
163  const TrackingVolume* worldVolume = nullptr;
164 
166  const TrackingVolume* startVolume = nullptr;
168  const Layer* startLayer = nullptr;
170  const Surface* startSurface = nullptr;
172  const Surface* currentSurface = nullptr;
174  const TrackingVolume* currentVolume = nullptr;
176  const TrackingVolume* targetVolume = nullptr;
178  const Layer* targetLayer = nullptr;
180  const Surface* targetSurface = nullptr;
181 
183  bool startLayerResolved = false;
185  bool targetReached = false;
190  bool navigationBreak = false;
191  // The navigation stage (@todo: integrate break, target)
195  };
196 
201  explicit Navigator(Config cfg,
202  std::shared_ptr<const Logger> _logger =
204  : m_cfg{std::move(cfg)}, m_logger{std::move(_logger)} {}
205 
206  State makeState(const Surface* startSurface,
207  const Surface* targetSurface) const {
208  State result;
209  result.startSurface = startSurface;
210  result.targetSurface = targetSurface;
211  return result;
212  }
213 
222  void resetState(State& state, const GeometryContext& geoContext,
223  const Vector3& pos, const Vector3& dir,
224  const Surface* ssurface, const Surface* tsurface) const {
225  // Reset everything first
226  state = State();
227 
228  // Set the start, current and target objects
229  state.startSurface = ssurface;
230  if (ssurface->associatedLayer() != nullptr) {
231  state.startLayer = ssurface->associatedLayer();
232  }
233  if (state.startLayer->trackingVolume() != nullptr) {
234  state.startVolume = state.startLayer->trackingVolume();
235  }
236  state.currentSurface = state.startSurface;
237  state.currentVolume = state.startVolume;
238  state.targetSurface = tsurface;
239 
240  // Get the compatible layers (including the current layer)
241  NavigationOptions<Layer> navOpts;
242  navOpts.resolveSensitive = true;
243  navOpts.resolveMaterial = true;
244  navOpts.resolvePassive = true;
245  state.navLayers =
246  state.currentVolume->compatibleLayers(geoContext, pos, dir, navOpts);
247 
248  // Set the index to the first
249  state.navLayerIndex = 0;
250  }
251 
252  const Surface* currentSurface(const State& state) const {
253  return state.currentSurface;
254  }
255 
256  const TrackingVolume* currentVolume(const State& state) const {
257  return state.currentVolume;
258  }
259 
260  const IVolumeMaterial* currentVolumeMaterial(const State& state) const {
261  if (state.currentVolume == nullptr) {
262  return nullptr;
263  }
264  return state.currentVolume->volumeMaterial();
265  }
266 
267  const Surface* startSurface(const State& state) const {
268  return state.startSurface;
269  }
270 
271  const Surface* targetSurface(const State& state) const {
272  return state.targetSurface;
273  }
274 
275  bool targetReached(const State& state) const { return state.targetReached; }
276 
277  bool endOfWorldReached(State& state) const {
278  return state.currentVolume == nullptr;
279  }
280 
281  bool navigationBreak(const State& state) const {
282  return state.navigationBreak;
283  }
284 
285  void currentSurface(State& state, const Surface* surface) const {
286  state.currentSurface = surface;
287  }
288 
289  void targetReached(State& state, bool targetReached) const {
290  state.targetReached = targetReached;
291  }
292 
293  void navigationBreak(State& state, bool navigationBreak) const {
294  state.navigationBreak = navigationBreak;
295  }
296 
297  void insertExternalSurface(State& state, GeometryIdentifier geoid) const {
298  state.externalSurfaces.insert(
299  std::pair<uint64_t, GeometryIdentifier>(geoid.layer(), geoid));
300  }
301 
311  template <typename propagator_state_t, typename stepper_t>
312  void initialize(propagator_state_t& state, const stepper_t& stepper) const {
313  // Call the navigation helper prior to actual navigation
314  ACTS_VERBOSE(volInfo(state) << "Initialization.");
315 
316  // Set the world volume if it is not set
317  if (not state.navigation.worldVolume) {
318  state.navigation.worldVolume =
319  m_cfg.trackingGeometry->highestTrackingVolume();
320  }
321 
322  // We set the current surface to the start surface
323  // for eventual post-update action, e.g. material integration
324  // or collection when leaving a surface at the start of
325  // an extrapolation process
326  state.navigation.currentSurface = state.navigation.startSurface;
327  if (state.navigation.currentSurface) {
328  ACTS_VERBOSE(volInfo(state)
329  << "Current surface set to start surface "
330  << state.navigation.currentSurface->geometryId());
331  }
332 
333  // Fast Navigation initialization for start condition:
334  // - short-cut through object association, saves navigation in the
335  // - geometry and volume tree search for the lowest volume
336  if (state.navigation.startSurface &&
337  state.navigation.startSurface->associatedLayer()) {
338  ACTS_VERBOSE(
339  volInfo(state)
340  << "Fast start initialization through association from Surface.");
341  // assign the current layer and volume by association
342  state.navigation.startLayer =
343  state.navigation.startSurface->associatedLayer();
344  state.navigation.startVolume =
345  state.navigation.startLayer->trackingVolume();
346  // Set the start volume as current volume
347  state.navigation.currentVolume = state.navigation.startVolume;
348  } else if (state.navigation.startVolume) {
349  ACTS_VERBOSE(
350  volInfo(state)
351  << "Fast start initialization through association from Volume.");
352  state.navigation.startLayer =
353  state.navigation.startVolume->associatedLayer(
354  state.geoContext, stepper.position(state.stepping));
355  // Set the start volume as current volume
356  state.navigation.currentVolume = state.navigation.startVolume;
357  } else {
358  ACTS_VERBOSE(volInfo(state)
359  << "Slow start initialization through search.");
360  // current volume and layer search through global search
361  ACTS_VERBOSE(volInfo(state)
362  << "Starting from position "
363  << toString(stepper.position(state.stepping))
364  << " and direction "
365  << toString(stepper.direction(state.stepping)));
366  state.navigation.startVolume =
367  m_cfg.trackingGeometry->lowestTrackingVolume(
368  state.geoContext, stepper.position(state.stepping));
369  state.navigation.startLayer =
370  state.navigation.startVolume
371  ? state.navigation.startVolume->associatedLayer(
372  state.geoContext, stepper.position(state.stepping))
373  : nullptr;
374  // Set the start volume as current volume
375  state.navigation.currentVolume = state.navigation.startVolume;
376  if (state.navigation.startVolume) {
377  ACTS_VERBOSE(volInfo(state) << "Start volume resolved.");
378  }
379  }
380  }
381 
394  template <typename propagator_state_t, typename stepper_t>
395  void preStep(propagator_state_t& state, const stepper_t& stepper) const {
396  // Check if the navigator is inactive
397  if (inactive(state, stepper)) {
398  return;
399  }
400 
401  // Call the navigation helper prior to actual navigation
402  ACTS_VERBOSE(volInfo(state) << "Entering navigator::preStep.");
403 
404  // Initialize the target and target volume
405  if (state.navigation.targetSurface and not state.navigation.targetVolume) {
406  // Find out about the target as much as you can
407  initializeTarget(state, stepper);
408  }
409  // Try targeting the surfaces - then layers - then boundaries
410  if (state.navigation.navigationStage <= Stage::surfaceTarget and
411  targetSurfaces(state, stepper)) {
412  ACTS_VERBOSE(volInfo(state) << "Target set to next surface.");
413  } else if (state.navigation.navigationStage <= Stage::layerTarget and
414  targetLayers(state, stepper)) {
415  ACTS_VERBOSE(volInfo(state) << "Target set to next layer.");
416  } else if (targetBoundaries(state, stepper)) {
417  ACTS_VERBOSE(volInfo(state) << "Target set to next boundary.");
418  } else {
419  ACTS_VERBOSE(volInfo(state)
420  << "No further navigation action, proceed to target.");
421  // Set navigation break and release the navigation step size
422  state.navigation.navigationBreak = true;
423  stepper.releaseStepSize(state.stepping);
424  }
425 
426  // Navigator target always resets the current surface
427  state.navigation.currentSurface = nullptr;
428  }
429 
449  template <typename propagator_state_t, typename stepper_t>
450  void postStep(propagator_state_t& state, const stepper_t& stepper) const {
451  // Check if the navigator is inactive
452  if (inactive(state, stepper)) {
453  return;
454  }
455 
456  // Set the navigation stage
457  state.navigation.navigationStage = Stage::undefined;
458 
459  // Call the navigation helper prior to actual navigation
460  ACTS_VERBOSE(volInfo(state) << "Entering navigator::postStep.");
461 
462  // Navigator post step always starts without current surface
463  state.navigation.currentSurface = nullptr;
464 
465  // (b) Status call within propagation loop
466  // Try finding status of surfaces
467  if (surfaceStatus(state, stepper, state.navigation.navSurfaces,
468  state.navigation.navSurfaceIndex)) {
469  ACTS_VERBOSE(volInfo(state) << "Post step: in surface handling.");
470  if (state.navigation.currentSurface) {
471  ACTS_VERBOSE(volInfo(state)
472  << "On surface: switch forward or release.");
473  if (++state.navigation.navSurfaceIndex ==
474  state.navigation.navSurfaces.size()) {
475  // this was the last surface, check if we have layers
476  if (!state.navigation.navLayers.empty()) {
477  ++state.navigation.navLayerIndex;
478  } else if (state.navigation.startLayer != nullptr and
479  state.navigation.currentSurface->associatedLayer() ==
480  state.navigation.startLayer) {
481  // this was the start layer, switch to layer target next
482  state.navigation.navigationStage = Stage::layerTarget;
483  return;
484  } else {
485  // no layers, go to boundary
486  state.navigation.navigationStage = Stage::boundaryTarget;
487  return;
488  }
489  }
490  }
491  // Set the navigation stage to surface target
492  state.navigation.navigationStage = Stage::surfaceTarget;
493  ACTS_VERBOSE(volInfo(state) << "Staying focussed on surface.");
494  // Try finding status of layer
495  } else if (surfaceStatus(state, stepper, state.navigation.navLayers,
496  state.navigation.navLayerIndex)) {
497  ACTS_VERBOSE(volInfo(state) << "Post step: in layer handling.");
498  if (state.navigation.currentSurface != nullptr) {
499  ACTS_VERBOSE(volInfo(state) << "On layer: update layer information.");
500  if (resolveSurfaces(state, stepper)) {
501  // Set the navigation stage back to surface handling
502  state.navigation.navigationStage = Stage::surfaceTarget;
503  return;
504  }
505  } else {
506  // Set the navigation stage to layer target
507  state.navigation.navigationStage = Stage::layerTarget;
508  ACTS_VERBOSE(volInfo(state) << "Staying focussed on layer.");
509  }
510  // Try finding status of boundaries
511  } else if (surfaceStatus(state, stepper, state.navigation.navBoundaries,
512  state.navigation.navBoundaryIndex)) {
513  ACTS_VERBOSE(volInfo(state) << "Post step: in boundary handling.");
514 
515  // Are we on the boundary - then overwrite the stage
516  if (state.navigation.currentSurface != nullptr) {
517  // Set the navigation stage back to surface handling
518  ACTS_VERBOSE(volInfo(state)
519  << "On boundary: update volume information.");
520  // We are on a boundary, reset all information
521  state.navigation.navSurfaces.clear();
522  state.navigation.navSurfaceIndex = state.navigation.navSurfaces.size();
523  state.navigation.navLayers.clear();
524  state.navigation.navLayerIndex = state.navigation.navLayers.size();
525  state.navigation.lastHierarchySurfaceReached = false;
526  // Update volume information
527  // get the attached volume information
528  auto boundary = state.navigation.navBoundary().object();
529  state.navigation.currentVolume = boundary->attachedVolume(
530  state.geoContext, stepper.position(state.stepping),
531  stepper.direction(state.stepping), state.options.direction);
532  // No volume anymore : end of known world
533  if (!state.navigation.currentVolume) {
534  ACTS_VERBOSE(
535  volInfo(state)
536  << "No more volume to progress to, stopping navigation.");
537  // Navigation break & release navigation stepping
538  state.navigation.navigationBreak = true;
539  stepper.releaseStepSize(state.stepping);
540  return;
541  } else {
542  ACTS_VERBOSE(volInfo(state) << "Volume updated.");
543  // Forget the boundary information
544  state.navigation.navBoundaries.clear();
545  state.navigation.navBoundaryIndex =
546  state.navigation.navBoundaries.size();
547  }
548  } else {
549  // Set the navigation stage back to boundary target
550  state.navigation.navigationStage = Stage::boundaryTarget;
551  ACTS_VERBOSE(volInfo(state) << "Staying focussed on boundary.");
552  }
553  } else if (state.navigation.currentVolume ==
554  state.navigation.targetVolume) {
555  if (state.navigation.targetSurface == nullptr) {
556  ACTS_WARNING(volInfo(state)
557  << "No further navigation action, proceed to "
558  "target. This is very likely an error");
559  } else {
560  ACTS_VERBOSE(volInfo(state)
561  << "No further navigation action, proceed to target.");
562  }
563  // Set navigation break and release the navigation step size
564  state.navigation.navigationBreak = true;
565  stepper.releaseStepSize(state.stepping);
566  } else {
567  ACTS_VERBOSE(volInfo(state)
568  << "Status could not be determined - good luck.");
569  }
570  }
571 
572  private:
588  template <typename propagator_state_t, typename stepper_t,
589  typename navigation_surfaces_t>
590  bool surfaceStatus(propagator_state_t& state, const stepper_t& stepper,
591  const navigation_surfaces_t& navSurfaces,
592  std::size_t navIndex) const {
593  // No surfaces, status check will be done on layer
594  if (navSurfaces.empty() or navIndex == navSurfaces.size()) {
595  return false;
596  }
597  // Take the current surface
598  auto surface = navSurfaces.at(navIndex).representation();
599  // Check if we are at a surface
600  // If we are on the surface pointed at by the index, we can make
601  // it the current one to pass it to the other actors
602  auto surfaceStatus = stepper.updateSurfaceStatus(
603  state.stepping, *surface, state.options.direction, true,
604  state.options.targetTolerance, logger());
605  if (surfaceStatus == Intersection3D::Status::onSurface) {
606  ACTS_VERBOSE(volInfo(state)
607  << "Status Surface successfully hit, storing it.");
608  // Set in navigation state, so actors and aborters can access it
609  state.navigation.currentSurface = surface;
610  if (state.navigation.currentSurface) {
611  ACTS_VERBOSE(volInfo(state)
612  << "Current surface set to surface "
613  << state.navigation.currentSurface->geometryId());
614  }
615  }
616  // Return a positive status: either on it, or on the way
617  return true;
618  }
619 
632  template <typename propagator_state_t, typename stepper_t>
633  bool targetSurfaces(propagator_state_t& state,
634  const stepper_t& stepper) const {
635  if (state.navigation.navigationBreak) {
636  return false;
637  }
638  // Make sure resolve Surfaces is called on the start layer
639  if (state.navigation.startLayer and
640  not state.navigation.startLayerResolved) {
641  ACTS_VERBOSE(volInfo(state) << "Start layer to be resolved.");
642  // We provide the layer to the resolve surface method in this case
643  state.navigation.startLayerResolved = true;
644  bool startResolved =
645  resolveSurfaces(state, stepper, state.navigation.startLayer);
646  if (not startResolved and
647  state.navigation.startLayer == state.navigation.targetLayer) {
648  ACTS_VERBOSE(volInfo(state)
649  << "Start is target layer, nothing left to do.");
650  // set the navigation break
651  state.navigation.navigationBreak = true;
652  stepper.releaseStepSize(state.stepping);
653  }
654  return startResolved;
655  }
656 
657  // The call that we are on a layer and have not yet resolved the surfaces
658  // No surfaces, do not return to stepper
659  if (state.navigation.navSurfaces.empty() or
660  state.navigation.navSurfaceIndex ==
661  state.navigation.navSurfaces.size()) {
662  ACTS_VERBOSE(volInfo(state)
663  << "No surfaces present, target at layer first.");
664  return false;
665  }
666  auto layerID = state.navigation.navSurface().object()->geometryId().layer();
667  std::pair<ExternalSurfaces::iterator, ExternalSurfaces::iterator>
668  externalSurfaceRange =
669  state.navigation.externalSurfaces.equal_range(layerID);
670  // Loop over the remaining navigation surfaces
671  while (state.navigation.navSurfaceIndex !=
672  state.navigation.navSurfaces.size()) {
673  // Screen output how much is left to try
674  ACTS_VERBOSE(volInfo(state)
675  << (state.navigation.navSurfaces.size() -
676  state.navigation.navSurfaceIndex)
677  << " out of " << state.navigation.navSurfaces.size()
678  << " surfaces remain to try.");
679  // Take the surface
680  auto surface = state.navigation.navSurface().object();
681  // Screen output which surface you are on
682  ACTS_VERBOSE(volInfo(state) << "Next surface candidate will be "
683  << surface->geometryId());
684  // Estimate the surface status
685  bool boundaryCheck = true;
686  for (auto it = externalSurfaceRange.first;
687  it != externalSurfaceRange.second; it++) {
688  if (surface->geometryId() == it->second) {
689  boundaryCheck = false;
690  break;
691  }
692  }
693  auto surfaceStatus = stepper.updateSurfaceStatus(
694  state.stepping, *surface, state.options.direction, boundaryCheck,
695  state.options.targetTolerance, logger());
696  if (surfaceStatus == Intersection3D::Status::reachable) {
697  ACTS_VERBOSE(volInfo(state)
698  << "Surface reachable, step size updated to "
699  << stepper.outputStepSize(state.stepping));
700  return true;
701  }
702  ++state.navigation.navSurfaceIndex;
703  continue;
704  }
705 
706  // Reached the end of the surface iteration
707  if (state.navigation.navSurfaceIndex ==
708  state.navigation.navSurfaces.size()) {
709  // first clear the surface cache
710  state.navigation.navSurfaces.clear();
711  state.navigation.navSurfaceIndex = state.navigation.navSurfaces.size();
712 
713  if (state.navigation.navLayerIndex != state.navigation.navLayers.size()) {
714  ACTS_VERBOSE(volInfo(state)
715  << "Last surface on layer reached, switching layer.");
716  // now switch to the next layer
717  ++state.navigation.navLayerIndex;
718  } else {
719  ACTS_VERBOSE(volInfo(state)
720  << "Last surface on layer reached, and no layer.");
721  // first clear the surface cache
722  state.navigation.lastHierarchySurfaceReached = true;
723  state.navigation.navigationBreak =
724  (state.navigation.currentVolume == state.navigation.targetVolume);
725  }
726  }
727  // Do not return to the propagator
728  return false;
729  }
730 
749  template <typename propagator_state_t, typename stepper_t>
750  bool targetLayers(propagator_state_t& state, const stepper_t& stepper) const {
751  using namespace UnitLiterals;
752 
753  if (state.navigation.navigationBreak ||
754  state.navigation.lastHierarchySurfaceReached) {
755  return false;
756  }
757 
758  // if there are no layers, go back to the navigator (not stepper yet)
759  if (state.navigation.navLayers.empty()) {
760  ACTS_VERBOSE(volInfo(state)
761  << "No layers present, resolve volume first.");
762 
763  // check if current volume has BVH, or layers
764  if (state.navigation.currentVolume->hasBoundingVolumeHierarchy()) {
765  // has hierarchy, use that, skip layer resolution
767  navOpts.resolveSensitive = m_cfg.resolveSensitive;
768  navOpts.resolveMaterial = m_cfg.resolveMaterial;
769  navOpts.resolvePassive = m_cfg.resolvePassive;
770  navOpts.endObject = state.navigation.targetSurface;
771  navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
772  double opening_angle = 0;
773 
774  // Preliminary version of the frustum opening angle estimation.
775  // Currently not used (only rays), but will be.
776 
777  /*
778  Vector3 pos = stepper.position(state.stepping);
779  double mom = stepper.momentum(state.stepping) / UnitConstants::GeV;
780  double q = stepper.charge(state.stepping);
781  Vector3 dir = stepper.direction(state.stepping);
782  Vector3 B = stepper.getField(state.stepping, pos);
783  if (B.squaredNorm() > 1e-9) {
784  // ~ non-zero field
785  double ir = (dir.cross(B).norm()) * q / mom;
786  double s;
787  if (state.options.direction == Direction::Forward) {
788  s = state.stepping.stepSize.max();
789  } else {
790  s = state.stepping.stepSize.min();
791  }
792  opening_angle = std::atan((1 - std::cos(s * ir)) / std::sin(s * ir));
793  }
794 
795  ACTS_VERBOSE(volInfo(state) << "Estimating opening angle for frustum
796  nav:"); ACTS_VERBOSE(volInfo(state) << "pos: " << pos.transpose());
797  ACTS_VERBOSE(volInfo(state) << "dir: " << dir.transpose());
798  ACTS_VERBOSE(volInfo(state) << "B: " << B.transpose() << " |B|: " <<
799  B.norm()); ACTS_VERBOSE(volInfo(state) << "step mom: " <<
800  stepper.momentum(state.stepping)); ACTS_VERBOSE(volInfo(state) << "=>
801  opening angle: " << opening_angle);
802  */
803 
804  auto protoNavSurfaces =
805  state.navigation.currentVolume->compatibleSurfacesFromHierarchy(
806  state.geoContext, stepper.position(state.stepping),
807  state.options.direction * stepper.direction(state.stepping),
808  opening_angle, navOpts);
809  if (!protoNavSurfaces.empty()) {
810  // did we find any surfaces?
811 
812  // Check: are we on the first surface?
813  // TODO magic number `1_um`
814  if ((state.navigation.currentSurface == nullptr &&
815  state.navigation.navSurfaces.empty()) ||
816  protoNavSurfaces.front().pathLength() > 1_um) {
817  // we are not, go on
818  // state.navigation.navSurfaces = std::move(protoNavSurfaces);
819  state.navigation.navSurfaces.clear();
820  state.navigation.navSurfaces.insert(
821  state.navigation.navSurfaces.begin(), protoNavSurfaces.begin(),
822  protoNavSurfaces.end());
823 
824  state.navigation.navSurfaceIndex = 0;
825  state.navigation.navLayers = {};
826  state.navigation.navLayerIndex = state.navigation.navLayers.size();
827  // The stepper updates the step size ( single / multi component)
828  stepper.updateStepSize(state.stepping,
829  state.navigation.navSurface(),
830  state.options.direction, true);
831  ACTS_VERBOSE(volInfo(state)
832  << "Navigation stepSize updated to "
833  << stepper.outputStepSize(state.stepping));
834  return true;
835  }
836  }
837  }
838 
839  if (resolveLayers(state, stepper)) {
840  // The layer resolving worked
841  return true;
842  }
843  }
844  // loop over the available navigation layer candidates
845  while (state.navigation.navLayerIndex !=
846  state.navigation.navLayers.size()) {
847  // The layer surface
848  auto layerSurface = state.navigation.navLayer().representation();
849  // We are on the layer
850  if (state.navigation.currentSurface == layerSurface) {
851  ACTS_VERBOSE(volInfo(state) << "We are on a layer, resolve Surfaces.");
852  // If you found surfaces return to the propagator
853  if (resolveSurfaces(state, stepper)) {
854  return true;
855  } else {
856  // Try the next one
857  ++state.navigation.navLayerIndex;
858  continue;
859  }
860  }
861  // Try to step towards it
862  auto layerStatus = stepper.updateSurfaceStatus(
863  state.stepping, *layerSurface, state.options.direction, true,
864  state.options.targetTolerance, logger());
865  if (layerStatus == Intersection3D::Status::reachable) {
866  ACTS_VERBOSE(volInfo(state) << "Layer reachable, step size updated to "
867  << stepper.outputStepSize(state.stepping));
868  return true;
869  }
870  ACTS_VERBOSE(volInfo(state)
871  << "Layer intersection not valid, skipping it.");
872  ++state.navigation.navLayerIndex;
873  }
874 
875  // Re-initialize target at last layer, only in case it is the target volume
876  // This avoids a wrong target volume estimation
877  if (state.navigation.currentVolume == state.navigation.targetVolume) {
878  initializeTarget(state, stepper);
879  }
880  // Screen output
881  if (logger().doPrint(Logging::VERBOSE)) {
882  std::ostringstream os;
883  os << "Last layer";
884  if (state.navigation.currentVolume == state.navigation.targetVolume) {
885  os << " (final volume) done, proceed to target.";
886  } else {
887  os << " done, target volume boundary.";
888  }
889  logger().log(Logging::VERBOSE, os.str());
890  }
891  // Set the navigation break if necessary
892  state.navigation.navigationBreak =
893  (state.navigation.currentVolume == state.navigation.targetVolume);
894  return false;
895  }
896 
922  template <typename propagator_state_t, typename stepper_t>
923  bool targetBoundaries(propagator_state_t& state,
924  const stepper_t& stepper) const {
925  if (state.navigation.navigationBreak) {
926  return false;
927  }
928 
929  if (!state.navigation.currentVolume) {
930  ACTS_VERBOSE(volInfo(state)
931  << "No sufficient information to resolve boundary, "
932  "stopping navigation.");
933  stepper.releaseStepSize(state.stepping);
934  return false;
935  } else if (state.navigation.currentVolume ==
936  state.navigation.targetVolume) {
937  ACTS_VERBOSE(volInfo(state)
938  << "In target volume: no need to resolve boundary, "
939  "stopping navigation.");
940  state.navigation.navigationBreak = true;
941  stepper.releaseStepSize(state.stepping);
942  return true;
943  }
944 
945  // Re-initialize target at volume boundary
946  initializeTarget(state, stepper);
947 
948  // Helper function to find boundaries
949  auto findBoundaries = [&]() -> bool {
950  // The navigation options
952  // Exclude the current surface in case it's a boundary
953  navOpts.startObject = state.navigation.currentSurface;
954  navOpts.pathLimit =
955  stepper.getStepSize(state.stepping, ConstrainedStep::aborter);
956  navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
957  navOpts.forceIntersectBoundaries =
958  state.navigation.forceIntersectBoundaries;
959 
960  ACTS_VERBOSE(volInfo(state)
961  << "Try to find boundaries, we are at: "
962  << stepper.position(state.stepping).transpose() << ", dir: "
963  << stepper.direction(state.stepping).transpose());
964 
965  // Evaluate the boundary surfaces
966  state.navigation.navBoundaries =
967  state.navigation.currentVolume->compatibleBoundaries(
968  state.geoContext, stepper.position(state.stepping),
969  state.options.direction * stepper.direction(state.stepping),
970  navOpts, logger());
971  // The number of boundary candidates
973  std::ostringstream os;
974  os << state.navigation.navBoundaries.size();
975  os << " boundary candidates found at path(s): ";
976  for (auto& bc : state.navigation.navBoundaries) {
977  os << bc.pathLength() << " ";
978  }
979  logger().log(Logging::VERBOSE, os.str());
980  }
981  // Set the begin index
982  state.navigation.navBoundaryIndex = 0;
983  if (not state.navigation.navBoundaries.empty()) {
984  // Set to the first and return to the stepper
985  stepper.updateStepSize(state.stepping, state.navigation.navBoundary(),
986  state.options.direction, true);
987  ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to "
988  << stepper.outputStepSize(state.stepping));
989  return true;
990  }
991  return false;
992  };
993 
994  // No boundaries are assigned yet, find them
995  if (state.navigation.navBoundaries.empty() and findBoundaries()) {
996  return true;
997  }
998 
999  // Loop over the boundary surface
1000  while (state.navigation.navBoundaryIndex !=
1001  state.navigation.navBoundaries.size()) {
1002  // That is the current boundary surface
1003  auto boundarySurface = state.navigation.navBoundary().representation();
1004  // Step towards the boundary surfrace
1005  auto boundaryStatus = stepper.updateSurfaceStatus(
1006  state.stepping, *boundarySurface, state.options.direction, true,
1007  state.options.targetTolerance, logger());
1008  if (boundaryStatus == Intersection3D::Status::reachable) {
1009  ACTS_VERBOSE(volInfo(state)
1010  << "Boundary reachable, step size updated to "
1011  << stepper.outputStepSize(state.stepping));
1012  return true;
1013  } else {
1014  ACTS_VERBOSE("Boundary "
1015  << (state.navigation.navBoundaries.size() -
1016  state.navigation.navBoundaryIndex)
1017  << " out of " << state.navigation.navBoundaries.size()
1018  << " not reachable anymore, switching to next.");
1019  ACTS_VERBOSE("Targeted boundary surface was: \n"
1020  << std::tie(*boundarySurface, state.geoContext));
1021  }
1022  // Increase the index to the next one
1023  ++state.navigation.navBoundaryIndex;
1024  }
1025  // We have to leave the volume somehow, so try again
1026  state.navigation.navBoundaries.clear();
1027  ACTS_VERBOSE(volInfo(state) << "Boundary navigation lost, re-targetting.");
1028  state.navigation.forceIntersectBoundaries = true;
1029  if (findBoundaries()) {
1030  // Resetting intersection check for boundary surfaces
1031  state.navigation.forceIntersectBoundaries = false;
1032  return true;
1033  }
1034 
1035  // Tried our best, but couldn't do anything
1036  return false;
1037  }
1038 
1056  template <typename propagator_state_t, typename stepper_t>
1057  void initializeTarget(propagator_state_t& state,
1058  const stepper_t& stepper) const {
1059  if (state.navigation.targetVolume and
1060  state.stepping.pathAccumulated == 0.) {
1061  ACTS_VERBOSE(volInfo(state)
1062  << "Re-initialzing cancelled as it is the first step.");
1063  return;
1064  }
1065  // Fast Navigation initialization for target:
1066  if (state.navigation.targetSurface &&
1067  state.navigation.targetSurface->associatedLayer() &&
1068  !state.navigation.targetVolume) {
1069  ACTS_VERBOSE(volInfo(state)
1070  << "Fast target initialization through association.");
1071  ACTS_VERBOSE(volInfo(state)
1072  << "Target surface set to "
1073  << state.navigation.targetSurface->geometryId());
1074  state.navigation.targetLayer =
1075  state.navigation.targetSurface->associatedLayer();
1076  state.navigation.targetVolume =
1077  state.navigation.targetLayer->trackingVolume();
1078  } else if (state.navigation.targetSurface) {
1079  // screen output that you do a re-initialization
1080  if (state.navigation.targetVolume) {
1081  ACTS_VERBOSE(volInfo(state)
1082  << "Re-initialization of target volume triggered.");
1083  }
1084  // Slow navigation initialization for target:
1085  // target volume and layer search through global search
1086  auto targetIntersection =
1087  state.navigation.targetSurface
1088  ->intersect(
1089  state.geoContext, stepper.position(state.stepping),
1090  state.options.direction * stepper.direction(state.stepping),
1091  false, state.options.targetTolerance)
1092  .closest();
1093  if (targetIntersection) {
1094  ACTS_VERBOSE(volInfo(state)
1095  << "Target estimate position ("
1096  << targetIntersection.position().x() << ", "
1097  << targetIntersection.position().y() << ", "
1098  << targetIntersection.position().z() << ")");
1100  auto tPosition = targetIntersection.position();
1101  state.navigation.targetVolume =
1102  m_cfg.trackingGeometry->lowestTrackingVolume(state.geoContext,
1103  tPosition);
1104  state.navigation.targetLayer =
1105  state.navigation.targetVolume
1106  ? state.navigation.targetVolume->associatedLayer(
1107  state.geoContext, tPosition)
1108  : nullptr;
1109  if (state.navigation.targetVolume) {
1110  ACTS_VERBOSE(volInfo(state)
1111  << "Target volume estimated : "
1112  << state.navigation.targetVolume->volumeName());
1113  }
1114  }
1115  }
1116  }
1117 
1128  template <typename propagator_state_t, typename stepper_t>
1129  bool resolveSurfaces(propagator_state_t& state, const stepper_t& stepper,
1130  const Layer* cLayer = nullptr) const {
1131  // get the layer and layer surface
1132  auto layerSurface = cLayer ? state.navigation.startSurface
1133  : state.navigation.navLayer().representation();
1134  auto navLayer = cLayer ? cLayer : state.navigation.navLayer().object();
1135  // are we on the start layer
1136  bool onStart = (navLayer == state.navigation.startLayer);
1137  auto startSurface = onStart ? state.navigation.startSurface : layerSurface;
1138  // Use navigation parameters and NavigationOptions
1140  navOpts.resolveSensitive = m_cfg.resolveSensitive;
1141  navOpts.resolveMaterial = m_cfg.resolveMaterial;
1142  navOpts.resolvePassive = m_cfg.resolvePassive;
1143  navOpts.startObject = startSurface;
1144  navOpts.endObject = state.navigation.targetSurface;
1145 
1146  std::vector<GeometryIdentifier> externalSurfaces;
1147  if (!state.navigation.externalSurfaces.empty()) {
1148  auto layerID = layerSurface->geometryId().layer();
1149  auto externalSurfaceRange =
1150  state.navigation.externalSurfaces.equal_range(layerID);
1151  navOpts.externalSurfaces.reserve(
1152  state.navigation.externalSurfaces.count(layerID));
1153  for (auto itSurface = externalSurfaceRange.first;
1154  itSurface != externalSurfaceRange.second; itSurface++) {
1155  navOpts.externalSurfaces.push_back(itSurface->second);
1156  }
1157  }
1158  // Check the limit
1159  navOpts.pathLimit =
1160  stepper.getStepSize(state.stepping, ConstrainedStep::aborter);
1161  // No overstepping on start layer, otherwise ask the stepper
1162  navOpts.overstepLimit = (cLayer != nullptr)
1163  ? state.options.targetTolerance
1164  : stepper.overstepLimit(state.stepping);
1165 
1166  // get the surfaces
1167  state.navigation.navSurfaces = navLayer->compatibleSurfaces(
1168  state.geoContext, stepper.position(state.stepping),
1169  state.options.direction * stepper.direction(state.stepping), navOpts);
1170  // the number of layer candidates
1171  if (!state.navigation.navSurfaces.empty()) {
1172  if (logger().doPrint(Logging::VERBOSE)) {
1173  std::ostringstream os;
1174  os << state.navigation.navSurfaces.size();
1175  os << " surface candidates found at path(s): ";
1176  for (auto& sfc : state.navigation.navSurfaces) {
1177  os << sfc.pathLength() << " ";
1178  }
1179  logger().log(Logging::VERBOSE, os.str());
1180  }
1181 
1182  // set the index
1183  state.navigation.navSurfaceIndex = 0;
1184  // The stepper updates the step size ( single / multi component)
1185  stepper.updateStepSize(state.stepping, state.navigation.navSurface(),
1186  state.options.direction, true);
1187  ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to "
1188  << stepper.outputStepSize(state.stepping));
1189  return true;
1190  }
1191  state.navigation.navSurfaceIndex = state.navigation.navSurfaces.size();
1192  ACTS_VERBOSE(volInfo(state) << "No surface candidates found.");
1193  return false;
1194  }
1195 
1210  template <typename propagator_state_t, typename stepper_t>
1211  bool resolveLayers(propagator_state_t& state,
1212  const stepper_t& stepper) const {
1213  ACTS_VERBOSE(volInfo(state) << "Searching for compatible layers.");
1214 
1215  // Check if we are in the start volume
1216  auto startLayer =
1217  (state.navigation.currentVolume == state.navigation.startVolume)
1218  ? state.navigation.startLayer
1219  : nullptr;
1220  // Create the navigation options
1221  // - and get the compatible layers, start layer will be excluded
1222  NavigationOptions<Layer> navOpts;
1223  navOpts.boundaryCheck = m_cfg.boundaryCheckLayerResolving;
1224  navOpts.resolveSensitive = m_cfg.resolveSensitive;
1225  navOpts.resolveMaterial = m_cfg.resolveMaterial;
1226  navOpts.resolvePassive = m_cfg.resolvePassive;
1227  navOpts.startObject = startLayer;
1228  // Set also the target surface
1229  navOpts.targetSurface = state.navigation.targetSurface;
1230  navOpts.pathLimit =
1231  stepper.getStepSize(state.stepping, ConstrainedStep::aborter);
1232  navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
1233  // Request the compatible layers
1234  state.navigation.navLayers =
1235  state.navigation.currentVolume->compatibleLayers(
1236  state.geoContext, stepper.position(state.stepping),
1237  state.options.direction * stepper.direction(state.stepping),
1238  navOpts);
1239 
1240  // Layer candidates have been found
1241  if (!state.navigation.navLayers.empty()) {
1242  // Screen output where they are
1243  if (logger().doPrint(Logging::VERBOSE)) {
1244  std::ostringstream os;
1245  os << state.navigation.navLayers.size();
1246  os << " layer candidates found at path(s): ";
1247  for (auto& lc : state.navigation.navLayers) {
1248  os << lc.pathLength() << " ";
1249  }
1250  logger().log(Logging::VERBOSE, os.str());
1251  }
1252  // Set the index to the first
1253  state.navigation.navLayerIndex = 0;
1254  // Setting the step size towards first
1255  if (state.navigation.startLayer &&
1256  state.navigation.navLayer().object() != state.navigation.startLayer) {
1257  ACTS_VERBOSE(volInfo(state) << "Target at layer.");
1258  // The stepper updates the step size ( single / multi component)
1259  stepper.updateStepSize(state.stepping, state.navigation.navLayer(),
1260  state.options.direction, true);
1261  ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to "
1262  << stepper.outputStepSize(state.stepping));
1263  return true;
1264  }
1265  }
1266 
1267  // Set the index to the end of the list
1268  state.navigation.navLayerIndex = state.navigation.navLayers.size();
1269 
1270  // Screen output - no layer candidates found
1271  ACTS_VERBOSE(volInfo(state) << "No compatible layer candidates found.");
1272  // Release the step size
1273  stepper.releaseStepSize(state.stepping);
1274  return false;
1275  }
1276 
1289  template <typename propagator_state_t, typename stepper_t>
1290  bool inactive(propagator_state_t& state, const stepper_t& stepper) const {
1291  // Void behavior in case no tracking geometry is present
1292  if (!m_cfg.trackingGeometry) {
1293  return true;
1294  }
1295  // turn the navigator into void when you are instructed to do nothing
1296  if (!m_cfg.resolveSensitive && !m_cfg.resolveMaterial &&
1297  !m_cfg.resolvePassive) {
1298  return true;
1299  }
1300 
1301  // Navigation break handling
1302  // This checks if a navigation break had been triggered:
1303  // - If so & the target exists or was hit - it simply returns
1304  // - If a target exists and was not yet hit, it checks for it
1305  // -> return is always to the stepper
1306  if (state.navigation.navigationBreak) {
1307  // target exists and reached, or no target exists
1308  if (state.navigation.targetReached || !state.navigation.targetSurface) {
1309  return true;
1310  }
1311  auto targetStatus = stepper.updateSurfaceStatus(
1312  state.stepping, *state.navigation.targetSurface,
1313  state.options.direction, true, state.options.targetTolerance,
1314  logger());
1315  // the only advance could have been to the target
1316  if (targetStatus == Intersection3D::Status::onSurface) {
1317  // set the target surface
1318  state.navigation.currentSurface = state.navigation.targetSurface;
1319  ACTS_VERBOSE(volInfo(state)
1320  << volInfo(state)
1321  << "Current surface set to target surface "
1322  << state.navigation.currentSurface->geometryId());
1323  return true;
1324  }
1325  }
1326  return false;
1327  }
1328 
1329  private:
1330  template <typename propagator_state_t>
1331  std::string volInfo(const propagator_state_t& state) const {
1332  return (state.navigation.currentVolume
1333  ? state.navigation.currentVolume->volumeName()
1334  : "No Volume") +
1335  " | ";
1336  }
1337 
1338  const Logger& logger() const { return *m_logger; }
1339 
1340  Config m_cfg;
1341 
1342  std::shared_ptr<const Logger> m_logger;
1343 };
1344 
1345 } // namespace Acts