Example #1
0
def analyze(self, E, PARAMS=None):
    if self.SP is None:
        raise Exception('[ANALYZER ERROR]: This script runs on signal only.')
    if self.TRIGGER:
        if not Selections.passedTrigger(E): return
    if '4Mu' in self.NAME:
        mu11, mu12, mu21, mu22, X1, X2, H, P, extramu = E.getPrimitives('GEN')
        genMuons = (mu11, mu12, mu21, mu22)
        genMuonPairs = ((mu11, mu12), (mu21, mu22))
    elif '2Mu2J' in self.NAME:
        mu1, mu2, j1, j2, X, XP, H, P, extramu = E.getPrimitives('GEN')
        genMuons = (mu1, mu2)
        genMuonPairs = ((mu1, mu2), )
    DSAmuons = E.getPrimitives('DSAMUON')
    Dimuons = E.getPrimitives('DIMUON')

    for genMuon in genMuons:
        #print '{:9.4f} {:7.4f} {:7.4f} : {:9.4f} {:7.4f} {:7.4f}'.format(genMuon.pt, genMuon.eta, genMuon.phi, genMuon.BS.pt, genMuon.BS.eta, genMuon.BS.phi)
        matchesRG = matchedMuons(genMuon, DSAmuons, vertex=None)
        matchesBS = matchedMuons(genMuon, DSAmuons, vertex='BS')
        if len(matchesRG) > 0:
            recoMuon = DSAmuons[matchesRG[0]['idx']]
            for QUANTITY in ('pT', 'd0'):
                PRETTY, AXES, RFUNC, GFUNC, RESFUNC = CONFIG[QUANTITY]
                self.HISTS[QUANTITY + 'ResRG'].Fill(
                    RESFUNC(RFUNC(recoMuon), GFUNC(genMuon, 'LIN')))
            self.HISTS['deltaRRG'].Fill(matchesRG[0]['deltaR'])
            self.HISTS['deltaphiRG'].Fill(recoMuon.p4.DeltaPhi(genMuon.p4))
            self.HISTS['deltaEtaRG'].Fill(recoMuon.eta - genMuon.eta)
        if len(matchesBS) > 0:
            recoMuon = DSAmuons[matchesBS[0]['idx']]
            for QUANTITY in ('pT', 'd0'):
                PRETTY, AXES, RFUNC, GFUNC, RESFUNC = CONFIG[QUANTITY]
                self.HISTS[QUANTITY + 'ResBS'].Fill(
                    RESFUNC(RFUNC(recoMuon), GFUNC(genMuon, 'FULL')))
            self.HISTS['deltaRBS'].Fill(matchesBS[0]['deltaR'])
            self.HISTS['deltaphiBS'].Fill(recoMuon.p4.DeltaPhi(genMuon.BS.p4))
            self.HISTS['deltaEtaBS'].Fill(recoMuon.eta - genMuon.BS.eta)

        if len(matchesRG) > 0 and len(matchesBS) > 0:
            dRRG = matchesRG[0]['deltaR']
            dRBS = matchesBS[0]['deltaR']
            self.HISTS['deltaDeltaR'].Fill(dRRG - dRBS)
        elif len(matchesRG) > 0:
            self.HISTS['deltaDeltaR'].Fill(.4)
        elif len(matchesBS) > 0:
            self.HISTS['deltaDeltaR'].Fill(-.4)
Example #2
0
def getGENmuon(muon, GENmuons):
    if isinstance(muon, Primitives.GenMuon):
        return muon
    else:
        matchedGenMuons = matchedMuons(muon, GENmuons)
        if len(matchedGenMuons) != 0:
            return GENmuons[matchedGenMuons[0]['idx']]
        else:
            return None
def getMuonGenLxy(muon, genMuons):
    try:
        Lxy = muon.Lxy()
    except:
        matchedGenMuons = matchedMuons(muon, genMuons)
        if len(matchedGenMuons) == 0:
            return None
        closestGenMuon = genMuons[matchedGenMuons[0]['idx']]
        Lxy = closestGenMuon.Lxy()
    return Lxy
Example #4
0
def analyze(self, E, PARAMS=None):
    if self.SP is None:
        raise Exception('[ANALYZER ERROR]: This script runs on signal only.')
    if '4Mu' in self.NAME:
        mu11, mu12, mu21, mu22, X1, X2, H, P, extramu = E.getPrimitives('GEN')
        genMuons = (mu11, mu12, mu21, mu22)
        genMuonPairs = ((mu11, mu12), (mu21, mu22))
    elif '2Mu2J' in self.NAME:
        mu1, mu2, j1, j2, X, XP, H, P, extramu = E.getPrimitives('GEN')
        genMuons = (mu1, mu2)
        genMuonPairs = ((mu1, mu2), )
    Event = E.getPrimitives('EVENT')
    DSAmuons = E.getPrimitives('DSAMUON')

    SelectMuons = False
    # require muons to pass all selections
    if SelectMuons:
        DSASelections = [Selections.MuonSelection(muon) for muon in DSAmuons]

        selectedDSAmuons = [
            mu for idx, mu in enumerate(DSAmuons) if DSASelections[idx]
        ]

    # don't require muons to pass all selections
    else:
        selectedDSAmuons = DSAmuons

    # loop over genMuons and count various matching criteria
    for genMuon in genMuons:

        self.COUNTERS['nGenMuons'] += 1

        if Selections.AcceptanceSelection(genMuon):
            self.COUNTERS['nGenAcc'] += 1

        matches = matchedMuons(genMuon, selectedDSAmuons)
        if len(matches) != 0:
            self.COUNTERS['nMatches'] += 1

            if len(matches) > 1:
                self.COUNTERS['nMultiple'] += 1

                if PARAMS.DUMP:
                    dumpInfo(Event, genMuon, selectedDSAmuons, len(matches),
                             extramu, PARAMS)

                if len(matches) not in self.COUNTERS['multiDict']:
                    self.COUNTERS['multiDict'][len(matches)] = 0
                self.COUNTERS['multiDict'][len(matches)] += 1
def analyze(self, E, PARAMS=None):
    if self.SP is None:
        raise Exception('[ANALYZER ERROR]: This script runs on signal only.')
    if '4Mu' in self.NAME:
        mu11, mu12, mu21, mu22, X1, X2, H, P, extramu = E.getPrimitives('GEN')
        genMuons = (mu11, mu12, mu21, mu22)
        genMuonPairs = ((mu11, mu12), (mu21, mu22))
    elif '2Mu2J' in self.NAME:
        mu1, mu2, j1, j2, X, XP, H, P, extramu = E.getPrimitives('GEN')
        genMuons = (mu1, mu2)
        genMuonPairs = ((mu1, mu2), )
    Event = E.getPrimitives('EVENT')
    DSAmuons = E.getPrimitives('DSAMUON')

    SelectMuons = False
    # require muons to pass all selections
    if SelectMuons:
        DSASelections = [Selections.MuonSelection(muon) for muon in DSAmuons]

        selectedDSAmuons = [
            mu for idx, mu in enumerate(DSAmuons) if DSASelections[idx]
        ]

    # don't require muons to pass all selections
    else:
        selectedDSAmuons = DSAmuons

    # loop over genMuons and print if pTRes is poor
    for genMuon in genMuons:

        matches = matchedMuons(genMuon, selectedDSAmuons)
        if len(matches) != 0:
            closestRecoMuon = selectedDSAmuons[matches[0]['idx']]

            if (closestRecoMuon.pt - genMuon.pt) / genMuon.pt < -0.8:
                dumpInfo(Event, genMuon, selectedDSAmuons, len(matches),
                         extramu, PARAMS)
