예제 #1
0
class VVBuilder(Analyzer):
    def __init__(self, cfg_ana, cfg_comp, looperName):
        super(VVBuilder, self).__init__(cfg_ana, cfg_comp, looperName)
        self.vbTool = VectorBosonToolBox()
        self.smearing = ROOT.TRandom(10101982)
        # btag reweighting
        self.btagSF = BTagEventWeights(
            'btagsf', os.path.expandvars(self.cfg_ana.btagCSVFile))
        puppiJecCorrWeightFile = os.path.expandvars(
            self.cfg_ana.puppiJecCorrFile)
        self.puppiJecCorr = ROOT.TFile.Open(puppiJecCorrWeightFile)
        self.puppisd_corrGEN = self.puppiJecCorr.Get("puppiJECcorr_gen")
        self.puppisd_corrRECO_cen = self.puppiJecCorr.Get(
            "puppiJECcorr_reco_0eta1v3")
        self.puppisd_corrRECO_for = self.puppiJecCorr.Get(
            "puppiJECcorr_reco_1v3eta2v5")

    def declareHandles(self):
        super(VVBuilder, self).declareHandles()
        if self.cfg_comp.isMC:
            self.handles['packedGen'] = AutoHandle(
                'packedGenParticles', 'std::vector<pat::PackedGenParticle>')

    def copyLV(self, LV):
        out = []
        for i in LV:
            out.append(
                ROOT.math.XYZTLorentzVector(i.px(), i.py(), i.pz(),
                                            i.energy()))
        return out

    def cleanOverlap(self, collection, toRemove):
        after = list(set(collection) - set(toRemove))
        return after

    def substructure(self, jet):
        if hasattr(jet, 'softDropMassCor'):
            return

        jet.softDropMassCor = self.getPUPPIMassWeight(jet) * jet.userFloat(
            'ak8PFJetsPuppiSoftDropMass')
        jet.softDropMassBare = jet.userFloat('ak8PFJetsPuppiSoftDropMass')

        jet.subJetTags = [-99.0] * 2
        jet.subJetCTagL = [-99.0] * 2
        jet.subJetCTagB = [-99.0] * 2
        jet.subJet_hadronFlavour = [-99.0] * 2
        jet.subJet_partonFlavour = [-99.0] * 2

        for i, o in enumerate(jet.subjets("SoftDropPuppi")):
            bTag = o.bDiscriminator(
                self.cfg_ana.fDiscriminatorB) + o.bDiscriminator(
                    self.cfg_ana.fDiscriminatorBB)
            cTag = o.bDiscriminator(self.cfg_ana.fDiscriminatorC)
            lTag = o.bDiscriminator(self.cfg_ana.fDiscriminatorL)
            jet.subJetTags[i] = bTag
            jet.subJetCTagL[i] = cTag / (cTag + lTag)
            jet.subJetCTagB[i] = cTag / (cTag + bTag)
            jet.subJet_partonFlavour[i] = o.partonFlavour()
            jet.subJet_hadronFlavour[i] = o.hadronFlavour()

    def softDropGen(self, jet, event):
        # if we already filled it exit
        if hasattr(jet, 'genSoftDrop') or not self.cfg_comp.isMC:
            return
        # constituents = []
        LVs = ROOT.std.vector("math::XYZTLorentzVector")()

        # we take LVs around the jets and recluster
        for p in event.genPacked:
            if p.status() == 1 and p.pt() > 0.05 and not (abs(p.pdgId())
                                                          in [12, 14, 16]):
                if deltaR(p.eta(), p.phi(), jet.eta(), jet.phi()) < 1.2:
                    LVs.push_back(p.p4())
        interface = ROOT.cmg.FastJetInterface(LVs, -1.0, 0.8, 1, 0.01, 5.0,
                                              4.4)
        # make jets
        interface.makeInclusiveJets(50.0)
        outputJets = interface.get(True)
        if len(outputJets) == 0:
            return

        jet.genJetP4 = ROOT.Candidate.LorentzVector(outputJets[0].px(),
                                                    outputJets[0].py(),
                                                    outputJets[0].pz(),
                                                    outputJets[0].energy())

        # OK!Now save the area
        interface.softDrop(True, 0, 0.0, 0.1, 0.8)
        jet.genSoftDrop = self.copyLV(interface.get(False))[0]

    def topology(self, VV, jets, leptons):
        VV.otherLeptons = leptons
        VV.satteliteJets = jets
        # VBF Tag
        if len(jets) > 1:
            VV.vbfDEta = abs(jets[0].eta() - jets[1].eta())
            VV.vbfMass = (jets[0].p4() + jets[1].p4()).M()
        else:
            VV.vbfDEta = -999
            VV.vbfMass = -999

        # Btags
        jetsCentral = filter(lambda x: abs(x.eta()) < 2.4, jets)

        VV.satteliteCentralJets = jetsCentral
        # cuts are taken from
        # https://twiki.cern.ch/twiki/bin/view/CMS/BtagRecommendation94X
        # (20.06.2016)
        VV.nLooseBTags = len(
            filter(
                lambda x: x.bDiscriminator(self.cfg_ana.fDiscriminatorB) + x.
                bDiscriminator(self.cfg_ana.fDiscriminatorBB) > 0.1522,
                jetsCentral))
        VV.nMediumBTags = len(
            filter(
                lambda x: x.bDiscriminator(self.cfg_ana.fDiscriminatorB) + x.
                bDiscriminator(self.cfg_ana.fDiscriminatorBB) > 0.4941,
                jetsCentral))
        VV.nTightBTags = len(
            filter(
                lambda x: x.bDiscriminator(self.cfg_ana.fDiscriminatorB) + x.
                bDiscriminator(self.cfg_ana.fDiscriminatorBB) > 0.8001,
                jetsCentral))
        VV.nOtherLeptons = len(leptons)

        maxbtag = -100.0

        VV.btagWeight = 1.0
        for j in jetsCentral:
            btag = j.bDiscriminator(
                self.cfg_ana.fDiscriminatorB) + j.bDiscriminator(
                    self.cfg_ana.fDiscriminatorBB)
            flavor = j.hadronFlavour()

            # btag event weight
            if self.cfg_comp.isMC:
                VV.btagWeight *= self.btagSF.getSF(j.pt(), j.eta(), flavor,
                                                   btag)
            # and systematics
            if btag > maxbtag:
                maxbtag = btag
        VV.highestEventBTag = maxbtag

    def selectJets(self,
                   jets,
                   func,
                   otherObjects,
                   DR,
                   otherObjects2=None,
                   DR2=0.0):
        output = []
        for j in jets:
            if not func(j):
                continue
            overlap = False
            for o in otherObjects:
                dr = deltaR(j.eta(), j.phi(), o.eta(), o.phi())
                if dr < DR:
                    overlap = True
                    break
            if otherObjects2 is not None:
                for o in otherObjects2:
                    dr = deltaR(j.eta(), j.phi(), o.eta(), o.phi())
                    if dr < DR2:
                        overlap = True
                        break
            if not overlap:
                output.append(j)
        return output

    def makeWV(self, event):
        output = []

        # loop on the leptons
        looseLeptonsForW = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepID) or
            (abs(x.pdgId()) == 13 and x.highPtIDIso), event.selectedLeptons)
        tightLeptonsForW = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepID and x.pt() > 55) or
            (abs(x.pdgId()) == 13 and x.highPtIDIso and x.pt() > 55),
            event.selectedLeptons)

        if len(tightLeptonsForW) == 0:
            return output

        # make leptonic W
        W = self.vbTool.makeW(tightLeptonsForW, event.met)
        if len(W) == 0:
            return output

        bestW = max(W, key=lambda x: x.leg1.pt())
        # now the jets, use lower pT cut since we'll recluster
        fatJets = self.selectJets(
            event.jetsAK8, lambda x: x.pt() > 200.0 and abs(x.eta()) < 2.4 and
            x.jetID('POG_PFID_Tight'), tightLeptonsForW, 1.0)
        if len(fatJets) == 0:
            return output
        bestJet = max(fatJets, key=lambda x: x.pt())
        self.substructure(bestJet)
        VV = Pair(bestW, bestJet)
        if deltaR(bestW.leg1.eta(), bestW.leg1.phi(), bestJet.eta(),
                  bestJet.phi()) < ROOT.TMath.Pi() / 2.0:
            return output
        if VV.deltaPhi() < 2.0:
            return output
        if abs(deltaPhi(bestW.leg2.phi(), bestJet.phi())) < 2.0:
            return output

        if self.cfg_comp.isMC:
            self.softDropGen(bestJet, event)

            if not hasattr(bestJet, 'genJetP4'):
                VV.genPartialMass = -1
            else:
                newMET = event.met.p4() + VV.leg2.p4() - VV.leg2.genJetP4
                newMET.SetPz(0.0)
                newW = Pair(VV.leg1.leg1, Singlet(newMET))
                self.vbTool.defaultWKinematicFit(newW)
                VV.genPartialMass = (VV.leg1.p4() + VV.leg2.genJetP4).M()
        else:
            VV.genPartialMass = -1

        # topology
        satteliteJets = self.selectJets(
            event.jets, lambda x: x.pt() > 30.0 and x.jetID('POG_PFID_Tight'),
            tightLeptonsForW, 0.4, [bestJet], 0.8)
        otherLeptons = self.cleanOverlap(looseLeptonsForW, [bestW.leg1])
        self.topology(VV, satteliteJets, otherLeptons)

        output.append(VV)
        return output

    def makeZV(self, event):
        output = []

        # loop on the leptons
        leptonsForZ = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepIDNoIso) or
            (abs(x.pdgId()) == 13 and (x.highPtID or x.highPtTrackID)),
            event.selectedLeptons)

        if len(leptonsForZ) < 2:
            return output

        # make leptonic Z
        Z = self.vbTool.makeZ(leptonsForZ)
        if len(Z) == 0:
            return output
        bestZ = max(Z, key=lambda x: x.pt())

        # other higbn pt isolated letpons in the event
        otherGoodLeptons = self.cleanOverlap(leptonsForZ,
                                             [bestZ.leg1, bestZ.leg2])
        otherTightLeptons = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepID) or
            (abs(x.pdgId()) == 13 and (x.highPtIDIso)), otherGoodLeptons)
        # now the jets
        fatJets = self.selectJets(
            event.jetsAK8, lambda x: x.pt() > 200.0 and abs(x.eta()) < 2.4 and
            x.jetID('POG_PFID_Tight'), [bestZ.leg1, bestZ.leg2], 1.0)
        if len(fatJets) == 0:
            return output
        bestJet = max(fatJets, key=lambda x: x.pt())
        self.substructure(bestJet)

        VV = Pair(bestZ, bestJet)

        if self.cfg_comp.isMC:
            self.softDropGen(bestJet, event)
            VV.genPartialMass = (VV.leg1.p4() + VV.leg2.genJet().p4()).M()
        else:
            VV.genPartialMass = -1

        satteliteJets = self.selectJets(
            event.jets, lambda x: x.pt() > 30.0 and x.jetID('POG_PFID_Tight'),
            otherTightLeptons, 0.4, [bestJet], 0.8)
        self.topology(VV, satteliteJets, otherTightLeptons)
        output.append(VV)
        return output

    def makeMETV(self, event):
        output = []

        # loop on the leptons
        leptons = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepID) or
            (abs(x.pdgId()) == 13 and x.highPtIDIso), event.selectedLeptons)
        fatJets = self.selectJets(
            event.jetsAK8, lambda x: x.pt() > 200.0 and abs(x.eta()) < 2.4 and
            x.jetID('POG_PFID_Tight'), leptons, 1.0)

        if len(fatJets) < 1:
            return output

        self.substructure(fatJets[0])
        VV = Pair(event.met, fatJets[0])

        # kinematics
        if VV.deltaPhi() < 2.0 or VV.leg1.pt() < 200:
            return output

        if self.cfg_comp.isMC:
            self.softDropGen(fatJets[0], event)
            VVGEN = Pair(event.met, Singlet(VV.leg2.genJet().p4()))
            VV.genPartialMass = VVGEN.mt()
        else:
            VV.genPartialMass = -1

        # topology
        satteliteJets = self.selectJets(
            event.jets, lambda x: x.pt() > 30.0 and x.jetID('POG_PFID_Tight'),
            leptons, 0.3, [VV.leg2], 0.8)
        self.topology(VV, satteliteJets, leptons)
        output.append(VV)
        return output

    def getPUPPIMassWeight(self, puppijet):
        # mass correction for PUPPI following
        # https://github.com/thaarres/PuppiSoftdropMassCorr

        genCorr = 1.
        recoCorr = 1.
        # corrections only valid up to |eta| < 2.5, use 1. beyond
        if (abs(puppijet.eta()) < 2.5):
            genCorr = self.puppisd_corrGEN.Eval(puppijet.pt())
            if (abs(puppijet.eta()) <= 1.3):
                recoCorr = self.puppisd_corrRECO_cen.Eval(puppijet.pt())
            else:
                recoCorr = self.puppisd_corrRECO_for.Eval(puppijet.pt())
        totalWeight = genCorr * recoCorr
        return totalWeight

    def fillTopPtReweighting(self, event, truth):
        """Top pT reweighting."""
        if not self.cfg_comp.isMC:
            truth.genTop_weight = 1.
            return truth

        ttbar = [
            p for p in event.genParticles
            if abs(p.pdgId()) == 6 and p.statusFlags().isLastCopy()
            and p.statusFlags().fromHardProcess()
        ]

        if self.cfg_comp.name.find('TT') != -1 and self.cfg_comp.name.find(
                'TTH') == -1 and len(ttbar) == 2:
            # store also individual pTs for later correction
            truth.genTop_1_pt = ttbar[0].pt()
            truth.genTop_2_pt = ttbar[1].pt()
            top_1_pt = truth.genTop_1_pt
            top_2_pt = truth.genTop_2_pt
            # only valid up to 400 GeV, assume constant afterwards
            if top_1_pt > 400:
                top_1_pt = 400.
            if top_2_pt > 400:
                top_2_pt = 400.
            # see
            # https://twiki.cern.ch/twiki/bin/view/CMS/TopSystematics#pt_top_Reweighting
            truth.genTop_weight = math.sqrt(
                math.exp(0.0615 - 0.0005 * top_1_pt) *
                math.exp(0.0615 - 0.0005 * top_2_pt))
        else:
            truth.genTop_weight = 1.
        return truth

    @staticmethod
    def p4sum(ps):
        '''Returns four-vector sum of objects in passed list. Returns None
        if empty. Note that python sum doesn't work since p4() + 0/None fails,
        but will be possible in future python'''
        if not ps:
            return None
        p4 = ps[0].p4()
        for i in xrange(len(ps) - 1):
            p4 += ps[i + 1].p4()
        return p4

    def getParentBoson(self, event):
        """Get generator level boson (last in chain) for correct kinematics."""
        # if we already filled it exit
        if hasattr(event, 'genBoson') or not self.cfg_comp.isMC:
            return
        leptons_prompt = [
            p for p in event.genParticles
            if abs(p.pdgId()) in [11, 12, 13, 14]
            and p.fromHardProcessFinalState()
        ]
        taus_prompt = [
            p for p in event.genParticles
            if p.statusFlags().isDirectHardProcessTauDecayProduct()
        ]
        all = leptons_prompt + taus_prompt
        genBoson = VVBuilder.p4sum(all)
        return genBoson

    def makeTruthType(self, event):
        """create truth collection."""
        truth = Truth()
        genBoson = self.getParentBoson(event)
        truth = self.fillTopPtReweighting(event, truth)
        if genBoson:
            truth.genBoson = genBoson
        return [truth]

    def process(self, event):
        self.readCollections(event.input)

        # if MC create the stable particles for Gen Jet reco and substructure
        event.genParticleLVs = ROOT.std.vector("math::XYZTLorentzVector")()
        if self.cfg_comp.isMC:
            event.genPacked = self.handles['packedGen'].product()

        LNuJJ = self.makeWV(event)
        LLJJ = []  #self.makeZV(event)
        JJNuNu = []  #self.makeMETV(event)
        TruthType = self.makeTruthType(event)

        setattr(event, 'LNuJJ' + self.cfg_ana.suffix, LNuJJ)
        setattr(event, 'LLJJ' + self.cfg_ana.suffix, LLJJ)
        setattr(event, 'JJNuNu' + self.cfg_ana.suffix, JJNuNu)
        setattr(event, 'TruthType' + self.cfg_ana.suffix, TruthType)
