Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-21 07:59:53

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
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   // Loop over all the tracks in the container
0089   for (const auto& track : tracks) {
0090     // get the trackFeatures map for the track
0091     const auto& trackFeaturesVector = trackFeaturesVectors[iTrack];
0092     double score = 1;
0093     auto eta = Acts::VectorHelpers::eta(track.momentum());
0094 
0095     // cuts on optional cuts
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     // Reject tracks which didn't pass the detector cuts.
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       // eta based cuts
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     // All cuts passed, now start scoring the track
0142 
0143     ACTS_VERBOSE("Using Simple Scoring function");
0144 
0145     score = 100;
0146     // Adding the score for each detector.
0147     // detector score is determined by the number of hits/hole/outliers *
0148     // hit/hole/outlier scoreWeights in a detector.
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     // Adding scores based on optional weights
0165     for (const auto& weightFunction : optionals.weights) {
0166       weightFunction(track, score);
0167     }
0168 
0169     // Adding the score based on the chi2/ndf
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     // Add the score to the vector
0182     trackScore.push_back(score);
0183     ACTS_VERBOSE("Track: " << iTrack << " score: " << score);
0184 
0185   }  // end of loop over tracks
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   // Loop over all the tracks in the container
0208   for (const auto& track : tracks) {
0209     // get the trackFeatures map for the track
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     // cuts on optional cuts
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     // Reject tracks which didn't pass the detector cuts.
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       // eta based cuts
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     // All cuts passed, now start scoring the track
0262 
0263     // start with larger score for tracks with higher pT.
0264     score = std::log10(pT / UnitConstants::MeV) - 1.;
0265     // pT in GeV, hence 100 MeV is minimum and gets score = 1
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       // choosing a scaling factor based on the number of hits in a track per
0276       // detector.
0277       std::size_t nHits = trackFeatures.nHits;
0278       if (nHits > detector.maxHits) {
0279         score = score * static_cast<double>(nHits - detector.maxHits +
0280                                             1);  // hits are good !
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       // choosing a scaling factor based on the number of holes in a track per
0289       // detector.
0290       std::size_t iHoles = trackFeatures.nHoles;
0291       if (iHoles > detector.maxHoles) {
0292         // holes are bad !
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     // Add the score to the vector
0319     trackScore.push_back(score);
0320     ACTS_VERBOSE("Track: " << iTrack << " score: " << score);
0321 
0322   }  // end of loop over tracks
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   // vector of trackFeaturesVectors. where each trackFeaturesVector contains the
0336   // number of hits/hole/outliers for each detector in a track.
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   // Stores tracks measurement into a vector or vectors
0358   // (measurementsPerTrackVector) and counts the number of tracks per
0359   // measurement.(nTracksPerMeasurement)
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       // assign a new measurement index if the source link was not seen yet
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   // Loop over all the tracks in the container
0388   // For each track, check if the track has too many shared hits to be accepted.
0389   // If the track is good, add it to the goodTracks
0390   for (std::size_t iTrack = 0; const auto& track : tracks) {
0391     // Check if the track has too many shared hits to be accepted.
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   // For tracks with shared hits, we need to check and remove bad hits
0418 
0419   std::vector<TrackStateTypes> trackStateTypes;
0420   // Loop over all measurements of the track and for each hit a
0421   // trackStateTypes is assigned.
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   // Loop over all measurements of the track and process them according to the
0457   // trackStateTypes and other conditions.
0458   // Good measurements are copied to the newMeasurementsPerTrack vector.
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       // Loop over all optionalHitSelections and apply them to trackStateType of
0487       // the TrackState.
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         // a counter called nshared is used to keep track of the number of
0500         // shared hits accepted.
0501       } else if (nshared >= m_cfg.maxShared) {
0502         ACTS_DEBUG("Too many shared hit, drop it");
0503       }
0504       // If the track is shared, the hit is only accepted if the track has
0505       // score higher than the minimum score for shared tracks.
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   // Check if the track has enough hits to be accepted.
0522   if (newMeasurementsPerTrack.size() < m_cfg.minUnshared) {
0523     return false;
0524   } else {
0525     return true;
0526   }
0527 }
0528 
0529 }  // namespace Acts