def analyze(self, E, PARAMS=None):
    if self.SP is None:
        raise Exception('[ANALYZER ERROR]: This script runs on signal only.')

    if self.TRIGGER:
        if not Selections.passedTrigger(E): return

    if '4Mu' in self.NAME:
        mu11, mu12, mu21, mu22, X1, X2, H, P, extramu = E.getPrimitives('GEN')
        genMuons = (mu11, mu12, mu21, mu22)
        genMuonPairs = ((mu11, mu12), (mu21, mu22))
    elif '2Mu2J' in self.NAME:
        mu1, mu2, j1, j2, X, XP, H, P, extramu = E.getPrimitives('GEN')
        genMuons = (mu1, mu2)
        genMuonPairs = ((mu1, mu2), )
    Event = E.getPrimitives('EVENT')
    dimuons = E.getPrimitives('DIMUON')
    muons = E.getPrimitives('DSAMUON')

    baseMuons = [
        mu for mu in muons
        if mu.nDTStations + mu.nCSCStations > 1 and mu.nCSCHits +
        mu.nDTHits > 12 and mu.ptError / mu.pt < 1.
    ]
    baseOIndices = [mu.idx for mu in baseMuons]
    baseDimuons = [
        dim for dim in dimuons
        if dim.idx1 in baseOIndices and dim.idx2 in baseOIndices
    ]

    for pTCut in PTCUTS:
        selectedMuons = [mu for mu in baseMuons if mu.pt > pTCut]
        selectedOIndices = [mu.idx for mu in selectedMuons]
        selectedDimuons = [
            dim for dim in baseDimuons
            if dim.idx1 in selectedOIndices and dim.idx2 in selectedOIndices
        ]

        # for a few special pT cut values, fill multiplicity histograms: all, and split by matched or not matched
        if pTCut in MULTCUTS:
            sPTCut = str(pTCut)
            self.HISTS['nMuon_' + sPTCut].Fill(len(selectedMuons))
            self.HISTS['nDimuon_' + sPTCut].Fill(len(selectedDimuons))

            # fill split muon histograms; len(0) goes directly to not matched
            matches = []
            if len(selectedMuons) > 0:
                for genMuonPair in genMuonPairs:
                    for genMuon in genMuonPair:
                        matches.extend(
                            matchedMuons(genMuon, selectedMuons, vertex='BS'))
                matches = list(set([m['oidx'] for m in matches]))
                self.HISTS['nMuon_Matched_' + sPTCut].Fill(len(matches))
                self.HISTS['nMuon_NotMatched_' +
                           sPTCut].Fill(len(selectedMuons) - len(matches))
            else:
                self.HISTS['nMuon_Matched_' + sPTCut].Fill(0)
                self.HISTS['nMuon_NotMatched_' + sPTCut].Fill(0)

            # fill split dimuon histograms; len(0) goes directly to not matched
            matches = []
            if len(selectedDimuons) > 0:
                for genMuonPair in genMuonPairs:
                    dimuonMatches, muonMatches, exitcode = matchedDimuons(
                        genMuonPair, selectedDimuons)
                    for m in dimuonMatches:
                        matches.append(m['idx'])
                matches = list(set(matches))
                self.HISTS['nDimuon_Matched_' + sPTCut].Fill(len(matches))
                self.HISTS['nDimuon_NotMatched_' +
                           sPTCut].Fill(len(selectedDimuons) - len(matches))
            else:
                self.HISTS['nDimuon_Matched_' + sPTCut].Fill(0)
                self.HISTS['nDimuon_NotMatched_' + sPTCut].Fill(0)

        #for i in range(len(selectedMuons  )): self.HISTS['nMuon'  ].Fill(pTCut)
        #for i in range(len(selectedDimuons)): self.HISTS['nDimuon'].Fill(pTCut)
        #for i in range(len(genMuonPairs   )): self.HISTS['nPair'  ].Fill(pTCut)

        if len(selectedDimuons) > 0:
            for genMuonPair in genMuonPairs:
                dimuonMatches, muonMatches, exitcode = matchedDimuons(
                    genMuonPair, selectedDimuons)
                if len(dimuonMatches) > 0:
                    self.HISTS['nMatch'].Fill(pTCut)

                    # best matched dimuon
                    matchedDimuon = dimuonMatches[0]['dim']

                    # fill nCorrectChi2 : chi^2/dof criterion, i.e. dimuon with lowest vertex chi2/dof
                    sortedDimuons = sorted(selectedDimuons,
                                           key=lambda dim: dim.normChi2)
                    bestDimuon = sortedDimuons[0]
                    if bestDimuon.idx1 == matchedDimuon.idx1 and bestDimuon.idx2 == matchedDimuon.idx2:
                        self.HISTS['nCorrectChi2'].Fill(pTCut)

                    # fill nCorrectHPD : pT criterion, i.e. dimuon made of highest 2 pT
                    sortedMuons = sorted(selectedMuons,
                                         key=lambda mu: mu.pt,
                                         reverse=True)
                    bestTwo = (sortedMuons[0].idx, sortedMuons[1].idx)
                    bestDimuon = None
                    for dim in selectedDimuons:
                        if dim.idx1 in bestTwo and dim.idx2 in bestTwo:
                            bestDimuon = dim
                            break
                    if bestDimuon is not None:
                        if bestDimuon.idx1 == matchedDimuon.idx1 and bestDimuon.idx2 == matchedDimuon.idx2:
                            self.HISTS['nCorrectHPD'].Fill(pTCut)

                    # test the 3mu and 4mu criteria on 2mu samples
                    bestDimuons = {}
                    if len(sortedMuons) == 3:
                        self.HISTS['nMatch3'].Fill(pTCut)

                        # I have not overwritten bestDimuon yet. It's still the HPD.
                        if bestDimuon is not None:
                            bestDimuons['HPD3'] = (bestDimuon.ID, )
                        else:
                            bestDimuons['HPD3'] = []
                        # Chi2 among all dimuons
                        bestDimuons['Chi23'] = (sortedDimuons[0].ID, )

                        for tag in ('Chi2', 'HPD'):
                            if matchedDimuon.ID in bestDimuons[tag + '3']:
                                self.HISTS['nCorrect' + tag + '3'].Fill(pTCut)

                    elif len(sortedMuons) >= 4:
                        self.HISTS['nMatch4'].Fill(pTCut)

                        highestPTMuons = sortedMuons[:4]
                        highestIndices = [mu.idx for mu in highestPTMuons]
                        bestDimuons['HPD'] = {
                            d.ID: d
                            for d in selectedDimuons
                            if d.idx1 in highestIndices
                            and d.idx2 in highestIndices
                        }

                        pairings = {}
                        for tag in ('HPD', ):
                            pairings[tag] = []
                            dimuonList = bestDimuons[tag].values()
                            for dim1 in dimuonList:
                                for dim2 in dimuonList:
                                    if dim1.ID == dim2.ID: continue
                                    muonIDs = set(dim1.ID + dim2.ID)
                                    if len(muonIDs) == 4:
                                        pairings[tag].append((dim1, dim2))

                        def AMD(pairing):
                            return abs(pairing[0].mass - pairing[1].mass)

                        def FMD(pairing):
                            return abs(pairing[0].mass - pairing[1].mass) / (
                                pairing[0].mass + pairing[1].mass)

                        def C2S(pairing):
                            return pairing[0].normChi2 + pairing[1].normChi2

                        functions = {'AMD4': AMD, 'C2S4': C2S}

                        # if there are pairings, use sortedPairings[0]
                        for tag in ('HPD', ):
                            if len(pairings[tag]) > 0:

                                for fkey in functions:
                                    sortedPairings = sorted(
                                        pairings[tag], key=functions[fkey])
                                    bestDimuons[fkey] = []
                                    if len(sortedPairings) > 0:
                                        for dim in sortedPairings[0]:
                                            bestDimuons[fkey].append(dim.ID)

                            else:
                                for fkey in functions:
                                    bestDimuons[fkey] = []

                        # I have not overwritten bestDimuon yet. It's still the HPD.
                        if bestDimuon is not None:
                            bestDimuons['HPD4'] = (bestDimuon.ID, )
                        else:
                            bestDimuons['HPD4'] = []
                        # Chi2 among all dimuons
                        bestDimuons['Chi24'] = (sortedDimuons[0].ID, )
                        # Chi2 among HPDs only
                        if len(bestDimuons['HPD']) > 0:
                            bestDimuons['LCD4'] = (sorted(
                                bestDimuons['HPD'].values(),
                                key=lambda dim: dim.normChi2)[0].ID, )
                        else:
                            bestDimuons['LCD4'] = []

                        for tag in ('Chi2', 'HPD', 'LCD', 'AMD', 'C2S'):
                            if matchedDimuon.ID in bestDimuons[tag + '4']:
                                self.HISTS['nCorrect' + tag + '4'].Fill(pTCut)
