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());
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
72 layerClusterTruthClassHandle(m_layerClusterTruthClassKey, ctx);
73 ATH_CHECK(layerClusterTruthClassHandle.isValid());
74
75 static const std::vector<char> s_no_holes(4, false);
76 std::optional<SG::ReadDecorHandle<xAOD::TrackParticleContainer, std::vector<char>>>
77 holesHGTDHandle;
78 if (!m_doActs) {
79 holesHGTDHandle.emplace(m_holesHGTDKey, ctx);
80 ATH_CHECK(holesHGTDHandle->isValid());
81 }
82
83 for (const auto* track_ptkl : *track_particles) {
84 // runs the time consistency checks
85 // if no hits are found in HGTD, returns a default time
86 const std::vector<float>& times = layerClusterTimeHandle(*track_ptkl);
87 const std::vector<char>& has_clusters = layerHasExtensionHandle(*track_ptkl);
88 const std::vector<int>& hit_classification = layerClusterTruthClassHandle(*track_ptkl);
89 const std::vector<char>& holes_HGTD = m_doActs ? s_no_holes : (*holesHGTDHandle)(*track_ptkl);
90
92 has_clusters,
93 hit_classification);
94
95 // check if the last hit on track was within the predefined area
96 if (lastHitIsOnLastSurface(*track_ptkl)) {
97 res.m_field |= (0b0000 << m_holes_ptrn_sft);
98 } else {
99 res.m_field |= (0b0001 << m_holes_ptrn_sft);
100 }
101
102 // keep which of the hits associated in reco were primary hits (truth info!)
103 short prime_pattern = 0x0;
104 for (short i = 0; i < s_hgtd_layers; i++) {
105 if (res.m_hits.at(i).m_isprime) {
106 prime_pattern |= (1 << i);
107 }
108 }
109 res.m_field |= (prime_pattern << m_primes_ptrn_sft);
110
111 // expected pattern : 'on which HGTD layer a hit was expected?' which means extrapolation has
112 // reached an active sensor, whether a matching cluster was found or not.
113 // So ‘expected = has_cluster OR HGTD_holes’.
114 short expected_pattern = 0x0;
115 for (short i = 0; i < s_hgtd_layers; i++) {
116 if (has_clusters.at(i) || holes_HGTD.at(i)) {
117 expected_pattern |= (1 << i);
118 }
119 }
120 res.m_field |= (expected_pattern << m_exp_ptrn_sft);
121
122 // decorate the track again with this info
123 time_handle(*track_ptkl) = res.m_time;
124 timeres_handle(*track_ptkl) = res.m_resolution;
125 hasValidTime_handle(*track_ptkl) = res.m_hasValidTime;
126 summary_handle(*track_ptkl) = res.m_field;
127 }
128 return StatusCode::SUCCESS;
129}
130
133
136 const std::vector<char>& has_clusters,
137 const std::vector<int>& hit_classification) const {
138 // get all available hits (see the struct Hit) in a first step
139 std::array<Hit, s_hgtd_layers> valid_hits = getValidHits(times,
140 has_clusters,
141 hit_classification);
142
143 CleaningResult result;
144 result.m_hits = valid_hits;
145 result.m_field = 0x0;
146 result.m_time = m_default_time;
147 result.m_resolution = m_default_time_res;
148 result.m_hasValidTime = 0;
149
150 short recoed_pattern = getValidPattern(valid_hits);
151 // stored the pattern of hits as retrieved from the iterative extension
152 result.m_field |= (recoed_pattern << m_recoed_ptrn_sft);
153
154 short nhits = std::count_if(valid_hits.begin(), valid_hits.end(),
155 [](const Hit& hit) { return hit.m_isvalid; });
156 if (nhits < 2) {
157 // fill the patern with the 1 hit (or none) and return
158 result.m_field |= (recoed_pattern << m_comp_ptrn_sft);
159 result.m_time = meanTime(valid_hits);
160 result.m_resolution = trackTimeResolution(valid_hits);
161 result.m_hasValidTime = recoed_pattern ? 1 : 0;
162 return result;
163 } else if (nhits == 2) {
164 // if the deltaT cut is passed, the pattern stays the same, otherwise set
165 // to 0 as no hit passes
166 // TODO: find better way to treat this!
167 if (passesDeltaT(valid_hits)) {
168 result.m_field |= (recoed_pattern << m_comp_ptrn_sft); // stays the same
169 result.m_time = meanTime(valid_hits);
170 result.m_resolution = trackTimeResolution(valid_hits);
171 result.m_hasValidTime = 1;
172 return result;
173 } else {
174 result.m_field |= (0b0000 << m_comp_ptrn_sft); // no hit passes
175 result.m_time = m_default_time; // TODO should I just use the mean?
176 result.m_resolution = m_default_time_res;
177 return result;
178 }
179
180 } else {
181 // for 3 or 4 hits, remove hit(s) with worst chi2 if needed
182 float chi2 = calculateChi2(valid_hits);
183 // if the chi2 is below the threshold, keep all hits
184 bool searching = chi2 > m_chi2_threshold;
185 while (searching) {
186 short remove_layer = findLayerWithBadChi2(valid_hits);
187 setLayerAsInvalid(valid_hits, remove_layer);
188 float new_chi2 = calculateChi2(valid_hits);
189 nhits = std::count_if(valid_hits.begin(), valid_hits.end(),
190 [](const Hit& hit) { return hit.m_isvalid; });
191 if (new_chi2 <= m_chi2_threshold or nhits < 3) {
192 searching = false;
193 }
194 } // while loop ended
195
196 short chi2_rej_pattern = getValidPattern(valid_hits);
197
198 if (nhits == 2) {
199 if (passesDeltaT(valid_hits)) {
200 result.m_field |= (chi2_rej_pattern << m_comp_ptrn_sft);
201 result.m_time = meanTime(valid_hits);
202 result.m_resolution = trackTimeResolution(valid_hits);
203 result.m_hasValidTime = 1;
204 return result;
205 } else {
206 result.m_field |= (0b0000 << m_comp_ptrn_sft); // no hit passes
207 result.m_time = m_default_time; // TODO should I just use the mean?
208 result.m_resolution = m_default_time_res;
209 return result;
210 }
211 } else {
212 // 3 or 4 hits, chi2 passed
213 result.m_field |= (chi2_rej_pattern << m_comp_ptrn_sft);
214 result.m_time = meanTime(valid_hits);
215 result.m_resolution = trackTimeResolution(valid_hits);
216 result.m_hasValidTime = 1;
217 return result;
218 }
219 }
220}
221
222std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>
223TrackTimeDefAndQualityAlg::getValidHits(const std::vector<float>& times,
224 const std::vector<char>& has_clusters,
225 const std::vector<int>& hit_classification) const {
226 std::array<Hit, s_hgtd_layers> valid_hits {};
227
228 for (size_t i = 0; i < s_hgtd_layers; i++) {
229 Hit& newhit = valid_hits[i];
230 if (has_clusters.at(i)) {
231 newhit.m_time = times.at(i);
232 newhit.m_isprime = hit_classification.at(i) == 1;
233 newhit.m_isvalid = true;
234 }
235 newhit.m_layer = i;
236 }
237
238 return valid_hits;
239}
240
242 const std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>& hits)
243 const {
244 short pattern = 0x0;
245 for (short i = 0; i < s_hgtd_layers; i++) {
246 if (hits.at(i).m_isvalid) {
247 pattern |= (1 << i);
248 }
249 }
250 return pattern;
251}
252
254 const std::array<Hit, s_hgtd_layers>& hits) const {
255
256 float mean = meanTime(hits);
257
258 float chi2 = 0.;
259 for (const auto& hit : hits) {
260 if (hit.m_isvalid) {
261 chi2 += (hit.m_time - mean) * (hit.m_time - mean) /
262 (hit.m_resolution * hit.m_resolution);
263 }
264 }
265 return chi2;
266}
267
269 const std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>& hits)
270 const {
271 // don't trust the user here.
272 short n_valid = std::count_if(hits.begin(), hits.end(),
273 [](const Hit& hit) { return hit.m_isvalid; });
274 if (n_valid != 2) {
275 return false;
276 }
277 // FIXME this should be doable in a simpler manner...
278 std::vector<float> times;
279 std::vector<float> res;
280 for (const auto& hit : hits) {
281 if (hit.m_isvalid) {
282 times.push_back(hit.m_time);
283 res.push_back(hit.m_resolution);
284 }
285 }
286 // pass if the distance in units of the resolution passes the cut
287 return std::abs(times.at(0) - times.at(1)) <
288 m_deltat_cut * hypot(res.at(0), res.at(1));
289}
290
292 const std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>& hits)
293 const {
294 float sum = 0.;
295 short n = 0;
296 for (const auto& hit : hits) {
297 if (hit.m_isvalid) {
298 sum += hit.m_time;
299 n++;
300 }
301 }
302 return n == 0 ? m_default_time.value() : sum / (float)n;
303}
304
306 const std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>& hits)
307 const {
308
309 float sum = 0.;
310 for (const auto& hit : hits) {
311 if (hit.m_isvalid) {
312 sum += 1. / (hit.m_resolution * hit.m_resolution);
313 }
314 }
315 return sum == 0. ? m_default_time_res.value()
316 : static_cast<float>(std::sqrt(1. / sum));
317}
318
320 std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers> hits) const {
321 short remove_layer = -1;
322 float local_min_chi2 = 999999;
323 for (auto& hit : hits) {
324 // "turn off" hits one after the other to test their impact on the chi2
325 bool validbuff = hit.m_isvalid;
326 hit.m_isvalid = false;
327 float local_chi2 = calculateChi2(hits);
328 hit.m_isvalid = validbuff;
329 if (local_chi2 < local_min_chi2) {
330 local_min_chi2 = local_chi2;
331 remove_layer = hit.m_layer;
332 }
333 }
334 return remove_layer;
335}
336
338 std::array<TrackTimeDefAndQualityAlg::Hit, s_hgtd_layers>& hits,
339 short layer) const {
340 for (auto& hit : hits) {
341 if (hit.m_layer == layer) {
342 hit.m_isvalid = false;
343 }
344 }
345}
346
349
350 const Trk::TrackStates* tsos =
351 track.trackStateOnSurfaces();
352 if (not tsos) {
353 ATH_MSG_ERROR("Failed to retrieve track state on surfaces");
354 return nullptr;
355 }
356 // loop over the associated hits in ITk in reverse order, since we want to
357 // select the one closest to HGTD to start the extrapolation
358 for (auto i = tsos->rbegin(); i != tsos->rend(); ++i) {
359 const auto* curr_last_tsos = *i;
360 if (not curr_last_tsos) {
361 continue;
362 }
363
364 if (curr_last_tsos->type(Trk::TrackStateOnSurface::Measurement) and
365 curr_last_tsos->trackParameters() and
366 curr_last_tsos->measurementOnTrack()) {
367 return curr_last_tsos->trackParameters();
368 }
369 }
370
371 return nullptr;
372}
373
374std::pair<float, float> TrackTimeDefAndQualityAlg::getRadiusAndZ(const xAOD::TrackParticle& track_particle) const
375{
376 float radius = 0.f;
377 float abs_z = 0.f;
378
379 if (not m_doActs) {
380 const Trk::Track* track = track_particle.track();
381 if (not track) throw std::runtime_error("Cannot retrieve Trk track from Track Particle");
382 const Trk::TrackParameters* last_hit_param = getLastHitOnTrack(*track);
383 if (not last_hit_param) throw std::runtime_error("Cannot retrieve Trk track parameters from Trk track");
384
385 radius = std::hypot(last_hit_param->position().x(),
386 last_hit_param->position().y());
387 abs_z = std::abs(last_hit_param->position().z());
388 } else {
389 // ACTS
390 static const SG::ConstAccessor< ElementLink<ActsTrk::TrackContainer> > actsTrackLink("actsTrack");
391 if (not actsTrackLink.isAvailable(track_particle)) throw std::runtime_error("Track particle does not have link to acts track");
392
393 ElementLink<ActsTrk::TrackContainer> link_to_track = actsTrackLink(track_particle);
394 if (not link_to_track.isValid()) throw std::runtime_error("Element link to acts track is not valid");
395
396 std::optional<ActsTrk::TrackContainer::ConstTrackProxy> optional_track = *link_to_track;
397 if (not optional_track.has_value()) throw std::runtime_error("Link to acts track has no value");
398
399 const ActsTrk::TrackContainer::ConstTrackProxy& track = optional_track.value();
400 const auto lastMeasurementState = Acts::findLastMeasurementState(track);
401 const auto state = lastMeasurementState.value();
402
403 const xAOD::UncalibratedMeasurement *cluster = ActsTrk::detail::xAODUncalibMeasCalibrator::unpack(state.getUncalibratedSourceLink());
404 xAOD::UncalibMeasType clusterType = cluster->type();
405
406 switch (clusterType) {
408 {
409 auto glob = static_cast<const xAOD::PixelCluster*>(cluster)->globalPosition();
410 radius = glob.perp();
411 abs_z = std::abs(glob.z());
412 }
413 break;
415 {
416 auto glob = static_cast<const xAOD::StripCluster*>(cluster)->globalPosition();
417 radius = glob.perp();
418 abs_z = std::abs(glob.z());
419 }
420 break;
422 // return some default numbers
423 return std::make_pair(700, 3000);
424 default:
425 return std::make_pair(radius, abs_z);
426 }; // switch
427 } // acts
428
429 return std::make_pair(radius, abs_z);
430}
431
433 const xAOD::TrackParticle& track_particle) const {
434 auto [radius, abs_z] = getRadiusAndZ(track_particle);
435
436 if (abs_z > 2700) {
437 return true;
438 }
439 if (radius < 350 and abs_z > 2400) {
440 return true;
441 }
442 // region 2
443 if (radius > 205 and radius < 350 and abs_z > 2100) {
444 return true;
445 }
446 // region 3
447 if (radius < 220 and abs_z > 2200) {
448 return true;
449 }
450
451 if (radius < 140 and abs_z > 1890) {
452 return true;
453 }
454
455 return false;
456}
457
458} // 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
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".