Beispiel #1
0
    def _estimate(self, region, channel, setup):
        if setup.verbose:
            printHeader("MC prediction for %s channel %s" %
                        (self.name, channel))

        if channel == 'all':
            return sum([
                self.cachedEstimate(region, c, setup)
                for c in ['MuMu', 'EE', 'EMu']
            ], u_float(0., 0.))
        else:
            preSelection = setup.preselection('MC', channel=channel)
            cut = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                preSelection['cut']
            ])
            weight = preSelection['weightStr']

            if setup.verbose:
                print "Using cut %s and weight %s" % (cut, weight)
            if not self.sample[channel].has_key('chain'):
                loadChain(self.sample[channel])
            return setup.lumi[channel] / 1000. * u_float(
                getYieldFromChain(self.sample[channel]['chain'],
                                  cutString=cut,
                                  weight=weight,
                                  returnError=True))
    def _estimate(self, region, channel, setup):
        ''' Concrete implementation of abstract method 'estimate' as defined in Systematic
        '''

        logger.debug("MC prediction for %s channel %s" % (self.name, channel))

        if channel == 'all':
            # 'all' is the total of all contributions
            return sum([
                self.cachedEstimate(region, c, setup)
                for c in ['MuMu', 'EE', 'EMu']
            ])

        else:

            # Important! We use 'allZ' (mll>20) in case of EMu
            zWindow = 'allZ' if channel == 'EMu' else 'offZ'

            preSelection = setup.preselection('MC',
                                              zWindow=zWindow,
                                              channel=channel)
            cut = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                preSelection['cut']
            ])
            weight = preSelection['weightStr']

            logger.debug("Using cut %s and weight %s" % (cut, weight))

            return setup.lumi[channel] / 1000. * u_float(
                **self.sample[channel].getYieldFromDraw(selectionString=cut,
                                                        weightString=weight))
    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
 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
Beispiel #5
0
  def _estimate(self, region, channel, setup):
    if setup.verbose: printHeader("MC prediction for %s channel %s" %(self.name, channel))

    if channel=='all':
      return sum( [ self.cachedEstimate(region, c, setup) for c in ['MuMu', 'EE', 'EMu'] ], u_float(0., 0.) )
    else:
      preSelection = setup.preselection('MC', channel=channel)
      cut = "&&".join([region.cutString(setup.sys['selectionModifier']), preSelection['cut']])
      weight = preSelection['weightStr']

      if setup.verbose: 
        print "Using cut %s and weight %s"%(cut, weight)
      if not self.sample[channel].has_key('chain'):
        loadChain(self.sample[channel])
      return setup.lumi[channel]/1000.*u_float(getYieldFromChain(self.sample[channel]['chain'], cutString = cut, weight=weight, returnError = True) )
Beispiel #6
0
  def _estimate(self, region, channel, setup):

    printHeader("DD DY prediction for %s channel %s" %(self.name, channel))

    #Sum of all channels for 'all'
    if channel=='all':
      return sum( [ self.cachedEstimate(region, c, setup) for c in ['MuMu', 'EE', 'EMu'] ], u_float(0.,0.) )

    #MC based for 'EMu'
    elif channel=='EMu':
      preSelection = setup.preselection('MC', zWindow="allZ", channel=channel)
      cut = "&&".join([region.cutString(setup.sys['selectionModifier']), preSelection['cut'] ])
      weight = preSelection['weightStr']

      if setup.verbose: 
        print "Using cut %s and weight %s"%(cut, weight)
      return setup.lumi[channel]/1000. * u_float( getYieldFromChain(setup.sample['DY'][channel]['chain'], cutString = cut, weight=weight, returnError = True) )

    #Data driven for EE and MuMu
    else:
      preSelection = setup.preselection('MC', zWindow="offZ", channel=channel)
      weight = preSelection['weightStr']

      assert abs(1.-setup.lumi[channel]/setup.sample['Data'][channel]['lumi'])<0.01, "Lumi specified in setup %f does not match lumi in data sample %f in channel %s"%(setup.lumi[channel], setup.sample['Data'][channel]['lumi'], channel)
      cut_offZ_1b = "&&".join([region.cutString(setup.sys['selectionModifier']), setup.selection('MC', channel=channel, zWindow = 'offZ', **setup.defaultParameters(update={'nBTags':(1,-1)}))['cut'] ])
      cut_onZ_1b  = "&&".join([region.cutString(setup.sys['selectionModifier']), setup.selection('MC', channel=channel, zWindow = 'onZ',  **setup.defaultParameters(update={'nBTags':(1,-1)}))['cut'] ])
      cut_onZ_0b  = "&&".join([region.cutString(setup.sys['selectionModifier']), setup.selection('MC', channel=channel, zWindow = 'onZ',  **setup.defaultParameters(update={'nBTags':(0,0)}))['cut'] ])
      cut_data_onZ_0b    = "&&".join([region.cutString(setup.sys['selectionModifier']), setup.selection('Data', channel=channel, zWindow = 'onZ',  **setup.defaultParameters(update={'nBTags':(0,0)}) )['cut'] ])
  #    R1 = DY-MC (offZ, 1b) / DY-MC (onZ, 1b)
  #    R2 = DY-MC (onZ, 1b) / DY-MC (onZ, 0b) 
  #    DY-est = R1*R2*(Data(2l, onZ, 0b) - EWK(onZ, 0b)) = DY-MC (offZ, 1b) / DY-MC (onZ, 0b) *( Data(2l, onZ, 0b) - EWK(onZ, 0b))
      
      yield_offZ_1b = setup.lumi[channel]/1000.*u_float( getYieldFromChain(setup.sample['DY'][channel]['chain'], cutString = cut_offZ_1b, weight=weight, returnError = True))
      if setup.verbose: print "yield_offZ_1b: %s"%yield_offZ_1b 
      yield_onZ_0b  = setup.lumi[channel]/1000.*u_float( getYieldFromChain(setup.sample['DY'][channel]['chain'], cutString = cut_onZ_0b,  weight=weight, returnError = True))
      if setup.verbose: print "yield_onZ_0b: %s"%yield_onZ_0b 
      yield_data    = u_float( getYieldFromChain(setup.sample['Data'][channel]['chain'], cutString = cut_data_onZ_0b,  weight=weight, returnError = True))
      if setup.verbose: print "yield_data: %s (for cut: %s \n with weight: %s)"%(yield_data, cut_data_onZ_0b, weight) 

      #electroweak subtraction
      print "\n Substracting electroweak backgrounds from data: \n"
      yield_other = u_float(0., 0.) 
      for s in ['TTJets' , 'TTZ' , 'other']:
        yield_other += setup.lumi[channel]/1000.*u_float(getYieldFromChain(setup.sample[s][channel]['chain'], cutString = cut_onZ_0b,  weight=weight, returnError=True))
        if setup.verbose: print "yield_other_onZ_0b %s added, now: %s"%(s, yield_other)
      
      normRegYield = yield_data - yield_other
      if normRegYield.val<0: print "\n !!!Warning!!! \n Negative normalization region yield data: (%s), MC: (%s) \n"%(yield_data, yield_other)
      
      mcRatio = yield_offZ_1b / yield_onZ_0b
      res = mcRatio * normRegYield

      print "mcRatio is: ", mcRatio

      return res
    def _estimate(self, region, channel, setup):

        ''' Concrete implementation of abstract method 'estimate' as defined in Systematic
        '''

        logger.debug( "MC prediction for %s channel %s" %(self.name, channel) )

        if channel=='all':
            # 'all' is the total of all contributions
            return sum( [ self.cachedEstimate(region, c, setup) for c in ['MuMu', 'EE', 'EMu'] ], u_float(0., 0.) )

        else:

            # Important! We use 'allZ' (mll>20) in case of EMu 
            zWindow= 'allZ' if channel=='EMu' else 'offZ'

            preSelection = setup.preselection('MC', zWindow=zWindow, channel=channel)
            cut = "&&".join([region.cutString(setup.sys['selectionModifier']), preSelection['cut']])
            weight = preSelection['weightStr']

            logger.debug( "Using cut %s and weight %s"%(cut, weight) )

            return setup.lumi[channel]/1000.*u_float(**self.sample[channel].getYieldFromDraw(selectionString = cut, weightString = weight) )
