FairShip
Loading...
Searching...
No Matches
convertEvtCalc.py
Go to the documentation of this file.
1# SPDX-License-Identifier: LGPL-3.0-or-later
2# SPDX-FileCopyrightText: Copyright CERN for the benefit of the SHiP Collaboration
3
4"""Convert files from EventCalc to ROOT format."""
5
6import os
7import sys
8from argparse import ArgumentParser
9
10import numpy as np
11import ROOT as r
12
13
14def parse_file(infile: str):
15 """
16 Parse the given text file and extracts process type, sample points, and variables.
17
18 Parameters:
19 infile (str): Path to the input text file.
20
21 Returns:
22 tuple: A tuple containing process type (str), sample points (int), and a list of numpy arrays for each variable.
23 """
24 try:
25 with open(infile) as file:
26 lines = file.readlines()
27 lines = lines[2:]
28
29 parsed_data = []
30 current_block = []
31 process_type = None
32 sampled_points = None
33
34 for line in lines:
35 if line.startswith("#<"):
36 if current_block:
37 data = np.loadtxt(current_block)
38 variables = [data[:, i] for i in range(data.shape[1])]
39 current_block = []
40 header = line.strip()
41 process_type = header.split(";")[0].split("=")[1]
42 # print(f'Process type: {process_type}')
43 sampled_points = int(header.split(";")[1].split("=")[1][:-1])
44 # print(f'Sampled points: {sampled_points}')
45 parsed_data.append((process_type, sampled_points, variables))
46 else:
47 current_block.append(line.strip())
48
49 if current_block:
50 data = np.loadtxt(current_block)
51 variables = [data[:, i] for i in range(data.shape[1])]
52 parsed_data.append((process_type, sampled_points, variables))
53
54 return parsed_data
55
56 except FileNotFoundError:
57 print(f"- convertEvtCalc - Error: The file {infile} was not found.")
58 return None, None, None
59 except Exception as e:
60 print(f"- convertEvtCalc - An error occurred: {e}")
61 return None, None, None
62
63
64def check_consistency_infile(nvars, ncols) -> None:
65 """
66 Check the consistency between number of columns in the input file (ncols) and variables (nvars).
67
68 Parameters:
69 nvars, ncols (int or float): The value to be checked. Must be equal.
70
71 """
72 if nvars != ncols:
73 raise ValueError("Nr of variables does not match input file.")
74 return
75
76
77def convert_file(infile, outdir) -> str | None:
78 """
79 Create a ROOT file with a TTree containing the variables from the parsed input text file.
80
81 Parameters:
82 infile (str): Path to the input text file.
83 outdir (str): Name of the output directory, the filename will be the same
84 as inputfile with the .dat replaced with .root.
85 """
86 vars_names = [
87 "px_llp",
88 "py_llp",
89 "pz_llp",
90 "e_llp",
91 "mass_llp",
92 "pdg_llp",
93 "decay_prob",
94 "vx",
95 "vy",
96 "vz",
97 ]
98 daughter_vars = [
99 "px_prod",
100 "py_prod",
101 "pz_prod",
102 "e_prod",
103 "mass_prod",
104 "pdg_prod",
105 ]
106 fname = infile.split("/")[-1]
107 command = f"cp {infile} {outdir}/{fname}"
108
109 if os.path.isfile(f"{outdir}/{fname}"):
110 print(f"Warning: The file {outdir}/{fname} already exists.")
111 else:
112 os.system(command)
113
114 infile = f"{outdir}/{fname}"
115 parsed_data = parse_file(infile)
116 outfile = infile.split(".dat")[0] + ".root"
117 ncols = len(parsed_data[0][2])
118 nvardau = 6 # qualifiers for each daughter
119 remaining_vars = ncols - len(vars_names)
120
121 if (remaining_vars % nvardau) != 0:
122 raise ValueError("- convertEvtCalc - Error: number of daughters is not exact.")
123
124 ndau = remaining_vars // nvardau
125 print(f"- convertEvtCalc - Max multiplicity of daughters: {ndau}")
126
127 vars_names.extend(f"{var}{i}" for i in range(1, ndau + 1) for var in daughter_vars)
128
129 try:
130 check_consistency_infile(nvars=len(vars_names), ncols=ncols)
131 except ValueError as e:
132 print(f"- convertEvtCalc - Error: {e}")
133 sys.exit(1)
134
135 vars_names += ["ndau"]
136
137 if parsed_data:
138 root_file = r.TFile.Open(outfile, "RECREATE")
139 tree = r.TTree("LLP_tree", "LLP_tree")
140
141 branch_f = {}
142 for var in vars_names:
143 branch_f[var] = np.zeros(1, dtype=float)
144 tree.Branch(var, branch_f[var], f"{var}/D")
145
146 for pt, sp, vars in parsed_data:
147 for row in zip(*vars):
148 for i, value in enumerate(row):
149 if i < len(vars_names) - 1:
150 branch_f[vars_names[i]][0] = value
151 branch_f["ndau"][0] = ndau / 1.0
152 tree.Fill()
153
154 tree.Write()
155 root_file.Close()
156 print(f"- convertEvtCalc - ROOT file '{outfile}' created successfully.")
157 return outfile
158
159
160def main() -> None:
161 """Convert files from EventCalc to ROOT format."""
162 parser = ArgumentParser(description=__doc__)
163 parser.add_argument(
164 "-f",
165 "--inputfile",
166 help="""Simulation results to use as input."""
167 """Supports retrieving file from EOS via the XRootD protocol.""",
168 required=True,
169 )
170 parser.add_argument("-o", "--outputdir", help="""Output directory, must exist.""", default=".")
171 args = parser.parse_args()
172 print(f"Opening input file for conversion: {args.inputfile}")
173 if not os.path.isfile(args.inputfile):
174 raise FileNotFoundError("EvtCalc: input .dat file does not exist")
175 if not os.path.isdir(args.outputdir):
176 print(f"Warning: The specified directory {args.outputdir} does not exist. Creating it now.")
177 command = f"mkdir {args.outputdir}"
178 os.system(command)
179 outputfile = convert_file(infile=args.inputfile, outdir=args.outputdir)
180 print(f"{args.inputfile} successfully converted to {outputfile}.")
181
182
183if __name__ == "__main__":
184 main()
str|None convert_file(infile, outdir)
None check_consistency_infile(nvars, ncols)
def parse_file(str infile)
int open(const char *, int)
Opens a file descriptor.