Пример #1
0
class VReco(AddCollectionsModule):
    def __init__(self,
                 debug=False,
                 replaceNominal=False,
                 puIdCut=6,
                 jetIdCut=4):
        self.debug = debug or 'XBBDEBUG' in os.environ
        self.replaceNominal = replaceNominal
        self.puIdCut = puIdCut
        self.jetIdCut = jetIdCut
        super(VReco, self).__init__()
        self.version = 2

    def customInit(self, initVars):
        self.sampleTree = initVars['sampleTree']
        self.isData = initVars['sample'].isData()
        self.sample = initVars['sample']
        self.config = initVars['config']
        self.xbbConfig = XbbConfigTools(self.config)

        self.systematics = self.xbbConfig.getJECuncertainties(
            step='VReco') + ['unclustEn']

        #if self.replaceNominal:
        self.allVariations = ['Nominal']
        #else:
        #    self.allVariations = []

        # jet and MET systematics for MC
        if not self.isData:
            self.allVariations += self.systematics

        for syst in self.allVariations:
            self.addVsystematics(syst)

        self.addBranch("selLeptonWln_pt")
        self.addBranch("selLeptonWln_eta")
        self.addBranch("selLeptonWln_phi")
        self.addBranch("selLeptonWln_mass")

    def addVsystematics(self, syst):
        if self._isnominal(syst):
            # replace values from post-processor / selection
            if self.replaceNominal:
                self.addBranch("V_pt")
                self.addBranch("V_eta")
                self.addBranch("V_phi")
                self.addBranch("V_mass")
                self.addBranch("V_mt")
            self.addBranch("MET_sig30")
            self.addBranch("MET_sig30puid")
        else:
            for UD in self._variations(syst):
                self.addBranch(self._v("V_mt", syst, UD))
                self.addBranch(self._v("V_pt", syst, UD))
                self.addBranch(self._v("V_eta", syst, UD))
                self.addBranch(self._v("V_phi", syst, UD))
                self.addBranch(self._v("V_mass", syst, UD))
                if 'jerReg' not in syst:
                    self.addBranch(self._v("MET_sig30", syst, UD))
                    self.addBranch(self._v("MET_sig30puid", syst, UD))

    def processEvent(self, tree):
        if not self.hasBeenProcessed(tree):
            self.markProcessed(tree)
            self._b("selLeptonWln_pt")[0] = -99.0
            self._b("selLeptonWln_eta")[0] = -99.0
            self._b("selLeptonWln_phi")[0] = -99.0
            self._b("selLeptonWln_mass")[0] = -99.0

            for syst in self.allVariations:
                directions = [''] if syst.lower() == 'nominal' else [
                    'Up', 'Down'
                ]
                for UD in self._variations(syst):
                    if not self._isnominal(syst) or self.replaceNominal:
                        self._b(self._v("V_mt", syst, UD))[0] = -1.0

                    if tree.Vtype == 0 or tree.Vtype == 1:
                        lep1 = ROOT.TLorentzVector()
                        lep2 = ROOT.TLorentzVector()
                        i1 = tree.vLidx[0]
                        i2 = tree.vLidx[1]
                        if tree.Vtype == 1:
                            lep1.SetPtEtaPhiM(tree.Electron_pt[i1],
                                              tree.Electron_eta[i1],
                                              tree.Electron_phi[i1],
                                              tree.Electron_mass[i1])
                            lep2.SetPtEtaPhiM(tree.Electron_pt[i2],
                                              tree.Electron_eta[i2],
                                              tree.Electron_phi[i2],
                                              tree.Electron_mass[i2])
                        else:
                            lep1.SetPtEtaPhiM(tree.Muon_pt[i1],
                                              tree.Muon_eta[i1],
                                              tree.Muon_phi[i1],
                                              tree.Muon_mass[i1])
                            lep2.SetPtEtaPhiM(tree.Muon_pt[i2],
                                              tree.Muon_eta[i2],
                                              tree.Muon_phi[i2],
                                              tree.Muon_mass[i2])
                        V = lep1 + lep2
                    elif tree.Vtype == 2 or tree.Vtype == 3:
                        i1 = tree.vLidx[0]
                        if tree.Vtype == 3:
                            sel_lepton_pt = tree.Electron_pt[i1]
                            sel_lepton_eta = tree.Electron_eta[i1]
                            sel_lepton_phi = tree.Electron_phi[i1]
                            sel_lepton_mass = tree.Electron_mass[i1]
                        else:
                            sel_lepton_pt = tree.Muon_pt[i1]
                            sel_lepton_eta = tree.Muon_eta[i1]
                            sel_lepton_phi = tree.Muon_phi[i1]
                            sel_lepton_mass = tree.Muon_mass[i1]

                        MET = ROOT.TLorentzVector()
                        Lep = ROOT.TLorentzVector()
                        if syst.lower() == 'nominal' or 'jerReg' in syst:
                            MET.SetPtEtaPhiM(tree.MET_Pt, 0.0, tree.MET_Phi,
                                             0.0)
                        else:
                            MET.SetPtEtaPhiM(
                                getattr(
                                    tree, "MET_pt_{syst}{UD}".format(syst=syst,
                                                                     UD=UD)),
                                0.0,
                                getattr(
                                    tree,
                                    "MET_phi_{syst}{UD}".format(syst=syst,
                                                                UD=UD)), 0.0)
                        Lep.SetPtEtaPhiM(sel_lepton_pt, sel_lepton_eta,
                                         sel_lepton_phi, sel_lepton_mass)
                        cosPhi12 = (Lep.Px() * MET.Px() + Lep.Py() *
                                    MET.Py()) / (Lep.Pt() * MET.Pt())
                        if not self._isnominal(syst) or self.replaceNominal:
                            self._b(self._v(
                                "V_mt", syst, UD))[0] = ROOT.TMath.Sqrt(
                                    2 * Lep.Pt() * MET.Pt() * (1 - cosPhi12))

                        V = MET + Lep

                        if self._isnominal(syst):
                            self._b("selLeptonWln_pt")[0] = Lep.Pt()
                            self._b("selLeptonWln_eta")[0] = Lep.Eta()
                            self._b("selLeptonWln_phi")[0] = Lep.Phi()
                            self._b("selLeptonWln_mass")[0] = Lep.M()
                    elif tree.Vtype == 4:
                        MET = ROOT.TLorentzVector()
                        if syst.lower() == 'nominal':
                            MET.SetPtEtaPhiM(tree.MET_Pt, 0.0, tree.MET_Phi,
                                             0.0)
                        else:
                            MET.SetPtEtaPhiM(
                                getattr(
                                    tree, "MET_pt_{syst}{UD}".format(syst=syst,
                                                                     UD=UD)),
                                0.0,
                                getattr(
                                    tree,
                                    "MET_phi_{syst}{UD}".format(syst=syst,
                                                                UD=UD)), 0.0)
                        V = MET
                    else:
                        V = None
                        if (not self._isnominal(syst)) or self.replaceNominal:
                            self._b(self._v("V_pt", syst, UD))[0] = -1.0
                            self._b(self._v("V_eta", syst, UD))[0] = -1.0
                            self._b(self._v("V_phi", syst, UD))[0] = -1.0
                            self._b(self._v("V_mass", syst, UD))[0] = -1.0

                    if V is not None:
                        if (not self._isnominal(syst)) or self.replaceNominal:
                            self._b(self._v("V_pt", syst, UD))[0] = V.Pt()
                            self._b(self._v("V_eta", syst, UD))[0] = V.Eta()
                            self._b(self._v("V_phi", syst, UD))[0] = V.Phi()
                            self._b(self._v("V_mass", syst, UD))[0] = V.M()

                    # MET significance (approx.)
                    if 'jerReg' not in syst:
                        HTsum30 = 0
                        HTsum30puid = 0
                        if syst.lower() == 'nominal' or syst == 'unclustEn':
                            jetPt = tree.Jet_Pt
                        else:
                            jetPt = getattr(
                                tree, "Jet_pt_{syst}{UD}".format(syst=syst,
                                                                 UD=UD))
                        for i in range(tree.nJet):
                            if jetPt[i] > 30 and tree.Jet_lepFilter[
                                    i] > 0 and tree.Jet_jetId[
                                        i] > self.jetIdCut:
                                HTsum30 += jetPt[i]
                                if tree.Jet_puId[i] > self.puIdCut or jetPt[
                                        i] > 50.0:
                                    HTsum30puid += jetPt[i]

                        if syst.lower() == 'nominal':
                            metPt = tree.MET_Pt
                        else:
                            metPt = getattr(
                                tree, "MET_pt_{syst}{UD}".format(syst=syst,
                                                                 UD=UD))

                        self._b(self._v("MET_sig30", syst, UD))[0] = (
                            metPt / np.sqrt(HTsum30)) if HTsum30 > 0 else -1.0
                        self._b(self._v("MET_sig30puid", syst, UD))[0] = (
                            metPt /
                            np.sqrt(HTsum30puid)) if HTsum30puid > 0 else -1.0
Пример #2
0
class HiggsCandidateSystematics(AddCollectionsModule):
    def __init__(self,
                 addSystematics=True,
                 prefix="H",
                 addBoostSystematics=False,
                 addMinMax=False,
                 puIdCut=6,
                 jetIdCut=4):
        super(HiggsCandidateSystematics, self).__init__()
        self.version = 1
        self.debug = 'XBBDEBUG' in os.environ
        self.nJet = -1
        self.nJetMax = 100
        self.addSystematics = addSystematics
        self.addMinMax = addMinMax
        self.prefix = prefix
        self.addBoostSystematics = addBoostSystematics
        self.puIdCut = puIdCut
        self.jetIdCut = jetIdCut

    def customInit(self, initVars):
        self.sample = initVars['sample']
        self.config = initVars['config']
        self.xbbConfig = XbbConfigTools(self.config)

        self.jetSystematicsResolved = self.xbbConfig.getJECuncertainties(
            step='Higgs')
        self.jetSystematics = self.jetSystematicsResolved[:]

        # corrected dijet (Higgs candidate) properties
        self.higgsProperties = [
            self.prefix + '_' + x for x in [
                'pt', 'eta', 'phi', 'mass', 'pt_noFSR', 'eta_noFSR',
                'phi_noFSR', 'mass_noFSR'
            ]
        ]

        # included JEC and JER systematic for msoftdrop
        if self.addBoostSystematics:
            self.higgsProperties += ['FatJet_msoftdrop_sys', 'FatJet_pt_sys']
            #self.higgsProperties +=['FatJet_msoftdrop_sys']
            # adding mass scale and resolution systematics
            self.rnd = ROOT.TRandom3(12345)

        self.dataset = self.config.get('General', 'dataset')
        if self.dataset == '2016':
            self.jetIdCut = 2
        else:
            self.jetIdCut = 4

