ATLAS Offline Software
Loading...
Searching...
No Matches
PFMuonFlowElementAssoc.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
9#include "xAODMuon/Muon.h"
13
14// includes for CaloCluster algo
15#include "CaloEvent/CaloCell.h"
17#include "Identifier/Identifier.h"
21//
22// Algorithm created by M.T. Anthony
23//
24// =============================================================
25PFMuonFlowElementAssoc::PFMuonFlowElementAssoc(const std::string& name, ISvcLocator* pSvcLocator) :
26 AthReentrantAlgorithm(name, pSvcLocator) {
27 // Declare the decoration keys
28}
29
31
32// =============================================================
34 ATH_MSG_VERBOSE("Initializing " << name() << "...");
35
36 // Initialise the decoration keys
44
45 // init the experimental keys
49
50 // init ReadHandleKeys
51 ATH_CHECK(m_muonReadHandleKey.initialize());
54
55 ATH_MSG_VERBOSE("Initialization completed successfully");
56
57 return StatusCode::SUCCESS;
58}
59
60// =========================================================================
61StatusCode PFMuonFlowElementAssoc::execute(const EventContext& ctx) const {
62 // WriteDecorHandles for the charged/neutral Flow Elements and Muons
63 // Links a Muon that has a track to a charged flow element if possible
64
65 ATH_MSG_VERBOSE("Started execute step");
66
67 // Get container for muons
69 ctx);
71 ctx);
72 // get container for charged flow elements
74 ctx);
76 ctx);
77
78 // extra container handles between neutral FE cluster and Muon CaloCluster - these are all for studies based on neutral Flow Element
79 // matching - All of these handles are experimental
80 SG::WriteDecorHandle<xAOD::FlowElementContainer, std::vector<double>> NeutralFE_efrac_match_muonWriteDecorHandle(
82 SG::WriteDecorHandle<xAOD::MuonContainer, std::vector<double>> muonNeutralFE_muon_efrac_WriteDecorHandle(
84 SG::WriteDecorHandle<xAOD::FlowElementContainer, int> NeutralFEmuon_nMatches_WriteDecorHandle(
86 SG::WriteDecorHandle<xAOD::MuonContainer, double> muon_ClusterInfo_deltaR_WriteDecorHandle(
88
89 // store readhandles for muon and charged flow elements
90 SG::ReadHandle<xAOD::MuonContainer> muonReadHandle(m_muonReadHandleKey, ctx); // readhandle for muon
94 // now init some Flow element link containers
95 std::vector<std::vector<FlowElementLink_t>> muonChargedFEVec(muonReadHandle->size());
96 std::vector<std::vector<FlowElementLink_t>> muonNeutralFEVec(muonReadHandle->size());
97
98 // for neutral flow element studies
99 std::vector<std::vector<double>> muonNeutralFE_frac_cluster_energy_matched_Vec(muonReadHandle->size());
100
102
103 // Loop over the Flow Elements
104
106 // CHARGED LOOP
108 for (const xAOD::FlowElement* FE : *ChargedFEmuonWriteDecorHandle) {
109 // get the track associated to the charged flow element (or at least the index of said track)
110 size_t FETrackIndex = FE->chargedObjects().at(0)->index();
111 // Init a vector of element links to muons
112 std::vector<MuonLink_t> FEMuonLinks;
113
114 //vector of fractions of FE cluster energy matched to muons
115 std::vector<double> FEMatchedClusterCellEnergies;
116 for (unsigned int counter = 0; counter < FE->otherObjects().size(); ++counter) FEMatchedClusterCellEnergies.push_back(0.0);
117
118 // loop over muons in container
119 for (const xAOD::Muon* muon : *muonChargedFEWriteDecorHandle) {
120 const xAOD::TrackParticle* muon_trk = muon->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle);
122 if (!muon_trk) continue;
123 // skip muon matching if the following cases occur
124 if (muon->muonType() == xAOD::Muon::MuonType::SiliconAssociatedForwardMuon) { // if muon is a forward muon, skip. Basically the tracks associated
125 // to this are the wrong type (InDetForwardTrackParticle instead of
126 // InDetTrackParticle), so the indices used would be wrong/generate
127 // spurious matches
128 ATH_MSG_DEBUG("Muon is identified as a forward muon, skipping");
129 continue;
130 }
131 if (muon->author() == xAOD::Muon::Author::STACO) { // remove muons primarily authored by STACO algorithm.
132 ATH_MSG_DEBUG("Muon is authored by STACO algorithm, skip");
133 continue;
134 }
135 size_t MuonTrkIndex = muon_trk->index();
136 if (MuonTrkIndex == FETrackIndex) {
137 // Add Muon element link to a vector
138 // index() is the unique index of the muon in the muon container
139 FEMuonLinks.emplace_back(*muonReadHandle, muon->index());
140 // Add flow element link to a vector
141 // index() is the unique index of the cFlowElement in the cFlowElementcontaine
142 muonChargedFEVec.at(muon->index()).emplace_back(*ChargedFEReadHandle, FE->index());
143 } // matching block
144
145 //if in mode where we can access calorimeter cells, then we will check if any of the clusters
146 //that this chargedFE has modified have calorimeter cells that match the muons calorimeter cells
147 //if so then we will decorate the charged FE with the fraction of cluster energy that was matched
149 const xAOD::CaloCluster* muonCluster = muon->cluster();
150 //these clusters are expected to be nullptr sometimes
151 if (!muonCluster) continue;
152 unsigned int counter = 0;
153 for (auto thisCluster : FE->otherObjects()){
154 const xAOD::CaloCluster* thisCaloCluster = dynamic_cast<const xAOD::CaloCluster*>(thisCluster);
155 if (!thisCaloCluster)[[unlikely]] continue;
156 bool isCellMatched = false;
157 std::pair <double,double> FEAndMuonMatchedCellEnergy = this->doMuonCellMatching(isCellMatched, *thisCaloCluster,*muonCluster);
158 FEMatchedClusterCellEnergies[counter] += FEAndMuonMatchedCellEnergy.first;
159 counter++;
160 }//loop over associated calorimeter clusters
161 }
162 }// end of muon loop
163
164 chargedFE_energy_match_muonWriteHandle(*FE) = std::move(FEMatchedClusterCellEnergies);
165
166 // Add vector of muon element links as decoration to FlowElement container
167 ChargedFEmuonWriteDecorHandle(*FE) = std::move(FEMuonLinks);
168 } // end of charged Flow Element loop
169
171 // Loop over Neutral FlowElements
173
183 ATH_MSG_VERBOSE("Experimental: Cluster Linkers between neutral FEs and Muons are used");
184 for (const xAOD::FlowElement* FE : *NeutralFEmuonWriteDecorHandle) {
185 int nMatchedFE = 0;
186 // get the index of the cluster corresponding to the Neutral FlowElements
187 ATH_MSG_DEBUG("P1");
188 ATH_MSG_DEBUG("FE with e, eta and phi" << FE->e() << ", " << FE->eta() << " and " << FE->phi());
189 const xAOD::IParticle* otherObject = FE->otherObjects().at(0);
190 //This is expected to happen for low energy FE - sometimes the linked cluster has E < 0 and
191 //is thinned away in the AOD
192 if (!otherObject){
193 ATH_MSG_DEBUG("No linked cluster for Neutral FE with E, eta and phi" << FE->e() << ", " << FE->eta() << " and " << FE->phi());
194 continue;
195 }
196 size_t FEclusterindex = otherObject->index();
197
198 // FE->otherObjects returns a vector of IParticles. We only want the first one
199 const xAOD::IParticle* FE_Iparticle = FE->otherObjects().at(0);
200
201 // dynamic cast to CaloCluster
202 const xAOD::CaloCluster* FE_cluster = dynamic_cast<const xAOD::CaloCluster*>(FE_Iparticle); // cast to CaloCluster
203 if (!FE_cluster) [[unlikely]] continue;
204 // debug for Negative energy cluster
205
206 double cluster_E = FE_cluster->p4().E();
207 bool neg_E_cluster = (cluster_E < 0.0);
208
209 // design the vector of ElementLinks
210 std::vector<MuonLink_t> FEMuonLinks;
211 std::vector<double> FE_efrac_clustermatch;
212 std::vector<double> Muon_efrac_clustermatch;
213 for (const xAOD::Muon* muon : *muonNeutralFEWriteDecorHandle) {
214 // Retrieve the ElementLink vector of clusters
215 const xAOD::CaloCluster* cluster = muon->cluster();
216 // de-ref the element link to retrieve the pointer to the original object
217 // check if the ElementLink is valid
218 if (!cluster) {
219 ATH_MSG_DEBUG("Muon has an invalid link to cluster");
220 continue;
221 }
223 // get the linker to the topo clusters
224 const std::vector<ElementLink<xAOD::CaloClusterContainer>>& linksToTopoClusters = acc_constClusterLinks(*cluster);
225 for (const ElementLink<xAOD::CaloClusterContainer>& TopoClusterLink : linksToTopoClusters) {
226 //This is expected to happen for low energy cluster - sometimes the linked cluster has E < 0 and
227 //is thinned away in the AOD
228 if (!TopoClusterLink.isValid()) {
229 ATH_MSG_DEBUG("Muon Calo cluster's TopoCluster link not found, skip");
230 continue;
231 }
232 const xAOD::CaloCluster* MuonTopoCluster = *TopoClusterLink; // de-ref the link to get the topo-cluster
233 size_t MuonTopoCluster_index = MuonTopoCluster->index();
234 if (MuonTopoCluster_index == FEclusterindex) {
235 // Add Muon element link to a vector
236 // index() is the unique index of the muon in the muon container
237 FEMuonLinks.emplace_back(*muonReadHandle, muon->index());
238 // index() is the unique index of the cFlowElement in the cFlowElementcontaine
239 muonNeutralFEVec.at(muon->index()).emplace_back(*NeutralFEReadHandle, FE->index());
240 ATH_MSG_VERBOSE("Got a match between NFE and Muon");
241 nMatchedFE++; // count number of matches between FE and muons
242 if (neg_E_cluster) ATH_MSG_ERROR("Muon cluster matched to negative E topocluster from FE");
243 } // check block of index matching
244 } // end of loop over element links
245 } // end of TopoCluster specific block
246 else { // case when we don't use Topoclusters, just match the caloclusters to the flow element
247 // if we don't match topoclusters, do something more complex:
248 // Retrieve cells in both the FE cluster and muon cluster
249 // Define the link as where at least one calo cell is shared between the FE cluster and the Muon Cluster
250
251 bool isCellMatched = false;
252 std::pair<double,double> FEAndMuonMatchedCellEnergy = this->doMuonCellMatching(isCellMatched, *FE_cluster,*cluster);
253
254 double FE_sum_matched_cellEnergy = FEAndMuonMatchedCellEnergy.first;
255 double Muon_sum_matched_cellEnergy = FEAndMuonMatchedCellEnergy.second;
256
257 double frac_FE_cluster_energy_matched = 0;
258 // retrieve total cluster energy from the FE cluster
259 double tot_FE_cluster_energy = FE_cluster->e();
260 if (tot_FE_cluster_energy != 0) { // ! div 0
261 frac_FE_cluster_energy_matched = FE_sum_matched_cellEnergy / tot_FE_cluster_energy;
262 }
263 double tot_muon_cluster_energy = cluster->e();
264 double frac_muon_cluster_energy_matched = 0;
265 if (tot_muon_cluster_energy != 0) {
266 frac_muon_cluster_energy_matched = Muon_sum_matched_cellEnergy / tot_muon_cluster_energy;
267 }
268 if (frac_FE_cluster_energy_matched > 0) {
269 ATH_MSG_VERBOSE("Fraction of FE cluster energy used in match: " << frac_FE_cluster_energy_matched << ", ismatched? "
270 << isCellMatched << "");
271 ATH_MSG_VERBOSE("Numerator and denominator are " << FE_sum_matched_cellEnergy << " and " << tot_FE_cluster_energy);
272 ATH_MSG_VERBOSE("Fraction of Muon cluster energy used in match: " << frac_muon_cluster_energy_matched << "");
273 }
274
275 if (isCellMatched) { // cell matched => Link the two objects.
276 // Add Muon element link to a vector
277 // index() is the unique index of the muon in the muon container
278 FEMuonLinks.emplace_back(*muonReadHandle, muon->index());
279 // index() is the unique index of the nFlowElement in the nFlowElementcontainer
280 muonNeutralFEVec.at(muon->index()).emplace_back(*NeutralFEReadHandle, FE->index());
281 // save the energy fraction used in the cluster matching - mostly for debug/extension studies
282 FE_efrac_clustermatch.push_back(frac_FE_cluster_energy_matched); // fraction of FE cluster energy matched
283 muonNeutralFE_frac_cluster_energy_matched_Vec.at(muon->index())
284 .push_back(frac_muon_cluster_energy_matched); // fraction of Muon cluster energy matched
285 nMatchedFE++; // count number of matches incrementally
286 if (neg_E_cluster) { ATH_MSG_ERROR("Muon cluster matched to negative E topocluster from FE"); }
287 }
288
289 } // end of calocluster specific block
290 // loop over caloclusters
291 } // loop over muons
292 NeutralFEmuon_nMatches_WriteDecorHandle(*FE) = nMatchedFE;
293 NeutralFEmuonWriteDecorHandle(*FE) = std::move(FEMuonLinks);
294 NeutralFE_efrac_match_muonWriteDecorHandle(*FE) = std::move(FE_efrac_clustermatch);
295 } // loop over neutral FE
296 } // end of the Gaudi check block
297
299 // WRITE OUTPUT: ADD HANDLES TO MUON CONTAINERS
301 // Add the vectors of the Flow Element Links as decoations to the muon container
302 for (const xAOD::Muon* muon : *muonChargedFEWriteDecorHandle) {
303 muonChargedFEWriteDecorHandle(*muon) = muonChargedFEVec.at(muon->index());
304 } // end of muon loop
305 if (m_LinkNeutralFEClusters) { // Experimental
306 for (const xAOD::Muon* muon : *muonNeutralFEWriteDecorHandle) {
307 if (!muonNeutralFEVec.empty()) {
308 muonNeutralFEWriteDecorHandle(*muon) = muonNeutralFEVec.at(muon->index());
309 muonNeutralFE_muon_efrac_WriteDecorHandle(*muon) = muonNeutralFE_frac_cluster_energy_matched_Vec.at(muon->index());
310 }
311 // For debug of the muon clusters used, add also: dR between caloclusters and number of caloclusters associated to each muon.
312 // retrieve element link again to cluster
313 // use elem link to retrieve container
314 const xAOD::CaloCluster* MuonCluster = muon->cluster();
315 // retrieve the vector of delta R between muon and its associated calo cluster.
316 muon_ClusterInfo_deltaR_WriteDecorHandle(*muon) = MuonCluster ? xAOD::P4Helpers::deltaR(MuonCluster,muon,false) : -1.;
317 }
318 } // end of experimental block
319 ATH_MSG_VERBOSE("Execute completed successfully");
320
321 return StatusCode::SUCCESS;
322}
323
324std::pair<double,double> PFMuonFlowElementAssoc::doMuonCellMatching(bool& isCellMatched,const xAOD::CaloCluster& FECluster, const xAOD::CaloCluster& muonCluster)
325const {
326
327 const CaloClusterCellLink* FECellLinks = FECluster.getCellLinks();
328 if (!FECellLinks && !m_useMuonTopoClusters) {
329 ATH_MSG_WARNING("Flow Element CaloCluster CaloClusterCellLink is nullptr");
330 return std::make_pair(0.0,0.0);
331 }
332
333 const CaloClusterCellLink* muonCellLinks = muonCluster.getCellLinks();
334 if (!muonCellLinks) {
335 ATH_MSG_WARNING("This Muon calo cluster does not have any cells associated to it");
336 return std::make_pair(0.0,0.0);
337 }
338
339 //sum of energy of FE cells that are matched
340 double FE_matchedCellEnergy = 0;
341 //sum of energy of muon cells that are matched
342 double muon_matchedCellEnergy = 0;
343
344 for (auto thisFECell : *FECellLinks){
345 Identifier FECellID = thisFECell->ID();
346 for (auto thisMuonCell : *muonCellLinks){
347 Identifier muonCellID = thisMuonCell->ID();
348 if (muonCellID == FECellID){
349 isCellMatched = true;
350 FE_matchedCellEnergy += thisFECell->e();
351 muon_matchedCellEnergy += thisMuonCell->e();
352 }//if FE cell is also a muon cell
353 }//loop on muon cells
354 }//loop on FE cluster cells
355
356 return std::make_pair(FE_matchedCellEnergy,muon_matchedCellEnergy);
357
358}
#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)
ElementLink< xAOD::FlowElementContainer > FlowElementLink_t
ElementLink< xAOD::MuonContainer > MuonLink_t
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.
An algorithm that can be simultaneously executed in multiple threads.
SG::WriteDecorHandleKey< xAOD::FlowElementContainer > m_ChargedFE_energy_match_muonWriteHandleKey
Write key for adding fraction of nFlowElement cluster energy used in cell matching decoration of Flow...
SG::WriteDecorHandleKey< xAOD::MuonContainer > m_muonNeutralFE_muon_efrac_WriteDecorHandleKey
Write key for adding fraction of Muon cluster energy used in cell matching decoration of MuonContaine...
SG::WriteDecorHandleKey< xAOD::FlowElementContainer > m_NeutralFEmuon_nMatches_WriteDecorHandleKey
Write key to count number of muons matched to a given neutral FE - EXPERIMENTAL.
SG::WriteDecorHandleKey< xAOD::MuonContainer > m_muon_ClusterInfo_deltaR_WriteDecorHandleKey
Write key to measure dR between calo clusters and the muon -EXPERIMENTAL.
SG::WriteDecorHandleKey< xAOD::FlowElementContainer > m_NeutralFE_efrac_match_muonWriteHandleKey
Write key for adding fraction of nFlowElement cluster energy used in cell matching decoration of Flow...
std::pair< double, double > doMuonCellMatching(bool &isCellMatched, const xAOD::CaloCluster &FECluster, const xAOD::CaloCluster &muonCluster) const
Function that flags whether the FE cluster has any cell that is also in the muon list of cells.
SG::ReadDecorHandleKey< xAOD::CaloClusterContainer > m_ClustCollectionLinkKey
Gaudi::Property< bool > m_useMuonTopoClusters
(EXPERIMENTAL) Gaudi Property to configure linkage of Neutral FEs to TopoClusters associated to Muons...
SG::WriteDecorHandleKey< xAOD::MuonContainer > m_muonNeutralFEWriteHandleKey
Write key for adding neutral Flow Element link decorations to muons.
SG::WriteDecorHandleKey< xAOD::FlowElementContainer > m_NeutralFEmuonWriteHandleKey
Write key for adding Muon link decorations to neutral Flow Elements.
SG::ReadHandleKey< xAOD::FlowElementContainer > m_neutralFEReadHandleKey
SG::WriteDecorHandleKey< xAOD::FlowElementContainer > m_ChargedFEmuonWriteHandleKey
Write key for adding Muon link decorations to charged Flow Elements.
SG::WriteDecorHandleKey< xAOD::MuonContainer > m_muonChargedFEWriteHandleKey
Write key for adding charged Flow Element link decorations to muons.
virtual ~PFMuonFlowElementAssoc()
SG::ReadHandleKey< xAOD::FlowElementContainer > m_chargedFEReadHandleKey
Gaudi::Property< bool > m_LinkNeutralFEClusters
Gaudi Property to configure linkage of Neutral Flow Elements to Muon clusters (EXPERIMENTAL - default...
virtual StatusCode initialize()
SG::ReadHandleKey< xAOD::MuonContainer > m_muonReadHandleKey
virtual StatusCode execute(const EventContext &ctx) const
PFMuonFlowElementAssoc(const std::string &name, ISvcLocator *pSvcLocator)
Handle class for reading a decoration on an object.
Handle class for adding a decoration to an object.
const CaloClusterCellLink * getCellLinks() const
Get a pointer to the CaloClusterCellLink object (const version).
virtual double e() const
The total energy of the particle.
virtual FourMom_t p4() const
The full 4-momentum of the particle.
Class providing the definition of the 4-vector interface.
double deltaR(double rapidity1, double phi1, double rapidity2, double phi2)
from bare bare rapidity,phi
CaloCluster_v1 CaloCluster
Define the latest version of the calorimeter cluster class.
FlowElement_v1 FlowElement
Definition of the current "pfo version".
Definition FlowElement.h:16
TrackParticle_v1 TrackParticle
Reference the current persistent version:
Muon_v1 Muon
Reference the current persistent version:
#define unlikely(x)