def drmatch(event, jet):
     dr_b, dr_wq1, dr_wq2 = 999, 999, 999
     if jet:
         top, _ = closest(jet, event.hadGenTops)
         if top:
             dr_b = deltaR(jet, top.genB)
             dr_wq1 = deltaR(jet, event.genparts[top.genW.dauIdx[0]])
             dr_wq2 = deltaR(jet, event.genparts[top.genW.dauIdx[1]])
         else:
             w, _ = closest(jet, event.hadGenWs)
             if w:
                 dr_wq1 = deltaR(jet, event.genparts[w.dauIdx[0]])
                 dr_wq2 = deltaR(jet, event.genparts[w.dauIdx[1]])
     return dr_b, max(dr_wq1, dr_wq2), min(dr_wq1, dr_wq2)
 def getmindr(event, jet, genParticles, pickGenW=False):
     dr = 999
     if jet:
         gp, _ = closest(jet, genParticles)
         if gp:
             if pickGenW:
                 gp = gp.genW
             dr = deltaR(jet, gp)
     return dr
 def _do_matching(self, partons, fatjets):
     rlt = {}
     jets_to_match = [j for j in fatjets]
     for p in partons:
         fj, dR = closest(p, jets_to_match)
         if dR < self._maxDeltaRJetParton:
             rlt[p] = fj
             jets_to_match.remove(fj)
         else:
             rlt[p] = _NullObject()
     return rlt
    def fillFatJetInfo(self, event):
        self.out.fillBranch("n_fatjet", len(event.fatjets))

        for idx in ([1, 2] if self._channel == 'qcd' else [1]):
            prefix = 'fj_%d_' % idx
            fj = event.fatjets[idx - 1]

            try:
                self.out.fillBranch(prefix + "DeepAK8MD_ZHbbvsQCD",
                                    fj.deepTagMD_ZHbbvsQCD)
                self.out.fillBranch(prefix + "DeepAK8MD_ZHccvsQCD",
                                    fj.deepTagMD_ZHccvsQCD)
                self.out.fillBranch(prefix + "DeepAK8MD_bbVsLight",
                                    fj.deepTagMD_bbvsLight)
                self.out.fillBranch(
                    prefix + "DeepAK8MD_bbVsTop",
                    (1 / (1 + (fj.deepTagMD_TvsQCD / fj.deepTagMD_HbbvsQCD) *
                          (1 - fj.deepTagMD_HbbvsQCD) /
                          (1 - fj.deepTagMD_TvsQCD))))
            except RuntimeError:
                self.out.fillBranch(prefix + "DeepAK8MD_ZHbbvsQCD", -1)
                self.out.fillBranch(prefix + "DeepAK8MD_ZHccvsQCD", -1)
                self.out.fillBranch(prefix + "DeepAK8MD_bbVsLight", -1)
                self.out.fillBranch(prefix + "DeepAK8MD_bbVsTop", -1)

            try:
                self.out.fillBranch(prefix + "ParticleNetMD_Xbb",
                                    fj.ParticleNetMD_probXbb)
                self.out.fillBranch(prefix + "ParticleNetMD_Xcc",
                                    fj.ParticleNetMD_probXcc)
                self.out.fillBranch(prefix + "ParticleNetMD_Xqq",
                                    fj.ParticleNetMD_probXqq)
                self.out.fillBranch(
                    prefix + "ParticleNetMD_QCD",
                    convert_prob(jet, None, prefix='ParticleNetMD_prob'))
                self.out.fillBranch(
                    prefix + "ParticleNetMD_XbbVsQCD",
                    convert_prob(fj, ['Xbb'], prefix='ParticleNetMD_prob'))
                self.out.fillBranch(
                    prefix + "ParticleNetMD_XccVsQCD",
                    convert_prob(fj, ['Xcc'], prefix='ParticleNetMD_prob'))
            except RuntimeError:
                self.out.fillBranch(prefix + "ParticleNetMD_HbbVsQCD", -1)
                self.out.fillBranch(prefix + "ParticleNetMD_HccVsQCD", -1)

            self.out.fillBranch(
                prefix + "is_lep_overlap",
                closest(fj, event.preselLeptons)[1] < self._jetConeSize)
            self.out.fillBranch(prefix + "pt", fj.pt)
            self.out.fillBranch(prefix + "eta", fj.eta)
            self.out.fillBranch(prefix + "phi", fj.phi)
            self.out.fillBranch(prefix + "sdmass", fj.msoftdrop)
            self.out.fillBranch(prefix + "tau21",
                                fj.tau2 / fj.tau1 if fj.tau1 > 0 else 99)
            self.out.fillBranch(prefix + "btagcsvv2", fj.btagCSVV2)
            try:
                self.out.fillBranch(prefix + "btagjp", fj.btagJP)
            except RuntimeError:
                self.out.fillBranch(prefix + "btagjp", -1)

            assert (len(fj.subjets) == 2)
            for idx_sj, sj in enumerate(fj.subjets):
                prefix_sj = prefix + 'sj%d_' % (idx_sj + 1)
                self.out.fillBranch(prefix_sj + "pt", sj.pt)
                self.out.fillBranch(prefix_sj + "eta", sj.eta)
                self.out.fillBranch(prefix_sj + "phi", sj.phi)
                self.out.fillBranch(prefix_sj + "btagcsvv2", sj.btagCSVV2)
                try:
                    self.out.fillBranch(prefix_sj + "btagdeepcsv",
                                        sj.btagDeepB)
                except RuntimeError:
                    self.out.fillBranch(prefix_sj + "btagdeepcsv", -1)
                try:
                    self.out.fillBranch(prefix_sj + "btagjp", sj.btagJP)
                except RuntimeError:
                    self.out.fillBranch(prefix_sj + "btagjp", -1)

                self.out.fillBranch(prefix_sj + "nsv", len(sj.sv_list))
                sv = sj.sv_list[0] if len(sj.sv_list) else _NullObject()
                fill_sv = self._get_filler(
                    sv)  # wrapper, fill default value if sv=None
                fill_sv(prefix_sj + "sv1_pt", sv.pt)
                fill_sv(prefix_sj + "sv1_mass", sv.mass)
                fill_sv(prefix_sj + "sv1_masscor",
                        corrected_svmass(sv) if sv else 0)
                fill_sv(prefix_sj + "sv1_ntracks", sv.ntracks)
                fill_sv(prefix_sj + "sv1_dxy", sv.dxy)
                fill_sv(prefix_sj + "sv1_dxysig", sv.dxySig)
                fill_sv(prefix_sj + "sv1_dlen", sv.dlen)
                fill_sv(prefix_sj + "sv1_dlensig", sv.dlenSig)
                fill_sv(prefix_sj + "sv1_chi2ndof", sv.chi2)
                fill_sv(prefix_sj + "sv1_pangle", sv.pAngle)

            sj1, sj2 = fj.subjets
            try:
                sv1, sv2 = sj1.sv_list[0], sj2.sv_list[0]
                sv = sv1 if sv1.dxySig > sv2.dxySig else sv2
                self.out.fillBranch(prefix + "sj12_masscor_dxysig",
                                    corrected_svmass(sv) if sv else 0)
            except IndexError:
                # if len(sv_list) == 0
                self.out.fillBranch(prefix + "sj12_masscor_dxysig", 0)

            # matching variables
            if self.isMC:
                self.out.fillBranch(prefix + "nbhadrons", fj.nBHadrons)
                self.out.fillBranch(prefix + "nchadrons", fj.nCHadrons)
                self.out.fillBranch(prefix + "sj1_nbhadrons", sj1.nBHadrons)
                self.out.fillBranch(prefix + "sj1_nchadrons", sj1.nCHadrons)
                self.out.fillBranch(prefix + "sj2_nbhadrons", sj2.nBHadrons)
                self.out.fillBranch(prefix + "sj2_nchadrons", sj2.nCHadrons)
