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 }
45 inline std::array<double, 2> pack(const double eta, const double phi) {
46 return std::array{eta, phi};
47 }
48}
49
50namespace ActsTrk{
52 ATH_CHECK(m_clusterSelector.retrieve(EnableTool{!m_clusterSelector.empty()}));
53 ATH_CHECK(m_trackSelector.retrieve(EnableTool{!m_trackSelector.empty()}));
56
57 ATH_CHECK(m_clusterContainerKey.initialize());
59 ATH_CHECK(m_ctxProvider.initialize());
60 ATH_CHECK(m_extensionDecorKey.initialize());
61 ATH_CHECK(m_caloDetDescrMgrKey.initialize(m_clusterSelector.isEnabled()));
62 ATH_CHECK(m_caloExtensionKey.initialize());
63
64 // Here we extract the geometry identifiers of the 4 calo volumes in the ACTS geometry
65 // It seems more robust to do the matching with the volume name, since the geometry ID
66 // can change if details of the geometry change.
67 std::vector<std::pair<std::string, CaloSample>> volIndexNames{};
68 for (std::size_t id = 0 ; id < Acts::toUnderlying(CaloSample::Unknown); ++id){
69 const auto smpId = static_cast<CaloSample>(id);
70 volIndexNames.emplace_back(std::make_pair(CaloSampling::getSamplingName(smpId), smpId));
71 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - "<<volIndexNames.back().first<<" -> "
72 <<volIndexNames.back().second);
73 }
75 const Acts::TrackingVolume* caloExit = m_trackingGeometrySvc->getEnvelope(SystemEnvelope::CaloExit);
76
77 caloExit->visitVolumes([&](const Acts::TrackingVolume *vol) {
78 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Check volume: "<<vol->volumeName()<<".");
79 auto smpItr = std::ranges::find_if(volIndexNames, [&](const auto& sampleID) {
80 return vol->volumeName().starts_with(sampleID.first);
81 });
82 if (smpItr != volIndexNames.end()) {
83 m_geoLayerIds[vol->geometryId()] = smpItr->second;
84 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Assign geometryID "<<vol->geometryId()
85 <<"volume: "<<vol->volumeName()<<", to "<<smpItr->second<<".");
86 }
87 });
88
89
90 return StatusCode::SUCCESS;
91 }
94 const CaloDetDescrManager* detMgr) const{
95 const std::size_t nEtaBins = std::ceil((2. * m_maxClustEta) / m_broadDeltaEta);
96 const std::size_t nPhiBins = std::ceil((2.*std::numbers::pi) / m_broadDeltaPhi);
97 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Create grid to sort the clusters with "<<nEtaBins
98 <<" bins in eta and "<<nPhiBins<<" bins in phi.");
100 PhiAxis_t{-std::numbers::pi, std::numbers::pi, nPhiBins}};
101
102 std::size_t selected{0ul};
103 for (const xAOD::CaloCluster* cluster : clusters) {
104 if(cluster->et() < m_minClustEt ||
105 std::abs(clusterEta(*cluster)) > m_maxClustEta ||
106 (m_clusterSelector.isEnabled() && !m_clusterSelector->passSelection(cluster, *detMgr))) {
107 continue;
108 }
109 ClusterVec_t& bin = grid.atPosition(pack(clusterEta(*cluster), cluster->phi()));
110 bin.push_back(cluster);
111 ++selected;
112 }
113 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Selected "<<selected
114 <<" out of "<<clusters.size()<<" calo clusters.");
115 return grid;
116 }
117
118 std::unique_ptr<CaloExtension> CaloExtensionAlg::propagateToCaloExit(const EventContext& ctx,
119 const xAOD::TrackParticle* track) const{
120
121 const Acts::TrackingVolume* caloExit = m_trackingGeometrySvc->getEnvelope(SystemEnvelope::CaloExit);
122
123 auto extension = std::make_unique<CaloExtension>(track);
125 auto lastTrackPars = extension->lastParameters();
126 using SurfaceRecordOptions = IExtrapolationTool::SurfaceRecordOptions;
127 SurfaceRecordOptions propOpts{caloExit, IExtrapolationTool::VolumeAbort::atExit};
128 propOpts.recordMaterial = true;
129 propOpts.recordPassive = true;
130 propOpts.recordSensitive = true;
131
132 auto surfaceRecord = m_extrapolationTool->propagateAndRecord(ctx, *lastTrackPars, propOpts);
133 if (!surfaceRecord.ok()) {
134 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Propagation through calorimeter did not succeed");
135 return nullptr;
136 }
138 for (Acts::BoundTrackParameters& record : *surfaceRecord) {
139 if (&record.referenceSurface() == &(lastTrackPars->referenceSurface())) {
140 continue;
141 }
142 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Append calorimeter parameters: "
143 <<record<<",\n surface: "<<record.referenceSurface().bounds()<<".");
144 extension->appendParameters(std::move(record));
145 }
146 if (extension->empty()){
147 extension.reset();
148 }
149 return extension;
150 }
151 StatusCode CaloExtensionAlg::execute(const EventContext& ctx) const {
152
153 const xAOD::TrackParticleContainer* idTracks{nullptr};
154 const xAOD::CaloClusterContainer* caloClusters{nullptr};
155 const CaloDetDescrManager* detMgr{nullptr};
158 ATH_CHECK(SG::get(caloClusters, m_clusterContainerKey, ctx));
160
161 const Acts::GeometryContext tgContext = m_ctxProvider.getGeometryContext(ctx);
163 const SortedCluster_t coneClusters = selectAndSort(*caloClusters, detMgr);
165 const auto& clusterAxes = coneClusters.multiAxis();
166
167 SG::WriteHandle writeHandle{m_caloExtensionKey, ctx};
168 ATH_CHECK(writeHandle.record(std::make_unique<CaloExtensionContainer>()));
169
172
174 for (const xAOD::TrackParticle* track : *idTracks) {
175 Link_t& extensionLink = decorHandle(*track);
176 if (track->pt() < m_trackPt ||
177 (m_trackSelector.isEnabled() && !m_trackSelector->accept(*track))){
178 continue;
179 }
180 auto extension = propagateToCaloExit(ctx, track);
181 if (!extension) {
182 continue;
183 }
184 // Loop over the neighbour bins corresponding to the track eta, phi
185 // and match the clusters in that bin to the track
186 const auto trackPos = pack(track->eta(), track->phi());
187 for (const auto binIdx : clusterAxes.getNeighborHoodIndices(
188 clusterAxes.getLocalBinsFromPoint(trackPos))){
189 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<"() - Try to match "<<coneClusters.at(binIdx).size()<<
190 " clusters from bin "<<binIdx<<". Central bin "
191 <<clusterAxes.getGlobalBinFromPoint(trackPos));
192 matchClusters(tgContext, coneClusters.at(binIdx), *extension);
193 }
194
195 extensionLink = Link_t{writeHandle.cptr(), writeHandle->size()};
196 writeHandle->push_back(std::move(extension));
197 }
198
199 return StatusCode::SUCCESS;
200 }
201 void CaloExtensionAlg::matchClusters(const Acts::GeometryContext& tgContext,
202 std::span<const xAOD::CaloCluster* const> clusterContainer,
203 CaloExtension& caloExtension) const {
204
205 const auto exitPars = caloExtension.lastTrackParameters();
206
207 for (const xAOD::CaloCluster* matchMe : clusterContainer) {
208 if (!checkBroadCriteria(tgContext, *matchMe, *exitPars)) {
209 continue;
210 }
211 for (const Acts::BoundTrackParameters& recordedPars: caloExtension.parameters()) {
212 const Acts::Surface& surf = recordedPars.referenceSurface();
213
214 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Try to match "<<recordedPars
215 <<",\nsurface:"<<surf.geometryId()<<", bounds: "<<surf.bounds());
216 const Acts::GeometryIdentifier volId = surf.geometryId().withSensitive(0).withBoundary(0);
217
218 auto layerItr = m_geoLayerIds.find(volId);
219 if (layerItr == m_geoLayerIds.end()){
220 continue;
221 }
222 const float dEta = clusterEta(*matchMe) - Acts::VectorHelpers::eta(recordedPars);
223 const float dPhi = P4Helpers::deltaPhi(matchMe->phi(), recordedPars.phi());
224 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - dEta: "<<dEta<<", dPhi: "<<dPhi<<".");
225 if (std::abs(dEta) < m_narrowDeltaEta &&
226 std::abs(dPhi) < m_narrowDeltaPhi) {
227 caloExtension.associateCluster(matchMe);
228 break;
229 }
230 }
231 }
232 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Associated: "<<caloExtension.associatedClusters().size()
233 <<" clusters: "<<clusterContainer.size()<<".");
234 }
235 bool CaloExtensionAlg::checkBroadCriteria(const Acts::GeometryContext& tgContext,
236 const xAOD::CaloCluster& cluster,
237 const Acts::BoundTrackParameters& lastTrkPars) const {
238 using namespace CandidateMatchHelpers;
239 // Get Cluster parameters.
240 const double etaClust = clusterEta(cluster);
241 const bool isEndCap = !xAOD::EgammaHelpers::isBarrel(&cluster);
242
243 const Amg::Vector3D globTrkPos = lastTrkPars.position(tgContext);
244 const double trkEta = Acts::VectorHelpers::eta(lastTrkPars);
245 // Calculate the eta/phi of the cluster as would be seen from the perigee
246 // position of the Track.
247 const Amg::Vector3D globalClusterPosWrtPerigee = approxXYZwrtPoint(cluster, globTrkPos, isEndCap);
248
249 auto passDeltaPhi = [&]() -> bool {
250 using namespace P4Helpers;
251 const double clusterPhi = globalClusterPosWrtPerigee.phi();
252 const double trkPhi = lastTrkPars.phi();
253 if (std::abs(deltaPhi(trkPhi, clusterPhi)) < m_broadDeltaPhi) {
254 return true;
255 }
256 // Calculate the possible rotation of the track.
257 // Once assuming the cluster Et being the better estimate (e.g big brem).
258 const double phiRotRescaled = PhiROT(cluster.et(), trkEta,
259 lastTrkPars.charge(),
260 globTrkPos.perp(), isEndCap);
261
262 // DeltaPhi between the track and the cluster accounting for rotation assuming
263 if (std::abs(deltaPhi(clusterPhi, deltaPhi(trkPhi, phiRotRescaled))) < m_broadDeltaPhi) {
264 return true;
265 }
266 // And also assuming the track Pt being correct.
267 const double phiRotTrack = PhiROT(lastTrkPars.transverseMomentum(), trkEta,
268 lastTrkPars.charge(), globTrkPos.perp(), isEndCap);
269
270
271 // DeltaPhi between the track and the cluster accounting for rotation.
272 if (std::abs(deltaPhi(clusterPhi, deltaPhi(trkPhi, phiRotTrack))) < m_broadDeltaPhi) {
273 return true;
274 }
275 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - broad dPhi matching fails with track phi: "<<
276 trkPhi<<", phiRotCluster: "<<phiRotRescaled<<", phiRotTrack: "<<phiRotTrack<<", "
277 "cluster phi: "<<cluster.phi()<<", phi corrected: "<<clusterPhi);
278 return false;
279 };
280
281 if (!passDeltaPhi()) {
282 return false;
283 }
285 if (std::abs(etaClust - trkEta) < m_broadDeltaEta ||
286 std::abs(globalClusterPosWrtPerigee.eta() - trkEta) < m_broadDeltaEta) {
287 return true;
288 }
289 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" broad dEta matching fails with track eta: "
290 <<trkEta<<", cluster eta: "<<etaClust<<", corrected eta: "<<globalClusterPosWrtPerigee.eta());
291 return false;
292 }
293
294}
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.