ATLAS Offline Software
Loading...
Searching...
No Matches
CalcWPartonHistory.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::getW(const std::string& str_lep,
16 const std::string& str_nu, PtEtaPhiMVector& p1,
17 int& pdgId1, PtEtaPhiMVector& p2, int& pdgId2) {
18 // Off-shell / non-resonant W reconstruction from lepton + neutrino
19 // candidates. Loops over all (lepton, neutrino) pairs and returns the first
20 // pair that passes the three conditions below. The caller is responsible for
21 // passing the correct lepton/neutrino flavour keys (e.g. MC_lbar + MC_nu for
22 // W+).
23 std::vector<const xAOD::TruthParticle*> W_offshell_decay1_candidates;
24 std::vector<const xAOD::TruthParticle*> W_offshell_decay2_candidates;
25 // str_lep/str_nu are full m_particleMap keys (caller passes m_prefix + "_" +
26 // ...)
27 bool has_candidates =
28 (RetrieveParticleInfo(str_lep, W_offshell_decay1_candidates) &&
29 RetrieveParticleInfo(str_nu, W_offshell_decay2_candidates));
30 if (has_candidates) {
31 for (const auto* particle_1 : W_offshell_decay1_candidates) {
32 for (const auto* particle_2 : W_offshell_decay2_candidates) {
33 // Condition 1: Opposite charge — lepton and neutrino must have
34 // opposite-sign pdgIds (e.g. e- has pdgId=11, νe has pdgId=12; product
35 // is positive, so we require the product to be negative for a valid W
36 // decay pair).
37 if ((particle_1->pdgId() * particle_2->pdgId()) > 0)
38 continue;
39 // Condition 2: Same lepton generation — |pdgId| of the neutrino must
40 // be that of the lepton plus 1 (11 & 12, 13 & 14, 15 & 16); a
41 // symmetric difference would also accept e.g. (mu, nu_e).
42 if (particle_2->absPdgId() - particle_1->absPdgId() != 1)
43 continue;
44 // Condition 3: Reject shower particles — particles produced during
45 // parton showering are assigned UIDs above 10100 by the generator.
46 // We require at least one of the pair to be a hard-scatter particle
47 // (UID ≤ 10100) to avoid picking up shower leptons.
48 if (particle_1->uid() > 10100 && particle_2->uid() > 10100)
49 continue;
50
51 p1 = GetPtEtaPhiMfromTruth(particle_1);
52 pdgId1 = particle_1->pdgId();
53 p2 = GetPtEtaPhiMfromTruth(particle_2);
54 pdgId2 = particle_2->pdgId();
55 return true;
56 }
57 }
58 }
59 return false;
60}
61
62void CalcPartonHistory::setW(const std::string& fsr, int nWs) {
63 PtEtaPhiMVector W;
64 PtEtaPhiMVector Wm, Wm_decay1, Wm_decay2;
65 PtEtaPhiMVector Wp, Wp_decay1, Wp_decay2;
66 int Wm_decay1_pdgId, Wm_decay2_pdgId;
67 int Wp_decay1_pdgId, Wp_decay2_pdgId;
68
69 if (nWs == 1) {
70 // W+: pass full m_particleMap keys to getW; bare names to m_dec.*
71 bool has_Wp =
72 getW(m_prefix + "_" + "MC_lbar_" + fsr, m_prefix + "_" + "MC_nu_" + fsr,
73 Wp_decay1, Wp_decay1_pdgId, Wp_decay2, Wp_decay2_pdgId);
74 if (has_Wp) {
75 Wp = Wp_decay1 + Wp_decay2;
76 m_dec.decorateParticle("MC_W_" + fsr, Wp, 24);
77 m_dec.decorateParticle("MC_Wdecay1_" + fsr, Wp_decay1, Wp_decay1_pdgId);
78 m_dec.decorateParticle("MC_Wdecay2_" + fsr, Wp_decay2, Wp_decay2_pdgId);
79 }
80 // W-
81 bool has_Wm =
82 getW(m_prefix + "_" + "MC_l_" + fsr, m_prefix + "_" + "MC_nubar_" + fsr,
83 Wm_decay1, Wm_decay1_pdgId, Wm_decay2, Wm_decay2_pdgId);
84 if (has_Wm) {
85 Wm = Wm_decay1 + Wm_decay2;
86 m_dec.decorateParticle("MC_W_" + fsr, Wm, -24);
87 m_dec.decorateParticle("MC_Wdecay1_" + fsr, Wm_decay1, Wm_decay1_pdgId);
88 m_dec.decorateParticle("MC_Wdecay2_" + fsr, Wm_decay2, Wm_decay2_pdgId);
89 }
90 } else if (nWs == 2) {
91 // W+
92 bool has_Wp =
93 getW(m_prefix + "_" + "MC_lbar_" + fsr, m_prefix + "_" + "MC_nu_" + fsr,
94 Wp_decay1, Wp_decay1_pdgId, Wp_decay2, Wp_decay2_pdgId);
95 if (has_Wp) {
96 Wp = Wp_decay1 + Wp_decay2;
97 m_dec.decorateParticle("MC_W1_" + fsr, Wp, 24);
98 m_dec.decorateParticle("MC_W1decay1_" + fsr, Wp_decay1, Wp_decay1_pdgId);
99 m_dec.decorateParticle("MC_W1decay2_" + fsr, Wp_decay2, Wp_decay2_pdgId);
100 }
101 // W-
102 bool has_Wm =
103 getW(m_prefix + "_" + "MC_l_" + fsr, m_prefix + "_" + "MC_nubar_" + fsr,
104 Wm_decay1, Wm_decay1_pdgId, Wm_decay2, Wm_decay2_pdgId);
105 if (has_Wm) {
106 Wm = Wm_decay1 + Wm_decay2;
107 m_dec.decorateParticle("MC_W2_" + fsr, Wm, -24);
108 m_dec.decorateParticle("MC_W2decay1_" + fsr, Wm_decay1, Wm_decay1_pdgId);
109 m_dec.decorateParticle("MC_W2decay2_" + fsr, Wm_decay2, Wm_decay2_pdgId);
110 }
111 }
112}
113
114void CalcPartonHistory::FillWPartonHistory(const std::string& parent, int nWs,
115 const std::string& mode) {
116 std::string parentstring = parent.empty() ? "" : "_from_" + parent;
117 // Bare suffix for FillGenericPartonHistory (it prepends m_prefix + "_"
118 // internally).
119 std::string prefix = "MC_" + (parent.empty() ? "W" : parent + "_W");
120
121 if (mode == "resonant") {
122 if (nWs == 1) {
123 if (parent.empty())
124 m_dec.decorateCustom(
125 "MC_W_IsOnShell",
126 ExistsInMap(m_prefix + "_" + prefix + "_beforeFSR") ? 1 : 0);
127 FillGenericPartonHistory(prefix + "_beforeFSR",
128 "MC_W_beforeFSR" + parentstring, 0);
129 FillGenericPartonHistory(prefix + "Decay1_beforeFSR",
130 "MC_Wdecay1_beforeFSR" + parentstring, 0);
131 FillGenericPartonHistory(prefix + "Decay2_beforeFSR",
132 "MC_Wdecay2_beforeFSR" + parentstring, 0);
133 FillGenericPartonHistory(prefix + "_afterFSR",
134 "MC_W_afterFSR" + parentstring, 0);
135 FillGenericPartonHistory(prefix + "Decay1_afterFSR",
136 "MC_Wdecay1_afterFSR" + parentstring, 0);
137 FillGenericPartonHistory(prefix + "Decay2_afterFSR",
138 "MC_Wdecay2_afterFSR" + parentstring, 0);
139 } else {
140 for (int idx = 0; idx < nWs; idx++) {
141 const std::string idxStr = std::to_string(idx + 1);
142 if (parent.empty())
143 m_dec.decorateCustom("MC_W" + idxStr + "_IsOnShell", 1);
144 FillGenericPartonHistory(prefix + "_beforeFSR",
145 "MC_W" + idxStr + "_beforeFSR" + parentstring,
146 0);
148 prefix + "Decay1_beforeFSR",
149 "MC_W" + idxStr + "decay1_beforeFSR" + parentstring, 0);
151 prefix + "Decay2_beforeFSR",
152 "MC_W" + idxStr + "decay2_beforeFSR" + parentstring, 0);
153 FillGenericPartonHistory(prefix + "_afterFSR",
154 "MC_W" + idxStr + "_afterFSR" + parentstring,
155 0);
157 prefix + "Decay1_afterFSR",
158 "MC_W" + idxStr + "decay1_afterFSR" + parentstring, 0);
160 prefix + "Decay2_afterFSR",
161 "MC_W" + idxStr + "decay2_afterFSR" + parentstring, 0);
162 }
163 }
164 } else if (mode == "non_resonant") {
165 if (parent.empty()) {
166 if (nWs == 1) {
167 m_dec.decorateCustom("MC_W_IsOnShell", 0);
168 } else {
169 for (int idx = 0; idx < nWs; idx++)
170 m_dec.decorateCustom("MC_W" + std::to_string(idx + 1) + "_IsOnShell",
171 0);
172 }
173 }
174 setW("beforeFSR", nWs);
175 setW("afterFSR", nWs);
176 }
177}
178} // namespace CP
bool RetrieveParticleInfo(const std::string &prefix, std::vector< const xAOD::TruthParticle * > &particles)
Gaudi::Property< std::string > m_prefix
void FillWPartonHistory(const std::string &parent, int nWs=1, const std::string &mode="resonant")
bool getW(const std::string &str_lep, const std::string &str_nu, PtEtaPhiMVector &p1, int &pdgId1, PtEtaPhiMVector &p2, int &pdgId2)
void setW(const std::string &fsr, int nWs)
bool ExistsInMap(const std::string &key) const
void FillGenericPartonHistory(const std::string &retrievalstring, const std::string &decorationstring, const int idx)
Select isolated Photons, Electrons and Muons.
ROOT::Math::PtEtaPhiMVector GetPtEtaPhiMfromTruth(const xAOD::TruthParticle *TruthParticle)
Four-vector of a truth particle, built from (pt, eta, phi, m) so that the mass is preserved exactly.
@ W
FillWPartonHistory.