#        if self.addBoostSystematics:
#            self.boosttagidx = 'Hbb_fjidx'
#            self.msoftdrop = 'FatJet_msoftdrop'
#            if self.sample.type != 'DATA':
#                self.FatJet_pt= 'FatJet_pt_nom'
#            else:
#                self.FatJet_pt= 'FatJet_pt'
#            # get all the info for scale and smearing
#            self.Snom   = eval(self.config.get('Sys', 'Snom'))
#            self.Sdown  = eval(self.config.get('Sys', 'Sdown'))
#            self.Sup    = eval(self.config.get('Sys', 'Sup'))
#            self.Rnom   = eval(self.config.get('Sys', 'Rnom'))
#            self.Rdown  = eval(self.config.get('Sys', 'Rdown'))
#            self.Rup    = eval(self.config.get('Sys', 'Rup'))
#
#            #Load .root file with resolution.
#            self.jetSystematics+= ['jms']
#            self.jetSystematics+= ['jmr']
#            self.config = initVars['config']
#            wdir = self.config.get('Directories', 'vhbbpath')
#            filejmr = ROOT.TFile.Open(wdir+"/python/data/softdrop/puppiSoftdropResol.root","READ")
#            self.puppisd_resolution_cen = filejmr.Get("massResolution_0eta1v3")
#            self.puppisd_resolution_for = filejmr.Get("massResolution_1v3eta2v5")
#
#            ## adding jms and jmr to FatJet pt
#            for syst in ['_jmr','_jms']:
#                for p in ['Jet_pt', 'Jet_mass']:
#                    for q in ['Up', 'Down']:
#                        print 'name is',p+syst+q
#                        self.branchBuffers[p+syst+q] = array.array('f', [0.0]*self.nJetMax)
#                        self.branches.append({'name': p+syst+q, 'formula': self.getVectorBranch, 'arguments': {'branch': p+syst+q}, 'length': self.nJetMax, 'leaflist': p+syst+q+'[nJet]/F'})
#
#        # they will be filled with the nominal...
#        for syst in ['jmr','jms','jerReg']:
#            for Q in ['Up', 'Down']:
#                self.addBranch('MET_pt_{s}{d}'.format(s=syst, d=Q))
#                self.addBranch('MET_phi_{s}{d}'.format(s=syst, d=Q))

        self.tagidx = self.config.get('General', 'hJidx')
        if self.debug:
            print "DEBUG: HiggsCandidateSystematics::__init__(), with idx=", self.tagidx, " prefix=", self.prefix

        # nominal + systematic variations
        self.systematicsResolved = [None]
        if self.sample.isMC():
            self.systematicsResolved += self.jetSystematicsResolved

        # dijet H candidate branches
        for higgsProperty in self.higgsProperties:
            for syst in self.systematicsResolved:
                for Q in self._variations(syst):
                    self.addBranch(self._v(higgsProperty, syst, Q))

        self.addBranch('H_noReg_pt')
        self.addBranch('H_noReg_eta')
        self.addBranch('H_noReg_phi')
        self.addBranch('H_noReg_mass')

        # additional jet branches
        fBranches = [
            'hJets_0_pt_noFSR', 'hJets_1_pt_noFSR', 'hJets_0_pt_FSRrecovered',
            'hJets_1_pt_FSRrecovered', 'hJets_FSRrecovered_dEta',
            'hJets_FSRrecovered_dPhi'
        ]
        for syst in self.systematicsResolved:
            for Q in self._variations(syst):
                for branchName in fBranches:
                    self.addBranch(self._v(branchName, syst, Q))
                self.addIntegerBranch(self._v('nFSRrecovered', syst, Q))

    # read from buffers which have been filled in processEvent()
    def getVectorBranch(self, event, arguments=None, destinationArray=None):
        self.processEvent(event)
        length = min(self.nJet, self.nJetMax)
        destinationArray[:length] = self.branchBuffers[
            arguments['branch']][:length]

    def getResolvedJetIndicesFromTree(self, tree, syst=None, UD=None):
        indexNameSyst = (self.tagidx + '_' + syst +
                         UD) if not self._isnominal(syst) else self.tagidx
        if hasattr(tree, indexNameSyst):
            self.count('_debug_resolved_idx_syst_exists')
            return getattr(tree, indexNameSyst)
        else:
            self.count('_debug_resolved_idx_fallback_nom')
            return getattr(tree, self.tagidx)

    def processEvent(self, tree):
        # if current entry has not been processed yet
        if not self.hasBeenProcessed(tree):
            self.markProcessed(tree)

            # ----------------------------------------------------------------------------------------------------
            # RESOLVED
            # ----------------------------------------------------------------------------------------------------

            # select branches from tree
            # for 2016 nano >= v5 needed!
            Jet_PtReg_nom = tree.Jet_PtReg
            Jet_pt_nom = tree.Jet_Pt
            Jet_phi = tree.Jet_phi
            Jet_eta = tree.Jet_eta
            Jet_puId = tree.Jet_puId
            Jet_jetId = tree.Jet_jetId
            Jet_lepFilter = tree.Jet_lepFilter
            nJet = tree.nJet

            # alias for jet mass
            if self.sample.isMC():
                Jet_mass_nom = tree.Jet_mass_nom
            else:
                Jet_mass_nom = tree.Jet_mass

            for syst in self.systematicsResolved:
                for Q in self._variations(syst):

                    hJ0 = ROOT.TLorentzVector()
                    hJ1 = ROOT.TLorentzVector()

                    # check if there are TWO resolved jets
                    hJidx0, hJidx1 = self.getResolvedJetIndicesFromTree(
                        tree, syst, Q)
                    if hJidx0 > -1 and hJidx1 > -1:

                        # Pt, Mass:       with JEC, JER
                        # PtReg, MassReg: with JEC, JER, regression+smearing
                        if self._isnominal(syst):
                            Jet_Pt = Jet_pt_nom
                            Jet_PtReg = Jet_PtReg_nom
                            Jet_Mass = Jet_mass_nom
                            Jet_MassReg = [
                                Jet_Mass[i] * Jet_PtReg_nom[i] / Jet_pt_nom[i]
                                for i in range(nJet)
                            ]
                        elif 'jerReg' in syst:
                            Jet_Pt = Jet_pt_nom
                            Jet_PtReg = getattr(tree, 'Jet_Pt' + syst[3:] + Q)
                            Jet_Mass = Jet_mass_nom
                            Jet_MassReg = [
                                Jet_Mass[i] * Jet_PtReg[i] / Jet_pt_nom[i]
                                for i in range(nJet)
                            ]
                        else:
                            Jet_Pt = getattr(
                                tree, 'Jet_pt_{s}{d}'.format(s=syst, d=Q))
                            Jet_PtReg = [
                                Jet_Pt[i] * Jet_PtReg_nom[i] / Jet_pt_nom[i]
                                for i in range(nJet)
                            ]
                            Jet_Mass = getattr(
                                tree, 'Jet_mass_{s}{d}'.format(s=syst, d=Q))
                            Jet_MassReg = [
                                Jet_Mass[i] * Jet_PtReg_nom[i] / Jet_pt_nom[i]
                                for i in range(nJet)
                            ]

                        # b-jet regression is applied to H candidate jets
                        hJ0.SetPtEtaPhiM(Jet_PtReg[hJidx0], Jet_eta[hJidx0],
                                         Jet_phi[hJidx0], Jet_MassReg[hJidx0])
                        hJ1.SetPtEtaPhiM(Jet_PtReg[hJidx1], Jet_eta[hJidx1],
                                         Jet_phi[hJidx1], Jet_MassReg[hJidx1])

                        self._b(self._v('hJets_0_pt_noFSR', syst,
                                        Q))[0] = hJ0.Pt()
                        self._b(self._v('hJets_1_pt_noFSR', syst,
                                        Q))[0] = hJ1.Pt()

                        dijet_noFSR = hJ0 + hJ1
                        self._b(self._v(self.prefix + '_pt_noFSR', syst,
                                        Q))[0] = dijet_noFSR.Pt()
                        self._b(self._v(self.prefix + '_eta_noFSR', syst,
                                        Q))[0] = dijet_noFSR.Eta()
                        self._b(self._v(self.prefix + '_phi_noFSR', syst,
                                        Q))[0] = dijet_noFSR.Phi()
                        self._b(self._v(self.prefix + '_mass_noFSR', syst,
                                        Q))[0] = dijet_noFSR.M()

                        # save information which FSR jets have been added
                        fsrIndices0 = []
                        fsrIndices1 = []

                        # FSR recovery
                        for i in range(nJet):
                            if i not in [hJidx0, hJidx1]:

                                if Jet_Pt[i] > 20.0 and abs(
                                        Jet_eta[i]
                                ) < 3.0 and (
                                        Jet_puId[i] > self.puIdCut
                                        or Jet_Pt[i] > 50.0
                                ) and Jet_lepFilter[i] > 0 and Jet_jetId[
                                        i] > self.jetIdCut:

                                    # b-jet regression is not applied to FSR jets
                                    FSR = ROOT.TLorentzVector()
                                    FSR.SetPtEtaPhiM(Jet_Pt[i], Jet_eta[i],
                                                     Jet_phi[i], Jet_Mass[i])

                                    deltaR0 = FSR.DeltaR(hJ0)
                                    deltaR1 = FSR.DeltaR(hJ1)
                                    if min(deltaR0, deltaR1) < 0.8:
                                        if deltaR0 < deltaR1:
                                            hJ0 = hJ0 + FSR
                                            fsrIndices0.append(i)
                                        else:
                                            hJ1 = hJ1 + FSR
                                            fsrIndices1.append(i)

                        # H with FSR recovery
                        dijet = hJ0 + hJ1
                        self._b(self._v(self.prefix + '_pt', syst,
                                        Q))[0] = dijet.Pt()
                        self._b(self._v(self.prefix + '_eta', syst,
                                        Q))[0] = dijet.Eta()
                        self._b(self._v(self.prefix + '_phi', syst,
                                        Q))[0] = dijet.Phi()
                        self._b(self._v(self.prefix + '_mass', syst,
                                        Q))[0] = dijet.M()

                        # write additional jet quantities after FSR recovery
                        self._b(self._v('hJets_0_pt_FSRrecovered', syst,
                                        Q))[0] = hJ0.Pt()
                        self._b(self._v('hJets_1_pt_FSRrecovered', syst,
                                        Q))[0] = hJ1.Pt()

                        self._b(self._v('hJets_FSRrecovered_dEta', syst,
                                        Q))[0] = abs(hJ0.Eta() - hJ1.Eta())
                        self._b(self._v('hJets_FSRrecovered_dPhi', syst,
                                        Q))[0] = abs(hJ0.DeltaPhi(hJ1))

                        self._b(self._v(
                            'nFSRrecovered', syst,
                            Q))[0] = len(fsrIndices0) + len(fsrIndices1)

                        # for nominal, add pT without regression
                        if self._isnominal(syst):
                            hJ0.SetPtEtaPhiM(Jet_Pt[hJidx0], Jet_eta[hJidx0],
                                             Jet_phi[hJidx0], Jet_Mass[hJidx0])
                            hJ1.SetPtEtaPhiM(Jet_Pt[hJidx1], Jet_eta[hJidx1],
                                             Jet_phi[hJidx1], Jet_Mass[hJidx1])

                            # FSR recovery
                            for i in range(nJet):
                                if i not in [hJidx0, hJidx1]:

                                    if Jet_Pt[i] > 20.0 and abs(
                                            Jet_eta[i]
                                    ) < 3.0 and (
                                            Jet_puId[i] > self.puIdCut
                                            or Jet_Pt[i] > 50.0
                                    ) and Jet_lepFilter[i] > 0 and Jet_jetId[
                                            i] > self.jetIdCut:

                                        # b-jet regression is not applied to FSR jets
                                        FSR = ROOT.TLorentzVector()
                                        FSR.SetPtEtaPhiM(
                                            Jet_Pt[i], Jet_eta[i], Jet_phi[i],
                                            Jet_Mass[i])

                                        deltaR0 = FSR.DeltaR(hJ0)
                                        deltaR1 = FSR.DeltaR(hJ1)
                                        if min(deltaR0, deltaR1) < 0.8:
                                            if deltaR0 < deltaR1:
                                                hJ0 = hJ0 + FSR
                                            else:
                                                hJ1 = hJ1 + FSR

                            dijet = hJ0 + hJ1
                            self._b('H_noReg_pt')[0] = dijet.Pt()
                            self._b('H_noReg_eta')[0] = dijet.Eta()
                            self._b('H_noReg_phi')[0] = dijet.Phi()
                            self._b('H_noReg_mass')[0] = dijet.M()

# jmr/jms taken from post-processor
#            # ----------------------------------------------------------------------------------------------------
#            # BOOSTED
#            # ----------------------------------------------------------------------------------------------------
#
#            if self.addBoostSystematics:
#                boosttagidx = getattr(tree, self.boosttagidx)
#                if boosttagidx > -1:
#                    FatJet_pt = getattr(tree, self.FatJet_pt)[boosttagidx]
#                    msoftdrop   = getattr(tree, self.msoftdrop)[boosttagidx]
#                    self._b('FatJet_msoftdrop_sys')[0] = msoftdrop
#                    self._b('FatJet_pt_sys')[0]        = FatJet_pt
#                    #print FatJet_pt
#                    #print msoftdrop
#                    #import sys
#                    #sys.exit()
#
#            # systematics
#            valueList = {x:[self.branchBuffers[x][0]] for x in self.higgsProperties}
#            if self.addSystematics and self.sample.type != 'DATA':
#                for syst in self.jetSystematics:
#                    for Q in ['Up', 'Down']:
#                        if self.sample.type != 'DATA':
#                            # included JEC and JER systematic for msoftdrop
#                            if self.addBoostSystematics  and boosttagidx > -1:
#                                if syst == 'jmr' or syst == 'jms':
#                                    pass
#                                else:
#                                    msoftdrop_sys = msoftdrop*getattr(tree, 'FatJet_pt_{s}{d}'.format(s=syst, d=Q))[boosttagidx]/getattr(tree, 'FatJet_pt_nom')[boosttagidx]
#                                    FatJet_pt_sys = getattr(tree, 'FatJet_pt_{s}{d}'.format(s=syst, d=Q))[boosttagidx]
#                            if tree.Hbb_fjidx > -1:
#                                if syst == 'jmr':
#                                    self.jmr_sys = self.get_msoftdrop_smear(tree.FatJet_pt[tree.Hbb_fjidx], tree.FatJet_eta[tree.Hbb_fjidx])
#                                elif syst == 'jms':
#                                    pass
#
#                        if self.addBoostSystematics  and boosttagidx > -1:
#                            #print FatJet_pt
#                            #import sys
#                            #sys.exit()
#                            if syst == 'jmr':
#                                if Q == 'Up':
#                                    self.branchBuffers['FatJet_msoftdrop_sys_jmr_Down'][0]   = msoftdrop*self.jmr_sys[0]
#                                    valueList['FatJet_msoftdrop_sys'].append(msoftdrop*self.jmr_sys[0])
#
#                                    # no sys variation on pT (put nominal value)
#                                    self.branchBuffers['FatJet_pt_sys_jmr_Down'][0]   = FatJet_pt
#                                    valueList['FatJet_pt_sys'].append(FatJet_pt)
#                                elif Q == 'Down':
#                                    self.branchBuffers['FatJet_msoftdrop_sys_jmr_Up'][0]     = msoftdrop*self.jmr_sys[1]
#                                    valueList['FatJet_msoftdrop_sys'].append(msoftdrop*self.jmr_sys[1])
#
#                                    # no sys variation on pT (put nominal value)
#                                    self.branchBuffers['FatJet_pt_sys_jmr_Up'][0]     = FatJet_pt
#                                    valueList['FatJet_pt_sys'].append(FatJet_pt)
#                            elif syst == 'jms':
#                                if Q == 'Up':
#                                    self.branchBuffers['FatJet_msoftdrop_sys_jms_Up'][0]     = msoftdrop*self.Sup
#                                    valueList['FatJet_msoftdrop_sys'].append(msoftdrop*self.Sup)
#
#                                    # no sys variation on pT (put nominal value)
#                                    self.branchBuffers['FatJet_pt_sys_jms_Up'][0]     = FatJet_pt
#                                    valueList['FatJet_pt_sys'].append(FatJet_pt)
#                                elif Q == 'Down':
#                                    self.branchBuffers['FatJet_msoftdrop_sys_jms_Down'][0]   = msoftdrop*self.Sdown
#                                    valueList['FatJet_msoftdrop_sys'].append(msoftdrop*self.Sdown)
#
#                                    # no sys variation on pT (put nominal value)
#                                    self.branchBuffers['FatJet_pt_sys_jms_Down'][0]   = FatJet_pt
#                                    valueList['FatJet_pt_sys'].append(FatJet_pt)
#                            else:
#                                self.branchBuffers['FatJet_msoftdrop_sys_{s}_{d}'.format(s=syst, d=Q)][0] = msoftdrop_sys
#                                valueList['FatJet_msoftdrop_sys'].append(msoftdrop_sys)
#
#                                ##valueList['FatJet_pt_sys'].append(getattr(tree,'FatJet_pt_{s}{d}'.format(s=syst, d=Q))[boosttagidx])
#                                #print 'FatJet_pt_sys', FatJet_pt_sys
#                                #import sys
#                                #sys.exit()
#                                self.branchBuffers['FatJet_pt_sys_{s}_{d}'.format(s=syst, d=Q)][0] = FatJet_pt_sys
#                                valueList['FatJet_pt_sys'].append(FatJet_pt_sys)

        return True

    def get_msoftdrop_smear(self, pt, eta):

        #get mass resolution
        massResolution = 0
        # version1: added abs() wrt previous version 0
        if abs(eta) <= 1.3:
            massResolution = self.puppisd_resolution_cen.Eval(pt)
        else:
            massResolution = self.puppisd_resolution_for.Eval(pt)

        ###
        cup = 1.
        cdown = 1.
        r = self.rnd.Gaus(0, massResolution - 1)

        cup = 1. + r * math.sqrt(max(self.Rup**2 - 1, 0))
        cdown = 1. + r * math.sqrt(max(self.Rdown**2 - 1, 0))

        return [cdown, cup]
