ATLAS Offline Software
rootcomp.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
4 # @file: rootcomp.py
5 # @purpose: Script to compare the histograms in two root files
6 # @author: Frank Winklmeier, Will Buttinger
7 #
8 from __future__ import print_function
9 import sys
10 import os
11 import os.path
12 
13 def main():
14 
15  from optparse import OptionParser
16  parser = OptionParser(usage = "usage: %prog [options] reference file",
17  description = "Compare the histograms in two root files. See https://twiki.cern.ch/twiki/bin/view/Atlas/TrigValTools#rootcomp_py for more details and examples.")
18 
19  parser.add_option("-c", "--chi2",
20  action = "store_true",
21  help = "use chi2 comparison instead of bin-by-bin")
22 
23  parser.add_option("-a", "--axis",
24  action = "store_true",
25  help = "use axis comparison instead of bin-by-bin")
26 
27  parser.add_option("-l", "--sortLabels",
28  action = "store_true", default=False,
29  help = "sort/deflate alphanumeric axis before comparing")
30 
31  parser.add_option("-t", "--threshold",
32  action = "store", type="float",
33  help = "threshold for bin or chi2 comparison (default: 1e-6 or 0.95)")
34 
35  parser.add_option("-o", "--output",
36  action = "store", dest = "outFile",
37  metavar = "NAME", default = "rootcomp",
38  help = "write output to NAME.[ps,root] (default: rootcomp)")
39 
40  parser.add_option("-s", "--skip",
41  action = "append", default=[], type="string",
42  help = "add objects to skip (regular expression, can be used multiple times, mutual exclusive with -e)")
43 
44  parser.add_option("-e", "--select",
45  action = "append", default=[], type="string",
46  help = "select histograms to compare (regular expression, can be used multiple times, mutual exclusive with -s)")
47 
48  parser.add_option("--refBaseDir",
49  action = "store", default = "",
50  help = "base directory of reference file")
51 
52  parser.add_option("--fileBaseDir",
53  action = "store", default = "",
54  help = "base directory of file")
55 
56  parser.add_option("-S", "--noSkipList",
57  action = "store_true", default=False,
58  help = "do not use default list of histograms to skip")
59 
60  parser.add_option("-n", "--noRoot",
61  action = "store_true",
62  help = "do not write .root file")
63 
64  parser.add_option("--noPS",
65  action = "store_true",
66  help = "do not write ps/pdf file")
67 
68  parser.add_option("-N", "--norm",
69  action = "store_true", default = False,
70  help = "draw normalized")
71 
72  parser.add_option("--noDiff",
73  action = "store_true", default = False,
74  help = "do not plot difference histogram")
75 
76  parser.add_option("--ignoreMissingRef",
77  action = "store_true", default = False,
78  help = "ignore missing references for overall test result")
79 
80  parser.add_option("-z", "--zip",
81  action = "store_true",
82  help = "gzip postscript output file")
83 
84  parser.add_option("-p", "--pdf",
85  action = "store_true",
86  help = "create pdf instead of ps output file")
87 
88  parser.add_option("-v", "--verbose",
89  action = "store_true", default = False,
90  help = "be verbose")
91 
92  parser.add_option("--html",action="store_true",default=False,help="generate root html code to view results in web browser")
93 
94  (opts, args) = parser.parse_args()
95 
96  if len(args)!=2:
97  parser.print_help()
98  return 255
99 
100  if not opts.noSkipList:
101  opts.skip += ["Unpck$", "BufFreeCnt$", "CalEvtSize$"] # muon calibration buffer
102  opts.skip += ["/TIME_"] # timers from Monitored framework
103  opts.skip += ["/athenaHLT.*/.*Time$"] # HLTMPPU timing histograms
104  opts.skip += ["HltEventLoopMgr/.*Time.*"] # custom timers
105  opts.skip += ["HltEventLoopMgr/PopScheduler.*"] # scheduler monitoring
106  opts.skip += ["MessageSvc/MessageCount"] # MessageSvc
107  opts.skip += ["TrigSignatureMoni/.*Rate"] # Rate monitoring
108  opts.skip += ["TrigOpMonitor/GeneralOpInfo"] # release number, etc.
109  opts.skip += ["TrigOpMonitor/IOVDb.*"] # conditions data IOVs, size and time
110  opts.skip += ["TrigOpMonitor/.*ReadTime"] # conditions read time
111  opts.skip += ["TrigOpMonitor/.*BytesRead"] # conditions read size
112  opts.skip += ["HLTFramework/ROBDataProviderSvc"] # RDP histograms differ in MT due to caching
113  opts.skip += ["HLTFramework/SchedulerMonSvc"] # ATR-22345, not reproducible by definition
114  opts.skip += ["HLTSeeding/Random"] # Prescale validation (ATR-21935)
115 
116 
117  # Default thresholds
118  if not opts.threshold:
119  if opts.chi2: opts.threshold = 0.95
120  else: opts.threshold = 1e-6
121 
122  print("-"*70)
123  print("Command : rootcomp.py %s\n" % (" ".join(sys.argv[1:])))
124  print("Reference : %s" % args[0])
125  print("File : %s" % args[1])
126  print("Comparison : ", end="")
127  if opts.chi2: print("CHI2 (%.2f)" % opts.threshold)
128  elif opts.axis: print("AXIS")
129  else: print("BIN-BY-BIN (%.1e)" % opts.threshold)
130  if not opts.skip==[]: print("Ignored histograms: %s" % (", ".join(opts.skip)))
131  if not opts.select==[]: print("Selected histograms: %s" % (", ".join(opts.select)))
132 
133  print("-"*70)
134 
135 
136  # Now import ROOT
137  import cppyy # noqa: F401
138  try:
139  from PerfMonAna import PyRootLib
140  ROOT = PyRootLib.importRoot( batch=True )
141  except ImportError:
142  import ROOT # noqa: F401
143 
144  sys.stdout.flush()
145  sys.stderr.flush()
146 
147  from ROOT import TRootCompare
148  from ROOT import gROOT, gStyle
149  gROOT.SetStyle("Plain")
150  gStyle.SetOptStat(111111)
151 
152  valid = TRootCompare()
153  valid.setVerbose(opts.verbose)
154 
155  # Set algorithm
156  if opts.chi2:
157  valid.setAlg(TRootCompare.CHI2, opts.threshold)
158  elif opts.axis:
159  valid.setAlg(TRootCompare.AXIS, opts.threshold)
160  else:
161  valid.setAlg(TRootCompare.BIN, opts.threshold)
162 
163  # Select these histograms
164  for s in opts.select:
165  valid.passBeforeFailRegexp()
166  valid.addPassRegexp(s)
167 
168  # Skip these histograms
169  for s in opts.skip:
170  valid.addFailRegexp(s)
171 
172  # Select output (root, ps)
173  if not opts.noRoot: valid.setOutputFile(opts.outFile+".root")
174 
175  if opts.noPS:
176  valid.setPsFile("")
177  elif opts.pdf:
178  valid.setPsFile(opts.outFile+".pdf")
179  else:
180  valid.setPsFile(opts.outFile+".ps")
181 
182  valid.sortLabels(opts.sortLabels)
183  valid.drawNormalized(opts.norm)
184  valid.drawDiff(not opts.noDiff)
185 
186  # Run
187  rc = valid.setReferenceFile(args[0],opts.refBaseDir)
188  if rc is False:
189  return 255
190 
191  rc = valid.run(args[1],opts.fileBaseDir)
192 
193  sys.stdout.flush()
194  sys.stderr.flush()
195 
196  if opts.zip and not opts.pdf:
197  print("GZipping postscript file -> %s.ps.gz" % opts.outFile)
198  os.system("gzip -f %s.ps" % (opts.outFile))
199 
200  if rc != 0:
201  result = 255
202  elif valid.totalHist()>0:
203  # Return 0 if all histograms are matching
204  if opts.ignoreMissingRef: result = min(valid.totalHist()-valid.matchingHist(),255)
205  # Return 0 if all histograms are matching and none missing
206  else: result = min(valid.totalHist()-valid.matchingHist()-valid.missingHist(),255)
207  elif valid.totalHist()==0 and valid.missingHist()==0 and valid.matchingHist()==0:
208  # Return 0 if no histograms found
209  result = 0
210  else:
211  result = 255
212 
213  if opts.html:
214  os.system("root2html.py *.root")
215 
216  print("Overall test result: %i" % result)
217  return result
218 
219 if __name__ == "__main__":
220  sys.exit(main())
rootcomp.main
def main()
Definition: rootcomp.py:13
TRootCompare
Class to compare the histograms in two root files.
Definition: TRootCompare.h:28
min
#define min(a, b)
Definition: cfImp.cxx:40
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
Muon::print
std::string print(const MuPatSegment &)
Definition: MuonTrackSteering.cxx:28