def analyze(self, E, PARAMS=None):
    if self.SP is None:
        raise RuntimeError(
            '[ANALYZER ERROR]: This script runs on signal only.')
    if '4Mu' in self.NAME:
        mu11, mu12, mu21, mu22, X1, X2, H, P, extramu = E.getPrimitives('GEN')
        GENmuons = (mu11, mu12, mu21, mu22)
    elif '2Mu2J' in self.NAME:
        mu1, mu2, j1, j2, X, XP, H, P, extramu = E.getPrimitives('GEN')
        GENmuons = (mu1, mu2)

    HLTPaths, HLTMuons, L1TMuons = E.getPrimitives('TRIGGER')

    DSAmuons = E.getPrimitives('DSAMUON')
    RSAmuons = E.getPrimitives('RSAMUON')

    # define which muon type the "denominator muons" should be matched with
    matchWith_den = {
        'GEN': None,
        'DSA': GENmuons,
        'RSA': GENmuons,
    }

    # define which muon type the "numerator muons" should be matched with (in
    # addition to the matching of the "denominator muons"
    matchWith_num = {
        'GEN': HLTMuons,
        'DSA': HLTMuons,
        'RSA': HLTMuons,
    }

    for MUONTYPE, MUONS in (('GEN', GENmuons), ('DSA', DSAmuons), ('RSA',
                                                                   RSAmuons)):

        # loop over the entire event and collect the selected muons, for the
        # denominator and the numerator separately
        accepted_muons_den = []
        accepted_muons_num = []
        for muon in MUONS:
            # apply selections
            if not Selections.CUTS['eta'].apply(muon): continue
            if MUONTYPE != 'GEN' and \
                    Selections.CUTS['nStations'].expr(muon) <= 1:
                continue

            # match the current muon if it is supposed to be matched for the
            # denominator
            if matchWith_den[MUONTYPE] is not None:
                muonMatches_den = matchedMuons(muon, matchWith_den[MUONTYPE])
                if len(muonMatches_den) != 0:
                    # collect the matched muon for the denominator
                    accepted_muons_den.append(muon)

                    # match the current muon if it is supposed to be matched for
                    # the numerator
                    if matchWith_num[MUONTYPE] is not None:
                        matchedMuons_den_num = matchedMuons(
                            muon, matchWith_num[MUONTYPE])
                        if len(matchedMuons_den_num) != 0:
                            # collect the matched muon for the numerator
                            accepted_muons_num.append(muon)

                    # just collect the muon if it is not supposed to be matched
                    # for the numerator
                    else:
                        accepted_muons_num.append(muon)

            # just collect the muon if it is not supposed to be matched for the
            # denominator
            else:
                accepted_muons_den.append(muon)

                # match the current muon if it is supposed to be matched for the
                # numerator
                if matchWith_num[MUONTYPE] is not None:
                    matchedMuons_den_num = matchedMuons(
                        muon, matchWith_num[MUONTYPE])
                    if len(matchedMuons_den_num) != 0:
                        # collect the matched muon for the numerator
                        accepted_muons_num.append(muon)

                # just collect the muon if it is not supposed to be matched for
                # the numerator
                else:
                    accepted_muons_num.append(muon)

        # sort collected muons according to their pT (in descending order)
        accepted_muons_den.sort(key=lambda m: m.pt, reverse=True)
        # accepted_muons_num.sort(key=lambda m: m.pt, reverse=True)

        # skip the event if not enough muons pass the selection for the
        # denominator
        if len(accepted_muons_den) < 2: continue

        # select the subleading denominator muon for further processing
        muon_den = accepted_muons_den[1]

        # if existing, select the same muon for the numerator
        muon_num = None
        for m in accepted_muons_num:
            if m == muon_den:
                muon_num = m
            else:
                pass

        # fill the histograms
        for KEY in CONFIG:
            F = CONFIG[KEY]['LAMBDA']
            if MUONTYPE in ['DSA', 'RSA'] and KEY == 'Lxy':
                Lxy = getMuonGenLxy(muon_den, GENmuons)
                if Lxy is not None:
                    self.HISTS[MUONTYPE + '_' + KEY + 'Den'].Fill(Lxy)
                if muon_num is not None and len(accepted_muons_num) >= 2 and \
                        Lxy is not None:
                    # muon_den and muon_num are the same object at this point,
                    # so they have the same Lxy
                    self.HISTS[MUONTYPE + '_' + KEY + 'Eff'].Fill(Lxy)

            else:
                self.HISTS[MUONTYPE + '_' + KEY + 'Den'].Fill(F(muon_den))

                # fill the numerator only if there are at least two muons in the
                # event that satisfy the "numerator (matching) requirement"
                if muon_num is not None and len(accepted_muons_num) >= 2:
                    self.HISTS[MUONTYPE + '_' + KEY + 'Eff'].Fill(F(muon_num))
