Example #1
0
def setup_candidates(df, cfg):
    if df['is_data'] and extract_year(df['dataset']) != 2018:
        # 2016, 2017 data
        jes_suffix = ''
        jes_suffix_met = ''
    elif df['is_data']:
        # 2018 data
        jes_suffix = '_nom'
        jes_suffix_met = '_nom'
    else:
        # MC, all years
        jes_suffix = '_nom'
        if cfg.MET.JER:
            jes_suffix_met = '_jer'
        else:
            jes_suffix_met = '_nom'

    muons = JaggedCandidateArray.candidatesfromcounts(
        df['nMuon'],
        pt=df['Muon_pt'],
        eta=df['Muon_eta'],
        abseta=np.abs(df['Muon_eta']),
        phi=df['Muon_phi'],
        mass=0 * df['Muon_pt'],
        charge=df['Muon_charge'],
        looseId=df['Muon_looseId'],
        iso=df["Muon_pfRelIso04_all"],
        tightId=df['Muon_tightId'],
        dxy=df['Muon_dxy'],
        dz=df['Muon_dz'])

    # All muons must be at least loose
    muons = muons[muons.looseId \
                    & (muons.iso < cfg.MUON.CUTS.LOOSE.ISO) \
                    & (muons.pt > cfg.MUON.CUTS.LOOSE.PT) \
                    & (muons.abseta<cfg.MUON.CUTS.LOOSE.ETA) \
                    ]

    electrons = JaggedCandidateArray.candidatesfromcounts(
        df['nElectron'],
        pt=df['Electron_pt'],
        eta=df['Electron_eta'],
        abseta=np.abs(df['Electron_eta']),
        etasc=df['Electron_eta'] + df['Electron_deltaEtaSC'],
        absetasc=np.abs(df['Electron_eta'] + df['Electron_deltaEtaSC']),
        phi=df['Electron_phi'],
        mass=0 * df['Electron_pt'],
        charge=df['Electron_charge'],
        looseId=(df[cfg.ELECTRON.BRANCH.ID] >= 1),
        tightId=(df[cfg.ELECTRON.BRANCH.ID] == 4),
        dxy=np.abs(df['Electron_dxy']),
        dz=np.abs(df['Electron_dz']),
        barrel=np.abs(df['Electron_eta'] + df['Electron_deltaEtaSC']) <=
        1.4442)
    # All electrons must be at least loose
    pass_dxy = (electrons.barrel & (np.abs(electrons.dxy) < cfg.ELECTRON.CUTS.LOOSE.DXY.BARREL)) \
    | (~electrons.barrel & (np.abs(electrons.dxy) < cfg.ELECTRON.CUTS.LOOSE.DXY.ENDCAP))

    pass_dz = (electrons.barrel & (np.abs(electrons.dz) < cfg.ELECTRON.CUTS.LOOSE.DZ.BARREL)) \
    | (~electrons.barrel & (np.abs(electrons.dz) < cfg.ELECTRON.CUTS.LOOSE.DZ.ENDCAP))

    electrons = electrons[electrons.looseId \
                                    & (electrons.pt>cfg.ELECTRON.CUTS.LOOSE.PT) \
                                    & (electrons.absetasc<cfg.ELECTRON.CUTS.LOOSE.ETA) \
                                    & pass_dxy \
                                    & pass_dz
                                    ]

    if cfg.OVERLAP.ELECTRON.MUON.CLEAN:
        electrons = electrons[object_overlap(electrons,
                                             muons,
                                             dr=cfg.OVERLAP.ELECTRON.MUON.DR)]

    taus = JaggedCandidateArray.candidatesfromcounts(
        df['nTau'],
        pt=df['Tau_pt'],
        eta=df['Tau_eta'],
        abseta=np.abs(df['Tau_eta']),
        phi=df['Tau_phi'],
        mass=0 * df['Tau_pt'],
        decaymode=df[cfg.TAU.BRANCH.ID],
        iso=df[cfg.TAU.BRANCH.ISO])

    # For MC, add the matched gen-particle info for checking
    if not df['is_data']:
        kwargs = {'genpartflav': df['Tau_genPartFlav']}
        taus.add_attributes(**kwargs)

    taus = taus[ (taus.decaymode) \
                & (taus.pt > cfg.TAU.CUTS.PT)\
                & (taus.abseta < cfg.TAU.CUTS.ETA) \
                & ((taus.iso&2)==2)]

    if cfg.OVERLAP.TAU.MUON.CLEAN:
        taus = taus[object_overlap(taus, muons, dr=cfg.OVERLAP.TAU.MUON.DR)]
    if cfg.OVERLAP.TAU.ELECTRON.CLEAN:
        taus = taus[object_overlap(taus,
                                   electrons,
                                   dr=cfg.OVERLAP.TAU.ELECTRON.DR)]

    # choose the right branch name for photon ID bitmap depending on the actual name in the file (different between nano v5 and v7)
    if cfg.PHOTON.BRANCH.ID in df.keys():
        PHOTON_BRANCH_ID = cfg.PHOTON.BRANCH.ID
    else:
        PHOTON_BRANCH_ID = cfg.PHOTON.BRANCH.IDV7
    photons = JaggedCandidateArray.candidatesfromcounts(
        df['nPhoton'],
        pt=df['Photon_pt'],
        eta=df['Photon_eta'],
        abseta=np.abs(df['Photon_eta']),
        phi=df['Photon_phi'],
        mass=0 * df['Photon_pt'],
        looseId=(df[PHOTON_BRANCH_ID] >= 1) & df['Photon_electronVeto'],
        mediumId=(df[PHOTON_BRANCH_ID] >= 2) & df['Photon_electronVeto'],
        r9=df['Photon_r9'],
        barrel=df['Photon_isScEtaEB'],
    )
    photons = photons[photons.looseId \
              & (photons.pt > cfg.PHOTON.CUTS.LOOSE.pt) \
              & (photons.abseta < cfg.PHOTON.CUTS.LOOSE.eta)
              ]

    if cfg.OVERLAP.PHOTON.MUON.CLEAN:
        photons = photons[object_overlap(photons,
                                         muons,
                                         dr=cfg.OVERLAP.PHOTON.MUON.DR)]
    if cfg.OVERLAP.PHOTON.ELECTRON.CLEAN:
        photons = photons[object_overlap(photons,
                                         electrons,
                                         dr=cfg.OVERLAP.PHOTON.ELECTRON.DR)]

    ak4 = JaggedCandidateArray.candidatesfromcounts(
        df['nJet'],
        pt=df[f'Jet_pt{jes_suffix}'] if
        (df['is_data'] or cfg.AK4.JER) else df[f'Jet_pt{jes_suffix}'] /
        df['Jet_corr_JER'],
        eta=df['Jet_eta'],
        abseta=np.abs(df['Jet_eta']),
        phi=df['Jet_phi'],
        mass=np.zeros_like(df['Jet_pt']),
        looseId=(
            df['Jet_jetId']
            & 2) == 2,  # bitmask: 1 = loose, 2 = tight, 3 = tight + lep veto
        tightId=(
            df['Jet_jetId']
            & 2) == 2,  # bitmask: 1 = loose, 2 = tight, 3 = tight + lep veto
        puid=((df['Jet_puId'] & 2 > 0) |
              ((df[f'Jet_pt{jes_suffix}'] if
                (df['is_data'] or cfg.AK4.JER) else df[f'Jet_pt{jes_suffix}'] /
                df['Jet_corr_JER']) > 50)),  # medium pileup jet ID
        csvv2=df["Jet_btagCSVV2"],
        deepcsv=df['Jet_btagDeepB'],
        nef=df['Jet_neEmEF'],
        nhf=df['Jet_neHEF'],
        chf=df['Jet_chHEF'],
        ptraw=df['Jet_pt'] * (1 - df['Jet_rawFactor']),
        nconst=df['Jet_nConstituents'],
        hadflav=0 * df['Jet_pt'] if df['is_data'] else df['Jet_hadronFlavour'])

    # Before cleaning, apply HEM veto
    hem_ak4 = ak4[(ak4.pt > 30) & (-3.0 < ak4.eta) & (ak4.eta < -1.3) &
                  (-1.57 < ak4.phi) & (ak4.phi < -0.87)]
    df['hemveto'] = hem_ak4.counts == 0

    # B jets have their own overlap cleaning,
    # so deal with them before applying filtering to jets
    btag_discriminator = getattr(ak4, cfg.BTAG.algo)
    btag_cut = cfg.BTAG.CUTS[cfg.BTAG.algo][cfg.BTAG.wp]
    bjets = ak4[
        (ak4.pt > cfg.BTAG.PT) \
        & (ak4.abseta < cfg.BTAG.ETA) \
        & (btag_discriminator > btag_cut)
    ]

    if cfg.OVERLAP.BTAG.MUON.CLEAN:
        bjets = bjets[object_overlap(bjets, muons,
                                     dr=cfg.OVERLAP.BTAG.MUON.DR)]
    if cfg.OVERLAP.BTAG.ELECTRON.CLEAN:
        bjets = bjets[object_overlap(bjets,
                                     electrons,
                                     dr=cfg.OVERLAP.BTAG.ELECTRON.DR)]
    if cfg.OVERLAP.BTAG.PHOTON.CLEAN:
        bjets = bjets[object_overlap(bjets,
                                     photons,
                                     dr=cfg.OVERLAP.BTAG.PHOTON.DR)]

    ak4 = ak4[ak4.looseId]

    if cfg.OVERLAP.AK4.MUON.CLEAN:
        ak4 = ak4[object_overlap(ak4, muons, dr=cfg.OVERLAP.AK4.MUON.DR)]
    if cfg.OVERLAP.AK4.ELECTRON.CLEAN:
        ak4 = ak4[object_overlap(ak4,
                                 electrons,
                                 dr=cfg.OVERLAP.AK4.ELECTRON.DR)]
    if cfg.OVERLAP.AK4.PHOTON.CLEAN:
        ak4 = ak4[object_overlap(ak4, photons, dr=cfg.OVERLAP.AK4.PHOTON.DR)]

    if df['is_data']:
        msd = df[f'FatJet_msoftdrop{jes_suffix}']
    else:
        msd = df[f'FatJet_msoftdrop{jes_suffix}'] / (
            df['FatJet_msoftdrop_corr_JMR'] * df['FatJet_msoftdrop_corr_JMS'])
        if not cfg.AK8.JER:
            msd = msd / df['FatJet_corr_JER']

    ak8 = JaggedCandidateArray.candidatesfromcounts(
        df['nFatJet'],
        pt=df[f'FatJet_pt{jes_suffix}'] if
        (df['is_data'] or cfg.AK8.JER) else df[f'FatJet_pt{jes_suffix}'] /
        df['FatJet_corr_JER'],
        eta=df['FatJet_eta'],
        abseta=np.abs(df['FatJet_eta']),
        phi=df['FatJet_phi'],
        mass=msd,
        tightId=(df['FatJet_jetId'] & 2) == 2,  # Tight
        csvv2=df["FatJet_btagCSVV2"],
        deepcsv=df['FatJet_btagDeepB'],
        tau1=df['FatJet_tau1'],
        tau2=df['FatJet_tau2'],
        tau21=df['FatJet_tau2'] / df['FatJet_tau1'],
        wvsqcd=df['FatJet_deepTag_WvsQCD'],
        wvsqcdmd=df['FatJet_deepTagMD_WvsQCD'],
        zvsqcd=df['FatJet_deepTag_ZvsQCD'],
        zvsqcdmd=df['FatJet_deepTagMD_ZvsQCD'],
        tvsqcd=df['FatJet_deepTag_TvsQCD'],
        tvsqcdmd=df['FatJet_deepTagMD_TvsQCD'],
        wvstqcd=df['FatJet_deepTag_WvsQCD'] *
        (1 - df['FatJet_deepTag_TvsQCD']) /
        (1 - df['FatJet_deepTag_WvsQCD'] * df['FatJet_deepTag_TvsQCD']),
        wvstqcdmd=df['FatJet_deepTagMD_WvsQCD'] *
        (1 - df['FatJet_deepTagMD_TvsQCD']) /
        (1 - df['FatJet_deepTagMD_WvsQCD'] * df['FatJet_deepTagMD_TvsQCD']),
    )
    ak8 = ak8[ak8.tightId & object_overlap(ak8, muons)
              & object_overlap(ak8, electrons) & object_overlap(ak8, photons)]

    if extract_year(df['dataset']) == 2017:
        met_branch = 'METFixEE2017'
    else:
        met_branch = 'MET'

    met_pt = df[f'{met_branch}_pt{jes_suffix_met}']
    met_phi = df[f'{met_branch}_phi{jes_suffix_met}']

    return met_pt, met_phi, ak4, bjets, ak8, muons, electrons, taus, photons
    def process(self, df):
        self._configure(df)
        output = self.accumulator.identity()
        dataset = df['dataset']

        # Lumi mask
        year = extract_year(dataset)
        if is_data(dataset):
            if year == 2016:
                json = bucoffea_path(
                    'data/json/Cert_271036-284044_13TeV_ReReco_07Aug2017_Collisions16_JSON.txt'
                )
            elif year == 2017:
                json = bucoffea_path(
                    'data/json/Cert_294927-306462_13TeV_EOY2017ReReco_Collisions17_JSON_v1.txt'
                )
            elif year == 2018:
                json = bucoffea_path(
                    'data/json/Cert_314472-325175_13TeV_17SeptEarlyReReco2018ABC_PromptEraD_Collisions18_JSON.txt'
                )
            lumi_mask = LumiMask(json)(df['run'], df['luminosityBlock'])
        else:
            lumi_mask = np.ones(df.size) == 1

        # MET filters
        if is_data(dataset):
            filt_met = mask_and(df, cfg.FILTERS.DATA)
        else:
            filt_met = mask_and(df, cfg.FILTERS.MC)

        if year == 2016:
            trigger = 'HLT_Photon175'
        else:
            trigger = 'HLT_Photon200'

        photons = setup_photons(df)

        ak4 = setup_jets(df)
        ak4 = ak4[
                  object_overlap(ak4, photons) \
                  & ak4.tightId \
                  & (ak4.pt > 100) \
                  & (ak4.abseta < 2.4)
                  ]

        event_mask = filt_met \
                     & lumi_mask \
                     & (ak4.counts > 0) \
                     & df[trigger] \
                     & (df['MET_pt'] < 60)

        # Generator weight
        weights = processor.Weights(size=df.size, storeIndividual=True)

        if is_data(dataset):
            weights.add('gen', np.ones(df.size))
        else:
            weights.add('gen', df['Generator_weight'])

        photon_kinematics = (photons.pt > 200) & (photons.barrel)

        # Medium
        vals = photons[photon_kinematics & photons.mediumId].sieie[event_mask]
        pt = photons[photon_kinematics & photons.mediumId].pt[event_mask]
        output['sieie'].fill(dataset=dataset,
                             cat='medium',
                             sieie=vals.flatten(),
                             pt=pt.flatten(),
                             weights=weight_shape(
                                 vals,
                                 weights.weight()[event_mask]))

        # No Sieie
        vals = photons[photon_kinematics
                       & medium_id_no_sieie(photons)].sieie[event_mask]
        pt = photons[photon_kinematics
                     & medium_id_no_sieie(photons)].pt[event_mask]
        output['sieie'].fill(dataset=dataset,
                             cat='medium_nosieie',
                             sieie=vals.flatten(),
                             pt=pt.flatten(),
                             weights=weight_shape(
                                 vals,
                                 weights.weight()[event_mask]))

        # No Sieie, inverted isolation
        vals = photons[photon_kinematics
                       & medium_id_no_sieie_inv_iso(photons)].sieie[event_mask]
        pt = photons[photon_kinematics
                     & medium_id_no_sieie_inv_iso(photons)].pt[event_mask]
        output['sieie'].fill(dataset=dataset,
                             cat='medium_nosieie_invertiso',
                             sieie=vals.flatten(),
                             pt=pt.flatten(),
                             weights=weight_shape(
                                 vals,
                                 weights.weight()[event_mask]))

        # Keep track of weight sum
        if not is_data(dataset):
            output['sumw'][dataset] += df['genEventSumw']
            output['sumw2'][dataset] += df['genEventSumw2']
        return output