예제 #2
0
class VVBuilder(Analyzer):

    def __init__(self, cfg_ana, cfg_comp, looperName):
        super(VVBuilder, self).__init__(cfg_ana, cfg_comp, looperName)
        self.vbTool = VectorBosonToolBox()
        self.smearing = ROOT.TRandom(10101982)
        if hasattr(self.cfg_ana, "doPUPPI") and self.cfg_ana.doPUPPI:
            self.doPUPPI = True
            puppiJecCorrWeightFile = os.path.expandvars(
                self.cfg_ana.puppiJecCorrFile)
            self.puppiJecCorr = ROOT.TFile.Open(puppiJecCorrWeightFile)
            self.puppisd_corrGEN = self.puppiJecCorr.Get("puppiJECcorr_gen")
            self.puppisd_corrRECO_cen = self.puppiJecCorr.Get(
                "puppiJECcorr_reco_0eta1v3")
            self.puppisd_corrRECO_for = self.puppiJecCorr.Get(
                "puppiJECcorr_reco_1v3eta2v5")

        else:
            self.doPUPPI = False

        # btag reweighting
        self.btagSF = BTagEventWeights(
            'btagsf', os.path.expandvars(self.cfg_ana.btagCSVFile))

    def declareHandles(self):
        super(VVBuilder, self).declareHandles()
        self.handles['packed'] = AutoHandle(
            'packedPFCandidates', 'std::vector<pat::PackedCandidate>')
        if self.cfg_comp.isMC:
            self.handles['packedGen'] = AutoHandle(
                'packedGenParticles', 'std::vector<pat::PackedGenParticle>')

    def copyLV(self, LV):
        out = []
        for i in LV:
            out.append(ROOT.math.XYZTLorentzVector(
                i.px(), i.py(), i.pz(), i.energy()))
        return out

    def substructure(self, jet, event, nSubjets=2, suffix=""):
        # if we already filled it exit
        tag = 'substructure' + suffix
        if hasattr(jet, tag):
            return

        # constituents = []
        LVs = ROOT.std.vector("math::XYZTLorentzVector")()

        # we take LVs around the jets and recluster
        for LV in event.LVs:
            if deltaR(LV.eta(), LV.phi(), jet.eta(), jet.phi()) < 1.2:
                LVs.push_back(LV)

        interface = ROOT.cmg.FastJetInterface(
            LVs, -1.0, 0.8, 1, 0.01, 5.0, 4.4)
        # make jets
        interface.makeInclusiveJets(150.0)

        outputJets = interface.get(True)
        if len(outputJets) == 0:
            return

