File indexing completed on 2025-10-21 08:02:48
0001
0002
0003
0004
0005
0006
0007
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
0036 state.annealingState = AnnealingUtility::State();
0037
0038
0039
0040
0041
0042
0043 bool isSmallShift = true;
0044
0045
0046 unsigned int nIter = 0;
0047
0048
0049 while (nIter < m_cfg.maxIterations &&
0050 (!state.annealingState.equilibriumReached || !isSmallShift)) {
0051
0052 for (auto vtx : state.vertexCollection) {
0053 VertexInfo& vtxInfo = state.vtxInfoMap[vtx];
0054 vtxInfo.relinearize = false;
0055
0056
0057
0058 vtxInfo.oldPosition = vtx->fullPosition();
0059
0060
0061
0062
0063
0064 Vector2 xyDiff = vtxInfo.oldPosition.template head<2>() -
0065 vtxInfo.linPoint.template head<2>();
0066 if (xyDiff.norm() > m_cfg.maxDistToLinPoint) {
0067
0068 vtxInfo.relinearize = true;
0069
0070
0071 auto prepareVertexResult =
0072 prepareVertexForFit(state, vtx, vertexingOptions);
0073 if (!prepareVertexResult.ok()) {
0074
0075 if (logger().doPrint(Logging::DEBUG)) {
0076 logDebugData(state, vertexingOptions.geoContext);
0077 }
0078 return prepareVertexResult.error();
0079 }
0080 }
0081
0082
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
0094
0095 auto setCompatibilitiesResult =
0096 setAllVertexCompatibilities(state, vtx, vertexingOptions);
0097 if (!setCompatibilitiesResult.ok()) {
0098
0099 if (logger().doPrint(Logging::DEBUG)) {
0100 logDebugData(state, vertexingOptions.geoContext);
0101 }
0102 return setCompatibilitiesResult.error();
0103 }
0104 }
0105
0106
0107 auto setWeightsResult = setWeightsAndUpdate(state, vertexingOptions);
0108 if (!setWeightsResult.ok()) {
0109
0110 if (logger().doPrint(Logging::DEBUG)) {
0111 logDebugData(state, vertexingOptions.geoContext);
0112 }
0113 return setWeightsResult.error();
0114 }
0115
0116
0117
0118 if (!state.annealingState.equilibriumReached) {
0119 m_cfg.annealingTool.anneal(state.annealingState);
0120 }
0121
0122 isSmallShift = checkSmallShift(state);
0123 ++nIter;
0124 }
0125
0126
0127
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
0150 std::vector<Vertex*> lastIterAddedVertices = newVertices;
0151
0152 std::vector<Vertex*> currentIterAddedVertices;
0153
0154
0155
0156 while (!lastIterAddedVertices.empty()) {
0157 for (auto& lastIterAddedVertex : lastIterAddedVertices) {
0158
0159 const std::vector<InputTrack>& trks =
0160 state.vtxInfoMap[lastIterAddedVertex].trackLinks;
0161 for (const auto& trk : trks) {
0162
0163
0164
0165
0166 auto [begin, end] = state.trackToVerticesMultiMap.equal_range(trk);
0167
0168 for (auto it = begin; it != end; ++it) {
0169
0170
0171 auto vtxToFit = it->second;
0172
0173 if (!isAlreadyInList(vtxToFit, verticesToFit)) {
0174 verticesToFit.push_back(vtxToFit);
0175
0176
0177 if (vtxToFit != lastIterAddedVertex) {
0178 currentIterAddedVertices.push_back(vtxToFit);
0179 }
0180 }
0181 }
0182 }
0183 }
0184
0185 lastIterAddedVertices = currentIterAddedVertices;
0186 currentIterAddedVertices.clear();
0187 }
0188
0189 state.vertexCollection = verticesToFit;
0190
0191 for (Vertex* newVertexPtr : newVertices) {
0192
0193 auto res = prepareVertexForFit(state, newVertexPtr, vertexingOptions);
0194 if (!res.ok()) {
0195
0196 if (logger().doPrint(Logging::DEBUG)) {
0197 logDebugData(state, vertexingOptions.geoContext);
0198 }
0199 return res.error();
0200 }
0201 }
0202
0203
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
0220 auto& vtxInfo = state.vtxInfoMap[vtx];
0221
0222 const Vector3& seedPos = vtxInfo.seedPosition.template head<3>();
0223
0224
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
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
0243
0244 for (const auto& trk : vtxInfo.trackLinks) {
0245 auto& trkAtVtx = state.tracksAtVerticesMap.at(std::make_pair(trk, vtx));
0246
0247
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
0257 vtxInfo.impactParams3D.emplace(trk, res.value());
0258 }
0259
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
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
0303
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
0317
0318
0319
0320 KalmanVertexUpdater::updateVertexWithTrack(*vtx, trkAtVtx,
0321 m_cfg.useTime ? 4 : 3);
0322 } else {
0323 ACTS_VERBOSE("Track weight too low. Skip track.");
0324 }
0325 }
0326 ACTS_VERBOSE("New vertex position: " << vtx->fullPosition().transpose());
0327 }
0328
0329 return {};
0330 }
0331
0332 std::vector<double>
0333 AdaptiveMultiVertexFitter::collectTrackToVertexCompatibilities(
0334 State& state, const InputTrack& trk) const {
0335
0336 std::vector<double> trkToVtxCompatibilities;
0337
0338
0339
0340
0341 auto [begin, end] = state.trackToVerticesMultiMap.equal_range(trk);
0342
0343 trkToVtxCompatibilities.reserve(std::distance(begin, end));
0344
0345 for (auto it = begin; it != end; ++it) {
0346
0347
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
0375
0376
0377
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 }