Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-21 08:02:48

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 #include "Acts/Vertexing/AdaptiveMultiVertexFitter.hpp"
0010 
0011 #include "Acts/Surfaces/PerigeeSurface.hpp"
0012 #include "Acts/Utilities/Helpers.hpp"
0013 #include "Acts/Vertexing/KalmanVertexUpdater.hpp"
0014 #include "Acts/Vertexing/VertexingError.hpp"
0015 
0016 namespace Acts {
0017 
0018 AdaptiveMultiVertexFitter::AdaptiveMultiVertexFitter(
0019     Config cfg, std::unique_ptr<const Logger> logger)
0020     : m_cfg(std::move(cfg)), m_logger(std::move(logger)) {
0021   if (!m_cfg.extractParameters.connected()) {
0022     throw std::invalid_argument(
0023         "AdaptiveMultiVertexFitter: No function to extract parameters "
0024         "from InputTrack_t provided.");
0025   }
0026 
0027   if (!m_cfg.trackLinearizer.connected()) {
0028     throw std::invalid_argument(
0029         "AdaptiveMultiVertexFitter: No track linearizer provided.");
0030   }
0031 }
0032 
0033 Result<void> AdaptiveMultiVertexFitter::fit(
0034     State& state, const VertexingOptions& vertexingOptions) const {
0035   // Reset annealing tool
0036   state.annealingState = AnnealingUtility::State();
0037 
0038   // Boolean indicating whether any of the vertices has moved more than
0039   // m_cfg.maxRelativeShift during the last iteration. We will keep iterating
0040   // until the equilibrium (i.e., the lowest temperature) is reached in
0041   // the annealing procedure and isSmallShift is true (or until the maximum
0042   // number of iterations is exceeded).
0043   bool isSmallShift = true;
0044 
0045   // Number of iterations counter
0046   unsigned int nIter = 0;
0047 
0048   // Start iterating
0049   while (nIter < m_cfg.maxIterations &&
0050          (!state.annealingState.equilibriumReached || !isSmallShift)) {
0051     // Initial loop over all vertices in state.vertexCollection
0052     for (auto vtx : state.vertexCollection) {
0053       VertexInfo& vtxInfo = state.vtxInfoMap[vtx];
0054       vtxInfo.relinearize = false;
0055       // Store old position of vertex, i.e. seed position
0056       // in case of first iteration or position determined
0057       // in previous iteration afterwards
0058       vtxInfo.oldPosition = vtx->fullPosition();
0059 
0060       // Calculate the x-y-distance between the current vertex position
0061       // and the linearization point of the tracks. If it is too large,
0062       // we relinearize the tracks and recalculate their 3D impact
0063       // parameters.
0064       Vector2 xyDiff = vtxInfo.oldPosition.template head<2>() -
0065                        vtxInfo.linPoint.template head<2>();
0066       if (xyDiff.norm() > m_cfg.maxDistToLinPoint) {
0067         // Set flag for relinearization
0068         vtxInfo.relinearize = true;
0069         // Recalculate the track impact parameters at the current vertex
0070         // position
0071         auto prepareVertexResult =
0072             prepareVertexForFit(state, vtx, vertexingOptions);
0073         if (!prepareVertexResult.ok()) {
0074           // Print vertices and associated tracks if logger is in debug mode
0075           if (logger().doPrint(Logging::DEBUG)) {
0076             logDebugData(state, vertexingOptions.geoContext);
0077           }
0078           return prepareVertexResult.error();
0079         }
0080       }
0081 
0082       // Check if we use the constraint during the vertex fit
0083       if (state.vtxInfoMap[vtx].constraint.fullCovariance() !=
0084           SquareMatrix4::Zero()) {
0085         const Vertex& constraint = state.vtxInfoMap[vtx].constraint;
0086         vtx->setFullPosition(constraint.fullPosition());
0087         vtx->setFitQuality(constraint.fitQuality());
0088         vtx->setFullCovariance(constraint.fullCovariance());
0089       } else if (vtx->fullCovariance() == SquareMatrix4::Zero()) {
0090         return VertexingError::NoCovariance;
0091       }
0092 
0093       // Set vertexCompatibility for all TrackAtVertex objects
0094       // at the current vertex
0095       auto setCompatibilitiesResult =
0096           setAllVertexCompatibilities(state, vtx, vertexingOptions);
0097       if (!setCompatibilitiesResult.ok()) {
0098         // Print vertices and associated tracks if logger is in debug mode
0099         if (logger().doPrint(Logging::DEBUG)) {
0100           logDebugData(state, vertexingOptions.geoContext);
0101         }
0102         return setCompatibilitiesResult.error();
0103       }
0104     }  // End loop over vertex collection
0105 
0106     // Recalculate all track weights and update vertices
0107     auto setWeightsResult = setWeightsAndUpdate(state, vertexingOptions);
0108     if (!setWeightsResult.ok()) {
0109       // Print vertices and associated tracks if logger is in debug mode
0110       if (logger().doPrint(Logging::DEBUG)) {
0111         logDebugData(state, vertexingOptions.geoContext);
0112       }
0113       return setWeightsResult.error();
0114     }
0115 
0116     // Cool the system down, i.e., reduce the temperature parameter. At lower
0117     // temperatures, outlying tracks are downweighted more.
0118     if (!state.annealingState.equilibriumReached) {
0119       m_cfg.annealingTool.anneal(state.annealingState);
0120     }
0121 
0122     isSmallShift = checkSmallShift(state);
0123     ++nIter;
0124   }
0125   // Multivertex fit is finished
0126 
0127   // Check if smoothing is required
0128   if (m_cfg.doSmoothing) {
0129     doVertexSmoothing(state);
0130   }
0131 
0132   return {};
0133 }
0134 
0135 Result<void> AdaptiveMultiVertexFitter::addVtxToFit(
0136     State& state, const std::vector<Vertex*>& newVertices,
0137     const VertexingOptions& vertexingOptions) const {
0138   for (const auto& newVertex : newVertices) {
0139     if (state.vtxInfoMap[newVertex].trackLinks.empty()) {
0140       ACTS_ERROR(
0141           "newVertex does not have any associated tracks (i.e., its trackLinks "
0142           "are empty).");
0143       return VertexingError::EmptyInput;
0144     }
0145   }
0146 
0147   std::vector<Vertex*> verticesToFit = newVertices;
0148 
0149   // List of vertices added in last iteration
0150   std::vector<Vertex*> lastIterAddedVertices = newVertices;
0151   // List of vertices added in current iteration
0152   std::vector<Vertex*> currentIterAddedVertices;
0153 
0154   // Fill verticesToFit with vertices that are connected to newVertex (via
0155   // tracks and/or other vertices).
0156   while (!lastIterAddedVertices.empty()) {
0157     for (auto& lastIterAddedVertex : lastIterAddedVertices) {
0158       // Loop over all tracks at lastIterAddedVertex
0159       const std::vector<InputTrack>& trks =
0160           state.vtxInfoMap[lastIterAddedVertex].trackLinks;
0161       for (const auto& trk : trks) {
0162         // Range of vertices that are associated with trk. The range is
0163         // represented via its bounds: begin refers to the first iterator of the
0164         // range; end refers to the iterator after the last iterator of the
0165         // range.
0166         auto [begin, end] = state.trackToVerticesMultiMap.equal_range(trk);
0167 
0168         for (auto it = begin; it != end; ++it) {
0169           // it->first corresponds to trk, it->second to one of its associated
0170           // vertices
0171           auto vtxToFit = it->second;
0172           // Add vertex to the fit if it is not already included
0173           if (!isAlreadyInList(vtxToFit, verticesToFit)) {
0174             verticesToFit.push_back(vtxToFit);
0175 
0176             // Collect vertices that were added this iteration
0177             if (vtxToFit != lastIterAddedVertex) {
0178               currentIterAddedVertices.push_back(vtxToFit);
0179             }
0180           }
0181         }  // End for loop over range of associated vertices
0182       }  // End loop over trackLinks
0183     }  // End loop over lastIterAddedVertices
0184 
0185     lastIterAddedVertices = currentIterAddedVertices;
0186     currentIterAddedVertices.clear();
0187   }  // End while loop
0188 
0189   state.vertexCollection = verticesToFit;
0190 
0191   for (Vertex* newVertexPtr : newVertices) {
0192     // Save the 3D impact parameters of all tracks associated with newVertex.
0193     auto res = prepareVertexForFit(state, newVertexPtr, vertexingOptions);
0194     if (!res.ok()) {
0195       // Print vertices and associated tracks if logger is in debug mode
0196       if (logger().doPrint(Logging::DEBUG)) {
0197         logDebugData(state, vertexingOptions.geoContext);
0198       }
0199       return res.error();
0200     }
0201   }
0202 
0203   // Perform fit on all added vertices
0204   auto fitRes = fit(state, vertexingOptions);
0205   if (!fitRes.ok()) {
0206     return fitRes.error();
0207   }
0208 
0209   return {};
0210 }
0211 
0212 bool AdaptiveMultiVertexFitter::isAlreadyInList(
0213     Vertex* vtx, const std::vector<Vertex*>& vertices) const {
0214   return rangeContainsValue(vertices, vtx);
0215 }
0216 
0217 Result<void> AdaptiveMultiVertexFitter::prepareVertexForFit(
0218     State& state, Vertex* vtx, const VertexingOptions& vertexingOptions) const {
0219   // Vertex info object
0220   auto& vtxInfo = state.vtxInfoMap[vtx];
0221   // Vertex seed position
0222   const Vector3& seedPos = vtxInfo.seedPosition.template head<3>();
0223 
0224   // Loop over all tracks at the vertex
0225   for (const auto& trk : vtxInfo.trackLinks) {
0226     auto res = m_cfg.ipEst.estimate3DImpactParameters(
0227         vertexingOptions.geoContext, vertexingOptions.magFieldContext,
0228         m_cfg.extractParameters(trk), seedPos, state.ipState);
0229     if (!res.ok()) {
0230       return res.error();
0231     }
0232     // Save 3D impact parameters of the track
0233     vtxInfo.impactParams3D.emplace(trk, res.value());
0234   }
0235   return {};
0236 }
0237 
0238 Result<void> AdaptiveMultiVertexFitter::setAllVertexCompatibilities(
0239     State& state, Vertex* vtx, const VertexingOptions& vertexingOptions) const {
0240   VertexInfo& vtxInfo = state.vtxInfoMap[vtx];
0241 
0242   // Loop over all tracks that are associated with vtx and estimate their
0243   // compatibility
0244   for (const auto& trk : vtxInfo.trackLinks) {
0245     auto& trkAtVtx = state.tracksAtVerticesMap.at(std::make_pair(trk, vtx));
0246     // Recover from cases where linearization point != 0 but
0247     // more tracks were added later on
0248     if (!vtxInfo.impactParams3D.contains(trk)) {
0249       auto res = m_cfg.ipEst.estimate3DImpactParameters(
0250           vertexingOptions.geoContext, vertexingOptions.magFieldContext,
0251           m_cfg.extractParameters(trk),
0252           VectorHelpers::position(vtxInfo.linPoint), state.ipState);
0253       if (!res.ok()) {
0254         return res.error();
0255       }
0256       // Set impactParams3D for current trackAtVertex
0257       vtxInfo.impactParams3D.emplace(trk, res.value());
0258     }
0259     // Set compatibility with current vertex
0260     Result<double> compatibilityResult(0.);
0261     if (m_cfg.useTime) {
0262       compatibilityResult = m_cfg.ipEst.getVertexCompatibility(
0263           vertexingOptions.geoContext, &(vtxInfo.impactParams3D.at(trk)),
0264           vtxInfo.oldPosition);
0265     } else {
0266       Vector3 vertexPosOnly = VectorHelpers::position(vtxInfo.oldPosition);
0267       compatibilityResult = m_cfg.ipEst.getVertexCompatibility(
0268           vertexingOptions.geoContext, &(vtxInfo.impactParams3D.at(trk)),
0269           vertexPosOnly);
0270     }
0271 
0272     if (!compatibilityResult.ok()) {
0273       return compatibilityResult.error();
0274     }
0275     trkAtVtx.vertexCompatibility = *compatibilityResult;
0276   }
0277   return {};
0278 }
0279 
0280 Result<void> AdaptiveMultiVertexFitter::setWeightsAndUpdate(
0281     State& state, const VertexingOptions& vertexingOptions) const {
0282   for (auto vtx : state.vertexCollection) {
0283     VertexInfo& vtxInfo = state.vtxInfoMap[vtx];
0284 
0285     if (vtxInfo.relinearize) {
0286       vtxInfo.linPoint = vtxInfo.oldPosition;
0287     }
0288 
0289     const std::shared_ptr<PerigeeSurface> vtxPerigeeSurface =
0290         Surface::makeShared<PerigeeSurface>(
0291             VectorHelpers::position(vtxInfo.linPoint));
0292 
0293     for (const auto& trk : vtxInfo.trackLinks) {
0294       auto& trkAtVtx = state.tracksAtVerticesMap.at(std::make_pair(trk, vtx));
0295 
0296       // Set trackWeight for current track
0297       trkAtVtx.trackWeight = m_cfg.annealingTool.getWeight(
0298           state.annealingState, trkAtVtx.vertexCompatibility,
0299           collectTrackToVertexCompatibilities(state, trk));
0300 
0301       if (trkAtVtx.trackWeight > m_cfg.minWeight) {
0302         // Check if track is already linearized and whether we need to
0303         // relinearize
0304         if (!trkAtVtx.isLinearized || vtxInfo.relinearize) {
0305           auto result = m_cfg.trackLinearizer(
0306               m_cfg.extractParameters(trk), vtxInfo.linPoint[3],
0307               *vtxPerigeeSurface, vertexingOptions.geoContext,
0308               vertexingOptions.magFieldContext, state.fieldCache);
0309           if (!result.ok()) {
0310             return result.error();
0311           }
0312 
0313           trkAtVtx.linearizedState = *result;
0314           trkAtVtx.isLinearized = true;
0315         }
0316         // Update the vertex with the new track. The second template
0317         // argument corresponds to the number of fitted vertex dimensions
0318         // (i.e., 3 if we only fit spatial coordinates and 4 if we also fit
0319         // time).
0320         KalmanVertexUpdater::updateVertexWithTrack(*vtx, trkAtVtx,
0321                                                    m_cfg.useTime ? 4 : 3);
0322       } else {
0323         ACTS_VERBOSE("Track weight too low. Skip track.");
0324       }
0325     }  // End loop over tracks at vertex
0326     ACTS_VERBOSE("New vertex position: " << vtx->fullPosition().transpose());
0327   }  // End loop over vertex collection
0328 
0329   return {};
0330 }
0331 
0332 std::vector<double>
0333 AdaptiveMultiVertexFitter::collectTrackToVertexCompatibilities(
0334     State& state, const InputTrack& trk) const {
0335   // Compatibilities of trk wrt all of its associated vertices
0336   std::vector<double> trkToVtxCompatibilities;
0337 
0338   // Range of vertices that are associated with trk. The range is
0339   // represented via its bounds: begin refers to the first iterator of the
0340   // range; end refers to the iterator after the last iterator of the range.
0341   auto [begin, end] = state.trackToVerticesMultiMap.equal_range(trk);
0342   // Allocate space in memory for the vector of compatibilities
0343   trkToVtxCompatibilities.reserve(std::distance(begin, end));
0344 
0345   for (auto it = begin; it != end; ++it) {
0346     // it->first corresponds to trk, it->second to one of its associated
0347     // vertices
0348     trkToVtxCompatibilities.push_back(
0349         state.tracksAtVerticesMap.at(std::make_pair(trk, it->second))
0350             .vertexCompatibility);
0351   }
0352 
0353   return trkToVtxCompatibilities;
0354 }
0355 
0356 bool AdaptiveMultiVertexFitter::checkSmallShift(State& state) const {
0357   for (auto* vtx : state.vertexCollection) {
0358     Vector3 diff =
0359         state.vtxInfoMap[vtx].oldPosition.template head<3>() - vtx->position();
0360     const SquareMatrix3& vtxCov = vtx->covariance();
0361     double relativeShift = diff.dot(vtxCov.inverse() * diff);
0362     if (relativeShift > m_cfg.maxRelativeShift) {
0363       return false;
0364     }
0365   }
0366   return true;
0367 }
0368 
0369 void AdaptiveMultiVertexFitter::doVertexSmoothing(State& state) const {
0370   for (const auto vtx : state.vertexCollection) {
0371     for (const auto& trk : state.vtxInfoMap[vtx].trackLinks) {
0372       auto& trkAtVtx = state.tracksAtVerticesMap.at(std::make_pair(trk, vtx));
0373       if (trkAtVtx.trackWeight > m_cfg.minWeight) {
0374         // Update the new track under the assumption that it originates at the
0375         // vertex. The second template argument corresponds to the number of
0376         // fitted vertex dimensions (i.e., 3 if we only fit spatial coordinates
0377         // and 4 if we also fit time).
0378         KalmanVertexUpdater::updateTrackWithVertex(trkAtVtx, *vtx,
0379                                                    m_cfg.useTime ? 4 : 3);
0380       }
0381     }
0382   }
0383 }
0384 
0385 void AdaptiveMultiVertexFitter::logDebugData(
0386     const State& state, const GeometryContext& geoContext) const {
0387   ACTS_DEBUG("Encountered an error when fitting the following "
0388              << state.vertexCollection.size() << " vertices:");
0389   for (std::size_t vtxInd = 0; vtxInd < state.vertexCollection.size();
0390        ++vtxInd) {
0391     auto vtx = state.vertexCollection[vtxInd];
0392     ACTS_DEBUG("Position of " << vtxInd << ". vertex seed:\n"
0393                               << state.vtxInfoMap.at(vtx).seedPosition);
0394     ACTS_DEBUG("Position of said vertex after the last fitting step:\n"
0395                << state.vtxInfoMap.at(vtx).oldPosition);
0396     ACTS_DEBUG("Associated tracks:");
0397     const auto& trks = state.vtxInfoMap.at(vtx).trackLinks;
0398     for (std::size_t trkInd = 0; trkInd < trks.size(); ++trkInd) {
0399       const auto& trkAtVtx =
0400           state.tracksAtVerticesMap.at(std::make_pair(trks[trkInd], vtx));
0401       const auto& trkParams = m_cfg.extractParameters(trkAtVtx.originalParams);
0402       ACTS_DEBUG(trkInd << ". track parameters:\n" << trkParams.parameters());
0403       ACTS_DEBUG(trkInd << ". track covariance matrix:\n"
0404                         << trkParams.covariance().value());
0405       ACTS_DEBUG("Origin of corresponding reference surface:\n"
0406                  << trkParams.referenceSurface().center(geoContext));
0407     }
0408   }
0409 }
0410 
0411 }  // namespace Acts