88"""
99
1010import argparse
11+ from sys import exit
1112
12- from ROOT import ( # pylint: disable=import-error
13+ from ROOT import ( # pylint: disable=import-error; RooUnfoldResponse,
1314 TH1 ,
15+ TH2 ,
16+ TH3 ,
17+ TAxis ,
1418 TCanvas ,
1519 TColor ,
1620 TFile ,
21+ THnSparse ,
1722 TLegend ,
18- gPad ,
1923 gROOT ,
2024)
2125
2226# import itertools
2327
2428
29+ def msg_err (message : str ):
30+ """Print error message"""
31+ print (f"Error: { message } " )
32+
33+
34+ def msg_fatal (message : str ):
35+ """Print error message and exit"""
36+ print (f"Fatal: { message } " )
37+ exit (1 )
38+
39+
40+ def are_valid (* objects ) -> bool :
41+ """Check whether objects exist"""
42+ result = True
43+ for i , o in enumerate (objects ):
44+ if not o :
45+ msg_err (f"Bad object { i } " )
46+ result = False
47+ return result
48+
49+
50+ def are_same_axes (axis1 , axis2 ) -> bool :
51+ """Tell whether two axes are same."""
52+ if not are_valid (axis1 , axis2 ):
53+ msg_fatal ("Bad input objects" )
54+ return False
55+ # Check classes
56+ for i , o in enumerate ((axis1 , axis2 )):
57+ if not isinstance (o , TAxis ):
58+ msg_fatal (f"Object { i } is not an axis" )
59+ return False
60+ # Check number of bins
61+ n_bins1 , n_bins2 = axis1 .GetNbins (), axis2 .GetNbins ()
62+ if n_bins1 != n_bins2 :
63+ return False
64+ # Check bin arrays
65+ array1 = [axis1 .GetBinLowEdge (i + 1 ) for i in range (n_bins1 + 1 )]
66+ array2 = [axis2 .GetBinLowEdge (i + 1 ) for i in range (n_bins2 + 1 )]
67+ if array1 != array2 :
68+ return False
69+ return True
70+
71+
72+ def get_object_type (obj ) -> int :
73+ """Return histogram degree"""
74+ # for num, tp in zip((5, 4, 3, 2, 1), (RooUnfoldResponse, THnSparse, TH3, TH2, TH1)):
75+ for num , tp in zip ((4 , 3 , 2 , 1 ), (THnSparse , TH3 , TH2 , TH1 )):
76+ if isinstance (obj , tp ):
77+ return num
78+ return 0
79+
80+
81+ def are_same_histograms (his1 : TH1 , his2 : TH1 ) -> bool :
82+ """Tell whether two histograms are same."""
83+ if not are_valid (his1 , his2 ):
84+ msg_fatal ("Bad input objects" )
85+ return False
86+ # Compare number of entries
87+ if his1 .GetEntries () != his2 .GetEntries ():
88+ print (f"Different number of entries { his1 .GetEntries ()} vs { his2 .GetEntries ()} " )
89+ return False
90+ # Compare axes
91+ for ax1 , ax2 in zip (
92+ (his1 .GetXaxis (), his1 .GetYaxis (), his1 .GetZaxis ()), (his2 .GetXaxis (), his2 .GetYaxis (), his2 .GetZaxis ())
93+ ):
94+ if not are_same_axes (ax1 , ax2 ):
95+ print ("Different axes" )
96+ return False
97+ # Compare bin counts and errors (include under/overflow bins)
98+ for bin_z in range (his1 .GetNbinsZ () + 2 ):
99+ for bin_y in range (his1 .GetNbinsY () + 2 ):
100+ for bin_x in range (his1 .GetNbinsX () + 2 ):
101+ bin = his1 .GetBin (bin_x , bin_y , bin_z )
102+ if his1 .GetBinContent (bin ) != his2 .GetBinContent (bin ) or his1 .GetBinError (bin ) != his2 .GetBinError (bin ):
103+ print (
104+ f"Different bin { bin } content: { his1 .GetBinContent (bin )} ± { his1 .GetBinError (bin )} vs "
105+ "{his2.GetBinContent(bin)} ± {his2.GetBinError(bin)}"
106+ )
107+ return False
108+ return True
109+
110+
111+ def are_same_thnspare (his1 : THnSparse , his2 : THnSparse ) -> bool :
112+ """Tell whether two THnSparse objects are same."""
113+ if not are_valid (his1 , his2 ):
114+ msg_fatal ("Bad input objects" )
115+ return False
116+ # Compare number of dimensions
117+ if his1 .GetNdimensions () != his2 .GetNdimensions ():
118+ return False
119+ # Compare number of entries
120+ if his1 .GetEntries () != his2 .GetEntries ():
121+ return False
122+ # Compare number of filled bins
123+ if his1 .GetNbins () != his2 .GetNbins ():
124+ return False
125+ # Compare axes
126+ for iAx in range (his1 .GetNdimensions ()):
127+ if not are_same_axes (his1 .GetAxis (iAx ), his2 .GetAxis (iAx )):
128+ return False
129+ # Compare bin content
130+ for iBin in range (his1 .GetNbins ()):
131+ if his1 .GetBinContent (iBin ) != his2 .GetBinContent (iBin ) or his1 .GetBinError (iBin ) != his2 .GetBinError (iBin ):
132+ return False
133+ return True
134+
135+
136+ # def are_same_response(his1 : RooUnfoldResponse, his2 : RooUnfoldResponse) -> bool:
137+ # """ Tell whether two RooUnfoldResponse objects are same. """
138+ # if not are_valid(his1, his2):
139+ # msg_fatal("Bad input objects")
140+ # return False
141+ # # Compare number of dimensions
142+ # if his1.GetDimensionMeasured() != his2.GetDimensionMeasured() or \
143+ # his1.GetDimensionTruth() != his2.GetDimensionTruth():
144+ # return False
145+ # # Compare number of bins
146+ # if his1.GetNbinsMeasured() != his2.GetNbinsMeasured() or his1.GetNbinsTruth() != his2.GetNbinsTruth():
147+ # return False
148+ # # Compare axes and bin content
149+ # if not are_same_histograms(his1.Hfakes(), his2.Hfakes()):
150+ # return False
151+ # if not are_same_histograms(his1.Hmeasured(), his2.Hmeasured()):
152+ # return False
153+ # if not are_same_histograms(his1.Htruth(), his2.Htruth()):
154+ # return False
155+ # if not are_same_histograms(his1.Hresponse(), his2.Hresponse()):
156+ # return False
157+ # return True
158+
159+
160+ def are_same_objects (obj1 , obj2 ) -> bool :
161+ """Tell whether two histogram-like objects are same."""
162+ if not are_valid (obj1 , obj2 ):
163+ msg_fatal ("Bad input objects" )
164+ return False
165+ # Compare types
166+ if type (obj1 ) is not type (obj2 ):
167+ print (f"Different types { type (obj1 )} { type (obj2 )} " )
168+ return False
169+ # Get ROOT types
170+ list_type = [- 1 , - 2 ]
171+ for i , o in enumerate ((obj1 , obj2 )):
172+ list_type [i ] = get_object_type (o )
173+ # Compare ROOT types (is it not covered by type(obj)?)
174+ if list_type [0 ] != list_type [1 ]:
175+ print (f"Different types { list_type [0 ]} { list_type [1 ]} " )
176+ return False
177+ type_obj = list_type [0 ]
178+ # Compare supported ROOT objects
179+ if type_obj == 0 :
180+ msg_fatal (f"Objects have an unsupported type { type (obj1 )} ." )
181+ return False
182+ # elif type_obj == 5:
183+ # return are_same_response(obj1, obj2)
184+ elif type_obj == 4 :
185+ return are_same_thnspare (obj1 , obj2 )
186+ return are_same_histograms (obj1 , obj2 )
187+
188+
25189def compare (dict_obj , add_leg_title = True , normalize = True ):
26190 print ("Comparing" )
27191 list_colors = ["#e41a1c" , "#377eb8" , "#4daf4a" ]
28192 list_markers = [21 , 20 , 34 ]
29193 dict_colors = {}
30194 dict_markers = {}
31195 dict_list_canvas = {}
196+
197+ # Explicit comparison
198+ list_files = list (dict_obj .keys ())
199+ name_file_0 = list_files [0 ]
200+ name_file_1 = list_files [1 ]
201+ for key_obj in dict_obj [name_file_0 ]:
202+ obj_0 = dict_obj [name_file_0 ][key_obj ]
203+ obj_1 = dict_obj [name_file_1 ][key_obj ]
204+ name_his = obj_0 .GetName ()
205+ if are_same_objects (obj_0 , obj_1 ):
206+ print (f"Objects { name_his } are same { obj_0 .GetEntries ()} " )
207+ else :
208+ print (f"Objects { name_his } are different" )
209+
32210 for key_file in dict_obj :
33211 print ("Entry" , len (dict_colors ), key_file )
34212 dict_colors [key_file ] = TColor .GetColor (list_colors [len (dict_colors )])
@@ -50,7 +228,7 @@ def compare(dict_obj, add_leg_title=True, normalize=True):
50228 else :
51229 opt += "same"
52230 dict_list_canvas [key_obj ][0 ].cd ()
53- print (f'Drawing { obj .GetName ()} with opt "{ opt } " on canvas { gPad .GetName ()} ' )
231+ # print(f'Drawing {obj.GetName()} with opt "{opt}" on canvas {gPad.GetName()}')
54232 obj .SetLineColor (dict_colors [key_file ])
55233 obj .SetMarkerStyle (dict_markers [key_file ])
56234 obj .SetMarkerColor (dict_colors [key_file ])
@@ -64,7 +242,7 @@ def compare(dict_obj, add_leg_title=True, normalize=True):
64242 # Ratio
65243 if not is_first_file :
66244 dict_list_canvas [key_obj ][1 ].cd ()
67- print (f'Drawing { obj .GetName ()} with opt "{ opt } " on canvas { gPad .GetName ()} ' )
245+ # print(f'Drawing {obj.GetName()} with opt "{opt}" on canvas {gPad.GetName()}')
68246 # line_1 = TLine(obj.GetXaxis().GetXmin(), 1, obj.GetXaxis().GetXmax(), 1)
69247 obj_ratio = obj .Clone (f"{ obj .GetName ()} _ratio" )
70248 obj_ratio .Divide (dict_obj [key_file_first ][key_obj ])
0 commit comments