-
Johannes Schumann authoredJohannes Schumann authored
output.py 10.72 KiB
# Filename: output.py
"""
IO for km3buu
"""
__author__ = "Johannes Schumann"
__copyright__ = "Copyright 2020, Johannes Schumann and the KM3NeT collaboration."
__credits__ = []
__license__ = "MIT"
__maintainer__ = "Johannes Schumann"
__email__ = "jschumann@km3net.de"
__status__ = "Development"
import re
import numpy as np
from io import StringIO
from os import listdir
from os.path import isfile, join, abspath
from tempfile import TemporaryDirectory
import awkward
import uproot
from scipy.interpolate import UnivariateSpline
from scipy.spatial.transform import Rotation
from .jobcard import Jobcard, read_jobcard, PDGID_LOOKUP
EVENT_FILENAME = "FinalEvents.dat"
ROOT_PERT_FILENAME = "EventOutput.Pert.[0-9]{8}.root"
ROOT_REAL_FILENAME = "EventOutput.Real.[0-9]{8}.root"
FLUXDESCR_FILENAME = "neutrino_initialized_energyFlux.dat"
XSECTION_FILENAMES = {"all": "neutrino_absorption_cross_section_ALL.dat"}
PARTICLE_COLUMNS = ["E", "Px", "Py", "Pz", "barcode"]
EVENTINFO_COLUMNS = [
"weight", "evType", "lepIn_E", "lepIn_Px", "lepIn_Py", "lepIn_Pz",
"lepOut_E", "lepOut_Px", "lepOut_Py", "lepOut_Pz", "nuc_E", "nuc_Px",
"nuc_Py", "nuc_Pz"
]
LHE_NU_INFO_DTYPE = np.dtype([
("type", np.int),
("weight", np.float64),
("mom_lepton_in_E", np.float64),
("mom_lepton_in_x", np.float64),
("mom_lepton_in_y", np.float64),
("mom_lepton_in_z", np.float64),
("mom_lepton_out_E", np.float64),
("mom_lepton_out_x", np.float64),
("mom_lepton_out_y", np.float64),
("mom_lepton_out_z", np.float64),
("mom_nucleon_in_E", np.float64),
("mom_nucleon_in_x", np.float64),
("mom_nucleon_in_y", np.float64),
("mom_nucleon_in_z", np.float64),
])
FLUX_INFORMATION_DTYPE = np.dtype([("energy", np.float64),
("flux", np.float64),
("events", np.float64)])
EVENT_TYPE = {
1: "QE",
32: "pi neutron-background",
33: "pi proton-background",
34: "DIS",
35: "2p2h QE",
36: "2p2h Delta",
37: "2pi background",
}
def read_nu_abs_xsection(filepath):
"""
Read the crosssections calculated by GiBUU
Parameters
----------
filepath: str
Filepath to the GiBUU output file with neutrino absorption cross-section
(neutrino_absorption_cross_section_*.dat)
"""
with open(filepath, "r") as f:
lines = f.readlines()
header = re.sub(r"\d+:|#", "", lines[0]).split()
dt = np.dtype([(field, np.float64) for field in header])
values = np.genfromtxt(StringIO(lines[-1]), dtype=dt)
return values
def parse_gibuu_event_info(line):
fields = line.split()[1:]
if int(fields[0]) != 5:
raise NotImplementedError(
"Event information type %s cannot be parsed yet!" % fields[0])
else:
return np.genfromtxt(StringIO(line[3:]), dtype=LHE_NU_INFO_DTYPE)
class GiBUUOutput:
def __init__(self, data_dir):
"""
Class for parsing GiBUU output files
Parameters
----------
data_dir: str
"""
if isinstance(data_dir, TemporaryDirectory):
self._tmp_dir = data_dir
self._data_path = abspath(data_dir.name)
else:
self._data_path = abspath(data_dir)
self.output_files = [
f for f in listdir(self._data_path)
if isfile(join(self._data_path, f))
]
self._read_xsection_file()
self._read_root_output()
self._read_flux_file()
self._read_jobcard()
def _read_root_output(self):
root_pert_regex = re.compile(ROOT_PERT_FILENAME)
self.root_pert_files = list(
filter(root_pert_regex.match, self.output_files))
root_real_regex = re.compile(ROOT_REAL_FILENAME)
self.root_real_files = list(
filter(root_real_regex.match, self.output_files))
def _read_xsection_file(self):
if XSECTION_FILENAMES["all"] in self.output_files:
setattr(
self,
"xsection",
read_nu_abs_xsection(
join(self._data_path, XSECTION_FILENAMES["all"])),
)
def _read_jobcard(self):
jobcard_regex = re.compile(".*.job")
jobcard_files = list(filter(jobcard_regex.match, self.output_files))
if len(jobcard_files) == 1:
self._jobcard_fname = jobcard_files[0]
self.jobcard = read_jobcard(
join(self._data_path, self._jobcard_fname))
else:
self.jobcard = None
def _read_flux_file(self):
fpath = join(self._data_path, FLUXDESCR_FILENAME)
self.flux_data = np.loadtxt(fpath, dtype=FLUX_INFORMATION_DTYPE)
self.flux_interpolation = UnivariateSpline(self.flux_data["energy"],
self.flux_data["events"])
@property
def event_weights(self):
event_df = self.event_info_df
gibuu_wgt = event_df["weight"]
flux = self.flux_interpolation(event_df["lepIn_E"])
energy_min = np.min(self.flux_data["energy"])
energy_max = np.max(self.flux_data["energy"])
total_events = self.flux_interpolation.integral(energy_min, energy_max)
n_files = len(self.root_pert_files)
wgt = np.divide(total_events * gibuu_wgt, flux * n_files)
return wgt
@property
def particle_df(self):
import pandas as pd
df = None
for fname in self.root_pert_files:
fobj = uproot.open(join(self._data_path, fname))
file_df = None
for col in PARTICLE_COLUMNS:
tmp = awkward.topandas(fobj["RootTuple"][col].array(),
flatten=True)
tmp.name = col
if file_df is None:
file_df = tmp
else:
file_df = pd.concat([file_df, tmp], axis=1)
if df is None:
df = file_df
else:
new_indices = file_df.index.levels[0] + df.index.levels[0].max(
) + 1
file_df.index = file_df.index.set_levels(new_indices, level=0)
df = df.append(file_df)
fobj.close()
return df
@property
def event_info_df(self):
import pandas as pd
df = None
for fname in self.root_pert_files:
fobj = uproot.open(join(self._data_path, fname))
event_data = fobj["RootTuple"]
dct = {k: event_data[k].array() for k in EVENTINFO_COLUMNS}
if df is None:
df = pd.DataFrame(dct)
else:
df = df.append(pd.DataFrame(dct), ignore_index=True)
df["By"] = 1 - df.lepOut_E / df.lepIn_E
return df
def write_detector_file(gibuu_output,
ofile="gibuu.aanet.root",
can=(0, 476.5, 403.4),
livetime=3.156e7):
"""
Convert the GiBUU output to a KM3NeT MC (AANET) file
Parameters
----------
gibuu_output: GiBUUOutput
Output object which wraps the information from the GiBUU output files
ofile: str
Output filename
can: tuple
The can dimensions which are used to distribute the events
livetime: float
The data livetime
"""
import aa, ROOT
aafile = ROOT.EventFile()
aafile.set_output(ofile)
mc_event_id = 0
is_cc = False
if gibuu_output.jobcard is None:
raise EnvironmentError("No jobcard provided within the GiBUU output!")
nu_type = PDGID_LOOKUP[gibuu_output.jobcard["neutrino_induced"]
["flavor_id"]]
sec_lep_type = nu_type
ichan = abs(gibuu_output.jobcard["neutrino_induced"]["process_id"])
if ichan == 2:
is_cc = True
sec_lep_type -= 1
if gibuu_output.jobcard["neutrino_induced"]["process_id"] < 0:
nu_type *= -1
sec_lep_type *= -1
for ifile in gibuu_output.root_pert_files:
fobj = uproot.open(ifile)
event_data = fobj["RootTuple"]
for event in event_data.lazyarrays():
aafile.evt.clear()
aafile.evt.id = mc_event_id
aafile.evt.mc_run_id = mc_event_id
mc_event_id += 1
# Vertex Position
r = can[2] * np.sqrt(np.random.uniform(0, 1))
phi = np.random.uniform(0, 2 * np.pi)
pos_x = r * np.cos(phi)
pos_y = r * np.sin(phi)
pos_z = np.random.uniform(can[0], can[1])
vtx_pos = np.array([pos_x, pos_y, pos_z])
# Direction
phi = np.random.uniform(0, 2 * np.pi)
cos_theta = np.random.uniform(-1, 1)
sin_theta = np.sqrt(1 - cos_theta**2)
dir_x = np.cos(phi) * sin_theta
dir_y = np.sin(phi) * sin_theta
dir_z = cos_theta
direction = np.array([dir_x, dir_y, dir_z])
rotation = np.array([dir_y, -dir_x, 0])
sin_rot = np.linalg.norm(rotation)
R = Rotation.from_rotvec(rotation * np.arcsin(sin_rot) / sin_rot)
timestamp = np.random.uniform(0, livetime)
event_info = parse_gibuu_event_info(event.optional[0])
nu_in_trk = ROOT.Trk()
nu_in_trk.id = 0
nu_in_trk.mother_id = -1
nu_in_trk.type = nu_type
nu_in_trk.pos.set(*vtx_pos)
nu_in_trk.dir.set(*direction)
nu_in_trk.E = event.lepIn_E
nu_in_trk.t = timestamp
lep_out_trk = ROOT.Trk()
lep_out_trk.id = 1
lep_out_trk.mother_id = 0
lep_out_trk.type = sec_lep_type
lep_out_trk.pos.set(*vtx_pos)
mom = np.array([event.lepOut_Px, event.lepOut_Py, event.lepOut_Pz])
p_dir = R.apply(mom / np.linalg.norm(mom))
lep_out_trk.dir.set(*p_dir)
lep_out_trk.E = event.lepOut_E
lep_out_trk.t = timestamp
bjorken_y = 1.0 - float(event.lepOut_E / event.lepIn_E)
nu_in_trk.setusr('bx', -1)
nu_in_trk.setusr('by', bjorken_y)
nu_in_trk.setusr('ichan', ichan)
nu_in_trk.setusr("cc", is_cc)
aafile.evt.mc_trks.push_back(nu_in_trk)
aafile.evt.mc_trks.push_back(lep_out_trk)
for i in range(len(event.E)):
trk = ROOT.Trk()
trk.id = i + 2
mom = np.array([event.Px[i], event.Py[i], event.Pz[i]])
p_dir = R.apply(mom / np.linalg.norm(mom))
trk.pos.set(*vtx_pos)
trk.dir.set(*p_dir)
trk.mother_id = 0
trk.type = int(event.barcode[i])
trk.E = event.E[i]
trk.t = timestamp
aafile.evt.mc_trks.push_back(trk)
aafile.write()
if mc_event_id > 100:
break
del aafile