def analyze(self, E, PARAMS=None):
    if self.TRIGGER and self.SP is not None:
        if not Selections.passedTrigger(E): return
    Event = E.getPrimitives('EVENT')
    DSAmuons = E.getPrimitives('DSAMUON')
    Dimuons = E.getPrimitives('DIMUON')

    Primitives.CopyExtraRecoMuonInfo(Dimuons, DSAmuons)

    eventWeight = 1.
    try:
        eventWeight = 1. if Event.weight > 0. else -1.
    except:
        pass

    # decide what set of cuts to apply based on self.CUTS cut string
    ALL = 'All' in self.CUTS
    PROMPT = '_Prompt' in self.CUTS
    NOPROMPT = '_NoPrompt' in self.CUTS
    NSTATIONS = '_NS' in self.CUTS
    NMUONHITS = '_NH' in self.CUTS
    FPTERR = '_FPTE' in self.CUTS
    PT = '_PT' in self.CUTS
    HLT = '_HLT' in self.CUTS
    PC = '_PC' in self.CUTS
    LXYERR = '_LXYE' in self.CUTS
    MASS = '_M' in self.CUTS

    def boolsToMuonCutList(NSTATIONS, NMUONHITS, FPTERR, PT):
        cutList = []
        if NSTATIONS:
            cutList.append('b_nStations')
        if NMUONHITS:
            cutList.append('b_nMuonHits')
        if FPTERR:
            cutList.append('b_FPTE')
        if PT:
            cutList.append('b_pT')
        return cutList

    def boolsToDimuonCutList(LXYERR, MASS):
        cutList = []
        if LXYERR:
            cutList.append('b_LxyErr')
        if MASS:
            cutList.append('b_mass')
        return cutList

    # require muons to pass all selections
    if ALL:
        DSASelections = [Selections.MuonSelection(muon) for muon in DSAmuons]
        selectedDSAmuons = [
            mu for idx, mu in enumerate(DSAmuons) if DSASelections[idx]
        ]
        selectedDimuons = Dimuons

    # don't require reco muons to pass all selections
    else:
        selectedDSAmuons = DSAmuons
        selectedDimuons = Dimuons

    # for PROMPT and NOPROMPT event selections
    if PROMPT or NOPROMPT:
        highLxySigExists = False
        for dimuon in Dimuons:
            if dimuon.LxySig() > 3.:
                highLxySigExists = True
                break

        # return if there are LxySig > 3
        if PROMPT:
            if highLxySigExists:
                return
        # return if there are NO LxySig > 3 -- that's category 1
        elif NOPROMPT:
            if not highLxySigExists:
                return

    if PROMPT or NOPROMPT:
        # compute all the baseline selection booleans
        DSASelections = [
            Selections.MuonSelection(muon, cutList='BaselineMuonCutList')
            for muon in DSAmuons
        ]

        # figure out which cuts we actually care about
        cutList = boolsToMuonCutList(NSTATIONS, NMUONHITS, FPTERR, PT)

        # no selection
        if len(cutList) == 0:
            selectedDSAmuons = DSAmuons
            selectedDimuons = Dimuons
        # cutList is some nonzero list, meaning keep only the muons that pass the cut keys in cutList
        else:
            selectedDSAmuons = [
                mu for i, mu in enumerate(DSAmuons)
                if DSASelections[i].allOf(*cutList)
            ]
            selectedOIndices = [mu.idx for mu in selectedDSAmuons]
            selectedDimuons = [
                dim for dim in Dimuons if dim.idx1 in selectedOIndices
                and dim.idx2 in selectedOIndices
            ]

    # apply HLT RECO matching
    if HLT:
        HLTPaths, HLTMuons, L1TMuons = E.getPrimitives('TRIGGER')
        DSAMuonsForHLTMatch = [
            mu for mu in selectedDSAmuons if abs(mu.eta) < 2.
        ]
        HLTMuonMatches = matchedTrigger(HLTMuons, DSAMuonsForHLTMatch)
        if not any([HLTMuonMatches[ij]['matchFound']
                    for ij in HLTMuonMatches]):
            return

    # apply pairing criteria and transform selectedDimuons
    if PC:
        selectedDimuons = applyPairingCriteria(selectedDSAmuons,
                                               selectedDimuons)

    if PROMPT or NOPROMPT:
        # compute all the baseline selection booleans
        DimuonSelections = {
            dim.ID: Selections.DimuonSelection(dim,
                                               cutList='BaselineDimuonCutList')
            for dim in selectedDimuons
        }

        # figure out which cuts we actually care about
        cutList = boolsToDimuonCutList(LXYERR, MASS)

        # cutList is some nonzero list, meaning keep only the muons that pass the cut keys in cutList
        if len(cutList) > 0:
            selectedDimuons = [
                dim for dim in selectedDimuons
                if DimuonSelections[dim.ID].allOf(*cutList)
            ]

    # for the MC/Data events, skip events with no dimuons, but not for "no selection"
    if (PROMPT or NOPROMPT) and NSTATIONS:
        if len(selectedDimuons) == 0: return

    # also filter selectedDSAmuons to only be of those indices that are in the final dimuons
    if PROMPT or NOPROMPT:
        selectedOIndices = []
        for dim in selectedDimuons:
            selectedOIndices.append(dim.idx1)
            selectedOIndices.append(dim.idx2)
        selectedOIndices = list(set(selectedOIndices))
        selectedDSAmuons = [
            mu for mu in selectedDSAmuons if mu.idx in selectedOIndices
        ]

    # all refitted muons
    allRefittedMuons = []
    for dimuon in selectedDimuons:
        allRefittedMuons.append(dimuon.mu1)
        allRefittedMuons.append(dimuon.mu2)

    # fill histograms for every reco muon
    for MUON, recoMuons in (('DSA', selectedDSAmuons), ('REF',
                                                        allRefittedMuons)):
        self.HISTS[MUON + '_nMuon'].Fill(len(recoMuons), eventWeight)
        for muon in recoMuons:
            for KEY in CONFIG:
                self.HISTS[MUON + '_' + KEY].Fill(CONFIG[KEY]['LAMBDA'](muon),
                                                  eventWeight)
            for KEY in EXTRACONFIG:
                F1 = EXTRACONFIG[KEY]['LAMBDA'][0]
                F2 = EXTRACONFIG[KEY]['LAMBDA'][1]
                self.HISTS[MUON + '_' + KEY].Fill(F1(muon), F2(muon),
                                                  eventWeight)

    # get gen particles if this is a signal sample
    if self.SP is not None:
        if '4Mu' in self.NAME:
            mu11, mu12, mu21, mu22, X1, X2, H, P, extramu = E.getPrimitives(
                'GEN')
            genMuons = (mu11, mu12, mu21, mu22)
            genMuonPairs = ((mu11, mu12), (mu21, mu22))
        elif '2Mu2J' in self.NAME:
            mu1, mu2, j1, j2, X, XP, H, P, extramu = E.getPrimitives('GEN')
            genMuons = (mu1, mu2)
            genMuonPairs = ((mu1, mu2), )

        MuonMatches = {'DSA': [], 'REF': []}
        # get matched reco muons
        for genMuon in genMuons:
            # cut genMuons outside the detector acceptance
            # don't do it for now
            #genMuonSelection = Selections.AcceptanceSelection(genMuon)

            for MUON, recoMuons in (('DSA', selectedDSAmuons), ):
                MuonMatches[MUON].append(
                    matchedMuons(genMuon, recoMuons, vertex='BS'))

        # and for refitted muons for matched dimuons
        for genMuonPair in genMuonPairs:
            for MUON in ('REF', ):
                dimuonMatches, muonMatches, exitcode = matchedDimuons(
                    genMuonPair, selectedDimuons)
                MuonMatches[MUON].append(muonMatches[0])
                MuonMatches[MUON].append(muonMatches[1])

        # fill histograms
        # for each major muon type,
        # MuonMatches contains 2 lists of matches corresponding to each gen muon
        # Each match in each of those 2 lists is a list of individual muon matches
        for MUON in ('DSA', 'REF'):
            for matches in MuonMatches[MUON]:
                for match in matches:
                    muon = match['muon']
                    deltaR = match['deltaR']
                    for KEY in CONFIG:
                        self.HISTS[MUON + '_' + KEY + '_Matched'].Fill(
                            CONFIG[KEY]['LAMBDA'](muon), eventWeight)
                    for KEY in EXTRACONFIG:
                        F1 = EXTRACONFIG[KEY]['LAMBDA'][0]
                        F2 = EXTRACONFIG[KEY]['LAMBDA'][1]
                        self.HISTS[MUON + '_' + KEY + '_Matched'].Fill(
                            F1(muon), F2(muon), eventWeight)
                    self.HISTS[MUON + '_deltaRGR_Matched'].Fill(
                        deltaR, eventWeight)
                self.HISTS[MUON + '_nMuon_Matched'].Fill(
                    len(matches), eventWeight)
                if len(matches) > 0:
                    self.HISTS[MUON + '_deltaRGR_Closest'].Fill(
                        matches[0]['deltaR'], eventWeight)
