ATLAS Offline Software
Loading...
Searching...
No Matches
CalcZPartonHistory.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
7
11
14
15#include <array>
16
17namespace CP {
18using ROOT::Math::PtEtaPhiMVector;
19
20bool CalcPartonHistory::getZ(const std::string& str_lep1,
21 const std::string& str_lep2, PtEtaPhiMVector& p1,
22 int& pdgId1, PtEtaPhiMVector& p2, int& pdgId2) {
23 // Off-shell / non-resonant Z reconstruction from two same-flavour
24 // opposite-sign lepton candidates. Uses MCTruthClassifier decorations
25 // (classifierParticleOrigin, classifierParticleType) to restrict to
26 // prompt, isolated leptons from a Z decay, rejecting fakes and non-prompt
27 // background. Only electrons and muons are considered (not taus); for
28 // Z→ττ use getZFromTaus instead. Returns the first pair that passes all
29 // conditions (same convention as getW).
30 static const SG::ConstAccessor<unsigned int> acc_classifierParticleOrigin(
31 "classifierParticleOrigin");
32 static const SG::ConstAccessor<unsigned int> acc_classifierParticleType(
33 "classifierParticleType");
34 std::vector<const xAOD::TruthParticle*> Z_offshell_decay1_candidates;
35 std::vector<const xAOD::TruthParticle*> Z_offshell_decay2_candidates;
36 // str_lep1/2 are full m_particleMap keys (caller passes m_prefix + "_" + ...)
37 bool has_candidates =
38 (RetrieveParticleInfo(str_lep1, Z_offshell_decay1_candidates) &&
39 RetrieveParticleInfo(str_lep2, Z_offshell_decay2_candidates));
40 if (has_candidates) {
41 for (const auto* pDecay1 : Z_offshell_decay1_candidates) {
42 for (const auto* pDecay2 : Z_offshell_decay2_candidates) {
43 // Condition 1: Opposite charge — pdgId product must be negative
44 // (e.g. e-=11, e+=−11 → product −121 < 0).
45 if ((pDecay1->pdgId() * pDecay2->pdgId()) > 0)
46 continue;
47 // Condition 2: Same flavour — both leptons must have the same |pdgId|
48 // (e.g. both electrons or both muons).
49 if (pDecay1->absPdgId() != pDecay2->absPdgId())
50 continue;
51 // Leptons without MCTruthClassifier decorations cannot be classified
52 // and are skipped.
53 if (!acc_classifierParticleOrigin.isAvailable(*pDecay1) ||
54 !acc_classifierParticleOrigin.isAvailable(*pDecay2) ||
55 !acc_classifierParticleType.isAvailable(*pDecay1) ||
56 !acc_classifierParticleType.isAvailable(*pDecay2))
57 continue;
58 const unsigned int o1 = acc_classifierParticleOrigin(*pDecay1);
59 const unsigned int o2 = acc_classifierParticleOrigin(*pDecay2);
60 const unsigned int t1 = acc_classifierParticleType(*pDecay1);
61 const unsigned int t2 = acc_classifierParticleType(*pDecay2);
62 // Condition 3: Origin ZBoson from MCTruthClassifier. Ensures both
63 // leptons are truth-matched to a Z decay and not to backgrounds such
64 // as photon conversions or heavy-flavour semileptonic decays.
67 continue;
68 // Condition 4: Type IsoElectron or IsoMuon from MCTruthClassifier.
69 // Selects prompt isolated leptons, rejecting non-isolated or
70 // non-prompt contributions.
75 continue;
76
77 p1 = GetPtEtaPhiMfromTruth(pDecay1);
78 pdgId1 = pDecay1->pdgId();
79 p2 = GetPtEtaPhiMfromTruth(pDecay2);
80 pdgId2 = pDecay2->pdgId();
81 return true;
82 }
83 }
84 }
85 return false;
86}
87
89 const std::string& fsr) {
90 // taus[0] is the tau- ("l"), taus[1] the tau+ ("lbar"). Their decay
91 // products decay1..3 are the charged lepton followed by the two neutrinos.
92 // All keys are full m_particleMap keys once the prefix is prepended.
93 static constexpr std::array<const char*, 2> tauKeys{{"MC_l_", "MC_lbar_"}};
94 static constexpr std::array<std::array<const char*, 3>, 2> tauDecayKeys{
95 {{{"MC_l_l_", "MC_l_nubar_", "MC_l_nu_"}},
96 {{"MC_lbar_lbar_", "MC_lbar_nu_", "MC_lbar_nubar_"}}}};
97
98 ZTauTauDecay result;
99 for (std::size_t i = 0; i < tauKeys.size(); ++i) {
100 std::vector<const xAOD::TruthParticle*> tau_candidates;
101 if (RetrieveParticleInfo(m_prefix + "_" + tauKeys[i] + fsr,
102 tau_candidates)) {
103 result.taus[i] = {GetPtEtaPhiMfromTruth(tau_candidates.at(0)),
104 tau_candidates.at(0)->pdgId(), true};
105 }
106 // The tau decay products are only used if all three are found.
107 std::array<std::vector<const xAOD::TruthParticle*>, 3> decay_candidates;
108 bool has_decay_candidates = true;
109 for (std::size_t j = 0; j < decay_candidates.size(); ++j) {
110 has_decay_candidates =
111 has_decay_candidates &&
112 RetrieveParticleInfo(m_prefix + "_" + tauDecayKeys[i][j] + fsr,
113 decay_candidates[j]);
114 }
115 if (has_decay_candidates) {
116 for (std::size_t j = 0; j < decay_candidates.size(); ++j) {
117 result.tauDecays[i][j] = {
118 GetPtEtaPhiMfromTruth(decay_candidates[j].at(0)),
119 decay_candidates[j].at(0)->pdgId(), true};
120 }
121 }
122 }
123 return result;
124}
125
126// For filling Z->ee,mumu
127void CalcPartonHistory::setZ(const std::string& fsr, int nZs) {
128 PtEtaPhiMVector Z, Z_decay1, Z_decay2;
129 int Z_decay1_pdgId, Z_decay2_pdgId;
130 m_dec.decorateCustom("MC_Z_IsOnShell", 0);
131 // getZ receives full m_particleMap keys; m_dec.decorate* receives bare names.
132 bool has_Z =
133 getZ(m_prefix + "_" + "MC_l_" + fsr, m_prefix + "_" + "MC_lbar_" + fsr,
134 Z_decay1, Z_decay1_pdgId, Z_decay2, Z_decay2_pdgId);
135 if (nZs == 1) {
136 if (has_Z) {
137 Z = Z_decay1 + Z_decay2;
138 m_dec.decorateParticle("MC_Z_" + fsr, Z, 23);
139 m_dec.decorateParticle("MC_Zdecay1_" + fsr, Z_decay1, Z_decay1_pdgId);
140 m_dec.decorateParticle("MC_Zdecay2_" + fsr, Z_decay2, Z_decay2_pdgId);
141 }
142 } else
143 ANA_MSG_ERROR("Reconstruction of multiple Zs is not supported yet!");
144}
145
146// For filling Z->tautau
147void CalcPartonHistory::setZtautau(const std::string& fsr, int nZs) {
148 const ZTauTauDecay decay = getZFromTaus(fsr);
149 const bool has_Z = decay.taus[0].found && decay.taus[1].found;
150 if (nZs == 1) {
151 if (has_Z) {
152 m_dec.decorateParticle("MC_Z_" + fsr,
153 decay.taus[0].p4 + decay.taus[1].p4, 23);
154 for (std::size_t i = 0; i < decay.taus.size(); ++i) {
155 const std::string zDecay = "MC_Zdecay" + std::to_string(i + 1);
156 m_dec.decorateParticle(zDecay + "_" + fsr, decay.taus[i].p4,
157 decay.taus[i].pdgId);
158 for (std::size_t j = 0; j < decay.tauDecays[i].size(); ++j) {
159 const std::string name =
160 zDecay + "_decay" + std::to_string(j + 1) + "_" + fsr;
161 const ZTauTauProduct& product = decay.tauDecays[i][j];
162 // Tau decay products that were not found (e.g. hadronic tau
163 // decays) get the sentinel defaults rather than a zero vector.
164 if (product.found)
165 m_dec.decorateParticle(name, product.p4, product.pdgId);
166 else
167 m_dec.decorateDefault(name);
168 }
169 }
170 }
171 } else
172 ANA_MSG_ERROR("Reconstruction of multiple Zs is not supported yet!");
173}
174
175void CalcPartonHistory::FillZPartonHistory(const std::string& parent, int nZs,
176 const std::string& mode) {
177 std::string parentstring = parent.empty() ? "" : "_from_" + parent;
178 // mapPrefix: full m_particleMap key base (with m_prefix) — used only for
179 // ExistsInMap.
180 std::string mapPrefix =
181 m_prefix + "_" + "MC_" + (parent.empty() ? "Z" : parent + "_Z");
182 // decPrefix: bare suffix passed to FillGenericPartonHistory as
183 // retrievalstring; FillGenericPartonHistory prepends m_prefix + "_"
184 // internally.
185 std::string decPrefix = "MC_" + (parent.empty() ? "Z" : parent + "_Z");
186
187 if (mode == "resonant") {
188 if (nZs == 1) {
189 if (ExistsInMap(mapPrefix + "_beforeFSR")) {
190 m_dec.decorateCustom("MC_Z_IsOnShell", 1);
191 FillGenericPartonHistory(decPrefix + "_beforeFSR",
192 "MC_Z" + parentstring + "_beforeFSR", 0);
193 FillGenericPartonHistory(decPrefix + "Decay1_beforeFSR",
194 "MC_Zdecay1" + parentstring + "_beforeFSR", 0);
195 FillGenericPartonHistory(decPrefix + "Decay2_beforeFSR",
196 "MC_Zdecay2" + parentstring + "_beforeFSR", 0);
197 FillGenericPartonHistory(decPrefix + "_afterFSR",
198 "MC_Z" + parentstring + "_afterFSR", 0);
199 FillGenericPartonHistory(decPrefix + "Decay1_afterFSR",
200 "MC_Zdecay1" + parentstring + "_afterFSR", 0);
201 FillGenericPartonHistory(decPrefix + "Decay2_afterFSR",
202 "MC_Zdecay2" + parentstring + "_afterFSR", 0);
203 } else {
204 setZ("beforeFSR", nZs);
205 setZ("afterFSR", nZs);
206 }
207 } else {
208 for (int idx = 0; idx < nZs; idx++) {
209 const std::string idxStr = std::to_string(idx + 1);
210 m_dec.decorateCustom("MC_Z" + idxStr + "_IsOnShell", 1);
211 FillGenericPartonHistory(decPrefix + "_beforeFSR",
212 "MC_Z" + idxStr + parentstring + "_beforeFSR",
213 idx);
215 decPrefix + "Decay1_beforeFSR",
216 "MC_Z" + idxStr + "decay1" + parentstring + "_beforeFSR", idx);
218 decPrefix + "Decay2_beforeFSR",
219 "MC_Z" + idxStr + "decay2" + parentstring + "_beforeFSR", idx);
220 FillGenericPartonHistory(decPrefix + "_afterFSR",
221 "MC_Z" + idxStr + parentstring + "_afterFSR",
222 idx);
224 decPrefix + "Decay1_afterFSR",
225 "MC_Z" + idxStr + "decay1" + parentstring + "_afterFSR", idx);
227 decPrefix + "Decay2_afterFSR",
228 "MC_Z" + idxStr + "decay2" + parentstring + "_afterFSR", idx);
229 }
230 }
231 } else if (mode == "non_resonant") {
232 setZ("beforeFSR", nZs);
233 setZ("afterFSR", nZs);
234 }
235}
236
237void CalcPartonHistory::FillZtautauPartonHistory(const std::string& parent,
238 int nZs,
239 const std::string& mode) {
240 std::string parentstring = parent.empty() ? "" : "_from_" + parent;
241 // decPrefix: bare suffix for FillGenericPartonHistory retrieval strings.
242 std::string decPrefix = "MC_" + (parent.empty() ? "Z" : parent + "_Z");
243
244 FillZPartonHistory(parent, nZs, mode);
245 m_dec.decorateCustom("MC_Z_IsOnShell",
246 0); // default; overwritten to 1 below if resonant
247 if (mode == "resonant") {
248 if (nZs == 1) {
249 m_dec.decorateCustom("MC_Z_IsOnShell", 1);
251 decPrefix + "Decay1_Decay1_beforeFSR",
252 "MC_Zdecay1_decay1" + parentstring + "_beforeFSR", 0);
254 decPrefix + "Decay1_Decay2_beforeFSR",
255 "MC_Zdecay1_decay2" + parentstring + "_beforeFSR", 0);
257 decPrefix + "Decay1_Decay3_beforeFSR",
258 "MC_Zdecay1_decay3" + parentstring + "_beforeFSR", 0);
259 FillGenericPartonHistory(decPrefix + "Decay1_Decay1_afterFSR",
260 "MC_Zdecay1_decay1" + parentstring + "_afterFSR",
261 0);
262 FillGenericPartonHistory(decPrefix + "Decay1_Decay2_afterFSR",
263 "MC_Zdecay1_decay2" + parentstring + "_afterFSR",
264 0);
265 FillGenericPartonHistory(decPrefix + "Decay1_Decay3_afterFSR",
266 "MC_Zdecay1_decay3" + parentstring + "_afterFSR",
267 0);
268 } else {
269 for (int idx = 0; idx < nZs; idx++) {
270 const std::string idxStr = std::to_string(idx + 1);
271 m_dec.decorateCustom("MC_Z" + idxStr + "_IsOnShell", 1);
273 decPrefix + "Decay1_Decay1_beforeFSR",
274 "MC_Z" + idxStr + "decay1_decay1" + parentstring + "_beforeFSR",
275 idx);
277 decPrefix + "Decay1_Decay2_beforeFSR",
278 "MC_Z" + idxStr + "decay1_decay2" + parentstring + "_beforeFSR",
279 idx);
281 decPrefix + "Decay1_Decay3_beforeFSR",
282 "MC_Z" + idxStr + "decay1_decay3" + parentstring + "_beforeFSR",
283 idx);
285 decPrefix + "Decay1_Decay1_afterFSR",
286 "MC_Z" + idxStr + "decay1_decay1" + parentstring + "_afterFSR",
287 idx);
289 decPrefix + "Decay1_Decay2_afterFSR",
290 "MC_Z" + idxStr + "decay1_decay2" + parentstring + "_afterFSR",
291 idx);
293 decPrefix + "Decay1_Decay3_afterFSR",
294 "MC_Z" + idxStr + "decay1_decay3" + parentstring + "_afterFSR",
295 idx);
296 }
297 }
298 } else if (mode == "non_resonant") {
299 setZtautau("beforeFSR", nZs);
300 setZtautau("afterFSR", nZs);
301 }
302}
303
304} // namespace CP
Helper class to provide constant type-safe access to aux data.
#define ANA_MSG_ERROR(xmsg,...)
Macro printing error messages.
ROOT::Math::PtEtaPhiMVector GetPtEtaPhiMfromTruth(const xAOD::TruthParticle *TruthParticle)
bool getZ(const std::string &str_lep1, const std::string &str_lep2, PtEtaPhiMVector &p1, int &pdgId1, PtEtaPhiMVector &p2, int &pdgId2)
bool RetrieveParticleInfo(const std::string &prefix, std::vector< const xAOD::TruthParticle * > &particles)
Gaudi::Property< std::string > m_prefix
void setZ(const std::string &fsr, int nZs)
bool ExistsInMap(const std::string &key) const
void FillZtautauPartonHistory(const std::string &parent, int nZs=1, const std::string &mode="resonant")
void FillZPartonHistory(const std::string &parent, int nZs=1, const std::string &mode="resonant")
void FillGenericPartonHistory(const std::string &retrievalstring, const std::string &decorationstring, const int idx)
ZTauTauDecay getZFromTaus(const std::string &fsr)
void setZtautau(const std::string &fsr, int nZs)
Helper class to provide constant type-safe access to aux data.
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
Select isolated Photons, Electrons and Muons.
@ Z
FillZPartonHistory.
Z->tautau decay chain: taus[0] = tau- (Zdecay1), taus[1] = tau+ (Zdecay2); tauDecays[i][j] = decay pr...
std::array< ZTauTauProduct, 2 > taus
std::array< std::array< ZTauTauProduct, 3 >, 2 > tauDecays
One Z->tautau decay product: kinematics, PDG ID and whether it was found.