Example #1
0
def makeLeps(event, sample):
    ''' Add a list of filtered leptons to the event
    '''
    # load leps
    event.leps = getCollection(event, 'GenLep',
                               ['pt', 'eta', 'phi', 'pdgId', 'motherPdgId'],
                               'nGenLep')

    # filter, pre-selection requires 3 leptons (no default leptons necessary)
    event.leps = list(filter(lambda l: isGoodGenLepton(l), event.leps))

    # Cross-cleaning: remove leptons that overlap with a jet within 0.4
    event.leps = list(
        filter(lambda l: min([deltaR(l, j) for j in event.jets] + [999]) > 0.4,
               event.leps))

    # sort
    event.leps = sorted(event.leps, key=lambda l: -l['pt'])

    # Add extra vectors
    for p in event.leps:
        addTransverseVector(p)
        addTLorentzVector(p)

    # 2l
    event.l0, event.l1 = (event.leps + [NanLepton(), NanLepton()])[:2]

    # We may loose some events by cross-cleaning or by thresholds.
    event.passing_2lep = len(
        event.leps) >= 2 and event.l0['pdgId'] * event.l1['pdgId'] > 0.
Example #2
0
def makeLeps(event, sample):
    ''' Add a list of filtered leptons to the event
    '''
    # load leps
    event.leps = getCollection(event, 'GenLep',
                               ['pt', 'eta', 'phi', 'pdgId', 'motherPdgId'],
                               'nGenLep')

    # filter, pre-selection requires 3 leptons (no default leptons necessary)
    event.leps = list(filter(lambda l: isGoodGenLepton(l), event.leps))

    # Cross-cleaning: remove leptons that overlap with a jet within 0.4
    event.leps = list(
        filter(lambda l: min([deltaR(l, j) for j in event.jets] + [999]) > 0.4,
               event.leps))

    # sort
    event.leps = sorted(event.leps, key=lambda l: -l['pt'])

    # Add extra vectors
    for p in event.leps:
        addTransverseVector(p)
        addTLorentzVector(p)

    # find leptons from Z
    event.lepsFromZ = list(filter(lambda j: j['motherPdgId'] == 23,
                                  event.leps))
    event.foundZ = len(event.lepsFromZ) == 2 and event.lepsFromZ[0][
        'pdgId'] * event.lepsFromZ[1]['pdgId'] < 0 and abs(
            event.lepsFromZ[0]['pdgId']) == abs(event.lepsFromZ[1]['pdgId'])
    event.Z_deltaPhi_ll = deltaPhi(
        event.lepsFromZ[0]['phi'],
        event.lepsFromZ[1]['phi']) if event.foundZ else float('nan')
    event.Z_deltaR_ll = deltaR(
        *event.lepsFromZ) if event.foundZ else float('nan')

    # find leptons that are NOT from Z
    event.lepsNotFromZ = list(
        filter(lambda j: j['motherPdgId'] != 23, event.leps))

    # We may loose some events by cross-cleaning or by thresholds.
    event.passing_4lep = event.foundZ and len(
        event.lepsNotFromZ) >= 2 and event.lepsNotFromZ[0][
            'pdgId'] * event.lepsNotFromZ[1]['pdgId'] < 0.

    # Add default lepton if leptons got filtered
    event.lepsNotFromZ += [NanLepton(), NanLepton()]

    # Define non-Z leptons
    event.l0, event.l1 = event.lepsNotFromZ[:2]