Пример #3
0
class kinFitterXbb(AddCollectionsModule):

    def __init__(self, year, branchName="kinFit", useMZconstraint=True, recoilPtThreshold=20.0, jetIdCut=4, puIdCut=6, hJidx="hJidx"):
        super(kinFitterXbb, self).__init__()
        self.version = 4
        self.branchName = branchName
        self.useMZconstraint = useMZconstraint
        self.debug = False
        self.enabled = True
        self.year = year
        self.jetIdCut = jetIdCut
        self.puIdCut = puIdCut
        self.HH4B_RES_SCALE = 0.62
        self.LLVV_PXY_VAR = 8.0**2 
        self.Z_MASS = 91.0
        self.Z_WIDTH = 1.7*3
        self.recoilPtThreshold = recoilPtThreshold
        self.hJidx = hJidx
        self.systematics = None
        
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TAbsFitConstraint_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TAbsFitParticle_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitConstraintEp_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitConstraintMGaus_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitConstraintM_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleCart_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleECart_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleEMomDev_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleEScaledMomDev_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleESpher_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleEtEtaPhi_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleEtThetaPhi_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleMCCart_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleMCMomDev_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleMCPInvSpher_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleMCSpher_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleMomDev_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TFitParticleSpher_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TKinFitter_cc.so")
        ROOT.gSystem.Load("../HelperClasses/KinFitter/TSLToyGen_cc.so")

        self.jetResolution = JMEres(year=self.year)

    def customInit(self, initVars):
        self.sample = initVars['sample']
        self.config = initVars['config']
        self.xbbConfig  = XbbConfigTools(self.config)
        if self.systematics is None:
            if not self.sample.isData():
                jetSystematics= self.xbbConfig.getJECuncertainties(step='KinFit')
                self.systematics = [None] + [x+'_Up' for x in jetSystematics] + [x+'_Down' for x in jetSystematics] 
            else:
                self.systematics = [None]
        print("INFO: computing the fit for", len(self.systematics), " variations.")

        self.bDict = {}
        for syst in self.systematics:
            self.bDict[syst] = {}
            for n in ['H_mass_fit', 'H_eta_fit', 'H_phi_fit', 'H_pt_fit', 'HVdPhi_fit', 'jjVPtRatio_fit', 'hJets_pt_0_fit', 'hJets_pt_1_fit', 'V_pt_fit', 'V_eta_fit', 'V_phi_fit', 'V_mass_fit', 'H_mass_sigma_fit','H_pt_sigma_fit', 'hJets_pt_0_sigma_fit', 'hJets_pt_1_sigma_fit', 'H_pt_corr_fit', 'llbb_pt_fit', 'llbb_eta_fit', 'llbb_phi_fit', 'llbb_mass_fit', 'llbbr_pt_fit', 'llbbr_eta_fit', 'llbbr_phi_fit', 'llbbr_mass_fit', 'H_mass_fit_noJetMass']:
                branchNameFull = self.branchName + "_" + n + ('_' + syst if not self._isnominal(syst) else '')
                self.bDict[syst][n] = branchNameFull
                self.addBranch(branchNameFull)
            for n in ['n_recoil_jets_fit', 'status']:
                branchNameFull = self.branchName + "_" + n + ('_' + syst if not self._isnominal(syst) else '')
                self.bDict[syst][n] = branchNameFull
                self.addIntegerBranch(branchNameFull)

    def enable(self):
        self.enabled = True

    def disable(self):
        self.enabled = False

    def cart_cov(self, v, rho):
        jme_res = self.jetResolution.eval(v.Eta(), rho, v.Pt()) * v.Pt()
        cos_phi = np.cos(v.Phi())
        sin_phi = np.sin(v.Phi())
        cov = ROOT.TMatrixD(4, 4)
        cov.Zero()
        cov[0][0] = (jme_res*cos_phi)**2
        cov[1][0] = jme_res**2 * cos_phi * sin_phi
        cov[0][1] = cov[1][0]
        cov[1][1] = (jme_res*sin_phi)**2
        return cov

    def getResolvedJetIndicesFromTree(self, tree, syst=None, UD=None):
        indexNameSyst = (self.hJidx + '_' + syst + UD) if not self._isnominal(syst) else self.hJidx
        if hasattr(tree, indexNameSyst):
            self.count('_debug_resolved_idx_syst_exists')
            return getattr(tree, indexNameSyst)
        else:
            print("\x1b[31mDOES NOT HAVE INDEX:", indexNameSyst,"\x1b[0m")
            self.count('_debug_resolved_idx_fallback_nom')
            self.count('_debug_resolved_idx_fallback_nom_for_'+str(syst))
            return getattr(tree, self.hJidx)

    def getInputSystFormatFromOutputSyst(self, syst):
        if syst.endswith('_Up'):
            return syst[:-3] + 'Up'
        elif syst.endswith('_Down'):
            return syst[:-5] + 'Down'
        return syst

    def processEvent(self, tree):
        if not self.hasBeenProcessed(tree) and self.enabled:
            self.markProcessed(tree)

            phi = tree.Jet_phi
            eta = tree.Jet_eta

            vLidx = tree.vLidx

            for syst in self.systematics:

                if not self._isnominal(syst):
                    if syst.endswith('Up'):
                        variation = 'Up'
                        systBase  = syst[:-2].strip('_')
                    elif syst.endswith('Down'):
                        variation = 'Down'
                        systBase  = syst[:-4].strip('_')
                    else:
                        variation = None
                        systBase  = syst
                else:
                    variation = None
                    systBase  = syst

                hJidx = self.getResolvedJetIndicesFromTree(tree, systBase, variation)

                if hJidx[0] > -1 and hJidx[1] > -1:
                    # systematic up/down variation
                    if not self._isnominal(syst):
                        # vary regression
                        if syst == 'jerReg_Up':
                            pt = tree.Jet_Pt
                            pt_reg = tree.Jet_PtRegUp
                            mass = tree.Jet_mass_nom
                        elif syst == 'jerReg_Down':
                            pt = tree.Jet_Pt
                            pt_reg = tree.Jet_PtRegDown
                            mass = tree.Jet_mass_nom
                        elif syst == 'jerRegScale_Down':
                            pt = tree.Jet_Pt
                            pt_reg = tree.Jet_PtRegScaleDown
                            mass = tree.Jet_mass_nom
                        elif syst == 'jerRegScale_Up':
                            pt = tree.Jet_Pt
                            pt_reg = tree.Jet_PtRegScaleUp
                            mass = tree.Jet_mass_nom
                        elif syst == 'jerRegSmear_Down':
                            pt = tree.Jet_Pt
                            pt_reg = tree.Jet_PtRegSmearDown
                            mass = tree.Jet_mass_nom
                        elif syst == 'jerRegSmear_Up':
                            pt = tree.Jet_Pt
                            pt_reg = tree.Jet_PtRegSmearUp
                            mass = tree.Jet_mass_nom
                        # vary JEC
                        else:
                            pt = getattr(tree, 'Jet_pt_' + self.getInputSystFormatFromOutputSyst(syst)) 
                            # propagate JEC to regressed pt
                            pt_reg = [tree.Jet_PtReg[i] * getattr(tree, 'Jet_pt_' + self.getInputSystFormatFromOutputSyst(syst))[i] / tree.Jet_Pt[i] for i in range(len(tree.Jet_PtReg))]
                            mass = getattr(tree, 'Jet_mass_' + self.getInputSystFormatFromOutputSyst(syst))

                    # nominal
                    else:
                        pt = tree.Jet_Pt
                        pt_reg = tree.Jet_PtReg
                        if self.sample.isMC():
                            mass = tree.Jet_mass_nom
                        else:
                            mass = tree.Jet_mass

                    # higgs jets
                    j1 = fourvector(pt_reg[hJidx[0]], eta[hJidx[0]], phi[hJidx[0]], mass[hJidx[0]] * tree.Jet_PtReg[hJidx[0]]/tree.Jet_Pt[hJidx[0]])
                    j2 = fourvector(pt_reg[hJidx[1]], eta[hJidx[1]], phi[hJidx[1]], mass[hJidx[1]] * tree.Jet_PtReg[hJidx[1]]/tree.Jet_Pt[hJidx[1]])

                    # FSR jets
                    fsrJets = []
                    fsrJidx = []
                    for i in range(tree.nJet):
                        if i not in hJidx and tree.Jet_lepFilter[i] > 0 and (tree.Jet_puId[i] > self.puIdCut or tree.Jet_Pt[i] > 50) and tree.Jet_jetId[i] > self.jetIdCut and pt[i] > 20 and abs(eta[i]) < 3.0:

                            j_fsr = fourvector(pt[i], eta[i], phi[i], mass[i])

                            delta_r1 = j_fsr.DeltaR(j1)
                            delta_r2 = j_fsr.DeltaR(j2)

                            if min(delta_r1, delta_r2) < 0.8:
                                fsrJets.append(j_fsr)
                                fsrJidx.append(i)
                                #if delta_r1 < delta_r2:
                                #    j1 += j_fsr
                                #else:
                                #    j2 += j_fsr

                    fit_j1 = fit_jet(j1, self.HH4B_RES_SCALE)
                    fit_j2 = fit_jet(j2, self.HH4B_RES_SCALE)

                    fit_jets = [fit_j1, fit_j2] + [fit_jet(fsrJet, self.HH4B_RES_SCALE) for fsrJet in fsrJets]

                    # recoil jets
                    recoil_jets = []
                    for i in range(tree.nJet):
                        if i not in hJidx and i not in fsrJidx and (tree.Jet_puId[i] > self.puIdCut or tree.Jet_Pt[i] > 50) and tree.Jet_jetId[i] > self.jetIdCut and tree.Jet_lepFilter[i] > 0 and pt[i] > self.recoilPtThreshold:
                            recoil_jets.append(fourvector(pt[i], eta[i], phi[i], mass[i]))
                    recoil_sum = sum(recoil_jets, ROOT.TLorentzVector())

                    cov_recoil = ROOT.TMatrixD(4, 4)
                    cov_recoil.Zero()
                    cov_recoil[0][0] = self.LLVV_PXY_VAR
                    cov_recoil[1][1] = self.LLVV_PXY_VAR
                    cov_recoil[2][2] = 1
                    cov_recoil[3][3] = 1e12

                    for j in recoil_jets:
                        cov_recoil += self.cart_cov(j, tree.fixedGridRhoFastjetAll)
                    fit_recoil = ROOT.TFitParticleECart(recoil_sum, cov_recoil)

                    #leptons
                    if tree.Vtype == 0:
                        fit_l1 = fit_lepton(fourvector(abs(tree.Muon_corrected_pt[vLidx[0]]), tree.Muon_eta[vLidx[0]], tree.Muon_phi[vLidx[0]], tree.Muon_mass[vLidx[0]]), tree.Muon_ptErr[vLidx[0]])
                        fit_l2 = fit_lepton(fourvector(abs(tree.Muon_corrected_pt[vLidx[1]]), tree.Muon_eta[vLidx[1]], tree.Muon_phi[vLidx[1]], tree.Muon_mass[vLidx[1]]), tree.Muon_ptErr[vLidx[1]])
                    else:
                        fit_l1 = fit_lepton(fourvector(abs(tree.Electron_pt[vLidx[0]]), tree.Electron_eta[vLidx[0]], tree.Electron_phi[vLidx[0]], tree.Electron_mass[vLidx[0]]), tree.Electron_energyErr[vLidx[0]])
                        fit_l2 = fit_lepton(fourvector(abs(tree.Electron_pt[vLidx[1]]), tree.Electron_eta[vLidx[1]], tree.Electron_phi[vLidx[1]], tree.Electron_mass[vLidx[1]]), tree.Electron_energyErr[vLidx[1]])

                    # constraints
                    cons_MZ = ROOT.TFitConstraintMGaus('cons_MZ', '', None, None, self.Z_MASS, self.Z_WIDTH)
                    cons_MZ.addParticle1(fit_l1)
                    cons_MZ.addParticle1(fit_l2)

                    # fit particles
                    #fit_particles = fit_jets + [fit_l1, fit_l2] + [fit_recoil]

                    cons_x = ROOT.TFitConstraintEp('cons_x', '', ROOT.TFitConstraintEp.pX)
                    cons_x.addParticles(*fit_jets)
                    cons_x.addParticles(fit_l1, fit_l2)
                    cons_x.addParticles(fit_recoil)

                    cons_y = ROOT.TFitConstraintEp('cons_y', '', ROOT.TFitConstraintEp.pY)
                    cons_y.addParticles(*fit_jets)
                    cons_y.addParticles(fit_l1, fit_l2)
                    cons_y.addParticles(fit_recoil)

                    # setup fitter
                    fitter = ROOT.TKinFitter()
                    fitter.addMeasParticles(*fit_jets)
                    fitter.addMeasParticles(fit_l1, fit_l2)
                    fitter.addMeasParticles(fit_recoil)

                    if self.useMZconstraint:
                        fitter.addConstraint(cons_MZ)
                    fitter.addConstraint(cons_x)
                    fitter.addConstraint(cons_y)

                    fitter.setMaxNbIter(30)
                    fitter.setMaxDeltaS(1e-2)
                    fitter.setMaxF(1e-1)
                    fitter.setVerbosity(0)

                    # run the fit
                    kinfit_fit = fitter.fit() + 1  # 0: not run; 1: fit converged; 2: fit didn't converge
                    kinfit_getNDF = fitter.getNDF()
                    kinfit_getS = fitter.getS()
                    kinfit_getF = fitter.getF()
                    #print("-"*10, kinfit_fit, kinfit_getNDF, kinfit_getS, kinfit_getF)
                    
                    # higgs jet output vectors
                    fit_result_j1 = fitter.get4Vec(0)
                    fit_result_j2 = fitter.get4Vec(1)
                    fit_result_H_noJetMass = fit_result_j1 + fit_result_j2
                    for iFsr in range(len(fit_jets)-2):
                        fit_result_H_noJetMass += fitter.get4Vec(2+iFsr)

                    # add back jet masses
                    fit_result_j1.SetPtEtaPhiM(fit_result_j1.Pt(),fit_result_j1.Eta(),fit_result_j1.Phi(), mass[hJidx[0]] * tree.Jet_PtReg[hJidx[0]]/tree.Jet_Pt[hJidx[0]])
                    fit_result_j2.SetPtEtaPhiM(fit_result_j2.Pt(),fit_result_j2.Eta(),fit_result_j2.Phi(), mass[hJidx[1]] * tree.Jet_PtReg[hJidx[1]]/tree.Jet_Pt[hJidx[1]])
                    fit_result_H = fit_result_j1 + fit_result_j2
                    for iFsr in range(len(fit_jets)-2):
                        j_fsr = fitter.get4Vec(2+iFsr)
                        j_fsr.SetPtEtaPhiM(j_fsr.Pt(),j_fsr.Eta(),j_fsr.Phi(), mass[fsrJidx[iFsr]])
                        fit_result_H += j_fsr

                    nFitJets = len(fit_jets)
                    fit_result_l1 = fitter.get4Vec(nFitJets)
                    fit_result_l2 = fitter.get4Vec(nFitJets+1)
                    fit_result_V = fit_result_l1 + fit_result_l2

                    # H + V (+ recoil)
                    llbb  = fit_result_H + fit_result_V
                    llbbr = llbb + fitter.get4Vec(nFitJets+2)

                    self._b(self.bDict[syst]['status'])[0] = kinfit_fit

                    if kinfit_fit == 1 and fit_result_H.M() > 0:
                        self.count("kinfit_success")
                        self._b(self.bDict[syst]['H_pt_fit'])[0]          = fit_result_H.Pt()
                        self._b(self.bDict[syst]['H_eta_fit'])[0]         = fit_result_H.Eta()
                        self._b(self.bDict[syst]['H_phi_fit'])[0]         = fit_result_H.Phi()
                        self._b(self.bDict[syst]['H_mass_fit'])[0]        = fit_result_H.M()
                        self._b(self.bDict[syst]['H_mass_fit_noJetMass'])[0] = fit_result_H_noJetMass.M()
                        self._b(self.bDict[syst]['V_pt_fit'])[0]          = fit_result_V.Pt()
                        self._b(self.bDict[syst]['V_eta_fit'])[0]         = fit_result_V.Eta()
                        self._b(self.bDict[syst]['V_phi_fit'])[0]         = fit_result_V.Phi()
                        self._b(self.bDict[syst]['V_mass_fit'])[0]        = fit_result_V.M()

                        self._b(self.bDict[syst]['HVdPhi_fit'])[0]        = abs(ROOT.TVector2.Phi_mpi_pi(fit_result_H.Phi() - fit_result_V.Phi()))
                        self._b(self.bDict[syst]['jjVPtRatio_fit'])[0]    = fit_result_H.Pt()/fit_result_V.Pt()
                        self._b(self.bDict[syst]['hJets_pt_0_fit'])[0]    = fit_result_j1.Pt()
                        self._b(self.bDict[syst]['hJets_pt_1_fit'])[0]    = fit_result_j2.Pt()

                        self._b(self.bDict[syst]['n_recoil_jets_fit'])[0] = len(recoil_jets)
                        
                        # higgs mass uncertainty
                        cov_fit = fitter.getCovMatrixFit()
                        dmH_by_dpt1 = ( (fit_result_H.X())*np.cos(fit_result_j1.Phi()) + (fit_result_H.Y())*np.sin(fit_result_j1.Phi()) )/fit_result_H.M()
                        dmH_by_dpt2 = ( (fit_result_H.X())*np.cos(fit_result_j2.Phi()) + (fit_result_H.Y())*np.sin(fit_result_j2.Phi()) )/fit_result_H.M()
                        self._b(self.bDict[syst]['H_mass_sigma_fit'])[0] = ( dmH_by_dpt1**2          * cov_fit(0,0)
                                                + dmH_by_dpt2**2          * cov_fit(3,3)
                                                + dmH_by_dpt1*dmH_by_dpt2 * cov_fit(0,3) )**.5

                        # Higgs pT uncertainty
                        self._b(self.bDict[syst]['hJets_pt_0_sigma_fit'])[0] = cov_fit(0,0)**0.5
                        self._b(self.bDict[syst]['hJets_pt_1_sigma_fit'])[0] = cov_fit(3,3)**0.5
                        self._b(self.bDict[syst]['H_pt_corr_fit'])[0]        = cov_fit(0,3) / ( (cov_fit(0,0)*cov_fit(3,3))**0.5 )
                        self._b(self.bDict[syst]['H_pt_sigma_fit'])[0]       = (cov_fit(0,0) + cov_fit(3,3) + 2.0*cov_fit(0,3) )**0.5

                        self._b(self.bDict[syst]['llbb_pt_fit'])[0]          = llbb.Pt()
                        self._b(self.bDict[syst]['llbb_phi_fit'])[0]         = llbb.Phi()
                        self._b(self.bDict[syst]['llbb_eta_fit'])[0]         = llbb.Eta()
                        self._b(self.bDict[syst]['llbb_mass_fit'])[0]        = llbb.M()
                        self._b(self.bDict[syst]['llbbr_pt_fit'])[0]          = llbbr.Pt()
                        self._b(self.bDict[syst]['llbbr_phi_fit'])[0]         = llbbr.Phi()
                        self._b(self.bDict[syst]['llbbr_eta_fit'])[0]         = llbbr.Eta()
                        self._b(self.bDict[syst]['llbbr_mass_fit'])[0]        = llbbr.M()
                    else:
                        if fit_result_H.M() <= 0:
                            self.count("kinfit_failure_mass_negative")
                        else:
                            self.count("kinfit_failure_convergence")

                        # fallback
                        self._b(self.bDict[syst]['H_pt_fit'])[0]          = tree.H_pt
                        self._b(self.bDict[syst]['H_eta_fit'])[0]         = tree.H_eta
                        self._b(self.bDict[syst]['H_phi_fit'])[0]         = tree.H_phi
                        self._b(self.bDict[syst]['H_mass_fit'])[0]        = tree.H_mass
                        self._b(self.bDict[syst]['H_mass_fit_noJetMass'])[0] = tree.H_mass
                        self._b(self.bDict[syst]['V_pt_fit'])[0]          = tree.V_pt
                        self._b(self.bDict[syst]['V_eta_fit'])[0]         = tree.V_eta
                        self._b(self.bDict[syst]['V_phi_fit'])[0]         = tree.V_phi
                        self._b(self.bDict[syst]['V_mass_fit'])[0]        = tree.V_mass
                        self._b(self.bDict[syst]['HVdPhi_fit'])[0]        = abs(ROOT.TVector2.Phi_mpi_pi(tree.H_phi - tree.V_phi))
                        self._b(self.bDict[syst]['jjVPtRatio_fit'])[0]    = tree.H_pt/tree.V_pt
                        self._b(self.bDict[syst]['hJets_pt_0_fit'])[0]    = pt_reg[hJidx[0]] 
                        self._b(self.bDict[syst]['hJets_pt_1_fit'])[0]    = pt_reg[hJidx[1]]

                        self._b(self.bDict[syst]['n_recoil_jets_fit'])[0] = -1 
                        self._b(self.bDict[syst]['H_mass_sigma_fit'])[0]  = -1

                        self._b(self.bDict[syst]['hJets_pt_0_sigma_fit'])[0] = -1
                        self._b(self.bDict[syst]['hJets_pt_1_sigma_fit'])[0] = -1
                        self._b(self.bDict[syst]['H_pt_corr_fit'])[0]        = -1
                        self._b(self.bDict[syst]['H_pt_sigma_fit'])[0]       = -1

                        self._b(self.bDict[syst]['llbb_pt_fit'])[0]          = -99
                        self._b(self.bDict[syst]['llbb_phi_fit'])[0]         = -99
                        self._b(self.bDict[syst]['llbb_eta_fit'])[0]         = -99
                        self._b(self.bDict[syst]['llbb_mass_fit'])[0]        = -99
                        self._b(self.bDict[syst]['llbbr_pt_fit'])[0]          = -99
                        self._b(self.bDict[syst]['llbbr_phi_fit'])[0]         = -99
                        self._b(self.bDict[syst]['llbbr_eta_fit'])[0]         = -99 
                        self._b(self.bDict[syst]['llbbr_mass_fit'])[0]        = -99

                else:
                    # no two resolved jets
                    self._b(self.bDict[syst]['H_pt_fit'])[0]          = -1 
                    self._b(self.bDict[syst]['H_eta_fit'])[0]         = -1 
                    self._b(self.bDict[syst]['H_phi_fit'])[0]         = -1 
                    self._b(self.bDict[syst]['H_mass_fit'])[0]        = -1 
                    self._b(self.bDict[syst]['H_mass_fit_noJetMass'])[0] = -1 
                    self._b(self.bDict[syst]['V_pt_fit'])[0]          = -1 
                    self._b(self.bDict[syst]['V_eta_fit'])[0]         = -1 
                    self._b(self.bDict[syst]['V_phi_fit'])[0]         = -1 
                    self._b(self.bDict[syst]['V_mass_fit'])[0]        = -1 
                    self._b(self.bDict[syst]['HVdPhi_fit'])[0]        = -1 
                    self._b(self.bDict[syst]['jjVPtRatio_fit'])[0]    = -1 
                    self._b(self.bDict[syst]['hJets_pt_0_fit'])[0]    = -1 
                    self._b(self.bDict[syst]['hJets_pt_1_fit'])[0]    = -1 

                    self._b(self.bDict[syst]['n_recoil_jets_fit'])[0] = -1 
                    self._b(self.bDict[syst]['H_mass_sigma_fit'])[0]  = -1

                    self._b(self.bDict[syst]['hJets_pt_0_sigma_fit'])[0] = -1
                    self._b(self.bDict[syst]['hJets_pt_1_sigma_fit'])[0] = -1
                    self._b(self.bDict[syst]['H_pt_corr_fit'])[0]        = -1
                    self._b(self.bDict[syst]['H_pt_sigma_fit'])[0]       = -1

                    self._b(self.bDict[syst]['llbb_pt_fit'])[0]          = -99
                    self._b(self.bDict[syst]['llbb_phi_fit'])[0]         = -99
                    self._b(self.bDict[syst]['llbb_eta_fit'])[0]         = -99
                    self._b(self.bDict[syst]['llbb_mass_fit'])[0]        = -99
                    self._b(self.bDict[syst]['llbbr_pt_fit'])[0]          = -99
                    self._b(self.bDict[syst]['llbbr_phi_fit'])[0]         = -99
                    self._b(self.bDict[syst]['llbbr_eta_fit'])[0]         = -99 
                    self._b(self.bDict[syst]['llbbr_mass_fit'])[0]        = -99
