def filler(event):

    event.run, event.lumi, event.evt = reader.evt

    if reader.position % 100 == 0:
        logger.info("At event %i/%i", reader.position, reader.nEvents)

    if args.addReweights:
        event.nrw = weightInfo.nid
        lhe_weights = reader.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))

        # Initialize
        if not hyperPoly.initialized: hyperPoly.initialize(param_points)
        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 )
        logger.debug("chi2_ndof %f", event.chi2_ndof)
        for n in xrange(hyperPoly.ndof):
            event.p_C[n] = coeff[n]

    # All gen particles
    gp = reader.products['gp']

    # for searching
    search = GenSearch(gp)

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

    tops.sort(key=lambda p: -p['pt'])
    fill_vector(event, "top", top_varnames, tops)

    gen_Zs = filter(lambda p: abs(p.pdgId()) == 23 and search.isLast(p), gp)
    gen_Zs.sort(key=lambda p: -p.pt())
    if len(gen_Zs) > 0:
        gen_Z = gen_Zs[0]
        for var in Z_read_varnames:
            setattr(event, "Z_" + var, getattr(gen_Z, var)())
    else:
        gen_Z = None

    if gen_Z is not None:

        d1, d2 = gen_Z.daughter(0), gen_Z.daughter(1)
        if d1.pdgId() > 0:
            lm, lp = d1, d2
        else:
            lm, lp = d2, d1
        event.Z_daughterPdg = lm.pdgId()
        event.Z_cosThetaStar = cosThetaStar(gen_Z.mass(), gen_Z.pt(),
                                            gen_Z.eta(), gen_Z.phi(), lm.pt(),
                                            lm.eta(), lm.phi())

    gen_Gammas = filter(lambda p: abs(p.pdgId()) == 22 and search.isLast(p),
                        gp)
    gen_Gammas.sort(key=lambda p: -p.pt())
    if len(gen_Gammas) > 0:
        gen_Gamma = gen_Gammas[0]
        for var in gamma_read_varnames:
            setattr(event, "gamma_" + var, getattr(gen_Gamma, var)())
    else:
        gen_Gamma = None

    # find all leptons
    leptons = [(search.ascend(l), l) for l in filter(
        lambda p: abs(p.pdgId()) in [11, 13] and search.isLast(p) and p.pt() >=
        0, gp)]
    leps = []
    for first, last in leptons:
        mother_pdgId = first.mother(
            0).pdgId() if first.numberOfMothers() > 0 else -1
        leps.append({var: getattr(last, var)() for var in lep_varnames})
        leps[-1]['motherPdgId'] = mother_pdgId

    leps.sort(key=lambda p: -p['pt'])
    fill_vector(event, "GenLep", lep_all_varnames, leps)

    # MET
    event.GenMet_pt = reader.products['genMET'][0].pt()
    event.GenMet_phi = reader.products['genMET'][0].phi()

    # jets
    jets = map(lambda t: {var: getattr(t, var)()
                          for var in jet_read_varnames},
               filter(lambda j: j.pt() > 30, reader.products['genJets']))

    # jet/lepton disambiguation
    jets = filter(
        lambda j: (min([999] + [deltaR2(j, l) for l in leps
                                if l['pt'] > 10]) > 0.3**2), jets)

    # 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)
    ]

    for jet in jets:
        jet['matchBParton'] = (min([999] + [
            deltaR2(jet, {
                'eta': b.eta(),
                'phi': b.phi()
            }) for b in b_partons
        ]) < 0.2**2)

    jets.sort(key=lambda p: -p['pt'])
    fill_vector(event, "GenJet", jet_write_varnames, jets)
