Skip to content

Commit 228398a

Browse files
committedDec 18, 2024·
Merge branch 'USRYIELD'
2 parents f950061 + 2e96813 commit 228398a

File tree

4 files changed

+375
-5
lines changed

4 files changed

+375
-5
lines changed
 

‎bin/usysuw2root

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../mctools/fluka/usysuw2root.py

‎mctools/fluka/fluka2root.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def __init__(self, args):
5959
Estimator("USRCOLL", "ustsuw"),
6060
Estimator("USRTRACK", "ustsuw"),
6161
Estimator("DETECT", "detsuw"),
62+
Estimator("USRYIELD", "usysuw"),
6263
Estimator("RESNUCLE", "usrsuw")]
6364
self.opened = {} # dict of opened units (if any)
6465

@@ -356,7 +357,7 @@ def main():
356357
c = Converter(args)
357358
c.Merge()
358359
val = c.Convert()
359-
print(c.root)
360+
# print(c.root)
360361

361362
# title = c.getRunTitle()
362363

‎mctools/fluka/ustsuw2root.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def readHeader(self, filename):
6767
""" Reads the file header info
6868
Based on Data.Usrbdx
6969
"""
70-
f = Data.Usrxxx.readHeader(self, filename)
70+
f = super().readHeader(self, filename)
7171
# self.sayHeader()
7272

7373
while True:
@@ -112,7 +112,7 @@ def readHeader(self, filename):
112112
print(f"{det.name}: Low energy neutrons scored with {det.ngroup} groups")
113113
else:
114114
det.ngroup = 0
115-
det.egroup = []
115+
det.egroup = ()
116116

117117
size = (det.ngroup+det.ne) * 4
118118
if size != fortran.skip(f):
@@ -209,8 +209,8 @@ def main():
209209
h.SetBinContent(i+1, val[i])
210210
h.SetBinError(i+1, err[n-i-1]*val[i])
211211

212-
h.SetEntries(b.weight)
213-
h.Write()
212+
h.SetEntries(b.weight)
213+
h.Write()
214214

215215
# not implemented - bugs with theINFN FLUKA, but it seems works with the CERN FLUKA
216216
if det.lowneu:

‎mctools/fluka/usysuw2root.py

