Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-12 07:51:32

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
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     /// Detector for this Navigation
0037     const Detector* detector = nullptr;
0038 
0039     /// Configuration for this Navigator
0040     /// stop at every sensitive surface (whether it has material or not)
0041     bool resolveSensitive = true;
0042     /// stop at every material surface (whether it is passive or not)
0043     bool resolveMaterial = true;
0044     /// stop at every surface regardless what it is
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   /// Nested State struct
0058   ///
0059   /// It acts as an internal state which is
0060   /// created for every propagation/extrapolation step
0061   /// and keep thread-local navigation information
0062   struct State : public NavigationState {
0063     explicit State(const Options& options_) : options(options_) {}
0064 
0065     Options options;
0066 
0067     /// Navigation state - external state: the current surface
0068     const Surface* currentSurface = nullptr;
0069     /// Navigation state : a break has been detected
0070     bool navigationBreak = false;
0071 
0072     /// Navigation statistics
0073     NavigatorStatistics statistics;
0074   };
0075 
0076   /// Constructor with configuration object
0077   ///
0078   /// @param cfg The navigator configuration
0079   /// @param _logger a logger instance
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       // we run out of surfaces and we are in a portal - try to reinitialize the
0171       // navigation state
0172       if (state.currentPortal == nullptr) {
0173         updateCandidateSurfaces(state, position);
0174         state.surfaceCandidateIndex = 0;
0175       } else {
0176         return NavigationTarget::None();
0177       }
0178     }
0179 
0180     // Screen output how much is left to try
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     // Take the surface
0188     const auto& candidate = state.surfaceCandidate();
0189     const auto& surface = (candidate.surface != nullptr)
0190                               ? (*candidate.surface)
0191                               : (candidate.portal->surface());
0192     // Screen output which surface you are on
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       // If no Volume is found, we are at the end of the world
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       // Switched to a new volume
0280       // Update candidate surfaces
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       // If we are on the surface pointed at by the iterator, we can make
0291       // it the current one to pass it to the other actors
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& /*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   /// This checks if a navigation break had been triggered or navigator
0320   /// is misconfigured
0321   ///
0322   /// @return true if the navigator is inactive
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   /// @brief Navigation (re-)initialisation for the target
0337   ///
0338   /// @note This is only called a few times every propagation/extrapolation
0339   ///
0340   /// As a straight line estimate can lead you to the wrong destination
0341   /// Volume, this will be called at:
0342   /// - initialization
0343   /// - attempted volume switch
0344   /// Target finding by association will not be done again
0345   ///
0346   /// @param [in,out] state is the propagation state object
0347   /// @param [in] position is the current position
0348   void updateCandidateSurfaces(State& state, const Vector3& position) const {
0349     ACTS_VERBOSE(volInfo(state)
0350                  << posInfo(state, position) << "initialize target");
0351 
0352     // Here we get the candidate surfaces
0353     state.currentVolume->updateNavigationState(state.options.geoContext, state);
0354 
0355     // Sort properly the surface candidates
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 }  // namespace Acts::Experimental