File indexing completed on 2026-04-19 07:47:24
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Definitions/Algebra.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/NavigatorError.hpp"
0017 #include "Acts/Propagator/NavigatorOptions.hpp"
0018 #include "Acts/Propagator/NavigatorStatistics.hpp"
0019 #include "Acts/Propagator/detail/NavigationHelpers.hpp"
0020 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0021 #include "Acts/Surfaces/Surface.hpp"
0022 #include "Acts/Utilities/Enumerate.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 <cstdint>
0029 #include <limits>
0030 #include <memory>
0031 #include <vector>
0032
0033 namespace Acts::Experimental {
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048 class TryAllNavigator final {
0049 public:
0050
0051 struct Config final {
0052
0053 std::shared_ptr<const TrackingGeometry> trackingGeometry;
0054
0055
0056 bool resolveSensitive = true;
0057
0058 bool resolveMaterial = true;
0059
0060 bool resolvePassive = false;
0061
0062
0063 BoundaryTolerance boundaryToleranceSurfaceApproach =
0064 BoundaryTolerance::None();
0065 };
0066
0067
0068 struct Options final : public NavigatorPlainOptions {
0069
0070 explicit Options(const GeometryContext& gctx)
0071 : NavigatorPlainOptions(gctx) {}
0072
0073
0074 double surfaceTolerance = s_onSurfaceTolerance;
0075
0076
0077 double nearLimit = s_onSurfaceTolerance;
0078
0079
0080 double farLimit = std::numeric_limits<double>::max();
0081
0082
0083 void setPlainOptions(const NavigatorPlainOptions& options) {
0084 static_cast<NavigatorPlainOptions&>(*this) = options;
0085 }
0086 };
0087
0088
0089 struct State final {
0090
0091 explicit State(const Options& options_) : options(options_) {}
0092
0093
0094 Options options;
0095
0096
0097
0098
0099
0100 const Surface* startSurface = nullptr;
0101
0102
0103
0104
0105
0106 const Surface* targetSurface = nullptr;
0107
0108
0109
0110
0111 const Surface* currentSurface = nullptr;
0112
0113 const TrackingVolume* currentVolume = nullptr;
0114
0115
0116 std::vector<detail::NavigationObjectCandidate> navigationCandidates;
0117
0118
0119 bool navigationBreak = false;
0120
0121
0122 NavigatorStatistics statistics;
0123
0124
0125 std::vector<NavigationTarget> activeTargetsAhead;
0126
0127 std::vector<NavigationTarget> activeTargetsBehind;
0128
0129
0130 std::int32_t activeTargetBehindIndex = -1;
0131
0132
0133 std::optional<Vector3> lastPosition;
0134
0135
0136
0137
0138
0139 const std::vector<NavigationTarget>& currentTargets() const {
0140 if (hasTargetsBehind()) {
0141 return activeTargetsBehind;
0142 }
0143 return activeTargetsAhead;
0144 }
0145
0146
0147
0148 const NavigationTarget& activeTargetBehind() const {
0149 return activeTargetsBehind.at(activeTargetBehindIndex);
0150 }
0151
0152
0153
0154
0155 bool hasTargetsBehind() const {
0156 return !activeTargetsBehind.empty() &&
0157 activeTargetBehindIndex <
0158 static_cast<std::int32_t>(activeTargetsBehind.size());
0159 }
0160 };
0161
0162
0163
0164
0165
0166 explicit TryAllNavigator(Config cfg, std::unique_ptr<const Logger> logger =
0167 getDefaultLogger("TryAllNavigator",
0168 Logging::INFO))
0169 : m_cfg(std::move(cfg)), m_logger(std::move(logger)) {}
0170
0171
0172
0173
0174 State makeState(const Options& options) const {
0175 State state(options);
0176 return state;
0177 }
0178
0179
0180
0181
0182 const Surface* currentSurface(const State& state) const {
0183 return state.currentSurface;
0184 }
0185
0186
0187
0188
0189 const TrackingVolume* currentVolume(const State& state) const {
0190 return state.currentVolume;
0191 }
0192
0193
0194
0195
0196 const IVolumeMaterial* currentVolumeMaterial(const State& state) const {
0197 if (state.currentVolume == nullptr) {
0198 return nullptr;
0199 }
0200 return state.currentVolume->volumeMaterial();
0201 }
0202
0203
0204
0205
0206 const Surface* startSurface(const State& state) const {
0207 return state.startSurface;
0208 }
0209
0210
0211
0212
0213 const Surface* targetSurface(const State& state) const {
0214 return state.targetSurface;
0215 }
0216
0217
0218
0219
0220 bool endOfWorldReached(State& state) const {
0221 return state.currentVolume == nullptr;
0222 }
0223
0224
0225
0226
0227 bool navigationBreak(const State& state) const {
0228 return state.navigationBreak;
0229 }
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241 [[nodiscard]] Result<void> initialize(State& state, const Vector3& position,
0242 const Vector3& direction,
0243 Direction propagationDirection) const {
0244 static_cast<void>(propagationDirection);
0245
0246 ACTS_VERBOSE("initialize");
0247
0248 state.startSurface = state.options.startSurface;
0249 state.targetSurface = state.options.targetSurface;
0250
0251 const TrackingVolume* startVolume = nullptr;
0252
0253 if (state.startSurface != nullptr &&
0254 state.startSurface->associatedLayer() != nullptr) {
0255 ACTS_VERBOSE(
0256 "Fast start initialization through association from Surface.");
0257 const auto* startLayer = state.startSurface->associatedLayer();
0258 startVolume = startLayer->trackingVolume();
0259 } else {
0260 ACTS_VERBOSE("Slow start initialization through search.");
0261 ACTS_VERBOSE("Starting from position " << toString(position)
0262 << " and direction "
0263 << toString(direction));
0264 startVolume = m_cfg.trackingGeometry->lowestTrackingVolume(
0265 state.options.geoContext, position);
0266 }
0267
0268
0269 {
0270 state.currentVolume = startVolume;
0271 if (state.currentVolume != nullptr) {
0272 ACTS_VERBOSE(volInfo(state) << "Start volume resolved.");
0273 } else {
0274 ACTS_DEBUG("Start volume not resolved.");
0275 state.navigationBreak = true;
0276 return NavigatorError::NoStartVolume;
0277 }
0278
0279 state.currentSurface = state.startSurface;
0280 if (state.currentSurface != nullptr) {
0281 ACTS_VERBOSE(volInfo(state) << "Current surface set to start surface "
0282 << state.currentSurface->geometryId());
0283 } else {
0284 ACTS_VERBOSE(volInfo(state) << "No start surface set.");
0285 }
0286 }
0287
0288
0289 reinitializeCandidates(state);
0290
0291 state.lastPosition.reset();
0292
0293 return Result<void>::success();
0294 }
0295
0296
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307 NavigationTarget nextTarget(State& state, const Vector3& position,
0308 const Vector3& direction) const {
0309
0310 state.currentSurface = nullptr;
0311
0312
0313 if (state.navigationBreak) {
0314 return NavigationTarget::None();
0315 }
0316
0317 ACTS_VERBOSE(volInfo(state) << "nextTarget");
0318
0319 if (state.lastPosition.has_value() && !state.hasTargetsBehind()) {
0320 ACTS_VERBOSE(volInfo(state) << "Evaluate blind step");
0321
0322 const Vector3 stepStart = state.lastPosition.value();
0323 state.lastPosition.reset();
0324 const Vector3 stepEnd = position;
0325 const Vector3 step = stepEnd - stepStart;
0326 const double stepDistance = step.norm();
0327
0328 ACTS_VERBOSE("- from: " << stepStart.transpose());
0329 ACTS_VERBOSE("- to: " << stepEnd.transpose());
0330 ACTS_VERBOSE("- distance: " << stepDistance);
0331
0332 if (stepDistance < std::numeric_limits<double>::epsilon()) {
0333 ACTS_DEBUG(volInfo(state) << "Step distance is zero: " << stepDistance
0334 << ". Retry to resolve the next target.");
0335 return nextTarget(state, position, direction);
0336 }
0337
0338 const Vector3 stepDirection = step.normalized();
0339
0340 const double nearLimit = -stepDistance + state.options.surfaceTolerance;
0341 const double farLimit = 0;
0342
0343 state.activeTargetsBehind =
0344 resolveTargets(state, stepEnd, stepDirection, nearLimit, farLimit);
0345 state.activeTargetBehindIndex = -1;
0346
0347 ACTS_VERBOSE(volInfo(state)
0348 << "Found " << state.activeTargetsBehind.size()
0349 << " intersections behind");
0350
0351 for (const auto& target : state.activeTargetsBehind) {
0352 ACTS_VERBOSE("Found target behind " << target.surface().geometryId());
0353 }
0354 }
0355
0356
0357 ++state.activeTargetBehindIndex;
0358 if (state.hasTargetsBehind()) {
0359 ACTS_VERBOSE(volInfo(state) << "Handle active candidates behind");
0360
0361 ACTS_VERBOSE(volInfo(state)
0362 << (state.activeTargetsBehind.size() -
0363 state.activeTargetBehindIndex)
0364 << " out of " << state.activeTargetsBehind.size()
0365 << " surfaces remain to try.");
0366
0367 const NavigationTarget& nextTarget = state.activeTargetBehind();
0368
0369 ACTS_VERBOSE(volInfo(state) << "Next target behind selected: "
0370 << nextTarget.surface().geometryId());
0371
0372 return nextTarget;
0373 }
0374
0375
0376
0377 state.lastPosition = position;
0378 state.activeTargetsBehind.clear();
0379 state.activeTargetBehindIndex = -1;
0380
0381 ACTS_VERBOSE(volInfo(state)
0382 << "No targets behind, try to find targets ahead");
0383
0384 const double nearLimit = state.options.nearLimit;
0385 const double farLimit = state.options.farLimit;
0386
0387 state.activeTargetsAhead =
0388 resolveTargets(state, position, direction, nearLimit, farLimit);
0389
0390 NavigationTarget nextTarget = NavigationTarget::None();
0391
0392 for (const auto& target : state.activeTargetsAhead) {
0393 const Intersection3D& intersection = target.intersection();
0394
0395 if (intersection.status() == IntersectionStatus::onSurface) {
0396 ACTS_ERROR(volInfo(state)
0397 << "We are on surface " << target.surface().geometryId()
0398 << " before trying to reach it. This should not happen. "
0399 "Good luck.");
0400 continue;
0401 }
0402
0403 if (intersection.status() == IntersectionStatus::reachable) {
0404 nextTarget = target;
0405 break;
0406 }
0407 }
0408
0409 if (nextTarget.isNone()) {
0410 ACTS_VERBOSE(volInfo(state)
0411 << "No target ahead found. Step blindly forward.");
0412 } else {
0413 ACTS_VERBOSE(volInfo(state) << "Next target ahead selected: "
0414 << nextTarget.surface().geometryId());
0415 }
0416
0417 return nextTarget;
0418 }
0419
0420
0421
0422
0423
0424
0425
0426
0427
0428
0429
0430
0431
0432
0433 bool checkTargetValid(const State& state, const Vector3& position,
0434 const Vector3& direction) const {
0435 static_cast<void>(state);
0436 static_cast<void>(position);
0437 static_cast<void>(direction);
0438
0439 return false;
0440 }
0441
0442
0443
0444
0445
0446
0447
0448
0449
0450 void handleSurfaceReached(State& state, const Vector3& position,
0451 const Vector3& direction,
0452 const Surface& ) const {
0453
0454 if (state.navigationBreak) {
0455 return;
0456 }
0457
0458 ACTS_VERBOSE(volInfo(state) << "handleSurfaceReached");
0459
0460 const std::vector<NavigationTarget>& currentTargets =
0461 state.currentTargets();
0462
0463 if (currentTargets.empty()) {
0464 ACTS_VERBOSE(volInfo(state) << "No current target set.");
0465 return;
0466 }
0467
0468 assert(state.currentSurface == nullptr && "Current surface must be reset.");
0469
0470
0471
0472 std::vector<NavigationTarget> hitTargets;
0473
0474 for (const auto& target : currentTargets) {
0475 const std::uint8_t index = target.intersectionIndex();
0476 const Surface& surface = target.surface();
0477 const BoundaryTolerance boundaryTolerance = BoundaryTolerance::None();
0478
0479 const Intersection3D intersection =
0480 surface
0481 .intersect(state.options.geoContext, position, direction,
0482 boundaryTolerance, state.options.surfaceTolerance)
0483 .at(index);
0484
0485 if (intersection.status() == IntersectionStatus::onSurface) {
0486 hitTargets.emplace_back(target);
0487 }
0488 }
0489
0490 ACTS_VERBOSE(volInfo(state)
0491 << "Found " << hitTargets.size()
0492 << " intersections on surface with bounds check.");
0493
0494
0495 state.lastPosition.reset();
0496 state.activeTargetsAhead.clear();
0497 state.activeTargetsBehind.clear();
0498 state.activeTargetBehindIndex = -1;
0499
0500 if (hitTargets.empty()) {
0501 ACTS_VERBOSE(volInfo(state) << "No hit targets found.");
0502 return;
0503 }
0504
0505 if (hitTargets.size() > 1) {
0506 ACTS_VERBOSE(volInfo(state)
0507 << "Only using first intersection within bounds.");
0508 }
0509
0510
0511 const NavigationTarget& target = hitTargets.front();
0512 const Surface& surface = target.surface();
0513
0514 ACTS_VERBOSE(volInfo(state) << "Surface " << surface.geometryId()
0515 << " successfully hit, storing it.");
0516 state.currentSurface = &surface;
0517
0518 if (target.isSurfaceTarget()) {
0519 ACTS_VERBOSE(volInfo(state) << "This is a surface");
0520 } else if (target.isLayerTarget()) {
0521 ACTS_VERBOSE(volInfo(state) << "This is a layer");
0522 } else if (target.isPortalTarget()) {
0523 ACTS_VERBOSE(volInfo(state)
0524 << "This is a boundary. Reinitialize navigation");
0525
0526 const BoundarySurface& boundary = target.boundarySurface();
0527
0528 state.currentVolume = boundary.attachedVolume(state.options.geoContext,
0529 position, direction);
0530
0531 ACTS_VERBOSE(volInfo(state) << "Switched volume");
0532
0533 reinitializeCandidates(state);
0534 } else {
0535 ACTS_ERROR(volInfo(state) << "Unknown intersection type");
0536 }
0537 }
0538
0539 private:
0540
0541 Config m_cfg;
0542
0543
0544 std::unique_ptr<const Logger> m_logger;
0545
0546
0547
0548 const Logger& logger() const { return *m_logger; }
0549
0550
0551 void reinitializeCandidates(State& state) const {
0552 state.navigationCandidates.clear();
0553 state.activeTargetsAhead.clear();
0554 state.activeTargetsBehind.clear();
0555 state.activeTargetBehindIndex = -1;
0556
0557 initializeVolumeCandidates(state);
0558 }
0559
0560
0561
0562 void initializeVolumeCandidates(State& state) const {
0563 const TrackingVolume* volume = state.currentVolume;
0564 ACTS_VERBOSE(volInfo(state) << "Initialize volume");
0565
0566 if (volume == nullptr) {
0567 state.navigationBreak = true;
0568 ACTS_VERBOSE(volInfo(state) << "No volume set. Good luck.");
0569 return;
0570 }
0571
0572 emplaceAllVolumeCandidates(
0573 state.navigationCandidates, *volume, m_cfg.resolveSensitive,
0574 m_cfg.resolveMaterial, m_cfg.resolvePassive,
0575 m_cfg.boundaryToleranceSurfaceApproach, logger());
0576 }
0577
0578 std::vector<NavigationTarget> resolveTargets(State& state,
0579 const Vector3& position,
0580 const Vector3& direction,
0581 double nearLimit,
0582 double farLimit) const {
0583 std::vector<NavigationTarget> targets;
0584
0585
0586 for (const auto& candidate : state.navigationCandidates) {
0587 auto intersections =
0588 candidate.intersect(state.options.geoContext, position, direction,
0589 state.options.surfaceTolerance);
0590 for (auto [intersectionIndex, intersection] :
0591 Acts::enumerate(intersections)) {
0592
0593 if (!intersection.isValid() ||
0594 !detail::checkPathLength(intersection.pathLength(), nearLimit,
0595 farLimit)) {
0596 continue;
0597 }
0598
0599 targets.emplace_back(candidate.target(intersection, intersectionIndex));
0600 }
0601 }
0602
0603 std::ranges::sort(targets, NavigationTarget::pathLengthOrder);
0604
0605 return targets;
0606 }
0607
0608
0609
0610
0611 std::string volInfo(const State& state) const {
0612 return (state.currentVolume != nullptr ? state.currentVolume->volumeName()
0613 : "No Volume") +
0614 " | ";
0615 }
0616 };
0617
0618 }