File indexing completed on 2025-08-05 08:09:42
0001
0002
0003
0004
0005
0006
0007
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
0018 state.annealingState = AnnealingUtility::State();
0019
0020
0021
0022
0023
0024
0025 bool isSmallShift = true;
0026
0027
0028 unsigned int nIter = 0;
0029
0030
0031 while (nIter < m_cfg.maxIterations &&
0032 (!state.annealingState.equilibriumReached || !isSmallShift)) {
0033
0034 for (auto vtx : state.vertexCollection) {
0035 VertexInfo& vtxInfo = state.vtxInfoMap[vtx];
0036 vtxInfo.relinearize = false;
0037
0038
0039
0040 vtxInfo.oldPosition = vtx->fullPosition();
0041
0042
0043
0044
0045
0046 ActsVector<2> xyDiff = vtxInfo.oldPosition.template head<2>() -
0047 vtxInfo.linPoint.template head<2>();
0048 if (xyDiff.norm() > m_cfg.maxDistToLinPoint) {
0049
0050 vtxInfo.relinearize = true;
0051
0052
0053 auto prepareVertexResult =
0054 prepareVertexForFit(state, vtx, vertexingOptions);
0055 if (!prepareVertexResult.ok()) {
0056
0057 if (logger().doPrint(Logging::DEBUG)) {
0058 logDebugData(state, vertexingOptions.geoContext);
0059 }
0060 return prepareVertexResult.error();
0061 }
0062 }
0063
0064
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
0076
0077 auto setCompatibilitiesResult =
0078 setAllVertexCompatibilities(state, vtx, vertexingOptions);
0079 if (!setCompatibilitiesResult.ok()) {
0080
0081 if (logger().doPrint(Logging::DEBUG)) {
0082 logDebugData(state, vertexingOptions.geoContext);
0083 }
0084 return setCompatibilitiesResult.error();
0085 }
0086 }
0087
0088
0089 auto setWeightsResult = setWeightsAndUpdate(state, vertexingOptions);
0090 if (!setWeightsResult.ok()) {
0091
0092 if (logger().doPrint(Logging::DEBUG)) {
0093 logDebugData(state, vertexingOptions.geoContext);
0094 }
0095 return setWeightsResult.error();
0096 }
0097
0098
0099
0100 if (!state.annealingState.equilibriumReached) {
0101 m_cfg.annealingTool.anneal(state.annealingState);
0102 }
0103
0104 isSmallShift = checkSmallShift(state);
0105 ++nIter;
0106 }
0107
0108
0109
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
0130 std::vector<Vertex*> lastIterAddedVertices = {&newVertex};
0131
0132 std::vector<Vertex*> currentIterAddedVertices;
0133
0134
0135
0136 while (!lastIterAddedVertices.empty()) {
0137 for (auto& lastIterAddedVertex : lastIterAddedVertices) {
0138
0139 const std::vector<InputTrack>& trks =
0140 state.vtxInfoMap[lastIterAddedVertex].trackLinks;
0141 for (const auto& trk : trks) {
0142
0143
0144
0145
0146 auto [begin, end] = state.trackToVerticesMultiMap.equal_range(trk);
0147
0148 for (auto it = begin; it != end; ++it) {
0149
0150
0151 auto vtxToFit = it->second;
0152
0153 if (!isAlreadyInList(vtxToFit, verticesToFit)) {
0154 verticesToFit.push_back(vtxToFit);
0155
0156
0157 if (vtxToFit != lastIterAddedVertex) {
0158 currentIterAddedVertices.push_back(vtxToFit);
0159 }
0160 }
0161 }
0162 }
0163 }
0164
0165 lastIterAddedVertices = currentIterAddedVertices;
0166 currentIterAddedVertices.clear();
0167 }
0168
0169 state.vertexCollection = verticesToFit;
0170
0171
0172 auto res = prepareVertexForFit(state, &newVertex, vertexingOptions);
0173 if (!res.ok()) {
0174
0175 if (logger().doPrint(Logging::DEBUG)) {
0176 logDebugData(state, vertexingOptions.geoContext);
0177 }
0178 return res.error();
0179 }
0180
0181
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
0198 auto& vtxInfo = state.vtxInfoMap[vtx];
0199
0200 const Vector3& seedPos = vtxInfo.seedPosition.template head<3>();
0201
0202
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
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
0221
0222 for (const auto& trk : vtxInfo.trackLinks) {
0223 auto& trkAtVtx = state.tracksAtVerticesMap.at(std::make_pair(trk, vtx));
0224
0225
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
0235 vtxInfo.impactParams3D.emplace(trk, res.value());
0236 }
0237
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
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
0282
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
0296
0297
0298
0299 KalmanVertexUpdater::updateVertexWithTrack(*vtx, trkAtVtx,
0300 m_cfg.useTime ? 4 : 3);
0301 } else {
0302 ACTS_VERBOSE("Track weight too low. Skip track.");
0303 }
0304 }
0305 ACTS_VERBOSE("New vertex position: " << vtx->fullPosition().transpose());
0306 }
0307
0308 return {};
0309 }
0310
0311 std::vector<double>
0312 Acts::AdaptiveMultiVertexFitter::collectTrackToVertexCompatibilities(
0313 State& state, const InputTrack& trk) const {
0314
0315 std::vector<double> trkToVtxCompatibilities;
0316
0317
0318
0319
0320 auto [begin, end] = state.trackToVerticesMultiMap.equal_range(trk);
0321
0322 trkToVtxCompatibilities.reserve(std::distance(begin, end));
0323
0324 for (auto it = begin; it != end; ++it) {
0325
0326
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
0354
0355
0356
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 }