ATLAS Offline Software
Loading...
Searching...
No Matches
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
5import ROOT
6import os
7import argparse
8import logging
9import PATJobTransforms.physvalPostProcessingTools as physvalPostProcessingTools
10from PATJobTransforms.physvalPostProcessingTools import (
11 ConfigManager,
12 EfficiencyComputation,
13 ProjectionComputation,
14 RebinningOperation,
15 HistogramAdjustment,
16 HistogramAddition,
17 HistogramBlackList,
18 ResolutionComputation,
19 ROCCurveComputation
20)
21
22logging.basicConfig(level=logging.INFO)
23
24from AthenaCommon.Utils.unixtools import find_datafile
25
26try:
27 electron_config = find_datafile("PATJobTransforms/physvalPostProcessingElectronConfig.yaml")
28 photon_config = find_datafile("PATJobTransforms/physvalPostProcessingPhotonConfig.yaml")
29 btag_config = find_datafile("PATJobTransforms/physvalPostProcessingBTagConfig.yaml")
30except (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")
35except Exception as e:
36 print(f"Unexpected error: {e}. Please ensure the configuration files are available at the correct paths.")
37 raise
38
39def 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
52def 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
76def 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
120if __name__ == "__main__":
121 main()
void print(char *figname, TCanvas *c1)
process_domain(root_file, domain_config, domain)
create_output_file(input_file, output_file)