Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-05 08:23:23

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 #include "ActsExamples/Validation/PatternRecognitionPerformanceCollector.hpp"
0010 
0011 #include "Acts/EventData/BoundTrackParameters.hpp"
0012 #include "Acts/Utilities/Logger.hpp"
0013 #include "Acts/Utilities/VectorHelpers.hpp"
0014 
0015 #include <format>
0016 #include <utility>
0017 
0018 namespace ActsExamples {
0019 
0020 PatternRecognitionPerformanceCollector::PatternRecognitionPerformanceCollector(
0021     Config cfg, std::unique_ptr<const Acts::Logger> logger)
0022     : m_cfg(std::move(cfg)),
0023       m_logger(std::move(logger)),
0024       m_effPlotTool([&]() {
0025         auto c = m_cfg.effPlotToolConfig;
0026         c.label = m_cfg.label;
0027         return EffPlotTool(c, m_logger->level());
0028       }()),
0029       m_fakePlotTool([&]() {
0030         auto c = m_cfg.fakePlotToolConfig;
0031         c.label = m_cfg.label;
0032         return FakePlotTool(c, m_logger->level());
0033       }()),
0034       m_duplicationPlotTool([&]() {
0035         auto c = m_cfg.duplicationPlotToolConfig;
0036         c.label = m_cfg.label;
0037         return DuplicationPlotTool(c, m_logger->level());
0038       }()),
0039       m_trackSummaryPlotTool(m_cfg.trackSummaryPlotToolConfig,
0040                              m_logger->level()),
0041       m_trackQualityPlotTool(m_cfg.trackQualityPlotToolConfig,
0042                              m_logger->level()) {
0043   for (const auto& [key, _] : m_cfg.subDetectorTrackSummaryVolumes) {
0044     TrackSummaryPlotTool::Config subConfig = m_cfg.trackSummaryPlotToolConfig;
0045     subConfig.prefix = key;
0046     m_subDetectorSummaryTools.emplace(
0047         std::piecewise_construct, std::forward_as_tuple(key),
0048         std::forward_as_tuple(subConfig, m_logger->level()));
0049   }
0050 }
0051 
0052 void PatternRecognitionPerformanceCollector::fill(
0053     const Acts::GeometryContext& geoContext, const ConstTrackContainer& tracks,
0054     const SimParticleContainer& particles,
0055     const TrackParticleMatching& trackParticleMatching,
0056     const ParticleTrackMatching& particleTrackMatching,
0057     const InverseMultimap<SimBarcode>& particleMeasurementsMap) {
0058   std::size_t unmatched = 0;
0059   std::size_t missingRefSurface = 0;
0060 
0061   std::string labelPlural = std::format("{}s", m_cfg.label);
0062 
0063   for (const auto& track : tracks) {
0064     m_stats.nTotalTracks++;
0065 
0066     if (!track.hasReferenceSurface()) {
0067       missingRefSurface++;
0068       continue;
0069     }
0070 
0071     Acts::BoundTrackParameters fittedParameters =
0072         track.createParametersAtReference();
0073 
0074     m_trackSummaryPlotTool.fill(fittedParameters, track.nTrackStates(),
0075                                 track.nMeasurements(), track.nOutliers(),
0076                                 track.nHoles(), track.nSharedHits());
0077 
0078     for (const auto& [key, volumes] : m_cfg.subDetectorTrackSummaryVolumes) {
0079       std::size_t nTrackStates{};
0080       std::size_t nMeasurements{};
0081       std::size_t nOutliers{};
0082       std::size_t nHoles{};
0083       std::size_t nSharedHits{};
0084 
0085       for (auto state : track.trackStatesReversed()) {
0086         if (!state.hasReferenceSurface() ||
0087             !volumes.contains(state.referenceSurface().geometryId().volume())) {
0088           continue;
0089         }
0090         nTrackStates++;
0091         nMeasurements +=
0092             static_cast<std::size_t>(state.typeFlags().isMeasurement());
0093         nOutliers += static_cast<std::size_t>(state.typeFlags().isOutlier());
0094         nHoles += static_cast<std::size_t>(state.typeFlags().isHole());
0095         nSharedHits +=
0096             static_cast<std::size_t>(state.typeFlags().isSharedHit());
0097       }
0098       m_subDetectorSummaryTools.at(key).fill(fittedParameters, nTrackStates,
0099                                              nMeasurements, nOutliers, nHoles,
0100                                              nSharedHits);
0101     }
0102 
0103     auto imatched = trackParticleMatching.find(track.index());
0104     if (imatched == trackParticleMatching.end()) {
0105       unmatched++;
0106       continue;
0107     }
0108 
0109     const auto& particleMatch = imatched->second;
0110 
0111     if (particleMatch.classification == TrackMatchClassification::Fake) {
0112       m_stats.nTotalFakeTracks++;
0113     }
0114     if (particleMatch.classification == TrackMatchClassification::Duplicate) {
0115       m_stats.nTotalDuplicateTracks++;
0116     }
0117 
0118     m_fakePlotTool.fill(fittedParameters, particleMatch.classification ==
0119                                               TrackMatchClassification::Fake);
0120     m_duplicationPlotTool.fill(
0121         fittedParameters,
0122         particleMatch.classification == TrackMatchClassification::Duplicate);
0123 
0124     if (particleMatch.particle.has_value() &&
0125         particleMeasurementsMap.contains(particleMatch.particle.value())) {
0126       const auto measurements =
0127           particleMeasurementsMap.equal_range(particleMatch.particle.value());
0128 
0129       std::size_t nTrackMeasurements =
0130           track.nMeasurements() + track.nOutliers();
0131       std::size_t nMatchedHits =
0132           particleMatch.contributingParticles.front().hitCount;
0133       std::size_t nParticleHits =
0134           std::distance(measurements.first, measurements.second);
0135 
0136       double completeness = static_cast<double>(nMatchedHits) / nParticleHits;
0137       double purity = static_cast<double>(nMatchedHits) / nTrackMeasurements;
0138 
0139       m_trackQualityPlotTool.fill(fittedParameters, completeness, purity);
0140     }
0141   }
0142 
0143   if (unmatched > 0) {
0144     ACTS_VERBOSE("No matching information found for " << unmatched << " "
0145                                                       << labelPlural);
0146   }
0147   if (missingRefSurface > 0) {
0148     ACTS_VERBOSE("Reference surface was missing for " << missingRefSurface
0149                                                       << " " << labelPlural);
0150   }
0151 
0152   for (const auto& particle : particles) {
0153     auto particleId = particle.particleId();
0154 
0155     std::size_t nMatchedTracks = 0;
0156     std::size_t nFakeTracks = 0;
0157     bool isReconstructed = false;
0158     if (auto imatched = particleTrackMatching.find(particleId);
0159         imatched != particleTrackMatching.end()) {
0160       isReconstructed = imatched->second.track.has_value();
0161       nMatchedTracks = (isReconstructed ? 1 : 0) + imatched->second.duplicates;
0162 
0163       m_stats.nTotalMatchedTracks += nMatchedTracks;
0164       m_stats.nTotalMatchedParticles += isReconstructed ? 1 : 0;
0165 
0166       if (nMatchedTracks > 1) {
0167         m_stats.nTotalDuplicateParticles += 1;
0168       }
0169 
0170       nFakeTracks = imatched->second.fakes;
0171       if (nFakeTracks > 0) {
0172         m_stats.nTotalFakeParticles += 1;
0173       }
0174     }
0175 
0176     double minDeltaR = -1;
0177     for (const auto& closeParticle : particles) {
0178       if (closeParticle.particleId() == particleId) {
0179         continue;
0180       }
0181       double distance = Acts::VectorHelpers::deltaR(particle.direction(),
0182                                                     closeParticle.direction());
0183       if (minDeltaR == -1 || distance < minDeltaR) {
0184         minDeltaR = distance;
0185       }
0186     }
0187 
0188     m_effPlotTool.fill(geoContext, particle.initialState(), minDeltaR,
0189                        isReconstructed);
0190     m_duplicationPlotTool.fill(particle.initialState(), nMatchedTracks);
0191     m_fakePlotTool.fill(particle.initialState(), nMatchedTracks, nFakeTracks);
0192 
0193     m_stats.nTotalParticles += 1;
0194   }
0195 }
0196 
0197 void PatternRecognitionPerformanceCollector::logSummary() const {
0198   std::string labelPlural = std::format("{}s", m_cfg.label);
0199 
0200   const Acts::Logger& log = *m_logger;
0201   float eff_tracks =
0202       static_cast<float>(m_stats.nTotalMatchedTracks) / m_stats.nTotalTracks;
0203   float fakeRatio_tracks =
0204       static_cast<float>(m_stats.nTotalFakeTracks) / m_stats.nTotalTracks;
0205   float duplicationRatio_tracks =
0206       static_cast<float>(m_stats.nTotalDuplicateTracks) / m_stats.nTotalTracks;
0207 
0208   float eff_particle = static_cast<float>(m_stats.nTotalMatchedParticles) /
0209                        m_stats.nTotalParticles;
0210   float fakeRatio_particle =
0211       static_cast<float>(m_stats.nTotalFakeParticles) / m_stats.nTotalParticles;
0212   float duplicationRatio_particle =
0213       static_cast<float>(m_stats.nTotalDuplicateParticles) /
0214       m_stats.nTotalParticles;
0215 
0216   ACTS_LOG_WITH_LOGGER(
0217       log, Acts::Logging::DEBUG,
0218       "nTotal" << m_cfg.label << "s                = " << m_stats.nTotalTracks);
0219   ACTS_LOG_WITH_LOGGER(log, Acts::Logging::DEBUG,
0220                        "nTotalMatched" << m_cfg.label << "s         = "
0221                                        << m_stats.nTotalMatchedTracks);
0222   ACTS_LOG_WITH_LOGGER(log, Acts::Logging::DEBUG,
0223                        "nTotalDuplicate" << m_cfg.label << "s       = "
0224                                          << m_stats.nTotalDuplicateTracks);
0225   ACTS_LOG_WITH_LOGGER(log, Acts::Logging::DEBUG,
0226                        "nTotalFake" << m_cfg.label << "s            = "
0227                                     << m_stats.nTotalFakeTracks);
0228 
0229   ACTS_LOG_WITH_LOGGER(log, Acts::Logging::INFO,
0230                        "Efficiency with "
0231                            << labelPlural << " (nMatched" << m_cfg.label
0232                            << "s/nAll" << m_cfg.label << "s) = " << eff_tracks);
0233   ACTS_LOG_WITH_LOGGER(
0234       log, Acts::Logging::INFO,
0235       "Fake ratio with " << labelPlural << " (nFake" << m_cfg.label << "s/nAll"
0236                          << m_cfg.label << "s) = " << fakeRatio_tracks);
0237   ACTS_LOG_WITH_LOGGER(log, Acts::Logging::INFO,
0238                        "Duplicate ratio with "
0239                            << labelPlural << " (nDuplicate" << m_cfg.label
0240                            << "s/nAll" << m_cfg.label
0241                            << "s) = " << duplicationRatio_tracks);
0242   ACTS_LOG_WITH_LOGGER(
0243       log, Acts::Logging::INFO,
0244       "Efficiency with particles (nMatchedParticles/nTrueParticles) = "
0245           << eff_particle);
0246   ACTS_LOG_WITH_LOGGER(
0247       log, Acts::Logging::INFO,
0248       "Fake ratio with particles (nFakeParticles/nTrueParticles) = "
0249           << fakeRatio_particle);
0250   ACTS_LOG_WITH_LOGGER(
0251       log, Acts::Logging::INFO,
0252       "Duplicate ratio with particles (nDuplicateParticles/nTrueParticles) = "
0253           << duplicationRatio_particle);
0254 }
0255 
0256 }  // namespace ActsExamples