Belle II Software  light-2403-persian
pdg.py
1 #!/usr/bin/env python3
2 
3 
10 
11 """
12 pdg - access particle definitions
13 ---------------------------------
14 
15 This module helps to access particle definitions. When the software is loaded a
16 list of known particles is read from the EvtGen particle definition file
17 :file:`framework/particledb/data/evt.pdl`. This file contains all well-known
18 standard-model particles and their properties: mass, width or lifetime, charge,
19 spin.
20 ...
21 
22 This module allows to easily access this information (see `get`) or if necessary
23 add new particles using `add_particle` and even replace the whole particle
24 definition list using `load`.
25 
26 It also provides simple getters to convert `PDG codes`_ into particle names and
27 vice versa for use with modules which require a list of PDG codes for the
28 particles 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 
33 import re
34 import basf2
35 from fractions import Fraction
36 
37 
38 def _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 
48 def 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 
63 def 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 
74 def 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 
87 def 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 
98 def 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 
111 def 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 
122 def load(filename):
123  """
124  Read particle database from given evtgen pdl file
125  """
126  _get_instance().ReadEvtGenTable(filename)
127 
128 
129 def load_default():
130  """Read default evt.pdl file"""
131  _get_instance().ReadEvtGenTable()
132 
133 
134 def 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 
180 def 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.