Top

flavio.parameters module

Functions for parsing the parameter data files

"""Functions for parsing the parameter data files"""

import yaml
import pkgutil
from flavio.classes import *
from flavio.statistics.probability import *
from flavio._parse_errors import errors_from_string
import flavio
import re
from flavio.measurements import _fix_correlation_matrix
from math import sqrt
from particle import Particle, data as p_data


def _read_yaml_object_metadata(obj, constraints):
    parameters = yaml.safe_load(obj)
    for parameter_name, info in parameters.items():
        p = Parameter(parameter_name)
        if 'description' in info and info['description'] is not None:
            p.description = info['description']
        if 'tex' in info and info['tex'] is not None:
            p.tex = info['tex']

def read_file_metadata(filename, constraints):
    """Read parameter values from a YAML file."""
    with open(filename, 'r') as f:
        _read_yaml_object_metadata(f, constraints)

def _read_yaml_object_values(obj, constraints):
    parameters = yaml.safe_load(obj)
    for parameter_name, value in parameters.items():
        p = Parameter[parameter_name] # this will raise an error if the parameter doesn't exist!
        if isinstance(value, dict):
            constraints.set_constraint(parameter_name, constraint_dict=value)
        else:
            constraints.set_constraint(parameter_name, value)

def _read_yaml_object_new(obj):
    """Read parameter constraints from a YAML stream or file that are compatible
    with the format generated by the `get_yaml` method of
    `flavio.classes.ParameterConstraints`."""
    parameters = yaml.safe_load(obj)
    return ParameterConstraints.from_yaml_dict(parameters)

def _read_yaml_object_values_correlated(obj, constraints):
    list_ = yaml.safe_load(obj)
    for parameter_group in list_:
        parameter_names = []
        central_values = []
        errors = []
        for dict_list in parameter_group['values']:
            parameter_name, value = list(dict_list.items())[0]
            Parameter[parameter_name] # this will raise an error if the parameter doesn't exist!
            parameter_names.append(parameter_name)
            error_dict = errors_from_string(value)
            central_values.append(error_dict['central_value'])
            squared_error = 0.
            for sym_err in error_dict['symmetric_errors']:
                squared_error += sym_err**2
            for asym_err in error_dict['asymmetric_errors']:
                squared_error += asym_err[0]*asym_err[1]
            errors.append(sqrt(squared_error))
        correlation = _fix_correlation_matrix(parameter_group['correlation'], len(parameter_names))
        covariance = np.outer(np.asarray(errors), np.asarray(errors))*correlation
        if not np.all(np.linalg.eigvals(covariance) > 0):
            # if the covariance matrix is not positive definite, try a dirty trick:
            # multiply all the correlations by 0.99.
            n_dim = len(correlation)
            correlation = (correlation - np.eye(n_dim))*0.99 + np.eye(n_dim)
            covariance = np.outer(np.asarray(errors), np.asarray(errors))*correlation
            # if it still isn't positive definite, give up.
            assert np.all(np.linalg.eigvals(covariance) > 0), "The covariance matrix is not positive definite!" + str(covariance)
        constraints.add_constraint(parameter_names, MultivariateNormalDistribution(central_values, covariance))

def read_file(filename):
    """Read parameter values from a YAML file in the format generated by the
    `get_yaml` method of the `ParameterConstraints` class, returning a
    `ParameterConstraints` instance."""
    with open(filename, 'r') as f:
        return _read_yaml_object_new(f)

def read_file_values(filename, constraints):
    """Read parameter values from a YAML file."""
    with open(filename, 'r') as f:
        _read_yaml_object_values(f, constraints)

def read_file_values_correlated(filename, constraints):
    """Read parameter values from a YAML file."""
    with open(filename, 'r') as f:
        _read_yaml_object_values_correlated(f, constraints)

def write_file(filename, constraints):
    """Write parameter constraints to a YAML file."""
    with open(filename, 'w') as f:
        yaml.dump(constraints.get_yaml_dict(), f)

class FlavioParticle(Particle):
    """This class extends the `particle.Particle` class.

    Additional class methods
    ------------------------
    - from_flavio_name(flavio_name)
      returns a class instance for a given `flavio_name`
    - flavio_all()
      returns a set of all class instances used in flavio

    Additional properties
    ---------------------
    - flavio_name
      the particle name as used in flavio if defined, otherwise `None`
    - latex_name_simplified
      a simplified version of the latex name returned by `latex_name`
    - flavio_m
      a tuple with data on the particle mass as used in flavio, containing
      entries `name`, `tex`, `description`, `central`, `right`, `left`
    - flavio_tau
      a tuple with data on the particle lifetime as used in flavio, containing
      entries `name`, `tex`, `description`, `central`, `right`, `left`
    """

    PDG_PARTICLES = {
        'Bs': 531,
        'Bc': 541,
        'Bs*': 533,
        'B*+': 523,
        'B*0': 513,
        'B+': 521,
        'B0': 511,
        'Ds': 431,
        'Ds*': 433,
        'D+': 411,
        'D0': 421,
        'h': 25,
        'J/psi': 443,
        'KL': 130,
        'KS': 310,
        'K*+': 323,
        'K*0': 313,
        'K+': 321,
        'K0': 311,
        'Lambda': 3122,
        'Lambda(1520)': 3124,
        'Lambdab': 5122,
        'Lambdac': 4122,
        'omega': 223,
        'D*0': 423,
        'D*+': 413,
        'W': 24,
        'Z': 23,
        'e': 11,
        'eta': 221,
        'f0': 9010221,
        'mu': 13,
        'phi': 333,
        'pi+': 211,
        'pi0': 111,
        'psi(2S)': 100443,
        'rho+': 213,
        'rho0': 113,
        't': 6,
        'tau': 15,
        'u': 2,
        'p': 2212,
        'n': 2112,
        'Upsilon(1S)' : 553,
        'Upsilon(2S)' : 100553,
        'Upsilon(3S)' : 200553,
        'eta_c(1S)' : 441,
        'chi_c0(1P)' : 10441,
        'chi_b0(1P)' : 10551,
        'chi_b0(2P)' : 110551,
    }
    _pdg_particles_inv = {v:k for k,v in PDG_PARTICLES.items()}
    _pdg_tex_regex = re.compile(
        r"^([A-Za-z\\/]+)" # latin or greek letters or slash
        r"(?:_\{(.*?)\})*" # _{...}
        r"(?:\^\{(.*?)\})*" # ^{...}
        r"(?:\((.*?)\))*" # (...)
        r"(?:\^\{(.*?)\})*" # ^{...}
    )

    @classmethod
    def from_flavio_name(cls, flavio_name):
        return cls.from_pdgid(cls.PDG_PARTICLES[flavio_name])

    @classmethod
    def flavio_all(cls):
        return {particle for particle in cls.all() if particle.flavio_name}

    @property
    def flavio_name(self):
        return self._pdg_particles_inv.get(self.pdgid, None)

    @property
    def latex_name_simplified(self):
        m = self._pdg_tex_regex.match(self.latex_name)
        if m is None:
            return self.latex_name
        name = m.group(1)
        sub = m.group(2)
        sup = (m.group(3) or '') + (m.group(5) or '')
        par = m.group(4)
        if sub or name in ('W', 'Z', 'H', 'e', '\\mu', '\\tau'):
            # remove superscripts +-0 and keep only *
            sup = '*' if '*' in sup else ''
        if not sub and par and not par.isdigit() and name != 'J/\\psi':
            # subscript absent and parantheses contain letter but not for 'J/\\psi'
            sub = par
        sub_tex = r'_{' + sub + r'}' if sub else ''
        sup_tex = r'^{' + sup + r'}' if sup else ''
        return name + sub_tex + sup_tex

    @property
    def flavio_m(self):
        name = 'm_' + self.flavio_name
        tex = r'$m_{' + self.latex_name_simplified + '}$'
        pole_mass = ' quark pole' if self.name == 't' else ''
        description = r'${}${} mass'.format(
            self.latex_name_simplified, pole_mass
        )
        central = self.mass*1e-3
        right = self.mass_upper*1e-3
        left = self.mass_lower*1e-3
        return name, tex, description, central, right, left

    @property
    def flavio_tau(self):
        if {self.width, self.width_upper, self.width_lower} & {None, 0}:
            return None
        name = 'tau_' + self.flavio_name
        tex = r'$\tau_{' + self.latex_name_simplified + '}$'
        description = r'${}$ lifetime'.format(self.latex_name_simplified)
        G_central = self.width*1e-3
        G_right = self.width_upper*1e-3
        G_left = self.width_lower*1e-3
        central = 1/G_central # life time = 1/width
        right = G_right/G_central**2
        left = G_left/G_central**2
        return name, tex, description, central, right, left