Example #9
0
def analyze(self, E, PARAMS=None):
    if '4Mu' in self.NAME:
        raise NotImplementedError('[ANALYZER ERROR]: 4Mu samples are not '
                                  'supported yet')

    if self.SP is None:
        raise NotImplementedError('[ANALYZER ERROR]: This script runs on '
                                  'signal samples only.')

    mu1, mu2, j1, j2, X, XP, H, P, extramu = E.getPrimitives('GEN')
    GENmuons = (mu1, mu2)

    HLTpaths, HLTmuons, L1Tmuons = E.getPrimitives('TRIGGER')

    DSAmuons = E.getPrimitives('DSAMUON')
    RSAmuons = E.getPrimitives('RSAMUON')

    # denominator matching: define which types of muons are to be matched with
    # which other types of muons
    matchWith_den = {
        'GEN': None,
        'DSA': GENmuons,
        'RSA': GENmuons,
    }

    # numerator matching: define which types of muons are to be matched with
    # which other types of muons (NB: the denominator matching automatically
    # applies to the numberator muons, so no need to re-declare it here)
    matchWith_num = {
        'GEN': HLTmuons,
        'DSA': HLTmuons,
        'RSA': HLTmuons,
    }

    do_skip_event = False
    for var in XCUTS.keys():
        if not XCUTS[var].apply(X): do_skip_event = True

    if do_skip_event: return

    for MUONTYPE, MUONS in (('GEN', GENmuons), ('DSA', DSAmuons), ('RSA',
                                                                   RSAmuons)):

        # loop over the entire event and collect the selected muons, separately
        # for the denominator and the numerator (but do not fill any histograms
        # yet)
        accepted_muons_den = []
        accepted_muons_num = []
        for muon in MUONS:

            # apply selections: skip muons that do not pass the cuts
            do_skip_muon = False
            for var in MUONCUTS.keys():
                if SELECT_ON_GEN_LEVEL is True:
                    selection_muon = getGENmuon(muon, GENmuons)
                    if selection_muon is None: do_skip_muon = True
                else:
                    selection_muon = muon

                # apply MUONCUTS
                if selection_muon is not None and not MUONCUTS[var].apply(
                        selection_muon):
                    do_skip_muon = True

            # apply MUONCUTS_RECOONLY
            if MUONTYPE != 'GEN':
                for var in MUONCUTS_RECOONLY.keys():
                    if not MUONCUTS_RECOONLY[var].apply(muon):
                        do_skip_muon = True

            # apply Lxy cuts
            temp_gen_mu = getGENmuon(muon, GENmuons)
            if temp_gen_mu is not None:
                if LXYMIN is not None and \
                        temp_gen_mu.Lxy() < LXYMIN:
                    do_skip_muon = True
                if LXYMAX is not None and \
                        temp_gen_mu.Lxy() > LXYMAX:
                    do_skip_muon = True
            else:
                do_skip_muon = True

            if do_skip_muon: continue

            # denominator: match the current muon if it's supposed to be matched
            if matchWith_den[MUONTYPE] is not None:
                muonMatches_den = matchedMuons(muon, matchWith_den[MUONTYPE])
                if len(muonMatches_den) != 0:
                    # collect the matched muon for the denominator
                    accepted_muons_den.append(muon)

                    # numerator: match the current muon if it is supposed to be
                    # matched
                    if matchWith_num[MUONTYPE] is not None:
                        matchedMuons_den_num = matchedMuons(
                            muon, matchWith_num[MUONTYPE])
                        if len(matchedMuons_den_num) != 0:
                            # collect the matched muon for the numerator
                            accepted_muons_num.append(muon)

                    else:
                        # numerator: just collect the muon if it is not supposed
                        # to be matched
                        accepted_muons_num.append(muon)

            else:
                # denominator: just collect the muon if it is not supposed to
                # be matched
                accepted_muons_den.append(muon)

                # numerator: match the current muon if it is supposed to be
                # matched
                if matchWith_num[MUONTYPE] is not None:
                    matchedMuons_den_num = matchedMuons(
                        muon, matchWith_num[MUONTYPE])
                    if len(matchedMuons_den_num) != 0:
                        # collect the matched muons for the numerator
                        accepted_muons_num.append(muon)

                else:
                    # numerator: just collect the muon if it is not supposed
                    # to be matched
                    accepted_muons_num.append(muon)

        # skip the event if not enough muons pass the selection for the
        # denominator
        if len(accepted_muons_den) < 2: continue

        # apply dimuon selections
        if SELECT_ON_GEN_LEVEL is True:
            temp_mu1 = getGENmuon(accepted_muons_den[0], GENmuons)
            temp_mu2 = getGENmuon(accepted_muons_den[1], GENmuons)
        else:
            temp_mu1 = accepted_muons_den[0]
            temp_mu2 = accepted_muons_den[1]

        do_skip_dimuon = False
        for var in DIMUONCUTS.keys():
            if not DIMUONCUTS[var].apply((temp_mu1, temp_mu2)):
                do_skip_dimuon = True

        if do_skip_dimuon: continue

        # sort collected muons according to their pT (descending order),
        # if applicable
        if MUON_TO_PROCESS == 'subleading':
            accepted_muons_den.sort(key=lambda m: m.pt, reverse=True)
            muon_den = accepted_muons_den[1]
        elif MUON_TO_PROCESS == 'largestD0':
            if abs(accepted_muons_den[0].d0()) > abs(
                    accepted_muons_den[1].d0()):
                muon_den = accepted_muons_den[0]
            else:
                muon_den = accepted_muons_den[1]

        # if existing, select the same muon for the numerator
        for m in accepted_muons_num:
            if m == muon_den:
                muon_num = m
                break
        else:
            muon_num = None

        # fill the histograms
        for KEY in CONFIG:
            F = CONFIG[KEY]['LAMBDA']

            if KEY not in PAIRVARIABLES:
                if MUONTYPE in ['DSA', 'RSA'] and KEY == 'Lxy':
                    gen_muon_den = getGENmuon(muon_den, GENmuons)
                    if gen_muon_den is not None:
                        self.HISTS[MUONTYPE + '_' + KEY + 'Den'].Fill(
                            F(gen_muon_den))

                        # fill the numberator only if there are at least two
                        # muons in the event that satisfy the numerator matching
                        # requirement
                        if muon_num is not None and len(
                                accepted_muons_num) > 1:
                            # muon_num and muon_den are the same object at this
                            # point, so they have the same Lxy
                            self.HISTS[MUONTYPE + '_' + KEY + 'Num'].Fill(
                                F(gen_muon_den))

                else:
                    self.HISTS[MUONTYPE + '_' + KEY + 'Den'].Fill(F(muon_den))

                    # fill the numerator only if there are at least two
                    # muons in the event that satisfy the numerator matching
                    # requirement
                    if muon_num is not None and len(accepted_muons_num) > 1:
                        self.HISTS[MUONTYPE + '_' + KEY + 'Num'].Fill(
                            F(muon_num))

            elif len(accepted_muons_den) > 1:
                if KEY == 'dimuonPTOverM':
                    temp_mu1 = getGENmuon(accepted_muons_den[0], GENmuons)
                    temp_mu2 = getGENmuon(accepted_muons_den[1], GENmuons)
                    if temp_mu1 is not None and temp_mu2 is not None:
                        if abs(temp_mu1.Lxy() - temp_mu2.Lxy()) < 1e-3:
                            self.HISTS[MUONTYPE + '_' + KEY + 'Den'].Fill(
                                F((accepted_muons_den[0],
                                   accepted_muons_den[1])))

                            if len(accepted_muons_num) > 1:
                                self.HISTS[MUONTYPE + '_' + KEY + 'Num'].Fill(
                                    F((accepted_muons_num[0],
                                       accepted_muons_num[1])))

                elif KEY == 'XBeta':
                    if MUONTYPE == 'GEN':
                        self.HISTS[MUONTYPE + '_' + KEY + 'Den'].Fill(F(X))
                        if len(accepted_muons_num) > 1:
                            self.HISTS[MUONTYPE + '_' + KEY + 'Num'].Fill(F(X))

                else:
                    self.HISTS[MUONTYPE + '_' + KEY + 'Den'].Fill(
                        F((accepted_muons_den[0], accepted_muons_den[1])))

                    if len(accepted_muons_num) > 1:
                        self.HISTS[MUONTYPE + '_' + KEY + 'Num'].Fill(
                            F((accepted_muons_num[0], accepted_muons_num[1])))
