Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CsvMultiTrajectoryWriter.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file CsvMultiTrajectoryWriter.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2021 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 
20 
21 #include <algorithm>
22 #include <fstream>
23 #include <iomanip>
24 #include <map>
25 #include <memory>
26 #include <stdexcept>
27 #include <string>
28 #include <unordered_map>
29 #include <unordered_set>
30 #include <utility>
31 
32 namespace ActsExamples {
33 class IndexSourceLink;
34 } // namespace ActsExamples
35 
36 using namespace ActsExamples;
37 
41  "CsvMultiTrajectoryWriter", level),
42  m_cfg(config) {
43  if (m_cfg.inputTrajectories.empty()) {
44  throw std::invalid_argument("Missing input trajectories collection");
45  }
46 
48 }
49 
51  const AlgorithmContext& context,
52  const TrajectoriesContainer& trajectories) {
53  // open per-event file
56  std::ofstream mos(path, std::ofstream::out | std::ofstream::trunc);
57  if (!mos) {
58  throw std::ios_base::failure("Could not open '" + path + "' to write");
59  }
60 
61  const auto& hitParticlesMap = m_inputMeasurementParticlesMap(context);
62 
63  std::unordered_map<Acts::MultiTrajectoryTraits::IndexType, trackInfo> infoMap;
64 
65  // Counter of truth-matched reco tracks
66  using RecoTrackInfo = std::pair<trackInfo, size_t>;
67  std::map<ActsFatras::Barcode, std::vector<RecoTrackInfo>> matched;
68 
69  size_t trackId = 0;
70  for (const auto& traj : trajectories) {
71  // The trajectory entry indices and the multiTrajectory
72  const auto& trackTips = traj.tips();
73  const auto& mj = traj.multiTrajectory();
74  if (trackTips.empty()) {
75  ACTS_WARNING("Empty multiTrajectory.");
76  continue;
77  }
78 
79  // Loop over all trajectories in a multiTrajectory
80  for (auto trackTip : trackTips) {
81  // Collect the trajectory summary info
82  auto trajState =
84  // Reco track selection
85  //@TODO: add interface for applying others cuts on reco tracks:
86  // -> pT, d0, z0, detector-specific hits/holes number cut
87  if (trajState.nMeasurements < m_cfg.nMeasurementsMin) {
88  continue;
89  }
90 
91  // Check if the reco track has fitted track parameters
92  if (not traj.hasTrackParameters(trackTip)) {
94  "No fitted track parameters for trajectory with entry index = "
95  << trackTip);
96  continue;
97  }
98 
99  // Get the majority truth particle to this track
100  std::vector<ParticleHitCount> particleHitCount;
101  identifyContributingParticles(hitParticlesMap, traj, trackTip,
102  particleHitCount);
103  if (m_cfg.onlyTruthMatched && particleHitCount.empty()) {
104  ACTS_WARNING(
105  "No truth particle associated with this trajectory with entry "
106  "index = "
107  << trackTip);
108  continue;
109  }
110 
111  // Requirement on the pT of the track
112  const auto& params = traj.trackParameters(trackTip);
113  const auto momentum = params.momentum();
114  const auto pT = Acts::VectorHelpers::perp(momentum);
115  if (pT < m_cfg.ptMin) {
116  continue;
117  }
118  size_t nMajorityHits = 0;
119  ActsFatras::Barcode majorityParticleId;
120  if (!particleHitCount.empty()) {
121  // Get the majority particle counts
122  majorityParticleId = particleHitCount.front().particleId;
123  // n Majority hits
124  nMajorityHits = particleHitCount.front().hitCount;
125  }
126  // track info
127  trackInfo toAdd;
128  toAdd.trackId = trackId;
129  toAdd.particleId = majorityParticleId;
130  toAdd.nStates = trajState.nStates;
131  toAdd.nMajorityHits = nMajorityHits;
132  toAdd.nMeasurements = trajState.nMeasurements;
133  toAdd.nOutliers = trajState.nOutliers;
134  toAdd.nHoles = trajState.nHoles;
135  toAdd.nSharedHits = trajState.nSharedHits;
136  toAdd.chi2Sum = trajState.chi2Sum;
137  toAdd.NDF = trajState.NDF;
138  toAdd.truthMatchProb = toAdd.nMajorityHits * 1. / trajState.nMeasurements;
139  toAdd.fittedParameters = &traj.trackParameters(trackTip);
140  toAdd.trackType = "unknown";
141 
142  mj.visitBackwards(trackTip, [&](const auto& state) {
143  if (state.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag) ==
144  true) {
145  auto sl =
146  state.getUncalibratedSourceLink().template get<IndexSourceLink>();
147  auto hitIndex = sl.index();
148  toAdd.measurementsID.insert(toAdd.measurementsID.begin(), hitIndex);
149  }
150  });
151 
152  // Check if the trajectory is matched with truth.
153  if (toAdd.truthMatchProb >= m_cfg.truthMatchProbMin) {
154  matched[toAdd.particleId].push_back({toAdd, toAdd.trackId});
155  } else {
156  toAdd.trackType = "fake";
157  }
158 
159  infoMap[toAdd.trackId] = toAdd;
160 
161  trackId++;
162  } // end of one trajectory
163  } // end of multi-trajectory
164 
165  // Find duplicates
166  std::unordered_set<size_t> listGoodTracks;
167  for (auto& [particleId, matchedTracks] : matched) {
168  std::sort(matchedTracks.begin(), matchedTracks.end(),
169  [](const RecoTrackInfo& lhs, const RecoTrackInfo& rhs) {
170  // sort by nMajorityHits
171  if (lhs.first.nMajorityHits != rhs.first.nMajorityHits) {
172  return (lhs.first.nMajorityHits > rhs.first.nMajorityHits);
173  }
174  // sort by nOutliers
175  if (lhs.first.nOutliers != rhs.first.nOutliers) {
176  return (lhs.first.nOutliers < rhs.first.nOutliers);
177  }
178  // sort by chi2
179  return (lhs.first.chi2Sum < rhs.first.chi2Sum);
180  });
181 
182  listGoodTracks.insert(matchedTracks.front().first.trackId);
183  }
184 
185  // write csv header
186  mos << "track_id,particleId,"
187  << "nStates,nMajorityHits,nMeasurements,nOutliers,nHoles,nSharedHits,"
188  << "chi2,ndf,chi2/ndf,"
189  << "pT,eta,phi,"
190  << "truthMatchProbability,"
191  << "good/duplicate/fake,"
192  << "Hits_ID";
193 
194  mos << '\n';
195  mos << std::setprecision(m_cfg.outputPrecision);
196 
197  // good/duplicate/fake = 0/1/2
198  for (auto& [id, trajState] : infoMap) {
199  if (listGoodTracks.find(id) != listGoodTracks.end()) {
200  trajState.trackType = "good";
201  } else if (trajState.trackType != "fake") {
202  trajState.trackType = "duplicate";
203  }
204 
205  const auto& params = *trajState.fittedParameters;
206  const auto momentum = params.momentum();
207 
208  // write the track info
209  mos << trajState.trackId << ",";
210  mos << trajState.particleId << ",";
211  mos << trajState.nStates << ",";
212  mos << trajState.nMajorityHits << ",";
213  mos << trajState.nMeasurements << ",";
214  mos << trajState.nOutliers << ",";
215  mos << trajState.nHoles << ",";
216  mos << trajState.nSharedHits << ",";
217  mos << trajState.chi2Sum << ",";
218  mos << trajState.NDF << ",";
219  mos << trajState.chi2Sum * 1.0 / trajState.NDF << ",";
220  mos << Acts::VectorHelpers::perp(momentum) << ",";
221  mos << Acts::VectorHelpers::eta(momentum) << ",";
222  mos << Acts::VectorHelpers::phi(momentum) << ",";
223  mos << trajState.truthMatchProb << ",";
224  mos << trajState.trackType << ",";
225  mos << "\"[";
226  for (auto& ID : trajState.measurementsID) {
227  mos << ID << ",";
228  }
229  mos << "]\"";
230  mos << '\n';
231  }
232 
233  return ProcessCode::SUCCESS;
234 }