def setup_candidates(df, cfg):
    muons = JaggedCandidateArray.candidatesfromcounts(
        df['nMuon'],
        pt=df['Muon_pt'],
        eta=df['Muon_eta'],
        phi=df['Muon_phi'],
        mass=0 * df['Muon_pt'],
        charge=df['Muon_charge'],
        looseId=df['Muon_looseId'],
        iso=df["Muon_pfRelIso04_all"],
        tightId=df['Muon_tightId'])

    # All muons must be at least loose
    muons = muons[muons.looseId \
                    & (muons.iso < cfg.MUON.CUTS.LOOSE.ISO) \
                    & (muons.pt > cfg.MUON.CUTS.LOOSE.PT) \
                    & (np.abs(muons.eta)<cfg.MUON.CUTS.LOOSE.ETA) \
                    ]

    electrons = JaggedCandidateArray.candidatesfromcounts(
        df['nElectron'],
        pt=df['Electron_pt'],
        eta=df['Electron_eta'],
        phi=df['Electron_phi'],
        mass=0 * df['Electron_pt'],
        charge=df['Electron_charge'],
        looseId=(df[cfg.ELECTRON.BRANCH.ID] >= 1),
        tightId=(df[cfg.ELECTRON.BRANCH.ID] == 4),
        dxy=np.abs(df['Electron_dxy']),
        dz=np.abs(df['Electron_dz']),
        barrel=np.abs(df['Electron_eta']) < 1.479)
    # All electrons must be at least loose
    pass_dxy = (electrons.barrel & (electrons.dxy < cfg.ELECTRON.CUTS.LOOSE.DXY.BARREL)) \
    | (~electrons.barrel & (electrons.dxy < cfg.ELECTRON.CUTS.LOOSE.DXY.ENDCAP))

    pass_dz = (electrons.barrel & (electrons.dz < cfg.ELECTRON.CUTS.LOOSE.DZ.BARREL)) \
    | (~electrons.barrel & (electrons.dz < cfg.ELECTRON.CUTS.LOOSE.DZ.ENDCAP))

    electrons = electrons[electrons.looseId \
                                    & (electrons.pt>cfg.ELECTRON.CUTS.LOOSE.PT) \
                                    & (np.abs(electrons.eta)<cfg.ELECTRON.CUTS.LOOSE.ETA) \
                                    & pass_dxy \
                                    & pass_dz
                                    ]

    taus = JaggedCandidateArray.candidatesfromcounts(
        df['nTau'],
        pt=df['Tau_pt'],
        eta=df['Tau_eta'],
        phi=df['Tau_phi'],
        mass=0 * df['Tau_pt'],
        decaymode=df['Tau_idDecayModeNewDMs'] & df['Tau_idDecayMode'],
        clean=df['Tau_cleanmask'],
        iso=df['Tau_idMVAnewDM2017v2'],
    )

    taus = taus[ object_overlap(taus, muons) \
                & object_overlap(taus, electrons) \
                & (taus.decaymode) \
                & (taus.pt > cfg.TAU.CUTS.PT)\
                & (np.abs(taus.eta) < cfg.TAU.CUTS.ETA) \
                & ((taus.iso&4)==4)]
    photons = JaggedCandidateArray.candidatesfromcounts(
        df['nPhoton'],
        pt=df['Photon_pt'],
        eta=df['Photon_eta'],
        phi=df['Photon_phi'],
        mass=0 * df['Photon_pt'],
        looseId=(df[cfg.PHOTON.BRANCH.ID] >= 1) & df['Photon_electronVeto'],
        mediumId=(df[cfg.PHOTON.BRANCH.ID] >= 2) & df['Photon_electronVeto'],
        barrel=np.abs(df['Photon_eta']) < 1.479,
    )
    photons = photons[photons.looseId \
              & (photons.pt > cfg.PHOTON.CUTS.LOOSE.pt) \
              & (np.abs(photons.eta) < cfg.PHOTON.CUTS.LOOSE.eta)]

    ak4 = JaggedCandidateArray.candidatesfromcounts(
        df['nJet'],
        pt=df['Jet_pt'],
        eta=df['Jet_eta'],
        phi=df['Jet_phi'],
        mass=df['Jet_mass'],
        looseId=(df['Jet_jetId'] & 2) == 2,  # bitmask: 1 = loose, 2 = tight
        tightId=(df['Jet_jetId'] & 2) == 2,  # bitmask: 1 = loose, 2 = tight
        csvv2=df["Jet_btagCSVV2"],
        deepcsv=df['Jet_btagDeepB'],
        # nef=df['Jet_neEmEF'],
        nhf=df['Jet_neHEF'],
        chf=df['Jet_chHEF'],
        # clean=df['Jet_cleanmask']
        # cef=df['Jet_chEmEF'],
    )

    ak4 = ak4[ak4.looseId & object_overlap(ak4, muons)
              & object_overlap(ak4, electrons)]

    ak8 = JaggedCandidateArray.candidatesfromcounts(
        df['nFatJet'],
        pt=df['FatJet_pt'],
        eta=df['FatJet_eta'],
        phi=df['FatJet_phi'],
        mass=df['FatJet_msoftdrop'],
        tightId=(df['FatJet_jetId'] & 2) == 2,  # Tight
        csvv2=df["FatJet_btagCSVV2"],
        deepcsv=df['FatJet_btagDeepB'],
        tau1=df['FatJet_tau1'],
        tau2=df['FatJet_tau2'])

    hlt = JaggedCandidateArray.candidatesfromcounts(
        df['nTrigObj'],
        pt=df['TrigObj_pt'],
        eta=df['TrigObj_eta'],
        phi=df['TrigObj_phi'],
        mass=0 * df['TrigObj_pt'],
        id=df['TrigObj_id'],
        filter=df['TrigObj_filterBits'])
    return ak4, ak8, muons, electrons, taus, photons, hlt
