Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 08:33:14

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 
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     // Screen output how much is left to try
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     // Take the surface
0178     const auto& candidate = state.surfaceCandidate();
0179     const auto& surface = (candidate.surface != nullptr)
0180                               ? (*candidate.surface)
0181                               : (candidate.portal->surface());
0182     // Screen output which surface you are on
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       // If no Volume is found, we are at the end of the world
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       // Switched to a new volume
0270       // Update candidate surfaces
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       // If we are on the surface pointed at by the iterator, we can make
0281       // it the current one to pass it to the other actors
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& /*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   /// This checks if a navigation break had been triggered or navigator
0311   /// is misconfigured
0312   ///
0313   /// @return true if the navigator is inactive
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   /// @brief Navigation (re-)initialisation for the target
0328   ///
0329   /// @note This is only called a few times every propagation/extrapolation
0330   ///
0331   /// As a straight line estimate can lead you to the wrong destination
0332   /// Volume, this will be called at:
0333   /// - initialization
0334   /// - attempted volume switch
0335   /// Target finding by association will not be done again
0336   ///
0337   /// @param [in,out] state is the propagation state object
0338   /// @param [in] position is the current position
0339   void updateCandidateSurfaces(State& state, const Vector3& position) const {
0340     ACTS_VERBOSE(volInfo(state)
0341                  << posInfo(state, position) << "initialize target");
0342 
0343     // Here we get the candidate surfaces
0344     state.currentVolume->updateNavigationState(state.options.geoContext, state);
0345 
0346     // Sort properly the surface candidates
0347     auto& nCandidates = state.surfaceCandidates;
0348     std::ranges::sort(nCandidates, {}, [](const auto& c) {
0349       return c.objectIntersection.pathLength();
0350     });
0351     // Set the surface candidate
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 }  // namespace Acts::Experimental