File indexing completed on 2025-10-16 08:02:16
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Definitions/Tolerance.hpp"
0012 #include "Acts/Geometry/GeometryIdentifier.hpp"
0013 #include "Acts/Geometry/Layer.hpp"
0014 #include "Acts/Geometry/TrackingGeometry.hpp"
0015 #include "Acts/Geometry/TrackingVolume.hpp"
0016 #include "Acts/Navigation/NavigationStream.hpp"
0017 #include "Acts/Propagator/NavigationTarget.hpp"
0018 #include "Acts/Propagator/NavigatorError.hpp"
0019 #include "Acts/Propagator/NavigatorOptions.hpp"
0020 #include "Acts/Propagator/NavigatorStatistics.hpp"
0021 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0022 #include "Acts/Surfaces/Surface.hpp"
0023 #include "Acts/Utilities/Intersection.hpp"
0024 #include "Acts/Utilities/Logger.hpp"
0025 #include "Acts/Utilities/StringHelpers.hpp"
0026
0027 #include <algorithm>
0028 #include <map>
0029 #include <optional>
0030 #include <sstream>
0031 #include <string>
0032
0033 #include <boost/container/small_vector.hpp>
0034
0035 namespace Acts {
0036
0037
0038
0039
0040 template <typename object_t>
0041 struct NavigationOptions {
0042
0043 BoundaryTolerance boundaryTolerance = BoundaryTolerance::None();
0044
0045
0046
0047 bool resolveSensitive = true;
0048
0049 bool resolveMaterial = true;
0050
0051 bool resolvePassive = false;
0052
0053
0054 const object_t* startObject = nullptr;
0055
0056 const object_t* endObject = nullptr;
0057
0058
0059 std::vector<GeometryIdentifier> externalSurfaces = {};
0060
0061
0062 double nearLimit = 0;
0063
0064 double farLimit = std::numeric_limits<double>::max();
0065 };
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084 class Navigator {
0085 public:
0086
0087 using NavigationSurfaces =
0088 boost::container::small_vector<NavigationTarget, 10>;
0089
0090
0091 using NavigationLayers = boost::container::small_vector<NavigationTarget, 10>;
0092
0093
0094 using NavigationBoundaries =
0095 boost::container::small_vector<NavigationTarget, 4>;
0096
0097
0098 using NavigationCandidates =
0099 boost::container::small_vector<NavigationTarget, 10>;
0100
0101 using ExternalSurfaces = std::multimap<std::uint64_t, GeometryIdentifier>;
0102
0103
0104 using GeometryVersion = TrackingGeometry::GeometryVersion;
0105
0106
0107 enum struct Stage : int {
0108 initial = 0,
0109 surfaceTarget = 1,
0110 layerTarget = 2,
0111 boundaryTarget = 3,
0112 };
0113
0114
0115 struct Config {
0116
0117 std::shared_ptr<const TrackingGeometry> trackingGeometry{nullptr};
0118
0119
0120 bool resolveSensitive = true;
0121
0122 bool resolveMaterial = true;
0123
0124 bool resolvePassive = false;
0125 };
0126
0127
0128 struct Options : public NavigatorPlainOptions {
0129
0130
0131 explicit Options(const GeometryContext& gctx)
0132 : NavigatorPlainOptions(gctx) {}
0133
0134
0135 double surfaceTolerance = s_onSurfaceTolerance;
0136
0137
0138 double nearLimit = s_onSurfaceTolerance;
0139
0140
0141 double farLimit = std::numeric_limits<double>::max();
0142
0143
0144 ExternalSurfaces externalSurfaces = {};
0145
0146
0147
0148 void insertExternalSurface(GeometryIdentifier geoid) {
0149 externalSurfaces.insert(
0150 std::pair<std::uint64_t, GeometryIdentifier>(geoid.layer(), geoid));
0151 }
0152
0153
0154
0155 void setPlainOptions(const NavigatorPlainOptions& options) {
0156 static_cast<NavigatorPlainOptions&>(*this) = options;
0157 }
0158 };
0159
0160
0161
0162
0163
0164 struct State {
0165
0166
0167 explicit State(const Options& options_) : options(options_) {}
0168
0169
0170 Options options;
0171
0172
0173
0174 NavigationSurfaces navSurfaces = {};
0175
0176 std::optional<std::size_t> navSurfaceIndex;
0177
0178
0179
0180 NavigationLayers navLayers = {};
0181
0182 std::optional<std::size_t> navLayerIndex;
0183
0184
0185
0186 NavigationBoundaries navBoundaries = {};
0187
0188 std::optional<std::size_t> navBoundaryIndex;
0189
0190
0191
0192 NavigationCandidates navCandidates = {};
0193
0194 std::optional<std::size_t> navCandidateIndex;
0195
0196 NavigationTarget& navSurface() {
0197 return navSurfaces.at(navSurfaceIndex.value());
0198 }
0199
0200
0201
0202 NavigationTarget& navLayer() { return navLayers.at(navLayerIndex.value()); }
0203
0204
0205
0206 NavigationTarget& navBoundary() {
0207 return navBoundaries.at(navBoundaryIndex.value());
0208 }
0209
0210
0211
0212 NavigationTarget& navCandidate() {
0213 return navCandidates.at(navCandidateIndex.value());
0214 }
0215
0216 const TrackingVolume* startVolume = nullptr;
0217
0218 const Layer* startLayer = nullptr;
0219
0220 const Surface* startSurface = nullptr;
0221
0222 const TrackingVolume* currentVolume = nullptr;
0223
0224 const Layer* currentLayer = nullptr;
0225
0226 const Surface* currentSurface = nullptr;
0227
0228 const Surface* targetSurface = nullptr;
0229
0230
0231 bool navigationBreak = false;
0232
0233 Stage navigationStage = Stage::initial;
0234
0235
0236 NavigatorStatistics statistics;
0237
0238
0239 NavigationStream stream;
0240
0241
0242 void resetAfterLayerSwitch() {
0243 navSurfaces.clear();
0244 navSurfaceIndex.reset();
0245 }
0246
0247
0248 void resetAfterVolumeSwitch() {
0249 resetAfterLayerSwitch();
0250
0251 navLayers.clear();
0252 navLayerIndex.reset();
0253 navBoundaries.clear();
0254 navBoundaryIndex.reset();
0255 navCandidates.clear();
0256 navCandidateIndex.reset();
0257
0258 currentLayer = nullptr;
0259 }
0260
0261
0262 void reset() {
0263 resetAfterVolumeSwitch();
0264
0265 currentVolume = nullptr;
0266 currentSurface = nullptr;
0267
0268 navigationBreak = false;
0269 navigationStage = Stage::initial;
0270 }
0271 };
0272
0273
0274
0275
0276
0277 explicit Navigator(Config cfg,
0278 std::shared_ptr<const Logger> _logger =
0279 getDefaultLogger("Navigator", Logging::Level::INFO))
0280 : m_cfg{std::move(cfg)}, m_logger{std::move(_logger)} {
0281 if (m_cfg.trackingGeometry == nullptr) {
0282 throw std::invalid_argument("Navigator: No tracking geometry provided.");
0283 }
0284 m_geometryVersion = m_cfg.trackingGeometry->geometryVersion();
0285 }
0286
0287
0288
0289
0290 State makeState(const Options& options) const {
0291 State state(options);
0292 return state;
0293 }
0294
0295
0296
0297
0298 const Surface* currentSurface(const State& state) const {
0299 return state.currentSurface;
0300 }
0301
0302
0303
0304
0305 const TrackingVolume* currentVolume(const State& state) const {
0306 return state.currentVolume;
0307 }
0308
0309
0310
0311
0312 const IVolumeMaterial* currentVolumeMaterial(const State& state) const {
0313 if (state.currentVolume == nullptr) {
0314 return nullptr;
0315 }
0316 return state.currentVolume->volumeMaterial();
0317 }
0318
0319
0320
0321
0322 const Surface* startSurface(const State& state) const {
0323 return state.startSurface;
0324 }
0325
0326
0327
0328
0329 const Surface* targetSurface(const State& state) const {
0330 return state.targetSurface;
0331 }
0332
0333
0334
0335
0336 bool endOfWorldReached(const State& state) const {
0337 return state.currentVolume == nullptr;
0338 }
0339
0340
0341
0342
0343 bool navigationBreak(const State& state) const {
0344 return state.navigationBreak;
0345 }
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355
0356
0357 [[nodiscard]] Result<void> initialize(State& state, const Vector3& position,
0358 const Vector3& direction,
0359 Direction propagationDirection) const {
0360 (void)propagationDirection;
0361
0362 ACTS_VERBOSE(volInfo(state) << "Initialization.");
0363
0364 auto printGeometryVersion = [](auto ver) {
0365 using enum TrackingGeometry::GeometryVersion;
0366 switch (ver) {
0367 case Gen1:
0368 return "Gen1";
0369 case Gen3:
0370 return "Gen3";
0371 default:
0372 throw std::runtime_error("Unknown geometry version.");
0373 }
0374 };
0375 ACTS_VERBOSE(volInfo(state) << "Geometry version is: "
0376 << printGeometryVersion(m_geometryVersion));
0377
0378 state.reset();
0379
0380 if (m_geometryVersion == GeometryVersion::Gen3) {
0381
0382
0383
0384 state.stream.candidates().reserve(50);
0385 }
0386
0387 state.startSurface = state.options.startSurface;
0388 state.targetSurface = state.options.targetSurface;
0389
0390
0391
0392
0393
0394
0395 if (state.startSurface != nullptr &&
0396 state.startSurface->associatedLayer() != nullptr) {
0397 ACTS_VERBOSE(
0398 volInfo(state)
0399 << "Fast start initialization through association from Surface.");
0400
0401 state.startLayer = state.startSurface->associatedLayer();
0402 state.startVolume = state.startLayer->trackingVolume();
0403 } else if (state.startVolume != nullptr) {
0404 ACTS_VERBOSE(
0405 volInfo(state)
0406 << "Fast start initialization through association from Volume.");
0407
0408 state.startLayer = state.startVolume->associatedLayer(
0409 state.options.geoContext, position);
0410 } else {
0411 ACTS_VERBOSE(volInfo(state)
0412 << "Slow start initialization through search.");
0413 ACTS_VERBOSE(volInfo(state)
0414 << "Starting from position " << toString(position)
0415 << " and direction " << toString(direction));
0416
0417
0418 state.startVolume = m_cfg.trackingGeometry->lowestTrackingVolume(
0419 state.options.geoContext, position);
0420
0421 if (state.startVolume != nullptr) {
0422 state.startLayer = state.startVolume->associatedLayer(
0423 state.options.geoContext, position);
0424 } else {
0425 ACTS_ERROR(volInfo(state)
0426 << "No start volume resolved. Nothing left to do.");
0427 state.navigationBreak = true;
0428 }
0429 }
0430
0431 state.currentVolume = state.startVolume;
0432 state.currentLayer = state.startLayer;
0433 state.currentSurface = state.startSurface;
0434
0435 if (state.currentVolume != nullptr) {
0436 ACTS_VERBOSE(volInfo(state) << "Start volume resolved "
0437 << state.currentVolume->geometryId());
0438
0439 if (!state.currentVolume->inside(position,
0440 state.options.surfaceTolerance)) {
0441 ACTS_DEBUG(
0442 volInfo(state)
0443 << "We did not end up inside the expected volume. position = "
0444 << position.transpose());
0445
0446 return Result<void>::failure(NavigatorError::NotInsideExpectedVolume);
0447 }
0448 }
0449 if (state.currentLayer != nullptr) {
0450 ACTS_VERBOSE(volInfo(state) << "Start layer resolved "
0451 << state.currentLayer->geometryId());
0452 }
0453 if (state.currentSurface != nullptr) {
0454 ACTS_VERBOSE(volInfo(state) << "Start surface resolved "
0455 << state.currentSurface->geometryId());
0456
0457 if (!state.currentSurface->isOnSurface(
0458 state.options.geoContext, position, direction,
0459 BoundaryTolerance::Infinite(), state.options.surfaceTolerance)) {
0460 ACTS_DEBUG(volInfo(state)
0461 << "We did not end up on the expected surface. surface = "
0462 << state.currentSurface->geometryId()
0463 << " position = " << position.transpose()
0464 << " direction = " << direction.transpose());
0465
0466 return Result<void>::failure(NavigatorError::NotOnExpectedSurface);
0467 }
0468 }
0469
0470 return Result<void>::success();
0471 }
0472
0473
0474
0475
0476
0477
0478
0479
0480
0481
0482 NavigationTarget nextTarget(State& state, const Vector3& position,
0483 const Vector3& direction) const {
0484
0485 state.currentSurface = nullptr;
0486
0487 if (inactive(state)) {
0488 return NavigationTarget::None();
0489 }
0490
0491 ACTS_VERBOSE(volInfo(state) << "Entering Navigator::nextTarget.");
0492
0493 NavigationTarget nextTarget = tryGetNextTarget(state, position, direction);
0494 if (!nextTarget.isNone()) {
0495 return nextTarget;
0496 }
0497
0498 state.reset();
0499 ++state.statistics.nRenavigations;
0500
0501
0502
0503 state.currentVolume = m_cfg.trackingGeometry->lowestTrackingVolume(
0504 state.options.geoContext, position);
0505
0506 if (state.currentVolume == nullptr) {
0507 ACTS_VERBOSE(volInfo(state) << "No volume found, stop navigation.");
0508 state.navigationBreak = true;
0509 return NavigationTarget::None();
0510 }
0511
0512 state.currentLayer = state.currentVolume->associatedLayer(
0513 state.options.geoContext, position);
0514
0515 ACTS_VERBOSE(volInfo(state) << "Resolved volume and layer.");
0516
0517
0518 nextTarget = tryGetNextTarget(state, position, direction);
0519 if (!nextTarget.isNone()) {
0520 return nextTarget;
0521 }
0522
0523 ACTS_VERBOSE(
0524 volInfo(state)
0525 << "No targets found again, we got really lost! Stop navigation.");
0526 state.navigationBreak = true;
0527 return NavigationTarget::None();
0528 }
0529
0530
0531
0532
0533
0534
0535
0536
0537
0538
0539 bool checkTargetValid(const State& state, const Vector3& position,
0540 const Vector3& direction) const {
0541 (void)position;
0542 (void)direction;
0543
0544 return state.navigationStage != Stage::initial;
0545 }
0546
0547
0548
0549
0550
0551
0552
0553
0554
0555 void handleSurfaceReached(State& state, const Vector3& position,
0556 const Vector3& direction,
0557 const Surface& surface) const {
0558 if (inactive(state)) {
0559 return;
0560 }
0561
0562 ACTS_VERBOSE(volInfo(state) << "Entering Navigator::handleSurfaceReached.");
0563
0564 state.currentSurface = &surface;
0565
0566 ACTS_VERBOSE(volInfo(state)
0567 << "Current surface: " << state.currentSurface->geometryId());
0568
0569
0570 if (m_geometryVersion == GeometryVersion::Gen3) {
0571 if (state.navCandidate().isPortalTarget() &&
0572 &state.navCandidate().surface() == &surface) {
0573 ACTS_VERBOSE(volInfo(state) << "Handling portal status.");
0574
0575
0576 const Portal* portal = &state.navCandidate().portal();
0577 auto res = portal->resolveVolume(state.options.geoContext, position,
0578 direction);
0579 if (!res.ok()) {
0580 ACTS_ERROR(volInfo(state)
0581 << "Failed to resolve volume through portal: "
0582 << res.error().message());
0583 return;
0584 }
0585
0586 state.currentVolume = res.value();
0587
0588
0589 state.resetAfterVolumeSwitch();
0590
0591 if (state.currentVolume != nullptr) {
0592 ACTS_VERBOSE(volInfo(state) << "Volume updated.");
0593
0594
0595
0596 state.navigationStage = Stage::surfaceTarget;
0597 } else {
0598 ACTS_VERBOSE(
0599 volInfo(state)
0600 << "No more volume to progress to, stopping navigation.");
0601 state.navigationBreak = true;
0602 }
0603 }
0604 return;
0605 }
0606
0607 if (state.navigationStage == Stage::surfaceTarget &&
0608 &state.navSurface().surface() == &surface) {
0609 ACTS_VERBOSE(volInfo(state) << "Handling surface status.");
0610
0611 return;
0612 }
0613
0614 if (state.navigationStage == Stage::layerTarget &&
0615 &state.navLayer().surface() == &surface) {
0616 ACTS_VERBOSE(volInfo(state) << "Handling layer status.");
0617
0618
0619 state.currentLayer = &state.navLayer().layer();
0620 state.navigationStage = Stage::surfaceTarget;
0621
0622
0623 state.resetAfterLayerSwitch();
0624
0625 return;
0626 }
0627
0628 if (state.navigationStage == Stage::boundaryTarget &&
0629 &state.navBoundary().surface() == &surface) {
0630 ACTS_VERBOSE(volInfo(state) << "Handling boundary status.");
0631
0632
0633 const BoundarySurface* boundary = &state.navBoundary().boundarySurface();
0634 assert(boundary != nullptr && "Retrieved boundary surface is nullptr");
0635 state.currentVolume = boundary->attachedVolume(state.options.geoContext,
0636 position, direction);
0637
0638
0639 state.resetAfterVolumeSwitch();
0640
0641 if (state.currentVolume != nullptr) {
0642 ACTS_VERBOSE(volInfo(state) << "Volume updated.");
0643 state.navigationStage = Stage::layerTarget;
0644 } else {
0645 ACTS_VERBOSE(volInfo(state)
0646 << "No more volume to progress to, stopping navigation.");
0647 state.navigationBreak = true;
0648 }
0649
0650 return;
0651 }
0652
0653 ACTS_ERROR(volInfo(state) << "Surface reached but unknown state.");
0654 }
0655
0656 private:
0657
0658
0659
0660
0661
0662 NavigationTarget getNextTargetGen1(State& state, const Vector3& position,
0663 const Vector3& direction) const {
0664
0665 if (state.navigationStage == Stage::surfaceTarget) {
0666 if (!state.navSurfaceIndex.has_value()) {
0667
0668 resolveSurfaces(state, position, direction);
0669 state.navSurfaceIndex = 0;
0670 } else {
0671 ++state.navSurfaceIndex.value();
0672 }
0673 if (state.navSurfaceIndex.value() < state.navSurfaces.size()) {
0674 ACTS_VERBOSE(volInfo(state) << "Target set to next surface.");
0675 return state.navSurface();
0676 } else {
0677
0678 ACTS_VERBOSE(volInfo(state) << "Target layers.");
0679 state.navigationStage = Stage::layerTarget;
0680 }
0681 }
0682
0683 if (state.navigationStage == Stage::layerTarget) {
0684 if (!state.navLayerIndex.has_value()) {
0685
0686 resolveLayers(state, position, direction);
0687 state.navLayerIndex = 0;
0688 } else {
0689 ++state.navLayerIndex.value();
0690 }
0691 if (state.navLayerIndex.value() < state.navLayers.size()) {
0692 ACTS_VERBOSE(volInfo(state) << "Target set to next layer.");
0693 return state.navLayer();
0694 } else {
0695
0696 ACTS_VERBOSE(volInfo(state) << "Target boundaries.");
0697 state.navigationStage = Stage::boundaryTarget;
0698 }
0699 }
0700
0701 if (state.navigationStage == Stage::boundaryTarget) {
0702 if (!state.navBoundaryIndex.has_value()) {
0703
0704 resolveBoundaries(state, position, direction);
0705 state.navBoundaryIndex = 0;
0706 } else {
0707 ++state.navBoundaryIndex.value();
0708 }
0709 if (state.navBoundaryIndex.value() < state.navBoundaries.size()) {
0710 ACTS_VERBOSE(volInfo(state) << "Target set to next boundary.");
0711 return state.navBoundary();
0712 } else {
0713
0714
0715 ACTS_VERBOSE(volInfo(state)
0716 << "Boundary targets exhausted. Renavigate.");
0717 return NavigationTarget::None();
0718 }
0719 }
0720
0721 ACTS_VERBOSE(volInfo(state)
0722 << "Unknown state. No target found. Renavigate.");
0723 return NavigationTarget::None();
0724 }
0725
0726
0727
0728
0729
0730
0731 NavigationTarget getNextTargetGen3(State& state, const Vector3& position,
0732 const Vector3& direction) const {
0733 if (!state.navCandidateIndex.has_value()) {
0734
0735 resolveCandidates(state, position, direction);
0736 state.navCandidateIndex = 0;
0737 } else {
0738 ++state.navCandidateIndex.value();
0739 }
0740 if (state.navCandidateIndex.value() < state.navCandidates.size()) {
0741 ACTS_VERBOSE(volInfo(state) << "Target set to next candidate.");
0742 return state.navCandidate();
0743 } else {
0744 ACTS_VERBOSE(volInfo(state)
0745 << "Candidate targets exhausted. Renavigate.");
0746 return NavigationTarget::None();
0747 }
0748 }
0749
0750
0751
0752
0753
0754
0755
0756
0757 NavigationTarget tryGetNextTarget(State& state, const Vector3& position,
0758 const Vector3& direction) const {
0759
0760
0761
0762
0763
0764 if (state.navigationStage == Stage::initial) {
0765 ACTS_VERBOSE(volInfo(state) << "Target surfaces.");
0766 state.navigationStage = Stage::surfaceTarget;
0767 }
0768
0769 if (m_geometryVersion == GeometryVersion::Gen1) {
0770 return getNextTargetGen1(state, position, direction);
0771
0772 } else {
0773
0774 return getNextTargetGen3(state, position, direction);
0775 }
0776 }
0777
0778
0779
0780
0781
0782
0783
0784
0785
0786 void resolveCandidates(State& state, const Vector3& position,
0787 const Vector3& direction) const {
0788 if (state.currentVolume == nullptr) {
0789 ACTS_VERBOSE(volInfo(state) << "No volume to resolve candidates.");
0790 return;
0791 }
0792 ACTS_VERBOSE(volInfo(state) << "Searching for compatible candidates.");
0793
0794 state.stream.reset();
0795 AppendOnlyNavigationStream appendOnly{state.stream};
0796 NavigationArguments args;
0797 args.position = position;
0798 args.direction = direction;
0799 state.currentVolume->initializeNavigationCandidates(args, appendOnly,
0800 logger());
0801
0802 ACTS_VERBOSE(volInfo(state) << "Found " << state.stream.candidates().size()
0803 << " navigation candidates.");
0804
0805 state.stream.initialize(state.options.geoContext, {position, direction},
0806 BoundaryTolerance::None(),
0807 state.options.surfaceTolerance);
0808
0809 ACTS_VERBOSE(volInfo(state)
0810 << "Now " << state.stream.candidates().size()
0811 << " navigation candidates after initialization");
0812
0813 state.navCandidates.clear();
0814
0815 for (auto& candidate : state.stream.candidates()) {
0816 if (!detail::checkPathLength(candidate.intersection().pathLength(),
0817 state.options.nearLimit,
0818 state.options.farLimit, logger())) {
0819 continue;
0820 }
0821
0822 state.navCandidates.emplace_back(candidate);
0823 }
0824
0825
0826 std::ranges::sort(state.navCandidates, [](const auto& a, const auto& b) {
0827 return a.intersection().pathLength() < b.intersection().pathLength();
0828 });
0829
0830
0831
0832 if (logger().doPrint(Logging::VERBOSE)) {
0833 std::ostringstream os;
0834 os << "Navigation candidates: " << state.navCandidates.size() << "\n";
0835 for (auto& candidate : state.navCandidates) {
0836 os << "Candidate: " << candidate.surface().geometryId()
0837 << " at path length: " << candidate.intersection().pathLength()
0838 << " ";
0839 }
0840
0841 logger().log(Logging::VERBOSE, os.str());
0842 }
0843 }
0844
0845
0846
0847
0848
0849
0850
0851
0852 void resolveSurfaces(State& state, const Vector3& position,
0853 const Vector3& direction) const {
0854 ACTS_VERBOSE(volInfo(state) << "Searching for compatible surfaces.");
0855
0856 const Layer* currentLayer = state.currentLayer;
0857
0858 if (currentLayer == nullptr) {
0859 ACTS_VERBOSE(volInfo(state) << "No layer to resolve surfaces.");
0860 return;
0861 }
0862
0863 const Surface* layerSurface = ¤tLayer->surfaceRepresentation();
0864
0865 NavigationOptions<Surface> navOpts;
0866 navOpts.resolveSensitive = m_cfg.resolveSensitive;
0867 navOpts.resolveMaterial = m_cfg.resolveMaterial;
0868 navOpts.resolvePassive = m_cfg.resolvePassive;
0869 navOpts.startObject = state.currentSurface;
0870 navOpts.endObject = state.targetSurface;
0871 navOpts.nearLimit = state.options.nearLimit;
0872 navOpts.farLimit = state.options.farLimit;
0873
0874 if (!state.options.externalSurfaces.empty()) {
0875 auto layerId = layerSurface->geometryId().layer();
0876 auto externalSurfaceRange =
0877 state.options.externalSurfaces.equal_range(layerId);
0878 navOpts.externalSurfaces.reserve(
0879 state.options.externalSurfaces.count(layerId));
0880 for (auto itSurface = externalSurfaceRange.first;
0881 itSurface != externalSurfaceRange.second; itSurface++) {
0882 navOpts.externalSurfaces.push_back(itSurface->second);
0883 }
0884 }
0885
0886
0887 state.navSurfaces = currentLayer->compatibleSurfaces(
0888 state.options.geoContext, position, direction, navOpts);
0889
0890
0891
0892 std::ranges::sort(state.navSurfaces, [&state](const NavigationTarget& a,
0893 const NavigationTarget& b) {
0894
0895
0896 if (std::abs(a.pathLength() - b.pathLength()) >
0897 state.options.surfaceTolerance) {
0898 return NavigationTarget::pathLengthOrder(a, b);
0899 }
0900
0901
0902 bool aIsExternal = a.boundaryTolerance().isInfinite();
0903 bool bIsExternal = b.boundaryTolerance().isInfinite();
0904 if (aIsExternal == bIsExternal) {
0905
0906
0907 return a.surface().geometryId() < b.surface().geometryId();
0908 }
0909
0910 return aIsExternal;
0911 });
0912
0913
0914
0915
0916 auto toBeRemoved = std::ranges::unique(
0917 state.navSurfaces, [&](const auto& a, const auto& b) {
0918 return std::abs(a.pathLength() - b.pathLength()) <
0919 state.options.surfaceTolerance;
0920 });
0921 if (toBeRemoved.begin() != toBeRemoved.end()) {
0922 ACTS_VERBOSE(volInfo(state)
0923 << "Removing "
0924 << std::distance(toBeRemoved.begin(), toBeRemoved.end())
0925 << " overlapping surfaces.");
0926 }
0927 state.navSurfaces.erase(toBeRemoved.begin(), toBeRemoved.end());
0928
0929
0930 if (logger().doPrint(Logging::VERBOSE)) {
0931 std::ostringstream os;
0932 os << state.navSurfaces.size();
0933 os << " surface candidates found at path(s): ";
0934 for (auto& sfc : state.navSurfaces) {
0935 os << sfc.pathLength() << " ";
0936 }
0937 logger().log(Logging::VERBOSE, os.str());
0938 }
0939
0940 if (state.navSurfaces.empty()) {
0941 ACTS_VERBOSE(volInfo(state) << "No surface candidates found.");
0942 }
0943 }
0944
0945
0946
0947
0948
0949
0950
0951
0952 void resolveLayers(State& state, const Vector3& position,
0953 const Vector3& direction) const {
0954 ACTS_VERBOSE(volInfo(state) << "Searching for compatible layers.");
0955
0956 NavigationOptions<Layer> navOpts;
0957 navOpts.resolveSensitive = m_cfg.resolveSensitive;
0958 navOpts.resolveMaterial = m_cfg.resolveMaterial;
0959 navOpts.resolvePassive = m_cfg.resolvePassive;
0960 navOpts.startObject = state.currentLayer;
0961 navOpts.nearLimit = state.options.nearLimit;
0962 navOpts.farLimit = state.options.farLimit;
0963
0964
0965 state.navLayers = state.currentVolume->compatibleLayers(
0966 state.options.geoContext, position, direction, navOpts);
0967 std::ranges::sort(state.navLayers, NavigationTarget::pathLengthOrder);
0968
0969
0970 if (logger().doPrint(Logging::VERBOSE)) {
0971 std::ostringstream os;
0972 os << state.navLayers.size();
0973 os << " layer candidates found at path(s): ";
0974 for (auto& lc : state.navLayers) {
0975 os << lc.pathLength() << " ";
0976 }
0977 logger().log(Logging::VERBOSE, os.str());
0978 }
0979
0980 if (state.navLayers.empty()) {
0981 ACTS_VERBOSE(volInfo(state) << "No layer candidates found.");
0982 }
0983 }
0984
0985
0986
0987
0988
0989
0990
0991
0992 void resolveBoundaries(State& state, const Vector3& position,
0993 const Vector3& direction) const {
0994 ACTS_VERBOSE(volInfo(state) << "Searching for compatible boundaries.");
0995
0996 NavigationOptions<Surface> navOpts;
0997 navOpts.startObject = state.currentSurface;
0998 navOpts.nearLimit = state.options.nearLimit;
0999 navOpts.farLimit = state.options.farLimit;
1000
1001 ACTS_VERBOSE(volInfo(state)
1002 << "Try to find boundaries, we are at: " << toString(position)
1003 << ", dir: " << toString(direction));
1004
1005
1006 state.navBoundaries = state.currentVolume->compatibleBoundaries(
1007 state.options.geoContext, position, direction, navOpts, logger());
1008 std::ranges::sort(state.navBoundaries, NavigationTarget::pathLengthOrder);
1009
1010
1011 if (logger().doPrint(Logging::VERBOSE)) {
1012 std::ostringstream os;
1013 os << state.navBoundaries.size();
1014 os << " boundary candidates found at path(s): ";
1015 for (const auto& bc : state.navBoundaries) {
1016 os << bc.pathLength() << " ";
1017 }
1018 logger().log(Logging::VERBOSE, os.str());
1019 }
1020
1021 if (state.navBoundaries.empty()) {
1022 ACTS_VERBOSE(volInfo(state) << "No boundary candidates found.");
1023 }
1024 }
1025
1026
1027
1028
1029
1030
1031
1032
1033 bool inactive(const State& state) const {
1034
1035 if (!m_cfg.resolveSensitive && !m_cfg.resolveMaterial &&
1036 !m_cfg.resolvePassive) {
1037 return true;
1038 }
1039
1040 if (state.navigationBreak) {
1041 return true;
1042 }
1043
1044 return false;
1045 }
1046
1047 private:
1048 template <typename propagator_state_t>
1049 std::string volInfo(const propagator_state_t& state) const {
1050 return (state.currentVolume != nullptr ? state.currentVolume->volumeName()
1051 : "No Volume") +
1052 " | ";
1053 }
1054
1055 const Logger& logger() const { return *m_logger; }
1056
1057 Config m_cfg;
1058
1059
1060 TrackingGeometry::GeometryVersion m_geometryVersion{};
1061
1062 std::shared_ptr<const Logger> m_logger;
1063 };
1064
1065 }