File indexing completed on 2025-07-12 07:51:32
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Detector/Detector.hpp"
0012 #include "Acts/Detector/DetectorVolume.hpp"
0013 #include "Acts/Detector/Portal.hpp"
0014 #include "Acts/Geometry/GeometryIdentifier.hpp"
0015 #include "Acts/Geometry/Layer.hpp"
0016 #include "Acts/Navigation/NavigationState.hpp"
0017 #include "Acts/Propagator/NavigationTarget.hpp"
0018 #include "Acts/Propagator/NavigatorOptions.hpp"
0019 #include "Acts/Propagator/NavigatorStatistics.hpp"
0020 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0021 #include "Acts/Surfaces/Surface.hpp"
0022 #include "Acts/Utilities/Logger.hpp"
0023
0024 #include <algorithm>
0025 #include <sstream>
0026 #include <string>
0027
0028 #include <boost/algorithm/string.hpp>
0029 #include <boost/container/small_vector.hpp>
0030
0031 namespace Acts::Experimental {
0032
0033 class DetectorNavigator {
0034 public:
0035 struct Config {
0036
0037 const Detector* detector = nullptr;
0038
0039
0040
0041 bool resolveSensitive = true;
0042
0043 bool resolveMaterial = true;
0044
0045 bool resolvePassive = false;
0046 };
0047
0048 struct Options : public NavigatorPlainOptions {
0049 explicit Options(const GeometryContext& gctx)
0050 : NavigatorPlainOptions(gctx) {}
0051
0052 void setPlainOptions(const NavigatorPlainOptions& options) {
0053 static_cast<NavigatorPlainOptions&>(*this) = options;
0054 }
0055 };
0056
0057
0058
0059
0060
0061
0062 struct State : public NavigationState {
0063 explicit State(const Options& options_) : options(options_) {}
0064
0065 Options options;
0066
0067
0068 const Surface* currentSurface = nullptr;
0069
0070 bool navigationBreak = false;
0071
0072
0073 NavigatorStatistics statistics;
0074 };
0075
0076
0077
0078
0079
0080 explicit DetectorNavigator(Config cfg,
0081 std::shared_ptr<const Logger> _logger =
0082 getDefaultLogger("DetectorNavigator",
0083 Logging::Level::INFO))
0084 : m_cfg{cfg}, m_logger{std::move(_logger)} {}
0085
0086 State makeState(const Options& options) const {
0087 State state(options);
0088 return state;
0089 }
0090
0091 const Surface* currentSurface(const State& state) const {
0092 return state.currentSurface;
0093 }
0094
0095 const DetectorVolume* currentVolume(const State& state) const {
0096 return state.currentVolume;
0097 }
0098
0099 const IVolumeMaterial* currentVolumeMaterial(const State& state) const {
0100 return state.currentVolume->volumeMaterial();
0101 }
0102
0103 const Surface* startSurface(const State& state) const {
0104 return state.options.startSurface;
0105 }
0106
0107 const Surface* targetSurface(const State& state) const {
0108 return state.options.targetSurface;
0109 }
0110
0111 bool endOfWorldReached(State& state) const {
0112 return state.currentVolume == nullptr;
0113 }
0114
0115 bool navigationBreak(const State& state) const {
0116 return state.navigationBreak;
0117 }
0118
0119 [[nodiscard]] Result<void> initialize(State& state, const Vector3& position,
0120 const Vector3& direction,
0121 Direction propagationDirection) const {
0122 (void)propagationDirection;
0123
0124 ACTS_VERBOSE(volInfo(state) << posInfo(state, position) << "initialize");
0125
0126 if (state.currentDetector == nullptr) {
0127 ACTS_VERBOSE("Assigning detector from the config.");
0128 state.currentDetector = m_cfg.detector;
0129 }
0130 if (state.currentDetector == nullptr) {
0131 throw std::invalid_argument("DetectorNavigator: no detector assigned");
0132 }
0133
0134 fillNavigationState(position, direction, state);
0135 if (state.currentVolume == nullptr) {
0136 state.currentVolume = state.currentDetector->findDetectorVolume(
0137 state.options.geoContext, state.position);
0138 }
0139 if (state.currentVolume == nullptr) {
0140 throw std::invalid_argument("DetectorNavigator: no current volume found");
0141 }
0142 updateCandidateSurfaces(state, position);
0143
0144 return Result<void>::success();
0145 }
0146
0147 NavigationTarget nextTarget(State& state, const Vector3& position,
0148 const Vector3& direction) const {
0149 ACTS_VERBOSE(volInfo(state)
0150 << posInfo(state, position) << "Entering navigator::preStep.");
0151
0152 if (inactive()) {
0153 ACTS_VERBOSE(volInfo(state)
0154 << posInfo(state, position) << "navigator inactive");
0155 return NavigationTarget::None();
0156 }
0157
0158 fillNavigationState(position, direction, state);
0159
0160 if (state.currentSurface != nullptr) {
0161 ACTS_VERBOSE(volInfo(state)
0162 << posInfo(state, position) << "stepping through surface");
0163 }
0164 ++state.surfaceCandidateIndex;
0165
0166 if (state.surfaceCandidateIndex ==
0167 static_cast<int>(state.surfaceCandidates.size())) {
0168 ACTS_VERBOSE(volInfo(state)
0169 << posInfo(state, position) << "no surface candidates");
0170
0171
0172 if (state.currentPortal == nullptr) {
0173 updateCandidateSurfaces(state, position);
0174 state.surfaceCandidateIndex = 0;
0175 } else {
0176 return NavigationTarget::None();
0177 }
0178 }
0179
0180
0181 ACTS_VERBOSE(volInfo(state) << posInfo(state, position)
0182 << (state.surfaceCandidates.size() -
0183 state.surfaceCandidateIndex)
0184 << " out of " << state.surfaceCandidates.size()
0185 << " surfaces remain to try.");
0186
0187
0188 const auto& candidate = state.surfaceCandidate();
0189 const auto& surface = (candidate.surface != nullptr)
0190 ? (*candidate.surface)
0191 : (candidate.portal->surface());
0192
0193 ACTS_VERBOSE(volInfo(state)
0194 << posInfo(state, position)
0195 << "next surface candidate will be " << surface.geometryId()
0196 << " (" << surface.center(state.options.geoContext).transpose()
0197 << ")");
0198
0199 state.currentSurface = nullptr;
0200 state.currentPortal = nullptr;
0201 return NavigationTarget(surface, candidate.objectIntersection.index(),
0202 candidate.boundaryTolerance);
0203 }
0204
0205 bool checkTargetValid(const State& state, const Vector3& position,
0206 const Vector3& direction) const {
0207 (void)state;
0208 (void)position;
0209 (void)direction;
0210
0211 return true;
0212 }
0213
0214 void handleSurfaceReached(State& state, const Vector3& position,
0215 const Vector3& direction,
0216 const Surface& surface) const {
0217 (void)surface;
0218
0219 ACTS_VERBOSE(volInfo(state) << posInfo(state, position)
0220 << "Entering navigator::handleSurfaceReached.");
0221
0222 fillNavigationState(position, direction, state);
0223
0224 if (inactive()) {
0225 ACTS_VERBOSE(volInfo(state)
0226 << posInfo(state, position) << "navigator inactive");
0227 return;
0228 }
0229
0230 if (state.surfaceCandidateIndex ==
0231 static_cast<int>(state.surfaceCandidates.size())) {
0232 ACTS_VERBOSE(volInfo(state)
0233 << posInfo(state, position)
0234 << "no surface candidates - waiting for target call");
0235 return;
0236 }
0237
0238 const Portal* nextPortal = nullptr;
0239 const Surface* nextSurface = nullptr;
0240 bool isPortal = false;
0241
0242 if (state.surfaceCandidate().surface != nullptr) {
0243 nextSurface = state.surfaceCandidate().surface;
0244 } else if (state.surfaceCandidate().portal != nullptr) {
0245 nextPortal = state.surfaceCandidate().portal;
0246 nextSurface = &nextPortal->surface();
0247 isPortal = true;
0248 } else {
0249 std::string msg = "DetectorNavigator: " + volInfo(state) +
0250 posInfo(state, position) +
0251 "panic: not a surface not a portal - what is it?";
0252 throw std::runtime_error(msg);
0253 }
0254
0255 ACTS_VERBOSE(volInfo(state)
0256 << posInfo(state, position) << "landed on surface");
0257
0258 if (isPortal) {
0259 ACTS_VERBOSE(volInfo(state)
0260 << posInfo(state, position)
0261 << "this is a portal, updating to new volume.");
0262 state.currentPortal = nextPortal;
0263 state.currentSurface = &nextPortal->surface();
0264 state.surfaceCandidates.clear();
0265 state.surfaceCandidateIndex = -1;
0266
0267 state.currentPortal->updateDetectorVolume(state.options.geoContext,
0268 state);
0269
0270
0271 if (state.currentVolume == nullptr) {
0272 ACTS_VERBOSE(volInfo(state)
0273 << posInfo(state, position)
0274 << "no volume after Portal update, end of world.");
0275 state.navigationBreak = true;
0276 return;
0277 }
0278
0279
0280
0281 updateCandidateSurfaces(state, position);
0282
0283 ACTS_VERBOSE(volInfo(state)
0284 << posInfo(state, position) << "current portal set to "
0285 << state.currentPortal->surface().geometryId());
0286 } else {
0287 ACTS_VERBOSE(volInfo(state) << posInfo(state, position)
0288 << "this is a surface, storing it.");
0289
0290
0291
0292 state.currentSurface = nextSurface;
0293 ACTS_VERBOSE(volInfo(state)
0294 << posInfo(state, position) << "current surface set to "
0295 << state.currentSurface->geometryId());
0296 }
0297 }
0298
0299 private:
0300 Config m_cfg;
0301
0302 std::shared_ptr<const Logger> m_logger;
0303
0304 std::string volInfo(const State& state) const {
0305 return (state.currentVolume != nullptr ? state.currentVolume->name()
0306 : "No Volume") +
0307 " | ";
0308 }
0309
0310 std::string posInfo(const State& , const Vector3& position) const {
0311 std::stringstream ss;
0312 ss << position.transpose();
0313 ss << " | ";
0314 return ss.str();
0315 }
0316
0317 const Logger& logger() const { return *m_logger; }
0318
0319
0320
0321
0322
0323 bool inactive() const {
0324 if (m_cfg.detector == nullptr) {
0325 return true;
0326 }
0327
0328 if (!m_cfg.resolveSensitive && !m_cfg.resolveMaterial &&
0329 !m_cfg.resolvePassive) {
0330 return true;
0331 }
0332
0333 return false;
0334 }
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348 void updateCandidateSurfaces(State& state, const Vector3& position) const {
0349 ACTS_VERBOSE(volInfo(state)
0350 << posInfo(state, position) << "initialize target");
0351
0352
0353 state.currentVolume->updateNavigationState(state.options.geoContext, state);
0354
0355
0356 auto& nCandidates = state.surfaceCandidates;
0357 std::ranges::sort(nCandidates, {}, [](const auto& c) {
0358 return c.objectIntersection.pathLength();
0359 });
0360 state.surfaceCandidateIndex = -1;
0361 }
0362
0363 void fillNavigationState(const Vector3& position, const Vector3& direction,
0364 State& state) const {
0365 state.position = position;
0366 state.direction = direction;
0367 }
0368 };
0369
0370 }