Particle database#
In PWA, you usually want to search for special resonances, possibly even some not listed in the PDG. In this notebook, we go through a few ways to add or overwrite Particle instances in the database with your own particle definitions.
Loading the default database#
In Generate transitions, we made use of the StateTransitionManager. By default, if you do not specify the particle_db argument, the StateTransitionManager calls the function load_default_particles(). This functions returns a ParticleCollection instance with Particle definitions from the PDG, along with additional definitions that are provided in the file additional_definitions.yml.
Here, we call this method directly to illustrate what happens (we use load_pdg(), which loads a subset):
from qrules.particle import load_pdg
particle_db = load_pdg()
print("Number of loaded particles:", len(particle_db))
Number of loaded particles: 541
In the following, we illustrate how to use the methods of the ParticleCollection class to find and ‘modify’ Particles and add() them back to the ParticleCollection.
Finding particles#
The ParticleCollection class offers some methods to search for particles by name or by PID (see find()):
particle_db.find(333)
Particle(
name='phi(1020)',
pid=333,
latex='\\phi(1020)',
spin=Fraction(1, 1),
mass=1.01946,
width=0.004248999999999999,
isospin=Spin(0, 0),
parity=+-1,
c_parity=+-1,
g_parity=+-1,
)
With filter(), you can perform more sophisticated searches. This is done by either passing a function or lambda.
subset = particle_db.filter(lambda p: p.name.startswith("f(2)"))
subset.names
['f(2)(1270)',
"f(2)'(1525)",
'f(2)(1565)',
'f(2)(1950)',
'f(2)(2010)',
'f(2)(2150)',
'f(2)(2300)',
'f(2)(2340)']
subset = particle_db.filter(
lambda p: p.strangeness == 1 and p.spin >= 1 and p.mass > 1.8 and p.mass < 1.9
)
subset.names
['K(2)(1820)0',
'K(2)(1820)+',
'Lambda(1820)~',
'Lambda(1830)~',
'Lambda(1890)~']
subset = particle_db.filter(lambda p: p.is_lepton())
subset.names
['e-',
'e+',
'mu-',
'mu+',
'nu(mu)',
'nu(tau)',
'nu(e)~',
'nu(mu)~',
'nu(e)',
'nu(tau)~',
'tau-',
'tau+']
Note that in each of these examples, we call the names property. This is just to only display the names, sorted alphabetically, otherwise the output becomes a bit of a mess:
particle_db.filter(lambda p: p.name.startswith("pi") and len(p.name) == 3)
ParticleCollection({
Particle(
name='pi+',
pid=211,
latex='\\pi^{+}',
spin=Fraction(0, 1),
mass=0.13957039000000002,
width=2.5284e-17,
charge=1,
isospin=Spin(1, +1),
parity=+-1,
g_parity=+-1,
),
Particle(
name='pi-',
pid=-211,
latex='\\pi^{-}',
spin=Fraction(0, 1),
mass=0.13957039000000002,
width=2.5284e-17,
charge=-1,
isospin=Spin(1, -1),
parity=+-1,
g_parity=+-1,
),
Particle(
name='pi0',
pid=111,
latex='\\pi^{0}',
spin=Fraction(0, 1),
mass=0.1349768,
width=7.81e-09,
isospin=Spin(1, 0),
parity=+-1,
c_parity=+1,
g_parity=+-1,
),
})
LaTeX representation#
Particles also contain a latex tag. Here, we use ipython to render them nicely as mathematical symbols:
from IPython.display import Math
sigmas = particle_db.filter(lambda p: p.name.startswith("Sigma") and p.charmness == 1)
Math(", ".join(p.latex for p in sigmas))
Adding custom particle definitions through Python#
A quick way to modify or overwrite particles, is through your Python script or notebook. Notice that the instances in the database are Particle instances:
N1650_plus = particle_db["N(1650)+"]
N1650_plus
Particle(
name='N(1650)+',
pid=32212,
latex='N(1650)^{+}',
spin=Fraction(1, 2),
mass=1.65,
width=0.125,
charge=1,
isospin=Spin(1/2, +1/2),
baryon_number=1,
parity=+-1,
)
The instances in the database are immutable. Therefore, if you want to modify, say, the width, you have to create a new Particle instance from the particle you want to modify and add() it back to the database. You can do this with create_particle():
from qrules.particle import create_particle
new_N1650_plus = create_particle(
template_particle=N1650_plus, name="Modified N(1650)+", width=0.2
)
particle_db.add(new_N1650_plus)
particle_db["Modified N(1650)+"].width
0.2
You often also want to add the antiparticle of the particle you modified to the database. Using create_antiparticle(), it is easy to create the corresponding antiparticle object.
from qrules.particle import create_antiparticle
new_N1650_minus = create_antiparticle(new_N1650_plus, new_name="Modified N(1650)-")
particle_db.add(new_N1650_minus)
particle_db["Modified N(1650)-"]
Particle(
name='Modified N(1650)-',
pid=-32212,
latex='\\overline{N(1650)^{+}}',
spin=Fraction(1, 2),
mass=1.65,
width=0.2,
charge=-1,
isospin=Spin(1/2, -1/2),
baryon_number=-1,
parity=+1,
)
When adding additional particles you may need for your research, it is easiest to work with an existing particle as template. Let’s say we want to study \(e^+e^-\) collisions of several energies:
energies_mev = {4180, 4220, 4420, 4600}
template_particle = particle_db["J/psi(1S)"]
for energy_mev in energies_mev:
energy_gev = energy_mev / 1e3
new_particle = create_particle(
template_particle,
name=f"EpEm ({energy_mev} MeV)",
mass=energy_gev,
)
particle_db.add(new_particle)
len(particle_db)
547
particle_db.filter(lambda p: "EpEm" in p.name).names
['EpEm (4180 MeV)', 'EpEm (4220 MeV)', 'EpEm (4420 MeV)', 'EpEm (4600 MeV)']
Of course, it’s also possible to add any kind of custom Particle, as long as its quantum numbers comply with the gellmann_nishijima() rule:
from qrules.particle import Particle
custom = Particle(
name="custom",
pid=99999,
latex=R"p_\mathrm{custom}",
spin=1.0,
mass=1,
charge=1,
isospin=(1.5, 0.5),
charmness=1,
)
custom
Particle(
name='custom',
pid=99999,
latex='p_\\mathrm{custom}',
spin=Fraction(1, 1),
mass=1.0,
charge=1,
isospin=Spin(3/2, +1/2),
charmness=1,
)
548
Loading custom definitions from a YAML file#
It’s also possible to add particles from a config file, with io.load(). Existing entries remain and if the imported file of particle definitions contains a particle with the same name, it is overwritten in the database.
It’s easiest to work with YAML. Here, we use the provided additional_particles.yml example file:
from qrules import io
particle_db += io.load("additional_particles.yml")
Writing to YAML#
You can also dump the existing particle lists to YAML. You do this with the io.write() function.
io.write(instance=particle_db, filename="dumped_particle_list.yaml")
Note that the function write can dump any ParticleCollection to an output file, also a specific subset.
from qrules.particle import ParticleCollection
output = ParticleCollection()
output += particle_db["J/psi(1S)"]
output += particle_db.find(22) # gamma
output += particle_db.filter(lambda p: p.name.startswith("f(0)"))
output += particle_db["pi0"]
output += particle_db["pi+"]
output += particle_db["pi-"]
output += particle_db["custom"]
io.write(output, "particle_list_selection.yml")
output.names
['custom',
'f(0)(500)',
'f(0)(980)',
'f(0)(1370)',
'f(0)(1500)',
'f(0)(1710)',
'f(0)(2020)',
'gamma',
'J/psi(1S)',
'pi0',
'pi-',
'pi+']
As a side note, qrules provides JSON schemas (reaction/particle-validation.json) to validate your particle list files (see also jsonschema.validators.validate()). If you have installed qrules as an Editable installation and use VSCode, your YAML particle list are checked automatically in the GUI.