Beispiel #8
0
    def _estimate(self, region, channel, setup):

        printHeader("DD DY prediction for %s channel %s" %
                    (self.name, channel))

        #Sum of all channels for 'all'
        if channel == 'all':
            return sum([
                self.cachedEstimate(region, c, setup)
                for c in ['MuMu', 'EE', 'EMu']
            ], u_float(0., 0.))

        #MC based for 'EMu'
        elif channel == 'EMu':
            preSelection = setup.preselection('MC',
                                              zWindow="allZ",
                                              channel=channel)
            cut = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                preSelection['cut']
            ])
            weight = preSelection['weightStr']

            if setup.verbose:
                print "Using cut %s and weight %s" % (cut, weight)
            return setup.lumi[channel] / 1000. * u_float(
                getYieldFromChain(setup.sample['DY'][channel]['chain'],
                                  cutString=cut,
                                  weight=weight,
                                  returnError=True))

        #Data driven for EE and MuMu
        else:
            preSelection = setup.preselection('MC',
                                              zWindow="offZ",
                                              channel=channel)
            weight = preSelection['weightStr']

            assert abs(
                1. -
                setup.lumi[channel] / setup.sample['Data'][channel]['lumi']
            ) < 0.01, "Lumi specified in setup %f does not match lumi in data sample %f in channel %s" % (
                setup.lumi[channel], setup.sample['Data'][channel]['lumi'],
                channel)
            cut_offZ_1b = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                setup.selection(
                    'MC',
                    channel=channel,
                    zWindow='offZ',
                    **setup.defaultParameters(update={'nBTags': (1,
                                                                 -1)}))['cut']
            ])
            cut_onZ_1b = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                setup.selection(
                    'MC',
                    channel=channel,
                    zWindow='onZ',
                    **setup.defaultParameters(update={'nBTags': (1,
                                                                 -1)}))['cut']
            ])
            cut_onZ_0b = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                setup.selection(
                    'MC',
                    channel=channel,
                    zWindow='onZ',
                    **setup.defaultParameters(update={'nBTags': (0,
                                                                 0)}))['cut']
            ])
            cut_data_onZ_0b = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                setup.selection(
                    'Data',
                    channel=channel,
                    zWindow='onZ',
                    **setup.defaultParameters(update={'nBTags': (0,
                                                                 0)}))['cut']
            ])
            #    R1 = DY-MC (offZ, 1b) / DY-MC (onZ, 1b)
            #    R2 = DY-MC (onZ, 1b) / DY-MC (onZ, 0b)
            #    DY-est = R1*R2*(Data(2l, onZ, 0b) - EWK(onZ, 0b)) = DY-MC (offZ, 1b) / DY-MC (onZ, 0b) *( Data(2l, onZ, 0b) - EWK(onZ, 0b))

            yield_offZ_1b = setup.lumi[channel] / 1000. * u_float(
                getYieldFromChain(setup.sample['DY'][channel]['chain'],
                                  cutString=cut_offZ_1b,
                                  weight=weight,
                                  returnError=True))
            if setup.verbose: print "yield_offZ_1b: %s" % yield_offZ_1b
            yield_onZ_0b = setup.lumi[channel] / 1000. * u_float(
                getYieldFromChain(setup.sample['DY'][channel]['chain'],
                                  cutString=cut_onZ_0b,
                                  weight=weight,
                                  returnError=True))
            if setup.verbose: print "yield_onZ_0b: %s" % yield_onZ_0b
            yield_data = u_float(
                getYieldFromChain(setup.sample['Data'][channel]['chain'],
                                  cutString=cut_data_onZ_0b,
                                  weight=weight,
                                  returnError=True))
            if setup.verbose:
                print "yield_data: %s (for cut: %s \n with weight: %s)" % (
                    yield_data, cut_data_onZ_0b, weight)

            #electroweak subtraction
            print "\n Substracting electroweak backgrounds from data: \n"
            yield_other = u_float(0., 0.)
            for s in ['TTJets', 'TTZ', 'other']:
                yield_other += setup.lumi[channel] / 1000. * u_float(
                    getYieldFromChain(setup.sample[s][channel]['chain'],
                                      cutString=cut_onZ_0b,
                                      weight=weight,
                                      returnError=True))
                if setup.verbose:
                    print "yield_other_onZ_0b %s added, now: %s" % (
                        s, yield_other)

            normRegYield = yield_data - yield_other
            if normRegYield.val < 0:
                print "\n !!!Warning!!! \n Negative normalization region yield data: (%s), MC: (%s) \n" % (
                    yield_data, yield_other)

            mcRatio = yield_offZ_1b / yield_onZ_0b
            res = mcRatio * normRegYield

            print "mcRatio is: ", mcRatio

            return res
    def _estimate(self, region, channel, setup):

        # Sum of all channels for 'all'
        if channel == "all":
            estimate = sum([self.cachedEstimate(region, c, setup) for c in ["MuMu", "EE", "EMu"]])

        # MC based for 'EMu'
        elif channel == "EMu":
            weight = setup.weightString()
            preSelection = setup.preselection("MC", zWindow="allZ", channel=channel)
            cut = "&&".join([region.cutString(setup.sys["selectionModifier"]), preSelection["cut"]])
            estimate = (
                setup.lumi[channel]
                / 1000.0
                * u_float(**setup.sample["DY"][channel].getYieldFromDraw(selectionString=cut, weightString=weight))
            )

        # Data driven for EE and MuMu (calculate for data luminosity)
        else:
            weight = setup.weightString()

            cut_offZ_1b = "&&".join(
                [
                    region.cutString(setup.sys["selectionModifier"]),
                    setup.selection(
                        "MC", channel=channel, zWindow="offZ", **setup.defaultParameters(update={"nBTags": (1, -1)})
                    )["cut"],
                ]
            )
            cut_onZ_0b = "&&".join(
                [
                    region.cutString(setup.sys["selectionModifier"]),
                    setup.selection(
                        "MC", channel=channel, zWindow="onZ", **setup.defaultParameters(update={"nBTags": (0, 0)})
                    )["cut"],
                ]
            )
            cut_data_onZ_0b = "&&".join(
                [
                    region.cutString(),
                    setup.selection(
                        "Data", channel=channel, zWindow="onZ", **setup.defaultParameters(update={"nBTags": (0, 0)})
                    )["cut"],
                ]
            )

            # Calculate ratio (offZ,1b)/(onZ,0b)
            yield_offZ_1b = (
                u_float(
                    **setup.sample["DY"][channel].getYieldFromDraw(selectionString=cut_offZ_1b, weightString=weight)
                )
                * setup.dataLumi[channel]
                / 1000
            )
            yield_onZ_0b = (
                u_float(**setup.sample["DY"][channel].getYieldFromDraw(selectionString=cut_onZ_0b, weightString=weight))
                * setup.dataLumi[channel]
                / 1000
            )
            R = yield_offZ_1b / yield_onZ_0b if yield_onZ_0b > 0 else 0

            # Calculate data-other onZ for 0 b-jets region
            yield_data = u_float(
                **setup.sample["Data"][channel].getYieldFromDraw(selectionString=cut_data_onZ_0b, weightString="(1)")
            )
            yield_other = (
                sum(
                    u_float(
                        **setup.sample[s][channel].getYieldFromDraw(selectionString=cut_onZ_0b, weightString=weight)
                    )
                    for s in ["TTJets", "TTZ", "other"]
                )
                * setup.dataLumi[channel]
                / 1000
            )
            normRegYield = yield_data - yield_other

            # Calculate DY estimate in 1 b-jet region (and scale back to MC lumi)
            estimate = R * normRegYield * setup.lumi[channel] / setup.dataLumi[channel]

            logger.info(
                "Calculating data-driven DY estimate in channel "
                + channel
                + " using lumi "
                + str(setup.dataLumi[channel])
                + ":"
            )
            logger.info("yield DY offZ/1b:          " + str(yield_offZ_1b))
            logger.info("yield DY onZ/0b:           " + str(yield_onZ_0b))
            logger.info("R:                         " + str(R))
            logger.info("yield data onZ/0b:         " + str(yield_data))
            logger.info("yield other onZ/0b:        " + str(yield_other))
            logger.info("yield (data-other) onZ/0b: " + str(normRegYield))
            logger.info("yield expected DY  onZ/1b: " + str(normRegYield * R))
            if normRegYield < 0 and yield_data > 0:
                logger.warn("Negative normalization region yield!")

        logger.info(
            "Estimate for DY in "
            + channel
            + " channel (lumi="
            + str(setup.lumi[channel])
            + "/pb): "
            + str(estimate)
            + (" (negative estimated being replaced by 0)" if estimate < 0 else "")
        )
        return estimate if estimate > 0 else u_float(0, 0)
