File indexing completed on 2025-01-18 09:10:58
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Geometry/GeometryIdentifier.hpp"
0012 #include "Acts/Geometry/Layer.hpp"
0013 #include "Acts/Geometry/TrackingGeometry.hpp"
0014 #include "Acts/Geometry/TrackingVolume.hpp"
0015 #include "Acts/Propagator/NavigationTarget.hpp"
0016 #include "Acts/Propagator/NavigatorOptions.hpp"
0017 #include "Acts/Propagator/NavigatorStatistics.hpp"
0018 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0019 #include "Acts/Surfaces/Surface.hpp"
0020 #include "Acts/Utilities/Intersection.hpp"
0021 #include "Acts/Utilities/Logger.hpp"
0022 #include "Acts/Utilities/StringHelpers.hpp"
0023
0024 #include <algorithm>
0025 #include <optional>
0026 #include <sstream>
0027 #include <string>
0028
0029 #include <boost/container/small_vector.hpp>
0030
0031 namespace Acts {
0032
0033
0034
0035
0036 template <typename object_t>
0037 struct NavigationOptions {
0038
0039 BoundaryTolerance boundaryTolerance = BoundaryTolerance::None();
0040
0041
0042
0043 bool resolveSensitive = true;
0044
0045 bool resolveMaterial = true;
0046
0047 bool resolvePassive = false;
0048
0049
0050 const object_t* startObject = nullptr;
0051
0052 const object_t* endObject = nullptr;
0053
0054
0055 std::vector<GeometryIdentifier> externalSurfaces = {};
0056
0057
0058 double nearLimit = 0;
0059
0060 double farLimit = std::numeric_limits<double>::max();
0061 };
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080 class Navigator {
0081 public:
0082 using NavigationSurfaces =
0083 boost::container::small_vector<SurfaceIntersection, 10>;
0084
0085 using NavigationLayers =
0086 boost::container::small_vector<LayerIntersection, 10>;
0087
0088 using NavigationBoundaries =
0089 boost::container::small_vector<BoundaryIntersection, 4>;
0090
0091 using ExternalSurfaces = std::multimap<std::uint64_t, GeometryIdentifier>;
0092
0093
0094 enum struct Stage : int {
0095 initial = 0,
0096 surfaceTarget = 1,
0097 layerTarget = 2,
0098 boundaryTarget = 3,
0099 };
0100
0101
0102 struct Config {
0103
0104 std::shared_ptr<const TrackingGeometry> trackingGeometry{nullptr};
0105
0106
0107 bool resolveSensitive = true;
0108
0109 bool resolveMaterial = true;
0110
0111 bool resolvePassive = false;
0112 };
0113
0114
0115 struct Options : public NavigatorPlainOptions {
0116 explicit Options(const GeometryContext& gctx)
0117 : NavigatorPlainOptions(gctx) {}
0118
0119
0120 double surfaceTolerance = s_onSurfaceTolerance;
0121
0122
0123 double nearLimit = s_onSurfaceTolerance;
0124
0125
0126 double farLimit = std::numeric_limits<double>::max();
0127
0128
0129 ExternalSurfaces externalSurfaces = {};
0130
0131 void insertExternalSurface(GeometryIdentifier geoid) {
0132 externalSurfaces.insert(
0133 std::pair<std::uint64_t, GeometryIdentifier>(geoid.layer(), geoid));
0134 }
0135
0136 void setPlainOptions(const NavigatorPlainOptions& options) {
0137 static_cast<NavigatorPlainOptions&>(*this) = options;
0138 }
0139 };
0140
0141
0142
0143
0144
0145 struct State {
0146 explicit State(const Options& options_) : options(options_) {}
0147
0148 Options options;
0149
0150
0151
0152 NavigationSurfaces navSurfaces = {};
0153
0154 std::optional<std::size_t> navSurfaceIndex;
0155
0156
0157
0158 NavigationLayers navLayers = {};
0159
0160 std::optional<std::size_t> navLayerIndex;
0161
0162
0163
0164 NavigationBoundaries navBoundaries = {};
0165
0166 std::optional<std::size_t> navBoundaryIndex;
0167
0168 SurfaceIntersection& navSurface() {
0169 return navSurfaces.at(navSurfaceIndex.value());
0170 }
0171 LayerIntersection& navLayer() {
0172 return navLayers.at(navLayerIndex.value());
0173 }
0174 BoundaryIntersection& navBoundary() {
0175 return navBoundaries.at(navBoundaryIndex.value());
0176 }
0177
0178 const TrackingVolume* startVolume = nullptr;
0179 const Layer* startLayer = nullptr;
0180 const Surface* startSurface = nullptr;
0181 const TrackingVolume* currentVolume = nullptr;
0182 const Layer* currentLayer = nullptr;
0183 const Surface* currentSurface = nullptr;
0184 const Surface* targetSurface = nullptr;
0185
0186 bool navigationBreak = false;
0187 Stage navigationStage = Stage::initial;
0188
0189 NavigatorStatistics statistics;
0190
0191 void resetAfterLayerSwitch() {
0192 navSurfaces.clear();
0193 navSurfaceIndex.reset();
0194 }
0195
0196 void resetAfterVolumeSwitch() {
0197 resetAfterLayerSwitch();
0198
0199 navLayers.clear();
0200 navLayerIndex.reset();
0201 navBoundaries.clear();
0202 navBoundaryIndex.reset();
0203
0204 currentLayer = nullptr;
0205 }
0206
0207 void reset() {
0208 resetAfterVolumeSwitch();
0209
0210 currentVolume = nullptr;
0211 currentSurface = nullptr;
0212
0213 navigationBreak = false;
0214 navigationStage = Stage::initial;
0215 }
0216 };
0217
0218
0219
0220
0221
0222 explicit Navigator(Config cfg,
0223 std::shared_ptr<const Logger> _logger =
0224 getDefaultLogger("Navigator", Logging::Level::INFO))
0225 : m_cfg{std::move(cfg)}, m_logger{std::move(_logger)} {}
0226
0227 State makeState(const Options& options) const {
0228 State state(options);
0229 state.startSurface = options.startSurface;
0230 state.targetSurface = options.targetSurface;
0231 return state;
0232 }
0233
0234 const Surface* currentSurface(const State& state) const {
0235 return state.currentSurface;
0236 }
0237
0238 const TrackingVolume* currentVolume(const State& state) const {
0239 return state.currentVolume;
0240 }
0241
0242 const IVolumeMaterial* currentVolumeMaterial(const State& state) const {
0243 if (state.currentVolume == nullptr) {
0244 return nullptr;
0245 }
0246 return state.currentVolume->volumeMaterial();
0247 }
0248
0249 const Surface* startSurface(const State& state) const {
0250 return state.startSurface;
0251 }
0252
0253 const Surface* targetSurface(const State& state) const {
0254 return state.targetSurface;
0255 }
0256
0257 bool endOfWorldReached(const State& state) const {
0258 return state.currentVolume == nullptr;
0259 }
0260
0261 bool navigationBreak(const State& state) const {
0262 return state.navigationBreak;
0263 }
0264
0265
0266
0267
0268
0269
0270
0271
0272
0273 void initialize(State& state, const Vector3& position,
0274 const Vector3& direction,
0275 Direction propagationDirection) const {
0276 (void)propagationDirection;
0277
0278 ACTS_VERBOSE(volInfo(state) << "Initialization.");
0279
0280 state.reset();
0281
0282
0283
0284
0285 if (state.startSurface != nullptr &&
0286 state.startSurface->associatedLayer() != nullptr) {
0287 ACTS_VERBOSE(
0288 volInfo(state)
0289 << "Fast start initialization through association from Surface.");
0290
0291 state.startLayer = state.startSurface->associatedLayer();
0292 state.startVolume = state.startLayer->trackingVolume();
0293 } else if (state.startVolume != nullptr) {
0294 ACTS_VERBOSE(
0295 volInfo(state)
0296 << "Fast start initialization through association from Volume.");
0297
0298 state.startLayer = state.startVolume->associatedLayer(
0299 state.options.geoContext, position);
0300 } else {
0301 ACTS_VERBOSE(volInfo(state)
0302 << "Slow start initialization through search.");
0303 ACTS_VERBOSE(volInfo(state)
0304 << "Starting from position " << toString(position)
0305 << " and direction " << toString(direction));
0306
0307
0308 state.startVolume = m_cfg.trackingGeometry->lowestTrackingVolume(
0309 state.options.geoContext, position);
0310
0311 if (state.startVolume != nullptr) {
0312 state.startLayer = state.startVolume->associatedLayer(
0313 state.options.geoContext, position);
0314 } else {
0315 ACTS_ERROR(volInfo(state)
0316 << "No start volume resolved. Nothing left to do.");
0317 state.navigationBreak = true;
0318 }
0319 }
0320
0321 state.currentVolume = state.startVolume;
0322 state.currentLayer = state.startLayer;
0323 state.currentSurface = state.startSurface;
0324
0325 if (state.currentVolume != nullptr) {
0326 ACTS_VERBOSE(volInfo(state) << "Start volume resolved "
0327 << state.currentVolume->geometryId());
0328 assert(state.currentVolume->inside(position,
0329 state.options.surfaceTolerance) &&
0330 "We did not end up inside the volume.");
0331 }
0332 if (state.currentLayer != nullptr) {
0333 ACTS_VERBOSE(volInfo(state) << "Start layer resolved "
0334 << state.currentLayer->geometryId());
0335 }
0336 if (state.currentSurface != nullptr) {
0337 ACTS_VERBOSE(volInfo(state) << "Start surface resolved "
0338 << state.currentSurface->geometryId());
0339 assert(state.currentSurface->isOnSurface(
0340 state.options.geoContext, position, direction,
0341 BoundaryTolerance::Infinite(),
0342 state.options.surfaceTolerance) &&
0343 "Stepper not on surface");
0344 }
0345 }
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355
0356 NavigationTarget nextTarget(State& state, const Vector3& position,
0357 const Vector3& direction) const {
0358 if (inactive(state)) {
0359 return NavigationTarget::None();
0360 }
0361
0362 ACTS_VERBOSE(volInfo(state) << "Entering Navigator::nextTarget.");
0363
0364
0365 state.currentSurface = nullptr;
0366
0367 auto tryGetNextTarget = [&]() -> NavigationTarget {
0368
0369
0370 if (state.navigationStage == Stage::initial) {
0371 ACTS_VERBOSE(volInfo(state) << "Target surfaces.");
0372 state.navigationStage = Stage::surfaceTarget;
0373 }
0374
0375 if (state.navigationStage == Stage::surfaceTarget) {
0376 if (!state.navSurfaceIndex.has_value()) {
0377
0378 resolveSurfaces(state, position, direction);
0379 state.navSurfaceIndex = 0;
0380 } else {
0381 ++state.navSurfaceIndex.value();
0382 }
0383 if (state.navSurfaceIndex.value() < state.navSurfaces.size()) {
0384 ACTS_VERBOSE(volInfo(state) << "Target set to next surface.");
0385 return NavigationTarget(*state.navSurface().object(),
0386 state.navSurface().index(),
0387 BoundaryTolerance::None());
0388 } else {
0389
0390 ACTS_VERBOSE(volInfo(state) << "Target layers.");
0391 state.navigationStage = Stage::layerTarget;
0392 }
0393 }
0394
0395 if (state.navigationStage == Stage::layerTarget) {
0396 if (!state.navLayerIndex.has_value()) {
0397
0398 resolveLayers(state, position, direction);
0399 state.navLayerIndex = 0;
0400 } else {
0401 ++state.navLayerIndex.value();
0402 }
0403 if (state.navLayerIndex.value() < state.navLayers.size()) {
0404 ACTS_VERBOSE(volInfo(state) << "Target set to next layer.");
0405 return NavigationTarget(*state.navLayer().first.object(),
0406 state.navLayer().first.index(),
0407 BoundaryTolerance::None());
0408 } else {
0409
0410 ACTS_VERBOSE(volInfo(state) << "Target boundaries.");
0411 state.navigationStage = Stage::boundaryTarget;
0412 }
0413 }
0414
0415 if (state.navigationStage == Stage::boundaryTarget) {
0416 if (!state.navBoundaryIndex.has_value()) {
0417
0418 resolveBoundaries(state, position, direction);
0419 state.navBoundaryIndex = 0;
0420 } else {
0421 ++state.navBoundaryIndex.value();
0422 }
0423 if (state.navBoundaryIndex.value() < state.navBoundaries.size()) {
0424 ACTS_VERBOSE(volInfo(state) << "Target set to next boundary.");
0425 return NavigationTarget(*state.navBoundary().first.object(),
0426 state.navBoundary().first.index(),
0427 BoundaryTolerance::None());
0428 } else {
0429
0430
0431 ACTS_VERBOSE(volInfo(state)
0432 << "Boundary targets exhausted. Renavigate.");
0433 }
0434 }
0435
0436 ACTS_VERBOSE(volInfo(state)
0437 << "Unknown state. No target found. Renavigate.");
0438 return NavigationTarget::None();
0439 };
0440
0441 NavigationTarget nextTarget = tryGetNextTarget();
0442 if (!nextTarget.isNone()) {
0443 return nextTarget;
0444 }
0445
0446 state.reset();
0447 ++state.statistics.nRenavigations;
0448
0449
0450
0451 state.currentVolume = m_cfg.trackingGeometry->lowestTrackingVolume(
0452 state.options.geoContext, position);
0453
0454 if (state.currentVolume == nullptr) {
0455 ACTS_VERBOSE(volInfo(state) << "No volume found, stop navigation.");
0456 state.navigationBreak = true;
0457 return NavigationTarget::None();
0458 }
0459
0460 state.currentLayer = state.currentVolume->associatedLayer(
0461 state.options.geoContext, position);
0462
0463 ACTS_VERBOSE(volInfo(state) << "Resolved volume and layer.");
0464
0465
0466 nextTarget = tryGetNextTarget();
0467 if (!nextTarget.isNone()) {
0468 return nextTarget;
0469 }
0470
0471 ACTS_VERBOSE(
0472 volInfo(state)
0473 << "No targets found again, we got really lost! Stop navigation.");
0474 state.navigationBreak = true;
0475 return NavigationTarget::None();
0476 }
0477
0478
0479
0480
0481
0482
0483
0484
0485
0486
0487 bool checkTargetValid(const State& state, const Vector3& position,
0488 const Vector3& direction) const {
0489 (void)position;
0490 (void)direction;
0491
0492 return state.navigationStage != Stage::initial;
0493 }
0494
0495
0496
0497
0498
0499
0500
0501
0502
0503 void handleSurfaceReached(State& state, const Vector3& position,
0504 const Vector3& direction,
0505 const Surface& surface) const {
0506 if (inactive(state)) {
0507 return;
0508 }
0509
0510 ACTS_VERBOSE(volInfo(state) << "Entering Navigator::handleSurfaceReached.");
0511
0512 state.currentSurface = &surface;
0513
0514 if (state.navigationStage == Stage::surfaceTarget &&
0515 state.navSurface().object() == &surface) {
0516 ACTS_VERBOSE(volInfo(state) << "Handling surface status.");
0517
0518 return;
0519 }
0520
0521 if (state.navigationStage == Stage::layerTarget &&
0522 state.navLayer().first.object() == &surface) {
0523 ACTS_VERBOSE(volInfo(state) << "Handling layer status.");
0524
0525
0526 state.currentLayer = state.navLayer().second;
0527 state.navigationStage = Stage::surfaceTarget;
0528
0529
0530 state.resetAfterLayerSwitch();
0531
0532 return;
0533 }
0534
0535 if (state.navigationStage == Stage::boundaryTarget &&
0536 state.navBoundary().first.object() == &surface) {
0537 ACTS_VERBOSE(volInfo(state) << "Handling boundary status.");
0538
0539
0540 const BoundarySurface* boundary = state.navBoundary().second;
0541 state.currentVolume = boundary->attachedVolume(state.options.geoContext,
0542 position, direction);
0543
0544
0545 state.resetAfterVolumeSwitch();
0546
0547 if (state.currentVolume != nullptr) {
0548 ACTS_VERBOSE(volInfo(state) << "Volume updated.");
0549 state.navigationStage = Stage::layerTarget;
0550 } else {
0551 ACTS_VERBOSE(volInfo(state)
0552 << "No more volume to progress to, stopping navigation.");
0553 state.navigationBreak = true;
0554 }
0555
0556 return;
0557 }
0558
0559 ACTS_ERROR(volInfo(state) << "Surface reached but unknown state.");
0560 }
0561
0562 private:
0563
0564
0565
0566
0567
0568
0569
0570 void resolveSurfaces(State& state, const Vector3& position,
0571 const Vector3& direction) const {
0572 ACTS_VERBOSE(volInfo(state) << "Searching for compatible surfaces.");
0573
0574 const Layer* currentLayer = state.currentLayer;
0575
0576 if (currentLayer == nullptr) {
0577 ACTS_VERBOSE(volInfo(state) << "No layer to resolve surfaces.");
0578 return;
0579 }
0580
0581 const Surface* layerSurface = ¤tLayer->surfaceRepresentation();
0582
0583 NavigationOptions<Surface> navOpts;
0584 navOpts.resolveSensitive = m_cfg.resolveSensitive;
0585 navOpts.resolveMaterial = m_cfg.resolveMaterial;
0586 navOpts.resolvePassive = m_cfg.resolvePassive;
0587 navOpts.startObject = state.currentSurface;
0588 navOpts.endObject = state.targetSurface;
0589 navOpts.nearLimit = state.options.nearLimit;
0590 navOpts.farLimit = state.options.farLimit;
0591
0592 if (!state.options.externalSurfaces.empty()) {
0593 auto layerId = layerSurface->geometryId().layer();
0594 auto externalSurfaceRange =
0595 state.options.externalSurfaces.equal_range(layerId);
0596 navOpts.externalSurfaces.reserve(
0597 state.options.externalSurfaces.count(layerId));
0598 for (auto itSurface = externalSurfaceRange.first;
0599 itSurface != externalSurfaceRange.second; itSurface++) {
0600 navOpts.externalSurfaces.push_back(itSurface->second);
0601 }
0602 }
0603
0604
0605 state.navSurfaces = currentLayer->compatibleSurfaces(
0606 state.options.geoContext, position, direction, navOpts);
0607 std::ranges::sort(state.navSurfaces, SurfaceIntersection::pathLengthOrder);
0608
0609
0610 if (logger().doPrint(Logging::VERBOSE)) {
0611 std::ostringstream os;
0612 os << state.navSurfaces.size();
0613 os << " surface candidates found at path(s): ";
0614 for (auto& sfc : state.navSurfaces) {
0615 os << sfc.pathLength() << " ";
0616 }
0617 logger().log(Logging::VERBOSE, os.str());
0618 }
0619
0620 if (state.navSurfaces.empty()) {
0621 ACTS_VERBOSE(volInfo(state) << "No surface candidates found.");
0622 }
0623 }
0624
0625
0626
0627
0628
0629
0630
0631
0632 void resolveLayers(State& state, const Vector3& position,
0633 const Vector3& direction) const {
0634 ACTS_VERBOSE(volInfo(state) << "Searching for compatible layers.");
0635
0636 NavigationOptions<Layer> navOpts;
0637 navOpts.resolveSensitive = m_cfg.resolveSensitive;
0638 navOpts.resolveMaterial = m_cfg.resolveMaterial;
0639 navOpts.resolvePassive = m_cfg.resolvePassive;
0640 navOpts.startObject = state.currentLayer;
0641 navOpts.nearLimit = state.options.nearLimit;
0642 navOpts.farLimit = state.options.farLimit;
0643
0644
0645 state.navLayers = state.currentVolume->compatibleLayers(
0646 state.options.geoContext, position, direction, navOpts);
0647 std::ranges::sort(state.navLayers, [](const auto& a, const auto& b) {
0648 return SurfaceIntersection::pathLengthOrder(a.first, b.first);
0649 });
0650
0651
0652 if (logger().doPrint(Logging::VERBOSE)) {
0653 std::ostringstream os;
0654 os << state.navLayers.size();
0655 os << " layer candidates found at path(s): ";
0656 for (auto& lc : state.navLayers) {
0657 os << lc.first.pathLength() << " ";
0658 }
0659 logger().log(Logging::VERBOSE, os.str());
0660 }
0661
0662 if (state.navLayers.empty()) {
0663 ACTS_VERBOSE(volInfo(state) << "No layer candidates found.");
0664 }
0665 }
0666
0667
0668
0669
0670
0671
0672
0673
0674 void resolveBoundaries(State& state, const Vector3& position,
0675 const Vector3& direction) const {
0676 ACTS_VERBOSE(volInfo(state) << "Searching for compatible boundaries.");
0677
0678 NavigationOptions<Surface> navOpts;
0679 navOpts.startObject = state.currentSurface;
0680 navOpts.nearLimit = state.options.nearLimit;
0681 navOpts.farLimit = state.options.farLimit;
0682
0683 ACTS_VERBOSE(volInfo(state)
0684 << "Try to find boundaries, we are at: " << toString(position)
0685 << ", dir: " << toString(direction));
0686
0687
0688 state.navBoundaries = state.currentVolume->compatibleBoundaries(
0689 state.options.geoContext, position, direction, navOpts, logger());
0690 std::ranges::sort(state.navBoundaries, [](const auto& a, const auto& b) {
0691 return SurfaceIntersection::pathLengthOrder(a.first, b.first);
0692 });
0693
0694
0695 if (logger().doPrint(Logging::VERBOSE)) {
0696 std::ostringstream os;
0697 os << state.navBoundaries.size();
0698 os << " boundary candidates found at path(s): ";
0699 for (auto& bc : state.navBoundaries) {
0700 os << bc.first.pathLength() << " ";
0701 }
0702 logger().log(Logging::VERBOSE, os.str());
0703 }
0704
0705 if (state.navBoundaries.empty()) {
0706 ACTS_VERBOSE(volInfo(state) << "No boundary candidates found.");
0707 }
0708 }
0709
0710
0711
0712
0713
0714
0715
0716
0717 bool inactive(const State& state) const {
0718
0719 if (m_cfg.trackingGeometry == nullptr) {
0720 return true;
0721 }
0722
0723
0724 if (!m_cfg.resolveSensitive && !m_cfg.resolveMaterial &&
0725 !m_cfg.resolvePassive) {
0726 return true;
0727 }
0728
0729 if (state.navigationBreak) {
0730 return true;
0731 }
0732
0733 return false;
0734 }
0735
0736 private:
0737 template <typename propagator_state_t>
0738 std::string volInfo(const propagator_state_t& state) const {
0739 return (state.currentVolume != nullptr ? state.currentVolume->volumeName()
0740 : "No Volume") +
0741 " | ";
0742 }
0743
0744 const Logger& logger() const { return *m_logger; }
0745
0746 Config m_cfg;
0747
0748 std::shared_ptr<const Logger> m_logger;
0749 };
0750
0751 }