Пример #4
0
class METXY(AddCollectionsModule):

    def __init__(self, year):
        super(METXY, self).__init__()
        self.debug = 'XBBDEBUG' in os.environ
        self.year  = int(year)
        self.quickloadWarningShown = False

    def customInit(self, initVars):
        self.sampleTree  = initVars['sampleTree']
        self.sample      = initVars['sample']
        self.config      = initVars['config']
        self.xbbConfig   = XbbConfigTools(self.config)
        self.systematics = self.xbbConfig.getJECuncertainties(step='METXY')
        self.METsystematics = [x for x in self.systematics if 'jerReg' not in x] + ['unclustEn']

        # load METXYCorr_Met_MetPhi from VHbb namespace
        VHbbNameSpace = self.config.get('VHbbNameSpace', 'library')
        ROOT.gSystem.Load(VHbbNameSpace)

        self.MET_Pt     = array.array('f', [0.0])
        self.MET_Phi    = array.array('f', [0.0])
        self.sampleTree.tree.SetBranchAddress("MET_Pt", self.MET_Pt)
        self.sampleTree.tree.SetBranchAddress("MET_Phi", self.MET_Phi)

        self.addBranch("MET_Pt_uncorrected")
        self.addBranch("MET_Phi_uncorrected")

        if self.sample.isMC():
            self.MET_Pt_syst  = {}
            self.MET_Phi_syst = {}
            for syst in self.METsystematics:
                self.MET_Pt_syst[syst] = {}
                self.MET_Phi_syst[syst] = {}
                for Q in self._variations(syst):
                    self.MET_Pt_syst[syst][Q]  = array.array('f', [0.0])
                    self.MET_Phi_syst[syst][Q] = array.array('f', [0.0])
                    self.sampleTree.tree.SetBranchAddress("MET_pt_"+syst+Q, self.MET_Pt_syst[syst][Q])
                    self.sampleTree.tree.SetBranchAddress("MET_phi_"+syst+Q, self.MET_Phi_syst[syst][Q])

                    self.addBranch("MET_pt_uncorrected_"+syst+Q)
                    self.addBranch("MET_phi_uncorrected_"+syst+Q)


    def processEvent(self, tree):
        if not self.hasBeenProcessed(tree):
            self.markProcessed(tree)

            # backup uncorrected branches
            self._b('MET_Pt_uncorrected')[0]  = tree.MET_Pt
            self._b('MET_Phi_uncorrected')[0] = tree.MET_Phi

            MET_Pt_corrected, MET_Phi_corrected = ROOT.VHbb.METXYCorr_Met_MetPhi(tree.MET_Pt, tree.MET_Phi, tree.run, self.year, self.sample.isMC(), tree.PV_npvs)

            # overwrite MET_Pt, MET_Phi branches
            self.MET_Pt[0]  = MET_Pt_corrected
            self.MET_Phi[0] = MET_Phi_corrected

            if self.sample.isMC():
                for syst in self.METsystematics:
                    for Q in self._variations(syst):
                        
                        # backup uncorrected branches
                        self._b("MET_pt_uncorrected_"+syst+Q)[0]  = self.MET_Pt_syst[syst][Q][0]
                        self._b("MET_phi_uncorrected_"+syst+Q)[0] = self.MET_Phi_syst[syst][Q][0]

                        MET_Pt_corrected, MET_Phi_corrected = ROOT.VHbb.METXYCorr_Met_MetPhi(self.MET_Pt_syst[syst][Q][0], self.MET_Phi_syst[syst][Q][0], tree.run, self.year, self.sample.isMC(), tree.PV_npvs)

                        # overwrite MET_Pt, MET_Phi branches
                        self.MET_Pt_syst[syst][Q][0]  = MET_Pt_corrected
                        self.MET_Phi_syst[syst][Q][0] = MET_Phi_corrected
            
            # formulas by default reload the branch content when evaluating the first instance of the object!
            # SetQuickLoad(1) turns off this behavior
            for formulaName, treeFormula in self.sampleTree.formulas.items():
                if 'MET' in formulaName:
                    if not self.quickloadWarningShown:
                        self.quickloadWarningShown = True
                        print("INFO: SetQuickLoad(1) called for formula:", formulaName)
                        print("INFO: -> EvalInstance(0) on formulas will not re-load branches but will take values from memory, which might have been modified by this module.") 
                    treeFormula.SetQuickLoad(1)