Beispiel #10
0
  def _estimate(self, region, channel, setup):
    printHeader("DD TTZ prediction for '%s' channel %s" %(self.name, channel))

    #Sum of all channels for 'all'
    if channel=='all':
      return sum( [ self.cachedEstimate(region, c, channel, setup) for c in ['MuMu', 'EE', 'EMu'] ] )
    else:
      #Data driven for EE, EMu and  MuMu. 
      preSelection = setup.preselection('MC', channel=channel)

      #check lumi consistency
      assert abs(1.-setup.lumi[channel]/setup.sample['Data'][channel]['lumi'])<0.01, "Lumi specified in setup %f does not match lumi in data sample %f in channel %s"%(setup.lumi[channel], setup.sample['Data'][channel]['lumi'], channel)
      selection_MC_2l = "&&".join([region.cutString(setup.sys['selectionModifier']), preSelection['cut']])
      weight = preSelection['weightStr']

      yield_MC_2l =  setup.lumi[channel]/1000.*u_float(getYieldFromChain(setup.sample['TTZ'][channel]['chain'], cutString = selection_MC_2l, weight=weight, returnError = True) )
      if setup.verbose: print "yield_MC_2l: %s"%yield_MC_2l 
      
      muonSelection_loosePt = looseMuIDString(ptCut=10)
      electronSelection_loosePt = looseEleIDString(ptCut=10)
      
      #mu_mu_mu
      MuMuMuSelection = "nGoodMuons>=2" + '&&' + muonSelection_loosePt + "==3"
      if setup.parameters['useTriggers']: MuMuMuSelection += '&&HLT_3mu'
      #e_e_e
      EEESelection = "nGoodElectrons>=2" + '&&' + electronSelection_loosePt + "==3"
      if setup.parameters['useTriggers']: EEESelection += '&&HLT_3e'
      #e_e_mu
      EEMuSelection = "(nGoodMuons+nGoodElectrons)>=2" + "&&" + electronSelection_loosePt + "==2&&" + muonSelection_loosePt + "==1" 
      if setup.parameters['useTriggers']: EEMuSelection += '&&HLT_2e1mu'
      #mu_mu_e
      MuMuESelection = "(nGoodMuons+nGoodElectrons)>=2" + "&&" + electronSelection_loosePt + "==1&&" + muonSelection_loosePt + "==2" 
      if setup.parameters['useTriggers']: MuMuESelection += '&&HLT_2mu1e'
      
      MC_hadronSelection    = setup.selection('MC', hadronicSelection = True, 
          **setup.defaultParameters(update={'nJets': self.nJets, 'nBTags':self.nMediumBTags, 'metMin': 0., 'metSigMin':0., 'dPhiJetMet':0. })
        )['cut']
      data_hadronSelection  = setup.selection('Data', hadronicSelection = True, 
          **setup.defaultParameters(update={'nJets': self.nJets, 'nBTags':self.nMediumBTags, 'metMin': 0., 'metSigMin':0., 'dPhiJetMet':0. })
        )['cut']

      #loose bjet selection added here
      if self.nLooseBTags[0]>=0:  
        MC_hadronSelection += '&&Sum$(Jet_pt>30&&abs(Jet_eta)<2.4&&Jet_id&&Jet_btagCSV>0.605)>='+str(self.nLooseBTags[0])
        data_hadronSelection += '&&Sum$(Jet_pt>30&&abs(Jet_eta)<2.4&&Jet_id&&Jet_btagCSV>0.605)>='+str(self.nLooseBTags[0])
      if self.nLooseBTags[1]>=0:  
        MC_hadronSelection += '&&Sum$(Jet_pt>30&&abs(Jet_eta)<2.4&&Jet_id&&Jet_btagCSV>0.605)<='+str(self.nLooseBTags[1])
        data_hadronSelection += '&&Sum$(Jet_pt>30&&abs(Jet_eta)<2.4&&Jet_id&&Jet_btagCSV>0.605)<='+str(self.nLooseBTags[1])

      MC_MuMuMu = "&&".join([
        MC_hadronSelection,
        MuMuMuSelection,
        "abs(mlmZ_mass-91.2)<10"
      ])
      MC_EEE = "&&".join([
        MC_hadronSelection,
        EEESelection,
        "abs(mlmZ_mass-91.2)<10"
      ])
      MC_EEMu = "&&".join([
        MC_hadronSelection,
        EEMuSelection,
        "abs(mlmZ_mass-91.2)<10"
      ])
      MC_MuMuE = "&&".join([
        MC_hadronSelection,
        MuMuESelection,
        "abs(mlmZ_mass-91.2)<10"
      ])

      MC_3l = "(("+MC_MuMuMu+")||("+MC_EEE+")||("+MC_EEMu+")||("+MC_MuMuE+"))"
      
      data_MuMuMu = "&&".join([
        data_hadronSelection,
        MuMuMuSelection,
        "abs(mlmZ_mass-91.2)<10"
      ])
      data_EEE = "&&".join([
        data_hadronSelection,
        EEESelection,
        "abs(mlmZ_mass-91.2)<10"
      ])
      data_EEMu = "&&".join([
        data_hadronSelection,
        EEMuSelection,
        "abs(mlmZ_mass-91.2)<10"
      ])
      data_MuMuE = "&&".join([
        data_hadronSelection,
        MuMuESelection,
        "abs(mlmZ_mass-91.2)<10"
      ])


      ######yield_MC_3l computed for ALL channels but lumi changes slightly here depending on channel
      yield_MC_3l = setup.lumi[channel]/1000.*u_float( getYieldFromChain(setup.sample['TTZ'][channel]['chain'], cutString = MC_3l, weight=weight, returnError = True))
      if setup.verbose: print "yield_MC_looseSelection_3l: %s"%yield_MC_3l 
      yield_data_MuMuMu = u_float( getYieldFromChain(setup.sample['Data']['MuMu']['chain'], cutString = data_MuMuMu, weight=weight, returnError = True))
      if setup.verbose: print "yield_data_looseSelection_MuMuMu: %s"%yield_data_MuMuMu
      yield_data_EEE = u_float( getYieldFromChain(setup.sample['Data']['EE']['chain'], cutString = data_EEE, weight=weight, returnError = True))
      if setup.verbose: print "yield_data_looseSelection_EEE: %s"%yield_data_EEE
      yield_data_EMu = u_float( getYieldFromChain(setup.sample['Data']['EMu']['chain'], cutString = "(("+data_MuMuE+')||('+data_EEMu+'))', weight=weight, returnError = True))
      if setup.verbose: print "yield_data_looseSelection_EMu: %s"%yield_data_EMu
      
      yield_data_3l = yield_data_MuMuMu+yield_data_EEE+yield_data_EMu
      if setup.verbose: print "yield_data_3l: %s"%yield_data_3l
      
      #electroweak subtraction
      print "\n Substracting electroweak backgrounds from data: \n"
      yield_other = u_float(0., 0.) 
      for s in ['TTJets' , 'DY', 'other']:
        yield_other+= setup.lumi[channel]/1000.* u_float(getYieldFromChain(setup.sample[s][channel]['chain'], cutString = MC_3l,  weight=weight, returnError=True))
        if setup.verbose: print "yield_looseSelection_other %s added, now: %s"%(s, yield_other)
        
      normRegYield = yield_data_3l - yield_other
      if normRegYield.val<0: print "\n !!!Warning!!! \n Negative normalization region yield data: (%s), MC: (%s) \n"%(yield_data_3l, yield_other)

      print  "normRegYield", normRegYield
      print "\n Control Region predictys ", normRegYield, " TTZ events in data; ", yield_MC_3l, " TTZ events in MC. Ratio ---> ", (normRegYield/yield_MC_3l)
      print "DD-TTZ ---> ", (normRegYield/yield_MC_3l)*yield_MC_2l
      return (normRegYield/yield_MC_3l)*yield_MC_2l
