Belle II Software  release-08-01-10
b2hlt_combine_results.py
1 #!/usr/bin/env python3
2 
3 
10 
11 from ROOT import PyConfig
12 PyConfig.IgnoreCommandLineOptions = True # noqa
13 PyConfig.StartGuiThread = False # noqa
14 
15 from argparse import ArgumentParser
16 import basf2 as b2
17 import os
18 import uproot
19 import pandas as pd
20 import numpy as np
21 
22 PRESCALE_ROW = 4
23 
24 
25 def 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 
41 def 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 
54 if __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("Could not find input files: %s" % 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("{}: Different prescale values found for this trigger line! ".format(col) +
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("Created file %s" % args.output)