Пример #5
0
class GetTopMass(AddCollectionsModule):
    def __init__(self,
                 sample=None,
                 nano=False,
                 propagateJES=False,
                 METmethod=2,
                 useHJets=0,
                 minbTag=0.5,
                 branchName='top_mass',
                 addBoostSystematics=False,
                 addMinMax=False,
                 puIdCut=6,
                 jetIdCut=4):
        super(GetTopMass, self).__init__()
        self.version = 2
        self.nano = nano
        self.lastEntry = -1
        self.branchName = branchName
        self.METmethod = METmethod  #Check the getJetPtMass function for the different methods
        self.useHJets = useHJets  # Using only H jets if useHJets == 1
        self.minbTag = minbTag
        self.propagateJES = propagateJES
        self.addBoostSystematics = addBoostSystematics
        self.addMinMax = addMinMax
        self.puIdCut = puIdCut
        self.jetIdCut = jetIdCut

    def dependencies(self):
        return {'VHbbSelection': 5}

    def customInit(self, initVars):
        self.sample = initVars['sample']
        self.config = initVars['config']
        self.xbbConfig = XbbConfigTools(self.config)

        # Branch names from config
        self.brJetPtReg = "Jet_PtReg"
        self.brJetPtNom = "Jet_Pt"

        self.Jet_btag = self.config.get('General', 'Jet_btag')
        self.tagidx = self.config.get('General', 'hJidx')

        if self.config.has_option('General',
                                  'eIdx') and self.config.has_option(
                                      'General', 'muIdx'):
            self.eIdx = self.config.get('General', 'eIdx')
            self.muIdx = self.config.get('General', 'muIdx')
        else:
            print "WARNING: eIdx and muIdx not defined in [General]! Using default lepton index: \'vLidx\' "
            self.eIdx = "vLidx"
            self.muIdx = "vLidx"

        self.dataset = self.config.get('General', 'dataset')

        self.METpt = 'MET_Pt'
        self.METphi = 'MET_Phi'

        if self.sample.isMC():
            self.brJetMassNom = "Jet_mass_nom"
        else:
            self.brJetMassNom = "Jet_mass"

        # nominal
        self.systematics = [None]

        ##########
        # add JES/JER systematics
        ##########
        # only defined for nano at the moment

        if self.propagateJES and self.nano:
            self.jetSystematics = self.xbbConfig.getJECuncertainties(
                step='Top') + ['unclustEn']

            if self.addBoostSystematics:
                self.jetSystematics += ['jms']
                self.jetSystematics += ['jmr']
            if self.sample.isMC():
                self.systematics += self.jetSystematics

        for syst in self.systematics:
            for Q in self._variations(syst):
                self.addBranch(self._v(self.branchName, syst, Q))

    def processEvent(self, tree):

        if not self.hasBeenProcessed(tree):
            self.markProcessed(tree)

            # leptons/MET
            treeMETpt, treeMETphi = self.getMET(tree)
            lep = TLorentzVector()
            met = TLorentzVector()
            if not self.nano:
                lep.SetPtEtaPhiM(tree.vLeptons_new_pt[0],
                                 tree.vLeptons_new_eta[0],
                                 tree.vLeptons_new_phi[0],
                                 tree.vLeptons_new_mass[0])
                met.SetPtEtaPhiM(tree.met_pt, tree.met_eta, tree.met_phi,
                                 tree.met_mass)
            else:
                if tree.Vtype == 2:
                    mu1Idx = getattr(tree, self.muIdx)[0]
                    lep.SetPtEtaPhiM(tree.Muon_pt[mu1Idx],
                                     tree.Muon_eta[mu1Idx],
                                     tree.Muon_phi[mu1Idx],
                                     tree.Muon_mass[mu1Idx])
                if tree.Vtype == 3:
                    e1Idx = getattr(tree, self.eIdx)[0]
                    lep.SetPtEtaPhiM(tree.Electron_pt[e1Idx],
                                     tree.Electron_eta[e1Idx],
                                     tree.Electron_phi[e1Idx],
                                     tree.Electron_mass[e1Idx])
                met.SetPtEtaPhiM(treeMETpt, 0, treeMETphi, 0)

            # compute top mass for all variations
            for syst in self.systematics:
                for UD in self._variations(syst):

                    # MET
                    treeMETpt, treeMETphi = self.getMET(tree, syst, UD)
                    met.SetPtEtaPhiM(treeMETpt, 0, treeMETphi, 0)

                    # jets
                    Jet_PtReg, Jet_MassReg, Jet_Pt = self.getJetPtMasses(
                        tree, syst=syst, UD=UD)

                    # closest jets
                    cJidx, cHJidx = self.closestJetIdx(tree,
                                                       lep,
                                                       syst=syst,
                                                       UD=UD,
                                                       Jet_PtReg=Jet_PtReg,
                                                       Jet_MassReg=Jet_MassReg,
                                                       Jet_Pt=Jet_Pt)
                    closestIdx = cJidx if self.useHJets == 0 else cHJidx

                    if closestIdx != -1:
                        cJet = TLorentzVector()
                        cJet.SetPtEtaPhiM(Jet_PtReg[closestIdx],
                                          tree.Jet_eta[closestIdx],
                                          tree.Jet_phi[closestIdx],
                                          Jet_MassReg[closestIdx])

                        top_mass = self.computeTopMass_new(
                            lep, met, cJet, self.METmethod)

                    elif closestIdx == -1:
                        top_mass = -99

                    self._b(self._v(self.branchName, syst, UD))[0] = top_mass

            return True

    def getResolvedJetIndicesFromTree(self, tree, syst=None, UD=None):
        indexNameSyst = (self.tagidx + '_' + syst +
                         UD) if not self._isnominal(syst) else self.tagidx
        if hasattr(tree, indexNameSyst):
            self.count('_debug_resolved_idx_syst_exists')
            return getattr(tree, indexNameSyst)
        else:
            self.count('_debug_resolved_idx_fallback_nom')
            self.count('_debug_resolved_idx_fallback_nom_for_' + str(syst))
            return getattr(tree, self.tagidx)

    ##
    # \Function EquationSolver:
    #
    # Solves 3rd degree equations
    #
    # \Author A. Orso M. Iorio
    #
    #
    # \version  $Id: EquationSolver.h,v 1.1 2013/02/27 12:18:42 degrutto Exp $
    #
    def EquationSolve(self, a, b, c, d):

        result = []

        if (a != 0):

            q = (3 * a * c - b * b) / (9 * a * a)
            r = (9 * a * b * c - 27 * a * a * d - 2 * b * b * b) / (54 * a *
                                                                    a * a)
            Delta = q * q * q + r * r

            rho = 0.
            theta = 0.

            if (Delta <= 0):
                rho = sqrt(-(q * q * q))

                theta = acos(r / rho)

                s = complex(
                    sqrt(-q) * cos(theta / 3.0),
                    sqrt(-q) * sin(theta / 3.0))
                t = complex(
                    sqrt(-q) * cos(-theta / 3.0),
                    sqrt(-q) * sin(-theta / 3.0))

            if (Delta > 0):
                #print r, sqrt(Delta)
                if (r + sqrt(Delta) > 0):
                    s = complex(pow((r + sqrt(Delta)), (1. / 3)), 0)
                else:
                    s = complex(-pow(abs((r + sqrt(Delta))), (1. / 3)), 0)
                if (r - sqrt(Delta) > 0):
                    t = complex(pow((r - sqrt(Delta)), (1. / 3)), 0)
                else:
                    t = complex(-pow(abs((r - sqrt(Delta))), (1. / 3)), 0)

            i = complex(0., 1.0)

            x1 = s + t + complex(-b / (3.0 * a), 0)
            x2 = (s + t) * complex(-0.5, 0) - complex(
                b / (3.0 * a), 0) + (s - t) * i * complex(sqrt(3) / 2.0, 0)
            x3 = (s + t) * complex(-0.5, 0) - complex(
                b / (3.0 * a), 0) - (s - t) * i * complex(sqrt(3) / 2.0, 0)

            if (abs(x1.imag) < 0.0001): result.append(x1.real)
            if (abs(x2.imag) < 0.0001): result.append(x2.real)
            if (abs(x3.imag) < 0.0001): result.append(x3.real)

            #print x1,x2,x3
            return result
        else:
            return result

        return result

    def getNu4Momentum(self, TLepton, TMET, debug=False):

        branch = -1

        Lepton = TLorentzVector()
        Lepton.SetPxPyPzE(TLepton.Px(), TLepton.Py(), TLepton.Pz(),
                          TLepton.E())
        MET = TLorentzVector()
        MET.SetPxPyPzE(TMET.Px(), TMET.Py(), 0., TMET.E())

        mW = 80.38

        result = []

        MisET2 = (MET.Px() * MET.Px() + MET.Py() * MET.Py())
        mu = (mW * mW) / 2 + MET.Px() * Lepton.Px() + MET.Py() * Lepton.Py()
        a = (mu * Lepton.Pz()) / (Lepton.Energy() * Lepton.Energy() -
                                  Lepton.Pz() * Lepton.Pz())
        a2 = pow(a, 2)
        b = (pow(Lepton.Energy(), 2.) * (MisET2) -
             pow(mu, 2.)) / (pow(Lepton.Energy(), 2) - pow(Lepton.Pz(), 2))
        pz1 = 0.
        pz2 = 0.
        pznu = 0.
        nNuSol = 0

        p4nu_rec = TLorentzVector()
        p4W_rec = TLorentzVector()
        p4b_rec = TLorentzVector()
        p4Top_rec = TLorentzVector()
        p4lep_rec = TLorentzVector()

        p4lep_rec.SetPxPyPzE(Lepton.Px(), Lepton.Py(), Lepton.Pz(),
                             Lepton.Energy())

        #print a2,b
        if (a2 - b > 0):
            root = sqrt(a2 - b)
            pz1 = a + root
            pz2 = a - root
            nNuSol = 2

            pznu = pz1
            branch = 1
            if abs(pz2) < abs(pz1):
                pznu = pz2
                branch = 2

            Enu = sqrt(MisET2 + pznu * pznu)

            p4nu_rec.SetPxPyPzE(MET.Px(), MET.Py(), pznu, Enu)

            result.append(p4nu_rec)

        else:

            ptlep = Lepton.Pt()
            pxlep = Lepton.Px()
            pylep = Lepton.Py()
            metpx = MET.Px()
            metpy = MET.Py()

            EquationA = 1.
            EquationB = -3. * pylep * mW / (ptlep)
            EquationC = mW * mW * (2 * pylep * pylep) / (
                ptlep *
                ptlep) + mW * mW - 4 * pxlep * pxlep * pxlep * metpx / (
                    ptlep * ptlep) - 4 * pxlep * pxlep * pylep * metpy / (
                        ptlep * ptlep)
            EquationD = 4. * pxlep * pxlep * mW * metpy / (
                ptlep) - pylep * mW * mW * mW / ptlep

            solutions = self.EquationSolve(EquationA, EquationB, EquationC,
                                           EquationD)

            solutions2 = self.EquationSolve(EquationA, -EquationB, EquationC,
                                            -EquationD)

            deltaMin = 14000 * 14000
            zeroValue = -mW * mW / (4 * pxlep)
            minPx = 0
            minPy = 0

            for i in range(len(solutions)):
                if (solutions[i] < 0): continue
                p_x = (solutions[i] * solutions[i] - mW * mW) / (4 * pxlep)
                p_y = (mW * mW * pylep + 2 * pxlep * pylep * p_x -
                       mW * ptlep * solutions[i]) / (2 * pxlep * pxlep)
                Delta2 = (p_x - metpx) * (p_x - metpx) + (p_y - metpy) * (
                    p_y - metpy)

                if (Delta2 < deltaMin and Delta2 > 0):
                    deltaMin = Delta2
                    minPx = p_x
                    minPy = p_y
                    branch = 3

            for i in range(len(solutions2)):
                if (solutions2[i] < 0): continue
                p_x = (solutions2[i] * solutions2[i] - mW * mW) / (4 * pxlep)
                p_y = (mW * mW * pylep + 2 * pxlep * pylep * p_x +
                       mW * ptlep * solutions2[i]) / (2 * pxlep * pxlep)
                Delta2 = (p_x - metpx) * (p_x - metpx) + (p_y - metpy) * (
                    p_y - metpy)
                if (Delta2 < deltaMin and Delta2 > 0):
                    deltaMin = Delta2
                    minPx = p_x
                    minPy = p_y
                    branch = 4

            pyZeroValue = (mW * mW * pxlep + 2 * pxlep * pylep * zeroValue)
            delta2ZeroValue = (zeroValue - metpx) * (zeroValue - metpx) + (
                pyZeroValue - metpy) * (pyZeroValue - metpy)

            if (deltaMin == 14000 * 14000):
                if debug:
                    branch = 6
                    return TLorentzVector(0, 0, 0, 0), branch
                else:
                    return TLorentzVector(0, 0, 0, 0)

            if (delta2ZeroValue < deltaMin):
                deltaMin = delta2ZeroValue
                minPx = zeroValue
                minPy = pyZeroValue
                branch = 5

            mu_Minimum = (mW * mW) / 2 + minPx * pxlep + minPy * pylep
            a_Minimum = (mu_Minimum * Lepton.Pz()) / (
                Lepton.Energy() * Lepton.Energy() - Lepton.Pz() * Lepton.Pz())
            pznu = a_Minimum

            Enu = sqrt(minPx * minPx + minPy * minPy + pznu * pznu)
            p4nu_rec.SetPxPyPzE(minPx, minPy, pznu, Enu)
            result.append(p4nu_rec)

        if debug:
            return result[0], branch
        else:
            return result[0]

#    def computeTopMass(self, lep, met, jets, jetsIdx):

    def computeTopMass(self, lep, met, jets):

        #neutrino = self.getNu4Momentum(lep, met)
        bjet = TLorentzVector()
        minDR = 99
        closeIdx = -1
        for i, jet in enumerate(jets):
            if (
                    jet.Pt() > 30
            ):  # and jet.bTagCSV > CSVL and jet.puID > 0 and jet.Id > 0? how?
                dR = jet.DeltaR(lep)
                if (dR < minDR):
                    minDR = dR
                    bjet = jet
#                    closeIdx = jetsIdx[i]
        if (bjet.Pt() <= 0):
            return -99
#        print "Old closest idx: {}".format(closeIdx)
        top = lep + met + bjet
        return top.M()

    def getJetPtMasses(self, tree, syst=None, UD=None):

        JetPtReg = np.array(getattr(tree, self.brJetPtReg))
        JetPtNom = np.array(getattr(tree, self.brJetPtNom))
        JetMassNom = np.array(getattr(tree, self.brJetMassNom))

        if self._isnominal(syst) or syst.startswith('unclustEn'):
            JetPt = JetPtReg
            JetMass = JetMassNom * JetPtReg / JetPtNom
        elif 'jerReg' in syst:
            JetPtSys = np.array(getattr(tree, "Jet_Pt" + syst[3:] + UD))
            JetPt = JetPtSys
            JetMass = JetMassNom * JetPtSys / JetPtNom
        else:
            JetPtSys = np.array(getattr(tree, "Jet_pt" + "_" + syst + UD))
            JetMassSys = np.array(getattr(tree, "Jet_mass" + "_" + syst + UD))
            JetPt = JetPtSys * JetPtReg / JetPtNom
            JetMass = JetMassSys * JetPtReg / JetPtNom
        return JetPt, JetMass, JetPtNom

#    def getJetPtMass(self, tree, i, variation=None):
#
#        JetPtReg = getattr(tree, self.brJetPtReg)[i]
#        JetPtNom = getattr(tree,self.brJetPtNom)[i]
#        JetMassNom = getattr(tree,self.brJetMassNom)[i]
#
#        if variation is None or variation.startswith('unclustEn'):
#           JetPt =  JetPtReg
#           JetMass = JetMassNom * JetPtReg /JetPtNom
#
#        elif 'jerReg' in variation:
#           Q = "Up" if "Up" in variation else "Down"
#           JetPtSys = getattr(tree,"Jet_PtReg" + Q)[i]
#
#           JetPt =  JetPtSys
#           JetMass = JetMassNom * JetPtSys / JetPtNom
#
#        else:
#           JetPtSys = getattr(tree,"Jet_pt" + "_" + variation)[i]
#           JetMassSys = getattr(tree, "Jet_mass" + "_" + variation)[i]
#
#           JetPt =  JetPtSys * JetPtReg / JetPtNom
#           JetMass = JetMassSys * JetPtReg / JetPtNom
#           print "{}: {}, {}: {}".format("Jet_pt" + "_" + variation,JetPtSys,"Jet_mass" + "_" + variation,JetMassSys)
#           print "JetPt {}, JetMass: {}".format(JetPt,JetMass)
#        return JetPt, JetMass

    def getMET(self, tree, syst=None, UD=None):
        if not self._isnominal(syst) and not syst.startswith('jerReg'):
            treeMETpt = getattr(tree, "MET_pt_" + syst + UD)
            treeMETphi = getattr(tree, "MET_phi_" + syst + UD)
        else:
            treeMETpt = getattr(tree, self.METpt)
            treeMETphi = getattr(tree, self.METphi)
        return treeMETpt, treeMETphi

    def closestJetIdx(self,
                      tree,
                      lep,
                      syst=None,
                      UD=None,
                      Jet_PtReg=None,
                      Jet_MassReg=None,
                      Jet_Pt=None):
        #neutrino = self.getNu4Momentum(lep, met)

        minDR = 999
        minHDR = 999

        ClosestJIdx = -1
        ClosestHJIdx = -1

        # reuse pt/mass already obtained from tree if available
        # if one is missing, recompute them again
        if Jet_PtReg is None or Jet_MassReg is None or Jet_Pt is None:
            Jet_PtReg_r, Jet_MassReg_r, Jet_Pt_r = self.getJetPtMasses(
                tree, syst, UD)
            Jet_PtReg = Jet_PtReg_r if Jet_PtReg is None else Jet_PtReg
            Jet_MassReg = Jet_MassReg_r if Jet_MassReg is None else Jet_MassReg
            Jet_Pt = Jet_Pt_r if Jet_Pt is None else Jet_Pt

        # b-tagger
        jetBtags = getattr(tree, self.Jet_btag)

        Jet_lepFilter = tree.Jet_lepFilter
        Jet_jetId = tree.Jet_jetId
        Jet_puId = tree.Jet_puId
        Jet_eta = tree.Jet_eta
        Jet_phi = tree.Jet_phi
        nJet = tree.nJet

        # selected jet indices
        hJidx0, hJidx1 = self.getResolvedJetIndicesFromTree(tree, syst, UD)

        for i in range(nJet):
            #if temPt < 30 or tembTag < self.minbTag or not (tree.Jet_lepFilter): continue

            if Jet_PtReg[i] >= 30 and jetBtags[
                    i] >= self.minbTag and tree.Jet_lepFilter[
                        i] and tree.Jet_jetId[i] > self.jetIdCut and (
                            tree.Jet_puId[i] > self.puIdCut
                            or Jet_Pt[i] > 50.0):

                tempJet = TLorentzVector()
                tempJet.SetPtEtaPhiM(Jet_PtReg[i], tree.Jet_eta[i],
                                     tree.Jet_phi[i], Jet_MassReg[i])
                dR = tempJet.DeltaR(lep)
                #           print "Jetidx: {}, dR = {}".format(i,dR)

                if dR < minDR:
                    minDR = dR
                    ClosestJIdx = i

                if (i == hJidx0 or i == hJidx1) and dR < minHDR:
                    minHDR = dR
                    ClosestHJIdx = i

#        print "hJidx0 = {}, hJidx1 = {}".format(self.hJidx0,self.hJidx1)
        return ClosestJIdx, ClosestHJIdx

    def computeTopMass_new(self, lep, met, jet, METmethod):
        #neutrino = self.getNu4Momentum(lep, met)

        new_top = TLorentzVector()
        jet_transverse = TLorentzVector()
        lep_transverse = TLorentzVector()

        if METmethod == 1:
            jet_transverse.SetPxPyPzE(jet.Px(), jet.Py(), 0,
                                      sqrt((jet.M())**2 + (jet.Pt())**2))
            lep_transverse.SetPxPyPzE(lep.Px(), lep.Py(), 0,
                                      sqrt((lep.M())**2 + (lep.Pt())**2))

            new_top = jet_transverse + lep_transverse + met
        elif METmethod == 2:
            neutrino = TLorentzVector()
            neutrino = self.getNu4Momentum(lep, met)
            new_top = lep + jet + neutrino
        elif METmethod == 3:
            new_top = lep + jet + met

        return new_top.M()