Example #10
0
def analyze(self, E, PARAMS=None):

#    print(E)

    event = E.getPrimitives('EVENT')
#    print(event)

    # require that the trigger has fired
    if not Selections.passedTrigger(E): return

    if self.SP is None:
        raise Exception('[ANALYZER ERROR]: This script runs on signal only.')
    if '4Mu' in self.NAME:
        mu11, mu12, mu21, mu22, X1, X2, H, P, extramu = E.getPrimitives('GEN')
        genMuons = (mu11, mu12, mu21, mu22)
        genMuonPairs = ((mu11, mu12), (mu21, mu22))
    elif '2Mu2J' in self.NAME:
        mu1, mu2, j1, j2, X, XP, H, P, extramu = E.getPrimitives('GEN')
        genMuons = (mu1, mu2)
        genMuonPairs = ((mu1, mu2),)

    # original DSA muons
    DSAMuons = E.getPrimitives('DSAMUON')

    # all dimuons and dimuons made of muons passing quality cuts
    allDimuons = E.getPrimitives('DIMUON')
    selectedDSAmuons  = [mu for mu in DSAMuons if mu.nDTStations+mu.nCSCStations>1 and mu.nCSCHits+mu.nDTHits>12 and mu.ptError/mu.pt<1.]
    DSAmuons_formatch = [mu for mu in DSAMuons if mu.nDTStations+mu.nCSCStations>1 and mu.nCSCHits+mu.nDTHits>12 and mu.ptError/mu.pt<1. and abs(mu.eta) < 2.0 and mu.pt > 5.]
    selectedOIndices = [mu.idx for mu in selectedDSAmuons]
    selectedDimuons  = [dim for dim in allDimuons if dim.idx1 in selectedOIndices and dim.idx2 in selectedOIndices]

    # studies of matching between HLT and DSA muons
    HLTPaths, HLTMuons, L1TMuons = E.getPrimitives('TRIGGER')
