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