File indexing completed on 2025-09-18 08:33:14
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
0165 if (state.surfaceCandidateIndex == state.surfaceCandidates.size()) {
0166 ACTS_VERBOSE(volInfo(state)
0167 << posInfo(state, position) << "no surface candidates");
0168 return NavigationTarget::None();
0169 }
0170
0171
0172 ACTS_VERBOSE(volInfo(state) << posInfo(state, position)
0173 << (state.surfaceCandidates.size() -
0174 state.surfaceCandidateIndex)
0175 << " out of " << state.surfaceCandidates.size()
0176 << " surfaces remain to try.");
0177
0178 const auto& candidate = state.surfaceCandidate();
0179 const auto& surface = (candidate.surface != nullptr)
0180 ? (*candidate.surface)
0181 : (candidate.portal->surface());
0182
0183 ACTS_VERBOSE(volInfo(state)
0184 << posInfo(state, position)
0185 << "next surface candidate will be " << surface.geometryId()
0186 << " (" << surface.center(state.options.geoContext).transpose()
0187 << ")");
0188
0189 state.currentSurface = nullptr;
0190 state.currentPortal = nullptr;
0191
0192 return NavigationTarget(surface, candidate.objectIntersection.index(),
0193 candidate.boundaryTolerance);
0194 }
0195
0196 bool checkTargetValid(const State& state, const Vector3& position,
0197 const Vector3& direction) const {
0198 (void)state;
0199 (void)position;
0200 (void)direction;
0201
0202 return true;
0203 }
0204
0205 void handleSurfaceReached(State& state, const Vector3& position,
0206 const Vector3& direction,
0207 const Surface& surface) const {
0208 (void)surface;
0209
0210 ACTS_VERBOSE(volInfo(state) << posInfo(state, position)
0211 << "Entering navigator::handleSurfaceReached.");
0212
0213 fillNavigationState(position, direction, state);
0214
0215 if (inactive()) {
0216 ACTS_VERBOSE(volInfo(state)
0217 << posInfo(state, position) << "navigator inactive");
0218 return;
0219 }
0220
0221 if (state.surfaceCandidateIndex == state.surfaceCandidates.size()) {
0222 ACTS_VERBOSE(volInfo(state)
0223 << posInfo(state, position)
0224 << "no surface candidates - waiting for target call");
0225 return;
0226 }
0227
0228 const Portal* nextPortal = nullptr;
0229 const Surface* nextSurface = nullptr;
0230 bool isPortal = false;
0231
0232 if (state.surfaceCandidate().surface != nullptr) {
0233 nextSurface = state.surfaceCandidate().surface;
0234 } else if (state.surfaceCandidate().portal != nullptr) {
0235 nextPortal = state.surfaceCandidate().portal;
0236 nextSurface = &nextPortal->surface();
0237 isPortal = true;
0238 } else {
0239 std::string msg = "DetectorNavigator: " + volInfo(state) +
0240 posInfo(state, position) +
0241 "panic: not a surface not a portal - what is it?";
0242 throw std::runtime_error(msg);
0243 }
0244
0245 ACTS_VERBOSE(volInfo(state)
0246 << posInfo(state, position) << "landed on surface");
0247
0248 if (isPortal) {
0249 ACTS_VERBOSE(volInfo(state)
0250 << posInfo(state, position)
0251 << "this is a portal, updating to new volume.");
0252 state.currentPortal = nextPortal;
0253 state.currentSurface = &nextPortal->surface();
0254 state.surfaceCandidates.clear();
0255 state.surfaceCandidateIndex = 0;
0256
0257 state.currentPortal->updateDetectorVolume(state.options.geoContext,
0258 state);
0259
0260
0261 if (state.currentVolume == nullptr) {
0262 ACTS_VERBOSE(volInfo(state)
0263 << posInfo(state, position)
0264 << "no volume after Portal update, end of world.");
0265 state.navigationBreak = true;
0266 return;
0267 }
0268
0269
0270
0271 updateCandidateSurfaces(state, position);
0272
0273 ACTS_VERBOSE(volInfo(state)
0274 << posInfo(state, position) << "current portal set to "
0275 << state.currentPortal->surface().geometryId());
0276 } else {
0277 ACTS_VERBOSE(volInfo(state) << posInfo(state, position)
0278 << "this is a surface, storing it.");
0279
0280
0281
0282 state.currentSurface = nextSurface;
0283 ACTS_VERBOSE(volInfo(state)
0284 << posInfo(state, position) << "current surface set to "
0285 << state.currentSurface->geometryId());
0286 ++state.surfaceCandidateIndex;
0287 }
0288 }
0289
0290 private:
0291 Config m_cfg;
0292
0293 std::shared_ptr<const Logger> m_logger;
0294
0295 std::string volInfo(const State& state) const {
0296 return (state.currentVolume != nullptr ? state.currentVolume->name()
0297 : "No Volume") +
0298 " | ";
0299 }
0300
0301 std::string posInfo(const State& , const Vector3& position) const {
0302 std::stringstream ss;
0303 ss << position.transpose();
0304 ss << " | ";
0305 return ss.str();
0306 }
0307
0308 const Logger& logger() const { return *m_logger; }
0309
0310
0311
0312
0313
0314 bool inactive() const {
0315 if (m_cfg.detector == nullptr) {
0316 return true;
0317 }
0318
0319 if (!m_cfg.resolveSensitive && !m_cfg.resolveMaterial &&
0320 !m_cfg.resolvePassive) {
0321 return true;
0322 }
0323
0324 return false;
0325 }
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339 void updateCandidateSurfaces(State& state, const Vector3& position) const {
0340 ACTS_VERBOSE(volInfo(state)
0341 << posInfo(state, position) << "initialize target");
0342
0343
0344 state.currentVolume->updateNavigationState(state.options.geoContext, state);
0345
0346
0347 auto& nCandidates = state.surfaceCandidates;
0348 std::ranges::sort(nCandidates, {}, [](const auto& c) {
0349 return c.objectIntersection.pathLength();
0350 });
0351
0352 state.surfaceCandidateIndex = 0;
0353 }
0354
0355 void fillNavigationState(const Vector3& position, const Vector3& direction,
0356 State& state) const {
0357 state.position = position;
0358 state.direction = direction;
0359 }
0360 };
0361
0362 }