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