ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TrackFinderPerformanceWriter.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file TrackFinderPerformanceWriter.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2019 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
10 
11 #include <TFile.h>
12 #include <TTree.h>
13 #include <algorithm>
14 #include <cstdint>
15 #include <mutex>
16 #include <unordered_map>
17 #include <vector>
18 
25 #include "Acts/Utilities/Units.hpp"
27 
28 namespace {
30 using HitParticlesMap = FW::IndexMultimap<ActsFatras::Barcode>;
32 } // namespace
33 
36  TFile* file = nullptr;
37 
38  // per-track tree
39  TTree* trkTree = nullptr;
41  // track identification
42  ULong64_t trkEventId;
43  ULong64_t trkTrackId;
44  // track content
45  // number of hits on track
46  UShort_t trkNumHits;
47  // number of particles contained in the track
48  UShort_t trkNumParticles;
49  // track particle content; for each contributing particle, largest first
50  std::vector<ULong64_t> trkParticleId;
51  // total number of hits generated by this particle
52  std::vector<UShort_t> trkParticleNumHitsTotal;
53  // number of hits within this track
54  std::vector<UShort_t> trkParticleNumHitsOnTrack;
55 
56  // per-particle tree
57  TTree* prtTree = nullptr;
59  // particle identification
60  ULong64_t prtEventId;
61  ULong64_t prtParticleId;
63  // particle kinematics
64  // vertex position in mm
65  float prtVx, prtVy, prtVz;
66  // vertex time in ns
67  float prtVt;
68  // particle momentum at production in GeV
69  float prtPx, prtPy, prtPz;
70  // particle mass in GeV
71  float prtM;
72  // particle charge in e
73  float prtQ;
74  // particle reconstruction
75  UShort_t prtNumHits; // number of hits for this particle
76  UShort_t prtNumTracks; // number of tracks this particle was reconstructed in
77  UShort_t prtNumTracksMajority; // number of tracks reconstructed as majority
78  // extra logger reference for the logging macros
80 
81  Impl(Config&& c, const Acts::Logger& l) : cfg(std::move(c)), _logger(l) {
82  if (cfg.inputParticles.empty()) {
83  throw std::invalid_argument("Missing particles input collection");
84  }
85  if (cfg.inputHitParticlesMap.empty()) {
86  throw std::invalid_argument("Missing hit-particles map input collection");
87  }
88  if (cfg.inputProtoTracks.empty()) {
89  throw std::invalid_argument("Missing proto tracks input collection");
90  }
91  if (cfg.outputFilename.empty()) {
92  throw std::invalid_argument("Missing output filename");
93  }
94 
95  // the output file can not be given externally since TFile accesses to the
96  // same file from multiple threads are unsafe.
97  // must always be opened internally
99  file = TFile::Open(path.c_str(), "RECREATE");
100  if (not file) {
101  throw std::invalid_argument("Could not open '" + path + "'");
102  }
103 
104  // construct trees
105  trkTree = new TTree("track_finder_tracks", "");
106  trkTree->SetDirectory(file);
107  trkTree->Branch("event_id", &trkEventId);
108  trkTree->Branch("track_id", &trkTrackId);
109  trkTree->Branch("size", &trkNumHits);
110  trkTree->Branch("nparticles", &trkNumParticles);
111  trkTree->Branch("particle_id", &trkParticleId);
112  trkTree->Branch("particle_nhits_total", &trkParticleNumHitsTotal);
113  trkTree->Branch("particle_nhits_on_track", &trkParticleNumHitsOnTrack);
114  prtTree = new TTree("track_finder_particles", "");
115  prtTree->SetDirectory(file);
116  prtTree->Branch("event_id", &prtEventId);
117  prtTree->Branch("particle_id", &prtParticleId);
118  prtTree->Branch("particle_type", &prtParticleType);
119  prtTree->Branch("vx", &prtVx);
120  prtTree->Branch("vy", &prtVy);
121  prtTree->Branch("vz", &prtVz);
122  prtTree->Branch("vt", &prtVt);
123  prtTree->Branch("px", &prtPx);
124  prtTree->Branch("py", &prtPy);
125  prtTree->Branch("pz", &prtPz);
126  prtTree->Branch("m", &prtM);
127  prtTree->Branch("q", &prtQ);
128  prtTree->Branch("nhits", &prtNumHits);
129  prtTree->Branch("ntracks", &prtNumTracks);
130  prtTree->Branch("ntracks_majority", &prtNumTracksMajority);
131  }
132 
133  const Acts::Logger& logger() const { return _logger; }
134 
135  void write(uint64_t eventId, const SimParticleContainer& particles,
136  const HitParticlesMap& hitParticlesMap,
137  const ProtoTrackContainer& tracks) {
138  // compute the inverse mapping on-the-fly
139  const auto& particleHitsMap = invertIndexMultimap(hitParticlesMap);
140  // How often a particle was reconstructed.
141  std::unordered_map<ActsFatras::Barcode, std::size_t> reconCount;
142  reconCount.reserve(particles.size());
143  // How often a particle was reconstructed as the majority particle.
144  std::unordered_map<ActsFatras::Barcode, std::size_t> majorityCount;
145  majorityCount.reserve(particles.size());
146  // For each particle within a track, how many hits did it contribute
147  std::vector<ParticleHitCount> particleHitCounts;
148 
149  // write per-track performance measures
150  {
151  std::lock_guard<std::mutex> guardTrk(trkMutex);
152  for (size_t itrack = 0; itrack < tracks.size(); ++itrack) {
153  const auto& track = tracks[itrack];
154 
155  identifyContributingParticles(hitParticlesMap, track,
156  particleHitCounts);
157  // extract per-particle reconstruction counts
158  // empty track hits counts could originate from a buggy track finder
159  // that results in empty tracks or from purely noise track where no hits
160  // is from a particle.
161  if (not particleHitCounts.empty()) {
162  auto it = majorityCount
163  .try_emplace(particleHitCounts.front().particleId, 0u)
164  .first;
165  it->second += 1;
166  }
167  for (const auto& hc : particleHitCounts) {
168  auto it = reconCount.try_emplace(hc.particleId, 0u).first;
169  it->second += 1;
170  }
171 
172  trkEventId = eventId;
173  trkTrackId = itrack;
174  trkNumHits = track.size();
175  trkNumParticles = particleHitCounts.size();
176  trkParticleId.clear();
177  trkParticleNumHitsTotal.clear();
179  for (const auto& phc : particleHitCounts) {
180  trkParticleId.push_back(phc.particleId.value());
181  // count total number of hits for this particle
182  auto trueParticleHits =
183  makeRange(particleHitsMap.equal_range(phc.particleId.value()));
184  trkParticleNumHitsTotal.push_back(trueParticleHits.size());
185  trkParticleNumHitsOnTrack.push_back(phc.hitCount);
186  }
187 
188  trkTree->Fill();
189  }
190  }
191 
192  // write per-particle performance measures
193  {
194  std::lock_guard<std::mutex> guardPrt(trkMutex);
195  for (const auto& particle : particles) {
196  // find all hits for this particle
197  auto hits =
198  makeRange(particleHitsMap.equal_range(particle.particleId()));
199 
200  // identification
201  prtEventId = eventId;
202  prtParticleId = particle.particleId().value();
203  prtParticleType = particle.pdg();
204  // kinematics
205  prtVx = particle.position().x() / Acts::UnitConstants::mm;
206  prtVy = particle.position().y() / Acts::UnitConstants::mm;
207  prtVz = particle.position().z() / Acts::UnitConstants::mm;
209  const auto p = particle.absMomentum() / Acts::UnitConstants::GeV;
210  prtPx = p * particle.unitDirection().x();
211  prtPy = p * particle.unitDirection().y();
212  prtPz = p * particle.unitDirection().z();
214  prtQ = particle.charge() / Acts::UnitConstants::e;
215  // reconstruction
216  prtNumHits = hits.size();
217  auto nt = reconCount.find(particle.particleId());
218  prtNumTracks = (nt != reconCount.end()) ? nt->second : 0u;
219  auto nm = majorityCount.find(particle.particleId());
220  prtNumTracksMajority = (nm != majorityCount.end()) ? nm->second : 0u;
221 
222  prtTree->Fill();
223  }
224  }
225  }
227  void close() {
228  if (not file) {
229  ACTS_ERROR("Output file is not available");
230  return;
231  }
232  file->Write();
233  file->Close();
234  }
235 };
236 
239  : WriterT(cfg.inputProtoTracks, "TrackFinderPerformanceWriter", lvl),
240  m_impl(std::make_unique<Impl>(std::move(cfg), logger())) {}
241 
243  // explicit destructor needed for pimpl idiom to work
244 }
245 
247  const FW::AlgorithmContext& ctx, const FW::ProtoTrackContainer& tracks) {
248  const auto& particles =
249  ctx.eventStore.get<SimParticleContainer>(m_impl->cfg.inputParticles);
250  const auto& hitParticlesMap =
251  ctx.eventStore.get<HitParticlesMap>(m_impl->cfg.inputHitParticlesMap);
252  m_impl->write(ctx.eventNumber, particles, hitParticlesMap, tracks);
253  return ProcessCode::SUCCESS;
254 }
255 
257  m_impl->close();
258  return ProcessCode::SUCCESS;
259 }