Package: particle
Particle: PDG particle data and identification codesParticle provides a pythonic interface to theParticle Data Group(PDG)
particle data tables and particle identification codes,
with extended particle information and extra goodies.The PDG defines the standard particle identification (ID) numbering scheme.
The package provides thePDGIDclass implementing queries on those PDG IDs.
The queries are also accessible through free standing functions mimicking,
and expanding from, the HepPID/HepPDT C++ interface.TheParticleclass wraps the information in the PDG particle data tables and
provides an object-oriented interface and powerful search and look-up utilities.InstallationInstallparticlelike any other Python package:python-mpipinstallparticleor similar (use--user,virtualenv, etc. if you wish).Strict dependenciesPython(3.7+)importlib_resources backportif using Python < 3.9attrsprovides classes without boilerplate (similar to DataClasses in Python 3.7)hepunitsprovides units for the Scikit-HEP packagesChangelogSee thechangelogfor a history of notable changes.Getting started: PDG IDs>>>fromparticleimportPDGID>>>>>>pid=PDGID(211)>>>pid<PDGID:211>>>>pid.is_mesonTrue>>>pid=PDGID(99999999)>>>pid<PDGID:99999999(is_valid==False)>For convenience, all properties of thePDGIDclass are available as standalone functions that work on any SupportsInt (includingParticle):>>>fromparticle.pdgidimportis_meson>>>>>>is_meson(211)TrueThese composable functions qualifying PDG IDs make it easy to classify particles.
For the sake of example, quarkonia can be specified with the following user-defined functions:>>>is_heavy_flavor=lambdax:has_charm(x)orhas_bottom(x)orhas_top(x)>>>is_quarkonium=lambdax:is_meson(x)andis_heavy_flavor(x)andParticle.from_pdgid(x).is_self_conjugatePDG ID literals provide (PDGIDclass) aliases for all particles loaded, with easily recognisable names.
For example:>>>fromparticle.pdgidimportliteralsaslid>>>>>>lid.pi_plus<PDGID:211>>>>>>>fromparticle.pdgid.literalsimportLambda_b_0>>>Lambda_b_0<PDGID:5122>>>>Lambda_b_0.has_bottomTrueYou can quickly displayPDGIDinfo from the command line with:$python-mparticlepdgid323<PDGID:323>ANoneJ1.0L0S1ZNoneabspid323charge1.0has_bottomFalse...Similarly, classes exist to express identification codes used by MC programs,
see information on converters below.Getting started: ParticlesYou can use a variety of methods to get particles. If you know the PDG ID number
or, say, the name used in EvtGen, you can get a particle directly.>>>fromparticleimportParticle>>>Particle.from_pdgid(211)<Particle:name="pi+",pdgid=211,mass=139.57039±0.00018MeV>>>>>>>Particle.from_evtgen_name("J/psi")<Particle:name="J/psi(1S)",pdgid=443,mass=3096.900±0.006MeV>>>>>>>Particle.from_nucleus_info(a=12,z=6)<Particle:name="C12",pdgid=1000060120,mass=11177.9291399MeV>A similar method exists to get a list of particles from a PDG style name:>>>Particle.findall(pdg_name="pi")returns the list of matching particles whose PDG name is “pi”,
which in this case comprises the three charged states of the pseudoscalar pion.Else, and more generally, you can use a search. A basic example is the following:>>>next(Particle.finditer('pi'))# first item in iterator of particles<Particle:name="pi0",pdgid=111,mass=134.9768±0.0005MeV>>>>>>>Particle.findall('pi')[0]# Same as above but returning a list of particles<Particle:name="pi0",pdgid=111,mass=134.9768±0.0005MeV>You can search for the properties using keyword arguments, which includepdg_name,name,mass,width,charge,three_charge,anti_flag,rank,I,J,G,P,quarks,status,mass_upper,mass_lower,width_upper, andwidth_lower.
You can pass a callable or an exact match for any property.
The argumentparticlecan be set toTrue/False, as well,
to limit the search to particles or antiparticles.You can also build the search yourself with the first positional
argument, which accepts a callable that is given the particle object itself.
If the first positional argument is a string, that will match against the
particle’sname.Here are possible sophisticated searches, all of which work with eitherParticle.findallorParticle.finditer, where the former method provides a list
whereas the latter returns an iterator.>>># Print out all particles with asymmetric decay width uncertainties>>>ps=Particle.finditer(lambdap:p.width_lower!=p.width_upper)>>>forpinps:...print(p.name,p.pdgid,p.width_lower,p.width_upper)>>>>>># Find all antiparticles with 'Omega' in the name>>>Particle.finditer('Omega',particle=False)# several found>>>>>># Find all antiparticles of name=='Omega'>>>Particle.finditer(name='Omega',particle=False)# none found>>>>>># Find all antiparticles of pdg_name=='Omega'>>>Particle.findall(pdg_name='Omega',particle=False)# only 1, of course[<Particle:name="Omega~+",pdgid=-3334,mass=1672.5±0.3MeV>]>>>>>># Find all neutral beauty hadrons>>>Particle.findall(lambdap:p.pdgid.has_bottomandp.charge==0)>>>>>># Find all strange mesons with c*tau > 1 meter>>>fromhepunitsimportmeter>>>Particle.findall(lambdap:p.pdgid.is_mesonandp.pdgid.has_strangeandp.ctau>1*meter,particle=True)[<Particle:name="K(L)0",pdgid=130,mass=497.611±0.013MeV>,<Particle:name="K+",pdgid=321,mass=493.677±0.016MeV>]Once you have a particle, any of the properties can be accessed, along with several methods.
Though they are not real properties, you can accessis_name_barred, andspin_type.
You can also.invert()a particle.There are lots of printing choices for particles:describe(),programmatic_name,latex_name,html_name, HTML printing outs in notebooks,
and of coursereprandstrsupport.You can get the.pdgidfrom a particle, as well.
Sorting particles will put lowestabs(PDGID)first.Particle literals provide (Particleclass) aliases for the particles loaded,
with easily recognisable names. For example:>>>fromparticleimportliteralsaslp>>>lp.pi_plus<Particle:name="pi+",pdgid=211,mass=139.57061±0.00024MeV>>>>>>>fromparticle.literalsimportLambda_b_0>>>Lambda_b_0<Particle:name="Lambda(b)0",pdgid=5122,mass=5619.60±0.17MeV>>>>Lambda_b_0.J0.5You can quickly search for particles from the command line with
(note: quotes may be used/needed but only double quotes work as expected on Windows):$python-mparticlesearch"K*0"<Particle:name="K*(892)0",pdgid=313,mass=895.55±0.20MeV><Particle:name="K*(1680)0",pdgid=30313,mass=1718±18MeV><Particle:name="K*(1410)0",pdgid=100313,mass=1421±9MeV>If you only select one particle, either by a search or by giving the PDG ID number,
you can see more information about the particle:$python-mparticlesearch311Name:K0ID:311Latex:$K^{0}$Mass=497.611±0.013MeVWidth=-1.0MeVQ(charge)=0J(totalangular)=0.0P(spaceparity)=-C(chargeparity)=?I(isospin)=1/2G(G-parity)=?SpinType:SpinType.PseudoScalarQuarks:dSAntiparticlename:K~0(antiparticlestatus:Barred)Advanced: Loading custom tablesYou can control the particle data tables if you so desire. You can append a new data table using the following syntax:>>>fromparticleimportParticle>>>Particle.load_table('new_particles.csv',append=True)You can also replace the particle table entirely withappend=False(the default).If you want a non-default data file distributed with the package just proceed as follows:>>>fromparticleimportdata>>>Particle.load_table(data.basepath/"particle2022.csv"))>>>Particle.load_table(data.basepath/"nuclei2022.csv"),append=True)# I still want nuclei info>>>Particle.table_names()# list the loaded tablesAdvanced: how to create user-defined particlesThere are situations where it may be handy to create user-defined particles.
But do so with care and having in mind the limitations, many of which are discussed or exemplified below!The simplest “particle” one may create is effectively a placeholder with no real information stored:>>># A Particle instance the simplest possible. Contains basically no info>>>p=Particle.empty()>>>p<Particle:name="Unknown",pdgid=0,mass=None>>>>>>>print(p.describe())Name:UnknownA more useful particle definition will likely involve at least a name and a PDG ID.
It is important to keep in mind that a meaningful PDG ID encodes by construction internal quantum numbers
and other information. As such, the definition of a particle with a “random” PDG ID
will result in a particle with undefined and/or wrong properties such as quantum numbers or the quality of being a meson.>>>p2=Particle(9912345,'MyPentaquark')>>>p2<Particle:name="MyPentaquark",pdgid=9912345,mass=None>>>>>>>p2.pdgid.is_pentaquarkFalse>>>print(p2.describe())# J=2 is an example of something effectively encoded in the PDG ID.Name:MyPentaquarkID:9912345Latex:$Unknown$Mass=NoneWidth=NoneQ(charge)=NoneJ(totalangular)=2.0P(spaceparity)=NoneC(chargeparity)=NoneI(isospin)=NoneG(G-parity)=NoneAntiparticlename:MyPentaquark(antiparticlestatus:Same)A yet more sophisticated definition:>>>p3=Particle(pdgid=9221132,pdg_name='Theta',three_charge=3,latex_name='\Theta^{+}')>>>p3<Particle:name="Theta",pdgid=9221132,mass=None>>>>>>>print(p3.describe())Name:ThetaID:9221132Latex:$\Theta^{+}$Mass=NoneWidth=NoneQ(charge)=+J(totalangular)=0.5P(spaceparity)=NoneC(chargeparity)=NoneI(isospin)=NoneG(G-parity)=NoneSpinType:SpinType.NonDefinedAntiparticlename:Theta(antiparticlestatus:Same)Advanced: ConversionYou can convert and update the particle tables with the utilities inparticle.particle.convert. This requires thepandaspackage, and is only tested with Python 3. Run the following command for more help:$python3-mparticle.particle.convert--helpGetting started: ConvertersYou can use mapping classes to convert between particle MC identification codes
and particle names. See theparticle.convertersmodules for the available
mapping classes. For example:>>>fromparticle.convertersimportPythia2PDGIDBiMap>>>fromparticleimportPDGID,PythiaID>>>>>>pyid=Pythia2PDGIDBiMap[PDGID(9010221)]>>>pyid<PythiaID:10221>>>>pdgid=Pythia2PDGIDBiMap[PythiaID(10221)]>>>pdgid<PDGID:9010221>This code makes use of classes similar toPDGID, which hold
particle identification codes used by MC programs.
Possible use cases are the following:>>>fromparticleimportParticle>>>fromparticleimportCorsika7ID,Geant3ID,PythiaID>>>>>>g3id=Geant3ID(8)>>>p=Particle.from_pdgid(g3id.to_pdgid())>>>>>>(p,)=Particle.finditer(pdgid=g3id.to_pdgid())# syntax (p,) throws an error if < 1 or > 1 particle is found>>>p.name'pi+'>>>pythiaid=PythiaID(211)>>>p=Particle.from_pdgid(pythiaid.to_pdgid())>>>(p,)=Particle.finditer(pdgid=pythiaid.to_pdgid())>>>p.name'pi+'>>>cid=Corsika7ID(5)>>>p=Particle.from_pdgid(cid.to_pdgid())>>>p.name'mu+'Corsika7TheCorsika7IDclass implements features to make it easier to work with Corsika7 output.
For a full feature set, please refer to theparticle.corsikasubmodule.Corsika7ID.from_particle_description(from_particle_description: int)returns(Corsika7ID, bool)to automatically parse theparticle_descriptionfrom the Corsika7 particle data sub-block.Corsika7ID.is_particle()checks if the ID refers to an actual particle or something else (like additional information).Corsika7ID.to_pdgid()converts theCorsika7IDto aPDGIDif possible.Getting started: experiment-specific modulesExperiment-specific submodules are welcome if they tie in nicely with the functionality of the package while providing
add-ons of particular relevance to experiments.LHCb-specific moduleAvailable via>>>fromparticleimportlhcbit contains the following converter and functions:>>>dir(lhcb)['LHCbName2PDGIDBiMap','from_lhcb_name','to_lhcb_name']>>>n,e,l=Particle.from_pdgid(-531).name,Particle.from_pdgid(531).evtgen_name,lhcb.to_lhcb_name(Particle.from_pdgid(-531))>>>print(f"Name:{n}\nEvtGen name:{e}\nLHCb name:{l}")Name:B(s)~0EvtGenname:B_s0LHCbname:B_s~0>>>p=Particle.from_pdgid(-531)>>>p<Particle:name="B(s)~0",pdgid=-531,mass=5366.88±0.14MeV>>>>to_lhcb_name(p)'B_s~0'Conversions PDG ID <-> LHCb name are available via a predefined bidirectional map
similarly to what is available in the standard (i.e. non-experiment-specific) converters:>>>name=LHCbName2PDGIDBiMap[PDGID(-531)]>>>name'B_s~0'>>>pdgid=LHCbName2PDGIDBiMap['B_s~0']>>>pdgid<PDGID:-531>AcknowledgementsThe UK Science and Technology Facilities Council (STFC) and the University of Liverpool
provide funding for Eduardo Rodrigues (2020-) to work on this project part-time.Support for this work was provided by the National Science Foundation cooperative agreement OAC-1450377 (DIANA/HEP) in 2016-2019
and has been provided by OAC-1836650 (IRIS-HEP) since 2019.
Any opinions, findings, conclusions or recommendations expressed in this material
are those of the authors and do not necessarily reflect the views of the National Science Foundation.