The Gaudi Framework
master (bdbe7744)
Toggle main menu visibility
Loading...
Searching...
No Matches
compareRootHistos.py
Go to the documentation of this file.
1
#! /usr/bin/env python3
2
12
13
import
re
14
import
sys
15
from
optparse
import
OptionParser
16
17
# Well known trick
18
backupArgv = sys.argv[:]
19
sys.argv = []
20
from
ROOT
import
TFile
21
22
sys.argv = backupArgv
23
24
# The list of histograms not to check. Expressed as regexps
25
gRegexBlackList = []
26
histos = [
"TH1D"
,
"TH1F"
,
"TH2D"
,
"TH2F"
,
"TProfile"
]
27
ref =
"REFERENCE"
28
test =
"TEST"
29
30
# =============================================================================
31
# Method : rec( o, path=None, lst=None )
32
#
33
# @param o : a ROOT object
34
# @param path : a string like a transient store path; ie '/stat/CaloPIDs/ECALPIDE'
35
# @param lst : a list to hold (path, object) tuples
36
#
37
# function : recursively pull apart a ROOT file, making a list of (path, TObject) tuples
38
# This is done by GetListOfKeys method, which lets one work down through directories
39
# until you hit the Histo at the end of the path. The list of tuples is returned
40
#
41
42
43
def
rec
(o, path=None, lst=None):
44
if
not
path:
45
path =
"/stat"
46
lst = []
47
else
:
48
path = path +
"/"
+ o.GetName()
49
lst.append((path, o))
50
if
"GetListOfKeys"
in
dir(o):
51
keys = o.GetListOfKeys()
52
for
k
in
keys:
53
name = k.GetName()
54
rec
(o.Get(name), path, lst)
55
else
:
56
pass
57
return
lst
58
59
60
# =============================================================================
61
62
# =============================================================================
63
# Method : composition( t )
64
#
65
# @param t : a tuple of ( type, d ) where type is either 'REFERENCE' or 'TEST'
66
# and d is a dictionary of ROOT objects, with each key = ROOT path
67
#
68
# function : deduce the composition, (objects/histos) counts
69
#
70
71
72
def
composition
(t):
73
typ, d = t
74
hists = 0
75
objs = 0
76
for
k
in
d.keys():
77
if
d[k].__class__.__name__
in
histos:
78
hists += 1
79
else
:
80
objs += 1
81
return
objs, hists
82
83
84
# =============================================================================
85
86
# =============================================================================
87
# Method : comparePaths( t1, t2 )
88
#
89
# @param t1, t2 : a tuple of ( type, d ) where type is either 'REFERENCE' or 'TEST'
90
# and d is a dictionary of ROOT objects, with each key = ROOT path
91
#
92
# function : compare the paths between the two histo files. If the files are identical, they
93
# should have the same set of paths. The Test file should definitely have the
94
# same paths as the Reference. Perhaps the Reference file will have some more paths due
95
# to extra histos added as part of Application Sequencer finalisation
96
# Arguments t1 and t2 are checked and the test/reference auto-detected
97
#
98
99
100
def
comparePaths
(t1, t2):
101
if
t1[0] == ref:
102
ds = t1[1]
103
dp = t2[1]
104
elif
t2[0] == ref:
105
ds = t2[1]
106
dp = t1[1]
107
else
:
108
print(
"Neither tuple is Reference Root file reference?"
)
109
return
110
111
dsks = list(ds.keys())
112
dpks = list(dp.keys())
113
dsks.sort()
114
dpks.sort()
115
116
sset = set(dsks)
117
pset = set(dpks)
118
os, hs =
composition
((ref, ds))
119
op, hp =
composition
((test, dp))
120
print(
"\n"
+
"="
* 80)
121
print(
"Comparison of Paths : Reference vs Test ROOT files"
)
122
print(
"-"
* 80)
123
print(
124
f
"Number of paths in Reference file : {len(dsks)} (objects, histos) = ( {os}, {hs} )"
125
)
126
print(
127
f
"Number of paths in Test file : {len(dpks)} (objects, histos) = ( {op}, {hp} )"
128
)
129
matching = sset.intersection(pset)
130
matchingHistos = 0
131
for
n
in
matching:
132
if
ds[n].__class__.__name__
in
histos:
133
matchingHistos += 1
134
print(f
"\nMatching paths : {len(matching)}"
)
135
uSer = sset - pset
136
# work out histos unique to test file
137
uniqueReferenceHistos = 0
138
for
n
in
uSer:
139
if
ds[n].__class__.__name__
in
histos:
140
uniqueReferenceHistos += 1
141
print(
142
f
"Paths unique to Reference file : {len(uSer)} ( {uniqueReferenceHistos} Histos )"
143
)
144
if
uSer:
145
for
n
in
uSer:
146
print(f
"\t{ds[n]} : \t{n}"
)
147
uPar = pset - sset
148
uniqueTestHistos = 0
149
for
n
in
uPar:
150
if
dp[n].__class__.__name__
in
histos:
151
uniqueTestHistos += 1
152
print(f
"Paths unique to Test file : {len(uPar)} ( {uniqueTestHistos} Histos )"
)
153
if
uPar:
154
for
n
in
uPar:
155
print(f
"\t{dp[n]} : \t{n}"
)
156
print(f
"Matching Histos to test : {matchingHistos}"
)
157
print(
"="
* 80 +
"\n"
)
158
return
(
159
((os, hs), (op, hp)),
160
(uSer, uniqueReferenceHistos),
161
(uPar, uniqueTestHistos),
162
matchingHistos,
163
)
164
165
166
# =============================================================================
167
168
# =============================================================================
169
# Method : bin2binIdentity(h1,h2)
170
#
171
# @param h1, h2 : The two histogtams to compare
172
# function : Return the number of different bins
173
174
175
def
bin2binIdentity
(h1, h2):
176
def
getNbins(h):
177
biny = h.GetNbinsY()
178
if
biny > 1:
179
biny += 1
180
binz = h.GetNbinsZ()
181
if
binz > 1:
182
binz += 1
183
return
(h.GetNbinsX() + 1) * (biny) * (binz)
184
185
nbins = getNbins(h1)
186
diffbins = 0
187
for
ibin
in
range(0, nbins):
188
h1bin = h1.GetBinContent(ibin)
189
h2bin = h2.GetBinContent(ibin)
190
diffbins += h1bin != h2bin
191
return
diffbins
192
193
194
# =============================================================================
195
# Method : compareHistos( t1, t2 )
196
#
197
# @param t1, t2 : a tuple of ( type, d ) where type is either 'REFERENCE' or 'TEST'
198
# and d is a dictionary of ROOT objects, with each key = ROOT path
199
#
200
# function : compare the histograms in Reference/Test ROOT files. First, go through each
201
# dict to collect the histos (ignore TDirectory objects, etc). Then the histos
202
# in the test file (experimental) are compared to their equivalents in the
203
# reference file (definitely correct) using 3 methods.
204
# 1) The entries are checked, they should be equal
205
# 2) If entries are equal, check the Integral(); should be equal
206
# 3) If integrals are equal, check the KolmogorovTest() ; should be 1
207
# 4) If identity flag is there and KS test is performed, perform bin2bin identity test
208
# Arguments t1 and t2 are checked and the test/reference auto-detected
209
#
210
def
compareHistos
(t1, t2, state, checkBin2BinIdentity):
211
(
212
((referenceObjects, referenceHistos), (parallObjects, parallHistos)),
213
(uniqueSerPaths, uniqueSerHistos),
214
(uniqueParPaths, uniqueParHistos),
215
mh,
216
) = state
217
218
# deduce which one is test, which reference
219
if
t1[0] == ref:
220
ds = t1[1]
221
dp = t2[1]
222
elif
t2[0] == ref:
223
ds = t2[1]
224
dp = t1[1]
225
else
:
226
print(
"Neither tuple is Reference Root file reference?"
)
227
return
228
229
# histocount, objectcount for test/reference
230
hcp = 0
231
pHistos = []
232
hcs = 0
233
sHistos = []
234
235
omit = [re.compile(regex)
for
regex
in
gRegexBlackList]
236
237
# find the histos in the reference file
238
for
k
in
ds.keys():
239
if
not
any(regex.search(k)
is
not
None
for
regex
in
omit):
240
if
ds[k].__class__.__name__
in
histos:
241
hcs += 1
242
sHistos.append(k)
243
# same for test
244
for
k
in
dp.keys():
245
if
not
any(regex.search(k)
is
not
None
for
regex
in
omit):
246
if
dp[k].__class__.__name__
in
histos:
247
hcp += 1
248
pHistos.append(k)
249
250
cEntries = 0
251
xEntries = 0
252
diffEntries = []
253
xIntegrals = 0
254
diffIntegrals = []
255
passedKol = 0
256
failedKol = 0
257
diffKols = []
258
passedIdentity = 0
259
failedIdentity = 0
260
diffIdentity = []
261
identityDiffBins = {}
262
kTested = 0
263
kTestResults = {}
264
notfound = 0
265
integralMatch = 0
266
otherTest = 0
267
zeroIntegralMatch = 0
268
for
h
in
sHistos:
269
if
h
in
pHistos:
270
# matching histos to check
271
cEntries += 1
272
sh = ds[h]
273
ph = dp[h]
274
# first check entries
275
if
sh.GetEntries() != ph.GetEntries():
276
diffEntries.append(h)
277
xEntries += 1
278
continue
279
# check for (non-zero sum of bin error) && (non-zero integrals) for K-Test
280
sBinError = 0.0
281
pBinError = 0.0
282
for
i
in
range(sh.GetNbinsX()):
283
sBinError += sh.GetBinError(i)
284
for
i
in
range(ph.GetNbinsX()):
285
pBinError += ph.GetBinError(i)
286
sint = sh.Integral()
287
pint = ph.Integral()
288
doKS = (bool(sint)
and
bool(pint))
and
(sBinError > 0
and
pBinError > 0)
289
if
checkBin2BinIdentity
and
doKS:
290
diffBins =
bin2binIdentity
(sh, ph)
291
if
diffBins == 0:
292
passedIdentity += 1
293
else
:
294
failedIdentity += 1
295
diffIdentity.append(h)
296
identityDiffBins[h] = diffBins
297
if
(bool(sint)
and
bool(pint))
and
(sBinError > 0
and
pBinError > 0):
298
kTested += 1
299
kTest = sh.KolmogorovTest(ph)
300
kTestResults[h] = kTest
301
if
int(kTest):
302
passedKol += 1
303
else
:
304
# ; print 'KTest result : ', kTest
305
failedKol += 1
306
diffKols.append(h)
307
else
:
308
# try the integral test?
309
otherTest += 1
310
if
all((sint, pint))
and
(sint == pint):
311
integralMatch += 1
312
elif
sint == pint:
313
zeroIntegralMatch += 1
314
else
:
315
diffIntegrals.append(h)
316
xIntegrals += 1
317
else
:
318
notfound += 1
319
print(
"not found? "
, h)
320
321
# report on Failed Entry-Checks
322
print(
"\n\n"
+
"-"
* 80)
323
print(
"Summary of histos with different Entries"
)
324
print(
"-"
* 80)
325
if
diffEntries:
326
diffEntries.sort()
327
for
e
in
diffEntries:
328
print(f
"\t\t\t{e}:\t{int(ds[e].GetEntries())} != {int(dp[e].GetEntries())}"
)
329
print(
"-"
* 80)
330
331
# report on Failed Kolmogorov Tests
332
print(
"\n\n"
+
"-"
* 60)
333
print(
"Summary of histos which failed Kolmogorov Test"
)
334
print(
"-"
* 60)
335
if
diffKols:
336
diffKols.sort()
337
for
e
in
diffKols:
338
result = kTestResults[e]
# DP Calculated twice ARGH!!
339
print(f
"{ds[e].ClassName()}\t\t{e} :\tK-Test Result :\t {result:5.16f}"
)
340
print(
"-"
* 60)
341
342
# report on Failed Integral Checks
343
print(
"\n\n"
+
"-"
* 60)
344
print(
"Summary of histos which failed Integral Check"
)
345
print(
"-"
* 60)
346
if
diffIntegrals:
347
diffIntegrals.sort()
348
for
e
in
diffIntegrals:
349
diff = dp[e].Integral() - ds[e].Integral()
350
pc = (diff * 100) / ds[e].Integral()
351
print(
352
f
"{ds[e].ClassName()}\t\t{e}:\t Diff = {diff:5.6f}\tPercent Diff to Reference : {pc:5.6f} "
353
)
354
print(
"-"
* 60 +
"\n"
)
355
print(
"="
* 80 +
"\n"
)
356
357
# Report on failed bin2bin identity
358
if
checkBin2BinIdentity:
359
# report on b2b checks
360
print(
"\n\n"
+
"-"
* 80)
361
print(
"Summary of histos with at least one bin with different Entries"
)
362
print(
"-"
* 80)
363
if
diffIdentity:
364
diffIdentity.sort()
365
for
e
in
diffIdentity:
366
print(
367
f
"{ds[e].ClassName()}\t\t{e}: {identityDiffBins[e]} different bins"
368
)
369
print(
"-"
* 80)
370
371
print(
"\n"
+
"="
* 80)
372
print(
"Comparison : Reference/Test ROOT Histo files"
)
373
print(
"\n\t\tReference\tTest"
)
374
print(
375
f
"\tObjects : {referenceObjects}\t{parallObjects}\t\t( p-s = {parallObjects - referenceObjects} )"
376
)
377
print(
378
f
"\tHistos : {referenceHistos}\t{parallHistos}\t\t( p-s = {parallHistos - referenceHistos} )"
379
)
380
print(
"\t __________"
)
381
print(
382
f
"\tTotal : {referenceHistos + referenceObjects}\t{parallHistos + parallObjects}"
383
)
384
print(
385
f
"Objects/Histos unique to Reference File : {len(uniqueSerPaths) - uniqueSerHistos} / {uniqueSerHistos}"
386
)
387
print(
388
f
"Objects/Histos unique to Test File : {len(uniqueParPaths) - uniqueParHistos} / {uniqueParHistos}"
389
)
390
print(f
"\nMatching Histograms valid for Comparison : {mh}"
)
391
print(
"\nOmissions' patterns : "
)
392
for
entry
in
gRegexBlackList:
393
print(f
"\t{entry}"
)
394
print(
395
f
"\nHistograms for Comparison (after Omissions) : {mh - len(gRegexBlackList)}"
396
)
397
print(
"\n\tHISTOGRAM TESTS : "
)
398
print(f
"\t\tKOLMOGOROV TEST : {kTested}"
)
399
print(f
"\t\tINTEGRAL TEST : {otherTest}"
)
400
print(f
"\t\tENTRIES TEST : {xEntries}"
)
401
if
checkBin2BinIdentity:
402
print(f
"\t\tBIN2BIN TEST : {passedIdentity}"
)
403
print(
"\t\t ____"
)
404
print(f
"\t\tTested : {cEntries}"
)
405
406
print(
"\n\tDISCREPANCIES : "
)
407
print(f
"\t\tK-Test : {failedKol}"
)
408
print(f
"\t\tIntegrals : {xIntegrals}"
)
409
print(f
"\t\tEntries : {xEntries}"
)
410
retval = failedKol + xIntegrals + xEntries + failedIdentity
411
if
retval != 0:
412
print(
"\nThe two sets of histograms were not identical"
)
413
print(
"\n"
+
"="
* 80)
414
return
retval
415
416
417
# =============================================================================
418
419
420
def
extractBlacklist
(listString):
421
global
gRegexBlackList
422
if
listString:
423
for
blackRegexp
in
listString.split(
","
):
424
gRegexBlackList.append(blackRegexp)
425
else
:
426
gRegexBlackList = []
427
428
429
# =============================================================================
430
431
if
__name__ ==
"__main__"
:
432
usage =
"usage: %prog testFile.root referenceFile.root [options]"
433
parser = OptionParser()
434
parser.add_option(
435
"-b"
,
436
dest=
"blacklist"
,
437
help=
'Comma separated list of regexps matching histograms to skip (for example -b"MemoryTool,ProcTime")'
,
438
)
439
440
parser.add_option(
441
"-i"
,
442
action=
"store_true"
,
443
dest=
"bin2bin"
,
444
default=
False
,
445
help=
"Check for bin to bin identity"
,
446
)
447
(options, args) = parser.parse_args()
448
449
if
len(args) != 2:
450
print(
"Wrong number of rootfiles. Usage:"
)
451
print(usage)
452
sys.exit(1)
453
454
extractBlacklist
(options.blacklist)
455
456
testFile, referenceFile = args
457
458
tfs = TFile(testFile,
"REC"
)
459
print(f
"opening Test File : {testFile}"
)
460
tfp = TFile(referenceFile,
"REC"
)
461
print(f
"opening Reference File : {referenceFile}"
)
462
463
# get structure of TFiles in a list of (path, object) tuples
464
lref =
rec
(tfs)
465
ltest =
rec
(tfp)
466
# make a dictionary of lser and lpar. keys=paths
467
dref = dict([(n, o)
for
n, o
in
lref])
468
dtest = dict([(n, o)
for
n, o
in
ltest])
469
# make a tuple of (type, dict) where type is either 'reference' or 'test'
470
ts = (ref, dref)
471
tp = (test, dtest)
472
473
# check objs/histos in each file
474
composition
(ts)
475
composition
(tp)
476
477
# compare paths from each file
478
state =
comparePaths
(ts, tp)
479
480
# compare histos from each file
481
retval =
compareHistos
(ts, tp, state, checkBin2BinIdentity=options.bin2bin)
482
483
# finished with TFiles
484
tfs.Close()
485
tfp.Close()
486
487
sys.exit(retval)
compareRootHistos.rec
rec(o, path=None, lst=None)
Definition
compareRootHistos.py:43
compareRootHistos.bin2binIdentity
bin2binIdentity(h1, h2)
Definition
compareRootHistos.py:175
compareRootHistos.composition
composition(t)
Definition
compareRootHistos.py:72
compareRootHistos.compareHistos
compareHistos(t1, t2, state, checkBin2BinIdentity)
Definition
compareRootHistos.py:210
compareRootHistos.extractBlacklist
extractBlacklist(listString)
Definition
compareRootHistos.py:420
compareRootHistos.comparePaths
comparePaths(t1, t2)
Definition
compareRootHistos.py:100
GaudiMP
scripts
compareRootHistos.py
Generated on
for The Gaudi Framework by
1.17.0