ATLAS Offline Software
Loading...
Searching...
No Matches
TrackTimeDefAndQualityAlg.cxx
Go to the documentation of this file.
1
10
12
16
17#include <optional>
18
21#include "Acts/Utilities/TrackHelpers.hpp"
22
25namespace HGTD {
26
28 ISvcLocator* pSvcLocator)
29 : AthReentrantAlgorithm(name, pSvcLocator) {}
30
32
35 ATH_CHECK(m_holesHGTDKey.initialize(!m_doActs)); //HGTD_holes not produced in ActsHGTDTrackExtensionAlg for now so make it optional
36 ATH_CHECK(m_layerClusterTimeKey.initialize());
37 ATH_CHECK(m_layerClusterTruthClassKey.initialize(m_doTruth)); //Only initialize if truth information is available
38 ATH_CHECK(m_time_dec_key.initialize());
39 ATH_CHECK(m_time_res_dec_key.initialize());
42
43 return StatusCode::SUCCESS;
44}
45
48
49StatusCode TrackTimeDefAndQualityAlg::execute(const EventContext& ctx) const {
50
51 const xAOD::TrackParticleContainer* track_particles{nullptr};
52 ATH_CHECK(SG::get(track_particles, m_trackParticleContainerKey, ctx));
53
55 m_time_dec_key, ctx);
62
64 layerClusterTimeHandle(m_layerClusterTimeKey, ctx);
65 ATH_CHECK(layerClusterTimeHandle.isValid());
66
68 layerHasExtensionHandle(m_layerHasExtensionKey, ctx);
69 ATH_CHECK(layerHasExtensionHandle.isValid());
70
71 static const std::vector<char> s_no_holes(s_hgtd_layers, false);
72 std::optional<SG::ReadDecorHandle<xAOD::TrackParticleContainer, std::vector<char>>>
73 holesHGTDHandle;
74 if (!m_doActs) {
75 holesHGTDHandle.emplace(m_holesHGTDKey, ctx);
76 ATH_CHECK(holesHGTDHandle->isValid());
77 }
78
79 static const std::vector<int> s_no_truth(s_hgtd_layers, 0);
80 std::optional<SG::ReadDecorHandle<xAOD::TrackParticleContainer, std::vector<int>>>
81 layerClusterTruthClass;
82 if (m_doTruth) {
83 layerClusterTruthClass.emplace(m_layerClusterTruthClassKey, ctx);
84 ATH_CHECK(layerClusterTruthClass->isValid());
85 }
86
87 for (const auto* track_ptkl : *track_particles) {
88 // runs the time consistency checks
89 // if no hits are found in HGTD, returns a default time
90 const std::vector<float>& times = layerClusterTimeHandle(*track_ptkl);
91 const std::vector<char>& has_clusters = layerHasExtensionHandle(*track_ptkl);
92 const std::vector<int>& hit_classification = m_doTruth ? (*layerClusterTruthClass)(*track_ptkl) : s_no_truth;
93 const std::vector<char>& holes_HGTD = m_doActs ? s_no_holes : (*holesHGTDHandle)(*track_ptkl);
94
96 has_clusters,
97 hit_classification);
98
99 // check if the last hit on track was within the predefined area
100 if (lastHitIsOnLastSurface(*track_ptkl)) {
101 res.m_field |= (0b0000 << m_holes_ptrn_sft);
102 } else {
103 res.m_field |= (0b0001 << m_holes_ptrn_sft);
104 }
105
106 // keep which of the hits associated in reco were primary hits (truth info!)
107 short prime_pattern = 0x0;
108 for (short i = 0; i < s_hgtd_layers; i++) {
109 if (res.m_hits.at(i).m_isprime) {
110 prime_pattern |= (1 << i);
111 }
112 }
113 res.m_field |= (prime_pattern << m_primes_ptrn_sft);
114
115 // expected pattern : 'on which HGTD layer a hit was expected?' which means extrapolation has
116 // reached an active sensor, whether a matching cluster was found or not.
117 // So ‘expected = has_cluster OR HGTD_holes’.
118 short expected_pattern = 0x0;
119 for (short i = 0; i < s_hgtd_layers; i++) {
120 if (has_clusters.at(i) || holes_HGTD.at(i)) {
121 expected_pattern |= (1 << i);
122 }
123 }
124 res.m_field |= (expected_pattern << m_exp_ptrn_sft);
125
126 // decorate the track again with this info
127 time_handle(*track_ptkl) = res.m_time;
128 timeres_handle(*track_ptkl) = res.m_resolution;
129 hasValidTime_handle(*track_ptkl) = res.m_hasValidTime;
130 summary_handle(*track_ptkl) = res.m_field;
131 }
132 return StatusCode::SUCCESS;
133}
134
137
140 const std::vector<char>& has_clusters,
141 const std::vector<int>& hit_classification) const {
142 // get all available hits (see the struct Hit) in a first step
143 std::array<Hit, s_hgtd_layers> valid_hits = getValidHits(times,
144 has_clusters,
145 hit_classification);
146
147 CleaningResult result;
148 result.m_hits = valid_hits;
149 result.m_field = 0x0;
150 result.m_time = m_default_time;
151 result.m_resolution = m_default_time_res;
152 result.m_hasValidTime = 0;
153
154 short recoed_pattern = getValidPattern(valid_hits);
155 // stored the pattern of hits as retrieved from the iterative extension
156 result.m_field |= (recoed_pattern << m_recoed_ptrn_sft);
157
158 short nhits = std::count_if(valid_hits.begin(), valid_hits.end(),
159 [](const Hit& hit) { return hit.m_isvalid; });
160 if (nhits < 2) {
161 // fill the patern with the 1 hit (or none) and return
162 result.m_field |= (recoed_pattern << m_comp_ptrn_sft);
163 result.m_time = meanTime(valid_hits);
164 result.m_resolution = trackTimeResolution(valid_hits);
165 result.m_hasValidTime = recoed_pattern ? 1 : 0;
166 return result;
167 } else if (nhits == 2) {
168 // if the deltaT cut is passed, the pattern stays the same, otherwise set
169 // to 0 as no hit passes
170 // TODO: find better way to treat this!
171 if (passesDeltaT(valid_hits)) {
172 result.m_field |= (recoed_pattern << m_comp_ptrn_sft); // stays the same
173 result.m_time = meanTime(valid_hits);
174 result.m_resolution = trackTimeResolution(valid_hits);
175 result.m_hasValidTime = 1;
176 return result;
177 } else {
178 result.m_field |= (0b0000 << m_comp_ptrn_sft); // no hit passes
179 result.m_time = m_default_time; // TODO should I just use the mean?
180 result.m_resolution = m_default_time_res;
181 return result;
182 }
183
184 } else {
185 // for 3 or 4 hits, remove hit(s) with worst chi2 if needed
186 float chi2 = calculateChi2(valid_hits);
187 // if the chi2 is below the threshold, keep all hits
188 bool searching = chi2 > m_chi2_threshold;
189 while (searching) {
190 short remove_layer = findLayerWithBadChi2(valid_hits);
191 setLayerAsInvalid(valid_hits, remove_layer);
192 float new_chi2 = calculateChi2(valid_hits);
193 nhits = std::count_if(valid_hits.begin(), valid_hits.end(),
194 [](const Hit& hit) { return hit.m_isvalid; });
195 if (new_chi2 <= m_chi2_threshold or nhits < 3) {
196 searching = false;
197 }
198 } // while loop ended
199
200 short chi2_rej_pattern = getValidPattern(valid_hits);
201
202 if (nhits == 2) {
203 if (passesDeltaT(valid_hits)) {
204 result.m_field |= (chi2_rej_pattern << m_comp_ptrn_sft);
205 result.m_time = meanTime(valid_hits);
206 result.m_resolution = trackTimeResolution(valid_hits);
207 result.m_hasValidTime = 1;
208 return result;
209 } else {
210 result.m_field |= (0b0000 << m_comp_ptrn_sft); // no hit passes
211 result.m_time = m_default_time; // TODO should I just use the mean?
212 result.m_resolution = m_default_time_res;
213 return result;
214 }
215 } else {
216 // 3 or 4 hits, chi2 passed
217 result.m_field |= (chi2_rej_pattern << m_comp_ptrn_sft);
218 result.m_time = meanTime(valid_hits);
219 result.m_resolution = trackTimeResolution(valid_hits);
220 result.m_hasValidTime = 1;
221 return result;
222 }
223 }
224}
225
226std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>
227TrackTimeDefAndQualityAlg::getValidHits(const std::vector<float>& times,
228 const std::vector<char>& has_clusters,
229 const std::vector<int>& hit_classification) const {
230 std::array<Hit, s_hgtd_layers> valid_hits {};
231
232 for (size_t i = 0; i < s_hgtd_layers; i++) {
233 Hit& newhit = valid_hits[i];
234 if (has_clusters.at(i)) {
235 newhit.m_time = times.at(i);
236 newhit.m_isprime = hit_classification.at(i) == 1;
237 newhit.m_isvalid = true;
238 }
239 newhit.m_layer = i;
240 }
241
242 return valid_hits;
243}
244
246 const std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>& hits)
247 const {
248 short pattern = 0x0;
249 for (short i = 0; i < s_hgtd_layers; i++) {
250 if (hits.at(i).m_isvalid) {
251 pattern |= (1 << i);
252 }
253 }
254 return pattern;
255}
256
258 const std::array<Hit, s_hgtd_layers>& hits) const {
259
260 float mean = meanTime(hits);
261
262 float chi2 = 0.;
263 for (const auto& hit : hits) {
264 if (hit.m_isvalid) {
265 chi2 += (hit.m_time - mean) * (hit.m_time - mean) /
266 (hit.m_resolution * hit.m_resolution);
267 }
268 }
269 return chi2;
270}
271
273 const std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>& hits)
274 const {
275 // don't trust the user here.
276 short n_valid = std::count_if(hits.begin(), hits.end(),
277 [](const Hit& hit) { return hit.m_isvalid; });
278 if (n_valid != 2) {
279 return false;
280 }
281 // FIXME this should be doable in a simpler manner...
282 std::vector<float> times;
283 std::vector<float> res;
284 for (const auto& hit : hits) {
285 if (hit.m_isvalid) {
286 times.push_back(hit.m_time);
287 res.push_back(hit.m_resolution);
288 }
289 }
290 // pass if the distance in units of the resolution passes the cut
291 return std::abs(times.at(0) - times.at(1)) <
292 m_deltat_cut * hypot(res.at(0), res.at(1));
293}
294
296 const std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>& hits)
297 const {
298 float sum = 0.;
299 short n = 0;
300 for (const auto& hit : hits) {
301 if (hit.m_isvalid) {
302 sum += hit.m_time;
303 n++;
304 }
305 }
306 return n == 0 ? m_default_time.value() : sum / (float)n;
307}
308
310 const std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>& hits)
311 const {
312
313 float sum = 0.;
314 for (const auto& hit : hits) {
315 if (hit.m_isvalid) {
316 sum += 1. / (hit.m_resolution * hit.m_resolution);
317 }
318 }
319 return sum == 0. ? m_default_time_res.value()
320 : static_cast<float>(std::sqrt(1. / sum));
321}
322
324 std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers> hits) const {
325 short remove_layer = -1;
326 float local_min_chi2 = 999999;
327 for (auto& hit : hits) {
328 // "turn off" hits one after the other to test their impact on the chi2
329 bool validbuff = hit.m_isvalid;
330 hit.m_isvalid = false;
331 float local_chi2 = calculateChi2(hits);
332 hit.m_isvalid = validbuff;
333 if (local_chi2 < local_min_chi2) {
334 local_min_chi2 = local_chi2;
335 remove_layer = hit.m_layer;
336 }
337 }
338 return remove_layer;
339}
340
342 std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>& hits,
343 short layer) const {
344 for (auto& hit : hits) {
345 if (hit.m_layer == layer) {
346 hit.m_isvalid = false;
347 }
348 }
349}
350
353
354 const Trk::TrackStates* tsos =
355 track.trackStateOnSurfaces();
356 if (not tsos) {
357 ATH_MSG_ERROR("Failed to retrieve track state on surfaces");
358 return nullptr;
359 }
360 // loop over the associated hits in ITk in reverse order, since we want to
361 // select the one closest to HGTD to start the extrapolation
362 for (auto i = tsos->rbegin(); i != tsos->rend(); ++i) {
363 const auto* curr_last_tsos = *i;
364 if (not curr_last_tsos) {
365 continue;
366 }
367
368 if (curr_last_tsos->type(Trk::TrackStateOnSurface::Measurement) and
369 curr_last_tsos->trackParameters() and
370 curr_last_tsos->measurementOnTrack()) {
371 return curr_last_tsos->trackParameters();
372 }
373 }
374
375 return nullptr;
376}
377
378std::pair<float, float> TrackTimeDefAndQualityAlg::getRadiusAndZ(const xAOD::TrackParticle& track_particle) const
379{
380 float radius = 0.f;
381 float abs_z = 0.f;
382
383 if (not m_doActs) {
384 const Trk::Track* track = track_particle.track();
385 if (not track) throw std::runtime_error("Cannot retrieve Trk track from Track Particle");
386 const Trk::TrackParameters* last_hit_param = getLastHitOnTrack(*track);
387 if (not last_hit_param) throw std::runtime_error("Cannot retrieve Trk track parameters from Trk track");
388
389 radius = std::hypot(last_hit_param->position().x(),
390 last_hit_param->position().y());
391 abs_z = std::abs(last_hit_param->position().z());
392 } else {
393 // ACTS
394 static const SG::ConstAccessor< ElementLink<ActsTrk::TrackContainer> > actsTrackLink("actsTrack");
395 if (not actsTrackLink.isAvailable(track_particle)) throw std::runtime_error("Track particle does not have link to acts track");
396
397 ElementLink<ActsTrk::TrackContainer> link_to_track = actsTrackLink(track_particle);
398 if (not link_to_track.isValid()) throw std::runtime_error("Element link to acts track is not valid");
399
400 std::optional<ActsTrk::TrackContainer::ConstTrackProxy> optional_track = *link_to_track;
401 if (not optional_track.has_value()) throw std::runtime_error("Link to acts track has no value");
402
403 const ActsTrk::TrackContainer::ConstTrackProxy& track = optional_track.value();
404 const auto lastMeasurementState = Acts::findLastMeasurementState(track);
405 const auto state = lastMeasurementState.value();
406
407 const xAOD::UncalibratedMeasurement *cluster = ActsTrk::detail::xAODUncalibMeasCalibrator::unpack(state.getUncalibratedSourceLink());
408 xAOD::UncalibMeasType clusterType = cluster->type();
409
410 switch (clusterType) {
412 {
413 auto glob = static_cast<const xAOD::PixelCluster*>(cluster)->globalPosition();
414 radius = glob.perp();
415 abs_z = std::abs(glob.z());
416 }
417 break;
419 {
420 auto glob = static_cast<const xAOD::StripCluster*>(cluster)->globalPosition();
421 radius = glob.perp();
422 abs_z = std::abs(glob.z());
423 }
424 break;
426 // return some default numbers
427 return std::make_pair(700, 3000);
428 default:
429 return std::make_pair(radius, abs_z);
430 }; // switch
431 } // acts
432
433 return std::make_pair(radius, abs_z);
434}
435
437 const xAOD::TrackParticle& track_particle) const {
438 auto [radius, abs_z] = getRadiusAndZ(track_particle);
439
440 if (abs_z > 2700) {
441 return true;
442 }
443 if (radius < 350 and abs_z > 2400) {
444 return true;
445 }
446 // region 2
447 if (radius > 205 and radius < 350 and abs_z > 2100) {
448 return true;
449 }
450 // region 3
451 if (radius < 220 and abs_z > 2200) {
452 return true;
453 }
454
455 if (radius < 140 and abs_z > 1890) {
456 return true;
457 }
458
459 return false;
460}
461
462} // namespace HGTD
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
std::pair< std::vector< unsigned int >, bool > res
bool hit(const Container &ids, int pdgId)
Handle class for reading a decoration on an object.
Handle class for reading from StoreGate.
Handle class for adding a decoration to an object.
static const xAOD::UncalibratedMeasurement * unpack(const Acts::SourceLink &sl)
Helper method to unpack an Acts source link to an uncalibrated measurement.
An algorithm that can be simultaneously executed in multiple threads.
const_reverse_iterator rend() const noexcept
Return a const_reverse_iterator pointing at the beginning of the collection.
const_reverse_iterator rbegin() const noexcept
Return a const_reverse_iterator pointing past the end of the collection.
float calculateChi2(const std::array< Hit, s_hgtd_layers > &hits) const
Calculates the chi2 of the hit times given their resolution.
CleaningResult runTimeConsistencyCuts(const std::vector< float > &times, const std::vector< char > &has_clusters, const std::vector< int > &hit_classification) const
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_time_dec_key
SG::ReadDecorHandleKey< xAOD::TrackParticleContainer > m_holesHGTDKey
SG::ReadDecorHandleKey< xAOD::TrackParticleContainer > m_layerClusterTruthClassKey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_time_res_dec_key
std::pair< float, float > getRadiusAndZ(const xAOD::TrackParticle &track_particle) const
SG::ReadHandleKey< xAOD::TrackParticleContainer > m_trackParticleContainerKey
float meanTime(const std::array< Hit, s_hgtd_layers > &hits) const
Calculates the arithmetic mean of the valid hit times;.
void setLayerAsInvalid(std::array< Hit, s_hgtd_layers > &hits, short layer) const
Given a layer number, the hit sitting on this layer is flagged as invalid.
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_hasValidTime_dec_key
float trackTimeResolution(const std::array< Hit, s_hgtd_layers > &hits) const
Calculates the combined resolution.
short findLayerWithBadChi2(std::array< Hit, s_hgtd_layers > hits) const
Identifies time outliers by finding the layer within which a hit contributes negatively to the overal...
SG::ReadDecorHandleKey< xAOD::TrackParticleContainer > m_layerClusterTimeKey
bool lastHitIsOnLastSurface(const xAOD::TrackParticle &track_particle) const
Checks if the last hit on track was found on a pre-specified set of Pixel and Strip layers close to t...
bool passesDeltaT(const std::array< Hit, s_hgtd_layers > &hits) const
Checks two hits for time compatibility.
virtual StatusCode execute(const EventContext &ctx) const override final
short getValidPattern(const std::array< Hit, s_hgtd_layers > &hits) const
Returns the pattern of valid hits in HGTD as a 4-bit bitfield, where a 1 encodes that a valid hit was...
std::array< Hit, s_hgtd_layers > getValidHits(const std::vector< float > &times, const std::vector< char > &has_clusters, const std::vector< int > &hit_classification) const
TrackTimeDefAndQualityAlg(const std::string &name, ISvcLocator *pSvcLocator)
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_summarypattern_dec_key
SG::ReadDecorHandleKey< xAOD::TrackParticleContainer > m_layerHasExtensionKey
const Trk::TrackParameters * getLastHitOnTrack(const Trk::Track &track) const
virtual StatusCode initialize() override final
Helper class to provide constant type-safe access to aux data.
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
Handle class for reading a decoration on an object.
Handle class for adding a decoration to an object.
const Amg::Vector3D & position() const
Access method for the position.
@ Measurement
This is a measurement, and will at least contain a Trk::MeasurementBase.
const Trk::Track * track() const
Returns a pointer (which can be NULL) to the Trk::Track which was used to make this TrackParticle.
virtual xAOD::UncalibMeasType type() const =0
Returns the type of the measurement type as a simple enumeration.
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.
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
DataVector< const Trk::TrackStateOnSurface > TrackStates
ParametersBase< TrackParametersDim, Charged > TrackParameters
StripCluster_v1 StripCluster
Define the version of the strip cluster class.
UncalibratedMeasurement_v1 UncalibratedMeasurement
Define the version of the uncalibrated measurement class.
TrackParticle_v1 TrackParticle
Reference the current persistent version:
PixelCluster_v1 PixelCluster
Define the version of the pixel cluster class.
UncalibMeasType
Define the type of the uncalibrated measurement.
TrackParticleContainer_v1 TrackParticleContainer
Definition of the current "TrackParticle container version".