Example #3
0
def filler(event):

    event.lumiweight1fb = lumiweight1fb

    if not args.HEPMC:
        event.run, event.lumi, event.evt = fwliteReader.evt
        if fwliteReader.position % 100 == 0:
            logger.info("At event %i/%i", fwliteReader.position,
                        fwliteReader.nEvents)

    if args.addReweights:
        event.nrw = weightInfo.nid
        lhe_weights = fwliteReader.products['lhe'].weights()
        weights = []
        param_points = []
        for weight in lhe_weights:
            # Store nominal weight (First position!)
            if weight.id == 'rwgt_1': event.rw_nominal = weight.wgt
            if not weight.id in weightInfo.id: continue
            pos = weightInfo.data[weight.id]
            event.rw_w[pos] = weight.wgt
            weights.append(weight.wgt)
            interpreted_weight = interpret_weight(weight.id)
            for var in weightInfo.variables:
                getattr(event, "rw_" + var)[pos] = interpreted_weight[var]
            # weight data for interpolation
            if not hyperPoly.initialized:
                param_points.append(
                    tuple(interpreted_weight[var]
                          for var in weightInfo.variables))

        # get list of values of ref point in specific order
        ref_point_coordinates = [
            weightInfo.ref_point_coordinates[var]
            for var in weightInfo.variables
        ]

        # Initialize with Reference Point
        if not hyperPoly.initialized:
            hyperPoly.initialize(param_points, ref_point_coordinates)
        coeff = hyperPoly.get_parametrization(weights)

        # = HyperPoly(weight_data, args.interpolationOrder)
        event.np = hyperPoly.ndof
        event.chi2_ndof = hyperPoly.chi2_ndof(coeff, weights)
        #logger.debug( "chi2_ndof %f coeff %r", event.chi2_ndof, coeff )
        if event.chi2_ndof > 10**-6:
            logger.warning("chi2_ndof is large: %f", event.chi2_ndof)
        for n in xrange(hyperPoly.ndof):
            event.p_C[n] = coeff[n]

        # lumi weight / w0
        event.ref_lumiweight1fb = event.lumiweight1fb / coeff[0]

    if not args.HEPMC:
        # All gen particles
        gp = fwliteReader.products['gp']

        # for searching
        search = GenSearch(gp)

        # find heavy objects before they decay
        genTops = map(
            lambda t: {var: getattr(t, var)()
                       for var in top_varnames},
            filter(lambda p: abs(p.pdgId()) == 6 and search.isLast(p), gp))

        genTops.sort(key=lambda p: -p['pt'])
        fill_vector_collection(event, "genTop", top_varnames, genTops)

        # generated Z's
        genZs = filter(
            lambda p: abs(p.pdgId()) == 23 and search.isLast(p) and abs(
                p.daughter(0).pdgId()) in [11, 13], gp)
        genZs.sort(key=lambda p: -p.pt())
        if len(genZs) > 0:
            genZ = genZs[0]
            for var in boson_read_varnames:
                setattr(event, "genZ_" + var, getattr(genZ, var)())
        else:
            genZ = None

        event.signalZ = 0  #ttZ with Z from gluon or top
        if genZ is not None:

            if abs(search.ascend(genZ).mother(0).pdgId()) in [6, 21]:
                event.signalZ = 1  #ttZ with Z from gluon or top

            d1, d2 = genZ.daughter(0), genZ.daughter(1)
            if d1.pdgId() > 0:
                lm, lp = d1, d2
            else:
                lm, lp = d2, d1
            event.genZ_daughterPdg = lm.pdgId()
            event.genZ_cosThetaStar = cosThetaStar(genZ.mass(), genZ.pt(),
                                                   genZ.eta(), genZ.phi(),
                                                   lm.pt(), lm.eta(), lm.phi())

        # generated W's
        genWs = filter(lambda p: abs(p.pdgId()) == 24 and search.isLast(p), gp)
        genWs.sort(key=lambda p: -p.pt())
        # W can't have a top-mother - We're looking for the extra boson (there is always an extra boson)
        genWs = filter(lambda p: abs(search.ascend(p).mother(0).pdgId()) != 6,
                       genWs)
        if len(genWs) > 0:
            genW = genWs[0]
            for var in boson_read_varnames:
                setattr(event, "genW_" + var, getattr(genW, var)())
        else:
            genW = None

        if genW is not None:
            d1, d2 = genW.daughter(0), genW.daughter(1)
            if abs(d1.pdgId()) in [11, 13, 15]:
                lep, neu = d1, d2
            else:
                lep, neu = d2, d1

            event.genW_daughterPdg = lep.pdgId()