#        setattr(jet,tag,Substructure())
#        substructure=getattr(jet,tag)
        substructure = Substructure()

        # For the pruned sub jets +PUPPIcalculate the correction
        # without L1
        corrNoL1 = jet.corr / jet.CorrFactor_L1

        # if PUPPI reset the jet four vector
        if self.doPUPPI:
            jet.setP4(outputJets[0] * jet.corr)

        substructure = Substructure()
        # OK!Now save the area
        substructure.area = interface.getArea(1, 0)

        # Get pruned lorentzVector and subjets
        interface.prune(True, 0, 0.1, 0.5)

        substructure.prunedJet = self.copyLV(
            interface.get(False))[0] * corrNoL1
        interface.makeSubJets(False, 0, nSubjets)
        substructure.prunedSubjets = self.copyLV(interface.get(False))
        # getv the btag of the pruned subjets

        jet.subJetTags = [-99.0] * nSubjets
        jet.subJetCTagL = [-99.0] * nSubjets
        jet.subJetCTagB = [-99.0] * nSubjets
        jet.subJet_hadronFlavour = [-99.0] * nSubjets
        jet.subJet_partonFlavour = [-99.0] * nSubjets

        for i, s in enumerate(substructure.prunedSubjets):
            for o in jet.subjets("SoftDrop"):
                dr = deltaR(s.eta(), s.phi(), o.eta(), o.phi())
                if dr < 0.1:
                    # found = True
                    jet.subJetTags[i] = o.bDiscriminator(
                        self.cfg_ana.bDiscriminator)
                    jet.subJetCTagL[i] = o.bDiscriminator(
                        self.cfg_ana.cDiscriminatorL)
                    jet.subJetCTagB[i] = o.bDiscriminator(
                        self.cfg_ana.cDiscriminatorB)
                    jet.subJet_partonFlavour[i] = o.partonFlavour()
                    jet.subJet_hadronFlavour[i] = o.hadronFlavour()
                    break
        # Get soft Drop lorentzVector and subjets
        interface.softDrop(True, 0, 0.0, 0.1, 0.8)
        substructure.softDropJet = self.copyLV(
            interface.get(False))[0] * corrNoL1
        substructure.softDropJetMassCor = 0
        substructure.softDropJetMassBare = 0
        substructure.softDropJetMassL2L3 = 0
        if self.doPUPPI:
            softDropJetUnCorr = self.copyLV(interface.get(False))[0]
            substructure.softDropJetMassCor = self.getPUPPIMassWeight(
                softDropJetUnCorr)
            substructure.softDropJetMassBare = softDropJetUnCorr.mass()
            substructure.softDropJetMassL2L3 = substructure.softDropJet.mass()

        interface.makeSubJets(False, 0, 2)
        substructure.softDropSubjets = self.copyLV(interface.get(False))

        # get NTau
        substructure.ntau = interface.nSubJettiness(
            0, 4, 0, 6, 1.0, 0.8, 999.0, 999.0, 999)
        # calculate DDT tau21 (currently without softDropJetMassCor, but the
        # L2L3 corrections)
        substructure.tau21_DDT = 0
        if (substructure.softDropJet.mass() > 0):
            substructure.tau21_DDT = substructure.ntau[1] / substructure.ntau[0] + (0.063 * math.log(
                (substructure.softDropJet.mass() * substructure.softDropJet.mass()) / substructure.softDropJet.pt()))
        setattr(jet, tag, substructure)

    def substructureGEN(self, jet, event):
        # if we already filled it exit
        if hasattr(jet, 'substructureGEN') or not self.cfg_comp.isMC:
            return

        # constituents = []
        LVs = ROOT.std.vector("math::XYZTLorentzVector")()

        # we take LVs around the jets and recluster
        for p in event.genParticleLVs:
            if deltaR(p.eta(), p.phi(), jet.eta(), jet.phi()) < 1.2:
                LVs.push_back(p)

        interface = ROOT.cmg.FastJetInterface(
            LVs, -1.0, 0.8, 1, 0.01, 5.0, 4.4)
        # make jets
        interface.makeInclusiveJets(50.0)

        outputJets = interface.get(True)
        if len(outputJets) == 0:
            return

        jet.substructureGEN = Substructure()
        # OK!Now save the area
        jet.substructureGEN.area = interface.getArea(1, 0)
        # Get pruned lorentzVector and subjets
        jet.substructureGEN.jet = self.copyLV(interface.get(True))[0]

        interface.prune(True, 0, 0.1, 0.5)

        jet.substructureGEN.prunedJet = self.copyLV(interface.get(False))[0]
        interface.softDrop(True, 0, 0.0, 0.1, 0.8)
        jet.substructureGEN.softDropJet = self.copyLV(interface.get(False))[0]
        jet.substructureGEN.ntau = interface.nSubJettiness(
            0, 4, 0, 6, 1.0, 0.8, 999.0, 999.0, 999)

    def cleanOverlap(self, collection, toRemove):
        after = list(set(collection) - set(toRemove))
        return after

    def topology(self, VV, jets, leptons):
        VV.otherLeptons = leptons
        VV.satteliteJets = jets
        # VBF Tag
        if len(jets) > 1:
            VV.vbfDEta = abs(jets[0].eta() - jets[1].eta())
            VV.vbfMass = (jets[0].p4() + jets[1].p4()).M()
        else:
            VV.vbfDEta = -999
            VV.vbfMass = -999

        # Btags
        jetsCentral = filter(lambda x: abs(x.eta()) < 2.4, jets)

        VV.satteliteCentralJets = jetsCentral
        # cuts are taken from
        # https://twiki.cern.ch/twiki/bin/view/CMS/BtagRecommendation80X
        # (20.06.2016)
        VV.nLooseBTags = len(filter(lambda x: x.bDiscriminator(
            self.cfg_ana.bDiscriminator) > 0.5426, jetsCentral))
        VV.nMediumBTags = len(filter(lambda x: x.bDiscriminator(
            self.cfg_ana.bDiscriminator) > 0.8484, jetsCentral))
        VV.nTightBTags = len(filter(lambda x: x.bDiscriminator(
            self.cfg_ana.bDiscriminator) > 0.9535, jetsCentral))
        VV.nOtherLeptons = len(leptons)

        maxbtag = -100.0

        VV.btagWeight = 1.0
        for j in jetsCentral:
            btag = j.bDiscriminator(self.cfg_ana.bDiscriminator)
            flavor = j.hadronFlavour()

            # btag event weight
            if self.cfg_comp.isMC:
                VV.btagWeight *= self.btagSF.getSF(j.pt(),
                                                   j.eta(), flavor, btag)
            # and systematics
            if btag > maxbtag:
                maxbtag = btag
        VV.highestEventBTag = maxbtag

    def selectJets(self, jets, func, otherObjects, DR, otherObjects2=None, DR2=0.0):
        output = []
        for j in jets:
            if not func(j):
                continue
            overlap = False
            for o in otherObjects:
                dr = deltaR(j.eta(), j.phi(), o.eta(), o.phi())
                if dr < DR:
                    overlap = True
                    break
            if otherObjects2 is not None:
                for o in otherObjects2:
                    dr = deltaR(j.eta(), j.phi(), o.eta(), o.phi())
                    if dr < DR2:
                        overlap = True
                        break
            if not overlap:
                output.append(j)
        return output

    def makeWV(self, event):
        output = []

        # loop on the leptons
        looseLeptonsForW = filter(lambda x: (abs(x.pdgId()) == 11 and x.heepID) or (
            abs(x.pdgId()) == 13 and x.highPtIDIso), event.selectedLeptons)
        tightLeptonsForW = filter(lambda x: (abs(x.pdgId()) == 11 and x.heepID and x.pt() > 55) or (
            abs(x.pdgId()) == 13 and x.highPtIDIso and x.pt() > 55), event.selectedLeptons)

        if len(tightLeptonsForW) == 0:
            return output

        # make leptonic W
        W = self.vbTool.makeW(tightLeptonsForW, event.met)
        if len(W) == 0:
            return output

        bestW = max(W, key=lambda x: x.leg1.pt())
        # now the jets, use lower pT cut since we'll recluster
        fatJets = self.selectJets(event.jetsAK8, lambda x: x.pt() > 150.0 and abs(
            x.eta()) < 2.4 and x.jetID('POG_PFID_Loose'), tightLeptonsForW, 1.0)
        if len(fatJets) == 0:
            return output
        bestJet = max(fatJets, key=lambda x: x.pt())

        VV = Pair(bestW, bestJet)
        if deltaR(bestW.leg1.eta(), bestW.leg1.phi(), bestJet.eta(), bestJet.phi()) < ROOT.TMath.Pi() / 2.0:
            return output
        if VV.deltaPhi() < 2.0:
            return output
        if abs(deltaPhi(bestW.leg2.phi(), bestJet.phi())) < 2.0:
            return output

        # substructure
        self.substructure(VV.leg2, event)
        if not hasattr(VV.leg2, 'substructure'):
            return output

        # substructure function has reclustered jet, so we need to check the pT
        # again
        if not VV.leg2.pt() > 200.:
            return output
        # also recalculate the resonance mass four vector
        VV = Pair(bestW, bestJet)

        # substructure truth
        if self.cfg_comp.isMC:
            self.substructureGEN(VV.leg2, event)
            if hasattr(VV.leg2, 'substructureGEN'):
                newMET = event.met.p4() + VV.leg2.p4() - VV.leg2.substructureGEN.jet
                newMET.SetPz(0.0)
                newW = Pair(VV.leg1.leg1, Singlet(newMET))
                self.vbTool.defaultWKinematicFit(newW)
                VV.genPartialMass = (
                    VV.leg1.p4() + VV.leg2.substructureGEN.jet).M()

        # topology
        satteliteJets = self.selectJets(event.jets, lambda x: x.pt() > 30.0 and x.jetID(
            'POG_PFID_Loose'), tightLeptonsForW, 0.4, [bestJet], 0.8)
        otherLeptons = self.cleanOverlap(looseLeptonsForW, [bestW.leg1])
        self.topology(VV, satteliteJets, otherLeptons)

        output.append(VV)
        return output

    def makeZV(self, event):
        output = []

        # loop on the leptons
        leptonsForZ = filter(lambda x: (abs(x.pdgId()) == 11 and x.heepIDNoIso) or (
            abs(x.pdgId()) == 13 and (x.highPtID or x.highPtTrackID)), event.selectedLeptons)

        if len(leptonsForZ) < 2:
            return output

        # make leptonic Z
        Z = self.vbTool.makeZ(leptonsForZ)
        if len(Z) == 0:
            return output
        bestZ = max(Z, key=lambda x: x.pt())

        # other higbn pt isolated letpons in the event
        otherGoodLeptons = self.cleanOverlap(
            leptonsForZ, [bestZ.leg1, bestZ.leg2])
        otherTightLeptons = filter(lambda x: (abs(x.pdgId()) == 11 and x.heepID) or (
            abs(x.pdgId()) == 13 and (x.highPtIDIso)), otherGoodLeptons)
        # now the jets
        fatJets = self.selectJets(event.jetsAK8, lambda x: x.pt() > 200.0 and abs(
            x.eta()) < 2.4 and x.jetID('POG_PFID_Loose'), [bestZ.leg1, bestZ.leg2], 1.0)
        if len(fatJets) == 0:
            return output
        bestJet = max(fatJets, key=lambda x: x.pt())

        VV = Pair(bestZ, bestJet)

        # substructure
        self.substructure(VV.leg2, event)

        # substructure changes jet, so we need to recalculate the resonance
        # mass
        VV = Pair(bestZ, bestJet)

        if not hasattr(VV.leg2, "substructure"):
            return output


        if self.cfg_comp.isMC:
            self.substructureGEN(VV.leg2, event)
            if hasattr(VV.leg2, 'substructureGEN'):
                VV.genPartialMass = (VV.leg1.p4() + VV.leg2.substructureGEN.jet).M()
        # check if there are subjets

        # if len(VV.leg2.substructure.prunedSubjets)<2:
        #     print 'No substructure',len(VV.leg2.substructure.prunedSubjets)
        #     return output

        # topology
        satteliteJets = self.selectJets(event.jets, lambda x: x.pt() > 30.0 and x.jetID(
            'POG_PFID_Loose'), otherTightLeptons, 0.4, [bestJet], 0.8)
        self.topology(VV, satteliteJets, otherTightLeptons)
        output.append(VV)
        return output

    def makeJJ(self, event):
        output = []

        # loop on the leptons
        leptons = filter(lambda x: (abs(x.pdgId()) == 11 and x.heepID) or (
            abs(x.pdgId()) == 13 and x.highPtIDIso), event.selectedLeptons)
        fatJets = self.selectJets(event.jetsAK8, lambda x: x.pt() > 200.0 and abs(
            x.eta()) < 2.4 and x.jetID('POG_PFID_Tight'), leptons, 1.0)

        if len(fatJets) < 2:
            return output

        VV = Pair(fatJets[0], fatJets[1])

        # kinematics
        if abs(VV.leg1.eta() - VV.leg2.eta()) > 1.3 or VV.mass() < 1000:
            return output

        self.substructure(VV.leg1, event)
        self.substructure(VV.leg2, event)

        # substructure changes jet, so we need to recalculate the resonance
        # mass
        VV = Pair(fatJets[0], fatJets[1])

        # substructure truth
        if self.cfg_comp.isMC:
            self.substructureGEN(VV.leg2, event)
            self.substructureGEN(VV.leg1, event)
            if hasattr(VV.leg2, 'substructureGEN') and hasattr(VV.leg1,'substructureGEN'):
                VV.genPartialMass = (VV.leg1.substructureGEN.jet + VV.leg2.substructureGEN.jet).M()




        if not hasattr(VV.leg1, "substructure"):
            return output

        if not hasattr(VV.leg2, "substructure"):
            return output

        # check if there are subjets

        # if len(VV.leg2.substructure.prunedSubjets)<2 or len(VV.leg1.substructure.prunedSubjets)<2:
        #     print 'No substructure'
        #     return output

        # topology
        satteliteJets = self.selectJets(event.jets, lambda x: x.pt() > 30.0 and x.jetID(
            'POG_PFID_Loose'), leptons, 0.3, [VV.leg1, VV.leg2], 0.8)
        self.topology(VV, satteliteJets, leptons)
        output.append(VV)
        return output

    def makeMETV(self, event):
        output = []

        # loop on the leptons
        leptons = filter(lambda x: (abs(x.pdgId()) == 11 and x.heepID) or (
            abs(x.pdgId()) == 13 and x.highPtIDIso), event.selectedLeptons)
        fatJets = self.selectJets(event.jetsAK8, lambda x: x.pt() > 200.0 and abs(
            x.eta()) < 2.4 and x.jetID('POG_PFID_Loose'), leptons, 1.0)

        if len(fatJets) < 1:
            return output

        VV = Pair(event.met, fatJets[0])

        # kinematics
        if VV.deltaPhi() < 2.0 or VV.leg1.pt() < 200:
            return output

        self.substructure(VV.leg2, event)

        if not hasattr(VV.leg2, "substructure"):
            return output

        # substructure changes jet, so we need to recalculate the resonance
        # mass
        VV = Pair(event.met, fatJets[0])

        # check if there are subjets

        # if len(VV.leg2.substructure.prunedSubjets)<2:
        #     print 'No substructure'
        #     return output
        if self.cfg_comp.isMC:
            self.substructureGEN(VV.leg2, event)
            if hasattr(VV.leg2, 'substructureGEN'):
                VVGEN = Pair(event.met,Singlet(VV.leg2.substructureGEN.jet))            
                VV.genPartialMass = VVGEN.mt()


        # topology
        satteliteJets = self.selectJets(event.jets, lambda x: x.pt() > 30.0 and x.jetID(
            'POG_PFID_Loose'), leptons, 0.3, [VV.leg2], 0.8)
        self.topology(VV, satteliteJets, leptons)
        output.append(VV)
        return output

    def getPUPPIMassWeight(self, puppijet):
        # mass correction for PUPPI following
        # https://github.com/thaarres/PuppiSoftdropMassCorr

        genCorr = 1.
        recoCorr = 1.
        # corrections only valid up to |eta| < 2.5, use 1. beyond
        if (abs(puppijet.eta()) < 2.5):
            genCorr = self.puppisd_corrGEN.Eval(puppijet.pt())
            if (abs(puppijet.eta()) <= 1.3):
                recoCorr = self.puppisd_corrRECO_cen.Eval(puppijet.pt())
            else:
                recoCorr = self.puppisd_corrRECO_for.Eval(puppijet.pt())
        totalWeight = genCorr * recoCorr
        return totalWeight

    def fillTopPtReweighting(self, event, truth):
        """Top pT reweighting."""
        if not self.cfg_comp.isMC:
            truth.genTop_weight = 1.
            return truth

        ttbar = [p for p in event.genParticles if abs(p.pdgId()) == 6 and p.statusFlags(
        ).isLastCopy() and p.statusFlags().fromHardProcess()]

        if self.cfg_comp.name.find('TT') != -1 and self.cfg_comp.name.find('TTH') == -1 and len(ttbar) == 2:
            # store also individual pTs for later correction
            truth.genTop_1_pt = ttbar[0].pt()
            truth.genTop_2_pt = ttbar[1].pt()
            top_1_pt = truth.genTop_1_pt
            top_2_pt = truth.genTop_2_pt
            # only valid up to 400 GeV, assume constant afterwards
            if top_1_pt > 400:
                top_1_pt = 400.
            if top_2_pt > 400:
                top_2_pt = 400.
            # see
            # https://twiki.cern.ch/twiki/bin/view/CMS/TopSystematics#pt_top_Reweighting
            truth.genTop_weight = math.sqrt(
                math.exp(0.0615 - 0.0005 * top_1_pt) * math.exp(0.0615 - 0.0005 * top_2_pt))
        else:
            truth.genTop_weight = 1.
        return truth

    @staticmethod
    def p4sum(ps):
        '''Returns four-vector sum of objects in passed list. Returns None
        if empty. Note that python sum doesn't work since p4() + 0/None fails,
        but will be possible in future python'''
        if not ps:
            return None
        p4 = ps[0].p4()
        for i in xrange(len(ps) - 1):
            p4 += ps[i + 1].p4()
        return p4

    def getParentBoson(self, event):
        """Get generator level boson (last in chain) for correct kinematics."""
        # if we already filled it exit
        if hasattr(event, 'genBoson') or not self.cfg_comp.isMC:
            return
        leptons_prompt = [p for p in event.genParticles if abs(
            p.pdgId()) in [11, 12, 13, 14] and p.fromHardProcessFinalState()]
        taus_prompt = [p for p in event.genParticles if p.statusFlags(
        ).isDirectHardProcessTauDecayProduct()]
        all = leptons_prompt + taus_prompt
        genBoson = VVBuilder.p4sum(all)
        return genBoson

    def makeTruthType(self, event):
        """create truth collection."""
        truth = Truth()
        genBoson = self.getParentBoson(event)
        truth = self.fillTopPtReweighting(event, truth)
        if genBoson:
            truth.genBoson = genBoson
        return [truth]

    def process(self, event):
        self.readCollections(event.input)
        # first create a set of four vectors to recluster jets later
        event.LVs = ROOT.std.vector("math::XYZTLorentzVector")()
        # load packed candidatyes
        cands = self.handles['packed'].product()

        # if use PUPPI weigh them or lese just pass through
        if self.doPUPPI:
            for c in cands:
                if c.pt() > 13000 or c.pt() == float('Inf'):
                    continue
                if c.puppiWeight() > 0:
                    event.LVs.push_back(c.p4() * c.puppiWeight())
        else:
            for c in cands:
                if c.pt() > 13000 or c.pt() == float('Inf'):
                    continue
                event.LVs.push_back(c.p4())

        # if MC create the stable particles for Gen Jet reco and substructure
        event.genParticleLVs = ROOT.std.vector("math::XYZTLorentzVector")()
        if self.cfg_comp.isMC:
            event.genPacked = self.handles['packedGen'].product()
            for p in event.genPacked:
                if p.status() == 1 and p.pt() > 0.05 and not (abs(p.pdgId()) in [12, 14, 16]):
                    event.genParticleLVs.push_back(p.p4())
        LNuJJ = self.makeWV(event)
        LLJJ = self.makeZV(event)
        JJ = self.makeJJ(event)
        JJNuNu = self.makeMETV(event)
        TruthType = self.makeTruthType(event)

        setattr(event, 'LNuJJ' + self.cfg_ana.suffix, LNuJJ)
        setattr(event, 'JJ' + self.cfg_ana.suffix, JJ)
        setattr(event, 'LLJJ' + self.cfg_ana.suffix, LLJJ)
        setattr(event, 'JJNuNu' + self.cfg_ana.suffix, JJNuNu)
        setattr(event, 'TruthType' + self.cfg_ana.suffix, TruthType)
