ATLAS Offline Software
Loading...
Searching...
No Matches
PDGHelpers.py
Go to the documentation of this file.
1# Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
2
3"""Parser for the PDGTABLE.MeV file
4
5 Creates a compact object that holds configuration
6 for the ExtraParticlesPhysicsTool.
7
8 Gaudi cannot parse nested std::map objects, so the extra
9 particles are converted into a
10
11 std::map< std::string, std::vector< double > >
12
13 object and ordering of the values is therefore important.
14
15 Rows are:
16 name
17 mass [MeV]
18 width [MeV]
19 pdg
20 lifetime [ns]
21
22 Charge, spin, parity, and isospin3 are set to zero.
23
24 AUTHOR: miha muskinja
25 DATE: August 2019
26
27"""
28
29import os
30
31from AthenaCommon.SystemOfUnits import MeV, joule
32from AthenaCommon.PhysicalConstants import hbar_Planck, h_Planck
33from AthenaCommon.Logging import logging
34from functools import lru_cache
35
36@lru_cache
37def getPDGTABLE(table):
38 # Delete a local file if present
39 if os.path.isfile(table):
40 os.remove(table)
41 # Grab the file
42 os.system('get_files -data %s' % table)
43 return True
44
45
46@lru_cache
48 # Delete a local file if present
49 if os.path.isfile(acceptlist):
50 os.remove(acceptlist)
51 #create blank file
52 blank = open('G4particle_acceptlist_ExtraParticles.txt', 'x')
53 blank.close()
54 return True
55
56
57def updateExtraParticleAcceptList(listName='G4particle_acceptlist_ExtraParticles.txt', pdgcodes=[]):
58 if getExtraParticleAcceptList(listName):
59 import shutil
60 shutil.copy(listName, listName+'.org')
61 existingpdgcodes = [int(x) for x in open(listName).readlines()]
62 newpdgcodes = list(set(pdgcodes).difference(existingpdgcodes))
63 # update the acceptlist for GenParticleSimAcceptList
64 with open(listName, 'a') as writer:
65 for pdg in newpdgcodes:
66 writer.write('%s\n' % pdg)
67
68
70 name = ""
71 mass = -1
72 width = -1
73 charge = 0
74 pdg = 0
75 lifetime = h_Planck / joule
76
77 def __init__(self, **kwargs):
78 self.__dict__.update(kwargs)
79
80 def __str__(self):
81 string = ''
82 string += 'name: %s\n' % self.name
83 string += ' mass: %s\n' % self.mass
84 string += ' width: %s\n' % self.width
85 string += ' charge: %s\n' % self.charge
86 string += ' pdg: %s\n' % self.pdg
87 string += ' lifetime: %s\n' % self.lifetime
88 return string
89
90 def __neg__(self):
91 antiParticle = ExtraParticle(**self.__dict__)
92 antiParticle.name = self.getAntiName()
93 antiParticle.charge *= -1
94 antiParticle.pdg *= -1
95 return antiParticle
96
97 def getAntiName(self):
98 if self.name[-1] == '0':
99 if 'anti_' in self.name:
100 return self.name.replace('anti_', '')
101 else:
102 return 'anti_' + self.name
103 elif self.name[-1] == '+':
104 return self.name.replace('+', '-')
105 elif self.name[-1] == '-':
106 return self.name.replace('-', '+')
107
108
110
111 def __init__(self, table, ranges):
112 self.log = logging.getLogger(__name__)
113 self.table = table
114 self.ranges = ranges
116 self.parsePDGTABLE()
117
118 def accept(self, pdg):
119 """Function to determine which extra particles are added
120
121 Function checks the ranges member variable
122 and evaluates whether the particle should be accepted.
123
124 TODO Consider adding a Sim.ExtraParticlesRanges ConfigFlag
125
126 For example, '111-556,1112-9090226' matches everything from
127 111 to 555 and 1112 to 9090225.
128 """
129 ranges = [r.split("-") for r in self.ranges.split(",")]
130 for r in ranges:
131 if int(r[0]) <= pdg < int(r[1]):
132 return True
133
134 def parsePDGTABLE(self):
135
136 # parse the PDGTABLE
137 with open(self.table, 'r') as f:
138 for line in f:
139 if line.startswith('*'):
140 # Comments start with '*'
141 continue
142 splitLine = line.split()
143
144 # Name of the particle
145 baseName = splitLine[-2]
146
147 # Number of particle entries
148 charges = splitLine[-1].split(',')
149
150 # Mass or Width
151 prop = ''
152 symbol = splitLine[0]
153 if symbol == 'M':
154 prop = 'mass'
155 elif symbol == 'W':
156 prop = 'width'
157 else:
158 raise ValueError(
159 'Unidentified symbol %s for particle %s' % (
160 symbol, baseName))
161
162 pdgs = splitLine[1:1+len(charges)]
163 value = float(splitLine[1+len(charges)])
164
165 for pdg, charge in zip(pdgs, charges):
166 if not self.accept(int(pdg)):
167 continue
168 name = self.formatName(baseName, charge)
169 kwargs = dict()
170 kwargs.setdefault('name', name)
171 kwargs.setdefault(prop, value * MeV)
172 kwargs.setdefault('pdg', int(pdg))
173 kwargs.setdefault('charge', self.formatCharge(charge))
174 if name not in self.extraParticles.keys():
175 self.extraParticles[name] = ExtraParticle(**kwargs)
176 else:
177 if getattr(self.extraParticles[name], prop) != -1:
178 self.log.warning(
179 "Property %s is already"
180 "set for particle %s."
181 "Current value is %s and"
182 "incoming value is %s.",
183 prop, name,
184 getattr(self.extraParticles[name], prop),
185 value)
186 continue
187 setattr(self.extraParticles[name], prop, value)
188
189 for name in self.extraParticles:
190 # calculate lifetime
191 width = self.extraParticles[name].width
192 if width > 0:
193 self.extraParticles[name].lifetime = hbar_Planck/width
194
195 # create anti-particles
196 for name in [x for x in self.extraParticles.keys()]:
197 antiParticle = -self.extraParticles[name]
198 self.extraParticles.update({antiParticle.name: antiParticle})
199
200 def formatName(self, name, charge):
201 nameOut = name
202 nameOut = nameOut.replace("*", "_star").replace("'", "_prime")
203 nameOut += charge
204 return nameOut
205
206 def formatCharge(self, charge):
207 # check that all characters are the same
208 # e.g.: '0', '+', '--', ...
209 if not (charge == len(charge) * charge[0]):
210 raise ValueError('Unexpected charge %s' % charge)
211
212 # parse string
213 if '+' in charge:
214 return len(charge)
215 elif '-' in charge:
216 return -len(charge)
217 elif '0' in charge:
218 return 0
219 else:
220 raise ValueError('Unexpected charge %s' % charge)
221
222 def createList(self):
223 pdgcodes = [ self.extraParticles[name].pdg for name in self.extraParticles ]
224 updateExtraParticleAcceptList('G4particle_acceptlist_ExtraParticles.txt', pdgcodes)
225
226 # generate output in correct format
227 outDict = dict()
228 for name in self.extraParticles:
229 outDict.update({name: [
230 self.extraParticles[name].mass,
231 self.extraParticles[name].width,
232 self.extraParticles[name].charge,
233 self.extraParticles[name].pdg,
234 self.extraParticles[name].lifetime
235 ]})
236
237 return outDict
__init__(self, **kwargs)
Definition PDGHelpers.py:77
formatCharge(self, charge)
__init__(self, table, ranges)
formatName(self, name, charge)
STL class.
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition hcg.cxx:310
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:177
getPDGTABLE(table)
Definition PDGHelpers.py:37
getExtraParticleAcceptList(acceptlist)
Definition PDGHelpers.py:47
updateExtraParticleAcceptList(listName='G4particle_acceptlist_ExtraParticles.txt', pdgcodes=[])
Definition PDGHelpers.py:57