Example #4
0
def setup_candidates(df, cfg):
    if df['is_data'] and extract_year(df['dataset']) != 2018:
        # 2016, 2017 data
        jes_suffix = ''
        jes_suffix_met = ''
    elif df['is_data']:
        # 2018 data
        jes_suffix = '_nom'
        jes_suffix_met = '_nom'
    else:
        # MC, all years
        jes_suffix = '_nom'
        jes_suffix_met = '_jer'

    muons = JaggedCandidateArray.candidatesfromcounts(
        df['nMuon'],
        pt=df['Muon_pt'],
        eta=df['Muon_eta'],
        abseta=np.abs(df['Muon_eta']),
        phi=df['Muon_phi'],
        mass=0 * df['Muon_pt'],
        charge=df['Muon_charge'],
        looseId=df['Muon_looseId'],
        iso=df["Muon_pfRelIso04_all"],
        tightId=df['Muon_tightId'],
        dxy=df['Muon_dxy'],
        dz=df['Muon_dz']
    )

    # All muons must be at least loose
    muons = muons[muons.looseId \
                    & (muons.iso < cfg.MUON.CUTS.LOOSE.ISO) \
                    & (muons.pt > cfg.MUON.CUTS.LOOSE.PT) \
                    & (muons.abseta<cfg.MUON.CUTS.LOOSE.ETA) \
                    ]


    electrons = JaggedCandidateArray.candidatesfromcounts(
        df['nElectron'],
        pt=df['Electron_pt'],
        eta=df['Electron_eta'],
        abseta=np.abs(df['Electron_eta']),
        phi=df['Electron_phi'],
        mass=0 * df['Electron_pt'],
        charge=df['Electron_charge'],
        looseId=(df[cfg.ELECTRON.BRANCH.ID]>=1),
        tightId=(df[cfg.ELECTRON.BRANCH.ID]==4),
        dxy=np.abs(df['Electron_dxy']),
        dz=np.abs(df['Electron_dz']),
        barrel=np.abs(df['Electron_eta']) <= 1.479
    )
    # All electrons must be at least loose
    pass_dxy = (electrons.barrel & (np.abs(electrons.dxy) < cfg.ELECTRON.CUTS.LOOSE.DXY.BARREL)) \
    | (~electrons.barrel & (np.abs(electrons.dxy) < cfg.ELECTRON.CUTS.LOOSE.DXY.ENDCAP))

    pass_dz = (electrons.barrel & (np.abs(electrons.dz) < cfg.ELECTRON.CUTS.LOOSE.DZ.BARREL)) \
    | (~electrons.barrel & (np.abs(electrons.dz) < cfg.ELECTRON.CUTS.LOOSE.DZ.ENDCAP))

    electrons = electrons[electrons.looseId \
                                    & (electrons.pt>cfg.ELECTRON.CUTS.LOOSE.PT) \
                                    & (electrons.abseta<cfg.ELECTRON.CUTS.LOOSE.ETA) \
                                    & pass_dxy \
                                    & pass_dz
                                    ]

    if cfg.OVERLAP.ELECTRON.MUON.CLEAN:
        electrons = electrons[object_overlap(electrons, muons, dr=cfg.OVERLAP.ELECTRON.MUON.DR)]


    taus = JaggedCandidateArray.candidatesfromcounts(
        df['nTau'],
        pt=df['Tau_pt'],
        eta=df['Tau_eta'],
        abseta=np.abs(df['Tau_eta']),
        phi=df['Tau_phi'],
        mass=0 * df['Tau_pt'],
        decaymode=df['Tau_idDecayMode'],
        iso=df['Tau_idMVAoldDM2017v2'],
    )

    taus = taus[ (taus.decaymode) \
                & (taus.pt > cfg.TAU.CUTS.PT)\
                & (taus.abseta < cfg.TAU.CUTS.ETA) \
                & ((taus.iso&2)==2)]

    if cfg.OVERLAP.TAU.MUON.CLEAN:
        taus = taus[object_overlap(taus, muons, dr=cfg.OVERLAP.TAU.MUON.DR)]
    if cfg.OVERLAP.TAU.ELECTRON.CLEAN:
        taus = taus[object_overlap(taus, electrons, dr=cfg.OVERLAP.TAU.ELECTRON.DR)]

    photons = JaggedCandidateArray.candidatesfromcounts(
        df['nPhoton'],
        pt=df['Photon_pt'],
        eta=df['Photon_eta'],
        abseta=np.abs(df['Photon_eta']),
        phi=df['Photon_phi'],
        mass=0*df['Photon_pt'],
        looseId=(df[cfg.PHOTON.BRANCH.ID]>=1) & df['Photon_electronVeto'],
        mediumId=(df[cfg.PHOTON.BRANCH.ID]>=2) & df['Photon_electronVeto'],
        r9=df['Photon_r9'],
        barrel=np.abs(df['Photon_eta']) < 1.479,
    )
    photons = photons[photons.looseId \
              & (photons.pt > cfg.PHOTON.CUTS.LOOSE.pt) \
              & (photons.abseta < cfg.PHOTON.CUTS.LOOSE.eta)
              ]

    if cfg.OVERLAP.PHOTON.MUON.CLEAN:
        photons = photons[object_overlap(photons, muons, dr=cfg.OVERLAP.PHOTON.MUON.DR)]
    if cfg.OVERLAP.PHOTON.ELECTRON.CLEAN:
        photons = photons[object_overlap(photons, electrons, dr=cfg.OVERLAP.PHOTON.ELECTRON.DR)]

    ak4 = JaggedCandidateArray.candidatesfromcounts(
        df['nJet'],
        pt=df[f'Jet_pt{jes_suffix}'],
        eta=df['Jet_eta'],
        abseta=np.abs(df['Jet_eta']),
        phi=df['Jet_phi'],
        mass=np.zeros_like(df['Jet_pt']),
        looseId=(df['Jet_jetId']&2) == 2, # bitmask: 1 = loose, 2 = tight, 3 = tight + lep veto
        tightId=(df['Jet_jetId']&2) == 2, # bitmask: 1 = loose, 2 = tight, 3 = tight + lep veto
        puid=((df['Jet_puId']&2>0) | (df[f'Jet_pt{jes_suffix}']>50)), # medium pileup jet ID
        csvv2=df["Jet_btagCSVV2"],
        deepcsv=df['Jet_btagDeepB'],
        # nef=df['Jet_neEmEF'],
        nhf=df['Jet_neHEF'],
        chf=df['Jet_chHEF'],
        ptraw=df['Jet_pt']*(1-df['Jet_rawFactor']),
        nconst=df['Jet_nConstituents']
        # clean=df['Jet_cleanmask']
        # cef=df['Jet_chEmEF'],
    )
    # Before cleaning, apply HEM veto
    hem_ak4 = ak4[ (ak4.pt>30) &
        (-3.0 < ak4.eta) &
        (ak4.eta < -1.3) &
        (-1.57 < ak4.phi) &
        (ak4.phi < -0.87)
        ]
    df['hemveto'] = hem_ak4.counts == 0

    # B jets have their own overlap cleaning,
    # so deal with them before applying filtering to jets
    btag_discriminator = getattr(ak4, cfg.BTAG.algo)
    btag_cut = cfg.BTAG.CUTS[cfg.BTAG.algo][cfg.BTAG.wp]
    bjets = ak4[
        (ak4.looseId) \
        & (ak4.pt > cfg.BTAG.PT) \
        & (ak4.abseta < cfg.BTAG.ETA) \
        & (btag_discriminator > btag_cut)
    ]

    if cfg.OVERLAP.BTAG.MUON.CLEAN:
        bjets = bjets[object_overlap(bjets, muons, dr=cfg.OVERLAP.BTAG.MUON.DR)]
    if cfg.OVERLAP.BTAG.ELECTRON.CLEAN:
        bjets = bjets[object_overlap(bjets, electrons, dr=cfg.OVERLAP.BTAG.ELECTRON.DR)]
    if cfg.OVERLAP.BTAG.PHOTON.CLEAN:
        bjets = bjets[object_overlap(bjets, photons, dr=cfg.OVERLAP.BTAG.PHOTON.DR)]

    ak4 = ak4[ak4.looseId]

    if cfg.OVERLAP.AK4.MUON.CLEAN:
        ak4 = ak4[object_overlap(ak4, muons, dr=cfg.OVERLAP.AK4.MUON.DR)]
    if cfg.OVERLAP.AK4.ELECTRON.CLEAN:
        ak4 = ak4[object_overlap(ak4, electrons, dr=cfg.OVERLAP.AK4.ELECTRON.DR)]
    if cfg.OVERLAP.AK4.PHOTON.CLEAN:
        ak4 = ak4[object_overlap(ak4, photons, dr=cfg.OVERLAP.AK4.PHOTON.DR)]


    ak8 = JaggedCandidateArray.candidatesfromcounts(
        df['nFatJet'],
        pt=df[f'FatJet_pt{jes_suffix}'],
        eta=df['FatJet_eta'],
        abseta=np.abs(df['FatJet_eta']),
        phi=df['FatJet_phi'],
        mass=df[f'FatJet_msoftdrop{jes_suffix}'],
        tightId=(df['FatJet_jetId']&2) == 2, # Tight
        csvv2=df["FatJet_btagCSVV2"],
        deepcsv=df['FatJet_btagDeepB'],
        tau1=df['FatJet_tau1'],
        tau2=df['FatJet_tau2'],
        tau21=df['FatJet_tau2']/df['FatJet_tau1'],
        wvsqcd=df['FatJet_deepTag_WvsQCD'],
        wvsqcdmd=df['FatJet_deepTagMD_WvsQCD'],
        zvsqcd=df['FatJet_deepTag_ZvsQCD'],
        zvsqcdmd=df['FatJet_deepTagMD_ZvsQCD']
    )
    ak8 = ak8[ak8.tightId & object_overlap(ak8, muons) & object_overlap(ak8, electrons) & object_overlap(ak8, photons)]

    if extract_year(df['dataset']) == 2017:
        met_branch = 'METFixEE2017'
    else:
        met_branch = 'MET'

    met_pt = df[f'{met_branch}_pt{jes_suffix_met}']
    met_phi = df[f'{met_branch}_phi{jes_suffix_met}']

    return met_pt, met_phi, ak4, bjets, ak8, muons, electrons, taus, photons