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