ATLAS Offline Software
Loading...
Searching...
No Matches
CaloExtensionAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3 */
4#include "CaloExtensionAlg.h"
5
10
11#include "ActsInterop/Logger.h"
12
13#include "Acts/Definitions/Units.hpp"
14#include "Acts/Definitions/Tolerance.hpp"
15
16#include "Acts/Propagator/ActorList.hpp"
17#include "Acts/Propagator/StandardAborters.hpp"
18#include "Acts/Propagator/SurfaceCollector.hpp"
19#include "Acts/Propagator/MaterialInteractor.hpp"
20
21#include "Acts/Utilities/AngleHelpers.hpp"
22
23#include "Acts/Utilities/VectorHelpers.hpp"
24#include "Acts/Utilities/Result.hpp"
25#include "Acts/Utilities/Helpers.hpp"
26#include "Acts/Utilities/Logger.hpp"
27
28
32
33using namespace Acts::VectorHelpers;
34using namespace Acts::UnitLiterals;
35using namespace Acts::AngleHelpers;
36
37namespace {
38 inline float clusterEta(const xAOD::CaloCluster& cluster) {
39 return xAOD::EgammaHelpers::isFCAL(&cluster) ?
40 cluster.eta() : cluster.etaBE(2);
41
42 }
43 inline std::array<float, 2> pack(const float eta, const float phi) {
44 return std::array{eta, phi};
45 }
46}
47
48namespace ActsTrk{
50 ATH_CHECK(m_clusterSelector.retrieve(EnableTool{!m_clusterSelector.empty()}));
51 ATH_CHECK(m_trackSelector.retrieve(EnableTool{!m_trackSelector.empty()}));
54
55 ATH_CHECK(m_clusterContainerKey.initialize());
57 ATH_CHECK(m_ctxProvider.initialize());
58 ATH_CHECK(m_extensionDecorKey.initialize());
59 ATH_CHECK(m_caloDetDescrMgrKey.initialize(m_clusterSelector.isEnabled()));
60 ATH_CHECK(m_caloExtensionKey.initialize());
61
62 // Here we extract the geometry identifiers of the 4 calo volumes in the ACTS geometry
63 // It seems more robust to do the matching with the volume name, since the geometry ID
64 // can change if details of the geometry change.
65 std::vector<std::pair<std::string, CaloSample>> volIndexNames{};
66 for (std::size_t id = 0 ; id < Acts::toUnderlying(CaloSample::Unknown); ++id){
67 const auto smpId = static_cast<CaloSample>(id);
68 volIndexNames.emplace_back(std::make_pair(CaloSampling::getSamplingName(smpId), smpId));
69 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - "<<volIndexNames.back().first<<" -> "
70 <<volIndexNames.back().second);
71 }
73 const Acts::TrackingVolume* caloExit = m_trackingGeometrySvc->getEnvelope(SystemEnvelope::CaloExit);
74
75 caloExit->visitVolumes([&](const Acts::TrackingVolume *vol) {
76 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Check volume: "<<vol->volumeName()<<".");
77 auto smpItr = std::ranges::find_if(volIndexNames, [&](const auto& sampleID) {
78 return vol->volumeName().starts_with(sampleID.first);
79 });
80 if (smpItr != volIndexNames.end()) {
81 m_geoLayerIds[vol->geometryId()] = smpItr->second;
82 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Assign geometryID "<<vol->geometryId()
83 <<"volume: "<<vol->volumeName()<<", to "<<smpItr->second<<".");
84 }
85 });
86
87
88 return StatusCode::SUCCESS;
89 }
92 const CaloDetDescrManager* detMgr) const{
93 const std::size_t nEtaBins = std::ceil((2. * m_maxClustEta) / m_broadDeltaEta);
94 const std::size_t nPhiBins = std::ceil((2.*std::numbers::pi) / m_broadDeltaPhi);
95 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Create grid to sort the clusters with "<<nEtaBins
96 <<" bins in eta and "<<nPhiBins<<" bins in phi.");
98 PhiAxis_t{-std::numbers::pi, std::numbers::pi, nPhiBins}};
99
100 std::size_t selected{0ul};
101 for (const xAOD::CaloCluster* cluster : clusters) {
102 if(cluster->et() < m_minClustEt ||
103 std::abs(clusterEta(*cluster)) > m_maxClustEta ||
104 (m_clusterSelector.isEnabled() && !m_clusterSelector->passSelection(cluster, *detMgr))) {
105 continue;
106 }
107 ClusterVec_t& bin = grid.atPosition(pack(clusterEta(*cluster), cluster->phi()));
108 bin.push_back(cluster);
109 ++selected;
110 }
111 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Selected "<<selected
112 <<" out of "<<clusters.size()<<" calo clusters.");
113 return grid;
114 }
115
116 std::unique_ptr<CaloExtension> CaloExtensionAlg::propagateToCaloExit(const EventContext& ctx,
117 const xAOD::TrackParticle* track) const{
118
119 const Acts::TrackingVolume* caloExit = m_trackingGeometrySvc->getEnvelope(SystemEnvelope::CaloExit);
120
121 auto extension = std::make_unique<CaloExtension>(track);
123 auto lastTrackPars = extension->lastParameters();
124 using SurfaceRecordOptions = IExtrapolationTool::SurfaceRecordOptions;
125 SurfaceRecordOptions propOpts{caloExit, IExtrapolationTool::VolumeAbort::atExit};
126 propOpts.recordMaterial = true;
127 propOpts.recordPassive = true;
128 propOpts.recordSensitive = true;
129
130 auto surfaceRecord = m_extrapolationTool->propagateAndRecord(ctx, *lastTrackPars, propOpts);
131 if (!surfaceRecord.ok()) {
132 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Propagation through calorimeter did not succeed");
133 return nullptr;
134 }
136 for (Acts::BoundTrackParameters& record : *surfaceRecord) {
137 if (&record.referenceSurface() == &(lastTrackPars->referenceSurface())) {
138 continue;
139 }
140 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Append calorimeter parameters: "
141 <<record<<",\n surface: "<<record.referenceSurface().bounds()<<".");
142 extension->appendParameters(std::move(record));
143 }
144 if (extension->empty()){
145 extension.reset();
146 }
147 return extension;
148 }
149 StatusCode CaloExtensionAlg::execute(const EventContext& ctx) const {
150
151 const xAOD::TrackParticleContainer* idTracks{nullptr};
152 const xAOD::CaloClusterContainer* caloClusters{nullptr};
153 const CaloDetDescrManager* detMgr{nullptr};
156 ATH_CHECK(SG::get(caloClusters, m_clusterContainerKey, ctx));
158
159 const Acts::GeometryContext tgContext = m_ctxProvider.getGeometryContext(ctx);
161 const SortedCluster_t coneClusters = selectAndSort(*caloClusters, detMgr);
162
163 SG::WriteHandle writeHandle{m_caloExtensionKey, ctx};
164 ATH_CHECK(writeHandle.record(std::make_unique<CaloExtensionContainer>()));
165
168
170 for (const xAOD::TrackParticle* track : *idTracks) {
171 Link_t& extensionLink = decorHandle(*track);
172 if (track->pt() < m_trackPt ||
173 (m_trackSelector.isEnabled() && !m_trackSelector->accept(*track))){
174 continue;
175 }
176 auto extension = propagateToCaloExit(ctx, track);
177 if (!extension) {
178 continue;
179 }
180 // Loop over the neighbour bins corresponding to the track eta, phi
181 // and match the clusters in that bin to the track
182 for (const auto binIdx : coneClusters.neighborHoodIndices(
183 coneClusters.localBinsFromPosition(pack(track->eta(), track->phi())))){
184 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<"() - Try to match "<<coneClusters.at(binIdx).size()<<
185 " clusters from bin "<<binIdx<<". Central bin "
186 <<coneClusters.globalBinFromPosition(pack(track->eta(), track->phi())));
187 matchClusters(tgContext, coneClusters.at(binIdx), *extension);
188 }
189
190 extensionLink = Link_t{writeHandle.cptr(), writeHandle->size()};
191 writeHandle->push_back(std::move(extension));
192 }
193
194 return StatusCode::SUCCESS;
195 }
196 void CaloExtensionAlg::matchClusters(const Acts::GeometryContext& tgContext,
197 std::span<const xAOD::CaloCluster* const> clusterContainer,
198 CaloExtension& caloExtension) const {
199
200 const auto exitPars = caloExtension.lastTrackParameters();
201
202 for (const xAOD::CaloCluster* matchMe : clusterContainer) {
203 if (!checkBroadCriteria(tgContext, *matchMe, *exitPars)) {
204 continue;
205 }
206 for (const Acts::BoundTrackParameters& recordedPars: caloExtension.parameters()) {
207 const Acts::Surface& surf = recordedPars.referenceSurface();
208
209 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Try to match "<<recordedPars
210 <<",\nsurface:"<<surf.geometryId()<<", bounds: "<<surf.bounds());
211 const Acts::GeometryIdentifier volId = surf.geometryId().withSensitive(0).withBoundary(0);
212
213 auto layerItr = m_geoLayerIds.find(volId);
214 if (layerItr == m_geoLayerIds.end()){
215 continue;
216 }
217 const float dEta = clusterEta(*matchMe) - Acts::VectorHelpers::eta(recordedPars);
218 const float dPhi = P4Helpers::deltaPhi(matchMe->phi(), recordedPars.phi());
219 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - dEta: "<<dEta<<", dPhi: "<<dPhi<<".");
220 if (std::abs(dEta) < m_narrowDeltaEta &&
221 std::abs(dPhi) < m_narrowDeltaPhi) {
222 caloExtension.associateCluster(matchMe);
223 break;
224 }
225 }
226 }
227 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Associated: "<<caloExtension.associatedClusters().size()
228 <<" clusters: "<<clusterContainer.size()<<".");
229 }
230 bool CaloExtensionAlg::checkBroadCriteria(const Acts::GeometryContext& tgContext,
231 const xAOD::CaloCluster& cluster,
232 const Acts::BoundTrackParameters& lastTrkPars) const {
233 using namespace CandidateMatchHelpers;
234 // Get Cluster parameters.
235 const double etaClust = clusterEta(cluster);
236 const bool isEndCap = !xAOD::EgammaHelpers::isBarrel(&cluster);
237
238 const Amg::Vector3D globTrkPos = lastTrkPars.position(tgContext);
239 const double trkEta = Acts::VectorHelpers::eta(lastTrkPars);
240 // Calculate the eta/phi of the cluster as would be seen from the perigee
241 // position of the Track.
242 const Amg::Vector3D globalClusterPosWrtPerigee = approxXYZwrtPoint(cluster, globTrkPos, isEndCap);
243
244 auto passDeltaPhi = [&]() -> bool {
245 using namespace P4Helpers;
246 const double clusterPhi = globalClusterPosWrtPerigee.phi();
247 const double trkPhi = lastTrkPars.phi();
248 if (std::abs(deltaPhi(trkPhi, clusterPhi)) < m_broadDeltaPhi) {
249 return true;
250 }
251 // Calculate the possible rotation of the track.
252 // Once assuming the cluster Et being the better estimate (e.g big brem).
253 const double phiRotRescaled = PhiROT(cluster.et(), trkEta,
254 lastTrkPars.charge(),
255 globTrkPos.perp(), isEndCap);
256
257 // DeltaPhi between the track and the cluster accounting for rotation assuming
258 if (std::abs(deltaPhi(clusterPhi, deltaPhi(trkPhi, phiRotRescaled))) < m_broadDeltaPhi) {
259 return true;
260 }
261 // And also assuming the track Pt being correct.
262 const double phiRotTrack = PhiROT(lastTrkPars.transverseMomentum(), trkEta,
263 lastTrkPars.charge(), globTrkPos.perp(), isEndCap);
264
265
266 // DeltaPhi between the track and the cluster accounting for rotation.
267 if (std::abs(deltaPhi(clusterPhi, deltaPhi(trkPhi, phiRotTrack))) < m_broadDeltaPhi) {
268 return true;
269 }
270 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - broad dPhi matching fails with track phi: "<<
271 trkPhi<<", phiRotCluster: "<<phiRotRescaled<<", phiRotTrack: "<<phiRotTrack<<", "
272 "cluster phi: "<<cluster.phi()<<", phi corrected: "<<clusterPhi);
273 return false;
274 };
275
276 if (!passDeltaPhi()) {
277 return false;
278 }
280 if (std::abs(etaClust - trkEta) < m_broadDeltaEta ||
281 std::abs(globalClusterPosWrtPerigee.eta() - trkEta) < m_broadDeltaEta) {
282 return true;
283 }
284 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" broad dEta matching fails with track eta: "
285 <<trkEta<<", cluster eta: "<<etaClust<<", corrected eta: "<<globalClusterPosWrtPerigee.eta());
286 return false;
287 }
288
289}
Scalar eta() const
pseudorapidity method
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
Scalar phi() const
phi method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
ElementLink< xAOD::TruthParticleContainer > Link_t
Handle class for reading from StoreGate.
Handle class for adding a decoration to an object.
Handle class for recording to StoreGate.
ToolHandle< InDet::IInDetTrackSelectionTool > m_trackSelector
Track quality selection tool (optional).
Gaudi::Property< float > m_minClustEt
Minimum requirement on the cluster's transversal energy in order to be considered.
ToolHandle< IegammaCaloClusterSelector > m_clusterSelector
Tool to filter the calo clusters.
virtual StatusCode execute(const EventContext &ctx) const override final
Gaudi::Property< float > m_narrowDeltaPhi
std::unique_ptr< CaloExtension > propagateToCaloExit(const EventContext &ctx, const xAOD::TrackParticle *track) const
Propagates the Track from the last track measurement to the calorimeter exit and records all surface ...
Acts::Grid< ClusterVec_t, EtaAxis_t, PhiAxis_t > SortedCluster_t
Define the segmen cluster grid made out of calo cluster containers segmented in eta & phi.
std::vector< const xAOD::CaloCluster * > ClusterVec_t
Use an Acts::Grid to segment the cluster in eta / phi space to reduce the matching combinatorics betw...
SortedCluster_t selectAndSort(const xAOD::CaloClusterContainer &clusters, const CaloDetDescrManager *detMgr) const
Selects the calo clusters for the matching to the ID tracks based on the eta range and on a minimum t...
bool checkBroadCriteria(const Acts::GeometryContext &tgContext, const xAOD::CaloCluster &cluster, const Acts::BoundTrackParameters &lastTrkPars) const
Checks whether the track @ its last measurement state is roughly compatible with the calorimeter clus...
virtual StatusCode initialize() override final
ServiceHandle< ActsTrk::ITrackingGeometrySvc > m_trackingGeometrySvc
Tracking geometry service.
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_extensionDecorKey
Decorate the link to the associated CaloExtension directly onto the ID / ITk track.
Gaudi::Property< float > m_broadDeltaPhi
Gaudi::Property< float > m_trackPt
The minimum momentum cut applied on the ID tracks to be considered.
Gaudi::Property< float > m_maxClustEta
Maximum cut on the cluster's eta in order to be considered.
SG::WriteHandleKey< CaloExtensionContainer > m_caloExtensionKey
std::unordered_map< Acts::GeometryIdentifier, CaloSample > m_geoLayerIds
Acts::Axis< Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Bound > EtaAxis_t
Along eta define an equidistance binning without any overflow bin.
Acts::Axis< Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Closed > PhiAxis_t
Along phi define an equidistance binning but with a circular wrapping connection between the two edge...
SG::ReadHandleKey< xAOD::CaloClusterContainer > m_clusterContainerKey
The input calorimeter cluster collection.
Gaudi::Property< float > m_narrowDeltaEta
Narrow windows.
xAOD::CaloCluster::CaloSample CaloSample
Gaudi::Property< float > m_broadDeltaEta
Broad windowsto initially match the calo cluster with the track exit parameters.
ActsTrk::ContextUtility m_ctxProvider
Context provider for geometry, magnetic field and calibration contexts.
SG::ReadHandleKey< xAOD::TrackParticleContainer > m_trackParticleContainerKey
The input track particle collection.
ToolHandle< IExtrapolationTool > m_extrapolationTool
Acts extrapolation tool to record the surface intersections.
void matchClusters(const Acts::GeometryContext &tgContext, std::span< const xAOD::CaloCluster *const > clusterContainer, CaloExtension &caloExtension) const
Match the selected calorimeter clusters to the calo extension.
SG::ReadCondHandleKey< CaloDetDescrManager > m_caloDetDescrMgrKey
Calo description manager.
The CaloExtension holds the ID extrapolation states through the calorimeter and the associated CaloCl...
const ClusterVec_t & associatedClusters() const
Returns the view on the asociated clusters.
void associateCluster(const xAOD::CaloCluster *clust)
Associates a calorimeter cluster with the calo extension.
std::optional< Acts::BoundTrackParameters > lastTrackParameters() const
Returns the track parameters asssociated with the last measurement state of the ID track.
const Acts::BoundTrackParameters & parameters(const std::size_t parIdx) const
Returns the extension parameters associated with the n-th extension.
This class provides the client interface for accessing the detector description information common to...
static std::string getSamplingName(CaloSample theSample)
Returns a string (name) for each CaloSampling.
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.
virtual double eta() const
The pseudorapidity ( ) of the particle.
virtual double phi() const
The azimuthal angle ( ) of the particle.
float etaBE(const unsigned layer) const
Get the eta in one layer of the EM Calo.
The AlignStoreProviderAlg loads the rigid alignment corrections and pipes them through the readout ge...
Eigen::Matrix< double, 3, 1 > Vector3D
double PhiROT(const double pt, const double eta, const int charge, const double r_start, const bool isEndCap)
Function to calculate the approximate rotation in phi/bending of a track until it reaches the calo.
Amg::Vector3D approxXYZwrtPoint(const xAOD::CaloCluster &cluster, const Amg::Vector3D &point, const bool isEndCap)
Function to get the (x,y,z) of the cluster wrt to a point (x0,y0,z0).
P4Helpers provides static helper functions for kinematic calculation on objects deriving from I4Momen...
Definition P4Helpers.h:32
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
Definition P4Helpers.h:34
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
bool isBarrel(const xAOD::Egamma *eg)
return true if the cluster is in the barrel
bool isFCAL(const xAOD::CaloCluster *cluster)
return true if the cluster (or the majority of its energy) is in the FCAL0
CaloCluster_v1 CaloCluster
Define the latest version of the calorimeter cluster class.
TrackParticle_v1 TrackParticle
Reference the current persistent version:
TrackParticleContainer_v1 TrackParticleContainer
Definition of the current "TrackParticle container version".
CaloClusterContainer_v1 CaloClusterContainer
Define the latest version of the calorimeter cluster container.
Configuration struct to steer the propagation with surface record.
bool recordMaterial
Flag to toggle whether track parameters at material surfaces shall be created if crossed.