Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/usr/bin/env python3
import sys, math, itertools, re, csv, pprint
import xml.etree.ElementTree as ET
from collections import OrderedDict
##############################################################
#
# reading xml input data, return line by line particle data
#
def parse(filename):
tree = ET.parse(filename)
root = tree.getroot()
for particle in root.iter("particle"):
name = particle.attrib["name"]
antiName = "Unknown"
# print (str(particle.attrib))
if ("antiName" in particle.attrib):
antiName = particle.attrib["antiName"]
# print ("found anti: " + name + " " + antiName + "\n" )
pdg_id = int(particle.attrib["id"])
mass = float(particle.attrib["m0"]) # GeV
electric_charge = int(particle.attrib["chargeType"]) # in units of e/3
decay_width = float(particle.attrib.get("mWidth", 0)) # GeV
lifetime = float(particle.attrib.get("tau0", math.inf)) # mm / c
yield (pdg_id, name, mass, electric_charge, antiName)
# TODO: read decay channels from child elements
if "antiName" in particle.attrib:
yield (-pdg_id, antiName, mass, -electric_charge, name)
##############################################################
#
# returns dict with particle codes and class names
#
def class_names(filename):
tree = ET.parse(filename)
root = tree.getroot()
map = {}
for particle in root.iter("particle"):
name = particle.attrib["classname"]
pdg_id = int(particle.attrib["id"])
map[pdg_id] = name
return map
##############################################################
#
# Automatically produce a string qualifying as C++ class name
#
# This function produces names of type "DELTA_PLUS_PLUS"
#
def c_identifier(name):
orig = name
name = name.upper()
name = name.replace(c, "_")
name = name.replace("BAR", "_BAR")
name = name.replace("0", "_0")
name = name.replace("*", "_STAR")
name = name.replace("'", "_PRIME")
name = name.replace("+", "_PLUS")
name = name.replace("-", "_MINUS")
while True:
tmp = name.replace("__", "_")
if tmp == name:
break
else:
name = tmp
pattern = re.compile(r'^[A-Z_][A-Z_0-9]*$')
if pattern.match(name):
return name.strip("_")
else:
raise Exception("could not generate C identifier for '{:s}'".format(orig))
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
##############################################################
#
# Automatically produce a string qualifying as C++ class name
#
# This function produces names of type "DeltaPlusPlus"
#
def c_identifier_cammel(name):
orig = name
name = name[0].upper() + name[1:].lower() # all lower case
for c in "() /": # replace funny characters
name = name.replace(c, "_")
name = name.replace("bar", "Bar")
name = name.replace("*", "Star")
name = name.replace("'", "Prime")
name = name.replace("+", "Plus")
name = name.replace("-", "Minus")
# move "Bar" to end of name
ibar = name.find('Bar')
if (ibar>0 and ibar<len(name)-3) :
name = name[:ibar] + name[ibar+3:] + str('Bar')
# cleanup "_"s
while True:
tmp = name.replace("__", "_")
if tmp == name:
break
else:
name = tmp
name.strip("_")
# remove all "_", if this does not by accident concatenate two number
istart = 0
while True:
i = name.find('_', istart)
if (i<1 or i>len(name)-1):
break
istart = i
if (name[i-1].isdigit() and name[i+1].isdigit()):
# there is a number on both sides
break
name = name[:i] + name[i+1:]
# and last, for example: make NuE out of Nue
if (name[i-1].islower() and name[i].islower()) :
if (i<len(name)-1) :
name = name[:i] + name[i].upper() + name[i+1:]
else :
name = name[:i] + name[i].upper()
# check if name is valid C++ identifier
pattern = re.compile(r'^[a-zA-Z_][a-zA-Z_0-9]*$')
if pattern.match(name):
return name
else:
raise Exception("could not generate C identifier for '{:s}' name='{:s}'".format(orig, name))
# ########################################################
#
# returns dict containing all data from pythia-xml input
#
def build_pythia_db(filename, classnames):
particle_db = OrderedDict()
for (pdg, name, mass, electric_charge, antiName) in parse(filename):
c_id = "unknown"
if (pdg in classnames):
c_id = classnames[pdg]
else:
c_id = c_identifier_cammel(name) # the cammel case names
#~ print(name, c_id, sep='\t', file=sys.stderr)
#~ enums += "{:s} = {:d}, ".format(c_id, corsika_id)
particle_db[c_id] = {
"name" : name,
"antiName" : antiName,
"pdg" : pdg,
"mass" : mass, # in GeV
"electric_charge" : electric_charge # in e/3
}
return particle_db
###############################################################
#
# return string with enum of all internal particle codes
#
def gen_internal_enum(pythia_db):
ralfulrich
committed
string = "enum class Code : uint8_t {\n"
string += " FirstParticle = 1, // if you want to loop over particles, you want to start with \"1\" \n" # identifier for eventual loops...
last_ngc_id = 0
for k in filter(lambda k: "ngc_code" in pythia_db[k], pythia_db):
ralfulrich
committed
last_ngc_id = pythia_db[k]['ngc_code']
string += " {key:s} = {code:d},\n".format(key = k, code = last_ngc_id)
string += " LastParticle = " + str(last_ngc_id+1) + ",\n" # identifier for eventual loops...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
string += "};"
return string
###############################################################
#
# return string with all data arrays
#
def gen_properties(pythia_db):
# number of particles, size of tables
string = "static constexpr std::size_t size = {size:d};\n".format(size = len(pythia_db))
string += "\n"
# particle masses table
string += "static constexpr std::array<quantity<energy_d> const, size> masses = {\n"
for p in pythia_db.values():
string += " {mass:f}_GeV, // {name:s}\n".format(mass = p['mass'], name = p['name'])
string += "};\n\n"
# PDG code table
string += "static constexpr std::array<PDGCode const, size> pdg_codes = {\n"
for p in pythia_db.values():
string += " {pdg:d}, // {name:s}\n".format(pdg = p['pdg'], name = p['name'])
string += "};\n"
# name string table
string += "static const std::array<std::string const, size> names = {\n"
for p in pythia_db.values():
string += " \"{name:s}\",\n".format(name = p['name'])
string += "};\n"
# electric charges table
string += "static constexpr std::array<int16_t, size> electric_charge = {\n"
for p in pythia_db.values():
string += " {charge:d},\n".format(charge = p['electric_charge'])
string += "};\n"
# anti-particle table
# string += "static constexpr std::array<size, size> anti_particle = {\n"
# for p in pythia_db.values():
# string += " {anti:d},\n".format(charge = p['anti_particle'])
# string += "};\n"
return string
###############################################################
#
# return string with a list of classes for all particles
#
def gen_classes(pythia_db):
string = "// list of C++ classes to access particle properties"
for cname in pythia_db:
antiP = 'unknown'
for cname_anti in pythia_db:
if (pythia_db[cname_anti]['name'] == pythia_db[cname]['antiName']):
antiP = cname_anti
break
string += "\n";
string += "/** @class " + cname + "\n\n"
string += " * Particle properties are taken from the PYTHIA8 ParticleData.xml file:<br>\n"
string += " * - pdg=" + str(pythia_db[cname]['pdg']) +"\n"
string += " * - mass=" + str(pythia_db[cname]['mass']) + " GeV \n"
string += " * - charge= " + str(pythia_db[cname]['electric_charge']/3) + " \n"
string += " * - name=" + str(cname) + "\n"
string += " * - anti=" + str(antiP) + "\n"
string += "*/\n\n"
string += "class " + cname + "{\n"
string += " public:\n"
ralfulrich
committed
string += " static Code GetCode() { return Type; }\n"
string += " static quantity<energy_d> GetMass() { return masses[TypeIndex]; }\n"
string += " static quantity<electric_charge_d> GetCharge() { return fwk::constants::e*electric_charge[TypeIndex]/3; }\n"
string += " static int GetChargeNumber() { return electric_charge[TypeIndex]/3; }\n"
string += " static std::string GetName() { return names[TypeIndex]; }\n"
ralfulrich
committed
string += " static Code GetAntiParticle() { return AntiType; }\n"
string += " static const Code Type = Code::" + cname + ";\n"
string += " static const Code AntiType = Code::" + antiP + ";\n"
string += " private:\n"
string += " static const uint8_t TypeIndex = static_cast<uint8_t const>(Type);\n"
string += "};\n"
return string
###############################################################
#
#
def inc_start():
string = ""
string += "#ifndef _include_GeneratedParticleDataTable_h_\n"
string += "#define _include_GeneratedParticleDataTable_h_\n\n"
ralfulrich
committed
string += "#include <fwk/PhysicalUnits.h>\n"
string += "#include <array>\n"
string += "#include <cstdint>\n"
ralfulrich
committed
# string += "#include <iostream>\n\n"
string += "typedef int16_t PDGCode;\n\n"
return string
###############################################################
#
#
def inc_end():
string = ""
string += "\n}\n\n"
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
string += "#endif\n"
return string
###################################################################
#
# Main function
#
if __name__ == "__main__":
if (len(sys.argv)!=3) :
print ("pdxml_reader.py Pythia8.xml ClassNames.xml")
sys.exit(0)
names = class_names(sys.argv[2])
pythia_db = build_pythia_db(sys.argv[1], names)
print ("\n pdxml_reader.py: Automatically produce particle-properties from PYTHIA8 xml file\n")
counter = itertools.count(0)
not_modeled = []
for p in pythia_db:
pythia_db[p]['ngc_code'] = next(counter)
with open("GeneratedParticleProperties.inc", "w") as f:
print(inc_start(), file=f)
print(gen_internal_enum(pythia_db), file=f)
print(gen_properties(pythia_db), file=f)
print(gen_classes(pythia_db), file=f)
print(inc_end(), file=f)
#~ print(pdg_id_table, mass_table, name_table, enums, sep='\n\n')