Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-23 08:20:09

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/MagneticField/MagneticFieldProvider.hpp"
0012 #include "Acts/Utilities/AnnealingUtility.hpp"
0013 #include "Acts/Utilities/Logger.hpp"
0014 #include "Acts/Utilities/Result.hpp"
0015 #include "Acts/Vertexing/AMVFInfo.hpp"
0016 #include "Acts/Vertexing/ImpactPointEstimator.hpp"
0017 #include "Acts/Vertexing/TrackAtVertex.hpp"
0018 #include "Acts/Vertexing/TrackLinearizer.hpp"
0019 #include "Acts/Vertexing/Vertex.hpp"
0020 #include "Acts/Vertexing/VertexFitProblem.hpp"
0021 #include "Acts/Vertexing/VertexingError.hpp"
0022 #include "Acts/Vertexing/VertexingOptions.hpp"
0023 
0024 #include <algorithm>
0025 
0026 namespace Acts {
0027 
0028 /// @class AdaptiveMultiVertexFitter
0029 /// @brief Implements an adaptive multi-vertex fitter as described
0030 ///   in detail in Section 5.3.5 in:
0031 ///   Ref. (1): CERN-THESIS-2010-027, Author: Piacquadio, Giacinto:
0032 ///   `Identification of b-jets and investigation of the discovery potential
0033 ///   of a Higgs boson in the WH−−>lvbb¯ channel with the ATLAS experiment`
0034 ///
0035 class AdaptiveMultiVertexFitter {
0036   /// @brief Per-vertex scratch data of the adaptive multi-vertex fitter
0037   ///
0038   /// This is the fitter-private counterpart of @c VertexFitCandidate: everything
0039   /// here is regenerated during fitting and carries no meaning for a caller. It
0040   /// lives in the fitter cache rather than in the fit problem.
0041   ///
0042   /// @note @c linPoint and @c oldPosition must be initialised to the seed
0043   /// position of the corresponding vertex when the scratch entry is first
0044   /// created. Starting them at zero would make the first iteration measure the
0045   /// distance to the origin rather than to the seed, and spuriously trigger
0046   /// relinearization for any displaced vertex. See
0047   /// @c AdaptiveMultiVertexFitter::scratchFor.
0048   struct VertexScratch {
0049     VertexScratch() = default;
0050 
0051     /// Construct scratch data seeded at @p pos
0052     /// @param pos Initial linearization point and previous position
0053     explicit VertexScratch(const Vector4& pos)
0054         : linPoint(pos), oldPosition(pos) {}
0055 
0056     /// Point where all associated tracks are linearized
0057     Vector4 linPoint{Vector4::Zero()};
0058 
0059     /// Vertex position from the last iteration of the fit
0060     Vector4 oldPosition{Vector4::Zero()};
0061 
0062     /// Flag indicating if associated tracks need relinearization
0063     bool relinearize = true;
0064 
0065     /// Map of 3D impact parameters for each associated track
0066     std::map<InputTrack, const BoundTrackParameters> impactParams3D;
0067   };
0068 
0069  public:
0070   /// @brief The fitter-private cache
0071   ///
0072   /// Holds everything that is scratch data of a running fit: the annealing
0073   /// state, the impact point estimator state, the magnetic field cache and the
0074   /// per-vertex linearization bookkeeping. The data describing *what* is being
0075   /// fitted lives in @c VertexFitProblem instead.
0076   struct Cache {
0077     /// Constructor for the multi-vertex fitter cache
0078     /// @param field Magnetic field provider for track extrapolation
0079     /// @param magContext Magnetic field context for field evaluations
0080     Cache(const MagneticFieldProvider& field,
0081           const Acts::MagneticFieldContext& magContext)
0082         : ipState{field.makeCache(magContext)},
0083           fieldCache(field.makeCache(magContext)) {}
0084 
0085     /// Drop the scratch data associated with @p vtx
0086     ///
0087     /// Must be called whenever a vertex's candidate description is re-seeded
0088     /// after it has already been fitted, since the cached linearization point
0089     /// and impact parameters then refer to a stale seed position.
0090     /// @param vtx Vertex whose scratch data is to be discarded
0091     void invalidate(const Vertex& vtx) { vertexScratch.erase(&vtx); }
0092 
0093    private:
0094     /// Annealing state for thermodynamic track weighting
0095     AnnealingUtility::State annealingState;
0096 
0097     /// Impact point estimator state for track parameter calculations
0098     ImpactPointEstimator::State ipState;
0099 
0100     /// Magnetic field cache for field evaluations during fitting
0101     MagneticFieldProvider::Cache fieldCache;
0102 
0103     /// Per-vertex scratch data, lazily created by @c scratchFor
0104     std::map<const Vertex*, VertexScratch> vertexScratch;
0105 
0106     /// Construct a cache adopting an already existing impact point estimator
0107     /// state and magnetic field cache. Used by the deprecated @c State based
0108     /// entry points, which own those two caches themselves.
0109     /// @param ipStateIn Impact point estimator state to adopt
0110     /// @param fieldCacheIn Magnetic field cache to adopt
0111     Cache(ImpactPointEstimator::State ipStateIn,
0112           MagneticFieldProvider::Cache fieldCacheIn)
0113         : ipState(std::move(ipStateIn)), fieldCache(std::move(fieldCacheIn)) {}
0114 
0115     friend class AdaptiveMultiVertexFitter;
0116   };
0117 
0118   /// @brief Configuration options for the adaptive multi-vertex fit.
0119   struct Config {
0120     /// @brief Config constructor
0121     ///
0122     /// @param est ImpactPointEstimator
0123     explicit Config(ImpactPointEstimator est) : ipEst(std::move(est)) {}
0124 
0125     /// ImpactPointEstimator
0126     ImpactPointEstimator ipEst;
0127 
0128     /// Annealing tool used for a thermodynamic annealing scheme for the
0129     /// track weight factors in such a way that with high temperature values
0130     /// (at the beginning) only a slight preference is given to tracks
0131     /// compatible with the estimated vertex position. With lower temperatures
0132     /// the weighting get stricter such that all incompatible tracks will be
0133     /// dropped at the end while keeping all compatible tracks with a weight=1.
0134     ///   Ref. (1): CERN-THESIS-2010-027, Author: Piacquadio, Giacinto:
0135     ///   `Identification of b-jets and investigation of the discovery potential
0136     ///   of a Higgs boson in the WH−−>lvbb¯ channel with the ATLAS experiment`
0137     AnnealingUtility annealingTool;
0138 
0139     /// Number of max iterations
0140     unsigned int maxIterations{30};
0141 
0142     /// Max distance to linearization point allowed
0143     /// without relinearization
0144     double maxDistToLinPoint{0.5};
0145 
0146     /// Minimum track weight needed for track to be considered
0147     double minWeight{0.0001};
0148 
0149     /// Max relative shift of vertex during one iteration
0150     double maxRelativeShift{0.01};
0151 
0152     /// Do smoothing after multivertex fit
0153     bool doSmoothing{false};
0154 
0155     /// Use time information when calculating the vertex compatibility
0156     bool useTime{false};
0157 
0158     /// Function to extract parameters from InputTrack
0159     InputTrack::Extractor extractParameters;
0160 
0161     /// TrackLinearizer
0162     TrackLinearizer trackLinearizer;
0163   };
0164 
0165   /// @brief Constructor for user-defined InputTrack_t type !=
0166   /// BoundTrackParameters
0167   ///
0168   /// @param cfg Configuration object
0169   /// object
0170   /// @param logger The logging instance
0171   explicit AdaptiveMultiVertexFitter(
0172       Config cfg, std::unique_ptr<const Logger> logger = getDefaultLogger(
0173                       "AdaptiveMultiVertexFitter", Logging::INFO));
0174 
0175   /// Move constructor
0176   /// @param other The fitter to move from
0177   AdaptiveMultiVertexFitter(AdaptiveMultiVertexFitter&& other) noexcept =
0178       default;
0179 
0180   /// @brief Adds a new vertex to an existing multi-vertex fit.
0181   /// 1. The 3D impact parameters are calculated for all tracks associated
0182   /// with newVertex.
0183   /// 2. A list of all vertices that are connected with newVertex via shared
0184   /// tracks is created. It also considers indirect connections (e.g., vertex A
0185   /// which shares a track with vertex B which, in turn, shares a track with
0186   /// newVertex).
0187   /// 3. The multivertex fit is performed for all vertices on said list.
0188   ///
0189   /// @param problem The multi-vertex fit problem
0190   /// @param newVertices Vertex to be added to fit
0191   /// @param vertexingOptions Vertexing options
0192   /// @param cache Fitter cache
0193   ///
0194   /// @return Result<void> object
0195   Result<void> addVtxToFit(VertexFitProblem& problem,
0196                            const std::vector<Vertex*>& newVertices,
0197                            const VertexingOptions& vertexingOptions,
0198                            Cache& cache) const;
0199 
0200   /// @brief Performs a simultaneous fit of all vertices in
0201   /// problem.vertices
0202   ///
0203   /// @param problem The multi-vertex fit problem
0204   /// @param vertexingOptions Vertexing options
0205   /// @param cache Fitter cache
0206   ///
0207   /// @return Result<void> object
0208   Result<void> fit(VertexFitProblem& problem,
0209                    const VertexingOptions& vertexingOptions,
0210                    Cache& cache) const;
0211 
0212   // Backwards compatibility: the pre-split fitter state and the entry points
0213   // taking it. Both are adapters on top of the interface above, which they
0214   // reproduce exactly: the state is decomposed into a problem and a cache on
0215   // the way in and recomposed from them on the way out.
0216   //
0217   // Only the entry points carry the deprecation attribute. The types are
0218   // documented as deprecated but not attributed, so that naming them in the
0219   // adapter's own declarations does not warn -- a caller reaching the legacy
0220   // interface has to go through fit() or addVtxToFit() anyway.
0221 
0222   /// @brief The fitter state
0223   ///
0224   /// @deprecated Split into @c VertexFitProblem, which describes the vertices
0225   /// to be fitted and is owned by the caller, and @c Cache, which holds the
0226   /// fitter's scratch data. Construct those two separately and use the
0227   /// corresponding @c fit and @c addVtxToFit overloads instead.
0228   struct State {
0229     /// Constructor for multi-vertex fitter state
0230     /// @param field Magnetic field provider for track extrapolation
0231     /// @param magContext Magnetic field context for field evaluations
0232     State(const MagneticFieldProvider& field,
0233           const MagneticFieldContext& magContext)
0234         : ipState{field.makeCache(magContext)},
0235           fieldCache(field.makeCache(magContext)) {}
0236 
0237     /// Vertex collection to be fitted
0238     std::vector<Vertex*> vertexCollection;
0239 
0240     /// Annealing state for thermodynamic track weighting
0241     AnnealingUtility::State annealingState;
0242 
0243     /// Impact point estimator state for track parameter calculations
0244     ImpactPointEstimator::State ipState;
0245 
0246     /// Magnetic field cache for field evaluations during fitting
0247     MagneticFieldProvider::Cache fieldCache;
0248 
0249     /// Map storing vertex information for each vertex in the fit
0250     std::map<Vertex*, VertexInfo> vtxInfoMap;
0251 
0252     /// Multimap connecting tracks to their associated vertices
0253     std::multimap<InputTrack, Vertex*> trackToVerticesMultiMap;
0254 
0255     /// Map storing track-at-vertex information for each track-vertex pair
0256     std::map<std::pair<InputTrack, Vertex*>, TrackAtVertex> tracksAtVerticesMap;
0257 
0258     /// Adds a vertex to trackToVerticesMultiMap
0259     /// @param vtx Vertex to add to the multimap with its associated tracks
0260     void addVertexToMultiMap(Vertex& vtx) {
0261       for (auto trk : vtxInfoMap[&vtx].trackLinks) {
0262         trackToVerticesMultiMap.emplace(trk, &vtx);
0263       }
0264     }
0265 
0266     /// Removes a vertex from trackToVerticesMultiMap
0267     /// @param vtx Vertex to remove from the multimap along with its track associations
0268     void removeVertexFromMultiMap(const Vertex& vtx) {
0269       for (auto iter = trackToVerticesMultiMap.begin();
0270            iter != trackToVerticesMultiMap.end();) {
0271         if (iter->second == &vtx) {
0272           iter = trackToVerticesMultiMap.erase(iter);
0273         } else {
0274           ++iter;
0275         }
0276       }
0277     }
0278 
0279     /// Remove a vertex from the vertex collection
0280     /// @param vtxToRemove Vertex to remove from the collection
0281     /// @param logger Logger for diagnostic messages
0282     /// @return Result indicating success or failure of the removal operation
0283     Result<void> removeVertexFromCollection(Vertex& vtxToRemove,
0284                                             const Logger& logger) {
0285       auto it = std::ranges::find(vertexCollection, &vtxToRemove);
0286       // Check if the value was found before erasing
0287       if (it == vertexCollection.end()) {
0288         ACTS_ERROR("vtxToRemove is not part of vertexCollection.");
0289         return VertexingError::ElementNotFound;
0290       }
0291       // Erase the element if found
0292       vertexCollection.erase(it);
0293       return {};
0294     }
0295   };
0296 
0297   /// @brief Adds a new vertex to an existing multi-vertex fit
0298   ///
0299   /// @deprecated Use the overload taking a @c VertexFitProblem and a @c Cache
0300   ///
0301   /// @param state Fitter state
0302   /// @param newVertices Vertex to be added to fit
0303   /// @param vertexingOptions Vertexing options
0304   ///
0305   /// @return Result<void> object
0306   [[deprecated(
0307       "Use addVtxToFit(VertexFitProblem&, const std::vector<Vertex*>&, const "
0308       "VertexingOptions&, Cache&)")]]
0309   Result<void> addVtxToFit(State& state,
0310                            const std::vector<Vertex*>& newVertices,
0311                            const VertexingOptions& vertexingOptions) const;
0312 
0313   /// @brief Performs a simultaneous fit of all vertices in
0314   /// state.vertexCollection
0315   ///
0316   /// @deprecated Use the overload taking a @c VertexFitProblem and a @c Cache
0317   ///
0318   /// @param state Fitter state
0319   /// @param vertexingOptions Vertexing options
0320   ///
0321   /// @return Result<void> object
0322   [[deprecated("Use fit(VertexFitProblem&, const VertexingOptions&, Cache&)")]]
0323   Result<void> fit(State& state,
0324                    const VertexingOptions& vertexingOptions) const;
0325 
0326  private:
0327   /// Configuration object
0328   const Config m_cfg;
0329 
0330   /// Logging instance
0331   std::unique_ptr<const Logger> m_logger;
0332 
0333   /// Private access to logging instance
0334   const Logger& logger() const { return *m_logger; }
0335 
0336   /// @brief Tests if vertex is already in list of vertices or not
0337   ///
0338   /// @param vtx Vertex to test
0339   /// @param verticesVec Vector of vertices to search
0340   ///
0341   /// @return True if vtx is already in verticesVec
0342   bool isAlreadyInList(Vertex* vtx,
0343                        const std::vector<Vertex*>& verticesVec) const;
0344 
0345   /// @brief Returns the scratch data for @p vtx, creating it on first access
0346   ///
0347   /// A newly created entry is seeded with the vertex's seed position, so that
0348   /// the first iteration measures the distance to the seed rather than to the
0349   /// origin.
0350   ///
0351   /// @param problem The multi-vertex fit problem
0352   /// @param vtx Vertex object
0353   /// @param cache Fitter cache
0354   ///
0355   /// @return Reference to the scratch data of @p vtx
0356   static VertexScratch& scratchFor(const VertexFitProblem& problem, Vertex* vtx,
0357                                    Cache& cache);
0358 
0359   /// @brief 1) Calls ImpactPointEstimator::estimate3DImpactParameters
0360   /// for all tracks that are associated with vtx (i.e., all elements
0361   /// of the trackLinks vector in the candidate of vtx).
0362   /// 2) Saves the 3D impact parameters in the scratch data of vtx.
0363   ///
0364   /// @param problem The multi-vertex fit problem
0365   /// @param vtx Vertex object
0366   /// @param vertexingOptions Vertexing options
0367   /// @param cache Fitter cache
0368   Result<void> prepareVertexForFit(const VertexFitProblem& problem, Vertex* vtx,
0369                                    const VertexingOptions& vertexingOptions,
0370                                    Cache& cache) const;
0371 
0372   /// @brief Sets the vertexCompatibility for all TrackAtVertex objects
0373   /// at the current vertex
0374   ///
0375   /// @param problem The multi-vertex fit problem
0376   /// @param currentVtx Current vertex
0377   /// @param vertexingOptions Vertexing options
0378   /// @param cache Fitter cache
0379   Result<void> setAllVertexCompatibilities(
0380       VertexFitProblem& problem, Vertex* currentVtx,
0381       const VertexingOptions& vertexingOptions, Cache& cache) const;
0382 
0383   /// @brief Sets weights to the track according to Eq.(5.46) in Ref.(1)
0384   ///  and updates the vertices by calling the VertexUpdater
0385   ///
0386   /// @param problem The multi-vertex fit problem
0387   /// @param vertexingOptions Vertexing options
0388   /// @param cache Fitter cache
0389   Result<void> setWeightsAndUpdate(VertexFitProblem& problem,
0390                                    const VertexingOptions& vertexingOptions,
0391                                    Cache& cache) const;
0392 
0393   /// @brief Collects the compatibility values of the track `trk`
0394   /// wrt to all of its associated vertices
0395   ///
0396   /// @param problem The multi-vertex fit problem
0397   /// @param trk Track
0398   ///
0399   /// @return Vector of compatibility values
0400   std::vector<double> collectTrackToVertexCompatibilities(
0401       const VertexFitProblem& problem, const InputTrack& trk) const;
0402 
0403   /// @brief Determines if any vertex position has shifted more than
0404   /// m_cfg.maxRelativeShift in the last iteration
0405   ///
0406   /// @param problem The multi-vertex fit problem
0407   /// @param cache Fitter cache
0408   ///
0409   /// @return False if at least one shift was larger than maxRelativeShift
0410   bool checkSmallShift(const VertexFitProblem& problem, Cache& cache) const;
0411 
0412   /// @brief Updates tracks for current vertex with knowledge
0413   /// of current vertex position
0414   ///
0415   /// @param problem The multi-vertex fit problem
0416   void doVertexSmoothing(VertexFitProblem& problem) const;
0417 
0418   /// @brief Logs vertices in problem.vertices and associated tracks
0419   ///
0420   /// @param problem The multi-vertex fit problem
0421   /// @param geoContext Geometry context
0422   /// @param cache Fitter cache
0423   void logDebugData(const VertexFitProblem& problem,
0424                     const GeometryContext& geoContext, Cache& cache) const;
0425 
0426   /// @brief Moves the contents of a deprecated state into a problem and a cache
0427   ///
0428   /// @param state Fitter state to take the contents from
0429   /// @param[out] problem The multi-vertex fit problem
0430   /// @param[out] cache Fitter cache
0431   static void decomposeState(State& state, VertexFitProblem& problem,
0432                              Cache& cache);
0433 
0434   /// @brief Moves a problem and a cache back into a deprecated state
0435   ///
0436   /// @param[out] state Fitter state to write the contents to
0437   /// @param problem The multi-vertex fit problem
0438   /// @param cache Fitter cache
0439   static void recomposeState(State& state, VertexFitProblem& problem,
0440                              Cache& cache);
0441 };
0442 
0443 }  // namespace Acts