ATLAS Offline Software
Toggle main menu visibility
Loading...
Searching...
No Matches
PhysicsAnalysis
Algorithms
TruthPartonLevelAnalysisAlgorithms
python
PartonHistoryConfig.py
Go to the documentation of this file.
1
# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3
from
typing
import
Optional
4
5
from
AnalysisAlgorithmsConfig.ConfigBlock
import
ConfigBlock
6
7
8
# ---------------------------------------------------------------------------
9
# Helpers
10
# ---------------------------------------------------------------------------
11
12
KINEMATIC_VARS = (
"pt"
,
"eta"
,
"phi"
,
"m"
)
13
INT_SUFFIXES = frozenset((
"pdgId"
,
"IsOnShell"
,
"origin"
))
14
VECTOR_PREFIXES = (
"MC_b_"
,
"MC_bbar_"
,
"MC_c_"
,
"MC_cbar_"
)
15
VECTOR_HISTORIES = frozenset((
"Ttbarbbbar"
,
"Ttbarccbar"
))
16
17
def
_get_aux_type
(branch: str, prefix: str) -> str:
18
# Handle specific cases where vectors are returned for each particle type
19
# In those cases, only external particles can be vectors, there is still
20
# a single particle allowed in named positions within decay chains
21
is_vector = prefix
in
VECTOR_HISTORIES
and
branch.startswith(VECTOR_PREFIXES)
and
"_from_"
not
in
branch
22
23
if
is_vector:
24
return
"vector_int"
if
any(branch.endswith(s)
for
s
in
INT_SUFFIXES)
else
"vector_float"
25
else
:
26
return
"int"
if
any(branch.endswith(s)
for
s
in
INT_SUFFIXES)
else
"float"
27
28
def
_make_particle_branches
(
29
prefix: str,
30
*,
31
include_pdgid: bool =
True
,
32
stages: Optional[tuple] =
None
,
33
extra: tuple = (),
34
) -> list:
35
"""Generate standard kinematic branch names for a particle.
36
37
Parameters
38
----------
39
prefix:
40
Particle label as it appears in the branch name, e.g. ``"t"`` ->
41
``MC_t_beforeFSR_pt``. When the prefix already encodes the FSR stage
42
(e.g. ``"W_beforeFSR_from_t"``), pass ``stages=()``.
43
include_pdgid:
44
Append ``pdgId`` after the kinematic variables for each stage.
45
stages:
46
FSR stage tokens to iterate over. Defaults to
47
``("beforeFSR", "afterFSR")``. Pass ``()`` when the FSR stage is
48
already embedded in prefix — branches become ``MC_{prefix}_{var}``.
49
extra:
50
Additional branch suffixes appended verbatim, e.g. ``("IsOnShell",)``.
51
"""
52
if
stages
is
None
:
53
stages = (
"beforeFSR"
,
"afterFSR"
)
54
result = []
55
if
stages:
56
for
stage
in
stages:
57
for
var
in
KINEMATIC_VARS:
58
result.append(f
"MC_{prefix}_{stage}_{var}"
)
59
if
include_pdgid:
60
result.append(f
"MC_{prefix}_{stage}_pdgId"
)
61
else
:
62
for
var
in
KINEMATIC_VARS:
63
result.append(f
"MC_{prefix}_{var}"
)
64
if
include_pdgid:
65
result.append(f
"MC_{prefix}_pdgId"
)
66
for
suffix
in
extra:
67
result.append(f
"MC_{prefix}_{suffix}"
)
68
return
result
69
70
71
def
_replace_in_list
(string_list: list[str], old: str, new: str) -> list[str]:
72
return
[item.replace(old, new)
for
item
in
string_list]
73
74
75
# ---------------------------------------------------------------------------
76
# Per-particle branch lists
77
# ---------------------------------------------------------------------------
78
79
80
def
_t_branches
(suffix: str =
"t"
) -> list:
81
"""Branches for a top quark and its full decay chain.
82
83
Sub-particles carry the FSR stage in their prefix so we pass ``stages=()``
84
to avoid appending a spurious stage token to the variable name.
85
For antitops (suffix starting with 'tbar'), the b-quark is named 'bbar'.
86
"""
87
b_name =
"bbar"
if
suffix.startswith(
"tbar"
)
else
"b"
88
sub_particles = (
"W"
, b_name,
"Wdecay1"
,
"Wdecay2"
)
89
result =
_make_particle_branches
(suffix)
90
for
particle
in
sub_particles:
91
for
stage
in
(
"beforeFSR"
,
"afterFSR"
):
92
result +=
_make_particle_branches
(
93
f
"{particle}_{stage}_from_{suffix}"
, stages=()
94
)
95
return
result
96
97
def
_t_FCNC_branches
(suffix: str =
"t"
) -> list:
98
"""Branches for a top quark undergoing FCNC decay t -> q X, X -> decay1 decay2.
99
100
Sub-particles carry the FSR stage in their prefix so we pass ``stages=()``
101
to avoid appending a spurious stage token to the variable name.
102
For tbar, the q-quark is named 'qbar'.
103
"""
104
q_name =
"qbar"
if
suffix ==
"tbar"
else
"q"
105
sub_particles = (
"X"
, q_name,
"Xdecay1"
,
"Xdecay2"
)
106
result =
_make_particle_branches
(suffix)
107
for
particle
in
sub_particles:
108
for
stage
in
(
"beforeFSR"
,
"afterFSR"
):
109
result +=
_make_particle_branches
(
110
f
"{particle}_{stage}_from_{suffix}"
, stages=()
111
)
112
return
result
113
114
BRANCHES: dict[str, list[str]] = {
115
"t"
:
_t_branches
(
"t"
),
116
"tbar"
:
_t_branches
(
"tbar"
),
117
"t_FCNC"
:
_t_FCNC_branches
(
"t"
),
118
"tbar_FCNC"
:
_t_FCNC_branches
(
"tbar"
),
119
"ttbar"
: (
120
_make_particle_branches
(
"ttbar"
, include_pdgid=
False
)
121
+
_make_particle_branches
(
"ttbar_fromDecay"
, include_pdgid=
False
)
122
),
123
"b"
:
_make_particle_branches
(
"b"
),
124
"bbar"
:
_make_particle_branches
(
"bbar"
),
125
"c"
:
_make_particle_branches
(
"c"
),
126
"cbar"
:
_make_particle_branches
(
"cbar"
),
127
"Z"
: (
128
_make_particle_branches
(
"Z"
, extra=(
"IsOnShell"
,))
129
+
_make_particle_branches
(
"Zdecay1"
)
130
+
_make_particle_branches
(
"Zdecay2"
)
131
),
132
"Z_extended"
: (
133
_make_particle_branches
(
"Z"
, extra=(
"IsOnShell"
,))
134
+
_make_particle_branches
(
"Zdecay1"
)
135
+
_make_particle_branches
(
"Zdecay1_decay1"
)
136
+
_make_particle_branches
(
"Zdecay1_decay2"
)
137
+
_make_particle_branches
(
"Zdecay1_decay3"
)
138
+
_make_particle_branches
(
"Zdecay2"
)
139
+
_make_particle_branches
(
"Zdecay2_decay1"
)
140
+
_make_particle_branches
(
"Zdecay2_decay2"
)
141
+
_make_particle_branches
(
"Zdecay2_decay3"
)
142
),
143
"W"
: (
144
_make_particle_branches
(
"W"
, extra=(
"IsOnShell"
,))
145
+
_make_particle_branches
(
"Wdecay1"
)
146
+
_make_particle_branches
(
"Wdecay2"
)
147
),
148
"Photon"
: [
149
"MC_gamma_m"
,
150
"MC_gamma_pt"
,
151
"MC_gamma_eta"
,
152
"MC_gamma_phi"
,
153
"MC_gamma_origin"
,
154
"MC_gamma_pdgId"
,
155
],
156
"Higgs"
: (
157
_make_particle_branches
(
"H"
)
158
+
_make_particle_branches
(
"Hdecay1"
)
159
+
_make_particle_branches
(
"Hdecay2"
)
160
+
_make_particle_branches
(
"Hdecay1_decay1"
)
161
+
_make_particle_branches
(
"Hdecay1_decay2"
)
162
+
_make_particle_branches
(
"Hdecay2_decay1"
)
163
+
_make_particle_branches
(
"Hdecay2_decay2"
)
164
),
165
}
166
167
_TTBAR = BRANCHES[
"t"
] + BRANCHES[
"tbar"
] + BRANCHES[
"ttbar"
]
168
169
TRUTH_BRANCHES: dict[str, list[str]] = {
170
"Ttbar"
: _TTBAR,
171
"TtbarFCNC"
: BRANCHES[
"t_FCNC"
] + BRANCHES[
"tbar_FCNC"
] + BRANCHES[
"ttbar"
],
172
"Ttbarbbbar"
: _TTBAR + BRANCHES[
"b"
] + BRANCHES[
"bbar"
],
173
"Ttbarccbar"
: _TTBAR + BRANCHES[
"c"
] + BRANCHES[
"cbar"
],
174
"Ttz"
: _TTBAR + BRANCHES[
"Z"
],
175
"Ttw"
: _TTBAR + BRANCHES[
"W"
],
176
"Tth"
: _TTBAR + BRANCHES[
"Higgs"
],
177
"Ttgamma"
: _TTBAR + BRANCHES[
"Photon"
],
178
"Tq"
: BRANCHES[
"t"
] + BRANCHES[
"b"
],
179
"Tzq"
: BRANCHES[
"t"
] + BRANCHES[
"Z"
] + BRANCHES[
"b"
],
180
"Thq"
: BRANCHES[
"t"
] + BRANCHES[
"Higgs"
] + BRANCHES[
"b"
] + BRANCHES[
"W"
],
181
"Tqgamma"
: BRANCHES[
"t"
] + BRANCHES[
"Photon"
] + BRANCHES[
"b"
],
182
"Wtb"
: BRANCHES[
"t"
] + BRANCHES[
"W"
] + BRANCHES[
"b"
],
183
"FourTop"
: (
184
_t_branches
(
"t1"
)
185
+
_t_branches
(
"t2"
)
186
+
_t_branches
(
"tbar1"
)
187
+
_t_branches
(
"tbar2"
)
188
),
189
"HWW"
: BRANCHES[
"Higgs"
],
190
"WW_nonresonant"
: (
191
_replace_in_list
(BRANCHES[
"W"
],
"W"
,
"W1"
)
192
+
_replace_in_list
(BRANCHES[
"W"
],
"W"
,
"W2"
)
193
),
194
"HWW_nonresonant"
: BRANCHES[
"Higgs"
],
195
"HZZ"
: BRANCHES[
"Higgs"
],
196
"Zb"
: BRANCHES[
"Z"
] + BRANCHES[
"b"
] + BRANCHES[
"bbar"
],
197
"Ztautau"
: BRANCHES[
"Z_extended"
],
198
}
199
200
# ---------------------------------------------------------------------------
201
# Config block
202
# ---------------------------------------------------------------------------
203
204
205
class
PartonHistoryBlock
(ConfigBlock):
206
"""ConfigBlock for truth/parton-level resonant histories."""
207
208
def
__init__
(self):
209
super().
__init__
()
210
self.addOption(
211
"history"
,
212
None
,
213
type=str,
214
required=
True
,
215
info=
"parton-level interpretation of the MC truth record. Possible values:"
216
+
", "
.join(sorted(TRUTH_BRANCHES))
217
+
"."
,
218
meta={
'choices'
:(sorted(TRUTH_BRANCHES),1)},
219
)
220
self.addOption(
221
"symbolFCNC"
,
222
"H"
,
223
type=str,
224
required=
False
,
225
info=
"The symbol for FCNC, valid only for 'TtbarFCNC' history. Default is 'H', can be set to 'pdg_id' for exotic candidates."
,
226
)
227
# Always skip on data
228
self.setOptionValue(
"skipOnData"
,
True
)
229
230
def
makeAlgs
(self, config):
231
if
self.history
not
in
TRUTH_BRANCHES:
232
valid =
", "
.join(sorted(TRUTH_BRANCHES))
233
raise
ValueError(
234
f
"Unknown parton history '{self.history}'. Valid options: {valid}"
235
)
236
237
alg = config.createAlgorithm(
238
"CP::RunPartonHistoryAlg"
, f
"PartonHistory{self.history}"
239
)
240
alg.partonScheme = self.history
241
alg.symbolFCNC = self.symbolFCNC
242
243
for
branch
in
TRUTH_BRANCHES[self.history]:
244
config.addOutputVar(
245
"EventInfo"
,
246
f
"{self.history}_{branch}"
,
247
f
"{self.history}_{branch}"
,
248
noSys=
True
,
249
auxType=
_get_aux_type
(branch, self.history),
250
)
PartonHistoryConfig.PartonHistoryBlock
Definition
PartonHistoryConfig.py:205
PartonHistoryConfig.PartonHistoryBlock.makeAlgs
makeAlgs(self, config)
Definition
PartonHistoryConfig.py:230
PartonHistoryConfig.PartonHistoryBlock.__init__
__init__(self)
Definition
PartonHistoryConfig.py:208
PartonHistoryConfig._t_FCNC_branches
list _t_FCNC_branches(str suffix="t")
Definition
PartonHistoryConfig.py:97
PartonHistoryConfig._t_branches
list _t_branches(str suffix="t")
Definition
PartonHistoryConfig.py:80
PartonHistoryConfig._get_aux_type
str _get_aux_type(str branch, str prefix)
Definition
PartonHistoryConfig.py:17
PartonHistoryConfig._make_particle_branches
list _make_particle_branches(str prefix, *, bool include_pdgid=True, Optional[tuple] stages=None, tuple extra=())
Definition
PartonHistoryConfig.py:34
PartonHistoryConfig._replace_in_list
list[str] _replace_in_list(list[str] string_list, str old, str new)
Definition
PartonHistoryConfig.py:71
Generated on
for ATLAS Offline Software by
1.17.0