Beispiel #11
0
    def _estimate(self, region, channel, setup):
        printHeader("DD TTZ prediction for '%s' channel %s" %
                    (self.name, channel))

        #Sum of all channels for 'all'
        if channel == 'all':
            return sum([
                self.cachedEstimate(region, c, channel, setup)
                for c in ['MuMu', 'EE', 'EMu']
            ])
        else:
            #Data driven for EE, EMu and  MuMu.
            preSelection = setup.preselection('MC', channel=channel)

            #check lumi consistency
            assert abs(
                1. -
                setup.lumi[channel] / setup.sample['Data'][channel]['lumi']
            ) < 0.01, "Lumi specified in setup %f does not match lumi in data sample %f in channel %s" % (
                setup.lumi[channel], setup.sample['Data'][channel]['lumi'],
                channel)
            selection_MC_2l = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                preSelection['cut']
            ])
            weight = preSelection['weightStr']

            yield_MC_2l = setup.lumi[channel] / 1000. * u_float(
                getYieldFromChain(setup.sample['TTZ'][channel]['chain'],
                                  cutString=selection_MC_2l,
                                  weight=weight,
                                  returnError=True))
            if setup.verbose: print "yield_MC_2l: %s" % yield_MC_2l

            muonSelection_loosePt = looseMuIDString(ptCut=10)
            electronSelection_loosePt = looseEleIDString(ptCut=10)

            #mu_mu_mu
            MuMuMuSelection = "nGoodMuons>=2" + '&&' + muonSelection_loosePt + "==3"
            if setup.parameters['useTriggers']: MuMuMuSelection += '&&HLT_3mu'
            #e_e_e
            EEESelection = "nGoodElectrons>=2" + '&&' + electronSelection_loosePt + "==3"
            if setup.parameters['useTriggers']: EEESelection += '&&HLT_3e'
            #e_e_mu
            EEMuSelection = "(nGoodMuons+nGoodElectrons)>=2" + "&&" + electronSelection_loosePt + "==2&&" + muonSelection_loosePt + "==1"
            if setup.parameters['useTriggers']: EEMuSelection += '&&HLT_2e1mu'
            #mu_mu_e
            MuMuESelection = "(nGoodMuons+nGoodElectrons)>=2" + "&&" + electronSelection_loosePt + "==1&&" + muonSelection_loosePt + "==2"
            if setup.parameters['useTriggers']: MuMuESelection += '&&HLT_2mu1e'

            MC_hadronSelection = setup.selection(
                'MC',
                hadronicSelection=True,
                **setup.defaultParameters(
                    update={
                        'nJets': self.nJets,
                        'nBTags': self.nMediumBTags,
                        'metMin': 0.,
                        'metSigMin': 0.,
                        'dPhiJetMet': 0.
                    }))['cut']
            data_hadronSelection = setup.selection(
                'Data',
                hadronicSelection=True,
                **setup.defaultParameters(
                    update={
                        'nJets': self.nJets,
                        'nBTags': self.nMediumBTags,
                        'metMin': 0.,
                        'metSigMin': 0.,
                        'dPhiJetMet': 0.
                    }))['cut']

            #loose bjet selection added here
            if self.nLooseBTags[0] >= 0:
                MC_hadronSelection += '&&Sum$(Jet_pt>30&&abs(Jet_eta)<2.4&&Jet_id&&Jet_btagCSV>0.605)>=' + str(
                    self.nLooseBTags[0])
                data_hadronSelection += '&&Sum$(Jet_pt>30&&abs(Jet_eta)<2.4&&Jet_id&&Jet_btagCSV>0.605)>=' + str(
                    self.nLooseBTags[0])
            if self.nLooseBTags[1] >= 0:
                MC_hadronSelection += '&&Sum$(Jet_pt>30&&abs(Jet_eta)<2.4&&Jet_id&&Jet_btagCSV>0.605)<=' + str(
                    self.nLooseBTags[1])
                data_hadronSelection += '&&Sum$(Jet_pt>30&&abs(Jet_eta)<2.4&&Jet_id&&Jet_btagCSV>0.605)<=' + str(
                    self.nLooseBTags[1])

            MC_MuMuMu = "&&".join([
                MC_hadronSelection, MuMuMuSelection, "abs(mlmZ_mass-91.2)<10"
            ])
            MC_EEE = "&&".join(
                [MC_hadronSelection, EEESelection, "abs(mlmZ_mass-91.2)<10"])
            MC_EEMu = "&&".join(
                [MC_hadronSelection, EEMuSelection, "abs(mlmZ_mass-91.2)<10"])
            MC_MuMuE = "&&".join(
                [MC_hadronSelection, MuMuESelection, "abs(mlmZ_mass-91.2)<10"])

            MC_3l = "((" + MC_MuMuMu + ")||(" + MC_EEE + ")||(" + MC_EEMu + ")||(" + MC_MuMuE + "))"

            data_MuMuMu = "&&".join([
                data_hadronSelection, MuMuMuSelection, "abs(mlmZ_mass-91.2)<10"
            ])
            data_EEE = "&&".join(
                [data_hadronSelection, EEESelection, "abs(mlmZ_mass-91.2)<10"])
            data_EEMu = "&&".join([
                data_hadronSelection, EEMuSelection, "abs(mlmZ_mass-91.2)<10"
            ])
            data_MuMuE = "&&".join([
                data_hadronSelection, MuMuESelection, "abs(mlmZ_mass-91.2)<10"
            ])

            ######yield_MC_3l computed for ALL channels but lumi changes slightly here depending on channel
            yield_MC_3l = setup.lumi[channel] / 1000. * u_float(
                getYieldFromChain(setup.sample['TTZ'][channel]['chain'],
                                  cutString=MC_3l,
                                  weight=weight,
                                  returnError=True))
            if setup.verbose:
                print "yield_MC_looseSelection_3l: %s" % yield_MC_3l
            yield_data_MuMuMu = u_float(
                getYieldFromChain(setup.sample['Data']['MuMu']['chain'],
                                  cutString=data_MuMuMu,
                                  weight=weight,
                                  returnError=True))
            if setup.verbose:
                print "yield_data_looseSelection_MuMuMu: %s" % yield_data_MuMuMu
            yield_data_EEE = u_float(
                getYieldFromChain(setup.sample['Data']['EE']['chain'],
                                  cutString=data_EEE,
                                  weight=weight,
                                  returnError=True))
            if setup.verbose:
                print "yield_data_looseSelection_EEE: %s" % yield_data_EEE
            yield_data_EMu = u_float(
                getYieldFromChain(setup.sample['Data']['EMu']['chain'],
                                  cutString="((" + data_MuMuE + ')||(' +
                                  data_EEMu + '))',
                                  weight=weight,
                                  returnError=True))
            if setup.verbose:
                print "yield_data_looseSelection_EMu: %s" % yield_data_EMu

            yield_data_3l = yield_data_MuMuMu + yield_data_EEE + yield_data_EMu
            if setup.verbose: print "yield_data_3l: %s" % yield_data_3l

            #electroweak subtraction
            print "\n Substracting electroweak backgrounds from data: \n"
            yield_other = u_float(0., 0.)
            for s in ['TTJets', 'DY', 'other']:
                yield_other += setup.lumi[channel] / 1000. * u_float(
                    getYieldFromChain(setup.sample[s][channel]['chain'],
                                      cutString=MC_3l,
                                      weight=weight,
                                      returnError=True))
                if setup.verbose:
                    print "yield_looseSelection_other %s added, now: %s" % (
                        s, yield_other)

            normRegYield = yield_data_3l - yield_other
            if normRegYield.val < 0:
                print "\n !!!Warning!!! \n Negative normalization region yield data: (%s), MC: (%s) \n" % (
                    yield_data_3l, yield_other)

            print "normRegYield", normRegYield
            print "\n Control Region predictys ", normRegYield, " TTZ events in data; ", yield_MC_3l, " TTZ events in MC. Ratio ---> ", (
                normRegYield / yield_MC_3l)
            print "DD-TTZ ---> ", (normRegYield / yield_MC_3l) * yield_MC_2l
            return (normRegYield / yield_MC_3l) * yield_MC_2l
    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
