Esempio n. 1
0
    def process_shift(self, events, shift_name):
        dataset = events.metadata['dataset']
        isRealData = not hasattr(events, "genWeight")
        selection = PackedSelection()
        weights = Weights(len(events), storeIndividual=True)
        output = self.make_output()
        if shift_name is None and not isRealData:
            output['sumw'] = ak.sum(events.genWeight)

        if isRealData or self._newTrigger:
            trigger = np.zeros(len(events), dtype='bool')
            for t in self._triggers[self._year]:
                if t in events.HLT.fields:
                    trigger = trigger | events.HLT[t]
            selection.add('trigger', trigger)
            del trigger
        else:
            selection.add('trigger', np.ones(len(events), dtype='bool'))

        if isRealData:
            selection.add(
                'lumimask', lumiMasks[self._year](events.run,
                                                  events.luminosityBlock))
        else:
            selection.add('lumimask', np.ones(len(events), dtype='bool'))

        if isRealData and self._skipRunB and self._year == '2017':
            selection.add('dropB', events.run > 299329)
        else:
            selection.add('dropB', np.ones(len(events), dtype='bool'))

        if isRealData:
            trigger = np.zeros(len(events), dtype='bool')
            for t in self._muontriggers[self._year]:
                if t in events.HLT.fields:
                    trigger |= np.array(events.HLT[t])
            selection.add('muontrigger', trigger)
            del trigger
        else:
            selection.add('muontrigger', np.ones(len(events), dtype='bool'))

        metfilter = np.ones(len(events), dtype='bool')
        for flag in self._met_filters[
                self._year]['data' if isRealData else 'mc']:
            metfilter &= np.array(events.Flag[flag])
        selection.add('metfilter', metfilter)
        del metfilter

        fatjets = events.FatJet
        fatjets['msdcorr'] = corrected_msoftdrop(fatjets)
        fatjets['qcdrho'] = 2 * np.log(fatjets.msdcorr / fatjets.pt)
        fatjets['n2ddt'] = fatjets.n2b1 - n2ddt_shift(fatjets, year=self._year)
        fatjets['msdcorr_full'] = fatjets['msdcorr'] * self._msdSF[self._year]

        candidatejet = fatjets[
            # https://github.com/DAZSLE/BaconAnalyzer/blob/master/Analyzer/src/VJetLoader.cc#L269
            (fatjets.pt > 200)
            & (abs(fatjets.eta) < 2.5)
            & fatjets.isTight  # this is loose in sampleContainer
        ]

        candidatejet = candidatejet[:, :
                                    2]  # Only consider first two to match generators
        if self._jet_arbitration == 'pt':
            candidatejet = ak.firsts(candidatejet)
        elif self._jet_arbitration == 'mass':
            candidatejet = ak.firsts(candidatejet[ak.argmax(
                candidatejet.msdcorr, axis=1, keepdims=True)])
        elif self._jet_arbitration == 'n2':
            candidatejet = ak.firsts(candidatejet[ak.argmin(candidatejet.n2ddt,
                                                            axis=1,
                                                            keepdims=True)])
        elif self._jet_arbitration == 'ddb':
            candidatejet = ak.firsts(candidatejet[ak.argmax(
                candidatejet.btagDDBvLV2, axis=1, keepdims=True)])
        elif self._jet_arbitration == 'ddc':
            candidatejet = ak.firsts(candidatejet[ak.argmax(
                candidatejet.btagDDCvLV2, axis=1, keepdims=True)])
        else:
            raise RuntimeError("Unknown candidate jet arbitration")

        if self._tagger == 'v1':
            bvl = candidatejet.btagDDBvL
            cvl = candidatejet.btagDDCvL
            cvb = candidatejet.btagDDCvB
        elif self._tagger == 'v2':
            bvl = candidatejet.btagDDBvLV2
            cvl = candidatejet.btagDDCvLV2
            cvb = candidatejet.btagDDCvBV2
        elif self._tagger == 'v3':
            bvl = candidatejet.particleNetMD_Xbb
            cvl = candidatejet.particleNetMD_Xcc / (
                1 - candidatejet.particleNetMD_Xbb)
            cvb = candidatejet.particleNetMD_Xcc / (
                candidatejet.particleNetMD_Xcc +
                candidatejet.particleNetMD_Xbb)

        elif self._tagger == 'v4':
            bvl = candidatejet.particleNetMD_Xbb
            cvl = candidatejet.btagDDCvLV2
            cvb = candidatejet.particleNetMD_Xcc / (
                candidatejet.particleNetMD_Xcc +
                candidatejet.particleNetMD_Xbb)
        else:
            raise ValueError("Not an option")

        selection.add('minjetkin', (candidatejet.pt >= 450)
                      & (candidatejet.pt < 1200)
                      & (candidatejet.msdcorr >= 40.)
                      & (candidatejet.msdcorr < 201.)
                      & (abs(candidatejet.eta) < 2.5))
        selection.add('_strict_mass', (candidatejet.msdcorr > 85) &
                      (candidatejet.msdcorr < 130))
        selection.add('_high_score', cvl > 0.8)
        selection.add('minjetkinmu', (candidatejet.pt >= 400)
                      & (candidatejet.pt < 1200)
                      & (candidatejet.msdcorr >= 40.)
                      & (candidatejet.msdcorr < 201.)
                      & (abs(candidatejet.eta) < 2.5))
        selection.add('minjetkinw', (candidatejet.pt >= 200)
                      & (candidatejet.pt < 1200)
                      & (candidatejet.msdcorr >= 40.)
                      & (candidatejet.msdcorr < 201.)
                      & (abs(candidatejet.eta) < 2.5))
        selection.add('jetid', candidatejet.isTight)
        selection.add('n2ddt', (candidatejet.n2ddt < 0.))
        if not self._tagger == 'v2':
            selection.add('ddbpass', (bvl >= 0.89))
            selection.add('ddcpass', (cvl >= 0.83))
            selection.add('ddcvbpass', (cvb >= 0.2))
        else:
            selection.add('ddbpass', (bvl >= 0.7))
            selection.add('ddcpass', (cvl >= 0.45))
            selection.add('ddcvbpass', (cvb >= 0.03))

        jets = events.Jet
        jets = jets[(jets.pt > 30.) & (abs(jets.eta) < 2.5) & jets.isTight]
        # only consider first 4 jets to be consistent with old framework
        jets = jets[:, :4]
        dphi = abs(jets.delta_phi(candidatejet))
        selection.add(
            'antiak4btagMediumOppHem',
            ak.max(jets[dphi > np.pi / 2][self._ak4tagBranch],
                   axis=1,
                   mask_identity=False) <
            BTagEfficiency.btagWPs[self._ak4tagger][self._year]['medium'])
        ak4_away = jets[dphi > 0.8]
        selection.add(
            'ak4btagMedium08',
            ak.max(ak4_away[self._ak4tagBranch], axis=1, mask_identity=False) >
            BTagEfficiency.btagWPs[self._ak4tagger][self._year]['medium'])

        met = events.MET
        selection.add('met', met.pt < 140.)

        goodmuon = ((events.Muon.pt > 10)
                    & (abs(events.Muon.eta) < 2.4)
                    & (events.Muon.pfRelIso04_all < 0.25)
                    & events.Muon.looseId)
        nmuons = ak.sum(goodmuon, axis=1)
        leadingmuon = ak.firsts(events.Muon[goodmuon])

        if self._looseTau:
            goodelectron = ((events.Electron.pt > 10)
                            & (abs(events.Electron.eta) < 2.5)
                            &
                            (events.Electron.cutBased >= events.Electron.VETO))
            nelectrons = ak.sum(goodelectron, axis=1)

            ntaus = ak.sum(
                ((events.Tau.pt > 20)
                 & (abs(events.Tau.eta) < 2.3)
                 & events.Tau.idDecayMode
                 & ((events.Tau.idMVAoldDM2017v2 & 2) != 0)
                 & ak.all(events.Tau.metric_table(events.Muon[goodmuon]) > 0.4,
                          axis=2)
                 & ak.all(events.Tau.metric_table(
                     events.Electron[goodelectron]) > 0.4,
                          axis=2)),
                axis=1,
            )
        else:
            goodelectron = (
                (events.Electron.pt > 10)
                & (abs(events.Electron.eta) < 2.5)
                & (events.Electron.cutBased >= events.Electron.LOOSE))
            nelectrons = ak.sum(goodelectron, axis=1)

            ntaus = ak.sum(
                (events.Tau.pt > 20)
                &
                events.Tau.idDecayMode  # bacon iso looser than Nano selection
                & ak.all(events.Tau.metric_table(events.Muon[goodmuon]) > 0.4,
                         axis=2)
                & ak.all(events.Tau.metric_table(events.Electron[goodelectron])
                         > 0.4,
                         axis=2),
                axis=1,
            )

        selection.add('noleptons',
                      (nmuons == 0) & (nelectrons == 0) & (ntaus == 0))
        selection.add('onemuon',
                      (nmuons == 1) & (nelectrons == 0) & (ntaus == 0))
        selection.add('muonkin',
                      (leadingmuon.pt > 55.) & (abs(leadingmuon.eta) < 2.1))
        selection.add('muonDphiAK8',
                      abs(leadingmuon.delta_phi(candidatejet)) > 2 * np.pi / 3)

        # W-Tag (Tag and Probe)
        # tag side
        selection.add(
            'ak4btagMediumOppHem',
            ak.max(jets[dphi > np.pi / 2][self._ak4tagBranch],
                   axis=1,
                   mask_identity=False) >
            BTagEfficiency.btagWPs[self._ak4tagger][self._year]['medium'])
        selection.add('met40p', met.pt > 40.)
        selection.add('tightMuon',
                      (leadingmuon.tightId) & (leadingmuon.pt > 53.))
        # selection.add('ptrecoW', (leadingmuon + met).pt > 250.)
        selection.add('ptrecoW200', (leadingmuon + met).pt > 200.)
        selection.add(
            'ak4btagNearMu',
            leadingmuon.delta_r(leadingmuon.nearest(ak4_away, axis=None)) <
            2.0)
        _bjets = jets[self._ak4tagBranch] > BTagEfficiency.btagWPs[
            self._ak4tagger][self._year]['medium']
        # _nearAK8 = jets.delta_r(candidatejet)  < 0.8
        # _nearMu = jets.delta_r(ak.firsts(events.Muon))  < 0.3
        # selection.add('ak4btagOld', ak.sum(_bjets & ~_nearAK8 & ~_nearMu, axis=1) >= 1)
        _nearAK8 = jets.delta_r(candidatejet) < 0.8
        _nearMu = jets.delta_r(leadingmuon) < 0.3
        selection.add('ak4btagOld',
                      ak.sum(_bjets & ~_nearAK8 & ~_nearMu, axis=1) >= 1)

        # _nearAK8 = jets.delta_r(candidatejet)  < 0.8
        # _nearMu = jets.delta_r(candidatejet.nearest(events.Muon[goodmuon], axis=None))  < 0.3
        # selection.add('ak4btagNew', ak.sum(_bjets & ~_nearAK8 & ~_nearMu, axis=1) >= 1)

        # probe side
        selection.add('minWjetpteta',
                      (candidatejet.pt >= 200) & (abs(candidatejet.eta) < 2.4))
        # selection.add('noNearMuon', candidatejet.delta_r(candidatejet.nearest(events.Muon[goodmuon], axis=None)) > 1.0)
        selection.add('noNearMuon', candidatejet.delta_r(leadingmuon) > 1.0)
        #####

        if isRealData:
            genflavor = ak.zeros_like(candidatejet.pt)
        else:
            if 'HToCC' in dataset or 'HToBB' in dataset:
                if self._ewkHcorr:
                    add_HiggsEW_kFactors(weights, events.GenPart, dataset)

            weights.add('genweight', events.genWeight)
            if "PSWeight" in events.fields:
                add_ps_weight(weights, events.PSWeight)
            else:
                add_ps_weight(weights, None)
            if "LHEPdfWeight" in events.fields:
                add_pdf_weight(weights, events.LHEPdfWeight)
            else:
                add_pdf_weight(weights, None)
            if "LHEScaleWeight" in events.fields:
                add_scalevar_7pt(weights, events.LHEScaleWeight)
                add_scalevar_3pt(weights, events.LHEScaleWeight)
            else:
                add_scalevar_7pt(weights, [])
                add_scalevar_3pt(weights, [])

            add_pileup_weight(weights, events.Pileup.nPU, self._year, dataset)
            bosons = getBosons(events.GenPart)
            matchedBoson = candidatejet.nearest(bosons,
                                                axis=None,
                                                threshold=0.8)
            if self._tightMatch:
                match_mask = (
                    (candidatejet.pt - matchedBoson.pt) / matchedBoson.pt <
                    0.5) & ((candidatejet.msdcorr - matchedBoson.mass) /
                            matchedBoson.mass < 0.3)
                selmatchedBoson = ak.mask(matchedBoson, match_mask)
                genflavor = bosonFlavor(selmatchedBoson)
            else:
                genflavor = bosonFlavor(matchedBoson)
            genBosonPt = ak.fill_none(ak.firsts(bosons.pt), 0)
            if self._newVjetsKfactor:
                add_VJets_kFactors(weights, events.GenPart, dataset)
            else:
                add_VJets_NLOkFactor(weights, genBosonPt, self._year, dataset)
            if shift_name is None:
                output['btagWeight'].fill(val=self._btagSF.addBtagWeight(
                    weights, ak4_away, self._ak4tagBranch))
            if self._nnlops_rew and dataset in [
                    'GluGluHToCC_M125_13TeV_powheg_pythia8'
            ]:
                weights.add('minlo_rew',
                            powheg_to_nnlops(ak.to_numpy(genBosonPt)))

            if self._newTrigger:
                add_jetTriggerSF(
                    weights, ak.firsts(fatjets),
                    self._year if not self._skipRunB else f'{self._year}CDEF',
                    selection)
            else:
                add_jetTriggerWeight(weights, candidatejet.msdcorr,
                                     candidatejet.pt, self._year)

            add_mutriggerSF(weights, leadingmuon, self._year, selection)
            add_mucorrectionsSF(weights, leadingmuon, self._year, selection)

            if self._year in ("2016", "2017"):
                weights.add("L1Prefiring", events.L1PreFiringWeight.Nom,
                            events.L1PreFiringWeight.Up,
                            events.L1PreFiringWeight.Dn)

            logger.debug("Weight statistics: %r" % weights.weightStatistics)

        msd_matched = candidatejet.msdcorr * self._msdSF[self._year] * (
            genflavor > 0) + candidatejet.msdcorr * (genflavor == 0)

        regions = {
            'signal': [
                'noleptons', 'minjetkin', 'met', 'metfilter', 'jetid',
                'antiak4btagMediumOppHem', 'n2ddt', 'trigger', 'lumimask'
            ],
            'signal_noddt': [
                'noleptons', 'minjetkin', 'met', 'jetid',
                'antiak4btagMediumOppHem', 'trigger', 'lumimask', 'metfilter'
            ],
            # 'muoncontrol': ['minjetkinmu', 'jetid', 'n2ddt', 'ak4btagMedium08', 'onemuon', 'muonkin', 'muonDphiAK8', 'muontrigger', 'lumimask', 'metfilter'],
            'muoncontrol': [
                'onemuon', 'muonkin', 'muonDphiAK8', 'metfilter',
                'minjetkinmu', 'jetid', 'ak4btagMedium08', 'n2ddt',
                'muontrigger', 'lumimask'
            ],
            'muoncontrol_noddt': [
                'onemuon', 'muonkin', 'muonDphiAK8', 'jetid', 'metfilter',
                'minjetkinmu', 'jetid', 'ak4btagMedium08', 'muontrigger',
                'lumimask'
            ],
            'wtag': [
                'onemuon', 'tightMuon', 'minjetkinw', 'jetid', 'met40p',
                'metfilter', 'ptrecoW200', 'ak4btagOld', 'muontrigger',
                'lumimask'
            ],
            'wtag0': [
                'onemuon', 'tightMuon', 'met40p', 'metfilter', 'ptrecoW200',
                'ak4btagOld', 'muontrigger', 'lumimask'
            ],
            'wtag2': [
                'onemuon', 'tightMuon', 'minjetkinw', 'jetid',
                'ak4btagMediumOppHem', 'met40p', 'metfilter', 'ptrecoW200',
                'ak4btagOld', 'muontrigger', 'lumimask'
            ],
            'noselection': [],
        }

        def normalize(val, cut):
            if cut is None:
                ar = ak.to_numpy(ak.fill_none(val, np.nan))
                return ar
            else:
                ar = ak.to_numpy(ak.fill_none(val[cut], np.nan))
                return ar

        import time
        tic = time.time()
        if shift_name is None:
            for region, cuts in regions.items():
                allcuts = set([])
                cut = selection.all(*allcuts)
                output['cutflow_msd'].fill(region=region,
                                           genflavor=normalize(
                                               genflavor, None),
                                           cut=0,
                                           weight=weights.weight(),
                                           msd=normalize(msd_matched, None))
                output['cutflow_eta'].fill(region=region,
                                           genflavor=normalize(genflavor, cut),
                                           cut=0,
                                           weight=weights.weight()[cut],
                                           eta=normalize(
                                               candidatejet.eta, cut))
                output['cutflow_pt'].fill(region=region,
                                          genflavor=normalize(genflavor, cut),
                                          cut=0,
                                          weight=weights.weight()[cut],
                                          pt=normalize(candidatejet.pt, cut))
                for i, cut in enumerate(cuts + ['ddcvbpass', 'ddcpass']):
                    allcuts.add(cut)
                    cut = selection.all(*allcuts)
                    output['cutflow_msd'].fill(region=region,
                                               genflavor=normalize(
                                                   genflavor, cut),
                                               cut=i + 1,
                                               weight=weights.weight()[cut],
                                               msd=normalize(msd_matched, cut))
                    output['cutflow_eta'].fill(
                        region=region,
                        genflavor=normalize(genflavor, cut),
                        cut=i + 1,
                        weight=weights.weight()[cut],
                        eta=normalize(candidatejet.eta, cut))
                    output['cutflow_pt'].fill(
                        region=region,
                        genflavor=normalize(genflavor, cut),
                        cut=i + 1,
                        weight=weights.weight()[cut],
                        pt=normalize(candidatejet.pt, cut))

                    if self._evtVizInfo and 'ddcpass' in allcuts and isRealData and region == 'signal':
                        if 'event' not in events.fields:
                            continue
                        _cut = selection.all(*allcuts, '_strict_mass',
                                             '_high_score')
                        # _cut = selection.all('_strict_mass'')
                        output['to_check'][
                            'mass'] += processor.column_accumulator(
                                normalize(msd_matched, _cut))
                        nfatjet = ak.sum(
                            ((fatjets.pt > 200) &
                             (abs(fatjets.eta) < 2.5) & fatjets.isTight),
                            axis=1)
                        output['to_check'][
                            'njet'] += processor.column_accumulator(
                                normalize(nfatjet, _cut))
                        output['to_check'][
                            'fname'] += processor.column_accumulator(
                                np.array([events.metadata['filename']] *
                                         len(normalize(msd_matched, _cut))))
                        output['to_check'][
                            'event'] += processor.column_accumulator(
                                normalize(events.event, _cut))
                        output['to_check'][
                            'luminosityBlock'] += processor.column_accumulator(
                                normalize(events.luminosityBlock, _cut))
                        output['to_check'][
                            'run'] += processor.column_accumulator(
                                normalize(events.run, _cut))

        if shift_name is None:
            systematics = [None] + list(weights.variations)
        else:
            systematics = [shift_name]

        def fill(region, systematic, wmod=None):
            selections = regions[region]
            cut = selection.all(*selections)
            sname = 'nominal' if systematic is None else systematic
            if wmod is None:
                if systematic in weights.variations:
                    weight = weights.weight(modifier=systematic)[cut]
                else:
                    weight = weights.weight()[cut]
            else:
                weight = weights.weight()[cut] * wmod[cut]

            output['templates'].fill(
                region=region,
                systematic=sname,
                runid=runmap(events.run)[cut],
                genflavor=normalize(genflavor, cut),
                pt=normalize(candidatejet.pt, cut),
                msd=normalize(msd_matched, cut),
                ddb=normalize(bvl, cut),
                ddc=normalize(cvl, cut),
                ddcvb=normalize(cvb, cut),
                weight=weight,
            )
            if region in [
                    'wtag', 'wtag0', 'wtag2', 'wtag3', 'wtag4', 'wtag5',
                    'wtag6', 'wtag7', 'noselection'
            ]:  # and sname in ['nominal', 'pileup_weightDown', 'pileup_weightUp', 'jet_triggerDown', 'jet_triggerUp']:
                output['wtag'].fill(
                    region=region,
                    systematic=sname,
                    genflavor=normalize(genflavor, cut),
                    pt=normalize(candidatejet.pt, cut),
                    msd=normalize(msd_matched, cut),
                    n2ddt=normalize(candidatejet.n2ddt, cut),
                    ddc=normalize(cvl, cut),
                    ddcvb=normalize(cvb, cut),
                    weight=weight,
                )
            # if region in ['signal', 'noselection']:
            #     output['etaphi'].fill(
            #         region=region,
            #         systematic=sname,
            #         runid=runmap(events.run)[cut],
            #         genflavor=normalize(genflavor, cut),
            #         pt=normalize(candidatejet.pt, cut),
            #         eta=normalize(candidatejet.eta, cut),
            #         phi=normalize(candidatejet.phi, cut),
            #         ddc=normalize(cvl, cut),
            #         ddcvb=normalize(cvb, cut),
            #     ),
            if not isRealData:
                if wmod is not None:
                    _custom_weight = events.genWeight[cut] * wmod[cut]
                else:
                    _custom_weight = np.ones_like(weight)
                output['genresponse_noweight'].fill(
                    region=region,
                    systematic=sname,
                    pt=normalize(candidatejet.pt, cut),
                    genpt=normalize(genBosonPt, cut),
                    weight=_custom_weight,
                )

                output['genresponse'].fill(
                    region=region,
                    systematic=sname,
                    pt=normalize(candidatejet.pt, cut),
                    genpt=normalize(genBosonPt, cut),
                    weight=weight,
                )
            if systematic is None:
                output['signal_opt'].fill(
                    region=region,
                    genflavor=normalize(genflavor, cut),
                    ddc=normalize(cvl, cut),
                    ddcvb=normalize(cvb, cut),
                    msd=normalize(msd_matched, cut),
                    weight=weight,
                )
                output['signal_optb'].fill(
                    region=region,
                    genflavor=normalize(genflavor, cut),
                    ddb=normalize(bvl, cut),
                    msd=normalize(msd_matched, cut),
                    weight=weight,
                )

        for region in regions:
            cut = selection.all(*(set(regions[region]) - {'n2ddt'}))
            if shift_name is None:
                output['nminus1_n2ddt'].fill(
                    region=region,
                    n2ddt=normalize(candidatejet.n2ddt, cut),
                    weight=weights.weight()[cut],
                )
            for systematic in systematics:
                if isRealData and systematic is not None:
                    continue
                fill(region, systematic)
            if shift_name is None and 'GluGluH' in dataset and 'LHEWeight' in events.fields:
                for i in range(9):
                    fill(region, 'LHEScale_%d' % i, events.LHEScaleWeight[:,
                                                                          i])
                for c in events.LHEWeight.fields[1:]:
                    fill(region, 'LHEWeight_%s' % c, events.LHEWeight[c])

        toc = time.time()
        output["filltime"] = toc - tic
        if shift_name is None:
            output["weightStats"] = weights.weightStatistics
        return {dataset: output}
