Belle II Software development
b2hlt_combine_results.py
1#!/usr/bin/env python3
2
3
10
11from ROOT import PyConfig
12PyConfig.IgnoreCommandLineOptions = True # noqa
13PyConfig.StartGuiThread = False # noqa
14
15from argparse import ArgumentParser
16import basf2 as b2
17import os
18import uproot
19import pandas as pd
20import numpy as np
21
22PRESCALE_ROW = 4
23
24
25def get_parser():
26 """Get the command line options
27
28 Returns:
29 argparse.ArgumentParser for this tool
30 """
31 parser = ArgumentParser(
32 description="Combines several ``software_trigger_result`` files.")
33 parser.add_argument("input", nargs='*',
34 help="Wildcard to select ``software_trigger_results`` files.")
35 parser.add_argument("--output",
36 help="The combined output ``software_trigger_result`` file name.",
37 default="software_trigger_results_combined.root")
38 return parser
39
40
41def get_prescales(df):
42 """Get prescale values from a data frame
43
44 Returns:
45 a list of the prescale values of each trigger line
46 """
47 prescales = []
48 for col in df.columns:
49 if col.find('software_trigger_cut_') >= 0 and df[col][PRESCALE_ROW] > 0:
50 prescales.append(df[col][PRESCALE_ROW])
51 return prescales
52
53
54if __name__ == "__main__":
55
56 args = get_parser().parse_args()
57
58 # get input file list
59 if not all([os.path.exists(f) for f in args.input]):
60 raise FileNotFoundError(f"Could not find input files: {args.input}")
61
62 # loop over SWTRs
63 sum_out = pd.DataFrame()
64 prescales = [] # prescale values of the trigger lines in each data frame
65 for fi in args.input:
66
67 # might have swtr files with no events selected: skip these
68 swtr = uproot.open(fi)["software_trigger_results"].arrays(library='pd')
69 if not swtr['total_events'][0].any():
70 continue
71
72 # add up all non-zero dataframes
73 if sum_out.empty:
74 sum_out = swtr
75 else:
76 sum_out = sum_out.add(swtr)
77 prescales.append(get_prescales(swtr))
78
79 prescales = np.array(prescales) # we want the prescales as numpy array for slicing
80 i = 0 # index the trigger lines
81
82 # the prescale values were also added up, to get the correct prescale values back,
83 # we take the first prescale value of each trigger line
84 # if the prescale value of a trigger line is changing in the files set the prescale value
85 # to nan for this trigger line and give a warning
86
87 for col in sum_out.columns:
88 # loop over all trigger lines
89 if col.find('software_trigger_cut_') >= 0 and sum_out[col][PRESCALE_ROW] > 0:
90 prescale_changed = False
91 for j in range(prescales[:, i].size - 1):
92 # check if prescales are changing in one of the files
93 if not prescales[j, i] == prescales[j+1, i]:
94 prescale_changed = True
95 break
96 if not prescale_changed:
97 # use prescale of first file
98 sum_out.at[PRESCALE_ROW, col] = prescales[0, i]
99 else:
100 b2.B2WARNING(f"{col}: Different prescale values found for this trigger line! " +
101 "Final prescale value is set to NaN.")
102 sum_out.at[PRESCALE_ROW, col] = np.nan
103 i += 1
104
105 with uproot.recreate(args.output) as outfile:
106 outfile['software_trigger_results'] = sum_out
107
108 print(f"Created file {args.output}")