예제 #3
0
class VVBuilder(Analyzer):
    def __init__(self, cfg_ana, cfg_comp, looperName):
        super(VVBuilder, self).__init__(cfg_ana, cfg_comp, looperName)
        self.vbTool = VectorBosonToolBox()
        self.smearing = ROOT.TRandom(10101982)
        if hasattr(self.cfg_ana, "doPUPPI") and self.cfg_ana.doPUPPI:
            self.doPUPPI = True
        else:
            self.doPUPPI = False

    def declareHandles(self):
        super(VVBuilder, self).declareHandles()
        self.handles['packed'] = AutoHandle(
            'packedPFCandidates', 'std::vector<pat::PackedCandidate>')

    def copyLV(self, LV):
        out = []
        for i in LV:
            out.append(
                ROOT.math.XYZTLorentzVector(i.px(), i.py(), i.pz(),
                                            i.energy()))
        return out

    def substructure(self, jet, event):
        #if we already filled it exit
        if hasattr(jet, 'substructure'):
            return

        constituents = []
        LVs = ROOT.std.vector("math::XYZTLorentzVector")()

        #we take LVs around the jets and recluster
        for LV in event.LVs:
            if deltaR(LV.eta(), LV.phi(), jet.eta(), jet.phi()) < 1.2:
                LVs.push_back(LV)

        interface = ROOT.cmg.FastJetInterface(LVs, -1.0, 0.8, 1, 0.01, 5.0,
                                              4.4)
        #make jets
        interface.makeInclusiveJets(150.0)

        outputJets = interface.get(True)
        if len(outputJets) == 0:
            return

        #For the pruned sub jets +PUPPIcalculate the correction
        #without L1
        corrNoL1 = jet.corr / jet.CorrFactor_L1

        #if PUPPI reset the jet four vector
        if self.doPUPPI:
            jet.setP4(outputJets[0] * jet.corr)

        jet.substructure = Substructure()
        #OK!Now save the area
        jet.substructure.area = interface.getArea(1, 0)

        #Get pruned lorentzVector and subjets
        interface.prune(True, 0, 0.1, 0.5)

        jet.substructure.prunedJet = self.copyLV(
            interface.get(False))[0] * corrNoL1
        jet.substructure.prunedJetUp = 1.05 * jet.substructure.prunedJet.mass()
        jet.substructure.prunedJetDown = 0.95 * jet.substructure.prunedJet.mass(
        )
        jet.substructure.prunedJetSmear = jet.substructure.prunedJet.mass(
        ) * self.smearing.Gaus(1.0, 1.1)

        interface.makeSubJets(False, 0, 2)
        jet.substructure.prunedSubjets = self.copyLV(interface.get(False))

        #getv the btag of the pruned subjets
        jet.subJetTags = [-1.0, -1.0]
        jet.subJetCTagL = [-1.0, -1.0]
        jet.subJetCTagB = [-1.0, -1.0]

        for i, s in enumerate(jet.substructure.prunedSubjets):
            for o in jet.subjets("SoftDrop"):
                dr = deltaR(s.eta(), s.phi(), o.eta(), o.phi())
                if dr < 0.1:
                    found = True
                    jet.subJetTags[i] = o.bDiscriminator(
                        self.cfg_ana.bDiscriminator)
                    jet.subJetCTagL[i] = o.bDiscriminator(
                        self.cfg_ana.cDiscriminatorL)
                    jet.subJetCTagB[i] = o.bDiscriminator(
                        self.cfg_ana.cDiscriminatorB)
                    break

        #Get soft Drop lorentzVector and subjets

        interface.softDrop(True, 0, 0.0, 0.1, 0.8)
        jet.substructure.softDropJet = self.copyLV(
            interface.get(False))[0] * corrNoL1
        jet.substructure.softDropJetUp = 1.05 * jet.substructure.softDropJet.mass(
        )
        jet.substructure.softDropJetDown = 0.95 * jet.substructure.softDropJet.mass(
        )
        jet.substructure.softDropJetSmear = jet.substructure.softDropJet.mass(
        ) * self.smearing.Gaus(1.0, 0.1)

        interface.makeSubJets(False, 0, 2)
        jet.substructure.softDropSubjets = self.copyLV(interface.get(False))

        #get NTau
        jet.substructure.ntau = interface.nSubJettiness(
            0, 4, 0, 6, 1.0, 0.8, 999.0, 999.0, 999)

        #recluster with CA and do massdrop

        interface = ROOT.cmg.FastJetInterface(LVs, 0.0, 1.5, 1, 0.01, 5.0, 4.4)
        interface.makeInclusiveJets(150.0)

        mu = ROOT.Double(0.667)
        y = ROOT.Double(0.08)
        jet.substructure.massDropTag = interface.massDropTag(0, mu, y)
        jet.substructure.massDrop = (mu, y)

    def cleanOverlap(self, collection, toRemove):
        after = list(set(collection) - set(toRemove))
        return after

    def topology(self, VV, jets, leptons):
        VV.otherLeptons = leptons
        VV.satteliteJets = jets
        #VBF Tag
        if len(jets) > 1:
            VV.vbfDEta = abs(jets[0].eta() - jets[1].eta())
            VV.vbfMass = (jets[0].p4() + jets[1].p4()).M()
        else:
            VV.vbfDEta = -999
            VV.vbfMass = -999

        # Btags
        # cuts are taken from https://twiki.cern.ch/twiki/bin/view/CMS/BtagRecommendation76X
        jetsCentral = filter(lambda x: abs(x.eta()) < 2.4, jets)
        VV.satteliteCentralJets = jetsCentral
        VV.nLooseBTags = len(
            filter(
                lambda x: x.bDiscriminator(self.cfg_ana.bDiscriminator) >
                0.460, jetsCentral))
        VV.nMediumBTags = len(
            filter(
                lambda x: x.bDiscriminator(self.cfg_ana.bDiscriminator) >
                0.800, jetsCentral))
        VV.nTightBTags = len(
            filter(
                lambda x: x.bDiscriminator(self.cfg_ana.bDiscriminator) >
                0.935, jetsCentral))
        VV.nOtherLeptons = len(leptons)

    def selectJets(self,
                   jets,
                   func,
                   otherObjects,
                   DR,
                   otherObjects2=None,
                   DR2=0.0):
        output = []
        for j in jets:
            if not func(j):
                continue
            overlap = False
            for o in otherObjects:
                dr = deltaR(j.eta(), j.phi(), o.eta(), o.phi())
                if dr < DR:
                    overlap = True
                    break
            if otherObjects2 != None:
                for o in otherObjects2:
                    dr = deltaR(j.eta(), j.phi(), o.eta(), o.phi())
                    if dr < DR2:
                        overlap = True
                        break
            if not overlap:
                output.append(j)
        return output

    def makeWV(self, event):
        output = []

        #loop on the leptons
        looseLeptonsForW = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepID) or
            (abs(x.pdgId()) == 13 and x.highPtIDIso), event.selectedLeptons)
        tightLeptonsForW = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepID and x.pt() > 120) or
            (abs(x.pdgId()) == 13 and x.highPtIDIso and x.pt() > 53 and abs(
                x.eta()) < 2.1), event.selectedLeptons)

        if len(tightLeptonsForW) == 0:
            return output

        #make leptonic W
        W = self.vbTool.makeW(tightLeptonsForW, event.met)
        if len(W) == 0:
            return output

        bestW = max(W, key=lambda x: x.leg1.pt())
        #now the jets
        fatJets = self.selectJets(
            event.jetsAK8, lambda x: x.pt() > 200.0 and abs(x.eta()) < 2.4 and
            x.jetID('POG_PFID_Loose'), tightLeptonsForW, 1.0)
        if len(fatJets) == 0:
            return output
        bestJet = max(fatJets, key=lambda x: x.pt())

        VV = Pair(bestW, bestJet)
        if deltaR(bestW.leg1.eta(), bestW.leg1.phi(), bestJet.eta(),
                  bestJet.phi()) < ROOT.TMath.Pi() / 2.0:
            return output
        if VV.deltaPhi() < 2.0:
            return output
        if abs(deltaPhi(bestW.leg2.phi(), bestJet.phi())) < 2.0:
            return output

        #substructure
        self.substructure(VV.leg2, event)
        if not hasattr(VV.leg2, 'substructure'):
            return output

        #check if there are subjets