Esempio n. 2
0
    def process(self, events):

        # get meta infos
        dataset = events.metadata["dataset"]
        isRealData = not hasattr(events, "genWeight")
        n_events = len(events)
        selection = processor.PackedSelection()
        weights = processor.Weights(n_events)
        output = self.accumulator.identity()

        # weights
        if not isRealData:
            output['sumw'][dataset] += awkward1.sum(events.genWeight)
        
        # trigger
        triggers = {}
        for channel in ["e","mu"]:
            trigger = np.zeros(len(events), dtype='bool')
            for t in self._trigger[channel]:
                try:
                    trigger = trigger | events.HLT[t]
                except:
                    warnings.warn("Missing trigger %s" % t, RuntimeWarning)
            triggers[channel] = trigger
            
        # met filter
        met_filters = ["goodVertices",
                       "globalSuperTightHalo2016Filter",
                       "HBHENoiseFilter",
                       "HBHENoiseIsoFilter",
                       "EcalDeadCellTriggerPrimitiveFilter",
                       "BadPFMuonFilter",
                       ]
        met_filters_mask = np.ones(len(events), dtype='bool')
        for t in met_filters:
            met_filters_mask = met_filters_mask & events.Flag[t]
        selection.add("met_filter", awkward1.to_numpy(met_filters_mask))
        
        # load objects
        muons = events.Muon
        electrons = events.Electron
        jets = events.Jet
        fatjets = events.FatJet
        subjets = events.SubJet
        fatjetsLS = events.FatJetLS
        met = events.MET
        
        # muons
        goodmuon = (
            (muons.mediumId)
            & (muons.miniPFRelIso_all <= 0.2)
            & (muons.pt >= 27)
            & (abs(muons.eta) <= 2.4)
            & (abs(muons.dz) < 0.1)
            & (abs(muons.dxy) < 0.05)
            & (muons.sip3d < 4)
        )
        good_muons = muons[goodmuon]
        ngood_muons = awkward1.sum(goodmuon, axis=1)

        # electrons
        goodelectron = (
            (electrons.mvaFall17V2noIso_WP90)
            & (electrons.pt >= 30)
            & (abs(electrons.eta) <= 1.479)
            & (abs(electrons.dz) < 0.1)
            & (abs(electrons.dxy) < 0.05)
            & (electrons.sip3d < 4)
        )
        good_electrons = electrons[goodelectron]
        ngood_electrons = awkward1.sum(goodelectron, axis=1)
        
        # good leptons
        good_leptons = awkward1.concatenate([good_muons, good_electrons], axis=1)
        good_leptons = good_leptons[awkward1.argsort(good_leptons.pt)]
        
        # lepton candidate
        candidatelep = awkward1.firsts(good_leptons)
        
        # lepton channel selection
        selection.add("ch_e", awkward1.to_numpy((triggers["e"]) & (ngood_electrons==1) & (ngood_muons==0))) # not sure if need to require 0 muons or 0 electrons in the next line
        selection.add("ch_mu", awkward1.to_numpy((triggers["mu"]) & (ngood_electrons==0) & (ngood_muons==1)))
        
        # jets
        ht = awkward1.sum(jets[jets.pt > 30].pt,axis=1)
        selection.add("ht_400", awkward1.to_numpy(ht>=400))
        goodjet = (
            (jets.isTight)
            & (jets.pt > 30)
            & (abs(jets.eta) <= 2.5)
            )
        good_jets = jets[goodjet]

        # fat jets
        jID = "isTight"
        # TODO: add mass correction

        # a way to get the first two subjets
        # cart = awkward1.cartesian([fatjets, subjets], nested=True)
        # idxes = awkward1.pad_none(awkward1.argsort(cart['0'].delta_r(cart['1'])), 2, axis=2)
        # sj1 = subjets[idxes[:,:,0]]
        # sj2 = subjets[idxes[:,:,1]]
        
        good_fatjet = (
            (getattr(fatjets, jID))
            & (abs(fatjets.eta) <= 2.4)
            & (fatjets.pt > 50)
            & (fatjets.msoftdrop > 30)
            & (fatjets.msoftdrop < 210)
            #& (fatjets.pt.copy(content=fatjets.subjets.content.counts) == 2) # TODO: require 2 subjets?
            # this can probably be done w FatJet_subJetIdx1 or FatJet_subJetIdx2
            & (awkward1.all(fatjets.subjets.pt >= 20))
            & (awkward1.all(abs(fatjets.subjets.eta) <= 2.4))
        )
        good_fatjets = fatjets[good_fatjet]

        # hbb candidate
        mask_hbb = (
            (good_fatjets.pt > 200)
            & (good_fatjets.delta_r(candidatelep) > 2.0)
            )
        candidateHbb = awkward1.firsts(good_fatjets[mask_hbb])

        # b-tag #& (good_fatjets.particleNetMD_Xbb > 0.9)
        selection.add('hbb_btag',awkward1.to_numpy(candidateHbb.deepTagMD_ZHbbvsQCD >= 0.8)) # score would be larger for tight category (0.97)  
        
        # No AK4 b-tagged jets away from bb jet
        jets_HbbV = jets[good_jets.delta_r(candidateHbb) >= 1.2]
        selection.add('hbb_vetobtagaway',  awkward1.to_numpy(awkward1.max(jets_HbbV.btagDeepB, axis=1, mask_identity=False) > BTagEfficiency.btagWPs[self._year]['medium']))
        
        # fat jets Lepton Subtracted
        # wjj candidate
        mask_wjj = (
            (fatjetsLS.pt > 50)
            & (fatjetsLS.delta_r(candidatelep) > 1.2)
            # need to add 2 subjets w pt > 20 & eta<2.4
            # need to add ID?
            )
        candidateWjj = awkward1.firsts(fatjetsLS[mask_wjj][awkward1.argmin(fatjetsLS[mask_wjj].delta_r(candidatelep),axis=1,keepdims=True)])
        # add t2/t1 <= 0.75 (0.45 HP)
        selection.add('hww_mass',  awkward1.to_numpy(candidateWjj.mass >= 10))

        print('met ',met)
        # wjjlnu info
        #HSolverLiInfo  hwwInfoLi;
        # qqSDmass = candidateWjj.msoftdrop
        # hwwLi   = hSolverLi->minimize(candidatelep.p4(), met.p4(), wjjcand.p4(), qqSDmass, hwwInfoLi)
        #neutrino = hwwInfoLi.neutrino;
        #wlnu     = hwwInfoLi.wlnu;
        #wqq      = hwwInfoLi.wqqjet;
        #hWW      = hwwInfoLi.hWW;
        #wwDM     = PhysicsUtilities::deltaR( wlnu,wqq) * hWW.pt()/2.0;
        # add dlvqq <= 11 (2.5 HP)
               
        # in the meantime let's add the mass
        '''
        mm = (candidatejet - candidatelep).mass2
        jmass = (mm>0)*np.sqrt(np.maximum(0, mm)) + (mm<0)*candidatejet.mass
        joffshell = jmass < 62.5
        massassumption = 80.*joffshell + (125 - 80.)*~joffshell
        x = massassumption**2/(2*candidatelep.pt*met.pt) + np.cos(candidatelep.phi - met.phi)
        met_eta = (
            (x < 1)*np.arcsinh(x*np.sinh(candidatelep.eta))
            + (x > 1)*(
                candidatelep.eta - np.sign(candidatelep.eta)*np.arccosh(candidatelep.eta)
                )
            )
        met_p4 = TLorentzVectorArray.from_ptetaphim(np.array([0.]),np.array([0.]),np.array([0.]),np.array([0.]))
        if met.size > 0:
            met_p4 = TLorentzVectorArray.from_ptetaphim(met.pt, met_eta.fillna(0.), met.phi, np.zeros(met.size))
        
        # hh system
        candidateHH = candidateWjj + met_p4 + candidateHbb
        selection.add('hh_mass', candidateHH.mass >= 700)
        selection.add('hh_centrality', candidateHH.pt/candidateHH.mass >= 0.3)
        '''
        
        channels = {"e": ["met_filter","ch_e","ht_400","hbb_btag","hbb_vetobtagaway","hww_mass"], #,"hh_mass","hh_centrality"],
                    "mu": ["met_filter","ch_mu","ht_400","hbb_btag","hbb_vetobtagaway","hww_mass"] #,"hh_mass","hh_centrality"],
                    }

        # need to add gen info
        
        if not isRealData:
            weights.add('genweight', events.genWeight)
            add_pileup_weight(weights, events.Pileup.nPU, self._year, dataset)
            
        for channel, cuts in channels.items():
            allcuts = set()
            output['cutflow'].fill(dataset=dataset, channel=channel, cut=0, weight=weights.weight())
            for i, cut in enumerate(cuts):
                allcuts.add(cut)
                cut = selection.all(*allcuts)
                output['cutflow'].fill(dataset=dataset, channel=channel, cut=i + 1, weight=weights.weight()[cut])

        return output
