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 the usual workflow, you make use of the StateTransitionManager
. By default, if you do not specify the particles
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_particle_definitions.yml.
Here, we call this method directly to illustrate what happens (we use io.load_pdg
, which loads a subset):
[1]:
from expertsystem import io
particle_db = io.load_pdg()
print("Number of loaded particles:", len(particle_db))
Number of loaded particles: 512
In the following, we illustrate how to use the methods of the ParticleCollection
class to modify or add particle definitions in the particle database.
Finding particles¶
The ParticleCollection
class offers some methods to search for particles by name or by PID (see ParticleCollection.find
):
[2]:
particle_db.find(333)
[2]:
Particle(name='phi(1020)', pid=333, spin=1.0, mass=1.019461, width=0.004248999999999999, charge=0, isospin=Spin(0.0, 0.0), strangeness=0, charmness=0, bottomness=0, topness=0, baryon_number=0, electron_lepton_number=0, muon_lepton_number=0, tau_lepton_number=0, parity=Parity(-1), c_parity=Parity(-1), g_parity=Parity(-1))
With ParticleCollection.filter
, you can perform more sophisticated searches. This is done by either passing a function or lambda.
[3]:
subset = particle_db.filter(lambda p: p.name.startswith("f(2)"))
subset.names
[3]:
{"f(2)'(1525)",
'f(2)(1270)',
'f(2)(1950)',
'f(2)(2010)',
'f(2)(2300)',
'f(2)(2340)'}
[4]:
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
[4]:
{'K(2)(1820)+',
'K(2)(1820)0',
'Lambda(1820)~',
'Lambda(1830)~',
'Lambda(1890)~'}
[5]:
subset = particle_db.filter(lambda p: p.is_lepton())
subset.names
[5]:
{'e+',
'e-',
'mu+',
'mu-',
'nu(e)',
'nu(e)~',
'nu(mu)',
'nu(mu)~',
'nu(tau)',
'nu(tau)~',
'tau+',
'tau-'}
In each of these examples, we call the names
property. This is just to only display the names, sorted alphabetically. The full representation of a ParticleCollection
looks like this:
[6]:
particle_db.filter(lambda p: p.name.startswith("pi") and len(p.name) == 3)
[6]:
ParticleCollection({Particle(name='pi0', pid=111, spin=0.0, mass=0.1349768, width=7.73e-09, charge=0, isospin=Spin(1.0, 0.0), strangeness=0, charmness=0, bottomness=0, topness=0, baryon_number=0, electron_lepton_number=0, muon_lepton_number=0, tau_lepton_number=0, parity=Parity(-1), c_parity=Parity(+1), g_parity=Parity(-1)), Particle(name='pi-', pid=-211, spin=0.0, mass=0.13957039000000002, width=2.5284e-17, charge=-1, isospin=Spin(1.0, -1.0), strangeness=0, charmness=0, bottomness=0, topness=0, baryon_number=0, electron_lepton_number=0, muon_lepton_number=0, tau_lepton_number=0, parity=Parity(-1), c_parity=None, g_parity=Parity(-1)), Particle(name='pi+', pid=211, spin=0.0, mass=0.13957039000000002, width=2.5284e-17, charge=1, isospin=Spin(1.0, 1.0), strangeness=0, charmness=0, bottomness=0, topness=0, baryon_number=0, electron_lepton_number=0, muon_lepton_number=0, tau_lepton_number=0, parity=Parity(-1), c_parity=None, g_parity=Parity(-1))})
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:
[7]:
N1650_plus = particle_db["N(1650)+"]
N1650_plus
[7]:
Particle(name='N(1650)+', pid=32212, spin=0.5, mass=1.65, width=0.125, charge=1, isospin=Spin(0.5, 0.5), strangeness=0, charmness=0, bottomness=0, topness=0, baryon_number=1, electron_lepton_number=0, muon_lepton_number=0, tau_lepton_number=0, parity=Parity(-1), c_parity=None, g_parity=None)
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
:
[8]:
from expertsystem.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
WARNING:root:Particle with PID 32212 already exists: "N(1650)+"
[8]:
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.
[9]:
from expertsystem.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)-"]
WARNING:root:Particle with PID -32212 already exists: "N(1650)~-"
[9]:
Particle(name='Modified N(1650)-', pid=-32212, spin=0.5, mass=1.65, width=0.2, charge=-1, isospin=Spin(0.5, -0.5), strangeness=0, charmness=0, bottomness=0, topness=0, baryon_number=-1, electron_lepton_number=0, muon_lepton_number=0, tau_lepton_number=0, parity=Parity(+1), c_parity=None, g_parity=None)
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:
[10]:
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)
WARNING:root:Particle with PID 443 already exists: "J/psi(1S)"
WARNING:root:Particle with PID 443 already exists: "EpEm (4220 MeV)"
WARNING:root:Particle with PID 443 already exists: "EpEm (4180 MeV)"
WARNING:root:Particle with PID 443 already exists: "EpEm (4600 MeV)"
[10]:
518
[11]:
particle_db.filter(lambda p: "EpEm" in p.name).names
WARNING:root:Particle with PID 443 already exists: "EpEm (4600 MeV)"
WARNING:root:Particle with PID 443 already exists: "EpEm (4220 MeV)"
WARNING:root:Particle with PID 443 already exists: "EpEm (4180 MeV)"
[11]:
{'EpEm (4180 MeV)', 'EpEm (4220 MeV)', 'EpEm (4420 MeV)', 'EpEm (4600 MeV)'}
Note
By convention, the expertsystem
uses GeV as energy unit.
Loading custom definitions from a YAML file¶
It’s also possible to add particles from a config file, with load_particle_collection
. 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:
[12]:
from expertsystem.io import load_particle_collection
particle_db += load_particle_collection("additional_particles.yml")
WARNING:root:Overwriting particle with name "Sigma(1385)+"
WARNING:root:Overwriting particle with name "Sigma(1660)+"
WARNING:root:Overwriting particle with name "Sigma(1750)+"
Writing to XML or YAML¶
You can also dump the existing particle lists to either YAML or XML. You do this with the expertsystem.io.write
function.
[13]:
io.write(instance=particle_db, filename="dumped_particle_list.xml")
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.
[14]:
from expertsystem.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-"]
io.write(output, "particle_list_selection.yml")
output.names
[14]:
{'J/psi(1S)',
'f(0)(1370)',
'f(0)(1500)',
'f(0)(1710)',
'f(0)(500)',
'f(0)(980)',
'gamma',
'pi+',
'pi-',
'pi0'}
As a side note, the expertsystem
provides JSON schemas (e.g. yaml/particle-list.json
) to validate your particle list files (see also jsonschema.validate
):
[15]:
import yaml
with open("dumped_particle_list.yaml") as stream:
definition = yaml.load(stream, Loader=yaml.SafeLoader)
io._yaml.validation.particle_list(definition)
In addition, if you have installed the expertsystem
in Development mode and use VSCode, your YAML particle list are checked automatically in the GUI.