File indexing completed on 2025-01-18 09:11:33
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Vertexing/IterativeVertexFinder.hpp"
0010
0011 #include "Acts/Surfaces/PerigeeSurface.hpp"
0012 #include "Acts/Vertexing/VertexingError.hpp"
0013
0014 Acts::IterativeVertexFinder::IterativeVertexFinder(
0015 Config cfg, std::unique_ptr<const Logger> logger)
0016 : m_cfg(std::move(cfg)), m_logger(std::move(logger)) {
0017 if (!m_cfg.extractParameters.connected()) {
0018 throw std::invalid_argument(
0019 "IterativeVertexFinder: "
0020 "No function to extract parameters "
0021 "provided.");
0022 }
0023
0024 if (!m_cfg.trackLinearizer.connected()) {
0025 throw std::invalid_argument(
0026 "IterativeVertexFinder: "
0027 "No track linearizer provided.");
0028 }
0029
0030 if (!m_cfg.seedFinder) {
0031 throw std::invalid_argument(
0032 "IterativeVertexFinder: "
0033 "No seed finder provided.");
0034 }
0035
0036 if (!m_cfg.field) {
0037 throw std::invalid_argument(
0038 "IterativeVertexFinder: "
0039 "No magnetic field provider provided.");
0040 }
0041 }
0042
0043 auto Acts::IterativeVertexFinder::find(
0044 const std::vector<InputTrack>& trackVector,
0045 const VertexingOptions& vertexingOptions,
0046 IVertexFinder::State& anyState) const -> Result<std::vector<Vertex>> {
0047 auto& state = anyState.as<State>();
0048
0049 const std::vector<InputTrack>& origTracks = trackVector;
0050
0051 std::vector<InputTrack> seedTracks = trackVector;
0052
0053
0054 std::vector<Vertex> vertexCollection;
0055
0056 int nInterations = 0;
0057
0058 while (seedTracks.size() > 1 && nInterations < m_cfg.maxVertices) {
0059
0060 auto seedRes = getVertexSeed(state, seedTracks, vertexingOptions);
0061
0062 if (!seedRes.ok()) {
0063 return seedRes.error();
0064 }
0065 const auto& seedOptional = *seedRes;
0066
0067 if (!seedOptional.has_value()) {
0068 ACTS_DEBUG("No more seed found. Break and stop primary vertex finding.");
0069 break;
0070 }
0071 const auto& seedVertex = *seedOptional;
0072
0073
0074
0075
0076 std::vector<InputTrack> tracksToFit;
0077 std::vector<InputTrack> tracksToFitSplitVertex;
0078
0079
0080 auto res = fillTracksToFit(seedTracks, seedVertex, tracksToFit,
0081 tracksToFitSplitVertex, vertexingOptions, state);
0082
0083 if (!res.ok()) {
0084 return res.error();
0085 }
0086
0087 ACTS_DEBUG("Number of tracks used for fit: " << tracksToFit.size());
0088
0089
0090 Vertex currentVertex;
0091 Vertex currentSplitVertex;
0092
0093 if (vertexingOptions.useConstraintInFit && !tracksToFit.empty()) {
0094 auto fitResult = m_cfg.vertexFitter.fit(tracksToFit, vertexingOptions,
0095 state.fieldCache);
0096 if (fitResult.ok()) {
0097 currentVertex = std::move(*fitResult);
0098 } else {
0099 return fitResult.error();
0100 }
0101 } else if (!vertexingOptions.useConstraintInFit && tracksToFit.size() > 1) {
0102 auto fitResult = m_cfg.vertexFitter.fit(tracksToFit, vertexingOptions,
0103 state.fieldCache);
0104 if (fitResult.ok()) {
0105 currentVertex = std::move(*fitResult);
0106 } else {
0107 return fitResult.error();
0108 }
0109 }
0110 if (m_cfg.createSplitVertices && tracksToFitSplitVertex.size() > 1) {
0111 auto fitResult = m_cfg.vertexFitter.fit(
0112 tracksToFitSplitVertex, vertexingOptions, state.fieldCache);
0113 if (fitResult.ok()) {
0114 currentSplitVertex = std::move(*fitResult);
0115 } else {
0116 return fitResult.error();
0117 }
0118 }
0119
0120 ACTS_DEBUG("Vertex position after fit: "
0121 << currentVertex.fullPosition().transpose());
0122
0123
0124 double ndf = currentVertex.fitQuality().second;
0125 double ndfSplitVertex = currentSplitVertex.fitQuality().second;
0126
0127
0128 int nTracksAtVertex = countSignificantTracks(currentVertex);
0129 int nTracksAtSplitVertex = countSignificantTracks(currentSplitVertex);
0130
0131 bool isGoodVertex = ((!vertexingOptions.useConstraintInFit && ndf > 0 &&
0132 nTracksAtVertex >= 2) ||
0133 (vertexingOptions.useConstraintInFit && ndf > 3 &&
0134 nTracksAtVertex >= 2));
0135
0136 if (!isGoodVertex) {
0137 removeTracks(tracksToFit, seedTracks);
0138 } else {
0139 if (m_cfg.reassignTracksAfterFirstFit && (!m_cfg.createSplitVertices)) {
0140
0141
0142
0143 auto result = reassignTracksToNewVertex(
0144 vertexCollection, currentVertex, tracksToFit, seedTracks,
0145 origTracks, vertexingOptions, state);
0146 if (!result.ok()) {
0147 return result.error();
0148 }
0149 isGoodVertex = *result;
0150
0151 }
0152
0153 if (isGoodVertex) {
0154 removeUsedCompatibleTracks(currentVertex, tracksToFit, seedTracks,
0155 vertexingOptions, state);
0156
0157 ACTS_DEBUG(
0158 "Number of seed tracks after removal of compatible tracks "
0159 "and outliers: "
0160 << seedTracks.size());
0161 }
0162 }
0163
0164
0165 bool isGoodSplitVertex = false;
0166 if (m_cfg.createSplitVertices) {
0167 isGoodSplitVertex = (ndfSplitVertex > 0 && nTracksAtSplitVertex >= 2);
0168
0169 if (!isGoodSplitVertex) {
0170 removeTracks(tracksToFitSplitVertex, seedTracks);
0171 } else {
0172 removeUsedCompatibleTracks(currentSplitVertex, tracksToFitSplitVertex,
0173 seedTracks, vertexingOptions, state);
0174 }
0175 }
0176
0177 if (isGoodVertex) {
0178 vertexCollection.push_back(currentVertex);
0179 }
0180 if (isGoodSplitVertex && m_cfg.createSplitVertices) {
0181 vertexCollection.push_back(currentSplitVertex);
0182 }
0183
0184 nInterations++;
0185 }
0186
0187 return vertexCollection;
0188 }
0189
0190 auto Acts::IterativeVertexFinder::getVertexSeed(
0191 State& state, const std::vector<InputTrack>& seedTracks,
0192 const VertexingOptions& vertexingOptions) const
0193 -> Result<std::optional<Vertex>> {
0194 auto finderState = m_cfg.seedFinder->makeState(state.magContext);
0195 auto res = m_cfg.seedFinder->find(seedTracks, vertexingOptions, finderState);
0196
0197 if (!res.ok()) {
0198 ACTS_ERROR("Internal seeding error. Number of input tracks: "
0199 << seedTracks.size());
0200 return VertexingError::SeedingError;
0201 }
0202 const auto& seedVector = *res;
0203
0204 ACTS_DEBUG("Found " << seedVector.size() << " seeds");
0205
0206 if (seedVector.empty()) {
0207 return std::nullopt;
0208 }
0209 const Vertex& seedVertex = seedVector.back();
0210
0211 ACTS_DEBUG("Use " << seedTracks.size() << " tracks for vertex seed finding.");
0212 ACTS_DEBUG(
0213 "Found seed at position: " << seedVertex.fullPosition().transpose());
0214
0215 return seedVertex;
0216 }
0217
0218 inline void Acts::IterativeVertexFinder::removeTracks(
0219 const std::vector<InputTrack>& tracksToRemove,
0220 std::vector<InputTrack>& seedTracks) const {
0221 for (const auto& trk : tracksToRemove) {
0222 const BoundTrackParameters& params = m_cfg.extractParameters(trk);
0223
0224 auto foundIter =
0225 std::ranges::find_if(seedTracks, [¶ms, this](const auto seedTrk) {
0226 return params == m_cfg.extractParameters(seedTrk);
0227 });
0228 if (foundIter != seedTracks.end()) {
0229
0230 seedTracks.erase(foundIter);
0231 } else {
0232 ACTS_WARNING("Track to be removed not found in seed tracks.");
0233 }
0234 }
0235 }
0236
0237 Acts::Result<double> Acts::IterativeVertexFinder::getCompatibility(
0238 const BoundTrackParameters& params, const Vertex& vertex,
0239 const Surface& perigeeSurface, const VertexingOptions& vertexingOptions,
0240 State& state) const {
0241
0242 auto result =
0243 m_cfg.trackLinearizer(params, vertex.fullPosition()[3], perigeeSurface,
0244 vertexingOptions.geoContext,
0245 vertexingOptions.magFieldContext, state.fieldCache);
0246 if (!result.ok()) {
0247 return result.error();
0248 }
0249
0250 auto linTrack = std::move(*result);
0251
0252
0253 SquareMatrix2 weightReduced =
0254 linTrack.covarianceAtPCA.template block<2, 2>(0, 0);
0255
0256 SquareMatrix2 errorVertexReduced =
0257 (linTrack.positionJacobian *
0258 (vertex.fullCovariance() * linTrack.positionJacobian.transpose()))
0259 .template block<2, 2>(0, 0);
0260 weightReduced += errorVertexReduced;
0261 weightReduced = weightReduced.inverse().eval();
0262
0263
0264 Vector2 trackParameters2D =
0265 linTrack.parametersAtPCA.template block<2, 1>(0, 0);
0266 double compatibility =
0267 trackParameters2D.dot(weightReduced * trackParameters2D);
0268
0269 return compatibility;
0270 }
0271
0272 Acts::Result<void> Acts::IterativeVertexFinder::removeUsedCompatibleTracks(
0273 Vertex& vertex, std::vector<InputTrack>& tracksToFit,
0274 std::vector<InputTrack>& seedTracks,
0275 const VertexingOptions& vertexingOptions, State& state) const {
0276 std::vector<TrackAtVertex> tracksAtVertex = vertex.tracks();
0277
0278 for (const auto& trackAtVtx : tracksAtVertex) {
0279
0280 if (trackAtVtx.trackWeight < m_cfg.cutOffTrackWeight) {
0281
0282 continue;
0283 }
0284
0285 auto foundSeedIter =
0286 std::ranges::find(seedTracks, trackAtVtx.originalParams);
0287 if (foundSeedIter != seedTracks.end()) {
0288 seedTracks.erase(foundSeedIter);
0289 } else {
0290 ACTS_WARNING("Track trackAtVtx not found in seedTracks!");
0291 }
0292
0293
0294 auto foundFitIter =
0295 std::ranges::find(tracksToFit, trackAtVtx.originalParams);
0296 if (foundFitIter != tracksToFit.end()) {
0297 tracksToFit.erase(foundFitIter);
0298 } else {
0299 ACTS_WARNING("Track trackAtVtx not found in tracksToFit!");
0300 }
0301 }
0302
0303 ACTS_DEBUG("After removal of tracks belonging to vertex, "
0304 << seedTracks.size() << " seed tracks left.");
0305
0306
0307
0308
0309 ACTS_DEBUG("Number of outliers: " << tracksToFit.size());
0310
0311 const std::shared_ptr<PerigeeSurface> vertexPerigeeSurface =
0312 Surface::makeShared<PerigeeSurface>(
0313 VectorHelpers::position(vertex.fullPosition()));
0314
0315 for (const auto& trk : tracksToFit) {
0316
0317 auto result =
0318 getCompatibility(m_cfg.extractParameters(trk), vertex,
0319 *vertexPerigeeSurface, vertexingOptions, state);
0320
0321 if (!result.ok()) {
0322 return result.error();
0323 }
0324
0325 double chi2 = *result;
0326
0327
0328
0329 if (chi2 < m_cfg.maximumChi2cutForSeeding) {
0330 auto foundIter = std::ranges::find(seedTracks, trk);
0331 if (foundIter != seedTracks.end()) {
0332
0333 seedTracks.erase(foundIter);
0334 }
0335
0336 } else {
0337
0338
0339 auto foundIter = std::ranges::find_if(
0340 tracksAtVertex,
0341 [&trk](auto trkAtVtx) { return trk == trkAtVtx.originalParams; });
0342 if (foundIter != tracksAtVertex.end()) {
0343
0344 tracksAtVertex.erase(foundIter);
0345 }
0346 }
0347 }
0348
0349
0350 vertex.setTracksAtVertex(tracksAtVertex);
0351
0352 return {};
0353 }
0354
0355 Acts::Result<void> Acts::IterativeVertexFinder::fillTracksToFit(
0356 const std::vector<InputTrack>& seedTracks, const Vertex& seedVertex,
0357 std::vector<InputTrack>& tracksToFitOut,
0358 std::vector<InputTrack>& tracksToFitSplitVertexOut,
0359 const VertexingOptions& vertexingOptions, State& state) const {
0360 int numberOfTracks = seedTracks.size();
0361
0362
0363 int count = 0;
0364
0365 for (const auto& sTrack : seedTracks) {
0366
0367
0368 if (numberOfTracks <= 2) {
0369 tracksToFitOut.push_back(sTrack);
0370 ++count;
0371 } else if (numberOfTracks <= 4 && !m_cfg.createSplitVertices) {
0372 tracksToFitOut.push_back(sTrack);
0373 ++count;
0374 } else if (numberOfTracks <= 4 * m_cfg.splitVerticesTrkInvFraction &&
0375 m_cfg.createSplitVertices) {
0376 if (count % m_cfg.splitVerticesTrkInvFraction != 0) {
0377 tracksToFitOut.push_back(sTrack);
0378 ++count;
0379 } else {
0380 tracksToFitSplitVertexOut.push_back(sTrack);
0381 ++count;
0382 }
0383 }
0384
0385
0386 else {
0387 const BoundTrackParameters& sTrackParams =
0388 m_cfg.extractParameters(sTrack);
0389 auto distanceRes = m_cfg.ipEst.calculateDistance(
0390 vertexingOptions.geoContext, sTrackParams, seedVertex.position(),
0391 state.ipState);
0392 if (!distanceRes.ok()) {
0393 return distanceRes.error();
0394 }
0395
0396 if (!sTrackParams.covariance()) {
0397 return VertexingError::NoCovariance;
0398 }
0399
0400
0401 double hypotVariance =
0402 sqrt((*(sTrackParams.covariance()))(eBoundLoc0, eBoundLoc0) +
0403 (*(sTrackParams.covariance()))(eBoundLoc1, eBoundLoc1));
0404
0405 if (hypotVariance == 0.) {
0406 ACTS_WARNING(
0407 "Track impact parameter covariances are zero. Track was not "
0408 "assigned to vertex.");
0409 continue;
0410 }
0411
0412 if (*distanceRes / hypotVariance < m_cfg.significanceCutSeeding) {
0413 if (!m_cfg.createSplitVertices ||
0414 count % m_cfg.splitVerticesTrkInvFraction != 0) {
0415 tracksToFitOut.push_back(sTrack);
0416 ++count;
0417 } else {
0418 tracksToFitSplitVertexOut.push_back(sTrack);
0419 ++count;
0420 }
0421 }
0422 }
0423 }
0424 return {};
0425 }
0426
0427 Acts::Result<bool> Acts::IterativeVertexFinder::reassignTracksToNewVertex(
0428 std::vector<Vertex>& vertexCollection, Vertex& currentVertex,
0429 std::vector<InputTrack>& tracksToFit, std::vector<InputTrack>& seedTracks,
0430 const std::vector<InputTrack>& ,
0431 const VertexingOptions& vertexingOptions, State& state) const {
0432 int numberOfAddedTracks = 0;
0433
0434 const std::shared_ptr<PerigeeSurface> currentVertexPerigeeSurface =
0435 Surface::makeShared<PerigeeSurface>(
0436 VectorHelpers::position(currentVertex.fullPosition()));
0437
0438
0439
0440 for (auto& vertexIt : vertexCollection) {
0441
0442 std::vector<TrackAtVertex> tracksAtVertex = vertexIt.tracks();
0443 auto tracksBegin = tracksAtVertex.begin();
0444 auto tracksEnd = tracksAtVertex.end();
0445
0446 const std::shared_ptr<PerigeeSurface> vertexItPerigeeSurface =
0447 Surface::makeShared<PerigeeSurface>(
0448 VectorHelpers::position(vertexIt.fullPosition()));
0449
0450 for (auto tracksIter = tracksBegin; tracksIter != tracksEnd;) {
0451
0452
0453 if (tracksIter->trackWeight > m_cfg.cutOffTrackWeightReassign) {
0454 tracksIter++;
0455 continue;
0456 }
0457
0458 BoundTrackParameters origParams =
0459 m_cfg.extractParameters(tracksIter->originalParams);
0460
0461
0462 auto resultNew = getCompatibility(origParams, currentVertex,
0463 *currentVertexPerigeeSurface,
0464 vertexingOptions, state);
0465 if (!resultNew.ok()) {
0466 return Result<bool>::failure(resultNew.error());
0467 }
0468 double chi2NewVtx = *resultNew;
0469
0470 auto resultOld =
0471 getCompatibility(origParams, vertexIt, *vertexItPerigeeSurface,
0472 vertexingOptions, state);
0473 if (!resultOld.ok()) {
0474 return Result<bool>::failure(resultOld.error());
0475 }
0476 double chi2OldVtx = *resultOld;
0477
0478 ACTS_DEBUG("Compatibility to new vs old vertex: " << chi2NewVtx << " vs "
0479 << chi2OldVtx);
0480
0481 if (chi2NewVtx < chi2OldVtx) {
0482 tracksToFit.push_back(tracksIter->originalParams);
0483
0484
0485
0486
0487
0488 seedTracks.push_back(tracksIter->originalParams);
0489
0490
0491
0492
0493
0494
0495 numberOfAddedTracks += 1;
0496
0497
0498 tracksIter = tracksAtVertex.erase(tracksIter);
0499 tracksBegin = tracksAtVertex.begin();
0500 tracksEnd = tracksAtVertex.end();
0501
0502 }
0503
0504 else {
0505
0506 ++tracksIter;
0507 }
0508 }
0509
0510 vertexIt.setTracksAtVertex(tracksAtVertex);
0511 }
0512
0513 ACTS_DEBUG("Added " << numberOfAddedTracks
0514 << " tracks from old (other) vertices for new fit");
0515
0516
0517
0518
0519 currentVertex = Vertex();
0520 if (vertexingOptions.useConstraintInFit && !tracksToFit.empty()) {
0521 auto fitResult =
0522 m_cfg.vertexFitter.fit(tracksToFit, vertexingOptions, state.fieldCache);
0523 if (fitResult.ok()) {
0524 currentVertex = std::move(*fitResult);
0525 } else {
0526 return Result<bool>::success(false);
0527 }
0528 } else if (!vertexingOptions.useConstraintInFit && tracksToFit.size() > 1) {
0529 auto fitResult =
0530 m_cfg.vertexFitter.fit(tracksToFit, vertexingOptions, state.fieldCache);
0531 if (fitResult.ok()) {
0532 currentVertex = std::move(*fitResult);
0533 } else {
0534 return Result<bool>::success(false);
0535 }
0536 }
0537
0538
0539 double ndf = currentVertex.fitQuality().second;
0540
0541
0542 int nTracksAtVertex = countSignificantTracks(currentVertex);
0543
0544 bool isGoodVertex = ((!vertexingOptions.useConstraintInFit && ndf > 0 &&
0545 nTracksAtVertex >= 2) ||
0546 (vertexingOptions.useConstraintInFit && ndf > 3 &&
0547 nTracksAtVertex >= 2));
0548
0549 if (!isGoodVertex) {
0550 removeTracks(tracksToFit, seedTracks);
0551
0552 ACTS_DEBUG("Going to new iteration with "
0553 << seedTracks.size() << "seed tracks after BAD vertex.");
0554 }
0555
0556 return Result<bool>::success(isGoodVertex);
0557 }
0558
0559 int Acts::IterativeVertexFinder::countSignificantTracks(
0560 const Vertex& vtx) const {
0561 return std::count_if(vtx.tracks().begin(), vtx.tracks().end(),
0562 [this](const TrackAtVertex& trk) {
0563 return trk.trackWeight > m_cfg.cutOffTrackWeight;
0564 });
0565 }