Esempio n. 3
0
    def process(self, events):
        dataset = events.metadata['dataset']
        print('process dataset', dataset)
        isRealData = 'genWeight' not in events.columns
        selection = processor.PackedSelection()
        weights = processor.Weights(len(events))
        output = self.accumulator.identity()
        if (len(events) == 0): return output
        if not isRealData:
            output['sumw'][dataset] += events.genWeight.sum()

        # trigger paths
        if isRealData:
            trigger_fatjet = np.zeros(events.size, dtype='bool')
            for t in self._triggers[self._year]:
                try:
                    trigger_fatjet = trigger_fatjet | events.HLT[t]
                except:
                    print('trigger %s not available' % t)
                    continue

            trigger_muon = np.zeros(events.size, dtype='bool')
            for t in self._muontriggers[self._year]:
                trigger_muon = trigger_muon | events.HLT[t]

        else:
            trigger_fatjet = np.ones(events.size, dtype='bool')
            trigger_muon = np.ones(events.size, dtype='bool')

        selection.add('fatjet_trigger', trigger_fatjet)
        selection.add('muon_trigger', trigger_muon)

        #jet corrected kinematics
        gru = events.GRU
        IN = events.IN
        fatjets = events.FatJet
        fatjets['msdcorr'] = corrected_msoftdrop(fatjets)
        fatjets['rhocorr'] = 2 * np.log(fatjets.msdcorr / fatjets.pt)
        fatjets['gruddt'] = gru.v25 - shift(
            fatjets, algo='gruddt', year=self._year)
        fatjets['gru'] = gru.v25
        fatjets['in_v3'] = IN.v3
        fatjets['in_v3_ddt'] = IN.v3 - shift(
            fatjets, algo='inddt', year=self._year)
        fatjets['in_v3_ddt_90pctl'] = IN.v3 - shift(
            fatjets, algo='inddt90pctl', year=self._year)
        fatjets['n2ddt'] = fatjets.n2b1 - n2ddt_shift(fatjets, year=self._year)

        fatjets["genMatchFull"] = genmatch(events, dataset)
        #else: fatjets["genMatchFull"] = fatjets.pt.zeros_like()  #np.zeros(events.size, dtype='bool')

        candidatejet = fatjets[:, :1]
        candidatemuon = events.Muon[:, :5]

        # run model on PFCands associated to FatJet (FatJetPFCands)
        #events.FatJet.array.content["PFCands"] = type(events.FatJetPFCands.array).fromcounts(events.FatJet.nPFConstituents.flatten(), events.FatJetPFCands.flatten())
        #events.FatJet.array.content["twoProngGru"] = run_model(events.FatJet.flatten())

        selection.add('pt', (candidatejet.pt > 525).any())
        selection.add('msdcorr', (candidatejet.msdcorr > 40).any())
        # basic jet selection
        goodjet_sel = ((candidatejet.pt > 525)
                       & (abs(candidatejet.eta) < 2.5)
                       & (candidatejet.msoftdrop > 40.)
                       & (candidatejet.rhocorr > -5.5)
                       & (candidatejet.rhocorr < -2)
                       &
                       (candidatejet.genMatchFull if
                        ('WJetsToQQ' in dataset or 'ZJetsToQQ' in dataset) else
                        (1 == 1))).any()

        vselection_goodjet_sel = ((candidatejet.pt > 200)
                                  & (abs(candidatejet.eta) < 2.5)
                                  & (candidatejet.msoftdrop > 40.)).any()
        #& (candidatejet.genMatchFull if ('TTTo' in dataset) else (1==1))).any()
        #& (candidatejet.rhocorr > -5.5)
        #& (candidatejet.rhocorr < -2)).any()

        selection.add('vselection_jetkin', vselection_goodjet_sel)

        #goodmuon sel for muon CR (lep vetos below)
        goodmuon_sel = ((candidatemuon.pt > 55)
                        & (abs(candidatemuon.eta) < 2.1)
                        & (candidatemuon.looseId).astype(bool)
                        & (candidatemuon.pfRelIso04_all < 0.15)).any()
        vselection_goodmuon_sel = ((candidatemuon.pt > 53)
                                   & (abs(candidatemuon.eta) < 2.1)
                                   & (candidatemuon.tightId).astype(bool))

        #& (candidatemuon.pfRelIso04_all < 0.15))

        vselection_goodmuon_sel_loose = ((candidatemuon.pt > 20)
                                         & (candidatemuon.looseId).astype(bool)
                                         & (abs(candidatemuon.eta) < 2.4))

        selection.add('vselection_muonkin', vselection_goodmuon_sel.any())
        selection.add('vselection_onetightmuon',
                      vselection_goodmuon_sel.sum() == 1)
        selection.add('vselection_oneloosemuon',
                      vselection_goodmuon_sel_loose.sum() == 1)

        candidatemuon = candidatemuon[:, 0:1]

        selection.add('muonkin', goodmuon_sel)
        selection.add('jetkin', goodjet_sel)

        selection.add('n2ddt', (candidatejet.n2ddt < 0.).any())
        selection.add('jetid', candidatejet.isTight.any())
        selection.add('met', events.MET.pt > 40.)

        muon_ak8_pair = candidatemuon.cross(candidatejet, nested=True)

        selection.add('muonDphiAK8',
                      (abs(muon_ak8_pair.i0.delta_phi(muon_ak8_pair.i1)) >
                       2 * np.pi / 3).all().all())
        selection.add('vselection_muonDphiAK8', (abs(
            muon_ak8_pair.i0.delta_phi(muon_ak8_pair.i1)) > 1).all().all())

        #ak4 puppi jet for CR
        jets = events.Jet[((events.Jet.pt > 50.)
                           & (abs(events.Jet.eta) < 2.5))][:, :10]

        # only consider first 4 jets to be consistent with old framework
        ak4_ak8_pair = jets.cross(candidatejet, nested=True)
        dr = abs(ak4_ak8_pair.i0.delta_r(ak4_ak8_pair.i1))
        dphi = abs(ak4_ak8_pair.i0.delta_phi(ak4_ak8_pair.i1))

        ak4_away = jets[(dr > 0.8).all()]
        selection.add('ak4btagMedium08', ak4_away.btagCSVV2.max() > 0.8838)
        ak4_opposite = jets[(dphi > np.pi / 2).all()]
        selection.add('antiak4btagMediumOppHem',
                      ak4_opposite.btagCSVV2.max() < 0.8838)

        mu_p4 = TLorentzVectorArray.from_ptetaphim(
            candidatemuon.pt.fillna(0), candidatemuon.eta.fillna(0),
            candidatemuon.phi.fillna(0), candidatemuon.mass.fillna(0))
        met_p4 = TLorentzVectorArray.from_ptetaphim(
            awkward.JaggedArray.fromiter([[v] for v in events.MET.pt]),
            awkward.JaggedArray.fromiter([[v] for v in np.zeros(events.size)]),
            awkward.JaggedArray.fromiter([[v] for v in events.MET.phi]),
            awkward.JaggedArray.fromiter([[v] for v in np.zeros(events.size)]))

        met_candidatemuon_pair = met_p4.cross(mu_p4)

        Wleptoniccandidate = met_candidatemuon_pair.i0 + met_candidatemuon_pair.i1

        selection.add('Wleptonic_candidate',
                      (Wleptoniccandidate.pt > 200).any())

        vselection_jets = events.Jet[((events.Jet.pt > 30.)
                                      & (abs(events.Jet.eta) < 2.4))]

        vselection_ak4_ak8_pair = vselection_jets.cross(candidatejet,
                                                        nested=True)
        muon_ak4_pair = vselection_jets.cross(candidatemuon, nested=True)
        dr_ak8 = abs(
            vselection_ak4_ak8_pair.i0.delta_r(vselection_ak4_ak8_pair.i1))
        dr_muon = abs(muon_ak4_pair.i0.delta_r(muon_ak4_pair.i1))
        ak4_away = vselection_jets[(dr_ak8 > 0.8).all()]
        selection.add('vselection_ak4btagMedium08',
                      ak4_away.btagCSVV2.max() > 0.8838)

        ak4_away = vselection_jets[(dr_muon > 0.3).all()]

        selection.add('vselection_muonDphiAK4',
                      ak4_away.btagCSVV2.max() > 0.8838)

        nelectrons = ((
            (events.Electron.pt > 10.)
            & (abs(events.Electron.eta) < 2.5)
            #& (events.Electron.cutBased >= events.Electron.LOOSE))
            #& (events.Electron.cutBased_Fall17_V1 >= 1))
            & (events.Electron.cutBased >= 2))).sum()
        nmuons = (((events.Muon.pt > 10)
                   & (abs(events.Muon.eta) < 2.1)
                   #& (events.Muon.pfRelIso04_all < 0.4)
                   & (events.Muon.looseId).astype(bool))).sum()

        ntaus = (((events.Tau.pt > 20.)
                  #& (events.Tau.idMVAnewDM2017v2 >=4))
                  & (events.Tau.idDecayMode).astype(bool)
                  & (events.Tau.rawIso < 5)
                  & (abs(events.Tau.eta) < 2.3))).sum()
        selection.add('noleptons',
                      (nmuons == 0) & (nelectrons == 0) & (ntaus == 0))
        selection.add('noelectron_notau', (nelectrons == 0) & (ntaus == 0))
        #weights.add('metfilter', events.Flag.METFilters)
        if isRealData:
            genflavor = candidatejet.pt.zeros_like().pad(
                1, clip=True).fillna(-1).flatten()
        if not isRealData:
            weights.add('genweight', events.genWeight)
            add_pileup_weight(weights, events.Pileup.nPU, self._year, dataset)
            #add_jetTriggerWeight(weights, candidatejet.msdcorr, candidatejet.pt, self._year) #signal region only
            #add_singleMuTriggerWeight(weights, abs(candidatemuon.eta), candidatemuon.pt, self._year)
            bosons = getBosons(events)
            genBosonPt = bosons.pt.pad(1, clip=True).fillna(0)
            add_VJets_NLOkFactor(weights, genBosonPt, self._year, dataset)
            genflavor = matchedBosonFlavor(candidatejet, bosons).pad(
                1, clip=True).fillna(-1).flatten()

            #b-tag weights
        regions = {
            'signal': [
                'fatjet_trigger',
                'jetkin',
                'noleptons',
                'jetid',
                'antiak4btagMediumOppHem',
            ],
            'ttbar_muoncontrol': [
                'muon_trigger',
                'pt',
                'msdcorr',
                'jetid',
                'jetkin',
                'muonkin',
                'muonDphiAK8',
                'ak4btagMedium08',
                'noelectron_notau',
            ],
            'vselection': [
                'muon_trigger', 'vselection_jetkin', 'vselection_muonkin',
                'vselection_onetightmuon', 'vselection_oneloosemuon',
                'vselection_muonDphiAK8', 'vselection_ak4btagMedium08',
                'vselection_muonDphiAK4', 'Wleptonic_candidate', 'met'
            ],
            'noselection':
            [],  #'vselection_muoncontrol' : ['muon_trigger', 'v_selection_jetkin', 'genmatch', 'jetid', 'ak4btagMedium08', 'muonkin','met'],
        }
        allcuts_signal = set()
        output['cutflow_signal'][dataset]['none'] += float(
            weights.weight().sum())
        allcuts_ttbar_muoncontrol = set()
        output['cutflow_ttbar_muoncontrol'][dataset]['none'] += float(
            weights.weight().sum())
        allcuts_vselection = set()
        output['cutflow_vselection'][dataset]['none'] += float(
            weights.weight().sum())

        for cut in regions['signal']:
            allcuts_signal.add(cut)
            output['cutflow_signal'][dataset][cut] += float(
                weights.weight()[selection.all(*allcuts_signal)].sum())

        for cut in regions['ttbar_muoncontrol']:
            allcuts_ttbar_muoncontrol.add(cut)
            output['cutflow_ttbar_muoncontrol'][dataset][cut] += float(
                weights.weight()[selection.all(
                    *allcuts_ttbar_muoncontrol)].sum())

        for cut in regions['vselection']:
            allcuts_vselection.add(cut)
            output['cutflow_vselection'][dataset][cut] += float(
                weights.weight()[selection.all(*allcuts_vselection)].sum())

        def normalize(val, cut):
            return val[cut].pad(1, clip=True).fillna(0).flatten()

        def fill(region, systematic=None, wmod=None):
            print('filling %s' % region)
            selections = regions[region]
            cut = selection.all(*selections)
            weight = weights.weight()[cut]
            output['templates'].fill(
                dataset=dataset,
                region=region,
                pt=normalize(candidatejet.pt, cut),
                msd=normalize(candidatejet.msdcorr, cut),
                n2ddt=normalize(candidatejet.n2ddt, cut),
                gruddt=normalize(candidatejet.gruddt, cut),
                in_v3_ddt=normalize(candidatejet.in_v3_ddt_90pctl, cut),
                weight=weight,
            ),
            output['event'].fill(
                dataset=dataset,
                region=region,
                MET=events.MET.pt[cut],
                nJet=fatjets.counts[cut],
                nPFConstituents=normalize(candidatejet.nPFConstituents, cut),
                weight=weight,
            ),
            output['muon'].fill(
                dataset=dataset,
                region=region,
                mu_pt=normalize(candidatemuon.pt, cut),
                mu_pfRelIso04_all=normalize(candidatemuon.pfRelIso04_all, cut),
                weight=weight,
            ),
            output['deepAK8'].fill(
                dataset=dataset,
                region=region,
                deepTagMDWqq=normalize(candidatejet.deepTagMDWqq, cut),
                deepTagMDZqq=normalize(candidatejet.deepTagMDZqq, cut),
                msd=normalize(candidatejet.msdcorr, cut),
                genflavor=genflavor[cut],
                weight=weight,
            ),
            output['in_v3'].fill(
                dataset=dataset,
                region=region,
                genflavor=genflavor[cut],
                in_v3=normalize(candidatejet.in_v3, cut),
                n2=normalize(candidatejet.n2b1, cut),
                gru=normalize(candidatejet.gru, cut),
                weight=weight,
            )

        for region in regions:
            fill(region)

        return output
