ATLAS Offline Software
Loading...
Searching...
No Matches
TauPi0ClusterCreator.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#ifndef XAOD_ANALYSIS
6
9
12#include "xAODJet/Jet.h"
13
14
16 TauRecToolBase(name) {
17}
18
19
20
22 xAOD::PFOContainer& neutralPFOContainer,
23 xAOD::PFOContainer& hadronicPFOContainer,
24 const xAOD::CaloClusterContainer& pi0ClusterContainer) const {
25 // Any tau needs to have PFO vectors. Set empty vectors before nTrack cut
26 std::vector<ElementLink<xAOD::PFOContainer>> empty;
27 tau.setProtoNeutralPFOLinks(empty);
28 tau.setHadronicPFOLinks(empty);
29
30 // only need pi0s and shots for 0-5 prong taus
32 return StatusCode::SUCCESS;
33 }
34
35 // Retrieve Ecal1 shots and match them to clusters
36 std::vector<const xAOD::PFO*> shotPFOs;
37
38 unsigned nShots = tau.nShotPFOs();
39 for (unsigned index=0; index<nShots; ++index) {
40 const xAOD::PFO* shotPFO = tau.shotPFO(index);
41 shotPFOs.push_back(shotPFO);
42 }
43
44 // We will always perform the vertex correction
45 const xAOD::Vertex* vertex = tau.vertex();
46
47 // Tau custom PFO reconstruction is only used in offline reconstrution
48 TLorentzVector tauAxis = tauRecTools::getTauAxis(tau);
49
50 // Preselect the pi0s clusters
51 std::vector<const xAOD::CaloCluster*> good_pi0s;
52 for (TLorentzVector clusterP4;const xAOD::CaloCluster* cluster: pi0ClusterContainer){
53 if (vertex) {
54 xAOD::CaloVertexedTopoCluster vertexedCluster(*cluster, vertex->position());
55 clusterP4 = vertexedCluster.p4();
56 }
57 else {
58 clusterP4 = cluster->p4();
59 }
60 // Clusters must have enough energy, and within 0.4 cone of the tau candidate
61 if ((clusterP4.Pt() < m_clusterEtCut) || (clusterP4.DeltaR(tauAxis) > m_maxDeltaRJetClust)) continue;
62
63 good_pi0s.push_back(cluster);
64 }
65
66 // Map shot to the pi0 cluster
67 std::map<unsigned, const xAOD::CaloCluster*> shotToClusterMap = getShotToClusterMap(shotPFOs, good_pi0s);
68
69 // Loop over preselected pi0 clusters, and create neutral PFOs
70 for (TLorentzVector clusterP4;const xAOD::CaloCluster* cluster: good_pi0s){
71 if (vertex) {
72 xAOD::CaloVertexedTopoCluster vertexedCluster(*cluster, vertex->position());
73 clusterP4 = vertexedCluster.p4();
74 }
75 else {
76 clusterP4 = cluster->p4();
77 }
78
79 // Create the neutral PFOs
80 xAOD::PFO* neutralPFO = new xAOD::PFO();
81 neutralPFOContainer.push_back(neutralPFO);
82
83 // -- Set the PFO four momentum corrected w.r.t. the tau vertex
84 neutralPFO->setP4(clusterP4.Pt(), clusterP4.Eta(), clusterP4.Phi(), 0.);
85
86 // Add the link to the tau candidate
88 PFOElementLink.toContainedElement(neutralPFOContainer, neutralPFO);
89 tau.addProtoNeutralPFOLink(PFOElementLink);
90
91 ATH_CHECK(configureNeutralPFO(*cluster, pi0ClusterContainer, tau, shotPFOs, shotToClusterMap, *neutralPFO));
92 }
93
94 // Loop over clusters from jet seed, and create hadronic PFOs
95 std::vector<xAOD::CaloVertexedTopoCluster> vertexedClusterList = tau.vertexedClusters();
96 for (const xAOD::CaloVertexedTopoCluster& vertexedCluster : vertexedClusterList){
97 TLorentzVector clusterP4 = vertexedCluster.p4();
98
99 // Clusters must have positive energy, and within 0.2 cone of the tau candidate
100 if((clusterP4.E()<=0.) || (clusterP4.DeltaR(tauAxis) > m_maxDeltaRNeutral) ) continue;
101
102 double clusterEnergyHad = 0.;
103
104 const xAOD::CaloCluster& cluster = vertexedCluster.clust();
105 const CaloClusterCellLink* cellLinks = cluster.getCellLinks();
106 if (!cellLinks) {
107 if (!m_recoFromAOD)
108 ATH_MSG_WARNING("The cell links of the tau cluster is unavailable.");
109 else
110 ATH_MSG_DEBUG("The cell links of the tau cluster is unavailable due to reconstruction from AOD");
111
112 continue;
113 }
114 CaloClusterCellLink::const_iterator cellLink = cellLinks->begin();
115 for (; cellLink != cellLinks->end(); ++cellLink) {
116 const CaloCell* cell = static_cast<const CaloCell*>(*cellLink);
117
118 int sampling = cell->caloDDE()->getSampling();
119 if (sampling < 8) continue;
120
121 // TODO: what is the weight for EMTopo
122 double cellEnergy = cell->e() * cellLink.weight();
123 clusterEnergyHad += cellEnergy;
124 }
125
126 // Energy in Had Calorimeter must be positive
127 if(clusterEnergyHad <= 0.) continue;
128
129 // Create the hadrnic PFO
130 xAOD::PFO* hadronicPFO = new xAOD::PFO();
131 hadronicPFOContainer.push_back(hadronicPFO);
132
133 // Add element link from tau to hadronic PFO
134 ElementLink<xAOD::PFOContainer> PFOElementLink;
135 PFOElementLink.toContainedElement( hadronicPFOContainer, hadronicPFO );
136 tau.addHadronicPFOLink( PFOElementLink );
137
138 ATH_CHECK(configureHadronicPFO(vertexedCluster, clusterEnergyHad, *hadronicPFO));
139 }
140
141 return StatusCode::SUCCESS;
142}
143
144
145
146std::map<unsigned, const xAOD::CaloCluster*> TauPi0ClusterCreator::getShotToClusterMap(const std::vector<const xAOD::PFO*>& shotPFOs,
147 std::vector<const xAOD::CaloCluster*>& goodPi0s) const {
148 std::map<unsigned, const xAOD::CaloCluster*> shotToClusterMap;
149 for (unsigned index = 0; index < shotPFOs.size(); ++index) {
150 const xAOD::PFO* shotPFO = shotPFOs.at(index);
151
152 int seedHashInt = -1;
153 if (!shotPFO->attribute(xAOD::PFODetails::PFOAttributes::tauShots_seedHash, seedHashInt)) {
154 ATH_MSG_WARNING("Couldn't find seed hash. Set it to -1, no cluster will be associated to shot.");
155 }
156 const IdentifierHash seedHash = static_cast<const IdentifierHash>(seedHashInt);
157
158 float weightInCluster = -1.;
159 float weightInPreviousCluster = -1;
160
161 // Loop over custom pi0 clusters, and map shot to the cluster
162 for (const xAOD::CaloCluster* cluster: goodPi0s){
163
164 weightInCluster = -1.;
165
166 const CaloClusterCellLink* cellLinks = cluster->getCellLinks();
167 CaloClusterCellLink::const_iterator cellLink = cellLinks->begin();
168 for (; cellLink != cellLinks->end(); ++cellLink) {
169 const CaloCell* cell = static_cast<const CaloCell*>(*cellLink);
170
171 // Check if seed cell is in cluster.
172 if (cell->caloDDE()->calo_hash() != seedHash) continue;
173
174 weightInCluster = cellLink.weight();
175 // found cell, no need to loop over other cells
176 break;
177 }
178
179 if (weightInCluster < 0) continue;
180
181 // Check if cell was already found in a previous cluster
182 if (weightInPreviousCluster < 0) {
183 // Cell not found in a previous cluster.
184 // Have to check whether cell is shared with other cluster
185 shotToClusterMap[index] = cluster;
186 weightInPreviousCluster = weightInCluster;
187 }
188 else {
189 // Cell has been found in a previous cluster
190 // assign shot to this cluster if it has larger weight for the cell
191 // otherwise the shots keeps assigned to the previous cluster
192 if (weightInCluster > weightInPreviousCluster) {
193 shotToClusterMap[index] = cluster;
194 }
195
196 break;
197 }
198 }
199 }
200
201 return shotToClusterMap;
202}
203
204
205
206std::vector<unsigned> TauPi0ClusterCreator::getShotsMatchedToCluster(const std::vector<const xAOD::PFO*>& shotPFOs,
207 const std::map<unsigned, const xAOD::CaloCluster*>& shotToClusterMap,
208 const xAOD::CaloCluster& pi0Cluster) const {
209 std::vector<unsigned> shotsMatchedToCluster;
210
211 // Loop over the shots, and select those matched to the cluster
212 for (unsigned index = 0; index < shotPFOs.size(); ++index) {
213 auto iterator = shotToClusterMap.find(index);
214 if (iterator == shotToClusterMap.end()) continue;
215 if (iterator->second != &pi0Cluster) continue;
216
217 shotsMatchedToCluster.push_back(index);
218 }
219
220 return shotsMatchedToCluster;
221}
222
223
224
225int TauPi0ClusterCreator::getNPhotons(const std::vector<const xAOD::PFO*>& shotPFOs,
226 const std::vector<unsigned>& shotsInCluster) const {
227 int totalPhotons = 0;
228
229 for (unsigned index = 0; index < shotsInCluster.size(); ++index) {
230 int nPhotons = 0;
231 const xAOD::PFO* shotPFO = shotPFOs.at(shotsInCluster.at(index));
232 if (! shotPFO->attribute(xAOD::PFODetails::PFOAttributes::tauShots_nPhotons, nPhotons)) {
233 ATH_MSG_WARNING("Can't find NHitsInEM1. Set it to 0.");
234 }
235 totalPhotons += nPhotons;
236 }
237
238 return totalPhotons;
239}
240
242 std::vector<int> &nPosECells,
243 float &coreEnergyEM1,
244 std::vector<float> &deltaEtaFirstMom,
245 std::vector<float> &deltaEtaSecondMom) const {
246
247 float totalEnergyEM1 = 0.;
248 std::vector<float> totalEnergy (3, 0.);
249
250 const CaloClusterCellLink* cellLinks = cluster.getCellLinks();
251 CaloClusterCellLink::const_iterator cellLink = cellLinks->begin();
252 for (; cellLink != cellLinks->end(); ++cellLink) {
253 const CaloCell* cell = static_cast<const CaloCell*>(*cellLink);
254
255 // Only consider PS, EM1, and EM2
256 int sampling = cell->caloDDE()->getSampling();
257 int layer = sampling%4;
258 if (layer >= 3) continue;
259
260 // Only consider positive cells
261 float cellEnergy_weighted = cell->e() * cellLink.weight();
262 if (cellEnergy_weighted <= 0) continue;
263
264 ++nPosECells[layer];
265
266 float deltaEta = cell->eta() - cluster.eta();
267
268 deltaEtaFirstMom[layer] += deltaEta * cellEnergy_weighted;
269 deltaEtaSecondMom[layer] += deltaEta * deltaEta * cellEnergy_weighted;
270 totalEnergy[layer] += cellEnergy_weighted;
271
272 // calculate coreEnergyEM1
273 if (sampling != 1 && sampling != 5) continue;
274
275 totalEnergyEM1 += cellEnergy_weighted;
276 float deltaPhi = P4Helpers::deltaPhi(cell->phi(), cluster.phi());
277
278 // Core region: [0.05, 0.05/8]
279 if(std::abs(deltaPhi) > 0.05 || std::abs(deltaEta) > 2 * 0.025/8.) continue;
280 coreEnergyEM1 += cellEnergy_weighted;
281 }
282
283 for (int layer=0; layer < 3; ++layer) {
284 if (totalEnergy[layer] != 0.) {
285 deltaEtaFirstMom[layer]/=std::abs(totalEnergy[layer]);
286 deltaEtaSecondMom[layer]/=std::abs(totalEnergy[layer]);
287 }
288 else {
289 deltaEtaFirstMom[layer]=0.;
290 deltaEtaSecondMom[layer]=0.;
291 }
292 }
293
294 coreEnergyEM1 = (totalEnergyEM1 > 0.) ? coreEnergyEM1/totalEnergyEM1 : 0.;
295
296}
297
299 const xAOD::CaloClusterContainer& pi0ClusterContainer,
300 const xAOD::TauJet& tau,
301 const std::vector<const xAOD::PFO*>& shotPFOs,
302 const std::map<unsigned, const xAOD::CaloCluster*>& shotToClusterMap,
303 xAOD::PFO& neutralPFO) const {
304 // Set the properties of the PFO
305 // -- Default value
306 neutralPFO.setBDTPi0Score(-9999.);
307 neutralPFO.setCharge(0);
308 neutralPFO.setAttribute<int>(xAOD::PFODetails::PFOAttributes::nPi0Proto, -1);
309
310 // -- CENTER_MAG
311 double CENTER_MAG = 0.0;
313 ATH_MSG_WARNING("Couldn't retrieve CENTER_MAG moment. Set it to 0.");
314 }
315 neutralPFO.setCenterMag( static_cast<float>(CENTER_MAG));
316
317 // -- Number of photons
318 std::vector<unsigned> shotsInCluster = getShotsMatchedToCluster(shotPFOs, shotToClusterMap, cluster);
319 int NHitsInEM1 = getNPhotons(shotPFOs, shotsInCluster);
320 neutralPFO.setAttribute<int>(xAOD::PFODetails::PFOAttributes::cellBased_NHitsInEM1, NHitsInEM1);
321
322 // -- Energy at each layer
323 float eEM1 = cluster.eSample(CaloSampling::EMB1) + cluster.eSample(CaloSampling::EME1);
324 neutralPFO.setAttribute<float>(xAOD::PFODetails::PFOAttributes::cellBased_energy_EM1, eEM1);
325
326 float eEM2 = cluster.eSample(CaloSampling::EMB2) + cluster.eSample(CaloSampling::EME2);
327 neutralPFO.setAttribute<float>(xAOD::PFODetails::PFOAttributes::cellBased_energy_EM2, eEM2);
328
329 //-- Get variables
330 std::vector<float> deltaEtaFirstMom (3, 0.);
331 std::vector<float> deltaEtaSecondMom (3, 0.);
332 std::vector<int> nPosECells(3, 0);
333 float EM1CoreFrac = 0.;
334
335 getClusterVariables(cluster, nPosECells, EM1CoreFrac, deltaEtaFirstMom, deltaEtaSecondMom);
336
337 // -- Number of positive cells in each layer
338 neutralPFO.setAttribute<int>(xAOD::PFODetails::PFOAttributes::cellBased_NPosECells_PS, nPosECells.at(0));
339 neutralPFO.setAttribute<int>(xAOD::PFODetails::PFOAttributes::cellBased_NPosECells_EM1, nPosECells.at(1));
340 neutralPFO.setAttribute<int>(xAOD::PFODetails::PFOAttributes::cellBased_NPosECells_EM2, nPosECells.at(2));
341
342 // -- Core Fraction of the energy in EM1
343 neutralPFO.setAttribute<float>(xAOD::PFODetails::PFOAttributes::cellBased_EM1CoreFrac, EM1CoreFrac);
344
345 // -- First moment of deltaEta(cluster, cell) in EM1 and EM2
346 neutralPFO.setAttribute<float>(xAOD::PFODetails::PFOAttributes::cellBased_firstEtaWRTClusterPosition_EM1, deltaEtaFirstMom.at(1));
347 neutralPFO.setAttribute<float>(xAOD::PFODetails::PFOAttributes::cellBased_firstEtaWRTClusterPosition_EM2, deltaEtaFirstMom.at(2));
348
349 // -- Second moment of deltaEta(cluster, cell) in EM1 and EM2
350 neutralPFO.setAttribute<float>(xAOD::PFODetails::PFOAttributes::cellBased_secondEtaWRTClusterPosition_EM1, deltaEtaSecondMom.at(1));
351 neutralPFO.setAttribute<float>(xAOD::PFODetails::PFOAttributes::cellBased_secondEtaWRTClusterPosition_EM2, deltaEtaSecondMom.at(2));
352
353 // -- Retrieve cluster moments
354 using Moment = xAOD::CaloCluster::MomentType;
355 using Attribute = xAOD::PFODetails::PFOAttributes;
356 const std::array< std::pair<Moment, Attribute>, 12> momentAttributePairs {{
357 {Moment::FIRST_ETA, Attribute::cellBased_FIRST_ETA},
358 {Moment::SECOND_R, Attribute::cellBased_SECOND_R},
359 {Moment::SECOND_LAMBDA, Attribute::cellBased_SECOND_LAMBDA},
360 {Moment::DELTA_PHI, Attribute::cellBased_DELTA_PHI},
361 {Moment::DELTA_THETA, Attribute::cellBased_DELTA_THETA},
362 {Moment::CENTER_LAMBDA, Attribute::cellBased_CENTER_LAMBDA},
363 {Moment::LATERAL, Attribute::cellBased_LATERAL},
364 {Moment::LONGITUDINAL, Attribute::cellBased_LONGITUDINAL},
365 {Moment::ENG_FRAC_EM, Attribute::cellBased_ENG_FRAC_EM},
366 {Moment::ENG_FRAC_MAX, Attribute::cellBased_ENG_FRAC_MAX},
367 {Moment::ENG_FRAC_CORE, Attribute::cellBased_ENG_FRAC_CORE},
368 {Moment::SECOND_ENG_DENS, Attribute::cellBased_SECOND_ENG_DENS}
369 }};
370
371 for (const auto& [moment, attribute] : momentAttributePairs) {
372 double value = 0.0;
373 if (! cluster.retrieveMoment(moment, value)) {
374 ATH_MSG_WARNING("Cound not retrieve " << moment);
375 }
376 neutralPFO.setAttribute(attribute, static_cast<float>(value));
377 }
378
379 // -- Element link to the cluster
381 clusElementLink.toContainedElement(pi0ClusterContainer, &cluster);
382 neutralPFO.setClusterLink( clusElementLink );
383
384 // -- Element link to the shots
385 std::vector<ElementLink<xAOD::IParticleContainer>> shotlinks;
386 for (unsigned index = 0; index < shotsInCluster.size(); ++index) {
387 auto& shotPFOElementLink = tau.shotPFOLinks().at(shotsInCluster.at(index));
388 ElementLink<xAOD::IParticleContainer> shotElementLink( shotPFOElementLink.dataID(), shotPFOElementLink.index() );
389 if (!shotElementLink.isValid()) {
390 ATH_MSG_WARNING("Created an invalid element link to xAOD::PFO");
391 }
392 shotlinks.push_back(shotElementLink);
393 }
394 if(!neutralPFO.setAssociatedParticleLinks( xAOD::PFODetails::TauShot,shotlinks)) {
395 ATH_MSG_WARNING("Couldn't add shot links to neutral PFO!");
396 }
397
398 return StatusCode::SUCCESS;
399}
400
401
402
404 double clusterEnergyHad,
405 xAOD::PFO& hadronicPFO) const {
406 double clusterPtHad = clusterEnergyHad/std::cosh(cluster.eta());
407 hadronicPFO.setP4(clusterPtHad, cluster.eta(), cluster.phi(), 0.);
408
409 return StatusCode::SUCCESS;
410}
411
412#endif
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_WARNING(x,...)
static const Attributes_t empty
Data object for each calorimeter readout cell.
Definition CaloCell.h:57
value_type push_back(value_type pElem)
Add an element to the end of the collection.
This is a "hash" representation of an Identifier.
Gaudi::Property< double > m_maxDeltaRJetClust
void getClusterVariables(const xAOD::CaloCluster &cluster, std::vector< int > &nPosECells, float &coreEnergyEM1, std::vector< float > &deltaEtaFirstMom, std::vector< float > &secondEtaWRTClusterPositionInLayer) const
eta moment in PS, EM1 and EM2 w.r.t cluster eta
StatusCode configureNeutralPFO(const xAOD::CaloCluster &cluster, const xAOD::CaloClusterContainer &pi0ClusterContainer, const xAOD::TauJet &tau, const std::vector< const xAOD::PFO * > &shotPFOs, const std::map< unsigned, const xAOD::CaloCluster * > &shotsInCluster, xAOD::PFO &neutralPFO) const
Configure the neutral PFO.
Gaudi::Property< double > m_clusterEtCut
virtual StatusCode executePi0ClusterCreator(xAOD::TauJet &pTau, xAOD::PFOContainer &neutralPFOContainer, xAOD::PFOContainer &hadronicClusterPFOContainer, const xAOD::CaloClusterContainer &pi0CaloClusContainer) const override
Gaudi::Property< double > m_recoFromAOD
StatusCode configureHadronicPFO(const xAOD::CaloVertexedTopoCluster &cluster, double clusterEnergyHad, xAOD::PFO &hadronicPFO) const
Configure the haronic PFO.
Gaudi::Property< double > m_maxDeltaRNeutral
std::map< unsigned, const xAOD::CaloCluster * > getShotToClusterMap(const std::vector< const xAOD::PFO * > &shotVector, std::vector< const xAOD::CaloCluster * > &goodpi0Vecor) const
std::vector< unsigned > getShotsMatchedToCluster(const std::vector< const xAOD::PFO * > &shotVector, const std::map< unsigned, const xAOD::CaloCluster * > &clusterToShotMap, const xAOD::CaloCluster &pi0Cluster) const
int getNPhotons(const std::vector< const xAOD::PFO * > &shotVector, const std::vector< unsigned > &shotsInCluster) const
TauPi0ClusterCreator(const std::string &name)
TauRecToolBase(const std::string &name)
bool retrieveMoment(MomentType type, double &value) const
Retrieve individual moment.
const CaloClusterCellLink * getCellLinks() const
Get a pointer to the CaloClusterCellLink object (const version).
virtual double eta() const
The pseudorapidity ( ) of the particle.
float eSample(const CaloSample sampling) const
virtual double phi() const
The azimuthal angle ( ) of the particle.
MomentType
Enums to identify different moments.
@ CENTER_MAG
Cluster Centroid ( ).
virtual FourMom_t p4() const final
The full 4-momentum of the particle.
virtual double phi() const final
The azimuthal angle ( ) of the particle.
virtual double eta() const final
The pseudorapidity ( ) of the particle.
Evaluate cluster kinematics with a different vertex / signal state.
bool attribute(PFODetails::PFOAttributes AttributeType, T &anAttribute) const
get a PFO Variable via enum
void setAttribute(PFODetails::PFOAttributes AttributeType, const T &anAttribute)
Set a PFO Variable via enum - overwrite is allowed.
bool setAssociatedParticleLinks(PFODetails::PFOParticleType ParticleType, const std::vector< ElementLink< IParticleContainer > > &theParticles)
Set a vector of PFO constituent particle types via enum - overwrite is allowed.
Definition PFO_v1.cxx:600
bool setClusterLink(const ElementLink< xAOD::CaloClusterContainer > &theCluster)
Set a cluster constituent - does NOT append to existing container.
Definition PFO_v1.cxx:549
void setCenterMag(float CenterMag)
set CenterMag moment needed for vertex correction
void setP4(const FourMom_t &vec)
set the 4-vec
Definition PFO_v1.cxx:107
void setBDTPi0Score(float BDTPi0Score)
set BDT Score used to classify clusters as Pi0 like or not
void setCharge(float charge)
set charge of PFO
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
Definition P4Helpers.h:34
Definition index.py:1
bool doPi0andShots(const xAOD::TauJet &tau)
Determines whether pi0s and shots should be built for a tau candidate.
TLorentzVector getTauAxis(const xAOD::TauJet &tau, bool doVertexCorrection=true)
Return the four momentum of the tau axis The tau axis is widely used to select clusters and cells in ...
PFO_v1 PFO
Definition of the current "pfo version".
Definition PFO.h:17
PFOContainer_v1 PFOContainer
Definition of the current "pfo container version".
CaloCluster_v1 CaloCluster
Define the latest version of the calorimeter cluster class.
Vertex_v1 Vertex
Define the latest version of the vertex class.
TauJet_v3 TauJet
Definition of the current "tau version".
Definition TauJet.h:17
CaloClusterContainer_v1 CaloClusterContainer
Define the latest version of the calorimeter cluster container.