Exemple #5
0
    def analyze(self, event):
        """
        process event, return True (go to next module)
        or False (fail, go to next event)
        """
        electrons = list(Collection(event, "Electron"))
        muons = list(Collection(event, "Muon"))
        jets = list(Collection(event, "Jet"))
        taus = list(Collection(event, "Tau"))
        flag = Object(event, "Flag")
        met = Object(event, "MET")

        # in case of systematic take the shifted values are default
        # For the central values, need to include jetMetTool all the time
        # Jet systematics
        if self.syst_var == "":
            syst_var = "nom"
        else:
            syst_var = self.syst_var

        ##############################
        ## corrections and MET
        ##############################
        
        try:
            var_jet_pts = getattr(event,  "Jet_pt_{}".format(syst_var), None)
            if var_jet_pts:
                for i,jet in enumerate(jets):
                    jet.pt = var_jet_pts[i]
            else:
                print 'WARNING: jet pts with variation {}'
                'not available, using the nominal value'.format(syst_var)
        except:
            var_jet_pts = getattr(event,  "Jet_pt_nom", None)
            for i,jet in enumerate(jets):
                jet.pt = var_jet_pts[i]

        try:
            var_met_pt  = getattr(event,  "MET_pt_{}".format(syst_var), None)
            var_met_phi = getattr(event, "MET_phi_{}".format(syst_var), None)
            if var_met_pt:
                met.pt = var_met_pt
            else:
                print 'WARNING: MET pt with variation '
                '{} not available, using the nominal value'.format(syst_var)
            if var_met_phi:
                met.phi = var_met_phi
            else:
                print 'WARNING: MET phi with variation {}'
                'not available, using the nominal value'.format(syst_var)
        except:
            var_met_pt  = getattr(event,  "MET_pt_nom", None)
            var_met_phi = getattr(event, "MET_phi_nom", None)
            if var_met_pt:
                met.pt = var_met_pt
            if var_met_phi:
                met.phi = var_met_phi

        met_p4 = ROOT.TLorentzVector()
        met_p4.SetPtEtaPhiM(met.pt,0.0,met.phi, 0.0)

        # Electrons Energy
        if "ElectronEn" in self.syst_var:
            (met_px, met_py) = ( met.pt*np.cos(met.phi), met.pt*np.sin(met.phi) )
            if "Up" in self.syst_var:
                for i, elec in enumerate(electrons):
                    met_px = met_px + (elec.energyErr)*np.cos(elec.phi)/math.cosh(elec.eta)
                    met_py = met_py + (elec.energyErr)*np.sin(elec.phi)/math.cosh(elec.eta)
                    elec.pt = elec.pt + elec.energyErr/math.cosh(elec.eta)
            else:
                for i, elec in enumerate(electrons):
                    met_px = met_px - (elec.energyErr)*np.cos(elec.phi)/math.cosh(elec.eta)
                    met_py = met_py - (elec.energyErr)*np.sin(elec.phi)/math.cosh(elec.eta)
                    elec.pt = elec.pt - elec.energyErr/math.cosh(elec.eta)
            met.pt  = math.sqrt(met_px**2 + met_py**2)
            met.phi = math.atan2(met_py, met_px)

        # Muons Energy
        if self.isMC:
            muons_pts = getattr(event, "Muon_corrected_pt")
            for i, muon in enumerate(muons):
                muon.pt = muons_pts[i]

        if "MuonEn" in self.syst_var:
            (met_px, met_py) = ( met.pt*np.cos(met.phi), met.pt*np.sin(met.phi) )
            if "Up" in self.syst_var:
                muons_pts = getattr(event, "Muon_correctedUp_pt")
                for i, muon in enumerate(muons):
                    met_px = met_px - (muons_pts[i] - muon.pt)*np.cos(muon.phi)
                    met_py = met_py - (muons_pts[i] - muon.pt)*np.sin(muon.phi)
                    muon.pt = muons_pts[i]
            else:
                muons_pts = getattr(event, "Muon_correctedDown_pt")
                for i, muon in enumerate(muons):
                    met_px =met_px - (muons_pts[i] - muon.pt)*np.cos(muon.phi)
                    met_py =met_py - (muons_pts[i] - muon.pt)*np.sin(muon.phi)
                    muon.pt = muons_pts[i]
            met.pt  = math.sqrt(met_px**2 + met_py**2)
            met.phi = math.atan2(met_py, met_px)
            
        pass_met_filter = self.met_filter(flag, True)

        # filling and contructing the event categorisation
        self.out.fillBranch("met_pt{}".format(self.syst_suffix), met.pt)
        self.out.fillBranch("met_phi{}".format(self.syst_suffix), met.phi)
        self.out.fillBranch("met_filter{}".format(self.syst_suffix), pass_met_filter)

        ##############################
        ## process leptons 
        ##############################

        muons.sort(key=lambda muon: muon.pt, reverse=True)
        electrons.sort(key=lambda el: el.pt, reverse=True)

        # Choose tight-quality e/mu for event categorization
        good_muons = []
        for idx,mu in enumerate(muons):
            isoLep   = mu.pfRelIso04_all
            pass_ips = abs(mu.dxy) < 0.02 and abs(mu.dz) < 0.1
            pass_fid = abs(mu.eta) < 2.4 and mu.pt >= (25 if idx==0 else 20)
            pass_ids = mu.tightId and isoLep <= 0.15
            if pass_fid and pass_ids and pass_ips:
                good_muons.append(mu)
                
        good_electrons = []
        for idy,el in enumerate(electrons):
            id_CB = el.cutBased
            # changing to MVA based ID :
            if el.pt >= (25 if idy==0 else 20) and abs(el.eta) <= 2.5 and self.electron_id(el, "90"):
                good_electrons.append(el)

        # let sort the muons in pt
        good_muons.sort(key=lambda x: x.pt, reverse=True)
        good_electrons.sort(key=lambda x: x.pt, reverse=True)

        # Find any remaining e/mu that pass looser selection
        extra_leptons = []
        for mu in muons:
            isoLep   = mu.pfRelIso04_all
            pass_ids = mu.softId and isoLep <= 0.25
            pass_fid = abs(mu.eta) < 2.4 and mu.pt >= 7
            if tk.closest(mu, good_muons)[1] < 0.01:
                continue
            if pass_fid and pass_ids:
                extra_leptons.append(mu)

        for el in electrons:
            pass_fid = abs(el.eta) < 2.5 and el.pt >= 7
            if tk.closest(el, good_electrons)[1] < 0.01:
                continue
            if pass_fid and self.electron_id(el, "WPL"):
                extra_leptons.append(el)

        good_leptons = good_electrons + good_muons
        good_leptons.sort(key=lambda x: x.pt, reverse=True)
        extra_leptons.sort(key=lambda x: x.pt, reverse=True)

        ngood_leptons = len(good_leptons)
        nextra_leptons = len(extra_leptons)
        
        _leading_lep_flavor = 0
        if len(good_muons) and len(good_electrons): 
            if good_muons[0].pt > good_electrons[0].pt: _leading_lep_flavor = 1

        _leading_lep_pt = good_leptons[0].pt if ngood_leptons else 0.0
        _leading_lep_eta = good_leptons[0].eta if ngood_leptons else -99.
        _leading_lep_phi = good_leptons[0].phi if ngood_leptons else -99.
        _trailing_lep_pt = good_leptons[1].pt if ngood_leptons >= 2 else 0.0
        _trailing_lep_eta = good_leptons[1].eta if ngood_leptons >= 2 else -99.
        _trailing_lep_phi = good_leptons[1].phi if ngood_leptons >= 2 else -99.

        self.out.fillBranch("ngood_leptons{}".format(self.syst_suffix), ngood_leptons)
        self.out.fillBranch("nextra_leptons{}".format(self.syst_suffix), nextra_leptons)
        self.out.fillBranch("leading_lep_flavor{}".format(self.syst_suffix), _leading_lep_flavor)
        self.out.fillBranch("leading_lep_pt{}".format(self.syst_suffix), _leading_lep_pt)
        self.out.fillBranch("leading_lep_eta{}".format(self.syst_suffix), _leading_lep_eta)
        self.out.fillBranch("leading_lep_phi{}".format(self.syst_suffix), _leading_lep_phi)
        self.out.fillBranch("trailing_lep_pt{}".format(self.syst_suffix), _trailing_lep_pt)
        self.out.fillBranch("trailing_lep_eta{}".format(self.syst_suffix), _trailing_lep_eta)
        self.out.fillBranch("trailing_lep_phi{}".format(self.syst_suffix), _trailing_lep_phi)

        if False:
            print "number of leptons [all, good, extra]: ", ngood_leptons, " : ", nextra_leptons
            print "        CBId electrons : ", [e.cutBased for e in good_electrons]
            print "        WP90 electrons : ", [e.mvaFall17Iso_WP90 for e in good_electrons]
            print "             muons     : ", [e.tightId for e in good_muons]
            print "        lepton pts     : ", [e.pt for e in good_leptons]

        # Leptons efficiency/Trigger/Isolation Scale factors
        # These are applied only of the first 2 leading leptons
        if self.isMC:
            w_muon_SF     = w_electron_SF     = 1.0
            w_muon_SFUp   = w_electron_SFUp   = 1.0
            w_muon_SFDown = w_electron_SFDown = 1.0

            if ngood_leptons >= 2:
                if abs(good_leptons[0].pdgId) == 11:
                    w_electron_SF     *=  good_leptons[0].SF
                    w_electron_SFUp   *= (good_leptons[0].SF + good_leptons[0].SFErr)
                    w_electron_SFDown *= (good_leptons[0].SF - good_leptons[0].SFErr)
                if abs(good_leptons[0].pdgId) == 11:
                    w_electron_SF     *=  good_leptons[1].SF
                    w_electron_SFUp   *= (good_leptons[1].SF + good_leptons[1].SFErr)
                    w_electron_SFDown *= (good_leptons[1].SF - good_leptons[1].SFErr)
                if abs(good_leptons[0].pdgId) == 13:
                    w_muon_SF     *=  good_leptons[0].SF
                    w_muon_SFUp   *= (good_leptons[0].SF + good_leptons[0].SFErr)
                    w_muon_SFDown *= (good_leptons[0].SF - good_leptons[0].SFErr)
                if abs(good_leptons[1].pdgId) == 13:
                    w_muon_SF     *=  good_leptons[1].SF
                    w_muon_SFUp   *= (good_leptons[1].SF + good_leptons[1].SFErr)
                    w_muon_SFDown *= (good_leptons[1].SF - good_leptons[1].SFErr)

            self.out.fillBranch("w_muon_SF"        , w_muon_SF        )
            self.out.fillBranch("w_muon_SFUp"      , w_muon_SFUp      )
            self.out.fillBranch("w_muon_SFDown"    , w_muon_SFDown    )
            self.out.fillBranch("w_electron_SF"    , w_electron_SF    )
            self.out.fillBranch("w_electron_SFUp"  , w_electron_SFUp  )
            self.out.fillBranch("w_electron_SFDown", w_electron_SFDown)

        # process taus
        had_taus = []
        for tau in taus:
            if tk.closest(tau, good_leptons)[1] < 0.4:
                continue
            # only hadronic tau decay
            if tau.decayMode != 5:
                continue
            if tau.pt > 18 and abs(tau.eta) <= 2.3:
                had_taus.append(tau)
        _nhad_taus = len(had_taus)

        self.out.fillBranch("nhad_taus{}".format(self.syst_suffix), _nhad_taus)
        self.out.fillBranch("lead_tau_pt{}".format(self.syst_suffix), had_taus[0].pt if _nhad_taus else 0)

        ##############################
        ## process jet 
        ##############################

        z_candidate = []
        zcand_p4 = ROOT.TLorentzVector()
        emulated_met = ROOT.TLorentzVector()
        all_lepton_p4 = ROOT.TLorentzVector()
        rem_lepton_p4 = ROOT.TLorentzVector()

        good_jets  = []
        good_bjets = []
        for jet in jets:
            if jet.pt < 30.0 or abs(jet.eta) > 4.7:
                continue
            if not jet.jetId:
                continue
            if tk.closest(jet, good_leptons)[1] < 0.4:
                continue
            good_jets.append(jet)
            # Count b-tag with medium WP DeepJet
            # ref : https://twiki.cern.ch/twiki/bin/view/CMS/BtagRecommendation
            if abs(jet.eta) <= 2.4 and jet.btagDeepFlavB > self.btag_id("loose"):
                good_bjets.append(jet)

        _ngood_jets = len(good_jets)
        _ngood_bjets = len(good_bjets)
        good_jets.sort(key=lambda jet: jet.pt, reverse=True)
        good_bjets.sort(key=lambda jet: jet.pt, reverse=True)

        _lead_jet_pt = good_jets[0].pt if _ngood_jets >= 1 else 0.
        _lead_jet_eta = good_jets[0].eta if _ngood_jets >= 1 else -99.
        _lead_jet_phi = good_jets[0].phi if _ngood_jets >= 1 else -99.
        _lead_jet_mass = good_jets[0].mass if _ngood_jets >= 1 else -99.
        _trail_jet_pt = good_jets[1].pt if _ngood_jets >= 2 else 0.
        _trail_jet_eta = good_jets[1].eta if _ngood_jets >= 2 else -99.
        _trail_jet_phi = good_jets[1].phi if _ngood_jets >= 2 else -99.
        _trail_jet_mass = good_jets[1].mass if _ngood_jets >= 2 else -99.
        _third_jet_pt = good_jets[2].pt if _ngood_jets >= 3 else 0.
        _third_jet_eta = good_jets[2].eta if _ngood_jets >= 3 else -99.
        _third_jet_phi = good_jets[2].phi if _ngood_jets >= 3 else -99.
        _third_jet_mass = good_jets[2].mass if _ngood_jets >= 3 else -99.
        _H_T = sum([jet.pt for jet in good_jets])
        _dphi_j_met = tk.deltaPhi(good_jets[0], met.phi) if _ngood_jets >= 1 else -99.
        _lead_bjet_pt = good_bjets[0].pt if _ngood_bjets else 0.

        self.out.fillBranch("ngood_jets{}".format(self.syst_suffix), _ngood_jets)
        self.out.fillBranch("ngood_bjets{}".format(self.syst_suffix), _ngood_bjets)
        self.out.fillBranch("lead_jet_pt{}".format(self.syst_suffix), _lead_jet_pt)
        self.out.fillBranch("lead_jet_eta{}".format(self.syst_suffix), _lead_jet_eta)
        self.out.fillBranch("lead_jet_phi{}".format(self.syst_suffix), _lead_jet_phi)
        self.out.fillBranch("lead_jet_mass{}".format(self.syst_suffix), _lead_jet_mass)
        self.out.fillBranch("trail_jet_pt{}".format(self.syst_suffix), _trail_jet_pt)
        self.out.fillBranch("trail_jet_eta{}".format(self.syst_suffix), _trail_jet_eta)
        self.out.fillBranch("trail_jet_phi{}".format(self.syst_suffix), _trail_jet_phi)
        self.out.fillBranch("trail_jet_mass{}".format(self.syst_suffix), _trail_jet_mass)
        self.out.fillBranch("third_jet_pt{}".format(self.syst_suffix), _third_jet_pt)
        self.out.fillBranch("third_jet_eta{}".format(self.syst_suffix), _third_jet_eta)
        self.out.fillBranch("third_jet_phi{}".format(self.syst_suffix), _third_jet_phi)
        self.out.fillBranch("third_jet_mass{}".format(self.syst_suffix), _third_jet_mass)
        self.out.fillBranch("H_T{}".format(self.syst_suffix), _H_T)
        self.out.fillBranch("delta_phi_j_met{}".format(self.syst_suffix), _dphi_j_met)
        self.out.fillBranch("lead_bjet_pt{}".format(self.syst_suffix), _lead_bjet_pt)

        ##############################
        ## construct Z and category
        ##############################

        lep_category = 0
        if ngood_leptons < 2:
            lep_category = -1

        if ngood_leptons == 2 and nextra_leptons==0:
            # constructing the signal region
            if (good_leptons[0].pdgId * good_leptons[1].pdgId) == -11*11:
                lep_category = 1 # EE category
            if (good_leptons[0].pdgId * good_leptons[1].pdgId) == -11*13:
                lep_category = 2 # EM category
            if (good_leptons[0].pdgId * good_leptons[1].pdgId) == -13*13:
                lep_category = 3 # MM category
            z_candidate = [good_leptons[0], good_leptons[1]]
            zcand_p4 = good_leptons[0].p4() + good_leptons[1].p4()
            all_lepton_p4 = zcand_p4

        elif ngood_leptons == 3 and nextra_leptons==0:
            # constructing the 3 leptons CR
            for pair in itertools.combinations(good_leptons, 2):
                if pair[0].pdgId == -pair[1].pdgId:
                    zcand_ = pair[0].p4() + pair[1].p4()
                    if abs(zcand_.M()-self.zmass) < abs(zcand_p4.M()-self.zmass):
                        zcand_p4 = zcand_
                        z_candidate = list(pair)
                        lep3_idx_ = 3 - good_leptons.index(pair[0]) - good_leptons.index(pair[1])
                        emulated_met = good_leptons[lep3_idx_].p4() + met_p4
                        all_lepton_p4 = zcand_ + good_leptons[lep3_idx_].p4()
                        rem_lepton_p4 = good_leptons[lep3_idx_].p4()
                        if abs(pair[0].pdgId) == 11:
                            lep_category = 4 # EEL category
                        if abs(pair[0].pdgId) == 13:
                            lep_category = 5 # MML category

        elif ngood_leptons>=2 and (ngood_leptons + nextra_leptons) == 4:
            # constructing the 4 leptons CR
            for pair in itertools.combinations(good_leptons, 2):
                # checking if OSSF pair
                rem_pair = [x for x in good_leptons + extra_leptons if x not in pair]
                if (pair[0].pdgId == -pair[1].pdgId) and (rem_pair[0].pdgId == -rem_pair[1].pdgId):
                    zcand_0 = pair[0].p4() + pair[1].p4()
                    zcand_1 = rem_pair[0].p4() + rem_pair[1].p4()
                    if abs(zcand_0.M()-self.zmass) < abs(zcand_p4.M()-self.zmass):
                        zcand_p4 = zcand_0
                        z_candidate = pair
                        emulated_met =  zcand_1 + met_p4
                        all_lepton_p4 = zcand_p4 + zcand_1
                        rem_lepton_p4 = zcand_1
                        if abs(pair[0].pdgId) == 11:
                            lep_category = 6 # EELL category
                        if abs(pair[0].pdgId) == 13:
                            lep_category = 7 # MMLL category

        else:
            # too many bad leptons, with no obvious meaning ?
            if ngood_leptons==1 and (ngood_leptons + nextra_leptons)>=1:
                lep_category = -2
            elif ngood_leptons>=2 and (ngood_leptons + nextra_leptons)>=2:
                lep_category = -3
            else:
                lep_category = -4

        # filling MonoZ type of variables
        self.out.fillBranch("lep_category{}".format(self.syst_suffix), lep_category)
        self.out.fillBranch("Z_pt{}".format(self.syst_suffix), zcand_p4.Pt())
        self.out.fillBranch("Z_eta{}".format(self.syst_suffix), zcand_p4.Eta())
        self.out.fillBranch("Z_phi{}".format(self.syst_suffix), zcand_p4.Phi())
        self.out.fillBranch("Z_mass{}".format(self.syst_suffix), zcand_p4.M())
        self.out.fillBranch("Z_mt{}".format(self.syst_suffix), zcand_p4.Mt())
        
        ##############################
        ## high level info
        ##############################

        _delta_zphi = tk.deltaPhi(z_candidate[0].phi, z_candidate[1].phi) if lep_category > 0 else -99
        _delta_zdR  = tk.deltaR(z_candidate[0].eta, z_candidate[0].phi,
                                z_candidate[1].eta, z_candidate[1].phi,) if lep_category > 0 else -99
        _delta_zeta     = abs(z_candidate[0].eta - z_candidate[1].eta) if lep_category > 0 else -99
        _delta_phi_zmet = tk.deltaPhi(zcand_p4.Phi(), met.phi)
        _vec_delta_balance  = (met_p4 - zcand_p4).Pt()/zcand_p4.Pt() if zcand_p4.Pt() != 0 else -1
        _sca_delta_balance  = met.pt/zcand_p4.Pt() if zcand_p4.Pt() != 0 else -1

        # hadronic recoil
        had_recoil_p4 = ROOT.TLorentzVector()
        had_recoil_p4 += met_p4
        for lep in good_leptons + extra_leptons:
            had_recoil_p4 += lep.p4()
        had_recoil_p4 = -had_recoil_p4
        _delta_met_rec = tk.deltaPhi(met.phi, had_recoil_p4.Phi()) if lep_category > 0 else -99

        _MT = np.sqrt(2 * zcand_p4.Pt() * var_met_pt * (1 - np.cos(_delta_phi_zmet)))
        # defined as from https://arxiv.org/pdf/1808.09054.pdf
        _Ell = np.sqrt(zcand_p4.Mag2() + zcand_p4.M2())
        _altMT = np.sqrt(np.power(_Ell + met_p4.Pt(),2) + (met_p4 + zcand_p4).Mag2())
        # checking the transverse mass
        _rem_p4 = ROOT.TLorentzVector()
        _rem_p4.SetPtEtaPhiM(rem_lepton_p4.Pt(), 0, rem_lepton_p4.Phi(), 0)

        self.out.fillBranch("delta_phi_ll{}".format(self.syst_suffix), _delta_zphi)
        self.out.fillBranch("delta_eta_ll{}".format(self.syst_suffix), _delta_zeta)
        self.out.fillBranch("delta_R_ll{}".format(self.syst_suffix), _delta_zdR)
        self.out.fillBranch("delta_phi_ZMet{}".format(self.syst_suffix), _delta_phi_zmet)
        self.out.fillBranch("vec_balance{}".format(self.syst_suffix), _vec_delta_balance)
        self.out.fillBranch("sca_balance{}".format(self.syst_suffix), _sca_delta_balance)
        self.out.fillBranch("hadronic_recoil{}".format(self.syst_suffix), had_recoil_p4.Pt())
        self.out.fillBranch("delta_met_rec{}".format(self.syst_suffix), _delta_met_rec)
        self.out.fillBranch("emulatedMET{}".format(self.syst_suffix), emulated_met.Pt())
        self.out.fillBranch("emulatedMET_phi{}".format(self.syst_suffix), emulated_met.Phi())
        self.out.fillBranch("mass_alllep{}".format(self.syst_suffix), all_lepton_p4.M())
        self.out.fillBranch("pt_alllep{}".format(self.syst_suffix), all_lepton_p4.Pt())
        self.out.fillBranch("remll_mass{}".format(self.syst_suffix), rem_lepton_p4.M())
        self.out.fillBranch("MT{}".format(self.syst_suffix), _MT)
        self.out.fillBranch("altMT{}".format(self.syst_suffix), _altMT)
        self.out.fillBranch("trans_mass{}".format(self.syst_suffix), (_rem_p4 + met_p4).M())

        # Let remove the negative categories with no obvious meaning meaning
        # This will reduce the size of most of the bacground and data
        if (lep_category > 0 and zcand_p4.Pt()>60 and zcand_p4.M() > 55 and zcand_p4.M() < 127):
            return True
        else:
            return False
    def prepareEvent(self, event):

        logging.debug('processing event %d' % event.event)

        ## met selection
        if event.met.pt < 50.0:
            return False

        ## muon selection
        muSubJets = []
        for j in Collection(event, "CustomAK4CHS"):
            if j.pt > 30 and abs(j.eta) < 2.4:
                muSubJets.append(j)

        event._allMuons = Collection(event, "Muon")
        event.muons = []
        for muon in event._allMuons:
            if muon.pt > 55 and abs(muon.eta) < 2.4 and muon.tightId and abs(
                    muon.dxy) < 0.2 and abs(muon.dz) < 0.5:
                j, muon.drjet = closest(muon, muSubJets)
                muon.ptrel = muon.p4().Perp(j.p4().Vect()) if j else 0
                #                 if muon.pfRelIso04_all < 0.15:
                if muon.drjet > 0.4 or muon.ptrel > 25:
                    event.muons.append(muon)
        if len(event.muons) != 1:
            return False

        # #leptonic W pt cut
        event.mu = event.muons[0]
        event.leptonicW = event.mu.p4() + event.met.p4()
        if event.leptonicW.Pt() < 250.0:
            return False

        ## b-tag AK4 jet selection
        event.ak4jets = []
        for j in event._allJets:
            if not (j.pt > 25.0 and abs(j.eta) < 2.4 and (j.jetId & 2)):
                continue
            if j.btagCSVV2 > 0.8484 and\
               abs(deltaPhi(j, event.muons[0])) < 2.0:
                event.ak4jets.append(j)

        if len(event.ak4jets) < 1:
            return False

        # # selection on AK8 jets
        event.ak8jets = []
        for fj in event._allAK8jets:
            if not (fj.pt > 200 and abs(fj.eta) < 2.4 and (fj.jetId & 2)):
                continue
            if abs(deltaPhi(fj, event.muons[0])) > 2.0:
                event.ak8jets.append(fj)

        if len(event.ak8jets) < 1:
            return False

        # # selection on CA15 jets
        event.ca15jets = []
        for fj in event._allCA15jets:
            if not (fj.pt > 200 and abs(fj.eta) < 2.4 and (fj.jetId & 2)):
                continue
            if abs(deltaPhi(fj, event.muons[0])) > 2.0:
                event.ca15jets.append(fj)

        if len(event.ca15jets) < 1:
            return False

        ## require the leading ak8 & ca15 jets overlap
        if deltaR(event.ak8jets[0], event.ca15jets[0]) > 0.8:
            return False

        # # selection on HOTVR jets
        event.hotvrjets = []
        for fj in event._allHOTVRjets:
            if not (fj.pt > 200 and abs(fj.eta) < 2.4):
                continue
            if abs(deltaPhi(fj, event.muons[0])) > 2.0:
                event.hotvrjets.append(fj)

        ## return True if passes selection
        return True