#        if len(VV.leg2.substructure.prunedSubjets)<2:
#            print 'No substructure',len(VV.leg2.substructure.prunedSubjets)
#            return output

#topology
        satteliteJets = self.selectJets(
            event.jets, lambda x: x.pt() > 30.0 and x.jetID('POG_PFID_Loose'),
            tightLeptonsForW, 0.3, [bestJet], 0.8)
        otherLeptons = self.cleanOverlap(looseLeptonsForW, [bestW.leg1])
        self.topology(VV, satteliteJets, otherLeptons)

        output.append(VV)
        return output

    def makeTOPCR(self, event):
        output = []

        #loop on the leptons
        looseLeptonsForW = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepID) or
            (abs(x.pdgId()) == 13 and x.highPtIDIso), event.selectedLeptons)
        tightLeptonsForW = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepID and x.pt() > 120) or
            (abs(x.pdgId()) == 13 and x.highPtIDIso and x.pt() > 53 and abs(
                x.eta()) < 2.1), event.selectedLeptons)

        if len(tightLeptonsForW) == 0:
            return output

        #make leptonic W
        W = self.vbTool.makeW(tightLeptonsForW, event.met)
        if len(W) == 0:
            return output

        bestW = max(W, key=lambda x: x.leg1.pt())
        #now the jets
        fatJets = self.selectJets(
            event.jetsAK8, lambda x: x.pt() > 200.0 and abs(x.eta()) < 2.4 and
            x.jetID('POG_PFID_Loose'), tightLeptonsForW, 1.0)
        fatJets = filter(
            lambda x: abs(deltaPhi(bestW.leg1.phi(), x.phi())) > ROOT.TMath.Pi(
            ) / 2.0, fatJets)

        if len(fatJets) == 0:
            return output

        bestJet = max(fatJets, key=lambda x: x.mass())

        VV = Pair(bestW, bestJet)
        if deltaR(bestW.leg1.eta(), bestW.leg1.phi(), bestJet.eta(),
                  bestJet.phi()) < ROOT.TMath.Pi() / 2.0:
            return output
        if VV.deltaPhi() < 2.0:
            return output
        if abs(deltaPhi(bestW.leg2.phi(), bestJet.phi())) < 2.0:
            return output

        #substructure
        self.substructure(VV.leg2, event)

        if not hasattr(VV.leg2, "substructure"):
            return output

        #check if there are subjets

