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 return StatusCode::SUCCESS;
121}
122
123// ---------------- Execute ----------------
124
125StatusCode HGTDTrackExtensionAlg::execute(const EventContext& ctx) const
126{
127 ATH_MSG_DEBUG("Executing " << name() << "...");
128
129 auto timer = Monitored::Timer<std::chrono::milliseconds>("TIME_execute");
130 auto mon_nTracks = Monitored::Scalar<int>("nTracks");
131 auto mon = Monitored::Group(m_monTool, timer, mon_nTracks);
132
133 // ================================================== //
134 // ========= RETRIEVE TRACK PARTICLES =============== //
135 // ================================================== //
136
137 const xAOD::TrackParticleContainer* trackParticles{nullptr};
138 ATH_CHECK(SG::get(trackParticles, m_trackParticleContainerName, ctx));
139
140 ATH_MSG_DEBUG("Size of trackParticles collection " << trackParticles->size());
141
142 // Create WriteDecorHandles for all decorations
151
152 // ================================================== //
153 // ============ RETRIEVE MEASUREMENTS =============== //
154 // ================================================== //
155 const xAOD::HGTDClusterContainer* hgtdClusters{nullptr};
156 ATH_CHECK(SG::get(hgtdClusters, m_HGTDClusterContainerName, ctx));
157
158
159 std::vector<const xAOD::UncalibratedMeasurementContainer *> uncalibratedMeasurementContainers;
160 std::size_t total_measurements = 0;
161 ATH_CHECK(getContainersFromKeys(ctx, m_uncalibratedMeasurementContainerKeys, uncalibratedMeasurementContainers, total_measurements));
162
163
164 detail::MeasurementIndex measurementIndex(uncalibratedMeasurementContainers.size());
165 for (std::size_t icontainer = 0; icontainer < uncalibratedMeasurementContainers.size(); ++icontainer) {
166 measurementIndex.addMeasurements(*uncalibratedMeasurementContainers[icontainer]);
167 }
168
169 detail::TrackFindingMeasurements measurements(uncalibratedMeasurementContainers.size());
170 for (std::size_t icontainer = 0; icontainer < uncalibratedMeasurementContainers.size(); ++icontainer) {
171 ATH_MSG_DEBUG("Create " << uncalibratedMeasurementContainers[icontainer]->size() <<
172 " source links from measurements in " << m_uncalibratedMeasurementContainerKeys[icontainer].key());
173 measurements.addMeasurements(icontainer,
174 *uncalibratedMeasurementContainers[icontainer],
175 *m_trackingGeometrySvc->surfaceIdMap(),
176 &measurementIndex);
177 }
178
179 ATH_MSG_DEBUG("measurement index size = " << measurementIndex.size());
180
181
182 if (m_trackStatePrinter.isSet()) {
183 m_trackStatePrinter->printMeasurements(ctx, uncalibratedMeasurementContainers, measurements.measurementOffsets());
184 }
185
186 // ================================================== //
187 // ===================== COMPUTATION ================ //
188 // ================================================== //
189
190 EventStats event_stat;
191 event_stat.resize(m_stat.size());
192
193 DetectorContextHolder detContext {
194 .geometry = m_ctxProvider.getGeometryContext(ctx),
195 .magField = m_ctxProvider.getMagneticFieldContext(ctx),
196 // CalibrationContext converter not implemented yet.
197 .calib = m_ctxProvider.getCalibrationContext(ctx)
198 };
199
200 Acts::VectorTrackContainer actsTrackBackend;
201 Acts::VectorMultiTrajectory actsTrackStateBackend;
202
203 auto atomicMax=[](std::size_t new_val, std::atomic<std::size_t> &dest) -> void {
204 std::size_t is_value;
205 do {
206 is_value = dest;
207 if (is_value>=new_val) return;
208 } while (!dest.compare_exchange_weak(is_value, new_val));
209 };
210 atomicMax(actsTrackBackend.size(), m_nTrackReserve);
211 atomicMax(actsTrackStateBackend.size(), m_nTrackStateReserve);
212
213 detail::RecoTrackContainer actsTracksContainer(actsTrackBackend,
214 actsTrackStateBackend);
215
216
217 addCountsAndProperties(actsTracksContainer, m_addCounts.value());
218
219 detail::ExpectedLayerPatternHelper::add(actsTracksContainer);
220
221 int extension_index{0};
222 // Loop over each track particle and decorate it with various information
223 std::unordered_map<uint32_t, uint32_t> extensions;
224 for (const xAOD::TrackParticle* trackParticle : *trackParticles) {
225 // Default to empty track data
226 TrackExtensionData trackData;
227
228 std::optional<ActsTrk::TrackContainer::ConstTrackProxy> optional_track = getActsTrack(*trackParticle);
229 if (!optional_track.has_value()) {
230 ATH_MSG_ERROR("No valid ACTS track associated with TrackParticle " << trackParticle->index());
231 return StatusCode::FAILURE;
232 }
233
234 const ActsTrk::TrackContainer::ConstTrackProxy& track = optional_track.value();
235
236 // Retrieve quantities for ACTS track
237 float trackEta = Acts::VectorHelpers::eta(track.momentum());
238
239 // Check eta coverage first
240 if (std::abs(trackEta) < m_minEtaAcceptance or std::abs(trackEta) > m_maxEtaAcceptance) {
241 ATH_MSG_DEBUG("!!!!! ------ Track eta " << trackEta
242 << " outside eta range [" << m_minEtaAcceptance.value() << ", " << m_maxEtaAcceptance.value()
243 << "], skipping extension ------ !!!!!");
244
245 // set default values
246 trackData.hasClusterVec = {false, false, false, false};
247 trackData.numHGTDHits = 0;
248 layerHasExtensionHandle(*trackParticle) = trackData.hasClusterVec;
249 layerExtensionChi2Handle(*trackParticle) = trackData.chi2Vec;
250 layerClusterRawTimeHandle(*trackParticle) = trackData.rawTimeVec;
251 layerClusterTimeHandle(*trackParticle) = trackData.timeVec;
252 extrapXHandle(*trackParticle) = trackData.extrapX;
253 extrapYHandle(*trackParticle) = trackData.extrapY;
254 numHGTDHitsHandle(*trackParticle) = trackData.numHGTDHits;
255
256 continue;
257 }
258
259 float trackpT = track.transverseMomentum();
260 float trackPhi = track.phi();
261 float trackNmeasurements = track.nMeasurements();
262
263 ATH_MSG_DEBUG("TrackParticle " << trackParticle->index() <<
264 " has ACTS track with eta: " << trackEta <<
265 ", phi: " << trackPhi <<
266 ", pT: " << trackpT <<
267 " and nMeasurements: " << trackNmeasurements);
268
269 // Parameters at last measurement state
270 const auto lastMeasurementState = Acts::findLastMeasurementState(track);
271 if (not lastMeasurementState.ok()) {
272 ATH_MSG_ERROR("Problem finding last measurement state for acts track");
273 return StatusCode::FAILURE;
274 }
275 const Acts::BoundTrackParameters lastMeasurementStateParameters = track.createParametersFromState(*lastMeasurementState);
276
277 // Parameters at reference state of track - not necessarily a measurement state!!!
278 const Acts::Surface& refSurface = track.referenceSurface();
279 const Acts::BoundTrackParameters parametersAtRefSurface(refSurface.getSharedPtr(),
280 track.parameters(),
281 track.covariance(),
282 track.particleHypothesis());
283
284 ATH_MSG_DEBUG("Initial track parameters for extension - lastMeasurementStateParameters:");
285 ATH_MSG_DEBUG(" - eta: " << -1 * log(tan(lastMeasurementStateParameters.theta() * 0.5)));
286 ATH_MSG_DEBUG(" - phi: " << lastMeasurementStateParameters.phi());
287 ATH_MSG_DEBUG(" - pT: " << std::abs(1./lastMeasurementStateParameters.qOverP() * std::sin(lastMeasurementStateParameters.theta())));
288 ATH_MSG_DEBUG(" - theta: " << lastMeasurementStateParameters.theta());
289 ATH_MSG_DEBUG(" - qOverP: " << lastMeasurementStateParameters.qOverP());
290 ATH_MSG_DEBUG(" - covariance exists: " << (lastMeasurementStateParameters.covariance().has_value() ? "yes" : "no"));
291
292
293 // ActsTrk::MutableTrackContainer tracksContainerTemp;
294 Acts::VectorTrackContainer trackBackend;
295 Acts::VectorMultiTrajectory trackStateBackend;
296 detail::RecoTrackContainer tracksContainerTemp(trackBackend, trackStateBackend);
297
298 addCountsAndProperties(tracksContainerTemp, m_addCounts.value());
299
300 detail::ExpectedLayerPatternHelper::add(tracksContainerTemp);
301
302 // Now use the *last measurement parameters* parameters for the CKF
303 if(findExtension(ctx,
304 detContext,
305 measurements,
306 measurementIndex,
307 lastMeasurementStateParameters,
308 tracksContainerTemp,
309 actsTracksContainer,
310 event_stat,
311 refSurface,
312 extension_index))
313 {
314 const detail::RecoTrackContainer::TrackProxy& trackProxy = tracksContainerTemp.at(extension_index);
315 trackData = processTrackExtension(ctx, trackParticle, trackProxy, hgtdClusters);
316 extensions.insert(std::make_pair(trackParticle->index(), actsTracksContainer.size() - 1));
317
318 }
319 else{
320 trackData.hasClusterVec = {false, false, false, false};
321 trackData.numHGTDHits = 0;
322 }
323 // Apply decorations from the track data
324 layerHasExtensionHandle(*trackParticle) = trackData.hasClusterVec;
325 layerExtensionChi2Handle(*trackParticle) = trackData.chi2Vec;
326 layerClusterRawTimeHandle(*trackParticle) = trackData.rawTimeVec;
327 layerClusterTimeHandle(*trackParticle) = trackData.timeVec;
328 extrapXHandle(*trackParticle) = trackData.extrapX;
329 extrapYHandle(*trackParticle) = trackData.extrapY;
330 numHGTDHitsHandle(*trackParticle) = trackData.numHGTDHits;
331 } // loop on tracks
332
333 // ================================================== //
334 // ===================== OUTPUTS ==================== //
335 // ================================================== //
336
337 ATH_MSG_DEBUG(" \\__ Found " << actsTracksContainer.size() << " extensions");
338
339 // update the reserve space
340 if (actsTrackBackend.size() > m_nTrackReserve) {
341 m_nTrackReserve = static_cast<std::size_t>( std::ceil(m_memorySafetyMargin * actsTrackBackend.size()) );
342 }
343 if (actsTrackStateBackend.size() > m_nTrackStateReserve) {
344 m_nTrackStateReserve = static_cast<std::size_t>( std::ceil(m_memorySafetyMargin * actsTrackStateBackend.size()) );
345 }
346
347 // convert to const
348 Acts::ConstVectorTrackContainer constTrackBackend( std::move(actsTrackBackend) );
349 Acts::ConstVectorMultiTrajectory constTrackStateBackend( std::move(actsTrackStateBackend) );
350 std::unique_ptr< ActsTrk::TrackContainer> constTracksContainer = std::make_unique< ActsTrk::TrackContainer >( std::move(constTrackBackend),
351 std::move(constTrackStateBackend) );
352
354
355 ATH_MSG_DEBUG(" \\__ Tracks Container `" << m_trackContainerKey.key() << "` created ...");
356 ATH_CHECK(trackContainerHandle.record(std::move(constTracksContainer)));
357
358 const ActsTrk::TrackContainer *const_track_container_ptr = trackContainerHandle.cptr();
359
360 for ( const std::pair< const uint32_t,uint32_t> &ext : extensions) {
361
362 hgtdTrackLink(*trackParticles->at(ext.first))
363 = ElementLink<ActsTrk::TrackContainer>( *const_track_container_ptr,
364 ext.second );
365
366}
367
368 return StatusCode::SUCCESS;
369}
370
372 const EventContext &ctx,
373 const DetectorContextHolder& detContext,
374 const detail::TrackFindingMeasurements &measurements,
375 const detail::MeasurementIndex& measurementIndex,
376 const Acts::BoundTrackParameters initialParameters,
377 detail::RecoTrackContainer &tracksContainerTemp,
378 detail::RecoTrackContainer &actsTracksContainer,
379 EventStats &event_stat,
380 const Acts::Surface& refSurface,
381 int& extension_index) const{
382
383 //Setting pSurface to nullptr
384 auto [options, secondOptions, measurementSelector] = getDefaultOptions(ctx, detContext, measurements, nullptr);
385
386
387 std::size_t category_i = 0;
388 const auto &trackSelectorCfg = trackFinder().trackSelector.config();
389 auto stopBranchProxy = [&](const detail::RecoTrackContainer::TrackProxy &track,
390 const detail::RecoTrackContainer::TrackStateProxy &trackState) -> BranchStopperResult {
391 return stopBranch(track, trackState, trackSelectorCfg, detContext.geometry, measurementIndex, 0, event_stat[category_i]);
392 };
393 options.extensions.branchStopper.connect(stopBranchProxy);
394
395 Acts::PropagatorOptions<detail::Stepper::Options, detail::Navigator::Options,
396 Acts::ActorList<Acts::MaterialInteractor>>
397 extrapolationOptions(detContext.geometry, detContext.magField);
398
399 Acts::TrackExtrapolationStrategy extrapolationStrategy =
400 Acts::TrackExtrapolationStrategy::first;
401
402 // Get the Acts tracks, given the initial parameters from last hit of itk track
403 Acts::Result<std::vector<TrkProxy> > result =
404 trackFinder().ckf.findTracks(initialParameters, options, tracksContainerTemp);
405
406 // Track finding result
407 if (not result.ok()) {
408 ATH_MSG_WARNING("Track finding failed with error" << result.error());
409 return false;
410 }
411
412 ATH_MSG_DEBUG("Built " << tracksContainerTemp.size() << " extensions from it");
413 auto &foundTracks = result.value();
414
415 // loop on the tracks we have just found
416 int best_track_index = -1;
417 float best_track_chi2 = 1000;
418 TrkProxy &best_track_proxy = foundTracks.at(0);
419
420 for (TrkProxy &firstTrack : foundTracks) {
421 if((firstTrack.chi2() > 0) and (firstTrack.chi2() < best_track_chi2)){
422 best_track_index = firstTrack.index();
423 best_track_chi2 = firstTrack.chi2();
424 best_track_proxy = firstTrack;
425 }
426 }
427
428 if(best_track_index == -1) return false;
429
430 ATH_MSG_DEBUG("Best extension index " << best_track_proxy.index() <<
431 " nMeas " << best_track_proxy.nMeasurements() <<
432 " chi2 " << best_track_proxy.chi2());
433
434 if(addTrack(detContext,
435 best_track_proxy,
436 refSurface,
437 extrapolationStrategy,
438 actsTracksContainer,
439 measurementIndex,
440 tracksContainerTemp)){
441
442 extension_index = best_track_index;
443 return true;
444 }
445 else {
446 ATH_MSG_DEBUG("Track failed selection, not adding it");
447 return false;
448 }
449
450}
451
453 const EventContext& ctx,
454 const xAOD::TrackParticle* trackParticle,
455 const detail::RecoTrackContainer::TrackProxy& trackProxy,
456 const xAOD::HGTDClusterContainer* hgtdClusters) const {
457
459
460 // Modern approach uses surface accessor instead of detector element map
461
462 // Apply track smoothing before trying to access chi2 values
463 Acts::GeometryContext geoContext = m_ctxProvider.getGeometryContext(ctx);
464 const Acts::TrackingGeometry* acts_tracking_geometry = m_trackingGeometrySvc->trackingGeometry().get();
465
466
467 // Count measurements, holes, and HGTD hits specifically
468 std::size_t nMeasurements = 0;
469 std::size_t nHoles = 0;
470 std::size_t nOutliers = 0;
471 std::size_t nHGTDHits = 0;
472
473 std::vector<char> hasHitInLayer = {false, false, false, false};
474 std::vector<float> chi2PerLayer = {-1.0, -1.0, -1.0, -1.0};
475 std::vector<float> timePerLayer = {-1.0, -1.0, -1.0, -1.0};
476 std::vector<float> rawTimePerLayer = {-1.0, -1.0, -1.0, -1.0};
477
478 // Extrapolated position - get the position at the first HGTD surface encountered
479 float extrapX = 0.0;
480 float extrapY = 0.0;
481 float extrapZ = 0.0;
482 bool foundExtrapolation = false;
483
484 for (auto state : trackProxy.trackStatesReversed()) {
485 auto flags = state.typeFlags();
486 if (flags.isHole()) {
487 nHoles++;
488 } else if (flags.isOutlier()) {
489 nOutliers++;
490 } else if (flags.isMeasurement()) {
491 nMeasurements++;
492
493 // Check if this is an HGTD hit
494 const auto& surface = state.referenceSurface();
495 Acts::GeometryIdentifier geoID = surface.geometryId();
496 std::size_t layerIndex = getHGTDLayerIndex(geoID);
497
498 // Check if measurement is at a valid HGTD layer
499 if (layerIndex != 99) {
500
501 const auto& calibrated = state.template calibrated<3>(); //x,y,time
502 const auto& predicted = state.predicted(); //6D
503 const auto& calibCov = state.template calibratedCovariance<3>(); // Full 3D covariance
504
505 Eigen::Vector2d residual2d;
506 residual2d(0) = calibrated(0) - predicted(Acts::eBoundLoc0);
507 residual2d(1) = calibrated(1) - predicted(Acts::eBoundLoc1);
508
509 // Extract the top-left 2x2 from the 3x3 measurement covariance
510 AmgSymMatrix(2) cov_2d{calibCov.template block<2,2>(0,0)};
511
512 // Get the predicted covariance for residual calculation
513 const auto& predictedCov = state.predictedCovariance();
514 AmgSymMatrix(2) predicted_cov_2d{predictedCov.template block<2,2>(0,0)};
515
516 // Total residual covariance is measurement + predicted covariances
517 AmgSymMatrix(2) residual_cov = cov_2d + predicted_cov_2d;
518
519 double chi2=0.0;
520 double ndf = 2.0;
521 if (residual_cov.determinant() != 0) {
522 chi2 = residual2d.transpose() * residual_cov.inverse() * residual2d;
523 }
524 else{
525 chi2=-99.9;
526 }
527
528 if (layerIndex < 4) {
529 nHGTDHits++;
530 hasHitInLayer[layerIndex] = true;
531 chi2PerLayer[layerIndex] =chi2/ndf; //state.chi2();
532
533 // Get the measured time from the calibrated 3D measurement (local x, y, time)
534 float rawTime = 0.0f;
535 float calibratedTime = 0.0f;
536
537 if (state.hasCalibrated()) {
538 // Extract time from calibrated data
539 try {
540 const auto& calibrated = state.template calibrated<3>();
541 calibratedTime = ActsTrk::timeToAthena(calibrated(2));
542 ATH_MSG_DEBUG("Got time from calibrated<3>: " << calibratedTime);
543 } catch (const std::exception& e) {
544 ATH_MSG_WARNING("Failed to extract time from calibrated<3>: " << e.what());
545 }
546 }
547
548 // Extract raw time from HGTD clusters
549 const xAOD::HGTDCluster* cluster = getHGTDClusterFromState(ctx, state, hgtdClusters);
550
551 if (cluster) {
552 rawTime = cluster->time();
553 ATH_MSG_DEBUG("Got raw time from cluster: " << rawTime);
554 } else {
555 ATH_MSG_WARNING("Could not get cluster from state");
556 }
557
558 // Store the raw time
559 rawTimePerLayer[layerIndex] = calibratedTime;
560 if (cluster) {
561 auto [correctedTime, timeErr] = correctTOF(
562 trackParticle,
563 cluster,
564 calibratedTime,
565 0.0, // time error set to zero for now!
566 acts_tracking_geometry,
567 geoContext);
568 timePerLayer[layerIndex] = correctedTime;
569 ATH_MSG_DEBUG("Applied TOF correction: " << calibratedTime << " -> " << correctedTime);
570 } else {
571 // No cluster or time, use raw time
572 timePerLayer[layerIndex] = calibratedTime;
573 ATH_MSG_DEBUG("No cluster found for TOF correction, using calibrated time: " << calibratedTime);
574 }
575 }
576 else {
577 ATH_MSG_DEBUG("State does not have calibrated data");
578 }
579
580 // For extrapolation: use the first HGTD hit's surface position.
581 if (!foundExtrapolation) {
582 foundExtrapolation = true;
583 if (state.hasPredicted()) {
584 // Get the local predicted position
585 const auto& predicted = state.predicted();
586 Acts::Vector2 localPos(predicted[Acts::eBoundLoc0], predicted[Acts::eBoundLoc1]);
587
588 // Transform to global coordinates
589 Acts::Vector3 globalPos = surface.localToGlobal(
590 geoContext,
591 localPos,
592 Acts::Vector3::Zero());
593
594 extrapX = globalPos.x();
595 extrapY = globalPos.y();
596 extrapZ = globalPos.z();
597
598 ATH_MSG_DEBUG("Extrapolated position (predicted) at HGTD: x=" << extrapX
599 << ", y=" << extrapY << ", z=" << extrapZ);
600 } else {
601 // Fallback to surface center
602 Acts::Vector3 globalPos = surface.center(geoContext);
603 extrapX = globalPos.x();
604 extrapY = globalPos.y();
605 extrapZ = globalPos.z();
606
607 ATH_MSG_DEBUG("Extrapolated position (surface center) at HGTD: x=" << extrapX
608 << ", y=" << extrapY << ", z=" << extrapZ);
609 }
610 }
611 ATH_MSG_DEBUG("Found HGTD hit on layer " << layerIndex
612 << ", chi2=" << chi2PerLayer[layerIndex]
613 << ", time=" << timePerLayer[layerIndex]);
614 }
615 }
616 }
617
618 ATH_MSG_DEBUG("Extension Statistics: "
619 << " nMeasurements=" << nMeasurements
620 << " nHGTDHits=" << nHGTDHits
621 << " nHoles=" << nHoles
622 << " nOutliers=" << nOutliers
623 << " extrapolation found: " << (foundExtrapolation ? "yes" : "no"));
624
625
626 // Fill the data structure with results
627 data.hasClusterVec = std::move(hasHitInLayer);
628 data.chi2Vec = std::move(chi2PerLayer);
629 data.timeVec = std::move(timePerLayer);
630 data.rawTimeVec = std::move(rawTimePerLayer);
631 data.extrapX = extrapX;
632 data.extrapY = extrapY;
633 data.extrapZ = extrapZ;
634 data.numHGTDHits = nHGTDHits;
635
636 return data;
637}
638
639std::size_t HGTDTrackExtensionAlg::getHGTDLayerIndex(const Acts::GeometryIdentifier& geoID) const {
640 // Get volume and layer ID
641 std::uint32_t volume = geoID.volume();
642 std::uint32_t layer = geoID.layer();
643
644 // Check if we're in the positive or negative endcap
645 bool isPositiveEndcap = (volume == 25);
646 bool isNegativeEndcap = (volume == 2);
647
648 // Different mapping for different sides to maintain consistent physical ordering
649 if (isPositiveEndcap) {
650 // Mapping for positive endcap
651 switch(layer) {
652 case 2: return 0; // First HGTD layer (closest to IP)
653 case 4: return 1; // Second HGTD layer
654 case 6: return 2; // Third HGTD layer
655 case 8: return 3; // Fourth HGTD layer (farthest from IP)
656 default: return 99; // Invalid layer
657 }
658 } else if (isNegativeEndcap) {
659 // Mapping for negative endcap - potentially different ordering
660 switch(layer) {
661 case 2: return 3;
662 case 4: return 2;
663 case 6: return 1;
664 case 8: return 0;
665 default: return 99; // Invalid layer
666 }
667 } else {
668 return 99; // Not an HGTD volume
669 }
670}
671
672std::pair<float, float> HGTDTrackExtensionAlg::correctTOF(
673 const xAOD::TrackParticle* trackParticle,
674 const xAOD::HGTDCluster* cluster,
675 float measuredTime,
676 float measuredTimeErr,
677 const Acts::TrackingGeometry*,
678 const Acts::GeometryContext& geoContext) const {
679
680 ATH_MSG_DEBUG("Correcting input time: " << measuredTime);
681
682 if (!trackParticle || !cluster) {
683 ATH_MSG_WARNING("Null pointer provided to correctTOF");
684 return {measuredTime, measuredTimeErr}; // Return uncorrected values
685 }
686
687 // Get the surface for this HGTD cluster
688 const Acts::Surface* surface = nullptr;
689 try {
690 surface = m_surfAcc.get(cluster);
691 } catch (const std::exception& e) {
692 ATH_MSG_WARNING("Exception getting surface: " << e.what());
693 return {measuredTime, measuredTimeErr}; // Return uncorrected values
694 }
695
696 if (!surface) {
697 ATH_MSG_WARNING("Could not determine surface for HGTD cluster with id "
698 << cluster->identifier());
699 return {measuredTime, measuredTimeErr}; // Return uncorrected values
700 }
701
702 // Get the global position of the hit
703 Acts::Vector3 globalHitPos;
704 try {
705 // Try to get the cluster's local position
706 auto localPos = cluster->localPosition<3>();
707 // Transform to global coordinates
708 globalHitPos = surface->localToGlobal(
709 geoContext,
710 Acts::Vector2(localPos[0], localPos[1]),
711 Acts::Vector3::Zero());
712 } catch (const std::exception& e) {
713 ATH_MSG_WARNING("Failed to transform position: " << e.what());
714 // Fall back to surface center
715 globalHitPos = surface->center(geoContext);
716 }
717
718 // Get track origin (vertex position)
719 //option 1 - use beamspot
720 //Amg::Vector3D trackOrigin(trackParticle->vx(), trackParticle->vy(), trackParticle->vz());
721
722 //option 2 - use perigee - this is what is done in legacy code:
723 // https://gitlab.cern.ch/atlas/athena/-/blob/main/HighGranularityTimingDetector/HGTD_Reconstruction/HGTD_RecTools/src/StraightLineTOFcorrectionTool.cxx
724 // Get track origin from perigee parameters instead of vertex
725 // In ACTS, this is the d0 and z0 parameter with reference to the beamline
726
727 // Get the perigee position (the point of closest approach to the beamline)
728 double d0 = trackParticle->d0();
729 double z0 = trackParticle->z0();
730 double phi0 = trackParticle->phi0();
731
732 Amg::Vector3D trackOrigin(-d0 * std::sin(phi0), d0 * std::cos(phi0), z0);
733 ATH_MSG_DEBUG("Track perigee: d0=" << d0 << ", z0=" << z0 << ", phi0=" << phi0);
734 ATH_MSG_DEBUG("Track origin (perigee): (" << trackOrigin.x() << ", "
735 << trackOrigin.y() << ", " << trackOrigin.z() << ")");
736
737 // Calculate distance components
738 float dx = globalHitPos.x() - trackOrigin.x();
739 float dy = globalHitPos.y() - trackOrigin.y();
740 float dz = globalHitPos.z() - trackOrigin.z();
741
742 // Calculate distance and time of flight
743 float distance = std::sqrt(dx*dx + dy*dy + dz*dz);
744 float tof = distance / Gaudi::Units::c_light;
745
746 // Apply TOF correction
747 float correctedTime = measuredTime - tof;
748
749 ATH_MSG_DEBUG("Track origin: (" << trackOrigin.x() << ", "
750 << trackOrigin.y() << ", " << trackOrigin.z() << ")");
751 ATH_MSG_DEBUG("Hit position: (" << globalHitPos.x() << ", "
752 << globalHitPos.y() << ", " << globalHitPos.z() << ")");
753 ATH_MSG_DEBUG("Distance = " << distance << " mm, TOF = " << tof
754 << " ns, Corrected time = " << correctedTime);
755
756 return {correctedTime, measuredTimeErr};
757}
758
760 const EventContext& ctx,
762 const xAOD::HGTDClusterContainer* hgtdClusters) const {
763
764 if (state.hasUncalibratedSourceLink()) {
765 auto uncalib_cluster = detail::xAODUncalibMeasCalibrator::unpack(state.getUncalibratedSourceLink());
766 assert( uncalib_cluster != nullptr);
767 xAOD::UncalibMeasType clusterType = uncalib_cluster->type();
768
769 if (clusterType == xAOD::UncalibMeasType::HGTDClusterType) {
770 ATH_MSG_DEBUG("Found HGTD cluster in source link");
771 auto hgtdCluster = static_cast<const xAOD::HGTDCluster *>(uncalib_cluster);
772 return hgtdCluster;
773 }
774 else {
775 ATH_MSG_DEBUG("Source link contains non-HGTD measurement type: " << static_cast<int>(clusterType));
776 }
777
778 // If we have a reference surface, try to match by position
779 if (state.hasReferenceSurface()) {
780 const auto& surface = state.referenceSurface();
781 Acts::GeometryIdentifier geoID = surface.geometryId();
782
783 const auto *acts_detector_element = getActsDetectorElement(surface);
784
785 // Check if this is an HGTD surface
786 if (acts_detector_element->detectorType() == DetectorType::Hgtd) {
787 ATH_MSG_DEBUG("This is an HGTD surface with ID: " << geoID.volume() << ":" << geoID.layer());
788
789 // Modern approach uses surface accessor instead of detector element map
790
791 // Get global position of the state surface
792 const Acts::GeometryContext& geoContext = m_ctxProvider.getGeometryContext(ctx);
793 Acts::Vector3 statePos = surface.center(geoContext);
794
795 // Find the closest cluster to this state position
796 const xAOD::HGTDCluster* closestCluster = nullptr;
797 double minDistance = 100.0; // Use a reasonable threshold (in mm)
798
799 for (const xAOD::HGTDCluster* cluster : *hgtdClusters) {
800 // Get the cluster's surface
801 const Acts::Surface* clusterSurface = m_surfAcc.get(cluster);
802
803 if (!clusterSurface) continue;
804
805 // Check if it's on the same surface by comparing geometry IDs
806 Acts::GeometryIdentifier clusterGeoID = clusterSurface->geometryId();
807 if (clusterGeoID.volume() == geoID.volume() && clusterGeoID.layer() == geoID.layer()) {
808 // Get cluster position
809 Acts::Vector3 clusterPos = clusterSurface->center(geoContext);
810
811 // Calculate 2D distance (x,y only, since z is fixed for a layer)
812 double dx = clusterPos.x() - statePos.x();
813 double dy = clusterPos.y() - statePos.y();
814 double distance = std::sqrt(dx*dx + dy*dy);
815
816 // Update closest if this is better
817 if (distance < minDistance) {
818 minDistance = distance;
819 closestCluster = cluster;
820 ATH_MSG_DEBUG("Found possible cluster match at distance " << distance << " mm");
821 }
822 }
823 }
824
825 if (closestCluster) {
826 ATH_MSG_DEBUG("Found closest cluster at distance " << minDistance << " mm");
827 return closestCluster;
828 } else {
829 ATH_MSG_DEBUG("No matching cluster found on this surface");
830 }
831 }
832 }
833 }
834 else {
835 ATH_MSG_DEBUG("State doesn't have uncalibrated source link");
836 }
837 return nullptr;
838}
839
842 const Acts::Surface& refSurface,
843 const Acts::TrackExtrapolationStrategy& extrapolationStrategy,
844 detail::RecoTrackContainer &actsTracksContainer,
845 const detail::MeasurementIndex& measurementIndex,
846 const detail::RecoTrackContainer& tracksContainerTemp) const{
847
848 std::array<unsigned int, 4> expectedLayerPattern{};
849
850 // if the the perigeeSurface was not hit (in particular the case for the inside-out pass,
851 // the track has no reference surface and the extrapolation to the perigee has not been done
852 // yet.
853 if (not track.hasReferenceSurface()) {
854 auto extrapolationResult =
855 extrapolateTrackToReferenceSurface(detContext, track,
856 refSurface,
857 trackFinder().extrapolator,
858 extrapolationStrategy,
859 expectedLayerPattern);
860 if (not extrapolationResult.ok()) {
861 ATH_MSG_WARNING("Extrapolation for "
862 << track.index()
863 << " failed with error " << extrapolationResult.error()
864 << " dropping track candidate.");
865 return false;
866 }
867 }
868
869 // Before trimming, inspect encountered surfaces from all track states
870 for(const auto ts : track.trackStatesReversed()) {
871 const auto* detElem = getActsDetectorElement(ts.referenceSurface());
872 if(detElem != nullptr) {
873 detail::addToExpectedLayerPattern(expectedLayerPattern, *detElem);
874 }
875 }
876 // Trim tracks
877 // - trimHoles
878 // - trimOutliers
879 // - trimMaterial
880 // - trimOtherNoneMeasurement
881 Acts::trimTrack(track, true, true, true, true);
882 Acts::calculateTrackQuantities(track);
883 if (m_addCounts) {
884 initCounts(track);
885 for (const auto trackState : track.trackStatesReversed()) {
886 updateCounts(track, trackState.typeFlags(), measurementType(trackState));
887 }
888 if (m_checkCounts) {
889 checkCounts(track);
890 }
891 }
892
893 if ( not trackFinder().trackSelector.isValidTrack(track)) {
894 ATH_MSG_WARNING("Track " << track.index() << " failed track selection");
895 if ( m_trackStatePrinter.isSet() ) {
896 m_trackStatePrinter->printTrack(detContext.geometry, tracksContainerTemp, track, measurementIndex, true);
897 }
898 return false;
899 }
900
901 auto actsDestProxy = actsTracksContainer.makeTrack();
902 actsDestProxy.copyFrom(track); // make sure we copy track states!
903 detail::ExpectedLayerPatternHelper::set(actsDestProxy, expectedLayerPattern);
904
905 ATH_MSG_DEBUG("Added Track " << track.index() << " into container");
906 return true;
907}
908
910 const DetectorContextHolder& detContext,
912 const Acts::Surface &referenceSurface,
913 const detail::Extrapolator &propagator,
914 Acts::TrackExtrapolationStrategy strategy,
915 ExpectedLayerPattern& expectedLayerPattern) const {
916
917 Acts::PropagatorOptions<detail::Stepper::Options, detail::Navigator::Options,
918 Acts::ActorList<Acts::MaterialInteractor, Collector>>
919 options(detContext.geometry, detContext.magField);
920
921 auto findResult = findTrackStateForExtrapolation(
922 options.geoContext, track, referenceSurface, strategy, logger());
923
924 if (!findResult.ok()) {
925 ATH_MSG_WARNING("Failed to find track state for extrapolation");
926 return findResult.error();
927 }
928
929 auto &[trackState, distance] = *findResult;
930
931 options.direction = Acts::Direction::fromScalarZeroAsPositive(distance);
932
933 Acts::BoundTrackParameters parameters = track.createParametersFromState(trackState);
934 ATH_MSG_VERBOSE("Extrapolating track to reference surface at distance "
935 << distance << " with direction " << options.direction
936 << " with starting parameters " << parameters);
937
938 auto state = propagator.makeState<decltype(options), Acts::ForcedSurfaceReached>(referenceSurface, options);
940 collectorResult = &expectedLayerPattern;
941
942 auto initRes = propagator.initialize(state, parameters);
943 if(!initRes.ok()) {
944 ATH_MSG_WARNING("Failed to initialize propagation state: " << initRes.error().message());
945 return initRes.error();
946 }
947
948 auto propagateOnlyResult =
949 propagator.propagate(state);
950
951 if (!propagateOnlyResult.ok()) {
952 ATH_MSG_WARNING("Failed to extrapolate track: " << propagateOnlyResult.error().message());
953 return propagateOnlyResult.error();
954 }
955
956 auto propagateResult = propagator.makeResult(
957 std::move(state), propagateOnlyResult, options, true, &referenceSurface);
958
959 if (!propagateResult.ok()) {
960 ATH_MSG_WARNING("Failed to extrapolate track: " << propagateResult.error().message());
961 return propagateResult.error();
962 }
963
964 track.setReferenceSurface(referenceSurface.getSharedPtr());
965 track.parameters() = propagateResult->endParameters.value().parameters();
966 track.covariance() = propagateResult->endParameters.value().covariance().value();
967
968 return Acts::Result<void>::success();
969}
970
971} // 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_ERROR(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
#define AmgSymMatrix(dim)
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.
std::size_t getHGTDLayerIndex(const Acts::GeometryIdentifier &geoID) const
returns the index of HGTD layer where surfaces lies.
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 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.
double chi2(TH1 *h0, TH1 *h1)
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)