Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-08-10 22:50:06

0001 from __future__ import print_function
0002 ##  Note: Please do not use or modify any data or functions with a
0003 ##  leading underscore.  If you "mess" with the internal structure,
0004 ##  the classes may not function as intended.
0005 
0006 
0007 from builtins import range
0008 from FWCore.Utilities.Enumerate import Enumerate
0009 from DataFormats.FWLite import Events, Handle
0010 import re
0011 import os
0012 import copy
0013 import pprint
0014 import random
0015 import sys
0016 import inspect
0017 import ROOT
0018 from functools import reduce
0019 ROOT.gROOT.SetBatch()
0020 
0021 # regex for reducing 'warn()' filenames
0022 filenameRE = re.compile (r'.+/Validation/Tools/')
0023 # Whether warn() should print anythingg
0024 quietWarn = False
0025 
0026 def setQuietWarn (quiet = True):
0027     global quietWarn
0028     quietWarn = quiet
0029 
0030 
0031 def warn (*args, **kwargs):
0032     """print out warning with line number and rest of arguments"""
0033     if quietWarn: return
0034     frame = inspect.stack()[1]
0035     filename = frame[1]
0036     lineNum  = frame[2]
0037     #print filename, filenameRE
0038     filename = filenameRE.sub ('', filename)
0039     #print "after '%s'" % filename
0040     blankLines = kwargs.get('blankLines', 0)
0041     if blankLines:
0042         print('\n' * blankLines)
0043     spaces = kwargs.get('spaces', 0)
0044     if spaces:
0045         print(' ' * spaces, end=' ')
0046     if len (args):
0047         print("%s (%s): " % (filename, lineNum), end=' ')
0048         for arg in args:
0049             print(arg, end=' ')
0050         print()
0051     else:
0052         print("%s (%s):" % (filename, lineNum))
0053 
0054 
0055 class GenObject (object):
0056     """Infrastruture to define general objects and their attributes."""
0057 
0058     ########################
0059     ## Static Member Data ##
0060     ########################
0061 
0062     types              = Enumerate ("float int long string", "type")
0063     _objFunc           = Enumerate ("obj func", "of")
0064     _cppType           = dict ( {types.float  : 'double',
0065                                  types.int    : 'int',
0066                                  types.long   : 'long',
0067                                  types.string : 'std::string' } )
0068     _basicSet          = set( [types.float, types.int, types.float,
0069                                types.string] )
0070     _defaultValue      = dict ( {types.float  : 0.,
0071                                  types.int    : 0,
0072                                  types.long   : 0,
0073                                  types.string : '""' } )
0074     _objsDict          = {} # info about GenObjects
0075     _equivDict         = {} # hold info about 'equivalent' muons
0076     _ntupleDict        = {} # information about different ntuples
0077     _tofillDict        = {} # information on how to fill from different ntuples
0078     _rootObjectDict    = {} # hold objects and stl::vectors to objects
0079                             # hooked up to a root tree
0080     _rootClassDict     = {} # holds classes (not instances) associated with
0081                             # a given GenObject
0082     _kitchenSinkDict   = {} # dictionary that holds everything else...
0083     _runEventList      = []
0084     _runEventListDone  = False
0085     uselessReturnCode  = 1 << 7 # pick a unique return code
0086     
0087     ####################
0088     ## Compile Regexs ##
0089     ####################
0090     _nonSpacesRE      = re.compile (r'\S')
0091     _colonRE          = re.compile (r'\s*:\s*')
0092     _singleColonRE    = re.compile (r'(.+?):(.+)')
0093     _doubleColonRE    = re.compile (r'(.+?):(.+?):(.+)')
0094     _doublePercentRE  = re.compile (r'%%')    
0095     _parenRE          = re.compile (r'(.+)\((.*)\)')
0096     _spacesRE         = re.compile (r'\s+')
0097     _dotRE            = re.compile (r'\s*\.\s*')
0098     _commaRE          = re.compile (r'\s*,\s*')
0099     _singleQuoteRE    = re.compile (r'^\'(.+)\'$')
0100     _doubleQuoteRE    = re.compile (r'^\"(.+)\"$')
0101     _bracketRE        = re.compile (r'\[\s*(.+?)\s*\]')
0102     _commentRE        = re.compile (r'#.+$')
0103     _aliasRE          = re.compile (r'alias=(\S+)',    re.IGNORECASE)
0104     _labelRE          = re.compile (r'label=(\S+)',    re.IGNORECASE)
0105     _typeRE           = re.compile (r'type=(\S+)',     re.IGNORECASE)
0106     _singletonRE      = re.compile (r'singleton',      re.IGNORECASE)
0107     _typeRE           = re.compile (r'type=(\S+)',     re.IGNORECASE)
0108     _defaultRE        = re.compile (r'default=(\S+)',  re.IGNORECASE)
0109     _shortcutRE       = re.compile (r'shortcut=(\S+)', re.IGNORECASE)
0110     _precRE           = re.compile (r'prec=(\S+)',     re.IGNORECASE)
0111     _formRE           = re.compile (r'form=(\S+)',     re.IGNORECASE)
0112     _nonAlphaRE       = re.compile (r'\W')
0113     _percentAsciiRE   = re.compile (r'%([0-9a-fA-F]{2})')
0114     
0115     #############################
0116     ## Static Member Functions ##
0117     #############################
0118 
0119     @staticmethod
0120     def char2ascii (match):
0121         return "%%%02x" % ord (match.group(0))
0122 
0123     @staticmethod
0124     def ascii2char (match):
0125         return chr( int( match.group(1), 16 ) )
0126 
0127     @staticmethod
0128     def encodeNonAlphanumerics (line):
0129         """Use a web like encoding of characters that are non-alphanumeric"""
0130         return GenObject._nonAlphaRE.sub( GenObject.char2ascii, line )
0131 
0132     @staticmethod
0133     def decodeNonAlphanumerics (line):
0134         """Decode lines encoded with encodeNonAlphanumerics()"""
0135         return GenObject._percentAsciiRE.sub( GenObject.ascii2char, line )
0136         
0137 
0138     @staticmethod
0139     def addObjectVariable (obj, var, **optionsDict):
0140         """ User passes in in object and variable names."""
0141         if 'varType' not in optionsDict:
0142             optionsDict['varType'] = GenObject.types.float
0143         varType = optionsDict['varType']
0144         if not GenObject.types.isValidValue (varType):
0145             print("Type '%s' not valid.  Skipping (%s, %s, %s)." % \
0146                   (varType, obj, var, varType))
0147             return
0148         if 'default' not in optionsDict:
0149             optionsDict['default'] = GenObject._defaultValue[varType]
0150         if obj.startswith ("_") or var.startswith ("_"):
0151             print("Skipping (%s, %s, %s) because of leading underscore." % \
0152                   (obj, var, varType))
0153             return
0154         GenObject._objsDict.setdefault (obj, {}).setdefault (var, optionsDict)
0155 
0156 
0157     @staticmethod
0158     def getVariableProperty (obj, var, key):
0159         """Returns property assoicated with 'key' for variable 'var'
0160         of object 'obj'.  Returns 'None' if any of the above are not
0161         defined."""
0162         return GenObject._objsDict.get (obj, {}).get (var, {}). get (key, None)
0163 
0164 
0165     @staticmethod
0166     def setEquivExpression (obj, variable, precision):
0167         """Adds an equivalence constraint.  Must have at least one to
0168         compare GO objects."""
0169         if obj.startswith ("_"):
0170             print("Skipping (%s, %s) because of leading underscore." % \
0171                   (obj, expression))
0172             return
0173         GenObject._equivDict.setdefault (obj,[]).append ( (variable,
0174                                                            precision) )
0175         
0176     
0177     @staticmethod
0178     def printGlobal():
0179         """Meant for debugging, but ok if called by user"""
0180         print("objs: ")
0181         pprint.pprint (GenObject._objsDict,        indent=4)
0182         print("equiv: ")                            
0183         pprint.pprint (GenObject._equivDict,       indent=4)
0184         print("ntuple: ")                           
0185         pprint.pprint (GenObject._ntupleDict,      indent=4)
0186         print("tofill: ")                           
0187         pprint.pprint (GenObject._tofillDict,      indent=4)
0188         print("kitchenSink: ")
0189         pprint.pprint (GenObject._kitchenSinkDict, indent=4)
0190         print("rootClassDict")
0191         pprint.pprint (GenObject._rootClassDict,   indent=4)
0192 
0193 
0194     @staticmethod
0195     def checksum (str):
0196         """Returns a string of hex value of a checksum of input
0197         string."""
0198         return hex( reduce( lambda x, y : x + y, map(ord, str) ) )[2:]
0199 
0200 
0201     @staticmethod
0202     def rootClassName (objName):
0203         """Returns the name of the equivalent Root object"""
0204         return "go_" + objName
0205 
0206 
0207     @staticmethod
0208     def rootDiffClassName (objName):
0209         """Returns the name of the equivalent Root diff object"""
0210         return "goDiff_" + objName
0211 
0212 
0213     @staticmethod
0214     def rootDiffContClassName (objName):
0215         """Returns the name of the equivalent Root diff container
0216         object"""
0217         return "goDiffCont_" + objName
0218 
0219 
0220     @staticmethod
0221     def _setupClassHeader (className, noColon = False):
0222         """Returns a string with the class header for a class
0223         'classname'"""
0224         retval  = "\nclass %s\n{\n  public:\n" % className
0225         retval += "      typedef std::vector< %s > Collection;\n\n" % className
0226         # constructor
0227         if noColon:
0228             retval += "      %s()" % className
0229         else:
0230             retval += "      %s() :\n" % className
0231         return retval
0232 
0233 
0234     @staticmethod
0235     def _finishClassHeader (className, datadec):
0236         """Returns a stringg with the end of a class definition"""
0237         retval  = "\n      {}\n" + datadec + "};\n"
0238         retval += "#ifdef __MAKECINT__\n#pragma link C++ class " + \
0239                  "vector< %s >+;\n#endif\n\n" % className
0240         return retval
0241 
0242 
0243     @staticmethod
0244     def _createCppClass (objName):
0245         """Returns a string containing the '.C' file necessary to
0246         generate a shared object library with dictionary."""
0247         if objName not in GenObject._objsDict:
0248             # not good
0249             print("Error: GenObject does not know about object '%s'." % objName)
0250             raise RuntimeError("Failed to create C++ class.")
0251         className   = GenObject.rootClassName (objName)
0252         diffName    = GenObject.rootDiffClassName (objName)
0253         contName    = GenObject.rootDiffContClassName (objName)
0254         goClass     = GenObject._setupClassHeader (className)
0255         diffClass   = GenObject._setupClassHeader (diffName)
0256         contClass   = GenObject._setupClassHeader (contName, noColon = True)
0257         goDataDec   = diffDataDec = contDataDec = "\n      // data members\n"
0258         first = True
0259         for key in sorted( GenObject._objsDict[objName].keys() ):
0260             if key.startswith ("_"): continue
0261             varTypeList = GenObject._objsDict[objName][key]
0262             cppType = GenObject._cppType[ varTypeList['varType'] ]
0263             default = varTypeList['default']
0264             if first:
0265                 first = False
0266             else:
0267                 goClass   += ",\n"
0268                 diffClass += ',\n'
0269             goClass   += "         %s (%s)" % (key, default)
0270             goDataDec += "      %s %s;\n" % (cppType, key)
0271             # is this a basic class?
0272             goType = varTypeList['varType']
0273             if goType in GenObject._basicSet:
0274                 # basic type
0275                 diffClass   += "         %s (%s),\n" % (key, default)
0276                 diffDataDec += "      %s %s;\n" % (cppType, key)
0277                 if goType == GenObject.types.string:
0278                     # string
0279                     otherKey = 'other_' + key
0280                     diffClass   += "         %s (%s)" % (otherKey, default)
0281                     diffDataDec += "      %s %s;\n" % (cppType, otherKey)
0282                 else:
0283                     # float, long, or int
0284                     deltaKey = 'delta_' + key
0285                     diffClass   += "         %s (%s)" % (deltaKey, default)
0286                     diffDataDec += "      %s %s;\n" % (cppType, deltaKey)
0287             else:
0288                 raise RuntimeError("Shouldn't be here yet.")
0289             # definition
0290         # do contClass
0291         if GenObject.isSingleton (objName):
0292             # singleton
0293             contDataDec += "      %s diff;\n" % diffName
0294             contDataDec += "      void setDiff (const %s &rhs)" % diffName + \
0295                            " { diff = rhs; }\n"
0296         else:
0297             # vector of objects
0298             contDataDec += "      void clear() {firstOnly.clear(); secondOnly.clear(); diff.clear(); }\n"
0299             contDataDec += "      %s::Collection firstOnly;\n"  % className
0300             contDataDec += "      %s::Collection secondOnly;\n" % className
0301             contDataDec += "      %s::Collection diff;\n"       % diffName
0302             # give me a way to clear them all at once
0303         # Finish off the classes
0304         goClass   += GenObject._finishClassHeader (className, goDataDec)
0305         diffClass += GenObject._finishClassHeader (diffName,  diffDataDec)
0306         contClass += GenObject._finishClassHeader (contName,  contDataDec)
0307         if objName == 'runevent':
0308             # we don't want a diff class for this
0309             diffClass = ''
0310             contClass = ''
0311         return goClass + diffClass + contClass
0312 
0313 
0314     @staticmethod
0315     def _loadGoRootLibrary ():
0316         """Loads Root shared object library associated with all
0317         defined GenObjects. Will create library if necessary."""
0318         print("Loading GO Root Library")
0319         key = "_loadedLibrary"        
0320         if GenObject._kitchenSinkDict.get (key):
0321             # Already done, don't do it again:
0322             return
0323         # Mark it as done
0324         GenObject._kitchenSinkDict[key] = True
0325         # Generate source code
0326         sourceCode = "#include <string>\n#include <vector>\n" \
0327                      + "using namespace std;\n"
0328         for objClassName in sorted( GenObject._objsDict.keys() ):
0329             sourceCode += GenObject._createCppClass (objClassName)
0330         GenObjectRootLibDir = "genobjectrootlibs"
0331         if not os.path.exists (GenObjectRootLibDir):
0332             os.mkdir (GenObjectRootLibDir)
0333         key = GenObject.checksum( sourceCode )
0334         basename = "%s_%s" % ("GenObject", key)
0335         SO = "%s/%s_C" % (GenObjectRootLibDir, basename)
0336         linuxSO = "%s.so" % SO
0337         windowsSO = "%s.dll" % SO
0338         if not os.path.exists (linuxSO) and not os.path.exists (windowsSO):
0339             print("creating SO")
0340             filename = "%s/%s.C" % (GenObjectRootLibDir, basename)
0341             if not os.path.exists (filename):
0342                 print("creating .C file")
0343                 target = open (filename, "w")
0344                 target.write (sourceCode)
0345                 target.close()
0346             else:
0347                 print("%s exists" % filename)
0348             ## command = "echo .L %s+ | root.exe -b" % filename
0349             ## os.system (command)
0350             ROOT.gSystem.CompileMacro (filename, "k")
0351         else:
0352             print("loading %s" % SO)
0353             ROOT.gSystem.Load(SO)
0354         return
0355 
0356 
0357     @staticmethod
0358     def _tofillGenObject():
0359         """Makes sure that I know how to read root files I made myself"""
0360         genObject = "GenObject"
0361         ntupleDict = GenObject._ntupleDict.setdefault (genObject, {})
0362         ntupleDict['_useChain'] = True
0363         ntupleDict['_tree'] = "goTree"
0364         for objName in GenObject._objsDict.keys():
0365             rootObjName = GenObject.rootClassName (objName)
0366             if GenObject.isSingleton (objName):
0367                 ntupleDict[objName] = objName
0368             else:
0369                 ntupleDict[objName] = objName + "s"
0370             tofillDict = GenObject._tofillDict.\
0371                          setdefault (genObject, {}).\
0372                          setdefault (objName, {})
0373             for varName in GenObject._objsDict [objName].keys():
0374                 # if the key starts with an '_', then it is not a
0375                 # variable, so don't treat it as one.
0376                 if varName.startswith ("_"):
0377                     continue
0378                 tofillDict[varName] = [ [(varName, GenObject._objFunc.obj)],
0379                                         {}]
0380 
0381 
0382     @staticmethod
0383     def prepareToLoadGenObject():
0384         """Makes all necessary preparations to load root files created
0385         by GenObject."""
0386         GenObject._tofillGenObject()
0387         GenObject._loadGoRootLibrary()
0388 
0389 
0390     @staticmethod
0391     def parseVariableTofill (fillname):
0392         """Returns tofill tuple made from string"""
0393         parts = GenObject._dotRE.split (fillname)
0394         partsList = []
0395         for part in parts:
0396             parenMatch = GenObject._parenRE.search (part)
0397             mode   = GenObject._objFunc.obj
0398             parens = []
0399             if parenMatch:
0400                 part   = parenMatch.group (1)
0401                 mode   = GenObject._objFunc.func
0402                 parens = \
0403                        GenObject._convertStringToParameters \
0404                        (parenMatch.group (2))
0405             partsList.append(  (part, mode, parens) )
0406         return partsList
0407 
0408     @staticmethod
0409     def _fixLostGreaterThans (handle):
0410         offset = handle.count ('<') - handle.count('>')
0411         if not offset:
0412             return handle
0413         if offset < 0:
0414             print("Huh?  Too few '<' for each '>' in handle '%'" % handle)
0415             return handle
0416         return handle + ' >' * offset
0417 
0418 
0419     @staticmethod
0420     def loadConfigFile (configFile):
0421         """Loads configuration file"""
0422         objName    = ""
0423         tupleName  = ""
0424         tofillName = ""
0425         modeEnum   = Enumerate ("none define tofill ntuple", "mode")
0426         mode       = modeEnum.none
0427         try:
0428             config = open (configFile, 'r')
0429         except:
0430             raise RuntimeError("Can't open configuration '%s'" % configFile)
0431         for lineNum, fullLine in enumerate (config):
0432             fullLine = fullLine.strip()
0433             # get rid of comments
0434             line = GenObject._commentRE.sub ('', fullLine)
0435             # Is there anything on this line?
0436             if not GenObject._nonSpacesRE.search (line):
0437                 # Nothing to see here folks.  Keep moving....
0438                 continue
0439             ###################
0440             ## Bracket Match ##
0441             ###################
0442             bracketMatch = GenObject._bracketRE.search (line)
0443             if bracketMatch:
0444                 # a header
0445                 section = bracketMatch.group(1)
0446                 words = GenObject._spacesRE.split( section )
0447                 if len (words) < 1:
0448                     raise RuntimeError("Don't understand line '%s'(%d)" \
0449                           % (fullLine, lineNum))
0450                 # The first word is the object name
0451                 # reset the rest of the list
0452                 objName = words[0]
0453                 words = words[1:]
0454                 colonWords = GenObject._colonRE.split (objName)
0455                 if len (colonWords) > 3:
0456                     raise RuntimeError("Don't understand line '%s'(%d)" \
0457                           % (fullLine, lineNum))
0458                 if len (colonWords) == 1:
0459                     ##########################
0460                     ## GenObject Definition ##
0461                     ##########################
0462                     mode = modeEnum.define
0463                     for word in words:
0464                         if GenObject._singletonRE.match (word):
0465                             #GenObject._singletonSet.add (objName)
0466                             objsDict = GenObject._objsDict.\
0467                                        setdefault (objName, {})
0468                             objsDict['_singleton'] = True
0469                             continue
0470                         # If we're still here, then we didn't have a valid
0471                         # option.  Complain vociferously
0472                         print("I don't understand '%s' in section '%s' : %s" \
0473                               % (word, section, mode))
0474                         raise RuntimeError("Config file parser error '%s'(%d)" \
0475                               % (fullLine, lineNum))
0476                 elif len (colonWords) == 2:
0477                     #######################
0478                     ## Ntuple Definition ##
0479                     #######################
0480                     mode = modeEnum.ntuple
0481                     ntupleDict = GenObject._ntupleDict.\
0482                                 setdefault (colonWords[0], {})
0483                     ntupleDict['_tree'] = colonWords[1]
0484                 else:
0485                     ##########################
0486                     ## Object 'tofill' Info ##
0487                     ##########################
0488                     mode = modeEnum.tofill
0489                     objName    = colonWords [0]
0490                     tupleName  = colonWords [1]
0491                     tofillName = colonWords [2]
0492                     ntupleDict = GenObject._ntupleDict.\
0493                                  setdefault (tupleName, {})
0494                     ntupleDict[objName] = tofillName
0495                     for word in words:
0496                         # label
0497                         labelMatch = GenObject._labelRE.search (word)
0498                         if labelMatch:
0499                             label = tuple( GenObject._commaRE.\
0500                                            split(  labelMatch.group (1) ) )
0501                             ntupleDict.setdefault ('_label', {}).\
0502                                                   setdefault (tofillName,
0503                                                               label)
0504                             continue
0505                         # shortcut
0506                         shortcutMatch = GenObject._shortcutRE.search (word)
0507                         if shortcutMatch:
0508                             shortcutFill = \
0509                                          GenObject.\
0510                                          parseVariableTofill ( shortcutMatch.\
0511                                                                group(1) )
0512                             ntupleDict.setdefault ('_shortcut', {}).\
0513                                                   setdefault (tofillName,
0514                                                               shortcutFill)
0515                             continue
0516                         # type/handle
0517                         typeMatch = GenObject._typeRE.search (word)
0518                         if typeMatch:
0519                             handleString = \
0520                                          GenObject.\
0521                                          _fixLostGreaterThans (typeMatch.group(1))
0522                             handle = Handle( handleString )
0523                             ntupleDict.setdefault ('_handle', {}).\
0524                                                   setdefault (tofillName,
0525                                                               handle)
0526                             continue
0527                         # alias 
0528                         aliasMatch = GenObject._aliasRE.search (word)
0529                         if aliasMatch:
0530                             ntupleDict.setdefault ('_alias', {}).\
0531                                                   setdefault (tofillName,
0532                                                               aliasMatch.\
0533                                                               group(1))
0534                             continue
0535                         # is this a lost '<'
0536                         if word == '>':
0537                             continue
0538                         # If we're still here, then we didn't have a valid
0539                         # option.  Complain vociferously
0540                         print("I don't understand '%s' in section '%s' : %s" \
0541                               % (word, section, mode))
0542                         raise RuntimeError("Config file parser error '%s'(%d)" \
0543                               % (fullLine, lineNum))
0544             ##############
0545             ## Variable ##
0546             ##############
0547             else:
0548                 # a variable
0549                 if modeEnum.none == mode:
0550                     # Poorly formatted 'section' tag
0551                     print("I don't understand line '%s'." % fullLine)
0552                     raise RuntimeError("Config file parser error '%s'(%d)" \
0553                           % (fullLine, lineNum))
0554                 colonWords = GenObject._colonRE.split (line, 1)
0555                 if len (colonWords) < 2:
0556                     # Poorly formatted 'section' tag
0557                     print("I don't understand line '%s'." % fullLine)
0558                     raise RuntimeError("Config file parser error '%s'(%d)" \
0559                           % (fullLine, lineNum))
0560                 varName = colonWords[0]
0561                 option  = colonWords[1]
0562                 if option:
0563                     pieces = GenObject._spacesRE.split (option)
0564                 else:
0565                     pieces = []
0566                 if modeEnum.define == mode:
0567                     #########################
0568                     ## Variable Definition ##
0569                     #########################
0570                     # is this a variable or an option?
0571                     if varName.startswith("-"):
0572                         # this is an option
0573                         if "-equiv" == varName.lower():
0574                             for part in pieces:
0575                                 halves = part.split (",")
0576                                 if 2 != len (halves):
0577                                     print("Problem with -equiv '%s' in '%s'" % \
0578                                           (part, section))
0579                                     raise RuntimeError("Config file parser error '%s'(%d)" \
0580                                           % (fullLine, lineNum))
0581                                 if halves[1]:
0582                                     halves[1] = float (halves[1])
0583                                     if not halves[1] >= 0:
0584                                         print("Problem with -equiv ",\
0585                                               "'%s' in '%s'" % \
0586                                               (part, section))
0587                                         raise RuntimeError("Config file parser error '%s'(%d)" \
0588                                               % (fullLine, lineNum))
0589                                 GenObject.setEquivExpression (section,
0590                                                               halves[0],
0591                                                               halves[1])
0592                         continue
0593                     # If we're here, then this is a variable
0594                     optionsDict = {}
0595                     for word in pieces:
0596                         typeMatch = GenObject._typeRE.search (word)
0597                         if typeMatch and \
0598                                GenObject.types.isValidKey (typeMatch.group(1)):
0599                             varType = typeMatch.group(1).lower()
0600                             optionsDict['varType'] = GenObject.types (varType)
0601                             continue
0602                         defaultMatch = GenObject._defaultRE.search (word)
0603                         if defaultMatch:
0604                             optionsDict['default'] = defaultMatch.group(1)
0605                             continue
0606                         precMatch = GenObject._precRE.search (word)
0607                         if precMatch:
0608                             optionsDict['prec'] = float (precMatch.group (1))
0609                             continue
0610                         formMatch = GenObject._formRE.search (word)
0611                         if formMatch:
0612                             form = GenObject._doublePercentRE.\
0613                                    sub ('%', formMatch.group (1))
0614                             optionsDict['form'] = form
0615                             continue
0616                         # If we're still here, then we didn't have a valid
0617                         # option.  Complain vociferously
0618                         print("I don't understand '%s' in section '%s'." \
0619                               % (word, option))
0620                         raise RuntimeError("Config file parser error '%s'(%d)" \
0621                               % (fullLine, lineNum))
0622                     GenObject.addObjectVariable (objName, varName, \
0623                                                  **optionsDict)
0624                 else: # if modeEnum.define != mode
0625                     ############################
0626                     ## Variable 'tofill' Info ##
0627                     ############################
0628                     if len (pieces) < 1:
0629                         continue
0630                     fillname, pieces = pieces[0], pieces[1:]
0631                     # I don't yet have any options available here, but
0632                     # I'm keeping the code here for when I add them.
0633                     optionsDict = {}
0634                     for word in pieces:
0635                         # If we're still here, then we didn't have a valid
0636                         # option.  Complain vociferously
0637                         print("I don't understand '%s' in section '%s'." \
0638                               % (word, option))
0639                         raise RuntimeError("Config file parser error '%s'(%d)" \
0640                               % (fullLine, lineNum))
0641                     tofillDict = GenObject._tofillDict.\
0642                                  setdefault (tupleName, {}).\
0643                                  setdefault (objName, {})
0644                     partsList = GenObject.parseVariableTofill (fillname)
0645                     tofillDict[varName] = [partsList, optionsDict]
0646         # for line
0647         for objName in GenObject._objsDict:
0648             # if this isn't a singleton, add 'index' as a variable
0649             if not GenObject.isSingleton (objName):
0650                 GenObject.addObjectVariable (objName, 'index',
0651                                              varType = GenObject.types.int,
0652                                              form = '%3d')
0653 
0654 
0655     @staticmethod
0656     def changeVariable (tupleName, objName, varName, fillname):
0657         """Updates the definition used to go from a Root object to a
0658         GenObject.  'tupleName' and 'objName' must already be defined."""
0659         parts = GenObject._dotRE.split (fillname)
0660         partsList = []
0661         for part in parts:
0662             parenMatch = GenObject._parenRE.search (part)
0663             mode   = GenObject._objFunc.obj
0664             parens = []
0665             if parenMatch:
0666                 part   = parenMatch.group (1)
0667                 mode   = GenObject._objFunc.func
0668                 parens = \
0669                        GenObject._convertStringToParameters \
0670                        (parenMatch.group (2))
0671             partsList.append(  (part, mode, parens) )
0672         GenObject._tofillDict[tupleName][objName][varName] = [partsList, {}]
0673 
0674 
0675 
0676     @staticmethod
0677     def evaluateFunction (obj, partsList, debug=False):
0678         """Evaluates function described in partsList on obj"""
0679         for part in partsList:
0680             if debug: warn (part, spaces=15)
0681             obj = getattr (obj, part[0])
0682             if debug: warn (obj, spaces=15)
0683             # if this is a function instead of a data member, make
0684             # sure you call it with its arguments:
0685             if GenObject._objFunc.func == part[1]:
0686                 # Arguments are stored as a list in part[2]
0687                 obj = obj (*part[2])
0688                 if debug: warn (obj, spaces=18)
0689         return obj
0690 
0691 
0692     @staticmethod
0693     def _genObjectClone (objName, tupleName, obj, index = -1):
0694         """Creates a GenObject copy of Root object"""
0695         debug = GenObject._kitchenSinkDict.get ('debug', False)
0696         if objName == 'runevent':
0697             debug = False
0698         tofillObjDict = GenObject._tofillDict.get(tupleName, {})\
0699                         .get(objName, {})
0700         genObj = GenObject (objName)
0701         origObj = obj
0702         if debug: warn (objName, spaces = 9)
0703         for genVar, ntDict in tofillObjDict.items():
0704             if debug: warn (genVar, spaces = 12)
0705             # lets work our way down the list
0706             partsList = ntDict[0]
0707             # start off with the original object
0708             obj = GenObject.evaluateFunction (origObj, partsList, debug)
0709             if debug: warn (obj, spaces=12)
0710             setattr (genObj, genVar, obj)
0711         # Do I need to store the index of this object?
0712         if index >= 0:
0713             setattr (genObj, 'index', index)
0714         return genObj
0715 
0716 
0717     @staticmethod
0718     def _rootObjectCopy (goSource, rootTarget):
0719         """Copies information from goSourse into Root Object"""
0720         objName = goSource._objName
0721         for varName in GenObject._objsDict [objName].keys():
0722             # if the key starts with an '_', then it is not a
0723             # variable, so don't treat it as one.
0724             if varName.startswith ("_"):
0725                 continue
0726             setattr( rootTarget, varName, goSource (varName) )
0727         
0728         
0729     @staticmethod
0730     def _rootObjectClone (obj):
0731         """Creates the approprite type of Root object and copies the
0732         information into it from the GO object."""
0733         objName = obj._objName
0734         classObj = GenObject._rootClassDict.get (objName)
0735         if not classObj:
0736             goName = GenObject.rootClassName (objName)
0737             classObj = GenObject._rootClassDict[ goName ]
0738         rootObj = classObj()
0739         for varName in GenObject._objsDict [objName].keys():
0740             setattr( rootObj, varName, obj (varName) )
0741         return rootObj
0742 
0743 
0744     @staticmethod
0745     def _rootDiffObject (obj1, obj2, rootObj = 0):
0746         """Given to GOs, it will create and fill the corresponding
0747         root diff object"""
0748         objName = obj1._objName
0749         # if we don't already have a root object, create one
0750         if not rootObj:
0751             diffName = GenObject.rootDiffClassName( objName )
0752             rootObj = GenObject._rootClassDict[diffName]()
0753         for varName in GenObject._objsDict [objName].keys():
0754             if varName.startswith ("_"): continue
0755             goType = GenObject._objsDict[objName][varName]['varType']
0756             if not goType in GenObject._basicSet:
0757                 # not yet
0758                 continue
0759             setattr( rootObj, varName, obj1 (varName) )            
0760             if  goType == GenObject.types.string:
0761                 # string
0762                 otherName = 'other_' + varName
0763                 if obj1 (varName) != obj2 (varName):
0764                     # don't agree
0765                     setattr( rootObj, otherName, obj2 (varName) )
0766                 else:
0767                     # agree
0768                     setattr( rootObj, otherName, '' )
0769             else:
0770                 # float, long, or int
0771                 deltaName = 'delta_' + varName
0772                 setattr( rootObj, deltaName,
0773                          obj2 (varName) - obj1 (varName) )
0774         return rootObj
0775 
0776 
0777     @staticmethod
0778     def setupOutputTree (outputFile, treeName, treeDescription = "",
0779                          otherNtupleName = ""):
0780         """Opens the output file, loads all of the necessary shared
0781         object libraries, and returns the output file and tree.  If
0782         'otherNtupleName' is given, it will check and make sure that
0783         only objects that are defined in it are written out."""
0784         rootfile = ROOT.TFile.Open (outputFile, "recreate")
0785         tree = ROOT.TTree (treeName, treeDescription)
0786         GenObject._loadGoRootLibrary()
0787         for objName in sorted (GenObject._objsDict.keys()):
0788             classname = GenObject.rootClassName (objName)
0789             rootObj = \
0790                     GenObject._rootClassDict[objName] = \
0791                     getattr (ROOT, classname)
0792             if GenObject.isSingleton (objName):
0793                 # singleton object
0794                 obj = GenObject._rootObjectDict[objName] = rootObj()
0795                 tree.Branch (objName, classname, obj)
0796             else:
0797                 # vector of objects - PLEASE don't forget the '()' on
0798                 # the end of the declaration.  Without this, you are
0799                 # defining a type, not instantiating an object.
0800                 vec = \
0801                     GenObject._rootObjectDict[objName] = \
0802                     ROOT.std.vector( rootObj )()
0803                 branchName = objName + "s"
0804                 vecName = "vector<%s>" % classname
0805                 tree.Branch( branchName, vecName, vec)
0806             # end else if isSingleton
0807         # end for objName
0808         return rootfile, tree
0809 
0810 
0811     @staticmethod
0812     def setupDiffOutputTree (outputFile, diffName, missingName,
0813                              diffDescription = '', missingDescription = ''):
0814         """Opens the diff output file, loads all of the necessary
0815         shared object libraries, and returns the output file and tree.b"""
0816         rootfile = ROOT.TFile.Open (outputFile, "recreate")
0817         GenObject._loadGoRootLibrary()
0818         # diff tree
0819         diffTree = ROOT.TTree (diffName, diffDescription)
0820         runEventObject = getattr (ROOT, 'go_runevent')()
0821         diffTree.Branch ('runevent', 'go_runevent', runEventObject)
0822         GenObject._rootClassDict['runevent'] = runEventObject
0823         for objName in sorted (GenObject._objsDict.keys()):
0824             if objName == 'runevent': continue
0825             classname = GenObject.rootClassName (objName)
0826             GenObject._rootClassDict[classname] = getattr (ROOT, classname)
0827             contName = GenObject.rootDiffContClassName (objName)
0828             diffName = GenObject.rootDiffClassName (objName)
0829             rootObj = GenObject._rootClassDict[contName] = \
0830                     getattr (ROOT, contName)
0831             GenObject._rootClassDict[diffName] = getattr (ROOT, diffName)
0832             obj = GenObject._rootObjectDict[objName] = rootObj()
0833             diffTree.Branch (objName, contName, obj)
0834         # missing tree
0835         missingTree = ROOT.TTree (missingName, missingDescription)
0836         rootRunEventClass = getattr (ROOT, 'go_runevent')
0837         firstOnly  = GenObject._rootClassDict['firstOnly'] = \
0838                      ROOT.std.vector( rootRunEventClass ) ()
0839         secondOnly = GenObject._rootClassDict['secondOnly'] = \
0840                      ROOT.std.vector( rootRunEventClass ) ()
0841         missingTree.Branch ('firstOnly',  'vector<go_runevent>', firstOnly) 
0842         missingTree.Branch ('secondOnly', 'vector<go_runevent>', secondOnly) 
0843         return rootfile, diffTree, missingTree
0844 
0845 
0846     @staticmethod
0847     def _fillRootObjects (event):
0848         """Fills root objects from GenObject 'event'"""
0849         for objName, obj in sorted (event.items()):
0850             if GenObject.isSingleton (objName):
0851                 # Just one
0852                 GenObject._rootObjectCopy (obj,
0853                                            GenObject._rootObjectDict[objName])
0854             else:
0855                 # a vector
0856                 vec = GenObject._rootObjectDict[objName]
0857                 vec.clear()
0858                 for goObj in obj:
0859                     vec.push_back( GenObject._rootObjectClone (goObj) )
0860 
0861 
0862     @staticmethod
0863     def _fillRootDiffs (event1, event2):
0864         """Fills root diff containers from two GenObject 'event's"""
0865         
0866 
0867 
0868     @staticmethod
0869     def isSingleton (objName):
0870         """Returns true if object is a singleton"""
0871         return GenObject._objsDict[objName].get('_singleton')
0872 
0873 
0874     @staticmethod
0875     def loadEventFromTree (eventTree, eventIndex,
0876                            onlyRunEvent  = False):
0877         """Loads event information from Root file (as interfaced by
0878         'cmstools.EventTree' or 'ROOT.TChain').  Returns a dictionary
0879         'event' containing lists of objects or singleton object.  If
0880         'onlyRunEvent' is et to True, then only run and event number
0881         is read in from the tree."""
0882         debug     = GenObject._kitchenSinkDict.get ('debug', False)
0883         tupleName = GenObject._kitchenSinkDict[eventTree]['tupleName']
0884         event = {}
0885         # is this a cint tree
0886         isChain = eventTree.__class__.__name__ == 'TChain'
0887         if isChain:
0888             # This means that 'eventTree' is a ROOT.TChain
0889             eventTree.GetEntry (eventIndex)
0890         else:
0891             # This means that 'eventTree' is a FWLite Events
0892             eventTree.to(eventIndex)
0893         tofillDict = GenObject._tofillDict.get (tupleName)
0894         ntupleDict = GenObject._ntupleDict.get (tupleName)
0895         if not tofillDict:
0896             print("Don't know how to fill from '%s' ntuple." % tupleName)
0897             return
0898         eventBranchName = ntupleDict['runevent']
0899         for objName in tofillDict:
0900             branchName = ntupleDict[objName]
0901             if onlyRunEvent and branchName != eventBranchName:
0902                 # not now
0903                 continue
0904             # Have we been given 'tofill' info for this object?
0905             if not branchName:
0906                 # guess not
0907                 continue
0908             if isChain:
0909                 # Root TChain
0910                 objects = getattr (eventTree, branchName)
0911             else:
0912                 # FWLite
0913                 shortcut = ntupleDict.get('_shortcut', {}).get(branchName)
0914                 if shortcut:
0915                     objects = GenObject.evaluateFunction (eventTree, shortcut)
0916                 else:
0917                     eventTree.toBegin()
0918                     handle = ntupleDict.get('_handle', {}).get(branchName)
0919                     label  = ntupleDict.get('_label' , {}).get(branchName)
0920                     if not handle or not label:
0921                         raise RuntimeError("Missing handle or label for '%s'"\
0922                               % branchName)
0923                     if not eventTree.getByLabel (label, handle):
0924                         raise RuntimeError("not able to get %s for %s" \
0925                               % (label, branchName))
0926                     objects = handle.product()
0927             # is this a singleton?
0928             if GenObject.isSingleton (objName):
0929                 event[objName] = GenObject.\
0930                                  _genObjectClone (objName,
0931                                                   tupleName,
0932                                                   objects)
0933                 continue
0934             # if we're here then we have a vector of items
0935             if debug: warn (objName, spaces = 3)
0936             event[objName] = []
0937             for index, obj in enumerate (objects):
0938                 event[objName].append( GenObject.\
0939                                        _genObjectClone (objName,
0940                                                         tupleName,
0941                                                         obj,
0942                                                         index) )
0943             del objects
0944             # end for obj        
0945         ## if not isChain:
0946         ##     del rootEvent
0947         # end for objName
0948         return event
0949 
0950 
0951     @staticmethod
0952     def printEvent (event):
0953         """Prints out event dictionary.  Mostly for debugging"""
0954         # Print out all singletons first
0955         for objName, obj in sorted (event.items()):
0956             #obj = event[objName]
0957             # is this a singleton?
0958             if GenObject.isSingleton (objName):
0959                 print("%s: %s" % (objName, obj))
0960         # Now print out all vectors
0961         for objName, obj in sorted (event.items()):
0962             #obj = event[objName]
0963             # is this a singleton?
0964             if not GenObject.isSingleton (objName):
0965                 # o.k. obj is a vector
0966                 print("%s:" % objName)
0967                 for single in obj:
0968                     print("  ", single)
0969         print()
0970 
0971 
0972     @staticmethod
0973     def setAliases (eventTree, tupleName):
0974         """runs SetAlias on all saved aliases"""
0975         aliases = GenObject._ntupleDict[tupleName].get('_alias', {})
0976         for name, alias in aliases.items():
0977             eventTree.SetAlias (name, alias)
0978 
0979 
0980     @staticmethod
0981     def changeAlias (tupleName, name, alias):
0982         """Updates an alias for an object for a given tuple"""
0983         aliasDict = GenObject._ntupleDict[tupleName]['_alias']
0984         if name not in aliasDict:
0985             raise RuntimeError("unknown name '%s' in tuple '%s'" % \
0986                   (name, tupleName))
0987         aliasDict[name] = alias
0988 
0989 
0990     @staticmethod
0991     def changeLabel (tupleName, objectName, label):
0992         """Updates an label for an object for a given tuple"""
0993         labelDict = GenObject._ntupleDict[tupleName]['_label']
0994         if objectName not in labelDict:
0995             raise RuntimeError("unknown name '%s' in tuple '%s'" % \
0996                   (objectName, tupleName))
0997         label = tuple( GenObject._commaRE.split( label ) )
0998         labelDict[objectName] = label
0999 
1000 
1001     @staticmethod
1002     def prepareTuple (tupleName, files, numEventsWanted = 0):
1003         """Given the tuple name and list of files, returns either a
1004         TChain or EventTree, and number of entries"""
1005         if "GenObject" == tupleName:
1006             GenObject.prepareToLoadGenObject()            
1007         if not isinstance (files, list):
1008             # If this isn't a list, make it one
1009             files = [files]
1010             ntupleDict = GenObject._ntupleDict[tupleName]
1011         treeName = ntupleDict["_tree"]
1012         if ntupleDict.get('_useChain'):
1013             chain = ROOT.TChain (treeName)
1014             for filename in files:
1015                 chain.AddFile (filename)
1016                 numEntries = chain.GetEntries()
1017         # Are we using a chain or EventTree here?
1018         else:
1019             chain = Events (files, forceEvent=True)
1020             numEntries = chain.size()
1021         chainDict = GenObject._kitchenSinkDict.setdefault (chain, {})
1022         if numEventsWanted and numEventsWanted < numEntries:
1023             numEntries = numEventsWanted
1024         chainDict['numEntries'] = numEntries
1025         chainDict['tupleName' ] = tupleName
1026         return chain
1027 
1028 
1029     @staticmethod
1030     def getRunEventEntryDict (chain, tupleName, numEntries):
1031         """Returns a dictionary of run, event tuples to entryIndicies"""
1032         reeDict = {}
1033         for entryIndex in range (numEntries):
1034             event = GenObject.loadEventFromTree (chain,
1035                                                  entryIndex,
1036                                                  onlyRunEvent = True)
1037             runevent = event['runevent']
1038             reeDict[ GenObject._re2key (runevent) ] = entryIndex
1039             #reeDict[ "one two three" ] = entryIndex
1040             del event
1041         return reeDict
1042 
1043 
1044     @staticmethod
1045     def _re2key (runevent):
1046         """Given a GO 'runevent' object, returns a sortable key"""
1047         # if we don't know how to make this object yet, let's figure
1048         # it out
1049         if not GenObject._runEventListDone:
1050             GenObject._runEventListDone = True
1051             ignoreSet = set( ['run', 'event'] )
1052             for varName in sorted (runevent.__dict__.keys()):
1053                 if varName.startswith ('_') or varName in ignoreSet:
1054                     continue
1055                 form = runevent.getVariableProperty (varName, "form")
1056                 if not form:
1057                     form = '%s'                
1058                 GenObject._runEventList.append ((varName, form))
1059         key = 'run:%d event:%d' % (runevent.run, runevent.event)
1060         for items in GenObject._runEventList:
1061             varName = items[0]
1062             form    = ' %s:%s' % (varName, items[1])
1063             key += form % runevent.getVariableProperty (varName)
1064         return key
1065 
1066 
1067     @staticmethod
1068     def _key2re (key, runevent=None):
1069         """Given a key, returns a GO 'runevent' object"""
1070         if not runevent:
1071             runevent = GenObject ('runevent')
1072         words = GenObject._spacesRE.split (key)
1073         for word in words:
1074             match = GenObject._singleColonRE.search (word)
1075             if match:
1076                 # for now, I'm assuming everything in the runevent
1077                 # tuple is an integer.  If this isn't the case, I'll
1078                 # have to come back and be more clever here.
1079                 runevent.__setattr__ (match.group(1), int( match.group(2) ))
1080         return runevent
1081                                      
1082 
1083     @staticmethod
1084     def compareRunEventDicts (firstDict, secondDict):
1085         """Compares the keys of the two dicts and returns three sets:
1086         the overlap, first but not second, and second but not first."""
1087         overlap    = set()
1088         firstOnly  = set()
1089         secondOnly = set()
1090         # loop over the keys of the first dict and compare to second dict
1091         for key in firstDict.keys():
1092             if key in secondDict:
1093                 overlap.add (key)
1094             else:
1095                 firstOnly.add (key)
1096         # now loop over keys of second dict and only check for missing
1097         # entries in first dict
1098         for key in secondDict.keys():
1099             if key not in firstDict:
1100                 secondOnly.add (key)
1101         # All done
1102         return overlap, firstOnly, secondOnly
1103 
1104 
1105     @staticmethod
1106     def pairEquivalentObjects (vec1, vec2):
1107         """Finds the equivalent objects in the two vectors"""
1108         len1, len2 = len (vec1), len (vec2)
1109         debug = GenObject._kitchenSinkDict.get ('debug', False)        
1110         if not len1 or not len2:
1111             # Nothing to see here folks.  Keep moving.
1112             if len1:
1113                 noMatch1Set = set( range(len1) )
1114             else:
1115                 noMatch1Set = set ()
1116             if len2:
1117                 noMatch2Set = set( range(len2) )
1118             else:
1119                 noMatch2Set = set ()
1120             if debug: warn ("Nothing found", sapces=6)
1121             return set(), noMatch1Set, noMatch2Set
1122         objName = vec1[0]._objName
1123         equivList = GenObject._equivDict[objName]
1124         firstDict = {}
1125         secondDict = {}
1126         # let's see if we're only using 'index' and nothing else
1127         if GenObject._kitchenSinkDict.get ('strictPairing') or \
1128                equivList == [('index', 0)]:
1129             # we're only matching on index, nothing else matters
1130             matchedSet = set (zip ( list(range( min (len1, len2))),
1131                                     list(range( min (len1, len2))) ) )
1132             if len1 > len2:
1133                 # since main pairing goes from 0..len2-1, we now want
1134                 # to go from len2..len1 inclusive
1135                 noMatch1Set = set (range(len2, len1 + 1))
1136             else:
1137                 noMatch1Set = set()
1138             if len2 > len1:
1139                 # same logic as above
1140                 noMatch2Set = set (range(len1, len2 + 1))
1141             else:
1142                 noMatch2Set = set()
1143             return matchedSet, noMatch1Set, noMatch2Set
1144         ##  # If we're still here, that means that we aren't matching on
1145         ##  # just index.  Instead of jumping to O(N^2), let's assume that
1146         ##  # both branches have everything in order and try to pair like
1147         ##  # that.  Anything not paired will be checked in a third pass.
1148         ##  unpairedFirst  = []
1149         ##  unpairedSecond = []
1150         ##  for index in xrange( min (len1, len2) ):
1151         ##      obj1 = vec1[index1]
1152         ##      total = 0.
1153         ##      obj2 = vec2[index2]
1154         ##      ok = True
1155         
1156         # First, look for vec2 objects that are equivalent to a
1157         # given vec1 object.
1158         for index1 in range (len1):
1159             objList = []
1160             obj1 = vec1[index1]
1161             for index2 in range (len2):
1162                 total = 0.
1163                 obj2 = vec2[index2]
1164                 ok = True
1165                 for equiv in equivList:
1166                     var, precision = equiv[0], equiv[1]
1167                     val1 = obj1 (var)
1168                     val2 = obj2 (var)
1169                     # Do we check equality or a precision
1170                     if precision:
1171                         value = abs (val1 - val2) / precision
1172                         if value >= 1.:
1173                             ok = False
1174                             break
1175                         total += value ** 2
1176                     elif val1 != val2:
1177                         ok = False
1178                         break
1179                 if ok:
1180                     objList.append( (total, index2) )
1181             objList.sort()
1182             firstDict[index1] = objList
1183         # Now do the same thing, but this time look for vec1 objects
1184         # that are equivalent to a given vec2 object
1185         for index2 in range (len2):
1186             objList = []
1187             obj2 = vec2[index2]
1188             for index1 in range (len1):
1189                 total = 0.
1190                 obj1 = vec1[index1]
1191                 ok = True
1192                 for equiv in equivList:
1193                     var, precision = equiv[0], equiv[1]
1194                     val2 = obj2 (var)
1195                     val1 = obj1 (var)
1196                     # Do we check equality or a precision
1197                     if precision:
1198                         value = abs (val2 - val1) / precision
1199                         if value > 1.:
1200                             ok = False
1201                             break
1202                         total += value ** 2
1203                     elif val2 != val1:
1204                         ok = False
1205                         break
1206                 if ok:
1207                     objList.append( (total, index1) )
1208             objList.sort()
1209             secondDict[index2] = objList
1210         # O.k. Now that we have the candidate matches, lets see who is
1211         # really matched.
1212         matchedSet = set()
1213         noMatch1Set = set()
1214         firstDictKeys = sorted (firstDict.keys())
1215         for index1 in firstDictKeys:
1216             list1 = firstDict[index1]
1217             # do I have a match?
1218             if not len (list1):
1219                 # no match
1220                 noMatch1Set.add (index1)
1221                 continue
1222             # we've got at least one match
1223             best1 = list1[0]
1224             index2 = best1[1]
1225             # Does this one match me?
1226             list2 = secondDict.get (index2, [])
1227             if len(list2) and list2[0][1] == index1:
1228                 matchedSet.add( (index1, index2) )
1229                 # get rid of the 2nd key hash
1230                 del firstDict[index1]
1231                 del secondDict[index2]
1232             else:
1233                 # no match
1234                 noMatch1Set.add (index1)
1235         noMatch2Set = set( secondDict.keys() )
1236         return matchedSet, noMatch1Set, noMatch2Set
1237 
1238 
1239     @staticmethod
1240     def compareTwoItems (item1, item2):
1241         """Compares all of the variables making sure they are the same
1242         on the two objects."""
1243         objName = item1._objName
1244         problems = {}
1245         relative = GenObject._kitchenSinkDict.get ('relative', False)
1246         for varName in GenObject._objsDict[objName].keys():
1247             prec = item1.getVariableProperty (varName, 'prec')
1248             if prec:
1249                 # we want to check within a precision
1250                 if relative:
1251                     val1 = item1(varName)
1252                     val2 = item2(varName)
1253                     numerator = 2 * abs (val1 - val2)
1254                     denominator = abs(val1) + abs(val2)
1255                     if not denominator:
1256                         # both are exactly zero, so there's no
1257                         # disagreement here.
1258                         continue
1259                     value = numerator / denominator
1260                     if value > prec:
1261                         # we've got a problem
1262                         problems[varName] = value                    
1263                 else:
1264                     value = abs( item1(varName) - item2(varName) )
1265                     if value > prec:
1266                         # we've got a problem
1267                         problems[varName] = value
1268             else:
1269                 # we want to check equality
1270                 if item1(varName) != item2(varName):
1271                     # we have a problem.  sort the values
1272                     val1, val2 = item1(varName), item2(varName)
1273                     if val1 > val2:
1274                         val1, val2 = val2, val1
1275                     problems[varName] = "%s != %s" % (val1, val2)
1276         # end for
1277         return problems
1278 
1279 
1280     @staticmethod
1281     def blurEvent (event, value, where = ""):
1282         """For debugging purposes only.  Will deliberately change
1283         values of first tree to verify that script is correctly
1284         finding problems."""
1285         for objName in sorted (event.keys()):
1286             if "runevent" == objName:
1287                 # runevent is a special case.  We don't compare these
1288                 continue
1289             if GenObject.isSingleton (objName):
1290                 # I'll add this in later.  For now, just skip it
1291                 continue
1292             count = 0
1293             for obj in event[objName]:
1294                 count += 1
1295                 for varName in GenObject._objsDict[objName].keys():
1296                     if isinstance (obj.__dict__[varName], str):
1297                         # don't bother
1298                         continue
1299                     randNumber = random.random()
1300                     #print "rN", randNumber
1301                     if randNumber < GenObject._kitchenSinkDict['blurRate']:
1302                         print("  %s: changing '%s' of '%s:%d'" \
1303                               % (where, varName, obj._objName, count))
1304                         ## print "objdict", obj.__dict__.get(varName), ':',\
1305                         ##       value
1306                         obj.__dict__[varName] += value
1307 
1308 
1309     @staticmethod
1310     def compareTwoTrees (chain1, chain2, **kwargs):
1311         """Given all of the necessary information, this routine will
1312         go through and compare two trees making sure they are
1313         'identical' within requested precision.  If 'diffOutputName'
1314         is passed in, a root file with a diffTree and missingTree will
1315         be produced."""
1316         print("Comparing Two Trees")
1317         diffOutputName = kwargs.get ('diffOutputName')
1318         tupleName1  = GenObject._kitchenSinkDict[chain1]['tupleName']
1319         numEntries1 = GenObject._kitchenSinkDict[chain1]['numEntries']
1320         tupleName2  = GenObject._kitchenSinkDict[chain2]['tupleName']
1321         numEntries2 = GenObject._kitchenSinkDict[chain2]['numEntries']
1322         debug       = GenObject._kitchenSinkDict.get ('debug', False)
1323         ree1 = GenObject.getRunEventEntryDict (chain1, tupleName1, numEntries1)
1324         ree2 = GenObject.getRunEventEntryDict (chain2, tupleName2, numEntries2)
1325         overlap, firstOnly, secondOnly = \
1326                  GenObject.compareRunEventDicts (ree1, ree2)
1327         if diffOutputName:
1328             rootfile, diffTree, missingTree = \
1329                       GenObject.setupDiffOutputTree (diffOutputName,
1330                                                      'diffTree',
1331                                                      'missingTree')
1332             if firstOnly:
1333                 vec = GenObject._rootClassDict['firstOnly']
1334                 for key in firstOnly:
1335                     runevent = GenObject._key2re (key)
1336                     vec.push_back( GenObject._rootObjectClone( runevent ) )
1337             if secondOnly:
1338                 vec = GenObject._rootClassDict['secondOnly']
1339                 for key in secondOnly:
1340                     runevent = GenObject._key2re (key)
1341                     vec.push_back( GenObject._rootObjectClone( runevent ) )
1342             missingTree.Fill()
1343         resultsDict = {}
1344         if firstOnly:
1345             resultsDict.setdefault ('_runevent', {})['firstOnly'] = \
1346                                    len (firstOnly)
1347         if secondOnly:
1348             resultsDict.setdefault ('_runevent', {})['secondOnly'] = \
1349                                    len (secondOnly)
1350         resultsDict['eventsCompared'] = len (overlap)
1351         for reTuple in sorted(overlap):
1352             # if we are filling the diff tree, then save the run and
1353             # event information.
1354             if diffOutputName:
1355                 GenObject._key2re (reTuple,
1356                                    GenObject._rootClassDict['runevent'])
1357             if debug: warn ('event1', blankLines = 3)
1358             event1 = GenObject.loadEventFromTree (chain1, ree1 [reTuple])
1359             if debug: warn ('event2', blankLines = 3)
1360             event2 = GenObject.loadEventFromTree (chain2, ree2 [reTuple])
1361             if GenObject._kitchenSinkDict.get('printEvent'):
1362                 print("event1:")
1363                 GenObject.printEvent (event1)
1364                 print("event2:")
1365                 GenObject.printEvent (event2)
1366             if GenObject._kitchenSinkDict.get('blur'):
1367                 where = reTuple
1368                 GenObject.blurEvent (event1,
1369                                      GenObject._kitchenSinkDict['blur'],
1370                                      where)
1371             for objName in sorted (event1.keys()):
1372                 if "runevent" == objName:
1373                     # runevent is a special case.  We don't compare these
1374                     continue
1375                 if not GenObject._equivDict.get (objName):
1376                     # we don't know how to compare these objects, so
1377                     # skip them.
1378                     continue
1379                 if GenObject.isSingleton (objName):
1380                     # I'll add this in later.  For now, just skip it
1381                     print("singleton")
1382                     continue
1383                 # Get ready to calculate root diff object if necessary
1384                 rootObj = 0
1385                 if diffOutputName:
1386                     rootObj = GenObject._rootObjectDict[objName]
1387                     rootObj.clear()
1388                 vec1 = event1[objName]
1389                 vec2 = event2[objName]
1390                 matchedSet, noMatch1Set, noMatch2Set = \
1391                             GenObject.pairEquivalentObjects (vec1, vec2)
1392                 if noMatch1Set or noMatch2Set:
1393                     ## print "No match 1", noMatch1Set
1394                     ## print "No match 2", noMatch2Set
1395                     count1 = len (noMatch1Set)
1396                     count2 = len (noMatch2Set)
1397                     key = (count1, count2)
1398                     countDict = resultsDict.\
1399                                 setdefault (objName, {}).\
1400                                 setdefault ('_missing', {})
1401                     if key in countDict:
1402                         countDict[key] += 1
1403                     else:
1404                         countDict[key] = 1
1405                     # should be calculating root diff objects
1406                     if diffOutputName:
1407                         # first set
1408                         for index in sorted(list(noMatch1Set)):
1409                             goObj = vec1 [index]
1410                             rootObj.firstOnly.push_back ( GenObject.\
1411                                                           _rootObjectClone \
1412                                                           (goObj) )
1413                         # second set
1414                         for index in sorted(list(noMatch2Set)):
1415                             goObj = vec2 [index]
1416                             rootObj.secondOnly.push_back ( GenObject.\
1417                                                           _rootObjectClone \
1418                                                            (goObj) )
1419                 # o.k.  Now that we have them matched, let's compare
1420                 # the proper items:                
1421                 for pair in sorted(list(matchedSet)):
1422                     if diffOutputName:
1423                         rootDiffObj = GenObject._rootDiffObject \
1424                                       ( vec1[ pair[1 - 1] ],
1425                                         vec2[ pair[2 - 1] ] ) 
1426                         rootObj.diff.push_back ( rootDiffObj )
1427                     problems = GenObject.\
1428                                compareTwoItems (vec1[ pair[1 - 1] ],
1429                                                 vec2[ pair[2 - 1] ])
1430                     if problems.keys():
1431                         # pprint.pprint (problems)
1432                         for varName in problems.keys():
1433                             countDict = resultsDict.\
1434                                         setdefault (objName, {}).\
1435                                         setdefault ('_var', {})
1436                             if varName in countDict:
1437                                 countDict[varName] += 1
1438                             else:
1439                                 countDict[varName] = 1
1440                 key = 'count_%s' % objName
1441                 if key not in resultsDict:
1442                     resultsDict[key] = 0
1443                 resultsDict[key] += len (matchedSet)
1444                 # try cleaning up
1445                 del vec1
1446                 del vec2
1447             # end for objName        
1448             if diffOutputName:
1449                 diffTree.Fill()
1450             del event1
1451             del event2
1452         # end for overlap
1453         if diffOutputName:
1454             diffTree.Write()
1455             missingTree.Write()
1456             rootfile.Close()
1457         return resultsDict
1458 
1459 
1460     @staticmethod
1461     def saveTupleAs (chain, rootFile):
1462         """Saves a chain as a GO tree"""
1463         print("saveTupleAs")
1464         rootfile, tree = GenObject.setupOutputTree (rootFile, "goTree")
1465         numEntries = GenObject._kitchenSinkDict[chain]['numEntries']        
1466         for entryIndex in range (numEntries):
1467             event = GenObject.loadEventFromTree (chain, entryIndex)            
1468             if GenObject._kitchenSinkDict.get('blur'):
1469                 where = "run %d event %d" % (event['runevent'].run,
1470                                              event['runevent'].event)
1471                 if random.random() < GenObject._kitchenSinkDict.get('blur'):
1472                     # dropping event
1473                     print("Dropping", where)
1474                     continue
1475                 GenObject.blurEvent (event,
1476                                      GenObject._kitchenSinkDict['blur'],
1477                                      where)
1478                 # check to see if we should drop the event
1479             if GenObject._kitchenSinkDict.get('printEvent'):
1480                 GenObject.printEvent (event)
1481             GenObject._fillRootObjects (event)
1482             tree.Fill()
1483         tree.Write()
1484         rootfile.Close()
1485 
1486 
1487     @staticmethod
1488     def setGlobalFlag (key, value):
1489         """Sets a global flag in _kitchenSinkDict"""
1490         GenObject._kitchenSinkDict [key] = value
1491 
1492 
1493     @staticmethod
1494     def printTuple (chain):
1495         """Prints out all events to stdout"""
1496         numEntries = GenObject._kitchenSinkDict[chain]['numEntries']
1497         debug = GenObject._kitchenSinkDict.get ('debug', False)
1498         if debug: warn (numEntries)
1499         for entryIndex in range (numEntries):
1500             if debug: warn (entryIndex, spaces=3)
1501             event = GenObject.loadEventFromTree (chain, entryIndex)            
1502             GenObject.printEvent (event)
1503             if debug: warn(spaces=3)
1504 
1505     @staticmethod
1506     def _convertStringToParameters (string):
1507         """Convert comma-separated string into a python3 list of
1508         parameters.  Currently only understands strings, floats, and
1509         integers."""
1510         retval = []        
1511         words = GenObject._commaRE.split (string)
1512         for word in words:
1513             if not len (word):
1514                 continue
1515             match = GenObject._singleQuoteRE.search (word)
1516             if match:
1517                 retval.append (match.group (1))
1518                 continue
1519             match = GenObject._doubleQuoteRE.search (word)
1520             if match:
1521                 retval.append (match.group (1))
1522                 continue
1523             try:
1524                 val = int (word)
1525                 retval.append (val)
1526                 continue
1527             except:
1528                 pass
1529             try:
1530                 val = float (word)
1531                 retval.append (val)
1532                 continue
1533             except:
1534                 pass
1535             # if we're still here, we've got a problem
1536             raise RuntimeError("Unknown parameter '%s'." % word)
1537         return retval
1538 
1539         
1540     ######################
1541     ## Member Functions ##
1542     ######################
1543 
1544 
1545     def __init__ (self, objName):
1546         """Class initializer"""
1547         if objName not in GenObject._objsDict:# or \
1548             #not GenObject._equivDict.has_key (objName) :
1549             # not good
1550             print("Error: GenObject does not know about object '%s'." % objName)
1551             raise RuntimeError("Failed to create GenObject object.")
1552         self._localObjsDict = GenObject._objsDict [objName]
1553         self._objName = objName;
1554         for key, varDict in self._localObjsDict.items():
1555             # if the key starts with an '_', then it is not a
1556             # variable, so don't treat it as one.
1557             if key.startswith ("_"):
1558                 continue
1559             self.setValue (key, varDict['default'])
1560             
1561 
1562     def setValue (self, name, value):
1563         """Wrapper for __setattr___"""
1564         self.__setattr__ (name, value)
1565 
1566     
1567     def getVariableProperty (self, var, key):
1568         """ Returns property assoicated with 'key' for variable 'var'
1569         of object of the same type as 'self'.  Returns 'None' if 'var'
1570         or 'key' is not defined."""
1571         return GenObject._objsDict.get (self._objName,
1572                                         {}).get (var, {}). get (key, None)
1573 
1574 
1575     def __setattr__ (self, name, value):
1576         """Controls setting of values."""
1577         if name.startswith ("_"):
1578             # The internal version. Set anything you want.
1579             object.__setattr__ (self, name, value)
1580         else:
1581             # user version - Make sure this variable has already been
1582 
1583             # defined for this type:
1584             if name not in self._localObjsDict:
1585                 # this variable has not been defined
1586                 print("Warning: '%s' for class '%s' not setup. Skipping." % \
1587                       (name, self._objName))
1588                 return
1589             varType = self.getVariableProperty (name, 'varType')
1590             # if this is an int, make sure it stays an int
1591             if GenObject.types.int == varType:
1592                 try:
1593                     # This will work with integers, floats, and string
1594                     # representations of integers.
1595                     value = int (value)
1596                 except:
1597                     # This works with string representations of floats
1598                     value = int( float( value ) )
1599             elif GenObject.types.long == varType:
1600                 try:
1601                     # This will work with integers, floats, and string
1602                     # representations of integers.
1603                     value = long (value)
1604                 except:
1605                     # This works with string representations of floats
1606                     value = long( float( value ) )
1607             elif GenObject.types.float == varType:
1608                 # Make sure it's a float
1609                 value = float (value)
1610             elif GenObject.types.string == varType:
1611                 # make sure it's a string
1612                 value = str (value)
1613             # if we're still here, set it
1614             object.__setattr__ (self, name, value)
1615 
1616 
1617     def __call__ (self, key):
1618         """Makes object callable"""
1619         return object.__getattribute__ (self, key)
1620 
1621 
1622     def __str__ (self):
1623         """String representation"""
1624         retval = ""
1625         for varName, value in sorted (self.__dict__.items()):
1626             if varName.startswith ('_'): continue
1627             form = self.getVariableProperty (varName, "form")
1628             if form:
1629                 format = "%s:%s  " % (varName, form)
1630                 retval = retval + format % value
1631             else:
1632                 retval = retval + "%s:%s  " % (varName, value)
1633         return retval