ATLAS Offline Software
Loading...
Searching...
No Matches
JetLeptonDecayLabelAlg.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// Ported from TDD's JetLeptonDecayLabelDecorator + TruthTools.
6// Computes LeptonDecayLabel and TauDecayLabel from ghost-associated
7// b/c hadron decay chains, without needing TruthParticles in the DAOD.
8
10
12
16
17#include <algorithm>
18#include <cmath>
19#include <unordered_set>
20#include <vector>
21#include <stdexcept>
22#include <limits>
23
24
25namespace {
26
27 // --- helper structs / functions ported from TDD ---
28
29 struct LeptonCounter {
30 int el_fromHad = 0;
31 int mu_fromHad = 0;
32 int tau_fromHad = 0;
33 int el_fromTau = 0;
34 int mu_fromTau = 0;
35 };
36
37 // DFS walk of decay products, skipping simulation particles and
38 // stopping at c/b hadron boundaries. (TruthTools::getAllChildren)
39 // Returns false if a cycle is detected.
40 bool getAllChildren(const xAOD::TruthParticle* particle,
41 std::vector<const xAOD::TruthParticle*>& out,
42 std::unordered_set<const xAOD::TruthParticle*>& visited) {
43 if (!particle->hasDecayVtx()) return true;
44 if (!visited.insert(particle).second) return false;
45 const xAOD::TruthVertex* vtx = particle->decayVtx();
46 for (unsigned i = 0; i < vtx->nOutgoingParticles(); ++i) {
47 const xAOD::TruthParticle* child = vtx->outgoingParticle(i);
48 if (!child) continue;
49 if (child->isSimulationParticle()) continue;
50 if ((child->status() == 1 || child->status() == 2)
51 && !child->isCharmHadron() && !child->isBottomHadron()) {
52 out.push_back(child);
53 }
54 if (!getAllChildren(child, out, visited)) return false;
55 }
56 return true;
57 }
58
59 // Sort hadron indices by deltaR to jet. (TruthTools::getDRSortedIndices)
60 std::vector<int> getDRSortedIndices(
61 const std::vector<const xAOD::TruthParticle*>& hadrons,
62 const xAOD::Jet& jet) {
63 std::vector<float> dRs;
64 dRs.reserve(hadrons.size());
65 for (const auto* h : hadrons) {
66 dRs.push_back(jet.p4().DeltaR(h->p4()));
67 }
68 std::vector<int> idx(dRs.size());
69 std::iota(idx.begin(), idx.end(), 0);
70 std::sort(idx.begin(), idx.end(),
71 [&](int a, int b) { return dRs[a] < dRs[b]; });
72 return idx;
73 }
74
75 // Count electrons, muons, taus (and tau sub-products) among hadron
76 // decay children. (JetLeptonDecayLabelDecorator::countLeptons)
77 LeptonCounter countLeptons(
78 const std::vector<const xAOD::TruthParticle*>& hadrons,
79 const xAOD::Jet& jet) {
80 LeptonCounter lc;
81 if (hadrons.empty()) return lc;
82 std::vector<int> indices = getDRSortedIndices(hadrons, jet);
83 for (unsigned iHad = 0; iHad < hadrons.size(); ++iHad) {
84 const xAOD::TruthParticle* had = hadrons.at(indices[iHad]);
85 std::vector<const xAOD::TruthParticle*> children;
86 std::unordered_set<const xAOD::TruthParticle*> visited;
87 if (!getAllChildren(had, children, visited)) {
88 // Cycle detected in truth record — skip this hadron to avoid
89 // infinite recursion. Should not happen in well-formed MC.
90 continue;
91 }
92 for (unsigned i = 0; i < children.size(); ++i) {
93 int absId = std::abs(children[i]->pdgId());
94 if (absId == 11) lc.el_fromHad += 1;
95 if (absId == 13) lc.mu_fromHad += 1;
96 if (absId == 15) {
97 lc.tau_fromHad += 1;
98 if (children.size() > i + 3) {
99 for (unsigned j = 1; j <= 3; ++j) {
100 int tauChildId = std::abs(children[i + j]->pdgId());
101 if (tauChildId == 11) lc.el_fromTau += 1;
102 if (tauChildId == 13) lc.mu_fromTau += 1;
103 }
104 }
105 }
106 }
107 }
108 return lc;
109 }
110
111 // Encode lepton species into a decimal label.
112 // (JetLeptonDecayLabelDecorator::getDecayLabel)
113 int getDecayLabel(const std::vector<int>& leps) {
114 //max int value is 2147483647
115 //log10(2147483647) ~ 9.3
116 //To avoid overflow of 'factor' we limit the leps vector size
117 if (leps.size()>9) throw std::overflow_error("getDecayLabel: too many leptons");
118 int factor = 1;
119 int label = 0;
120 for (int pdg : leps) {
121 if (pdg == 15) label += factor * 3;
122 if (pdg == 13) label += factor * 2;
123 if (pdg == 11) label += factor * 1;
124 factor *= 10; //possible wrap around to negative value if factor is too big
125 }
126 return label;
127 }
128
129 // Encode tau sub-decay leptons.
130 // (JetLeptonDecayLabelDecorator::getTauLabel)
131 int getTauLabel(const std::vector<int>& tauLeps,
132 const std::vector<int>& leps) {
133 if (std::find(leps.begin(), leps.end(), 15) == leps.end()) {
134 return -999;
135 }
136 int maxFactor = std::numeric_limits<int>::max() / 10;
137 int factor = 1;
138 int label = 0;
139 for (int pdg : tauLeps) {
140 if (pdg == 11) {
141 label += factor;
142 if (factor>maxFactor) throw std::overflow_error("getTauLabel:(pdg = 11) factor overflow");
143 factor *= 10;
144 }
145 if (pdg == 13) {
146 label += 2 * factor;
147 if (factor>maxFactor) throw std::overflow_error("getTauLabel:(pdg = 13) factor overflow");
148 factor *= 10;
149 }
150 }
151 return label;
152 }
153
154} // anonymous namespace
155
156
157namespace FlavorTagDiscriminants {
158
160 const std::string& name, ISvcLocator* loc)
161 : AthReentrantAlgorithm(name, loc) {}
162
164 ATH_MSG_INFO("Initializing " << name() << "...");
165 ATH_CHECK(m_jetContainerKey.initialize());
166 ATH_CHECK(m_dec_leptonDecayLabel.initialize());
167 ATH_CHECK(m_dec_tauDecayLabel.initialize());
168 return StatusCode::SUCCESS;
169 }
170
171 StatusCode JetLeptonDecayLabelAlg::execute(const EventContext& ctx) const {
173 if (!jets.isValid()) {
174 ATH_MSG_ERROR("Could not retrieve jet container "
175 << m_jetContainerKey.key());
176 return StatusCode::FAILURE;
177 }
178
183
184 for (const xAOD::Jet* jet : *jets) {
185
186 std::vector<const xAOD::TruthParticle*> bHadrons;
187 jet->getAssociatedObjects<xAOD::TruthParticle>(
188 "ConeExclBHadronsFinal", bHadrons);
189
190 std::vector<const xAOD::TruthParticle*> cHadrons;
191 jet->getAssociatedObjects<xAOD::TruthParticle>(
192 "ConeExclCHadronsFinal", cHadrons);
193
194 decDecay(*jet) = -999;
195 decTau(*jet) = -999;
196
197 if (bHadrons.empty() && cHadrons.empty()) continue;
198
199 LeptonCounter allLep = countLeptons(bHadrons, *jet);
200 LeptonCounter cLep = countLeptons(cHadrons, *jet);
201
202 int el_fromB = allLep.el_fromHad - cLep.el_fromHad;
203 int mu_fromB = allLep.mu_fromHad - cLep.mu_fromHad;
204 int tau_fromB = allLep.tau_fromHad - cLep.tau_fromHad;
205 int tauel_fromB = allLep.el_fromTau - cLep.el_fromTau;
206 int taumu_fromB = allLep.mu_fromTau - cLep.mu_fromTau;
207
208 int el_fromC = cLep.el_fromHad;
209 int mu_fromC = cLep.mu_fromHad;
210 int tau_fromC = cLep.tau_fromHad;
211 int tauel_fromC = cLep.el_fromTau;
212 int taumu_fromC = cLep.mu_fromTau;
213
214 std::vector<int> leps_in_decays;
215 if (tau_fromB > 0) leps_in_decays.push_back(15);
216 if (tau_fromC > 0) leps_in_decays.push_back(15);
217 if (mu_fromB > 0) leps_in_decays.push_back(13);
218 if (mu_fromC > 0) leps_in_decays.push_back(13);
219 if (el_fromB > 0) leps_in_decays.push_back(11);
220 if (el_fromC > 0) leps_in_decays.push_back(11);
221
222 std::vector<int> tauleps_in_decays;
223 tauleps_in_decays.push_back(tauel_fromB > 0 ? 11 : 0);
224 tauleps_in_decays.push_back(tauel_fromC > 0 ? 11 : 0);
225 tauleps_in_decays.push_back(taumu_fromB > 0 ? 13 : 0);
226 tauleps_in_decays.push_back(taumu_fromC > 0 ? 13 : 0);
227
228 decDecay(*jet) = getDecayLabel(leps_in_decays);
229 decTau(*jet) = getTauLabel(tauleps_in_decays, leps_in_decays);
230 }
231
232 return StatusCode::SUCCESS;
233 }
234
235} // namespace FlavorTagDiscriminants
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
static Double_t a
Handle class for adding a decoration to an object.
Header file for AthHistogramAlgorithm.
An algorithm that can be simultaneously executed in multiple threads.
JetLeptonDecayLabelAlg(const std::string &name, ISvcLocator *pSvcLocator)
virtual StatusCode execute(const EventContext &) const override
SG::ReadHandleKey< xAOD::JetContainer > m_jetContainerKey
SG::WriteDecorHandleKey< xAOD::JetContainer > m_dec_leptonDecayLabel
SG::WriteDecorHandleKey< xAOD::JetContainer > m_dec_tauDecayLabel
Handle class for adding a decoration to an object.
bool isSimulationParticle() const
Check if this particle was produced during the simulation.
int status() const
Status code.
bool isBottomHadron() const
Determine if the PID is that of a b-hadron.
bool isCharmHadron() const
Determine if the PID is that of a c-hadron.
const TruthParticle_v1 * outgoingParticle(size_t index) const
Get one of the outgoing particles.
size_t nOutgoingParticles() const
Get the number of outgoing particles.
std::string label(const std::string &format, int i)
Definition label.h:19
float j(const xAOD::IParticle &, const xAOD::TrackMeasurementValidation &hit, const Eigen::Matrix3d &jab_inv)
constexpr ParticleHypothesis particle[PARTICLEHYPOTHESES]
the array of masses
std::pair< long int, long int > indices
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
Jet_v1 Jet
Definition of the current "jet version".
TruthVertex_v1 TruthVertex
Typedef to implementation.
Definition TruthVertex.h:15
TruthParticle_v1 TruthParticle
Typedef to implementation.