Пример #6
0
class mSD_scale_res_shift(AddCollectionsModule):
    def __init__(self, year=None):
        super(mSD_scale_res_shift, self).__init__()
        self.debug = 'XBBDEBUG' in os.environ

        self.year = year if type(year) == str else str(year)
        self.scale_params = {
            '2018': [0.9, 0.01, 0.9, 0.01],
        }
        if self.year not in self.scale_params.keys():
            raise Exception(
                'Please specify the year correspoding to the correction!')

        self.scale, self.scale_err, self.res, self.res_err = self.scale_params[
            self.year]

        self.scale_up = self.scale + self.scale_err
        self.scale_down = self.scale - self.scale_err
        self.res_up = self.res + self.res_err
        self.res_down = self.res - self.res_err

    def customInit(self, initVars):
        self.sampleTree = initVars['sampleTree']
        self.isData = initVars['sample'].isData()
        self.sample = initVars['sample']
        self.config = initVars['config']
        self.xbbConfig = XbbConfigTools(self.config)
        self.systematics = self.xbbConfig.getJECuncertainties()
        self.systematicsBoosted = [
            x for x in self.systematics if 'jerReg' not in x
        ] + ['jms', 'jmr']
        self.maxnFatJet = 256

        if self.sample.isMC():

            self.FatJet_Msoftdrop = array.array('f', [0.0] * self.maxnFatJet)
            self.FatJet_msoftdrop_nom = array.array('f',
                                                    [0.0] * self.maxnFatJet)

            self.sampleTree.tree.SetBranchAddress("FatJet_Msoftdrop",
                                                  self.FatJet_Msoftdrop)
            self.sampleTree.tree.SetBranchAddress("FatJet_msoftdrop_nom",
                                                  self.FatJet_msoftdrop_nom)

            #create backup branches
            self.addVectorBranch("FatJet_MsoftdropOld",
                                 default=0.0,
                                 branchType='f',
                                 length=self.maxnFatJet,
                                 leaflist="FatJet_MsoftdropOld[nFatJet]/F")
            self.addVectorBranch("FatJet_msoftdrop_nomOld",
                                 default=0.0,
                                 branchType='f',
                                 length=self.maxnFatJet,
                                 leaflist="FatJet_msoftdrop_nomOld[nFatJet]/F")

            self.FatJet_msoftdrop_syst = {}
            for syst in self.systematicsBoosted:
                self.FatJet_msoftdrop_syst[syst] = {}
                for Q in self._variations(syst):

                    self.FatJet_msoftdrop_syst[syst][Q] = array.array(
                        'f', [0.0] * self.maxnFatJet)
                    self.sampleTree.tree.SetBranchAddress(
                        "FatJet_msoftdrop_" + syst + Q,
                        self.FatJet_msoftdrop_syst[syst][Q])

                    #backup branches
                    self.addVectorBranch(
                        'FatJet_msoftdrop_' + syst + Q + 'Old',
                        default=0.0,
                        branchType='f',
                        length=self.maxnFatJet,
                        leaflist='FatJet_msoftdrop_' + syst + Q +
                        'Old[nFatJet]/F')

    def processEvent(self, tree):
        if not self.hasBeenProcessed(tree) and self.sample.isMC():
            self.markProcessed(tree)

            nFatJet = tree.nFatJet

            if self.sample.isMC():
                for i in range(nFatJet):

                    #fill backup branches
                    self._b(
                        "FatJet_MsoftdropOld")[i] = self.FatJet_Msoftdrop[i]
                    self._b("FatJet_msoftdrop_nomOld"
                            )[i] = self.FatJet_msoftdrop_nom[i]

                    #overwrite branches
                    self.FatJet_Msoftdrop[i] = self._b(
                        'FatJet_MsoftdropOld')[i] * self.scale
                    self.FatJet_msoftdrop_nom[i] = self._b(
                        "FatJet_msoftdrop_nomOld")[i] * self.scale

                    for syst in self.systematicsBoosted:
                        for Q in self._variations(syst):

                            # fill backup branches
                            self._b("FatJet_msoftdrop_" + syst + Q + 'Old')[
                                i] = self.FatJet_msoftdrop_syst[syst][Q][i]

                            # overwrite branches
                            if syst == 'jms':
                                if Q == 'Up':
                                    self.FatJet_msoftdrop_syst[syst][Q][
                                        i] = self._b("FatJet_msoftdrop_nomOld"
                                                     )[i] * self.scale_up
                                else:
                                    self.FatJet_msoftdrop_syst[syst][Q][
                                        i] = self._b("FatJet_msoftdrop_nomOld"
                                                     )[i] * self.scale_down
                            elif syst == 'jmr':
                                self.FatJet_msoftdrop_syst[syst][Q][
                                    i] = self._b("FatJet_msoftdrop_" + syst +
                                                 Q + 'Old')[i] * self.res

                            else:
                                self.FatJet_msoftdrop_syst[syst][Q][
                                    i] = self._b("FatJet_msoftdrop_" + syst +
                                                 Q + 'Old')[i] * self.scale