Esempio n. 4
0
    def process(self, df):
        dataset = df.metadata['dataset']
        isRealData = 'genWeight' not in df.columns
        output = self.accumulator.identity()
        selection = processor.PackedSelection()
        output = self.accumulator.identity()

        good = False
        goodMuon = ((df.Muon.pt > 27.) & (np.abs(df.Muon.eta) < 2.4))
        nmuons = goodMuon.sum()

        goodElectron = ((df.Electron.pt > 30.)
                        & (np.abs(df.Electron.eta) < 2.5))
        nelectrons = goodElectron.sum()

        df.FatJet['msdcorr'] = corrected_msoftdrop(df.FatJet)

        goodFatJet = ((df.FatJet.pt > 300.)
                      & (np.abs(df.FatJet.eta) < 2.4)
                      & (df.FatJet.msdcorr > 10.)
                      & (df.FatJet.isTight))
        nfatjets = goodFatJet.sum()

        if self._channel == 'muon':
            good = ((nmuons >= 1) & (nfatjets >= 1))
        else:
            good = ((nelectrons >= 1) & (nfatjets >= 1))
        events = df[good]

        if not isRealData:
            output['sumw'][dataset] += events.genWeight.sum()

        # trigger
        trigger = np.zeros(df.size, dtype='bool')
        for t in self._triggers[self._year + '_' + self._trigger]:
            try:
                trigger = trigger | df.HLT[t]
            except:
                warnings.warn("Missing trigger %s" % t, RuntimeWarning)
        selection.add('trigger', trigger[good])

        # Muons
        candidatemuon = events.Muon[:, 0:1]
        nmuons = events.Muon.counts

        # Electrons
        candidateelectron = events.Electron[:, 0:1]
        nelectrons = events.Electron.counts

        if self._channel == 'muon':
            candidatelep = candidatemuon
            selection.add('nootherlepton', (nelectrons == 0))
        else:
            candidatelep = candidateelectron
            selection.add('nootherlepton', (nmuons == 0))

        selection.add('iplepton', ((np.abs(candidatelep.dz) < 0.1)
                                   & (np.abs(candidatelep.dxy) < 0.05)).any())

        # FatJets
        ak8_lep_pair = candidatelep.cross(events.FatJet)
        ak8_lep_dR = ak8_lep_pair.i0.delta_r(ak8_lep_pair.i1)

        candidatejet = events.FatJet[ak8_lep_dR.argmin()]
        leadingjet = events.FatJet[:, 0:1]

        ak8_lep_dR_closest = candidatelep.delta_r(candidatejet)

        selection.add('jetkin', (candidatejet.pt > self._fjetptMIN).any())
        selection.add('jetmsd', (candidatejet.msdcorr > 20).any())
        selection.add('LSF3medium', (candidatejet.lsf3 > 0.7).any())
        selection.add('LSF3tight', (candidatejet.lsf3 > 0.78).any())
        selection.add('lepnearjet', (ak8_lep_dR.min() < 1.5))
        selection.add('lepinjet', (ak8_lep_dR.min() < 0.8))

        # FatJet substracted Lepton
        # sj1_sj2_btagDeepB_pair = candidatejet.LSsubJet1btagDeepB.cross(candidatejet.LSsubJet2btagDeepB)
        # fls_btagDeepB_max = max(sj1_sj2_btagDeepB_pair.i0,sj1_sj2_btagDeepB_pair.i1)

        # Jets
        jets = events.Jet[(events.Jet.pt > 30.)
                          & (abs(events.Jet.eta) < 2.5)
                          & (events.Jet.isTight)]
        ak4_ak8_pair = jets.cross(candidatejet, nested=True)
        ak4_ak8_dphi = abs(ak4_ak8_pair.i0.delta_phi(ak4_ak8_pair.i1))
        ak4_opposite = jets[(ak4_ak8_dphi > np.pi / 2).all()]
        ak4_away = jets[(ak4_ak8_dphi > 0.8).all()]

        selection.add(
            'antiak4btagMediumOppHem',
            ak4_opposite.btagDeepB.max() < self._btagWPs['med'][self._year])
        selection.add(
            'ak4btagMedium08',
            ak4_away.btagDeepB.max() < self._btagWPs['med'][self._year])

        # MET
        met = events.MET

        # MET eta with mass assumption
        mm = (candidatejet - candidatelep).mass2
        jmass = (mm > 0) * np.sqrt(np.maximum(
            0, mm)) + (mm < 0) * candidatejet.mass

        joffshell = jmass < 62.5
        massassumption = 80. * joffshell + (125 - 80.) * ~joffshell
        x = massassumption**2 / (2 * candidatelep.pt *
                                 met.pt) + np.cos(candidatelep.phi - met.phi)
        met_eta = ((x < 1) * np.arcsinh(x * np.sinh(candidatelep.eta)) +
                   (x > 1) *
                   (candidatelep.eta -
                    np.sign(candidatelep.eta) * np.arccosh(candidatelep.eta)))

        met_p4 = TLorentzVectorArray.from_ptetaphim(np.array([0.]),
                                                    np.array([0.]),
                                                    np.array([0.]),
                                                    np.array([0.]))
        if met.size > 0:
            met_p4 = TLorentzVectorArray.from_ptetaphim(
                met.pt, met_eta.fillna(0.), met.phi, np.zeros(met.size))
            hmass = (candidatejet + met_p4).mass
        else:
            hmass = candidatejet.pt.zeros_like()

        # weights
        weights = processor.Weights(len(events), storeIndividual=True)
        if isRealData:
            genflavor = candidatejet.pt.zeros_like()
        else:
            try:
                weights.add('genweight', events.genWeight)
                add_pileup_weight(weights, events.Pileup.nPU, self._year)
                #print("Weight statistics: %r" % weights._weightStats)
            except:
                print('no gen weight')
            if 'TTTo' in dataset:
                genW, genW_idx = getParticles(
                    events, 24, ['fromHardProcess', 'isLastCopy'])
                genb, genb_idx = getParticles(
                    events, 5, ['fromHardProcess', 'isLastCopy'])
                genflavorW = matchedParticleFlavor(candidatelep, genW, 'child',
                                                   0.4)
                genflavorb = matchedParticleFlavor(candidatelep, genb, 'mom',
                                                   0.4)
                genflavor = getFlavor(genflavorW, genflavorb)
            elif (('hww_2017' in dataset) or ('GluGluHToWW' in dataset)):
                genH, genH_idx = getParticles(
                    events, 25, ['fromHardProcess', 'isLastCopy'])
                genW, genW_idx = getParticles(
                    events, 24, ['fromHardProcess', 'isLastCopy'])
                genE, genE_idx = getParticles(
                    events, 11, ['fromHardProcess', 'isFirstCopy'], 1)
                genM, genM_idx = getParticles(
                    events, 13, ['fromHardProcess', 'isFirstCopy'], 1)
                genT, genT_idx = getParticles(
                    events, 15, ['fromHardProcess', 'isFirstCopy'], 1)
                genQ, genQ_idx = getParticles(
                    events, [0, 5], ['fromHardProcess', 'isFirstCopy'])
                ishWW_qqelev = (genH.counts == 1) & (genW.counts == 2) & (
                    genE.counts == 1) & (genM.counts == 0) & (genT.counts == 0)
                ishWW_qqmuv = (genH.counts == 1) & (genW.counts == 2) & (
                    genM.counts == 1) & (genE.counts == 0) & (genT.counts == 0)
                ishWW_qqtauv = (genH.counts == 1) & (genW.counts == 2) & (
                    genT.counts == 1) & (genM.counts == 0) & (genE.counts == 0)
                ishWW_qqqq = (genH.counts == 1) & (genW.counts == 2) & (
                    genQ.counts == 4) & (genM.counts == 0) & (genE.counts == 0)
                ishWW_muvelev = (genH.counts == 1) & (genW.counts == 2) & (
                    genE.counts == 1) & (genM.counts == 1)
                ishWW_elevelev = (genH.counts == 1) & (genW.counts == 2) & (
                    genE.counts == 2) & (genM.counts == 0)
                ishWW_tauvtauv = (genH.counts == 1) & (genW.counts == 2) & (
                    genT.counts == 2) & (genM.counts == 0) & (genE.counts == 0)
                ishWW_muvmuv = (genH.counts == 1) & (genW.counts == 2) & (
                    genE.counts == 0) & (genM.counts == 2)
                genflavor = ((ishWW_qqelev) * 8 + (ishWW_qqmuv) * 9)
            else:
                genflavor = candidatejet.pt.zeros_like()

        # fill cutflow
        cutflow = [
            'trigger', 'jetkin', 'jetmsd', 'lepnearjet', 'lepinjet',
            'antiak4btagMediumOppHem', 'nootherlepton', 'iplepton',
            'LSF3medium', 'LSF3tight'
        ]
        allcuts = set()
        output['cutflow']['none'] += len(events)
        for cut in cutflow:
            allcuts.add(cut)
            output['cutflow'][cut] += selection.all(*allcuts).sum()

        regions = {}
        regions['presel'] = {'trigger', 'jetkin', 'jetmsd', 'lepinjet'}
        regions['antibtag'] = {
            'trigger', 'jetkin', 'jetmsd', 'antiak4btagMediumOppHem'
        }
        regions['noinjet'] = {
            'trigger', 'jetkin', 'jetmsd', 'lepnearjet',
            'antiak4btagMediumOppHem'
        }
        regions['nolsf'] = {
            'trigger', 'jetkin', 'jetmsd', 'lepinjet',
            'antiak4btagMediumOppHem'
        }  #,'nootherlepton'}
        regions['lsf'] = {
            'trigger', 'jetkin', 'jetmsd', 'lepinjet', 'LSF3tight'
        }
        regions['bopp'] = {
            'trigger', 'jetkin', 'jetmsd', 'lepinjet', 'LSF3tight',
            'antiak4btagMediumOppHem'
        }
        regions['lep'] = {
            'trigger', 'jetkin', 'jetmsd', 'lepinjet', 'LSF3tight',
            'antiak4btagMediumOppHem', 'nootherlepton', 'iplepton'
        }

        for region in self._regions:
            selections = regions[region]
            cut = selection.all(*selections)
            weight = weights.weight()[cut]

            def normalize(val):
                try:
                    return val[cut].pad(1, clip=True).fillna(0).flatten()
                except:
                    try:
                        return val[cut].flatten()
                    except:
                        return val[cut]

            # output['%s_fjetprop'%region].fill(#fjet_pt = normalize(candidatejet.pt),
            #                                   fjet_msd = normalize(candidatejet.msdcorr),
            #                                   fjet_lsf3 = normalize(candidatejet.lsf3),
            #                                   #jet_oppbtag = normalize(ak4_opposite.btagDeepB.max()),
            #                                   genflavor = normalize(genflavor),
            #                                   dataset=dataset,
            #                                   weight=weight
            # )
            # output['%s_fjetextraprop'%region].fill(fjet_t41 = normalize(candidatejet.tau4/candidatejet.tau1),
            #                                        fjet_t42 = normalize(candidatejet.tau4/candidatejet.tau2),
            #                                        fjet_t31 = normalize(candidatejet.tau3/candidatejet.tau1),
            #                                        dataset=dataset,
            #                                        weight=weight
            #                                    )
            # output['%s_jetprop'%region].fill(jet_oppbtag = normalize(ak4_opposite.btagDeepB.max()),
            #                                  genflavor = normalize(genflavor),
            #                                  dataset=dataset,
            #                                  weight=weight
            #                                 )
            output['%s_fmmjetprop' % region].fill(
                fjet_pt=normalize(candidatejet.pt),
                #fjet_mmass = normalize(jmass),
                #fjet_hmass = normalize(hmass),
                lep_pt=normalize(candidatelep.pt),
                fjet_lsf3=normalize(candidatejet.lsf3),
                genflavor=normalize(genflavor),
                dataset=dataset,
                weight=weight)
            output['%s_fmmjetprop2' % region].fill(
                fjet_mmass=normalize(jmass),
                fjet_lsf3=normalize(candidatejet.lsf3),
                genflavor=normalize(genflavor),
                dataset=dataset,
                weight=weight)
            # output['%s_flsjetprop'%region].fill(#flsjet_pt = normalize(candidatejet.LSpt),
            #                                     flsjet_msd = normalize(candidatejet.LSmsoftdrop),
            #                                     #flsjet_n2b1 = normalize(candidatejet.LSn2b1),
            #                                     #flsjet_n3b1 = normalize(candidatejet.LSn3b1),
            #                                     #flsjet_t21 = normalize(candidatejet.LStau2/candidatejet.LStau1),
            #                                     #flsjet_t32 = normalize(candidatejet.LStau3/candidatejet.LStau2),
            #                                     genflavor = normalize(genflavor),
            #                                     dataset=dataset,
            #                                     weight=weight)
            #output['%s_metprop'%region].fill(met_pt = normalize(met.pt),
            #                                 met_phi = normalize(met.phi),
            #                                 dataset=dataset,
            #                                 weight=weight)
            # output['%s_weight'%region].fill(puweight=weights.partial_weight(include=["pileup_weight"])[cut],
            #                                 genweight=weights.partial_weight(include=["genweight"])[cut],
            #                                 dataset=dataset,
            #                                 )
            # if self._channel=='muon':
            #     output['%s_muonprop'%region].fill(muon_pt = normalize(candidatemuon.pt),
            #                                       muon_miso = normalize(candidatemuon.miniPFRelIso_all),
            #                                       muon_sip = normalize(candidatemuon.sip3d),
            #                                       dataset=dataset,
            #                                       weight=weight)
            #     output['%s_muonextraprop'%region].fill(nmuons = normalize(nmuons),
            #                                            nelectrons = normalize(nelectrons),
            #                                            muon_dz = normalize(candidatemuon.dz),
            #                                            muon_dxy = normalize(candidatemuon.dxy),
            #                                            dataset=dataset,
            #                                            weight=weight)

            # else:
            #     output['%s_electronprop'%region].fill(electron_pt = normalize(candidateelectron.pt),
            #                                           electron_miso = normalize(candidateelectron.miniPFRelIso_all),
            #                                           electron_sip = normalize(candidateelectron.sip3d),
            #                                           dataset=dataset,
            #                                           weight=weight)
            #     output['%s_electronextraprop'%region].fill(nmuons = normalize(nmuons),
            #                                                nelectrons = normalize(nelectrons),
            #                                                electron_dz = normalize(candidateelectron.dz),
            #                                                electron_dxy = normalize(candidateelectron.dxy),
            #                                                dataset=dataset,
            #                                                weight=weight)

        return output
