ATLAS Offline Software
physvalPostProcessing.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 # Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
4 
5 import ROOT
6 import os
7 import argparse
8 import logging
9 import PATJobTransforms.physvalPostProcessingTools as physvalPostProcessingTools
10 from PATJobTransforms.physvalPostProcessingTools import (
11  ConfigManager,
12  EfficiencyComputation,
13  ProjectionComputation,
14  RebinningOperation,
15  HistogramAdjustment,
16  HistogramAddition,
17  HistogramBlackList,
18  ResolutionComputation,
19  ROCCurveComputation
20 )
21 
22 logging.basicConfig(level=logging.INFO)
23 
24 from AthenaCommon.Utils.unixtools import find_datafile
25 
26 try:
27  electron_config = find_datafile("PATJobTransforms/physvalPostProcessingElectronConfig.yaml")
28  photon_config = find_datafile("PATJobTransforms/physvalPostProcessingPhotonConfig.yaml")
29  btag_config = find_datafile("PATJobTransforms/physvalPostProcessingBTagConfig.yaml")
30 except (ImportError, FileNotFoundError) as e:
31  print(f"Error: {e}. Attempting fallback paths.")
32  electron_config = find_datafile("../share/physvalPostProcessingElectronConfig.yaml")
33  photon_config = find_datafile("../share/physvalPostProcessingPhotonConfig.yaml")
34  btag_config = find_datafile("../share/physvalPostProcessingBTagConfig.yaml")
35 except Exception as e:
36  print(f"Unexpected error: {e}. Please ensure the configuration files are available at the correct paths.")
37  raise
38 
39 def create_output_file(input_file, output_file):
40  try:
41  ROOT.TFile.Cp(input_file, output_file)
42  output_root = ROOT.TFile.Open(output_file, "UPDATE")
43  if not output_root or output_root.IsZombie():
44  logging.error(f"Could not open {output_file}")
45  return None
46  # logging.info(f"Opened {output_file} file for updating.")
47  return output_root
48  except Exception as e:
49  logging.error(f"Failed to create output file: {e}")
50  return None
51 
52 def process_domain(root_file, domain_config, domain):
53  logging.info(f"For domain: '{domain}'")
54 
55  for rebin in domain_config.get("rebinning", []): RebinningOperation.from_yaml(rebin)(root_file)
56  actions = []
57  if "resolutions" in domain_config:
58  for res in domain_config["resolutions"]: actions.append(lambda res=res: ResolutionComputation.from_yaml(res)(root_file))
59  if "projections" in domain_config:
60  for proj in domain_config["projections"]: actions.append(lambda proj=proj: ProjectionComputation.from_yaml(proj)(root_file))
61  if "efficiencies" in domain_config:
62  for eff in domain_config["efficiencies"]: actions.append(lambda eff=eff: EfficiencyComputation.from_yaml(eff)(root_file))
63  if "adding_histograms" in domain_config:
64  for add in domain_config["adding_histograms"]: actions.append(lambda add=add: HistogramAddition.from_yaml(add)(root_file))
65  if "adjustments" in domain_config:
66  for adj in domain_config["adjustments"]: actions.append(lambda adj=adj: HistogramAdjustment.from_yaml(adj)(root_file))
67  if "roc_curves" in domain_config:
68  for roc in domain_config["roc_curves"]: actions.append(lambda roc=roc: ROCCurveComputation.from_yaml(roc)(root_file))
69  for action in actions: action()
70  for list in domain_config.get("blacklist", []): HistogramBlackList.from_yaml(list)(root_file)
71 
72  for rebin in domain_config.get("rebinning_later", []): RebinningOperation.from_yaml(rebin)(root_file)
73 
74  logging.info(f"Finished post processing for domain: '{domain}'")
75 
76 def main():
77  parser = argparse.ArgumentParser(description="NTUPLE PHYSVAL Histogram Post-processing Tool")
78  parser.add_argument("files", nargs="*", help="Input ROOT file and optionally output file name")
79  parser.add_argument("--domain", type=str, help="Specific domain to process (e.g., Electron, Photon, BTag)")
80  parser.add_argument("--crash_on_error", action="store_true", help="Crash on first error instead of just warning")
81  args = parser.parse_args()
82 
83  physvalPostProcessingTools.crash_on_error = args.crash_on_error
84 
85  if len(args.files) == 1:
86  input_file = args.files[0]
87  output_file = os.path.splitext(input_file)[0] + "_after_post_process.root"
88  elif len(args.files) == 2:
89  input_file, output_file = args.files
90  else:
91  logging.error("You must provide 1 or 2 positional arguments: <input_file> [output_file]")
92  exit(1)
93 
94  config_manager = ConfigManager()
95  config_manager.add_configuration("Electron", electron_config)
96  config_manager.add_configuration("Photon", photon_config)
97  config_manager.add_configuration("BTag", btag_config)
98 
99  logging.info(f"Input: {input_file}")
100  logging.info(f"Output: {output_file}")
101 
102  root_file = create_output_file(input_file, output_file)
103  if not root_file:
104  exit(1)
105 
106  logging.info("Post Processing started...")
107  if args.domain:
108  process_domain(root_file, config_manager.get_config(args.domain), args.domain) # Processing specified domain
109  else:
110  for domain, domain_config in config_manager.get_all_configs():
111  process_domain(root_file, domain_config, domain) # Processing all domains
112 
113  try:
114  root_file.Close()
115  logging.info(f"Postprocessing complete. Output saved to: {output_file}")
116  except Exception as e:
117  logging.error(f"Failed to close ROOT file: {e}")
118  exit(1)
119 
120 if __name__ == "__main__":
121  main()
physvalPostProcessing.process_domain
def process_domain(root_file, domain_config, domain)
Definition: physvalPostProcessing.py:52
physvalPostProcessing.main
def main()
Definition: physvalPostProcessing.py:76
calibdata.exit
exit
Definition: calibdata.py:235
print
void print(char *figname, TCanvas *c1)
Definition: TRTCalib_StrawStatusPlots.cxx:26
physvalPostProcessing.create_output_file
def create_output_file(input_file, output_file)
Definition: physvalPostProcessing.py:39
python.CaloScaleNoiseConfig.action
action
Definition: CaloScaleNoiseConfig.py:77
python.Utils.unixtools.find_datafile
def find_datafile(fname, pathlist=None, access=os.R_OK)
pathresolver-like helper function --------------------------------------—
Definition: unixtools.py:67