Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2023-03-17 11:15:49

0001 from __future__ import print_function
0002 from __future__ import absolute_import
0003 # Copyright (C) 2014 Colin Bernet
0004 # https://github.com/cbernet/heppy/blob/master/LICENSE
0005 
0006 from .weight import Weight
0007 import copy
0008 import glob
0009 
0010 def printComps(comps, details=False):
0011     '''
0012     Summary printout for  a list of components comps.
0013     The components are assumed to have a name, and a list of files,
0014     like the ones from this module.
0015     '''
0016     nJobs = 0
0017     nCompsWithFiles = 0
0018     for c in comps:
0019         if not hasattr(c, 'splitFactor'):
0020             c.splitFactor = 1
0021         print(c.name, c.splitFactor, len(c.files))
0022         if len(c.files)==0:
0023             continue
0024         else:
0025             if details:
0026                 print(c.files[0])
0027             nJobs += c.splitFactor
0028             nCompsWithFiles += 1
0029 
0030     print('-'*70)
0031     print('# components with files = ', nCompsWithFiles)
0032     print('# jobs                  = ', nJobs)
0033 
0034 
0035 class CFG(object):
0036     '''Base configuration class. The attributes are used to store parameters of any type'''
0037     def __init__(self, **kwargs):
0038         '''All keyword arguments are added as attributes.'''
0039         self.__dict__.update( **kwargs )
0040 
0041     def __str__(self):
0042         '''A useful printout'''
0043         header = '{type}: {name}'.format( type=self.__class__.__name__,
0044                                           name=self.name)
0045         varlines = ['\t{var:<15}:   {value}'.format(var=var, value=value) \
0046                     for var,value in sorted(vars(self.items())) \
0047                     if var is not 'name']
0048         all = [ header ]
0049         all.extend(varlines)
0050         return '\n'.join( all )
0051 
0052     def clone(self, **kwargs):
0053         '''Make a copy of this object, redefining (or adding) some parameters, just
0054            like in the CMSSW python configuration files. 
0055 
0056            For example, you can do
0057               module1 = cfg.Analyzer(SomeClass, 
0058                           param1 = value1, 
0059                           param2 = value2, 
0060                           param3 = value3, 
0061                           ...)
0062               module2 = module1.clone(
0063                          param2 = othervalue,
0064                          newparam = newvalue)
0065            and module2 will inherit the configuration of module2 except for
0066            the value of param2, and for having an extra newparam of value newvalue
0067            (the latter may be useful if e.g. newparam were optional, and needed
0068            only when param2 == othervalue)
0069 
0070            Note that, just like in CMSSW, this is a shallow copy and not a deep copy,
0071            i.e. if in the example above value1 were to be an object, them module1 and
0072            module2 will share the same instance of value1, and not have two copies.
0073         '''
0074         other = copy.copy(self)
0075         for k,v in kwargs.items():
0076             setattr(other, k, v)
0077         return other
0078     
0079 class Analyzer( CFG ):
0080     '''Base analyzer configuration, see constructor'''
0081 
0082     num_instance = 0
0083     
0084     def __init__(self, class_object, instance_label=None, 
0085                  verbose=False, **kwargs):
0086         '''
0087         One could for example define the analyzer configuration for a
0088         di-muon framework.Analyzer.Analyzer in the following way:
0089 
0090         ZMuMuAna = cfg.Analyzer(
0091         "ZMuMuAnalyzer",
0092         pt1 = 20,
0093         pt2 = 20,
0094         iso1 = 0.1,
0095         iso2 = 0.1,
0096         eta1 = 2,
0097         eta2 = 2,
0098         m_min = 0,
0099         m_max = 200
0100         )
0101 
0102         Any kinds of keyword arguments can be added.
0103         The name must be present, and must be well chosen, as it will be used
0104         by the Looper to find the module containing the Analyzer class.
0105         This module should be in your PYTHONPATH. If not, modify your python path
0106         accordingly in your script.
0107         '''
0108 
0109         self.class_object = class_object
0110         self.__class__.num_instance += 1 
0111         if instance_label is None:
0112             instance_label = str(self.__class__.num_instance)
0113         self.instance_label = instance_label
0114         self.verbose = verbose
0115         super(Analyzer, self).__init__(**kwargs)
0116 
0117     def __setattr__(self, name, value):
0118         '''You may decide to copy an existing analyzer and change
0119         its instance_label. In that case, one must stay consistent.'''
0120         self.__dict__[name] = value
0121         if name == 'instance_label':
0122             self.name = self.build_name()   
0123 
0124     def build_name(self):
0125         class_name = '.'.join([self.class_object.__module__, 
0126                                self.class_object.__name__])
0127         name = '_'.join([class_name, self.instance_label])
0128         return name 
0129 
0130     def clone(self, **kwargs):
0131         other = super(Analyzer, self).clone(**kwargs)
0132         if 'class_object' in kwargs and 'name' not in kwargs:
0133             other.name = other.build_name()
0134         return other
0135 
0136     
0137 class Service( CFG ):
0138     
0139     num_instance = 0
0140 
0141     def __init__(self, class_object, instance_label=None, 
0142                  verbose=False, **kwargs):
0143         self.class_object = class_object
0144         self.__class__.num_instance += 1 
0145         if instance_label is None:
0146             instance_label = str(self.__class__.num_instance)
0147         self.instance_label = instance_label
0148         self.__class__.num_instance += 1 
0149         self.name = self.build_name()
0150         self.verbose = verbose
0151         super(Service, self).__init__(**kwargs)
0152 
0153     def build_name(self):
0154         class_name = '.'.join([self.class_object.__module__, 
0155                                self.class_object.__name__])
0156         name = '_'.join([class_name, self.instance_label])
0157         return name 
0158 
0159     def __setattr__(self, name, value):
0160         '''You may decide to copy an existing analyzer and change
0161         its instance_label. In that case, one must stay consistent.'''
0162         self.__dict__[name] = value
0163         if name == 'instance_label':
0164             self.name = self.build_name()   
0165 
0166     def clone(self, **kwargs):
0167         other = super(Service, self).clone(**kwargs)
0168         if 'class_object' in kwargs and 'name' not in kwargs:
0169             other.name = other.build_name()
0170         return other
0171 
0172 
0173 class Sequence( list ):
0174     '''A list with print functionalities.
0175 
0176     Used to define a sequence of analyzers.'''
0177     def __str__(self):
0178         tmp = []
0179         for index, ana in enumerate( self ):
0180             tmp.append( '{index} :'.format(index=index) )
0181             tmp.append( '{ana} :'.format(ana=ana) )
0182         return '\n'.join(tmp)
0183 
0184 #TODO review inheritance, and in particular constructor args - this is a mess.
0185 
0186 class Component( CFG ):
0187     '''Base component class.
0188 
0189     See the child classes:
0190     DataComponent, MCComponent, EmbedComponent
0191     for more information.'''
0192     def __init__(self, name, files, tree_name=None, triggers=None, **kwargs):
0193         if isinstance(triggers, str):
0194             triggers = [triggers]
0195         if isinstance(files, str):
0196             files = sorted(glob.glob(files))
0197         super( Component, self).__init__( name = name,
0198                                           files = files,
0199                                           tree_name = tree_name,
0200                                           triggers = triggers, **kwargs)
0201         self.dataset_entries = 0
0202         self.isData = False
0203         self.isMC = False
0204         self.isEmbed = False
0205 
0206 class DataComponent( Component ):
0207 
0208     def __init__(self, name, files, intLumi=None, triggers=[], json=None):
0209         super(DataComponent, self).__init__(name, files, triggers=triggers)
0210         self.isData = True
0211         self.intLumi = intLumi
0212         self.json = json
0213 
0214     def getWeight( self, intLumi = None):
0215         return Weight( genNEvents = -1,
0216                        xSection = None,
0217                        genEff = -1,
0218                        intLumi = self.intLumi,
0219                        addWeight = 1. )
0220 
0221 
0222 
0223 class MCComponent( Component ):
0224     def __init__(self, name, files, triggers=[], xSection=1,
0225                  nGenEvents=None,
0226                  effCorrFactor=None, **kwargs ):
0227         super( MCComponent, self).__init__( name = name,
0228                                             files = files,
0229                                             triggers = triggers, **kwargs )
0230         self.xSection = xSection
0231         self.nGenEvents = nGenEvents
0232         self.effCorrFactor = effCorrFactor
0233         self.isMC = True
0234         self.intLumi = 1.
0235         self.addWeight = 1.
0236 
0237     def getWeight( self, intLumi = None):
0238         # if intLumi is None:
0239         #    intLumi = Weight.FBINV
0240         #COLIN THIS WEIGHT STUFF IS REALLY BAD!!
0241         # use the existing Weight class or not? guess so...
0242         return Weight( genNEvents = self.nGenEvents,
0243                        xSection = self.xSection,
0244                        intLumi = self.intLumi,
0245                        genEff = 1/self.effCorrFactor,
0246                        addWeight = self.addWeight )
0247 
0248 class Config( object ):
0249     '''Main configuration object, holds a sequence of analyzers, and
0250     a list of components.'''
0251     def __init__(self, components, sequence, services, events_class,preprocessor=None):
0252         self.preprocessor = preprocessor
0253         self.components = components
0254         self.sequence = sequence
0255         self.services = services
0256         self.events_class = events_class
0257 
0258     def __str__(self):
0259         comp = '\n'.join(map(str, self.components))
0260         sequence = str(self.sequence)
0261         services = '\n'.join( map(str, self.services))
0262         return '\n'.join([comp, sequence, services])
0263 
0264