Esempio n. 5
0
    def process(self, events):
        dataset = events.metadata['dataset']
        isRealData = 'genWeight' not in events.columns
        selection = processor.PackedSelection()
        weights = processor.Weights(len(events))
        output = self.accumulator.identity()
        if not isRealData:
            output['sumw'][dataset] += events.genWeight.sum()

        if isRealData:
            trigger = np.zeros(events.size, dtype='bool')
            for t in self._triggers[self._year]:
                trigger = trigger | events.HLT[t]
        else:
            trigger = np.ones(events.size, dtype='bool')
        selection.add('trigger', trigger)

        if isRealData:
            trigger = np.zeros(events.size, dtype='bool')
            for t in self._muontriggers[self._year]:
                trigger = trigger | events.HLT[t]
        else:
            trigger = np.ones(events.size, dtype='bool')
        selection.add('muontrigger', trigger)

        try:
            fatjets = events.FatJet
        except AttributeError:
            # early pancakes
            fatjets = events.CustomAK8Puppi
        fatjets['msdcorr'] = corrected_msoftdrop(fatjets)
        fatjets['rho'] = 2 * np.log(fatjets.msdcorr / fatjets.pt)
        fatjets['n2ddt'] = fatjets.n2b1 - n2ddt_shift(fatjets, year=self._year)
        fatjets['msdcorr_full'] = fatjets['msdcorr'] * self._msdSF[self._year]

        candidatejet = fatjets[
            # https://github.com/DAZSLE/BaconAnalyzer/blob/master/Analyzer/src/VJetLoader.cc#L269
            (fatjets.pt > 200)
            & (abs(fatjets.eta) < 2.5)
            # & fatjets.isLoose  # not always available
        ][:, 0:1]
        selection.add('minjetkin', (
            (candidatejet.pt >= 450)
            & (candidatejet.msdcorr >= 47.)
            & (abs(candidatejet.eta) < 2.5)
        ).any())
        selection.add('jetacceptance', (
            (candidatejet.msdcorr >= 47.)
            & (candidatejet.pt < 1200)
            & (candidatejet.msdcorr < 201.)
        ).any())
        selection.add('jetid', candidatejet.isTight.any())
        selection.add('n2ddt', (candidatejet.n2ddt < 0.).any())
        selection.add('ddbpass', (candidatejet.btagDDBvL >= 0.89).any())

        jets = events.Jet[
            (events.Jet.pt > 30.)
            & (abs(events.Jet.eta) < 2.5)
            & events.Jet.isTight
        ]
        # only consider first 4 jets to be consistent with old framework
        jets = jets[:, :4]
        ak4_ak8_pair = jets.cross(candidatejet, nested=True)
        dphi = abs(ak4_ak8_pair.i0.delta_phi(ak4_ak8_pair.i1))
        ak4_opposite = jets[(dphi > np.pi / 2).all()]
        selection.add('antiak4btagMediumOppHem', ak4_opposite.btagDeepB.max() < BTagEfficiency.btagWPs[self._year]['medium'])
        ak4_away = jets[(dphi > 0.8).all()]
        selection.add('ak4btagMedium08', ak4_away.btagDeepB.max() > BTagEfficiency.btagWPs[self._year]['medium'])

        selection.add('met', events.MET.pt < 140.)

        goodmuon = (
            (events.Muon.pt > 10)
            & (abs(events.Muon.eta) < 2.4)
            & (events.Muon.pfRelIso04_all < 0.25)
            & (events.Muon.looseId).astype(bool)
        )
        nmuons = goodmuon.sum()
        leadingmuon = events.Muon[goodmuon][:, 0:1]
        muon_ak8_pair = leadingmuon.cross(candidatejet, nested=True)

        nelectrons = (
            (events.Electron.pt > 10)
            & (abs(events.Electron.eta) < 2.5)
            & (events.Electron.cutBased >= events.Electron.LOOSE)
        ).sum()

        ntaus = (
            (events.Tau.pt > 20)
            & (events.Tau.idDecayMode).astype(bool)
            # bacon iso looser than Nano selection
        ).sum()

        selection.add('noleptons', (nmuons == 0) & (nelectrons == 0) & (ntaus == 0))
        selection.add('onemuon', (nmuons == 1) & (nelectrons == 0) & (ntaus == 0))
        selection.add('muonkin', (
            (leadingmuon.pt > 55.)
            & (abs(leadingmuon.eta) < 2.1)
        ).all())
        selection.add('muonDphiAK8', (
            abs(muon_ak8_pair.i0.delta_phi(muon_ak8_pair.i1)) > 2*np.pi/3
        ).all().all())

        if isRealData:
            genflavor = candidatejet.pt.zeros_like()
        else:
            weights.add('genweight', events.genWeight)
            add_pileup_weight(weights, events.Pileup.nPU, self._year, dataset)
            bosons = getBosons(events)
            genBosonPt = bosons.pt.pad(1, clip=True).fillna(0)
            add_VJets_NLOkFactor(weights, genBosonPt, self._year, dataset)
            genflavor = matchedBosonFlavor(candidatejet, bosons).pad(1, clip=True).fillna(-1).flatten()
            add_jetTriggerWeight(weights, candidatejet.msdcorr, candidatejet.pt, self._year)
            output['btagWeight'].fill(dataset=dataset, val=self._btagSF.addBtagWeight(weights, ak4_away))
            logger.debug("Weight statistics: %r" % weights._weightStats)

        msd_matched = candidatejet.msdcorr * self._msdSF[self._year] * (genflavor > 0) + candidatejet.msdcorr * (genflavor == 0)

        regions = {
            'signal': ['trigger','minjetkin',],#'noleptons','jetacceptance', 'noleptons','jetid',],#'jetid', 'noleptons',],# 'n2ddt','antiak4btagMediumOppHem'],#, 'met',],
            'muoncontrol': ['muontrigger','minjetkin', 'jetid','muonDphiAK8','muonkin', 'ak4btagMedium08', 'onemuon',],# 'muonkin', 'muonDphiAK8'],
            'noselection': [],
        }

        for region, cuts in regions.items():
            allcuts = set()
            logger.debug(f"Filling cutflow with: {dataset}, {region}, {genflavor}, {weights.weight()}")
            #output['cutflow'].fill(dataset=dataset, region=region, genflavor=genflavor, cut=0, weight=weights.weight())
            #for i, cut in enumerate(cuts + ['ddbpass']):
            #    allcuts.add(cut)
            #    cut = selection.all(*allcuts)
            #    output['cutflow'].fill(dataset=dataset, region=region, genflavor=genflavor[cut], cut=i + 1, weight=weights.weight()[cut])

        systematics = [
            None,
            'jet_triggerUp',
            'jet_triggerDown',
            'btagWeightUp',
            'btagWeightDown',
            'btagEffStatUp',
            'btagEffStatDown',
        ]

        def normalize(val, cut):
            return val[cut].pad(1, clip=True).fillna(0).flatten()

        def fill(region, systematic=None, wmod=None):
            selections = regions[region]
            cut = selection.all(*selections)
            sname = 'nominal' if systematic is None else systematic
            if wmod is None:
                weight = weights.weight(modifier=systematic)[cut]
            else:
                weight = weights.weight()[cut] * wmod[cut]

            output['templates'].fill(
                dataset=dataset,
                region=region,
                #systematic=sname,
                #genflavor=genflavor[cut],
                pt=normalize(candidatejet.pt, cut),
                msd=normalize(msd_matched, cut),
                #ddb=normalize(candidatejet.btagDDBvL, cut),
                weight=weight,
            )
            if wmod is not None:
                output['genresponse_noweight'].fill(
                    dataset=dataset,
                    region=region,
                    systematic=sname,
                    pt=normalize(candidatejet.pt, cut),
                    genpt=normalize(genBosonPt, cut),
                    weight=events.genWeight[cut] * wmod[cut],
                )
                output['genresponse'].fill(
                    dataset=dataset,
                    region=region,
                    systematic=sname,
                    pt=normalize(candidatejet.pt, cut),
                    genpt=normalize(genBosonPt, cut),
                    weight=weight,
                )

        for region in regions:
            cut = selection.all(*(set(regions[region]) - {'n2ddt'}))
            output['nminus1_n2ddt'].fill(
                dataset=dataset,
                region=region,
                n2ddt=normalize(candidatejet.n2ddt, cut),
                weight=weights.weight()[cut],
            )
            #for systematic in systematics:
            fill(region)#, systematic)
            if 'GluGluHToBB' in dataset:
                for i in range(9):
                    fill(region, 'LHEScale_%d' % i, events.LHEScaleWeight[:, i])
                for c in events.LHEWeight.columns[1:]:
                    fill(region, 'LHEWeight_%s' % c, events.LHEWeight[c])

        return output
