ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Navigator.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file Navigator.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2016-2018 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 
11 #include <boost/algorithm/string.hpp>
12 #include <iomanip>
13 #include <iterator>
14 #include <sstream>
15 #include <string>
17 #include "Acts/Geometry/Layer.hpp"
23 #include "Acts/Utilities/Units.hpp"
24 
25 namespace Acts {
26 
27 using namespace Acts::UnitLiterals;
28 
34 template <typename object_t>
38 
41 
42  // How to resolve the geometry
44  bool resolveSensitive = true;
46  bool resolveMaterial = true;
48  bool resolvePassive = false;
49 
51  const object_t* startObject = nullptr;
53  const object_t* endObject = nullptr;
54 
56  const Surface* targetSurface = nullptr;
57 
59  double pathLimit = std::numeric_limits<double>::max();
60 
64  double overstepLimit = -1_um;
65 
74  bool resolves = true, bool resolvem = true,
75  bool resolvep = false, const object_t* sobject = nullptr,
76  const object_t* eobject = nullptr)
77  : navDir(ndir),
78  boundaryCheck(std::move(bcheck)),
79  resolveSensitive(resolves),
80  resolveMaterial(resolvem),
81  resolvePassive(resolvep),
82  startObject(sobject),
83  endObject(eobject),
84  pathLimit(ndir * std::numeric_limits<double>::max()),
85  overstepLimit(-1_um) {}
86 };
87 
111 class Navigator {
112  public:
113  using Surfaces = std::vector<const Surface*>;
114  using SurfaceIter = std::vector<const Surface*>::iterator;
115 
116  using NavigationSurfaces = std::vector<SurfaceIntersection>;
117  using NavigationSurfaceIter = NavigationSurfaces::iterator;
118 
119  using NavigationLayers = std::vector<LayerIntersection>;
120  using NavigationLayerIter = NavigationLayers::iterator;
121 
122  using NavigationBoundaries = std::vector<BoundaryIntersection>;
123  using NavigationBoundaryIter = NavigationBoundaries::iterator;
124 
125  using ExternalSurfaces = std::multimap<const Layer*, const Surface*>;
126 
128  enum struct Stage : int {
129  undefined = 0,
130  surfaceTarget = 1,
131  layerTarget = 2,
132  boundaryTarget = 3
133  };
134 
138  Navigator(std::shared_ptr<const TrackingGeometry> tGeometry = nullptr)
139  : trackingGeometry(std::move(tGeometry)) {}
140 
142  std::shared_ptr<const TrackingGeometry> trackingGeometry;
143 
145  double tolerance = s_onSurfaceTolerance;
146 
149  bool resolveSensitive = true;
151  bool resolveMaterial = true;
153  bool resolvePassive = false;
154 
160  struct State {
161  // Navigation on surface level
163  NavigationSurfaces navSurfaces = {};
165  NavigationSurfaceIter navSurfaceIter = navSurfaces.end();
166 
167  // Navigation on layer level
169  NavigationLayers navLayers = {};
171  NavigationLayerIter navLayerIter = navLayers.end();
172 
173  // Navigation on volume level
175  NavigationBoundaries navBoundaries = {};
177  NavigationBoundaryIter navBoundaryIter = navBoundaries.end();
178 
180  ExternalSurfaces externalSurfaces = {};
181 
183  const TrackingVolume* worldVolume = nullptr;
184 
186  const TrackingVolume* startVolume = nullptr;
188  const Layer* startLayer = nullptr;
190  const Surface* startSurface = nullptr;
192  const Surface* currentSurface = nullptr;
194  const TrackingVolume* currentVolume = nullptr;
196  const TrackingVolume* targetVolume = nullptr;
198  const Layer* targetLayer = nullptr;
200  const Surface* targetSurface = nullptr;
201 
203  bool startLayerResolved = false;
205  bool targetReached = false;
207  bool navigationBreak = false;
208  // The navigation stage (@todo: integrate break, target)
209  Stage navigationStage = Stage::undefined;
210  };
211 
231  template <typename propagator_state_t, typename stepper_t>
232  void status(propagator_state_t& state, const stepper_t& stepper) const {
233  // Check if the navigator is inactive
234  if (inactive(state, stepper)) {
235  return;
236  }
237 
238  // Set the navigation stage
239  state.navigation.navigationStage = Stage::undefined;
240 
241  // Call the navigation helper prior to actual navigation
242  debugLog(state, [&] { return std::string("Entering navigator::status."); });
243 
244  // (a) Pre-stepping call from propgator
245  if (not state.navigation.startVolume) {
246  // Initialize and return
247  initialize(state, stepper);
248  return;
249  }
250 
251  // Navigator status always starts without current surface
252  state.navigation.currentSurface = nullptr;
253 
254  // (b) Status call within propagation loop
255  // Try finding status of surfaces
256  if (status(state, stepper, state.navigation.navSurfaces,
257  state.navigation.navSurfaceIter)) {
258  debugLog(state,
259  [&] { return std::string("Status: in surface handling."); });
260  if (state.navigation.currentSurface) {
261  debugLog(state, [&] {
262  return std::string("On surface: switch forward or release.");
263  });
264  if (++state.navigation.navSurfaceIter ==
265  state.navigation.navSurfaces.end()) {
266  // this was the last surface, check if we have layers
267  if (!state.navigation.navLayers.empty()) {
268  ++state.navigation.navLayerIter;
269  } else {
270  // no layers, go to boundary
271  state.navigation.navigationStage = Stage::boundaryTarget;
272  return;
273  }
274  }
275  }
276  // Set the navigation stage to surface target
277  state.navigation.navigationStage = Stage::surfaceTarget;
278  debugLog(state,
279  [&] { return std::string("Staying focussed on surface."); });
280  // Try finding status of layer
281  } else if (status(state, stepper, state.navigation.navLayers,
282  state.navigation.navLayerIter)) {
283  debugLog(state,
284  [&] { return std::string("Status: in layer handling."); });
285  if (state.navigation.currentSurface != nullptr) {
286  debugLog(state, [&] {
287  return std::string("On layer: update layer information.");
288  });
289  if (resolveSurfaces(state, stepper)) {
290  // Set the navigation stage back to surface handling
291  state.navigation.navigationStage = Stage::surfaceTarget;
292  return;
293  }
294  } else {
295  // Set the navigation stage to layer target
296  state.navigation.navigationStage = Stage::layerTarget;
297  debugLog(state,
298  [&] { return std::string("Staying focussed on layer."); });
299  }
300  // Try finding status of boundaries
301  } else if (status(state, stepper, state.navigation.navBoundaries,
302  state.navigation.navBoundaryIter)) {
303  debugLog(state,
304  [&] { return std::string("Status: in boundary handling."); });
305 
306  // Are we on the boundary - then overwrite the stage
307  if (state.navigation.currentSurface != nullptr) {
308  // Set the navigation stage back to surface handling
309  debugLog(state, [&] {
310  return std::string("On boundary: update volume information.");
311  });
312  // We are on a boundary, reset all information
313  state.navigation.navSurfaces.clear();
314  state.navigation.navSurfaceIter = state.navigation.navSurfaces.end();
315  state.navigation.navLayers.clear();
316  state.navigation.navLayerIter = state.navigation.navLayers.end();
317  // Update volume information
318  // get the attached volume information
319  auto boundary = state.navigation.navBoundaryIter->object;
320  state.navigation.currentVolume = boundary->attachedVolume(
321  state.geoContext, stepper.position(state.stepping),
322  stepper.direction(state.stepping), state.stepping.navDir);
323  // No volume anymore : end of known world
324  if (!state.navigation.currentVolume) {
325  debugLog(state, [&] {
326  return std::string(
327  "No more volume to progress to, stopping navigation.");
328  });
329  // Navigation break & release navigation stepping
330  state.navigation.navigationBreak = true;
331  stepper.releaseStepSize(state.stepping);
332  return;
333  } else {
334  debugLog(state, [&] { return std::string("Volume updated."); });
335  // Forget the bounday information
336  state.navigation.navBoundaries.clear();
337  state.navigation.navBoundaryIter =
338  state.navigation.navBoundaries.end();
339  }
340  } else {
341  // Set the navigation stage back to boundary target
342  state.navigation.navigationStage = Stage::boundaryTarget;
343  debugLog(state,
344  [&] { return std::string("Staying focussed on boundary."); });
345  }
346  } else if (state.navigation.currentVolume ==
347  state.navigation.targetVolume) {
348  debugLog(state, [&] {
349  return std::string("No further navigation action, proceed to target.");
350  });
351  // Set navigation break and release the navigation step size
352  state.navigation.navigationBreak = true;
353  stepper.releaseStepSize(state.stepping);
354  } else {
355  debugLog(state, [&] {
356  return std::string("Status could not be determined - good luck.");
357  });
358  }
359  return;
360  }
361 
374  template <typename propagator_state_t, typename stepper_t>
375  void target(propagator_state_t& state, const stepper_t& stepper) const {
376  // Check if the navigator is inactive
377  if (inactive(state, stepper)) {
378  return;
379  }
380 
381  // Call the navigation helper prior to actual navigation
382  debugLog(state, [&] { return std::string("Entering navigator::target."); });
383 
384  // Initialize the target and target volume
385  if (state.navigation.targetSurface and not state.navigation.targetVolume) {
386  // Find out about the target as much as you can
387  initializeTarget(state, stepper);
388  }
389 
390  // Try targeting the surfaces - then layers - then boundaries
391  if (state.navigation.navigationStage <= Stage::surfaceTarget and
392  targetSurfaces(state, stepper)) {
393  debugLog(state,
394  [&] { return std::string("Target set to next surface."); });
395  } else if (state.navigation.navigationStage <= Stage::layerTarget and
396  targetLayers(state, stepper)) {
397  debugLog(state, [&] { return std::string("Target set to next layer."); });
398  } else if (targetBoundaries(state, stepper)) {
399  debugLog(state,
400  [&] { return std::string("Target set to next boundary."); });
401  } else {
402  debugLog(state, [&] {
403  return std::string("No further navigation action, proceed to target.");
404  });
405  // Set navigation break and release the navigation step size
406  state.navigation.navigationBreak = true;
407  stepper.releaseStepSize(state.stepping);
408  }
409 
410  // Navigator target always resets the current surface
411  state.navigation.currentSurface = nullptr;
412 
413  // Return to the propagator
414  return;
415  }
416 
417  private:
428  template <typename propagator_state_t, typename stepper_t>
429  void initialize(propagator_state_t& state, const stepper_t& stepper) const {
430  // Call the navigation helper prior to actual navigation
431  debugLog(state, [&] { return std::string("Initialization."); });
432 
433  // Set the world volume if it is not set
434  if (not state.navigation.worldVolume) {
435  state.navigation.worldVolume = trackingGeometry->highestTrackingVolume();
436  }
437 
438  // We set the current surface to the start surface
439  // for eventual post-update action, e.g. material integration
440  // or collection when leaving a surface at the start of
441  // an extrapolation process
442  state.navigation.currentSurface = state.navigation.startSurface;
443  if (state.navigation.currentSurface) {
444  debugLog(state, [&] {
445  std::stringstream dstream;
446  dstream << "Current surface set to start surface ";
447  dstream << state.navigation.currentSurface->geoID();
448  return dstream.str();
449  });
450  }
451  // Fast Navigation initialization for start condition:
452  // - short-cut through object association, saves navigation in the
453  // - geometry and volume tree search for the lowest volume
454  if (state.navigation.startSurface &&
455  state.navigation.startSurface->associatedLayer()) {
456  debugLog(state, [&] {
457  return std::string("Fast start initialization through association.");
458  });
459  // assign the current layer and volume by association
460  state.navigation.startLayer =
461  state.navigation.startSurface->associatedLayer();
462  state.navigation.startVolume =
463  state.navigation.startLayer->trackingVolume();
464  // Set the start volume as current volume
465  state.navigation.currentVolume = state.navigation.startVolume;
466  } else {
467  debugLog(state, [&] {
468  return std::string("Slow start initialization through search.");
469  });
470  // current volume and layer search through global search
471  debugLog(state, [&] {
472  std::stringstream dstream;
473  dstream << "Starting from position "
474  << toString(stepper.position(state.stepping));
475  dstream << " and direction "
476  << toString(stepper.direction(state.stepping));
477  return dstream.str();
478  });
479  state.navigation.startVolume = trackingGeometry->lowestTrackingVolume(
480  state.geoContext, stepper.position(state.stepping));
481  state.navigation.startLayer =
482  state.navigation.startVolume
483  ? state.navigation.startVolume->associatedLayer(
484  state.geoContext, stepper.position(state.stepping))
485  : nullptr;
486  // Set the start volume as current volume
487  state.navigation.currentVolume = state.navigation.startVolume;
488  if (state.navigation.startVolume) {
489  debugLog(state, [&] { return std::string("Start volume resolved."); });
490  }
491  }
492  return;
493  }
494 
512  template <typename propagator_state_t, typename stepper_t,
513  typename navigation_surfaces_t, typename navigation_iter_t>
514  bool status(propagator_state_t& state, const stepper_t& stepper,
515  navigation_surfaces_t& navSurfaces,
516  const navigation_iter_t& navIter) const {
517  // No surfaces, status check will be done on layer
518  if (navSurfaces.empty() or navIter == navSurfaces.end()) {
519  return false;
520  }
521  // Take the current surface
522  auto surface = navIter->representation;
523  // Check if we are at a surface
524  // If we are on the surface pointed at by the iterator, we can make
525  // it the current one to pass it to the other actors
526  auto surfaceStatus =
527  stepper.updateSurfaceStatus(state.stepping, *surface, true);
528  if (surfaceStatus == Intersection::Status::onSurface) {
529  debugLog(state, [&] {
530  return std::string("Status Surface successfully hit, storing it.");
531  });
532  // Set in navigation state, so actors and aborters can access it
533  state.navigation.currentSurface = surface;
534  if (state.navigation.currentSurface) {
535  debugLog(state, [&] {
536  std::stringstream dstream;
537  dstream << "Current surface set to surface ";
538  dstream << state.navigation.currentSurface->geoID();
539  return dstream.str();
540  });
541  }
542  }
543  // Return a positive status: either on it, or on the way
544  return true;
545  }
546 
559  template <typename propagator_state_t, typename stepper_t>
560  bool targetSurfaces(propagator_state_t& state,
561  const stepper_t& stepper) const {
562  if (state.navigation.navigationBreak) {
563  return false;
564  }
565 
566  // Make sure resolve Surfaces is called on the start layer
567  if (state.navigation.startLayer and
568  not state.navigation.startLayerResolved) {
569  debugLog(state,
570  [&] { return std::string("Start layer to be resolved."); });
571  // We provide the layer to the resolve surface method in this case
572  state.navigation.startLayerResolved = true;
573  bool startResolved =
574  resolveSurfaces(state, stepper, state.navigation.startLayer);
575  if (not startResolved and
576  state.navigation.startLayer == state.navigation.targetLayer) {
577  debugLog(state, [&] {
578  return std::string("Start is target layer, nothing left to do.");
579  });
580  // set the navigation break
581  state.navigation.navigationBreak = true;
582  stepper.releaseStepSize(state.stepping);
583  }
584  return startResolved;
585  }
586 
587  // The call that we are on a layer and have not yet resolved the surfaces
588  // No surfaces, do not return to stepper
589  if (state.navigation.navSurfaces.empty() or
590  state.navigation.navSurfaceIter == state.navigation.navSurfaces.end()) {
591  debugLog(state, [&] {
592  return std::string("No surfaces present, target at layer first.");
593  });
594  return false;
595  }
596  // Loop over the remaining navigation surfaces
597  while (state.navigation.navSurfaceIter !=
598  state.navigation.navSurfaces.end()) {
599  // Screen output how much is left to try
600  debugLog(state, [&] {
601  std::stringstream dstream;
602  dstream << std::distance(state.navigation.navSurfaceIter,
603  state.navigation.navSurfaces.end());
604  dstream << " out of " << state.navigation.navSurfaces.size();
605  dstream << " surfaces remain to try.";
606  return dstream.str();
607  });
608  // Take the surface
609  auto surface = state.navigation.navSurfaceIter->object;
610  // Screen output which surface you are on
611  debugLog(state, [&] {
612  std::stringstream dstream;
613  dstream << "Next surface candidate will be ";
614  dstream << surface->geoID();
615  return dstream.str();
616  });
617  // Estimate the surface status
618  auto surfaceStatus =
619  stepper.updateSurfaceStatus(state.stepping, *surface, true);
620  if (surfaceStatus == Intersection::Status::reachable) {
621  debugLog(state, [&] {
622  std::stringstream dstream;
623  dstream << "Surface reachable, step size updated to ";
624  dstream << stepper.outputStepSize(state.stepping);
625  return dstream.str();
626  });
627  return true;
628  }
629  ++state.navigation.navSurfaceIter;
630  continue;
631  }
632 
633  // Reached the end of the surface iteration
634  if (state.navigation.navSurfaceIter == state.navigation.navSurfaces.end()) {
635  debugLog(state, [&] {
636  return std::string("Last surface on layer reached, switching layer.");
637  });
638  // first clear the surface cache
639  state.navigation.navSurfaces.clear();
640  state.navigation.navSurfaceIter = state.navigation.navSurfaces.end();
641  // now switch to the next layer
642  ++state.navigation.navLayerIter;
643  }
644  // Do not return to the propagator
645  return false;
646  }
647 
666  template <typename propagator_state_t, typename stepper_t>
667  bool targetLayers(propagator_state_t& state, const stepper_t& stepper) const {
668  if (state.navigation.navigationBreak) {
669  return false;
670  }
671 
672  // if there are no layers, go back to the navigator (not stepper yet)
673  if (state.navigation.navLayers.empty()) {
674  debugLog(state, [&] {
675  return std::string("No layers present, resolve volume first.");
676  });
677 
678  // check if current volume has BVH, or layers
679  if (state.navigation.currentVolume->hasBoundingVolumeHierarchy()) {
680  // has hierarchy, use that, skip layer resolution
682  state.stepping.navDir, true, resolveSensitive, resolveMaterial,
683  resolvePassive, nullptr, state.navigation.targetSurface);
684  navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
685  double opening_angle = 0;
686 
687  // Preliminary version of the frustum opening angle estimation.
688  // Currently not used (only rays), but will be.
689 
690  /*
691  Vector3D pos = stepper.position(state.stepping);
692  double mom
693  = units::Nat2SI<units::MOMENTUM>(stepper.momentum(state.stepping));
694  double q = stepper.charge(state.stepping);
695  Vector3D dir = stepper.direction(state.stepping);
696  Vector3D B = stepper.getField(state.stepping, pos);
697  if (B.squaredNorm() > 1e-9) {
698  // ~ non-zero field
699  double ir = (dir.cross(B).norm()) * q / mom;
700  double s;
701  if (state.stepping.navDir == forward) {
702  s = state.stepping.stepSize.max();
703  } else {
704  s = state.stepping.stepSize.min();
705  }
706  opening_angle = std::atan((1 - std::cos(s * ir)) / std::sin(s * ir));
707  }
708  debugLog(state, [&] {
709  std::stringstream ss;
710  ss << std::fixed << std::setprecision(50);
711  ss << "Estimating opening angle for frustum nav:" << std::endl;
712  ss << "pos: " << pos.transpose() << std::endl;
713  ss << "dir: " << dir.transpose() << std::endl;
714  ss << "B: " << B.transpose() << " |B|: " << B.norm() << std::endl;
715  ss << "step mom: " << stepper.momentum(state.stepping) << std::endl;
716  ss << "=> opening angle: " << opening_angle << std::endl;
717  return ss.str();
718  });
719  */
720 
721  auto protoNavSurfaces =
722  state.navigation.currentVolume->compatibleSurfacesFromHierarchy(
723  state.geoContext, stepper.position(state.stepping),
724  stepper.direction(state.stepping), opening_angle, navOpts);
725  if (!protoNavSurfaces.empty()) {
726  // did we find any surfaces?
727 
728  // Check: are we on the first surface?
729  if (state.navigation.currentSurface == nullptr ||
730  state.navigation.currentSurface !=
731  protoNavSurfaces.front().object) {
732  // we are not, go on
733  state.navigation.navSurfaces = std::move(protoNavSurfaces);
734 
735  state.navigation.navSurfaceIter =
736  state.navigation.navSurfaces.begin();
737  state.navigation.navLayers = {};
738  state.navigation.navLayerIter = state.navigation.navLayers.end();
739  // The stepper updates the step size ( single / multi component)
740  stepper.updateStepSize(state.stepping,
741  *state.navigation.navSurfaceIter, true);
742  debugLog(state, [&] {
743  std::stringstream dstream;
744  dstream << "Navigation stepSize updated to ";
745  dstream << stepper.outputStepSize(state.stepping);
746  return dstream.str();
747  });
748  return true;
749  }
750  }
751  }
752 
753  if (resolveLayers(state, stepper)) {
754  // The layer resolving worked
755  return true;
756  }
757  }
758  // loop over the available navigation layer candiates
759  while (state.navigation.navLayerIter != state.navigation.navLayers.end()) {
760  // The layer surface
761  auto layerSurface = state.navigation.navLayerIter->representation;
762  // We are on the layer
763  if (state.navigation.currentSurface == layerSurface) {
764  debugLog(state, [&] {
765  return std::string("We are on a layer, resolve Surfaces.");
766  });
767  // If you found surfaces return to the propagator
768  if (resolveSurfaces(state, stepper)) {
769  return true;
770  } else {
771  // Try the next one
772  ++state.navigation.navLayerIter;
773  continue;
774  }
775  }
776  // Try to step towards it
777  auto layerStatus =
778  stepper.updateSurfaceStatus(state.stepping, *layerSurface, true);
779  if (layerStatus == Intersection::Status::reachable) {
780  debugLog(state, [&] {
781  std::stringstream dstream;
782  dstream << "Layer reachable, step size updated to ";
783  dstream << stepper.outputStepSize(state.stepping);
784  return dstream.str();
785  });
786  return true;
787  }
788  debugLog(state, [&] {
789  return std::string("Layer intersection not valid, skipping it.");
790  });
791  ++state.navigation.navLayerIter;
792  }
793 
794  // Re-initialize target at last layer, only in case it is the target volume
795  // This avoids a wrong target volume estimation
796  if (state.navigation.currentVolume == state.navigation.targetVolume) {
797  initializeTarget(state, stepper);
798  }
799  // Screen output
800  debugLog(state, [&] {
801  std::stringstream dstream;
802  dstream << "Last layer";
803  if (state.navigation.currentVolume == state.navigation.targetVolume) {
804  dstream << " (final volume) done, proceed to target.";
805  } else {
806  dstream << " done, target volume boundary.";
807  }
808  return dstream.str();
809  });
810  // Set the navigation break if necessary
811  state.navigation.navigationBreak =
812  (state.navigation.currentVolume == state.navigation.targetVolume);
813  return false;
814  }
815 
842  template <typename propagator_state_t, typename stepper_t>
843  bool targetBoundaries(propagator_state_t& state,
844  const stepper_t& stepper) const {
845  if (state.navigation.navigationBreak) {
846  return false;
847  }
848 
849  if (!state.navigation.currentVolume) {
850  debugLog(state, [&] {
851  return std::string(
852  "No sufficient information to resolve boundary, "
853  "stopping navigation.");
854  });
855  stepper.releaseStepSize(state.stepping);
856  return false;
857  } else if (state.navigation.currentVolume ==
858  state.navigation.targetVolume) {
859  debugLog(state, [&] {
860  return std::string(
861  "In target volume: no need to resolve boundary, "
862  "stopping navigation.");
863  state.navigation.navigationBreak = true;
864  stepper.releaseStepSize(state.stepping);
865  });
866  return true;
867  }
868 
869  // Re-initialize target at volume boundary
870  initializeTarget(state, stepper);
871 
872  // Helper function to find boundaries
873  auto findBoundaries = [&]() -> bool {
874  // The navigation options
875  NavigationOptions<Surface> navOpts(state.stepping.navDir, true);
876  navOpts.pathLimit =
877  state.stepping.stepSize.value(ConstrainedStep::aborter);
878  navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
879 
880  // Exclude the current surface in case it's a boundary
881  navOpts.startObject = state.navigation.currentSurface;
882  debugLog(state, [&] {
883  std::stringstream ss;
884  ss << "Try to find boundaries, we are at: "
885  << stepper.position(state.stepping).transpose()
886  << ", dir: " << stepper.direction(state.stepping).transpose();
887  return ss.str();
888  });
889  // Evaluate the boundary surfaces
890  state.navigation.navBoundaries =
891  state.navigation.currentVolume->compatibleBoundaries(
892  state.geoContext, stepper.position(state.stepping),
893  stepper.direction(state.stepping), navOpts);
894  // The number of boundary candidates
895  debugLog(state, [&] {
896  std::stringstream dstream;
897  dstream << state.navigation.navBoundaries.size();
898  dstream << " boundary candidates found at path(s): ";
899  for (auto& bc : state.navigation.navBoundaries) {
900  dstream << bc.intersection.pathLength << " ";
901  }
902  return dstream.str();
903  });
904  // Set the begin iterator
905  state.navigation.navBoundaryIter = state.navigation.navBoundaries.begin();
906  if (not state.navigation.navBoundaries.empty()) {
907  // Set to the first and return to the stepper
908  stepper.updateStepSize(state.stepping,
909  *state.navigation.navBoundaryIter, true);
910  debugLog(state, [&] {
911  std::stringstream dstream;
912  dstream << "Navigation stepSize updated to ";
913  dstream << stepper.outputStepSize(state.stepping);
914  return dstream.str();
915  });
916  return true;
917  }
918  return false;
919  };
920 
921  // No boundaries are assigned yet, find them
922  if (state.navigation.navBoundaries.empty() and findBoundaries()) {
923  return true;
924  }
925 
926  // Loop over the boundary surface
927  while (state.navigation.navBoundaryIter !=
928  state.navigation.navBoundaries.end()) {
929  // That is the current boundary surface
930  auto boundarySurface = state.navigation.navBoundaryIter->representation;
931  // Step towards the boundary surfrace
932  auto boundaryStatus =
933  stepper.updateSurfaceStatus(state.stepping, *boundarySurface, true);
934  if (boundaryStatus == Intersection::Status::reachable) {
935  debugLog(state, [&] {
936  std::stringstream dstream;
937  dstream << "Boundary reachable, step size updated to ";
938  dstream << stepper.outputStepSize(state.stepping);
939  return dstream.str();
940  });
941  return true;
942  } else {
943  debugLog(state, [&] {
944  std::stringstream dstream;
945  dstream << "Boundary ";
946  dstream << std::distance(state.navigation.navBoundaryIter,
947  state.navigation.navBoundaries.end());
948  dstream << " out of " << state.navigation.navBoundaries.size();
949  dstream << " not reachable anymore, switching to next.";
950  return dstream.str();
951  });
952  }
953  // Increase the iterator to the next one
954  ++state.navigation.navBoundaryIter;
955  }
956  // We have to leave the volume somehow, so try again
957  state.navigation.navBoundaries.clear();
958  debugLog(state, [&] {
959  return std::string("Boundary navigation lost, re-targetting.");
960  });
961  if (findBoundaries()) {
962  return true;
963  }
964 
965  // Tried our best, but couldn't do anything
966  return false;
967  }
968 
989  template <typename propagator_state_t, typename stepper_t>
990  void initializeTarget(propagator_state_t& state,
991  const stepper_t& stepper) const {
992  if (state.navigation.targetVolume and
993  state.stepping.pathAccumulated == 0.) {
994  debugLog(state, [&] {
995  return std::string("Re-initialzing cancelled as it is the first step.");
996  });
997  return;
998  }
999  // Fast Navigation initialization for target:
1000  if (state.navigation.targetSurface &&
1001  state.navigation.targetSurface->associatedLayer() &&
1002  !state.navigation.targetVolume) {
1003  debugLog(state, [&] {
1004  return std::string("Fast target initialization through association.");
1005  });
1006  debugLog(state, [&] {
1007  std::stringstream dstream;
1008  dstream << "Target surface set to ";
1009  dstream << state.navigation.targetSurface->geoID();
1010  return dstream.str();
1011  });
1012  // assign the target volume and the target surface
1013  state.navigation.targetLayer =
1014  state.navigation.targetSurface->associatedLayer();
1015  state.navigation.targetVolume =
1016  state.navigation.targetLayer->trackingVolume();
1017  } else if (state.navigation.targetSurface) {
1018  // screen output that you do a re-initialization
1019  if (state.navigation.targetVolume) {
1020  debugLog(state, [&] {
1021  return std::string("Re-initialization of target volume triggered.");
1022  });
1023  }
1024  // Slow navigation initialization for target:
1025  // target volume and layer search through global search
1026  auto targetIntersection = state.navigation.targetSurface->intersect(
1027  state.geoContext, stepper.position(state.stepping),
1028  state.stepping.navDir * stepper.direction(state.stepping), false);
1029  if (targetIntersection) {
1030  debugLog(state, [&] {
1031  std::stringstream dstream;
1032  dstream << "Target estimate position (";
1033  dstream << targetIntersection.intersection.position.x() << ", ";
1034  dstream << targetIntersection.intersection.position.y() << ", ";
1035  dstream << targetIntersection.intersection.position.z() << ")";
1036  return dstream.str();
1037  });
1039  auto tPosition = targetIntersection.intersection.position;
1040  state.navigation.targetVolume =
1041  trackingGeometry->lowestTrackingVolume(state.geoContext, tPosition);
1042  state.navigation.targetLayer =
1043  state.navigation.targetVolume
1044  ? state.navigation.targetVolume->associatedLayer(
1045  state.geoContext, tPosition)
1046  : nullptr;
1047  if (state.navigation.targetVolume) {
1048  debugLog(state, [&] {
1049  std::stringstream dstream;
1050  dstream << "Target volume estimated : ";
1051  dstream << state.navigation.targetVolume->volumeName();
1052  return dstream.str();
1053  });
1054  }
1055  }
1056  }
1057  }
1058 
1069  template <typename propagator_state_t, typename stepper_t>
1070  bool resolveSurfaces(propagator_state_t& state, const stepper_t& stepper,
1071  const Layer* cLayer = nullptr) const {
1072  // get the layer and layer surface
1073  auto layerSurface = cLayer ? state.navigation.startSurface
1074  : state.navigation.navLayerIter->representation;
1075  auto navLayer = cLayer ? cLayer : state.navigation.navLayerIter->object;
1076  // are we on the start layer
1077  bool onStart = (navLayer == state.navigation.startLayer);
1078  auto startSurface = onStart ? state.navigation.startSurface : layerSurface;
1079  // Use navigation parameters and NavigationOptions
1081  state.stepping.navDir, true, resolveSensitive, resolveMaterial,
1082  resolvePassive, startSurface, state.navigation.targetSurface);
1083  // Check the limit
1084  navOpts.pathLimit = state.stepping.stepSize.value(ConstrainedStep::aborter);
1085  // No overstepping on start layer, otherwise ask the stepper
1086  navOpts.overstepLimit = (cLayer != nullptr)
1088  : stepper.overstepLimit(state.stepping);
1089 
1090  // get the surfaces
1091  state.navigation.navSurfaces = navLayer->compatibleSurfaces(
1092  state.geoContext, stepper.position(state.stepping),
1093  stepper.direction(state.stepping), navOpts);
1094  // the number of layer candidates
1095  if (!state.navigation.navSurfaces.empty()) {
1096  debugLog(state, [&] {
1097  std::stringstream dstream;
1098  dstream << state.navigation.navSurfaces.size();
1099  dstream << " surface candidates found at path(s): ";
1100  for (auto& sfc : state.navigation.navSurfaces) {
1101  dstream << sfc.intersection.pathLength << " ";
1102  }
1103  return dstream.str();
1104  });
1105  // set the iterator
1106  state.navigation.navSurfaceIter = state.navigation.navSurfaces.begin();
1107  // The stepper updates the step size ( single / multi component)
1108  stepper.updateStepSize(state.stepping, *state.navigation.navSurfaceIter,
1109  true);
1110  debugLog(state, [&] {
1111  std::stringstream dstream;
1112  dstream << "Navigation stepSize updated to ";
1113  dstream << stepper.outputStepSize(state.stepping);
1114  return dstream.str();
1115  });
1116  return true;
1117  }
1118  state.navigation.navSurfaceIter = state.navigation.navSurfaces.end();
1119  debugLog(state,
1120  [&] { return std::string("No surface candidates found."); });
1121  return false;
1122  }
1123 
1139  template <typename propagator_state_t, typename stepper_t>
1140  bool resolveLayers(propagator_state_t& state,
1141  const stepper_t& stepper) const {
1142  debugLog(state,
1143  [&] { return std::string("Searching for compatible layers."); });
1144 
1145  // Check if we are in the start volume
1146  auto startLayer =
1147  (state.navigation.currentVolume == state.navigation.startVolume)
1148  ? state.navigation.startLayer
1149  : nullptr;
1150  // Create the navigation options
1151  // - and get the compatible layers, start layer will be excluded
1152  NavigationOptions<Layer> navOpts(state.stepping.navDir, true,
1153  resolveSensitive, resolveMaterial,
1154  resolvePassive, startLayer, nullptr);
1155  // Set also the target surface
1156  navOpts.targetSurface = state.navigation.targetSurface;
1157  navOpts.pathLimit = state.stepping.stepSize.value(ConstrainedStep::aborter);
1158  navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
1159  // Request the compatible layers
1160  state.navigation.navLayers =
1161  state.navigation.currentVolume->compatibleLayers(
1162  state.geoContext, stepper.position(state.stepping),
1163  stepper.direction(state.stepping), navOpts);
1164 
1165  // Layer candidates have been found
1166  if (!state.navigation.navLayers.empty()) {
1167  // Screen output where they are
1168  debugLog(state, [&] {
1169  std::stringstream dstream;
1170  dstream << state.navigation.navLayers.size();
1171  dstream << " layer candidates found at path(s): ";
1172  for (auto& lc : state.navigation.navLayers) {
1173  dstream << lc.intersection.pathLength << " ";
1174  }
1175  return dstream.str();
1176  });
1177  // Set the iterator to the first
1178  state.navigation.navLayerIter = state.navigation.navLayers.begin();
1179  // Setting the step size towards first
1180  if (state.navigation.startLayer &&
1181  state.navigation.navLayerIter->object !=
1182  state.navigation.startLayer) {
1183  debugLog(state, [&] { return std::string("Target at layer."); });
1184  // The stepper updates the step size ( single / multi component)
1185  stepper.updateStepSize(state.stepping, *state.navigation.navLayerIter,
1186  true);
1187  debugLog(state, [&] {
1188  std::stringstream dstream;
1189  dstream << "Navigation stepSize updated to ";
1190  dstream << stepper.outputStepSize(state.stepping);
1191  return dstream.str();
1192  });
1193  return true;
1194  }
1195  }
1196 
1197  // Set the iterator to the end of the list
1198  state.navigation.navLayerIter = state.navigation.navLayers.end();
1199 
1200  // Screen output - no layer candidates found
1201  debugLog(state, [&] {
1202  return std::string("No compatible layer candidates found.");
1203  });
1204  // Release the step size
1205  stepper.releaseStepSize(state.stepping);
1206  return false;
1207  }
1208 
1222  template <typename propagator_state_t, typename stepper_t>
1223  bool inactive(propagator_state_t& state, const stepper_t& stepper) const {
1224  // Void behavior in case no tracking geometry is present
1225  if (!trackingGeometry) {
1226  return true;
1227  }
1228  // turn the navigator into void when you are intructed to do nothing
1229  if (!resolveSensitive && !resolveMaterial && !resolvePassive) {
1230  return true;
1231  }
1232 
1233  // --------------------------------------------------------------------
1234  // Navigation break handling
1235  // This checks if a navigation break had been triggered:
1236  // - If so & the target exists or was hit - it simply returns
1237  // - If a target exists and was not yet hit, it checks for it
1238  // -> return is always to the stepper
1239  if (state.navigation.navigationBreak) {
1240  // target exists and reached, or no target exists
1241  if (state.navigation.targetReached || !state.navigation.targetSurface) {
1242  return true;
1243  }
1244  auto targetStatus = stepper.updateSurfaceStatus(
1245  state.stepping, *state.navigation.targetSurface, true);
1246  // the only advance could have been to the target
1247  if (targetStatus == Intersection::Status::onSurface) {
1248  // set the target surface
1249  state.navigation.currentSurface = state.navigation.targetSurface;
1250  debugLog(state, [&] {
1251  std::stringstream dstream;
1252  dstream << "Current surface set to target surface ";
1253  dstream << state.navigation.currentSurface->geoID();
1254  return dstream.str();
1255  });
1256  return true;
1257  }
1258  }
1259  return false;
1260  }
1261 
1274  template <typename propagator_state_t>
1275  void debugLog(propagator_state_t& state,
1276  const std::function<std::string()>& logAction) const {
1277  if (state.options.debug) {
1278  std::string vName = "No Volume";
1279  if (state.navigation.currentVolume) {
1280  vName = state.navigation.currentVolume->volumeName();
1281  }
1282  std::vector<std::string> lines;
1283  std::string input = logAction();
1284  boost::split(lines, input, boost::is_any_of("\n"));
1285  for (const auto& line : lines) {
1286  std::stringstream dstream;
1287  dstream << ">>>" << std::setw(state.options.debugPfxWidth) << vName
1288  << " | ";
1289  dstream << std::setw(state.options.debugMsgWidth) << line << '\n';
1290  state.options.debugString += dstream.str();
1291  }
1292  }
1293  }
1294 };
1295 
1296 } // namespace Acts