#        if len(VV.leg2.substructure.prunedSubjets)<2:
#            print 'No substructure',len(VV.leg2.substructure.prunedSubjets)
#            return output

#topology
        satteliteJets = self.selectJets(
            event.jets, lambda x: x.pt() > 30.0 and x.jetID('POG_PFID_Loose'),
            tightLeptonsForW, 0.3, [bestJet], 0.8)
        otherLeptons = self.cleanOverlap(looseLeptonsForW, [bestW.leg1])
        self.topology(VV, satteliteJets, otherLeptons)

        output.append(VV)
        return output

    def makeZV(self, event):
        output = []

        #loop on the leptons

        leptonsForZ = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepIDNoIso) or
            (abs(x.pdgId()) == 13 and (x.highPtID or x.highPtTrackID)),
            event.selectedLeptons)

        if len(leptonsForZ) < 2:
            return output

        #make leptonic Z
        Z = self.vbTool.makeZ(leptonsForZ)
        if len(Z) == 0:
            return output
        bestZ = max(Z, key=lambda x: x.pt())

        #other higbn pt isolated letpons in the event
        otherGoodLeptons = self.cleanOverlap(leptonsForZ,
                                             [bestZ.leg1, bestZ.leg2])
        otherTightLeptons = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepID) or
            (abs(x.pdgId()) == 13 and (x.highPtIDIso)), otherGoodLeptons)
        #now the jets
        fatJets = self.selectJets(
            event.jetsAK8, lambda x: x.pt() > 200.0 and abs(x.eta()) < 2.4 and
            x.jetID('POG_PFID_Loose'), [bestZ.leg1, bestZ.leg2], 1.0)
        if len(fatJets) == 0:
            return output
        bestJet = max(fatJets, key=lambda x: x.pt())

        VV = Pair(bestZ, bestJet)

        #substructure
        self.substructure(VV.leg2, event)

        if not hasattr(VV.leg2, "substructure"):
            return output

        #check if there are subjets