Exemple #2
0
def filler(event):

    event.evt, event.lumi, event.run = reader.evt

    if reader.position % 100 == 0:
        logger.info("At event %i/%i", reader.position, reader.nEvents)

    # All gen particles
    gp = reader.products['gp']

    # for searching
    search = GenSearch(gp)

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

    tops.sort(key=lambda p: -p['pt'])
    fill_vector(event, "top", top_varnames, tops)

    gen_Zs = filter(lambda p: abs(p.pdgId()) == 23 and search.isLast(p), gp)
    gen_Zs.sort(key=lambda p: -p.pt())
    if len(gen_Zs) > 0:
        gen_Z = gen_Zs[0]
        for var in Z_read_varnames:
            setattr(event, "Z_" + var, getattr(gen_Z, var)())
    else:
        gen_Z = None

    if gen_Z is not None:

        d1, d2 = gen_Z.daughter(0), gen_Z.daughter(1)
        if d1.pdgId() > 0:
            lm, lp = d1, d2
        else:
            lm, lp = d2, d1
        event.Z_daughterPdg = lm.pdgId()
        event.Z_cosThetaStar = cosThetaStar(gen_Z.mass(), gen_Z.pt(),
                                            gen_Z.eta(), gen_Z.phi(), lm.pt(),
                                            lm.eta(), lm.phi())

    gen_Gammas = filter(lambda p: abs(p.pdgId()) == 22 and search.isLast(p),
                        gp)
    gen_Gammas.sort(key=lambda p: -p.pt())
    if len(gen_Gammas) > 0:
        gen_Gamma = gen_Gammas[0]
        for var in gamma_read_varnames:
            setattr(event, "gamma_" + var, getattr(gen_Gamma, var)())
    else:
        gen_Gamma = None

    # find all leptons
    leptons = [(search.ascend(l), l) for l in filter(
        lambda p: abs(p.pdgId()) in [11, 13] and search.isLast(p) and p.pt() >=
        0, gp)]
    leps = []
    for first, last in leptons:
        mother_pdgId = first.mother(
            0).pdgId() if first.numberOfMothers() > 0 else -1
        leps.append({var: getattr(last, var)() for var in lep_varnames})
        leps[-1]['motherPdgId'] = mother_pdgId

    leps.sort(key=lambda p: -p['pt'])
    fill_vector(event, "GenLep", lep_all_varnames, leps)

    # MET
    event.GenMet_pt = reader.products['genMET'][0].pt()
    event.GenMet_phi = reader.products['genMET'][0].phi()

    # jets
    jets = map(lambda t: {var: getattr(t, var)()
                          for var in jet_read_varnames},
               filter(lambda j: j.pt() > 30, reader.products['genJets']))

    # jet/lepton disambiguation
    jets = filter(
        lambda j: (min([999] + [deltaR2(j, l) for l in leps
                                if l['pt'] > 10]) > 0.3**2), jets)

    # 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)
    ]

    for jet in jets:
        jet['matchBParton'] = (min([999] + [
            deltaR2(jet, {
                'eta': b.eta(),
                'phi': b.phi()
            }) for b in b_partons
        ]) < 0.2**2)

    jets.sort(key=lambda p: -p['pt'])
    fill_vector(event, "GenJet", jet_write_varnames, jets)
