Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-11-03 08:57:03

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/Surface.hpp"
0021 #include "Acts/Utilities/Logger.hpp"
0022 
0023 #include <algorithm>
0024 #include <sstream>
0025 #include <string>
0026 
0027 #include <boost/algorithm/string.hpp>
0028 #include <boost/container/small_vector.hpp>
0029 
0030 namespace Acts::Experimental {
0031 
0032 class DetectorNavigator {
0033  public:
0034   struct Config {
0035     /// Detector for this Navigation
0036     const Detector* detector = nullptr;
0037 
0038     /// Configuration for this Navigator
0039     /// stop at every sensitive surface (whether it has material or not)
0040     bool resolveSensitive = true;
0041     /// stop at every material surface (whether it is passive or not)
0042     bool resolveMaterial = true;
0043     /// stop at every surface regardless what it is
0044     bool resolvePassive = false;
0045   };
0046 
0047   struct Options : public NavigatorPlainOptions {
0048     explicit Options(const GeometryContext& gctx)
0049         : NavigatorPlainOptions(gctx) {}
0050 
0051     void setPlainOptions(const NavigatorPlainOptions& options) {
0052       static_cast<NavigatorPlainOptions&>(*this) = options;
0053     }
0054   };
0055 
0056   /// Nested State struct
0057   ///
0058   /// It acts as an internal state which is
0059   /// created for every propagation/extrapolation step
0060   /// and keep thread-local navigation information
0061   struct State : public NavigationState {
0062     /// Construct navigation state with configuration options.
0063     /// @param options_ Configuration options for the detector navigator
0064     explicit State(const Options& options_) : options(options_) {}
0065 
0066     /// Configuration options for the detector navigator
0067     Options options;
0068 
0069     /// Navigation state - external state: the current surface
0070     const Surface* currentSurface = nullptr;
0071     /// Navigation state : a break has been detected
0072     bool navigationBreak = false;
0073 
0074     /// Navigation statistics
0075     NavigatorStatistics statistics;
0076   };
0077 
0078   /// Constructor with configuration object
0079   ///
0080   /// @param cfg The navigator configuration
0081   /// @param _logger a logger instance
0082   explicit DetectorNavigator(Config cfg,
0083                              std::shared_ptr<const Logger> _logger =
0084                                  getDefaultLogger("DetectorNavigator",
0085                                                   Logging::Level::INFO))
0086       : m_cfg{cfg}, m_logger{std::move(_logger)} {}
0087 
0088   State makeState(const Options& options) const {
0089     State state(options);
0090     return state;
0091   }
0092 
0093   const Surface* currentSurface(const State& state) const {
0094     return state.currentSurface;
0095   }
0096 
0097   const DetectorVolume* currentVolume(const State& state) const {
0098     return state.currentVolume;
0099   }
0100 
0101   const IVolumeMaterial* currentVolumeMaterial(const State& state) const {
0102     return state.currentVolume->volumeMaterial();
0103   }
0104 
0105   const Surface* startSurface(const State& state) const {
0106     return state.options.startSurface;
0107   }
0108 
0109   const Surface* targetSurface(const State& state) const {
0110     return state.options.targetSurface;
0111   }
0112 
0113   bool endOfWorldReached(State& state) const {
0114     return state.currentVolume == nullptr;
0115   }
0116 
0117   bool navigationBreak(const State& state) const {
0118     return state.navigationBreak;
0119   }
0120 
0121   [[nodiscard]] Result<void> initialize(State& state, const Vector3& position,
0122                                         const Vector3& direction,
0123                                         Direction propagationDirection) const {
0124     (void)propagationDirection;
0125 
0126     ACTS_VERBOSE(volInfo(state) << posInfo(state, position) << "initialize");
0127 
0128     if (state.currentDetector == nullptr) {
0129       ACTS_VERBOSE("Assigning detector from the config.");
0130       state.currentDetector = m_cfg.detector;
0131     }
0132     if (state.currentDetector == nullptr) {
0133       throw std::invalid_argument("DetectorNavigator: no detector assigned");
0134     }
0135 
0136     fillNavigationState(position, direction, state);
0137     if (state.currentVolume == nullptr) {
0138       state.currentVolume = state.currentDetector->findDetectorVolume(
0139           state.options.geoContext, state.position);
0140     }
0141     if (state.currentVolume == nullptr) {
0142       throw std::invalid_argument("DetectorNavigator: no current volume found");
0143     }
0144     updateCandidateSurfaces(state, position);
0145 
0146     return Result<void>::success();
0147   }
0148 
0149   NavigationTarget nextTarget(State& state, const Vector3& position,
0150                               const Vector3& direction) const {
0151     ACTS_VERBOSE(volInfo(state)
0152                  << posInfo(state, position) << "Entering navigator::preStep.");
0153 
0154     if (inactive()) {
0155       ACTS_VERBOSE(volInfo(state)
0156                    << posInfo(state, position) << "navigator inactive");
0157       return NavigationTarget::None();
0158     }
0159 
0160     fillNavigationState(position, direction, state);
0161 
0162     if (state.currentSurface != nullptr) {
0163       ACTS_VERBOSE(volInfo(state)
0164                    << posInfo(state, position) << "stepping through surface");
0165     }
0166     ++state.surfaceCandidateIndex;
0167 
0168     if (state.surfaceCandidateIndex ==
0169         static_cast<int>(state.surfaceCandidates.size())) {
0170       ACTS_VERBOSE(volInfo(state)
0171                    << posInfo(state, position) << "no surface candidates");
0172       // we run out of surfaces and we are in a portal - try to reinitialize the
0173       // navigation state
0174       if (state.currentPortal == nullptr) {
0175         updateCandidateSurfaces(state, position);
0176         state.surfaceCandidateIndex = 0;
0177       } else {
0178         return NavigationTarget::None();
0179       }
0180     }
0181 
0182     // Screen output how much is left to try
0183     ACTS_VERBOSE(volInfo(state) << posInfo(state, position)
0184                                 << (state.surfaceCandidates.size() -
0185                                     state.surfaceCandidateIndex)
0186                                 << " out of " << state.surfaceCandidates.size()
0187                                 << " surfaces remain to try.");
0188 
0189     // Take the surface
0190     const auto& candidate = state.surfaceCandidate();
0191     const auto& surface = candidate.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 candidate;
0202   }
0203 
0204   bool checkTargetValid(const State& state, const Vector3& position,
0205                         const Vector3& direction) const {
0206     (void)state;
0207     (void)position;
0208     (void)direction;
0209 
0210     return true;
0211   }
0212 
0213   void handleSurfaceReached(State& state, const Vector3& position,
0214                             const Vector3& direction,
0215                             const Surface& surface) const {
0216     (void)surface;
0217 
0218     ACTS_VERBOSE(volInfo(state) << posInfo(state, position)
0219                                 << "Entering navigator::handleSurfaceReached.");
0220 
0221     fillNavigationState(position, direction, state);
0222 
0223     if (inactive()) {
0224       ACTS_VERBOSE(volInfo(state)
0225                    << posInfo(state, position) << "navigator inactive");
0226       return;
0227     }
0228 
0229     if (state.surfaceCandidateIndex ==
0230         static_cast<int>(state.surfaceCandidates.size())) {
0231       ACTS_VERBOSE(volInfo(state)
0232                    << posInfo(state, position)
0233                    << "no surface candidates - waiting for target call");
0234       return;
0235     }
0236 
0237     const Portal* nextPortal = nullptr;
0238     const Surface* nextSurface = nullptr;
0239     bool isPortal = false;
0240 
0241     nextSurface = &state.surfaceCandidate().surface();
0242     if (state.surfaceCandidate().isPortalTarget()) {
0243       nextPortal = &state.surfaceCandidate().gen2Portal();
0244       nextSurface = &nextPortal->surface();
0245       isPortal = true;
0246     }
0247 
0248     ACTS_VERBOSE(volInfo(state)
0249                  << posInfo(state, position) << "landed on surface");
0250 
0251     if (isPortal) {
0252       ACTS_VERBOSE(volInfo(state)
0253                    << posInfo(state, position)
0254                    << "this is a portal, updating to new volume.");
0255       state.currentPortal = nextPortal;
0256       state.currentSurface = &nextPortal->surface();
0257       state.surfaceCandidates.clear();
0258       state.surfaceCandidateIndex = -1;
0259 
0260       state.currentPortal->updateDetectorVolume(state.options.geoContext,
0261                                                 state);
0262 
0263       // If no Volume is found, we are at the end of the world
0264       if (state.currentVolume == nullptr) {
0265         ACTS_VERBOSE(volInfo(state)
0266                      << posInfo(state, position)
0267                      << "no volume after Portal update, end of world.");
0268         state.navigationBreak = true;
0269         return;
0270       }
0271 
0272       // Switched to a new volume
0273       // Update candidate surfaces
0274       updateCandidateSurfaces(state, position);
0275 
0276       ACTS_VERBOSE(volInfo(state)
0277                    << posInfo(state, position) << "current portal set to "
0278                    << state.currentPortal->surface().geometryId());
0279     } else {
0280       ACTS_VERBOSE(volInfo(state) << posInfo(state, position)
0281                                   << "this is a surface, storing it.");
0282 
0283       // If we are on the surface pointed at by the iterator, we can make
0284       // it the current one to pass it to the other actors
0285       state.currentSurface = nextSurface;
0286       ACTS_VERBOSE(volInfo(state)
0287                    << posInfo(state, position) << "current surface set to "
0288                    << state.currentSurface->geometryId());
0289     }
0290   }
0291 
0292  private:
0293   Config m_cfg;
0294 
0295   std::shared_ptr<const Logger> m_logger;
0296 
0297   std::string volInfo(const State& state) const {
0298     return (state.currentVolume != nullptr ? state.currentVolume->name()
0299                                            : "No Volume") +
0300            " | ";
0301   }
0302 
0303   std::string posInfo(const State& /*state*/, const Vector3& position) const {
0304     std::stringstream ss;
0305     ss << position.transpose();
0306     ss << " | ";
0307     return ss.str();
0308   }
0309 
0310   const Logger& logger() const { return *m_logger; }
0311 
0312   /// This checks if a navigation break had been triggered or navigator
0313   /// is misconfigured
0314   ///
0315   /// @return true if the navigator is inactive
0316   bool inactive() const {
0317     if (m_cfg.detector == nullptr) {
0318       return true;
0319     }
0320 
0321     if (!m_cfg.resolveSensitive && !m_cfg.resolveMaterial &&
0322         !m_cfg.resolvePassive) {
0323       return true;
0324     }
0325 
0326     return false;
0327   }
0328 
0329   /// @brief Navigation (re-)initialisation for the target
0330   ///
0331   /// @note This is only called a few times every propagation/extrapolation
0332   ///
0333   /// As a straight line estimate can lead you to the wrong destination
0334   /// Volume, this will be called at:
0335   /// - initialization
0336   /// - attempted volume switch
0337   /// Target finding by association will not be done again
0338   ///
0339   /// @param [in,out] state is the propagation state object
0340   /// @param [in] position is the current position
0341   void updateCandidateSurfaces(State& state, const Vector3& position) const {
0342     ACTS_VERBOSE(volInfo(state)
0343                  << posInfo(state, position) << "initialize target");
0344 
0345     // Here we get the candidate surfaces
0346     state.currentVolume->updateNavigationState(state.options.geoContext, state);
0347 
0348     // Sort properly the surface candidates
0349     auto& nCandidates = state.surfaceCandidates;
0350     std::ranges::sort(nCandidates, NavigationTarget::pathLengthOrder);
0351     state.surfaceCandidateIndex = -1;
0352   }
0353 
0354   void fillNavigationState(const Vector3& position, const Vector3& direction,
0355                            State& state) const {
0356     state.position = position;
0357     state.direction = direction;
0358   }
0359 };
0360 
0361 }  // namespace Acts::Experimental