Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:09:42

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