Beispiel #13
0
    def _estimate(self, region, channel, setup):

        #Sum of all channels for 'all'
        if channel == 'all':
            estimate = sum([
                self.cachedEstimate(region, c, setup)
                for c in ['MuMu', 'EE', 'EMu']
            ])

        #MC based for 'EMu'
        elif channel == 'EMu':
            weight = setup.weightString()
            preSelection = setup.preselection('MC',
                                              zWindow='allZ',
                                              channel=channel)
            cut = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                preSelection['cut']
            ])
            estimate = setup.lumi[channel] / 1000. * u_float(
                **setup.sample['DY'][channel].getYieldFromDraw(
                    selectionString=cut, weightString=weight))

        #Data driven for EE and MuMu (calculate for data luminosity)
        else:
            weight = setup.weightString()

            cut_offZ_1b = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                setup.selection(
                    'MC',
                    channel=channel,
                    zWindow='offZ',
                    **setup.defaultParameters(update={'nBTags': (1,
                                                                 -1)}))['cut']
            ])
            cut_onZ_0b = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                setup.selection(
                    'MC',
                    channel=channel,
                    zWindow='onZ',
                    **setup.defaultParameters(update={'nBTags': (0,
                                                                 0)}))['cut']
            ])
            cut_data_onZ_0b = "&&".join([
                region.cutString(),
                setup.selection(
                    'Data',
                    channel=channel,
                    zWindow='onZ',
                    **setup.defaultParameters(update={'nBTags': (0,
                                                                 0)}))['cut']
            ])

            # Calculate ratio (offZ,1b)/(onZ,0b)
            yield_offZ_1b = u_float(
                **setup.sample['DY'][channel].getYieldFromDraw(
                    selectionString=cut_offZ_1b,
                    weightString=weight)) * setup.dataLumi[channel] / 1000
            yield_onZ_0b = u_float(
                **setup.sample['DY'][channel].getYieldFromDraw(
                    selectionString=cut_onZ_0b,
                    weightString=weight)) * setup.dataLumi[channel] / 1000
            R = yield_offZ_1b / yield_onZ_0b if yield_onZ_0b > 0 else 0

            # Calculate data-other onZ for 0 b-jets region
            yield_data = u_float(
                **setup.sample['Data'][channel].getYieldFromDraw(
                    selectionString=cut_data_onZ_0b, weightString="(1)"))
            yield_other = sum(
                u_float(**setup.sample[s][channel].getYieldFromDraw(
                    selectionString=cut_onZ_0b, weightString=weight)) for s in
                ['TTJets', 'TTZ', 'other']) * setup.dataLumi[channel] / 1000
            normRegYield = yield_data - yield_other

            # Calculate DY estimate in 1 b-jet region (and scale back to MC lumi)
            estimate = R * normRegYield * setup.lumi[channel] / setup.dataLumi[
                channel]

            logger.info("Calculating data-driven DY estimate in channel " +
                        channel + " using lumi " +
                        str(setup.dataLumi[channel]) + ":")
            logger.info("yield DY offZ/1b:          " + str(yield_offZ_1b))
            logger.info("yield DY onZ/0b:           " + str(yield_onZ_0b))
            logger.info("R:                         " + str(R))
            logger.info("yield data onZ/0b:         " + str(yield_data))
            logger.info("yield other onZ/0b:        " + str(yield_other))
            logger.info("yield (data-other) onZ/0b: " + str(normRegYield))
            logger.info("yield expected DY  onZ/1b: " + str(normRegYield * R))
            if normRegYield < 0 and yield_data > 0:
                logger.warn("Negative normalization region yield!")

        logger.info('Estimate for DY in ' + channel + ' channel (lumi=' +
                    str(setup.lumi[channel]) + '/pb): ' + str(estimate) +
                    (" (negative estimated being replaced by 0)"
                     if estimate < 0 else ""))
        return estimate if estimate > 0 else u_float(0, 0)
    def _estimate(self, region, channel, setup):

        #Sum of all channels for 'all'
        if channel == 'all':
            return sum([
                self.cachedEstimate(region, c, channel, setup)
                for c in ['MuMu', 'EE', 'EMu']
            ])
        else:
            #Data driven for EE, EMu and  MuMu.
            zWindow = 'allZ' if channel == 'EMu' else 'offZ'
            preSelection = setup.preselection('MC',
                                              zWindow=zWindow,
                                              channel=channel)

            #check lumi consistency
            assert abs(
                1. -
                setup.lumi[channel] / setup.sample['Data'][channel]['lumi']
            ) < 0.01, "Lumi specified in setup %f does not match lumi in data sample %f in channel %s" % (
                setup.lumi[channel], setup.sample['Data'][channel]['lumi'],
                channel)
            MC_2l = "&&".join([
                region.cutString(setup.sys['selectionModifier']),
                preSelection['cut']
            ])
            weight = preSelection['weightStr']
            logger.debug("weight: %s", weight)

            yield_MC_2l = setup.lumi[channel] / 1000. * u_float(
                getYieldFromChain(setup.sample['TTZ'][channel]['chain'],
                                  cutString=selection_MC_2l,
                                  weight=weight,
                                  returnError=True))
            if setup.verbose: print "yield_MC_2l: %s" % yield_MC_2l

            # pt leptons > 30, 20, 10 GeV
            useTrigger = False  # setup.parameters['useTriggers'] # better not to use three lepton triggers, seems to be too inefficient
            mumumuSelection = "&&".join([
                getLeptonString(3, 0),
                getPtThresholdString(30, 20, 10)
            ]) + ("&&HLT_3mu" if useTrigger else "")
            mumueSelection = "&&".join([
                getLeptonString(2, 1),
                getPtThresholdString(30, 20, 10)
            ]) + ("&&HLT_2mu1e" if useTrigger else "")
            mueeSelection = "&&".join([
                getLeptonString(1, 2),
                getPtThresholdString(30, 20, 10)
            ]) + ("&&HLT_2e1mu" if useTrigger else "")
            eeeSelection = "&&".join([
                getLeptonString(0, 3),
                getPtThresholdString(30, 20, 10)
            ]) + ("&&HLT_3e" if useTrigger else "")
            lllSelection = "((" + ")||(".join([
                mumumuSelection, mumueSelection, mueeSelection, eeeSelection
            ]) + "))"

            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

            MC_3l = lllSelection + "&&" + selection["MC"]
            data_mumumu = mumumuSelection + "&&" + selection["Data"]
            data_mumue = mumueSelection + "&&" + selection["Data"]
            data_muee = mueeSelection + "&&" + selection["Data"]
            data_eee = eeeSelection + "&&" + selection["Data"]

            # Calculate yields (take together)
            yield_ttZ_2l = setup.lumi[channel] / 1000. * u_float(
                getYieldFromChain(setup.sample['TTZ'][channel]['chain'],
                                  cutString=MC_2l,
                                  weight=weight,
                                  returnError=True))
            yield_ttZ_3l = setup.lumi[channel] / 1000. * u_float(
                getYieldFromChain(setup.sample['TTZ'][channel]['chain'],
                                  cutString=MC_3l,
                                  weight=weight,
                                  returnError=True))
            yield_data_mumumu = u_float(
                getYieldFromChain(setup.sample['Data']['MuMu']['chain'],
                                  cutString=data_mumumu,
                                  weight=weight,
                                  returnError=True))
            yield_data_eee = u_float(
                getYieldFromChain(setup.sample['Data']['EE']['chain'],
                                  cutString=data_eee,
                                  weight=weight,
                                  returnError=True))
            yield_data_mue = u_float(
                getYieldFromChain(setup.sample['Data']['EMu']['chain'],
                                  cutString="((" + data_mumue + ')||(' +
                                  data_muee + '))',
                                  weight=weight,
                                  returnError=True))
            yield_data_3l = yield_data_mumumu + yield_data_mue + yield_data_eee

            #electroweak subtraction
            yield_other = u_float(0., 0.)
            for s in ['TTJets', 'DY', 'other']:
                yield_other += setup.lumi[channel] / 1000. * u_float(
                    getYieldFromChain(setup.sample[s][channel]['chain'],
                                      cutString=MC_3l,
                                      weight=weight,
                                      returnError=True))

            yield_ttZ_data = yield_data_3l - yield_other

            if normRegYield.val < 0:
                logger.warn("Data-driven estimate is negative!")
            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))
            return (yield_ttZ_data / yield_ttZ_3l) * yield_MC_2l
    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
    def _estimate(self, region, channel, setup):

        #Sum of all channels for 'all'
        if channel=='all':
            return sum( [ self.cachedEstimate(region, c, channel, setup) for c in ['MuMu', 'EE', 'EMu'] ] )
        else:
            #Data driven for EE, EMu and  MuMu.
            zWindow= 'allZ' if channel=='EMu' else 'offZ'
            preSelection = setup.preselection('MC', zWindow=zWindow, channel=channel)

            #check lumi consistency
            assert abs(1.-setup.lumi[channel]/setup.sample['Data'][channel]['lumi'])<0.01, "Lumi specified in setup %f does not match lumi in data sample %f in channel %s"%(setup.lumi[channel], setup.sample['Data'][channel]['lumi'], channel)
            MC_2l = "&&".join([region.cutString(setup.sys['selectionModifier']), preSelection['cut']])
            weight = preSelection['weightStr']
            logger.debug("weight: %s", weight)

            yield_MC_2l =  setup.lumi[channel]/1000.*u_float(getYieldFromChain(setup.sample['TTZ'][channel]['chain'], cutString = selection_MC_2l, weight=weight, returnError = True) )
            if setup.verbose: print "yield_MC_2l: %s"%yield_MC_2l

            # pt leptons > 30, 20, 10 GeV
            useTrigger      = False # setup.parameters['useTriggers'] # better not to use three lepton triggers, seems to be too inefficient
            mumumuSelection = "&&".join([getLeptonString(3, 0), getPtThresholdString(30, 20, 10)]) + ("&&HLT_3mu"   if useTrigger else "")
            mumueSelection  = "&&".join([getLeptonString(2, 1), getPtThresholdString(30, 20, 10)]) + ("&&HLT_2mu1e" if useTrigger else "") 
            mueeSelection   = "&&".join([getLeptonString(1, 2), getPtThresholdString(30, 20, 10)]) + ("&&HLT_2e1mu" if useTrigger else "")
            eeeSelection    = "&&".join([getLeptonString(0, 3), getPtThresholdString(30, 20, 10)]) + ("&&HLT_3e"    if useTrigger else "")
            lllSelection    = "((" + ")||(".join([mumumuSelection, mumueSelection, mueeSelection, eeeSelection]) + "))"

            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 


            MC_3l       = lllSelection    + "&&" + selection["MC"]
            data_mumumu = mumumuSelection + "&&" + selection["Data"]
            data_mumue  = mumueSelection  + "&&" + selection["Data"]
            data_muee   = mueeSelection   + "&&" + selection["Data"]
            data_eee    = eeeSelection    + "&&" + selection["Data"]

            # Calculate yields (take together)
            yield_ttZ_2l      = setup.lumi[channel]/1000.*u_float(getYieldFromChain(setup.sample['TTZ'][channel]['chain'], cutString = MC_2l,                                 weight=weight, returnError = True))
            yield_ttZ_3l      = setup.lumi[channel]/1000.*u_float(getYieldFromChain(setup.sample['TTZ'][channel]['chain'], cutString = MC_3l,                                 weight=weight, returnError = True))
            yield_data_mumumu =                           u_float(getYieldFromChain(setup.sample['Data']['MuMu']['chain'], cutString = data_mumumu,                           weight=weight, returnError = True))
            yield_data_eee    =                           u_float(getYieldFromChain(setup.sample['Data']['EE']['chain'],   cutString = data_eee,                              weight=weight, returnError = True))
            yield_data_mue    =                           u_float(getYieldFromChain(setup.sample['Data']['EMu']['chain'],  cutString = "(("+data_mumue+')||('+data_muee+'))', weight=weight, returnError = True))
            yield_data_3l     = yield_data_mumumu + yield_data_mue + yield_data_eee

            #electroweak subtraction
            yield_other = u_float(0., 0.)
            for s in ['TTJets' , 'DY', 'other']:
                yield_other+= setup.lumi[channel]/1000.* u_float(getYieldFromChain(setup.sample[s][channel]['chain'], cutString = MC_3l,  weight=weight, returnError=True))

            yield_ttZ_data = yield_data_3l - yield_other

            if normRegYield.val<0: logger.warn("Data-driven estimate is negative!")
            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))
            return (yield_ttZ_data/yield_ttZ_3l)*yield_MC_2l