ATLAS Offline Software
Loading...
Searching...
No Matches
HGTDTrackExtensionAlg.cxx
Go to the documentation of this file.
1
11
13
14// Athena
15#include "AsgTools/ToolStore.h"
17
18// ACTS
24#include "ActsInterop/Logger.h"
25
26// ActsTrk
38
39// STL
41#include "Acts/TrackFinding/TrackStateCreator.hpp"
42#include "Acts/Surfaces/PlaneSurface.hpp"
43#include "Acts/Surfaces/RectangleBounds.hpp"
44#include "Acts/Utilities/VectorHelpers.hpp"
45#include <optional>
49#include "GaudiKernel/PhysicalConstants.h" // for Gaudi::Units::c_light
51
52
53
54namespace ActsTrk{
55
57
58namespace {
59
63// by the propagator
64
65struct Collector {
67
68 template <typename propagator_state_t, typename stepper_t,
69 typename navigator_t>
70 Acts::Result<void> act(propagator_state_t& state, const stepper_t& /*stepper*/,
71 const navigator_t& navigator, result_type& result,
72 const Acts::Logger& /*logger*/) const {
73 const Acts::Surface* currentSurface = navigator.currentSurface(state.navigation);
74 if (currentSurface == nullptr) {
75 return Acts::Result<void>::success();
76 }
77
78 assert(result != nullptr && "Result type is nullptr");
79
80 if (currentSurface->surfacePlacement() != nullptr) {
81 const auto* detElem = dynamic_cast<const ActsDetectorElement*>(currentSurface->surfacePlacement());
82 if(detElem != nullptr) {
83 detail::addToExpectedLayerPattern(*result, *detElem);
84 }
85 }
86
87 return Acts::Result<void>::success();
88 }
89};
90}
91
92// ---------------- Initialize ----------------
94{
95 ATH_MSG_DEBUG("Initializing " << name() << "...");
96
100
103 ATH_CHECK(m_actsTrackLinkKey.initialize());
104
106
107 // Initialize all WriteDecorHandleKeys
108 ATH_CHECK(m_layerHasExtensionKey.initialize());
111 ATH_CHECK(m_layerClusterTimeKey.initialize());
112 ATH_CHECK(m_extrapXKey.initialize());
113 ATH_CHECK(m_extrapYKey.initialize());
114 ATH_CHECK(m_numHGTDHitsKey.initialize());
115 ATH_CHECK(m_hgtdTrackLinkKey.initialize());
116
117 // Initialize surface accessor
119
120 //Retreive HGTD ID helper
121 ATH_CHECK(detStore()->retrieve(m_id_helper, "HGTD_ID"));
122
123 return StatusCode::SUCCESS;
124}
125
126// ---------------- Execute ----------------
127
128StatusCode HGTDTrackExtensionAlg::execute(const EventContext& ctx) const
129{
130 ATH_MSG_DEBUG("Executing " << name() << "...");
131
132 auto timer = Monitored::Timer<std::chrono::milliseconds>("TIME_execute");
133 auto mon_nTracks = Monitored::Scalar<int>("nTracks");
134 auto mon = Monitored::Group(m_monTool, timer, mon_nTracks);
135
136 // ================================================== //
137 // ========= RETRIEVE TRACK PARTICLES =============== //
138 // ================================================== //
139
140 const xAOD::TrackParticleContainer* trackParticles{nullptr};
141 ATH_CHECK(SG::get(trackParticles, m_trackParticleContainerName, ctx));
142
143 ATH_MSG_DEBUG("Size of trackParticles collection " << trackParticles->size());
144
145 // Create WriteDecorHandles for all decorations
154
155 // ================================================== //
156 // ============ RETRIEVE MEASUREMENTS =============== //
157 // ================================================== //
158 const xAOD::HGTDClusterContainer* hgtdClusters{nullptr};
159 ATH_CHECK(SG::get(hgtdClusters, m_HGTDClusterContainerName, ctx));
160
161
162 std::vector<const xAOD::UncalibratedMeasurementContainer *> uncalibratedMeasurementContainers;
163 std::size_t total_measurements = 0;
164 ATH_CHECK(getContainersFromKeys(ctx, m_uncalibratedMeasurementContainerKeys, uncalibratedMeasurementContainers, total_measurements));
165
166
167 detail::MeasurementIndex measurementIndex(uncalibratedMeasurementContainers.size());
168 for (std::size_t icontainer = 0; icontainer < uncalibratedMeasurementContainers.size(); ++icontainer) {
169 measurementIndex.addMeasurements(*uncalibratedMeasurementContainers[icontainer]);
170 }
171
172 detail::TrackFindingMeasurements measurements(uncalibratedMeasurementContainers.size());
173 for (std::size_t icontainer = 0; icontainer < uncalibratedMeasurementContainers.size(); ++icontainer) {
174 ATH_MSG_DEBUG("Create " << uncalibratedMeasurementContainers[icontainer]->size() <<
175 " source links from measurements in " << m_uncalibratedMeasurementContainerKeys[icontainer].key());
176 measurements.addMeasurements(icontainer,
177 *uncalibratedMeasurementContainers[icontainer],
178 *m_trackingGeometrySvc->surfaceIdMap(),
179 &measurementIndex);
180 }
181
182 ATH_MSG_DEBUG("measurement index size = " << measurementIndex.size());
183
184
185 if (m_trackStatePrinter.isSet()) {
186 m_trackStatePrinter->printMeasurements(ctx, uncalibratedMeasurementContainers, measurements.measurementOffsets());
187 }
188
189 // ================================================== //
190 // ===================== COMPUTATION ================ //
191 // ================================================== //
192
193 EventStats event_stat;
194 event_stat.resize(m_stat.size());
195
196 DetectorContextHolder detContext {
197 .geometry = m_ctxProvider.getGeometryContext(ctx),
198 .magField = m_ctxProvider.getMagneticFieldContext(ctx),
199 // CalibrationContext converter not implemented yet.
200 .calib = m_ctxProvider.getCalibrationContext(ctx)
201 };
202
203 Acts::VectorTrackContainer actsTrackBackend;
204 Acts::VectorMultiTrajectory actsTrackStateBackend;
205
206 auto atomicMax=[](std::size_t new_val, std::atomic<std::size_t> &dest) -> void {
207 std::size_t is_value;
208 do {
209 is_value = dest;
210 if (is_value>=new_val) return;
211 } while (!dest.compare_exchange_weak(is_value, new_val));
212 };
213 atomicMax(actsTrackBackend.size(), m_nTrackReserve);
214 atomicMax(actsTrackStateBackend.size(), m_nTrackStateReserve);
215
216 detail::RecoTrackContainer actsTracksContainer(actsTrackBackend,
217 actsTrackStateBackend);
218
219
220 addCountsAndProperties(actsTracksContainer, m_addCounts.value());
221
222 detail::ExpectedLayerPatternHelper::add(actsTracksContainer);
223
224 int extension_index{0};
225 // Loop over each track particle and decorate it with various information
226 std::unordered_map<uint32_t, uint32_t> extensions;
227 for (const xAOD::TrackParticle* trackParticle : *trackParticles) {
228 // Default to empty track data
229 TrackExtensionData trackData;
230
231 std::optional<ActsTrk::TrackContainer::ConstTrackProxy> optional_track = getActsTrack(*trackParticle);
232 if (!optional_track.has_value()) {
233 ATH_MSG_ERROR("No valid ACTS track associated with TrackParticle " << trackParticle->index());
234 return StatusCode::FAILURE;
235 }
236
237 const ActsTrk::TrackContainer::ConstTrackProxy& track = optional_track.value();
238
239 // Retrieve quantities for ACTS track
240 float trackEta = Acts::VectorHelpers::eta(track.momentum());
241
242 // Check eta coverage first
243 if (std::abs(trackEta) < m_minEtaAcceptance or std::abs(trackEta) > m_maxEtaAcceptance) {
244 ATH_MSG_DEBUG("!!!!! ------ Track eta " << trackEta
245 << " outside eta range [" << m_minEtaAcceptance.value() << ", " << m_maxEtaAcceptance.value()
246 << "], skipping extension ------ !!!!!");
247
248 // set default values
249 trackData.hasClusterVec = {false, false, false, false};
250 trackData.numHGTDHits = 0;
251 layerHasExtensionHandle(*trackParticle) = trackData.hasClusterVec;
252 layerExtensionChi2Handle(*trackParticle) = trackData.chi2Vec;
253 layerClusterRawTimeHandle(*trackParticle) = trackData.rawTimeVec;
254 layerClusterTimeHandle(*trackParticle) = trackData.timeVec;
255 extrapXHandle(*trackParticle) = trackData.extrapX;
256 extrapYHandle(*trackParticle) = trackData.extrapY;
257 numHGTDHitsHandle(*trackParticle) = trackData.numHGTDHits;
258
259 continue;
260 }
261
262 float trackpT = track.transverseMomentum();
263 float trackPhi = track.phi();
264 float trackNmeasurements = track.nMeasurements();
265
266 ATH_MSG_DEBUG("TrackParticle " << trackParticle->index() <<
267 " has ACTS track with eta: " << trackEta <<
268 ", phi: " << trackPhi <<
269 ", pT: " << trackpT <<
270 " and nMeasurements: " << trackNmeasurements);
271
272 // Parameters at last measurement state
273 const auto lastMeasurementState = Acts::findLastMeasurementState(track);
274 if (not lastMeasurementState.ok()) {
275 ATH_MSG_ERROR("Problem finding last measurement state for acts track");
276 return StatusCode::FAILURE;
277 }
278 const Acts::BoundTrackParameters lastMeasurementStateParameters = track.createParametersFromState(*lastMeasurementState);
279
280 // Parameters at reference state of track - not necessarily a measurement state!!!
281 const Acts::Surface& refSurface = track.referenceSurface();
282 const Acts::BoundTrackParameters parametersAtRefSurface(refSurface.getSharedPtr(),
283 track.parameters(),
284 track.covariance(),
285 track.particleHypothesis());
286
287 ATH_MSG_DEBUG("Initial track parameters for extension - lastMeasurementStateParameters:");
288 ATH_MSG_DEBUG(" - eta: " << -1 * log(tan(lastMeasurementStateParameters.theta() * 0.5)));
289 ATH_MSG_DEBUG(" - phi: " << lastMeasurementStateParameters.phi());
290 ATH_MSG_DEBUG(" - pT: " << std::abs(1./lastMeasurementStateParameters.qOverP() * std::sin(lastMeasurementStateParameters.theta())));
291 ATH_MSG_DEBUG(" - theta: " << lastMeasurementStateParameters.theta());
292 ATH_MSG_DEBUG(" - qOverP: " << lastMeasurementStateParameters.qOverP());
293 ATH_MSG_DEBUG(" - covariance exists: " << (lastMeasurementStateParameters.covariance().has_value() ? "yes" : "no"));
294
295
296 // ActsTrk::MutableTrackContainer tracksContainerTemp;
297 Acts::VectorTrackContainer trackBackend;
298 Acts::VectorMultiTrajectory trackStateBackend;
299 detail::RecoTrackContainer tracksContainerTemp(trackBackend, trackStateBackend);
300
301 addCountsAndProperties(tracksContainerTemp, m_addCounts.value());
302
303 detail::ExpectedLayerPatternHelper::add(tracksContainerTemp);
304
305 // Now use the *last measurement parameters* parameters for the CKF
306 if(findExtension(ctx,
307 detContext,
308 measurements,
309 measurementIndex,
310 lastMeasurementStateParameters,
311 tracksContainerTemp,
312 actsTracksContainer,
313 event_stat,
314 refSurface,
315 extension_index))
316 {
317 const detail::RecoTrackContainer::TrackProxy& trackProxy = tracksContainerTemp.at(extension_index);
318 trackData = processTrackExtension(ctx, trackParticle, trackProxy, hgtdClusters);
319 extensions.insert(std::make_pair(trackParticle->index(), actsTracksContainer.size() - 1));
320
321 }
322 else{
323 trackData.hasClusterVec = {false, false, false, false};
324 trackData.numHGTDHits = 0;
325 }
326 // Apply decorations from the track data
327 layerHasExtensionHandle(*trackParticle) = trackData.hasClusterVec;
328 layerExtensionChi2Handle(*trackParticle) = trackData.chi2Vec;
329 layerClusterRawTimeHandle(*trackParticle) = trackData.rawTimeVec;
330 layerClusterTimeHandle(*trackParticle) = trackData.timeVec;
331 extrapXHandle(*trackParticle) = trackData.extrapX;
332 extrapYHandle(*trackParticle) = trackData.extrapY;
333 numHGTDHitsHandle(*trackParticle) = trackData.numHGTDHits;
334 } // loop on tracks
335
336 // ================================================== //
337 // ===================== OUTPUTS ==================== //
338 // ================================================== //
339
340 ATH_MSG_DEBUG(" \\__ Found " << actsTracksContainer.size() << " extensions");
341
342 // update the reserve space
343 if (actsTrackBackend.size() > m_nTrackReserve) {
344 m_nTrackReserve = static_cast<std::size_t>( std::ceil(m_memorySafetyMargin * actsTrackBackend.size()) );
345 }
346 if (actsTrackStateBackend.size() > m_nTrackStateReserve) {
347 m_nTrackStateReserve = static_cast<std::size_t>( std::ceil(m_memorySafetyMargin * actsTrackStateBackend.size()) );
348 }
349
350 // convert to const
351 Acts::ConstVectorTrackContainer constTrackBackend( std::move(actsTrackBackend) );
352 Acts::ConstVectorMultiTrajectory constTrackStateBackend( std::move(actsTrackStateBackend) );
353 std::unique_ptr< ActsTrk::TrackContainer> constTracksContainer = std::make_unique< ActsTrk::TrackContainer >( std::move(constTrackBackend),
354 std::move(constTrackStateBackend) );
355
357
358 ATH_MSG_DEBUG(" \\__ Tracks Container `" << m_trackContainerKey.key() << "` created ...");
359 ATH_CHECK(trackContainerHandle.record(std::move(constTracksContainer)));
360
361 const ActsTrk::TrackContainer *const_track_container_ptr = trackContainerHandle.cptr();
362
363 for ( const std::pair< const uint32_t,uint32_t> &ext : extensions) {
364
365 hgtdTrackLink(*trackParticles->at(ext.first))
366 = ElementLink<ActsTrk::TrackContainer>( *const_track_container_ptr,
367 ext.second );
368
369}
370
371 return StatusCode::SUCCESS;
372}
373
375 const EventContext &ctx,
376 const DetectorContextHolder& detContext,
377 const detail::TrackFindingMeasurements &measurements,
378 const detail::MeasurementIndex& measurementIndex,
379 const Acts::BoundTrackParameters & initialParameters,
380 detail::RecoTrackContainer &tracksContainerTemp,
381 detail::RecoTrackContainer &actsTracksContainer,
382 EventStats &event_stat,
383 const Acts::Surface& refSurface,
384 int& extension_index) const{
385
386 //Setting pSurface to nullptr
387 auto [options, secondOptions, measurementSelector] = getDefaultOptions(ctx, detContext, measurements, nullptr);
388
389
390 std::size_t category_i = 0;
391 const auto &trackSelectorCfg = trackFinder().trackSelector.config();
392 auto stopBranchProxy = [&](const detail::RecoTrackContainer::TrackProxy &track,
393 const detail::RecoTrackContainer::TrackStateProxy &trackState) -> BranchStopperResult {
394 return stopBranch(track, trackState, trackSelectorCfg, detContext.geometry, measurementIndex, 0, event_stat[category_i]);
395 };
396 options.extensions.branchStopper.connect(stopBranchProxy);
397
398 Acts::PropagatorOptions<detail::Stepper::Options, detail::Navigator::Options,
399 Acts::ActorList<Acts::MaterialInteractor>>
400 extrapolationOptions(detContext.geometry, detContext.magField);
401
402 Acts::TrackExtrapolationStrategy extrapolationStrategy =
403 Acts::TrackExtrapolationStrategy::first;
404
405 // Get the Acts tracks, given the initial parameters from last hit of itk track
406 Acts::Result<std::vector<TrkProxy> > result =
407 trackFinder().ckf.findTracks(initialParameters, options, tracksContainerTemp);
408
409 // Track finding result
410 if (not result.ok()) {
411 ATH_MSG_WARNING("Track finding failed with error" << result.error());
412 return false;
413 }
414
415 ATH_MSG_DEBUG("Built " << tracksContainerTemp.size() << " extensions from it");
416 auto &foundTracks = result.value();
417
418 // loop on the tracks we have just found
419 int best_track_index = -1;
420 float best_track_chi2 = 1000;
421 TrkProxy &best_track_proxy = foundTracks.at(0);
422
423 for (TrkProxy &firstTrack : foundTracks) {
424 if((firstTrack.chi2() > 0) and (firstTrack.chi2() < best_track_chi2)){
425 best_track_index = firstTrack.index();
426 best_track_chi2 = firstTrack.chi2();
427 best_track_proxy = firstTrack;
428 }
429 }
430
431 if(best_track_index == -1) return false;
432
433 ATH_MSG_DEBUG("Best extension index " << best_track_proxy.index() <<
434 " nMeas " << best_track_proxy.nMeasurements() <<
435 " chi2 " << best_track_proxy.chi2());
436
437 if(addTrack(detContext,
438 best_track_proxy,
439 refSurface,
440 extrapolationStrategy,
441 actsTracksContainer,
442 measurementIndex,
443 tracksContainerTemp)){
444
445 extension_index = best_track_index;
446 return true;
447 }
448 else {
449 ATH_MSG_DEBUG("Track failed selection, not adding it");
450 return false;
451 }
452
453}
454
456 const EventContext& ctx,
457 const xAOD::TrackParticle* trackParticle,
458 const detail::RecoTrackContainer::TrackProxy& trackProxy,
459 const xAOD::HGTDClusterContainer* hgtdClusters) const {
460
462
463 // Modern approach uses surface accessor instead of detector element map
464
465 // Apply track smoothing before trying to access chi2 values
466 Acts::GeometryContext geoContext = m_ctxProvider.getGeometryContext(ctx);
467 const Acts::TrackingGeometry* acts_tracking_geometry = m_trackingGeometrySvc->trackingGeometry().get();
468
469
470 // Count measurements, holes, and HGTD hits specifically
471 std::size_t nMeasurements = 0;
472 std::size_t nHoles = 0;
473 std::size_t nOutliers = 0;
474 std::size_t nHGTDHits = 0;
475
476 std::vector<char> hasHitInLayer = {false, false, false, false};
477 std::vector<float> chi2PerLayer = {-1.0, -1.0, -1.0, -1.0};
478 std::vector<float> timePerLayer = {-1.0, -1.0, -1.0, -1.0};
479 std::vector<float> rawTimePerLayer = {-1.0, -1.0, -1.0, -1.0};
480
481 // Extrapolated position - get the position at the first HGTD surface encountered
482 float extrapX = 0.0;
483 float extrapY = 0.0;
484 float extrapZ = 0.0;
485 bool foundExtrapolation = false;
486
487 for (auto state : trackProxy.trackStatesReversed()) {
488 auto flags = state.typeFlags();
489 if (flags.isHole()) {
490 nHoles++;
491 } else if (flags.isOutlier()) {
492 nOutliers++;
493 } else if (flags.isMeasurement()) {
494 nMeasurements++;
495
496 // Check if this is an HGTD hit
497 const auto& surface = state.referenceSurface();
498 const auto* detElem = getActsDetectorElement(surface);
499
500 // Check if measurement is at a valid HGTD layer
501 if (detElem == nullptr || detElem->detectorType() != DetectorType::Hgtd) {
502 continue;
503 }
504 const std::size_t layerIndex = m_id_helper->layer(detElem->identify());
505
506 nHGTDHits++;
507 hasHitInLayer[layerIndex] = true;
508 chi2PerLayer[layerIndex] = state.chi2();
509
510 // Get the measured time from the calibrated 3D measurement (local x, y, time)
511 float rawTime = 0.0f;
512 float calibratedTime = 0.0f;
513
514 if (state.hasCalibrated()) {
515 // Extract time from calibrated data
516 try {
517 const auto& calibrated = state.template calibrated<3>();
518 calibratedTime = ActsTrk::timeToAthena(calibrated(2));
519 ATH_MSG_DEBUG("Got time from calibrated<3>: " << calibratedTime);
520 } catch (const std::exception& e) {
521 ATH_MSG_WARNING("Failed to extract time from calibrated<3>: " << e.what());
522 }
523 }
524
525 // Extract raw time from HGTD clusters
526 const xAOD::HGTDCluster* cluster = getHGTDClusterFromState(ctx, state, hgtdClusters);
527
528 if (cluster) {
529 rawTime = cluster->time();
530 ATH_MSG_DEBUG("Got raw time from cluster: " << rawTime);
531 } else {
532 ATH_MSG_WARNING("Could not get cluster from state");
533 }
534
535 // Store the raw time
536 rawTimePerLayer[layerIndex] = calibratedTime;
537 if (cluster) {
538 auto [correctedTime, timeErr] = correctTOF(
539 trackParticle,
540 cluster,
541 calibratedTime,
542 0.0, // time error set to zero for now!
543 acts_tracking_geometry,
544 geoContext);
545 timePerLayer[layerIndex] = correctedTime;
546 ATH_MSG_DEBUG("Applied TOF correction: " << calibratedTime << " -> " << correctedTime);
547 } else {
548 // No cluster or time, use raw time
549 timePerLayer[layerIndex] = calibratedTime;
550 ATH_MSG_DEBUG("No cluster found for TOF correction, using calibrated time: " << calibratedTime);
551 }
552
553 // For extrapolation: use the first HGTD hit's surface position.
554 if (!foundExtrapolation) {
555 foundExtrapolation = true;
556 if (state.hasPredicted()) {
557 // Get the local predicted position
558 const auto& predicted = state.predicted();
559 Acts::Vector2 localPos(predicted[Acts::eBoundLoc0], predicted[Acts::eBoundLoc1]);
560
561 // Transform to global coordinates
562 Acts::Vector3 globalPos = surface.localToGlobal(
563 geoContext,
564 localPos,
565 Acts::Vector3::Zero());
566
567 extrapX = globalPos.x();
568 extrapY = globalPos.y();
569 extrapZ = globalPos.z();
570
571 ATH_MSG_DEBUG("Extrapolated position (predicted) at HGTD: x=" << extrapX
572 << ", y=" << extrapY << ", z=" << extrapZ);
573 } else {
574 // Fallback to surface center
575 Acts::Vector3 globalPos = surface.center(geoContext);
576 extrapX = globalPos.x();
577 extrapY = globalPos.y();
578 extrapZ = globalPos.z();
579
580 ATH_MSG_DEBUG("Extrapolated position (surface center) at HGTD: x=" << extrapX
581 << ", y=" << extrapY << ", z=" << extrapZ);
582 }
583 }
584 ATH_MSG_DEBUG("Found HGTD hit on layer " << layerIndex
585 << ", chi2=" << chi2PerLayer[layerIndex]
586 << ", time=" << timePerLayer[layerIndex]);
587 }
588 }
589
590 ATH_MSG_DEBUG("Extension Statistics: "
591 << " nMeasurements=" << nMeasurements
592 << " nHGTDHits=" << nHGTDHits
593 << " nHoles=" << nHoles
594 << " nOutliers=" << nOutliers
595 << " extrapolation found: " << (foundExtrapolation ? "yes" : "no"));
596
597
598 // Fill the data structure with results
599 data.hasClusterVec = std::move(hasHitInLayer);
600 data.chi2Vec = std::move(chi2PerLayer);
601 data.timeVec = std::move(timePerLayer);
602 data.rawTimeVec = std::move(rawTimePerLayer);
603 data.extrapX = extrapX;
604 data.extrapY = extrapY;
605 data.extrapZ = extrapZ;
606 data.numHGTDHits = nHGTDHits;
607
608 return data;
609}
610
611std::pair<float, float> HGTDTrackExtensionAlg::correctTOF(
612 const xAOD::TrackParticle* trackParticle,
613 const xAOD::HGTDCluster* cluster,
614 float measuredTime,
615 float measuredTimeErr,
616 const Acts::TrackingGeometry*,
617 const Acts::GeometryContext& geoContext) const {
618
619 ATH_MSG_DEBUG("Correcting input time: " << measuredTime);
620
621 if (!trackParticle || !cluster) {
622 ATH_MSG_WARNING("Null pointer provided to correctTOF");
623 return {measuredTime, measuredTimeErr}; // Return uncorrected values
624 }
625
626 // Get the surface for this HGTD cluster
627 const Acts::Surface* surface = nullptr;
628 try {
629 surface = m_surfAcc.get(cluster);
630 } catch (const std::exception& e) {
631 ATH_MSG_WARNING("Exception getting surface: " << e.what());
632 return {measuredTime, measuredTimeErr}; // Return uncorrected values
633 }
634
635 if (!surface) {
636 ATH_MSG_WARNING("Could not determine surface for HGTD cluster with id "
637 << cluster->identifier());
638 return {measuredTime, measuredTimeErr}; // Return uncorrected values
639 }
640
641 // Get the global position of the hit
642 Acts::Vector3 globalHitPos;
643 try {
644 // Try to get the cluster's local position
645 auto localPos = cluster->localPosition<3>();
646 // Transform to global coordinates
647 globalHitPos = surface->localToGlobal(
648 geoContext,
649 Acts::Vector2(localPos[0], localPos[1]),
650 Acts::Vector3::Zero());
651 } catch (const std::exception& e) {
652 ATH_MSG_WARNING("Failed to transform position: " << e.what());
653 // Fall back to surface center
654 globalHitPos = surface->center(geoContext);
655 }
656
657 // Get track origin (vertex position)
658 //option 1 - use beamspot
659 //Amg::Vector3D trackOrigin(trackParticle->vx(), trackParticle->vy(), trackParticle->vz());
660
661 //option 2 - use perigee - this is what is done in legacy code:
662 // https://gitlab.cern.ch/atlas/athena/-/blob/main/HighGranularityTimingDetector/HGTD_Reconstruction/HGTD_RecTools/src/StraightLineTOFcorrectionTool.cxx
663 // Get track origin from perigee parameters instead of vertex
664 // In ACTS, this is the d0 and z0 parameter with reference to the beamline
665
666 // Get the perigee position (the point of closest approach to the beamline)
667 double d0 = trackParticle->d0();
668 double z0 = trackParticle->z0();
669 double phi0 = trackParticle->phi0();
670
671 Amg::Vector3D trackOrigin(-d0 * std::sin(phi0), d0 * std::cos(phi0), z0);
672 ATH_MSG_DEBUG("Track perigee: d0=" << d0 << ", z0=" << z0 << ", phi0=" << phi0);
673 ATH_MSG_DEBUG("Track origin (perigee): (" << trackOrigin.x() << ", "
674 << trackOrigin.y() << ", " << trackOrigin.z() << ")");
675
676 // Calculate distance components
677 float dx = globalHitPos.x() - trackOrigin.x();
678 float dy = globalHitPos.y() - trackOrigin.y();
679 float dz = globalHitPos.z() - trackOrigin.z();
680
681 // Calculate distance and time of flight
682 float distance = std::sqrt(dx*dx + dy*dy + dz*dz);
683 float tof = distance / Gaudi::Units::c_light;
684
685 // Apply TOF correction
686 float correctedTime = measuredTime - tof;
687
688 ATH_MSG_DEBUG("Track origin: (" << trackOrigin.x() << ", "
689 << trackOrigin.y() << ", " << trackOrigin.z() << ")");
690 ATH_MSG_DEBUG("Hit position: (" << globalHitPos.x() << ", "
691 << globalHitPos.y() << ", " << globalHitPos.z() << ")");
692 ATH_MSG_DEBUG("Distance = " << distance << " mm, TOF = " << tof
693 << " ns, Corrected time = " << correctedTime);
694
695 return {correctedTime, measuredTimeErr};
696}
697
699 const EventContext& ctx,
701 const xAOD::HGTDClusterContainer* hgtdClusters) const {
702
703 if (state.hasUncalibratedSourceLink()) {
704 auto uncalib_cluster = detail::xAODUncalibMeasCalibrator::unpack(state.getUncalibratedSourceLink());
705 assert( uncalib_cluster != nullptr);
706 xAOD::UncalibMeasType clusterType = uncalib_cluster->type();
707
708 if (clusterType == xAOD::UncalibMeasType::HGTDClusterType) {
709 ATH_MSG_DEBUG("Found HGTD cluster in source link");
710 auto hgtdCluster = static_cast<const xAOD::HGTDCluster *>(uncalib_cluster);
711 return hgtdCluster;
712 }
713 else {
714 ATH_MSG_DEBUG("Source link contains non-HGTD measurement type: " << static_cast<int>(clusterType));
715 }
716
717 // If we have a reference surface, try to match by position
718 if (state.hasReferenceSurface()) {
719 const auto& surface = state.referenceSurface();
720 Acts::GeometryIdentifier geoID = surface.geometryId();
721
722 const auto *acts_detector_element = getActsDetectorElement(surface);
723
724 // Check if this is an HGTD surface
725 if (acts_detector_element->detectorType() == DetectorType::Hgtd) {
726 ATH_MSG_DEBUG("This is an HGTD surface with ID: " << geoID.volume() << ":" << geoID.layer());
727
728 // Modern approach uses surface accessor instead of detector element map
729
730 // Get global position of the state surface
731 const Acts::GeometryContext& geoContext = m_ctxProvider.getGeometryContext(ctx);
732 Acts::Vector3 statePos = surface.center(geoContext);
733
734 // Find the closest cluster to this state position
735 const xAOD::HGTDCluster* closestCluster = nullptr;
736 double minDistance = 100.0; // Use a reasonable threshold (in mm)
737
738 for (const xAOD::HGTDCluster* cluster : *hgtdClusters) {
739 // Get the cluster's surface
740 const Acts::Surface* clusterSurface = m_surfAcc.get(cluster);
741
742 if (!clusterSurface) continue;
743
744 // Check if it's on the same surface by comparing geometry IDs
745 Acts::GeometryIdentifier clusterGeoID = clusterSurface->geometryId();
746 if (clusterGeoID.volume() == geoID.volume() && clusterGeoID.layer() == geoID.layer()) {
747 // Get cluster position
748 Acts::Vector3 clusterPos = clusterSurface->center(geoContext);
749
750 // Calculate 2D distance (x,y only, since z is fixed for a layer)
751 double dx = clusterPos.x() - statePos.x();
752 double dy = clusterPos.y() - statePos.y();
753 double distance = std::sqrt(dx*dx + dy*dy);
754
755 // Update closest if this is better
756 if (distance < minDistance) {
757 minDistance = distance;
758 closestCluster = cluster;
759 ATH_MSG_DEBUG("Found possible cluster match at distance " << distance << " mm");
760 }
761 }
762 }
763
764 if (closestCluster) {
765 ATH_MSG_DEBUG("Found closest cluster at distance " << minDistance << " mm");
766 return closestCluster;
767 } else {
768 ATH_MSG_DEBUG("No matching cluster found on this surface");
769 }
770 }
771 }
772 }
773 else {
774 ATH_MSG_DEBUG("State doesn't have uncalibrated source link");
775 }
776 return nullptr;
777}
778
781 const Acts::Surface& refSurface,
782 const Acts::TrackExtrapolationStrategy& extrapolationStrategy,
783 detail::RecoTrackContainer &actsTracksContainer,
784 const detail::MeasurementIndex& measurementIndex,
785 const detail::RecoTrackContainer& tracksContainerTemp) const{
786
787 std::array<unsigned int, 4> expectedLayerPattern{};
788
789 // if the the perigeeSurface was not hit (in particular the case for the inside-out pass,
790 // the track has no reference surface and the extrapolation to the perigee has not been done
791 // yet.
792 if (not track.hasReferenceSurface()) {
793 auto extrapolationResult =
794 extrapolateTrackToReferenceSurface(detContext, track,
795 refSurface,
796 trackFinder().extrapolator,
797 extrapolationStrategy,
798 expectedLayerPattern);
799 if (not extrapolationResult.ok()) {
800 ATH_MSG_WARNING("Extrapolation for "
801 << track.index()
802 << " failed with error " << extrapolationResult.error()
803 << " dropping track candidate.");
804 return false;
805 }
806 }
807
808 // Before trimming, inspect encountered surfaces from all track states
809 for(const auto ts : track.trackStatesReversed()) {
810 const auto* detElem = getActsDetectorElement(ts.referenceSurface());
811 if(detElem != nullptr) {
812 detail::addToExpectedLayerPattern(expectedLayerPattern, *detElem);
813 }
814 }
815 // Trim tracks
816 // - trimHoles
817 // - trimOutliers
818 // - trimMaterial
819 // - trimOtherNoneMeasurement
820 Acts::trimTrack(track, true, true, true, true);
821 Acts::calculateTrackQuantities(track);
822 if (m_addCounts) {
823 initCounts(track);
824 for (const auto trackState : track.trackStatesReversed()) {
825 updateCounts(track, trackState.typeFlags(), measurementType(trackState));
826 }
827 if (m_checkCounts) {
828 checkCounts(track);
829 }
830 }
831
832 if ( not trackFinder().trackSelector.isValidTrack(track)) {
833 ATH_MSG_WARNING("Track " << track.index() << " failed track selection");
834 if ( m_trackStatePrinter.isSet() ) {
835 m_trackStatePrinter->printTrack(detContext.geometry, tracksContainerTemp, track, measurementIndex, true);
836 }
837 return false;
838 }
839
840 auto actsDestProxy = actsTracksContainer.makeTrack();
841 actsDestProxy.copyFrom(track); // make sure we copy track states!
842 detail::ExpectedLayerPatternHelper::set(actsDestProxy, expectedLayerPattern);
843
844 ATH_MSG_DEBUG("Added Track " << track.index() << " into container");
845 return true;
846}
847
849 const DetectorContextHolder& detContext,
851 const Acts::Surface &referenceSurface,
852 const detail::Extrapolator &propagator,
853 Acts::TrackExtrapolationStrategy strategy,
854 ExpectedLayerPattern& expectedLayerPattern) const {
855
856 Acts::PropagatorOptions<detail::Stepper::Options, detail::Navigator::Options,
857 Acts::ActorList<Acts::MaterialInteractor, Collector>>
858 options(detContext.geometry, detContext.magField);
859
860 auto findResult = findTrackStateForExtrapolation(
861 options.geoContext, track, referenceSurface, strategy, logger());
862
863 if (!findResult.ok()) {
864 ATH_MSG_WARNING("Failed to find track state for extrapolation");
865 return findResult.error();
866 }
867
868 auto &[trackState, distance] = *findResult;
869
870 options.direction = Acts::Direction::fromScalarZeroAsPositive(distance);
871
872 Acts::BoundTrackParameters parameters = track.createParametersFromState(trackState);
873 ATH_MSG_VERBOSE("Extrapolating track to reference surface at distance "
874 << distance << " with direction " << options.direction
875 << " with starting parameters " << parameters);
876
877 auto state = propagator.makeState<decltype(options), Acts::ForcedSurfaceReached>(options);
879 collectorResult = &expectedLayerPattern;
880
881 auto initRes = propagator.initialize<decltype(state), Acts::ForcedSurfaceReached>(
882 state, parameters, &referenceSurface);
883 if(!initRes.ok()) {
884 ATH_MSG_WARNING("Failed to initialize propagation state: " << initRes.error().message());
885 return initRes.error();
886 }
887
888 auto propagateOnlyResult =
889 propagator.propagate(state);
890
891 if (!propagateOnlyResult.ok()) {
892 ATH_MSG_WARNING("Failed to extrapolate track: " << propagateOnlyResult.error().message());
893 return propagateOnlyResult.error();
894 }
895
896 auto propagateResult = propagator.makeResult(
897 std::move(state), propagateOnlyResult, options, true, &referenceSurface);
898
899 if (!propagateResult.ok()) {
900 ATH_MSG_WARNING("Failed to extrapolate track: " << propagateResult.error().message());
901 return propagateResult.error();
902 }
903
904 track.setReferenceSurface(referenceSurface.getSharedPtr());
905 track.parameters() = propagateResult->endParameters.value().parameters();
906 track.covariance() = propagateResult->endParameters.value().covariance().value();
907
908 return Acts::Result<void>::success();
909}
910
911} // End namespace ActsTrk
const ActsDetectorElement * getActsDetectorElement(const Acts::Surface &surf)
Attempts to retrieve the ActsDetectorElement associated to the passed ActsSurface.
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_VERBOSE(x,...)
Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration.
Header file to be included by clients of the Monitored infrastructure.
Handle class for reading a decoration on an object.
Handle class for adding a decoration to an object.
size_t size() const
Number of registered mappings.
SG::ReadHandleKey< xAOD::TrackParticleContainer > m_trackParticleContainerName
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_layerClusterRawTimeKey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_numHGTDHitsKey
virtual StatusCode execute(const EventContext &ctx) const override
const xAOD::HGTDCluster * getHGTDClusterFromState(const EventContext &ctx, const ActsTrk::detail::RecoConstTrackStateContainerProxy &state, const xAOD::HGTDClusterContainer *hgtdClusters) const
Get xAOD::HGTDCluster from track state, so it is possible to retrieve its raw time and position for e...
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_layerExtensionChi2Key
Gaudi::Property< float > m_minEtaAcceptance
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_layerHasExtensionKey
bool addTrack(const DetectorContextHolder &detContext, detail::RecoTrackContainerProxy &track, const Acts::Surface &refSurface, const Acts::TrackExtrapolationStrategy &extrapolationStrategy, detail::RecoTrackContainer &actsTracksContainer, const detail::MeasurementIndex &measurementIndex, const detail::RecoTrackContainer &tracksContainerTemp) const
add extension to track container if it passes the track selector criteria
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_extrapYKey
std::pair< float, float > correctTOF(const xAOD::TrackParticle *trackParticle, const xAOD::HGTDCluster *cluster, float measuredTime, float measuredTimeErr, const Acts::TrackingGeometry *trackingGeometry, const Acts::GeometryContext &geoContext) const
subtracts the time of flight (TOF) from a measured hit time.
const HGTD_ID * m_id_helper
Handle to the ID helper.
SG::ReadDecorHandleKey< xAOD::TrackParticleContainer > m_actsTrackLinkKey
virtual StatusCode initialize() override
Gaudi::Property< float > m_maxEtaAcceptance
SG::ReadHandleKeyArray< xAOD::UncalibratedMeasurementContainer > m_uncalibratedMeasurementContainerKeys
bool findExtension(const EventContext &ctx, const DetectorContextHolder &detContext, const detail::TrackFindingMeasurements &measurements, const detail::MeasurementIndex &measurementIndex, const Acts::BoundTrackParameters &lastMeasurementStateParameters, detail::RecoTrackContainer &tracksContainerTemp, detail::RecoTrackContainer &actsTracksContainer, EventStats &event_stat, const Acts::Surface &refSurface, int &extension_index) const
invoke track finding procedure to extend ITk tracks to HGTD layers using CKF.
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_layerClusterTimeKey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_extrapXKey
Acts::Result< void > extrapolateTrackToReferenceSurface(const DetectorContextHolder &detContext, detail::RecoTrackContainerProxy &track, const Acts::Surface &referenceSurface, const detail::Extrapolator &propagator, Acts::TrackExtrapolationStrategy strategy, ExpectedLayerPattern &expectedLayerPattern) const
it can happen that the last hit of an extension doesn't have a surface associated with it,...
ActsTrk::detail::xAODUncalibMeasSurfAcc m_surfAcc
SG::ReadHandleKey< xAOD::HGTDClusterContainer > m_HGTDClusterContainerName
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_hgtdTrackLinkKey
Gaudi::Property< float > m_memorySafetyMargin
std::array< unsigned int, 4 > ExpectedLayerPattern
TrackExtensionData processTrackExtension(const EventContext &ctx, const xAOD::TrackParticle *trackParticle, const detail::RecoTrackContainer::TrackProxy &trackProxy, const xAOD::HGTDClusterContainer *hgtdClusters) const
Create and fills the TrackExtensionData with HGTD hits at the extension.
static xAOD::UncalibMeasType measurementType(const detail::RecoTrackContainer::TrackStateProxy &trackState)
ToolHandle< ActsTrk::TrackStatePrinterTool > m_trackStatePrinter
SG::WriteHandleKey< ActsTrk::TrackContainer > m_trackContainerKey
detail::RecoTrackContainer::TrackProxy TrkProxy
ToolHandle< GenericMonitoringTool > m_monTool
Gaudi::Property< double > m_absEtaMax
virtual StatusCode initialize() override
StatusCode getContainersFromKeys(const EventContext &ctx, HandleArrayKeyType &handleKeyArray, std::vector< const ContainerType * > &outputContainers, std::size_t &sum) const
Take the array of handle keys and for each key retrieve containers, then append them to the output ve...
void checkCounts(const detail::RecoTrackContainer::TrackProxy &track) const
static void addCountsAndProperties(detail::RecoTrackContainer &tracksContainer, bool add_counts)
const Acts::Logger & logger() const
Private access to the logger.
Gaudi::Property< double > m_absEtaMin
BranchStopperResult stopBranch(const detail::RecoTrackContainer::TrackProxy &track, const detail::RecoTrackContainer::TrackStateProxy &trackState, const Acts::TrackSelector::EtaBinnedConfig &trackSelectorCfg, const Acts::GeometryContext &tgContext, const detail::MeasurementIndex &measurementIndex, const std::size_t typeIndex, EventStats::value_type &event_stat_category_i) const
Branch stopper.
static void initCounts(const detail::RecoTrackContainer::TrackProxy &track)
Gaudi::Property< bool > m_checkCounts
ServiceHandle< ActsTrk::ITrackingGeometrySvc > m_trackingGeometrySvc
TrackFindingDefaultOptions getDefaultOptions(const EventContext &ctx, const DetectorContextHolder &detContext, const detail::TrackFindingMeasurements &measurements, const Acts::PerigeeSurface *pSurface) const
Get CKF options for first and second pass + pointer to MeasurementSelector.
ContextUtility m_ctxProvider
Utility to fetch the geometry, magnetic field and calibration context in the event.
std::vector< std::array< unsigned int, kNStat > > EventStats
static void updateCounts(const detail::RecoTrackContainer::TrackProxy &track, Acts::ConstTrackStateTypeMap typeFlags, xAOD::UncalibMeasType detType)
Gaudi::Property< bool > m_addCounts
Acts::CombinatorialKalmanFilterBranchStopperResult BranchStopperResult
void addMeasurements(const xAOD::UncalibratedMeasurementContainer &clusterContainer)
const std::vector< std::size_t > & measurementOffsets() const
void addMeasurements(std::size_t typeIndex, const xAOD::UncalibratedMeasurementContainer &clusterContainer, const DetectorElementToActsGeometryIdMap &detectorElementToGeoid, const MeasurementIndex *measurementIndex=nullptr)
static const xAOD::UncalibratedMeasurement * unpack(const Acts::SourceLink &sl)
Helper method to unpack an Acts source link to an uncalibrated measurement.
Helper class to access the Acts::surface associated with an Uncalibrated xAOD measurement.
const ServiceHandle< StoreGateSvc > & detStore() const
const T * at(size_type n) const
Access an element, as an rvalue.
size_type size() const noexcept
Returns the number of elements in the collection.
Group of local monitoring quantities and retain correlation when filling histograms
Declare a monitored scalar variable.
A monitored timer.
Handle class for adding a decoration to an object.
const_pointer_type cptr() const
Dereference the pointer.
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
float time() const
Return the measured time in ns.
float z0() const
Returns the parameter.
float d0() const
Returns the parameter.
float phi0() const
Returns the parameter, which has range to .
DetectorIdentType identifier() const
Returns the full Identifier of the measurement.
ConstVectorMap< N > localPosition() const
Returns the local position of the measurement.
int ts
Definition globals.cxx:24
Acts::TrackContainer< Acts::VectorTrackContainer, Acts::VectorMultiTrajectory > RecoTrackContainer
RecoTrackStateContainer::ConstTrackStateProxy RecoConstTrackStateContainerProxy
void addToExpectedLayerPattern(std::array< unsigned int, 4 > &pattern, const ActsDetectorElement &detElement)
The AlignStoreProviderAlg loads the rigid alignment corrections and pipes them through the readout ge...
std::optional< ActsTrk::TrackContainer::ConstTrackStateProxy > lastMeasurementState(const xAOD::TrackParticle &trkPart, const bool skipOutlier=true)
Returns the track state proxy corresponding to the last measurement on track.
constexpr double timeToAthena(T actsT)
Converts a time unit from Acts to Athena units.
std::optional< ActsTrk::TrackContainer::ConstTrackProxy > getActsTrack(const xAOD::TrackParticle &trkPart)
Return the proxy to the Acts track from which the track particle was made frome.
Definition Decoration.cxx:9
Eigen::Matrix< double, 3, 1 > Vector3D
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
SG::ReadCondHandle< T > makeHandle(const SG::ReadCondHandleKey< T > &key, const EventContext &ctx=Gaudi::Hive::currentContext())
HGTDClusterContainer_v1 HGTDClusterContainer
Define the version of the HGTD cluster container.
TrackParticle_v1 TrackParticle
Reference the current persistent version:
UncalibMeasType
Define the type of the uncalibrated measurement.
TrackParticleContainer_v1 TrackParticleContainer
Definition of the current "TrackParticle container version".
HGTDCluster_v1 HGTDCluster
Define the version of the pixel cluster class.
Definition HGTDCluster.h:13
Data structure to hold HGTD track extension results Contains information about hits,...
std::vector< float > chi2Vec
Chi2 contribution per HGTD layer.
std::vector< float > timeVec
TOF-corrected time per HGTD layer.
std::vector< float > rawTimeVec
Raw measured time per HGTD layer.
int numHGTDHits
Total number of HGTD hits on extended track.
std::vector< char > hasClusterVec
Whether extension has cluster in each HGTD layer.
static void add(track_container_t &trackContainer)
static void set(track_proxy_t &track, std::array< unsigned int, 4 > values)