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