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