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