ATLAS Offline Software
Loading...
Searching...
No Matches
difftuple.py
Go to the documentation of this file.
1# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
3#
4# File: D3PDMakerTest/python/difftuple.py
5# Author: snyder@bnl.gov
6# Date: Feb, 2010
7# Purpose: Diff two root tuple files.
8#
9
10
11# Always run in batch mode.
12import os
13if 'DISPLAY' in os.environ:
14 del os.environ['DISPLAY']
15
16import ROOT
17import types
18import os
19from fnmatch import fnmatch
20
21# new : old
22renames = {
23# 'jet_antikt4h1topo_LArQuality' : 'jet_antikt4h1topo_quality',
24# 'jet_antikt4h1tower_LArQuality' : 'jet_antikt4h1tower_quality',
25# 'jet_antikt4truth_LArQuality' : 'jet_antikt4truth_quality',
26# 'jet_antikt6h1tower_LArQuality' : 'jet_antikt6h1tower_quality',
27
28# 'METJetsInfo_JetPtWeightedEventEMFraction' : 'MET_JetsInfo_JetPtWeightedEventEMFraction',
29# 'METJetsInfo_JetPtWeightedNumAssociatedTracks' : 'MET_JetsInfo_JetPtWeightedNumAssociatedTracks',
30# 'METJetsInfo_JetPtWeightedSize' : 'MET_JetsInfo_JetPtWeightedSize',
31# 'METJetsInfo_LeadingJetEt' : 'MET_JetsInfo_LeadingJetEt',
32# 'METJetsInfo_LeadingJetEta' : 'MET_JetsInfo_LeadingJetEta',
33
34# 'MET_DM_All_et' : 'MET_DMAll_et',
35# 'MET_DM_All_etx' : 'MET_DMAll_etx',
36# 'MET_DM_All_ety' : 'MET_DMAll_ety',
37# 'MET_DM_All_phi' : 'MET_DMAll_phi',
38# 'MET_DM_All_sumet' : 'MET_DMAll_sumet',
39
40# 'MET_DM_Crack1_et' : 'MET_DMCrack1_et',
41# 'MET_DM_Crack1_etx' : 'MET_DMCrack1_etx',
42# 'MET_DM_Crack1_ety' : 'MET_DMCrack1_ety',
43# 'MET_DM_Crack1_phi' : 'MET_DMCrack1_phi',
44# 'MET_DM_Crack1_sumet' : 'MET_DMCrack1_sumet',
45
46# 'MET_DM_Crack2_et' : 'MET_DMCrack2_et',
47# 'MET_DM_Crack2_etx' : 'MET_DMCrack2_etx',
48# 'MET_DM_Crack2_ety' : 'MET_DMCrack2_ety',
49# 'MET_DM_Crack2_phi' : 'MET_DMCrack2_phi',
50# 'MET_DM_Crack2_sumet' : 'MET_DMCrack2_sumet',
51
52# 'MET_DM_Cryo_et' : 'MET_DMCryo_et',
53# 'MET_DM_Cryo_etx' : 'MET_DMCryo_etx',
54# 'MET_DM_Cryo_ety' : 'MET_DMCryo_ety',
55# 'MET_DM_Cryo_phi' : 'MET_DMCryo_phi',
56# 'MET_DM_Cryo_sumet' : 'MET_DMCryo_sumet',
57
58# 'MET_Muid_Spectro_et' : 'MET_MuidSpectro_et',
59# 'MET_Muid_Spectro_etx' : 'MET_MuidSpectro_etx',
60# 'MET_Muid_Spectro_ety' : 'MET_MuidSpectro_ety',
61# 'MET_Muid_Spectro_phi' : 'MET_MuidSpectro_phi',
62# 'MET_Muid_Spectro_sumet' : 'MET_MuidSpectro_sumet',
63
64# 'MET_Muid_Track_et' : 'MET_MuidTrack_et',
65# 'MET_Muid_Track_etx' : 'MET_MuidTrack_etx',
66# 'MET_Muid_Track_ety' : 'MET_MuidTrack_ety',
67# 'MET_Muid_Track_phi' : 'MET_MuidTrack_phi',
68# 'MET_Muid_Track_sumet' : 'MET_MuidTrack_sumet',
69
70# 'MET_MuonBoy_Spectro_et' : 'MET_MuonBoySpectro_et',
71# 'MET_MuonBoy_Spectro_etx' : 'MET_MuonBoySpectro_etx',
72# 'MET_MuonBoy_Spectro_ety' : 'MET_MuonBoySpectro_ety',
73# 'MET_MuonBoy_Spectro_phi' : 'MET_MuonBoySpectro_phi',
74# 'MET_MuonBoy_Spectro_sumet' : 'MET_MuonBoySpectro_sumet',
75
76# 'MET_MuonBoy_Track_et' : 'MET_MuonBoyTrack_et',
77# 'MET_MuonBoy_Track_etx' : 'MET_MuonBoyTrack_etx',
78# 'MET_MuonBoy_Track_ety' : 'MET_MuonBoyTrack_ety',
79# 'MET_MuonBoy_Track_phi' : 'MET_MuonBoyTrack_phi',
80# 'MET_MuonBoy_Track_sumet' : 'MET_MuonBoyTrack_sumet',
81
82# 'MET_RefMuon_Track_et' : 'MET_RefMuonTrack_et',
83# 'MET_RefMuon_Track_etx' : 'MET_RefMuonTrack_etx',
84# 'MET_RefMuon_Track_ety' : 'MET_RefMuonTrack_ety',
85# 'MET_RefMuon_Track_phi' : 'MET_RefMuonTrack_phi',
86# 'MET_RefMuon_Track_sumet' : 'MET_RefMuonTrack_sumet',
87
88 #'vxp_trk_phi' : 'vxp_trk_phi0',
89 #'ef_el_trackcov_d0' : 'ef_el_trackcovd0',
90 #'ef_el_trackcov_d0_phi' : 'ef_el_trackcovd0phi',
91 #'ef_el_trackcov_d0_qoverp' : 'ef_el_trackcovd0qoverp',
92 #'ef_el_trackcov_d0_theta' : 'ef_el_trackcovd0theta',
93 #'ef_el_trackcov_d0_z0' : 'ef_el_trackcovd0z0',
94 #'ef_el_trackcov_phi' : 'ef_el_trackcovphi',
95 #'ef_el_trackcov_phi_qoverp' : 'ef_el_trackcovphiqoverp',
96 #'ef_el_trackcov_phi_theta' : 'ef_el_trackcovphitheta',
97 #'ef_el_trackcov_theta' : 'ef_el_trackcovtheta',
98 #'ef_el_trackcov_theta_qoverp' : 'ef_el_trackcovthetaqoverp',
99 #'ef_el_trackcov_qoverp' : 'ef_el_trackcovqoverp',
100 #'ef_el_trackcov_z0' : 'ef_el_trackcovz0',
101 #'ef_el_trackcov_z0_phi' : 'ef_el_trackcovz0phi',
102 #'ef_el_trackcov_z0_qoverp' : 'ef_el_trackcovz0qoverp',
103 #'ef_el_trackcov_z0_theta' : 'ef_el_trackcovz0theta',#
104
105 #'el_trackcov_d0' : 'el_trackcovd0',
106 #'el_trackcov_d0_phi' : 'el_trackcovd0phi',
107 #'el_trackcov_d0_qoverp' : 'el_trackcovd0qoverp',
108 #'el_trackcov_d0_theta' : 'el_trackcovd0theta',
109 #'el_trackcov_d0_z0' : 'el_trackcovd0z0',
110 #'el_trackcov_phi' : 'el_trackcovphi',
111 #'el_trackcov_phi_qoverp' : 'el_trackcovphiqoverp',
112 #'el_trackcov_phi_theta' : 'el_trackcovphitheta',
113 #'el_trackcov_theta' : 'el_trackcovtheta',
114 #'el_trackcov_theta_qoverp' : 'el_trackcovthetaqoverp',
115 #'el_trackcov_qoverp' : 'el_trackcovqoverp',
116 #'el_trackcov_z0' : 'el_trackcovz0',
117 #'el_trackcov_z0_phi' : 'el_trackcovz0phi',
118 #'el_trackcov_z0_qoverp' : 'el_trackcovz0qoverp',
119 #'el_trackcov_z0_theta' : 'el_trackcovz0theta',
120
121 #'elfilt_trackcov_d0' : 'elfilt_trackcovd0',
122 #'elfilt_trackcov_d0_phi' : 'elfilt_trackcovd0phi',
123 #'elfilt_trackcov_d0_qoverp' : 'elfilt_trackcovd0qoverp',
124 #'elfilt_trackcov_d0_theta' : 'elfilt_trackcovd0theta',
125 #'elfilt_trackcov_d0_z0' : 'elfilt_trackcovd0z0',
126 #'elfilt_trackcov_phi' : 'elfilt_trackcovphi',
127 #'elfilt_trackcov_phi_qoverp' : 'elfilt_trackcovphiqoverp',
128 #'elfilt_trackcov_phi_theta' : 'elfilt_trackcovphitheta',
129 #'elfilt_trackcov_theta' : 'elfilt_trackcovtheta',
130 #'elfilt_trackcov_theta_qoverp' : 'elfilt_trackcovthetaqoverp',
131 #'elfilt_trackcov_qoverp' : 'elfilt_trackcovqoverp',
132 #'elfilt_trackcov_z0' : 'elfilt_trackcovz0',
133 #'elfilt_trackcov_z0_phi' : 'elfilt_trackcovz0phi',
134 #'elfilt_trackcov_z0_qoverp' : 'elfilt_trackcovz0qoverp',
135 #'elfilt_trackcov_z0_theta' : 'elfilt_trackcovz0theta',
136
137 #'mu_trackcov_d0' : 'mu_trackcovd0',
138 #'mu_trackcov_d0_phi' : 'mu_trackcovd0phi',
139 #'mu_trackcov_d0_qoverp' : 'mu_trackcovd0qoverp',
140 #'mu_trackcov_d0_theta' : 'mu_trackcovd0theta',
141 #'mu_trackcov_d0_z0' : 'mu_trackcovd0z0',
142 #'mu_trackcov_phi' : 'mu_trackcovphi',
143 #'mu_trackcov_phi_qoverp' : 'mu_trackcovphiqoverp',
144 #'mu_trackcov_phi_theta' : 'mu_trackcovphitheta',
145 #'mu_trackcov_theta' : 'mu_trackcovtheta',
146 #'mu_trackcov_theta_qoverp' : 'mu_trackcovthetaqoverp',
147 #'mu_trackcov_qoverp' : 'mu_trackcovqoverp',
148 #'mu_trackcov_z0' : 'mu_trackcovz0',
149 #'mu_trackcov_z0_phi' : 'mu_trackcovz0phi',
150 #'mu_trackcov_z0_qoverp' : 'mu_trackcovz0qoverp',
151 #'mu_trackcov_z0_theta' : 'mu_trackcovz0theta',
152
153 #'trk_cov_d0' : 'trk_covd0',
154 #'trk_cov_phi' : 'trk_covphi',
155 #'trk_cov_qoverp' : 'trk_covqoverp',
156 #'trk_cov_theta' : 'trk_covtheta',
157 #'trk_cov_z0' : 'trk_covz0',
158 #'trk_cov_d0_phi' : 'trk_covd0phi',
159 #'trk_cov_d0_qoverp' : 'trk_covd0qoverp',
160 #'trk_cov_d0_theta' : 'trk_covd0theta',
161 #'trk_cov_d0_z0' : 'trk_covd0z0',
162 #'trk_cov_z0_phi' : 'trk_covz0phi',
163 #'trk_cov_z0_qoverp' : 'trk_covz0qoverp',
164 #'trk_cov_z0_theta' : 'trk_covz0theta',
165 #'trk_cov_phi_qoverp' : 'trk_covphiqoverp',
166 #'trk_cov_phi_theta' : 'trk_covphitheta',
167 #'trk_cov_theta_qoverp' : 'trk_covthetaqoverp',
168
169 #'vxp_x' : 'vxp_vertx',
170 #'vxp_y' : 'vxp_verty',
171 #'vxp_z' : 'vxp_vertz',
172 #'vxp_err_x' : 'vxp_errx',
173 #'vxp_err_y' : 'vxp_erry',
174 #'vxp_err_z' : 'vxp_errz',
175 #'vxp_E' : 'vxp_E',
176 #'vxp_m' : 'vxp_vertM',
177 #'vxp_px' : 'vxp_vertPx',
178 #'vxp_py' : 'vxp_vertPy',
179 #'vxp_pz' : 'vxp_vertPz',
180 #'vxp_sumPt' : 'vxp_vertSumPt',
181 #'vxp_nTracks' : 'vxp_vertNtrk',
182 }
183
184# List of branches to ignore for various versions.
185# Format is a list of tuples: (V, V, V, [BRANCHES...])
186# If any of the strings in V appear in CMTPATH, then
187# we do not ignore the branches. Otherwise, we ignore
188# branches matching any of the glob paths in BRANCHES.
189_ignore_branches = [
190 ('15.6',
191 ['mb_eta',
192 'mb_phi',
193 'tau_calcVars_*',
194 'el_rawcl_*',
195 'ph_rawcl_*',
196 'elfilt_rawcl_*',
197 'ph_mvaptcone*',
198 'MET_Goodness_N_Jets',
199 'MET_Goodness_DeltaTrackTopo',
200 'MET_Goodness_isBad_Jet',
201 'MET_Goodness_isGood_Jet',
202 'MET_Goodness_isUgly_Jet',
203 'MET_Goodness_tileGap3F_Jet',
204 'MET_Goodness_MET_CorrTopo_etx',
205 'MET_Goodness_MET_CorrTopo_ety',
206 'MET_Goodness_MET_CorrTopo_sumet',
207 'MET_Goodness_MET_Topo_etx',
208 'MET_Goodness_MET_Topo_ety',
209 'MET_Goodness_MET_Topo_sumet',
210 'jet_*',
211 '*_OriginIndex',
212 '*_Timing',
213 '*_timing',
214 '*ntrk',
215 '*sumPtTrk',
216 'el_medium',
217 'elfilt_medium',
218 'trk_blayerPrediction_locX',
219 ]
220 ),
221 ('15.6', '16.0',
222 [
223 ]
224 )
225 ]
226
227# Should a branch be ignored?
228def ignore_p (br):
229 ver = os.environ.get ('CMTPATH')
230
231 for vlist in _ignore_branches:
232 for v in vlist[:-1]:
233 found = False
234 if ver and ver.find (v) >= 0:
235 found = True
236 break
237 if found: continue
238 for b in vlist[-1]:
239 if fnmatch (br, b):
240 return True
241 return False
242
243
244
245def topy (o):
246 if type(o).__name__.startswith ('vector<'):
247 ll = list(o)
248 if ll and type(ll[0]).__name__.startswith ('vector<'):
249 ll = [list(l) for l in ll]
250 return ll
251 return o
252
253
254inttypes = [types.IntType, types.LongType]
255def compare (o1, o2, thresh = 1e-6, ithresh = None):
256 # Allow comparing int/long int.
257 if type(o1) in inttypes and type(o2) in inttypes:
258 if o1 < 0: o1 = o1 + (1<<32)
259 if o2 < 0: o2 = o2 + (1<<32)
260 return o1 == o2
261 if type(o1) is not type(o2):
262 return False
263 if isinstance(o1,list):
264 if len(o1) != len(o2):
265 return False
266 for i in range(len(o1)):
267 if not compare (o1[i], o2[i],
268 thresh=thresh, ithresh=ithresh): return False
269 return True
270 if type(o1).__name__ in ['map<string,int>',
271 'map<string,float>',
272 'map<string,string>']:
273 return ROOT.D3PDTest.MapDumper.equal (o1, o2)
274 if isinstance(o1, float):
275 if ithresh and abs(o1) < ithresh and abs(o2) < ithresh:
276 return True
277 num = o1-o2
278 den = abs(o1)+abs(o2)
279 if den == 0: return True
280 x = abs(num / den)
281 if callable(thresh): thresh = thresh(den)
282 if x > thresh:
283 print ('fmismatch', o1, o2, x)
284 return False
285 return True
286 return o1 == o2
287
288
290 if x > 36: return 1e-2
291 if x > 34: return 2e-3
292 if x > 32: return 2e-4
293 if x > 30: return 1e-4
294 if x > 28: return 1e-5
295 return 1e-6
296
297
298def diff_trees (t1, t2):
299 n1 = t1.GetEntries()
300 n2 = t2.GetEntries()
301 if n1 != n2:
302 print ('Different nentries for tree', t1.GetName(), ': ', n1, n2)
303 n1 = min(n1, n2)
304 b1 = [b.GetName() for b in t1.GetListOfBranches()]
305 b2 = [b.GetName() for b in t2.GetListOfBranches()]
306 b1.sort()
307 b2.sort()
308 branchmap = renames.copy()
309 for b in b1:
310 if b not in b2:
311 bb = branchmap.get (b)
312 if not bb or bb not in b2:
313 if not ignore_p(b):
314 print ('Branch', b, 'in first tree but not in second.')
315 if bb: del branchmap[b]
316 else:
317 b2.remove (bb)
318 else:
319 b2.remove (b)
320 branchmap[b] = b
321 for b in b2:
322 if not ignore_p(b):
323 print ('Branch', b, 'in second tree but not in first.')
324
325 for i in range (n1):
326 t1.GetEntry(i)
327 t2.GetEntry(i)
328 for b in b1:
329 if ignore_p(b): continue
330 bb = branchmap.get(b)
331 if not bb: continue
332 o1 = topy (getattr(t1, b))
333 o2 = topy (getattr(t2, bb))
334
335 ithresh = None
336 thresh = 1e-6
337 if b.find('jet_')>=0 and b.endswith ('_m'): ithresh = 0.1
338 if b == 'mc_m': ithresh = 0.1
339 if b.find ('_rawcl_etas') >= 0: thresh = 2e-4
340 if b.endswith ('_convIP'): thresh = 3e-5
341 if b.endswith ('_emscale_E'): thresh = 9e-5
342 if b.endswith ('_emscale_eta'): thresh = 9e-5
343 if b.endswith ('_emscale_m'): ithresh = 0.1
344 if b.endswith ('_constscale_E'): thresh = 9e-5
345 if b.endswith ('_constscale_eta'): thresh = 9e-5
346 if b == 'mc_eta': thresh = mc_eta_thresh
347 if b.endswith ('_seg_locX'): ithresh = 2e-12
348 if b.endswith ('_seg_locY'): ithresh = 2e-12
349 if b == 'MET_Goodness_DeltaEt_JetAlgs_Jet': ithresh = 2e-11
350 if b == 'MET_Goodness_EEM_Jet': thresh = 2e-5
351 if b == 'MET_Goodness_HECf_Jet': thresh = 3e-6
352 if b.find ('_blayerPrediction') >= 0: thresh = 4e-4
353 if not compare (o1, o2, thresh = thresh, ithresh = ithresh):
354 print ('Branch mismatch', b, 'entry', i, ':', ithresh)
355 print (o1)
356 print (o2)
357
358 return
359
360
361def diff_objstrings (k, s1, s2):
362 # nb. != not working correctly for TObjString in 5.26.00c_python2.6
363 if not (s1 == s2):
364 print ('Objstring', k, 'mismatch:')
365 print (repr(s1))
366 print (repr(s2))
367 return
368
369
370def diff_dirs (f1, f2):
371 k1 = [k.GetName() for k in f1.GetListOfKeys()]
372 k2 = [k.GetName() for k in f2.GetListOfKeys()]
373 k1.sort()
374 k2.sort()
375 if k1 != k2:
376 print ("Key list mismatch for", f1.GetName(), f2.GetName(), ":")
377 print (k1)
378 print (k2)
379 for k in k1:
380 if k not in k2: continue
381 o1 = f1.Get(k)
382 o2 = f2.Get(k)
383 if type(o1) is not type(o2):
384 print ('Type mismatch for ', k)
385 print (o1, o2)
386 if k == 'Schema':
387 pass
388 elif isinstance (o1, ROOT.TTree):
389 diff_trees (o1, o2)
390 elif isinstance (o1, ROOT.TDirectory):
391 diff_dirs (o1, o2)
392 elif isinstance (o1, ROOT.TObjString):
393 diff_objstrings (k, o1, o2)
394 else:
395 print (k, type(o1))
396 return
397
398
399if __name__ == '__main__':
400 import sys
401 f1 = ROOT.TFile (sys.argv[1])
402 f2 = ROOT.TFile (sys.argv[2])
403 diff_dirs (f1, f2)
#define min(a, b)
Definition cfImp.cxx:40
diff_trees(t1, t2)
Definition difftuple.py:298
diff_objstrings(k, s1, s2)
Definition difftuple.py:361
diff_dirs(f1, f2)
Definition difftuple.py:370
compare(o1, o2, thresh=1e-6, ithresh=None)
Definition difftuple.py:255