## this def should only be called in debug mode
#        def printTopMothers():
#                genTopCheck = [ (search.ascend(l), l, search.ancestry( search.ascend(l) )) for l in filter( lambda p:abs(p.pdgId())==6,  gp) ]
#
#                print genTopCheck
#                genTopCheck.sort( key = lambda p: -p[1].pt() )
#                if len(genTopCheck) > 0:
#                    topPdg_ids = filter( lambda p:p!=2212, [abs(particle.pdgId()) for particle in genTopCheck[0][2]])
#                    print 'TOP 1'
#                    print topPdg_ids
#                    previous = genTopCheck[0][0].mother(1)
#                    print 'first', genTopCheck[0][0].pdgId()
#                    for i, pdg in enumerate(topPdg_ids):
#                        try:
#                            print 'mother', i, previous.pdgId()
#                            print 'mothers', i, [p.pdgId() for p in search.ancestry( previous )]
#                            previous = previous.mother(0)
#                        except Exception as val:
#                            print val
#                            break
#                if len(genTopCheck) > 1:
#                    topPdg_ids = filter( lambda p:p!=2212, [abs(particle.pdgId()) for particle in genTopCheck[1][2]])
#                    print 'TOP 2'
#                    print topPdg_ids
#                    previous = genTopCheck[1][0].mother(1)
#                    print 'first', genTopCheck[1][0].pdgId()
#                    for i, pdg in enumerate(topPdg_ids):
#                        try:
#                            print 'mother', i, previous.pdgId()
#                            print 'mothers', i, [p.pdgId() for p in search.ancestry( previous )]
#                            previous = previous.mother(0)
#                        except Exception as val:
#                            print val
#                            break

