File indexing completed on 2026-07-16 08:07:25
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 int 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 += trackFeatures.nHits * detector.hitsScoreWeight;
0155 score += trackFeatures.nHoles * detector.holesScoreWeight;
0156 score += trackFeatures.nOutliers * detector.outliersScoreWeight;
0157 score += trackFeatures.nSharedHits * detector.otherScoreWeight;
0158 }
0159
0160
0161 for (const auto& weightFunction : optionals.weights) {
0162 weightFunction(track, score);
0163 }
0164
0165
0166 if (track.chi2() > 0 && track.nDoF() > 0) {
0167 double p = 1. / std::log10(10. + 10. * track.chi2() / track.nDoF());
0168 if (p > 0) {
0169 score += p;
0170 } else {
0171 score -= 50;
0172 }
0173 }
0174
0175 iTrack++;
0176
0177
0178 trackScore.push_back(score);
0179 ACTS_VERBOSE("Track: " << iTrack << " score: " << score);
0180
0181 }
0182
0183 return trackScore;
0184 }
0185
0186 template <TrackContainerFrontend track_container_t>
0187 std::vector<double> Acts::ScoreBasedAmbiguityResolution::ambiguityScore(
0188 const track_container_t& tracks,
0189 const std::vector<std::vector<TrackFeatures>>& trackFeaturesVectors,
0190 const Optionals<typename track_container_t::ConstTrackProxy>& optionals)
0191 const {
0192 std::vector<double> trackScore;
0193 trackScore.reserve(tracks.size());
0194
0195 ACTS_VERBOSE("Using Ambiguity Scoring function");
0196
0197 int iTrack = 0;
0198
0199 ACTS_VERBOSE("Number of detectors: " << m_cfg.detectorConfigs.size());
0200
0201 ACTS_INFO("Starting to score tracks");
0202
0203
0204 for (const auto& track : tracks) {
0205
0206 const auto& trackFeaturesVector = trackFeaturesVectors[iTrack];
0207 double score = 1;
0208 auto pT = Acts::VectorHelpers::perp(track.momentum());
0209 auto eta = Acts::VectorHelpers::eta(track.momentum());
0210
0211
0212 for (const auto& cutFunction : optionals.cuts) {
0213 if (cutFunction(track)) {
0214 score = 0;
0215 ACTS_DEBUG("Track: " << iTrack
0216 << " has score = 0, due to optional cuts.");
0217 break;
0218 }
0219 }
0220
0221 if (score == 0) {
0222 iTrack++;
0223 trackScore.push_back(score);
0224 ACTS_DEBUG("Track: " << iTrack << " score : " << score);
0225 continue;
0226 }
0227
0228
0229 for (std::size_t detectorId = 0; detectorId < m_cfg.detectorConfigs.size();
0230 detectorId++) {
0231 const auto& detector = m_cfg.detectorConfigs.at(detectorId);
0232
0233 const auto& trackFeatures = trackFeaturesVector[detectorId];
0234
0235 ACTS_VERBOSE("---> Found summary information");
0236 ACTS_VERBOSE("---> Detector ID: " << detectorId);
0237 ACTS_VERBOSE("---> Number of hits: " << trackFeatures.nHits);
0238 ACTS_VERBOSE("---> Number of holes: " << trackFeatures.nHoles);
0239 ACTS_VERBOSE("---> Number of outliers: " << trackFeatures.nOutliers);
0240
0241
0242 if (etaBasedCuts(detector, trackFeatures, eta)) {
0243 score = 0;
0244 ACTS_DEBUG("Track: " << iTrack
0245 << " has score = 0, due to detector cuts");
0246 break;
0247 }
0248 }
0249
0250 if (score == 0) {
0251 iTrack++;
0252 trackScore.push_back(score);
0253 ACTS_DEBUG("Track: " << iTrack << " score : " << score);
0254 continue;
0255 }
0256
0257
0258
0259
0260 score = std::log10(pT / UnitConstants::MeV) - 1.;
0261
0262 ACTS_DEBUG("Modifier for pT = " << pT << " GeV is : " << score
0263 << " New score now: " << score);
0264
0265 for (std::size_t detectorId = 0; detectorId < m_cfg.detectorConfigs.size();
0266 detectorId++) {
0267 const auto& detector = m_cfg.detectorConfigs.at(detectorId);
0268
0269 const auto& trackFeatures = trackFeaturesVector[detectorId];
0270
0271
0272
0273 std::size_t nHits = trackFeatures.nHits;
0274 if (nHits > detector.maxHits) {
0275 score = score * (nHits - detector.maxHits + 1);
0276 nHits = detector.maxHits;
0277 }
0278 score = score * detector.factorHits[nHits];
0279 ACTS_DEBUG("Modifier for " << nHits
0280 << " hits: " << detector.factorHits[nHits]
0281 << " New score now: " << score);
0282
0283
0284
0285 std::size_t iHoles = trackFeatures.nHoles;
0286 if (iHoles > detector.maxHoles) {
0287 score /= (iHoles - detector.maxHoles + 1);
0288 iHoles = detector.maxHoles;
0289 }
0290 score = score * detector.factorHoles[iHoles];
0291 ACTS_DEBUG("Modifier for " << iHoles
0292 << " holes: " << detector.factorHoles[iHoles]
0293 << " New score now: " << score);
0294 }
0295
0296 for (const auto& scoreFunction : optionals.scores) {
0297 scoreFunction(track, score);
0298 }
0299
0300 if (track.chi2() > 0 && track.nDoF() > 0) {
0301 double chi2 = track.chi2();
0302 int indf = track.nDoF();
0303 double fac = 1. / std::log10(10. + 10. * chi2 / indf);
0304 score = score * fac;
0305 ACTS_DEBUG("Modifier for chi2 = " << chi2 << " and NDF = " << indf
0306 << " is : " << fac
0307 << " New score now: " << score);
0308 }
0309
0310 iTrack++;
0311
0312
0313 trackScore.push_back(score);
0314 ACTS_VERBOSE("Track: " << iTrack << " score: " << score);
0315
0316 }
0317
0318 return trackScore;
0319 }
0320
0321 template <TrackContainerFrontend track_container_t, typename source_link_hash_t,
0322 typename source_link_equality_t>
0323 std::vector<int> Acts::ScoreBasedAmbiguityResolution::solveAmbiguity(
0324 const track_container_t& tracks, source_link_hash_t sourceLinkHash,
0325 source_link_equality_t sourceLinkEquality,
0326 const Optionals<typename track_container_t::ConstTrackProxy>& optionals)
0327 const {
0328 ACTS_INFO("Number of tracks before Ambiguty Resolution: " << tracks.size());
0329
0330
0331
0332 const std::vector<std::vector<TrackFeatures>> trackFeaturesVectors =
0333 computeInitialState<track_container_t>(tracks);
0334
0335 std::vector<double> trackScore;
0336 trackScore.reserve(tracks.size());
0337 if (m_cfg.useAmbiguityScoring) {
0338 trackScore = ambiguityScore(tracks, trackFeaturesVectors, optionals);
0339 } else {
0340 trackScore = simpleScore(tracks, trackFeaturesVectors, optionals);
0341 }
0342
0343 auto MeasurementIndexMap =
0344 std::unordered_map<SourceLink, std::size_t, source_link_hash_t,
0345 source_link_equality_t>(0, sourceLinkHash,
0346 sourceLinkEquality);
0347
0348 std::vector<std::vector<std::size_t>> measurementsPerTrackVector;
0349 std::map<std::size_t, std::size_t> nTracksPerMeasurement;
0350
0351
0352
0353
0354
0355 for (const auto& track : tracks) {
0356 std::vector<std::size_t> measurementsPerTrack;
0357 for (const auto& ts : track.trackStatesReversed()) {
0358 if (!ts.typeFlags().isOutlier() && !ts.typeFlags().isMeasurement()) {
0359 continue;
0360 }
0361 Acts::SourceLink sourceLink = ts.getUncalibratedSourceLink();
0362
0363 auto emplace = MeasurementIndexMap.try_emplace(
0364 sourceLink, MeasurementIndexMap.size());
0365 std::size_t iMeasurement = emplace.first->second;
0366 measurementsPerTrack.push_back(iMeasurement);
0367 if (nTracksPerMeasurement.find(iMeasurement) ==
0368 nTracksPerMeasurement.end()) {
0369 nTracksPerMeasurement[iMeasurement] = 0;
0370 }
0371 nTracksPerMeasurement[iMeasurement]++;
0372 }
0373 measurementsPerTrackVector.push_back(std::move(measurementsPerTrack));
0374 }
0375
0376 std::vector<int> goodTracks;
0377 int cleanTrackIndex = 0;
0378
0379 auto optionalHitSelections = optionals.hitSelections;
0380
0381
0382
0383
0384 for (std::size_t iTrack = 0; const auto& track : tracks) {
0385
0386 if (getCleanedOutTracks(track, trackScore[iTrack],
0387 measurementsPerTrackVector[iTrack],
0388 nTracksPerMeasurement, optionalHitSelections)) {
0389 cleanTrackIndex++;
0390 if (trackScore[iTrack] > m_cfg.minScore) {
0391 goodTracks.push_back(track.index());
0392 }
0393 }
0394 iTrack++;
0395 }
0396 ACTS_INFO("Number of clean tracks: " << cleanTrackIndex);
0397 ACTS_VERBOSE("Min score: " << m_cfg.minScore);
0398 ACTS_INFO("Number of Good tracks: " << goodTracks.size());
0399 return goodTracks;
0400 }
0401
0402 template <TrackProxyConcept track_proxy_t>
0403 bool Acts::ScoreBasedAmbiguityResolution::getCleanedOutTracks(
0404 const track_proxy_t& track, const double& trackScore,
0405 const std::vector<std::size_t>& measurementsPerTrack,
0406 const std::map<std::size_t, std::size_t>& nTracksPerMeasurement,
0407 const std::vector<
0408 std::function<void(const track_proxy_t&,
0409 const typename track_proxy_t::ConstTrackStateProxy&,
0410 TrackStateTypes&)>>& optionalHitSelections) const {
0411
0412
0413 std::vector<TrackStateTypes> trackStateTypes;
0414
0415
0416 for (std::size_t index = 0; const auto& ts : track.trackStatesReversed()) {
0417 if (ts.typeFlags().isOutlier() || ts.typeFlags().isMeasurement()) {
0418 std::size_t iMeasurement = measurementsPerTrack[index];
0419 auto it = nTracksPerMeasurement.find(iMeasurement);
0420 if (it == nTracksPerMeasurement.end()) {
0421 trackStateTypes.push_back(TrackStateTypes::OtherTrackStateType);
0422 index++;
0423 continue;
0424 }
0425
0426 std::size_t nTracksShared = it->second;
0427 auto isoutliner = ts.typeFlags().isOutlier();
0428
0429 if (isoutliner) {
0430 ACTS_VERBOSE("Measurement is outlier on a fitter track, copy it over");
0431 trackStateTypes.push_back(TrackStateTypes::Outlier);
0432 continue;
0433 }
0434 if (nTracksShared == 1) {
0435 ACTS_VERBOSE("Measurement is not shared, copy it over");
0436
0437 trackStateTypes.push_back(TrackStateTypes::UnsharedHit);
0438 continue;
0439 } else if (nTracksShared > 1) {
0440 ACTS_VERBOSE("Measurement is shared, copy it over");
0441 trackStateTypes.push_back(TrackStateTypes::SharedHit);
0442 continue;
0443 }
0444 }
0445 }
0446 std::vector<std::size_t> newMeasurementsPerTrack;
0447 std::size_t measurement = 0;
0448 std::size_t nshared = 0;
0449
0450
0451
0452
0453 for (std::size_t index = 0; auto ts : track.trackStatesReversed()) {
0454 if (ts.typeFlags().isOutlier() || ts.typeFlags().isMeasurement()) {
0455 if (!ts.hasReferenceSurface()) {
0456 ACTS_DEBUG("Track state has no reference surface");
0457 continue;
0458 }
0459
0460 std::size_t ivolume = ts.referenceSurface().geometryId().volume();
0461 auto volume_it = m_cfg.volumeMap.find(ivolume);
0462 if (volume_it == m_cfg.volumeMap.end()) {
0463 ACTS_ERROR("Volume " << ivolume << " not found in the volume map");
0464 continue;
0465 }
0466
0467 std::size_t detectorID = volume_it->second;
0468
0469 const auto& detector = m_cfg.detectorConfigs.at(detectorID);
0470
0471 measurement = measurementsPerTrack[index];
0472
0473 auto it = nTracksPerMeasurement.find(measurement);
0474 if (it == nTracksPerMeasurement.end()) {
0475 index++;
0476 continue;
0477 }
0478 auto nTracksShared = it->second;
0479
0480
0481
0482
0483 for (const auto& hitSelection : optionalHitSelections) {
0484 hitSelection(track, ts, trackStateTypes[index]);
0485 }
0486
0487 if (trackStateTypes[index] == TrackStateTypes::RejectedHit) {
0488 ACTS_DEBUG("Dropping rejected hit");
0489 } else if (trackStateTypes[index] != TrackStateTypes::SharedHit) {
0490 ACTS_DEBUG("Good TSOS, copy hit");
0491 newMeasurementsPerTrack.push_back(measurement);
0492
0493
0494
0495 } else if (nshared >= m_cfg.maxShared) {
0496 ACTS_DEBUG("Too many shared hit, drop it");
0497 }
0498
0499
0500 else {
0501 ACTS_DEBUG("Try to recover shared hit ");
0502 if (nTracksShared <= m_cfg.maxSharedTracksPerMeasurement &&
0503 trackScore > m_cfg.minScoreSharedTracks &&
0504 !detector.sharedHitsFlag) {
0505 ACTS_DEBUG("Accepted hit shared with " << nTracksShared << " tracks");
0506 newMeasurementsPerTrack.push_back(measurement);
0507 nshared++;
0508 } else {
0509 ACTS_DEBUG("Rejected hit shared with " << nTracksShared << " tracks");
0510 }
0511 }
0512 index++;
0513 }
0514 }
0515
0516 if (newMeasurementsPerTrack.size() < m_cfg.minUnshared) {
0517 return false;
0518 } else {
0519 return true;
0520 }
0521 }
0522
0523 }