def read_pdg(year, constraints):
    """Read particle masses and widths from the PDG data file of a given year."""
    FlavioParticle.load_table(p_data.basepath / f"particle{year}.csv")
    for particle in FlavioParticle.flavio_all():
        for data in (particle.flavio_m, particle.flavio_tau):
            if data is None:
                continue
            name, tex, description, central, right, left = data
            try:
                # if parameter already exists, remove existing constraints on it
                p = Parameter[name]
                constraints.remove_constraint(name)
            except KeyError:
                # otherwise, create it
                p = Parameter(name)
            p.tex = tex
            p.description = description
            if right == left:
                constraints.add_constraint([name],
                    NormalDistribution(central, right))
            else:
                constraints.add_constraint([name],
                    AsymmetricNormalDistribution(central,
                    right_deviation=right, left_deviation=left))



############### Read default parameters ###################

# Create the object
default_parameters = ParameterConstraints()

# read default parameters
default_parameters.read_default()

Module variables

var class_from_string

var config

var default_parameters

Functions

def read_file(

filename)

Read parameter values from a YAML file in the format generated by the get_yaml method of the ParameterConstraints class, returning a ParameterConstraints instance.

def read_file(filename):
    """Read parameter values from a YAML file in the format generated by the
    `get_yaml` method of the `ParameterConstraints` class, returning a
    `ParameterConstraints` instance."""
    with open(filename, 'r') as f:
        return _read_yaml_object_new(f)

def read_file_metadata(

filename, constraints)

Read parameter values from a YAML file.

def read_file_metadata(filename, constraints):
    """Read parameter values from a YAML file."""
    with open(filename, 'r') as f:
        _read_yaml_object_metadata(f, constraints)

def read_file_values(

filename, constraints)

Read parameter values from a YAML file.

def read_file_values(filename, constraints):
    """Read parameter values from a YAML file."""
    with open(filename, 'r') as f:
        _read_yaml_object_values(f, constraints)

def read_file_values_correlated(

filename, constraints)

Read parameter values from a YAML file.

def read_file_values_correlated(filename, constraints):
    """Read parameter values from a YAML file."""
    with open(filename, 'r') as f:
        _read_yaml_object_values_correlated(f, constraints)

def read_pdg(

year, constraints)

Read particle masses and widths from the PDG data file of a given year.

def read_pdg(year, constraints):
    """Read particle masses and widths from the PDG data file of a given year."""
    FlavioParticle.load_table(p_data.basepath / f"particle{year}.csv")
    for particle in FlavioParticle.flavio_all():
        for data in (particle.flavio_m, particle.flavio_tau):
            if data is None:
                continue
            name, tex, description, central, right, left = data
            try:
                # if parameter already exists, remove existing constraints on it
                p = Parameter[name]
                constraints.remove_constraint(name)
            except KeyError:
                # otherwise, create it
                p = Parameter(name)
            p.tex = tex
            p.description = description
            if right == left:
                constraints.add_constraint([name],
                    NormalDistribution(central, right))
            else:
                constraints.add_constraint([name],
                    AsymmetricNormalDistribution(central,
                    right_deviation=right, left_deviation=left))

def write_file(

filename, constraints)

Write parameter constraints to a YAML file.

def write_file(filename, constraints):
    """Write parameter constraints to a YAML file."""
    with open(filename, 'w') as f:
        yaml.dump(constraints.get_yaml_dict(), f)

Classes

class FlavioParticle

This class extends the particle.Particle class.

Additional class methods

  • from_flavio_name(flavio_name) returns a class instance for a given flavio_name
  • flavio_all() returns a set of all class instances used in flavio

Additional properties

  • flavio_name the particle name as used in flavio if defined, otherwise None
  • latex_name_simplified a simplified version of the latex name returned by latex_name
  • flavio_m a tuple with data on the particle mass as used in flavio, containing entries name, tex, description, central, right, left
  • flavio_tau a tuple with data on the particle lifetime as used in flavio, containing entries name, tex, description, central, right, left