Esempio n. 6
0
    def process(self, events):
        dataset = events.metadata['dataset']
        isRealData = not hasattr(events, "genWeight")
        selection = PackedSelection()
        weights = Weights(len(events))
        output = self.accumulator.identity()
        if not isRealData:
            output['sumw'][dataset] += ak.sum(events.genWeight)

        if isRealData:
            trigger = np.zeros(len(events), dtype='bool')
            for t in self._triggers[self._year]:
                trigger = trigger | events.HLT[t]
        else:
            trigger = np.ones(len(events), dtype='bool')
        selection.add('trigger', trigger)

        if isRealData:
            trigger = np.zeros(len(events), dtype='bool')
            for t in self._muontriggers[self._year]:
                trigger = trigger | events.HLT[t]
        else:
            trigger = np.ones(len(events), dtype='bool')
        selection.add('muontrigger', trigger)

        fatjets = events.FatJet
        fatjets['msdcorr'] = corrected_msoftdrop(fatjets)
        fatjets['qcdrho'] = 2 * np.log(fatjets.msdcorr / fatjets.pt)
        fatjets['n2ddt'] = fatjets.n2b1 - n2ddt_shift(fatjets, year=self._year)
        fatjets['msdcorr_full'] = fatjets['msdcorr'] * self._msdSF[self._year]

        candidatejet = fatjets[
            # https://github.com/DAZSLE/BaconAnalyzer/blob/master/Analyzer/src/VJetLoader.cc#L269
            (fatjets.pt > 200)
            & (abs(fatjets.eta) < 2.5)
            & fatjets.isTight  # this is loose in sampleContainer
        ]
        if self._jet_arbitration == 'pt':
            candidatejet = ak.firsts(candidatejet)
        elif self._jet_arbitration == 'mass':
            candidatejet = candidatejet[ak.argmax(candidatejet.msdcorr)]
        elif self._jet_arbitration == 'n2':
            candidatejet = candidatejet[ak.argmin(candidatejet.n2ddt)]
        elif self._jet_arbitration == 'ddb':
            candidatejet = candidatejet[ak.argmax(candidatejet.btagDDBvL)]
        else:
            raise RuntimeError("Unknown candidate jet arbitration")

        selection.add('minjetkin', (candidatejet.pt >= 450)
                      & (candidatejet.msdcorr >= 40.)
                      & (abs(candidatejet.eta) < 2.5))
        selection.add('jetacceptance', (candidatejet.msdcorr >= 47.)
                      & (candidatejet.pt < 1200)
                      & (candidatejet.msdcorr < 201.))
        selection.add('jetid', candidatejet.isTight)
        selection.add('n2ddt', (candidatejet.n2ddt < 0.))
        selection.add('ddbpass', (candidatejet.btagDDBvL >= 0.89))

        jets = events.Jet[(events.Jet.pt > 30.)
                          & (abs(events.Jet.eta) < 2.5)
                          & events.Jet.isTight]
        # only consider first 4 jets to be consistent with old framework
        jets = jets[:, :4]
        dphi = abs(jets.delta_phi(candidatejet))
        selection.add(
            'antiak4btagMediumOppHem',
            ak.max(
                jets[dphi > np.pi / 2].btagDeepB, axis=1, mask_identity=False)
            < BTagEfficiency.btagWPs[self._year]['medium'])
        ak4_away = jets[dphi > 0.8]
        selection.add(
            'ak4btagMedium08',
            ak.max(ak4_away.btagDeepB, axis=1, mask_identity=False) >
            BTagEfficiency.btagWPs[self._year]['medium'])

        selection.add('met', events.MET.pt < 140.)

        goodmuon = ((events.Muon.pt > 10)
                    & (abs(events.Muon.eta) < 2.4)
                    & (events.Muon.pfRelIso04_all < 0.25)
                    & events.Muon.looseId)
        nmuons = ak.sum(goodmuon, axis=1)
        leadingmuon = ak.firsts(events.Muon[goodmuon])

        nelectrons = ak.sum(
            (events.Electron.pt > 10)
            & (abs(events.Electron.eta) < 2.5)
            & (events.Electron.cutBased >= events.Electron.LOOSE),
            axis=1,
        )

        ntaus = ak.sum(
            (events.Tau.pt > 20)
            & events.Tau.idDecayMode,  # bacon iso looser than Nano selection
            axis=1,
        )

        selection.add('noleptons',
                      (nmuons == 0) & (nelectrons == 0) & (ntaus == 0))
        selection.add('onemuon',
                      (nmuons == 1) & (nelectrons == 0) & (ntaus == 0))
        selection.add('muonkin',
                      (leadingmuon.pt > 55.) & (abs(leadingmuon.eta) < 2.1))
        selection.add('muonDphiAK8',
                      abs(leadingmuon.delta_phi(candidatejet)) > 2 * np.pi / 3)

        if isRealData:
            genflavor = 0
        else:
            weights.add('genweight', events.genWeight)
            add_pileup_weight(weights, events.Pileup.nPU, self._year, dataset)
            bosons = getBosons(events.GenPart)
            matchedBoson = candidatejet.nearest(bosons,
                                                axis=None,
                                                threshold=0.8)
            genflavor = bosonFlavor(matchedBoson)
            genBosonPt = ak.fill_none(ak.firsts(bosons.pt), 0)
            add_VJets_NLOkFactor(weights, genBosonPt, self._year, dataset)
            add_jetTriggerWeight(weights, candidatejet.msdcorr,
                                 candidatejet.pt, self._year)
            output['btagWeight'].fill(dataset=dataset,
                                      val=self._btagSF.addBtagWeight(
                                          weights, ak4_away))
            logger.debug("Weight statistics: %r" % weights.weightStatistics)

        msd_matched = candidatejet.msdcorr * self._msdSF[self._year] * (
            genflavor > 0) + candidatejet.msdcorr * (genflavor == 0)

        regions = {
            'signal': [
                'trigger', 'minjetkin', 'jetacceptance', 'jetid', 'n2ddt',
                'antiak4btagMediumOppHem', 'met', 'noleptons'
            ],
            'muoncontrol': [
                'muontrigger', 'minjetkin', 'jetacceptance', 'jetid', 'n2ddt',
                'ak4btagMedium08', 'onemuon', 'muonkin', 'muonDphiAK8'
            ],
            'noselection': [],
        }

        for region, cuts in regions.items():
            allcuts = set()
            output['cutflow'].fill(dataset=dataset,
                                   region=region,
                                   genflavor=genflavor,
                                   cut=0,
                                   weight=weights.weight())
            for i, cut in enumerate(cuts + ['ddbpass']):
                allcuts.add(cut)
                cut = selection.all(*allcuts)
                output['cutflow'].fill(dataset=dataset,
                                       region=region,
                                       genflavor=genflavor[cut],
                                       cut=i + 1,
                                       weight=weights.weight()[cut])

        systematics = [
            None,
            'jet_triggerUp',
            'jet_triggerDown',
            'btagWeightUp',
            'btagWeightDown',
            'btagEffStatUp',
            'btagEffStatDown',
        ]

        def normalize(val, cut):
            return ak.to_numpy(ak.fill_none(val[cut], np.nan))

        def fill(region, systematic, wmod=None):
            selections = regions[region]
            cut = selection.all(*selections)
            sname = 'nominal' if systematic is None else systematic
            if wmod is None:
                weight = weights.weight(modifier=systematic)[cut]
            else:
                weight = weights.weight()[cut] * wmod[cut]

            output['templates'].fill(
                dataset=dataset,
                region=region,
                systematic=sname,
                genflavor=genflavor[cut],
                pt=normalize(candidatejet.pt, cut),
                msd=normalize(msd_matched, cut),
                ddb=normalize(candidatejet.btagDDBvL, cut),
                weight=weight,
            )
            if wmod is not None:
                output['genresponse_noweight'].fill(
                    dataset=dataset,
                    region=region,
                    systematic=sname,
                    pt=normalize(candidatejet.pt, cut),
                    genpt=normalize(genBosonPt, cut),
                    weight=events.genWeight[cut] * wmod[cut],
                )
                output['genresponse'].fill(
                    dataset=dataset,
                    region=region,
                    systematic=sname,
                    pt=normalize(candidatejet.pt, cut),
                    genpt=normalize(genBosonPt, cut),
                    weight=weight,
                )

        for region in regions:
            cut = selection.all(*(set(regions[region]) - {'n2ddt'}))
            output['nminus1_n2ddt'].fill(
                dataset=dataset,
                region=region,
                n2ddt=normalize(candidatejet.n2ddt, cut),
                weight=weights.weight()[cut],
            )
            for systematic in systematics:
                fill(region, systematic)
            if 'GluGluHToBB' in dataset:
                for i in range(9):
                    fill(region, 'LHEScale_%d' % i, events.LHEScaleWeight[:,
                                                                          i])
                for c in events.LHEWeight.columns[1:]:
                    fill(region, 'LHEWeight_%s' % c, events.LHEWeight[c])

        output["weightStats"] = weights.weightStatistics
        return output