Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-12-01 23:40:46

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