Exemple #3
0
def filler(event):
    # shortcut
    r = reader.event
    if isMC: gPart = getGenPartsAll(r)

    # weight
    if isMC:
        event.weight = lumiScaleFactor * r.genWeight if lumiScaleFactor is not None else 1
    elif isData:
        event.weight = 1
    else:
        raise NotImplementedError("isMC %r isData %r " % (isMC, isData))

    # lumi lists and vetos
    if isData:
        #event.vetoPassed  = vetoList.passesVeto(r.run, r.lumi, r.evt)
        event.jsonPassed = lumiList.contains(r.run, r.lumi)
        # store decision to use after filler has been executed
        event.jsonPassed_ = event.jsonPassed

    if isMC:
        event.reweightPU36fb = nTrueInt36fb_puRW(r.nTrueInt)
        event.reweightPU36fbDown = nTrueInt36fb_puRWDown(r.nTrueInt)
        event.reweightPU36fbUp = nTrueInt36fb_puRWUp(r.nTrueInt)

    # top pt reweighting
    if isMC and options.doTopPtReweighting:
        event.reweightTopPt = topPtReweightingFunc(getTopPtsForReweighting(
            r)) / topScaleF if doTopPtReweighting else 1.

    # Leptons: Reading LepGood and LepOther and fill new LepGood collection in the output tree
    mu_selector = muonSelector(isoVar="relIso04",
                               barrelIso=0.25,
                               endcapIso=0.25,
                               absEtaCut=2.4,
                               dxy=0.05,
                               dz=0.1)
    ele_selector = eleSelector(isoVar="relIso03",
                               barrelIso=0.1,
                               endcapIso=0.1,
                               absEtaCut=2.5,
                               dxy=0.05,
                               dz=0.1,
                               eleId="M",
                               noMissingHits=False)
    leptons = getGoodAndOtherLeptons(r,
                                     ptCut=10,
                                     mu_selector=mu_selector,
                                     ele_selector=ele_selector)
    leptons.sort(key=lambda p: -p['pt'])

    # Store leptons
    event.nlep = len(leptons)
    for iLep, lep in enumerate(leptons):
        lep['index'] = iLep  # Index wrt to the output collection!
        lep['relIso'] = lep["relIso04"] if abs(
            lep['pdgId']) == 13 else lep["relIso03"]
        for b in lepton_vars_store:
            getattr(event, "lep_" + b)[iLep] = lep[b]

    # Storing lepton counters
    event.nGoodMuons = len(filter(lambda l: abs(l['pdgId']) == 13, leptons))
    event.nGoodElectrons = len(filter(lambda l: abs(l['pdgId']) == 11,
                                      leptons))
    event.nGoodLeptons = len(leptons)

    # Lepton convinience
    if options.leptonConvinience:
        for i in range(min(4, len(leptons))):
            for var in lep_convinience_vars:
                setattr(event, "l{n}_{var}".format(n=i + 1, var=var),
                        leptons[i][var])

    # Identify best Z
    (event.Z_mass, event.Z_l1_index,
     event.Z_l2_index) = closestOSDLMassToMZ(leptons)
    nonZ_lepton_indices = [
        i for i in range(len(leptons))
        if i not in [event.Z_l1_index, event.Z_l2_index]
    ]
    event.nonZ_l1_index = nonZ_lepton_indices[0] if len(
        nonZ_lepton_indices) > 0 else -1
    event.nonZ_l2_index = nonZ_lepton_indices[1] if len(
        nonZ_lepton_indices) > 1 else -1

    # Store Z information
    if event.Z_mass >= 0:
        if leptons[event.Z_l1_index]['pdgId'] * leptons[
                event.Z_l2_index]['pdgId'] > 0 or abs(
                    leptons[event.Z_l1_index]['pdgId']) != abs(
                        leptons[event.Z_l2_index]['pdgId']):
            raise RuntimeError("not a Z! Should never happen")
        Z_l1 = ROOT.TLorentzVector()
        Z_l1.SetPtEtaPhiM(leptons[event.Z_l1_index]['pt'],
                          leptons[event.Z_l1_index]['eta'],
                          leptons[event.Z_l1_index]['phi'], 0)
        Z_l2 = ROOT.TLorentzVector()
        Z_l2.SetPtEtaPhiM(leptons[event.Z_l2_index]['pt'],
                          leptons[event.Z_l2_index]['eta'],
                          leptons[event.Z_l2_index]['phi'], 0)
        Z = Z_l1 + Z_l2
        event.Z_pt = Z.Pt()
        event.Z_eta = Z.Eta()
        event.Z_phi = Z.Phi()
        event.Z_lldPhi = deltaPhi(leptons[event.Z_l1_index]['phi'],
                                  leptons[event.Z_l2_index]['phi'])
        event.Z_lldR = deltaR(leptons[event.Z_l1_index],
                              leptons[event.Z_l2_index])

        ## get the information for cos(theta*)
        # get the Z and lepton (negative charge) vectors
        lm_index = event.Z_l1_index if event.lep_pdgId[
            event.Z_l1_index] > 0 else event.Z_l2_index
        event.cosThetaStar = cosThetaStar(event.Z_mass, event.Z_pt,
                                          event.Z_eta, event.Z_phi,
                                          event.lep_pt[lm_index],
                                          event.lep_eta[lm_index],
                                          event.lep_phi[lm_index])

    # Jets and lepton jet cross-cleaning
    allJets = getAllJets(r,
                         leptons,
                         ptCut=0,
                         jetVars=jetVarNames,
                         absEtaCut=99,
                         jetCollections=["Jet", "DiscJet"])  #JetId is required
    selected_jets, other_jets = [], []
    for j in allJets:
        if isAnalysisJet(j, ptCut=30, absEtaCut=2.4):
            selected_jets.append(j)
        else:
            other_jets.append(j)

    # Don't change analysis jets even if we keep all jets, hence, apply abs eta cut
    bJets = filter(
        lambda j: isBJet(j, tagger='CSVv2') and abs(j['eta']) <= 2.4,
        selected_jets)
    bJetsDeepCSV = filter(
        lambda j: isBJet(j, tagger='DeepCSV') and abs(j['eta']) <= 2.4,
        selected_jets)
    nonBJets = filter(
        lambda j: not (isBJet(j, tagger='CSVv2') and abs(j['eta']) <= 2.4),
        selected_jets)
    nonBJetsDeepCSV = filter(
        lambda j: not (isBJet(j, tagger='DeepCSV') and abs(j['eta']) <= 2.4),
        selected_jets)

    # Store jets
    event.nJetSelected = len(selected_jets)
    jets_stored = allJets
    event.njet = len(jets_stored)
    for iJet, jet in enumerate(jets_stored):
        for b in jetVarNames:
            getattr(event, "jet_" + b)[iJet] = jet[b]

    # ETmiss
    event.met_pt = r.met_pt
    event.met_phi = r.met_phi

    # Analysis observables
    event.ht = sum([j['pt'] for j in selected_jets])
    event.metSig = event.met_pt / sqrt(event.ht) if event.ht > 0 else float(
        'nan')
    event.nBTag = len(bJets)
    event.nBTagDeepCSV = len(bJetsDeepCSV)

    # Systematics
    jets_sys = {}
    bjets_sys = {}
    nonBjets_sys = {}

    metVariants = ['']  # default

    if options.keepPhotons:
        # Keep photons and estimate met including (leading pt) photon
        photons = getGoodPhotons(r, ptCut=20, idLevel="loose", isData=isData)
        event.nPhotonGood = len(photons)
        if event.nPhotonGood > 0:
            metVariants += [
                '_photonEstimated'
            ]  # do all met calculations also for the photonEstimated variant
            event.photon_pt = photons[0]['pt']
            event.photon_eta = photons[0]['eta']
            event.photon_phi = photons[0]['phi']
            event.photon_idCutBased = photons[0]['idCutBased']
            if isMC:
                genPhoton = getGenPhoton(gPart)
                event.photon_genPt = genPhoton[
                    'pt'] if genPhoton is not None else float('nan')
                event.photon_genEta = genPhoton[
                    'eta'] if genPhoton is not None else float('nan')

            event.met_pt_photonEstimated, event.met_phi_photonEstimated = getMetPhotonEstimated(
                r.met_pt, r.met_phi, photons[0])
            event.metSig_photonEstimated = event.met_pt_photonEstimated / sqrt(
                event.ht) if event.ht > 0 else float('nan')

            event.photonJetdR = min(
                deltaR(photons[0], j)
                for j in selected_jets) if len(selected_jets) > 0 else 999
            event.photonLepdR = min(
                deltaR(photons[0], l)
                for l in leptons) if len(leptons) > 0 else 999

        if isMC:
            event.TTGJetsEventType = getTTGJetsEventType(r)

    if addSystematicVariations:
        for j in allJets:
            j['pt_JECUp'] = j['pt'] / j['corr'] * j['corr_JECUp']
            j['pt_JECDown'] = j['pt'] / j['corr'] * j['corr_JECDown']
            # JERUp, JERDown, JER
            addJERScaling(j)
        for var in ['JECUp', 'JECDown', 'JERUp', 'JERDown']:
            jets_sys[var] = filter(
                lambda j: isAnalysisJet(
                    j, ptCut=30, absEtaCut=2.4, ptVar='pt_' + var), allJets)
            bjets_sys[var] = filter(
                lambda j: isBJet(j) and abs(j['eta']) < 2.4, jets_sys[var])
            nonBjets_sys[var] = filter(
                lambda j: not (isBJet(j) and abs(j['eta']) < 2.4),
                jets_sys[var])

            setattr(event, "nJetSelected_" + var, len(jets_sys[var]))
            setattr(event, "ht_" + var,
                    sum([j['pt_' + var] for j in jets_sys[var]]))
            setattr(event, "nBTag_" + var, len(bjets_sys[var]))

        for var in [
                'JECUp', 'JECDown', 'JERUp', 'JERDown', 'UnclusteredEnUp',
                'UnclusteredEnDown'
        ]:
            for i in metVariants:
                # use cmg MET correction values ecept for JER where it is zero. There, propagate jet variations.
                if 'JER' in var or 'JECV' in var:
                    (met_corr_pt, met_corr_phi) = getMetJetCorrected(
                        getattr(event, "met_pt" + i),
                        getattr(event, "met_phi" + i), jets_sys[var], var)
                else:
                    (met_corr_pt, met_corr_phi) = getMetCorrected(
                        r, var,
                        photons[0] if i.count("photonEstimated") else None)

                setattr(event, "met_pt" + i + "_" + var, met_corr_pt)
                setattr(event, "met_phi" + i + "_" + var, met_corr_phi)
                ht = getattr(event, "ht_" +
                             var) if 'Unclustered' not in var else event.ht
                setattr(
                    event, "metSig" + i + "_" + var,
                    getattr(event, "met_pt" + i + "_" + var) /
                    sqrt(ht) if ht > 0 else float('nan'))

    if addSystematicVariations:

        # B tagging weights method 1a, first for CSVv2
        for j in selected_jets:
            btagEff_CSVv2.addBTagEffToJet(j)
        #print "CSVv2", selected_jets[0]['beff']['SF'], selected_jets[0]['pt']
        for var in btagEff_CSVv2.btagWeightNames:
            if var != 'MC':
                setattr(
                    event, 'reweightBTagCSVv2_' + var,
                    btagEff_CSVv2.getBTagSF_1a(
                        var, bJets,
                        filter(lambda j: abs(j['eta']) < 2.4, nonBJets)))

        # B tagging weights method 1a, now for DeepCSV
        for j in selected_jets:
            btagEff_DeepCSV.addBTagEffToJet(j)
        #print "DeepCSV", selected_jets[0]['beff']['SF'], selected_jets[0]['pt']
        for var in btagEff_DeepCSV.btagWeightNames:
            if var != 'MC':
                setattr(
                    event, 'reweightBTagDeepCSV_' + var,
                    btagEff_DeepCSV.getBTagSF_1a(
                        var, bJetsDeepCSV,
                        filter(lambda j: abs(j['eta']) < 2.4,
                               nonBJetsDeepCSV)))

    # gen information on extra leptons
    if isMC and not options.skipGenMatching:
        genSearch.init(gPart)
        # Start with status 1 gen leptons

        # gLep = filter( lambda p:abs(p['pdgId']) in [11, 13] and p['status']==1 and p['pt']>10 and abs(p['eta'])<2.5, gPart )
        # ... no acceptance cuts
        gLep = filter(
            lambda p: abs(p['pdgId']) in [11, 13] and p['status'] == 1, gPart)
        for l in gLep:
            ancestry = [gPart[x]['pdgId'] for x in genSearch.ancestry(l)]
            l["n_D"] = sum([ancestry.count(p) for p in D_mesons])
            l["n_B"] = sum([ancestry.count(p) for p in B_mesons])
            l["n_W"] = sum([ancestry.count(p) for p in [24, -24]])
            l["n_t"] = sum([ancestry.count(p) for p in [6, -6]])
            l["n_tau"] = sum([ancestry.count(p) for p in [15, -15]])
            matched_lep = bestDRMatchInCollection(
                l, leptons)  #FIXME -> fix all the gen / reco match indices!
            if matched_lep:
                l["lepGood2MatchIndex"] = matched_lep['index']
            else:
                l["lepGood2MatchIndex"] = -1

        # store genleps
        event.nGenLep = len(gLep)
        for iLep, lep in enumerate(gLep):
            for b in genLepVarNames:
                getattr(event, "GenLep_" + b)[iLep] = lep[b]

        # gen Z
        genZs = filter(
            lambda p: (abs(p['pdgId']) == 23 and genSearch.isLast(p)), gPart)
        genZs.sort(key=lambda p: -p['pt'])
        if len(genZs) > 0:
            genZ = genZs[0]
            event.genZ_pt = genZ['pt']
            event.genZ_mass = genZ['mass']
            event.genZ_eta = genZ['eta']
            event.genZ_phi = genZ['phi']

            lep_m = filter(lambda p: p['pdgId'] in [11, 13, 15],
                           genSearch.daughters(genZ))
            event.genZ_daughter_flavor = max(
                [p['pdgId'] for p in genSearch.daughters(genZ)])
            if len(lep_m) == 1:
                event.genZ_cosThetaStar = cosThetaStar(
                    event.genZ_mass, event.genZ_pt, event.genZ_eta,
                    event.genZ_phi, lep_m[0]['pt'], lep_m[0]['eta'],
                    lep_m[0]['phi'])
