class DataDrivenTTZEstimate(SystematicEstimator): def __init__(self, name, cacheDir=None, useTop16009=False): super(DataDrivenTTZEstimate, self).__init__(name, cacheDir=cacheDir) self.nJets = (3, -1) # jet selection (min, max) self.nLooseBTags = (2, -1) # loose bjet selection (min, max) self.nMediumBTags = (0, -1) # bjet selection (min, max) self.useTop16009 = useTop16009 self.ratioTop16009 = 1.27 # self.sysErrTop16009 = (-0.17, +0.20) self.statErrTop16009 = (-0.37, +0.42) # Because we are going to reuse a lot of yields which otherwise will be terribly slow self.helperCacheName = os.path.join('.', 'helperCache.pkl') self.helperCache = Cache(self.helperCacheName, verbosity=2) def yieldFromCache(self, setup, sample, channel, selectionString, weightString): s = (sample, channel, selectionString, weightString) if self.helperCache.contains(s): return self.helperCache.get(s) else: yieldFromDraw = u_float( **setup.sample[sample][channel].getYieldFromDraw( selectionString, weightString)) self.helperCache.add(s, yieldFromDraw, save=True) return yieldFromDraw #Concrete implementation of abstract method 'estimate' as defined in Systematic def _estimate(self, region, channel, setup): logger.info("Data-driven TTZ estimate for region " + str(region) + " in channel " + channel + " and setup " + str(setup.sys) + ":") #Sum of all channels for 'all' if channel == 'all': estimate = sum([ self.cachedEstimate(region, c, setup) for c in ['MuMu', 'EE', 'EMu'] ]) else: zWindow = 'allZ' if channel == 'EMu' else 'offZ' preSelection = setup.preselection('MC', zWindow=zWindow, channel=channel) MC_2l = "&&".join([ region.cutString(setup.sys['selectionModifier']), preSelection['cut'] ]) weight = setup.weightString() logger.info("weight: %s", weight) yield_ttZ_2l = setup.lumi[channel] / 1000. * self.yieldFromCache( setup, 'TTZ', channel, MC_2l, weight) logger.info("yield_MC_2l: %s" % yield_ttZ_2l) if self.useTop16009: sysError = max((abs(x) for x in self.sysErrTop16009 )) # not sure yet to handle assymetric errors statError = max((abs(x) for x in self.statErrTop16009)) error = sqrt(sysError * sysError + statError * statError) return u_float(self.ratioTop16009, error) * yield_ttZ_2l else: # pt leptons > 30, 20, 10 GeV useTrigger = False # setup.parameters['useTriggers'] # better not to use three lepton triggers, seems to be too inefficient lllSelection = {} lllSelection['MuMu'] = "&&".join([ getLeptonString(3, 0), getPtThresholdString(30, 20, 10) ]) + ("&&HLT_3mu" if useTrigger else "") lllSelection['MuMuE'] = "&&".join([ getLeptonString(2, 1), getPtThresholdString(30, 20, 10) ]) + ("&&HLT_2mu1e" if useTrigger else "") lllSelection['MuEE'] = "&&".join([ getLeptonString(1, 2), getPtThresholdString(30, 20, 10) ]) + ("&&HLT_2e1mu" if useTrigger else "") lllSelection['EE'] = "&&".join([ getLeptonString(0, 3), getPtThresholdString(30, 20, 10) ]) + ("&&HLT_3e" if useTrigger else "") lllSelection['EMu'] = "((" + lllSelection[ 'MuMuE'] + ")||(" + lllSelection['MuEE'] + "))" bJetSelectionM = "(Sum$(JetGood_pt>30&&abs(JetGood_eta)<2.4&&JetGood_id&&JetGood_btagCSV>0.890))" bJetSelectionL = "(Sum$(JetGood_pt>30&&abs(JetGood_eta)<2.4&&JetGood_id&&JetGood_btagCSV>0.605))" zMassSelection = "abs(mlmZ_mass-91.1876)<10" # Start from base hadronic selection and add loose b-tag and Z-mass requirement selection = {} for dataOrMC in ["Data", "MC"]: selection[dataOrMC] = setup.selection( dataOrMC, hadronicSelection=True, **setup.defaultParameters( update={ 'nJets': self.nJets, 'nBTags': self.nMediumBTags, 'metMin': 0., 'metSigMin': 0., 'dPhiJetMet': 0. }))['cut'] selection[dataOrMC] += "&&" + bJetSelectionL + ">=" + str( self.nLooseBTags[0]) selection[dataOrMC] += "&&" + zMassSelection logger.info("Selection " + dataOrMC + ": " + selection[dataOrMC]) # Calculate yields (take together channels together) channels = ['MuMu', 'EMu', 'EE'] yield_ttZ_3l = sum( self.yieldFromCache( setup, 'TTZ', c, "&&".join( [lllSelection[c], selection["MC"]]), weight) * setup.dataLumi[channel] / 1000 for c in ['MuMu', 'EMu', 'EE']) yield_other = sum( self.yieldFromCache( setup, s, c, "&&".join( [lllSelection[c], selection["MC"]]), weight) * setup.dataLumi[channel] / 1000 for c in ['MuMu', 'EMu', 'EE'] for s in ['TTJets', 'DY', 'other']) yield_data_3l = sum( self.yieldFromCache( setup, 'Data', c, "&&".join( [lllSelection[c], selection["Data"]]), "(1)") for c in ['MuMu', 'EMu', 'EE']) if not yield_ttZ_3l > 0: logger.warn("No yield for 3l selection") estimate = u_float(0, 0) yield_ttZ_data = yield_data_3l - yield_other if yield_ttZ_data < 0: logger.warn("Data-driven ratio is negative!") yield_ttZ_data = u_float(0, 0) logger.info("Control region predictions: ") logger.info(" data: " + str(yield_data_3l)) logger.info(" MC other: " + str(yield_other)) logger.info(" TTZ (MC): " + str(yield_ttZ_3l)) logger.info(" TTZ (data): " + str(yield_ttZ_data)) logger.info(" TTZ (ratio): " + str(yield_ttZ_data / yield_ttZ_3l)) estimate = (yield_ttZ_data / yield_ttZ_3l) * yield_ttZ_2l logger.info(" --> " + str(estimate)) return estimate
class SystematicEstimator: __metaclass__ = abc.ABCMeta def __init__(self, name, cacheDir=None): self.name = name self.initCache(cacheDir) def initCache(self, cacheDir): if cacheDir: self.cacheDir=cacheDir cacheFileName = os.path.join(cacheDir, self.name+'.pkl') if not os.path.exists(os.path.dirname(cacheFileName)): os.makedirs(os.path.dirname(cacheFileName)) self.cache = Cache(cacheFileName, verbosity=2) else: self.cache=None def uniqueKey(self, region, channel, setup): return region, channel, json.dumps(setup.sys, sort_keys=True), json.dumps(setup.parameters, sort_keys=True), json.dumps(setup.lumi, sort_keys=True) def cachedEstimate(self, region, channel, setup, save=True): key = self.uniqueKey(region, channel, setup) if self.cache and self.cache.contains(key): res = self.cache.get(key) logger.debug( "Loading cached %s result for %r : %r"%(self.name, key, res) ) return res elif self.cache: return self.cache.add( key, self._estimate( region, channel, setup), save=save) else: return self._estimate( region, channel, setup) @abc.abstractmethod def _estimate(self, region, channel, setup): '''Estimate yield in 'region' using setup''' return def PUSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightPUUp']})) down = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightPUDown']})) return 0.5*(up-down)/ref def topPtSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightTopPt']})) return 0.5*(up-ref)/ref def JERSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate(region, channel, setup.sysClone({'selectionModifier':'JERUp'})) down = self.cachedEstimate(region, channel, setup.sysClone({'selectionModifier':'JERDown'})) return 0.5*(up-down)/ref def JECSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate(region, channel, setup.sysClone({'selectionModifier':'JECUp'})) down = self.cachedEstimate(region, channel, setup.sysClone({'selectionModifier':'JECDown'})) return 0.5*(up-down)/ref def leptonFSSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSF']})) up = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSFUp']})) down = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSFDown']})) return 0.5*(up-down)/ref def btaggingSFbSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightBTag_SF']})) up = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightBTag_SF_b_Up']})) down = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightBTag_SF_b_Down']})) return 0.5*(up-down)/ref def btaggingSFlSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightBTag_SF']})) up = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightBTag_SF_l_Up']})) down = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightBTag_SF_l_Down']})) return 0.5*(up-down)/ref def btaggingSFFSSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightBTag_SF']})) up = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightBTag_SF_FS_Up']})) down = self.cachedEstimate(region, channel, setup.sysClone({'reweight':['reweightBTag_SF_FS_Down']})) return 0.5*(up-down)/ref def getBkgSysJobs(self, region, channel, setup): l = [ (region, channel, setup.sysClone({'reweight':['reweightPUUp']})), (region, channel, setup.sysClone({'reweight':['reweightPUDown']})), (region, channel, setup.sysClone({'reweight':['reweightTopPt']})), (region, channel, setup.sysClone({'selectionModifier':'JERUp'})), (region, channel, setup.sysClone({'selectionModifier':'JERDown'})), (region, channel, setup.sysClone({'selectionModifier':'JECUp'})), (region, channel, setup.sysClone({'selectionModifier':'JECDown'})), (region, channel, setup.sysClone({'reweight':['reweightBTag_SF']})), (region, channel, setup.sysClone({'reweight':['reweightBTag_SF_b_Up']})), (region, channel, setup.sysClone({'reweight':['reweightBTag_SF_b_Down']})), (region, channel, setup.sysClone({'reweight':['reweightBTag_SF_l_Up']})), (region, channel, setup.sysClone({'reweight':['reweightBTag_SF_l_Down']})), ] return l def getSigSysJobs(self, region, channel, setup, isFastSim = False): l = self.getBkgSysJobs(region = region, channel = channel, setup = setup) if isFastSim: l.extend( [\ (region, channel, setup.sysClone({'reweight':['reweightBTag_SF_FS_Up']})), (region, channel, setup.sysClone({'reweight':['reweightBTag_SF_FS_Down']})), (region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSF']})), (region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSFUp']})), (region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSFDown']})), ] ) return l
class SystematicBaseClass: __metaclass__ = abc.ABCMeta def __init__(self, name, cacheDir=None): self.name = name self.initCache(cacheDir) def initCache(self, cacheDir): if cacheDir: self.cacheDir = cacheDir cacheFileName = os.path.join(cacheDir, self.name + ".pkl") if not os.path.exists(os.path.dirname(cacheFileName)): os.makedirs(os.path.dirname(cacheFileName)) self.cache = Cache(cacheFileName, verbosity=2) else: self.cache = None def uniqueKey(self, region, channel, setup): return ( region, channel, json.dumps(setup.sys, sort_keys=True), json.dumps(setup.parameters, sort_keys=True), json.dumps(setup.lumi, sort_keys=True), ) def cachedEstimate(self, region, channel, setup, save=True): key = self.uniqueKey(region, channel, setup) if self.cache and self.cache.contains(key): res = self.cache.get(key) if setup.verbose: print "Loading cached %s result for %r : %r" % (self.name, key, res) return res elif self.cache: return self.cache.add(key, self._estimate(region, channel, setup), save=save) else: return self._estimate(region, channel, setup) @abc.abstractmethod def _estimate(self, region, channel, setup): """Estimate yield in 'region' using setup""" return def PUSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate(region, channel, setup.sysClone({"weight": "weightPUUp"})) down = self.cachedEstimate(region, channel, setup.sysClone({"weight": "weightPUDown"})) return 0.5 * (up - down) / ref def topPtSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate(region, channel, setup.sysClone({"reweight": ["reweightTopPt"]})) return 0.5 * (up - ref) / ref def JERSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate(region, channel, setup.sysClone({"selectionModifier": "JERUp"})) down = self.cachedEstimate(region, channel, setup.sysClone({"selectionModifier": "JERDown"})) return 0.5 * (up - down) / ref def JECSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate(region, channel, setup.sysClone({"selectionModifier": "JECUp"})) down = self.cachedEstimate(region, channel, setup.sysClone({"selectionModifier": "JECDown"})) return 0.5 * (up - down) / ref def leptonFSSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup.sysClone({"reweight": ["reweightLeptonFastSimSF"]})) up = self.cachedEstimate(region, channel, setup.sysClone({"reweight": ["reweightLeptonFastSimSFUp"]})) down = self.cachedEstimate(region, channel, setup.sysClone({"reweight": ["reweightLeptonFastSimSFDown"]})) return 0.5 * (up - down) / ref def btaggingSFbSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup.sysClone({"useBTagWeights": "SF"})) up = self.cachedEstimate(region, channel, setup.sysClone({"useBTagWeights": "SF_b_Up"})) down = self.cachedEstimate(region, channel, setup.sysClone({"useBTagWeights": "SF_b_Down"})) return 0.5 * (up - down) / ref def btaggingSFlSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup.sysClone({"useBTagWeights": "SF"})) up = self.cachedEstimate(region, channel, setup.sysClone({"useBTagWeights": "SF_l_Up"})) down = self.cachedEstimate(region, channel, setup.sysClone({"useBTagWeights": "SF_l_Down"})) return 0.5 * (up - down) / ref def btaggingSFFSSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup.sysClone({"useBTagWeights": "SF"})) up = self.cachedEstimate(region, channel, setup.sysClone({"useBTagWeights": "SF_FS_Up"})) down = self.cachedEstimate(region, channel, setup.sysClone({"useBTagWeights": "SF_FS_Down"})) return 0.5 * (up - down) / ref def getBkgSysJobs(self, region, channel, setup): return [ (region, channel, setup.sysClone({"weight": "weightPUUp"})), (region, channel, setup.sysClone({"weight": "weightPUDown"})), (region, channel, setup.sysClone({"reweight": ["reweightTopPt"]})), (region, channel, setup.sysClone({"selectionModifier": "JERUp"})), (region, channel, setup.sysClone({"selectionModifier": "JERDown"})), (region, channel, setup.sysClone({"selectionModifier": "JECUp"})), (region, channel, setup.sysClone({"selectionModifier": "JECDown"})), # (region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSF']})), # (region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSFUp']})), # (region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSFDown']})), (region, channel, setup.sysClone({"useBTagWeights": "SF"})), (region, channel, setup.sysClone({"useBTagWeights": "SF_b_Up"})), (region, channel, setup.sysClone({"useBTagWeights": "SF_b_Down"})), (region, channel, setup.sysClone({"useBTagWeights": "SF"})), (region, channel, setup.sysClone({"useBTagWeights": "SF_l_Up"})), (region, channel, setup.sysClone({"useBTagWeights": "SF_l_Down"})), (region, channel, setup.sysClone({"useBTagWeights": "SF"})), # (region, channel, setup.sysClone({'useBTagWeights':'SF_FS_Up'})), # (region, channel, setup.sysClone({'useBTagWeights':'SF_FS_Down'})), ] def getSigSysJobs(self, region, channel, setup): return [ (region, channel, setup.sysClone({"reweight": ["reweightLeptonFastSimSF"]})), (region, channel, setup.sysClone({"reweight": ["reweightLeptonFastSimSFUp"]})), (region, channel, setup.sysClone({"reweight": ["reweightLeptonFastSimSFDown"]})), (region, channel, setup.sysClone({"useBTagWeights": "SF_FS_Up"})), (region, channel, setup.sysClone({"useBTagWeights": "SF_FS_Down"})), ]
class SystematicBaseClass: __metaclass__ = abc.ABCMeta def __init__(self, name, cacheDir=None): self.name = name self.initCache(cacheDir) def initCache(self, cacheDir): if cacheDir: self.cacheDir = cacheDir cacheFileName = os.path.join(cacheDir, self.name + '.pkl') if not os.path.exists(os.path.dirname(cacheFileName)): os.makedirs(os.path.dirname(cacheFileName)) self.cache = Cache(cacheFileName, verbosity=2) else: self.cache = None def uniqueKey(self, region, channel, setup): return region, channel, json.dumps( setup.sys, sort_keys=True), json.dumps(setup.parameters, sort_keys=True), json.dumps( setup.lumi, sort_keys=True) def cachedEstimate(self, region, channel, setup, save=True): key = self.uniqueKey(region, channel, setup) if self.cache and self.cache.contains(key): res = self.cache.get(key) if setup.verbose: print "Loading cached %s result for %r : %r" % (self.name, key, res) return res elif self.cache: return self.cache.add(key, self._estimate(region, channel, setup), save=save) else: return self._estimate(region, channel, setup) @abc.abstractmethod def _estimate(self, region, channel, setup): '''Estimate yield in 'region' using setup''' return def PUSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate(region, channel, setup.sysClone({'weight': 'weightPUUp'})) down = self.cachedEstimate(region, channel, setup.sysClone({'weight': 'weightPUDown'})) return 0.5 * (up - down) / ref def topPtSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate( region, channel, setup.sysClone({'reweight': ['reweightTopPt']})) return 0.5 * (up - ref) / ref def JERSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate( region, channel, setup.sysClone({'selectionModifier': 'JERUp'})) down = self.cachedEstimate( region, channel, setup.sysClone({'selectionModifier': 'JERDown'})) return 0.5 * (up - down) / ref def JECSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup) up = self.cachedEstimate( region, channel, setup.sysClone({'selectionModifier': 'JECUp'})) down = self.cachedEstimate( region, channel, setup.sysClone({'selectionModifier': 'JECDown'})) return 0.5 * (up - down) / ref def leptonFSSystematic(self, region, channel, setup): ref = self.cachedEstimate( region, channel, setup.sysClone({'reweight': ['reweightLeptonFastSimSF']})) up = self.cachedEstimate( region, channel, setup.sysClone({'reweight': ['reweightLeptonFastSimSFUp']})) down = self.cachedEstimate( region, channel, setup.sysClone({'reweight': ['reweightLeptonFastSimSFDown']})) return 0.5 * (up - down) / ref def btaggingSFbSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup.sysClone({'useBTagWeights': 'SF'})) up = self.cachedEstimate(region, channel, setup.sysClone({'useBTagWeights': 'SF_b_Up'})) down = self.cachedEstimate( region, channel, setup.sysClone({'useBTagWeights': 'SF_b_Down'})) return 0.5 * (up - down) / ref def btaggingSFlSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup.sysClone({'useBTagWeights': 'SF'})) up = self.cachedEstimate(region, channel, setup.sysClone({'useBTagWeights': 'SF_l_Up'})) down = self.cachedEstimate( region, channel, setup.sysClone({'useBTagWeights': 'SF_l_Down'})) return 0.5 * (up - down) / ref def btaggingSFFSSystematic(self, region, channel, setup): ref = self.cachedEstimate(region, channel, setup.sysClone({'useBTagWeights': 'SF'})) up = self.cachedEstimate( region, channel, setup.sysClone({'useBTagWeights': 'SF_FS_Up'})) down = self.cachedEstimate( region, channel, setup.sysClone({'useBTagWeights': 'SF_FS_Down'})) return 0.5 * (up - down) / ref def getBkgSysJobs(self, region, channel, setup): return [ (region, channel, setup.sysClone({'weight': 'weightPUUp'})), (region, channel, setup.sysClone({'weight': 'weightPUDown'})), (region, channel, setup.sysClone({'reweight': ['reweightTopPt']})), (region, channel, setup.sysClone({'selectionModifier': 'JERUp'})), (region, channel, setup.sysClone({'selectionModifier': 'JERDown'})), (region, channel, setup.sysClone({'selectionModifier': 'JECUp'})), (region, channel, setup.sysClone({'selectionModifier': 'JECDown'})), # (region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSF']})), # (region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSFUp']})), # (region, channel, setup.sysClone({'reweight':['reweightLeptonFastSimSFDown']})), (region, channel, setup.sysClone({'useBTagWeights': 'SF'})), (region, channel, setup.sysClone({'useBTagWeights': 'SF_b_Up'})), (region, channel, setup.sysClone({'useBTagWeights': 'SF_b_Down'})), (region, channel, setup.sysClone({'useBTagWeights': 'SF'})), (region, channel, setup.sysClone({'useBTagWeights': 'SF_l_Up'})), (region, channel, setup.sysClone({'useBTagWeights': 'SF_l_Down'})), (region, channel, setup.sysClone({'useBTagWeights': 'SF'})), # (region, channel, setup.sysClone({'useBTagWeights':'SF_FS_Up'})), # (region, channel, setup.sysClone({'useBTagWeights':'SF_FS_Down'})), ] def getSigSysJobs(self, region, channel, setup): return [ (region, channel, setup.sysClone({'reweight': ['reweightLeptonFastSimSF']})), (region, channel, setup.sysClone({'reweight': ['reweightLeptonFastSimSFUp']})), (region, channel, setup.sysClone({'reweight': ['reweightLeptonFastSimSFDown']})), (region, channel, setup.sysClone({'useBTagWeights': 'SF_FS_Up'})), (region, channel, setup.sysClone({'useBTagWeights': 'SF_FS_Down'})), ]
class DataDrivenTTZEstimate(SystematicEstimator): def __init__(self, name, cacheDir=None, useTop16009=False): super(DataDrivenTTZEstimate, self).__init__(name, cacheDir=cacheDir) self.nJets = (3,-1) # jet selection (min, max) self.nLooseBTags = (2,-1) # loose bjet selection (min, max) self.nMediumBTags = (0,-1) # bjet selection (min, max) self.useTop16009 = useTop16009 self.ratioTop16009 = 1.27 # self.sysErrTop16009 = (-0.17, +0.20) self.statErrTop16009 = (-0.37, +0.42) # Because we are going to reuse a lot of yields which otherwise will be terribly slow self.helperCacheName = os.path.join('.', 'helperCache.pkl') self.helperCache = Cache(self.helperCacheName, verbosity=2) def yieldFromCache(self, setup, sample, channel, selectionString, weightString): s = (sample, channel, selectionString, weightString) if self.helperCache.contains(s): return self.helperCache.get(s) else: yieldFromDraw = u_float(**setup.sample[sample][channel].getYieldFromDraw(selectionString, weightString)) self.helperCache.add(s, yieldFromDraw, save=True) return yieldFromDraw #Concrete implementation of abstract method 'estimate' as defined in Systematic def _estimate(self, region, channel, setup): logger.info("Data-driven TTZ estimate for region " + str(region) + " in channel " + channel + " and setup " + str(setup.sys) + ":") #Sum of all channels for 'all' if channel=='all': estimate = sum([self.cachedEstimate(region, c, setup) for c in ['MuMu', 'EE', 'EMu']]) else: zWindow= 'allZ' if channel=='EMu' else 'offZ' preSelection = setup.preselection('MC', zWindow=zWindow, channel=channel) MC_2l = "&&".join([region.cutString(setup.sys['selectionModifier']), preSelection['cut']]) weight = setup.weightString() logger.info("weight: %s", weight) yield_ttZ_2l = setup.lumi[channel]/1000.*self.yieldFromCache(setup, 'TTZ', channel, MC_2l, weight) logger.info("yield_MC_2l: %s"%yield_ttZ_2l) if self.useTop16009: sysError = max((abs(x) for x in self.sysErrTop16009)) # not sure yet to handle assymetric errors statError = max((abs(x) for x in self.statErrTop16009)) error = sqrt(sysError*sysError+statError*statError) return u_float(self.ratioTop16009, error)*yield_ttZ_2l else: # pt leptons > 30, 20, 10 GeV useTrigger = False # setup.parameters['useTriggers'] # better not to use three lepton triggers, seems to be too inefficient lllSelection = {} lllSelection['MuMu'] = "&&".join([getLeptonString(3, 0), getPtThresholdString(30, 20, 10)]) + ("&&HLT_3mu" if useTrigger else "") lllSelection['MuMuE'] = "&&".join([getLeptonString(2, 1), getPtThresholdString(30, 20, 10)]) + ("&&HLT_2mu1e" if useTrigger else "") lllSelection['MuEE'] = "&&".join([getLeptonString(1, 2), getPtThresholdString(30, 20, 10)]) + ("&&HLT_2e1mu" if useTrigger else "") lllSelection['EE'] = "&&".join([getLeptonString(0, 3), getPtThresholdString(30, 20, 10)]) + ("&&HLT_3e" if useTrigger else "") lllSelection['EMu'] = "(("+lllSelection['MuMuE']+")||("+lllSelection['MuEE']+"))" bJetSelectionM = "(Sum$(JetGood_pt>30&&abs(JetGood_eta)<2.4&&JetGood_id&&JetGood_btagCSV>0.890))" bJetSelectionL = "(Sum$(JetGood_pt>30&&abs(JetGood_eta)<2.4&&JetGood_id&&JetGood_btagCSV>0.605))" zMassSelection = "abs(mlmZ_mass-91.1876)<10" # Start from base hadronic selection and add loose b-tag and Z-mass requirement selection = {} for dataOrMC in ["Data", "MC"]: selection[dataOrMC] = setup.selection(dataOrMC, hadronicSelection = True, **setup.defaultParameters(update={'nJets': self.nJets, 'nBTags':self.nMediumBTags, 'metMin': 0., 'metSigMin':0., 'dPhiJetMet':0. }))['cut'] selection[dataOrMC] += "&&" + bJetSelectionL+">="+str(self.nLooseBTags[0]) selection[dataOrMC] += "&&" + zMassSelection logger.info("Selection " + dataOrMC + ": " + selection[dataOrMC]) # Calculate yields (take together channels together) channels = ['MuMu','EMu','EE'] yield_ttZ_3l = sum(self.yieldFromCache(setup, 'TTZ', c, "&&".join([lllSelection[c], selection["MC"]]), weight)*setup.dataLumi[channel]/1000 for c in ['MuMu','EMu','EE']) yield_other = sum(self.yieldFromCache(setup, s, c, "&&".join([lllSelection[c], selection["MC"]]), weight)*setup.dataLumi[channel]/1000 for c in ['MuMu','EMu','EE'] for s in ['TTJets', 'DY', 'other']) yield_data_3l = sum(self.yieldFromCache(setup, 'Data', c, "&&".join([lllSelection[c], selection["Data"]]), "(1)") for c in ['MuMu','EMu','EE']) if not yield_ttZ_3l > 0: logger.warn("No yield for 3l selection") estimate = u_float(0, 0) yield_ttZ_data = yield_data_3l - yield_other if yield_ttZ_data < 0: logger.warn("Data-driven ratio is negative!") yield_ttZ_data = u_float(0, 0) logger.info("Control region predictions: ") logger.info(" data: " + str(yield_data_3l)) logger.info(" MC other: " + str(yield_other)) logger.info(" TTZ (MC): " + str(yield_ttZ_3l)) logger.info(" TTZ (data): " + str(yield_ttZ_data)) logger.info(" TTZ (ratio): " + str(yield_ttZ_data/yield_ttZ_3l)) estimate = (yield_ttZ_data/yield_ttZ_3l)*yield_ttZ_2l logger.info(" --> " + str(estimate)) return estimate