# MET
        genMet = {
            'pt': fwliteReader.products['genMET'][0].pt(),
            'phi': fwliteReader.products['genMET'][0].phi()
        }
        event.genMet_pt = genMet['pt']
        event.genMet_phi = genMet['phi']

        #    for ttgamma and tt events, categorize events:
        #        a1) ttgamma vertex, gamma from t/g, isolated, must be in ttgamma sample   = ttgamma signal (photonSignal == 1)
        #            -> gamma isolated
        #            -> gamma with only tops/gluons in ancestry
        #        a2) ttgamma, gamma from ISR, must be in ttgamma sample                    = tt ISR bg (photonISR == 1)
        #            -> gamma isolated
        #            -> gamma with no top in ancestry
        #            -> gamma with only gluons and light quarks in ancestry
        #            -> direct gamma mother != gluon!!! (this is a1)
        #        b) tt + isolated gamma from W, l, tau, must be in tt sample               = ttgamma (W,l,tau) bg (photonLep == 1)
        #            -> gamma isolated
        #            -> gamma with direct abs mother being 11, 13, 15 or 24
        #        c1) tt + non isolated gamma from anything including mesons                = tt bg (ttBg == 1)
        #            -> gamma non isolated or meson in ancestry
        #        c2) tt + isolated gamma from bottom quark (from t decay) or jets (from W) = tt bottom bg (ttBg == 1)
        #            -> gamma isolated from b or j (from t/W decay)
        #            ATTENTION: gammas from bottoms with off-shell tops where the
        #                       top decay is done by pythia are currently labeled as ISR!
        #                       However this case is currently not simulated in any sample
        #        d) tt + gamma fake                                                        = ttgamma fake (photonFake == 1)
        #            -> everything else does not contain a photon
        #            -> if it still passes selection: it is a photon fake

        genPhotonsSignalCheck = [
            (search.ascend(l), l, search.ancestry(search.ascend(l)))
            for l in filter(
                lambda p: abs(p.pdgId()) == 22 and p.pt() > 10 and abs(p.eta())
                < 2.5 and search.isLast(p) and p.status() == 1, gp)
        ]
        genPhotonsSignalCheck.sort(key=lambda p: -p[1].pt())

        photonSignal = 0  #a1
        photonISR = 0  #a2
        photonLep = 0  #b
        ttBg = 0  #c1
        photonJets = 0  #c2
        photonFake = 0  #d

        if len(genPhotonsSignalCheck) > 0:
            # check hardest photon with pT>13 and abs(eta)<2.5
            first, last, ancestry = genPhotonsSignalCheck[0]
            # get abs pdgIDs of ancestry
            pdg_ids = filter(lambda p: p != 2212,
                             [abs(particle.pdgId()) for particle in ancestry])
            # check if particles are close by
            close_particles = filter(
                lambda p: p != last and p.pt() > 5 and deltaR2(
                    {
                        'phi': last.phi(),
                        'eta': last.eta()
                    }, {
                        'phi': p.phi(),
                        'eta': p.eta()
                    }) < 0.2**2, search.final_state_particles_no_neutrinos)

            #        print 'mothers pdg', pdg_ids
            #        print 'close', [p.pdgId() for p in close_particles]
            #        print 'first mother pdg', first.mother(0).pdgId()

            #        if len(pdg_ids) < 999:
            #            previous = first.mother(0)
            #            for i, pdg in enumerate(pdg_ids):
            #                try:
            #                    print 'mother', i, previous.pdgId()
            #                    previous = previous.mother(0)
            #                except Exception as val:
            #                    print val
            #                    break

            # deside the categories
            if max(pdg_ids) > 100 or len(close_particles) != 0:
                #photon with meson in ancestry or non isolated -> cat c1)
                ttBg = 1
            elif abs(first.mother(0).pdgId()) in [11, 13, 15, 24]:
                #isolated photon with W, l or tau direct mother -> cat b)
                photonLep = 1
    #        elif all( [ p in [ 6, 21 ] for p in pdg_ids ] ) or abs(first.mother(0).pdgId()) == 21: # not working for photons from top, as the gluons can come from light quarks
            elif abs(first.mother(0).pdgId()) in [6, 21]:
                #isolated photon with photon from top or gluon -> cat a1)
                photonSignal = 1
    #            printTopMothers()
            elif all([p in [1, 2, 3, 4, 5, 21] for p in pdg_ids]):
                #isolated photon with photon ancestry only containing light quarks or gluons (ISR) -> cat a1)
                photonISR = 1
            else:
                #isolated gammas from bottoms originating from the top decay or jets from W -> cat c2)
                photonJets = 1

        else:
            # if events with photonFake == 1 pass selection: fake gamma -> cat d)
            photonFake = 1

        # if all flags are 0, it is an isolated gamma from a process I havn't thought of!
        # should not be there! - check!
        event.signalPhoton = photonSignal
        event.isrPhoton = photonISR
        event.lepPhoton = photonLep
        event.nonIsoPhoton = ttBg
        event.jetPhoton = photonJets
        event.fakePhoton = photonFake

        # gen photons: particle-level isolated gen photons
        genPhotons = [(search.ascend(l), l) for l in filter(
            lambda p: abs(p.pdgId()) == 22 and p.pt() > 15 and search.isLast(p)
            and p.status() == 1, gp)]
        genPhotons.sort(key=lambda p: -p[1].pt())
        genPhotons_ = []

        for first, last in genPhotons[:100]:
            mother_pdgId = first.mother(
                0).pdgId() if first.numberOfMothers() > 0 else 0
            genPhoton_ = {
                var: getattr(last, var)()
                for var in boson_read_varnames
            }
            # kinematic photon selection
            if not isGoodGenPhoton(genPhoton_): continue
            genPhoton_['motherPdgId'] = mother_pdgId
            genPhoton_['status'] = last.status()

            close_particles = filter(
                lambda p: p != last and deltaR2(
                    {
                        'phi': last.phi(),
                        'eta': last.eta()
                    }, {
                        'phi': p.phi(),
                        'eta': p.eta()
                    }) < 0.4**2, search.final_state_particles_no_neutrinos)
            genPhoton_['relIso04'] = sum([p.pt() for p in close_particles],
                                         0) / last.pt()
            # require isolation
            if genPhoton_['relIso04'] < 0.4:
                genPhotons_.append(genPhoton_)

        # genLeptons: prompt gen-leptons
        genLeptons = [(search.ascend(l), l) for l in filter(
            lambda p: abs(p.pdgId()) in [11, 13] and search.isLast(p) and p.pt(
            ) >= 0 and p.status() == 1, gp)]
        promptGenLeps = []
        allGenLeps = []
        for first, last in genLeptons:
            mother = first.mother(0) if first.numberOfMothers() > 0 else None
            if mother is not None:
                mother_pdgId = mother.pdgId()
                mother_ascend = search.ascend(mother)
                grandmother = mother_ascend.mother(
                    0) if mother.numberOfMothers() > 0 else None
                grandmother_pdgId = grandmother.pdgId(
                ) if grandmother is not None else 0
            else:
                mother_pdgId = 0
                grandmother_pdgId = 0
            genLep = {var: getattr(last, var)() for var in lep_varnames}
            genLep['motherPdgId'] = mother_pdgId
            genLep['grandmotherPdgId'] = grandmother_pdgId
            allGenLeps.append(genLep)
            if abs(genLep['motherPdgId']) in [11, 13, 15, 23, 24, 25]:
                promptGenLeps.append(genLep)

        # filter gen leptons
        promptGenLeps = list(
            filter(lambda l: isGoodGenLepton(l), promptGenLeps))
        promptGenLeps.sort(key=lambda p: -p['pt'])
        addIndex(promptGenLeps)

        ## removing photons in dR cone leptons (radiation photons)
        for genPhoton in genPhotons_:
            genPhoton['minLeptonDR'] = min(
                [999] + [deltaR(genPhoton, l) for l in allGenLeps])
        genPhotons_ = list(
            filter(lambda g: g['minLeptonDR'] > 0.4, genPhotons_))
        addIndex(genPhotons_)

        # jets
        fwlite_genJets = filter(genJetId, fwliteReader.products['genJets'])
        genJets = map(
            lambda t: {var: getattr(t, var)()
                       for var in jet_read_varnames},
            filter(lambda j: j.pt() > 30, fwlite_genJets))
        # filter genJets
        genJets = list(filter(lambda j: isGoodGenJet(j), genJets))
        # cleaning of jets with isolated photons
        genJets = list(
            filter(
                lambda j: min([999] + [deltaR2(j, p)
                                       for p in genPhotons_]) > 0.4**2,
                genJets))

        # store minimum DR to jets
        for genPhoton in genPhotons_:
            genPhoton['minJetDR'] = min(
                [999] + [deltaR(genPhoton, j) for j in genJets])

        # find b's from tops:
        b_partons = [
            b for b in filter(
                lambda p: abs(p.pdgId()) == 5 and p.numberOfMothers() == 1 and
                abs(p.mother(0).pdgId()) == 6, gp)
        ]

        # store if gen-jet is DR matched to a B parton
        for genJet in genJets:
            genJet['matchBParton'] = (min([999] + [
                deltaR2(genJet, {
                    'eta': b.eta(),
                    'phi': b.phi()
                }) for b in b_partons
            ]) < 0.2**2)

        genJets = filter(
            lambda j: (min(
                [999] + [deltaR2(j, l) for l in promptGenLeps
                         if l['pt'] > 10]) > 0.3**2), genJets)
        genJets.sort(key=lambda p: -p['pt'])
        addIndex(genJets)

        # gen b jets
        trueBjets = list(filter(lambda j: j['matchBParton'], genJets))
        trueNonBjets = list(filter(lambda j: not j['matchBParton'], genJets))

        # Mimick b reconstruction ( if the trailing b fails acceptance, we supplement with the leading non-b jet )
        genBj0, genBj1 = (trueBjets + trueNonBjets + [None, None])[:2]
        if genBj0: fill_vector(event, "genBj0", jet_write_varnames, genBj0)
        if genBj1: fill_vector(event, "genBj1", jet_write_varnames, genBj1)

        # reco-bjet/leading lepton association
        if len(promptGenLeps) > 0 and genBj0 and genBj1:
            if vecSumPt(genBj0, promptGenLeps[0], genMet) > vecSumPt(
                    genBj1, promptGenLeps[0], genMet):
                event.genBjLeadlep_index, event.genBjLeadhad_index = genBj0[
                    'index'], genBj1['index']
            else:
                event.genBjLeadlep_index, event.genBjLeadhad_index = genBj1[
                    'index'], genBj0['index']

        # find Z in genLep
        (event.genLepZ_mass, genLepZ_l1_index,
         genLepZ_l2_index) = closestOSDLMassToMZ(promptGenLeps)
        genLepNonZ_indices = [
            i for i in range(len(promptGenLeps))
            if i not in [genLepZ_l1_index, genLepZ_l2_index]
        ]
        event.genLepZ_l1_index = promptGenLeps[genLepZ_l1_index][
            'index'] if genLepZ_l1_index >= 0 else -1
        event.genLepZ_l2_index = promptGenLeps[genLepZ_l2_index][
            'index'] if genLepZ_l2_index >= 0 else -1
        event.genLepNonZ_l1_index = promptGenLeps[genLepNonZ_indices[0]][
            'index'] if len(genLepNonZ_indices) > 0 else -1
        event.genLepNonZ_l2_index = promptGenLeps[genLepNonZ_indices[1]][
            'index'] if len(genLepNonZ_indices) > 1 else -1
        # store genLepZ stuff
        if event.genLepZ_mass > 0:
            genLepZ_l1 = ROOT.TLorentzVector()
            genLepZ_l1.SetPtEtaPhiM(
                promptGenLeps[event.genLepZ_l1_index]['pt'],
                promptGenLeps[event.genLepZ_l1_index]['eta'],
                promptGenLeps[event.genLepZ_l1_index]['phi'], 0)
            genLepZ_l2 = ROOT.TLorentzVector()
            genLepZ_l2.SetPtEtaPhiM(
                promptGenLeps[event.genLepZ_l2_index]['pt'],
                promptGenLeps[event.genLepZ_l2_index]['eta'],
                promptGenLeps[event.genLepZ_l2_index]['phi'], 0)
            genLepZ = genLepZ_l1 + genLepZ_l2
            event.genLepZ_pt = genLepZ.Pt()
            event.genLepZ_eta = genLepZ.Eta()
            event.genLepZ_phi = genLepZ.Phi()
            event.genLepZ_lldPhi = deltaPhi(
                promptGenLeps[event.genLepZ_l1_index]['phi'],
                promptGenLeps[event.genLepZ_l2_index]['phi'])
            event.genLepZ_lldR = deltaR(promptGenLeps[event.genLepZ_l1_index],
                                        promptGenLeps[event.genLepZ_l2_index])
            genLepMinus_index = event.genLepZ_l1_index if promptGenLeps[
                event.
                genLepZ_l1_index]['pdgId'] > 0 else event.genLepZ_l2_index
            event.genLepZ_cosThetaStar = cosThetaStar(
                event.genLepZ_mass, event.genLepZ_pt, event.genLepZ_eta,
                event.genLepZ_phi, promptGenLeps[genLepMinus_index]['pt'],
                promptGenLeps[genLepMinus_index]['eta'],
                promptGenLeps[genLepMinus_index]['phi'])

        # reco-bjet/nonZ lepton association
        if event.genLepNonZ_l1_index >= 0 and genBj0 and genBj1:
            if vecSumPt(genBj0, promptGenLeps[event.genLepNonZ_l1_index],
                        genMet) > vecSumPt(
                            genBj1, promptGenLeps[event.genLepNonZ_l1_index],
                            genMet):
                event.genBjNonZlep_index, event.genBjNonZhad_index = genBj0[
                    'index'], genBj1['index']
            else:
                event.genBjNonZlep_index, event.genBjNonZhad_index = genBj1[
                    'index'], genBj0['index']

        #for jet in genJets:
        #    print jet['isMuon'], jet['isElectron'], jet['isPhoton'], min([999]+[deltaR2(jet, l) for l in promptGenLeps if l['pt']>10]), jet

        # jet/lepton disambiguation -> remove jets, because gen-jets cluster all leptons
        #if args.logLevel == 'DEBUG':
        #    for jet in filter( lambda j: not (min([999]+[deltaR2(j, l) for l in promptGenLeps if l['pt']>10]) > 0.3**2 ), genJets ):
        #        logger.debug( "Filtered gen %f jet %r lep %r", sqrt((min([999]+[deltaR2(jet, l) for l in promptGenLeps if l['pt']>10]))), jet, [ (l['eta'], jet['pt']/l['pt']) for l in promptGenLeps] )
        #        assert False, ""

        fill_vector_collection(event, "genPhoton", gen_photon_varnames,
                               genPhotons_)
        fill_vector_collection(event, "genLep", lep_all_varnames,
                               promptGenLeps)
        fill_vector_collection(event, "genJet", jet_write_varnames, genJets)

    # Reco quantities
    if args.delphes or args.HEPMC:
        #delphesReader.event.GetEntry(fwliteReader.position-1 ) # do this differently now ...

        # add JEC info
        allRecoJets = delphesReader.jets()
        # add btag info
        for i_btagWP, btagWP in enumerate(btagWPs):
            count = 0
            for jet in allRecoJets:
                btag = (jet["bTag"] & (2**i_btagWP) > 0)  # Read b-tag bitmap
                jet["bTag_" + btagWP] = btag

        # read jets
        recoJets = filter(isGoodRecoJet, allRecoJets)
        recoJets.sort(key=lambda p: -p['pt'])
        addIndex(recoJets)

        # count b-tag multiplicities
        for i_btagWP, btagWP in enumerate(btagWPs):
            count = 0
            for jet in recoJets:
                if jet["bTag_" + btagWP]: count += 1
            setattr(event, "nBTag_" + btagWP, count)
            if btagWP == default_btagWP:
                setattr(event, "nBTag", count)

        # upgrade JEC are flavor dependent
        for jet in allRecoJets:
            btag_ = jet["bTag_" + default_btagWP]
            upgradeJECUncertainty.applyJECInfo(jet, flavor=5 if btag else 0)

        # count JEC varied jet multiplicities
        event.nrecoJets_JEC_up = len(
            filter(lambda j: isGoodRecoJet(j, pt_var='pt_JEC_up'),
                   allRecoJets))
        event.nrecoJets_JEC_down = len(
            filter(lambda j: isGoodRecoJet(j, pt_var='pt_JEC_down'),
                   allRecoJets))

        event.nBTag_JEC_up = len(
            filter(
                lambda j: j["bTag_" + default_btagWP] and isGoodRecoJet(
                    j, pt_var='pt_JEC_up'), allRecoJets))
        event.nBTag_JEC_down = len(
            filter(
                lambda j: j["bTag_" + default_btagWP] and isGoodRecoJet(
                    j, pt_var='pt_JEC_down'), allRecoJets))

        # read jets
        recoJets = filter(isGoodRecoJet, allRecoJets)
        recoJets.sort(key=lambda p: -p['pt'])
        addIndex(recoJets)

        # make reco b jets
        recoBJets = filter(lambda j: j['bTag_' + default_btagWP], recoJets)
        recoNonBJets = filter(lambda j: not j['bTag_' + default_btagWP],
                              recoJets)
        recoBj0, recoBj1 = (recoBJets + recoNonBJets + [None, None])[:2]
        if recoBj0: fill_vector(event, "recoBj0", recoJet_varnames, recoBj0)
        if recoBj1: fill_vector(event, "recoBj1", recoJet_varnames, recoBj1)

        # add b-tag reweights
        event.reweight_BTag_B = getBTagSF_1a(default_btagWP, 'B', recoBJets,
                                             recoNonBJets)
        event.reweight_BTag_L = getBTagSF_1a(default_btagWP, 'L', recoBJets,
                                             recoNonBJets)

        # read leptons
        allRecoLeps = delphesReader.muons() + delphesReader.electrons()
        allRecoLeps.sort(key=lambda p: -p['pt'])
        recoLeps = filter(isGoodRecoLepton, allRecoLeps)
        # Photons
        recoPhotons = filter(isGoodRecoPhoton, delphesReader.photons())

        # Remove radiated photons in dR cone
        for recoPhoton in recoPhotons:
            recoPhoton['minLeptonDR'] = 999
            recoPhoton['minLeptonPt'] = -1.
            dr_values = [deltaR(recoPhoton, l) for l in allRecoLeps]
            recoPhoton['minLeptonDR'] = min([999] + dr_values)
            if len(dr_values) > 0:
                closest_lepton = dr_values.index(min(dr_values))
                recoPhoton['minLeptonPt'] = allRecoLeps[closest_lepton]['pt']
        recoPhotons = list(
            filter(lambda g: g['minLeptonDR'] > 0.4, recoPhotons))
        for recoPhoton in recoPhotons:
            recoPhoton['minJetDR'] = min(
                [999] + [deltaR(recoPhoton, j) for j in recoJets])
        recoPhotons = list(filter(lambda g: g['minJetDR'] > 0.4, recoPhotons))

        # cross-cleaning of reco-objects
        recoLeps = filter(
            lambda l:
            (min([999] + [deltaR2(l, j) for j in recoJets
                          if j['pt'] > 30]) > 0.3**2), recoLeps)
        # give index to leptons
        addIndex(recoLeps)

        # lepton uncertainties
        event.reweight_id_mu = 1.
        event.reweight_id_ele = 1.
        for l in recoLeps:
            if abs(l['pdgId']) == 11:
                event.reweight_id_ele *= 1.005
            elif abs(l['pdgId']) == 13:
                event.reweight_id_mu *= 1.005
        # MET
        recoMet = delphesReader.met()[0]

        # reco-bjet/leading lepton association
        if len(recoLeps) > 0 and recoBj0 and recoBj1:
            if vecSumPt(recoBj0, recoLeps[0], recoMet) > vecSumPt(
                    recoBj1, recoLeps[0], recoMet):
                event.recoBjLeadlep_index, event.recoBjLeadhad_index = recoBj0[
                    'index'], recoBj1['index']
            else:
                event.recoBjLeadlep_index, event.recoBjLeadhad_index = recoBj1[
                    'index'], recoBj0['index']

        # Photons
        for recoPhoton in recoPhotons:
            recoPhoton['genIndex'] = -1
            minDR = 999
            if not args.HEPMC:
                for index, genPhoton in enumerate(genPhotons_):
                    dr = deltaR(recoPhoton, genPhoton)
                    if dr < 0.4 and dr < minDR:
                        minDR = dr
                        recoPhoton['genIndex'] = index
        # Store
        fill_vector_collection(event, "recoLep", recoLep_varnames, recoLeps)
        fill_vector_collection(event, "recoJet", recoJet_varnames, recoJets)
        fill_vector_collection(event, "recoPhoton", recoPhoton_varnames,
                               recoPhotons)

        event.recoMet_pt = recoMet['pt']
        event.recoMet_phi = recoMet['phi']

        # search for reco Z in reco leptons
        (event.recoZ_mass, recoZ_l1_index,
         recoZ_l2_index) = closestOSDLMassToMZ(recoLeps)
        recoNonZ_indices = [
            i for i in range(len(recoLeps))
            if i not in [recoZ_l1_index, recoZ_l2_index]
        ]
        event.recoZ_l1_index = recoLeps[recoZ_l1_index][
            'index'] if recoZ_l1_index >= 0 else -1
        event.recoZ_l2_index = recoLeps[recoZ_l2_index][
            'index'] if recoZ_l2_index >= 0 else -1
        event.recoNonZ_l1_index = recoLeps[
            recoNonZ_indices[0]]['index'] if len(recoNonZ_indices) > 0 else -1
        event.recoNonZ_l2_index = recoLeps[
            recoNonZ_indices[1]]['index'] if len(recoNonZ_indices) > 1 else -1

        # Store Z information
        if event.recoZ_mass >= 0:
            if recoLeps[event.recoZ_l1_index]['pdgId'] * recoLeps[
                    event.recoZ_l2_index]['pdgId'] > 0 or abs(
                        recoLeps[event.recoZ_l1_index]['pdgId']) != abs(
                            recoLeps[event.recoZ_l2_index]['pdgId']):
                raise RuntimeError("not a Z! Should not happen")
            Z_l1 = ROOT.TLorentzVector()
            Z_l1.SetPtEtaPhiM(recoLeps[event.recoZ_l1_index]['pt'],
                              recoLeps[event.recoZ_l1_index]['eta'],
                              recoLeps[event.recoZ_l1_index]['phi'], 0)
            Z_l2 = ROOT.TLorentzVector()
            Z_l2.SetPtEtaPhiM(recoLeps[event.recoZ_l2_index]['pt'],
                              recoLeps[event.recoZ_l2_index]['eta'],
                              recoLeps[event.recoZ_l2_index]['phi'], 0)
            Z = Z_l1 + Z_l2
            event.recoZ_pt = Z.Pt()
            event.recoZ_eta = Z.Eta()
            event.recoZ_phi = Z.Phi()
            event.recoZ_lldPhi = deltaPhi(
                recoLeps[event.recoZ_l1_index]['phi'],
                recoLeps[event.recoZ_l2_index]['phi'])
            event.recoZ_lldR = deltaR(recoLeps[event.recoZ_l1_index],
                                      recoLeps[event.recoZ_l2_index])
            lm_index = event.recoZ_l1_index if recoLeps[
                event.recoZ_l1_index]['pdgId'] > 0 else event.recoZ_l2_index
            event.recoZ_cosThetaStar = cosThetaStar(
                event.recoZ_mass, event.recoZ_pt, event.recoZ_eta,
                event.recoZ_phi, recoLeps[lm_index]['pt'],
                recoLeps[lm_index]['eta'], recoLeps[lm_index]['phi'])

            # reco-bjet/lepton association
            if event.recoNonZ_l1_index >= 0 and recoBj0 and recoBj1:
                if vecSumPt(recoBj0, recoLeps[event.recoNonZ_l1_index],
                            recoMet) > vecSumPt(
                                recoBj1, recoLeps[event.recoNonZ_l1_index],
                                recoMet):
                    event.recoBjNonZlep_index, event.recoBjNonZhad_index = recoBj0[
                        'index'], recoBj1['index']
                else:
                    event.recoBjNonZlep_index, event.recoBjNonZhad_index = recoBj1[
                        'index'], recoBj0['index']