def filler(event):

    if reader.position % 100 == 0:
        logger.info("At event %i/%i", reader.position, reader.nEvents)
    event.lumiweight1fb = lumiweight1fb

    # read jets
    jets = filter(lambda j: isGoodDelphesJet(j), reader.jets())
    jets.sort(key=lambda p: -p['pt'])
    addIndex(jets)

    # make b jets
    #    for j in jets:
    #        print j['pt'], j['eta'], j['bTag'], j['bTagPhys'], j['bTagAlgo']
    bJets = filter(lambda j: j['bTagPhys'] >= 4, jets)
    nonBJets = filter(lambda j: not (j['bTagPhys'] < 4), jets)
    bj0, bj1 = (bJets + nonBJets + [None, None])[:2]
    fill_vector(event, "bj0", jet_write_varnames, bj0)
    fill_vector(event, "bj1", jet_write_varnames, bj1)

    event.nBTag = len(bJets)

    # read leptons
    allLeps = reader.muonsTight() + reader.electrons()
    allLeps.sort(key=lambda p: -p['pt'])
    leps = filter(isGoodDelphesLepton, allLeps)
    # cross-cleaning of reco-objects
    # leps = filter( lambda l: (min([999]+[deltaR2(l, j) for j in jets if j['pt']>30]) > 0.3**2 ), leps )
    # give index to leptons
    addIndex(leps)

    # Store
    fill_vector_collection(event, "lep", lep_varnames, leps)
    fill_vector_collection(event, "jet", jet_varnames, jets)

    event.nMuons = len(filter(lambda l: abs(l['pdgId']) == 13, leps))
    event.nElectrons = len(filter(lambda l: abs(l['pdgId']) == 11, leps))

    # MET
    met = reader.met()[0]

    event.met_pt = met['pt']
    event.met_phi = met['phi']

    # search for Z in leptons
    (event.Z_mass, Z_l1_index, Z_l2_index) = closestOSDLMassToMZ(leps)
    nonZ_indices = [
        i for i in range(len(leps)) if i not in [Z_l1_index, Z_l2_index]
    ]
    event.Z_l1_index = leps[Z_l1_index]['index'] if Z_l1_index >= 0 else -1
    event.Z_l2_index = leps[Z_l2_index]['index'] if Z_l2_index >= 0 else -1
    event.nonZ_l1_index = leps[
        nonZ_indices[0]]['index'] if len(nonZ_indices) > 0 else -1
    event.nonZ_l2_index = leps[
        nonZ_indices[1]]['index'] if len(nonZ_indices) > 1 else -1

    # Store Z information
    if event.Z_mass >= 0:
        if leps[event.Z_l1_index]['pdgId'] * leps[
                event.Z_l2_index]['pdgId'] > 0 or abs(
                    leps[event.Z_l1_index]['pdgId']) != abs(
                        leps[event.Z_l2_index]['pdgId']):
            raise RuntimeError("not a Z! Should not happen")
        Z_l1 = ROOT.TLorentzVector()
        Z_l1.SetPtEtaPhiM(leps[event.Z_l1_index]['pt'],
                          leps[event.Z_l1_index]['eta'],
                          leps[event.Z_l1_index]['phi'], 0)
        Z_l2 = ROOT.TLorentzVector()
        Z_l2.SetPtEtaPhiM(leps[event.Z_l2_index]['pt'],
                          leps[event.Z_l2_index]['eta'],
                          leps[event.Z_l2_index]['phi'], 0)
        Z = Z_l1 + Z_l2
        event.Z_pt = Z.Pt()
        event.Z_eta = Z.Eta()
        event.Z_phi = Z.Phi()
        event.Z_lldPhi = deltaPhi(leps[event.Z_l1_index]['phi'],
                                  leps[event.Z_l2_index]['phi'])
        event.Z_lldR = deltaR(leps[event.Z_l1_index], leps[event.Z_l2_index])
        lm_index = event.Z_l1_index if leps[
            event.Z_l1_index]['pdgId'] > 0 else event.Z_l2_index
        event.Z_cosThetaStar = cosThetaStar(event.Z_mass, event.Z_pt,
                                            event.Z_eta, event.Z_phi,
                                            leps[lm_index]['pt'],
                                            leps[lm_index]['eta'],
                                            leps[lm_index]['phi'])