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