#    print "List of HLT muons:"
#    for hltmuon in HLTMuons:
#        print(hltmuon)
    for muon in DSAMuons:
        print muon

    match_found = False
    HLTDSAMatches = matchedTrigger(HLTMuons, DSAmuons_formatch, saveDeltaR=True, threshold=0.4, printAllMatches=True)
    for i, j in HLTDSAMatches:
        imatch = 0
        for match in HLTDSAMatches[(i,j)]['bestMatches']:
            if   imatch == 0: print 'best match:', i, j, match
            elif imatch == 1: print '2nd best:',   i, j, match
            imatch += 1
            dR = match['deltaR']
            self.HISTS['dR_HLT_DSA'].Fill(dR)
        if HLTDSAMatches[(i,j)]['matchFound'] == True:
            match_found = True

    # check for how many events a DSA-HLT match was found
    self.HISTS['matches_HLT_DSA'].Fill(0)
    if match_found:
        self.HISTS['matches_HLT_DSA'].Fill(1)
    else:
        self.HISTS['matches_HLT_DSA'].Fill(2)

        print 'match not found'
        print(E)

        # check if there were any matching dimuons
        for genMuonPair in genMuonPairs:
            dimuonMatches, muonMatches, exitcode = matchedDimuons(genMuonPair, allDimuons)
            if len(dimuonMatches) > 0:
                self.HISTS['matches_HLT_DSA'].Fill(3)
            else:
                for i, j in HLTDSAMatches:
                    imatch = 0
                    for match in HLTDSAMatches[(i,j)]['bestMatches']:
                        dR = match['deltaR']
                        if   imatch == 0: self.HISTS['dR1_HLT_DSA_BadEvent'].Fill(dR)
                        elif imatch == 1: self.HISTS['dR2_HLT_DSA_BadEvent'].Fill(dR)
                        imatch += 1

            # more detailed diagnostics for cases when a good-quality
            # signal dimuon is lost
            dimuonMatches, muonMatches, exitcode = matchedDimuons(genMuonPair, selectedDimuons)
            if len(dimuonMatches) > 0:
                print "Lose good event!"
                self.HISTS['matches_HLT_DSA'].Fill(4)
                for i, j in HLTDSAMatches:
                    imatch = 0
                    for match in HLTDSAMatches[(i,j)]['bestMatches']:
                        dR = match['deltaR']
                        if   imatch == 0: self.HISTS['dR1_HLT_DSA_GoodEvent'].Fill(dR)
                        elif imatch == 1: self.HISTS['dR2_HLT_DSA_GoodEvent'].Fill(dR)
                        imatch += 1

                gen_dim = genMuonPair[0].p4 + genMuonPair[1].p4
                gen_invm = gen_dim.M()
                for dimuonMatch in dimuonMatches:
                    dimuon = dimuonMatch['dim']
                    rec_invm = dimuon.p4.M()
                    print "gen_mass = ", gen_invm, "rec_mass = ", rec_invm
                    print "gen_Lxy = ", genMuonPair[0].Lxy_, "rec_Lxy = ", dimuon.Lxy(), "+/-", dimuon.LxyErr()
                    print(dimuon)
                    unmatched_invm_res = (rec_invm - gen_invm)/gen_invm
                    self.HISTS['unmatched_invm_res'].Fill(unmatched_invm_res)
                    for muonMatch in muonMatches:
                        recmu_idx = muonMatch[0]['oidx']
                        muon_found = False
                        for i, j in HLTDSAMatches:
                            for hltMatch in HLTDSAMatches[(i,j)]['bestMatches']:
                                if hltMatch['rec_idx'] == recmu_idx and hltMatch['deltaR'] < 0.4:
                                    muon_found = True
                                    break
                        if muon_found == False:
                            print "DSA muon not matched to HLT muon:", recmu_idx
                            recmu_p4 = muonMatch[0]['muon'].p4
                            recmu_pt = muonMatch[0]['muon'].pt
                            for genmu in genMuonPair:
                                genmu_p4 = genmu.p4
                                dr = recmu_p4.DeltaR(genmu_p4)
                                if dr < 0.2:
                                    genmu_pt = genmu.pt
                                    unmatched_pt_res = (recmu_pt - genmu_pt)/genmu_pt
                                    print "muon pT = ", recmu_pt, "pt of matched gen muon = ", genmu_pt, "pt_res =", unmatched_pt_res
                                    self.HISTS['unmatched_pt_res'].Fill(unmatched_pt_res)
                                    self.HISTS['unmatched_pt_rec_vs_pt_gen'].Fill(genmu_pt, recmu_pt)
                                    self.HISTS['unmatched_pt_rec_vs_pt_gen_zoomed'].Fill(genmu_pt, recmu_pt)

    # studies of single-muon variables
    # loop over genMuons and fill histograms
    # for genMuon in genMuons:
    for gen_idx, genMuon in enumerate(genMuons):
#        print(genMuon)
        matches = matchedMuons(genMuon, DSAMuons, vertex='BS')
        if len(matches) > 0:
            for match in matches:

                muon = match['muon']

                # number of hits
                nDTCSCHits = muon.nDTHits + muon.nCSCHits
                nRPCHits   = muon.nMuonHits - nDTCSCHits
                self.HISTS['nMuonHits'].Fill(muon.nMuonHits)
                self.HISTS['nDTCSCHits'].Fill(nDTCSCHits)
                self.HISTS['nDTHits'].Fill(muon.nDTHits)
                self.HISTS['nCSCHits'].Fill(muon.nCSCHits)
                self.HISTS['nRPCHits'].Fill(nRPCHits)

                # number of hits for various numbers of stations
                nStations = muon.nDTStations + muon.nCSCStations
                self.HISTS['CSC_vs_DT_Stations'].Fill(muon.nDTStations, muon.nCSCStations)
                if nStations >= 1 and nStations <= 4:
                    self.HISTS['nMuonHits_%dStat'%nStations]. Fill(muon.nMuonHits)
                    self.HISTS['nDTCSCHits_%dStat'%nStations].Fill(nDTCSCHits)
                    if muon.nDTHits > 0:
                        self.HISTS['nDTHits_%dStat'%nStations]. Fill(muon.nDTHits)
                    if muon.nCSCHits > 0:
                        self.HISTS['nCSCHits_%dStat'%nStations].Fill(muon.nCSCHits)
                    self.HISTS['nRPCHits_%dStat'%nStations].  Fill(nRPCHits)
                    self.HISTS['nCSCHits_vs_nDTHits_%dStat'%nStations].Fill(muon.nDTHits, muon.nCSCHits)
                elif nStations < 1 or nStations > 4:
                    print '+++ Warning: no histo filled; N(stat) = ', nStations

                # pT and 1/pT resolutions
                pt_res    = (muon.pt - genMuon.BS.pt)/genMuon.BS.pt
                invpt_res = (1./muon.pt - 1./genMuon.BS.pt)/(1./genMuon.BS.pt)
                # (rec-gen) charge and d0
                q_dif     = muon.charge - genMuon.BS.charge
                d0_dif    = muon.d0(extrap=None) - genMuon.d0(extrap=None)