Exemple #7
0
 def _fill_matching(self, parton, daughters, fatjetCollection, fjname):
     fj, dR = closest(parton, fatjetCollection)
     self.out.fillBranch("dR_gen_%s" % fjname, dR)
     return _NullObject() if dR > self._maxDeltaRJetParton else fj
    def analyze(self, event):
        """
        process event, return True (go to next module)
        or False (fail, go to next event)
        """

        # skip useless events
        if event.lep_category <= 0:
            return False

        # Get collection from event tree
        electrons = list(Collection(event, "Electron"))
        muons = list(Collection(event, "Muon"))
        jets = list(Collection(event, "Jet"))

        # in case of systematic take the shifted values are default
        # For the central values, need to include jetMetTool all the time
        # Jet systematics
        if self.syst_var == "":
            syst_var = "nom"
        else:
            syst_var = self.syst_var
        # checking something
        try:
            var_jet_pts = getattr(event, "Jet_pt_{}".format(syst_var), None)
            if var_jet_pts:
                for i, jet in enumerate(jets):
                    jet.pt = var_jet_pts[i]
            else:
                print 'WARNING: jet pts with variation {}'
                'not available, using the nominal value'.format(syst_var)
        except:
            var_jet_pts = getattr(event, "Jet_pt_nom", None)
            for i, jet in enumerate(jets):
                jet.pt = var_jet_pts[i]

        # Get variables from event tree
        Z_pt = event.Z_pt
        Z_eta = event.Z_eta
        Z_phi = event.Z_phi
        Z_mass = event.Z_mass
        Z_p4 = ROOT.TLorentzVector()
        Z_p4.SetPtEtaPhiM(Z_pt, Z_eta, Z_phi, Z_mass)
        Z_p4_bst = ROOT.TLorentzVector(Z_p4)

        met_pt = event.met_pt
        met_phi = event.met_phi
        met_p4 = ROOT.TLorentzVector()
        met_p4.SetPtEtaPhiM(met_pt, 0., met_phi, 0.)
        met_p4_bst = ROOT.TLorentzVector(met_p4)

        emulatedMET_pt_bst = event.emulatedMET
        emulatedMET_phi = event.emulatedMET_phi
        emulatedMET_p4 = ROOT.TLorentzVector()
        emulatedMET_p4.SetPtEtaPhiM(emulatedMET_pt_bst, 0., emulatedMET_phi,
                                    0.)
        emulatedMET_p4_bst = ROOT.TLorentzVector(emulatedMET_p4)

        # process leptons
        good_leptons = []
        good_muons = []
        good_electrons = []
        # Choose tight-quality e/mu for event categorization
        for idx, mu in enumerate(muons):
            isoLep = mu.pfRelIso04_all
            pass_ips = abs(mu.dxy) < 0.02 and abs(mu.dz) < 0.1
            pass_fid = abs(mu.eta) < 2.4 and mu.pt >= (25 if idx == 0 else 20)
            pass_ids = mu.tightId and isoLep <= 0.15
            if pass_fid and pass_ids and pass_ips:
                good_muons.append(mu)
        for idy, el in enumerate(electrons):
            if el.pt >= (25 if idy == 0 else 20) and abs(
                    el.eta) <= 2.5 and self.electron_id(el, "90"):
                good_electrons.append(el)

        # Find any remaining e/mu that pass looser selection
        extra_leptons = []
        for mu in muons:
            isoLep = mu.pfRelIso04_all
            pass_ids = mu.softId and isoLep <= 0.25
            pass_fid = abs(mu.eta) < 2.4 and mu.pt >= 10
            if tk.closest(mu, good_muons)[1] < 0.01:
                continue
            if pass_fid and pass_ids:
                extra_leptons.append(mu)

        for el in electrons:
            pass_fid = abs(el.eta) < 2.5 and el.pt >= 10
            if tk.closest(el, good_electrons)[1] < 0.01:
                continue
            if pass_fid and self.electron_id(el, "WPL"):
                extra_leptons.append(el)

        # sort the leptons in pt
        good_muons.sort(key=lambda x: x.pt, reverse=True)
        good_electrons.sort(key=lambda x: x.pt, reverse=True)
        good_leptons = good_electrons + good_muons
        good_leptons.sort(key=lambda x: x.pt, reverse=True)
        extra_leptons.sort(key=lambda x: x.pt, reverse=True)
        ngood_leptons = len(good_leptons)
        nextra_leptons = len(extra_leptons)
        if ngood_leptons != event.ngood_leptons or nextra_leptons != event.nextra_leptons:
            print(
                'n good (extra) leptons: {} ({}) in VBSProducer, {} ({}) in MonoZProducer.'
                .format(ngood_leptons, nextra_leptons, event.ngood_leptons,
                        event.nextra_leptons))
        lead_lep_p4 = good_leptons[0].p4(
        ) if ngood_leptons else ROOT.TLorentzVector(0., 0., 0., 0.)
        lead_lep_p4_bst = ROOT.TLorentzVector(lead_lep_p4)
        lead_lep_pt = good_leptons[0].pt if ngood_leptons else 0.
        trail_lep_p4 = good_leptons[1].p4(
        ) if ngood_leptons >= 2 else ROOT.TLorentzVector(0., 0., 0., 0.)
        trail_lep_p4_bst = ROOT.TLorentzVector(trail_lep_p4)
        trail_lep_pt = good_leptons[1].pt if ngood_leptons >= 2 else 0.

        # process jet
        good_jets = []
        good_jets_p4 = ROOT.TLorentzVector(0., 0., 0., 0.)
        et_jets20 = 0.
        et_jets30 = 0.
        for jet in jets:
            if not jet.jetId:
                continue
            if tk.closest(jet, good_leptons)[1] < 0.4:
                continue
            if jet.pt <= 20 or abs(jet.eta) > 4.7:
                continue
            et_jets20 += jet.p4().Et()
            if jet.pt < 30.:
                continue
            et_jets30 += jet.p4().Et()
            good_jets.append(jet)
            good_jets_p4 += jet.p4()
        # sort the jets by pt
        good_jets.sort(key=lambda jet: jet.pt, reverse=True)
        ngood_jets = len(good_jets)
        if ngood_jets != event.ngood_jets:
            print 'ngood_jets: {} in VBSProducer, {} in MonoZProducer'.format(
                ngood_jets, event.ngood_jets)
        lead_jet_p4 = good_jets[0].p4() if ngood_jets else ROOT.TLorentzVector(
            0., 0., 0., 0.)
        lead_jet_pt = good_jets[0].pt if ngood_jets else 0.
        lead_jet_phi = good_jets[0].phi if ngood_jets else 0.
        lead_jet_eta = good_jets[0].eta if ngood_jets else -99.
        trail_jet_p4 = good_jets[1].p4(
        ) if ngood_jets > 1 else ROOT.TLorentzVector(0., 0., 0., 0.)
        trail_jet_pt = good_jets[1].pt if ngood_jets > 1 else 0.
        trail_jet_phi = good_jets[1].phi if ngood_jets > 1 else 0.
        trail_jet_eta = good_jets[1].eta if ngood_jets > 1 else -99.

        # boosting 4 vectors
        boost_vet = good_jets_p4.BoostVector()
        lead_lep_p4_bst.Boost(boost_vet)
        trail_lep_p4_bst.Boost(boost_vet)
        Z_p4_bst.Boost(boost_vet)
        met_p4_bst.Boost(boost_vet)
        emulatedMET_p4_bst.Boost(boost_vet)

        # variables in bossted frame
        delta_eta_ll_bst = abs(lead_lep_p4_bst.Eta() - trail_lep_p4_bst.Eta())
        delta_phi_ll_bst = tk.deltaPhi(lead_lep_p4_bst.Phi(),
                                       trail_lep_p4_bst.Phi())
        delta_phi_ZMet_bst = tk.deltaPhi(Z_p4_bst.Phi(), met_p4_bst.Phi())

        # more variables
        x_denom20 = (et_jets20 + met_pt + Z_pt)
        x_denom30 = (et_jets30 + met_pt + Z_pt)
        x_Z = Z_pt / x_denom30 if x_denom30 > 0. else -99.
        x_jet20 = et_jets20 / x_denom20 if x_denom20 > 0. else -99.
        x_jet30 = et_jets30 / x_denom30 if x_denom30 > 0. else -99.
        x_MET = met_pt / x_denom30 if x_denom30 > 0. else -99.
        H_T = sum([jet.pt for jet in good_jets])
        HT_F = (lead_jet_pt + trail_jet_pt) / H_T if H_T > 0. else 0.
        Jet_etas_multiplied = lead_jet_eta * trail_jet_eta

        # more variables
        if ngood_jets >= 2:
            S_T_jets = (lead_jet_p4 + trail_jet_p4).Pt() / (lead_jet_pt +
                                                            trail_jet_pt)
            S_T_hard = (lead_jet_p4 + trail_jet_p4 +
                        Z_p4).Pt() / (lead_jet_pt + trail_jet_pt + Z_pt)
            S_T_all = (lead_jet_p4 + trail_jet_p4 + Z_p4 + met_p4).Pt() / (
                lead_jet_pt + trail_jet_pt + Z_pt + met_pt)
            Jet_pt_Ratio = trail_jet_pt / lead_jet_pt
            R_pt = lead_lep_pt * trail_lep_pt / (lead_jet_pt * trail_jet_pt)
            dijet_abs_dEta = abs(lead_jet_eta - trail_jet_eta)
            dijet_Mjj = (lead_jet_p4 + trail_jet_p4).M()
            dijet_Zep = Z_eta - 0.5 * (lead_jet_eta + trail_jet_eta)
            dijet_centrality = np.exp(-4 * (dijet_Zep / dijet_abs_dEta)**
                                      2) if dijet_abs_dEta > 0 else -99.
            # study jets between leading and trailing jets
            eta_range = sorted([lead_jet_eta, trail_jet_eta])
            pT_mid_Jets = [
                jet.pt for jet in good_jets[2:]
                if eta_range[0] < jet.eta < eta_range[1]
            ]
            CJV_Pt = pT_mid_Jets[0] if len(pT_mid_Jets) else 0.
            CJV_Pt_Sum = sum(pT_mid_Jets) if len(pT_mid_Jets) else 0.
        else:
            S_T_jets = -99.
            S_T_hard = -99.
            S_T_all = -99.
            Jet_pt_Ratio = -99.
            R_pt = -99.
            dijet_abs_dEta = -99.
            dijet_Mjj = -99.
            dijet_Zep = -99.
            dijet_centrality = -99.
            CJV_Pt = 0.
            CJV_Pt_Sum = 0.

        # more variables
        dPT_OZ = (lead_jet_pt + trail_jet_pt) / Z_pt if Z_pt > 0. else -99.
        etaThirdJet = good_jets[2].eta if ngood_jets >= 3 else -99.
        deltaPhiClosestJetMet = 99.
        deltaPhiFarthestJetMet = -99.
        for jet in good_jets:
            if deltaPhiClosestJetMet > abs(tk.deltaPhi(jet.phi, met_phi)):
                deltaPhiClosestJetMet = abs(tk.deltaPhi(jet.phi, met_phi))
            if deltaPhiFarthestJetMet < abs(tk.deltaPhi(jet.phi, met_phi)):
                deltaPhiFarthestJetMet = abs(tk.deltaPhi(jet.phi, met_phi))

        self.out.fillBranch("lead_jet_eta{}".format(self.syst_suffix),
                            lead_jet_eta)
        self.out.fillBranch("lead_jet_phi{}".format(self.syst_suffix),
                            lead_jet_phi)
        self.out.fillBranch("trail_jet_pt{}".format(self.syst_suffix),
                            trail_jet_pt)
        self.out.fillBranch("trail_jet_eta{}".format(self.syst_suffix),
                            trail_jet_eta)
        self.out.fillBranch("trail_jet_phi{}".format(self.syst_suffix),
                            trail_jet_phi)
        self.out.fillBranch("dijet_abs_dEta{}".format(self.syst_suffix),
                            dijet_abs_dEta)
        self.out.fillBranch("dijet_Mjj{}".format(self.syst_suffix), dijet_Mjj)
        self.out.fillBranch("dijet_Zep{}".format(self.syst_suffix), dijet_Zep)
        self.out.fillBranch("dijet_centrality{}".format(self.syst_suffix),
                            dijet_centrality)
        self.out.fillBranch("S_T_hard{}".format(self.syst_suffix), S_T_hard)
        self.out.fillBranch("S_T_jets{}".format(self.syst_suffix), S_T_jets)
        self.out.fillBranch("S_T_all{}".format(self.syst_suffix), S_T_all)

        self.out.fillBranch("x_Z{}".format(self.syst_suffix), x_Z)
        self.out.fillBranch("x_jet20{}".format(self.syst_suffix), x_jet20)
        self.out.fillBranch("x_jet30{}".format(self.syst_suffix), x_jet30)
        self.out.fillBranch("x_MET{}".format(self.syst_suffix), x_MET)
        self.out.fillBranch("H_T{}".format(self.syst_suffix), H_T)
        self.out.fillBranch("HT_F{}".format(self.syst_suffix), HT_F)
        self.out.fillBranch("Jet_pt_Ratio{}".format(self.syst_suffix),
                            Jet_pt_Ratio)
        self.out.fillBranch("R_pt{}".format(self.syst_suffix), R_pt)
        self.out.fillBranch("Jet_etas_multiplied{}".format(self.syst_suffix),
                            Jet_etas_multiplied)
        self.out.fillBranch("dPT_OZ{}".format(self.syst_suffix), dPT_OZ)
        self.out.fillBranch("CJV_Pt{}".format(self.syst_suffix), CJV_Pt)
        self.out.fillBranch("CJV_Pt_Sum{}".format(self.syst_suffix),
                            CJV_Pt_Sum)
        self.out.fillBranch("deltaPhiClosestJetMet{}".format(self.syst_suffix),
                            deltaPhiClosestJetMet)
        self.out.fillBranch(
            "deltaPhiFarthestJetMet{}".format(self.syst_suffix),
            deltaPhiFarthestJetMet)
        self.out.fillBranch("etaThirdJet{}".format(self.syst_suffix),
                            etaThirdJet)

        self.out.fillBranch("Z_pt_bst{}".format(self.syst_suffix),
                            Z_p4_bst.Pt())
        self.out.fillBranch("Z_phi_bst{}".format(self.syst_suffix),
                            Z_p4_bst.Phi())
        self.out.fillBranch("met_pt_bst{}".format(self.syst_suffix),
                            met_p4_bst.Pt())
        self.out.fillBranch("met_phi_bst{}".format(self.syst_suffix),
                            met_p4_bst.Phi())
        self.out.fillBranch("emulatedMET_pt_bst{}".format(self.syst_suffix),
                            emulatedMET_p4_bst.Pt())
        self.out.fillBranch("emulatedMET_phi_bst{}".format(self.syst_suffix),
                            emulatedMET_p4_bst.Phi())
        self.out.fillBranch("delta_phi_ll_bst{}".format(self.syst_suffix),
                            delta_phi_ll_bst)
        self.out.fillBranch("delta_eta_ll_bst{}".format(self.syst_suffix),
                            delta_eta_ll_bst)
        self.out.fillBranch("delta_phi_ZMet_bst{}".format(self.syst_suffix),
                            delta_phi_ZMet_bst)

        return True