class FlavioParticle(Particle):
    """This class extends the `particle.Particle` class.

    Additional class methods
    ------------------------
    - from_flavio_name(flavio_name)
      returns a class instance for a given `flavio_name`
    - flavio_all()
      returns a set of all class instances used in flavio

    Additional properties
    ---------------------
    - flavio_name
      the particle name as used in flavio if defined, otherwise `None`
    - latex_name_simplified
      a simplified version of the latex name returned by `latex_name`
    - flavio_m
      a tuple with data on the particle mass as used in flavio, containing
      entries `name`, `tex`, `description`, `central`, `right`, `left`
    - flavio_tau
      a tuple with data on the particle lifetime as used in flavio, containing
      entries `name`, `tex`, `description`, `central`, `right`, `left`
    """

    PDG_PARTICLES = {
        'Bs': 531,
        'Bc': 541,
        'Bs*': 533,
        'B*+': 523,
        'B*0': 513,
        'B+': 521,
        'B0': 511,
        'Ds': 431,
        'Ds*': 433,
        'D+': 411,
        'D0': 421,
        'h': 25,
        'J/psi': 443,
        'KL': 130,
        'KS': 310,
        'K*+': 323,
        'K*0': 313,
        'K+': 321,
        'K0': 311,
        'Lambda': 3122,
        'Lambda(1520)': 3124,
        'Lambdab': 5122,
        'Lambdac': 4122,
        'omega': 223,
        'D*0': 423,
        'D*+': 413,
        'W': 24,
        'Z': 23,
        'e': 11,
        'eta': 221,
        'f0': 9010221,
        'mu': 13,
        'phi': 333,
        'pi+': 211,
        'pi0': 111,
        'psi(2S)': 100443,
        'rho+': 213,
        'rho0': 113,
        't': 6,
        'tau': 15,
        'u': 2,
        'p': 2212,
        'n': 2112,
        'Upsilon(1S)' : 553,
        'Upsilon(2S)' : 100553,
        'Upsilon(3S)' : 200553,
        'eta_c(1S)' : 441,
        'chi_c0(1P)' : 10441,
        'chi_b0(1P)' : 10551,
        'chi_b0(2P)' : 110551,
    }
    _pdg_particles_inv = {v:k for k,v in PDG_PARTICLES.items()}
    _pdg_tex_regex = re.compile(
        r"^([A-Za-z\\/]+)" # latin or greek letters or slash
        r"(?:_\{(.*?)\})*" # _{...}
        r"(?:\^\{(.*?)\})*" # ^{...}
        r"(?:\((.*?)\))*" # (...)
        r"(?:\^\{(.*?)\})*" # ^{...}
    )

    @classmethod
    def from_flavio_name(cls, flavio_name):
        return cls.from_pdgid(cls.PDG_PARTICLES[flavio_name])

    @classmethod
    def flavio_all(cls):
        return {particle for particle in cls.all() if particle.flavio_name}

    @property
    def flavio_name(self):
        return self._pdg_particles_inv.get(self.pdgid, None)

    @property
    def latex_name_simplified(self):
        m = self._pdg_tex_regex.match(self.latex_name)
        if m is None:
            return self.latex_name
        name = m.group(1)
        sub = m.group(2)
        sup = (m.group(3) or '') + (m.group(5) or '')
        par = m.group(4)
        if sub or name in ('W', 'Z', 'H', 'e', '\\mu', '\\tau'):
            # remove superscripts +-0 and keep only *
            sup = '*' if '*' in sup else ''
        if not sub and par and not par.isdigit() and name != 'J/\\psi':
            # subscript absent and parantheses contain letter but not for 'J/\\psi'
            sub = par
        sub_tex = r'_{' + sub + r'}' if sub else ''
        sup_tex = r'^{' + sup + r'}' if sup else ''
        return name + sub_tex + sup_tex

    @property
    def flavio_m(self):
        name = 'm_' + self.flavio_name
        tex = r'$m_{' + self.latex_name_simplified + '}$'
        pole_mass = ' quark pole' if self.name == 't' else ''
        description = r'${}${} mass'.format(
            self.latex_name_simplified, pole_mass
        )
        central = self.mass*1e-3
        right = self.mass_upper*1e-3
        left = self.mass_lower*1e-3
        return name, tex, description, central, right, left

    @property
    def flavio_tau(self):
        if {self.width, self.width_upper, self.width_lower} & {None, 0}:
            return None
        name = 'tau_' + self.flavio_name
        tex = r'$\tau_{' + self.latex_name_simplified + '}$'
        description = r'${}$ lifetime'.format(self.latex_name_simplified)
        G_central = self.width*1e-3
        G_right = self.width_upper*1e-3
        G_left = self.width_lower*1e-3
        central = 1/G_central # life time = 1/width
        right = G_right/G_central**2
        left = G_left/G_central**2
        return name, tex, description, central, right, left

Ancestors (in MRO)

Class variables

var PDG_PARTICLES

Static methods

def __init__(

self, pdgid, pdg_name, mass=-1.0, mass_upper=-1.0, mass_lower=-1.0, width=-1.0, width_upper=-1.0, width_lower=-1.0, three_charge=<Charge.u: 50>, I=None, G=<Parity.u: 5>, P=<Parity.u: 5>, C=<Parity.u: 5>, anti_flag=<Inv.Same: 0>, rank=0, status=<Status.NotInPDT: 4>, quarks='', latex_name='Unknown')

Method generated by attrs for class Particle.

def __init__(self, pdgid, pdg_name, mass=attr_dict['mass'].default, mass_upper=attr_dict['mass_upper'].default, mass_lower=attr_dict['mass_lower'].default, width=attr_dict['width'].default, width_upper=attr_dict['width_upper'].default, width_lower=attr_dict['width_lower'].default, three_charge=attr_dict['_three_charge'].default, I=attr_dict['I'].default, G=attr_dict['G'].default, P=attr_dict['P'].default, C=attr_dict['C'].default, anti_flag=attr_dict['anti_flag'].default, rank=attr_dict['rank'].default, status=attr_dict['status'].default, quarks=attr_dict['quarks'].default, latex_name=attr_dict['latex_name'].default):
    self.pdgid = __attr_converter_pdgid(pdgid)
    self.pdg_name = pdg_name
    self.mass = __attr_converter_mass(mass)
    self.mass_upper = __attr_converter_mass_upper(mass_upper)
    self.mass_lower = __attr_converter_mass_lower(mass_lower)
    self.width = __attr_converter_width(width)
    self.width_upper = __attr_converter_width_upper(width_upper)
    self.width_lower = __attr_converter_width_lower(width_lower)
    self._three_charge = __attr_converter__three_charge(three_charge)
    self.I = __attr_converter_I(I)
    self.G = __attr_converter_G(G)
    self.P = __attr_converter_P(P)
    self.C = __attr_converter_C(C)
    self.anti_flag = __attr_converter_anti_flag(anti_flag)
    self.rank = rank
    self.status = __attr_converter_status(status)
    self.quarks = __attr_converter_quarks(quarks)
    self.latex_name = latex_name

def describe(

self)

Make a nice high-density string for a particle's properties.

