ATLAS Offline Software
Loading...
Searching...
No Matches
ExpertTrackTimeFromClustersTool.cxx
Go to the documentation of this file.
1
9
11
12#include <algorithm>
13
14using namespace HGTD;
15
17 const std::string& t, const std::string& n, const IInterface* p)
18 : base_class(t, n, p), m_dec_prefix(n) {
19 // aux variable names may not contain a '.', but the instance name of a
20 // private tool does
21 std::replace(m_dec_prefix.begin(), m_dec_prefix.end(), '.', '_');
22}
23
25 ATH_CHECK(AthAlgTool::initialize());
26
27 m_dec_isset = std::make_unique<SG::AuxElement::Decorator<bool>>(
28 m_dec_prefix + "_isset");
29 m_dec_hastime = std::make_unique<SG::AuxElement::Decorator<bool>>(
30 m_dec_prefix + "_hastime");
31 m_dec_time = std::make_unique<SG::AuxElement::Decorator<float>>(
32 m_dec_prefix + "_time");
33 m_dec_nhits = std::make_unique<SG::AuxElement::Decorator<int>>(
34 m_dec_prefix + "_nhits");
35 m_dec_nprimehits = std::make_unique<SG::AuxElement::Decorator<int>>(
36 m_dec_prefix + "_nprimehits");
37 m_dec_resolution = std::make_unique<SG::AuxElement::Decorator<float>>(
38 m_dec_prefix + "_resolution");
39
41 std::make_unique<SG::ConstAccessor<bool>>(m_dec_prefix + "_isset");
43 std::make_unique<SG::ConstAccessor<bool>>(m_dec_prefix + "_hastime");
45 std::make_unique<SG::ConstAccessor<float>>(m_dec_prefix + "_time");
47 std::make_unique<SG::ConstAccessor<int>>(m_dec_prefix + "_nhits");
49 std::make_unique<SG::ConstAccessor<int>>(m_dec_prefix + "_nprimehits");
51 std::make_unique<SG::ConstAccessor<float>>(m_dec_prefix + "_resolution");
52
53 return StatusCode::SUCCESS;
54}
55
57 const xAOD::TrackParticle& track_particle) const {
58 // if the track has been used before, access the decoration instead of
59 // recalculating
60 if (m_acc_hastime->isAvailable(track_particle) and
61 m_acc_isset->operator()(track_particle)) {
62 return m_acc_hastime->operator()(track_particle);
63 }
64
65 // check if the last measurement is close to HGTD
66 if (m_do_last_hit and not lastHitIsOnLastSurface(track_particle)) {
67 m_dec_isset->set(track_particle, true);
68 m_dec_hastime->set(track_particle, false);
69 m_dec_time->set(track_particle, -999.);
70 m_dec_nhits->set(track_particle, 0);
71 m_dec_nprimehits->set(track_particle, 0);
72 m_dec_resolution->set(track_particle, -999.);
73 return false;
74 }
75
76 HitVec_t used_hits;
77 if (m_do_time_cons) {
78 // retrieve the hits that survive the preset cuts
79 used_hits = getTimeCompatibleHits(track_particle);
80 } else {
81 // retrieve all hits, no time compatibility check was required
82 used_hits = getValidHits(track_particle);
83 }
84
85 if (m_do_min_nhits and used_hits.size() == 1) {
86 // if a 2 hit minimum is required, reject the case of a single associated
87 // hit if the track falls into the defined eta region
88 float fabs_eta = std::abs(track_particle.eta());
89 if (fabs_eta > m_min_eta and fabs_eta < m_max_eta) {
90 m_dec_isset->set(track_particle, true);
91 m_dec_hastime->set(track_particle, false);
92 m_dec_time->set(track_particle, -999.);
93 m_dec_nhits->set(track_particle, 0);
94 m_dec_nprimehits->set(track_particle, 0);
95 m_dec_resolution->set(track_particle, -999.);
96 return false;
97 }
98 }
99
100 m_dec_isset->set(track_particle, true);
101 m_dec_hastime->set(track_particle, used_hits.size() > 0);
102 m_dec_time->set(track_particle, calculateMean(used_hits));
103 m_dec_nhits->set(track_particle, used_hits.size());
104 m_dec_nprimehits->set(track_particle, numberOfPrimaryHits(used_hits));
105 m_dec_resolution->set(track_particle, calculateTrackResolution(used_hits));
106
107 return used_hits.size() > 0;
108}
109
111 const xAOD::TrackParticle& track_particle) const {
112 if (m_acc_time->isAvailable(track_particle) and
113 m_acc_isset->operator()(track_particle)) {
114 return m_acc_time->operator()(track_particle);
115 } else {
116 throw std::runtime_error(
117 "[ExpertTrackTimeFromClustersTool::expertTime] ERROR, always call "
118 "expertHasTime on a track fist!");
119 }
120}
121
123 const xAOD::TrackParticle& track_particle) const {
124 if (m_acc_resolution->isAvailable(track_particle) and
125 m_acc_isset->operator()(track_particle)) {
126 return m_acc_resolution->operator()(track_particle);
127 } else {
128 throw std::runtime_error(
129 "[ExpertTrackTimeFromClustersTool::expertTimeRes] ERROR, always call "
130 "expertHasTime on a track fist!");
131 }
132}
133
135 const xAOD::TrackParticle& track_particle) const {
136 if (m_acc_nhits->isAvailable(track_particle) and
137 m_acc_isset->operator()(track_particle)) {
138 return m_acc_nhits->operator()(track_particle);
139 } else {
140 throw std::runtime_error(
141 "[ExpertTrackTimeFromClustersTool::nHits] ERROR, always call "
142 "expertHasTime on a track fist!");
143 }
144}
145
146std::vector<ExpertTrackTimeFromClustersTool::Hit>
148 const xAOD::TrackParticle& track_particle) const {
149
150 // get all available hits in a first step
151 HitVec_t valid_hits = getValidHits(track_particle);
152
153 size_t vts = valid_hits.size();
154
155 // if there is only one hit, no time consistency check can be done
156 // to improve efficiency, accept it
157 if (vts <= 1) {
158 return valid_hits;
159 }
160
161 // in case of two hits, check for time compatibility
162 if (vts == 2) {
163 if (passesDeltaT(valid_hits)) {
164 return valid_hits;
165 } else {
166 // if times are too far away from each other, don't accept time
167 return {};
168 }
169 }
170 // if there are 3 or 4 hits, perform chi2 outlier removal
171 // calculate the chi2 value of the available hits in a first step
172 float chi2 = calculateChi2(valid_hits);
173
174 // if the chi2 value doesn't surpass the set threshold, the hits are accepted
175 // as compatible in time
176 if (chi2 < m_chi2_threshold) {
177 return valid_hits;
178 }
179
180 HitVec_t time_candidates_copy = valid_hits; // TODO do I need this copy?
181 bool searching = true;
182 while (searching) {
183 // calculate chi2 contribution of each value
184 FloatVec_t chi2_contributions(time_candidates_copy.size(), 0.0);
185 for (size_t i = 0; i < time_candidates_copy.size(); i++) {
186 HitVec_t buff = time_candidates_copy;
187 buff.erase(buff.begin() + i);
188
189 // calculate the chi2 value we would get when removing the i-th hit
190 double local_chi2 = calculateChi2(buff);
191
192 chi2_contributions.at(i) = local_chi2;
193 }
194 // if removing one of the hits gives a much smaller chi2, it should be
195 // removed, so find the position where the "local chi2" is the smallest, and
196 // this is the hit that should be removed (since it gave a big
197 // contribution)]
198
199 // find minimum local chi2
200 int position = std::distance(
201 chi2_contributions.begin(),
202 std::min_element(chi2_contributions.begin(), chi2_contributions.end()));
203
204 // and remove it from the hits
205 time_candidates_copy.erase(time_candidates_copy.begin() + position);
206
207 // recompute chi2 value
208 chi2 = calculateChi2(time_candidates_copy);
209
210 // check for accepted chi2
211 if (chi2 < m_chi2_threshold) {
212 // if the threshold is now fulfilled, break out of the while loop
213 searching = false;
214 }
215 // if everything except 2 values has been removed, check again for
216 // consistency
217 if (time_candidates_copy.size() == 2) {
218
219 if (passesDeltaT(time_candidates_copy)) {
220 return time_candidates_copy;
221 } else {
222 // if times are too far away, don't accept any TODO maybe accept one,
223 // can the spatial chi2 be used?
224 return {};
225 }
226 }
227 }
228
229 return time_candidates_copy;
230}
231
233 // WARNING I don't check it here, but the vector has to be of size 2!!!
234 // pass if the distance in units of the resolution passes the cut
235 if (std::abs(hits.at(0).time - hits.at(1).time) <
236 m_deltat_cut * std::hypot(hits.at(0).resolution, hits.at(1).resolution)) {
237 return true;
238 }
239 return false;
240}
241
243 float mean = calculateMean(hits);
244
245 float chi2 = 0.;
246 for (size_t i = 0; i < hits.size(); i++) {
247
248 chi2 += (hits.at(i).time - mean) * (hits.at(i).time - mean) /
249 (hits.at(i).resolution * hits.at(i).resolution);
250 }
251 // TODO should I better use chi2/ndof, where ndof = hits.size() - 1 (due to
252 // mean)?
253 return chi2;
254}
255
257 // FIXME improve this
258 if (hits.size() == 0) {
259 return -999.;
260 }
261 float sum = 0.;
262 for (const Hit& hit : hits) {
263 sum += hit.time;
264 }
265 return sum / (float)hits.size();
266}
267
268std::vector<ExpertTrackTimeFromClustersTool::Hit>
270 const xAOD::TrackParticle& track_particle) const {
271
272 const std::vector<float>& times = m_acc_perLayer_clusterTime(track_particle);
273 const std::vector<bool>& has_clusters =
274 m_acc_perLayer_hasCluster(track_particle);
275 const std::vector<int>& hit_classification =
276 m_acc_perLayer_clusterTruthClass(track_particle);
277
278 HitVec_t valid_hits;
279 valid_hits.reserve(4);
280
281 for (size_t i = 0; i < has_clusters.size(); i++) {
282 if (not has_clusters.at(i)) {
283 continue;
284 }
285 Hit newhit;
286 newhit.time = times.at(i);
287 newhit.resolution = 0.035; // nano seconds
288 newhit.isprime = hit_classification.at(i) == 1;
289
290 valid_hits.push_back(newhit);
291 }
292 return valid_hits;
293}
294
296 const xAOD::TrackParticle& track) const {
297
298 TVector3 last_hit = this->getLastMeasurement(track);
299 double radius = std::hypot(last_hit.X(), last_hit.Y());
300 double abs_z = std::abs(last_hit.Z());
301
302 // 21.9 numbers
303 bool is_last = abs_z > 2700;
304 is_last = is_last || (radius < 350 and abs_z > 2400);
305 is_last = is_last || (radius > 205 and radius < 350 and abs_z > 2100);
306 is_last = is_last || (radius < 220 and abs_z > 2200);
307 is_last = is_last || (radius < 140 and abs_z > 1890);
308 return is_last;
309}
310
312 const HitVec_t& hits) const {
313 int n = 0;
314 for (const Hit& hit : hits) {
315 if (hit.isprime) {
316 n++;
317 }
318 }
319 return n;
320}
321
323 const xAOD::TrackParticle& track_particle) const {
324 if (m_acc_nprimehits->isAvailable(track_particle) and
325 m_acc_isset->operator()(track_particle)) {
326 return m_acc_nprimehits->operator()(track_particle);
327 } else {
328 return 0;
329 }
330}
331
333 const xAOD::TrackParticle& track_particle) const {
334 if (not expertHasTime(track_particle)) {
335 ATH_MSG_WARNING("[ExpertTrackTimeFromClustersTool::fracPrimaryHits]"
336 "No available hits, returning -999.");
337 return -999.;
338 }
339 int n_primaries = nPrimaryHits(track_particle);
340 int n_assigned = nHits(track_particle);
341 return (float)n_primaries / (float)n_assigned;
342}
343
345 const HitVec_t& hits) const {
346 // should never happen
347 if (hits.size() == 0) {
348 return -999.;
349 }
350 float sum = 0;
351 for (const Hit& hit : hits) {
352 sum += 1. / (hit.resolution * hit.resolution);
353 }
354 return std::sqrt(1. / sum);
355}
356
358 const xAOD::TrackParticle& track) const {
359
360 unsigned int index = 0;
361
362 if (not track.indexOfParameterAtPosition(index, xAOD::LastMeasurement)) {
363 return TVector3(0, 0, 0);
364 }
365
366 return TVector3(track.parameterX(index), track.parameterY(index),
367 track.parameterZ(index));
368}
369
371 const xAOD::TrackParticle& track_particle) const {
372 const std::vector<bool>& expected_hits =
373 m_acc_perLayer_expectCluster(track_particle);
374 return std::count(expected_hits.begin(), expected_hits.end(), true);
375}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_WARNING(x,...)
Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration.
bool hit(const Container &ids, int pdgId)
static const uint32_t nHits
std::unique_ptr< SG::AuxElement::Decorator< int > > m_dec_nhits
ExpertTrackTimeFromClustersTool(const std::string &, const std::string &, const IInterface *)
std::unique_ptr< SG::ConstAccessor< float > > m_acc_time
std::unique_ptr< SG::ConstAccessor< bool > > m_acc_isset
std::unique_ptr< SG::ConstAccessor< int > > m_acc_nhits
std::unique_ptr< SG::AuxElement::Decorator< float > > m_dec_resolution
int nPrimaryHits(const xAOD::TrackParticle &track_particle) const
Number of hits used for the track time that were left by a primary particle.
std::unique_ptr< SG::AuxElement::Decorator< float > > m_dec_time
virtual float fracPrimaryHits(const xAOD::TrackParticle &track_particle) const override final
float calculateMean(const HitVec_t &hits) const
Returns the arithmetic average mean of the hit times.
std::unique_ptr< SG::ConstAccessor< float > > m_acc_resolution
std::string m_dec_prefix
Prefix of the decorations this tool writes.
HitVec_t getTimeCompatibleHits(const xAOD::TrackParticle &track_particle) const
Retrieve the hit information from the decorators and build a vector of hits for those hits that survi...
SG::ConstAccessor< std::vector< float > > m_acc_perLayer_clusterTime
HitVec_t getValidHits(const xAOD::TrackParticle &track_particle) const
Retrieve the hit information from the decorators and build a vector of hits.
virtual int numberPotentialPrimaryHits(const xAOD::TrackParticle &track_particle) const override final
bool lastHitIsOnLastSurface(const xAOD::TrackParticle &track) const
Check wheather the last hit on track is on a surface close to HGTD.
virtual bool expertHasTime(const xAOD::TrackParticle &track_particle) const override final
SG::ConstAccessor< std::vector< int > > m_acc_perLayer_clusterTruthClass
std::unique_ptr< SG::ConstAccessor< bool > > m_acc_hastime
std::unique_ptr< SG::AuxElement::Decorator< bool > > m_dec_isset
TVector3 getLastMeasurement(const xAOD::TrackParticle &track) const
In the samples used for HGTD studies the last hit on track in ITk is written to file and can be used ...
SG::ConstAccessor< std::vector< bool > > m_acc_perLayer_expectCluster
std::unique_ptr< SG::AuxElement::Decorator< int > > m_dec_nprimehits
int nHits(const xAOD::TrackParticle &track_particle) const
Number of hits that were used to build the track time.
bool passesDeltaT(const HitVec_t &hits) const
Tests if a given set of exactly 2 Hit objects passes the set time difference cut, which is defined in...
virtual float expertTime(const xAOD::TrackParticle &track_particle) const override final
float calculateTrackResolution(const HitVec_t &vals) const
SG::ConstAccessor< std::vector< bool > > m_acc_perLayer_hasCluster
Input decorations produced by the HGTD track extension, read with fixed names.
virtual float expertTimeRes(const xAOD::TrackParticle &track_particle) const override final
std::unique_ptr< SG::ConstAccessor< int > > m_acc_nprimehits
std::unique_ptr< SG::AuxElement::Decorator< bool > > m_dec_hastime
virtual double eta() const override final
The pseudorapidity ( ) of the particle.
double chi2(TH1 *h0, TH1 *h1)
void mean(std::vector< double > &bins, std::vector< double > &values, const std::vector< std::string > &files, const std::string &histname, const std::string &tplotname, const std::string &label="")
Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration.
Definition index.py:1
TrackParticle_v1 TrackParticle
Reference the current persistent version:
@ LastMeasurement
Parameter defined at the position of the last measurement.