#       if len(VV.leg2.substructure.prunedSubjets)<2:
#           print 'No substructure',len(VV.leg2.substructure.prunedSubjets)
#           return output

#topology
        satteliteJets = self.selectJets(
            event.jets, lambda x: x.pt() > 30.0 and x.jetID('POG_PFID_Loose'),
            otherTightLeptons, 0.3, [bestJet], 0.8)
        self.topology(VV, satteliteJets, otherTightLeptons)
        output.append(VV)
        return output

    def makeJJ(self, event):
        output = []

        #loop on the leptons
        leptons = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepID) or
            (abs(x.pdgId()) == 13 and x.highPtIDIso), event.selectedLeptons)
        fatJets = self.selectJets(
            event.jetsAK8, lambda x: x.pt() > 200.0 and abs(x.eta()) < 2.4 and
            x.jetID('POG_PFID_Tight'), leptons, 1.0)

        if len(fatJets) < 2:
            return output

        VV = Pair(fatJets[0], fatJets[1])

        #kinematics
        if abs(VV.leg1.eta() - VV.leg2.eta()) > 1.3 or VV.mass() < 1000:
            return output

        self.substructure(VV.leg1, event)
        self.substructure(VV.leg2, event)

        if not hasattr(VV.leg1, "substructure"):
            return output

        if not hasattr(VV.leg2, "substructure"):
            return output

        #check if there are subjets

