File indexing completed on 2026-04-17 07:46:43
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Propagator/Navigator.hpp"
0010
0011 #include "Acts/Definitions/Units.hpp"
0012 #include "Acts/Geometry/BoundarySurfaceT.hpp"
0013 #include "Acts/Geometry/GeometryIdentifier.hpp"
0014 #include "Acts/Geometry/Portal.hpp"
0015 #include "Acts/Propagator/NavigatorError.hpp"
0016 #include "Acts/Surfaces/Surface.hpp"
0017 #include "Acts/Utilities/Intersection.hpp"
0018 #include "Acts/Utilities/Logger.hpp"
0019 #include "Acts/Utilities/StringHelpers.hpp"
0020
0021 #include <algorithm>
0022 #include <cassert>
0023 #include <sstream>
0024
0025 namespace Acts {
0026 std::ostream& operator<<(
0027 std::ostream& ostr,
0028 const std::span<const Acts::NavigationTarget>& candidates) {
0029 for (const auto& target : candidates) {
0030 ostr << "\n -- " << target;
0031 }
0032 return ostr;
0033 }
0034
0035 Navigator::Navigator(Config cfg, std::shared_ptr<const Logger> _logger)
0036 : m_cfg{std::move(cfg)}, m_logger{std::move(_logger)} {
0037 if (m_cfg.trackingGeometry == nullptr) {
0038 throw std::invalid_argument("Navigator: No tracking geometry provided.");
0039 }
0040 m_geometryVersion = m_cfg.trackingGeometry->geometryVersion();
0041 }
0042
0043 Navigator::State Navigator::makeState(const Options& options) const {
0044 State state(options);
0045 return state;
0046 }
0047
0048 const Surface* Navigator::currentSurface(const State& state) const {
0049 return state.currentSurface;
0050 }
0051
0052 const TrackingVolume* Navigator::currentVolume(const State& state) const {
0053 return state.currentVolume;
0054 }
0055
0056 const IVolumeMaterial* Navigator::currentVolumeMaterial(
0057 const State& state) const {
0058 if (state.currentVolume == nullptr) {
0059 return nullptr;
0060 }
0061 return state.currentVolume->volumeMaterial();
0062 }
0063
0064 const Surface* Navigator::startSurface(const State& state) const {
0065 return state.startSurface;
0066 }
0067
0068 const Surface* Navigator::targetSurface(const State& state) const {
0069 return state.targetSurface;
0070 }
0071
0072 bool Navigator::endOfWorldReached(const State& state) const {
0073 return state.currentVolume == nullptr;
0074 }
0075
0076 bool Navigator::navigationBreak(const State& state) const {
0077 return state.navigationBreak;
0078 }
0079
0080 Result<void> Navigator::initialize(State& state, const Vector3& position,
0081 const Vector3& direction,
0082 Direction propagationDirection) const {
0083 static_cast<void>(propagationDirection);
0084
0085 ACTS_VERBOSE(volInfo(state) << "Initialization.");
0086
0087 auto printGeometryVersion = [](auto ver) {
0088 using enum TrackingGeometry::GeometryVersion;
0089 switch (ver) {
0090 case Gen1:
0091 return "Gen1";
0092 case Gen3:
0093 return "Gen3";
0094 default:
0095 throw std::runtime_error("Unknown geometry version.");
0096 }
0097 };
0098 ACTS_VERBOSE(volInfo(state) << "Geometry version is: "
0099 << printGeometryVersion(m_geometryVersion));
0100
0101 state.resetForRenavigation();
0102
0103 if (m_geometryVersion == GeometryVersion::Gen3) {
0104
0105
0106
0107 state.stream.candidates().reserve(50);
0108
0109 state.freeCandidates.clear();
0110 state.freeCandidates.reserve(state.options.externalSurfaces.size());
0111 for (const Surface* candidate : state.options.externalSurfaces) {
0112 if (candidate->geometryId() == GeometryIdentifier{}) {
0113 state.freeCandidates.emplace_back(candidate, false);
0114 }
0115 }
0116 }
0117
0118 state.startSurface = state.options.startSurface;
0119 state.targetSurface = state.options.targetSurface;
0120
0121
0122
0123
0124
0125
0126
0127 if (state.startSurface != nullptr &&
0128 state.startSurface->associatedLayer() != nullptr) {
0129 ACTS_VERBOSE(
0130 volInfo(state)
0131 << "Fast start initialization through association from Surface.");
0132
0133 state.startLayer = state.startSurface->associatedLayer();
0134 state.startVolume = state.startLayer->trackingVolume();
0135 } else if (state.startVolume != nullptr) {
0136 ACTS_VERBOSE(
0137 volInfo(state)
0138 << "Fast start initialization through association from Volume.");
0139
0140 state.startLayer =
0141 state.startVolume->associatedLayer(state.options.geoContext, position);
0142 } else {
0143 ACTS_VERBOSE(volInfo(state) << "Slow start initialization through search.");
0144 ACTS_VERBOSE(volInfo(state)
0145 << "Starting from position " << toString(position)
0146 << " and direction " << toString(direction));
0147
0148
0149 state.startVolume = m_cfg.trackingGeometry->lowestTrackingVolume(
0150 state.options.geoContext, position);
0151
0152 if (state.startVolume != nullptr) {
0153 state.startLayer = state.startVolume->associatedLayer(
0154 state.options.geoContext, position);
0155 } else {
0156 ACTS_DEBUG(volInfo(state)
0157 << "No start volume resolved. Nothing left to do.");
0158 state.navigationBreak = true;
0159 return Result<void>::failure(NavigatorError::NoStartVolume);
0160 }
0161 }
0162
0163 state.currentVolume = state.startVolume;
0164 state.currentLayer = state.startLayer;
0165 state.currentSurface = state.startSurface;
0166
0167 if (state.currentVolume != nullptr) {
0168 ACTS_VERBOSE(volInfo(state) << "Start volume resolved "
0169 << state.currentVolume->geometryId());
0170
0171 if (!state.currentVolume->inside(state.options.geoContext, position,
0172 state.options.surfaceTolerance)) {
0173 ACTS_DEBUG(volInfo(state)
0174 << "We did not end up inside the expected volume. position = "
0175 << position.transpose());
0176
0177 return Result<void>::failure(NavigatorError::NotInsideExpectedVolume);
0178 }
0179
0180 if (const auto* policy = state.currentVolume->navigationPolicy();
0181 policy != nullptr) {
0182 ACTS_VERBOSE(volInfo(state)
0183 << "Creating initial navigation policy state for volume.");
0184 policy->createState(state.options.geoContext,
0185 {.position = position, .direction = direction},
0186 state.policyStateManager, logger());
0187 }
0188 }
0189 if (state.currentLayer != nullptr) {
0190 ACTS_VERBOSE(volInfo(state) << "Start layer resolved "
0191 << state.currentLayer->geometryId());
0192 }
0193 if (state.currentSurface != nullptr) {
0194 ACTS_VERBOSE(volInfo(state) << "Start surface resolved "
0195 << state.currentSurface->geometryId());
0196
0197 if (!state.currentSurface->isOnSurface(
0198 state.options.geoContext, position, direction,
0199 BoundaryTolerance::Infinite(), state.options.surfaceTolerance)) {
0200 ACTS_DEBUG(volInfo(state)
0201 << "We did not end up on the expected surface. surface = "
0202 << state.currentSurface->geometryId()
0203 << " position = " << position.transpose()
0204 << " direction = " << direction.transpose());
0205
0206 return Result<void>::failure(NavigatorError::NotOnExpectedSurface);
0207 }
0208 }
0209
0210 return Result<void>::success();
0211 }
0212
0213 NavigationTarget Navigator::nextTarget(State& state, const Vector3& position,
0214 const Vector3& direction) const {
0215
0216 state.currentSurface = nullptr;
0217
0218 if (inactive(state)) {
0219 return NavigationTarget::None();
0220 }
0221
0222 ACTS_VERBOSE(volInfo(state) << "Entering Navigator::nextTarget.");
0223
0224 NavigationTarget nextTarget = tryGetNextTarget(state, position, direction);
0225 if (!nextTarget.isNone()) {
0226 return nextTarget;
0227 }
0228
0229 state.resetForRenavigation();
0230 ++state.statistics.nRenavigations;
0231
0232
0233
0234 state.currentVolume = m_cfg.trackingGeometry->lowestTrackingVolume(
0235 state.options.geoContext, position);
0236
0237 if (state.currentVolume == nullptr) {
0238 ACTS_VERBOSE(volInfo(state) << "No volume found, stop navigation.");
0239 state.navigationBreak = true;
0240 return NavigationTarget::None();
0241 }
0242
0243 if (m_geometryVersion == GeometryVersion::Gen3) {
0244
0245
0246 const auto* policy = state.currentVolume->navigationPolicy();
0247 if (policy == nullptr) {
0248 ACTS_ERROR(volInfo(state) << "No navigation policy for new volume, this "
0249 "should not happen. Stop navigation.");
0250 return NavigationTarget::None();
0251 }
0252
0253 ACTS_VERBOSE(volInfo(state) << "Creating navigation policy state for new "
0254 "volume after renavigation.");
0255 policy->createState(state.options.geoContext,
0256 {.position = position, .direction = direction},
0257 state.policyStateManager, logger());
0258 }
0259
0260 state.currentLayer =
0261 state.currentVolume->associatedLayer(state.options.geoContext, position);
0262
0263 ACTS_VERBOSE(volInfo(state) << "Resolved volume and layer.");
0264
0265
0266 nextTarget = tryGetNextTarget(state, position, direction);
0267 if (!nextTarget.isNone()) {
0268 return nextTarget;
0269 }
0270
0271 ACTS_VERBOSE(
0272 volInfo(state)
0273 << "No targets found again, we got really lost! Stop navigation.");
0274 state.navigationBreak = true;
0275 return NavigationTarget::None();
0276 }
0277
0278 bool Navigator::checkTargetValid(State& state, const Vector3& position,
0279 const Vector3& direction) const {
0280 ACTS_VERBOSE(volInfo(state) << "Entering Navigator::checkTargetValid.");
0281
0282 if (state.navigationStage == Stage::initial) {
0283 return false;
0284 }
0285
0286 if (state.currentVolume != nullptr &&
0287 state.currentVolume->navigationPolicy() != nullptr) {
0288 ACTS_VERBOSE(volInfo(state) << "Checking policy validity for volume");
0289
0290 auto policyState = state.policyStateManager.currentState();
0291 bool isValid = state.currentVolume->navigationPolicy()->isValid(
0292 state.options.geoContext,
0293 {.position = position, .direction = direction}, policyState, logger());
0294 return isValid;
0295 }
0296
0297 return true;
0298 }
0299
0300 void Navigator::handleSurfaceReached(State& state, const Vector3& position,
0301 const Vector3& direction,
0302 const Surface& surface) const {
0303 if (inactive(state)) {
0304 return;
0305 }
0306
0307 ACTS_VERBOSE(volInfo(state) << "Entering Navigator::handleSurfaceReached.");
0308
0309 state.currentSurface = &surface;
0310
0311 ACTS_VERBOSE(volInfo(state)
0312 << "Current surface: " << state.currentSurface->geometryId());
0313
0314
0315 if (m_geometryVersion == GeometryVersion::Gen3) {
0316 if (state.navCandidate().isPortalTarget() &&
0317 &state.navCandidate().surface() == &surface) {
0318 ACTS_VERBOSE(volInfo(state) << "Handling portal status.");
0319
0320
0321 const Portal* portal = &state.navCandidate().portal();
0322 auto res =
0323 portal->resolveVolume(state.options.geoContext, position, direction);
0324 if (!res.ok()) {
0325 ACTS_ERROR(volInfo(state) << "Failed to resolve volume through portal: "
0326 << res.error().message());
0327 return;
0328 }
0329
0330 if (state.currentVolume != nullptr &&
0331 state.currentVolume->navigationPolicy() != nullptr) {
0332 ACTS_VERBOSE(volInfo(state)
0333 << "Popping navigation policy state for volume on exit");
0334 state.currentVolume->navigationPolicy()->popState(
0335 state.policyStateManager, logger());
0336 }
0337
0338 state.currentVolume = res.value();
0339
0340
0341 state.resetAfterVolumeSwitch();
0342
0343 if (state.currentVolume != nullptr) {
0344 ACTS_VERBOSE(volInfo(state) << "Volume updated.");
0345
0346 const auto* policy = state.currentVolume->navigationPolicy();
0347
0348 if (policy == nullptr) {
0349 ACTS_ERROR(
0350 volInfo(state)
0351 << "No navigation policy for new volume, this should not happen");
0352 return;
0353 }
0354
0355 ACTS_VERBOSE(volInfo(state)
0356 << "Creating navigation policy state for new "
0357 "volume after portal transition.");
0358 policy->createState(state.options.geoContext,
0359 {.position = position, .direction = direction},
0360 state.policyStateManager, logger());
0361
0362
0363
0364 state.navigationStage = Stage::surfaceTarget;
0365 } else {
0366 ACTS_VERBOSE(volInfo(state)
0367 << "No more volume to progress to, stopping navigation.");
0368 state.navigationBreak = true;
0369 }
0370 }
0371
0372 else if (&state.navCandidate().surface() == &surface &&
0373 surface.geometryId() == GeometryIdentifier{}) {
0374 auto freeItr = std::ranges::find_if(
0375 state.freeCandidates,
0376 [&surface](const std::pair<const Surface*, bool>& cand) {
0377 return &surface == cand.first;
0378 });
0379 if (freeItr != state.freeCandidates.end()) {
0380 freeItr->second = true;
0381 }
0382 }
0383 return;
0384 }
0385
0386 if (state.navigationStage == Stage::surfaceTarget &&
0387 &state.navSurface().surface() == &surface) {
0388 ACTS_VERBOSE(volInfo(state) << "Handling surface status.");
0389
0390 return;
0391 }
0392
0393 if (state.navigationStage == Stage::layerTarget &&
0394 &state.navLayer().surface() == &surface) {
0395 ACTS_VERBOSE(volInfo(state) << "Handling layer status.");
0396
0397
0398 state.currentLayer = &state.navLayer().layer();
0399 state.navigationStage = Stage::surfaceTarget;
0400
0401
0402 state.resetAfterLayerSwitch();
0403
0404 return;
0405 }
0406
0407 if (state.navigationStage == Stage::boundaryTarget &&
0408 &state.navBoundary().surface() == &surface) {
0409 ACTS_VERBOSE(volInfo(state) << "Handling boundary status.");
0410
0411
0412 const BoundarySurface* boundary = &state.navBoundary().boundarySurface();
0413 assert(boundary != nullptr && "Retrieved boundary surface is nullptr");
0414 state.currentVolume =
0415 boundary->attachedVolume(state.options.geoContext, position, direction);
0416
0417
0418 state.resetAfterVolumeSwitch();
0419
0420 if (state.currentVolume != nullptr) {
0421 ACTS_VERBOSE(volInfo(state) << "Volume updated.");
0422 state.navigationStage = Stage::layerTarget;
0423 } else {
0424 ACTS_VERBOSE(volInfo(state)
0425 << "No more volume to progress to, stopping navigation.");
0426 state.navigationBreak = true;
0427 }
0428
0429 return;
0430 }
0431
0432 ACTS_ERROR(volInfo(state) << "Surface reached but unknown state.");
0433 }
0434
0435 NavigationTarget Navigator::getNextTargetGen1(State& state,
0436 const Vector3& position,
0437 const Vector3& direction) const {
0438
0439 if (state.navigationStage == Stage::surfaceTarget) {
0440 if (!state.navSurfaceIndex.has_value()) {
0441
0442 resolveSurfaces(state, position, direction);
0443 state.navSurfaceIndex = 0;
0444 } else {
0445 ++state.navSurfaceIndex.value();
0446 }
0447 if (state.navSurfaceIndex.value() < state.navSurfaces.size()) {
0448 ACTS_VERBOSE(volInfo(state)
0449 << "Target set to next surface. " << state.navSurface());
0450 return state.navSurface();
0451 } else {
0452
0453 ACTS_VERBOSE(volInfo(state) << "Target layers.");
0454 state.navigationStage = Stage::layerTarget;
0455 }
0456 }
0457
0458 if (state.navigationStage == Stage::layerTarget) {
0459 if (!state.navLayerIndex.has_value()) {
0460
0461 resolveLayers(state, position, direction);
0462 state.navLayerIndex = 0;
0463 } else {
0464 ++state.navLayerIndex.value();
0465 }
0466 if (state.navLayerIndex.value() < state.navLayers.size()) {
0467 ACTS_VERBOSE(volInfo(state)
0468 << "Target set to next layer. " << state.navLayer());
0469 return state.navLayer();
0470 } else {
0471
0472 ACTS_VERBOSE(volInfo(state) << "Target boundaries.");
0473 state.navigationStage = Stage::boundaryTarget;
0474 }
0475 }
0476
0477 if (state.navigationStage == Stage::boundaryTarget) {
0478 if (!state.navBoundaryIndex.has_value()) {
0479
0480 resolveBoundaries(state, position, direction);
0481 state.navBoundaryIndex = 0;
0482 } else {
0483 ++state.navBoundaryIndex.value();
0484 }
0485 if (state.navBoundaryIndex.value() < state.navBoundaries.size()) {
0486 ACTS_VERBOSE(volInfo(state)
0487 << "Target set to next boundary " << state.navBoundary());
0488 return state.navBoundary();
0489 } else {
0490
0491
0492 ACTS_VERBOSE(volInfo(state) << "Boundary targets exhausted. Renavigate.");
0493 return NavigationTarget::None();
0494 }
0495 }
0496
0497 ACTS_VERBOSE(volInfo(state) << "Unknown state. No target found. Renavigate.");
0498 return NavigationTarget::None();
0499 }
0500
0501 NavigationTarget Navigator::getNextTargetGen3(State& state,
0502 const Vector3& position,
0503 const Vector3& direction) const {
0504 if (state.currentVolume == nullptr) {
0505 ACTS_VERBOSE(volInfo(state) << "No volume to get next target.");
0506 return NavigationTarget::None();
0507 }
0508
0509 auto policyState = state.policyStateManager.currentState();
0510 bool isValid = state.currentVolume->navigationPolicy()->isValid(
0511 state.options.geoContext, {.position = position, .direction = direction},
0512 policyState, logger());
0513
0514 ACTS_VERBOSE(volInfo(state) << "Current policy says navigation sequence is "
0515 << (isValid ? "VALID" : "INVALID"));
0516
0517 ACTS_VERBOSE(volInfo(state)
0518 << "Current candidate index is "
0519 << (state.navCandidateIndex.has_value()
0520 ? std::to_string(state.navCandidateIndex.value())
0521 : "n/a"));
0522
0523 if (!isValid || !state.navCandidateIndex.has_value()) {
0524
0525 resolveCandidates(state, position, direction);
0526 state.navCandidateIndex = 0;
0527 } else {
0528 ++state.navCandidateIndex.value();
0529 }
0530 if (state.navCandidateIndex.value() < state.navCandidates.size()) {
0531 ACTS_VERBOSE(volInfo(state)
0532 << "Target set to next candidate " << state.navCandidate());
0533 return state.navCandidate();
0534 } else {
0535 ACTS_VERBOSE(volInfo(state) << "Candidate targets exhausted. Renavigate.");
0536 return NavigationTarget::None();
0537 }
0538 }
0539
0540 NavigationTarget Navigator::tryGetNextTarget(State& state,
0541 const Vector3& position,
0542 const Vector3& direction) const {
0543
0544
0545
0546
0547
0548 if (state.navigationStage == Stage::initial) {
0549 ACTS_VERBOSE(volInfo(state) << "Target surfaces.");
0550 state.navigationStage = Stage::surfaceTarget;
0551 }
0552
0553 if (m_geometryVersion == GeometryVersion::Gen1) {
0554 return getNextTargetGen1(state, position, direction);
0555
0556 } else {
0557
0558 return getNextTargetGen3(state, position, direction);
0559 }
0560 }
0561
0562 void Navigator::resolveCandidates(State& state, const Vector3& position,
0563 const Vector3& direction) const {
0564 if (state.currentVolume == nullptr) {
0565 ACTS_VERBOSE(volInfo(state) << "No volume to resolve candidates.");
0566 return;
0567 }
0568 ACTS_VERBOSE(volInfo(state) << "Searching for compatible candidates.");
0569
0570 state.stream.reset();
0571 AppendOnlyNavigationStream appendOnly{state.stream};
0572 NavigationArguments args;
0573 args.position = position;
0574 args.direction = direction;
0575
0576 const INavigationPolicy* policy = state.currentVolume->navigationPolicy();
0577 if (policy == nullptr) {
0578 ACTS_ERROR(volInfo(state) << "No navigation policy found for volume. "
0579 "Cannot resolve navigation candidates.");
0580 throw std::runtime_error(
0581 "Navigator: No navigation policy found for current volume.");
0582 }
0583
0584 auto policyState = state.policyStateManager.currentState();
0585 state.currentVolume->initializeNavigationCandidates(
0586 state.options.geoContext, args, policyState, appendOnly, logger());
0587
0588 ACTS_VERBOSE(volInfo(state) << "Found " << state.stream.candidates().size()
0589 << " navigation candidates.");
0590 for (const Surface* surface : state.options.externalSurfaces) {
0591 const GeometryIdentifier geoId = surface->geometryId();
0592
0593
0594 if (geoId.withSensitive(0) != state.currentVolume->geometryId()) {
0595 continue;
0596 }
0597 ACTS_VERBOSE(volInfo(state) << "Try to navigate to " << surface->type()
0598 << " surface " << geoId);
0599 appendOnly.addSurfaceCandidate(*surface, BoundaryTolerance::Infinite());
0600 }
0601 bool pruneFreeCand{false};
0602 if (!state.freeCandidates.empty()) {
0603 for (const auto& [surface, wasReached] : state.freeCandidates) {
0604
0605 if (wasReached) {
0606 continue;
0607 }
0608 if (!state.options.freeSurfaceSelector.connected() ||
0609 state.options.freeSurfaceSelector(state.options.geoContext,
0610 *state.currentVolume, position,
0611 direction, *surface)) {
0612 ACTS_VERBOSE(volInfo(state)
0613 << "Append free " << surface->type() << " surface \n"
0614 << surface->toStream(state.options.geoContext));
0615 appendOnly.addSurfaceCandidate(*surface, BoundaryTolerance::Infinite());
0616 pruneFreeCand = !state.options.freeSurfaceSelector.connected();
0617 }
0618 };
0619 }
0620 state.stream.initialize(state.options.geoContext, {position, direction},
0621 BoundaryTolerance::None(),
0622 state.options.surfaceTolerance);
0623
0624 ACTS_VERBOSE(volInfo(state)
0625 << "Now " << state.stream.candidates().size()
0626 << " navigation candidates after initialization.\n"
0627 << state.stream.candidates());
0628
0629 state.navCandidates.clear();
0630
0631 double farLimit = state.options.farLimit;
0632
0633
0634
0635 if (pruneFreeCand) {
0636 farLimit = state.options.nearLimit;
0637 for (const auto& candidate : state.stream.candidates()) {
0638 if (candidate.isPortalTarget()) {
0639 farLimit = std::max(farLimit, candidate.intersection().pathLength() +
0640 state.options.surfaceTolerance);
0641 }
0642 }
0643 }
0644
0645 for (auto& candidate : state.stream.candidates()) {
0646 if (!detail::checkPathLength(candidate.intersection().pathLength(),
0647 state.options.nearLimit, farLimit, logger())) {
0648 continue;
0649 }
0650
0651 state.navCandidates.emplace_back(candidate);
0652 }
0653
0654
0655 std::ranges::sort(state.navCandidates, [](const auto& a, const auto& b) {
0656 return a.intersection().pathLength() < b.intersection().pathLength();
0657 });
0658
0659
0660 ACTS_VERBOSE("Navigation candidates: " << state.navCandidates.size() << "\n"
0661 << state.navCandidates);
0662 }
0663
0664 void Navigator::resolveSurfaces(State& state, const Vector3& position,
0665 const Vector3& direction) const {
0666
0667 ACTS_VERBOSE(volInfo(state) << "Searching for compatible surfaces.");
0668
0669 const Layer* currentLayer = state.currentLayer;
0670
0671 if (currentLayer == nullptr) {
0672 ACTS_VERBOSE(volInfo(state) << "No layer to resolve surfaces.");
0673 return;
0674 }
0675
0676 const Surface* layerSurface = ¤tLayer->surfaceRepresentation();
0677
0678 NavigationOptions<Surface> navOpts;
0679 navOpts.resolveSensitive = m_cfg.resolveSensitive;
0680 navOpts.resolveMaterial = m_cfg.resolveMaterial;
0681 navOpts.resolvePassive = m_cfg.resolvePassive;
0682 navOpts.startObject = state.currentSurface;
0683 navOpts.endObject = state.targetSurface;
0684 navOpts.nearLimit = state.options.nearLimit;
0685 navOpts.farLimit = state.options.farLimit;
0686
0687 const auto layerId = layerSurface->geometryId().layer();
0688 for (const Surface* surface : state.options.externalSurfaces) {
0689 const GeometryIdentifier geoId = surface->geometryId();
0690 if (geoId.layer() == layerId) {
0691 navOpts.externalSurfaces.push_back(geoId);
0692 }
0693 }
0694
0695
0696 state.navSurfaces = currentLayer->compatibleSurfaces(
0697 state.options.geoContext, position, direction, navOpts);
0698
0699
0700
0701 std::ranges::sort(state.navSurfaces, [&state](const NavigationTarget& a,
0702 const NavigationTarget& b) {
0703
0704
0705 if (std::abs(a.pathLength() - b.pathLength()) >
0706 state.options.surfaceTolerance) {
0707 return NavigationTarget::pathLengthOrder(a, b);
0708 }
0709
0710
0711 bool aIsExternal = a.boundaryTolerance().isInfinite();
0712 bool bIsExternal = b.boundaryTolerance().isInfinite();
0713 if (aIsExternal == bIsExternal) {
0714
0715
0716 return a.surface().geometryId() < b.surface().geometryId();
0717 }
0718
0719 return aIsExternal;
0720 });
0721
0722
0723
0724
0725 auto toBeRemoved =
0726 std::ranges::unique(state.navSurfaces, [&](const auto& a, const auto& b) {
0727 return std::abs(a.pathLength() - b.pathLength()) <
0728 state.options.surfaceTolerance;
0729 });
0730 if (toBeRemoved.begin() != toBeRemoved.end()) {
0731 ACTS_VERBOSE(volInfo(state)
0732 << "Removing "
0733 << std::distance(toBeRemoved.begin(), toBeRemoved.end())
0734 << " overlapping surfaces. " << toBeRemoved);
0735 }
0736 state.navSurfaces.erase(toBeRemoved.begin(), toBeRemoved.end());
0737
0738 ACTS_VERBOSE(volInfo(state) << state.navSurfaces.size()
0739 << " surface candidates found at path(s): "
0740 << state.navSurfaces);
0741
0742 if (state.navSurfaces.empty()) {
0743 ACTS_VERBOSE(volInfo(state) << "No surface candidates found.");
0744 }
0745 }
0746
0747 void Navigator::resolveLayers(State& state, const Vector3& position,
0748 const Vector3& direction) const {
0749 ACTS_VERBOSE(volInfo(state) << "Searching for compatible layers.");
0750
0751 NavigationOptions<Layer> navOpts;
0752 navOpts.resolveSensitive = m_cfg.resolveSensitive;
0753 navOpts.resolveMaterial = m_cfg.resolveMaterial;
0754 navOpts.resolvePassive = m_cfg.resolvePassive;
0755 navOpts.startObject = state.currentLayer;
0756 navOpts.nearLimit = state.options.nearLimit;
0757 navOpts.farLimit = state.options.farLimit;
0758
0759
0760 state.navLayers = state.currentVolume->compatibleLayers(
0761 state.options.geoContext, position, direction, navOpts);
0762 std::ranges::sort(state.navLayers, NavigationTarget::pathLengthOrder);
0763
0764 ACTS_VERBOSE(state.navLayers.size()
0765 << " layer candidates found at path(s): " << state.navLayers);
0766
0767 if (state.navLayers.empty()) {
0768 ACTS_VERBOSE(volInfo(state) << "No layer candidates found.");
0769 }
0770 }
0771
0772 void Navigator::resolveBoundaries(State& state, const Vector3& position,
0773 const Vector3& direction) const {
0774 ACTS_VERBOSE(volInfo(state) << "Searching for compatible boundaries.");
0775
0776 NavigationOptions<Surface> navOpts;
0777 navOpts.startObject = state.currentSurface;
0778 navOpts.nearLimit = state.options.nearLimit;
0779 navOpts.farLimit = state.options.farLimit;
0780
0781 ACTS_VERBOSE(volInfo(state)
0782 << "Try to find boundaries, we are at: " << toString(position)
0783 << ", dir: " << toString(direction));
0784
0785
0786 state.navBoundaries = state.currentVolume->compatibleBoundaries(
0787 state.options.geoContext, position, direction, navOpts, logger());
0788 std::ranges::sort(state.navBoundaries, NavigationTarget::pathLengthOrder);
0789
0790
0791 ACTS_VERBOSE(state.navBoundaries.size()
0792 << " boundary candidates found at path(s): "
0793 << state.navBoundaries);
0794
0795 if (state.navBoundaries.empty()) {
0796 ACTS_VERBOSE(volInfo(state) << "No boundary candidates found.");
0797 }
0798 }
0799
0800 bool Navigator::inactive(const State& state) const {
0801
0802 if (!m_cfg.resolveSensitive && !m_cfg.resolveMaterial &&
0803 !m_cfg.resolvePassive) {
0804 return true;
0805 }
0806
0807 if (state.navigationBreak) {
0808 return true;
0809 }
0810
0811 return false;
0812 }
0813
0814 std::string Navigator::volInfo(const State& state) const {
0815 if (state.currentVolume == nullptr) {
0816 return "No Volume | ";
0817 }
0818 std::stringstream sstr{};
0819 sstr << state.currentVolume->volumeName() << " ("
0820 << state.currentVolume->geometryId() << ") | ";
0821 return sstr.str();
0822 }
0823
0824 }