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