def describe(self) -> str:
    "Make a nice high-density string for a particle's properties."
    if self.pdgid == 0:
        return "Name: Unknown"
    G = Parity_undo[self.G]
    C = Parity_undo[self.C]
    Q = self._str_charge()
    P = Parity_undo[self.P]
    mass = self._str_mass()
    width_or_lifetime = self._width_or_lifetime()
    latex_name = self._repr_latex_()
    val = f"""Name: {self!s:<14} ID: {self.pdgid:<12} Latex: {latex_name}
  = {mass}
th_or_lifetime}
harge)        = {Q:<6}  J (total angular) = {self.J!s:<7}  P (space parity) = {P}
harge parity) = {C:<6}  I (isospin)       = {self.I!s:<7}  G (G-parity)     = {G}
    if self.spin_type != SpinType.Unknown:
        val += f"    SpinType: {self.spin_type!s}\n"
    if self.quarks:
        val += f"    Quarks: {self.quarks}\n"
    val += f"    Antiparticle name: {self.invert().name} (antiparticle status: {self.anti_flag.name})"
    return val

def invert(

self)

Get the antiparticle.

def invert(self: Self) -> Self:
    "Get the antiparticle."
    if self.anti_flag == Inv.Barred or (
        self.anti_flag == Inv.ChargeInv and self.three_charge != Charge.o
    ):
        return self.from_pdgid(-self.pdgid)
    return copy(self)

Instance variables

var C

var G

var I

var J

The total spin J quantum number.

Note that the returned value corresponds to that effectively encoded in the particle PDG ID.

var L

The orbital angular momentum L quantum number (None if not a meson).

Note that the returned value corresponds to that effectively encoded in the particle PDG ID.

var P

var S

The spin S quantum number (None if not a meson).

Note that the returned value corresponds to that effectively encoded in the particle PDG ID.

var anti_flag

var charge

The particle charge, in units of the positron charge.

Design note: the particle charge can also be retrieved from the particle PDG ID. To allow for user-defined particles, it is necessary to rather store the particle charge in the data tables themselves. Consistency of both ways of retrieving the particle charge is guaranteed for all PDG table particles.

var ctau

The particle c*tau, in millimeters.

None is returned if the particle width (stored in the DB) is unknown.

var evtgen_name

This is the name used in EvtGen.

var flavio_m

var flavio_name

var flavio_tau

var html_name

This is the name in HTML.

var is_name_barred

Check to see if particle is inverted (hence is it an antiparticle) and has a bar in its name.

var is_self_conjugate

Is the particle self-conjugate, i.e. its own antiparticle?

var is_unflavoured_meson

Is the particle a light non-strange meson or a quarkonium?

Indeed, unflavoured mesons are either: all light mesons with no net flavour quantum number (S = C = B = T = 0), or quarkonia, which are self-conjugate heavy flavour mesons, with all their flavour quantum numbers and charge equal to zero.

var latex_name

var latex_name_simplified

var lifetime

The particle lifetime, in nanoseconds.

None is returned if the particle width (stored in the DB) is unknown.

var mass

var mass_lower

var mass_upper

var name

The nice name, with charge added, and a tilde for an antiparticle, if relevant.

var pdg_name

var pdgid

var programmatic_name

This name could be used for a variable name.

var quarks

var rank

var spin_type

Access the SpinType enum.

Note that this is relevant for bosons only. SpinType.NonDefined is returned otherwise.

var status

var three_charge

Three times the particle charge (charge * 3), in units of the positron charge.

var width

var width_lower

var width_upper

Methods

def all(

cls)

Access, hence get hold of, the internal particle data CSV table, loading it from the default location if no table has yet been loaded. All Particle (instances) are returned as a list.

@classmethod
def all(cls: type[Self]) -> list[Self]:
    """
    Access, hence get hold of, the internal particle data CSV table,
    loading it from the default location if no table has yet been loaded.
    All `Particle` (instances) are returned as a list.
    """
    if not cls.table_loaded():
        cls.load_table()
    return cls._table if cls._table is not None else []  # type: ignore[return-value]

def empty(

cls)

Make a new empty particle.

@classmethod
def empty(cls: type[Self]) -> Self:
    "Make a new empty particle."
    return cls(0, "Unknown", anti_flag=Inv.Same)

def findall(

cls, filter_fn=None, **search_terms)

Search for a particle, returning a list of candidates.

The first and only positional argument is given to each particle candidate, and returns True/False. Example:

>>> Particle.findall(lambda p: 'p' in p.name)    # doctest: +SKIP
# Returns list of all particles with p somewhere in name

You can pass particle=True/False to force a particle or antiparticle. If this is not callable, it will do a "fuzzy" search on the name. So this is identical:

>>> Particle.findall('p')    # doctest: +SKIP
# Returns list of all particles with p somewhere in name (same as example above)

You can also pass keyword arguments, which are either called with the matching property if they are callable, or are compared if they are not. This would do an exact search on the name, instead of a fuzzy search:

Returns proton and antiproton only

Particle.findall(pdg_name='p') # doctest: +NORMALIZE_WHITESPACE [, , , ]

Returns proton only

Particle.findall(pdg_name='p', particle=True) # doctest: +NORMALIZE_WHITESPACE [, ]

Versatile searches require a (lambda) function as argument:

Get all neutral beauty hadrons

Particle.findall(lambda p: p.pdgid.has_bottom and p.charge==0) # doctest: +SKIP

Trivially find all pseudoscalar charm mesons

Particle.findall(lambda p: p.pdgid.is_meson and p.pdgid.has_charm and p.spin_type==SpinType.PseudoScalar) # doctest: +SKIP

See also finditer, which provides an iterator instead of a list.

@classmethod
def findall(
    cls: type[Self],
    filter_fn: Callable[[Particle], bool] | None = None,
    *,
    particle: bool | None = None,
    **search_terms: Any,
) -> list[Self]:
    """
    Search for a particle, returning a list of candidates.
    The first and only positional argument is given to each particle
    candidate, and returns True/False. Example:
        >>> Particle.findall(lambda p: 'p' in p.name)    # doctest: +SKIP
        # Returns list of all particles with p somewhere in name
    You can pass particle=True/False to force a particle or antiparticle.
    If this is not callable, it will do a "fuzzy" search on the name. So this is identical:
        >>> Particle.findall('p')    # doctest: +SKIP
        # Returns list of all particles with p somewhere in name (same as example above)
    You can also pass keyword arguments, which are either called with the
    matching property if they are callable, or are compared if they are not.
    This would do an exact search on the name, instead of a fuzzy search:
       >>> # Returns proton and antiproton only
       >>> Particle.findall(pdg_name='p')    # doctest: +NORMALIZE_WHITESPACE
       [<Particle: name="p", pdgid=2212, mass=938.2720882 ± 0.0000003 MeV>,
        <Particle: name="p~", pdgid=-2212, mass=938.2720882 ± 0.0000003 MeV>,
        <Particle: name="p", pdgid=1000010010, mass=938.2720882 ± 0.0000003 MeV>,
        <Particle: name="p~", pdgid=-1000010010, mass=938.2720882 ± 0.0000003 MeV>]
       >>> # Returns proton only
       >>> Particle.findall(pdg_name='p', particle=True)    # doctest: +NORMALIZE_WHITESPACE
       [<Particle: name="p", pdgid=2212, mass=938.2720882 ± 0.0000003 MeV>,
       <Particle: name="p", pdgid=1000010010, mass=938.2720882 ± 0.0000003 MeV>]
    Versatile searches require a (lambda) function as argument:
    >>> # Get all neutral beauty hadrons
    >>> Particle.findall(lambda p: p.pdgid.has_bottom and p.charge==0)    # doctest: +SKIP
    >>>
    >>> # Trivially find all pseudoscalar charm mesons
    >>> Particle.findall(lambda p: p.pdgid.is_meson and p.pdgid.has_charm and p.spin_type==SpinType.PseudoScalar)  # doctest: +SKIP
    See also ``finditer``, which provides an iterator instead of a list.
    """
    return list(
        cls.finditer(filter_fn=filter_fn, particle=particle, **search_terms)
    )

def finditer(

cls, filter_fn=None, **search_terms)

Search for a particle, returning an iterator of candidates.

The first and only positional argument is given to each particle candidate, and returns True/False. Example:

>>> Particle.finditer(lambda p: 'p' in p.name)    # doctest: +SKIP
# Returns iterator of all particles with p somewhere in name

You can pass particle=True/False to force a particle or antiparticle. If this is not callable, it will do a "fuzzy" search on the name. So this is identical:

>>> Particle.finditer('p')    # doctest: +SKIP
# Returns literator of all particles with p somewhere in name (same as example above)

You can also pass keyword arguments, which are either called with the matching property if they are callable, or are compared if they are not. This would do an exact search on the name, instead of a fuzzy search:

Returns proton and antiproton only

Particle.finditer(pdg_name='p') # doctest: +SKIP # Returns iterator

Returns proton only

Particle.finditer(pdg_name='p', particle=True) # doctest: +SKIP # Returns iterator

Versatile searches require a (lambda) function as argument:

Get all neutral beauty hadrons

Particle.finditer(lambda p: p.pdgid.has_bottom and p.charge==0) # doctest: +SKIP

Trivially find all pseudoscalar charm mesons

Particle.finditer(lambda p: p.pdgid.is_meson and p.pdgid.has_charm and p.spin_type==SpinType.PseudoScalar) # doctest: +SKIP

See also findall, which returns the same thing, but as a list.

@classmethod
def finditer(
    cls: type[Self],
    filter_fn: Callable[[Particle], bool] | str | None = None,
    *,
    particle: bool | None = None,
    **search_terms: Any,
) -> Iterator[Self]:
    """
    Search for a particle, returning an iterator of candidates.
    The first and only positional argument is given to each particle
    candidate, and returns True/False. Example:
        >>> Particle.finditer(lambda p: 'p' in p.name)    # doctest: +SKIP
        # Returns iterator of all particles with p somewhere in name
    You can pass particle=True/False to force a particle or antiparticle.
    If this is not callable, it will do a "fuzzy" search on the name. So this is identical:
        >>> Particle.finditer('p')    # doctest: +SKIP
        # Returns literator of all particles with p somewhere in name (same as example above)
    You can also pass keyword arguments, which are either called with the
    matching property if they are callable, or are compared if they are not.
    This would do an exact search on the name, instead of a fuzzy search:
       >>> # Returns proton and antiproton only
       >>> Particle.finditer(pdg_name='p')    # doctest: +SKIP
       # Returns iterator
       >>> # Returns proton only
       >>> Particle.finditer(pdg_name='p', particle=True)    # doctest: +SKIP
       # Returns iterator
    Versatile searches require a (lambda) function as argument:
    >>> # Get all neutral beauty hadrons
    >>> Particle.finditer(lambda p: p.pdgid.has_bottom and p.charge==0)    # doctest: +SKIP
    >>>
    >>> # Trivially find all pseudoscalar charm mesons
    >>> Particle.finditer(lambda p: p.pdgid.is_meson and p.pdgid.has_charm and p.spin_type==SpinType.PseudoScalar)  # doctest: +SKIP
    See also ``findall``, which returns the same thing, but as a list.
    """
    # Filter out values
    for item in cls.all():
        # At this point, continue if a match fails
        # particle=True is particle, False is antiparticle, and None is both
        if particle is not None:
            if particle and int(item.pdgid) < 0:
                continue
            if (not particle) and int(item.pdgid) > 0:
                continue
        # If a filter function is passed, evaluate and skip if False
        if filter_fn is not None:
            if callable(filter_fn):
                # Just skip exceptions, which are there for good reasons
                # Example: calls to Particle.ctau for particles given
                #          default negative, and hence invalid, widths
                try:
                    if not filter_fn(item):
                        continue
                except TypeError:  # skip checks such as 'lambda p: p.width > 0',
                    continue  # which fail when width=None
            elif filter_fn not in item.name:
                continue
        # At this point, if you break, you will not add a match
        for term, value in search_terms.items():
            # If pvalue cannot be accessed, skip this particle
            # (invalid lifetime, for example)
            try:
                pvalue = getattr(item, term)
            except ValueError:
                break
            # Callables are supported
            if callable(value):
                try:
                    if not value(pvalue):
                        break
                except TypeError:  # catch issues with None values
                    break
            # And, finally, just compare if nothing else matched
            elif pvalue != value:
                break
        # If the loop was not broken
        else:
            yield item

def flavio_all(

cls)

@classmethod
def flavio_all(cls):
    return {particle for particle in cls.all() if particle.flavio_name}

def from_evtgen_name(

cls, name)

Get a particle from an EvtGen particle name, as in .dec decay files.

Raises

ParticleNotFound If from_pdgid, internally called, returns no match. MatchingIDNotFound If the matching EvtGen name - PDG ID done internally is unsuccessful.

@classmethod
def from_evtgen_name(cls: type[Self], name: str) -> Self:
    """
    Get a particle from an EvtGen particle name, as in .dec decay files.
    Raises
    ------
    ParticleNotFound
        If `from_pdgid`, internally called, returns no match.
    MatchingIDNotFound
        If the matching EvtGen name - PDG ID done internally is unsuccessful.
    """
    return cls.from_pdgid(EvtGenName2PDGIDBiMap[name])

def from_flavio_name(

cls, flavio_name)

@classmethod
def from_flavio_name(cls, flavio_name):
    return cls.from_pdgid(cls.PDG_PARTICLES[flavio_name])

def from_name(

cls, name)

Get a particle from its name.

Raises

ParticleNotFound If no particle matches the input name uniquely and exactly.

@classmethod
def from_name(cls: type[Self], name: str) -> Self:
    """
    Get a particle from its name.
    Raises
    ------
    ParticleNotFound
        If no particle matches the input name uniquely and exactly.
    """
    try:
        (particle,) = cls.finditer(
            name=name
        )  # throws an error if < 1 or > 1 particle is found
        return particle
    except ValueError:
        raise ParticleNotFound(f"Could not find name {name!r}") from None

def from_nucleus_info(

cls, z, a, anti=False, i=0, l_strange=0)

Get a nucleus particle from the proton Z and atomic mass A numbers.

As described in the PDG numbering scheme: "To avoid ambiguities, nucleus codes should not be applied to a single hadron, such as the p, n or the Λ, where quark-contents-based codes already exist."

Number of neutrons is equal to a-z. The PDG ID format is ±10LZZZAAAI, see the is_nucleus() function in submodule particle.pdgid.functions.py.

Parameters

z: int Atomic number Z (number of protons). Maximum three decimal digits. a: int Atomic mass number A (total number of baryons). Maximum three decimal digits. anti: bool, optional, defaults to False (not antimatter). Whether the nucleus should be antimatter. i: int, optional, default is 0 Isomer level I. I=0 corresponds to the ground-state. I>0 are excitations. Maximum is one decimal digit. l_strange: int, optional, default is 0 Total number of strange quarks. Maximum is one decimal digit.

Raises

ParticleNotFound If from_pdgid, internally called, returns no match. InvalidParticle If the input combination is invalid.

@classmethod
def from_nucleus_info(
    cls: type[Self],
    z: int,
    a: int,
    anti: bool = False,
    i: int = 0,
    l_strange: int = 0,
) -> Self:
    """
    Get a nucleus particle from the proton Z and atomic mass A numbers.
    As described in the PDG numbering scheme:
    "To avoid ambiguities, nucleus codes should not be applied to a single hadron, such as the p, n
    or the Λ, where quark-contents-based codes already exist."
    Number of neutrons is equal to a-z.
    The PDG ID format is ±10LZZZAAAI, see the `is_nucleus()` function in submodule
    `particle.pdgid.functions.py`.
    Parameters
    ----------
    z: int
        Atomic number Z (number of protons).
        Maximum three decimal digits.
    a: int
        Atomic mass number A (total number of baryons).
        Maximum three decimal digits.
    anti: bool, optional, defaults to False (not antimatter).
        Whether the nucleus should be antimatter.
    i: int, optional, default is 0
        Isomer level I. I=0 corresponds to the ground-state.
        I>0 are excitations.
        Maximum is one decimal digit.
    l_strange: int, optional, default is 0
        Total number of strange quarks.
        Maximum is one decimal digit.
    Raises
    ------
    ParticleNotFound
        If `from_pdgid`, internally called, returns no match.
    InvalidParticle
        If the input combination is invalid.
    """
    if l_strange < 0 or l_strange > 9:
        raise InvalidParticle(
            f"Number of strange quarks l={l_strange} is invalid. Must be 0 <= l <= 9."
        )
    if z < 0 or z > 999:
        raise InvalidParticle(
            f"Atomic number Z={z} is invalid. Must be 0 <= A <= 999."
        )
    if a < 0 or a > 999:
        raise InvalidParticle(
            f"Atomic mass number A={a} is invalid. Must be 0 <= A <= 999."
        )
    if i < 0 or i > 9:
        raise InvalidParticle(
            f"Isomer level I={i} is invalid. Must be 0 <= I <= 9."
        )
    if z > a:
        raise InvalidParticle(
            f"Nucleus A={a}, Z={z} is invalid. Z must be smaller or equal to Z."
        )
    pdgid = int(1e9 + l_strange * 1e5 + z * 1e4 + a * 10 + i)
    if anti:
        return cls.from_pdgid(-pdgid)
    return cls.from_pdgid(pdgid)

def from_pdgid(

cls, value)

Get a particle from a PDGID. Uses by default the package extended PDG data table.

Raises

InvalidParticle If the input PDG ID is an invalid identification code. ParticleNotFound If no matching PDG ID is found in the loaded data table(s).

@classmethod
def from_pdgid(cls: type[Self], value: SupportsInt) -> Self:
    """
    Get a particle from a PDGID. Uses by default the package
    extended PDG data table.
    Raises
    ------
    InvalidParticle
        If the input PDG ID is an invalid identification code.
    ParticleNotFound
        If no matching PDG ID is found in the loaded data table(s).
    """
    if not is_valid(value):
        raise InvalidParticle(f"Input PDGID {value} is invalid!")
    if not cls.table_loaded():
        cls.load_table()
    assert cls._hash_table is not None
    try:
        return cls._hash_table[int(value)]  # type: ignore[return-value]
    except KeyError:
        raise ParticleNotFound(f"Could not find PDGID {value}") from None

def from_string(

cls, name)

Get a particle from a PDG style name - returns the best match.

@classmethod
@deprecated(
    version="0.22",
    reason="This method is deprecated and will be removed from version 0.23.0. Use finditer or findall instead.",
)
def from_string(cls: type[Self], name: str) -> Self:
    "Get a particle from a PDG style name - returns the best match."
    matches = cls.from_string_list(name)
    if matches:
        return matches[0]
    raise ParticleNotFound(f"{name} not found in particle table")

def from_string_list(

cls, name)

Get a list of particles from a PDG style name.

@classmethod
@deprecated(
    version="0.22",
    reason="This method is deprecated and will be removed from version 0.23.0. Use finditer or findall instead.",
)
def from_string_list(cls: type[Self], name: str) -> list[Self]:
    "Get a list of particles from a PDG style name."
    # Forcible override
    particle = None
    short_name = name
    if "~" in name:
        short_name = name.replace("~", "")
        particle = False
    # Try the simplest searches first
    list_can = cls.findall(name=name, particle=particle)
    if list_can:
        return list_can
    list_can = cls.findall(pdg_name=short_name, particle=particle)
    if list_can:
        return list_can
    mat_str = getname.match(short_name)
    if mat_str is None:
        return []
    mat = mat_str.groupdict()
    if particle is False:
        mat["bar"] = "bar"
    try:
        return cls._from_group_dict_list(mat)
    except ParticleNotFound:
        return []

def load_table(

cls, filename=None, append=False, _name=None)

Load a particle data CSV table. Optionally append to the existing data already loaded if append=True. As a special case, if this is called with append=True and the table is not loaded, the default will be loaded first before appending (set append=False if you don't want this behavior).

A parameter is also included that should be considered private for now. It is _name, which will override the filename for the stored filename in _table_names.

@classmethod
def load_table(
    cls,
    filename: StringOrIO | None = None,
    append: bool = False,
    _name: str | None = None,
) -> None:
    """
    Load a particle data CSV table. Optionally append to the existing data already loaded if append=True.
    As a special case, if this is called with append=True and the table is not loaded, the default will
    be loaded first before appending (set append=False if you don't want this behavior).
    A parameter is also included that should be considered private for now. It is _name, which
    will override the filename for the stored filename in _table_names.
    """
    if append and not cls.table_loaded():
        cls.load_table(append=False)  # default load
    elif not append:
        cls._table = []
        cls._hash_table = {}
        cls._table_names = []
    # Tell MyPy that this is true
    assert cls._table is not None
    assert cls._hash_table is not None
    assert cls._table_names is not None
    if filename is None:
        with data.basepath.joinpath("particle2022.csv").open() as fa:
            cls.load_table(fa, append=append, _name="particle2022.csv")
        with data.basepath.joinpath("nuclei2022.csv").open() as fb:
            cls.load_table(fb, append=True, _name="nuclei2022.csv")
        return
    if isinstance(filename, HasRead):
        tmp_name = _name or filename.name
        cls._table_names.append(tmp_name or f"{filename!r} {len(cls._table_names)}")
        open_file = filename
    elif isinstance(filename, HasOpen):
        cls._table_names.append(str(filename))
        open_file = filename.open()
    else:
        cls._table_names.append(str(filename))
        assert not isinstance(filename, Traversable)
        open_file = open(filename, encoding="utf_8")  # noqa: SIM115
    with open_file as f:
        r = csv.DictReader(line for line in f if not line.startswith("#"))
        for v in r:
            with contextlib.suppress(ValueError):
                value = int(v["ID"])
                p = cls(
                    pdgid=value,
                    mass=float(v["Mass"]),
                    mass_upper=float(v["MassUpper"]),
                    mass_lower=float(v["MassLower"]),
                    width=float(v["Width"]),
                    width_upper=float(v["WidthUpper"]),
                    width_lower=float(v["WidthLower"]),
                    I=v["I"],
                    G=int(v["G"]),
                    P=int(v["P"]),
                    C=int(v["C"]),
                    anti_flag=int(v["Anti"]),
                    three_charge=int(v["Charge"]),
                    rank=int(v["Rank"]),
                    status=int(v["Status"]),
                    pdg_name=v["Name"],
                    quarks=v["Quarks"],
                    latex_name=v["Latex"],
                )
                # Replace the previous value if it exists
                cls._hash_table[value] = p
    cls._table = sorted(cls._hash_table.values())

def table_loaded(

cls)

Check to see if the table is loaded.

@classmethod
def table_loaded(cls) -> bool:
    """
    Check to see if the table is loaded.
    """
    return cls._table is not None

def table_names(

cls)

Return the list of names loaded.

Note

Calling this class method will load the default table, if no table has so far been loaded. Check with table_loaded() first if you don't want this loading to be triggered by the call.

@classmethod
def table_names(cls) -> tuple[str, ...]:
    """
    Return the list of names loaded.
    Note
    ----
    Calling this class method will load the default table,
    if no table has so far been loaded.
    Check with table_loaded() first if you don't want this loading
    to be triggered by the call.
    """
    if cls._table_names is None:
        cls.load_table()
    if cls._table_names is not None:
        return tuple(cls._table_names)  # make a copy to avoid user manipulation
    return ()

def to_dict(

cls, *args, **kwargs)

Render a search (via findall) on the internal particle data CSV table as a dict, loading the table from the default location if no table has yet been loaded.

See to_list for details on the full function signature.

The returned attributes are those of the class. By default all attributes are used as fields. Their complete list is: pdgid pdg_name mass mass_upper mass_lower width width_upper width_lower three_charge I G P C anti_flag rank status quarks latex_name

It is possible to add as returned fields any Particle class property, e.g. 'name', J or ctau, see examples below.

Parameters

exclusive_fields: list, optional, default is [] Exclusive list of fields to print out, which can be any Particle class property. exclude_fields: list, optional, default is [] List of table fields to exclude in the printout. Relevant only when exclusive_fields is not given. n_rows: int, optional, defaults to all rows Number of table rows to print out. filter_fn: function, optional, default is None Apply a filter to each particle. See findall(...)`` for typical use cases. particle: bool, optional, default is None Pass particle=True/False to only return particles or antiparticles. Option passed on internally tofindall(...). search_terms: keyword arguments, optional See `findall(...) for typical use cases.

Returns

The particle table query as a dict.

Note

The tabulate package is suggested as a means to print-out the contents of the query as a nicely formatted table.

Examples

Reproduce the whole particle table kept internally:

query_as_dict = Particle.to_dict()

Reduce the information on the particle table to the only fields ['pdgid', 'pdg_name'] and render the first 5 particles:

query_as_dict = Particle.to_dict(exclusive_fields=['pdgid', 'pdg_name'], n_rows=5) from tabulate import tabulate # doctest: +SKIP print(tabulate(query_as_dict, headers='keys')) # doctest: +SKIP pdgid pdg_name


  1  d
 -1  d
  2  u
 -2  u
  3  s

Request the properties of a specific list of particles:

query_as_dict = Particle.to_dict(filter_fn=lambda p: p.pdgid.is_lepton and p.charge!=0, exclusive_fields=['pdgid', 'name', 'mass', 'charge'], particle=True) print(tabulate(query_as_dict, headers='keys', tablefmt="rst", floatfmt=".12g", numalign="decimal")) # doctest: +SKIP ======= ====== =============== ======== pdgid name mass charge ======= ====== =============== ======== 11 e- 0.5109989461 -1 13 mu- 105.6583745 -1 15 tau- 1776.86 -1 17 tau'- -1 ======= ====== =============== ========

query_as_dict = Particle.to_dict(filter_fn=lambda p: p.pdgid.is_lepton, pdg_name='tau', exclusive_fields=['pdgid', 'name', 'mass', 'charge']) print(tabulate(query_as_dict, headers='keys')) # doctest: +SKIP pdgid name mass charge


 15  tau-    1776.86        -1
-15  tau+    1776.86         1

Save it to a file:

with open('particles.txt', "w") as outfile: # doctest: +SKIP ... print(tabulate(query_as_dict, headers='keys', tablefmt="rst", floatfmt=".12g", numalign="decimal"), file=outfile) # doctest: +SKIP

@classmethod
def to_dict(
    cls, *args: Any, **kwargs: Any
) -> dict[str, tuple[bool | int | str | float, ...]]:
    """
    Render a search (via `findall`) on the internal particle data CSV table
    as a `dict`, loading the table from the default location if no table has yet been loaded.
    See `to_list` for details on the full function signature.
    The returned attributes are those of the class. By default all attributes
    are used as fields. Their complete list is:
        pdgid
        pdg_name
        mass
        mass_upper
        mass_lower
        width
        width_upper
        width_lower
        three_charge
        I
        G
        P
        C
        anti_flag
        rank
        status
        quarks
        latex_name
    It is possible to add as returned fields any `Particle` class property,
    e.g. 'name', `J` or `ctau`, see examples below.
    Parameters
    ----------
    exclusive_fields: list, optional, default is []
        Exclusive list of fields to print out,
        which can be any `Particle` class property.
    exclude_fields: list, optional, default is []
        List of table fields to exclude in the printout.
        Relevant only when exclusive_fields is not given.
    n_rows: int, optional, defaults to all rows
        Number of table rows to print out.
    filter_fn: function, optional, default is None
        Apply a filter to each particle.
        See `findall(...)`` for typical use cases.
    particle: bool, optional, default is None
        Pass particle=True/False to only return particles or antiparticles.
        Option passed on internally to `findall(...)``.
    search_terms: keyword arguments, optional
        See `findall(...)`` for typical use cases.
    Returns
    -------
    The particle table query as a `dict`.
    Note
    ----
    The `tabulate` package is suggested as a means to print-out
    the contents of the query as a nicely formatted table.
    Examples
    --------
    Reproduce the whole particle table kept internally:
    >>> query_as_dict = Particle.to_dict()
    Reduce the information on the particle table to the only fields
    ['pdgid', 'pdg_name'] and render the first 5 particles:
    >>> query_as_dict = Particle.to_dict(exclusive_fields=['pdgid', 'pdg_name'], n_rows=5)
    >>> from tabulate import tabulate    # doctest: +SKIP
    >>> print(tabulate(query_as_dict, headers='keys'))    # doctest: +SKIP
      pdgid  pdg_name
    -------  ----------
          1  d
         -1  d
          2  u
         -2  u
          3  s
    Request the properties of a specific list of particles:
    >>> query_as_dict = Particle.to_dict(filter_fn=lambda p: p.pdgid.is_lepton and p.charge!=0, exclusive_fields=['pdgid', 'name', 'mass', 'charge'], particle=True)
    >>> print(tabulate(query_as_dict, headers='keys', tablefmt="rst", floatfmt=".12g", numalign="decimal"))    # doctest: +SKIP
    =======  ======  ===============  ========
      pdgid  name               mass    charge
    =======  ======  ===============  ========
         11  e-         0.5109989461        -1
         13  mu-      105.6583745           -1
         15  tau-    1776.86                -1
         17  tau'-                          -1
    =======  ======  ===============  ========
    >>> query_as_dict = Particle.to_dict(filter_fn=lambda p: p.pdgid.is_lepton, pdg_name='tau', exclusive_fields=['pdgid', 'name', 'mass', 'charge'])
    >>> print(tabulate(query_as_dict, headers='keys'))    # doctest: +SKIP
      pdgid  name       mass    charge
    -------  ------  -------  --------
         15  tau-    1776.86        -1
        -15  tau+    1776.86         1
    Save it to a file:
    >>> with open('particles.txt', "w") as outfile:    # doctest: +SKIP
    ...    print(tabulate(query_as_dict, headers='keys', tablefmt="rst", floatfmt=".12g", numalign="decimal"), file=outfile)    # doctest: +SKIP
    """
    query_as_list = cls.to_list(*args, **kwargs)
    headers = query_as_list[0]
    rows = zip(*query_as_list[1:])
    return {str(h): r for h, r in zip(headers, rows)}

def to_list(

cls, exclusive_fields=(), exclude_fields=(), n_rows=-1, filter_fn=None, particle=None, **search_terms)

Render a search (via findall) on the internal particle data CSV table as a list, loading the table from the default location if no table has yet been loaded.

The returned attributes are those of the class. By default all attributes are used as fields. Their complete list is: pdgid pdg_name mass mass_upper mass_lower width width_upper width_lower three_charge I G P C anti_flag rank status quarks latex_name

It is possible to add as returned fields any Particle class property, e.g. 'name', J or ctau, see examples below.

Parameters

exclusive_fields: list, optional, default is [] Exclusive list of fields to print out, which can be any Particle class property. exclude_fields: list, optional, default is [] List of table fields to exclude in the printout. Relevant only when exclusive_fields is not given. n_rows: int, optional, defaults to all rows Number of table rows to print out. filter_fn: function, optional, default is None Apply a filter to each particle. See findall(...)`` for typical use cases. particle: bool, optional, default is None Pass particle=True/False to only return particles or antiparticles. Option passed on internally tofindall(...). search_terms: keyword arguments, optional See `findall(...) for typical use cases.

Returns

The particle table query as a list.

Note

The tabulate package is suggested as a means to print-out the contents of the query as a nicely formatted table.

Examples

Reproduce the whole particle table kept internally:

query_as_list = Particle.to_list()

Reduce the information on the particle table to the only fields ['pdgid', 'pdg_name'] and render the first 5 particles:

query_as_list = Particle.to_list(exclusive_fields=['pdgid', 'pdg_name'], n_rows=5) from tabulate import tabulate print(tabulate(query_as_list, headers='firstrow')) pdgid pdg_name


  1  d
 -1  d
  2  u
 -2  u
  3  s

Request the properties of a specific list of particles:

query_as_list = Particle.to_list(filter_fn=lambda p: p.pdgid.is_lepton and p.charge!=0, exclusive_fields=['pdgid', 'name', 'mass', 'charge'], particle=False) print(tabulate(query_as_list, headers='firstrow', tablefmt="rst", floatfmt=".12g", numalign="decimal")) ======= ====== ============= ======== pdgid name mass charge ======= ====== ============= ======== -11 e+ 0.51099895 1 -13 mu+ 105.6583755 1 -15 tau+ 1776.86 1 -17 tau'+ 1 ======= ====== ============= ========

query_as_list = Particle.to_list(filter_fn=lambda p: p.pdgid.is_lepton, pdg_name='tau', exclusive_fields=['pdgid', 'name', 'mass', 'charge']) print(tabulate(query_as_list, headers='firstrow')) pdgid name mass charge


 15  tau-    1776.86        -1
-15  tau+    1776.86         1

Save it to a file:

with open('particles.txt', "w") as outfile: # doctest: +SKIP ... print(tabulate(query_as_list, headers='firstrow', tablefmt="rst", floatfmt=".12g", numalign="decimal"), file=outfile) # doctest: +SKIP

@classmethod
def to_list(
    cls,
    exclusive_fields: Iterable[str] = (),
    exclude_fields: Iterable[str] = (),
    n_rows: int = -1,
    filter_fn: Callable[[Particle], bool] | None = None,
    particle: bool | None = None,
    **search_terms: Any,
) -> Sequence[Sequence[bool | int | float | str]]:
    """
    Render a search (via `findall`) on the internal particle data CSV table
    as a `list`, loading the table from the default location if no table has yet been loaded.
    The returned attributes are those of the class. By default all attributes
    are used as fields. Their complete list is:
        pdgid
        pdg_name
        mass
        mass_upper
        mass_lower
        width
        width_upper
        width_lower
        three_charge
        I
        G
        P
        C
        anti_flag
        rank
        status
        quarks
        latex_name
    It is possible to add as returned fields any `Particle` class property,
    e.g. 'name', `J` or `ctau`, see examples below.
    Parameters
    ----------
    exclusive_fields: list, optional, default is []
        Exclusive list of fields to print out,
        which can be any `Particle` class property.
    exclude_fields: list, optional, default is []
        List of table fields to exclude in the printout.
        Relevant only when exclusive_fields is not given.
    n_rows: int, optional, defaults to all rows
        Number of table rows to print out.
    filter_fn: function, optional, default is None
        Apply a filter to each particle.
        See `findall(...)`` for typical use cases.
    particle: bool, optional, default is None
        Pass particle=True/False to only return particles or antiparticles.
        Option passed on internally to `findall(...)``.
    search_terms: keyword arguments, optional
        See `findall(...)`` for typical use cases.
    Returns
    -------
    The particle table query as a `list`.
    Note
    ----
    The `tabulate` package is suggested as a means to print-out
    the contents of the query as a nicely formatted table.
    Examples
    --------
    Reproduce the whole particle table kept internally:
    >>> query_as_list = Particle.to_list()
    Reduce the information on the particle table to the only fields
    ['pdgid', 'pdg_name'] and render the first 5 particles:
    >>> query_as_list = Particle.to_list(exclusive_fields=['pdgid', 'pdg_name'], n_rows=5)
    >>> from tabulate import tabulate
    >>> print(tabulate(query_as_list, headers='firstrow'))
      pdgid  pdg_name
    -------  ----------
          1  d
         -1  d
          2  u
         -2  u
          3  s
    Request the properties of a specific list of particles:
    >>> query_as_list = Particle.to_list(filter_fn=lambda p: p.pdgid.is_lepton and p.charge!=0, exclusive_fields=['pdgid', 'name', 'mass', 'charge'], particle=False)
    >>> print(tabulate(query_as_list, headers='firstrow', tablefmt="rst", floatfmt=".12g", numalign="decimal"))
    =======  ======  =============  ========
      pdgid  name             mass    charge
    =======  ======  =============  ========
        -11  e+         0.51099895         1
        -13  mu+      105.6583755          1
        -15  tau+    1776.86               1
        -17  tau'+                         1
    =======  ======  =============  ========
    >>> query_as_list = Particle.to_list(filter_fn=lambda p: p.pdgid.is_lepton, pdg_name='tau', exclusive_fields=['pdgid', 'name', 'mass', 'charge'])
    >>> print(tabulate(query_as_list, headers='firstrow'))
      pdgid  name       mass    charge
    -------  ------  -------  --------
         15  tau-    1776.86        -1
        -15  tau+    1776.86         1
    Save it to a file:
    >>> with open('particles.txt', "w") as outfile:    # doctest: +SKIP
    ...    print(tabulate(query_as_list, headers='firstrow', tablefmt="rst", floatfmt=".12g", numalign="decimal"), file=outfile)    # doctest: +SKIP
    """
    if not cls.table_loaded():
        cls.load_table()
    # Get all table headers from the class attributes
    tbl_names = [a.name for a in attr.fields(Particle)]
    # ... and replace '_three_charge' with the better, public property
    tbl_names[tbl_names.index("_three_charge")] = "three_charge"
    list_exclusive_fields = list(exclusive_fields)
    if list_exclusive_fields:
        tbl_names = list_exclusive_fields
    else:
        for fld in exclude_fields:
            with contextlib.suppress(ValueError):
                tbl_names.remove(fld)
    # Start with the full table
    tbl_all = sorted(cls.all())
    # Apply a filter, if specified
    if filter_fn is not None:
        tbl_all = cls.findall(filter_fn, particle=particle, **search_terms)
    # In any case, only keep a given number of rows?
    if n_rows >= 0:
        tbl_all = tbl_all[:n_rows]
    # Build all table rows
    tbl = [tbl_names]
    for p in tbl_all:
        tbl.append([getattr(p, attr) for attr in tbl_names])
    return tbl