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
12namespace CP {
13using ROOT::Math::PtEtaPhiMVector;
14
15bool CalcPartonHistory::getZ(const std::string& str_lep1,
16 const std::string& str_lep2, PtEtaPhiMVector& p1,
17 int& pdgId1, PtEtaPhiMVector& p2, int& pdgId2) {
18 // Off-shell / non-resonant Z reconstruction from two same-flavour
19 // opposite-sign lepton candidates. Uses MCTruthClassifier decorations
20 // (classifierParticleOrigin, classifierParticleType) to restrict to
21 // prompt, isolated leptons from a Z decay, rejecting fakes and non-prompt
22 // background. Only electrons and muons are considered (not taus); for
23 // Z→ττ use getZFromTaus instead.
24 static const SG::Accessor<unsigned int> acc_classifierParticleOrigin(
25 "classifierParticleOrigin");
26 static const SG::Accessor<unsigned int> acc_classifierParticleType(
27 "classifierParticleType");
28 std::vector<const xAOD::TruthParticle*> Z_offshell_decay1_candidates;
29 std::vector<const xAOD::TruthParticle*> Z_offshell_decay2_candidates;
30 // str_lep1/2 are full m_particleMap keys (caller passes m_prefix + "_" + ...)
31 bool has_candidates =
32 (RetrieveParticleInfo(str_lep1, Z_offshell_decay1_candidates) &&
33 RetrieveParticleInfo(str_lep2, Z_offshell_decay2_candidates));
34 if (has_candidates) {
35 const xAOD::TruthParticle* bp1 = nullptr;
36 const xAOD::TruthParticle* bp2 = nullptr;
37 for (const auto* pDecay1 : Z_offshell_decay1_candidates) {
38 for (const auto* pDecay2 : Z_offshell_decay2_candidates) {
39 unsigned int o1 = acc_classifierParticleOrigin(*pDecay1);
40 unsigned int o2 = acc_classifierParticleOrigin(*pDecay2);
41 unsigned int t1 = acc_classifierParticleType(*pDecay1);
42 unsigned int t2 = acc_classifierParticleType(*pDecay2);
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 // Condition 3: Origin == 13 (ZBoson) from MCTruthClassifier
52 // (see TruthUtils/TruthClasses.h). Ensures both leptons are
53 // truth-matched to a Z decay and not to backgrounds such as photon
54 // conversions or heavy-flavour semileptonic decays.
55 if (!(o1 == 13 && o2 == 13))
56 continue;
57 // Condition 4: Type == 2 (IsoElectron) or 4 (IsoMuon) from
58 // MCTruthClassifier. Selects prompt isolated leptons, rejecting
59 // non-isolated or non-prompt contributions.
60 if (!((t1 == 2 && t2 == 2) || (t1 == 4 && t2 == 4)))
61 continue;
62 bp1 = pDecay1;
63 bp2 = pDecay2;
64 }
65 }
66 if (bp1 && bp2) {
67 p1 = GetPtEtaPhiMfromTruth(bp1);
68 pdgId1 = bp1->pdgId();
69 p2 = GetPtEtaPhiMfromTruth(bp2);
70 pdgId2 = bp2->pdgId();
71 return true;
72 }
73 }
74 return false;
75}
76
78 const std::string& fsr, PtEtaPhiMVector& Zdecay1, int& Zdecay1_pdgId,
79 PtEtaPhiMVector& Zdecay2, int& Zdecay2_pdgId,
80 PtEtaPhiMVector& Zdecay1_decay1, int& Zdecay1_decay1_pdgId,
81 PtEtaPhiMVector& Zdecay1_decay2, int& Zdecay1_decay2_pdgId,
82 PtEtaPhiMVector& Zdecay1_decay3, int& Zdecay1_decay3_pdgId,
83 PtEtaPhiMVector& Zdecay2_decay1, int& Zdecay2_decay1_pdgId,
84 PtEtaPhiMVector& Zdecay2_decay2, int& Zdecay2_decay2_pdgId,
85 PtEtaPhiMVector& Zdecay2_decay3, int& Zdecay2_decay3_pdgId) {
86
87 std::vector<const xAOD::TruthParticle*> Z_offshell_l_candidates; // tau
88 std::vector<const xAOD::TruthParticle*>
89 Z_offshell_l_l_candidates; // l from tau
90 std::vector<const xAOD::TruthParticle*>
91 Z_offshell_l_nubar_candidates; // anti-nu from tau
92 std::vector<const xAOD::TruthParticle*>
93 Z_offshell_l_nu_candidates; // nu from tau
94 std::vector<const xAOD::TruthParticle*> Z_offshell_lbar_candidates; // taubar
95 std::vector<const xAOD::TruthParticle*>
96 Z_offshell_lbar_lbar_candidates; // lbar from taubar
97 std::vector<const xAOD::TruthParticle*>
98 Z_offshell_lbar_nu_candidates; // nu from taubar
99 std::vector<const xAOD::TruthParticle*>
100 Z_offshell_lbar_nubar_candidates; // anti-nu from taubar
101
102 // All RetrieveParticleInfo calls use full m_particleMap keys (with prefix).
103 bool has_taum_candidates = RetrieveParticleInfo(
104 m_prefix + "_" + "MC_l_" + fsr, Z_offshell_l_candidates);
105 bool has_taum_decay_candidates =
106 (RetrieveParticleInfo(m_prefix + "_" + "MC_l_l_" + fsr,
107 Z_offshell_l_l_candidates) &&
108 RetrieveParticleInfo(m_prefix + "_" + "MC_l_nubar_" + fsr,
109 Z_offshell_l_nubar_candidates) &&
110 RetrieveParticleInfo(m_prefix + "_" + "MC_l_nu_" + fsr,
111 Z_offshell_l_nu_candidates));
112 bool has_taup_candidates = RetrieveParticleInfo(
113 m_prefix + "_" + "MC_lbar_" + fsr, Z_offshell_lbar_candidates);
114 bool has_taup_decay_candidates =
115 (RetrieveParticleInfo(m_prefix + "_" + "MC_lbar_lbar_" + fsr,
116 Z_offshell_lbar_lbar_candidates) &&
117 RetrieveParticleInfo(m_prefix + "_" + "MC_lbar_nu_" + fsr,
118 Z_offshell_lbar_nu_candidates) &&
119 RetrieveParticleInfo(m_prefix + "_" + "MC_lbar_nubar_" + fsr,
120 Z_offshell_lbar_nubar_candidates));
121
122 if (has_taum_candidates) {
123 Zdecay1 = GetPtEtaPhiMfromTruth(Z_offshell_l_candidates.at(0));
124 Zdecay1_pdgId = Z_offshell_l_candidates.at(0)->pdgId();
125 }
126 if (has_taum_decay_candidates) {
127 Zdecay1_decay1 = GetPtEtaPhiMfromTruth(Z_offshell_l_l_candidates.at(0));
128 Zdecay1_decay1_pdgId = Z_offshell_l_l_candidates.at(0)->pdgId();
129 Zdecay1_decay2 = GetPtEtaPhiMfromTruth(Z_offshell_l_nubar_candidates.at(0));
130 Zdecay1_decay2_pdgId = Z_offshell_l_nubar_candidates.at(0)->pdgId();
131 Zdecay1_decay3 = GetPtEtaPhiMfromTruth(Z_offshell_l_nu_candidates.at(0));
132 Zdecay1_decay3_pdgId = Z_offshell_l_nu_candidates.at(0)->pdgId();
133 }
134 if (has_taup_candidates) {
135 Zdecay2 = GetPtEtaPhiMfromTruth(Z_offshell_lbar_candidates.at(0));
136 Zdecay2_pdgId = Z_offshell_lbar_candidates.at(0)->pdgId();
137 }
138 if (has_taup_decay_candidates) {
139 Zdecay2_decay1 =
140 GetPtEtaPhiMfromTruth(Z_offshell_lbar_lbar_candidates.at(0));
141 Zdecay2_decay1_pdgId = Z_offshell_lbar_lbar_candidates.at(0)->pdgId();
142 Zdecay2_decay2 = GetPtEtaPhiMfromTruth(Z_offshell_lbar_nu_candidates.at(0));
143 Zdecay2_decay2_pdgId = Z_offshell_lbar_nu_candidates.at(0)->pdgId();
144 Zdecay2_decay3 =
145 GetPtEtaPhiMfromTruth(Z_offshell_lbar_nubar_candidates.at(0));
146 Zdecay2_decay3_pdgId = Z_offshell_lbar_nubar_candidates.at(0)->pdgId();
147 }
148 return has_taum_candidates && has_taup_candidates;
149}
150
151// For filling Z->ee,mumu
152void CalcPartonHistory::setZ(const std::string& fsr, int nZs) {
153 PtEtaPhiMVector Z, Z_decay1, Z_decay2;
154 int Z_decay1_pdgId, Z_decay2_pdgId;
155 m_dec.decorateCustom("MC_Z_IsOnShell", 0);
156 // getZ receives full m_particleMap keys; m_dec.decorate* receives bare names.
157 bool has_Z =
158 getZ(m_prefix + "_" + "MC_l_" + fsr, m_prefix + "_" + "MC_lbar_" + fsr,
159 Z_decay1, Z_decay1_pdgId, Z_decay2, Z_decay2_pdgId);
160 if (nZs == 1) {
161 if (has_Z) {
162 Z = Z_decay1 + Z_decay2;
163 m_dec.decorateParticle("MC_Z_" + fsr, Z, 23);
164 m_dec.decorateParticle("MC_Zdecay1_" + fsr, Z_decay1, Z_decay1_pdgId);
165 m_dec.decorateParticle("MC_Zdecay2_" + fsr, Z_decay2, Z_decay2_pdgId);
166 }
167 } else
168 ANA_MSG_ERROR("Reconstruction of multiple Zs is not supported yet!");
169}
170
171// For filling Z->tautau
172void CalcPartonHistory::setZtautau(const std::string& fsr, int nZs) {
173 PtEtaPhiMVector Z, Zdecay1, Zdecay2;
174 PtEtaPhiMVector Zdecay1_decay1, Zdecay1_decay2, Zdecay1_decay3;
175 PtEtaPhiMVector Zdecay2_decay1, Zdecay2_decay2, Zdecay2_decay3;
176 int Zdecay1_pdgId = 0, Zdecay2_pdgId = 0;
177 int Zdecay1_decay1_pdgId = 0, Zdecay1_decay2_pdgId = 0,
178 Zdecay1_decay3_pdgId = 0;
179 int Zdecay2_decay1_pdgId = 0, Zdecay2_decay2_pdgId = 0,
180 Zdecay2_decay3_pdgId = 0;
181
182 bool has_Z =
183 getZFromTaus(fsr, Zdecay1, Zdecay1_pdgId, Zdecay2, Zdecay2_pdgId,
184 Zdecay1_decay1, Zdecay1_decay1_pdgId, Zdecay1_decay2,
185 Zdecay1_decay2_pdgId, Zdecay1_decay3, Zdecay1_decay3_pdgId,
186 Zdecay2_decay1, Zdecay2_decay1_pdgId, Zdecay2_decay2,
187 Zdecay2_decay2_pdgId, Zdecay2_decay3, Zdecay2_decay3_pdgId);
188 if (nZs == 1) {
189 if (has_Z) {
190 Z = Zdecay1 + Zdecay2;
191 m_dec.decorateParticle("MC_Z_" + fsr, Z, 23);
192 m_dec.decorateParticle("MC_Zdecay1_" + fsr, Zdecay1, Zdecay1_pdgId);
193 m_dec.decorateParticle("MC_Zdecay2_" + fsr, Zdecay2, Zdecay2_pdgId);
194 m_dec.decorateParticle("MC_Zdecay1_decay1_" + fsr, Zdecay1_decay1,
195 Zdecay1_decay1_pdgId);
196 m_dec.decorateParticle("MC_Zdecay2_decay1_" + fsr, Zdecay2_decay1,
197 Zdecay2_decay1_pdgId);
198 m_dec.decorateParticle("MC_Zdecay1_decay2_" + fsr, Zdecay1_decay2,
199 Zdecay1_decay2_pdgId);
200 m_dec.decorateParticle("MC_Zdecay2_decay2_" + fsr, Zdecay2_decay2,
201 Zdecay2_decay2_pdgId);
202 m_dec.decorateParticle("MC_Zdecay1_decay3_" + fsr, Zdecay1_decay3,
203 Zdecay1_decay3_pdgId);
204 m_dec.decorateParticle("MC_Zdecay2_decay3_" + fsr, Zdecay2_decay3,
205 Zdecay2_decay3_pdgId);
206 }
207 } else
208 ANA_MSG_ERROR("Reconstruction of multiple Zs is not supported yet!");
209}
210
211void CalcPartonHistory::FillZPartonHistory(const std::string& parent, int nZs,
212 const std::string& mode) {
213 std::string parentstring = parent.empty() ? "" : "_from_" + parent;
214 // mapPrefix: full m_particleMap key base (with m_prefix) — used only for
215 // ExistsInMap.
216 std::string mapPrefix =
217 m_prefix + "_" + "MC_" + (parent.empty() ? "Z" : parent + "_Z");
218 // decPrefix: bare suffix passed to FillGenericPartonHistory as
219 // retrievalstring; FillGenericPartonHistory prepends m_prefix + "_"
220 // internally.
221 std::string decPrefix = "MC_" + (parent.empty() ? "Z" : parent + "_Z");
222
223 if (mode == "resonant") {
224 if (nZs == 1) {
225 if (ExistsInMap(mapPrefix + "_beforeFSR")) {
226 m_dec.decorateCustom("MC_Z_IsOnShell", 1);
227 FillGenericPartonHistory(decPrefix + "_beforeFSR",
228 "MC_Z" + parentstring + "_beforeFSR", 0);
229 FillGenericPartonHistory(decPrefix + "Decay1_beforeFSR",
230 "MC_Zdecay1" + parentstring + "_beforeFSR", 0);
231 FillGenericPartonHistory(decPrefix + "Decay2_beforeFSR",
232 "MC_Zdecay2" + parentstring + "_beforeFSR", 0);
233 FillGenericPartonHistory(decPrefix + "_afterFSR",
234 "MC_Z" + parentstring + "_afterFSR", 0);
235 FillGenericPartonHistory(decPrefix + "Decay1_afterFSR",
236 "MC_Zdecay1" + parentstring + "_afterFSR", 0);
237 FillGenericPartonHistory(decPrefix + "Decay2_afterFSR",
238 "MC_Zdecay2" + parentstring + "_afterFSR", 0);
239 } else {
240 setZ("beforeFSR", nZs);
241 setZ("afterFSR", nZs);
242 }
243 } else {
244 for (int idx = 0; idx < nZs; idx++) {
245 const std::string idxStr = std::to_string(idx + 1);
246 m_dec.decorateCustom("MC_Z" + idxStr + "_IsOnShell", 1);
247 FillGenericPartonHistory(decPrefix + "_beforeFSR",
248 "MC_Z" + idxStr + parentstring + "_beforeFSR",
249 idx);
251 decPrefix + "Decay1_beforeFSR",
252 "MC_Z" + idxStr + "decay1" + parentstring + "_beforeFSR", idx);
254 decPrefix + "Decay2_beforeFSR",
255 "MC_Z" + idxStr + "decay2" + parentstring + "_beforeFSR", idx);
256 FillGenericPartonHistory(decPrefix + "_afterFSR",
257 "MC_Z" + idxStr + parentstring + "_afterFSR",
258 idx);
260 decPrefix + "Decay1_afterFSR",
261 "MC_Z" + idxStr + "decay1" + parentstring + "_afterFSR", idx);
263 decPrefix + "Decay2_afterFSR",
264 "MC_Z" + idxStr + "decay2" + parentstring + "_afterFSR", idx);
265 }
266 }
267 } else if (mode == "non_resonant") {
268 setZ("beforeFSR", nZs);
269 setZ("afterFSR", nZs);
270 }
271}
272
273void CalcPartonHistory::FillZtautauPartonHistory(const std::string& parent,
274 int nZs,
275 const std::string& mode) {
276 std::string parentstring = parent.empty() ? "" : "_from_" + parent;
277 // mapPrefix: full key for ExistsInMap (used indirectly via
278 // FillZPartonHistory).
279 std::string mapPrefix =
280 m_prefix + "_" + "MC_" + (parent.empty() ? "Z" : parent + "_Z");
281 // decPrefix: bare suffix for FillGenericPartonHistory retrieval strings.
282 std::string decPrefix = "MC_" + (parent.empty() ? "Z" : parent + "_Z");
283
284 FillZPartonHistory(parent, nZs, mode);
285 m_dec.decorateCustom("MC_Z_IsOnShell",
286 0); // default; overwritten to 1 below if resonant
287 if (mode == "resonant") {
288 if (nZs == 1) {
289 m_dec.decorateCustom("MC_Z_IsOnShell", 1);
291 decPrefix + "Decay1_Decay1_beforeFSR",
292 "MC_Zdecay1_decay1" + parentstring + "_beforeFSR", 0);
294 decPrefix + "Decay1_Decay2_beforeFSR",
295 "MC_Zdecay1_decay2" + parentstring + "_beforeFSR", 0);
297 decPrefix + "Decay1_Decay3_beforeFSR",
298 "MC_Zdecay1_decay3" + parentstring + "_beforeFSR", 0);
299 FillGenericPartonHistory(decPrefix + "Decay1_Decay1_afterFSR",
300 "MC_Zdecay1_decay1" + parentstring + "_afterFSR",
301 0);
302 FillGenericPartonHistory(decPrefix + "Decay1_Decay2_afterFSR",
303 "MC_Zdecay1_decay2" + parentstring + "_afterFSR",
304 0);
305 FillGenericPartonHistory(decPrefix + "Decay1_Decay3_afterFSR",
306 "MC_Zdecay1_decay3" + parentstring + "_afterFSR",
307 0);
308 } else {
309 for (int idx = 0; idx < nZs; idx++) {
310 const std::string idxStr = std::to_string(idx + 1);
311 m_dec.decorateCustom("MC_Z" + idxStr + "_IsOnShell", 1);
313 decPrefix + "Decay1_Decay1_beforeFSR",
314 "MC_Z" + idxStr + "decay1_decay1" + parentstring + "_beforeFSR",
315 idx);
317 decPrefix + "Decay1_Decay2_beforeFSR",
318 "MC_Z" + idxStr + "decay1_decay2" + parentstring + "_beforeFSR",
319 idx);
321 decPrefix + "Decay1_Decay3_beforeFSR",
322 "MC_Z" + idxStr + "decay1_decay3" + parentstring + "_beforeFSR",
323 idx);
325 decPrefix + "Decay1_Decay1_afterFSR",
326 "MC_Z" + idxStr + "decay1_decay1" + parentstring + "_afterFSR",
327 idx);
329 decPrefix + "Decay1_Decay2_afterFSR",
330 "MC_Z" + idxStr + "decay1_decay2" + parentstring + "_afterFSR",
331 idx);
333 decPrefix + "Decay1_Decay3_afterFSR",
334 "MC_Z" + idxStr + "decay1_decay3" + parentstring + "_afterFSR",
335 idx);
336 }
337 }
338 } else if (mode == "non_resonant") {
339 setZtautau("beforeFSR", nZs);
340 setZtautau("afterFSR", nZs);
341 }
342}
343
344} // namespace CP
#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)
std::string m_prefix
prefix applied to all decorator and m_particleMap names
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)
void setZtautau(const std::string &fsr, int nZs)
bool getZFromTaus(const std::string &fsr, PtEtaPhiMVector &Zdecay1, int &Zdecay1_pdgId, PtEtaPhiMVector &Zdecay2, int &Zdecay2_pdgId, PtEtaPhiMVector &Zdecay1_decay1, int &Zdecay1_decay1_pdgId, PtEtaPhiMVector &Zdecay1_decay2, int &Zdecay1_decay2_pdgId, PtEtaPhiMVector &Zdecay1_decay3, int &Zdecay1_decay3_pdgId, PtEtaPhiMVector &Zdecay2_decay1, int &Zdecay2_decay1_pdgId, PtEtaPhiMVector &Zdecay2_decay2, int &Zdecay2_decay2_pdgId, PtEtaPhiMVector &Zdecay2_decay3, int &Zdecay2_decay3_pdgId)
Helper class to provide type-safe access to aux data.
int pdgId() const
PDG ID code.
Select isolated Photons, Electrons and Muons.
@ Z
FillZPartonHistory(history, parent, dec, count, mode).
TruthParticle_v1 TruthParticle
Typedef to implementation.