Exemple #9
0
    def fillFatJetInfo(self, event):
        self.out.fillBranch("n_fatjet", len(event.fatjets))

        for idx in ([1, 2] if self._channel == 'qcd' else [1]):
            prefix = 'fj_%d_' % idx
            fj = event.fatjets[idx - 1]

            if self.isMC:
                h, dr_h = closest(fj, event.hadGenHs)
                z, dr_z = closest(fj, event.hadGenZs)
                self.out.fillBranch(prefix + "dr_H", dr_h)
                self.out.fillBranch(prefix + "H_dau_pdgid",
                                    abs(h.daughters[0].pdgId) if h else 0)
                self.out.fillBranch(prefix + "dr_Z", dr_z)
                self.out.fillBranch(prefix + "Z_dau_pdgid",
                                    abs(z.daughters[0].pdgId) if z else 0)

            try:
                self.out.fillBranch(prefix + "DeepAK8MD_ZHbbvsQCD",
                                    fj.deepTagMD_ZHbbvsQCD)
                self.out.fillBranch(prefix + "DeepAK8MD_ZHccvsQCD",
                                    fj.deepTagMD_ZHccvsQCD)
                self.out.fillBranch(prefix + "DeepAK8MD_bbVsLight",
                                    fj.deepTagMD_bbvsLight)
                self.out.fillBranch(
                    prefix + "DeepAK8MD_bbVsTop",
                    (1 / (1 + (fj.deepTagMD_TvsQCD / fj.deepTagMD_HbbvsQCD) *
                          (1 - fj.deepTagMD_HbbvsQCD) /
                          (1 - fj.deepTagMD_TvsQCD))))
            except RuntimeError:
                self.out.fillBranch(prefix + "DeepAK8MD_ZHbbvsQCD", -1)
                self.out.fillBranch(prefix + "DeepAK8MD_ZHccvsQCD", -1)
                self.out.fillBranch(prefix + "DeepAK8MD_bbVsLight", -1)
                self.out.fillBranch(prefix + "DeepAK8MD_bbVsTop", -1)

            try:
                self.out.fillBranch(prefix + "ParticleNetMD_Xbb",
                                    fj.ParticleNetMD_probXbb)
                self.out.fillBranch(prefix + "ParticleNetMD_Xcc",
                                    fj.ParticleNetMD_probXcc)
                self.out.fillBranch(prefix + "ParticleNetMD_Xqq",
                                    fj.ParticleNetMD_probXqq)
                if self.isParticleNetV01:
                    self.out.fillBranch(prefix + "ParticleNetMD_QCD",
                                        fj.ParticleNetMD_probQCD)
                    self.out.fillBranch(
                        prefix + "ParticleNetMD_XbbVsQCD",
                        convert_prob(fj, ['Xbb'], ['QCD'],
                                     prefix='ParticleNetMD_prob'))
                    self.out.fillBranch(
                        prefix + "ParticleNetMD_XccVsQCD",
                        convert_prob(fj, ['Xcc'], ['QCD'],
                                     prefix='ParticleNetMD_prob'))
                else:
                    self.out.fillBranch(
                        prefix + "ParticleNetMD_QCD",
                        convert_prob(fj, None, prefix='ParticleNetMD_prob'))
                    self.out.fillBranch(
                        prefix + "ParticleNetMD_XbbVsQCD",
                        convert_prob(fj, ['Xbb'], prefix='ParticleNetMD_prob'))
                    self.out.fillBranch(
                        prefix + "ParticleNetMD_XccVsQCD",
                        convert_prob(fj, ['Xcc'], prefix='ParticleNetMD_prob'))
            except RuntimeError:
                self.out.fillBranch(prefix + "ParticleNetMD_Xbb", -1)
                self.out.fillBranch(prefix + "ParticleNetMD_Xcc", -1)
                self.out.fillBranch(prefix + "ParticleNetMD_Xqq", -1)
                self.out.fillBranch(prefix + "ParticleNetMD_QCD", -1)
                self.out.fillBranch(prefix + "ParticleNetMD_HbbVsQCD", -1)
                self.out.fillBranch(prefix + "ParticleNetMD_HccVsQCD", -1)

            self.out.fillBranch(
                prefix + "is_lep_overlap",
                closest(fj, event.looseLeptons)[1] < self._jetConeSize)
            self.out.fillBranch(prefix + "pt", fj.pt)
            self.out.fillBranch(prefix + "eta", fj.eta)
            self.out.fillBranch(prefix + "phi", fj.phi)
            self.out.fillBranch(prefix + "energy", fj.p4().E())
            self.out.fillBranch(prefix + "rawmass", fj.mass)
            self.out.fillBranch(prefix + "sdmass", fj.msoftdrop)
            self.out.fillBranch(prefix + "tau21",
                                fj.tau2 / fj.tau1 if fj.tau1 > 0 else 99)
            self.out.fillBranch(prefix + "btagcsvv2", fj.btagCSVV2)
            try:
                self.out.fillBranch(prefix + "btagjp", fj.btagJP)
            except RuntimeError:
                self.out.fillBranch(prefix + "btagjp", -1)

            self._matchSVToFatjet(event, fj)
            nsv_ptgt25_ = 0
            nsv_ptgt50_ = 0
            ntracks_ = 0
            ntracks_sv12_ = 0
            for isv, sv in enumerate(fj.sv_list):
                ntracks_ += sv.ntracks
                if isv < 2:
                    ntracks_sv12_ += sv.ntracks
                if sv.pt > 25.:
                    nsv_ptgt25_ += 1
                if sv.pt > 50.:
                    nsv_ptgt50_ += 1
            self.out.fillBranch(prefix + "nsv", len(fj.sv_list))
            self.out.fillBranch(prefix + "nsv_ptgt25", nsv_ptgt25_)
            self.out.fillBranch(prefix + "nsv_ptgt50", nsv_ptgt50_)
            self.out.fillBranch(prefix + "ntracks", ntracks_)
            self.out.fillBranch(prefix + "ntracks_sv12", ntracks_sv12_)

            assert (len(fj.subjets) == 2)
            self.out.fillBranch(prefix + "deltaR_sj12",
                                deltaR(*fj.subjets[:2]))
            for idx_sj, sj in enumerate(fj.subjets):
                prefix_sj = prefix + 'sj%d_' % (idx_sj + 1)
                self.out.fillBranch(prefix_sj + "pt", sj.pt)
                self.out.fillBranch(prefix_sj + "eta", sj.eta)
                self.out.fillBranch(prefix_sj + "phi", sj.phi)
                self.out.fillBranch(prefix_sj + "energy", sj.p4().E())
                self.out.fillBranch(prefix_sj + "rawmass", sj.mass)
                self.out.fillBranch(prefix_sj + "btagcsvv2", sj.btagCSVV2)
                try:
                    self.out.fillBranch(prefix_sj + "btagdeepcsv",
                                        sj.btagDeepB)
                except RuntimeError:
                    self.out.fillBranch(prefix_sj + "btagdeepcsv", -1)
                try:
                    self.out.fillBranch(prefix_sj + "btagjp", sj.btagJP)
                except RuntimeError:
                    self.out.fillBranch(prefix_sj + "btagjp", -1)

                self.out.fillBranch(prefix_sj + "ntracks",
                                    sum([sv.ntracks for sv in sj.sv_list]))
                self.out.fillBranch(prefix_sj + "nsv", len(sj.sv_list))
                sv = sj.sv_list[0] if len(sj.sv_list) else _NullObject()
                fill_sv = self._get_filler(
                    sv)  # wrapper, fill default value if sv=None
                fill_sv(prefix_sj + "sv1_pt", sv.pt)
                fill_sv(prefix_sj + "sv1_mass", sv.mass)
                fill_sv(prefix_sj + "sv1_masscor",
                        corrected_svmass(sv) if sv else 0)
                fill_sv(prefix_sj + "sv1_ntracks", sv.ntracks)
                fill_sv(prefix_sj + "sv1_dxy", sv.dxy)
                fill_sv(prefix_sj + "sv1_dxysig", sv.dxySig)
                fill_sv(prefix_sj + "sv1_dlen", sv.dlen)
                fill_sv(prefix_sj + "sv1_dlensig", sv.dlenSig)
                fill_sv(prefix_sj + "sv1_chi2ndof", sv.chi2)
                fill_sv(prefix_sj + "sv1_pangle", sv.pAngle)

            sj1, sj2 = fj.subjets
            try:
                sv1, sv2 = sj1.sv_list[0], sj2.sv_list[0]
                sv = sv1 if sv1.dxySig > sv2.dxySig else sv2
                self.out.fillBranch(prefix + "sj12_masscor_dxysig",
                                    corrected_svmass(sv) if sv else 0)
            except IndexError:
                # if len(sv_list) == 0
                self.out.fillBranch(prefix + "sj12_masscor_dxysig", 0)

            # sfBDT
            sfbdt_inputs = {
                k: self.out._branches[k.replace('fj_2_', prefix)].buff[0]
                for k in self._sfbdt_vars
            }
            self.out.fillBranch(
                prefix + "sfBDT",
                self.xgb.eval(sfbdt_inputs, model_idx=(event.event % 10)))

            # matching variables
            if self.isMC:
                self.out.fillBranch(prefix + "nbhadrons", fj.nBHadrons)
                self.out.fillBranch(prefix + "nchadrons", fj.nCHadrons)
                self.out.fillBranch(prefix + "sj1_nbhadrons", sj1.nBHadrons)
                self.out.fillBranch(prefix + "sj1_nchadrons", sj1.nCHadrons)
                self.out.fillBranch(prefix + "sj2_nbhadrons", sj2.nBHadrons)
                self.out.fillBranch(prefix + "sj2_nchadrons", sj2.nCHadrons)
                try:
                    self.out.fillBranch(prefix + "partonflavour",
                                        fj.partonFlavour)
                    self.out.fillBranch(prefix + "sj1_partonflavour",
                                        sj1.partonFlavour)
                    self.out.fillBranch(prefix + "sj2_partonflavour",
                                        sj2.partonFlavour)
                except:
                    self.out.fillBranch(prefix + "partonflavour", -1)
                    self.out.fillBranch(prefix + "sj1_partonflavour", -1)
                    self.out.fillBranch(prefix + "sj2_partonflavour", -1)