def process(self, df): output = self.accumulator.identity() datasetFull = df['dataset'] dataset = datasetFull.replace('_2016', '') isData = 'Data' in dataset year = 2016 yearStr = "2016" muTrigger = df['HLT_IsoMu24'] | df['HLT_IsoTkMu24'] eleTrigger = df['HLT_Ele27_WPTight_Gsf'] photonBitMapName = 'Photon_cutBased' #### These are already applied in the skim # filters = (df['Flag_goodVertices'] & # df['Flag_globalSuperTightHalo2016Filter'] & # df['Flag_HBHENoiseFilter'] & # df['Flag_HBHENoiseIsoFilter'] & # df['Flag_EcalDeadCellTriggerPrimitiveFilter'] & # df['Flag_BadPFMuonFilter'] # ) # if year > 2016: # filters = (filters & # df['Flag_ecalBadCalibFilterV2'] # ) muons = JaggedCandidateArray.candidatesfromcounts( df['nMuon'], pt=df['Muon_pt'], eta=df['Muon_eta'], phi=df['Muon_phi'], mass=df['Muon_mass'], charge=df['Muon_charge'], relIso=df['Muon_pfRelIso04_all'], tightId=df['Muon_tightId'], isPFcand=df['Muon_isPFcand'], isTracker=df['Muon_isTracker'], isGlobal=df['Muon_isGlobal'], ) electrons = JaggedCandidateArray.candidatesfromcounts( df['nElectron'], pt=df['Electron_pt'], eta=df['Electron_eta'], phi=df['Electron_phi'], mass=df['Electron_mass'], charge=df['Electron_charge'], cutBased=df['Electron_cutBased'], d0=df['Electron_dxy'], dz=df['Electron_dz'], ) jets = JaggedCandidateArray.candidatesfromcounts( df['nJet'], pt=df['Jet_pt'], eta=df['Jet_eta'], phi=df['Jet_phi'], mass=df['Jet_mass'], jetId=df['Jet_jetId'], btag=df['Jet_btagDeepB'], hadFlav=df['Jet_hadronFlavour'] if not isData else np.ones_like(df['Jet_jetId']), genIdx=df['Jet_genJetIdx'] if not isData else np.ones_like(df['Jet_jetId']), ) photons = JaggedCandidateArray.candidatesfromcounts( df['nPhoton'], pt=df['Photon_pt'], eta=df['Photon_eta'], phi=df['Photon_phi'], mass=np.zeros_like(df['Photon_pt']), isEE=df['Photon_isScEtaEE'], isEB=df['Photon_isScEtaEB'], photonId=df[photonBitMapName], passEleVeto=df['Photon_electronVeto'], pixelSeed=df['Photon_pixelSeed'], sieie=df['Photon_sieie'], chIso=df['Photon_pfRelIso03_chg'] * df['Photon_pt'], vidCuts=df['Photon_vidNestedWPBitmap'], genFlav=df['Photon_genPartFlav'] if not isData else np.ones_like(df['Photon_electronVeto']), genIdx=df['Photon_genPartIdx'] if not isData else np.ones_like(df['Photon_electronVeto']), ) if not isData: genPart = JaggedCandidateArray.candidatesfromcounts( df['nGenPart'], pt=df['GenPart_pt'], eta=df['GenPart_eta'], phi=df['GenPart_phi'], mass=df['GenPart_mass'], pdgid=df['GenPart_pdgId'], motherIdx=df['GenPart_genPartIdxMother'], status=df['GenPart_status'], statusFlags=df['GenPart_statusFlags'], ) genmotherIdx = genPart.motherIdx genpdgid = genPart.pdgid ## TTbar vs TTGamma Overlap Removal (work in progress, still buggy) doOverlapRemoval = False if 'TTbar' in dataset: doOverlapRemoval = True overlapPt = 10. overlapEta = 5. overlapDR = 0.1 if re.search("^W[1234]jets$", dataset): doOverlapRemoval = True overlapPt = 10. overlapEta = 2.5 overlapDR = 0.05 if 'DYjetsM' in dataset: doOverlapRemoval = True overlapPt = 15. overlapEta = 2.6 overlapDR = 0.05 if doOverlapRemoval: overlapPhoSelect = ((genPart.pt >= overlapPt) & (abs(genPart.eta) < overlapEta) & (genPart.pdgid == 22) & (genPart.status == 1)) OverlapPhotons = genPart[overlapPhoSelect] idx = OverlapPhotons.motherIdx maxParent = maxHistoryPDGID(idx.content, idx.starts, idx.stops, genpdgid.content, genpdgid.starts, genpdgid.stops, genmotherIdx.content, genmotherIdx.starts, genmotherIdx.stops) isNonPrompt = (maxParent > 37).any() finalGen = genPart[( (genPart.status == 1) | (genPart.status == 71)) & ~( (abs(genPart.pdgid) == 12) | (abs(genPart.pdgid) == 14) | (abs(genPart.pdgid) == 16))] genPairs = OverlapPhotons['p4'].cross(finalGen['p4'], nested=True) ##remove the case where the cross produce is the gen photon with itself genPairs = genPairs[~(genPairs.i0 == genPairs.i1)] dRPairs = genPairs.i0.delta_r(genPairs.i1) isOverlap = ((dRPairs.min() > overlapDR) & (maxParent < 37)).any() passOverlapRemoval = ~isOverlap else: passOverlapRemoval = np.ones_like(df['event']) == 1 muonSelectTight = ((muons.pt > 30) & (abs(muons.eta) < 2.4) & (muons.tightId) & (muons.relIso < 0.15)) muonSelectLoose = ((muons.pt > 15) & (abs(muons.eta) < 2.4) & ((muons.isPFcand) & (muons.isTracker | muons.isGlobal)) & (muons.relIso < 0.25) & np.invert(muonSelectTight)) eleEtaGap = (abs(electrons.eta) < 1.4442) | (abs(electrons.eta) > 1.566) elePassD0 = ((abs(electrons.eta) < 1.479) & (abs(electrons.d0) < 0.05) | (abs(electrons.eta) > 1.479) & (abs(electrons.d0) < 0.1)) elePassDZ = ((abs(electrons.eta) < 1.479) & (abs(electrons.dz) < 0.1) | (abs(electrons.eta) > 1.479) & (abs(electrons.dz) < 0.2)) electronSelectTight = ((electrons.pt > 35) & (abs(electrons.eta) < 2.1) & eleEtaGap & (electrons.cutBased >= 4) & elePassD0 & elePassDZ) electronSelectLoose = ((electrons.pt > 15) & (abs(electrons.eta) < 2.4) & eleEtaGap & (electrons.cutBased >= 1) & elePassD0 & elePassDZ & np.invert(electronSelectTight)) tightMuon = muons[muonSelectTight] looseMuon = muons[muonSelectLoose] tightElectron = electrons[electronSelectTight] looseElectron = electrons[electronSelectLoose] oneMuon = (tightMuon.counts == 1) muVeto = (tightMuon.counts == 0) oneEle = (tightElectron.counts == 1) eleVeto = (tightElectron.counts == 0) looseMuonSel = (looseMuon.counts == 0) looseElectronSel = (looseElectron.counts == 0) #### Calculate deltaR between photon and nearest muon ####### make combination pairs phoMu = photons['p4'].cross(tightMuon['p4'], nested=True) ####### check delta R of each combination, if min is >0.1 it is okay, or if there are no tight muons it passes dRphomu = (phoMu.i0.delta_r(phoMu.i1) > 0.4).all() | (tightMuon.counts == 0) phoEle = photons['p4'].cross(tightElectron['p4'], nested=True) dRphoele = ((phoEle.i0.delta_r(phoEle.i1)).min() > 0.4) | (tightElectron.counts == 0) #photon selection (no ID requirement used here) photonSelect = ((photons.pt > 20) & (abs(photons.eta) < 1.4442) & (photons.isEE | photons.isEB) & (photons.passEleVeto) & np.invert(photons.pixelSeed) & dRphomu & dRphoele) #split out the ID requirement, enabling Iso and SIEIE to be inverted for control regions photonID = photons.photonId >= 2 #parse VID cuts, define loose photons (not used yet) photon_MinPtCut = (photons.vidCuts >> 0 & 3) >= 2 photon_PhoSCEtaMultiRangeCut = (photons.vidCuts >> 2 & 3) >= 2 photon_PhoSingleTowerHadOverEmCut = (photons.vidCuts >> 4 & 3) >= 2 photon_PhoFull5x5SigmaIEtaIEtaCut = (photons.vidCuts >> 6 & 3) >= 2 photon_ChIsoCut = (photons.vidCuts >> 8 & 3) >= 2 photon_NeuIsoCut = (photons.vidCuts >> 10 & 3) >= 2 photon_PhoIsoCut = (photons.vidCuts >> 12 & 3) >= 2 photonID_NoChIsoSIEIE = (photon_MinPtCut & photon_PhoSCEtaMultiRangeCut & photon_PhoSingleTowerHadOverEmCut & photon_PhoFull5x5SigmaIEtaIEtaCut & photon_NeuIsoCut & photon_PhoIsoCut) tightPhotons = photons[photonSelect & photonID] loosePhotons = photons[photonSelect & photonID_NoChIsoSIEIE & photon_PhoFull5x5SigmaIEtaIEtaCut] loosePhotonsSideband = photons[photonSelect & photonID_NoChIsoSIEIE & (photons.sieie > 0.012)] ##medium jet ID cut jetIDbit = 1 if year > 2016: jetIDbit = 2 ##check dR jet,lepton & jet,photon jetMu = jets['p4'].cross(tightMuon['p4'], nested=True) dRjetmu = ( (jetMu.i0.delta_r(jetMu.i1)).min() > 0.4) | (tightMuon.counts == 0) jetEle = jets['p4'].cross(tightElectron['p4'], nested=True) dRjetele = ((jetEle.i0.delta_r(jetEle.i1)).min() > 0.4) | (tightElectron.counts == 0) jetPho = jets['p4'].cross(tightPhotons['p4'], nested=True) dRjetpho = ((jetPho.i0.delta_r(jetPho.i1)).min() > 0.1) | (tightPhotons.counts == 0) jetSelect = ((jets.pt > 30) & (abs(jets.eta) < 2.4) & ((jets.jetId >> jetIDbit & 1) == 1) & dRjetmu & dRjetele & dRjetpho) tightJets = jets[jetSelect] bTagWP = 0.6321 #2016 DeepCSV working point btagged = tightJets.btag > bTagWP bJets = tightJets[btagged] ## Define M3, mass of 3-jet pair with highest pT triJet = tightJets['p4'].choose(3) triJetPt = (triJet.i0 + triJet.i1 + triJet.i2).pt triJetMass = (triJet.i0 + triJet.i1 + triJet.i2).mass M3 = triJetMass[triJetPt.argmax()] leadingMuon = tightMuon[::1] leadingElectron = tightElectron[::1] leadingPhoton = tightPhotons[:, :1] leadingPhotonLoose = loosePhotons[:, :1] leadingPhotonSideband = loosePhotonsSideband[:, :1] # egammaMass = (leadingElectron['p4'] + leadingPhoton['p4']).mass egamma = leadingElectron['p4'].cross(leadingPhoton['p4']) mugamma = leadingMuon['p4'].cross(leadingPhoton['p4']) egammaMass = (egamma.i0 + egamma.i1).mass mugammaMass = (mugamma.i0 + mugamma.i1).mass if not isData: #### Photon categories, using genIdx branch # reco photons really generated as electrons idx = leadingPhoton.genIdx matchedPho = (genpdgid[idx] == 22).any() isMisIDele = (abs(genpdgid[idx]) == 11).any() maxParent = maxHistoryPDGID(idx.content, idx.starts, idx.stops, genpdgid.content, genpdgid.starts, genpdgid.stops, genmotherIdx.content, genmotherIdx.starts, genmotherIdx.stops) hadronicParent = maxParent > 25 isGenPho = matchedPho & ~hadronicParent isHadPho = matchedPho & hadronicParent isHadFake = ~(isMisIDele | isGenPho | isHadPho) & ( leadingPhoton.counts == 1) #define integer definition for the photon category axis phoCategory = 1 * isGenPho + 2 * isMisIDele + 3 * isHadPho + 4 * isHadFake isMisIDeleLoose = (leadingPhotonLoose.genFlav == 13).any() matchedPhoLoose = (leadingPhotonLoose.genFlav == 1).any() # look through parentage to find if any hadrons in genPhoton parent history idx = leadingPhotonLoose.genIdx maxParent = maxHistoryPDGID(idx.content, idx.starts, idx.stops, genpdgid.content, genpdgid.starts, genpdgid.stops, genmotherIdx.content, genmotherIdx.starts, genmotherIdx.stops) hadronicParent = maxParent > 25 isGenPhoLoose = matchedPhoLoose & ~hadronicParent isHadPhoLoose = matchedPhoLoose & hadronicParent isHadFakeLoose = ~(isMisIDeleLoose | isGenPhoLoose | isHadPhoLoose) & (leadingPhotonLoose.counts == 1) #define integer definition for the photon category axis phoCategoryLoose = 1 * isGenPhoLoose + 2 * isMisIDeleLoose + 3 * isHadPhoLoose + 4 * isHadFakeLoose isMisIDeleSideband = (leadingPhotonSideband.genFlav == 13).any() matchedPhoSideband = (leadingPhotonSideband.genFlav == 1).any() # look through parentage to find if any hadrons in genPhoton parent history idx = leadingPhotonSideband.genIdx maxParent = maxHistoryPDGID(idx.content, idx.starts, idx.stops, genpdgid.content, genpdgid.starts, genpdgid.stops, genmotherIdx.content, genmotherIdx.starts, genmotherIdx.stops) hadronicParent = maxParent > 25 isGenPhoSideband = matchedPhoSideband & ~hadronicParent isHadPhoSideband = matchedPhoSideband & hadronicParent isHadFakeSideband = ~(isMisIDeleSideband | isGenPhoSideband | isHadPhoSideband) & ( leadingPhotonSideband.counts == 1) #define integer definition for the photon category axis phoCategorySideband = 1 * isGenPhoSideband + 2 * isMisIDeleSideband + 3 * isHadPhoSideband + 4 * isHadFakeSideband else: phoCategory = np.ones_like(df['event']) phoCategoryLoose = np.ones_like(df['event']) phoCategorySideband = np.ones_like(df['event']) ### remove filter selection ### This is already applied in the skim, and is causing data to fail for some reason (the flag branches are duplicated in NanoAOD for data, is it causing problems???) # mu_noLoose = (muTrigger & filters & passOverlapRemoval & # oneMuon & eleVeto & # looseMuonSel & looseElectronSel) # ele_noLoose = (eleTrigger & filters & passOverlapRemoval & # oneEle & muVeto & # looseMuonSel & looseElectronSel) mu_noLoose = (muTrigger & passOverlapRemoval & oneMuon & eleVeto & looseMuonSel & looseElectronSel) ele_noLoose = (eleTrigger & passOverlapRemoval & oneEle & muVeto & looseMuonSel & looseElectronSel) lep_noLoose = mu_noLoose | ele_noLoose lep_jetSel = (lep_noLoose & (tightJets.counts >= 4) & (bJets.counts >= 1)) lep_zeropho = (lep_jetSel & (tightPhotons.counts == 0)) lep_phosel = (lep_jetSel & (tightPhotons.counts == 1)) lep_phoselLoose = (lep_jetSel & (loosePhotons.counts == 1)) lep_phoselSideband = (lep_jetSel & (loosePhotonsSideband.counts == 1)) lep_phosel_3j0t = (lep_noLoose & (tightJets.counts >= 3) & (bJets.counts == 0) & (tightPhotons.counts == 1)) lepFlavor = -0.5 * ele_noLoose + 0.5 * mu_noLoose evtWeight = np.ones_like(df['event'], dtype=np.float64) if not 'Data' in dataset: nMCevents = self.mcEventYields[datasetFull] xsec = crossSections[dataset] evtWeight *= xsec * lumis[year] / nMCevents #btag key name #name / working Point / type / systematic / jetType # ... / 0-loose 1-medium 2-tight / comb,mujets,iterativefit / central,up,down / 0-b 1-c 2-udcsg bJetSF_b = self.evaluator['btag%iDeepCSV_1_comb_central_0' % year]( tightJets[tightJets.hadFlav == 5].eta, tightJets[tightJets.hadFlav == 5].pt, tightJets[tightJets.hadFlav == 5].btag) bJetSF_c = self.evaluator['btag%iDeepCSV_1_comb_central_1' % year]( tightJets[tightJets.hadFlav == 4].eta, tightJets[tightJets.hadFlav == 4].pt, tightJets[tightJets.hadFlav == 4].btag) bJetSF_udcsg = self.evaluator[ 'btag%iDeepCSV_1_incl_central_2' % year](tightJets[tightJets.hadFlav == 0].eta, tightJets[tightJets.hadFlav == 0].pt, tightJets[tightJets.hadFlav == 0].btag) bJetSF = JaggedArray(content=np.ones_like(tightJets.pt.content, dtype=np.float64), starts=tightJets.starts, stops=tightJets.stops) bJetSF.content[(tightJets.hadFlav == 5).content] = bJetSF_b.content bJetSF.content[(tightJets.hadFlav == 4).content] = bJetSF_c.content bJetSF.content[( tightJets.hadFlav == 0).content] = bJetSF_udcsg.content ## mc efficiency lookup, data efficiency is eff* scale factor btagEfficiencies = taggingEffLookup(datasetFull, tightJets.hadFlav, tightJets.pt, tightJets.eta) btagEfficienciesData = btagEfficiencies * bJetSF ##probability is the product of all efficiencies of tagged jets, times product of 1-eff for all untagged jets ## https://twiki.cern.ch/twiki/bin/view/CMS/BTagSFMethods#1a_Event_reweighting_using_scale pMC = btagEfficiencies[btagged].prod() * ( 1. - btagEfficiencies[np.invert(btagged)]).prod() pData = btagEfficienciesData[btagged].prod() * ( 1. - btagEfficienciesData[np.invert(btagged)]).prod() btagWeight = pData / pMC btagWeight[pData == 0] = 0 evtWeight *= btagWeight eleID = self.ele_id_sf(tightElectron.eta, tightElectron.pt) eleIDerr = self.ele_id_err(tightElectron.eta, tightElectron.pt) eleRECO = self.ele_reco_sf(tightElectron.eta, tightElectron.pt) eleRECOerr = self.ele_reco_err(tightElectron.eta, tightElectron.pt) eleSF = (eleID * eleRECO).prod() eleSFup = ((eleID + eleIDerr) * (eleRECO + eleRECOerr)).prod() eleSFdo = ((eleID - eleIDerr) * (eleRECO - eleRECOerr)).prod() evtWeight *= eleSF muID = self.mu_id_sf(tightMuon.eta, tightMuon.pt) muIDerr = self.mu_id_err(tightMuon.eta, tightMuon.pt) muIso = self.mu_iso_sf(tightMuon.eta, tightMuon.pt) muIsoerr = self.mu_iso_err(tightMuon.eta, tightMuon.pt) muTrig = self.mu_iso_sf(abs(tightMuon.eta), tightMuon.pt) muTrigerr = self.mu_iso_err(abs(tightMuon.eta), tightMuon.pt) muSF = (muID * muIso * muTrig).prod() muSF_up = ((muID + muIDerr) * (muIso + muIsoerr) * (muTrig + muTrigerr)).prod() muSF_down = ((muID - muIDerr) * (muIso - muIsoerr) * (muTrig - muTrigerr)).prod() evtWeight *= muSF output['photon_pt'].fill( dataset=dataset, pt=tightPhotons.p4.pt[:, :1][lep_phosel].flatten(), category=phoCategory[lep_phosel].flatten(), lepFlavor=lepFlavor[lep_phosel], weight=evtWeight[lep_phosel].flatten()) output['photon_eta'].fill( dataset=dataset, eta=tightPhotons.eta[:, :1][lep_phosel].flatten(), category=phoCategory[lep_phosel].flatten(), lepFlavor=lepFlavor[lep_phosel], weight=evtWeight[lep_phosel].flatten()) output['photon_chIsoSideband'].fill( dataset=dataset, chIso=loosePhotonsSideband.chIso[:, :1] [lep_phoselSideband].flatten(), category=phoCategorySideband[lep_phoselSideband].flatten(), lepFlavor=lepFlavor[lep_phoselSideband], weight=evtWeight[lep_phoselSideband].flatten()) output['photon_chIso'].fill( dataset=dataset, chIso=loosePhotons.chIso[:, :1][lep_phoselLoose].flatten(), category=phoCategoryLoose[lep_phoselLoose].flatten(), lepFlavor=lepFlavor[lep_phoselLoose], weight=evtWeight[lep_phoselLoose].flatten()) output['photon_lepton_mass'].fill( dataset=dataset, mass=egammaMass[lep_phosel & ele_noLoose].flatten(), category=phoCategory[lep_phosel & ele_noLoose].flatten(), lepFlavor=lepFlavor[lep_phosel & ele_noLoose], weight=evtWeight[lep_phosel & ele_noLoose].flatten()) output['photon_lepton_mass'].fill( dataset=dataset, mass=mugammaMass[lep_phosel & mu_noLoose].flatten(), category=phoCategory[lep_phosel & mu_noLoose].flatten(), lepFlavor=lepFlavor[lep_phosel & mu_noLoose], weight=evtWeight[lep_phosel & mu_noLoose].flatten()) output['photon_lepton_mass_3j0t'].fill( dataset=dataset, mass=egammaMass[lep_phosel_3j0t & ele_noLoose].flatten(), category=phoCategory[lep_phosel_3j0t & ele_noLoose].flatten(), lepFlavor=lepFlavor[lep_phosel_3j0t & ele_noLoose], weight=evtWeight[lep_phosel_3j0t & ele_noLoose].flatten()) output['photon_lepton_mass_3j0t'].fill( dataset=dataset, mass=mugammaMass[lep_phosel_3j0t & mu_noLoose].flatten(), category=phoCategory[lep_phosel_3j0t & mu_noLoose].flatten(), lepFlavor=lepFlavor[lep_phosel_3j0t & mu_noLoose], weight=evtWeight[lep_phosel_3j0t & mu_noLoose].flatten()) output['M3'].fill(dataset=dataset, M3=M3[lep_phosel].flatten(), category=phoCategoryLoose[lep_phosel].flatten(), lepFlavor=lepFlavor[lep_phosel], weight=evtWeight[lep_phosel].flatten()) output['M3Presel'].fill(dataset=dataset, M3=M3[lep_zeropho].flatten(), lepFlavor=lepFlavor[lep_zeropho], weight=evtWeight[lep_zeropho].flatten()) output['EventCount'] = len(df['event']) return output
def jagged_1(): boundaries = [0, 3, 5, 6, 9, 12, 12] return JaggedArray( boundaries[:-1], boundaries[1:], [0.0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.0, 11.0])