File indexing completed on 2026-07-26 08:22:20
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "traccc/efficiency/finding_performance_writer.hpp"
0010
0011 #include "../resolution/stat_plot_tool.hpp"
0012 #include "duplication_plot_tool.hpp"
0013 #include "eff_plot_tool.hpp"
0014 #include "fake_tracks_plot_tool.hpp"
0015 #include "traccc/edm/track_fit_outcome.hpp"
0016 #include "traccc/utils/logging.hpp"
0017 #include "track_classification.hpp"
0018
0019
0020 #ifdef TRACCC_HAVE_ROOT
0021 #include <TFile.h>
0022 #endif
0023
0024
0025 #include <iostream>
0026 #include <memory>
0027 #include <stdexcept>
0028
0029 namespace traccc {
0030 namespace details {
0031
0032 struct finding_performance_writer_data {
0033
0034 finding_performance_writer_data(const finding_performance_writer::config& cfg)
0035 : m_eff_plot_tool({cfg.var_binning}),
0036 m_duplication_plot_tool({cfg.var_binning}),
0037 m_fake_tracks_plot_tool({cfg.var_binning}),
0038 m_stat_plot_tool(cfg.stat_config) {}
0039
0040
0041 eff_plot_tool m_eff_plot_tool;
0042 eff_plot_tool::eff_plot_cache m_eff_plot_cache;
0043
0044
0045 duplication_plot_tool m_duplication_plot_tool;
0046 duplication_plot_tool::duplication_plot_cache m_duplication_plot_cache;
0047
0048
0049 fake_tracks_plot_tool m_fake_tracks_plot_tool;
0050 fake_tracks_plot_tool::fake_tracks_plot_cache m_fake_tracks_plot_cache;
0051
0052 std::map<event_data::measurement_proxy, std::map<particle, std::size_t>>
0053 m_measurement_particle_map;
0054 std::map<std::uint64_t, particle> m_particle_map;
0055
0056
0057 stat_plot_tool m_stat_plot_tool;
0058 stat_plot_tool::stat_plot_cache m_stat_plot_cache;
0059
0060 };
0061
0062 }
0063
0064 finding_performance_writer::finding_performance_writer(
0065 const config& cfg, std::unique_ptr<const traccc::Logger> logger)
0066 : messaging(std::move(logger)),
0067 m_cfg(cfg),
0068 m_data(std::make_unique<details::finding_performance_writer_data>(cfg)) {
0069 m_data->m_eff_plot_tool.book(m_cfg.algorithm_name, m_data->m_eff_plot_cache);
0070 m_data->m_duplication_plot_tool.book(m_cfg.algorithm_name,
0071 m_data->m_duplication_plot_cache);
0072 m_data->m_fake_tracks_plot_tool.book(m_cfg.algorithm_name,
0073 m_data->m_fake_tracks_plot_cache);
0074 m_data->m_stat_plot_tool.book(m_data->m_stat_plot_cache);
0075 }
0076
0077 finding_performance_writer::~finding_performance_writer() {}
0078
0079 namespace {
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089 std::vector<std::vector<event_data::measurement_proxy>> prepare_data(
0090 const edm::track_container<default_algebra>::const_view& track_view,
0091 bool require_fit = false) {
0092 std::vector<std::vector<event_data::measurement_proxy>> result;
0093
0094
0095 const edm::track_container<default_algebra>::const_device tracks(track_view);
0096
0097
0098 const unsigned int n_tracks = tracks.tracks.size();
0099 result.reserve(n_tracks);
0100
0101 for (unsigned int i = 0; i < n_tracks; i++) {
0102 if (require_fit &&
0103 tracks.tracks.at(i).fit_outcome() != track_fit_outcome::SUCCESS) {
0104 continue;
0105 }
0106 std::vector<event_data::measurement_proxy> result_measurements;
0107 for (const edm::track_constituent_link& link :
0108 tracks.tracks.constituent_links().at(i)) {
0109 if (link.type == edm::track_constituent_link::measurement) {
0110 result_measurements.push_back(tracks.measurements.at(link.index));
0111 } else if (link.type == edm::track_constituent_link::track_state) {
0112 result_measurements.push_back(tracks.measurements.at(
0113 tracks.states.at(link.index).measurement_index()));
0114 }
0115 }
0116 result.push_back(std::move(result_measurements));
0117 }
0118 return result;
0119 }
0120
0121 }
0122
0123 void finding_performance_writer::write_common(
0124 const std::vector<std::vector<event_data::measurement_proxy>>& tracks,
0125 const event_data& evt_data) {
0126
0127
0128 std::map<particle_id, std::size_t> match_counter;
0129
0130
0131
0132 std::map<particle_id, std::size_t> fake_counter;
0133
0134
0135 const std::size_t n_tracks = tracks.size();
0136
0137 std::size_t total_fake_tracks = 0;
0138
0139 for (std::size_t i = 0; i < n_tracks; i++) {
0140 const std::vector<event_data::measurement_proxy>& found_measurements =
0141 tracks[i];
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154 std::vector<particle_hit_count> particle_hit_counts;
0155
0156 if (!evt_data.m_found_meas_to_ptc_map.empty()) {
0157 particle_hit_counts = identify_contributing_particles(
0158 found_measurements, evt_data.m_found_meas_to_ptc_map);
0159 } else {
0160 particle_hit_counts = identify_contributing_particles(
0161 found_measurements, evt_data.m_meas_to_ptc_map);
0162 }
0163
0164 if (particle_hit_counts.empty()) {
0165 TRACCC_VERBOSE("No contributing particles found for track: " << n_tracks);
0166 continue;
0167 }
0168
0169 const auto major_ptc = particle_hit_counts.at(0).ptc;
0170 const auto n_major_hits = particle_hit_counts.at(0).hit_counts;
0171
0172
0173 const std::vector<event_data::measurement_proxy> truth_measurements =
0174 evt_data.m_ptc_to_meas_map.at(major_ptc);
0175
0176
0177
0178 assert(found_measurements.size() > 0u);
0179 assert(truth_measurements.size() > 0u);
0180
0181 const double purity = static_cast<double>(n_major_hits) /
0182 static_cast<double>(found_measurements.size());
0183 const double completeness = static_cast<double>(n_major_hits) /
0184 static_cast<double>(truth_measurements.size());
0185
0186 const bool reco_matched = purity >= m_cfg.track_truth_config.matching_ratio;
0187 const bool truth_matched =
0188 completeness >= m_cfg.track_truth_config.matching_ratio;
0189
0190 m_data->m_stat_plot_tool.fill(m_data->m_stat_plot_cache, purity,
0191 completeness);
0192
0193 if ((!m_cfg.track_truth_config.double_matching && reco_matched) ||
0194 (m_cfg.track_truth_config.double_matching && reco_matched &&
0195 truth_matched)) {
0196 const auto pid = major_ptc.particle_id;
0197 match_counter[pid]++;
0198 } else {
0199 for (particle_hit_count const& phc : particle_hit_counts) {
0200 const auto pid = phc.ptc.particle_id;
0201 fake_counter[pid]++;
0202 }
0203 total_fake_tracks++;
0204 }
0205 }
0206
0207 std::size_t total_truth_particles = 0;
0208 std::size_t total_matched_truth_particles = 0;
0209 std::size_t total_duplicate_tracks = 0;
0210
0211
0212 for (auto const& [pid, ptc] : evt_data.m_particle_map) {
0213 auto ptc_particle =
0214 detail::particle_from_pdg_number<scalar>(ptc.particle_type);
0215 if (ptc_particle.pdg_num() == 0) {
0216
0217 continue;
0218 }
0219
0220
0221 std::size_t num_measurements = 0;
0222 if (auto it = evt_data.m_ptc_to_meas_map.find(ptc);
0223 it != evt_data.m_ptc_to_meas_map.end()) {
0224 num_measurements = it->second.size();
0225 } else {
0226 continue;
0227 }
0228
0229
0230 if (ptc.charge == 0 ||
0231 vector::perp(ptc.momentum) < m_cfg.truth_config.pT_min ||
0232 ptc.vertex[2] < m_cfg.truth_config.z_min ||
0233 ptc.vertex[2] > m_cfg.truth_config.z_max ||
0234 vector::perp(ptc.vertex) > m_cfg.truth_config.r_max ||
0235 std::abs(vector::eta(ptc.momentum)) > m_cfg.truth_config.eta_max ||
0236 (m_cfg.truth_config.process_id >= 0 &&
0237 m_cfg.truth_config.process_id != ptc.process) ||
0238 num_measurements < m_cfg.truth_config.min_track_candidates) {
0239 continue;
0240 }
0241
0242 total_truth_particles++;
0243
0244
0245
0246 bool is_matched = false;
0247 std::size_t n_matched_seeds_for_particle = 0;
0248 auto it = match_counter.find(pid);
0249 if (it != match_counter.end()) {
0250 is_matched = true;
0251 total_matched_truth_particles++;
0252 n_matched_seeds_for_particle = it->second;
0253 total_duplicate_tracks += n_matched_seeds_for_particle - 1;
0254 } else {
0255 TRACCC_DEBUG("Not matched: " << pid);
0256 }
0257
0258
0259
0260 std::size_t fake_count = 0;
0261 auto itf = fake_counter.find(pid);
0262 if (itf != fake_counter.end()) {
0263 fake_count = itf->second;
0264 }
0265
0266 m_data->m_eff_plot_tool.fill(m_data->m_eff_plot_cache, ptc, is_matched);
0267 m_data->m_duplication_plot_tool.fill(m_data->m_duplication_plot_cache, ptc,
0268 n_matched_seeds_for_particle - 1);
0269 m_data->m_fake_tracks_plot_tool.fill(m_data->m_fake_tracks_plot_cache, ptc,
0270 fake_count);
0271 }
0272
0273 TRACCC_INFO("Total number of truth particles was " << total_truth_particles);
0274 TRACCC_INFO("Total number of found tracks was " << n_tracks);
0275 TRACCC_INFO("Total number of track-matched particles was "
0276 << total_matched_truth_particles);
0277 TRACCC_INFO("Total number of duplicated tracks was "
0278 << total_duplicate_tracks);
0279 TRACCC_INFO("Total number of fake tracks was " << total_fake_tracks);
0280 TRACCC_INFO("Total track efficiency was "
0281 << (100. * static_cast<double>(total_matched_truth_particles) /
0282 static_cast<double>(total_truth_particles))
0283 << "%");
0284 TRACCC_INFO("Total track duplicate rate was "
0285 << (static_cast<double>(total_duplicate_tracks) /
0286 static_cast<double>(total_matched_truth_particles)));
0287 TRACCC_INFO("Total track fake rate was "
0288 << (static_cast<double>(total_fake_tracks) /
0289 static_cast<double>(total_truth_particles)));
0290 }
0291
0292
0293 void finding_performance_writer::write(
0294 const edm::track_container<default_algebra>::const_view& track_view,
0295 const event_data& evt_data) {
0296
0297 const edm::track_container<default_algebra>::const_device tracks{track_view};
0298
0299 const unsigned int n_tracks = tracks.tracks.size();
0300 for (unsigned int i = 0; i < n_tracks; i++) {
0301 if (m_cfg.require_fit &&
0302 tracks.tracks.at(i).fit_outcome() != track_fit_outcome::SUCCESS) {
0303 continue;
0304 }
0305
0306
0307 m_data->m_stat_plot_tool.fill(m_data->m_stat_plot_cache,
0308 tracks.tracks.at(i));
0309 }
0310
0311 auto prep_data = prepare_data(track_view, m_cfg.require_fit);
0312 write_common(prep_data, evt_data);
0313 }
0314
0315 void finding_performance_writer::finalize() {
0316 #ifdef TRACCC_HAVE_ROOT
0317
0318 std::unique_ptr<TFile> ofile(
0319 TFile::Open(m_cfg.file_path.c_str(), m_cfg.file_mode.c_str()));
0320 if ((!ofile) || ofile->IsZombie()) {
0321 throw std::runtime_error("Could not open output file \"" + m_cfg.file_path +
0322 "\" in mode \"" + m_cfg.file_mode + "\"");
0323 }
0324 ofile->cd();
0325 #else
0326 std::cout << "ROOT file \"" << m_cfg.file_path << "\" is NOT created"
0327 << std::endl;
0328 #endif
0329
0330 m_data->m_eff_plot_tool.write(m_data->m_eff_plot_cache);
0331 m_data->m_duplication_plot_tool.write(m_data->m_duplication_plot_cache);
0332 m_data->m_fake_tracks_plot_tool.write(m_data->m_fake_tracks_plot_cache);
0333 m_data->m_stat_plot_tool.write(m_data->m_stat_plot_cache);
0334 }
0335
0336 }