#      if len(VV.leg2.substructure.prunedSubjets)<2 or len(VV.leg1.substructure.prunedSubjets)<2:
#          print 'No substructure'
#          return output

#topology
        satteliteJets = self.selectJets(
            event.jets, lambda x: x.pt() > 30.0 and x.jetID('POG_PFID_Loose'),
            leptons, 0.3, [VV.leg1, VV.leg2], 0.8)
        self.topology(VV, satteliteJets, leptons)
        output.append(VV)
        return output

    def makeMETV(self, event):
        output = []

        #loop on the leptons
        leptons = filter(
            lambda x: (abs(x.pdgId()) == 11 and x.heepID) or
            (abs(x.pdgId()) == 13 and x.highPtIDIso), event.selectedLeptons)
        fatJets = self.selectJets(
            event.jetsAK8, lambda x: x.pt() > 200.0 and abs(x.eta()) < 2.4 and
            x.jetID('POG_PFID_Loose'), leptons, 1.0)

        if len(fatJets) < 1:
            return output

        VV = Pair(event.met, fatJets[0])

        #kinematics
        if VV.deltaPhi() < 2.0 or VV.leg1.pt() < 200:
            return output

        self.substructure(VV.leg2, event)

        if not hasattr(VV.leg2, "substructure"):
            return output

        #check if there are subjets

#        if len(VV.leg2.substructure.prunedSubjets)<2:
#            print 'No substructure'
#            return output

#topology
        satteliteJets = self.selectJets(
            event.jets, lambda x: x.pt() > 30.0 and x.jetID('POG_PFID_Loose'),
            leptons, 0.3, [VV.leg2], 0.8)
        self.topology(VV, satteliteJets, leptons)
        output.append(VV)
        return output

    def process(self, event):
        self.readCollections(event.input)
        #first create a set of four vectors to recluster jets later
        event.LVs = ROOT.std.vector("math::XYZTLorentzVector")()
        #load packed candidatyes
        cands = self.handles['packed'].product()

        #if use PUPPI weigh them or lese just pass through
        if self.doPUPPI:
            for c in cands:
                if c.pt() > 13000 or c.pt() == float('Inf'):
                    continue
                if c.puppiWeight() > 0:
                    event.LVs.push_back(c.p4() * c.puppiWeight())
        else:
            for c in cands:
                if c.pt() > 13000 or c.pt() == float('Inf'):
                    continue
                event.LVs.push_back(c.p4())

        LNuJJ = self.makeWV(event)
        LLJJ = self.makeZV(event)
        JJ = self.makeJJ(event)
        JJNuNu = self.makeMETV(event)
        TopCR = self.makeTOPCR(event)

        setattr(event, 'LNuJJ' + self.cfg_ana.suffix, LNuJJ)
        setattr(event, 'JJ' + self.cfg_ana.suffix, JJ)
        setattr(event, 'LLJJ' + self.cfg_ana.suffix, LLJJ)
        setattr(event, 'JJNuNu' + self.cfg_ana.suffix, JJNuNu)
        setattr(event, 'TopCR' + self.cfg_ana.suffix, TopCR)