Пример #7
0
class VHbbSelection(AddCollectionsModule):

    # original 2017: puIdCut=0, jetIdCut=-1
    # now:           puIdCut=6, jetIdCut=4
    def __init__(self,
                 debug=False,
                 year="none",
                 channels=["Wln", "Zll", "Znn"],
                 puIdCut=6,
                 jetIdCut=4,
                 debugEvents=[],
                 isoWen=0.06,
                 isoWmn=0.06,
                 isoZmm=0.25,
                 isoZee=0.15,
                 recomputeVtype=False,
                 btagMaxCut=None,
                 idWmn="default",
                 idWmnCut=1,
                 idWen="default",
                 idWenCut=1,
                 idZmm="default",
                 idZmmCut=1,
                 idZee="default",
                 idZeeCut=1,
                 skipJetSelection=False,
                 vpt0lep=170.0,
                 vpt1lep=150.0,
                 vpt2lep=75.0):
        super(VHbbSelection, self).__init__()
        self.debug = debug or 'XBBDEBUG' in os.environ
        self.version = 7
        self.skipJetSelection = skipJetSelection
        self.stats = {}
        self.year = year
        self.channels = channels
        self.debugEvents = debugEvents
        self.puIdCut = puIdCut
        self.jetIdCut = jetIdCut
        self.isoWen = isoWen
        self.isoWmn = isoWmn
        self.isoZee = isoZee
        self.isoZmm = isoZmm
        self.idWmn = idWmn
        self.idWmnCut = idWmnCut
        self.idWen = idWen
        self.idWenCut = idWenCut
        self.idZmm = idZmm
        self.idZmmCut = idZmmCut
        self.idZee = idZee
        self.idZeeCut = idZeeCut
        self.vpt0lep = vpt0lep
        self.vpt1lep = vpt1lep
        self.vpt2lep = vpt2lep
        self.recomputeVtype = recomputeVtype
        self.btagMaxCut = btagMaxCut
        # only use puId below this pT:
        self.puIdMaxPt = 50.0
        # run QCD estimation analysis with inverted isolation cuts (CAREFUL!)
        if self.recomputeVtype:
            print("\x1b[45m\x1b[37m" + "-" * 160 + "\x1b[0m")
            print("\x1b[45m\x1b[37m" + "-" * 160 + "\x1b[0m")
            print(
                "\x1b[45m\x1b[37m RECOMPUTE Vtype, custom definitions might be used!\x1b[0m"
            )
            print("\x1b[45m\x1b[37m" + "-" * 160 + "\x1b[0m")
            print("\x1b[45m\x1b[37m" + "-" * 160 + "\x1b[0m")

    def customInit(self, initVars):
        self.sampleTree = initVars['sampleTree']
        self.isData = initVars['sample'].isData()
        self.sample = initVars['sample']
        self.config = initVars['config']
        self.xbbConfig = XbbConfigTools(self.config)

        # settings
        # originally Electron_mvaFall17V1Iso_WP80 was used for 2017, which was called Electron_mvaFall17Iso_WP80
        # consistently available for all 3 years: Electron_mvaFall17V2Iso
        self.electronID = {
            1: {
                "2018": "Electron_mvaFall17V2Iso_WP80",
                "2017": "Electron_mvaFall17V2Iso_WP80",
                #"2016": "Electron_mvaSpring16GP_WP80",
                "2016": "Electron_mvaFall17V2Iso_WP80",
            },
            2: {
                "2018": "Electron_mvaFall17V2Iso_WP90",
                "2017": "Electron_mvaFall17V2Iso_WP90",
                #"2016": "Electron_mvaSpring16GP_WP90",
                "2016": "Electron_mvaFall17V2Iso_WP90",
            }
        }

        # default lepton IDs for backward compatibility
        if self.idWen == "default":
            self.idWen = self.electronID[1][self.year]
        if self.idZee == "default":
            self.idZee = self.electronID[2][self.year]
        if self.idWmn == "default":
            # cut-based muon ID
            self.idWmn = "Muon_tightId"
        if self.idZmm == "default":
            self.idZmm = None

        # updated to july 2018 Jet/MET recommendations
        # reference: https://twiki.cern.ch/twiki/bin/viewauth/CMS/MissingETOptionalFiltersRun2
        if self.year == "2016":
            self.metFilters = [
                "Flag_goodVertices", "Flag_globalSuperTightHalo2016Filter",
                "Flag_HBHENoiseFilter", "Flag_HBHENoiseIsoFilter",
                "Flag_EcalDeadCellTriggerPrimitiveFilter",
                "Flag_BadPFMuonFilter"
            ]
        elif self.year in ["2017", "2018"]:
            self.metFilters = [
                "Flag_goodVertices", "Flag_globalSuperTightHalo2016Filter",
                "Flag_HBHENoiseFilter", "Flag_HBHENoiseIsoFilter",
                "Flag_EcalDeadCellTriggerPrimitiveFilter",
                "Flag_BadPFMuonFilter", "Flag_ecalBadCalibFilter"
            ]
        if self.isData:
            self.metFilters.append("Flag_eeBadScFilter")

        # main tagger (-> hJidx)
        self.taggerName = self.config.get('General', 'Jet_btag')

        self.jetDefinitions = [
            {
                'taggerName': self.taggerName
            },
        ]

        self.btagWPs = {
            "2018": {
                'Jet_btagDeepB': {
                    'loose': 0.1241,
                    'medium': 0.4184,
                    'tight': 0.7527,
                    'none': -1.0,
                },
                'Jet_btagDeepFlavB': {
                    'loose': 0.0494,
                    'medium': 0.2770,
                    'tight': 0.7264,
                    'none': -1.0,
                },
            },
            "2017": {
                'Jet_btagDeepB': {
                    'loose': 0.1522,
                    'medium': 0.4941,
                    'tight': 0.8001,
                    'none': -1.0,
                },
                'Jet_btagDeepFlavB': {
                    'loose': 0.0521,
                    'medium': 0.3033,
                    'tight': 0.7489,
                    'none': -1.0,
                },
            },
            "2016": {
                'Jet_btagDeepB': {
                    'loose': 0.2217,
                    'medium': 0.6321,
                    'tight': 0.8953,
                    'none': -1.0,
                },
                'Jet_btagDeepFlavB': {
                    'loose': 0.0614,
                    'medium': 0.3093,
                    'tight': 0.7221,
                    'none': -1.0,
                },
            },
        }
        # for main tagger, for others use self.btagWPs below
        self.btagWP = self.btagWPs[self.year][self.taggerName]

        if self.year == "2018":
            self.HltPaths = {
                'Znn': ['HLT_PFMET120_PFMHT120_IDTight'],
                'Wln': ['HLT_Ele32_WPTight_Gsf', 'HLT_IsoMu24'],
                'Zll': [
                    'HLT_Mu17_TrkIsoVVL_Mu8_TrkIsoVVL_DZ_Mass3p8',
                    'HLT_Ele23_Ele12_CaloIdL_TrackIdL_IsoVL'
                ],
            }
        elif self.year == "2017":
            self.HltPaths = {
                'Znn': [
                    'HLT_PFMET120_PFMHT120_IDTight',
                    'HLT_PFMET120_PFMHT120_IDTight_PFHT60'
                ],
                'Wln': ['HLT_Ele32_WPTight_Gsf_L1DoubleEG', 'HLT_IsoMu27'],
                'Zll': [
                    'HLT_Mu17_TrkIsoVVL_Mu8_TrkIsoVVL_DZ_Mass3p8',
                    'HLT_Mu17_TrkIsoVVL_Mu8_TrkIsoVVL_DZ_Mass8',
                    'HLT_Ele23_Ele12_CaloIdL_TrackIdL_IsoVL'
                ],
            }
        elif self.year == "2016":
            self.HltPaths = {
                'Znn': [
                    'HLT_PFMET110_PFMHT110_IDTight',
                    'HLT_PFMET120_PFMHT120_IDTight',
                    'HLT_PFMET170_NoiseCleaned', 'HLT_PFMET170_HBHECleaned',
                    'HLT_PFMET170_HBHE_BeamHaloCleaned'
                ],
                'Wln':
                ['HLT_Ele27_WPTight_Gsf', 'HLT_IsoMu24', 'HLT_IsoTkMu24'],
                'Zll': [
                    'HLT_Mu17_TrkIsoVVL_Mu8_TrkIsoVVL',
                    'HLT_Mu17_TrkIsoVVL_TkMu8_TrkIsoVVL',
                    'HLT_Mu17_TrkIsoVVL_Mu8_TrkIsoVVL_DZ',
                    'HLT_Mu17_TrkIsoVVL_TkMu8_TrkIsoVVL_DZ',
                    'HLT_Ele23_Ele12_CaloIdL_TrackIdL_IsoVL_DZ'
                ],
            }
        else:
            raise Execption("unknown year")

        # Vtype cut on datasets
        self.leptonFlav = {
            'DoubleMuon': 0,
            'DoubleEG': 1,
            'SingleMuon': 2,
            'SingleElectron': 3,
            'MET': 4,
        }

        if self.year == '2018':
            if "Zll" in self.channels:
                self.leptonFlav['EGamma'] = 1
            elif "Wlv" in self.channels:
                self.leptonFlav['EGamma'] = 3

        self.systematics = self.xbbConfig.getJECuncertainties(
            step='Preselection')
        self.METsystematics = [
            x for x in self.systematics if 'jerReg' not in x
        ] + ['unclustEn']
        self.systematicsBoosted = [
            x for x in self.systematics if 'jerReg' not in x
        ] + ['jms', 'jmr']

        self.cutFlow = [0] * 16

        # new branches to write
        self.addIntegerBranch("isZee")
        self.addIntegerBranch("isZmm")
        self.addIntegerBranch("isWmunu")
        self.addIntegerBranch("isWenu")
        self.addIntegerBranch("isZnn")

        # selected lepton indices
        self.addIntegerVectorBranch("vLidx", default=-1, length=2)

        # selected jet indices
        defaultJetDefinitions = [
            x for x in self.jetDefinitions
            if 'indexName' not in x or x['indexName'] == ''
        ]
        assert (len(defaultJetDefinitions) == 1)

        if not self.skipJetSelection:
            for jetDefinition in self.jetDefinitions:
                indexName = jetDefinition[
                    'indexName'] if 'indexName' in jetDefinition else ''
                self.addIntegerVectorBranch(
                    "hJidx{suffix}".format(suffix=indexName),
                    default=-1,
                    length=2)
                if self.sample.isMC():
                    for syst in self.systematics:
                        for UD in ['Up', 'Down']:
                            self.addIntegerVectorBranch(
                                "hJidx{suffix}_{syst}{UD}".format(
                                    suffix=indexName, syst=syst, UD=UD),
                                default=-1,
                                length=2)

            # selected fatjet index
            self.addIntegerBranch("Hbb_fjidx")
            if self.sample.isMC():
                for syst in self.systematicsBoosted:
                    for UD in ['Up', 'Down']:
                        self.addIntegerBranch("Hbb_fjidx_{syst}{UD}".format(
                            syst=syst, UD=UD))

        self.addBranch("lepMetDPhi")
        self.addBranch("V_mt")
        self.addBranch("V_pt")
        self.addBranch("V_eta")
        self.addBranch("V_phi")
        self.addBranch("V_mass")

        print "DEBUG: sample identifier:", self.sample.identifier, " lep flav", self.leptonFlav, " -> ", self.leptonFlav[
            self.sample.
            identifier] if self.sample.identifier in self.leptonFlav else "UNDEFINED LEPTON FLAVOR!!"

    def HighestPtGoodElectronsOppCharge(self,
                                        tree,
                                        min_pt,
                                        max_rel_iso,
                                        idcut,
                                        etacut,
                                        isOneLepton,
                                        electronID=None):
        indices = []
        for i in range(tree.nElectron):
            passIso = tree.Electron_pfRelIso03_all[i] < max_rel_iso
            passID = getattr(
                tree,
                electronID)[i] >= idcut if electronID is not None else True
            passAcceptance = abs(
                tree.Electron_eta[i]) < etacut and tree.Electron_pt[i] > min_pt
            if passAcceptance and passIso and passID:
                if len(indices) < 1:
                    indices.append(i)
                    if isOneLepton:
                        break
                else:
                    if tree.Electron_charge[i] * tree.Electron_charge[
                            indices[0]] < 0:
                        indices.append(i)
                        break
        return indices

    def HighestPtGoodMuonsOppCharge(self,
                                    tree,
                                    min_pt,
                                    max_rel_iso,
                                    idcut,
                                    etacut,
                                    isOneLepton,
                                    muonID=None):
        indices = []
        for i in range(tree.nMuon):
            passIso = tree.Muon_pfRelIso04_all[i] < max_rel_iso
            passID = getattr(
                tree, muonID)[i] >= idcut if muonID is not None else True

            passAcceptance = abs(
                tree.Muon_eta[i]) < etacut and tree.Muon_pt[i] > min_pt

            if passAcceptance and passIso and passID:
                if len(indices) < 1:
                    indices.append(i)
                    if isOneLepton:
                        break
                else:
                    if tree.Muon_charge[i] * tree.Muon_charge[indices[0]] < 0:
                        indices.append(i)
                        break
        return indices

    def HighestTaggerValueFatJet(self,
                                 tree,
                                 ptCut,
                                 msdCut,
                                 etaCut,
                                 taggerName,
                                 systList=None,
                                 Vphi=None,
                                 Vphi_syst=None):
        fatJetMaxTagger = []
        Hbb_fjidx = []
        Pt = []
        Msd = []
        systematics = systList if systList is not None else [None]
        nSystematics = len(systematics)

        Pt_nom = tree.FatJet_Pt
        Msd_nom = tree.FatJet_Msoftdrop
        eta = tree.FatJet_eta
        phi = tree.FatJet_phi
        lepFilter = tree.FatJet_lepFilter
        tagger = getattr(tree, taggerName)

        for syst in systematics:
            Hbb_fjidx.append(-1)
            fatJetMaxTagger.append(-999.9)

            if syst is None:
                Pt.append(Pt_nom)
                Msd.append(Msd_nom)
            elif syst in ['jmrUp', 'jmrDown', 'jmsUp', 'jmsDown']:
                # for jms,jmr the branches contain the mass itself
                Pt.append(Pt_nom)
                Msd.append(getattr(tree, "FatJet_msoftdrop_" + syst))
            else:
                # for all other variations, the branches contain a multiplicative factor... duh
                Pt_scale = getattr(tree, "FatJet_pt_{syst}".format(syst=syst))
                Pt.append(
                    [Pt_nom[i] * Pt_scale[i] for i in range(len(Pt_nom))])
                Msd_scale = getattr(
                    tree, "FatJet_msoftdrop_{syst}".format(syst=syst))
                Msd.append(
                    [Msd_nom[i] * Msd_scale[i] for i in range(len(Msd_nom))])

        for i in range(tree.nFatJet):
            for j in range(nSystematics):
                selectedVphi = Vphi_syst[systematics[j]] if (
                    Vphi_syst is not None
                    and systematics[j] in Vphi_syst) else Vphi
                #if Pt[j][i] > ptCut and abs(eta[i]) < etaCut and Msd[j][i] > msdCut and lepFilter[i]:
                if Pt[j][i] > ptCut and abs(
                        eta[i]) < etaCut and Msd[j][i] > msdCut:
                    if selectedVphi is None or abs(
                            ROOT.TVector2.Phi_mpi_pi(selectedVphi -
                                                     phi[i])) > 1.57:
                        if tagger[i] > fatJetMaxTagger[j]:
                            fatJetMaxTagger[j] = tagger[i]
                            Hbb_fjidx[j] = i

        # return list for systematic variations given and scalar otherwise
        if systList is not None:
            return Hbb_fjidx
        else:
            return Hbb_fjidx[0]

    # returns indices of the two second highest b-tagged jets passing the selection, if at least two exist
    # indices are sorted by b-tagger, descending
    def HighestTaggerValueBJets(self,
                                tree,
                                j1ptCut,
                                j2ptCut,
                                taggerName,
                                puIdCut=0,
                                jetIdCut=-1,
                                maxJetPtCut=0,
                                systList=None):
        jets = []
        indices = []
        nJet = tree.nJet
        systematics = systList if systList is not None else [None]

        jetTagger = getattr(tree, taggerName)
        Eta = tree.Jet_eta
        jetId = tree.Jet_jetId
        lepFilter = tree.Jet_lepFilter
        puId = tree.Jet_puId
        PtReg_nom = tree.Jet_PtReg
        Pt_nom = tree.Jet_Pt

        # momenta with sys variations applied
        PtReg = []
        Pt = []
        nSystematics = len(systematics)
        for syst in systematics:
            indices.append([])
            if syst is None:
                PtReg.append(PtReg_nom)
                Pt.append(Pt_nom)
            elif syst == 'jerRegUp':
                PtReg.append(tree.Jet_PtRegUp)
                Pt.append(Pt_nom)
            elif syst == 'jerRegDown':
                PtReg.append(tree.Jet_PtRegDown)
                Pt.append(Pt_nom)
            elif syst in [
                    'jerRegScaleUp', 'jerRegScaleDown', 'jerRegSmearUp',
                    'jerRegSmearDown'
            ]:
                PtReg.append(self._r('Jet_Pt' + syst[3:]))
                Pt.append(Pt_nom)
            else:
                Pt_syst = getattr(tree, "Jet_pt_" + syst)
                PtReg_syst = [
                    PtReg_nom[i] * Pt_syst[i] / Pt_nom[i] for i in range(nJet)
                ]
                Pt.append(Pt_syst)
                PtReg.append(PtReg_syst)

        for i in range(nJet):
            for j in range(nSystematics):
                pass_acceptance = PtReg[j][i] > j1ptCut and abs(Eta[i]) < 2.5
                pass_jetIDandPUIDlepFilter = (
                    puId[i] > puIdCut or Pt[j][i] > self.puIdMaxPt
                ) and jetId[i] > jetIdCut and lepFilter[i]
                if pass_acceptance and pass_jetIDandPUIDlepFilter:
                    if len(indices[j]) < 1:
                        indices[j].append(i)
                    else:
                        if jetTagger[i] > jetTagger[indices[j][0]]:
                            indices[j][0] = i
        for i in range(nJet):
            for j in range(nSystematics):
                if len(indices[j]) < 1 or i == indices[j][0]:
                    continue
                pass_acceptance = PtReg[j][i] > j2ptCut and abs(Eta[i]) < 2.5
                pass_jetIDandPUIDlepFilter = (
                    puId[i] > puIdCut or Pt[j][i] > self.puIdMaxPt
                ) and jetId[i] > jetIdCut and lepFilter[i]

                if pass_acceptance and pass_jetIDandPUIDlepFilter:
                    if len(indices[j]) < 2:
                        indices[j].append(i)
                    else:
                        if jetTagger[i] > jetTagger[indices[j][1]]:
                            indices[j][1] = i

        for j in range(nSystematics):
            if len(indices[j]) > 1:
                if jetTagger[indices[j][1]] > jetTagger[indices[j][0]]:
                    indices = [indices[j][1], indices[j][0]]

            if len(indices[j]) == 2 and max(
                    PtReg[j][indices[j][0]],
                    PtReg[j][indices[j][1]]) < maxJetPtCut:
                indices[j] = []

        if systList is not None:
            return indices
        else:
            return indices[0]

    def processEvent(self, tree):

        if not self.hasBeenProcessed(tree):
            self.markProcessed(tree)

            for n in ["isZmm", "isZee", "isWenu", "isWmunu", "isZnn"]:
                self._b(n)[0] = 0
            self._b("lepMetDPhi")[0] = -99.0
            self._b("V_mt")[0] = -99.0
            self._b("V_pt")[0] = -99.0
            self._b("V_eta")[0] = -99.0
            self._b("V_phi")[0] = -99.0
            self._b("V_mass")[0] = -99.0
            self._b("vLidx")[0] = -1
            self._b("vLidx")[1] = -1

            if not self.skipJetSelection:
                self._b("hJidx")[0] = -1
                self._b("hJidx")[1] = -1

            self.cutFlow[0] += 1

            debugEvent = [tree.run, tree.event] in self.debugEvents
            if debugEvent:
                print "DEBUG-EVENT:", tree.run, tree.event

            # TRIGGER
            triggerPassed = {
                k: any([getattr(tree, x) for x in v if hasattr(tree, x)])
                for k, v in self.HltPaths.items()
            }
            #print("------------------------------------")
            #for k,v in self.HltPaths.items():
            #    print(k,v)
            #    for x in v:
            #        print(x, hasattr(tree,x))
            #        if hasattr(tree,x):
            #            print(getattr(tree,x))

            #print(triggerPassed, "triggerPassed")
            #print(self.channels, "self.channels")
            #if not ((triggerPassed['0-lep'] and "Znn" in self.channels) or (triggerPassed['1-lep'] and "Wln" in self.channels) or (triggerPassed['2-lep'] and  "Zll" in self.channels)):

            if debugEvent:
                print "triggers:", triggerPassed

            Vtype = tree.Vtype
            if self.recomputeVtype:
                leptonSelector = vLeptonSelector(tree,
                                                 config=self.config,
                                                 isoZmm=self.isoZmm,
                                                 isoZee=self.isoZee,
                                                 isoWmn=self.isoWmn,
                                                 isoWen=self.isoWen)
                customVtype = leptonSelector.getVtype()
                if customVtype != Vtype:
                    self.count("recomputeVtype_mismatch")
                else:
                    self.count("recomputeVtype_match")
                self.count("recomputeVtype_Vtype%d" % customVtype)
                Vtype = customVtype

            isZnn = Vtype == 4 and triggerPassed['Znn']
            isWln = Vtype in [2, 3] and triggerPassed['Wln']
            isZll = Vtype in [0, 1] and triggerPassed['Zll']

            if not any([isZll, isWln, isZnn]):
                return False
            self.cutFlow[1] += 1

            # LEPTONS
            if self.sample.identifier not in self.leptonFlav or self.leptonFlav[
                    self.sample.identifier] == Vtype:
                if Vtype == 0:
                    good_muons_2lep = self.HighestPtGoodMuonsOppCharge(
                        tree,
                        min_pt=20.0,
                        max_rel_iso=self.isoZmm,
                        idcut=self.idZmmCut,
                        etacut=2.4,
                        isOneLepton=False,
                        muonID=self.idZmm)
                    if len(good_muons_2lep) > 1:
                        self._b("isZmm")[0] = 1
                        self._b("vLidx")[0] = good_muons_2lep[0]
                        self._b("vLidx")[1] = good_muons_2lep[1]
                    elif debugEvent:
                        print "DEBUG-EVENT: 2 mu event, but less than 2 good muons -> discard"
                elif Vtype == 1:
                    good_elecs_2lep = self.HighestPtGoodElectronsOppCharge(
                        tree,
                        min_pt=20.0,
                        max_rel_iso=self.isoZee,
                        idcut=self.idZeeCut,
                        etacut=2.5,
                        isOneLepton=False,
                        electronID=self.idZee)
                    if len(good_elecs_2lep) > 1:
                        self._b("isZee")[0] = 1
                        self._b("vLidx")[0] = good_elecs_2lep[0]
                        self._b("vLidx")[1] = good_elecs_2lep[1]
                    elif debugEvent:
                        print "DEBUG-EVENT: 2 e event, but less than 2 good electrons -> discard"
                elif Vtype == 2:
                    good_muons_1lep = self.HighestPtGoodMuonsOppCharge(
                        tree,
                        min_pt=25.0,
                        max_rel_iso=self.isoWmn,
                        idcut=self.idWmnCut,
                        etacut=2.4,
                        isOneLepton=True,
                        muonID=self.idWmn)
                    if len(good_muons_1lep) > 0:
                        self._b("isWmunu")[0] = 1
                        self._b("vLidx")[0] = good_muons_1lep[0]
                        self._b("vLidx")[1] = -1
                    elif debugEvent:
                        print "DEBUG-EVENT: 1 mu event, but no good muon found"
                elif Vtype == 3:
                    good_elecs_1lep = self.HighestPtGoodElectronsOppCharge(
                        tree,
                        min_pt=30.0,
                        max_rel_iso=self.isoWen,
                        idcut=self.idWenCut,
                        etacut=2.5,
                        isOneLepton=True,
                        electronID=self.idWen)
                    if len(good_elecs_1lep) > 0:
                        self._b("isWenu")[0] = 1
                        self._b("vLidx")[0] = good_elecs_1lep[0]
                        self._b("vLidx")[1] = -1
                    elif debugEvent:
                        print "DEBUG-EVENT: 1 e event, but no good electron found"
                elif Vtype == 4:
                    passMetFilters = all(
                        [getattr(tree, x) for x in self.metFilters])
                    if tree.MET_Pt > self.vpt0lep and passMetFilters:
                        self._b("isZnn")[0] = 1
                    else:
                        if debugEvent:
                            print "DEBUG-EVENT: 0 lep event, but MET criteria not passed!"
                        return False
                else:
                    return False
            else:
                return False
            self.cutFlow[2] += 1

            # CHANNEL
            if (self._b("isZmm")[0]
                    or self._b("isZee")[0]) and not ("Zll" in self.channels):
                return False
            #elif (self._b("isWmunu")[0] or self._b("isWenu")[0]) and not ("Wln" in self.channels or "Znn" in self.channels):
            # update: now 1-lepton events are only needed for 1-lepton channel (before they were also needed for 0-lepton channel for tt CR)
            elif (self._b("isWmunu")[0]
                  or self._b("isWenu")[0]) and not ("Wln" in self.channels):
                return False
            elif (self._b("isZnn")[0]) and not ("Znn" in self.channels):
                return False
            self.cutFlow[3] += 1

            # VECTOR BOSON
            Vphi_syst = {}
            any_v_sysvar_passes = False
            if self._b("isZee")[0] or self._b("isZmm")[0]:
                lep1 = ROOT.TLorentzVector()
                lep2 = ROOT.TLorentzVector()
                i1 = self._b("vLidx")[0]
                i2 = self._b("vLidx")[1]
                if self._b("isZee")[0]:
                    lep1.SetPtEtaPhiM(tree.Electron_pt[i1],
                                      tree.Electron_eta[i1],
                                      tree.Electron_phi[i1],
                                      tree.Electron_mass[i1])
                    lep2.SetPtEtaPhiM(tree.Electron_pt[i2],
                                      tree.Electron_eta[i2],
                                      tree.Electron_phi[i2],
                                      tree.Electron_mass[i2])
                elif self._b("isZmm")[0]:
                    lep1.SetPtEtaPhiM(tree.Muon_pt[i1], tree.Muon_eta[i1],
                                      tree.Muon_phi[i1], tree.Muon_mass[i1])
                    lep2.SetPtEtaPhiM(tree.Muon_pt[i2], tree.Muon_eta[i2],
                                      tree.Muon_phi[i2], tree.Muon_mass[i2])
                V = lep1 + lep2
            elif self._b("isWenu")[0] or self._b("isWmunu")[0]:
                i1 = self._b("vLidx")[0]
                if self._b("isWenu")[0]:
                    sel_lepton_pt = tree.Electron_pt[i1]
                    sel_lepton_eta = tree.Electron_eta[i1]
                    sel_lepton_phi = tree.Electron_phi[i1]
                    sel_lepton_mass = tree.Electron_mass[i1]
                elif self._b("isWmunu")[0]:
                    sel_lepton_pt = tree.Muon_pt[i1]
                    sel_lepton_eta = tree.Muon_eta[i1]
                    sel_lepton_phi = tree.Muon_phi[i1]
                    sel_lepton_mass = tree.Muon_mass[i1]
                self._b("lepMetDPhi")[0] = abs(
                    ROOT.TVector2.Phi_mpi_pi(sel_lepton_phi - tree.MET_Phi))
                #if self._b("lepMetDPhi")[0] > 2.0:
                #    return False

                MET = ROOT.TLorentzVector()
                Lep = ROOT.TLorentzVector()
                MET.SetPtEtaPhiM(tree.MET_Pt, 0.0, tree.MET_Phi, 0.0)
                Lep.SetPtEtaPhiM(sel_lepton_pt, sel_lepton_eta, sel_lepton_phi,
                                 sel_lepton_mass)
                cosPhi12 = (Lep.Px() * MET.Px() +
                            Lep.Py() * MET.Py()) / (Lep.Pt() * MET.Pt())
                self._b("V_mt")[0] = ROOT.TMath.Sqrt(2 * Lep.Pt() * MET.Pt() *
                                                     (1 - cosPhi12))

                V = MET + Lep

                if self.sample.isMC():
                    MET_syst = ROOT.TLorentzVector()
                    for syst in self.METsystematics:
                        for UD in self._variations(syst):
                            MET_syst.SetPtEtaPhiM(
                                self._r('MET_pt_' + syst + UD), 0.0,
                                self._r('MET_phi_' + syst + UD), 0.0)
                            V_syst = MET_syst + Lep
                            Vphi_syst[syst + UD] = V_syst.Phi()
                            if V_syst.Pt() > self.vpt1lep:
                                any_v_sysvar_passes = True

            elif self._b("isZnn")[0]:
                MET = ROOT.TLorentzVector()
                MET.SetPtEtaPhiM(tree.MET_Pt, 0.0, tree.MET_Phi, 0.0)
                if self.sample.isMC():
                    for syst in self.METsystematics:
                        for UD in self._variations(syst):
                            Vphi_syst[syst + UD] = self._r('MET_phi_' + syst +
                                                           UD)
                            if self._r('MET_pt_' + syst + UD) > self.vpt0lep:
                                any_v_sysvar_passes = True
                V = MET
            else:
                self.count("fail_lepton_selection")
                return False

            self.cutFlow[4] += 1

            self._b("V_pt")[0] = V.Pt()
            self._b("V_eta")[0] = V.Eta()
            self._b("V_phi")[0] = V.Phi()
            self._b("V_mass")[0] = V.M()

            if (self._b("isZee")[0] or
                    self._b("isZmm")[0]) and self._b("V_pt")[0] < self.vpt2lep:
                return False
            elif self._b("isZnn")[0] and self._b(
                    "V_pt")[0] < self.vpt0lep and not any_v_sysvar_passes:
                return False
            elif (self._b("isWenu")[0] or
                  self._b("isWmunu")[0]) and (self._b("V_pt")[0] < self.vpt1lep
                                              and not any_v_sysvar_passes):
                return False

            self.cutFlow[5] += 1

            if self.skipJetSelection:
                self.cutFlow[7] += 1
                return True

            # JETS
            if self._b("isZnn")[0]:
                j1ptCut = 35.0
                j2ptCut = 35.0
                maxJetPtCut = 60.0
                j1BtagName = 'loose'
            elif self._b("isWmunu")[0] or self._b("isWenu")[0]:
                j1ptCut = 25.0
                j2ptCut = 25.0
                maxJetPtCut = 0.0
                j1BtagName = 'loose'
            elif self._b("isZmm")[0] or self._b("isZee")[0]:
                j1ptCut = 20.0
                j2ptCut = 20.0
                maxJetPtCut = 0.0
                j1BtagName = 'none'
            else:
                return False
            j2BtagName = 'none'

            # btagMaxCut can overwrite the default b-tag max cut
            if self.btagMaxCut is not None and len(self.btagMaxCut) > 0:
                j1BtagName = self.btagMaxCut

            any_taggerPassed_syst = False
            for jetDefinition in self.jetDefinitions:

                indexName = jetDefinition[
                    'indexName'] if 'indexName' in jetDefinition else ''
                taggerName = jetDefinition['taggerName']
                j1Btag = self.btagWPs[self.year][taggerName][j1BtagName]
                j2Btag = self.btagWPs[self.year][taggerName][j2BtagName]

                jetTagger = getattr(tree, taggerName)
                taggerSelection = lambda x: (len(x) == 2 and jetTagger[x[
                    0]] >= j1Btag and jetTagger[x[1]] >= j2Btag)

                # -> nominal
                selectedJets = self.HighestTaggerValueBJets(
                    tree,
                    j1ptCut,
                    j2ptCut,
                    taggerName,
                    puIdCut=self.puIdCut,
                    jetIdCut=self.jetIdCut,
                    maxJetPtCut=maxJetPtCut,
                    systList=None)
                taggerPassed = taggerSelection(selectedJets)
                if taggerPassed:
                    self._b("hJidx" + indexName)[0] = selectedJets[0]
                    self._b("hJidx" + indexName)[1] = selectedJets[1]
                    any_taggerPassed_syst = True
                    self.count("pass_tagger_" + taggerName)
                else:
                    if debugEvent:
                        print("DEBUG-EVENT: did not pass jet-selection.")
                        print("DEBUG-EVENT: selected jets:", selectedJets)
                        print("DEBUG-EVENT: passed btag cuts:", taggerPassed)
                    self._b("hJidx" + indexName)[0] = -1
                    self._b("hJidx" + indexName)[1] = -1

                # -> systematic variations
                if self.sample.isMC():
                    systList = [
                        "".join(x) for x in itertools.product(
                            self.systematics, ["Up", "Down"])
                    ]
                    nSystematics = len(systList)

                    # this returns a list of lists of jet indices for all systematic variations
                    selectedJets = self.HighestTaggerValueBJets(
                        tree,
                        j1ptCut,
                        j2ptCut,
                        taggerName,
                        puIdCut=self.puIdCut,
                        jetIdCut=self.jetIdCut,
                        maxJetPtCut=maxJetPtCut,
                        systList=systList)

                    # apply pre-selection on tagger
                    for j in range(nSystematics):
                        hJidxName_syst = "hJidx{suffix}_{syst}".format(
                            suffix=indexName, syst=systList[j])
                        if taggerSelection(selectedJets[j]):
                            self._b(hJidxName_syst)[0] = selectedJets[j][0]
                            self._b(hJidxName_syst)[1] = selectedJets[j][1]
                            any_taggerPassed_syst = True
                        else:
                            self._b(hJidxName_syst)[0] = -1
                            self._b(hJidxName_syst)[1] = -1

            #print tree.Hbb_fjidx,tree.FatJet_pt[tree.Hbb_fjidx] if(tree.Hbb_fjidx>-1) else "outside",V.Pt()

            # BOOSTED JET selection
            any_boostedJet_syst = False

            # nominal
            Hbb_fjidx = self.HighestTaggerValueFatJet(
                tree,
                ptCut=250.0,
                msdCut=50.0,
                etaCut=2.5,
                taggerName='FatJet_deepTagMD_bbvsLight',
                systList=None,
                Vphi=V.Phi())
            self._b("Hbb_fjidx")[0] = Hbb_fjidx
            if Hbb_fjidx > -1:
                any_boostedJet_syst = True

            # systematics
            if self.sample.isMC():
                systList = [
                    "".join(x) for x in itertools.product(
                        self.systematicsBoosted, ["Up", "Down"])
                ]
                nSystematics = len(systList)

                # return list of jet indices for systematic variations
                Hbb_fjidx_syst = self.HighestTaggerValueFatJet(
                    tree,
                    ptCut=250.0,
                    msdCut=50.0,
                    etaCut=2.5,
                    taggerName='FatJet_deepTagMD_bbvsLight',
                    systList=systList,
                    Vphi=V.Phi(),
                    Vphi_syst=Vphi_syst)
                for j in range(nSystematics):
                    fJidxName_syst = "Hbb_fjidx_{syst}".format(
                        syst=systList[j])
                    self._b(fJidxName_syst)[0] = Hbb_fjidx_syst[j]
                    if Hbb_fjidx_syst[j] > -1:
                        any_boostedJet_syst = True

            #boostedPass = (tree.Hbb_fjidx>-1 and tree.FatJet_Pt[tree.Hbb_fjidx]>250 and V.Pt()>250)  #AC: selection for boosted analysis
            boostedPass = (any_boostedJet_syst and V.Pt() > 250)

            if boostedPass:
                self.count("pass_boosted")

            # discard event if none of the jet selections finds two Higgs candidate jets
            #if not (boostedPass or defaultTaggerPassed or any([jets['isSelected'] for jets in self.jetDefinitions])):
            if not (boostedPass or any_taggerPassed_syst):
                return False

            self.cutFlow[6] += 1

            # yield in the end
            self.cutFlow[7] += 1
            if debugEvent:
                print "DEBUG-EVENT: event passed!!!"

        return True

    def afterProcessing(self):
        super(VHbbSelection, self).afterProcessing()

        print "cut-flow:"
        print "  beginning          ", self.cutFlow[0]
        print "  HLT                ", self.cutFlow[1]
        print "  Leptons            ", self.cutFlow[2]
        print "  Channel            ", self.cutFlow[3]
        print "  Vector boson       ", self.cutFlow[4]
        print "  Vpt                ", self.cutFlow[5]
        print "  Jets               ", self.cutFlow[6]
        print "  end                ", self.cutFlow[7]

        print "efficiency:", (1.0 * self.cutFlow[7] /
                              self.cutFlow[0]) if self.cutFlow[0] > 0 else "-"