+368
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
#!/usr/bin/env python
2+
3+
import sys, argparse, struct
4+
import logging
5+
from os import path
6+
import numpy as np
7+
from mctools import fluka, getLogBins, getLinBins
8+
from mctools.fluka.flair import Data, fortran
9+
import ROOT
10+
ROOT.PyConfig.IgnoreCommandLineOptions = True
11+
12+
logger = logging.getLogger(__name__)
13+
14+
def getType(n):
15+
""" Decrypt what(1) of USRYIELD """
16+
for ie in range(-38,39):
17+
if ie == 0:
18+
continue
19+
for ia in range(1,39):
20+
for i4 in (-1,0,1):
21+
if (ie+100*ia+10000*i4 == n):
22+
return (ie,ia,i4)
23+
print(f"usysuw2root: what(1) == {n} undefined", file=sys.stderr)
24+
sys.exit(1)
25+
26+
def getDistTitle(i, var1, var2):
27+
"""
28+
Parse WHAT(6) and return the distribution title
29+
"""
30+
31+
x1 = var1[2] if len(var1) == 3 else "x_{1}"
32+
x2 = var2[2] if len(var2) == 3 else "x_{2}"
33+
34+
ixa = i
35+
ixm = 0
36+
if i>100:
37+
ixa = i % 100
38+
ixm = i // 100
39+
40+
match ixa:
41+
case 1:
42+
return "#frac{d^{2}#sigma}{d%sd%s}" % (x1, x2)
43+
# case 3:
44+
# return f"\#frac{d^{2}N}{d{x1}d{x2}}"
45+
# case 6:
46+
# return f"\#frac{1}{cos#theta}#frac{d^{2}N}{d{x1}d{x2}}"
47+
# case 9:
48+
# return f"\#frac{d^{2}N}{x_{2}d{x1}d{x2}}"
49+
# case 10:
50+
# return f"\#frac{d^{2}N}{{x1}d{x1}d{x2}}"
51+
case _:
52+
return "unknown (not implemented)"
53+
54+
def getVarTitle(i):
55+
match i:
56+
case 1:
57+
return ("Kinetic energy", "GeV", "E")
58+
case 2:
59+
return ("Total momentum", "GeV/c", "p")
60+
case 3:
61+
return ("Rapidity in the LAB frame", "", "w")
62+
case 4:
63+
return ("Rapidity in the CMS frame", "", "w")
64+
case 5:
65+
return ("Pseudo-rapidity in the LAB frame", "", "#eta")
66+
case 6:
67+
return ("Pseudo-rapidity in the CMS frame", "", "#eta")
68+
case 7:
69+
return ("Feynman-x in the LAB frame", "")
70+
case 8:
71+
return ("Feynman-x in the CMS frame", "")
72+
case 9:
73+
return ("Transverse momentum", "GeV/c")
74+
case 10:
75+
return ("Transverse mass", "GeV")
76+
case 11:
77+
return ("Longitudinal momentum in the LAB frame", "GeV/c")
78+
case 12:
79+
return ("Longitudinal momentum in the CMS frame", "GeV/c")
80+
case 13:
81+
return ("Total energy", "GeV")
82+
case 14:
83+
return ("Polar angle in the LAB frame", "rad", "#theta")
84+
case 15:
85+
return ("Polar angle in the CMS frame", "rad", "#theta")
86+
case 16:
87+
return ("Square transverse momentum", "(GeV/c)^{2}")
88+
case 17:
89+
return ("1/(2#pi sin#theta) weighted angle in the LAB frame", "")
90+
case 18:
91+
return ("1/(2#pi p_T) weighted transverse momentum", "GeV/c")
92+
case 19:
93+
return ("Rratio laboratory momentum to beam momentum", "")
94+
case 20:
95+
return ("Transverse kinetic energy", "")
96+
case 21:
97+
return ("Excitation energy", "")
98+
case 22:
99+
return ("Particle charge", "")
100+
case 23:
101+
return ("Particle LET", "")
102+
case 24:
103+
return ("Polar angle in the LAB frame", "deg", "#theta")
104+
case 25:
105+
return ("Polar angle in the CMS frame", "deg", "#theta")
106+
case 26:
107+
return ("Laboratory kinetic energy per nucleon", "")
108+
case 27:
109+
return ("Laboratory momentum per nucleon", "")
110+
case 28:
111+
return ("Particle baryonic charge", "")
112+
case 29:
113+
return ("Four-momentum transfer -t", "")
114+
case 30:
115+
return ("CMS longitudinal Feynman-x", "")
116+
case 31:
117+
return ("Excited mass squared", "")
118+
case 32:
119+
return ("Excited mass squared/s", "")
120+
case 33:
121+
return ("Time", "s", "t")
122+
case 34:
123+
return ("sin weighted angle in the LAB frame", "")
124+
case 35:
125+
return ("Total momentum in the CMS frame", "GeV/c")
126+
case 36:
127+
return ("Total energy in the CMS frame", "GeV/c")
128+
case _:
129+
return (f"Unknown: {i=}", "")
130+
131+
class USYSUW(Data.Usrxxx):
132+
"""
133+
Read the usysuw binary output (USRYIELD average)
134+
"""
135+
def setVerbose(self, val):
136+
self.verbose = val
137+
138+
def say(self, i):
139+
det = self.detector[i]
140+
print(f"Detector {det.NYL}:\t{det.TITUYL}")
141+
print(" cross section kind: ",det.ITUSYL)
142+
print(f" distributions: {det.IXUSYL} {fluka.particle.get(det.IDUSYL)}")
143+
print(f" from region {det.NR1UYL} to region {det.NR2UYL}")
144+
print(" normalisation factor:",det.USNRYL)
145+
print(" normalisatoin cross section:",det.SGUSYL)
146+
print(" low energy neutron scoring:",det.LLNUYL)
147+
print(f" min/max energies: {det.EYLLOW} {det.EYLHGH} GeV")
148+
print(" number of energy or other quantity intervals:",det.NEYLBN)
149+
print(" first variable bin width:",det.DEYLBN)
150+
print(f" second variable min/max: {det.AYLLOW} {det.AYLHGH} rad")
151+
152+
def getBins(self,det):
153+
""" Return lin or log energy bins depending on the value of i """
154+
ie, ia, i4 = getType(det.ITUSYL)
155+
if ie < 0:
156+
return getLogBins(det.NEYLBN, det.EYLLOW, det.EYLHGH)
157+
else:
158+
return getLinBins(det.NEYLBN, det.EYLLOW, det.EYLHGH)
159+
160+
def hist(self,det):
161+
""" Create histogram for the given detector """
162+
if det.NEYLBN == 0:
163+
logger.warning(f"Not saving detector {det.name} into ROOT file since it has 0 energy bins: {det.elow} < E < {det.ehigh}")
164+
logger.warning(" This happens for neutron-contributing estimators if max scoring energy is below groupwise max energy, 20 MeV.")
165+
return None
166+
167+
ie, ia, i4 = getType(det.ITUSYL)
168+
var1 = getVarTitle(ie)
169+
var2 = getVarTitle(ia)
170+
171+
title = fluka.particle.get(det.IDUSYL, "undefined")
172+
title += " #diamond "
173+
if int(det.NR1UYL) == -1 and int(det.NR2UYL) == -2:
174+
if det.IDUSYL > -800.0: # see WHAT(2)
175+
title += "Emerging Inelastic yield"
176+
else:
177+
title += "Entering Inelastic yield"
178+
else:
179+
title += "f{det.NR1UYL} #to {det.NR2UYL} #diamond "
180+
title += " #diamond "
181+
title += var2[0]
182+
title += f": {det.AYLLOW:.5g} to {det.AYLHGH:.5g} {var2[1]}"
183+
title += ";%s [%s]" % (var1[0:2])
184+
185+
# y-axis
186+
title += ";" + getDistTitle(det.IXUSYL, var1, var2)
187+
188+
189+
return ROOT.TH1F(det.TITUYL, title, det.NEYLBN, self.getBins(det))
190+
191+
192+
def readHeader(self, filename):
193+
f = super().readHeader(filename)
194+
195+
data = fortran.read(f)
196+
197+
if data is None:
198+
logger.error("Invalid usrysuw file")
199+
sys.exit(1)
200+
201+
size = len(data)
202+
logger.debug(f"{size=}")
203+
IJUSYL, JTUSYL, PUSRYL, SQSUYL, UUSRYL, VUSRYL, WUSRYL = struct.unpack("=2i5f", data)
204+
205+
logger.info(f"Projectile: {IJUSYL}, its lab. momentum: {PUSRYL}, lab. direction: {UUSRYL} {VUSRYL} {WUSRYL}")
206+
logger.info(f"Target: {JTUSYL}")
207+
logger.info(f"CMS energy for Lorentz transformation: {SQSUYL} GeV")
208+
209+
while True:
210+
data = fortran.read(f)
211+
if data is None:
212+
logger.debug("data = None -> break")
213+
break
214+
size = len(data)
215+
logger.debug(f"read while True: \t{size=}")
216+
if size == 70: # new detector
217+
logger.debug(f" Reading the header...\t{size=}")
218+
header = struct.unpack("=i10s3i2i2fi2fif2f", data)
219+
det = Data.Detector()
220+
det.NYL = header[0]
221+
det.TITUYL = header[1].decode('utf-8').strip() # detector title (sdum)
222+
det.ITUSYL = header[2] # sdum
223+
det.IXUSYL = header[3] # what(6)
224+
det.IDUSYL = header[4]
225+
det.NR1UYL = header[5]
226+
det.NR2UYL = header[6]
227+
det.USNRYL = header[7]
228+
det.SGUSYL = header[8]
229+
det.LLNUYL = header[9]
230+
if det.LLNUYL == 1:
231+
logger.warning(f"{det.TITUYL}: No groupwise low energy neutrons are implemented yet. Their data will be skipped.")
232+
det.EYLLOW = header[10]
233+
det.EYLHGH = header[11]
234+
det.NEYLBN = header[12]
235+
det.DEYLBN = header[13]
236+
det.AYLLOW = header[14]
237+
det.AYLHGH = header[15]
238+
self.detector.append(det)
239+
elif size == 14: # and data.decode('utf8')[:10] == "STATISTICS":
240+
self.statpos = f.tell()
241+
break
242+
# logger.debug(f" Reading STATISTICS...\t{size=}")
243+
# for det in self.detector:
244+
# data = Data.unpackArray(fortran.read(f))
245+
# logger.debug(f" detector {det.NYL}")
246+
# det.total = data[0]
247+
# det.totalerror = data[1]
248+
# print("total:",det.total, det.totalerror)
249+
# for j in range(6):
250+
# fortran.skip(f)
251+
# elif size == det.NEYLBN*4:
252+
# logger.debug(f" Reading the data block\t{size=}")
253+
# elif size == 8:
254+
# det.total, det.totalerror = struct.unpack("=2f", data)
255+
# logger.debug(f"{det.total=} {det.totalerror=}")
256+
# else:
257+
# logger.debug(f"else {size=}")
258+
# if det.LLNUYL: # low energy neutrons
259+
# logger.debug(f"\t low energy neutrons")
260+
# det.ngroup = struct.unpack("=i",data[:4])[0]
261+
# det.egroup = struct.unpack("=%df"%(det.ngroup+1), data[4:])
262+
# print(det.ngroup)
263+
# else:
264+
# det.ngroup = 0
265+
# det.egroup = ()
266+
# size = (det.ngroup+det.ne) * 4
267+
# if size != fortran.skip(f):
268+
# raise IOError("Invalid USRTRACK file")
269+
# f.close()
270+
271+
def readStat(self, i, lowneu):# almost the same as ustsuw2root. TODO: use a common base class
272+
""" Read detector # det statistical data """
273+
if self.statpos < 0:
274+
logger.warning(f"Negative statpos: {self.statpos=}")
275+
return None
276+
277+
logger.debug(f"{self.statpos=}")
278+
279+
with open(self.file,"rb") as f:
280+
f.seek(self.statpos)
281+
for _ in range(7*i+3):
282+
fortran.skip(f) # skip previous detectors
283+
data = fortran.read(f)
284+
285+
return data
286+
287+
def readData(self, i, lowneu): # almost the same as ustsuw2root. TODO: use a common base class
288+
"""Read detector det data structure
289+
290+
"""
291+
with open(self.file,"rb") as f:
292+
fortran.skip(f) # Skip the header read by super()
293+
for _ in range(2*i+2):
294+
fortran.skip(f) # Previous Detector Header & Data
295+
data = fortran.read(f) # Current Detector Data
296+
297+
return data
298+
299+
300+
def main():
301+
""" Converts usysuw output into a ROOT TH2F histogram """
302+
303+
parser = argparse.ArgumentParser(description=main.__doc__,
304+
epilog="Homepage: https://github.com/kbat/mc-tools")
305+
parser.add_argument('usryield', type=str, help='usysuw binary output')
306+
parser.add_argument('root', type=str, nargs='?', help='output ROOT file name', default="")
307+
parser.add_argument('-v', '--verbose', action='store_true', default=False, dest='verbose', help='print what is being done')
308+
309+
args = parser.parse_args()
310+
311+
loglevel = logging.INFO if args.verbose else logging.WARNING
312+
313+
logging.basicConfig(
314+
encoding='utf-8',
315+
level=loglevel,
316+
format="{levelname}: {message}",
317+
style="{",
318+
datefmt="%Y-%m-%d %H:%M",
319+
)#, filename='usysuw2root.log')
320+
321+
logger.warning("The USRYIELD converter is not thoroughly tested yet. Compare the ROOT histograms (both values and errors) with the FLUKA-generated %s." % args.usryield.replace(".usryield", "_tab.lis"))
322+
323+
if not path.isfile(args.usryield):
324+
logger.error("File %s does not exist." % args.usryield)
325+
return 1
326+
327+
if args.root == "":
328+
rootFileName = "%s%s" % (args.usryield,".root")
329+
else:
330+
rootFileName = args.root
331+
332+
logger.debug(rootFileName)
333+
334+
b = USYSUW()
335+
b.setVerbose(args.verbose)
336+
b.readHeader(args.usryield) # data file closed here
337+
338+
ND = len(b.detector)
339+
340+
if args.verbose:
341+
b.sayHeader()
342+
logger.info("%s %d %s found:" % ('*'*20, ND, "estimator" if ND==1 else "estimators"))
343+
for i in range(ND):
344+
b.say(i)
345+
print("")
346+
347+
fout = ROOT.TFile(rootFileName, "recreate")
348+
for i in range(ND):
349+
det = b.detector[i]
350+
val = Data.unpackArray(b.readData(i,det.LLNUYL))
351+
logger.debug(f"{val=}")
352+
err = Data.unpackArray(b.readStat(i,det.LLNUYL))
353+
logger.debug(f"{err=}")
354+
355+
assert len(val) == len(err), "val and err length are different: %d %d" % (len(val), len(err))
356+
357+
h = b.hist(det)
358+
359+
if h:
360+
n = h.GetNbinsX()
361+
for i in range(n):
362+
h.SetBinContent(i+1, val[i])
363+
h.SetBinError(i+1, err[n-i-1]*val[i])
364+
h.SetEntries(b.weight)
365+
h.Write()
366+
367+
if __name__=="__main__":
368+
sys.exit(main())

0 commit comments

Comments
 (0)
Please sign in to comment.