File indexing completed on 2025-11-29 09:18:26
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Io/Root/RootTrackFinderNTupleWriter.hpp"
0010
0011 #include "Acts/Definitions/Units.hpp"
0012 #include "ActsExamples/EventData/Measurement.hpp"
0013 #include "ActsExamples/EventData/SimParticle.hpp"
0014 #include "ActsExamples/EventData/TruthMatching.hpp"
0015 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0016 #include "ActsExamples/Framework/DataHandle.hpp"
0017 #include "ActsExamples/Utilities/Range.hpp"
0018 #include "ActsExamples/Validation/TrackClassification.hpp"
0019 #include "ActsFatras/EventData/Barcode.hpp"
0020
0021 #include <cstddef>
0022 #include <cstdint>
0023 #include <mutex>
0024 #include <stdexcept>
0025 #include <unordered_map>
0026 #include <utility>
0027 #include <vector>
0028
0029 #include <RtypesCore.h>
0030 #include <TFile.h>
0031 #include <TTree.h>
0032
0033 namespace ActsExamples {
0034
0035 struct RootTrackFinderNTupleWriter::Impl {
0036 Config cfg;
0037
0038 ReadDataHandle<SimParticleContainer> inputParticles;
0039 ReadDataHandle<ParticleMeasurementsMap> inputParticleMeasurementsMap;
0040 ReadDataHandle<TrackParticleMatching> inputTrackParticleMatching;
0041
0042 TFile* file = nullptr;
0043
0044
0045 TTree* trkTree = nullptr;
0046 std::mutex trkMutex;
0047
0048 ULong64_t trkEventId = 0;
0049 ULong64_t trkTrackId = 0;
0050
0051
0052 UShort_t trkNumHits = 0;
0053
0054 UShort_t trkNumParticles = 0;
0055
0056 std::vector<std::uint32_t> trkParticleVertexPrimary;
0057 std::vector<std::uint32_t> trkParticleVertexSecondary;
0058 std::vector<std::uint32_t> trkParticleParticle;
0059 std::vector<std::uint32_t> trkParticleGeneration;
0060 std::vector<std::uint32_t> trkParticleSubParticle;
0061
0062 std::vector<UShort_t> trkParticleNumHitsTotal;
0063
0064 std::vector<UShort_t> trkParticleNumHitsOnTrack;
0065
0066
0067 TTree* prtTree = nullptr;
0068 std::mutex prtMutex;
0069
0070 ULong64_t prtEventId = 0;
0071 std::uint32_t prtParticleVertexPrimary = 0;
0072 std::uint32_t prtParticleVertexSecondary = 0;
0073 std::uint32_t prtParticleParticle = 0;
0074 std::uint32_t prtParticleGeneration = 0;
0075 std::uint32_t prtParticleSubParticle = 0;
0076 Int_t prtParticleType = 0;
0077
0078
0079 float prtVx = 0, prtVy = 0, prtVz = 0;
0080
0081 float prtVt = 0;
0082
0083 float prtPx = 0, prtPy = 0, prtPz = 0;
0084
0085 float prtM = 0;
0086
0087 float prtQ = 0;
0088
0089 UShort_t prtNumMeasurements = 0;
0090 UShort_t prtNumTracks =
0091 0;
0092 UShort_t prtNumTracksMajority =
0093 0;
0094
0095 const Acts::Logger& _logger;
0096
0097 Impl(RootTrackFinderNTupleWriter* parent, Config&& c, const Acts::Logger& l)
0098 : cfg(std::move(c)),
0099 inputParticles{parent, "InputParticles"},
0100 inputParticleMeasurementsMap{parent, "InputParticleMeasurementsMap"},
0101 inputTrackParticleMatching{parent, "InputTrackParticleMatching"},
0102 _logger(l) {
0103 if (cfg.inputTracks.empty()) {
0104 throw std::invalid_argument("Missing track input collection");
0105 }
0106 if (cfg.inputParticles.empty()) {
0107 throw std::invalid_argument("Missing particles input collection");
0108 }
0109 if (cfg.inputParticleMeasurementsMap.empty()) {
0110 throw std::invalid_argument(
0111 "Missing particle-measurements map input collection");
0112 }
0113 if (cfg.inputTrackParticleMatching.empty()) {
0114 throw std::invalid_argument(
0115 "Missing proto track-particle matching input collection");
0116 }
0117 if (cfg.filePath.empty()) {
0118 throw std::invalid_argument("Missing output filename");
0119 }
0120
0121 inputParticles.initialize(cfg.inputParticles);
0122 inputParticleMeasurementsMap.initialize(cfg.inputParticleMeasurementsMap);
0123 inputTrackParticleMatching.initialize(cfg.inputTrackParticleMatching);
0124
0125
0126
0127
0128 file = TFile::Open(cfg.filePath.c_str(), cfg.fileMode.c_str());
0129 if (file == nullptr) {
0130 throw std::invalid_argument("Could not open '" + cfg.filePath + "'");
0131 }
0132
0133
0134 trkTree = new TTree(cfg.treeNameTracks.c_str(), cfg.treeNameTracks.c_str());
0135 trkTree->SetDirectory(file);
0136 trkTree->Branch("event_id", &trkEventId);
0137 trkTree->Branch("track_id", &trkTrackId);
0138 trkTree->Branch("size", &trkNumHits);
0139 trkTree->Branch("nparticles", &trkNumParticles);
0140 trkTree->Branch("particle_id_vertex_primary", &trkParticleVertexPrimary);
0141 trkTree->Branch("particle_id_vertex_secondary",
0142 &trkParticleVertexSecondary);
0143 trkTree->Branch("particle_id_particle", &trkParticleParticle);
0144 trkTree->Branch("particle_id_generation", &trkParticleGeneration);
0145 trkTree->Branch("particle_id_sub_particle", &trkParticleSubParticle);
0146 trkTree->Branch("particle_nhits_total", &trkParticleNumHitsTotal);
0147 trkTree->Branch("particle_nhits_on_track", &trkParticleNumHitsOnTrack);
0148 prtTree =
0149 new TTree(cfg.treeNameParticles.c_str(), cfg.treeNameParticles.c_str());
0150 prtTree->SetDirectory(file);
0151 prtTree->Branch("event_id", &prtEventId);
0152 prtTree->Branch("particle_id_vertex_primary", &prtParticleVertexPrimary);
0153 prtTree->Branch("particle_id_vertex_secondary",
0154 &prtParticleVertexSecondary);
0155 prtTree->Branch("particle_id_particle", &prtParticleParticle);
0156 prtTree->Branch("particle_id_generation", &prtParticleGeneration);
0157 prtTree->Branch("particle_id_sub_particle", &prtParticleSubParticle);
0158 prtTree->Branch("particle_type", &prtParticleType);
0159 prtTree->Branch("vx", &prtVx);
0160 prtTree->Branch("vy", &prtVy);
0161 prtTree->Branch("vz", &prtVz);
0162 prtTree->Branch("vt", &prtVt);
0163 prtTree->Branch("px", &prtPx);
0164 prtTree->Branch("py", &prtPy);
0165 prtTree->Branch("pz", &prtPz);
0166 prtTree->Branch("m", &prtM);
0167 prtTree->Branch("q", &prtQ);
0168 prtTree->Branch("nhits", &prtNumMeasurements);
0169 prtTree->Branch("ntracks", &prtNumTracks);
0170 prtTree->Branch("ntracks_majority", &prtNumTracksMajority);
0171 }
0172
0173 const Acts::Logger& logger() const { return _logger; }
0174
0175 void write(std::uint64_t eventId, const ConstTrackContainer& tracks,
0176 const SimParticleContainer& particles,
0177 const ParticleMeasurementsMap& particleMeasurementsMap,
0178 const TrackParticleMatching& trackParticleMatching) {
0179
0180 std::unordered_map<ActsFatras::Barcode, std::size_t> reconCount;
0181 reconCount.reserve(particles.size());
0182
0183 std::unordered_map<ActsFatras::Barcode, std::size_t> majorityCount;
0184 majorityCount.reserve(particles.size());
0185
0186
0187 {
0188 std::lock_guard<std::mutex> guardTrk(trkMutex);
0189 for (auto track : tracks) {
0190
0191 auto imatched = trackParticleMatching.find(track.index());
0192 if (imatched == trackParticleMatching.end()) {
0193 ACTS_DEBUG(
0194 "No truth particle associated with this proto track, index = "
0195 << track.index());
0196 continue;
0197 }
0198 const auto& particleMatch = imatched->second;
0199
0200 if (particleMatch.particle.has_value()) {
0201 SimBarcode majorityParticleId = particleMatch.particle.value();
0202
0203 auto it = majorityCount.try_emplace(majorityParticleId, 0u).first;
0204 it->second += 1;
0205
0206
0207 if (auto ip = particles.find(majorityParticleId);
0208 ip == particles.end()) {
0209 ACTS_WARNING(
0210 "Majority particle not found in the particles collection.");
0211 }
0212 }
0213
0214 for (const auto& hc : particleMatch.contributingParticles) {
0215 auto it = reconCount.try_emplace(hc.particleId, 0u).first;
0216 it->second += 1;
0217 }
0218
0219 trkEventId = eventId;
0220 trkTrackId = track.index();
0221 trkNumHits = track.nMeasurements();
0222 trkNumParticles = particleMatch.contributingParticles.size();
0223 trkParticleVertexPrimary.clear();
0224 trkParticleVertexSecondary.clear();
0225 trkParticleParticle.clear();
0226 trkParticleGeneration.clear();
0227 trkParticleSubParticle.clear();
0228 trkParticleNumHitsTotal.clear();
0229 trkParticleNumHitsOnTrack.clear();
0230 for (const auto& phc : particleMatch.contributingParticles) {
0231 const auto barcode = phc.particleId;
0232 trkParticleVertexPrimary.push_back(barcode.vertexPrimary());
0233 trkParticleVertexSecondary.push_back(barcode.vertexSecondary());
0234 trkParticleParticle.push_back(barcode.particle());
0235 trkParticleGeneration.push_back(barcode.generation());
0236 trkParticleSubParticle.push_back(barcode.subParticle());
0237
0238 auto trueParticleHits =
0239 makeRange(particleMeasurementsMap.equal_range(phc.particleId));
0240 trkParticleNumHitsTotal.push_back(trueParticleHits.size());
0241 trkParticleNumHitsOnTrack.push_back(phc.hitCount);
0242 }
0243
0244 trkTree->Fill();
0245 }
0246 }
0247
0248
0249 {
0250 std::lock_guard<std::mutex> guardPrt(trkMutex);
0251 for (const auto& particle : particles) {
0252
0253 auto measurements = makeRange(
0254 particleMeasurementsMap.equal_range(particle.particleId()));
0255
0256
0257 prtEventId = eventId;
0258 const auto particleBarcode = particle.particleId();
0259 prtParticleVertexPrimary = particleBarcode.vertexPrimary();
0260 prtParticleVertexSecondary = particleBarcode.vertexSecondary();
0261 prtParticleParticle = particleBarcode.particle();
0262 prtParticleGeneration = particleBarcode.generation();
0263 prtParticleSubParticle = particleBarcode.subParticle();
0264 prtParticleType = particle.pdg();
0265
0266 prtVx = particle.position().x() / Acts::UnitConstants::mm;
0267 prtVy = particle.position().y() / Acts::UnitConstants::mm;
0268 prtVz = particle.position().z() / Acts::UnitConstants::mm;
0269 prtVt = particle.time() / Acts::UnitConstants::mm;
0270 const auto p = particle.absoluteMomentum() / Acts::UnitConstants::GeV;
0271 prtPx = p * particle.direction().x();
0272 prtPy = p * particle.direction().y();
0273 prtPz = p * particle.direction().z();
0274 prtM = particle.mass() / Acts::UnitConstants::GeV;
0275 prtQ = particle.charge() / Acts::UnitConstants::e;
0276
0277 prtNumMeasurements = measurements.size();
0278 auto nt = reconCount.find(particle.particleId());
0279 prtNumTracks = (nt != reconCount.end()) ? nt->second : 0u;
0280 auto nm = majorityCount.find(particle.particleId());
0281 prtNumTracksMajority = (nm != majorityCount.end()) ? nm->second : 0u;
0282
0283 prtTree->Fill();
0284 }
0285 }
0286 }
0287
0288
0289 void close() {
0290 if (file == nullptr) {
0291 ACTS_ERROR("Output file is not available");
0292 return;
0293 }
0294 file->Write();
0295 file->Close();
0296 }
0297 };
0298
0299 RootTrackFinderNTupleWriter::RootTrackFinderNTupleWriter(
0300 RootTrackFinderNTupleWriter::Config config, Acts::Logging::Level level)
0301 : WriterT(config.inputTracks, "RootTrackFinderNTupleWriter", level),
0302 m_impl(std::make_unique<Impl>(this, std::move(config), logger())) {}
0303
0304 RootTrackFinderNTupleWriter::~RootTrackFinderNTupleWriter() = default;
0305
0306 ProcessCode RootTrackFinderNTupleWriter::writeT(
0307 const AlgorithmContext& ctx, const ConstTrackContainer& tracks) {
0308 const auto& particles = m_impl->inputParticles(ctx);
0309 const auto& particleMeasurementsMap =
0310 m_impl->inputParticleMeasurementsMap(ctx);
0311 const auto& trackParticleMatching = m_impl->inputTrackParticleMatching(ctx);
0312 m_impl->write(ctx.eventNumber, tracks, particles, particleMeasurementsMap,
0313 trackParticleMatching);
0314 return ProcessCode::SUCCESS;
0315 }
0316
0317 ProcessCode RootTrackFinderNTupleWriter::finalize() {
0318 m_impl->close();
0319 return ProcessCode::SUCCESS;
0320 }
0321
0322 const RootTrackFinderNTupleWriter::Config& RootTrackFinderNTupleWriter::config()
0323 const {
0324 return m_impl->cfg;
0325 }
0326
0327 }