Пример #8
0
class isBoosted(AddCollectionsModule):
    def __init__(self,
                 branchName='isBoosted',
                 cutName='all_BOOST',
                 useFlags=False,
                 flags=None):
        super(isBoosted, self).__init__()
        self.branchName = branchName
        self.cutName = cutName
        self.version = 4
        self.useFlags = useFlags
        self.flags = ['resolvedCR', 'resolvedSR', 'boostedCR', 'boostedSR'
                      ] if flags is None else flags
        self.variations = self._variations("isBoosted")

    # returns cut string with variables replaced by their systematic variations
    def getSystVarCut(self, cut, syst, UD):
        replacementRulesList = XbbTools.getReplacementRulesList(
            self.config, syst)
        systVarCut = XbbTools.getSystematicsVariationTemplate(
            cut, replacementRulesList)
        systVarCut = systVarCut.replace('{syst}', syst).replace('{UD}', UD)
        return systVarCut

    def customInit(self, initVars):
        self.sample = initVars['sample']
        self.sampleTree = initVars['sampleTree']
        self.config = initVars['config']
        self.xbbConfig = XbbConfigTools(self.config)

        self.boostedCut = XbbTools.sanitizeExpression(self.config.get(
            'Cuts', self.cutName),
                                                      config=self.config)
        self.systVarCuts = {}

        #self.systematics = sorted(list(set(sum([eval(self.config.get('LimitGeneral', x)) for x in ['sys_cr', 'sys_BDT', 'sys_Mjj']], []))))
        self.systematics = self.xbbConfig.getJECuncertainties(
            step='BoostedFlags') + ['jms', 'jmr', 'unclustEn']

        # Nominal
        self.addIntegerBranch(self.branchName)
        self.sampleTree.addFormula(self.boostedCut)

        # systematic variations
        if self.sample.isMC():
            for syst in self.systematics:
                for UD in self.variations:
                    systVarBranchName = self._v(self.branchName, syst, UD)
                    self.addIntegerBranch(systVarBranchName)
                    self.systVarCuts[systVarBranchName] = self.getSystVarCut(
                        self.boostedCut, syst=syst, UD=UD)
                    self.sampleTree.addFormula(
                        self.systVarCuts[systVarBranchName])

        if self.useFlags:
            # CR/SR flags
            self.flagCuts = {
                k: XbbTools.sanitizeExpression(self.config.get('Cuts', k),
                                               config=self.config)
                for k in self.flags
            }
            self.systVarFlagCuts = {k: {} for k in self.flags}

            for k in self.flags:
                self.sampleTree.addFormula(self.flagCuts[k])
                self.addIntegerBranch(k)

            # systematic variations
            if self.sample.isMC():
                for k in self.flags:
                    for syst in self.systematics:
                        for UD in self.variations:
                            systVarflagName = self._v(k, syst, UD)
                            self.addIntegerBranch(systVarflagName)
                            self.systVarFlagCuts[k][
                                systVarflagName] = XbbTools.sanitizeExpression(
                                    self.getSystVarCut(self.flagCuts[k],
                                                       syst=syst,
                                                       UD=UD),
                                    config=self.config)
                            self.sampleTree.addFormula(
                                self.systVarFlagCuts[k][systVarflagName])

    def processEvent(self, tree):
        # if current entry has not been processed yet
        if not self.hasBeenProcessed(tree):
            self.markProcessed(tree)

            # Nominal
            b = int(self.sampleTree.evaluate(self.boostedCut))
            self._b(self._v(self.branchName))[0] = 1 if b > 0 else 0

            # systematic variations
            if self.sample.isMC():
                for syst in self.systematics:
                    for UD in self.variations:
                        systVarBranchName = self._v(self.branchName, syst, UD)
                        b = int(
                            self.sampleTree.evaluate(
                                self.systVarCuts[systVarBranchName]))
                        self._b(systVarBranchName)[0] = 1 if b > 0 else 0

            if self.useFlags:
                # CR/SR flags
                for k in self.flags:
                    b = int(self.sampleTree.evaluate(self.flagCuts[k]))
                    self._b(self._v(k))[0] = 1 if b > 0 else 0
                if self.sample.isMC():
                    for k in self.flags:
                        for syst in self.systematics:
                            for UD in self.variations:
                                systVarflagName = self._v(k, syst, UD)
                                b = int(
                                    self.sampleTree.evaluate(
                                        self.systVarFlagCuts[k]
                                        [systVarflagName]))
                                self._b(systVarflagName)[0] = 1 if b > 0 else 0