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 }
76 return StatusCode::SUCCESS;
77 }
80 const CaloDetDescrManager* detMgr) const{
81 const std::size_t nEtaBins = std::ceil((2. * m_maxClustEta) / m_broadDeltaEta);
82 const std::size_t nPhiBins = std::ceil((2.*std::numbers::pi) / m_broadDeltaPhi);
83 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Create grid to sort the clusters with "<<nEtaBins
84 <<" bins in eta and "<<nPhiBins<<" bins in phi.");
86 PhiAxis_t{-std::numbers::pi, std::numbers::pi, nPhiBins}};
87
88 std::size_t selected{0ul};
89 for (const xAOD::CaloCluster* cluster : clusters) {
90 if(cluster->et() < m_minClustEt ||
91 std::abs(clusterEta(*cluster)) > m_maxClustEta ||
92 (m_clusterSelector.isEnabled() && !m_clusterSelector->passSelection(cluster, *detMgr))) {
93 continue;
94 }
95 ClusterVec_t& bin = grid.atPosition(pack(clusterEta(*cluster), cluster->phi()));
96 bin.push_back(cluster);
97 ++selected;
98 }
99 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Selected "<<selected
100 <<" out of "<<clusters.size()<<" calo clusters.");
101 return grid;
102 }
103
104 std::unique_ptr<CaloExtension> CaloExtensionAlg::propagateToCaloExit(const EventContext& ctx,
105 const xAOD::TrackParticle* track) const{
106
107
108 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Extrapolate track with pT: "<<(track->pt() * 1.e-3)
109 <<" [GeV], eta: "<<track->eta()<<", phi: "<<(track->phi() / 1._degree)<<", q: "<<track->charge());
110 const Acts::TrackingVolume* caloExit = m_trackingGeometrySvc->getEnvelope(SystemEnvelope::CaloExit);
111 const Acts::TrackingVolume* caloEntrance = m_trackingGeometrySvc->getEnvelope(m_useHGTD ? SystemEnvelope::HgtdExit:
113 const Acts::GeometryContext tgContext = m_ctxProvider.getGeometryContext(ctx);
114 auto extension = std::make_unique<CaloExtension>(track);
116 auto lastTrackPars = extension->lastParameters();
117 if (!lastTrackPars) {
118 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - The track does not have any Acts::BoundTrack parameters");
119 return nullptr;
120 }
121 using SurfaceRecordOptions = IExtrapolationTool::SurfaceRecordOptions;
122 SurfaceRecordOptions propOpts{caloExit, IExtrapolationTool::VolumeAbort::atExit};
123 propOpts.recordMaterial = true;
124 propOpts.recordPassive = true;
125 propOpts.recordSensitive = true;
126 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Start to propagate \n"<<(*lastTrackPars)<<"\n, position: "
127 <<Amg::toString(lastTrackPars->position(tgContext))<< " through the calorimeter.");
128 auto surfaceRecord = m_extrapolationTool->propagateAndRecord(ctx, *lastTrackPars, propOpts);
129 if (!surfaceRecord.ok()) {
130 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Propagation through calorimeter did not succeed");
131 return nullptr;
132 }
134 for (Acts::BoundTrackParameters& record : *surfaceRecord) {
136 if (caloEntrance->inside(tgContext, record.position(tgContext), Acts::s_onSurfaceTolerance) ||
137 record.referenceSurface().geometryId().withBoundary(0) == caloEntrance->geometryId()) {
138 continue;
139 }
140 const Acts::GeometryIdentifier volId{record.referenceSurface().geometryId().withBoundary(0).withSensitive(0)};
141 const Acts::TrackingVolume* vol = m_trackingGeometrySvc->trackingGeometry()->findVolume(volId);
142 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Append calorimeter parameters:\n"
143 <<record<<",\n position:"<< Amg::toString(record.position(tgContext))
144 <<", surface: "<<record.referenceSurface().bounds()<<", volume: "<<vol->volumeName()<<".");
145 extension->appendParameters(std::move(record));
146 }
147 if (extension->empty()){
148 extension.reset();
149 }
150 return extension;
151 }
152 StatusCode CaloExtensionAlg::execute(const EventContext& ctx) const {
153
154 const xAOD::TrackParticleContainer* idTracks{nullptr};
155 const xAOD::CaloClusterContainer* caloClusters{nullptr};
156 const CaloDetDescrManager* detMgr{nullptr};
159 ATH_CHECK(SG::get(caloClusters, m_clusterContainerKey, ctx));
161
162 const Acts::GeometryContext tgContext = m_ctxProvider.getGeometryContext(ctx);
164 const SortedCluster_t coneClusters = selectAndSort(*caloClusters, detMgr);
166 const auto& clusterAxes = coneClusters.multiAxis();
167
168 SG::WriteHandle writeHandle{m_caloExtensionKey, ctx};
169 ATH_CHECK(writeHandle.record(std::make_unique<CaloExtensionContainer>()));
170
173
175 for (const xAOD::TrackParticle* track : *idTracks) {
176 Link_t& extensionLink = decorHandle(*track);
177 if (track->pt() < m_trackPt ||
178 (m_trackSelector.isEnabled() && !m_trackSelector->accept(*track))){
179 continue;
180 }
181 auto extension = propagateToCaloExit(ctx, track);
182 if (!extension) {
183 continue;
184 }
185 // Loop over the neighbour bins corresponding to the track eta, phi
186 // and match the clusters in that bin to the track
187 const auto trackPos = pack(track->eta(), track->phi());
188 for (const auto binIdx : clusterAxes.getNeighborHoodIndices(
189 clusterAxes.getLocalBinsFromPoint(trackPos))){
190 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<"() - Try to match "<<coneClusters.at(binIdx).size()<<
191 " clusters from bin "<<binIdx<<". Central bin "
192 <<clusterAxes.getGlobalBinFromPoint(trackPos));
193 matchClusters(tgContext, coneClusters.at(binIdx), *extension);
194 }
195
196 extensionLink = Link_t{writeHandle.key(), writeHandle->size(), ctx};
197 writeHandle->push_back(std::move(extension));
198 }
199
200 return StatusCode::SUCCESS;
201 }
202 void CaloExtensionAlg::matchClusters(const Acts::GeometryContext& tgContext,
203 std::span<const xAOD::CaloCluster* const> clusterContainer,
204 CaloExtension& caloExtension) const {
205
206 const auto exitPars = caloExtension.lastTrackParameters();
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 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Try to match "<<recordedPars
214 <<",\nsurface:"<<surf.geometryId()<<", bounds: "<<surf.bounds());
215
216 const float dEta = clusterEta(*matchMe) - Acts::VectorHelpers::eta(recordedPars);
217 const float dPhi = P4Helpers::deltaPhi(matchMe->phi(), recordedPars.phi());
218 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - dEta: "<<dEta<<", dPhi: "<<dPhi<<".");
219 if (std::abs(dEta) < m_narrowDeltaEta &&
220 std::abs(dPhi) < m_narrowDeltaPhi) {
221 caloExtension.associateCluster(matchMe);
222 break;
223 }
224 }
225 }
226 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Associated: "<<caloExtension.associatedClusters().size()
227 <<" clusters: "<<clusterContainer.size()<<".");
228 }
229 bool CaloExtensionAlg::checkBroadCriteria(const Acts::GeometryContext& tgContext,
230 const xAOD::CaloCluster& cluster,
231 const Acts::BoundTrackParameters& lastTrkPars) const {
232 using namespace CandidateMatchHelpers;
233 // Get Cluster parameters.
234 const double etaClust = clusterEta(cluster);
235 const bool isEndCap = !xAOD::EgammaHelpers::isBarrel(&cluster);
236
237 const Amg::Vector3D globTrkPos = lastTrkPars.position(tgContext);
238 const double trkEta = Acts::VectorHelpers::eta(lastTrkPars);
239 // Calculate the eta/phi of the cluster as would be seen from the perigee
240 // position of the Track.
241 const Amg::Vector3D globalClusterPosWrtPerigee = approxXYZwrtPoint(cluster, globTrkPos, isEndCap);
242
243 auto passDeltaPhi = [&]() -> bool {
244 using namespace P4Helpers;
245 const double clusterPhi = globalClusterPosWrtPerigee.phi();
246 const double trkPhi = lastTrkPars.phi();
247 if (std::abs(deltaPhi(trkPhi, clusterPhi)) < m_broadDeltaPhi) {
248 return true;
249 }
250 // Calculate the possible rotation of the track.
251 // Once assuming the cluster Et being the better estimate (e.g big brem).
252 const double phiRotRescaled = PhiROT(cluster.et(), trkEta,
253 lastTrkPars.charge(),
254 globTrkPos.perp(), isEndCap);
255
256 // DeltaPhi between the track and the cluster accounting for rotation assuming
257 if (std::abs(deltaPhi(clusterPhi, deltaPhi(trkPhi, phiRotRescaled))) < m_broadDeltaPhi) {
258 return true;
259 }
260 // And also assuming the track Pt being correct.
261 const double phiRotTrack = PhiROT(lastTrkPars.transverseMomentum(), trkEta,
262 lastTrkPars.charge(), globTrkPos.perp(), isEndCap);
263
264
265 // DeltaPhi between the track and the cluster accounting for rotation.
266 if (std::abs(deltaPhi(clusterPhi, deltaPhi(trkPhi, phiRotTrack))) < m_broadDeltaPhi) {
267 return true;
268 }
269 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - broad dPhi matching fails with track phi: "<<
270 trkPhi<<", phiRotCluster: "<<phiRotRescaled<<", phiRotTrack: "<<phiRotTrack<<", "
271 "cluster phi: "<<cluster.phi()<<", phi corrected: "<<clusterPhi);
272 return false;
273 };
274
275 if (!passDeltaPhi()) {
276 return false;
277 }
279 if (std::abs(etaClust - trkEta) < m_broadDeltaEta ||
280 std::abs(globalClusterPosWrtPerigee.eta() - trkEta) < m_broadDeltaEta) {
281 return true;
282 }
283 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" broad dEta matching fails with track eta: "
284 <<trkEta<<", cluster eta: "<<etaClust<<", corrected eta: "<<globalClusterPosWrtPerigee.eta());
285 return false;
286 }
287
288}
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.
Gaudi::Property< bool > m_useHGTD
Toggle whether the ITk or the HGTD envelope shall be used to reject non-calo track states.
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
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.
virtual const std::string & key() const override final
Return the StoreGate ID for the referenced object.
Handle class for adding a decoration to an object.
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.