File indexing completed on 2026-05-21 07:59:53
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/AmbiguityResolution/ScoreBasedAmbiguityResolution.hpp"
0012
0013 #include "Acts/Definitions/Units.hpp"
0014 #include "Acts/EventData/TrackContainerFrontendConcept.hpp"
0015 #include "Acts/Utilities/VectorHelpers.hpp"
0016
0017 #include <unordered_map>
0018
0019 namespace Acts {
0020
0021 inline const Logger& ScoreBasedAmbiguityResolution::logger() const {
0022 return *m_logger;
0023 }
0024
0025 template <TrackContainerFrontend track_container_t>
0026 std::vector<std::vector<ScoreBasedAmbiguityResolution::TrackFeatures>>
0027 ScoreBasedAmbiguityResolution::computeInitialState(
0028 const track_container_t& tracks) const {
0029 ACTS_VERBOSE("Starting to compute initial state");
0030 std::vector<std::vector<TrackFeatures>> trackFeaturesVectors;
0031 trackFeaturesVectors.reserve(tracks.size());
0032
0033 for (const auto& track : tracks) {
0034 std::size_t numberOfDetectors = m_cfg.detectorConfigs.size();
0035
0036 std::vector<TrackFeatures> trackFeaturesVector(numberOfDetectors);
0037
0038 for (const auto& ts : track.trackStatesReversed()) {
0039 if (!ts.hasReferenceSurface()) {
0040 ACTS_DEBUG("Track state has no reference surface");
0041 continue;
0042 }
0043 auto iVolume = ts.referenceSurface().geometryId().volume();
0044 auto volume_it = m_cfg.volumeMap.find(iVolume);
0045 if (volume_it == m_cfg.volumeMap.end()) {
0046 ACTS_ERROR("Volume " << iVolume << "not found in the volume map");
0047 continue;
0048 }
0049 auto detectorId = volume_it->second;
0050
0051 if (ts.typeFlags().isHole()) {
0052 ACTS_VERBOSE("Track state type is HoleFlag");
0053 trackFeaturesVector[detectorId].nHoles++;
0054 } else if (ts.typeFlags().isOutlier()) {
0055 ACTS_VERBOSE("Track state type is OutlierFlag");
0056 trackFeaturesVector[detectorId].nOutliers++;
0057
0058 } else if (ts.typeFlags().isMeasurement()) {
0059 ACTS_VERBOSE("Track state type is MeasurementFlag");
0060
0061 if (ts.typeFlags().isSharedHit()) {
0062 trackFeaturesVector[detectorId].nSharedHits++;
0063 }
0064 trackFeaturesVector[detectorId].nHits++;
0065 }
0066 }
0067 trackFeaturesVectors.push_back(std::move(trackFeaturesVector));
0068 }
0069
0070 return trackFeaturesVectors;
0071 }
0072
0073 template <TrackContainerFrontend track_container_t>
0074 std::vector<double> Acts::ScoreBasedAmbiguityResolution::simpleScore(
0075 const track_container_t& tracks,
0076 const std::vector<std::vector<TrackFeatures>>& trackFeaturesVectors,
0077 const Optionals<typename track_container_t::ConstTrackProxy>& optionals)
0078 const {
0079 std::vector<double> trackScore;
0080 trackScore.reserve(tracks.size());
0081
0082 int iTrack = 0;
0083
0084 ACTS_VERBOSE("Number of detectors: " << m_cfg.detectorConfigs.size());
0085
0086 ACTS_INFO("Starting to score tracks");
0087
0088
0089 for (const auto& track : tracks) {
0090
0091 const auto& trackFeaturesVector = trackFeaturesVectors[iTrack];
0092 double score = 1;
0093 auto eta = Acts::VectorHelpers::eta(track.momentum());
0094
0095
0096 for (const auto& cutFunction : optionals.cuts) {
0097 if (cutFunction(track)) {
0098 score = 0;
0099 ACTS_DEBUG("Track: " << iTrack
0100 << " has score = 0, due to optional cuts.");
0101 break;
0102 }
0103 }
0104
0105 if (score == 0) {
0106 iTrack++;
0107 trackScore.push_back(score);
0108 ACTS_DEBUG("Track: " << iTrack << " score : " << score);
0109 continue;
0110 }
0111
0112
0113 for (std::size_t detectorId = 0; detectorId < m_cfg.detectorConfigs.size();
0114 detectorId++) {
0115 const auto& detector = m_cfg.detectorConfigs.at(detectorId);
0116
0117 const auto& trackFeatures = trackFeaturesVector[detectorId];
0118
0119 ACTS_VERBOSE("---> Found summary information");
0120 ACTS_VERBOSE("---> Detector ID: " << detectorId);
0121 ACTS_VERBOSE("---> Number of hits: " << trackFeatures.nHits);
0122 ACTS_VERBOSE("---> Number of holes: " << trackFeatures.nHoles);
0123 ACTS_VERBOSE("---> Number of outliers: " << trackFeatures.nOutliers);
0124
0125
0126 if (etaBasedCuts(detector, trackFeatures, eta)) {
0127 score = 0;
0128 ACTS_DEBUG("Track: " << iTrack
0129 << " has score = 0, due to detector cuts");
0130 break;
0131 }
0132 }
0133
0134 if (score == 0) {
0135 iTrack++;
0136 trackScore.push_back(score);
0137 ACTS_DEBUG("Track: " << iTrack << " score : " << score);
0138 continue;
0139 }
0140
0141
0142
0143 ACTS_VERBOSE("Using Simple Scoring function");
0144
0145 score = 100;
0146
0147
0148
0149 for (std::size_t detectorId = 0; detectorId < m_cfg.detectorConfigs.size();
0150 detectorId++) {
0151 const auto& detector = m_cfg.detectorConfigs.at(detectorId);
0152 const auto& trackFeatures = trackFeaturesVector[detectorId];
0153
0154 score +=
0155 static_cast<double>(trackFeatures.nHits * detector.hitsScoreWeight);
0156 score +=
0157 static_cast<double>(trackFeatures.nHoles * detector.holesScoreWeight);
0158 score += static_cast<double>(trackFeatures.nOutliers *
0159 detector.outliersScoreWeight);
0160 score += static_cast<double>(trackFeatures.nSharedHits *
0161 detector.otherScoreWeight);
0162 }
0163
0164
0165 for (const auto& weightFunction : optionals.weights) {
0166 weightFunction(track, score);
0167 }
0168
0169
0170 if (track.chi2() > 0 && track.nDoF() > 0) {
0171 double p = 1. / std::log10(10. + 10. * track.chi2() / track.nDoF());
0172 if (p > 0) {
0173 score += p;
0174 } else {
0175 score -= 50;
0176 }
0177 }
0178
0179 iTrack++;
0180
0181
0182 trackScore.push_back(score);
0183 ACTS_VERBOSE("Track: " << iTrack << " score: " << score);
0184
0185 }
0186
0187 return trackScore;
0188 }
0189
0190 template <TrackContainerFrontend track_container_t>
0191 std::vector<double> Acts::ScoreBasedAmbiguityResolution::ambiguityScore(
0192 const track_container_t& tracks,
0193 const std::vector<std::vector<TrackFeatures>>& trackFeaturesVectors,
0194 const Optionals<typename track_container_t::ConstTrackProxy>& optionals)
0195 const {
0196 std::vector<double> trackScore;
0197 trackScore.reserve(tracks.size());
0198
0199 ACTS_VERBOSE("Using Ambiguity Scoring function");
0200
0201 int iTrack = 0;
0202
0203 ACTS_VERBOSE("Number of detectors: " << m_cfg.detectorConfigs.size());
0204
0205 ACTS_INFO("Starting to score tracks");
0206
0207
0208 for (const auto& track : tracks) {
0209
0210 const auto& trackFeaturesVector = trackFeaturesVectors[iTrack];
0211 double score = 1;
0212 auto pT = Acts::VectorHelpers::perp(track.momentum());
0213 auto eta = Acts::VectorHelpers::eta(track.momentum());
0214
0215
0216 for (const auto& cutFunction : optionals.cuts) {
0217 if (cutFunction(track)) {
0218 score = 0;
0219 ACTS_DEBUG("Track: " << iTrack
0220 << " has score = 0, due to optional cuts.");
0221 break;
0222 }
0223 }
0224
0225 if (score == 0) {
0226 iTrack++;
0227 trackScore.push_back(score);
0228 ACTS_DEBUG("Track: " << iTrack << " score : " << score);
0229 continue;
0230 }
0231
0232
0233 for (std::size_t detectorId = 0; detectorId < m_cfg.detectorConfigs.size();
0234 detectorId++) {
0235 const auto& detector = m_cfg.detectorConfigs.at(detectorId);
0236
0237 const auto& trackFeatures = trackFeaturesVector[detectorId];
0238
0239 ACTS_VERBOSE("---> Found summary information");
0240 ACTS_VERBOSE("---> Detector ID: " << detectorId);
0241 ACTS_VERBOSE("---> Number of hits: " << trackFeatures.nHits);
0242 ACTS_VERBOSE("---> Number of holes: " << trackFeatures.nHoles);
0243 ACTS_VERBOSE("---> Number of outliers: " << trackFeatures.nOutliers);
0244
0245
0246 if (etaBasedCuts(detector, trackFeatures, eta)) {
0247 score = 0;
0248 ACTS_DEBUG("Track: " << iTrack
0249 << " has score = 0, due to detector cuts");
0250 break;
0251 }
0252 }
0253
0254 if (score == 0) {
0255 iTrack++;
0256 trackScore.push_back(score);
0257 ACTS_DEBUG("Track: " << iTrack << " score : " << score);
0258 continue;
0259 }
0260
0261
0262
0263
0264 score = std::log10(pT / UnitConstants::MeV) - 1.;
0265
0266 ACTS_DEBUG("Modifier for pT = " << pT << " GeV is : " << score
0267 << " New score now: " << score);
0268
0269 for (std::size_t detectorId = 0; detectorId < m_cfg.detectorConfigs.size();
0270 detectorId++) {
0271 const auto& detector = m_cfg.detectorConfigs.at(detectorId);
0272
0273 const auto& trackFeatures = trackFeaturesVector[detectorId];
0274
0275
0276
0277 std::size_t nHits = trackFeatures.nHits;
0278 if (nHits > detector.maxHits) {
0279 score = score * static_cast<double>(nHits - detector.maxHits +
0280 1);
0281 nHits = detector.maxHits;
0282 }
0283 score = score * detector.factorHits[nHits];
0284 ACTS_DEBUG("Modifier for " << nHits
0285 << " hits: " << detector.factorHits[nHits]
0286 << " New score now: " << score);
0287
0288
0289
0290 std::size_t iHoles = trackFeatures.nHoles;
0291 if (iHoles > detector.maxHoles) {
0292
0293 score /= static_cast<double>(iHoles - detector.maxHoles + 1);
0294 iHoles = detector.maxHoles;
0295 }
0296 score = score * detector.factorHoles[iHoles];
0297 ACTS_DEBUG("Modifier for " << iHoles
0298 << " holes: " << detector.factorHoles[iHoles]
0299 << " New score now: " << score);
0300 }
0301
0302 for (const auto& scoreFunction : optionals.scores) {
0303 scoreFunction(track, score);
0304 }
0305
0306 if (track.chi2() > 0 && track.nDoF() > 0) {
0307 double chi2 = track.chi2();
0308 int indf = track.nDoF();
0309 double fac = 1. / std::log10(10. + 10. * chi2 / indf);
0310 score = score * fac;
0311 ACTS_DEBUG("Modifier for chi2 = " << chi2 << " and NDF = " << indf
0312 << " is : " << fac
0313 << " New score now: " << score);
0314 }
0315
0316 iTrack++;
0317
0318
0319 trackScore.push_back(score);
0320 ACTS_VERBOSE("Track: " << iTrack << " score: " << score);
0321
0322 }
0323
0324 return trackScore;
0325 }
0326
0327 template <TrackContainerFrontend track_container_t, typename source_link_hash_t,
0328 typename source_link_equality_t>
0329 std::vector<int> Acts::ScoreBasedAmbiguityResolution::solveAmbiguity(
0330 const track_container_t& tracks, source_link_hash_t sourceLinkHash,
0331 source_link_equality_t sourceLinkEquality,
0332 const Optionals<typename track_container_t::ConstTrackProxy>& optionals)
0333 const {
0334 ACTS_INFO("Number of tracks before Ambiguty Resolution: " << tracks.size());
0335
0336
0337
0338 const std::vector<std::vector<TrackFeatures>> trackFeaturesVectors =
0339 computeInitialState<track_container_t>(tracks);
0340
0341 std::vector<double> trackScore;
0342 trackScore.reserve(tracks.size());
0343 if (m_cfg.useAmbiguityScoring) {
0344 trackScore = ambiguityScore(tracks, trackFeaturesVectors, optionals);
0345 } else {
0346 trackScore = simpleScore(tracks, trackFeaturesVectors, optionals);
0347 }
0348
0349 auto MeasurementIndexMap =
0350 std::unordered_map<SourceLink, std::size_t, source_link_hash_t,
0351 source_link_equality_t>(0, sourceLinkHash,
0352 sourceLinkEquality);
0353
0354 std::vector<std::vector<std::size_t>> measurementsPerTrackVector;
0355 std::map<std::size_t, std::size_t> nTracksPerMeasurement;
0356
0357
0358
0359
0360
0361 for (const auto& track : tracks) {
0362 std::vector<std::size_t> measurementsPerTrack;
0363 for (const auto& ts : track.trackStatesReversed()) {
0364 if (!ts.typeFlags().isOutlier() && !ts.typeFlags().isMeasurement()) {
0365 continue;
0366 }
0367 Acts::SourceLink sourceLink = ts.getUncalibratedSourceLink();
0368
0369 auto emplace = MeasurementIndexMap.try_emplace(
0370 sourceLink, MeasurementIndexMap.size());
0371 std::size_t iMeasurement = emplace.first->second;
0372 measurementsPerTrack.push_back(iMeasurement);
0373 if (nTracksPerMeasurement.find(iMeasurement) ==
0374 nTracksPerMeasurement.end()) {
0375 nTracksPerMeasurement[iMeasurement] = 0;
0376 }
0377 nTracksPerMeasurement[iMeasurement]++;
0378 }
0379 measurementsPerTrackVector.push_back(std::move(measurementsPerTrack));
0380 }
0381
0382 std::vector<int> goodTracks;
0383 int cleanTrackIndex = 0;
0384
0385 auto optionalHitSelections = optionals.hitSelections;
0386
0387
0388
0389
0390 for (std::size_t iTrack = 0; const auto& track : tracks) {
0391
0392 if (getCleanedOutTracks(track, trackScore[iTrack],
0393 measurementsPerTrackVector[iTrack],
0394 nTracksPerMeasurement, optionalHitSelections)) {
0395 cleanTrackIndex++;
0396 if (trackScore[iTrack] > m_cfg.minScore) {
0397 goodTracks.push_back(track.index());
0398 }
0399 }
0400 iTrack++;
0401 }
0402 ACTS_INFO("Number of clean tracks: " << cleanTrackIndex);
0403 ACTS_VERBOSE("Min score: " << m_cfg.minScore);
0404 ACTS_INFO("Number of Good tracks: " << goodTracks.size());
0405 return goodTracks;
0406 }
0407
0408 template <TrackProxyConcept track_proxy_t>
0409 bool Acts::ScoreBasedAmbiguityResolution::getCleanedOutTracks(
0410 const track_proxy_t& track, const double& trackScore,
0411 const std::vector<std::size_t>& measurementsPerTrack,
0412 const std::map<std::size_t, std::size_t>& nTracksPerMeasurement,
0413 const std::vector<
0414 std::function<void(const track_proxy_t&,
0415 const typename track_proxy_t::ConstTrackStateProxy&,
0416 TrackStateTypes&)>>& optionalHitSelections) const {
0417
0418
0419 std::vector<TrackStateTypes> trackStateTypes;
0420
0421
0422 for (std::size_t index = 0; const auto& ts : track.trackStatesReversed()) {
0423 if (ts.typeFlags().isOutlier() || ts.typeFlags().isMeasurement()) {
0424 std::size_t iMeasurement = measurementsPerTrack[index];
0425 auto it = nTracksPerMeasurement.find(iMeasurement);
0426 if (it == nTracksPerMeasurement.end()) {
0427 trackStateTypes.push_back(TrackStateTypes::OtherTrackStateType);
0428 index++;
0429 continue;
0430 }
0431
0432 std::size_t nTracksShared = it->second;
0433 auto isoutliner = ts.typeFlags().isOutlier();
0434
0435 if (isoutliner) {
0436 ACTS_VERBOSE("Measurement is outlier on a fitter track, copy it over");
0437 trackStateTypes.push_back(TrackStateTypes::Outlier);
0438 continue;
0439 }
0440 if (nTracksShared == 1) {
0441 ACTS_VERBOSE("Measurement is not shared, copy it over");
0442
0443 trackStateTypes.push_back(TrackStateTypes::UnsharedHit);
0444 continue;
0445 } else if (nTracksShared > 1) {
0446 ACTS_VERBOSE("Measurement is shared, copy it over");
0447 trackStateTypes.push_back(TrackStateTypes::SharedHit);
0448 continue;
0449 }
0450 }
0451 }
0452 std::vector<std::size_t> newMeasurementsPerTrack;
0453 std::size_t measurement = 0;
0454 std::size_t nshared = 0;
0455
0456
0457
0458
0459 for (std::size_t index = 0; auto ts : track.trackStatesReversed()) {
0460 if (ts.typeFlags().isOutlier() || ts.typeFlags().isMeasurement()) {
0461 if (!ts.hasReferenceSurface()) {
0462 ACTS_DEBUG("Track state has no reference surface");
0463 continue;
0464 }
0465
0466 std::size_t ivolume = ts.referenceSurface().geometryId().volume();
0467 auto volume_it = m_cfg.volumeMap.find(ivolume);
0468 if (volume_it == m_cfg.volumeMap.end()) {
0469 ACTS_ERROR("Volume " << ivolume << " not found in the volume map");
0470 continue;
0471 }
0472
0473 std::size_t detectorID = volume_it->second;
0474
0475 const auto& detector = m_cfg.detectorConfigs.at(detectorID);
0476
0477 measurement = measurementsPerTrack[index];
0478
0479 auto it = nTracksPerMeasurement.find(measurement);
0480 if (it == nTracksPerMeasurement.end()) {
0481 index++;
0482 continue;
0483 }
0484 auto nTracksShared = it->second;
0485
0486
0487
0488
0489 for (const auto& hitSelection : optionalHitSelections) {
0490 hitSelection(track, ts, trackStateTypes[index]);
0491 }
0492
0493 if (trackStateTypes[index] == TrackStateTypes::RejectedHit) {
0494 ACTS_DEBUG("Dropping rejected hit");
0495 } else if (trackStateTypes[index] != TrackStateTypes::SharedHit) {
0496 ACTS_DEBUG("Good TSOS, copy hit");
0497 newMeasurementsPerTrack.push_back(measurement);
0498
0499
0500
0501 } else if (nshared >= m_cfg.maxShared) {
0502 ACTS_DEBUG("Too many shared hit, drop it");
0503 }
0504
0505
0506 else {
0507 ACTS_DEBUG("Try to recover shared hit ");
0508 if (nTracksShared <= m_cfg.maxSharedTracksPerMeasurement &&
0509 trackScore > m_cfg.minScoreSharedTracks &&
0510 !detector.sharedHitsFlag) {
0511 ACTS_DEBUG("Accepted hit shared with " << nTracksShared << " tracks");
0512 newMeasurementsPerTrack.push_back(measurement);
0513 nshared++;
0514 } else {
0515 ACTS_DEBUG("Rejected hit shared with " << nTracksShared << " tracks");
0516 }
0517 }
0518 index++;
0519 }
0520 }
0521
0522 if (newMeasurementsPerTrack.size() < m_cfg.minUnshared) {
0523 return false;
0524 } else {
0525 return true;
0526 }
0527 }
0528
0529 }