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