#                print 'gen idx', gen_idx, 'muon idx = ', muon.idx, ' pT res = ', pt_res, 'd0 dif = ', d0_dif

                # sigma(pT)/pT and chi2/dof
                sigmapt_over_pt = muon.ptError/muon.pt
                chi2_over_ndof  = muon.chi2/muon.ndof

                # resolutions for groups of 3 DT+CSC hits
                ihist = nDTCSCHits//3
                self.HISTS['pTres_DTCSChits_hist%d'%ihist].Fill(pt_res)
                self.HISTS['invpTres_DTCSChits_hist%d'%ihist].Fill(invpt_res)
                self.HISTS['qdif_DTCSChits_hist%d'%ihist].Fill(q_dif)
                self.HISTS['d0dif_DTCSChits_hist%d'%ihist].Fill(d0_dif)

                # ditto for Nstat > 1
                if nStations > 1:
                    self.HISTS['pTres_DTCSChits_Stat234_hist%d'%ihist].Fill(pt_res)
                    self.HISTS['invpTres_DTCSChits_Stat234_hist%d'%ihist].Fill(invpt_res)
                    self.HISTS['qdif_DTCSChits_Stat234_hist%d'%ihist].Fill(q_dif)
                    self.HISTS['d0dif_DTCSChits_Stat234_hist%d'%ihist].Fill(d0_dif)

                    # fine scan for Nhits between 12 and 19
                    if nDTCSCHits >= 12 and nDTCSCHits <= 19:
                        self.HISTS['pTres_Stat234_%dDTCSChits'%nDTCSCHits].Fill(pt_res)
                        self.HISTS['invpTres_Stat234_%dDTCSChits'%nDTCSCHits].Fill(pt_res)
                        self.HISTS['qdif_Stat234_%dDTCSChits'%nDTCSCHits].Fill(q_dif)

                # sigma(pT)/pT and chi2/dof for groups of 3 DT+CSC hits
                self.HISTS['dpt_over_pt_DTCSChits_hist%d'%ihist].Fill(sigmapt_over_pt)
                self.HISTS['chi2_over_ndof_DTCSChits_hist%d'%ihist].Fill(chi2_over_ndof)

                # ditto for Nstat > 1
                if nStations > 1:
                    self.HISTS['dpt_over_pt_DTCSChits_Stat234_hist%d'%ihist].Fill(sigmapt_over_pt)
                    self.HISTS['chi2_over_ndof_DTCSChits_Stat234_hist%d'%ihist].Fill(chi2_over_ndof)

                # resolutions in barrel, endcap, and overlap
                if muon.nDTStations > 0 and muon.nCSCStations == 0:
                    ihist = muon.nDTHits//6
#                    print 'barrel: ', muon.nDTHits, ihist
                    self.HISTS['pTres_DThits_barrel_hist%d'%ihist].Fill(pt_res)
                    self.HISTS['d0dif_DThits_barrel_hist%d'%ihist].Fill(d0_dif)
                elif muon.nDTStations == 0 and muon.nCSCStations > 0:
                    ihist = muon.nCSCHits//3
                    # overlapping chambers?
                    if ihist > 9:
                        print "CSCs: > 29 hits", muon.nCSCHits
                        ihist = 8
#                    print 'endcap: ', muon.nCSCHits, ihist
                    self.HISTS['pTres_CSChits_endcap_hist%d'%ihist].Fill(pt_res)
                    self.HISTS['d0dif_CSChits_endcap_hist%d'%ihist].Fill(d0_dif)
                elif muon.nDTStations > 0 and muon.nCSCStations > 0:
                    ihist = nDTCSCHits//6
#                    print 'overlap: ', nDTCSCHits, ihist
                    self.HISTS['pTres_DTCSChits_overlap_hist%d'%ihist].Fill(pt_res)
                    self.HISTS['d0dif_DTCSChits_overlap_hist%d'%ihist].Fill(d0_dif)

                # make sure that the nDTCSCHits > 12 cut supersedes the nStations > 1 one
                if nDTCSCHits > 12 and nStations <= 1:
                    print "+++ Mismatch between nStations and nHits: nStations = ", nStations, "nDTCSCHits = ", nDTCSCHits, "+++"

                # sigma(pT)/pT and chi2/dof
                self.HISTS['dpt_over_pt_vs_chi2_over_ndof'].Fill(chi2_over_ndof, sigmapt_over_pt)
                if nStations >= 1 and nStations <= 4:
                    self.HISTS['dpt_over_pt_%dStat'%nStations].Fill(sigmapt_over_pt)
                    self.HISTS['chi2_over_ndof_%dStat'%nStations].Fill(chi2_over_ndof)

                # pT resolution in slices of sigma(pT)/pT
                ihist = 0
                while sigmapt_over_pt >= SLICE_EDGES[ihist]: ihist += 1
                self.HISTS['pTres_for_dpt_over_pt_hist%d'%ihist].Fill(pt_res)
                if nDTCSCHits > 12:
                    self.HISTS['pTres_for_dpt_over_pt_passed_hist%d'%ihist].Fill(pt_res)

                # pT pull in slices of sigma(pT)/pT
                pt_pull = (muon.pt - genMuon.BS.pt)/muon.ptError
                self.HISTS['pTpull_for_dpt_over_pt_hist%d'%ihist].Fill(pt_pull)

                # Refitted muons for DSA muons failing dpT/pT cut
                if sigmapt_over_pt > 1.:
                    self.HISTS['eta_for_dpt_over_pt_gt_1'].Fill(muon.eta)
                    for genMuonPair in genMuonPairs:
                        dimuonMatches, muonMatches, exitcode = matchedDimuons(genMuonPair, allDimuons)
                        for match in dimuonMatches:
                            dimuon = match['dim']
                            for refmu in (dimuon.mu1, dimuon.mu2):
                                if refmu.idx == muon.idx:
#                                    print "Found refitted muon", refmu.idx, refmu.pt
                                    pt_res_ref          = (refmu.pt - genMuon.pt)/genMuon.pt
                                    sigmapt_over_pt_ref = refmu.ptError/refmu.pt
                                    self.HISTS['pTres_ref_for_dpt_over_pt_gt_1'].Fill(pt_res_ref)
                                    self.HISTS['dpt_over_pt_ref_for_dpt_over_pt_gt_1'].Fill(sigmapt_over_pt_ref)
                                    if sigmapt_over_pt_ref < 1:
                                        self.HISTS['pTres_ref_for_dpt_over_pt_gt_1_ref_lt_1'].Fill(pt_res_ref)


                # pT resolution in slices of chi2/ndof
                ihist = 0
                while chi2_over_ndof >= SLICE_EDGES[ihist]: ihist += 1
                self.HISTS['pTres_for_chi2_over_ndof_hist%d'%ihist].Fill(pt_res)
                if nDTCSCHits > 12:
                    self.HISTS['pTres_for_chi2_over_ndof_passed_hist%d'%ihist].Fill(pt_res)