Belle II Software development
pdg.py
1#!/usr/bin/env python3
2
3
10
11"""
12pdg - access particle definitions
13---------------------------------
14
15This module helps to access particle definitions. When the software is loaded a
16list of known particles is read from the EvtGen particle definition file
17:file:`framework/particledb/data/evt.pdl`. This file contains all well-known
18standard-model particles and their properties: mass, width or lifetime, charge,
19spin.
20...
21
22This module allows to easily access this information (see `get`) or if necessary
23add new particles using `add_particle` and even replace the whole particle
24definition list using `load`.
25
26It also provides simple getters to convert `PDG codes`_ into particle names and
27vice versa for use with modules which require a list of PDG codes for the
28particles to generate. See `from_name`, `from_names`, `to_name` and `to_names`
29
30.. _PDG codes: http://pdg.lbl.gov/2020/reviews/rpp2020-rev-monte-carlo-numbering.pdf
31"""
32
33import re
34import basf2
35from fractions import Fraction
36
37
38def _get_instance():
39 """
40 Function to return an instance of the EvtGenDatabasePDG class.
41 """
42 # Always avoid the top-level 'import ROOT'.
43 from ROOT import Belle2 # noqa
45 return instance
46
47
48def get(name):
49 """
50 Function to return particle information (TParticlePDG) from ROOT Database.
51
52 'name' can be either the name of the particle, or a pdg code.
53 Will throw an LookupError of no such particle exists.
54 """
55
56 p = _get_instance().GetParticle(name)
57 if not p:
58 raise LookupError(f"No particle with name '{name}'")
59
60 return p
61
62
63def from_name(name):
64 """
65 Function to return pdg code for the given particle name.
66
67 >>> pdg.from_name("pi+")
68 211
69 """
70
71 return get(name).PdgCode()
72
73
74def from_names(names):
75 """
76 for a list/tuple of particle names, return list of pdg codes.
77
78 >>> pdg.from_names(["e+","e-","gamma"])
79 [-11, 11, 22]
80 """
81
82 assert not isinstance(names, str), 'Argument is not a list!'
83
84 return [from_name(n) for n in names]
85
86
87def to_name(pdg):
88 """
89 Return particle name for given pdg code.
90
91 >>> pdg.to_name(321)
92 K+
93 """
94
95 return get(pdg).GetName()
96
97
98def to_names(pdg_codes):
99 """
100 for a list/tuple of pdg codes, return list of paricle names.
101
102 >>> pdg.to_names([11, -11, -211, 3212])
103 ['e-', 'e+', 'pi-', 'Sigma0']
104 """
105
106 assert not isinstance(pdg_codes, int), 'Argument is not a list!'
107
108 return [to_name(pdg) for pdg in pdg_codes]
109
110
111def conjugate(name):
112 """
113 Function to return name of conjugated particle
114 """
115
116 try:
117 return to_name(-from_name(name))
118 except LookupError:
119 return name
120
121
122def load(filename):
123 """
124 Read particle database from given evtgen pdl file
125 """
126 _get_instance().ReadEvtGenTable(filename)
127
128
129def load_default():
130 """Read default evt.pdl file"""
131 _get_instance().ReadEvtGenTable()
132
133
134def add_particle(name, pdgCode, mass, width, charge, spin, max_width=None, lifetime=0, pythiaID=0,
135 define_anti_particle=False):
136 """
137 Add a new particle to the list of known particles.
138
139 The name cannot contain any whitespace character.
140
141 Args:
142 name (str): name of the particle
143 pdgCode (int): pdg code identifiert for the particle
144 mass (float): mass of the particle in GeV
145 width (float): width of the particle in GeV
146 charge (float): charge of the particle in e
147 spin (float): spin of the particle
148 max_width (float): max width, if omitted 3*width will be used
149 lifetime (float): lifetime in ns, should be 0 as geant4 cannot handle it correctly otherwise
150 pythiaID (int): pythiaID of the particle (if any), if omitted 0 will be used
151 define_anti_particle (bool): if True, an anti-particle with the default name anti-{name} is defined and added.
152 """
153 if lifetime > 0:
154 basf2.B2WARNING("Userdefined particle with non-zero lifetime will not be simulated correctly")
155
156 if max_width is None:
157 # FIXME: is 3 a good default?
158 max_width = width * 3
159
160 particle = _get_instance().AddParticle(name, name, mass, False, width, charge * 3, "userdefined",
161 pdgCode, 0, 0, lifetime, spin, max_width, pythiaID)
162 if particle:
163 basf2.B2INFO(
164 f"Adding new particle '{name}' (pdg={int(pdgCode)}, mass={mass:.3g} GeV, width={width:.3g} GeV, " +
165 f"charge={int(charge)}, spin={Fraction(spin)})")
166
167 if define_anti_particle:
168 anti_particle = _get_instance().AddParticle(
169 f'anti-{name}', f'anti-{name}', mass, False, width, -charge * 3, "userdefined", -pdgCode, 0, 0,
170 lifetime, spin, max_width, pythiaID
171 )
172 particle.SetAntiParticle(anti_particle)
173 basf2.B2INFO(f"Adding new particle 'anti-{name}' as anti-particle of '{name}'")
174
175 return True
176
177 return False
178
179
180def search(name=None, min_mass=None, max_mass=None, name_regex=False, include_width=False):
181 r"""
182 Search for a particles by name or mass or both.
183
184 This function allows to search for particle by name or mass and will return
185 a list of all particles which match the given criteria.
186
187 By default all searches for the name are case insensitive but if ``name``
188 starts with "~" the search will be case sensitive. The "~" will not be part
189 of the search.
190
191 If ``name_regex=True`` the name will be interpreted as a python
192 :py:mod:`regular expression <re>` and the function will return all particles
193 whose names match the expression. If ``name_regex=False`` the function will
194 return a list of all particles containing the given pattern as substring
195 ignoring case with two special cases:
196
197 - if ``name`` begins with "^", only particles beginning with the pattern
198 will be searched. The "^" will not be part of the search.
199 - if ``name`` ends with "$" the pattern will only be matched to the end
200 of the particle name. The "$" will not be part of the search.
201
202 If ``include_width=True`` the search will include all particles if their
203 (mass ± width) is within the given limit. If ``include_width`` is a positive
204 number then the particle will be returned if :math:`m ± n*\Gamma` is within the
205 required range where n is the value of ``include_width`` and :math:`\Gamma` the
206 width of the particle.
207
208 Examples:
209 Return a list of all particles
210
211 >>> search()
212
213 Search for all particles containing a "pi" somewhere in the name and ignore the case
214
215 >>> search("pi")
216
217 Search for all particles beginning with K or k
218
219 >>> search("^K")
220
221 Search for all particles ending with "+" and having a maximal mass of 3 GeV:
222
223 >>> search("+$", max_mass=3.0)
224
225 Search for all particles which contain a capital D and have a minimal mass of 1 GeV
226
227 >>> search("~D", min_mass=1.0)
228
229 Search for all partiles which contain a set of parenthesis containing a number
230
231 >>> search(r".*\‍(\d*\‍).*", name_regex=True)
232
233 Search all particles whose mass ± width covers 1 to 1.2 GeV
234
235 >>> search(min_mass=1.0, max_mass=1.2, include_width=True)
236
237 Search all particles whose mass ± 3*width touches 1 GeV
238
239 >>> search(min_mass=1.0, max_mass=1.0, include_width=3)
240
241
242 Parameters:
243 name (str): Search pattern which will either be matched as a substring
244 or as regular expression if ``name_regex=True``
245 min_mass (float): minimal mass for all returned particles or None for no limit
246 max_mass (float): maximal mass for all returned particles or None for no limit
247 name_regex (bool): if True then ``name`` will be treated as a regular expression
248 include_width (float or bool): if True or >0 include the particles if
249 (mass ± include_width*width) falls within the mass limits
250 """
251
252 pattern = None
253 if name:
254 options = re.IGNORECASE
255 if name[0] == "~":
256 name = name[1:]
257 options = 0
258
259 if not name_regex:
260 if name[0] == "^" and name[-1] == "$":
261 name = f"^{re.escape(name[1:-1])}$"
262 elif name[0] == "^":
263 name = f"^{re.escape(name[1:])}.*"
264 elif name[-1] == "$":
265 name = f".*{re.escape(name[:-1])}$"
266 else:
267 name = f".*{re.escape(name)}.*"
268
269 pattern = re.compile(name, options)
270
271 if include_width is True:
272 include_width = 1
273
274 if include_width < 0:
275 include_width = 0
276
277 result = []
278 for p in _get_instance().ParticleList():
279 if pattern is not None and not pattern.match(p.GetName()):
280 continue
281 m = p.Mass()
282 w = p.Width() * include_width
283 if min_mass is not None and min_mass > (m + w):
284 continue
285 if max_mass is not None and max_mass < (m - w):
286 continue
287 result.append(p)
288
289 return result
static EvtGenDatabasePDG * Instance()
Instance method that loads the EvtGen table.
def from_names(names)
Definition: pdg.py:74
def to_names(pdg_codes)
Definition: pdg.py:98
def to_name(pdg)
Definition: pdg.py:87
def from_name(name)
Definition: pdg.py:63