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