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 "<< m_jetContainerKey.key());
175 return StatusCode::FAILURE;
176 }
177
180 const std::string bHadronsStr{"ConeExclBHadronsFinal"};
181 const std::string cHadronsStr{"ConeExclCHadronsFinal"};
182 for (const xAOD::Jet* jet : *jets) {
183 std::vector<const xAOD::TruthParticle*> bHadrons;
184 jet->getAssociatedObjects<xAOD::TruthParticle>(bHadronsStr, bHadrons);
185
186 std::vector<const xAOD::TruthParticle*> cHadrons;
187 jet->getAssociatedObjects<xAOD::TruthParticle>(cHadronsStr, cHadrons);
188 //
189 decDecay(*jet) = -999;
190 decTau(*jet) = -999;
191
192 if (bHadrons.empty() && cHadrons.empty()) continue;
193
194 LeptonCounter allLep = countLeptons(bHadrons, *jet);
195 LeptonCounter cLep = countLeptons(cHadrons, *jet);
196
197 int el_fromB = allLep.el_fromHad - cLep.el_fromHad;
198 int mu_fromB = allLep.mu_fromHad - cLep.mu_fromHad;
199 int tau_fromB = allLep.tau_fromHad - cLep.tau_fromHad;
200 int tauel_fromB = allLep.el_fromTau - cLep.el_fromTau;
201 int taumu_fromB = allLep.mu_fromTau - cLep.mu_fromTau;
202
203 int el_fromC = cLep.el_fromHad;
204 int mu_fromC = cLep.mu_fromHad;
205 int tau_fromC = cLep.tau_fromHad;
206 int tauel_fromC = cLep.el_fromTau;
207 int taumu_fromC = cLep.mu_fromTau;
208
209 std::vector<int> leps_in_decays;
210 if (tau_fromB > 0) leps_in_decays.push_back(15);
211 if (tau_fromC > 0) leps_in_decays.push_back(15);
212 if (mu_fromB > 0) leps_in_decays.push_back(13);
213 if (mu_fromC > 0) leps_in_decays.push_back(13);
214 if (el_fromB > 0) leps_in_decays.push_back(11);
215 if (el_fromC > 0) leps_in_decays.push_back(11);
216
217 std::vector<int> tauleps_in_decays;
218 tauleps_in_decays.push_back(tauel_fromB > 0 ? 11 : 0);
219 tauleps_in_decays.push_back(tauel_fromC > 0 ? 11 : 0);
220 tauleps_in_decays.push_back(taumu_fromB > 0 ? 13 : 0);
221 tauleps_in_decays.push_back(taumu_fromC > 0 ? 13 : 0);
222
223 decDecay(*jet) = getDecayLabel(leps_in_decays);
224 decTau(*jet) = getTauLabel(tauleps_in_decays, leps_in_decays);
225 }
226
227 return StatusCode::SUCCESS;
228 }
229
230} // 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.