Source code for Particle

import numpy as np
import math
from particle import PDGID

[docs]class Particle: """Defines a particle object. The member variables of the Particle class are the quantities in the OSCAR2013/OSCAR2013Extended or JETSCAPE hadron output. If they are not set, they stay None to throw an error if one tries to access a non existing quantity. Attributes ---------- t_ : float The time of the particle. x_ : float The x coordinate of the position. y_ : float The y coordinate of the position. z_ : float The z coordinate of the position. mass_ : float The mass of the particle. E_ : float The energy of the particle. px_ : float The x component of the momentum. py_ : float The y component of the momentum. pz_ : float The z component of the momentum. pdg_ : int The PDG code of the particle. ID_ : int The ID of the particle (unique label of each particle in an event). charge_ : int Electric charge of the particle. ncoll_ : int Number of collisions undergone by the particle. form_time_ : double Formation time of the particle. xsecfac_ : double Scaling factor of the cross section. proc_id_origin_ : int ID for the process the particle stems from. proc_type_origin_ : int Thype information for the process the particle stems from. t_last_coll_ : double Time of the last collision. pdg_mother1_ : int PDG code of the mother particle 1. pdg_mother2_ : int PDG code of the mother particle 2. status_ : int Status code of the particle. baryon_number_ : int Baryon number of the particle. weight_ : float Weight of the particle. Methods ------- t: Get/set t x: Get/set x y: Get/set y z: Get/set z mass: Get/set mass E: Get/set E px: Get/set px py: Get/set py pz: Get/set pz pdg: Get/set pdg ID: Get/set ID charge: Get/set charge ncoll: Get/set ncoll form_time: Get/set form_time xsecfac: Get/set xsecfactor proc_id_origin: Get/set proc_id_origin proc_type_origin: Get/set proc_type_origin t_last_coll: Get/set t_last_coll pdg_mother1: Get/set pdg_mother1 pdg_mother2: Get/set pdg_mother2 status: Get/set status baryon_number: Get/set baryon_number print_particle: Print the particle as CSV to terminal set_quantities_OSCAR2013: Set particle properties OSCAR2013 set_quantities_OSCAR2013Extended: Set particle properties OSCAR2013Extended set_quantities_JETSCAPE: Set particle properties JETSCAPE angular_momentum: Compute angular momentum momentum_rapidity_Y: Compute momentum rapidity p_abs: Compute absolute momentum pt_abs: Compute absolute value of transverse momentum phi: Compute azimuthal angle theta: Compute polar angle pseudorapidity: Compute pseudorapidity spatial_rapidity: Compute spatial rapidity proper_time: Compute proper time compute_mass_from_energy_momentum: Compute mass from energy momentum relation compute_charge_from_pdg: Compute charge from PDG code is_meson: Is the particle a meson? is_baryon: Is the particle a baryon? is_hadron: Is the particle a hadron? is_strange: Is the particle a strange particle? is_heavy_flavor: Is the particle a heavy flavor particle? weight: What is the weight of the particle? Notes ----- To set a value of one of the attributes by hand, please use the corresponding getter methods with parentheses including the value for the attribute of the particle. Examples -------- To use the particle class a particle has to be created and the attributes can be set or obtained with the corresponding functions. .. highlight:: python .. code-block:: python :linenos: >>> from Particle import Particle >>> >>> particle = Particle() >>> particle.t = 1.0 >>> print(particle.t) 1.0 """ def __init__(self): self.t_ = None self.x_ = None self.y_ = None self.z_ = None self.mass_ = None self.E_ = None self.px_ = None self.py_ = None self.pz_ = None self.pdg_ = None self.ID_ = None self.charge_ = None self.ncoll_ = None self.form_time_ = None self.xsecfac_ = None self.proc_id_origin_ = None self.proc_type_origin_ = None self.t_last_coll_ = None self.pdg_mother1_ = None self.pdg_mother2_ = None self.status_ = None self.baryon_number_ = None self.weight_ = None @property def t(self): """Get or set the time of the particle. Returns ------- t_ : float Raises ------ ValueError if time is not set """ if self.t_ == None: raise ValueError("t not set") else: return self.t_ @t.setter def t(self,value): self.t_ = value @property def x(self): """Get or set the x-position of the particle. Returns ------- x_ : float Raises ------ ValueError if x is not set """ if self.x_ == None: raise ValueError("x not set") else: return self.x_ @x.setter def x(self,value): self.x_ = value @property def y(self): """Get or set the y-position of the particle. Returns ------- y_ : float Raises ------ ValueError if y is not set """ if self.y_ == None: raise ValueError("y not set") else: return self.y_ @y.setter def y(self,value): self.y_ = value @property def z(self): """Get or set the z-position of the particle. Returns ------- z_ : float Raises ------ ValueError if z is not set """ if self.z_ == None: raise ValueError("z not set") else: return self.z_ @z.setter def z(self,value): self.z_ = value @property def mass(self): """Get or set the mass of the particle. Returns ------- mass_ : float Raises ------ ValueError if mass is not set """ if self.mass_ == None: raise ValueError("mass not set") else: return self.mass_ @mass.setter def mass(self,value): self.mass_ = value @property def E(self): """Get or set the energy of the particle. Returns ------- E_ : float Raises ------ ValueError if E is not set """ if self.E_ == None: raise ValueError("E not set") else: return self.E_ @E.setter def E(self,value): self.E_ = value @property def px(self): """Get or set the momentum x-component of the particle. Returns ------- px_ : float Raises ------ ValueError if px is not set """ if self.px_ == None: raise ValueError("px not set") else: return self.px_ @px.setter def px(self,value): self.px_ = value @property def py(self): """Get or set the momentum y-component of the particle. Returns ------- py_ : float Raises ------ ValueError if py is not set """ if self.py_ == None: raise ValueError("py not set") else: return self.py_ @py.setter def py(self,value): self.py_ = value @property def pz(self): """Get or set the momentum z-component of the particle. Returns ------- pz_ : float Raises ------ ValueError if pz is not set """ if self.pz_ == None: raise ValueError("pz not set") else: return self.pz_ @pz.setter def pz(self,value): self.pz_ = value @property def pdg(self): """Get or set the PDG code of the particle. Returns ------- pdg_ : int Raises ------ ValueError if pdg is not set """ if self.pdg_ == None: raise ValueError("pdg not set") else: return self.pdg_ @pdg.setter def pdg(self,value): self.pdg_ = value @property def ID(self): """Get or set the ID of the particle. This is a unique number in SMASH. Returns ------- ID_ : int Raises ------ ValueError if ID is not set """ if self.ID_ == None: raise ValueError("ID not set") else: return self.ID_ @ID.setter def ID(self,value): self.ID_ = value @property def charge(self): """Get or set the electrical charge of the particle. Returns ------- charge_ : int Raises ------ ValueError if charge is not set """ if self.charge_ == None: raise ValueError("charge not set") else: return self.charge_ @charge.setter def charge(self,value): self.charge_ = value @property def ncoll(self): """Get or set the number of collisions of the particle. Returns ------- ncoll_ : int Raises ------ ValueError if ncoll is not set """ if self.ncoll_ == None: raise ValueError("ncoll not set") else: return self.ncoll_ @ncoll.setter def ncoll(self,value): self.ncoll_ = value @property def form_time(self): """Get or set the formation time of the particle. Returns ------- form_time_ : float Raises ------ ValueError if form_time is not set """ if self.form_time_ == None: raise ValueError("form_time not set") else: return self.form_time_ @form_time.setter def form_time(self,value): self.form_time_ = value @property def xsecfac(self): """Get or set the crosssection scaling factor of the particle. Returns ------- xsecfac_ : float Raises ------ ValueError if xsecfac is not set """ if self.xsecfac_ == None: raise ValueError("xsecfac not set") else: return self.xsecfac_ @xsecfac.setter def xsecfac(self,value): self.xsecfac_ = value @property def proc_id_origin(self): """Get or set the process ID of the particle's origin. Returns ------- proc_id_origin_ : int Raises ------ ValueError if proc_id_origin is not set """ if self.proc_id_origin_ == None: raise ValueError("proc_id_origin not set") else: return self.proc_id_origin_ @proc_id_origin.setter def proc_id_origin(self,value): self.proc_id_origin_ = value @property def proc_type_origin(self): """Get or set the process type of the particle's origin. Returns ------- proc_type_origin_ : int Raises ------ ValueError if proc_type_origin is not set """ if self.proc_type_origin_ == None: raise ValueError("proc_type_origin not set") else: return self.proc_type_origin_ @proc_type_origin.setter def proc_type_origin(self,value): self.proc_type_origin_ = value @property def t_last_coll(self): """Get or set the last time of a collision of the particle. Returns ------- t_last_coll_ : float Raises ------ ValueError if t_last_coll is not set """ if self.t_last_coll_ == None: raise ValueError("t_last_coll not set") else: return self.t_last_coll_ @t_last_coll.setter def t_last_coll(self,value): self.t_last_coll_ = value @property def pdg_mother1(self): """Get the PDG code of the first mother particle. Returns ------- pdg_mother1_ : int Raises ------ ValueError if pdg_mother1 is not set """ if self.pdg_mother1_ == None: raise ValueError("pdg_mother1 not set") else: return self.pdg_mother1_ @pdg_mother1.setter def pdg_mother1(self,value): self.pdg_mother1_ = value @property def pdg_mother2(self): """Get the PDG code of the second mother particle. Returns ------- pdg_mother2_ : int Raises ------ ValueError if pdg_mother2 is not set """ if self.pdg_mother2_ == None: raise ValueError("pdg_mother2 not set") else: return self.pdg_mother2_ @pdg_mother2.setter def pdg_mother2(self,value): self.pdg_mother2_ = value @property def status(self): """ Get the hadron status (stores information on the module origin of a JETSCAPE hadron). Returns ------- status_ : int Raises ------ ValueError if status is not set """ if self.status_ == None: raise ValueError("status not set") else: return self.status_ @status.setter def status(self,value): self.status_ = value @property def baryon_number(self): """Get the baryon number of the particle. Returns ------- baryon_number_ : int Raises ------ ValueError if baryon_number is not set """ if self.baryon_number_ == None: raise ValueError("baryon number not set") else: return self.baryon_number_ @baryon_number.setter def baryon_number(self,value): self.baryon_number_ = value @property def weight(self): """Get the weight of the particle. Returns ------- weight_ : float Raises ------ ValueError if weight is not set """ if self.weight_ == None: raise ValueError("weight not set") else: return self.weight_ @weight.setter def weight(self,value): self.weight_ = value
[docs] def print_particle(self): """Print the whole particle information as csv string. This function prints a header line with the different quantities. All particle quantities are then printed in the next line separated by a comma. """ print('t,x,y,z,mass,E,px,py,pz,pdg,ID,charge,ncoll,form_time,xsecfac,\ proc_id_origin,proc_type_origin,t_last_coll,pdg_mother1,\ pdg_mother2,status,baryon_number,weight') print(f'{self.t_},{self.x_},{self.y_},{self.z_},{self.mass_},{self.E_},\ {self.px_},{self.py_},{self.pz_},{self.pdg_},{self.ID_},\ {self.charge_},{self.ncoll_},{self.form_time_},{self.xsecfac_},\ {self.proc_id_origin_},{self.proc_type_origin_}\ ,{self.t_last_coll_},{self.pdg_mother1_},{self.pdg_mother2_},\ {self.status_},{self.baryon_number_},{self.weight_}')
[docs] def set_quantities_OSCAR2013(self,line_from_file): """ Sets the particle quantities obtained from OSCAR2013. Parameters ---------- line_from_file: list, numpy.ndarray Contains the values read from the file. Raises ------ ValueError if the input line has not the same number of columns as OSCAR2013 """ # check if the line is a list or numpy array if (type(line_from_file) == list or isinstance(line_from_file, np.ndarray))\ and len(line_from_file)==12: self.t = float(line_from_file[0]) self.x = float(line_from_file[1]) self.y = float(line_from_file[2]) self.z = float(line_from_file[3]) self.mass = float(line_from_file[4]) self.E = float(line_from_file[5]) self.px = float(line_from_file[6]) self.py = float(line_from_file[7]) self.pz = float(line_from_file[8]) self.pdg = int(line_from_file[9]) self.ID = int(line_from_file[10]) self.charge = int(line_from_file[11]) else: error_message = 'The input line does not have the same number of '+\ 'columns as the OSCAR2013 format' raise ValueError(error_message)
[docs] def set_quantities_OSCAR2013Extended(self,line_from_file,photon=False): """ Sets the particle quantities obtained from OSCAR2013Extended. Parameters ---------- line_from_file: list, numpy.ndarray Contains the values read from the file. photon: bool Read in the extra field 'weight' for photon output. Raises ------ ValueError if the input line has not the same number of columns as OSCAR2013Extended """ # check if the line is a list or numpy array if (type(line_from_file) == list or isinstance(line_from_file, np.ndarray))\ and len(line_from_file)<=21 and len(line_from_file)>=20: self.t = float(line_from_file[0]) self.x = float(line_from_file[1]) self.y = float(line_from_file[2]) self.z = float(line_from_file[3]) self.mass = float(line_from_file[4]) self.E = float(line_from_file[5]) self.px = float(line_from_file[6]) self.py = float(line_from_file[7]) self.pz = float(line_from_file[8]) self.pdg = int(line_from_file[9]) self.ID = int(line_from_file[10]) self.charge = int(line_from_file[11]) self.ncoll = int(line_from_file[12]) self.form_time = float(line_from_file[13]) self.xsecfac = float(line_from_file[14]) self.proc_id_origin = int(line_from_file[15]) self.proc_type_origin = int(line_from_file[16]) self.t_last_coll = float(line_from_file[17]) self.pdg_mother1 = int(line_from_file[18]) self.pdg_mother2 = int(line_from_file[19]) if len(line_from_file)==21 and not photon: self.baryon_number = int(line_from_file[20]) elif len(line_from_file)==21 and photon: self.weight = float(line_from_file[20]) else: error_message = 'The input line does not have the same number of '+\ 'columns as the OSCAR2013Extended format' raise ValueError(error_message)
[docs] def set_quantities_JETSCAPE(self,line_from_file): """ Sets the particle quantities obtained from a JETSCAPE hadron file. The JETSCAPE hadron output does not directly contain information about the mass and the charge. Thus, they are computed from the four-momentum and the PDG code respectively. Parameters ---------- line_from_file: list, numpy.ndarray Contains the values read from the file. Raises ------ ValueError if the input line has not the same number of columns as JETSCAPE hadron file """ # check if the line is a list or numpy array if (type(line_from_file) == list or isinstance(line_from_file, np.ndarray))\ and len(line_from_file)==7: self.ID = int(line_from_file[0]) self.pdg = int(line_from_file[1]) self.status = int(line_from_file[2]) self.E = float(line_from_file[3]) self.px = float(line_from_file[4]) self.py = float(line_from_file[5]) self.pz = float(line_from_file[6]) self.mass = self.compute_mass_from_energy_momentum() self.charge = self.compute_charge_from_pdg() else: error_message = 'The input line does not have the same number of '+\ 'columns as the JETSCAPE hadron output format' raise ValueError(error_message)
[docs] def angular_momentum(self): """Compute the angular momentum :math:`L=r \\times p` of a particle Returns ------- angular_momentum : numpy.ndarray Array containing all three components of the angular momentum as :math:`[L_x, L_y, L_z]`. """ r = [self.x, self.y, self.z] p = [self.px, self.py, self.pz] return np.cross(r, p)
[docs] def momentum_rapidity_Y(self): """ Compute the momentum rapidity :math:`Y=\\frac{1}{2}\\ln\\left(\\frac{E+p_z}{E-p_z}\\right)` of the particle. Returns ------- float momentum rapidity """ if abs(self.E - self.pz) < 1e-10: denominator = (self.E - self.pz) + 1e-10 # Adding a small positive value else: denominator = (self.E - self.pz) return 0.5 * np.log((self.E + self.pz) / denominator)
[docs] def p_abs(self): """ Compute the absolute momentum :math:`|\\vec{p}|=\\sqrt{p_x^2+p_y^2+p_z^2}` of the particle. Returns ------- float absolute momentum """ return np.sqrt(self.px**2.+self.py**2.+self.pz**2.)
[docs] def pt_abs(self): """ Compute the absolute transverse momentum :math:`|\\vec{p}_{\\mathrm{T}}|=\\sqrt{p_x^2+p_y^2}` of the particle. Returns ------- float absolute transverse momentum """ return np.sqrt(self.px**2.+self.py**2.)
[docs] def phi(self): """ Compute the azimuthal angle of the particle. Returns ------- float azimuthal angle """ if (np.abs(self.px) < 1e-6) and (np.abs(self.py) < 1e-6): return 0. else: return math.atan2(self.py,self.px)
[docs] def theta(self): """ Compute the polar angle of the particle. Returns ------- float polar angle """ if self.p_abs() == 0: return 0. else: return np.arccos(self.pz / self.p_abs())
[docs] def pseudorapidity(self): """ Compute the pseudorapidity :math:`\eta=\\frac{1}{2}\\ln\\left(\\frac{|\\vec{p}|+p_z}{|\\vec{p}|-p_z}\\right)` of the particle. Returns ------- float pseudorapidity """ if abs(self.p_abs() - self.pz) < 1e-10: denominator = (self.p_abs() - self.pz) + 1e-10 # Adding a small positive value else: denominator = (self.p_abs() - self.pz) return 0.5 * np.log((self.p_abs()+self.pz) / denominator)
[docs] def spatial_rapidity(self): """ Compute the spatial rapidity :math:`y=\\frac{1}{2}\\ln\\left(\\frac{t+z}{t-z}\\right)` of the particle. Returns ------- float spatial rapidity """ if self.t > np.abs(self.z): return 0.5 * np.log((self.t+self.z) / (self.t-self.z)) else: raise ValueError("|z| < t not fulfilled")
[docs] def proper_time(self): """ Compute the proper time :math:`\\tau=\\sqrt{t^2-z^2}` of the particle. Returns ------- float proper time """ if self.t > np.abs(self.z): return np.sqrt(self.t**2.-self.z**2.) else: raise ValueError("|z| < t not fulfilled")
[docs] def compute_mass_from_energy_momentum(self): """ Compute the mass from the energy momentum relation. Returns ------- float mass """ if np.abs(self.E**2. - self.p_abs()**2.) > 1e-6 and\ self.E**2. - self.p_abs()**2. > 0.: return np.sqrt(self.E**2.-self.p_abs()**2.) else: return 0.
[docs] def compute_charge_from_pdg(self): """ Compute the charge from the PDG code. This function is called automatically if a JETSCAPE file is read in with the :meth:`ParticleClass.Particle.set_quantities_JETSCAPE` function. Returns ------- float charge """ return PDGID(self.pdg).charge
[docs] def is_meson(self): """ Is the particle a meson? Returns ------- bool True, False """ return PDGID(self.pdg).is_meson
[docs] def is_baryon(self): """ Is the particle a baryon? Returns ------- bool True, False """ return PDGID(self.pdg).is_baryon
[docs] def is_hadron(self): """ Is the particle a hadron? Returns ------- bool True, False """ return PDGID(self.pdg).is_hadron
[docs] def is_strange(self): """ Does the particle contain strangeness? Returns ------- bool True, False """ return PDGID(self.pdg).has_strange
[docs] def is_heavy_flavor(self): """ Is the particle a heavy flavor hadron? Returns ------- bool True, False """ if PDGID(self.pdg).has_charm or PDGID(self.pdg).has_bottom\ or PDGID(self.pdg).has_top: return True else: return False