ATLAS Offline Software
Loading...
Searching...
No Matches
MCTruthClassifierAthena.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#if !defined(XAOD_ANALYSIS) && !defined(GENERATIONBASE) // Can only be used in Athena
7// xAOD EDM includes
13// Athena only includes
14#include "AthenaKernel/Units.h"
20
21#include <cmath>
22
23using namespace MCTruthPartClassifier;
24
25namespace {
26std::unique_ptr<Trk::CurvilinearParameters> extractParamFromTruth(const xAOD::TruthParticle& particle) {
27 // get start parameters
28 const xAOD::TruthVertex* pvtx = particle.prodVtx();
29 if (pvtx == nullptr) return nullptr;
30 double charge = particle.charge();
31 Amg::Vector3D pos(pvtx->x(), pvtx->y(), pvtx->z());
32 Amg::Vector3D mom(particle.px(), particle.py(), particle.pz());
33 // Aproximate neutral particles as charged with infinite momentum
34 if (particle.isNeutral()) {
35 charge = 1.;
36 mom.normalize();
37 mom *= 1e10;
38 }
39 return std::make_unique<Trk::CurvilinearParameters>(pos, mom, charge);
40}
41}
42
43// Methods using directly the extrapolator usable only from Athena
45{
46 ATH_MSG_DEBUG("Executing egammaClusMatch ");
47 const xAOD::TruthParticle* theMatchPart = nullptr;
48 const EventContext& ctx = info ? info->eventContext : Gaudi::Hive::currentContext();
49 // retrieve collection and get a pointer
51
52 if (!truthParticleContainerReadHandle.isValid()) {
53 ATH_MSG_WARNING( " Invalid ReadHandle for xAOD::TruthParticleContainer with key: " << truthParticleContainerReadHandle.key());
54 return theMatchPart;
55 }
56
58 if (!caloMgrHandle.isValid()) {
59 ATH_MSG_WARNING(" Invalid ReadCondHandle for CaloDetDescrManager with key: " << m_caloMgrKey.key());
60 return theMatchPart;
61 }
62
63 const CaloDetDescrManager* caloDDMgr = *caloMgrHandle;
64 const xAOD::TruthParticle* theEgamma(nullptr);
65 const xAOD::TruthParticle* theLeadingPartInCone(nullptr);
66 const xAOD::TruthParticle* theBestPartOutCone(nullptr);
67 const xAOD::TruthParticle* theBestPartdR(nullptr);
68 double LeadingPhtPT(0);
69 double LeadingPartPT(0);
70 double LeadingPhtdR(999.);
71 double LeadingPartdR(999.);
72 double BestPartdR(999.);
73 double etaClus = clus->etaBE(2);
74 double phiClus = clus->phiBE(2);
75 if (etaClus < -900) {
76 etaClus = clus->eta();
77 }
78 if (phiClus < -900) {
79 phiClus = clus->phi();
80 }
81 std::vector<const xAOD::TruthParticle*> tps;
82 if (!m_truthInConeTool->particlesInCone(ctx, etaClus, phiClus, 0.5, tps)) {
83 ATH_MSG_WARNING("Truth Particle in Cone failed");
84 return theMatchPart;
85 }
86
87 for (const auto* const thePart : tps) {
88 if (!thePart)[[unlikely]] continue;
89 // loop over the stable particle
90 if (!MC::isStable(thePart)) continue;
91 // excluding G4 particle
92 if ((!isFwrdEle || (isFwrdEle && m_FwdElectronUseG4Sel)) && HepMC::is_simulation_particle(thePart)) continue;
93 long iParticlePDG = thePart->pdgId();
94 // excluding neutrino
95 if (std::abs(iParticlePDG) == 12 || std::abs(iParticlePDG) == 14 || std::abs(iParticlePDG) == 16) continue;
96 double pt = thePart->pt() / Athena::Units::GeV;
97 double q = thePart->charge();
98 // exclude charged particles with pT<1 GeV
99 if (q != 0 && pt < m_pTChargePartCut) continue;
100 if (q == 0 && pt < m_pTNeutralPartCut) continue;
101 float deltaPhi = std::abs(std::remainder(phiClus - thePart->phi(), 2*std::numbers::pi));
102 float deteta = std::abs(etaClus - thePart->eta());
103 // eleptical cone for extrapolations m_partExtrConePhi X m_partExtrConeEta
104 if (!isFwrdEle && m_ROICone && std::hypot( deltaPhi/m_partExtrConePhi, deteta/m_partExtrConeEta) > 1.0) {
105 continue;
106 }
107 // Also check if the clus and true have different sign , i they need both to be <0 or >0
108 if (isFwrdEle && // It is forward and
109 (((etaClus < 0) - (thePart->eta() < 0) != 0)
110 // The truth eta has different sign wrt to the fwd electron
111 || (std::fabs(thePart->eta()) < m_FwdElectronTruthExtrEtaCut) // or the truth is less than 2.4 (default cut)
112 || (std::fabs(thePart->eta() - etaClus) > m_FwdElectronTruthExtrEtaWindowCut) // or if the delta Eta between el and truth is > 0.15
113 ) // then do no extrapolate this truth Particle for this fwd electron
114 ) {
115 continue;
116 }
117 double dR(-999.);
118 bool isNCone = false;
119 bool isExt = genPartToCalo(ctx, clus, thePart, isFwrdEle, dR, isNCone, *caloDDMgr);
120 if (!isExt) continue;
121 theMatchPart = MC::findMatching(truthParticleContainerReadHandle.ptr(), thePart);
122 if (info) {
123 info->egPartPtr.push_back(thePart);
124 info->egPartdR.push_back(dR);
125 info->egPartClas.push_back(particleTruthClassifier(theMatchPart, info));
126 }
127 // Gen particles Not forward
128 if (!isFwrdEle) {
129 // the leading photon or electron inside narrow eleptical cone
130 // m_phtClasConePhi X m_phtClasConeEta
131 if ((iParticlePDG == 22 || std::abs(iParticlePDG) == 11) && isNCone && pt > LeadingPhtPT) {
132 theEgamma = thePart;
133 LeadingPhtPT = pt;
134 LeadingPhtdR = dR;
135 }
136 // leading particle (excluding photon and electron) inside narrow eleptic
137 // cone m_phtClasConePhi X m_phtClasConeEta
138 if ((iParticlePDG != 22 && std::abs(iParticlePDG) != 11) && isNCone && pt > LeadingPartPT) {
139 theLeadingPartInCone = thePart;
140 LeadingPartPT = pt;
141 LeadingPartdR = dR;
142 };
143 // the best dR matched particle outside narrow eleptic cone cone
144 // m_phtClasConePhi X m_phtClasConeEta
145 if (!isNCone && dR < BestPartdR) {
146 theBestPartOutCone = thePart;
147 BestPartdR = dR;
148 };
149 } else {
150 if (dR < BestPartdR) {
151 theBestPartdR = thePart;
152 BestPartdR = dR;
153 };
154 }
155 }
156
157 if (theEgamma != nullptr) {
158 theMatchPart = MC::findMatching(truthParticleContainerReadHandle.ptr(), theEgamma);
159 if (info) info->deltaRMatch = LeadingPhtdR;
160 } else if (theLeadingPartInCone != nullptr) {
161 theMatchPart = MC::findMatching(truthParticleContainerReadHandle.ptr(),theLeadingPartInCone);
162 if (info) info->deltaRMatch = LeadingPartdR;
163 } else if (theBestPartOutCone != nullptr) {
164 theMatchPart = MC::findMatching(truthParticleContainerReadHandle.ptr(),theBestPartOutCone);
165 if (info) info->deltaRMatch = BestPartdR;
166 } else if (isFwrdEle && theBestPartdR != nullptr) {
167 theMatchPart = MC::findMatching(truthParticleContainerReadHandle.ptr(),theBestPartdR );
168 if (info) info->deltaRMatch = BestPartdR;
169 } else {
170 theMatchPart = nullptr;
171 }
172 if (isFwrdEle || theMatchPart != nullptr || !m_inclG4part) return theMatchPart;
173
174 // additional loop over G4 particles,
175 for (const auto* const thePart : tps) {
176 if (!MC::isStable(thePart)) continue;
177 if (!HepMC::is_simulation_particle(thePart)) continue;
178 long iParticlePDG = thePart->pdgId();
179 // exclude neutrino
180 if (std::abs(iParticlePDG) == 12 || std::abs(iParticlePDG) == 14 || std::abs(iParticlePDG) == 16) continue;
181 if (thePart->decayVtx() != nullptr) continue;
182 const double dPhi = std::abs(std::remainder(phiClus - thePart->phi(), 2*std::numbers::pi));
183 const double dEta = std::abs(etaClus - thePart->eta());
184 if (std::hypot( dPhi/m_partExtrConePhi, dEta/m_partExtrConeEta ) > 1.0) continue;
185
186 double pt = thePart->pt() / Athena::Units::GeV;
187 double q = thePart->charge();
188 // exclude charged particles with pT<1 GeV
189 if (q != 0 && pt < m_pTChargePartCut) continue;
190 if (q == 0 && pt < m_pTNeutralPartCut) continue;
191
192 double dR(-999.);
193 bool isNCone = false;
194 bool isExt = genPartToCalo(ctx, clus, thePart, isFwrdEle, dR, isNCone, *caloDDMgr);
195 if (!isExt) continue;
196
197 theMatchPart = MC::findMatching(truthParticleContainerReadHandle.ptr(),thePart);
198 if (info) {
199 info->egPartPtr.push_back(thePart);
200 info->egPartdR.push_back(dR);
201 info->egPartClas.push_back(particleTruthClassifier(theMatchPart, info));
202 }
203 // the leading photon or electron inside narrow eleptical cone
204 // m_phtClasConePhi X m_phtClasConeEta
205 if ((iParticlePDG == 22 || std::abs(iParticlePDG) == 11) && isNCone && pt > LeadingPhtPT) {
206 theEgamma = thePart;
207 LeadingPhtPT = pt;
208 LeadingPhtdR = dR;
209 }
210 // leading particle (excluding photon or electron) inside narrow eleptic
211 // cone m_phtClasConePhi X m_phtClasConeEta
212 if ((iParticlePDG != 22 && std::abs(iParticlePDG) != 11) && isNCone && pt > LeadingPartPT) {
213 theLeadingPartInCone = thePart;
214 LeadingPartPT = pt;
215 LeadingPartdR = dR;
216 }
217 // the best dR matched particle outside narrow eleptic cone cone
218 // m_phtClasConePhi X m_phtClasConeEta
219 if (!isNCone && dR < BestPartdR) {
220 theBestPartOutCone = thePart;
221 BestPartdR = dR;
222 }
223 }
224
225 if (theEgamma != nullptr) {
226 theMatchPart = MC::findMatching(truthParticleContainerReadHandle.ptr(),theEgamma);
227 if (info) info->deltaRMatch = LeadingPhtdR;
228 } else if (theLeadingPartInCone != nullptr) {
229 theMatchPart = MC::findMatching(truthParticleContainerReadHandle.ptr(),theLeadingPartInCone);
230 if (info) info->deltaRMatch = LeadingPartdR;
231 } else if (theBestPartOutCone != nullptr) {
232 theMatchPart = MC::findMatching(truthParticleContainerReadHandle.ptr(),theBestPartOutCone);
233 if (info) info->deltaRMatch = BestPartdR;
234 } else {
235 theMatchPart = nullptr;
236 }
237 ATH_MSG_DEBUG("succeeded egammaClusMatch ");
238 return theMatchPart;
239}
240
241bool MCTruthClassifier::genPartToCalo(const EventContext& ctx,
242 const xAOD::CaloCluster* clus,
243 const xAOD::TruthParticle* thePart,
244 bool isFwrdEle,
245 double& dRmatch,
246 bool& isNarrowCone,
247 const CaloDetDescrManager& caloDDMgr) const
248{
249 dRmatch = -999.;
250 isNarrowCone = false;
251 if (thePart == nullptr) return false;
252 double phiClus = clus->phiBE(2);
253 double etaClus = clus->etaBE(2);
254 if (etaClus < -900) {
255 etaClus = clus->eta();
256 }
257 if (phiClus < -900) {
258 phiClus = clus->phi();
259 }
260 //--FixMe
261 if (isFwrdEle || (etaClus == 0. && phiClus == 0.)) {
262 phiClus = clus->phi();
263 etaClus = clus->eta();
264 }
265 // define calo sample
266 CaloSampling::CaloSample sample = CaloSampling::EMB2;
267 if ((clus->inBarrel() && !clus->inEndcap()) ||
268 (clus->inBarrel() && clus->inEndcap() && clus->eSample(CaloSampling::EMB2) >= clus->eSample(CaloSampling::EME2))) {
269 // Barrel
270 sample = CaloSampling::EMB2;
271 } else if ((!clus->inBarrel() && clus->inEndcap() && !isFwrdEle) ||
272 (clus->inBarrel() && clus->inEndcap() && clus->eSample(CaloSampling::EME2) > clus->eSample(CaloSampling::EMB2))) {
273 // End-cap
274 sample = CaloSampling::EME2;
275 } else if (isFwrdEle && clus->inEndcap()) {
276 // FCAL
277 sample = CaloSampling::FCAL2;
278 } else {
279 return false;
280 }
281 std::unique_ptr<Trk::CurvilinearParameters> params = extractParamFromTruth(*thePart);
282 if (!params) return false;
283 // create extension to sample
284 std::vector<CaloSampling::CaloSample> samples = { sample };
285 auto extension = m_caloExtensionTool->layersCaloExtension(ctx, *params, samples, etaClus, caloDDMgr);
286 bool extensionOK = (!extension.empty());
287 if (!extensionOK) {
288 ATH_MSG_WARNING("extrapolation of Truth Particle with eta " << thePart->eta() << " , charge " << thePart->charge() << " , Pt " << thePart->pt() << " to calo failed");
289 return false;
290 }
291 double etaCalo = extension[0].second->position().eta();
292 double phiCalo = extension[0].second->position().phi();
293
294 const double dPhi = std::abs(std::remainder(phiCalo - phiClus, 2*std::numbers::pi));
295 const double dEta = std::abs(etaCalo - etaClus);
296 dRmatch = std::hypot(dPhi, dEta);
297
298 if ((!isFwrdEle && dRmatch > m_phtdRtoTrCut) || (isFwrdEle && dRmatch > m_fwrdEledRtoTrCut)) return false;
299 if (!isFwrdEle && std::hypot( dPhi/m_phtClasConePhi, dEta/m_phtClasConeEta ) <= 1.0) isNarrowCone = true;
300 return true;
301}
302#endif
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
double charge(const T &p)
Definition AtlasPID.h:1003
Wrapper to avoid constant divisions when using units.
This class provides the client interface for accessing the detector description information common to...
virtual std::pair< MCTruthPartClassifier::ParticleType, MCTruthPartClassifier::ParticleOrigin > particleTruthClassifier(const xAOD::TruthParticle *, IMCTruthClassifier::Info *info) const override final
ToolHandle< xAOD::ITruthParticlesInConeTool > m_truthInConeTool
SG::ReadCondHandleKey< CaloDetDescrManager > m_caloMgrKey
bool genPartToCalo(const EventContext &ctx, const xAOD::CaloCluster *clus, const xAOD::TruthParticle *thePart, bool isFwrdEle, double &dRmatch, bool &isNarrowCone, const CaloDetDescrManager &caloDDMgr) const
float m_FwdElectronTruthExtrEtaWindowCut
virtual const xAOD::TruthParticle * egammaClusMatch(const xAOD::CaloCluster *, bool, IMCTruthClassifier::Info *info) const override final
ToolHandle< Trk::IParticleCaloExtensionTool > m_caloExtensionTool
SG::ReadHandleKey< xAOD::TruthParticleContainer > m_truthParticleContainerKey
const_pointer_type ptr()
Dereference the pointer.
virtual bool isValid() override final
Can the handle be successfully dereferenced?
virtual const std::string & key() const override final
Return the StoreGate ID for the referenced object.
float phiBE(const unsigned layer) const
Get the phi in one layer of the EM Calo.
virtual double eta() const
The pseudorapidity ( ) of the particle.
bool inBarrel() const
Returns true if at least one clustered cell in the barrel.
float eSample(const CaloSample sampling) const
bool inEndcap() const
Returns true if at least one clustered cell in the endcap.
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.
virtual double pt() const override final
The transverse momentum ( ) of the particle.
double charge() const
Physical charge.
virtual double eta() const override final
The pseudorapidity ( ) of the particle.
float z() const
Vertex longitudinal distance along the beam line form the origin.
float y() const
Vertex y displacement.
float x() const
Vertex x displacement.
Eigen::Matrix< double, 3, 1 > Vector3D
bool is_simulation_particle(const T &p)
Method to establish if a particle (or barcode) was created during the simulation (TODO update to be s...
T findMatching(C TruthContainer, T p)
Function to find a particle in container.
bool isStable(const T &p)
Identify if the particle is stable, i.e. has not decayed.
constexpr ParticleHypothesis particle[PARTICLEHYPOTHESES]
the array of masses
CaloCluster_v1 CaloCluster
Define the latest version of the calorimeter cluster class.
TruthVertex_v1 TruthVertex
Typedef to implementation.
Definition TruthVertex.h:15
TruthParticle_v1 TruthParticle
Typedef to implementation.
#define unlikely(x)