CMS 3D CMS Logo

runEdmFileComparison.py
Go to the documentation of this file.
1 #! /usr/bin/env python3
2 
3 from __future__ import print_function
4 import optparse
5 import subprocess
6 import pprint
7 import re
8 import os
9 import sys
10 
11 piecesRE = re.compile (r'(.+?)\s+"(\S+)"\s+"(\S*)"\s+"(\S+)"')
12 #colonRE = re.compile (r':+')
13 nonAlphaRE = re.compile (r'\W')
14 commaRE = re.compile (r',')
15 queueCommand = '/uscms/home/cplager/bin/clpQueue.pl addjob %s'
16 logDir = 'logfiles'
17 compRootDir = 'compRoot'
18 # Containers
19 #vectorRE = re.compile (r'^vector<(\S+)>')
20 doubleRE = re.compile (r'^(double|int)')
21 vectorRE = re.compile (r'^vector<([^<>]+)>')
22 detSetVecRE = re.compile (r'^edm::DetSetVector<([^<>]+)>')
23 edColRE = re.compile (r'^edm::EDCollection<([^<>]+)>')
24 sortedColRE = re.compile (r'^edm::SortedCollection<([^<>]+),\S+?> >')
25 singletonRE = re.compile (r'^([\w:]+)$')
26 containerList = [vectorRE, detSetVecRE, edColRE, sortedColRE, doubleRE]
27 
28 class EdmObject (object):
29 
30  def __init__ (self, tup):
31  self.container, self.one, self.two, self.three = tup
32  self.bool = False
33  for regex in containerList:
34  match = regex.search( self.container)
35  if match:
36  self.bool = True
37  self.name = match.group(1)
38  break
39 
40  def __str__ (self):
41  return pprint.pformat (self.__dict__)
42 
43  def __bool__ (self):
44  return self.bool
45 
46  def label (self):
47  return "%s,%s,%s" % (self.one, self.two, self.three)
48 
49 
50 if __name__ == "__main__":
51 
52 
55  parser = optparse.OptionParser ("%prog [options] file1.root [file2.root]"\
56  "\nFor more details, see\nhttps://twiki.cern.ch/twiki/bin/view/CMS/SWGuidePhysicsToolsEdmOneToOneComparison")
57  describeGroup = optparse.OptionGroup (parser, "Description Options")
58  precisionGroup = optparse.OptionGroup (parser, "Precision Options")
59  summaryGroup = optparse.OptionGroup (parser, "Summary Options")
60  queueGroup = optparse.OptionGroup (parser, "Queue Options")
61  verboseGroup = optparse.OptionGroup (parser, "Verbose Options")
62  # general optionos
63  parser.add_option ("--compRoot", dest="compRoot",
64  action="store_true", default=False,
65  help="Make compRoot files.")
66  parser.add_option ('--strictPairing', dest='strictPairing',
67  action='store_true',
68  help="Objects are paired uniquely by order in collection")
69  parser.add_option ("--prefix", dest="prefix", type="string",
70  help="Prefix to prepend to logfile name")
71  parser.add_option ("--regex", dest='regex', action="append",
72  type="string", default=[],
73  help="Only run on branches containing regex")
74  # describe options
75  describeGroup.add_option ("--describeOnly", dest="describeOnly",
76  action="store_true", default=False,
77  help="Run description step only and stop.")
78  describeGroup.add_option ("--forceDescribe", dest="forceDescribe",
79  action="store_true", default=False,
80  help="Run description step even if "\
81  "file already exists.")
82  describeGroup.add_option ("--singletons", dest="singletons",
83  action="store_true", default=False,
84  help="Describe singleton objects (" \
85  "used only with --describeOnly option).")
86  describeGroup.add_option ("--privateMemberData", dest="privateMemberData",
87  action="store_true", default=False,
88  help="include private member data "\
89  "(NOT for comparisons)")
90  # precision options
91  precisionGroup.add_option ("--precision", dest="precision", type="string",
92  help="Change precision use for floating "\
93  "point numbers")
94  precisionGroup.add_option ('--absolute', dest='absolute',
95  action='store_true', default=False,
96  help='Precision is checked against '\
97  'absolute difference')
98  precisionGroup.add_option ('--relative', dest='relative',
99  action='store_true', default=False,
100  help='Precision is checked against '\
101  'relative difference')
102  # summary options
103  summaryGroup.add_option ("--summary", dest="summary",
104  action="store_true", default=False,
105  help="Print out summary counts at end")
106  summaryGroup.add_option ("--summaryFull", dest="summaryFull",
107  action="store_true", default=False,
108  help="Print out full summary at end (VERY LONG)")
109  # queue options
110  queueGroup.add_option ("--noQueue", dest="noQueue",
111  action="store_true", default=True,
112  help="Do not use queue, but run "\
113  "jobs serially (default).")
114  queueGroup.add_option ("--queue", dest="noQueue",
115  action="store_false",
116  help="Use defaultqueueing system.")
117  queueGroup.add_option ("--queueCommand", dest="queueCommand", type="string",
118  help="Use QUEUECOMMAND TO queue jobs")
119  # verbose options
120  verboseGroup.add_option ("--verbose", dest="verbose",
121  action="store_true", default=False,
122  help="Verbose output")
123  verboseGroup.add_option ("--verboseDebug", dest="verboseDebug",
124  action="store_true", default=False,
125  help="Verbose output for debugging")
126  parser.add_option_group (describeGroup)
127  parser.add_option_group (precisionGroup)
128  parser.add_option_group (summaryGroup)
129  parser.add_option_group (queueGroup)
130  parser.add_option_group (verboseGroup)
131  options, args = parser.parse_args()
132  # Because Root and PyRoot are _really annoying_, you have wait to
133  # import this until command line options are parsed.
134  from Validation.Tools.GenObject import GenObject
135  if len (args) < 1 or len (args) > 2:
136  raise RuntimeError("You must provide 1 or 2 root files")
137 
138 
141  if options.queueCommand:
142  queueCommand = options.queueCommand
143  options.noQueue = False
144  if not re.match (r'%%s', queueCommand):
145  queueCommand += ' %s'
146  if options.noQueue:
147  command = 'src/Validation/Tools/scripts/runCommand.bash'
148  else:
149  command = 'src/Validation/Tools/scripts/runCMScommand.bash'
150  # make sure we aren't trying to use options that should not be
151  # used with the queueing system
152  if options.compRoot or options.summary or options.summaryFull:
153  raise RuntimeError("You can not use --compRoot or --summary "\
154  "in --queue mode")
155 
156 
159  base = os.environ.get ('CMSSW_BASE')
160  release_base = os.environ.get ('CMSSW_RELEASE_BASE')
161  if not base or not release_base:
162  raise RuntimeError("You must have already setup a CMSSW environment.")
163  # find command
164  found = False
165  for directory in [base, release_base]:
166  fullCommand = directory + '/' + command
167  if os.path.exists (fullCommand):
168  found = True
169  break
170  if not found:
171  raise RuntimeError("Can not find %s" % command)
172  if not options.noQueue:
173  fullCommand = queueCommand % fullCommand
174  if not os.path.isdir (logDir):
175  os.mkdir (logDir)
176  if not os.path.isdir (logDir):
177  raise RuntimeError("Can't create %s directory" % logDir)
178  if options.compRoot and not os.path.isdir (compRootDir):
179  os.mkdir (compRootDir)
180  if not os.path.isdir (compRootDir):
181  raise RuntimeError("Can't create %s directory" % compRootDir)
182  logPrefix = logDir + '/'
183  compRootPrefix = compRootDir + '/'
184  if options.prefix:
185  logPrefix += options.prefix + '_'
186  currentDir = os.getcwd()
187  filename1 = args[0]
188  if len (args) == 2:
189  filename2 = args[1]
190  else:
191  filename2 = filename1
192  if not os.path.exists (filename1) or not os.path.exists (filename2):
193  raise RuntimeError("Can not find '%s' or '%s'" % (filename1, filename2))
194  # if verboseDebug is set, set verbose as well
195  if options.verboseDebug:
196  options.verbose = True
197  if options.verbose:
198  print("files", filename1, filename2)
199  if options.singletons and not options.describeOnly:
200  raise RuntimeError("--singletons can only be used with "\
201  "--describeOnly option")
202  if options.privateMemberData and not options.describeOnly:
203  raise RuntimeError("--privateMemberData can only be used with "\
204  "--describeOnly option")
205  if options.singletons:
206  containerList.append (singletonRE)
207 
208 
211  print("Getting edmDumpEventContent output")
212  regexLine = ""
213  for regex in options.regex:
214  regexLine += ' "--regex=%s"' % regex
215  dumpCommand = 'edmDumpEventContent %s %s' % (regexLine, filename1)
216  if options.verboseDebug:
217  print(dumpCommand, '\n')
218  output = subprocess.getoutput (dumpCommand).split("\n")
219  if not len(output):
220  raise RuntimeError("No output from edmDumpEventContent.")
221  if options.verboseDebug:
222  print("output:\n", "\n".join(output))
223  collection = {}
224  total = failed = skipped = useless = 0
225  for line in output:
226  total += 1
227  match = piecesRE.search(line)
228  if match:
229  obj = EdmObject( match.group(1,2,3,4) )
230  if obj.bool:
231  collection.setdefault( obj.container, [] ).append(obj)
232  else:
233  skipped += 1
234  else:
235  skipped += 1
236 
237 
240  for key, value in sorted (collection.items()):
241  name = value[0].name
242  prettyName = nonAlphaRE.sub('', name)
243  descriptionName = prettyName + '.txt'
244  if os.path.exists (descriptionName) \
245  and os.path.getsize (descriptionName) \
246  and not options.forceDescribe:
247  if options.verbose:
248  print('%s exists. Skipping' % descriptionName)
249  continue
250  #print name, prettyName, key
251  describeCmd = "%s %s %s useReflexToDescribeForGenObject.py %s '--type=%s'" \
252  % (fullCommand, currentDir, logPrefix + prettyName,
253  GenObject.encodeNonAlphanumerics (name),
254  #name,
255  GenObject.encodeNonAlphanumerics (key))
256  if options.precision:
257  describeCmd += " --precision=" + options.precision
258  if options.verbose:
259  print("describing %s" % name)
260  if options.verboseDebug:
261  print(describeCmd, '\n')
262  returnCode = os.system (describeCmd)
263  if returnCode:
264  # return codes are shifted by 8 bits:
265  if returnCode == GenObject.uselessReturnCode << 8:
266  useless += 1
267  else:
268  print("Error trying to describe '%s'. Continuing.\n" % \
269  (name))
270  failed += 1
271  if options.describeOnly:
272  print("Total: %3d Skipped: %3d Failed: %3d Useless: %3d" % \
273  (total, skipped, failed, useless))
274  if not options.noQueue:
275  print("Note: Failed not recorded when using queuing system.")
276  sys.exit()
277 
278 
281  for key, value in sorted (collection.items()):
282  #print "%-40s" % key,
283  for obj in value:
284  # print " ", obj.label(),
285  name = obj.name
286  prettyName = nonAlphaRE.sub('', name)
287  scriptName = 'edmOneToOneComparison.py'
288  if prettyName in ['int', 'double']:
289  scriptName = 'simpleEdmComparison.py'
290  prettyLabel = commaRE.sub ('_', obj.label())
291  compareCmd = '%s %s %s %s --compare --label=reco^%s^%s' \
292  % (scriptName,
293  prettyName + '.txt',
294  filename1,
295  filename2,
296  prettyName,
297  obj.label())
298  fullCompareCmd = '%s %s %s %s' \
299  % (fullCommand, currentDir,
300  logPrefix + prettyName + '_' + prettyLabel,
301  compareCmd)
302  if options.relative:
303  fullCompareCmd += ' --relative'
304  elif options.absolute:
305  fullCompareCmd += ' --absolute'
306  if options.compRoot:
307  compRootName = compRootPrefix + prettyName \
308  + '_' + prettyLabel + '.root'
309  fullCompareCmd += ' --compRoot=%s' % compRootName
310  if options.strictPairing:
311  fullCompareCmd += ' --strictPairing'
312  if options.verbose:
313  print("comparing branch %s %s" % (name, obj.label()))
314  if options.verboseDebug:
315  print(fullCompareCmd,'\n')
316  os.system (fullCompareCmd)
317 
318 
321  if options.summary or options.summaryFull:
322  if options.prefix:
323  summaryMask = options.prefix + '_%_'
324  else:
325  summaryMask = '%_'
326  if options.summaryFull:
327  summaryOptions = '--diffTree'
328  else:
329  summaryOptions = '--counts'
330  summaryCmd = 'summarizeEdmComparisonLogfiles.py %s %s logfiles' \
331  % (summaryOptions, summaryMask)
332  print(summaryCmd)
333  print(subprocess.getoutput (summaryCmd))
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
static std::string join(char **cmd)
Definition: RemoteFile.cc:21