Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:11:32

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