class jetRecalib(Module): def __init__(self, globalTag, archive, jetType = "AK4PFchs", redoJEC=False): self.redoJEC = redoJEC if "AK4" in jetType : self.jetBranchName = "Jet" elif "AK8" in jetType : self.jetBranchName = "FatJet" self.subJetBranchName = "SubJet" else: raise ValueError("ERROR: Invalid jet type = '%s'!" % jetType) self.rhoBranchName = "fixedGridRhoFastjetAll" self.lenVar = "n" + self.jetBranchName self.jesInputArchivePath = os.environ['CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/" # Text files are now tarred so must extract first into temporary directory (gets deleted during python memory management at script exit) self.jesArchive = tarfile.open(self.jesInputArchivePath+archive+".tgz", "r:gz") self.jesInputFilePath = tempfile.mkdtemp() self.jesArchive.extractall(self.jesInputFilePath) self.jetReCalibrator = JetReCalibrator(globalTag, jetType , True, self.jesInputFilePath, calculateSeparateCorrections = False, calculateType1METCorrection = False) # load libraries for accessing JES scale factors and uncertainties from txt files for library in [ "libCondFormatsJetMETObjects", "libPhysicsToolsNanoAODTools" ]: if library not in ROOT.gSystem.GetLibraries(): print("Load Library '%s'" % library.replace("lib", "")) ROOT.gSystem.Load(library) self.puppiCorrFile = ROOT.TFile.Open(os.environ['CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/puppiCorr.root") self.puppisd_corrGEN = self.puppiCorrFile.Get("puppiJECcorr_gen") self.puppisd_corrRECO_cen = self.puppiCorrFile.Get("puppiJECcorr_reco_0eta1v3") self.puppisd_corrRECO_for = self.puppiCorrFile.Get("puppiJECcorr_reco_1v3eta2v5") def beginJob(self): pass def endJob(self): pass def beginFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): self.out = wrappedOutputTree self.out.branch("%s_pt_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_pt_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_groomed_corr_PUPPI" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("MET_pt_nom" , "F") self.out.branch("MET_phi_nom", "F") self.out.branch("%s_corr_JEC" % self.jetBranchName, "F", lenVar=self.lenVar) def endFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): pass def analyze(self, event): """process event, return True (go to next module) or False (fail, go to next event)""" jets = Collection(event, self.jetBranchName ) subJets = Collection(event, self.subJetBranchName ) met = Object(event, "MET") jets_pt_raw = [] jets_pt_nom = [] jets_mass_raw = [] jets_mass_nom = [] jets_msoftdrop_raw = [] jets_msoftdrop_nom = [] jets_groomed_corr_PUPPI = [] jets_corr_JEC = [] ( met_px, met_py ) = ( met.pt*math.cos(met.phi), met.pt*math.sin(met.phi) ) ( met_px_nom, met_py_nom ) = ( met_px, met_py ) met_px_nom = met_px met_py_nom = met_py rho = getattr(event, self.rhoBranchName) for jet in jets: #jet pt and mass corrections jet_pt=jet.pt jet_mass=jet.mass #redo JECs if desired if hasattr(jet, "rawFactor"): jet_rawpt = jet_pt * (1 - jet.rawFactor) jet_rawmass = jet_mass * (1 - jet.rawFactor) else: jet_rawpt = -1.0 * jet_pt #If factor not present factor will be saved as -1 jet_rawmass = -1.0 * jet_mass #If factor not present factor will be saved as -1 if self.redoJEC : (jet_pt, jet_mass) = self.jetReCalibrator.correct(jet,rho) jets_pt_raw.append(jet_rawpt) jets_mass_raw.append(jet_rawmass) jets_corr_JEC.append(jet_pt/jet_rawpt) jet_pt_nom = jet_pt # don't smear resolution in data if jet_pt_nom < 0.0: jet_pt_nom *= -1.0 jets_pt_nom .append(jet_pt_nom) jet_mass_nom = jet_mass if jet_mass_nom < 0.0: jet_mass_nom *= -1.0 jets_mass_nom .append(jet_mass_nom) if jet_pt_nom > 15.: jet_cosPhi = math.cos(jet.phi) jet_sinPhi = math.sin(jet.phi) met_px_nom = met_px_nom - (jet_pt_nom - jet.pt)*jet_cosPhi met_py_nom = met_py_nom - (jet_pt_nom - jet.pt)*jet_sinPhi # Do PUPPI SD mass correction if jet.subJetIdx1 >= 0 and jet.subJetIdx2 >= 0 : groomedP4 = subJets[ jet.subJetIdx1 ].p4() + subJets[ jet.subJetIdx2].p4() #check subjet jecs else : groomedP4 = None jets_msoftdrop_raw.append(0.0) if groomedP4 == None else jets_msoftdrop_raw.append(groomedP4.M()) puppisd_genCorr = self.puppisd_corrGEN.Eval(jet.pt) if abs(jet.eta) <= 1.3: puppisd_recoCorr = self.puppisd_corrRECO_cen.Eval(jet.pt) else: puppisd_recoCorr = self.puppisd_corrRECO_for.Eval(jet.pt) puppisd_total = puppisd_genCorr * puppisd_recoCorr if groomedP4 != None: groomedP4.SetPtEtaPhiM(groomedP4.Perp(), groomedP4.Eta(), groomedP4.Phi(), groomedP4.M()*puppisd_total) jets_groomed_corr_PUPPI.append(puppisd_total) jets_msoftdrop_nom.append(0.0) if groomedP4 == None else jets_msoftdrop_nom.append(groomedP4.M()) self.out.fillBranch("%s_pt_raw" % self.jetBranchName, jets_pt_raw) self.out.fillBranch("%s_pt_nom" % self.jetBranchName, jets_pt_nom) self.out.fillBranch("%s_mass_raw" % self.jetBranchName, jets_mass_raw) self.out.fillBranch("%s_mass_nom" % self.jetBranchName, jets_mass_nom) self.out.fillBranch("%s_groomed_corr_PUPPI" % self.jetBranchName, jets_groomed_corr_PUPPI) self.out.fillBranch("%s_msoftdrop_raw" % self.jetBranchName, jets_msoftdrop_raw) self.out.fillBranch("%s_msoftdrop_nom" % self.jetBranchName, jets_msoftdrop_nom) self.out.fillBranch("MET_pt_nom", math.sqrt(met_px_nom**2 + met_py_nom**2)) self.out.fillBranch("MET_phi_nom", math.atan2(met_py_nom, met_px_nom)) self.out.fillBranch("%s_corr_JEC" % self.jetBranchName, jets_corr_JEC) return True
class susysinglelep(Module): def __init__( self, isMC, isSig, era, muonSelectionTag, electronSelectionTag, CorrMET ): #, HTFilter, LTFilter):#, muonSelection, electronSelection): self.isMC = isMC self.isSig = isSig self.era = era self.muonSelectionTag = muonSelectionTag self.electronSelectionTag = electronSelectionTag self.CorrMET = CorrMET #self.HTFilt = HTFilter #self.LTFilt = LTFilter # smear jet pT to account for measured difference in JER between data and simulation. self.jerInputFileName = "Spring16_25nsV10_MC_PtResolution_AK4PFchs.txt" self.jerUncertaintyInputFileName = "Spring16_25nsV10_MC_SF_AK4PFchs.txt" ###################### LepSF, JECSFs and JERSF for 2017 era ############################################################### if self.era == "2016": self.btag_LooseWP = 0.5426 self.btag_MediumWP = 0.8484 self.btag_TightWP = 0.9535 #https://twiki.cern.ch/twiki/bin/viewauth/CMS/BtagRecommendation80XReReco self.btag_DeepLooseWP = 0.2219 self.btag_DeepMediumWP = 0.6324 self.btag_DeepTightWP = 0.8958 if self.isMC: if self.muonSelectionTag == "LooseWP_2016": mu_f = ["Mu_Trg.root", "Mu_ID.root", "Mu_Iso.root"] mu_h = [ "IsoMu24_OR_IsoTkMu24_PtEtaBins/pt_abseta_ratio", "MC_NUM_LooseID_DEN_genTracks_PAR_pt_eta/pt_abseta_ratio", "LooseISO_LooseID_pt_eta/pt_abseta_ratio" ] if self.electronSelectionTag == "GPMVA90_2016": el_f = ["EGM2D_eleGSF.root", "EGM2D_eleMVA90.root"] el_h = ["EGamma_SF2D", "EGamma_SF2D"] if self.muonSelectionTag == "MediumWP_2016": mu_f = ["Mu_Trg.root", "Mu_ID.root", "Mu_Iso.root"] mu_h = [ "IsoMu24_OR_IsoTkMu24_PtEtaBins/pt_abseta_ratio", "MC_NUM_MediumID_DEN_genTracks_PAR_pt_eta/pt_abseta_ratio", "LooseISO_MediumID_pt_eta/pt_abseta_ratio" ] if self.electronSelectionTag == "Tight_2016": el_f = ["EGM2D_eleGSF.root", "EGM2D_eleMVA90.root"] el_h = ["EGamma_SF2D", "EGamma_SF2D"] #self.jerUncertaintyInputFileName = "Spring16_25nsV10_MC_SF_AK4PFchs.txt" self.jetSmearer = jetSmearer("Summer16_23Sep2016V4_MC", "AK4PFchs", self.jerInputFileName, self.jerUncertaintyInputFileName) ###################### LepSF, JECSFs and JERSF for 2017 era ############################################################### elif self.era == "2017": self.btag_LooseWP = 0.5803 self.btag_MediumWP = 0.8838 self.btag_TightWP = 0.9693 # DeepCSV (new Deep Flavour tagger) #https://twiki.cern.ch/twiki/bin/viewauth/CMS/BtagRecommendation94X self.btag_DeepLooseWP = 0.1522 self.btag_DeepMediumWP = 0.4941 self.btag_DeepTightWP = 0.8001 if self.isMC: if self.muonSelectionTag == "LooseWP_2017": mu_f = ["Mu_Trg17.root", "Mu_ID17.root", "Mu_Iso17.root"] mu_h = [ "IsoMu27_PtEtaBins/pt_abseta_ratio", "NUM_LooseID_DEN_genTracks_pt_abseta", "NUM_LooseRelIso_DEN_LooseID_pt_abseta" ] if self.electronSelectionTag == "GPMVA90_2017": el_f = ["EGM2D_eleGSF17.root", "EGM2D_eleMVA90_17.root"] el_h = ["EGamma_SF2D", "EGamma_SF2D"] if self.muonSelectionTag == "MediumWP_2017": mu_f = ["Mu_Trg17.root", "Mu_ID17.root", "Mu_Iso17.root"] mu_h = [ "IsoMu27_PtEtaBins/pt_abseta_ratio", "NUM_MediumID_DEN_genTracks_pt_abseta", "NUM_LooseRelIso_DEN_MediumID_pt_abseta" ] if self.electronSelectionTag == "Tight_2017": el_f = ["EGM2D_eleGSF17.root", "EGM2D_eleMVA90_17.root"] el_h = ["EGamma_SF2D", "EGamma_SF2D"] # Temporarly use the jetmet uncertainty for 2016 #self.jerUncertaintyInputFileName = "Spring16_25nsV10_MC_SF_AK4PFchs.txt" #self.jetSmearer = jetSmearer("Summer16_23Sep2016V4_MC", "AK4PFchs", self.jerInputFileName, self.jerUncertaintyInputFileName) #self.jerUncertaintyInputFileName = "Fall17_17Nov2017_V6_MC_Uncertainty_AK4PFchs.txt" self.jetSmearer = jetSmearer("Fall17_17Nov2017_V6_MC", "AK4PFchs", self.jerInputFileName, self.jerUncertaintyInputFileName) else: raise ValueError("ERROR: Invalid era = '%s'!" % self.era) if self.isMC: mu_f = [ "%s/src/PhysicsTools/NanoAODTools/python/postprocessing/data/leptonSF/" % os.environ['CMSSW_BASE'] + f for f in mu_f ] el_f = [ "%s/src/PhysicsTools/NanoAODTools/python/postprocessing/data/leptonSF/" % os.environ['CMSSW_BASE'] + f for f in el_f ] self.mu_f = ROOT.std.vector(str)(len(mu_f)) self.mu_h = ROOT.std.vector(str)(len(mu_f)) for i in range(len(mu_f)): self.mu_f[i] = mu_f[i] self.mu_h[i] = mu_h[i] self.el_f = ROOT.std.vector(str)(len(el_f)) self.el_h = ROOT.std.vector(str)(len(el_f)) for i in range(len(el_f)): self.el_f[i] = el_f[i] self.el_h[i] = el_h[i] self.jetReCalibrator = JetReCalibrator( "Fall17_17Nov2017_V6_MC", "AK4PFchs", True, os.environ['CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/", calculateSeparateCorrections=False, calculateType1METCorrection=False) self.unclEnThreshold = 15. if "/LeptonEfficiencyCorrector_cc.so" not in ROOT.gSystem.GetLibraries( ): print "Load C++ Worker" ROOT.gROOT.ProcessLine( ".L %s/src/PhysicsTools/NanoAODTools/python/postprocessing/helpers/LeptonEfficiencyCorrector.cc+" % os.environ['CMSSW_BASE']) pass def beginJob(self): self.jetSmearer.beginJob() if self.isMC: self._worker_mu = ROOT.LeptonEfficiencyCorrector( self.mu_f, self.mu_h) self._worker_el = ROOT.LeptonEfficiencyCorrector( self.el_f, self.el_h) pass def endJob(self): self.jetSmearer.endJob() pass def matchLeptons(self, event): def plausible(rec, gen): if abs(rec.pdgId) == 11 and abs(gen.pdgId) != 11: return False if abs(rec.pdgId) == 13 and abs(gen.pdgId) != 13: return False dr = deltaR(rec.etc, rec.phi, gen.eta, gen.phi) if dr < 0.3: return True if rec.pt < 10 and abs(rec.pdgId) == 13 and gen.pdgId != rec.pdgId: return False if dr < 0.7: return True if min(rec.pt, gen.pt) / max(rec.pt, gen.pt) < 0.3: return False return True leps = event.selectedLeptons match = matchObjectCollectionMultiple(leps, event.genleps + event.gentauleps, dRmax=1.2, presel=lambda plausible: True) for lep in leps: gen = match[lep] lep.mcMatchId = (gen.sourceId if gen != None else 0) lep.mcMatchTau = (gen in event.gentauleps if gen else -99) lep.mcLep = gen def beginFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): self.out = wrappedOutputTree self.out.branch("isData", "I") self.out.branch("nLep", "I") self.out.branch("nVeto", "I") self.out.branch("nEl", "I") self.out.branch("nMu", "I") self.out.branch("nTightLeps", "I") self.out.branch("nTightMu", "I") self.out.branch("nTightEl", "I") self.out.branch("tightLepsIdx", "I", 10, "nTightLeps") #("tightLeps_DescFlag","I",10,"nTightLeps"), self.out.branch("Lep_pdgId", "F") self.out.branch("Lep_pt", "F") self.out.branch("Lep_eta", "F") self.out.branch("Lep_phi", "F") self.out.branch("Lep_Idx", "I") self.out.branch("Lep_relIso", "F") self.out.branch("Lep_miniIso", "F") self.out.branch("Lep_hOverE", "F") self.out.branch("Selected", "I") # selected (tight) or anti-selected lepton # second leading lepton self.out.branch("Lep2_pt", "F") self.out.branch("Selected2", "I") self.out.branch("LT", "F") self.out.branch("ST", "F") self.out.branch("MT", "F") self.out.branch("DeltaPhiLepW", "F") self.out.branch("dPhi", "F") self.out.branch("Lp", "F") self.out.branch("GendPhi", "F") self.out.branch("GenLT", "F") self.out.branch("GenMET", "F") ## jets self.out.branch("ST1", "F") self.out.branch("HT1", "F") self.out.branch("HT", "F") #self.out.branch("HTphi","F"); self.out.branch("nJets", "I") self.out.branch("nBJet", "I") self.out.branch("nBJetDeep", "I") self.out.branch("nJets30", "I") self.out.branch("Jets30Idx", "I", 50, "nJets30") self.out.branch("nBJets30", "I") self.out.branch("nJets30Clean", "I") self.out.branch("nJets40", "I") self.out.branch("nBJets40", "I") self.out.branch("htJet30j", "F") self.out.branch("htJet30ja", "F") self.out.branch("htJet40j", "F") self.out.branch("Jet1_pt", "F") self.out.branch("Jet2_pt", "F") self.out.branch("Jet1_eta", "F") self.out.branch("Jet2_eta", "F") ## top tags self.out.branch("nHighPtTopTag", "I") self.out.branch("nHighPtTopTagPlusTau23", "I") ## special Vars self.out.branch("LSLjetptGT80", "F") # leading + subl. jet pt > 80 self.out.branch("isSR", "I") # is it Signal or Control region self.out.branch("Mll", "F") #di-lepton mass #self.out.branch("METfilters","I"); not needed for nanoAOD #Datasets self.out.branch("PD_JetHT", "O") self.out.branch("PD_SingleEle", "O") self.out.branch("PD_SingleMu", "O") self.out.branch("PD_MET", "O") self.out.branch("isDPhiSignal", "I") self.out.branch("RA2_muJetFilter", "I") self.out.branch("Flag_fastSimCorridorJetCleaning", "I") self.out.branch("minMWjj", "F") self.out.branch("minMWjjPt", "F") self.out.branch("bestMWjj", "F") self.out.branch("bestMWjjPt", "F") self.out.branch("bestMTopHad", "F") self.out.branch("bestMTopHadPt", "F") # self.out.branch("Jet_mhtCleaning", "b", lenVar="nJet") #Iso_track parameters self.out.branch("iso_had", "I") self.out.branch("iso_pt", "F") self.out.branch("iso_MT2", "F") self.out.branch("iso_Veto", "O") self.out.branch("nLepGood", "F") self.out.branch("nLepOther", "F") self.out.branch("LepGood_Cutbased", "I") self.out.branch("LepOther_Cutbased", "I") # Store the Xsec self.out.branch("Xsec", "F") self.xs = getXsec(inputFile.GetName()) self.filename = inputFile.GetName() print inputFile.GetName() print self.xs self.out.branch("Muon_effSF", "F") self.out.branch("Electron_effSF", "F") self.out.branch("lepSF", "F") # only for checking the electron corr self.out.branch("TightEl_eCorr", "F") self.h_eCorr_vs_eta_tight = ROOT.TH2F("h_eCorr_vs_eta_tight", "eCorr_vs_eta", 25, 0, 5, 68, -3, 3) self.h_eCorr_vs_phi_tight = ROOT.TH2F("h_eCorr_vs_phi_tight", "eCorr_vs_phi", 25, 0, 5, 140, -math.pi, math.pi) self.h_eCorr_vs_pt_tight = ROOT.TH2F("h_eCorr_vs_pt_tight", "eCorr_vs_pt", 25, 0, 5, 200, 0, 200) self.h_eCorr_vs_eta = ROOT.TH2F("h_eCorr_vs_eta", "eCorr_vs_eta", 25, 0, 30, 68, -3, 3) self.h_eCorr_vs_phi = ROOT.TH2F("h_eCorr_vs_phi", "eCorr_vs_phi", 25, 0, 30, 140, -math.pi, math.pi) self.h_eCorr_vs_pt = ROOT.TH2F("h_eCorr_vs_pt", "eCorr_vs_pt", 25, 0, 30, 200, 0, 200) self.h_eCorr_vs_eta_veto = ROOT.TH2F("h_eCorr_vs_eta_veto", "eCorr_vs_eta_veto", 25, 0, 5, 68, -3, 3) self.h_eCorr_vs_phi_veto = ROOT.TH2F("h_eCorr_vs_phi_veto", "eCorr_vs_phi_veto", 25, 0, 5, 140, -math.pi, math.pi) self.h_eCorr_vs_pt_veto = ROOT.TH2F("h_eCorr_vs_pt_veto", "eCorr_vs_pt_veto", 25, 0, 5, 200, 0, 200) self.out.branch("Met", "F") def endFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): outputFile.cd() self.h_eCorr_vs_eta_veto.Write() self.h_eCorr_vs_phi_veto.Write() self.h_eCorr_vs_pt_veto.Write() self.h_eCorr_vs_eta_tight.Write() self.h_eCorr_vs_phi_tight.Write() self.h_eCorr_vs_pt_tight.Write() self.h_eCorr_vs_eta.Write() self.h_eCorr_vs_phi.Write() self.h_eCorr_vs_pt.Write() pass def analyze(self, event): """process event, return True (go to next module) or False (fail, go to next event)""" leptonSel(event) for elect in event.allelectrons: self.h_eCorr_vs_eta.Fill(elect.eCorr, elect.eta) self.h_eCorr_vs_phi.Fill(elect.eCorr, elect.phi) self.h_eCorr_vs_pt.Fill(elect.eCorr, elect.pt) Jets = Collection(event, "Jet") met = Object(event, "MET") genmet = Object(event, "GenMET") if self.isMC or self.isSig: Gen = Collection(event, "GenPart") event.genleps = [ l for l in Gen if abs(l.pdgId) == 11 or abs(l.pdgId) == 13 ] GenTau = Collection(event, "GenVisTau") event.gentauleps = [l for l in GenTau] goodLep = [l for l in event.selectedLeptons] LepOther = [l for l in event.otherLeptons] self.out.fillBranch("nLepGood", len(goodLep)) self.out.fillBranch("nLepOther", len(LepOther)) #LepOther = goodLep leps = goodLep nlep = len(leps) # adding the ST HT filter if nlep > 0: ST1 = leps[0].pt + met.pt self.out.fillBranch("ST1", ST1) HT1 = sum([j.pt for j in Jets if (j.pt > 30 and abs(j.eta) < 2.4)]) self.out.fillBranch("HT1", HT1) ### LEPTONS Selected = False if self.isMC == False and self.isSig == False: self.out.fillBranch("isData", 1) else: self.out.fillBranch("isData", 0) # selected good leptons selectedTightLeps = [] selectedTightLepsIdx = [] selectedVetoLeps = [] # anti-selected leptons antiTightLeps = [] antiTightLepsIdx = [] antiVetoLeps = [] for idx, lep in enumerate(leps): # for acceptance check lepEta = abs(lep.eta) # Pt cut if lep.pt < 10: continue # Iso cut -- to be compatible with the trigger if lep.miniPFRelIso_all > trig_miniIsoCut: continue ################### # MUONS ################### if (abs(lep.pdgId) == 13): if lepEta > 2.4: continue ## Lower ID is POG_LOOSE (see cfg) # ID, IP and Iso check: passID = False #if muID=='ICHEPmediumMuonId': passID = lep.ICHEPmediumMuonId -->> not needed any more passID = lep.mediumId passIso = lep.miniPFRelIso_all < muo_miniIsoCut passIP = lep.sip3d < goodMu_sip3d # selected muons if passID and passIso and passIP: selectedTightLeps.append(lep) selectedTightLepsIdx.append(idx) antiVetoLeps.append(lep) else: selectedVetoLeps.append(lep) # anti-selected muons if not passIso: antiTightLeps.append(lep) antiTightLepsIdx.append(idx) else: antiVetoLeps.append(lep) ################### # ELECTRONS ################### elif (abs(lep.pdgId) == 11): if lepEta > eleEta: continue # pass variables passIso = False passConv = False if eleID == 'CB': # ELE CutBased ID eidCB = lep.cutBased passTightID = ( eidCB == 4 ) # and abs(lep.dxy) < 0.05 and abs(lep.dz) < 0.1) # and lep.convVeto) passMediumID = ( eidCB >= 3 ) # and abs(lep.dxy) < 0.05 and abs(lep.dz) < 0.1)# and lep.convVeto) #passLooseID = (eidCB >= 2) passVetoID = ( eidCB >= 1 ) # and abs(lep.dxy) < 0.2 and abs(lep.dz) < 0.5) # and lep.convVeto) # selected if passTightID: # all tight leptons are veto for anti antiVetoLeps.append(lep) # Iso check: if lep.miniPFRelIso_all < ele_miniIsoCut: passIso = True # conversion check elif eleID == 'CB': passConv = True #if lep.lostHits <= goodEl_lostHits and lep.convVeto and lep.sip3d < goodEl_sip3d else False # cuts already included in POG_Cuts_ID_SPRING15_25ns_v1_ConvVetoDxyDz_X passPostICHEPHLTHOverE = True #if (lep.hoe < 0.04 and abs(lep.eta)>1.479) or abs(lep.eta)<=1.479 else False # fill if passIso and passConv and passPostICHEPHLTHOverE: selectedTightLeps.append(lep) selectedTightLepsIdx.append(idx) else: selectedVetoLeps.append(lep) # anti-selected elif not passMediumID: #passVetoID: # all anti leptons are veto for selected selectedVetoLeps.append(lep) # Iso check passIso = lep.miniPFRelIso_all < Lep_miniIsoCut # should be true anyway # other checks passOther = False if hasattr(lep, "hoe"): passOther = lep.hoe > 0.01 # fill if passIso and passOther: antiTightLeps.append(lep) antiTightLepsIdx.append(idx) else: antiVetoLeps.append(lep) # Veto leptons elif passVetoID: # the rest is veto for selected and anti selectedVetoLeps.append(lep) antiVetoLeps.append(lep) # end lepton loop ################### # EXTRA Loop for lepOther -- for anti-selected leptons ################### otherleps = [ l for l in LepOther ] #(set(selectedTightLeps) or set(selectedVetoLeps) or set(antiTightLeps) or set(antiVetoLeps) )] nLepOther = len(otherleps) for idx, lep in enumerate(otherleps): # check acceptance lepEta = abs(lep.eta) if lepEta > 2.4: continue # Pt cut if lep.pt < 10: continue # Iso cut -- to be compatible with the trigger if lep.miniPFRelIso_all > trig_miniIsoCut: continue ############ # Muons if (abs(lep.pdgId) == 13): ## Lower ID is POG_LOOSE (see cfg) # ID, IP and Iso check: passIso = lep.miniPFRelIso_all > muo_miniIsoCut #if passIso and passID and passIP: if passIso: antiTightLeps.append(lep) antiTightLepsIdx.append(idx) else: antiVetoLeps.append(lep) ############ # Electrons elif (abs(lep.pdgId) == 11): if (lepEta > eleEta): continue ## Iso selection: ele should have MiniIso < 0.4 (for trigger) if lep.miniPFRelIso_all > Lep_miniIsoCut: continue ## Set Ele IDs if eleID == 'CB': # ELE CutBased ID eidCB = lep.cutBased passMediumID = ( eidCB >= 3 ) # and lep.convVeto and abs(lep.dxy) < 0.05 and abs(lep.dz) < 0.1) passVetoID = ( eidCB >= 1 ) #and lep.convVeto and abs(lep.dxy) < 0.05 and abs(lep.dz) < 0.1) else: passMediumID = False passVetoID = False # Cuts for Anti-selected electrons if not passMediumID: # should always be true for LepOther # other checks passOther = False if hasattr(lep, "hoe"): passOther = lep.hoe > 0.01 #if not lep.conVeto: if passOther: antiTightLeps.append(lep) antiTightLepsIdx.append(idx) else: antiVetoLeps.append(lep) elif passVetoID: #all Medium+ eles in LepOther antiVetoLeps.append(lep) ############# ############# # retrive and fill branches ############# # choose common lepton collection: select selected or anti lepton if len(selectedTightLeps) > 0: tightLeps = selectedTightLeps tightLepsIdx = selectedTightLepsIdx vetoLeps = selectedVetoLeps self.out.fillBranch("nTightLeps", len(tightLeps)) self.out.fillBranch( "nTightMu", sum([abs(lep.pdgId) == 13 for lep in tightLeps])) self.out.fillBranch( "nTightEl", sum([abs(lep.pdgId) == 11 for lep in tightLeps])) self.out.fillBranch("tightLepsIdx", tightLepsIdx) self.out.fillBranch("Selected", 1) # Is second leading lepton selected, too? if len(selectedTightLeps) > 1: self.out.fillBranch("Selected2", 1) else: self.out.fillBranch("Selected2", 0) elif len(antiTightLeps) > 0: tightLeps = antiTightLeps tightLepsIdx = antiTightLepsIdx vetoLeps = antiVetoLeps self.out.fillBranch("nTightLeps", 0) self.out.fillBranch("nTightMu", 0) self.out.fillBranch("nTightEl", 0) self.out.fillBranch("tightLepsIdx", []) self.out.fillBranch("Selected", -1) else: tightLeps = [] tightLepsIdx = [] vetoLeps = [] self.out.fillBranch("nTightLeps", 0) self.out.fillBranch("nTightMu", 0) self.out.fillBranch("nTightEl", 0) self.out.fillBranch("tightLepsIdx", []) self.out.fillBranch("Selected", 0) # store Tight and Veto lepton numbers self.out.fillBranch("nLep", len(tightLeps)) self.out.fillBranch("nVeto", len(vetoLeps)) # get number of tight el and mu tightEl = [lep for lep in tightLeps if abs(lep.pdgId) == 11] tightMu = [lep for lep in tightLeps if abs(lep.pdgId) == 13] VetoEl = [lep for lep in vetoLeps if abs(lep.pdgId) == 11] VetoMu = [lep for lep in vetoLeps if abs(lep.pdgId) == 13] self.out.fillBranch("nEl", len(tightEl)) self.out.fillBranch("nMu", len(tightMu)) for El in tightEl: # this branch is for investigating the electron energy calibraition self.out.fillBranch("TightEl_eCorr", El.eCorr) self.h_eCorr_vs_eta_tight.Fill(El.eCorr, El.eta) self.h_eCorr_vs_phi_tight.Fill(El.eCorr, El.phi) self.h_eCorr_vs_pt_tight.Fill(El.eCorr, El.pt) for El in VetoEl: self.h_eCorr_vs_eta_veto.Fill(El.eCorr, El.eta) self.h_eCorr_vs_phi_veto.Fill(El.eCorr, El.phi) self.h_eCorr_vs_pt_veto.Fill(El.eCorr, El.pt) # save leading lepton vars if len(tightLeps) > 0: # leading tight lep self.out.fillBranch("Lep_Idx", tightLepsIdx[0]) self.out.fillBranch("Lep_pt", tightLeps[0].pt) self.out.fillBranch("Lep_eta", tightLeps[0].eta) self.out.fillBranch("Lep_phi", tightLeps[0].phi) self.out.fillBranch("Lep_pdgId", tightLeps[0].pdgId) self.out.fillBranch("Lep_relIso", tightLeps[0].pfRelIso03_all) self.out.fillBranch("Lep_miniIso", tightLeps[0].miniPFRelIso_all) if hasattr(tightLeps[0], "hoe"): self.out.fillBranch("Lep_hOverE", tightLeps[0].hoe) if self.isMC: for tlep in tightLeps: if abs(tlep.pdgId) == 13: sf_mu = self._worker_mu.getSF(tlep.pdgId, tlep.pt, tlep.eta) self.out.fillBranch("lepSF", sf_mu) self.out.fillBranch("Muon_effSF", sf_mu) elif abs(tlep.pdgId) == 11: sf_el = self._worker_el.getSF(tlep.pdgId, tlep.pt, tlep.eta) self.out.fillBranch("lepSF", sf_el) self.out.fillBranch("Electron_effSF", sf_el) else: self.out.fillBranch("Muon_effSF", 1.0) self.out.fillBranch("Electron_effSF", 1.0) self.out.fillBranch("lepSF", 1.0) else: self.out.fillBranch("Muon_effSF", 1.0) self.out.fillBranch("Electron_effSF", 1.0) self.out.fillBranch("lepSF", 1.0) elif len(leps) > 0: # fill it with leading lepton self.out.fillBranch("Lep_Idx", 0) self.out.fillBranch("Lep_pt", leps[0].pt) self.out.fillBranch("Lep_eta", leps[0].eta) self.out.fillBranch("Lep_phi", leps[0].phi) self.out.fillBranch("Lep_pdgId", leps[0].pdgId) self.out.fillBranch("Lep_relIso", leps[0].pfRelIso03_all) self.out.fillBranch("Lep_miniIso", leps[0].miniPFRelIso_all) if hasattr(leps[0], "hoe"): self.out.fillBranch("Lep_hOverE", leps[0].hoe) # save second leading lepton vars if len(tightLeps) > 1: # 2nd tight lep self.out.fillBranch("Lep2_pt", tightLeps[1].pt) # print " Nlep : ", len(leps) , " nTightleps " , len(tightLeps), " nVetoLEp ", len(vetoLeps) ##################################################################### ##################################################################### #################Jets, BJets, METS and filters ###################### ##################################################################### ##################################################################### ######## ### Jets ######## metp4 = ROOT.TLorentzVector(0, 0, 0, 0) Genmetp4 = ROOT.TLorentzVector(0, 0, 0, 0) if self.isMC: Genmetp4.SetPtEtaPhiM(genmet.pt, 0, genmet.phi, 0) #self.out.fillBranch("MET", metp4.Pt()) Jets = Collection(event, "Jet") jets = [j for j in Jets if j.pt > 20 and abs(j.eta) < 2.4] njet = len(jets) (met_px, met_py) = (met.pt * math.cos(met.phi), met.pt * math.sin(met.phi)) (met_px_nom, met_py_nom) = (met_px, met_py) # match reconstructed jets to generator level ones # (needed to evaluate JER scale factors and uncertainties) if self.isMC and self.CorrMET: rho = getattr(event, "fixedGridRhoFastjetAll") genJets = Collection(event, "GenJet") pairs = matchObjectCollection(Jets, genJets) for jet in jets: genJet = pairs[jet] (jet_pt_jerNomVal, jet_pt_jerUpVal, jet_pt_jerDownVal) = self.jetSmearer.getSmearValsPt( jet, genJet, rho) # exclude this from the JECs correction to follow the SUSY recipe, well see if it will slove the prefire issue if self.era == "2017": if jet.pt < 75 and (2.0 < abs(jet.eta) < 3.0): jet_pt = jet.pt * (1. - jet.rawFactor) else: jet_pt = self.jetReCalibrator.correct(jet, rho) else: jet_pt = jet.pt jet_pt_nom = jet_pt_jerNomVal * jet_pt if jet_pt_nom < 0.0: jet_pt_nom *= -1.0 jet_pt_jerUp = jet_pt_jerUpVal * jet_pt jet_pt_jerDown = jet_pt_jerDownVal * jet_pt # recalculate the MET after applying the JEC JER if jet_pt > 15.: jet_cosPhi = math.cos(jet.phi) jet_sinPhi = math.sin(jet.phi) met_px_nom = met_px_nom - (jet_pt_nom - jet_pt) * jet_cosPhi met_py_nom = met_py_nom - (jet_pt_nom - jet_pt) * jet_sinPhi met_pt_nom = math.sqrt(met_px_nom**2 + met_py_nom**2) met_phi_nom = math.atan2(met_py_nom, met_px_nom) met.pt = met_pt_nom met.phi = met_phi_nom # JECs already applied so apply only JER jet.pt = jet_pt_jerNomVal * jet.pt if jet.pt < 0.0: jet.pt *= -1.0 metp4.SetPtEtaPhiM( met.pt, 0., met.phi, 0.) # only use met vector to derive transverse quantities) self.out.fillBranch("Met", met.pt) centralJet30 = [] centralJet30idx = [] centralJet40 = [] cleanJets25 = [] cleanJets25idx = [] cleanJets = [] cleanJetsidx = [] # fill this flage but defults to 1 and then change it after the proper selection self.out.fillBranch("Flag_fastSimCorridorJetCleaning", 1) for i, j in enumerate(jets): # Cleaning up of fastsim jets (from "corridor" studies) https://twiki.cern.ch/twiki/bin/viewauth/CMS/SUSRecommendationsMoriond17#Cleaning_up_of_fastsim_jets_from if self.isSig: #only check for signals (see condition check above) self.out.fillBranch("isDPhiSignal", 1) genj = pairs[j] if genj is not None: if j.pt > 20 and abs( j.eta) < 2.5 and (genj.pt == 0) and j.chHEF < 0.1: self.out.fillBranch("Flag_fastSimCorridorJetCleaning", 0) if j.pt > 25: cleanJets25.append(j) cleanJets25idx.append(j) if j.pt > 30 and abs(j.eta) < centralEta: centralJet30.append(j) centralJet30idx.append(i) if j.pt > 40 and abs(j.eta) < centralEta: centralJet40.append(j) # jets 30 (cmg cleaning only) nJetC = len(centralJet30) self.out.fillBranch("nJets", nJetC) self.out.fillBranch("nJets30", nJetC) # store indeces self.out.fillBranch("Jets30Idx", centralJet30idx) #print "nJets30:", len(centralJet30), " nIdx:", len(centralJet30idx) # jets 40 nJet40C = len(centralJet40) self.out.fillBranch("nJets40", nJet40C) ############################## ## Local cleaning from leptons ############################## cJet30Clean = [] dRminCut = 0.4 # Do cleaning a la CMG: clean max 1 jet for each lepton (the nearest) cJet30Clean = centralJet30 cleanJets30 = centralJet30 #clean selected leptons at First '''for lep in goodLep: if lep.pt < 20 : continue jNear, dRmin = None, 99 # find nearest jet for jet in centralJet30: dR = jet.p4().DeltaR(lep.p4()) if dR < dRmin: jNear, dRmin = jet, dR # remove nearest jet if dRmin < dRminCut: cJet30Clean.remove(jNear)''' #then clean other tight leptons for lep in tightLeps: # don't clean LepGood, only LepOther #if lep not in otherleps: continue jNear, dRmin = None, 99 # find nearest jet for jet in centralJet30: dR = jet.p4().DeltaR(lep.p4()) if dR < dRmin: jNear, dRmin = jet, dR # remove nearest jet if dRmin < dRminCut: cJet30Clean.remove(jNear) for ijet, jet25 in enumerate(cleanJets25): dR = jet25.p4().DeltaR(lep.p4()) if dR < dRmin: cleanJets.append(jet25) cleanJetsidx.append(ijet) # cleaned jets nJet30C = len(cJet30Clean) self.out.fillBranch("nJets30Clean", len(cJet30Clean)) if nJet30C > 0: self.out.fillBranch("Jet1_pt", cJet30Clean[0].pt) self.out.fillBranch("Jet1_eta", cJet30Clean[0].eta) if nJet30C > 1: self.out.fillBranch("Jet2_pt", cJet30Clean[1].pt) self.out.fillBranch("Jet2_eta", cJet30Clean[1].eta) # imho, use Jet2_pt > 80 instead self.out.fillBranch( "LSLjetptGT80", 1 if sum([j.pt > 80 for j in cJet30Clean]) >= 2 else 0) self.out.fillBranch("htJet30j", sum([j.pt for j in cJet30Clean])) self.out.fillBranch("htJet30ja", sum([j.pt for j in jets if j.pt > 30])) self.out.fillBranch("htJet40j", sum([j.pt for j in centralJet40])) self.out.fillBranch("HT", sum([j.pt for j in cJet30Clean])) ## B tagging WPs for CSVv2 (CSV-IVF) ## from: https://twiki.cern.ch/twiki/bin/view/CMSPublic/SWGuideBTagging#Preliminary_working_or_operating # WP defined on top btagWP = self.btag_MediumWP btag_DeepMediumWP = self.btag_DeepMediumWP btag_LooseWP = self.btag_LooseWP BJetMedium30 = [] BJetMedium40 = [] nBJetDeep = 0 for i, j in enumerate(cJet30Clean): if j.btagCSVV2 > btagWP: BJetMedium30.append(j) if (j.btagDeepB) > btag_DeepMediumWP: nBJetDeep += 1 for i, j in enumerate(centralJet40): if j.btagCSVV2 > btagWP: BJetMedium40.append(j) # using cleaned collection! self.out.fillBranch("nBJet", len(BJetMedium30)) self.out.fillBranch("nBJets30", len(BJetMedium30)) self.out.fillBranch("nBJetDeep", nBJetDeep) # using normal collection self.out.fillBranch("nBJets40", len(BJetMedium40)) ###### # MET ##### ##################################################################### ##################################################################### #################High level variables ############################### ##################################################################### ##################################################################### dPhiLepW = -999 # set default value to -999 to spot "empty" entries GendPhiLepW = -999 # set default value to -999 to spot "empty" entries # LT of lepton and MET LT = -999 GenLT = -999 Lp = -99 MT = -99 if len(tightLeps) >= 1: recoWp4 = tightLeps[0].p4() + metp4 GenrecoWp4 = tightLeps[0].p4() + Genmetp4 GendPhiLepW = tightLeps[0].p4().DeltaPhi(GenrecoWp4) GenLT = tightLeps[0].pt + Genmetp4.Pt() dPhiLepW = tightLeps[0].p4().DeltaPhi(recoWp4) LT = tightLeps[0].pt + metp4.Pt() Lp = tightLeps[0].pt / recoWp4.Pt() * math.cos(dPhiLepW) #MT = recoWp4.Mt() # doesn't work MT = math.sqrt(2 * metp4.Pt() * tightLeps[0].pt * (1 - math.cos(dPhiLepW))) self.out.fillBranch("DeltaPhiLepW", dPhiLepW) dPhi = abs(dPhiLepW) # nickname for absolute dPhiLepW self.out.fillBranch("dPhi", dPhi) self.out.fillBranch("ST", LT) self.out.fillBranch("LT", LT) self.out.fillBranch("Lp", Lp) self.out.fillBranch("MT", MT) self.out.fillBranch("GendPhi", abs(GendPhiLepW)) self.out.fillBranch("GenLT", GenLT) self.out.fillBranch("GenMET", Genmetp4.Pt()) ##################### ## SIGNAL REGION FLAG ##################### ## Signal region flag # isSR SR vs CR flag isSR = 0 # 0-B SRs -- simplified dPhi if len(BJetMedium30) == 0: # check the no. of Bjets if LT < 250: isSR = 0 elif LT > 250: isSR = dPhi > 0.75 # BLIND data if (not self.isMC) and nJet30C >= 5: isSR = -isSR # Multi-B SRs elif nJet30C < 99: if LT < 250: isSR = 0 elif LT < 350: isSR = dPhi > 1.0 elif LT < 600: isSR = dPhi > 0.75 elif LT > 600: isSR = dPhi > 0.5 # BLIND data if (not self.isMC) and nJet30C >= 6: isSR = -isSR self.out.fillBranch("isSR", isSR) ############# ## Playground ############# # di-lepton mass: opposite-sign, same flavour Mll = 0 if len(tightLeps) > 1: lep1 = tightLeps[0] id1 = lep1.pdgId for lep2 in leps[1:]: if lep2.pdgId + lep1.pdgId == 0: dilepP4 = lep1.p4() + lep2.p4() Mll = dilepP4.M() self.out.fillBranch("Mll", Mll) # RA2 proposed filter self.out.fillBranch( "RA2_muJetFilter", True ) # normally true for now # don't know how to get the Muon energy fraction from EMEF #for j in cJet30Clean: # if j.pt > 200 and j.chEmEF > 0.5 and abs(math.acos(math.cos(j.phi-metp4.Phi()))) > (math.pi - 0.4): # self.out.fillBranch("RA2_muJetFilter", False) ## MET FILTERS for data looks like the met filters are applied already for nanoAOD ##################### ## Top Tagging ------->>>>>>. to be moved to different modules keep it commented here ##################### lightJets = [j for j in cleanJets if not j.btagCSVV2 == btagWP] bjetsLoose = [j for j in cleanJets if j.btagCSVV2 == btag_LooseWP] minMWjj = 999 minMWjjPt = 0 bestMWjj = 0 bestMWjjPt = 0 bestMTopHad = 0 bestMTopHadPt = 0 for i1, j1 in enumerate(lightJets): for i2 in xrange(i1 + 1, len(lightJets)): j2 = lightJets[i2] jjp4 = j1.p4() + j2.p4() mjj = jjp4.M() if mjj > 30 and mjj < minMWjj: minMWjj = mjj minMWjjPt = jjp4.Pt() self.out.fillBranch("minMWjj", minMWjj) self.out.fillBranch("minMWjjPt", minMWjjPt) if abs(mjj - 80.4) < abs(bestMWjj - 80.4): bestMWjj = mjj bestMWjjPt = jjp4.Pt() self.out.fillBranch("bestMWjj", bestMWjj) self.out.fillBranch("bestMWjjPt", bestMWjjPt) for bj in bjetsLoose: if deltaR(bj.eta(), bj.phi(), j1.eta(), j1.phi()) < 0.1 or deltaR( bj.eta(), bj.phi(), j2.eta(), j2.phi()) < 0.1: continue tp4 = jjp4 + bj.p4() mtop = tp4.M() if abs(mtop - 172) < abs(bestMTopHad - 172): bestMTopHad = mtop bestMTopHadPt = tp4.Pt() self.out.fillBranch("bestMTopHad", bestMTopHad) self.out.fillBranch("bestMTopHadPt", bestMTopHadPt) ##################################################################### ##################################################################### #################Fill MT2 here, not point to make a separate module## ##################################################################### ##################################################################### # isolated tracks after basic selection (((pt>5 && (abs(pdgId) == 11 || abs(pdgId) == 13)) || pt > 10) && (abs(pdgId) < 15 || abs(eta) < 2.5) && abs(dxy) < 0.2 && abs(dz) < 0.1 && ((pfIsolationDR03().chargedHadronIso < 5 && pt < 25) || pfIsolationDR03().chargedHadronIso/pt < 0.2)) and lepton veto # First check is the event has the IsoTrack or not if hasattr(event, "nIsoTrack"): trks = [j for j in Collection(event, "IsoTrack", "nIsoTrack")] tp4 = ROOT.TLorentzVector(0, 0, 0, 0) # min dR between good lep and iso track minDR = 0.1 # MT2 cuts for hadronic and leptonic veto tracks hadMT2cut = 60 lepMT2cut = 80 if (len(tightLeps) >= 1) and len(trks) >= 1: for i, t in enumerate(trks): # looking for opposite charged tracks #if tightLeps[0].charge == t.charge: continue # not track charge is founded replace with the next copule of lines if t.isHighPurityTrack == False: continue #print t.miniPFRelIso_chg # not track mass is founded tp4.SetPtEtaPhiM(t.pt, t.eta, t.phi, 0.) dR = tp4.DeltaR(tightLeps[0].p4()) if minDR > dR: continue p1 = tightLeps[0].p4() p2 = tp4 a = array.array('d', [p1.M(), p1.Px(), p1.Py()]) b = array.array('d', [p2.M(), p2.Px(), p2.Py()]) c = array.array('d', [metp4.M(), metp4.Px(), metp4.Py()]) mt2obj.set_momenta(a, b, c) mt2obj.set_mn(0) self.out.fillBranch("iso_MT2", mt2obj.get_mt2()) self.out.fillBranch("iso_pt", p2.Pt()) # cuts on MT2 as defined above if abs(t.pdgId) > 10 and abs(t.pdgId) < 14: self.out.fillBranch("iso_had", 0) #leptonic cut = lepMT2cut else: self.out.fillBranch("iso_had", 1) #hadronic track cut = hadMT2cut if mt2obj.get_mt2() <= cut: self.out.fillBranch("iso_Veto", True) self.out.fillBranch("Xsec", self.xs) if 'JetHT' in self.filename: self.out.fillBranch("PD_JetHT", True) else: self.out.fillBranch("PD_JetHT", False) if 'SingleEle' in self.filename: self.out.fillBranch("PD_SingleEle", True) else: self.out.fillBranch("PD_SingleEle", False) if 'SingleMu' in self.filename: self.out.fillBranch("PD_SingleMu", True) else: self.out.fillBranch("PD_SingleMu", False) if 'MET' in self.filename: self.out.fillBranch("PD_MET", True) else: self.out.fillBranch("PD_MET", False) return True
class jetmetUncertaintiesProducer(Module): def __init__(self, era, globalTag, jesUncertainties=["Total"], jetType="AK4PFchs", redoJEC=False, noGroom=False): self.era = era self.redoJEC = redoJEC self.noGroom = noGroom #-------------------------------------------------------------------------------------------- # CV: globalTag and jetType not yet used, as there is no consistent set of txt files for # JES uncertainties and JER scale factors and uncertainties yet #-------------------------------------------------------------------------------------------- self.jesUncertainties = jesUncertainties # smear jet pT to account for measured difference in JER between data and simulation. self.jerInputFileName = "Spring16_25nsV10_MC_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = "Spring16_25nsV10_MC_SF_" + jetType + ".txt" self.jetSmearer = jetSmearer(globalTag, jetType, self.jerInputFileName, self.jerUncertaintyInputFileName) if "AK4" in jetType: self.jetBranchName = "Jet" self.genJetBranchName = "GenJet" self.genSubJetBranchName = None self.doGroomed = False self.corrMET = True elif "AK8" in jetType: self.jetBranchName = "FatJet" self.subJetBranchName = "SubJet" self.genJetBranchName = "GenJetAK8" self.genSubJetBranchName = "SubGenJetAK8" if not self.noGroom: self.doGroomed = True else: self.doGroomed = False self.corrMET = False else: raise ValueError("ERROR: Invalid jet type = '%s'!" % jetType) self.metBranchName = "MET" self.rhoBranchName = "fixedGridRhoFastjetAll" self.lenVar = "n" + self.jetBranchName # To do : change to real values self.jmsVals = [1.00, 0.99, 1.01] # read jet energy scale (JES) uncertainties # (downloaded from https://twiki.cern.ch/twiki/bin/view/CMS/JECDataMC ) self.jesInputFilePath = os.environ[ 'CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/" if len(jesUncertainties) == 1 and jesUncertainties[0] == "Total": if self.era == "2016": self.jesUncertaintyInputFileName = "Summer16_23Sep2016V4_MC_Uncertainty_" + jetType + ".txt" elif self.era == "2017": self.jesUncertaintyInputFileName = "Fall17_17Nov2017_V6_MC_Uncertainty_" + jetType + ".txt" else: raise ValueError("ERROR: Invalid era = '%s'!" % self.era) else: if self.era == "2016": self.jesUncertaintyInputFileName = "Summer16_23Sep2016V4_MC_UncertaintySources_" + jetType + ".txt" elif self.era == "2017": self.jesUncertaintyInputFileName = "Fall17_17Nov2017_V6_MC_UncertaintySources_" + jetType + ".txt" else: raise ValueError("ERROR: Invalid era = '%s'!" % self.era) # read all uncertainty source names from the loaded file if jesUncertainties[0] == "All": with open(self.jesInputFilePath + self.jesUncertaintyInputFileName) as f: lines = f.read().split("\n") sources = filter( lambda x: x.startswith("[") and x.endswith("]"), lines) sources = map(lambda x: x[1:-1], sources) self.jesUncertainties = sources if self.redoJEC: self.jetReCalibrator = JetReCalibrator( globalTag, jetType, True, self.jesInputFilePath, calculateSeparateCorrections=False, calculateType1METCorrection=False) # define energy threshold below which jets are considered as "unclustered energy" # (cf. JetMETCorrections/Type1MET/python/correctionTermsPfMetType1Type2_cff.py ) self.unclEnThreshold = 15. # load libraries for accessing JES scale factors and uncertainties from txt files for library in [ "libCondFormatsJetMETObjects", "libPhysicsToolsNanoAODTools" ]: if library not in ROOT.gSystem.GetLibraries(): print("Load Library '%s'" % library.replace("lib", "")) ROOT.gSystem.Load(library) def beginJob(self): print("Loading jet energy scale (JES) uncertainties from file '%s'" % os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName)) #self.jesUncertainty = ROOT.JetCorrectionUncertainty(os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName)) self.jesUncertainty = {} # implementation didn't seem to work for factorized JEC, try again another way for jesUncertainty in self.jesUncertainties: jesUncertainty_label = jesUncertainty if self.era == "2016" and jesUncertainty == 'Total' and len( self.jesUncertainties) == 1: jesUncertainty_label = '' pars = ROOT.JetCorrectorParameters( os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName), jesUncertainty_label) self.jesUncertainty[ jesUncertainty] = ROOT.JetCorrectionUncertainty(pars) self.jetSmearer.beginJob() def endJob(self): self.jetSmearer.endJob() def beginFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): self.out = wrappedOutputTree self.out.branch("%s_pt_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_nom" % self.jetBranchName, "F", lenVar=self.lenVar) if self.doGroomed: self.out.branch("%s_msoftdrop_nom" % self.jetBranchName, "F", lenVar=self.lenVar) if self.corrMET: self.out.branch("%s_pt_nom" % self.metBranchName, "F") self.out.branch("%s_phi_nom" % self.metBranchName, "F") for shift in ["Up", "Down"]: self.out.branch("%s_pt_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jmr%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jms%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) if self.doGroomed: self.out.branch("%s_msoftdrop_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_jmr%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_jms%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) if self.corrMET: self.out.branch("%s_pt_jer%s" % (self.metBranchName, shift), "F") self.out.branch("%s_phi_jer%s" % (self.metBranchName, shift), "F") for jesUncertainty in self.jesUncertainties: self.out.branch("%s_pt_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) if self.doGroomed: self.out.branch( "%s_msoftdrop_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) if self.corrMET: self.out.branch( "%s_pt_jes%s%s" % (self.metBranchName, jesUncertainty, shift), "F") self.out.branch( "%s_phi_jes%s%s" % (self.metBranchName, jesUncertainty, shift), "F") if self.corrMET: self.out.branch( "%s_pt_unclustEn%s" % (self.metBranchName, shift), "F") self.out.branch( "%s_phi_unclustEn%s" % (self.metBranchName, shift), "F") def endFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): pass def analyze(self, event): """process event, return True (go to next module) or False (fail, go to next event)""" jets = Collection(event, self.jetBranchName) genJets = Collection(event, self.genJetBranchName) if self.doGroomed: subJets = Collection(event, self.subJetBranchName) genSubJets = Collection(event, self.genSubJetBranchName) genSubJetMatcher = matchObjectCollectionMultiple(genJets, genSubJets, dRmax=0.8) jets_pt_nom = [] jets_pt_jerUp = [] jets_pt_jerDown = [] jets_pt_jesUp = {} jets_pt_jesDown = {} jets_mass_nom = [] jets_mass_jerUp = [] jets_mass_jerDown = [] jets_mass_jmrUp = [] jets_mass_jmrDown = [] jets_mass_jesUp = {} jets_mass_jesDown = {} jets_mass_jmsUp = [] jets_mass_jmsDown = [] for jesUncertainty in self.jesUncertainties: jets_pt_jesUp[jesUncertainty] = [] jets_pt_jesDown[jesUncertainty] = [] jets_mass_jesUp[jesUncertainty] = [] jets_mass_jesDown[jesUncertainty] = [] if self.corrMET: met = Object(event, self.metBranchName) (met_px, met_py) = (met.pt * math.cos(met.phi), met.pt * math.sin(met.phi)) (met_px_nom, met_py_nom) = (met_px, met_py) (met_px_jerUp, met_py_jerUp) = (met_px, met_py) (met_px_jerDown, met_py_jerDown) = (met_px, met_py) (met_px_jesUp, met_py_jesUp) = ({}, {}) (met_px_jesDown, met_py_jesDown) = ({}, {}) for jesUncertainty in self.jesUncertainties: met_px_jesUp[jesUncertainty] = met_px met_py_jesUp[jesUncertainty] = met_py met_px_jesDown[jesUncertainty] = met_px met_py_jesDown[jesUncertainty] = met_py if self.doGroomed: jets_msdcorr_nom = [] jets_msdcorr_jerUp = [] jets_msdcorr_jerDown = [] jets_msdcorr_jmrUp = [] jets_msdcorr_jmrDown = [] jets_msdcorr_jesUp = {} jets_msdcorr_jesDown = {} jets_msdcorr_jmsUp = [] jets_msdcorr_jmsDown = [] for jesUncertainty in self.jesUncertainties: jets_msdcorr_jesUp[jesUncertainty] = [] jets_msdcorr_jesDown[jesUncertainty] = [] rho = getattr(event, self.rhoBranchName) # match reconstructed jets to generator level ones # (needed to evaluate JER scale factors and uncertainties) pairs = matchObjectCollection(jets, genJets) for jet in jets: genJet = pairs[jet] if self.doGroomed: genGroomedSubJets = genSubJetMatcher[ genJet] if genJet != None else None genGroomedJet = genGroomedSubJets[0].p4() + genGroomedSubJets[ 1].p4() if genGroomedSubJets != None and len( genGroomedSubJets) >= 2 else None if jet.subJetIdx1 >= 0 and jet.subJetIdx2 >= 0: groomedP4 = subJets[jet.subJetIdx1].p4() + subJets[ jet.subJetIdx2].p4() else: groomedP4 = None # evaluate JER scale factors and uncertainties # (cf. https://twiki.cern.ch/twiki/bin/view/CMS/JetResolution and https://twiki.cern.ch/twiki/bin/view/CMSPublic/WorkBookJetEnergyResolution ) (jet_pt_jerNomVal, jet_pt_jerUpVal, jet_pt_jerDownVal) = self.jetSmearer.getSmearValsPt( jet, genJet, rho) jet_pt = jet.pt if self.redoJEC: jet_pt = self.jetReCalibrator.correct(jet, rho) jet_pt_nom = jet_pt_jerNomVal * jet_pt if jet_pt_nom < 0.0: jet_pt_nom *= -1.0 jet_pt_jerUp = jet_pt_jerUpVal * jet_pt jet_pt_jerDown = jet_pt_jerDownVal * jet_pt jets_pt_nom.append(jet_pt_nom) jets_pt_jerUp.append(jet_pt_jerUpVal * jet_pt) jets_pt_jerDown.append(jet_pt_jerDownVal * jet_pt) # evaluate JES uncertainties jet_pt_jesUp = {} jet_pt_jesDown = {} jet_mass_jesUp = {} jet_mass_jesDown = {} jet_mass_jmsUp = [] jet_mass_jmsDown = [] # Evaluate JMS and JMR scale factors and uncertainties jmsNomVal = self.jmsVals[0] jmsDownVal = self.jmsVals[1] jmsUpVal = self.jmsVals[2] (jet_mass_jmrNomVal, jet_mass_jmrUpVal, jet_mass_jmrDownVal) = self.jetSmearer.getSmearValsM(jet, genJet) jet_mass_nom = jet_pt_jerNomVal * jet_mass_jmrNomVal * jmsNomVal * jet.mass if jet_mass_nom < 0.0: jet_mass_nom *= -1.0 jets_mass_nom.append(jet_mass_nom) jets_mass_jerUp.append(jet_pt_jerUpVal * jet_mass_jmrNomVal * jmsNomVal * jet.mass) jets_mass_jerDown.append(jet_pt_jerDownVal * jet_mass_jmrNomVal * jmsNomVal * jet.mass) jets_mass_jmrUp.append(jet_pt_jerNomVal * jet_mass_jmrUpVal * jmsNomVal * jet.mass) jets_mass_jmrDown.append(jet_pt_jerNomVal * jet_mass_jmrDownVal * jmsNomVal * jet.mass) jets_mass_jmsUp.append(jet_pt_jerNomVal * jet_mass_jmrNomVal * jmsUpVal * jet.mass) jets_mass_jmsDown.append(jet_pt_jerNomVal * jet_mass_jmrNomVal * jmsDownVal * jet.mass) if self.doGroomed: # to evaluate JES uncertainties jet_msdcorr_jmsUp = [] jet_msdcorr_jmsDown = [] jet_msdcorr_jesUp = {} jet_msdcorr_jesDown = {} (jet_msdcorr_jmrNomVal, jet_msdcorr_jmrUpVal, jet_msdcorr_jmrDownVal) = self.jetSmearer.getSmearValsM( groomedP4, genGroomedJet ) if groomedP4 != None and genGroomedJet != None else (0., 0., 0.) jet_msdcorr_raw = groomedP4.M() if groomedP4 != None else 0.0 if jet_msdcorr_raw < 0.0: jet_msdcorr_raw *= -1.0 jet_msdcorr_nom = jet_pt_jerNomVal * jet_msdcorr_jmrNomVal * jet_msdcorr_raw jets_msdcorr_nom.append(jet_msdcorr_nom) jets_msdcorr_jerUp.append(jet_pt_jerUpVal * jet_msdcorr_jmrNomVal * jmsNomVal * jet_msdcorr_raw) jets_msdcorr_jerDown.append(jet_pt_jerDownVal * jet_msdcorr_jmrNomVal * jmsNomVal * jet_msdcorr_raw) jets_msdcorr_jmrUp.append(jet_pt_jerNomVal * jet_msdcorr_jmrUpVal * jmsNomVal * jet_msdcorr_raw) jets_msdcorr_jmrDown.append( jet_pt_jerNomVal * jet_msdcorr_jmrDownVal * jmsNomVal * jet_msdcorr_raw) jets_msdcorr_jmsUp.append(jet_pt_jerNomVal * jet_msdcorr_jmrNomVal * jmsUpVal * jet_msdcorr_raw) jets_msdcorr_jmsDown.append( jet_pt_jerNomVal * jet_msdcorr_jmrNomVal * jmsDownVal * jet_msdcorr_raw) for jesUncertainty in self.jesUncertainties: # (cf. https://twiki.cern.ch/twiki/bin/view/CMSPublic/WorkBookJetEnergyCorrections#JetCorUncertainties ) self.jesUncertainty[jesUncertainty].setJetPt(jet_pt_nom) self.jesUncertainty[jesUncertainty].setJetEta(jet.eta) delta = self.jesUncertainty[jesUncertainty].getUncertainty( True) jet_pt_jesUp[jesUncertainty] = jet_pt_nom * (1. + delta) jet_pt_jesDown[jesUncertainty] = jet_pt_nom * (1. - delta) jets_pt_jesUp[jesUncertainty].append( jet_pt_jesUp[jesUncertainty]) jets_pt_jesDown[jesUncertainty].append( jet_pt_jesDown[jesUncertainty]) jet_mass_jesUp[jesUncertainty] = jet_mass_nom * (1. + delta) jet_mass_jesDown[jesUncertainty] = jet_mass_nom * (1. - delta) jets_mass_jesUp[jesUncertainty].append( jet_mass_jesUp[jesUncertainty]) jets_mass_jesDown[jesUncertainty].append( jet_mass_jesDown[jesUncertainty]) if self.doGroomed: jet_msdcorr_jesUp[jesUncertainty] = jet_msdcorr_nom * ( 1. + delta) jet_msdcorr_jesDown[jesUncertainty] = jet_msdcorr_nom * ( 1. - delta) jets_msdcorr_jesUp[jesUncertainty].append( jet_msdcorr_jesUp[jesUncertainty]) jets_msdcorr_jesDown[jesUncertainty].append( jet_msdcorr_jesDown[jesUncertainty]) # progate JER and JES corrections and uncertainties to MET if self.corrMET and jet_pt_nom > self.unclEnThreshold: jet_cosPhi = math.cos(jet.phi) jet_sinPhi = math.sin(jet.phi) met_px_nom = met_px_nom - (jet_pt_nom - jet_pt) * jet_cosPhi met_py_nom = met_py_nom - (jet_pt_nom - jet_pt) * jet_sinPhi met_px_jerUp = met_px_jerUp - (jet_pt_jerUp - jet_pt_nom) * jet_cosPhi met_py_jerUp = met_py_jerUp - (jet_pt_jerUp - jet_pt_nom) * jet_sinPhi met_px_jerDown = met_px_jerDown - (jet_pt_jerDown - jet_pt_nom) * jet_cosPhi met_py_jerDown = met_py_jerDown - (jet_pt_jerDown - jet_pt_nom) * jet_sinPhi for jesUncertainty in self.jesUncertainties: met_px_jesUp[jesUncertainty] = met_px_jesUp[ jesUncertainty] - (jet_pt_jesUp[jesUncertainty] - jet_pt_nom) * jet_cosPhi met_py_jesUp[jesUncertainty] = met_py_jesUp[ jesUncertainty] - (jet_pt_jesUp[jesUncertainty] - jet_pt_nom) * jet_sinPhi met_px_jesDown[jesUncertainty] = met_px_jesDown[ jesUncertainty] - (jet_pt_jesDown[jesUncertainty] - jet_pt_nom) * jet_cosPhi met_py_jesDown[jesUncertainty] = met_py_jesDown[ jesUncertainty] - (jet_pt_jesDown[jesUncertainty] - jet_pt_nom) * jet_sinPhi # propagate "unclustered energy" uncertainty to MET if self.corrMET: (met_px_unclEnUp, met_py_unclEnUp) = (met_px, met_py) (met_px_unclEnDown, met_py_unclEnDown) = (met_px, met_py) met_deltaPx_unclEn = getattr( event, self.metBranchName + "_MetUnclustEnUpDeltaX") met_deltaPy_unclEn = getattr( event, self.metBranchName + "_MetUnclustEnUpDeltaY") met_px_unclEnUp = met_px_unclEnUp + met_deltaPx_unclEn met_py_unclEnUp = met_py_unclEnUp + met_deltaPy_unclEn met_px_unclEnDown = met_px_unclEnDown - met_deltaPx_unclEn met_py_unclEnDown = met_py_unclEnDown - met_deltaPy_unclEn # propagate effect of jet energy smearing to MET met_px_jerUp = met_px_jerUp + (met_px_nom - met_px) met_py_jerUp = met_py_jerUp + (met_py_nom - met_py) met_px_jerDown = met_px_jerDown + (met_px_nom - met_px) met_py_jerDown = met_py_jerDown + (met_py_nom - met_py) for jesUncertainty in self.jesUncertainties: met_px_jesUp[jesUncertainty] = met_px_jesUp[jesUncertainty] + ( met_px_nom - met_px) met_py_jesUp[jesUncertainty] = met_py_jesUp[jesUncertainty] + ( met_py_nom - met_py) met_px_jesDown[jesUncertainty] = met_px_jesDown[ jesUncertainty] + (met_px_nom - met_px) met_py_jesDown[jesUncertainty] = met_py_jesDown[ jesUncertainty] + (met_py_nom - met_py) met_px_unclEnUp = met_px_unclEnUp + (met_px_nom - met_px) met_py_unclEnUp = met_py_unclEnUp + (met_py_nom - met_py) met_px_unclEnDown = met_px_unclEnDown + (met_px_nom - met_px) met_py_unclEnDown = met_py_unclEnDown + (met_py_nom - met_py) self.out.fillBranch("%s_pt_nom" % self.jetBranchName, jets_pt_nom) self.out.fillBranch("%s_pt_jerUp" % self.jetBranchName, jets_pt_jerUp) self.out.fillBranch("%s_pt_jerDown" % self.jetBranchName, jets_pt_jerDown) self.out.fillBranch("%s_mass_nom" % self.jetBranchName, jets_mass_nom) self.out.fillBranch("%s_mass_jerUp" % self.jetBranchName, jets_mass_jerUp) self.out.fillBranch("%s_mass_jerDown" % self.jetBranchName, jets_mass_jerDown) self.out.fillBranch("%s_mass_jmrUp" % self.jetBranchName, jets_mass_jmrUp) self.out.fillBranch("%s_mass_jmrDown" % self.jetBranchName, jets_mass_jmrDown) self.out.fillBranch("%s_mass_jmsUp" % self.jetBranchName, jets_mass_jmsUp) self.out.fillBranch("%s_mass_jmsDown" % self.jetBranchName, jets_mass_jmsDown) if self.doGroomed: self.out.fillBranch("%s_msoftdrop_nom" % self.jetBranchName, jets_msdcorr_nom) self.out.fillBranch("%s_msoftdrop_jerUp" % self.jetBranchName, jets_msdcorr_jerUp) self.out.fillBranch("%s_msoftdrop_jerDown" % self.jetBranchName, jets_msdcorr_jerDown) self.out.fillBranch("%s_msoftdrop_jmrUp" % self.jetBranchName, jets_msdcorr_jmrUp) self.out.fillBranch("%s_msoftdrop_jmrDown" % self.jetBranchName, jets_msdcorr_jmrDown) self.out.fillBranch("%s_msoftdrop_jmsUp" % self.jetBranchName, jets_msdcorr_jmsUp) self.out.fillBranch("%s_msoftdrop_jmsDown" % self.jetBranchName, jets_msdcorr_jmsDown) if self.corrMET: self.out.fillBranch("%s_pt_nom" % self.metBranchName, math.sqrt(met_px_nom**2 + met_py_nom**2)) self.out.fillBranch("%s_phi_nom" % self.metBranchName, math.atan2(met_py_nom, met_px_nom)) self.out.fillBranch("%s_pt_jerUp" % self.metBranchName, math.sqrt(met_px_jerUp**2 + met_py_jerUp**2)) self.out.fillBranch("%s_phi_jerUp" % self.metBranchName, math.atan2(met_py_jerUp, met_px_jerUp)) self.out.fillBranch( "%s_pt_jerDown" % self.metBranchName, math.sqrt(met_px_jerDown**2 + met_py_jerDown**2)) self.out.fillBranch("%s_phi_jerDown" % self.metBranchName, math.atan2(met_py_jerDown, met_px_jerDown)) for jesUncertainty in self.jesUncertainties: self.out.fillBranch( "%s_pt_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_pt_jesUp[jesUncertainty]) self.out.fillBranch( "%s_pt_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_pt_jesDown[jesUncertainty]) self.out.fillBranch( "%s_mass_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_mass_jesUp[jesUncertainty]) self.out.fillBranch( "%s_mass_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_mass_jesDown[jesUncertainty]) if self.doGroomed: self.out.fillBranch( "%s_msoftdrop_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_msdcorr_jesUp[jesUncertainty]) self.out.fillBranch( "%s_msoftdrop_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_msdcorr_jesDown[jesUncertainty]) if self.corrMET: self.out.fillBranch( "%s_pt_jes%sUp" % (self.metBranchName, jesUncertainty), math.sqrt(met_px_jesUp[jesUncertainty]**2 + met_py_jesUp[jesUncertainty]**2)) self.out.fillBranch( "%s_phi_jes%sUp" % (self.metBranchName, jesUncertainty), math.atan2(met_py_jesUp[jesUncertainty], met_px_jesUp[jesUncertainty])) self.out.fillBranch( "%s_pt_jes%sDown" % (self.metBranchName, jesUncertainty), math.sqrt(met_px_jesDown[jesUncertainty]**2 + met_py_jesDown[jesUncertainty]**2)) self.out.fillBranch( "%s_phi_jes%sDown" % (self.metBranchName, jesUncertainty), math.atan2(met_py_jesDown[jesUncertainty], met_px_jesDown[jesUncertainty])) if self.corrMET: self.out.fillBranch( "%s_pt_unclustEnUp" % self.metBranchName, math.sqrt(met_px_unclEnUp**2 + met_py_unclEnUp**2)) self.out.fillBranch("%s_phi_unclustEnUp" % self.metBranchName, math.atan2(met_py_unclEnUp, met_px_unclEnUp)) self.out.fillBranch( "%s_pt_unclustEnDown" % self.metBranchName, math.sqrt(met_px_unclEnDown**2 + met_py_unclEnDown**2)) self.out.fillBranch( "%s_phi_unclustEnDown" % self.metBranchName, math.atan2(met_py_unclEnDown, met_px_unclEnDown)) return True
class fatJetUncertaintiesProducer(Module): def __init__(self, era, globalTag, jesUncertainties=["Total"], archive=None, jetType="AK8PFPuppi", noGroom=False, jerTag="", jmrVals=[], jmsVals=[], isData=False, applySmearing=True, applyHEMfix=False, splitJER=False): self.era = era self.noGroom = noGroom self.isData = isData self.applySmearing = applySmearing if not isData else False # don't smear for data # --------------------------------------------------------------------- # CV: globalTag and jetType not yet used in the jet smearer, as there # is no consistent set of txt files for JES uncertainties and JER scale # factors and uncertainties yet # --------------------------------------------------------------------- self.splitJER = splitJER if self.splitJER: self.splitJERIDs = list(range(6)) else: self.splitJERIDs = [""] # "empty" ID for the overall JER self.jesUncertainties = jesUncertainties # smear jet pT to account for measured difference in JER between data # and simulation. if jerTag != "": self.jerInputFileName = jerTag + "_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = jerTag + "_SF_" + jetType + ".txt" else: print("WARNING: jerTag is empty!!! This module will soon be " "deprecated! Please use jetmetHelperRun2 in the future.") if era == "2016": self.jerInputFileName = ''.join( ["Summer16_25nsV1_MC_PtResolution_", jetType, ".txt"]) self.jerUncertaintyInputFileName = ''.join( ["Summer16_25nsV1_MC_SF_", jetType, ".txt"]) elif era == "2017" or era == "2018": # use Fall17 as temporary placeholder until # post-Moriond 2019 JERs are out self.jerInputFileName = ''.join( ["Fall17_V3_MC_PtResolution_", jetType, ".txt"]) self.jerUncertaintyInputFileName = ''.join( ["Fall17_V3_MC_SF_", jetType, ".txt"]) # jet mass resolution: https://twiki.cern.ch/twiki/bin/view/CMS/JetWtagging self.jmrVals = jmrVals if not self.jmrVals: print("WARNING: jmrVals is empty!!! Using default values. This " "module will soon be deprecated! Please use " "jetmetHelperRun2 in the future.") self.jmrVals = [1.0, 1.2, 0.8] # nominal, up, down # Use 2017 values for 2018 until 2018 are released if self.era in ["2017", "2018"]: self.jmrVals = [1.09, 1.14, 1.04] self.jetSmearer = jetSmearer(globalTag, jetType, self.jerInputFileName, self.jerUncertaintyInputFileName, self.jmrVals) if "AK4" in jetType: self.jetBranchName = "Jet" self.genJetBranchName = "GenJet" self.genSubJetBranchName = None self.doGroomed = False elif "AK8" in jetType: self.jetBranchName = "FatJet" self.subJetBranchName = "SubJet" self.genJetBranchName = "GenJetAK8" self.genSubJetBranchName = "SubGenJetAK8" if not self.noGroom: self.doGroomed = True self.puppiCorrFile = ROOT.TFile.Open( os.environ['CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/puppiCorr.root") self.puppisd_corrGEN = self.puppiCorrFile.Get( "puppiJECcorr_gen") self.puppisd_corrRECO_cen = self.puppiCorrFile.Get( "puppiJECcorr_reco_0eta1v3") self.puppisd_corrRECO_for = self.puppiCorrFile.Get( "puppiJECcorr_reco_1v3eta2v5") else: self.doGroomed = False else: raise ValueError("ERROR: Invalid jet type = '%s'!" % jetType) self.rhoBranchName = "fixedGridRhoFastjetAll" self.lenVar = "n" + self.jetBranchName # jet mass scale self.jmsVals = jmsVals if not self.jmsVals: print("WARNING: jmsVals is empty!!! Using default values! This " + "module will soon be deprecated! Please use " + "jetmetHelperRun2 in the future.") # 2016 values self.jmsVals = [1.00, 0.9906, 1.0094] # nominal, down, up # Use 2017 values for 2018 until 2018 are released if self.era in ["2017", "2018"]: self.jmsVals = [0.982, 0.978, 0.986] # read jet energy scale (JES) uncertainties # (downloaded from https://twiki.cern.ch/twiki/bin/view/CMS/JECDataMC ) self.jesInputArchivePath = os.environ['CMSSW_BASE'] + \ "/src/PhysicsTools/NanoAODTools/data/jme/" # Text files are now tarred so must extract first into temporary # directory (gets deleted during python memory management at script exit) self.jesArchive = tarfile.open( self.jesInputArchivePath + globalTag + ".tgz", "r:gz") if not archive else tarfile.open( self.jesInputArchivePath + archive + ".tgz", "r:gz") self.jesInputFilePath = tempfile.mkdtemp() self.jesArchive.extractall(self.jesInputFilePath) if len(jesUncertainties) == 1 and jesUncertainties[0] == "Total": self.jesUncertaintyInputFileName = globalTag + "_Uncertainty_" + jetType + ".txt" elif jesUncertainties[0] == "Merged" and not self.isData: self.jesUncertaintyInputFileName = "Regrouped_" + \ globalTag + "_UncertaintySources_" + jetType + ".txt" else: self.jesGroupedFilePath = os.environ[ 'CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/regrouped/" self.jesGroupedUncertaintyFileName = "RegroupedV2_" + globalTag + "_UncertaintySources_" + jetType + ".txt" self.jesGroupedUncertaintyFilePath = pjoin( self.jesGroupedFilePath, self.jesGroupedUncertaintyFileName) # Copy the uncertainty source file to the tmp directory shutil.copy( self.jesGroupedUncertaintyFilePath, pjoin(self.jesInputFilePath, self.jesGroupedUncertaintyFileName)) self.jesUncertaintyInputFileName = self.jesGroupedUncertaintyFileName #self.jesUncertaintyInputFileName = "RegroupedV2_" + globalTag + "_UncertaintySources_" + jetType + ".txt" # read all uncertainty source names from the loaded file if jesUncertainties[0] in ["All", "Merged"]: with open(self.jesInputFilePath + '/' + self.jesUncertaintyInputFileName) as f: lines = f.read().split("\n") sources = [ x for x in lines if x.startswith("[") and x.endswith("]") ] sources = [x[1:-1] for x in sources] self.jesUncertainties = sources if applyHEMfix: self.jesUncertainties.append("HEMIssue") self.jetReCalibrator = JetReCalibrator( globalTag, jetType, True, self.jesInputFilePath, calculateSeparateCorrections=False, calculateType1METCorrection=False) # load libraries for accessing JES scale factors and uncertainties # from txt files for library in [ "libCondFormatsJetMETObjects", "libPhysicsToolsNanoAODTools" ]: if library not in ROOT.gSystem.GetLibraries(): print("Load Library '%s'" % library.replace("lib", "")) ROOT.gSystem.Load(library) def getJERsplitID(self, pt, eta): if not self.splitJER: return "" if abs(eta) < 1.93: return 0 elif abs(eta) < 2.5: return 1 elif abs(eta) < 3: if pt < 50: return 2 else: return 3 else: if pt < 50: return 4 else: return 5 def beginJob(self): print("Loading jet energy scale (JES) uncertainties from file '%s'" % os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName)) # self.jesUncertainty = ROOT.JetCorrectionUncertainty(os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName)) self.jesUncertainty = {} # implementation didn't seem to work for factorized JEC,try again # another way for jesUncertainty in self.jesUncertainties: jesUncertainty_label = jesUncertainty if jesUncertainty == 'Total' \ and (len(self.jesUncertainties) == 1 or len(self.jesUncertainties) == 2 and 'HEMIssue' in self.jesUncertainties): jesUncertainty_label = '' if jesUncertainty != "HEMIssue": pars = ROOT.JetCorrectorParameters( os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName), jesUncertainty_label) self.jesUncertainty[ jesUncertainty] = ROOT.JetCorrectionUncertainty(pars) if not self.isData: self.jetSmearer.beginJob() def endJob(self): if not self.isData: self.jetSmearer.endJob() shutil.rmtree(self.jesInputFilePath) def beginFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): self.out = wrappedOutputTree self.out.branch("%s_pt_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_pt_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JEC" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JMR" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JER" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JMS" % self.jetBranchName, "F", lenVar=self.lenVar) if self.doGroomed: self.out.branch("%s_msoftdrop_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_corr_JMR" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_corr_JMS" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_corr_PUPPI" % self.jetBranchName, "F", lenVar=self.lenVar) if not self.isData: self.out.branch("%s_msoftdrop_tau21DDT_nom" % self.jetBranchName, "F", lenVar=self.lenVar) for shift in ["Up", "Down"]: for jerID in self.splitJERIDs: self.out.branch("%s_pt_jer%s%s" % (self.jetBranchName, jerID, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jer%s%s" % (self.jetBranchName, jerID, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jmr%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jms%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) if self.doGroomed: for jerID in self.splitJERIDs: self.out.branch("%s_msoftdrop_jer%s%s" % (self.jetBranchName, jerID, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_tau21DDT_jer%s%s" % (self.jetBranchName, jerID, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_jmr%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_jms%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_tau21DDT_jmr%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_tau21DDT_jms%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) for jesUncertainty in self.jesUncertainties: self.out.branch( "%s_pt_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) self.out.branch( "%s_mass_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) if self.doGroomed: self.out.branch( "%s_msoftdrop_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) def endFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): pass def analyze(self, event): """process event, return True (go to next module) or False (fail, go to next event)""" jets = Collection(event, self.jetBranchName) if not self.isData: genJets = Collection(event, self.genJetBranchName) if self.doGroomed: subJets = Collection(event, self.subJetBranchName) if not self.isData: genSubJets = Collection(event, self.genSubJetBranchName) genSubJetMatcher = matchObjectCollectionMultiple(genJets, genSubJets, dRmax=0.8) if not self.isData: self.jetSmearer.setSeed(event) jets_pt_raw = [] jets_pt_nom = [] jets_mass_raw = [] jets_mass_nom = [] jets_corr_JEC = [] jets_corr_JER = [] jets_corr_JMS = [] jets_corr_JMR = [] jets_pt_jerUp = {} jets_pt_jerDown = {} jets_pt_jesUp = {} jets_pt_jesDown = {} jets_mass_jerUp = {} jets_mass_jerDown = {} jets_mass_jmrUp = [] jets_mass_jmrDown = [] jets_mass_jesUp = {} jets_mass_jesDown = {} jets_mass_jmsUp = [] jets_mass_jmsDown = [] for jerID in self.splitJERIDs: jets_pt_jerUp[jerID] = [] jets_pt_jerDown[jerID] = [] jets_mass_jerUp[jerID] = [] jets_mass_jerDown[jerID] = [] for jesUncertainty in self.jesUncertainties: jets_pt_jesUp[jesUncertainty] = [] jets_pt_jesDown[jesUncertainty] = [] jets_mass_jesUp[jesUncertainty] = [] jets_mass_jesDown[jesUncertainty] = [] if self.doGroomed: jets_msdcorr_raw = [] jets_msdcorr_nom = [] jets_msdcorr_corr_JMR = [] jets_msdcorr_corr_JMS = [] jets_msdcorr_corr_PUPPI = [] jets_msdcorr_jerUp = {} jets_msdcorr_jerDown = {} jets_msdcorr_jmrUp = [] jets_msdcorr_jmrDown = [] jets_msdcorr_jesUp = {} jets_msdcorr_jesDown = {} jets_msdcorr_jmsUp = [] jets_msdcorr_jmsDown = [] jets_msdcorr_tau21DDT_nom = [] jets_msdcorr_tau21DDT_jerUp = {} jets_msdcorr_tau21DDT_jerDown = {} jets_msdcorr_tau21DDT_jmrUp = [] jets_msdcorr_tau21DDT_jmrDown = [] jets_msdcorr_tau21DDT_jmsUp = [] jets_msdcorr_tau21DDT_jmsDown = [] for jerID in self.splitJERIDs: jets_msdcorr_jerUp[jerID] = [] jets_msdcorr_jerDown[jerID] = [] jets_msdcorr_tau21DDT_jerUp[jerID] = [] jets_msdcorr_tau21DDT_jerDown[jerID] = [] for jesUncertainty in self.jesUncertainties: jets_msdcorr_jesUp[jesUncertainty] = [] jets_msdcorr_jesDown[jesUncertainty] = [] rho = getattr(event, self.rhoBranchName) # match reconstructed jets to generator level ones # (needed to evaluate JER scale factors and uncertainties) if not self.isData: pairs = matchObjectCollection(jets, genJets) for jet in jets: # jet pt and mass corrections jet_pt = jet.pt jet_mass = jet.mass if hasattr(jet, "rawFactor"): jet_rawpt = jet_pt * (1 - jet.rawFactor) jet_rawmass = jet_mass * (1 - jet.rawFactor) else: jet_rawpt = -1.0 * jet_pt # If factor not present factor will be saved as -1 jet_rawmass = -1.0 * jet_mass # If factor not present factor will be saved as -1 (jet_pt, jet_mass) = self.jetReCalibrator.correct(jet, rho) jet.pt = jet_pt jet.mass = jet_mass jets_pt_raw.append(jet_rawpt) jets_mass_raw.append(jet_rawmass) jets_corr_JEC.append(jet_pt / jet_rawpt) if not self.isData: genJet = pairs[jet] # evaluate JER scale factors and uncertainties # (cf. https://twiki.cern.ch/twiki/bin/view/CMS/JetResolution and https://twiki.cern.ch/twiki/bin/view/CMSPublic/WorkBookJetEnergyResolution ) if not self.isData: (jet_pt_jerNomVal, jet_pt_jerUpVal, jet_pt_jerDownVal) = self.jetSmearer.getSmearValsPt( jet, genJet, rho) else: # set values to 1 for data so that jet_pt_nom is not smeared (jet_pt_jerNomVal, jet_pt_jerUpVal, jet_pt_jerDownVal) = (1, 1, 1) jets_corr_JER.append(jet_pt_jerNomVal) jet_pt_nom = jet_pt_jerNomVal * jet_pt if self.applySmearing else jet_pt if jet_pt_nom < 0.0: jet_pt_nom *= -1.0 jets_pt_nom.append(jet_pt_nom) # Evaluate JMS and JMR scale factors and uncertainties jmsNomVal, jmsDownVal, jmsUpVal = self.jmsVals if not self.isData else ( 1, 1, 1) if not self.isData: (jet_mass_jmrNomVal, jet_mass_jmrUpVal, jet_mass_jmrDownVal) = self.jetSmearer.getSmearValsM( jet, genJet) else: # set values to 1 for data so that jet_mass_nom is not smeared (jet_mass_jmrNomVal, jet_mass_jmrUpVal, jet_mass_jmrDownVal) = (1, 1, 1) jets_corr_JMS.append(jmsNomVal) jets_corr_JMR.append(jet_mass_jmrNomVal) jet_mass_nom = jet_pt_jerNomVal * jet_mass_jmrNomVal * \ jmsNomVal * jet_mass if self.applySmearing else jet_mass if jet_mass_nom < 0.0: jet_mass_nom *= -1.0 jets_mass_nom.append(jet_mass_nom) if not self.isData: jet_pt_jerUp = { jerID: jet_pt_nom for jerID in self.splitJERIDs } jet_pt_jerDown = { jerID: jet_pt_nom for jerID in self.splitJERIDs } jet_mass_jerUp = { jerID: jet_mass_nom for jerID in self.splitJERIDs } jet_mass_jerDown = { jerID: jet_mass_nom for jerID in self.splitJERIDs } thisJERID = self.getJERsplitID(jet_pt_nom, jet.eta) jet_pt_jerUp[thisJERID] = jet_pt_jerUpVal * jet_pt jet_pt_jerDown[thisJERID] = jet_pt_jerDownVal * jet_pt jet_mass_jerUp[thisJERID] = jet_pt_jerUpVal * \ jet_mass_jmrNomVal * jmsNomVal * jet_mass jet_mass_jerDown[thisJERID] = jet_pt_jerDownVal * \ jet_mass_jmrNomVal * jmsNomVal * jet_mass for jerID in self.splitJERIDs: jets_pt_jerUp[jerID].append(jet_pt_jerUp[jerID]) jets_pt_jerDown[jerID].append(jet_pt_jerDown[jerID]) jets_mass_jerUp[jerID].append(jet_mass_jerUp[jerID]) jets_mass_jerDown[jerID].append(jet_mass_jerDown[jerID]) jets_mass_jmrUp.append(jet_pt_jerNomVal * jet_mass_jmrUpVal * jmsNomVal * jet_mass) jets_mass_jmrDown.append(jet_pt_jerNomVal * jet_mass_jmrDownVal * jmsNomVal * jet_mass) jets_mass_jmsUp.append(jet_pt_jerNomVal * jet_mass_jmrNomVal * jmsUpVal * jet_mass) jets_mass_jmsDown.append(jet_pt_jerNomVal * jet_mass_jmrNomVal * jmsDownVal * jet_mass) if self.doGroomed: if not self.isData: genGroomedSubJets = genSubJetMatcher[ genJet] if genJet is not None else None genGroomedJet = genGroomedSubJets[0].p4( ) + genGroomedSubJets[1].p4( ) if genGroomedSubJets is not None and len( genGroomedSubJets) >= 2 else None else: genGroomedSubJets = None genGroomedJet = None if jet.subJetIdx1 >= 0 and jet.subJetIdx2 >= 0: groomedP4 = subJets[jet.subJetIdx1].p4() + subJets[ jet.subJetIdx2].p4() # check subjet jecs else: groomedP4 = None jet_msdcorr_raw = groomedP4.M( ) if groomedP4 is not None else 0.0 # raw value always stored withoud mass correction jets_msdcorr_raw.append(jet_msdcorr_raw) # LC: Apply PUPPI SD mass correction https://github.com/cms-jet/PuppiSoftdropMassCorr/ puppisd_genCorr = self.puppisd_corrGEN.Eval(jet.pt) if abs(jet.eta) <= 1.3: puppisd_recoCorr = self.puppisd_corrRECO_cen.Eval(jet.pt) else: puppisd_recoCorr = self.puppisd_corrRECO_for.Eval(jet.pt) puppisd_total = puppisd_genCorr * puppisd_recoCorr jets_msdcorr_corr_PUPPI.append(puppisd_total) if groomedP4 is not None: groomedP4.SetPtEtaPhiM(groomedP4.Perp(), groomedP4.Eta(), groomedP4.Phi(), groomedP4.M() * puppisd_total) # now apply the mass correction to the raw value jet_msdcorr_raw = groomedP4.M( ) if groomedP4 is not None else 0.0 if jet_msdcorr_raw < 0.0: jet_msdcorr_raw *= -1.0 # Evaluate JMS and JMR scale factors and uncertainties if not self.isData: (jet_msdcorr_jmrNomVal, jet_msdcorr_jmrUpVal, jet_msdcorr_jmrDownVal) = \ (self.jetSmearer.getSmearValsM(groomedP4, genGroomedJet) if groomedP4 is not None and genGroomedJet is not None else (0., 0., 0.)) else: (jet_msdcorr_jmrNomVal, jet_msdcorr_jmrUpVal, jet_msdcorr_jmrDownVal) = (1, 1, 1) jets_msdcorr_corr_JMS.append(jmsNomVal) jets_msdcorr_corr_JMR.append(jet_msdcorr_jmrNomVal) jet_msdcorr_nom = jet_pt_jerNomVal * \ jet_msdcorr_jmrNomVal * jmsNomVal * jet_msdcorr_raw # store the nominal mass value jets_msdcorr_nom.append(jet_msdcorr_nom) if not self.isData: jet_msdcorr_jerUp = { jerID: jet_msdcorr_nom for jerID in self.splitJERIDs } jet_msdcorr_jerDown = { jerID: jet_msdcorr_nom for jerID in self.splitJERIDs } thisJERID = self.getJERsplitID(jet_pt_nom, jet.eta) jet_msdcorr_jerUp[thisJERID] = jet_pt_jerUpVal * \ jet_msdcorr_jmrNomVal * jmsNomVal * jet_msdcorr_raw jet_msdcorr_jerDown[thisJERID] = jet_pt_jerDownVal * \ jet_msdcorr_jmrNomVal * jmsNomVal * jet_msdcorr_raw for jerID in self.splitJERIDs: jets_msdcorr_jerUp[jerID].append( jet_msdcorr_jerUp[jerID]) jets_msdcorr_jerDown[jerID].append( jet_msdcorr_jerDown[jerID]) jets_msdcorr_jmrUp.append( jet_pt_jerNomVal * jet_msdcorr_jmrUpVal * jmsNomVal * jet_msdcorr_raw) jets_msdcorr_jmrDown.append( jet_pt_jerNomVal * jet_msdcorr_jmrDownVal * jmsNomVal * jet_msdcorr_raw) jets_msdcorr_jmsUp.append( jet_pt_jerNomVal * jet_msdcorr_jmrNomVal * jmsUpVal * jet_msdcorr_raw) jets_msdcorr_jmsDown.append( jet_pt_jerNomVal * jet_msdcorr_jmrNomVal * jmsDownVal * jet_msdcorr_raw) # Also evaluated JMS&JMR SD corr in tau21DDT region: https://twiki.cern.ch/twiki/bin/viewauth/CMS/JetWtagging#tau21DDT_0_43 if self.era in ["2016"]: jmstau21DDTNomVal = 1.014 jmstau21DDTDownVal = 1.007 jmstau21DDTUpVal = 1.021 self.jetSmearer.jmr_vals = [1.086, 1.176, 0.996] elif self.era in ["2017"]: jmstau21DDTNomVal = 0.983 jmstau21DDTDownVal = 0.976 jmstau21DDTUpVal = 0.99 self.jetSmearer.jmr_vals = [1.080, 1.161, 0.999] elif self.era in ["2018"]: jmstau21DDTNomVal = 1.000 # tau21DDT < 0.43 WP jmstau21DDTDownVal = 0.990 jmstau21DDTUpVal = 1.010 self.jetSmearer.jmr_vals = [1.124, 1.208, 1.040] ( jet_msdcorr_tau21DDT_jmrNomVal, jet_msdcorr_tau21DDT_jmrUpVal, jet_msdcorr_tau21DDT_jmrDownVal ) = self.jetSmearer.getSmearValsM( groomedP4, genGroomedJet ) if groomedP4 is not None and genGroomedJet is not None else ( 0., 0., 0.) jet_msdcorr_tau21DDT_nom = jet_pt_jerNomVal * \ jet_msdcorr_tau21DDT_jmrNomVal * jmstau21DDTNomVal * jet_msdcorr_raw jets_msdcorr_tau21DDT_nom.append(jet_msdcorr_tau21DDT_nom) jet_msdcorr_tau21DDT_jerUp = { jerID: jet_msdcorr_tau21DDT_nom for jerID in self.splitJERIDs } jet_msdcorr_tau21DDT_jerDown = { jerID: jet_msdcorr_tau21DDT_nom for jerID in self.splitJERIDs } jet_msdcorr_tau21DDT_jerUp[thisJERID] = jet_pt_jerUpVal * \ jet_msdcorr_tau21DDT_jmrNomVal * jmstau21DDTNomVal * jet_msdcorr_raw jet_msdcorr_tau21DDT_jerDown[thisJERID] = jet_pt_jerDownVal * \ jet_msdcorr_tau21DDT_jmrNomVal * jmstau21DDTNomVal * jet_msdcorr_raw for jerID in self.splitJERIDs: jets_msdcorr_tau21DDT_jerUp[jerID].append( jet_msdcorr_tau21DDT_jerUp[jerID]) jets_msdcorr_tau21DDT_jerDown[jerID].append( jet_msdcorr_tau21DDT_jerDown[jerID]) jets_msdcorr_tau21DDT_jmrUp.append( jet_pt_jerNomVal * jet_msdcorr_tau21DDT_jmrUpVal * jmstau21DDTNomVal * jet_msdcorr_raw) jets_msdcorr_tau21DDT_jmrDown.append( jet_pt_jerNomVal * jet_msdcorr_tau21DDT_jmrDownVal * jmstau21DDTNomVal * jet_msdcorr_raw) jets_msdcorr_tau21DDT_jmsUp.append( jet_pt_jerNomVal * jet_msdcorr_tau21DDT_jmrNomVal * jmstau21DDTUpVal * jet_msdcorr_raw) jets_msdcorr_tau21DDT_jmsDown.append( jet_pt_jerNomVal * jet_msdcorr_tau21DDT_jmrNomVal * jmstau21DDTDownVal * jet_msdcorr_raw) # Restore original jmr_vals in jetSmearer self.jetSmearer.jmr_vals = self.jmrVals if not self.isData: # evaluate JES uncertainties jet_pt_jesUp = {} jet_pt_jesDown = {} jet_mass_jesUp = {} jet_mass_jesDown = {} jet_msdcorr_jesUp = {} jet_msdcorr_jesDown = {} for jesUncertainty in self.jesUncertainties: # (cf. https://twiki.cern.ch/twiki/bin/view/CMSPublic/WorkBookJetEnergyCorrections#JetCorUncertainties) # cf. https://hypernews.cern.ch/HyperNews/CMS/get/JetMET/2000.html if jesUncertainty == "HEMIssue": delta = 1. if jet_pt_nom > 15 and jet.jetId & 2 and jet.phi > -1.57 and jet.phi < -0.87: if jet.eta > -2.5 and jet.eta < -1.3: delta = 0.8 elif jet.eta <= -2.5 and jet.eta > -3: delta = 0.65 jet_pt_jesUp[jesUncertainty] = jet_pt_nom jet_pt_jesDown[jesUncertainty] = delta * jet_pt_nom jet_mass_jesUp[jesUncertainty] = jet_mass_nom jet_mass_jesDown[jesUncertainty] = delta * jet_mass_nom if self.doGroomed: jet_msdcorr_jesUp[jesUncertainty] = jet_msdcorr_nom jet_msdcorr_jesDown[jesUncertainty] = delta * \ jet_msdcorr_nom else: self.jesUncertainty[jesUncertainty].setJetPt( jet_pt_nom) self.jesUncertainty[jesUncertainty].setJetEta(jet.eta) delta = self.jesUncertainty[ jesUncertainty].getUncertainty(True) jet_pt_jesUp[jesUncertainty] = jet_pt_nom * \ (1. + delta) jet_pt_jesDown[jesUncertainty] = jet_pt_nom * \ (1. - delta) jet_mass_jesUp[jesUncertainty] = jet_mass_nom * \ (1. + delta) jet_mass_jesDown[jesUncertainty] = jet_mass_nom * \ (1. - delta) if self.doGroomed: jet_msdcorr_jesUp[jesUncertainty] = jet_msdcorr_nom * \ (1. + delta) jet_msdcorr_jesDown[ jesUncertainty] = jet_msdcorr_nom * (1. - delta) jets_pt_jesUp[jesUncertainty].append( jet_pt_jesUp[jesUncertainty]) jets_pt_jesDown[jesUncertainty].append( jet_pt_jesDown[jesUncertainty]) jets_mass_jesUp[jesUncertainty].append( jet_mass_jesUp[jesUncertainty]) jets_mass_jesDown[jesUncertainty].append( jet_mass_jesDown[jesUncertainty]) if self.doGroomed: jets_msdcorr_jesUp[jesUncertainty].append( jet_msdcorr_jesUp[jesUncertainty]) jets_msdcorr_jesDown[jesUncertainty].append( jet_msdcorr_jesDown[jesUncertainty]) self.out.fillBranch("%s_pt_raw" % self.jetBranchName, jets_pt_raw) self.out.fillBranch("%s_pt_nom" % self.jetBranchName, jets_pt_nom) self.out.fillBranch("%s_corr_JEC" % self.jetBranchName, jets_corr_JEC) self.out.fillBranch("%s_mass_raw" % self.jetBranchName, jets_mass_raw) self.out.fillBranch("%s_mass_nom" % self.jetBranchName, jets_mass_nom) if not self.isData: self.out.fillBranch("%s_corr_JER" % self.jetBranchName, jets_corr_JER) self.out.fillBranch("%s_corr_JMS" % self.jetBranchName, jets_corr_JMS) self.out.fillBranch("%s_corr_JMR" % self.jetBranchName, jets_corr_JMR) for jerID in self.splitJERIDs: self.out.fillBranch( "%s_pt_jer%sUp" % (self.jetBranchName, jerID), jets_pt_jerUp[jerID]) self.out.fillBranch( "%s_pt_jer%sDown" % (self.jetBranchName, jerID), jets_pt_jerDown[jerID]) self.out.fillBranch( "%s_mass_jer%sUp" % (self.jetBranchName, jerID), jets_mass_jerUp[jerID]) self.out.fillBranch( "%s_mass_jer%sDown" % (self.jetBranchName, jerID), jets_mass_jerDown[jerID]) self.out.fillBranch("%s_mass_jmrUp" % self.jetBranchName, jets_mass_jmrUp) self.out.fillBranch("%s_mass_jmrDown" % self.jetBranchName, jets_mass_jmrDown) self.out.fillBranch("%s_mass_jmsUp" % self.jetBranchName, jets_mass_jmsUp) self.out.fillBranch("%s_mass_jmsDown" % self.jetBranchName, jets_mass_jmsDown) if self.doGroomed: self.out.fillBranch("%s_msoftdrop_raw" % self.jetBranchName, jets_msdcorr_raw) self.out.fillBranch("%s_msoftdrop_nom" % self.jetBranchName, jets_msdcorr_nom) self.out.fillBranch("%s_msoftdrop_corr_JMS" % self.jetBranchName, jets_msdcorr_corr_JMS) self.out.fillBranch("%s_msoftdrop_corr_JMR" % self.jetBranchName, jets_msdcorr_corr_JMR) self.out.fillBranch("%s_msoftdrop_corr_PUPPI" % self.jetBranchName, jets_msdcorr_corr_PUPPI) if not self.isData: self.out.fillBranch( "%s_msoftdrop_tau21DDT_nom" % self.jetBranchName, jets_msdcorr_tau21DDT_nom) for jerID in self.splitJERIDs: self.out.fillBranch( "%s_msoftdrop_jer%sUp" % (self.jetBranchName, jerID), jets_msdcorr_jerUp[jerID]) self.out.fillBranch( "%s_msoftdrop_jer%sDown" % (self.jetBranchName, jerID), jets_msdcorr_jerDown[jerID]) self.out.fillBranch( "%s_msoftdrop_tau21DDT_jer%sUp" % (self.jetBranchName, jerID), jets_msdcorr_tau21DDT_jerUp[jerID]) self.out.fillBranch( "%s_msoftdrop_tau21DDT_jer%sDown" % (self.jetBranchName, jerID), jets_msdcorr_tau21DDT_jerDown[jerID]) self.out.fillBranch("%s_msoftdrop_jmrUp" % self.jetBranchName, jets_msdcorr_jmrUp) self.out.fillBranch( "%s_msoftdrop_jmrDown" % self.jetBranchName, jets_msdcorr_jmrDown) self.out.fillBranch("%s_msoftdrop_jmsUp" % self.jetBranchName, jets_msdcorr_jmsUp) self.out.fillBranch( "%s_msoftdrop_jmsDown" % self.jetBranchName, jets_msdcorr_jmsDown) self.out.fillBranch( "%s_msoftdrop_tau21DDT_jmrUp" % self.jetBranchName, jets_msdcorr_tau21DDT_jmrUp) self.out.fillBranch( "%s_msoftdrop_tau21DDT_jmrDown" % self.jetBranchName, jets_msdcorr_tau21DDT_jmrDown) self.out.fillBranch( "%s_msoftdrop_tau21DDT_jmsUp" % self.jetBranchName, jets_msdcorr_tau21DDT_jmsUp) self.out.fillBranch( "%s_msoftdrop_tau21DDT_jmsDown" % self.jetBranchName, jets_msdcorr_tau21DDT_jmsDown) if not self.isData: for jesUncertainty in self.jesUncertainties: self.out.fillBranch( "%s_pt_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_pt_jesUp[jesUncertainty]) self.out.fillBranch( "%s_pt_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_pt_jesDown[jesUncertainty]) self.out.fillBranch( "%s_mass_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_mass_jesUp[jesUncertainty]) self.out.fillBranch( "%s_mass_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_mass_jesDown[jesUncertainty]) if self.doGroomed: self.out.fillBranch( "%s_msoftdrop_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_msdcorr_jesUp[jesUncertainty]) self.out.fillBranch( "%s_msoftdrop_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_msdcorr_jesDown[jesUncertainty]) return True
class jetmetUncertaintiesProducer(Module): def __init__(self, era, globalTag, jesUncertainties=["Total"], archive=None, globalTagProd=None, jetType="AK4PFchs", metBranchName="MET", jerTag="", isData=False, applySmearing=True): # globalTagProd only needs to be defined if METFixEE2017 is to be recorrected, and should be the GT that was used for the production of the nanoAOD files self.era = era self.isData = isData self.applySmearing = applySmearing if not isData else False # if set to true, Jet_pt_nom will have JER applied. not to be switched on for data. self.metBranchName = metBranchName self.rhoBranchName = "fixedGridRhoFastjetAll" #-------------------------------------------------------------------------------------------- # CV: globalTag and jetType not yet used in the jet smearer, as there is no consistent set of # txt files for JES uncertainties and JER scale factors and uncertainties yet #-------------------------------------------------------------------------------------------- self.jesUncertainties = jesUncertainties # smear jet pT to account for measured difference in JER between data and simulation. if jerTag != "": self.jerInputFileName = jerTag + "_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = jerTag + "_SF_" + jetType + ".txt" else: print "WARNING: jerTag is empty!!! This module will soon be deprecated! Please use jetmetHelperRun2 in the future." if era == "2016": self.jerInputFileName = "Summer16_25nsV1_MC_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = "Summer16_25nsV1_MC_SF_" + jetType + ".txt" elif era == "2017" or era == "2018": ## use 2017 JER for 2018 for the time being self.jerInputFileName = "Fall17_V3_MC_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = "Fall17_V3_MC_SF_" + jetType + ".txt" elif era == "2018" and False: ## jetSmearer not working with 2018 JERs yet self.jerInputFileName = "Autumn18_V7_MC_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = "Autumn18_V7_MC_SF_" + jetType + ".txt" self.jetSmearer = jetSmearer(globalTag, jetType, self.jerInputFileName, self.jerUncertaintyInputFileName) if "AK4" in jetType: self.jetBranchName = "Jet" self.genJetBranchName = "GenJet" self.genSubJetBranchName = None else: raise ValueError("ERROR: Invalid jet type = '%s'!" % jetType) self.lenVar = "n" + self.jetBranchName # read jet energy scale (JES) uncertainties # (downloaded from https://twiki.cern.ch/twiki/bin/view/CMS/JECDataMC ) self.jesInputArchivePath = os.environ[ 'CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/" # Text files are now tarred so must extract first into temporary directory (gets deleted during python memory management at script exit) self.jesArchive = tarfile.open( self.jesInputArchivePath + globalTag + ".tgz", "r:gz") if not archive else tarfile.open( self.jesInputArchivePath + archive + ".tgz", "r:gz") self.jesInputFilePath = tempfile.mkdtemp() self.jesArchive.extractall(self.jesInputFilePath) # to fully re-calculate type-1 MET the JEC that are currently applied are also needed. IS THAT EVEN CORRECT? if len(jesUncertainties) == 1 and jesUncertainties[0] == "Total": self.jesUncertaintyInputFileName = globalTag + "_Uncertainty_" + jetType + ".txt" else: self.jesUncertaintyInputFileName = globalTag + "_UncertaintySources_" + jetType + ".txt" # read all uncertainty source names from the loaded file if jesUncertainties[0] == "All": with open(self.jesInputFilePath + '/' + self.jesUncertaintyInputFileName) as f: lines = f.read().split("\n") sources = filter( lambda x: x.startswith("[") and x.endswith("]"), lines) sources = map(lambda x: x[1:-1], sources) self.jesUncertainties = sources # Define the jet recalibrator self.jetReCalibrator = JetReCalibrator( globalTag, jetType, True, self.jesInputFilePath, calculateSeparateCorrections=False, calculateType1METCorrection=False) # Define the recalibrator for level 1 corrections only self.jetReCalibratorL1 = JetReCalibrator( globalTag, jetType, False, self.jesInputFilePath, calculateSeparateCorrections=True, calculateType1METCorrection=False, upToLevel=1) # Define the recalibrators for GT used in nanoAOD production (only needed to reproduce 2017 v2 MET) if globalTagProd: self.jetReCalibratorProd = JetReCalibrator( globalTagProd, jetType, True, self.jesInputFilePath, calculateSeparateCorrections=False, calculateType1METCorrection=False) self.jetReCalibratorProdL1 = JetReCalibrator( globalTagProd, jetType, False, self.jesInputFilePath, calculateSeparateCorrections=True, calculateType1METCorrection=False, upToLevel=1) else: self.jetReCalibratorProd = False self.jetReCalibratorProdL1 = False # define energy threshold below which jets are considered as "unclustered energy" # (cf. JetMETCorrections/Type1MET/python/correctionTermsPfMetType1Type2_cff.py ) self.unclEnThreshold = 15. # load libraries for accessing JES scale factors and uncertainties from txt files for library in [ "libCondFormatsJetMETObjects", "libPhysicsToolsNanoAODTools" ]: if library not in ROOT.gSystem.GetLibraries(): print("Load Library '%s'" % library.replace("lib", "")) ROOT.gSystem.Load(library) def beginJob(self): print( "Loading jet energy scale (JES) uncertainties from file '%s'" % os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName)) #self.jesUncertainty = ROOT.JetCorrectionUncertainty(os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName)) self.jesUncertainty = {} # implementation didn't seem to work for factorized JEC, try again another way for jesUncertainty in self.jesUncertainties: jesUncertainty_label = jesUncertainty if jesUncertainty == 'Total' and len(self.jesUncertainties) == 1: jesUncertainty_label = '' pars = ROOT.JetCorrectorParameters( os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName), jesUncertainty_label) self.jesUncertainty[ jesUncertainty] = ROOT.JetCorrectionUncertainty(pars) if not self.isData: self.jetSmearer.beginJob() def endJob(self): if not self.isData: self.jetSmearer.endJob() shutil.rmtree(self.jesInputFilePath) def beginFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): self.out = wrappedOutputTree self.out.branch("%s_pt_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_pt_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JEC" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JER" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_pt_nom" % self.metBranchName, "F") self.out.branch("%s_phi_nom" % self.metBranchName, "F") if not self.isData: self.out.branch("%s_pt_jer" % self.metBranchName, "F") self.out.branch("%s_phi_jer" % self.metBranchName, "F") for shift in ["Up", "Down"]: self.out.branch("%s_pt_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_pt_jer%s" % (self.metBranchName, shift), "F") self.out.branch("%s_phi_jer%s" % (self.metBranchName, shift), "F") for jesUncertainty in self.jesUncertainties: self.out.branch( "%s_pt_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) self.out.branch( "%s_mass_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) self.out.branch( "%s_pt_jes%s%s" % (self.metBranchName, jesUncertainty, shift), "F") self.out.branch( "%s_phi_jes%s%s" % (self.metBranchName, jesUncertainty, shift), "F") self.out.branch( "%s_pt_unclustEn%s" % (self.metBranchName, shift), "F") self.out.branch( "%s_phi_unclustEn%s" % (self.metBranchName, shift), "F") self.isV5NanoAOD = hasattr(inputTree, "Jet_muonSubtrFactor") print "nanoAODv5?", self.isV5NanoAOD def endFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): pass def analyze(self, event): """process event, return True (go to next module) or False (fail, go to next event)""" jets = Collection(event, self.jetBranchName) nJet = event.nJet lowPtJets = Collection(event, "CorrT1METJet") if self.isV5NanoAOD else [] muons = Collection( event, "Muon" ) # to subtract out of the jets for proper type-1 MET corrections if not self.isData: genJets = Collection(event, self.genJetBranchName) # prepare the low pt jets (they don't have a rawFactor) for jet in lowPtJets: jet.pt = jet.rawPt jet.rawFactor = 0 jet.mass = 0 # the following dummy values should be removed once the values are kept in nanoAOD jet.neEmEF = 0 jet.chEmEF = 0 if not self.isData: self.jetSmearer.setSeed(event) jets_pt_raw = [] jets_pt_jer = [] jets_pt_nom = [] jets_mass_raw = [] jets_mass_nom = [] jets_corr_JEC = [] jets_corr_JER = [] jets_pt_jerUp = [] jets_pt_jerDown = [] jets_pt_jesUp = {} jets_pt_jesDown = {} jets_mass_jerUp = [] jets_mass_jerDown = [] jets_mass_jesUp = {} jets_mass_jesDown = {} for jesUncertainty in self.jesUncertainties: jets_pt_jesUp[jesUncertainty] = [] jets_pt_jesDown[jesUncertainty] = [] jets_mass_jesUp[jesUncertainty] = [] jets_mass_jesDown[jesUncertainty] = [] met = Object(event, self.metBranchName) rawmet = Object(event, "RawMET") if "Puppi" in self.metBranchName: rawmet = Object(event, "RawPuppiMET") defmet = Object(event, "MET") (t1met_px, t1met_py) = (met.pt * math.cos(met.phi), met.pt * math.sin(met.phi)) (def_met_px, def_met_py) = (defmet.pt * math.cos(defmet.phi), defmet.pt * math.sin(defmet.phi)) (met_px, met_py) = (rawmet.pt * math.cos(rawmet.phi), rawmet.pt * math.sin(rawmet.phi)) (met_px_nom, met_py_nom) = (met_px, met_py) (met_px_jer, met_py_jer) = (met_px, met_py) (met_px_jerUp, met_py_jerUp) = (met_px, met_py) (met_px_jerDown, met_py_jerDown) = (met_px, met_py) (met_px_jesUp, met_py_jesUp) = ({}, {}) (met_px_jesDown, met_py_jesDown) = ({}, {}) for jesUncertainty in self.jesUncertainties: met_px_jesUp[jesUncertainty] = met_px met_py_jesUp[jesUncertainty] = met_py met_px_jesDown[jesUncertainty] = met_px met_py_jesDown[jesUncertainty] = met_py # variables needed for re-applying JECs to 2017 v2 MET delta_x_T1Jet, delta_y_T1Jet = 0, 0 delta_x_rawJet, delta_y_rawJet = 0, 0 rho = getattr(event, self.rhoBranchName) # match reconstructed jets to generator level ones # (needed to evaluate JER scale factors and uncertainties) def resolution_matching(jet, genjet): '''Helper function to match to gen based on pt difference''' params = ROOT.PyJetParametersWrapper() params.setJetEta(jet.eta) params.setJetPt(jet.pt) params.setRho(rho) resolution = self.jetSmearer.jer.getResolution(params) return abs(jet.pt - genjet.pt) < 3 * resolution * jet.pt if not self.isData: pairs = matchObjectCollection(jets, genJets, dRmax=0.2, presel=resolution_matching) lowPtPairs = matchObjectCollection(lowPtJets, genJets, dRmax=0.2, presel=resolution_matching) pairs.update(lowPtPairs) for iJet, jet in enumerate(itertools.chain(jets, lowPtJets)): #jet pt and mass corrections jet_pt = jet.pt jet_mass = jet.mass jet_pt_orig = jet_pt rawFactor = jet.rawFactor #redo JECs if desired if hasattr(jet, "rawFactor"): jet_rawpt = jet_pt * (1 - jet.rawFactor) jet_rawmass = jet_mass * (1 - jet.rawFactor) else: jet_rawpt = -1.0 * jet_pt #If factor not present factor will be saved as -1 jet_rawmass = -1.0 * jet_mass #If factor not present factor will be saved as -1 (jet_pt, jet_mass) = self.jetReCalibrator.correct(jet, rho) (jet_pt_l1, jet_mass_l1) = self.jetReCalibratorL1.correct(jet, rho) jet.pt = jet_pt jet.mass = jet_mass # Get the JEC factors jec = jet_pt / jet_rawpt jecL1 = jet_pt_l1 / jet_rawpt if self.jetReCalibratorProd: jecProd = self.jetReCalibratorProd.correct(jet, rho)[0] / jet_rawpt jecL1Prod = self.jetReCalibratorProdL1.correct( jet, rho)[0] / jet_rawpt if not self.isData: genJet = pairs[jet] # get the jet for type-1 MET newjet = ROOT.TLorentzVector() if self.isV5NanoAOD: newjet.SetPtEtaPhiM( jet_pt_orig * (1 - jet.rawFactor) * (1 - jet.muonSubtrFactor), jet.eta, jet.phi, jet.mass) muon_pt = jet_pt_orig * (1 - jet.rawFactor) * jet.muonSubtrFactor else: newjet.SetPtEtaPhiM(jet_pt_orig * (1 - jet.rawFactor), jet.eta, jet.phi, jet.mass) muon_pt = 0 if hasattr(jet, 'muonIdx1'): if jet.muonIdx1 > -1: if muons[jet.muonIdx1].isGlobal: newjet = newjet - muons[jet.muonIdx1].p4() muon_pt += muons[jet.muonIdx1].pt if jet.muonIdx2 > -1: if muons[jet.muonIdx2].isGlobal: newjet = newjet - muons[jet.muonIdx2].p4() muon_pt += muons[jet.muonIdx2].pt # set the jet pt to the muon subtracted raw pt jet.pt = newjet.Pt() jet.rawFactor = 0 # get the proper jet pts for type-1 MET jet_pt_noMuL1L2L3 = jet.pt * jec jet_pt_noMuL1 = jet.pt * jecL1 # this step is only needed for v2 MET in 2017 when different JECs are applied compared to the nanoAOD production if self.jetReCalibratorProd: jet_pt_noMuProdL1L2L3 = jet.pt * jecProd if jet.pt * jecProd > self.unclEnThreshold else jet.pt jet_pt_noMuProdL1 = jet.pt * jecL1Prod if jet.pt * jecProd > self.unclEnThreshold else jet.pt else: jet_pt_noMuProdL1L2L3 = jet_pt_noMuL1L2L3 jet_pt_noMuProdL1 = jet_pt_noMuL1 ## setting jet back to central values jet.pt = jet_pt jet.rawFactor = rawFactor # evaluate JER scale factors and uncertainties # (cf. https://twiki.cern.ch/twiki/bin/view/CMS/JetResolution and https://twiki.cern.ch/twiki/bin/view/CMSPublic/WorkBookJetEnergyResolution ) if not self.isData: (jet_pt_jerNomVal, jet_pt_jerUpVal, jet_pt_jerDownVal) = self.jetSmearer.getSmearValsPt( jet, genJet, rho) else: # if you want to do something with JER in data, please add it here. (jet_pt_jerNomVal, jet_pt_jerUpVal, jet_pt_jerDownVal) = (1, 1, 1) # these are the important jet pt values #jet_pt_nom = jet_pt if jet_pt > 0 else 0 jet_pt_nom = jet_pt * jet_pt_jerNomVal if self.applySmearing else jet_pt jet_pt_L1L2L3 = jet_pt_noMuL1L2L3 + muon_pt jet_pt_L1 = jet_pt_noMuL1 + muon_pt # not nice, but needed for METv2 in 2017 jet_pt_prodL1L2L3 = jet_pt_noMuProdL1L2L3 + muon_pt jet_pt_prodL1 = jet_pt_noMuProdL1 + muon_pt if self.metBranchName == 'METFixEE2017': # get the delta for removing L1L2L3-L1 corrected jets (corrected with GT from nanoAOD production!!) in the EE region from the default MET branch. if jet_pt_prodL1L2L3 > self.unclEnThreshold and 2.65 < abs( jet.eta) < 3.14 and jet_rawpt < 50: delta_x_T1Jet += ( jet_pt_prodL1L2L3 - jet_pt_prodL1) * math.cos( jet.phi) + jet_rawpt * math.cos(jet.phi) delta_y_T1Jet += ( jet_pt_prodL1L2L3 - jet_pt_prodL1) * math.sin( jet.phi) + jet_rawpt * math.sin(jet.phi) # get the delta for removing raw jets in the EE region from the raw MET if jet_pt_prodL1L2L3 > self.unclEnThreshold and 2.65 < abs( jet.eta) < 3.14 and jet_rawpt < 50: delta_x_rawJet += jet_rawpt * math.cos(jet.phi) delta_y_rawJet += jet_rawpt * math.sin(jet.phi) # don't store the low pt jets in the Jet_pt_nom branch if iJet < nJet: jets_pt_raw.append(jet_rawpt) jets_pt_nom.append(jet_pt_nom) jets_mass_raw.append(jet_rawmass) jets_corr_JEC.append(jet_pt / jet_rawpt) jets_corr_JER.append( jet_pt_jerNomVal) # can be used to undo JER # no need to do this for low pt jets jet_mass_nom = jet_pt_jerNomVal * jet_mass if self.applySmearing else jet_mass if jet_mass_nom < 0.0: jet_mass_nom *= -1.0 jets_mass_nom.append(jet_mass_nom) if not self.isData: jet_pt_jerUp = jet_pt_jerUpVal * jet_pt jet_pt_jerDown = jet_pt_jerDownVal * jet_pt # evaluate JES uncertainties jet_pt_jesUp = {} jet_pt_jesDown = {} jet_pt_jesUpT1 = {} jet_pt_jesDownT1 = {} jet_mass_jesUp = {} jet_mass_jesDown = {} # don't store the low pt jets in the Jet_pt_nom branch if iJet < nJet: jets_pt_jerUp.append(jet_pt_jerUpVal * jet_pt) jets_pt_jerDown.append(jet_pt_jerDownVal * jet_pt) jets_mass_jerUp.append(jet_pt_jerUpVal * jet_mass) jets_mass_jerDown.append(jet_pt_jerDownVal * jet_mass) for jesUncertainty in self.jesUncertainties: # (cf. https://twiki.cern.ch/twiki/bin/view/CMSPublic/WorkBookJetEnergyCorrections#JetCorUncertainties ) self.jesUncertainty[jesUncertainty].setJetPt(jet_pt_nom) self.jesUncertainty[jesUncertainty].setJetEta(jet.eta) delta = self.jesUncertainty[jesUncertainty].getUncertainty( True) jet_pt_jesUp[jesUncertainty] = jet_pt_nom * (1. + delta) jet_pt_jesDown[jesUncertainty] = jet_pt_nom * (1. - delta) if iJet < nJet: jets_pt_jesUp[jesUncertainty].append( jet_pt_jesUp[jesUncertainty]) jets_pt_jesDown[jesUncertainty].append( jet_pt_jesDown[jesUncertainty]) jet_mass_jesUp[jesUncertainty] = jet_mass_nom * (1. + delta) jet_mass_jesDown[jesUncertainty] = jet_mass_nom * ( 1. - delta) jets_mass_jesUp[jesUncertainty].append( jet_mass_jesUp[jesUncertainty]) jets_mass_jesDown[jesUncertainty].append( jet_mass_jesDown[jesUncertainty]) # redo JES variations for T1 MET self.jesUncertainty[jesUncertainty].setJetPt(jet_pt_L1L2L3) self.jesUncertainty[jesUncertainty].setJetEta(jet.eta) delta = self.jesUncertainty[jesUncertainty].getUncertainty( True) jet_pt_jesUpT1[jesUncertainty] = jet_pt_L1L2L3 * (1. + delta) jet_pt_jesDownT1[jesUncertainty] = jet_pt_L1L2L3 * (1. - delta) # progate JER and JES corrections and uncertainties to MET. Only propagate JECs to MET if the corrected pt without the muon is above the threshold if jet_pt_noMuL1L2L3 > self.unclEnThreshold and (jet.neEmEF + jet.chEmEF) < 0.9: if not ( self.metBranchName == 'METFixEE2017' and 2.65 < abs(jet.eta) < 3.14 and jet.pt * (1 - jet.rawFactor) < 50 ): # do not re-correct for jets that aren't included in METv2 recipe jet_cosPhi = math.cos(jet.phi) jet_sinPhi = math.sin(jet.phi) met_px_nom = met_px_nom - (jet_pt_L1L2L3 - jet_pt_L1) * jet_cosPhi met_py_nom = met_py_nom - (jet_pt_L1L2L3 - jet_pt_L1) * jet_sinPhi if not self.isData: met_px_jer = met_px_jer - ( jet_pt_L1L2L3 * jet_pt_jerNomVal - jet_pt_L1) * jet_cosPhi met_py_jer = met_py_jer - ( jet_pt_L1L2L3 * jet_pt_jerNomVal - jet_pt_L1) * jet_sinPhi met_px_jerUp = met_px_jerUp - ( jet_pt_L1L2L3 * jet_pt_jerUpVal - jet_pt_L1) * jet_cosPhi met_py_jerUp = met_py_jerUp - ( jet_pt_L1L2L3 * jet_pt_jerUpVal - jet_pt_L1) * jet_sinPhi met_px_jerDown = met_px_jerDown - ( jet_pt_L1L2L3 * jet_pt_jerDownVal - jet_pt_L1) * jet_cosPhi met_py_jerDown = met_py_jerDown - ( jet_pt_L1L2L3 * jet_pt_jerDownVal - jet_pt_L1) * jet_sinPhi for jesUncertainty in self.jesUncertainties: met_px_jesUp[jesUncertainty] = met_px_jesUp[ jesUncertainty] - ( jet_pt_jesUpT1[jesUncertainty] - jet_pt_L1) * jet_cosPhi met_py_jesUp[jesUncertainty] = met_py_jesUp[ jesUncertainty] - ( jet_pt_jesUpT1[jesUncertainty] - jet_pt_L1) * jet_sinPhi met_px_jesDown[jesUncertainty] = met_px_jesDown[ jesUncertainty] - ( jet_pt_jesDownT1[jesUncertainty] - jet_pt_L1) * jet_cosPhi met_py_jesDown[jesUncertainty] = met_py_jesDown[ jesUncertainty] - ( jet_pt_jesDownT1[jesUncertainty] - jet_pt_L1) * jet_sinPhi # propagate "unclustered energy" uncertainty to MET if self.metBranchName == 'METFixEE2017': # Remove the L1L2L3-L1 corrected jets in the EE region from the default MET branch def_met_px += delta_x_T1Jet def_met_py += delta_y_T1Jet # get unclustered energy part that is removed in the v2 recipe met_unclEE_x = def_met_px - t1met_px met_unclEE_y = def_met_py - t1met_py # finalize the v2 recipe for the rawMET by removing the unclustered part in the EE region met_px_nom += delta_x_rawJet - met_unclEE_x met_py_nom += delta_y_rawJet - met_unclEE_y if not self.isData: # apply v2 recipe correction factor also to JER central value and variations met_px_jer += delta_x_rawJet - met_unclEE_x met_py_jer += delta_y_rawJet - met_unclEE_y met_px_jerUp += delta_x_rawJet - met_unclEE_x met_py_jerUp += delta_y_rawJet - met_unclEE_y met_px_jerDown += delta_x_rawJet - met_unclEE_x met_py_jerDown += delta_y_rawJet - met_unclEE_y for jesUncertainty in self.jesUncertainties: met_px_jesUp[ jesUncertainty] += delta_x_rawJet - met_unclEE_x met_py_jesUp[ jesUncertainty] += delta_y_rawJet - met_unclEE_y met_px_jesDown[ jesUncertainty] += delta_x_rawJet - met_unclEE_x met_py_jesDown[ jesUncertainty] += delta_y_rawJet - met_unclEE_y if not self.isData: (met_px_unclEnUp, met_py_unclEnUp) = (met_px_nom, met_py_nom) (met_px_unclEnDown, met_py_unclEnDown) = (met_px_nom, met_py_nom) met_deltaPx_unclEn = getattr( event, self.metBranchName + "_MetUnclustEnUpDeltaX") met_deltaPy_unclEn = getattr( event, self.metBranchName + "_MetUnclustEnUpDeltaY") met_px_unclEnUp = met_px_unclEnUp + met_deltaPx_unclEn met_py_unclEnUp = met_py_unclEnUp + met_deltaPy_unclEn met_px_unclEnDown = met_px_unclEnDown - met_deltaPx_unclEn met_py_unclEnDown = met_py_unclEnDown - met_deltaPy_unclEn self.out.fillBranch("%s_pt_raw" % self.jetBranchName, jets_pt_raw) self.out.fillBranch("%s_pt_nom" % self.jetBranchName, jets_pt_nom) self.out.fillBranch("%s_corr_JEC" % self.jetBranchName, jets_corr_JEC) self.out.fillBranch("%s_corr_JER" % self.jetBranchName, jets_corr_JER) if not self.isData: self.out.fillBranch("%s_pt_jerUp" % self.jetBranchName, jets_pt_jerUp) self.out.fillBranch("%s_pt_jerDown" % self.jetBranchName, jets_pt_jerDown) self.out.fillBranch("%s_pt_nom" % self.metBranchName, math.sqrt(met_px_nom**2 + met_py_nom**2)) self.out.fillBranch("%s_phi_nom" % self.metBranchName, math.atan2(met_py_nom, met_px_nom)) self.out.fillBranch("%s_mass_raw" % self.jetBranchName, jets_mass_raw) self.out.fillBranch("%s_mass_nom" % self.jetBranchName, jets_mass_nom) if not self.isData: self.out.fillBranch("%s_mass_jerUp" % self.jetBranchName, jets_mass_jerUp) self.out.fillBranch("%s_mass_jerDown" % self.jetBranchName, jets_mass_jerDown) if not self.isData: self.out.fillBranch("%s_pt_jer" % self.metBranchName, math.sqrt(met_px_jer**2 + met_py_jer**2)) self.out.fillBranch("%s_phi_jer" % self.metBranchName, math.atan2(met_py_jer, met_px_jer)) self.out.fillBranch("%s_pt_jerUp" % self.metBranchName, math.sqrt(met_px_jerUp**2 + met_py_jerUp**2)) self.out.fillBranch("%s_phi_jerUp" % self.metBranchName, math.atan2(met_py_jerUp, met_px_jerUp)) self.out.fillBranch( "%s_pt_jerDown" % self.metBranchName, math.sqrt(met_px_jerDown**2 + met_py_jerDown**2)) self.out.fillBranch("%s_phi_jerDown" % self.metBranchName, math.atan2(met_py_jerDown, met_px_jerDown)) for jesUncertainty in self.jesUncertainties: self.out.fillBranch( "%s_pt_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_pt_jesUp[jesUncertainty]) self.out.fillBranch( "%s_pt_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_pt_jesDown[jesUncertainty]) self.out.fillBranch( "%s_pt_jes%sUp" % (self.metBranchName, jesUncertainty), math.sqrt(met_px_jesUp[jesUncertainty]**2 + met_py_jesUp[jesUncertainty]**2)) self.out.fillBranch( "%s_phi_jes%sUp" % (self.metBranchName, jesUncertainty), math.atan2(met_py_jesUp[jesUncertainty], met_px_jesUp[jesUncertainty])) self.out.fillBranch( "%s_pt_jes%sDown" % (self.metBranchName, jesUncertainty), math.sqrt(met_px_jesDown[jesUncertainty]**2 + met_py_jesDown[jesUncertainty]**2)) self.out.fillBranch( "%s_phi_jes%sDown" % (self.metBranchName, jesUncertainty), math.atan2(met_py_jesDown[jesUncertainty], met_px_jesDown[jesUncertainty])) self.out.fillBranch( "%s_mass_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_mass_jesUp[jesUncertainty]) self.out.fillBranch( "%s_mass_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_mass_jesDown[jesUncertainty]) self.out.fillBranch( "%s_pt_unclustEnUp" % self.metBranchName, math.sqrt(met_px_unclEnUp**2 + met_py_unclEnUp**2)) self.out.fillBranch("%s_phi_unclustEnUp" % self.metBranchName, math.atan2(met_py_unclEnUp, met_px_unclEnUp)) self.out.fillBranch( "%s_pt_unclustEnDown" % self.metBranchName, math.sqrt(met_px_unclEnDown**2 + met_py_unclEnDown**2)) self.out.fillBranch( "%s_phi_unclustEnDown" % self.metBranchName, math.atan2(met_py_unclEnDown, met_px_unclEnDown)) return True
class jetmetUncertaintiesProducer(Module): def __init__(self, era, globalTag, jesUncertainties = [ "Total" ], jetType = "AK4PFchs", redoJEC=False, noGroom=False, jerTag="", jmrVals = [], jmsVals = []): self.era = era self.redoJEC = redoJEC self.noGroom = noGroom #-------------------------------------------------------------------------------------------- # CV: globalTag and jetType not yet used in the jet smearer, as there is no consistent set of # txt files for JES uncertainties and JER scale factors and uncertainties yet #-------------------------------------------------------------------------------------------- self.jesUncertainties = jesUncertainties # smear jet pT to account for measured difference in JER between data and simulation. if jerTag != "": self.jerInputFileName = jerTag + "_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = jerTag + "_SF_" + jetType + ".txt" else: print "WARNING: jerTag is empty!!! This module will soon be deprecated! Please use jetmetHelperRun2 in the future." if era == "2016": self.jerInputFileName = "Summer16_25nsV1_MC_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = "Summer16_25nsV1_MC_SF_" + jetType + ".txt" elif era == "2017" or era == "2018": # use Fall17 as temporary placeholder until post-Moriond 2019 JERs are out self.jerInputFileName = "Fall17_V3_MC_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = "Fall17_V3_MC_SF_" + jetType + ".txt" #jet mass resolution: https://twiki.cern.ch/twiki/bin/view/CMS/JetWtagging self.jmrVals = jmrVals if not self.jmrVals: print "WARNING: jmrVals is empty!!! Using default values. This module will soon be deprecated! Please use jetmetHelperRun2 in the future." self.jmrVals = [1.0, 1.2, 0.8] #nominal, up, down # Use 2017 values for 2018 until 2018 are released if self.era in ["2017","2018"]: self.jmrVals = [1.09, 1.14, 1.04] self.jetSmearer = jetSmearer(globalTag, jetType, self.jerInputFileName, self.jerUncertaintyInputFileName, self.jmrVals) if "AK4" in jetType : self.jetBranchName = "Jet" self.genJetBranchName = "GenJet" self.genSubJetBranchName = None self.doGroomed = False self.corrMET = True elif "AK8" in jetType : self.jetBranchName = "FatJet" self.subJetBranchName = "SubJet" self.genJetBranchName = "GenJetAK8" self.genSubJetBranchName = "SubGenJetAK8" if not self.noGroom: self.doGroomed = True self.puppiCorrFile = ROOT.TFile.Open(os.environ['CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/puppiCorr.root") self.puppisd_corrGEN = self.puppiCorrFile.Get("puppiJECcorr_gen") self.puppisd_corrRECO_cen = self.puppiCorrFile.Get("puppiJECcorr_reco_0eta1v3") self.puppisd_corrRECO_for = self.puppiCorrFile.Get("puppiJECcorr_reco_1v3eta2v5") else: self.doGroomed = False self.corrMET = False else: raise ValueError("ERROR: Invalid jet type = '%s'!" % jetType) self.metBranchName = "MET" self.rhoBranchName = "fixedGridRhoFastjetAll" self.lenVar = "n" + self.jetBranchName #jet mass scale self.jmsVals = jmsVals if not self.jmsVals: print "WARNING: jmsVals is empty!!! Using default values! This module will soon be deprecated! Please use jetmetHelperRun2 in the future." #2016 values self.jmsVals = [1.00, 0.9906, 1.0094] #nominal, down, up # Use 2017 values for 2018 until 2018 are released if self.era in ["2017","2018"]: self.jmsVals = [0.982, 0.978, 0.986] # read jet energy scale (JES) uncertainties # (downloaded from https://twiki.cern.ch/twiki/bin/view/CMS/JECDataMC ) self.jesInputArchivePath = os.environ['CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/" # Text files are now tarred so must extract first into temporary directory (gets deleted during python memory management at script exit) self.jesArchive = tarfile.open(self.jesInputArchivePath+globalTag+".tgz", "r:gz") self.jesInputFilePath = tempfile.mkdtemp() self.jesArchive.extractall(self.jesInputFilePath) if len(jesUncertainties) == 1 and jesUncertainties[0] == "Total": self.jesUncertaintyInputFileName = globalTag + "_Uncertainty_" + jetType + ".txt" else: self.jesUncertaintyInputFileName = globalTag + "_UncertaintySources_" + jetType + ".txt" # read all uncertainty source names from the loaded file if jesUncertainties[0] == "All": with open(self.jesInputFilePath+'/'+self.jesUncertaintyInputFileName) as f: lines = f.read().split("\n") sources = filter(lambda x: x.startswith("[") and x.endswith("]"), lines) sources = map(lambda x: x[1:-1], sources) self.jesUncertainties = sources if self.redoJEC : self.jetReCalibrator = JetReCalibrator(globalTag, jetType , True, self.jesInputFilePath, calculateSeparateCorrections = False, calculateType1METCorrection = False) # define energy threshold below which jets are considered as "unclustered energy" # (cf. JetMETCorrections/Type1MET/python/correctionTermsPfMetType1Type2_cff.py ) self.unclEnThreshold = 15. # load libraries for accessing JES scale factors and uncertainties from txt files for library in [ "libCondFormatsJetMETObjects", "libPhysicsToolsNanoAODTools" ]: if library not in ROOT.gSystem.GetLibraries(): print("Load Library '%s'" % library.replace("lib", "")) ROOT.gSystem.Load(library) def beginJob(self): print("Loading jet energy scale (JES) uncertainties from file '%s'" % os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName)) #self.jesUncertainty = ROOT.JetCorrectionUncertainty(os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName)) self.jesUncertainty = {} # implementation didn't seem to work for factorized JEC, try again another way for jesUncertainty in self.jesUncertainties: jesUncertainty_label = jesUncertainty if jesUncertainty == 'Total' and len(self.jesUncertainties) == 1: jesUncertainty_label = '' pars = ROOT.JetCorrectorParameters(os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName),jesUncertainty_label) self.jesUncertainty[jesUncertainty] = ROOT.JetCorrectionUncertainty(pars) self.jetSmearer.beginJob() def endJob(self): self.jetSmearer.endJob() shutil.rmtree(self.jesInputFilePath) def beginFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): self.out = wrappedOutputTree print inputFile, outputFile, inputTree, wrappedOutputTree self.out.branch("%s_pt_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_pt_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JEC" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JER" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JMS" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JMR" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_L2L3" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_L1" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_LRes" % self.jetBranchName, "F", lenVar=self.lenVar) if self.doGroomed: self.out.branch("%s_msoftdrop_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_tau21DDT_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_corr_JMR" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_corr_JMS" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_corr_PUPPI" % self.jetBranchName, "F", lenVar=self.lenVar) if self.corrMET: self.out.branch("%s_pt_nom" % self.metBranchName, "F") self.out.branch("%s_phi_nom" % self.metBranchName, "F") for shift in [ "Up", "Down" ]: self.out.branch("%s_pt_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jmr%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jms%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) if self.doGroomed: self.out.branch("%s_msoftdrop_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_jmr%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_jms%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_tau21DDT_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_tau21DDT_jmr%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_tau21DDT_jms%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) if self.corrMET : self.out.branch("%s_pt_jer%s" % (self.metBranchName, shift), "F") self.out.branch("%s_phi_jer%s" % (self.metBranchName, shift), "F") for jesUncertainty in self.jesUncertainties: self.out.branch("%s_pt_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) if self.doGroomed: self.out.branch("%s_msoftdrop_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) if self.corrMET : self.out.branch("%s_pt_jes%s%s" % (self.metBranchName, jesUncertainty, shift), "F") self.out.branch("%s_phi_jes%s%s" % (self.metBranchName, jesUncertainty, shift), "F") if self.corrMET : self.out.branch("%s_pt_unclustEn%s" % (self.metBranchName, shift), "F") self.out.branch("%s_phi_unclustEn%s" % (self.metBranchName, shift), "F") def endFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): pass def analyze(self, event): """process event, return True (go to next module) or False (fail, go to next event)""" jets = Collection(event, self.jetBranchName ) genJets = Collection(event, self.genJetBranchName ) if self.doGroomed : subJets = Collection(event, self.subJetBranchName ) genSubJets = Collection(event, self.genSubJetBranchName ) genSubJetMatcher = matchObjectCollectionMultiple( genJets, genSubJets, dRmax=0.8 ) self.jetSmearer.setSeed(event) jet_L1 = [] jet_L2L3 = [] jet_LRes = [] jets_pt_raw = [] jets_pt_nom = [] jets_mass_raw = [] jets_mass_nom = [] jets_corr_JEC = [] jets_corr_JER = [] jets_corr_JMS = [] jets_corr_JMR = [] jets_pt_jerUp = [] jets_pt_jerDown = [] jets_pt_jesUp = {} jets_pt_jesDown = {} jets_mass_jerUp = [] jets_mass_jerDown = [] jets_mass_jmrUp = [] jets_mass_jmrDown = [] jets_mass_jesUp = {} jets_mass_jesDown = {} jets_mass_jmsUp = [] jets_mass_jmsDown = [] for jesUncertainty in self.jesUncertainties: jets_pt_jesUp[jesUncertainty] = [] jets_pt_jesDown[jesUncertainty] = [] jets_mass_jesUp[jesUncertainty] = [] jets_mass_jesDown[jesUncertainty] = [] if self.corrMET : met = Object(event, self.metBranchName) ( met_px, met_py ) = ( met.pt*math.cos(met.phi), met.pt*math.sin(met.phi) ) ( met_px_nom, met_py_nom ) = ( met_px, met_py ) ( met_px_jerUp, met_py_jerUp ) = ( met_px, met_py ) ( met_px_jerDown, met_py_jerDown ) = ( met_px, met_py ) ( met_px_jesUp, met_py_jesUp ) = ( {}, {} ) ( met_px_jesDown, met_py_jesDown ) = ( {}, {} ) for jesUncertainty in self.jesUncertainties: met_px_jesUp[jesUncertainty] = met_px met_py_jesUp[jesUncertainty] = met_py met_px_jesDown[jesUncertainty] = met_px met_py_jesDown[jesUncertainty] = met_py if self.doGroomed: jets_msdcorr_raw = [] jets_msdcorr_nom = [] jets_msdcorr_corr_JMR = [] jets_msdcorr_corr_JMS = [] jets_msdcorr_corr_PUPPI = [] jets_msdcorr_jerUp = [] jets_msdcorr_jerDown = [] jets_msdcorr_jmrUp = [] jets_msdcorr_jmrDown = [] jets_msdcorr_jesUp = {} jets_msdcorr_jesDown = {} jets_msdcorr_jmsUp = [] jets_msdcorr_jmsDown = [] jets_msdcorr_tau21DDT_nom = [] jets_msdcorr_tau21DDT_jerUp = [] jets_msdcorr_tau21DDT_jerDown = [] jets_msdcorr_tau21DDT_jmrUp = [] jets_msdcorr_tau21DDT_jmrDown = [] jets_msdcorr_tau21DDT_jmsUp = [] jets_msdcorr_tau21DDT_jmsDown = [] for jesUncertainty in self.jesUncertainties: jets_msdcorr_jesUp[jesUncertainty] = [] jets_msdcorr_jesDown[jesUncertainty] = [] rho = getattr(event, self.rhoBranchName) # match reconstructed jets to generator level ones # (needed to evaluate JER scale factors and uncertainties) pairs = matchObjectCollection(jets, genJets) for jet in jets: #jet pt and mass corrections jet_pt=jet.pt jet_mass=jet.mass jet_L1.append(self.jetReCalibrator.getCorrection(jet,rho, corrector=self.jetReCalibrator.separateJetCorrectors["myL1"])) jet_L2L3.append(self.jetReCalibrator.getCorrection(jet,rho, corrector=self.jetReCalibrator.separateJetCorrectors["myL2L3"])) jet_LRes.append(self.jetReCalibrator.getCorrection(jet,rho, corrector=self.jetReCalibrator.separateJetCorrectors["myRes"])) #redo JECs if desired if hasattr(jet, "rawFactor"): jet_rawpt = jet_pt * (1 - jet.rawFactor) jet_rawmass = jet_mass * (1 - jet.rawFactor) else: jet_rawpt = -1.0 * jet_pt #If factor not present factor will be saved as -1 jet_rawmass = -1.0 * jet_mass #If factor not present factor will be saved as -1 if self.redoJEC : (jet_pt, jet_mass) = self.jetReCalibrator.correct(jet,rho) jet.pt = jet_pt jet.mass = jet_mass jets_pt_raw.append(jet_rawpt) jets_mass_raw.append(jet_rawmass) jets_corr_JEC.append(jet_pt/jet_rawpt) genJet = pairs[jet] if self.doGroomed: genGroomedSubJets = genSubJetMatcher[genJet] if genJet != None else None genGroomedJet = genGroomedSubJets[0].p4() + genGroomedSubJets[1].p4() if genGroomedSubJets != None and len(genGroomedSubJets) >= 2 else None if jet.subJetIdx1 >= 0 and jet.subJetIdx2 >= 0 : groomedP4 = subJets[ jet.subJetIdx1 ].p4() + subJets[ jet.subJetIdx2].p4() #check subjet jecs else : groomedP4 = None # LC: Apply PUPPI SD mass correction https://github.com/cms-jet/PuppiSoftdropMassCorr/ puppisd_genCorr = self.puppisd_corrGEN.Eval(jet.pt) if abs(jet.eta) <= 1.3: puppisd_recoCorr = self.puppisd_corrRECO_cen.Eval(jet.pt) else: puppisd_recoCorr = self.puppisd_corrRECO_for.Eval(jet.pt) puppisd_total = puppisd_genCorr * puppisd_recoCorr jets_msdcorr_corr_PUPPI.append(puppisd_total) if groomedP4 != None: groomedP4.SetPtEtaPhiM(groomedP4.Perp(), groomedP4.Eta(), groomedP4.Phi(), groomedP4.M()*puppisd_total) # evaluate JER scale factors and uncertainties # (cf. https://twiki.cern.ch/twiki/bin/view/CMS/JetResolution and https://twiki.cern.ch/twiki/bin/view/CMSPublic/WorkBookJetEnergyResolution ) ( jet_pt_jerNomVal, jet_pt_jerUpVal, jet_pt_jerDownVal ) = self.jetSmearer.getSmearValsPt(jet, genJet, rho) jets_corr_JER.append(jet_pt_jerNomVal) jet_pt_nom = jet_pt_jerNomVal *jet_pt if jet_pt_nom < 0.0: jet_pt_nom *= -1.0 jet_pt_jerUp = jet_pt_jerUpVal *jet_pt jet_pt_jerDown = jet_pt_jerDownVal*jet_pt jets_pt_nom .append(jet_pt_nom) jets_pt_jerUp .append(jet_pt_jerUpVal*jet_pt) jets_pt_jerDown.append(jet_pt_jerDownVal*jet_pt) # evaluate JES uncertainties jet_pt_jesUp = {} jet_pt_jesDown = {} jet_mass_jesUp = {} jet_mass_jesDown = {} jet_mass_jmsUp = [] jet_mass_jmsDown = [] # Evaluate JMS and JMR scale factors and uncertainties jmsNomVal = self.jmsVals[0] jmsDownVal = self.jmsVals[1] jmsUpVal = self.jmsVals[2] ( jet_mass_jmrNomVal, jet_mass_jmrUpVal, jet_mass_jmrDownVal ) = self.jetSmearer.getSmearValsM(jet, genJet) jets_corr_JMS .append(jmsNomVal) jets_corr_JMR .append(jet_mass_jmrNomVal) jet_mass_nom = jet_pt_jerNomVal*jet_mass_jmrNomVal*jmsNomVal*jet_mass if jet_mass_nom < 0.0: jet_mass_nom *= -1.0 jets_mass_nom .append(jet_mass_nom) jets_mass_jerUp .append(jet_pt_jerUpVal *jet_mass_jmrNomVal *jmsNomVal *jet_mass) jets_mass_jerDown.append(jet_pt_jerDownVal*jet_mass_jmrNomVal *jmsNomVal *jet_mass) jets_mass_jmrUp .append(jet_pt_jerNomVal *jet_mass_jmrUpVal *jmsNomVal *jet_mass) jets_mass_jmrDown.append(jet_pt_jerNomVal *jet_mass_jmrDownVal*jmsNomVal *jet_mass) jets_mass_jmsUp .append(jet_pt_jerNomVal *jet_mass_jmrNomVal *jmsUpVal *jet_mass) jets_mass_jmsDown.append(jet_pt_jerNomVal *jet_mass_jmrNomVal *jmsDownVal *jet_mass) if self.doGroomed : # evaluate JES uncertainties jet_msdcorr_jesUp = {} jet_msdcorr_jesDown = {} # Evaluate JMS and JMR scale factors and uncertainties ( jet_msdcorr_jmrNomVal, jet_msdcorr_jmrUpVal, jet_msdcorr_jmrDownVal ) = self.jetSmearer.getSmearValsM(groomedP4, genGroomedJet) if groomedP4 != None and genGroomedJet != None else (0.,0.,0.) jet_msdcorr_raw = groomedP4.M() if groomedP4 != None else 0.0 jets_msdcorr_corr_JMS.append(jmsNomVal) jets_msdcorr_corr_JMR.append(jet_msdcorr_jmrNomVal) if jet_msdcorr_raw < 0.0: jet_msdcorr_raw *= -1.0 jet_msdcorr_nom = jet_pt_jerNomVal*jet_msdcorr_jmrNomVal*jmsNomVal*jet_msdcorr_raw jets_msdcorr_raw .append(jet_msdcorr_raw) #fix later so jec's not applied - LC: Current PUPPI SD mass correction implementation needs the JECs applied before looking up the correction so make sure that's accounted for if this is changed. jets_msdcorr_nom .append(jet_msdcorr_nom) jets_msdcorr_jerUp .append(jet_pt_jerUpVal *jet_msdcorr_jmrNomVal *jmsNomVal *jet_msdcorr_raw) jets_msdcorr_jerDown.append(jet_pt_jerDownVal*jet_msdcorr_jmrNomVal *jmsNomVal *jet_msdcorr_raw) jets_msdcorr_jmrUp .append(jet_pt_jerNomVal *jet_msdcorr_jmrUpVal *jmsNomVal *jet_msdcorr_raw) jets_msdcorr_jmrDown.append(jet_pt_jerNomVal *jet_msdcorr_jmrDownVal*jmsNomVal *jet_msdcorr_raw) jets_msdcorr_jmsUp .append(jet_pt_jerNomVal *jet_msdcorr_jmrNomVal *jmsUpVal *jet_msdcorr_raw) jets_msdcorr_jmsDown.append(jet_pt_jerNomVal *jet_msdcorr_jmrNomVal *jmsDownVal *jet_msdcorr_raw) #Also evaluated JMS&JMR SD corr in tau21DDT region: https://twiki.cern.ch/twiki/bin/viewauth/CMS/JetWtagging#tau21DDT_0_43 if self.era in ["2016"]: jmstau21DDTNomVal = 1.014 jmstau21DDTDownVal = 1.007 jmstau21DDTUpVal = 1.021 self.jetSmearer.jmr_vals = [1.086,1.176,0.996] elif self.era in ["2017","2018"]: jmstau21DDTNomVal = 0.983 jmstau21DDTDownVal = 0.976 jmstau21DDTUpVal = 0.99 self.jetSmearer.jmr_vals = [1.080,1.161,0.999] ( jet_msdcorr_tau21DDT_jmrNomVal, jet_msdcorr_tau21DDT_jmrUpVal, jet_msdcorr_tau21DDT_jmrDownVal ) = self.jetSmearer.getSmearValsM(groomedP4, genGroomedJet) if groomedP4 != None and genGroomedJet != None else (0.,0.,0.) jet_msdcorr_tau21DDT_nom = jet_pt_jerNomVal*jet_msdcorr_tau21DDT_jmrNomVal*jmstau21DDTNomVal*jet_msdcorr_raw jets_msdcorr_tau21DDT_nom .append(jet_msdcorr_tau21DDT_nom) jets_msdcorr_tau21DDT_jerUp .append(jet_pt_jerUpVal *jet_msdcorr_tau21DDT_jmrNomVal *jmstau21DDTNomVal *jet_msdcorr_raw) jets_msdcorr_tau21DDT_jerDown.append(jet_pt_jerDownVal*jet_msdcorr_tau21DDT_jmrNomVal *jmstau21DDTNomVal *jet_msdcorr_raw) jets_msdcorr_tau21DDT_jmrUp .append(jet_pt_jerNomVal *jet_msdcorr_tau21DDT_jmrUpVal *jmstau21DDTNomVal *jet_msdcorr_raw) jets_msdcorr_tau21DDT_jmrDown.append(jet_pt_jerNomVal *jet_msdcorr_tau21DDT_jmrDownVal*jmstau21DDTNomVal *jet_msdcorr_raw) jets_msdcorr_tau21DDT_jmsUp .append(jet_pt_jerNomVal *jet_msdcorr_tau21DDT_jmrNomVal *jmstau21DDTUpVal *jet_msdcorr_raw) jets_msdcorr_tau21DDT_jmsDown.append(jet_pt_jerNomVal *jet_msdcorr_tau21DDT_jmrNomVal *jmstau21DDTDownVal *jet_msdcorr_raw) #Restore original jmr_vals in jetSmearer self.jetSmearer.jmr_vals = self.jmrVals for jesUncertainty in self.jesUncertainties: # (cf. https://twiki.cern.ch/twiki/bin/view/CMSPublic/WorkBookJetEnergyCorrections#JetCorUncertainties ) self.jesUncertainty[jesUncertainty].setJetPt(jet_pt_nom) self.jesUncertainty[jesUncertainty].setJetEta(jet.eta) delta = self.jesUncertainty[jesUncertainty].getUncertainty(True) jet_pt_jesUp[jesUncertainty] = jet_pt_nom*(1. + delta) jet_pt_jesDown[jesUncertainty] = jet_pt_nom*(1. - delta) jets_pt_jesUp[jesUncertainty].append(jet_pt_jesUp[jesUncertainty]) jets_pt_jesDown[jesUncertainty].append(jet_pt_jesDown[jesUncertainty]) jet_mass_jesUp [jesUncertainty] = jet_mass_nom*(1. + delta) jet_mass_jesDown [jesUncertainty] = jet_mass_nom*(1. - delta) jets_mass_jesUp [jesUncertainty].append(jet_mass_jesUp[jesUncertainty]) jets_mass_jesDown[jesUncertainty].append(jet_mass_jesDown[jesUncertainty]) if self.doGroomed : jet_msdcorr_jesUp [jesUncertainty] = jet_msdcorr_nom*(1. + delta) jet_msdcorr_jesDown [jesUncertainty] = jet_msdcorr_nom*(1. - delta) jets_msdcorr_jesUp [jesUncertainty].append(jet_msdcorr_jesUp[jesUncertainty]) jets_msdcorr_jesDown[jesUncertainty].append(jet_msdcorr_jesDown[jesUncertainty]) # propagate JER and JES corrections and uncertainties to MET if self.corrMET and jet_pt_nom > self.unclEnThreshold: jet_cosPhi = math.cos(jet.phi) jet_sinPhi = math.sin(jet.phi) met_px_nom = met_px_nom - (jet_pt_nom - jet_pt)*jet_cosPhi met_py_nom = met_py_nom - (jet_pt_nom - jet_pt)*jet_sinPhi met_px_jerUp = met_px_jerUp - (jet_pt_jerUp - jet_pt_nom)*jet_cosPhi met_py_jerUp = met_py_jerUp - (jet_pt_jerUp - jet_pt_nom)*jet_sinPhi met_px_jerDown = met_px_jerDown - (jet_pt_jerDown - jet_pt_nom)*jet_cosPhi met_py_jerDown = met_py_jerDown - (jet_pt_jerDown - jet_pt_nom)*jet_sinPhi for jesUncertainty in self.jesUncertainties: met_px_jesUp[jesUncertainty] = met_px_jesUp[jesUncertainty] - (jet_pt_jesUp[jesUncertainty] - jet_pt_nom)*jet_cosPhi met_py_jesUp[jesUncertainty] = met_py_jesUp[jesUncertainty] - (jet_pt_jesUp[jesUncertainty] - jet_pt_nom)*jet_sinPhi met_px_jesDown[jesUncertainty] = met_px_jesDown[jesUncertainty] - (jet_pt_jesDown[jesUncertainty] - jet_pt_nom)*jet_cosPhi met_py_jesDown[jesUncertainty] = met_py_jesDown[jesUncertainty] - (jet_pt_jesDown[jesUncertainty] - jet_pt_nom)*jet_sinPhi # propagate "unclustered energy" uncertainty to MET if self.corrMET : ( met_px_unclEnUp, met_py_unclEnUp ) = ( met_px, met_py ) ( met_px_unclEnDown, met_py_unclEnDown ) = ( met_px, met_py ) met_deltaPx_unclEn = getattr(event, self.metBranchName + "_MetUnclustEnUpDeltaX") met_deltaPy_unclEn = getattr(event, self.metBranchName + "_MetUnclustEnUpDeltaY") met_px_unclEnUp = met_px_unclEnUp + met_deltaPx_unclEn met_py_unclEnUp = met_py_unclEnUp + met_deltaPy_unclEn met_px_unclEnDown = met_px_unclEnDown - met_deltaPx_unclEn met_py_unclEnDown = met_py_unclEnDown - met_deltaPy_unclEn # propagate effect of jet energy smearing to MET met_px_jerUp = met_px_jerUp + (met_px_nom - met_px) met_py_jerUp = met_py_jerUp + (met_py_nom - met_py) met_px_jerDown = met_px_jerDown + (met_px_nom - met_px) met_py_jerDown = met_py_jerDown + (met_py_nom - met_py) for jesUncertainty in self.jesUncertainties: met_px_jesUp[jesUncertainty] = met_px_jesUp[jesUncertainty] + (met_px_nom - met_px) met_py_jesUp[jesUncertainty] = met_py_jesUp[jesUncertainty] + (met_py_nom - met_py) met_px_jesDown[jesUncertainty] = met_px_jesDown[jesUncertainty] + (met_px_nom - met_px) met_py_jesDown[jesUncertainty] = met_py_jesDown[jesUncertainty] + (met_py_nom - met_py) met_px_unclEnUp = met_px_unclEnUp + (met_px_nom - met_px) met_py_unclEnUp = met_py_unclEnUp + (met_py_nom - met_py) met_px_unclEnDown = met_px_unclEnDown + (met_px_nom - met_px) met_py_unclEnDown = met_py_unclEnDown + (met_py_nom - met_py) self.out.fillBranch("%s_pt_raw" % self.jetBranchName, jets_pt_raw) self.out.fillBranch("%s_pt_nom" % self.jetBranchName, jets_pt_nom) self.out.fillBranch("%s_corr_JEC" % self.jetBranchName, jets_corr_JEC) self.out.fillBranch("%s_corr_JER" % self.jetBranchName, jets_corr_JER) self.out.fillBranch("%s_pt_jerUp" % self.jetBranchName, jets_pt_jerUp) self.out.fillBranch("%s_pt_jerDown" % self.jetBranchName, jets_pt_jerDown) self.out.fillBranch("%s_mass_raw" % self.jetBranchName, jets_mass_raw) self.out.fillBranch("%s_mass_nom" % self.jetBranchName, jets_mass_nom) self.out.fillBranch("%s_corr_JMS" % self.jetBranchName, jets_corr_JMS) self.out.fillBranch("%s_corr_JMR" % self.jetBranchName, jets_corr_JMR) self.out.fillBranch("%s_mass_jerUp" % self.jetBranchName, jets_mass_jerUp) self.out.fillBranch("%s_mass_jerDown" % self.jetBranchName, jets_mass_jerDown) self.out.fillBranch("%s_mass_jmrUp" % self.jetBranchName, jets_mass_jmrUp) self.out.fillBranch("%s_mass_jmrDown" % self.jetBranchName, jets_mass_jmrDown) self.out.fillBranch("%s_mass_jmsUp" % self.jetBranchName, jets_mass_jmsUp) self.out.fillBranch("%s_mass_jmsDown" % self.jetBranchName, jets_mass_jmsDown) self.out.fillBranch("%s_L1" % self.jetBranchName, jet_L1) self.out.fillBranch("%s_L2L3" % self.jetBranchName, jet_L2L3) self.out.fillBranch("%s_LRes" % self.jetBranchName, jet_LRes) if self.doGroomed : self.out.fillBranch("%s_msoftdrop_raw" % self.jetBranchName, jets_msdcorr_raw) self.out.fillBranch("%s_msoftdrop_nom" % self.jetBranchName, jets_msdcorr_nom) self.out.fillBranch("%s_msoftdrop_corr_JMS" % self.jetBranchName, jets_msdcorr_corr_JMS) self.out.fillBranch("%s_msoftdrop_corr_JMR" % self.jetBranchName, jets_msdcorr_corr_JMR) self.out.fillBranch("%s_msoftdrop_jerUp" % self.jetBranchName, jets_msdcorr_jerUp) self.out.fillBranch("%s_msoftdrop_jerDown" % self.jetBranchName, jets_msdcorr_jerDown) self.out.fillBranch("%s_msoftdrop_corr_PUPPI" % self.jetBranchName, jets_msdcorr_corr_PUPPI) self.out.fillBranch("%s_msoftdrop_jmrUp" % self.jetBranchName, jets_msdcorr_jmrUp) self.out.fillBranch("%s_msoftdrop_jmrDown" % self.jetBranchName, jets_msdcorr_jmrDown) self.out.fillBranch("%s_msoftdrop_jmsUp" % self.jetBranchName, jets_msdcorr_jmsUp) self.out.fillBranch("%s_msoftdrop_jmsDown" % self.jetBranchName, jets_msdcorr_jmsDown) self.out.fillBranch("%s_msoftdrop_tau21DDT_nom" % self.jetBranchName, jets_msdcorr_tau21DDT_nom) self.out.fillBranch("%s_msoftdrop_tau21DDT_jerUp" % self.jetBranchName, jets_msdcorr_tau21DDT_jerUp) self.out.fillBranch("%s_msoftdrop_tau21DDT_jerDown" % self.jetBranchName, jets_msdcorr_tau21DDT_jerDown) self.out.fillBranch("%s_msoftdrop_tau21DDT_jmrUp" % self.jetBranchName, jets_msdcorr_tau21DDT_jmrUp) self.out.fillBranch("%s_msoftdrop_tau21DDT_jmrDown" % self.jetBranchName, jets_msdcorr_tau21DDT_jmrDown) self.out.fillBranch("%s_msoftdrop_tau21DDT_jmsUp" % self.jetBranchName, jets_msdcorr_tau21DDT_jmsUp) self.out.fillBranch("%s_msoftdrop_tau21DDT_jmsDown" % self.jetBranchName, jets_msdcorr_tau21DDT_jmsDown) if self.corrMET : self.out.fillBranch("%s_pt_nom" % self.metBranchName, math.sqrt(met_px_nom**2 + met_py_nom**2)) self.out.fillBranch("%s_phi_nom" % self.metBranchName, math.atan2(met_py_nom, met_px_nom)) self.out.fillBranch("%s_pt_jerUp" % self.metBranchName, math.sqrt(met_px_jerUp**2 + met_py_jerUp**2)) self.out.fillBranch("%s_phi_jerUp" % self.metBranchName, math.atan2(met_py_jerUp, met_px_jerUp)) self.out.fillBranch("%s_pt_jerDown" % self.metBranchName, math.sqrt(met_px_jerDown**2 + met_py_jerDown**2)) self.out.fillBranch("%s_phi_jerDown" % self.metBranchName, math.atan2(met_py_jerDown, met_px_jerDown)) for jesUncertainty in self.jesUncertainties: self.out.fillBranch("%s_pt_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_pt_jesUp[jesUncertainty]) self.out.fillBranch("%s_pt_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_pt_jesDown[jesUncertainty]) self.out.fillBranch("%s_mass_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_mass_jesUp[jesUncertainty]) self.out.fillBranch("%s_mass_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_mass_jesDown[jesUncertainty]) if self.doGroomed : self.out.fillBranch("%s_msoftdrop_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_msdcorr_jesUp[jesUncertainty]) self.out.fillBranch("%s_msoftdrop_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_msdcorr_jesDown[jesUncertainty]) if self.corrMET: self.out.fillBranch("%s_pt_jes%sUp" % (self.metBranchName, jesUncertainty), math.sqrt(met_px_jesUp[jesUncertainty]**2 + met_py_jesUp[jesUncertainty]**2)) self.out.fillBranch("%s_phi_jes%sUp" % (self.metBranchName, jesUncertainty), math.atan2(met_py_jesUp[jesUncertainty], met_px_jesUp[jesUncertainty])) self.out.fillBranch("%s_pt_jes%sDown" % (self.metBranchName, jesUncertainty), math.sqrt(met_px_jesDown[jesUncertainty]**2 + met_py_jesDown[jesUncertainty]**2)) self.out.fillBranch("%s_phi_jes%sDown" % (self.metBranchName, jesUncertainty), math.atan2(met_py_jesDown[jesUncertainty], met_px_jesDown[jesUncertainty])) if self.corrMET: self.out.fillBranch("%s_pt_unclustEnUp" % self.metBranchName, math.sqrt(met_px_unclEnUp**2 + met_py_unclEnUp**2)) self.out.fillBranch("%s_phi_unclustEnUp" % self.metBranchName, math.atan2(met_py_unclEnUp, met_px_unclEnUp)) self.out.fillBranch("%s_pt_unclustEnDown" % self.metBranchName, math.sqrt(met_px_unclEnDown**2 + met_py_unclEnDown**2)) self.out.fillBranch("%s_phi_unclustEnDown" % self.metBranchName, math.atan2(met_py_unclEnDown, met_px_unclEnDown)) return True
class jetRecalib(Module): def __init__(self, globalTag, jetType="AK4PFchs"): if "AK4" in jetType: self.jetBranchName = "Jet" elif "AK8" in jetType: self.jetBranchName = "FatJet" self.subJetBranchName = "SubJet" else: raise ValueError("ERROR: Invalid jet type = '%s'!" % jetType) self.rhoBranchName = "fixedGridRhoFastjetAll" self.lenVar = "n" + self.jetBranchName # To do : change to real values self.jmsVals = [1.00, 0.99, 1.01] self.jesInputFilePath = os.environ[ 'CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/" self.jetReCalibrator = JetReCalibrator( globalTag, jetType, True, self.jesInputFilePath, calculateSeparateCorrections=False, calculateType1METCorrection=False) # load libraries for accessing JES scale factors and uncertainties from txt files for library in [ "libCondFormatsJetMETObjects", "libPhysicsToolsNanoAODTools" ]: if library not in ROOT.gSystem.GetLibraries(): print("Load Library '%s'" % library.replace("lib", "")) ROOT.gSystem.Load(library) def beginJob(self): pass def endJob(self): pass def beginFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): self.out = wrappedOutputTree self.out.branch("%s_pt_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("MET_pt_nom", "F") self.out.branch("MET_phi_nom", "F") def endFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): pass def analyze(self, event): """process event, return True (go to next module) or False (fail, go to next event)""" jets = Collection(event, self.jetBranchName) met = Object(event, "MET") jets_pt_nom = [] (met_px, met_py) = (met.pt * math.cos(met.phi), met.pt * math.sin(met.phi)) (met_px_nom, met_py_nom) = (met_px, met_py) met_px_nom = met_px met_py_nom = met_py rho = getattr(event, self.rhoBranchName) for jet in jets: jet_pt = jet.pt jet_pt = self.jetReCalibrator.correct(jet, rho) jet_pt_nom = jet_pt # don't smear resolution in data if jet_pt_nom < 0.0: jet_pt_nom *= -1.0 jets_pt_nom.append(jet_pt_nom) if jet_pt_nom > 15.: jet_cosPhi = math.cos(jet.phi) jet_sinPhi = math.sin(jet.phi) met_px_nom = met_px_nom - (jet_pt_nom - jet.pt) * jet_cosPhi met_py_nom = met_py_nom - (jet_pt_nom - jet.pt) * jet_sinPhi self.out.fillBranch("%s_pt_nom" % self.jetBranchName, jets_pt_nom) self.out.fillBranch("MET_pt_nom", math.sqrt(met_px_nom**2 + met_py_nom**2)) self.out.fillBranch("MET_phi_nom", math.atan2(met_py_nom, met_px_nom)) return True
class jetRecalib(Module): # Module based on https://github.com/cms-nanoAOD/nanoAOD-tools/blob/master/python/postprocessing/modules/jme/jetRecalib.py def __init__(self, globalTag, jetCollections=["CleanJet"], metCollections=["MET"], jetType="AK4PFchs"): if "AK4" in jetType: self.jetBranchName = "Jet" elif "AK8" in jetType: self.jetBranchName = "FatJet" self.subJetBranchName = "SubJet" else: raise ValueError("ERROR: Invalid jet type = '%s'!" % jetType) self.otherJetBranches = jetCollections # Any jet collections based on full Jet-collection self.metCollections = metCollections self.rhoBranchName = "fixedGridRhoFastjetAll" # To do : change to real values self.jmsVals = [1.00, 0.99, 1.01] self.jesInputFilePath = os.environ[ 'CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/" self.jetReCalibrator = JetReCalibrator( globalTag, jetType, True, self.jesInputFilePath, calculateSeparateCorrections=False, calculateType1METCorrection=False) # load libraries for accessing JES scale factors and uncertainties from txt files for library in [ "libCondFormatsJetMETObjects", "libPhysicsToolsNanoAODTools" ]: if library not in ROOT.gSystem.GetLibraries(): print("Load Library '%s'" % library.replace("lib", "")) ROOT.gSystem.Load(library) def beginJob(self): pass def endJob(self): pass def beginFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): self.out = wrappedOutputTree self.out.branch("%s_pt" % self.jetBranchName, "F", lenVar="n" + self.jetBranchName) self.out.branch("%s_rawFactor" % self.jetBranchName, "F", lenVar="n" + self.jetBranchName) for jname in self.otherJetBranches: self.out.branch("%s_pt" % jname, "F", lenVar="n" + jname) for met in self.metCollections: self.out.branch("%s_pt" % met, "F") self.out.branch("%s_phi" % met, "F") def endFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): pass def analyze(self, event): """process event, return True (go to next module) or False (fail, go to next event)""" jets = Collection(event, self.jetBranchName) mets = [] met_px = [] met_py = [] for mid, met in enumerate(self.metCollections): mets.append(Object(event, met)) met_px.append(mets[mid].pt * math.cos(mets[mid].phi)) met_py.append(mets[mid].pt * math.sin(mets[mid].phi)) jets_pt_newlist = [] rawFactor_newlist = [] otherjets = [] otherjets_pt_newlist = [] for jname in self.otherJetBranches: otherjets.append(Collection(event, jname)) otherjets_pt_newlist.append([]) rho = getattr(event, self.rhoBranchName) for jid, jet in enumerate(jets): # Apply new correction (this includes undoing previous correction first) newjet_pt = self.jetReCalibrator.correct(jet, rho)[0] # Rewrite new correction factor rawFactor_newlist.append(1. - ((jet.pt * (1. - jet.rawFactor)) / newjet_pt)) if newjet_pt < 0.0: newjet_pt *= -1.0 jets_pt_newlist.append(newjet_pt) if newjet_pt > 15.: jet_cosPhi = math.cos(jet.phi) jet_sinPhi = math.sin(jet.phi) for mid, met in enumerate(self.metCollections): met_px[mid] = met_px[mid] - (newjet_pt - jet.pt) * jet_cosPhi met_py[mid] = met_py[mid] - (newjet_pt - jet.pt) * jet_sinPhi for oj, jname in enumerate(self.otherJetBranches): for ojet in otherjets[oj]: if jid == ojet.jetIdx: otherjets_pt_newlist[oj].append(newjet_pt) self.out.fillBranch("%s_pt" % self.jetBranchName, jets_pt_newlist) self.out.fillBranch("%s_rawFactor" % self.jetBranchName, rawFactor_newlist) for oj, jname in enumerate(self.otherJetBranches): self.out.fillBranch("%s_pt" % jname, otherjets_pt_newlist[oj]) for mid, met in enumerate(self.metCollections): self.out.fillBranch("%s_pt" % met, math.sqrt(met_px[mid]**2 + met_py[mid]**2)) self.out.fillBranch("%s_phi" % met, math.atan2(met_py[mid], met_px[mid])) return True
class jetRecalib(Module): def __init__(self, globalTag, archive, jetType="AK4PFchs", redoJEC=False): self.redoJEC = redoJEC if "AK4" in jetType: self.jetBranchName = "Jet" elif "AK8" in jetType: self.jetBranchName = "FatJet" self.subJetBranchName = "SubJet" else: raise ValueError("ERROR: Invalid jet type = '%s'!" % jetType) self.rhoBranchName = "fixedGridRhoFastjetAll" self.lenVar = "n" + self.jetBranchName self.jesInputArchivePath = os.environ[ 'CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/" # Text files are now tarred so must extract first into temporary directory (gets deleted during python memory management at script exit) self.jesArchive = tarfile.open( self.jesInputArchivePath + archive + ".tar.gz", "r:gz") self.jesInputFilePath = tempfile.mkdtemp() self.jesArchive.extractall(self.jesInputFilePath) print("Loading jet energy scale (JES) from file '%s'" % os.path.join(self.jesInputArchivePath + archive + ".tar.gz")) self.jetReCalibrator = JetReCalibrator( globalTag, jetType, True, self.jesInputFilePath, calculateSeparateCorrections=False, calculateType1METCorrection=False) # load libraries for accessing JES scale factors and uncertainties from txt files for library in [ "libCondFormatsJetMETObjects", "libPhysicsToolsNanoAODTools" ]: if library not in ROOT.gSystem.GetLibraries(): print("Load Library '%s'" % library.replace("lib", "")) ROOT.gSystem.Load(library) def beginJob(self): pass def endJob(self): pass def beginFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): self.out = wrappedOutputTree self.out.branch("%s_pt_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_pt_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("MET_pt_nom", "F") self.out.branch("MET_phi_nom", "F") self.out.branch("%s_corr_JEC" % self.jetBranchName, "F", lenVar=self.lenVar) def endFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): pass def analyze(self, event): """process event, return True (go to next module) or False (fail, go to next event)""" jets = Collection(event, self.jetBranchName) met = Object(event, "MET") jets_pt_raw = [] jets_pt_nom = [] jets_mass_raw = [] jets_mass_nom = [] jets_corr_JEC = [] (met_px, met_py) = (met.pt * math.cos(met.phi), met.pt * math.sin(met.phi)) (met_px_nom, met_py_nom) = (met_px, met_py) met_px_nom = met_px met_py_nom = met_py rho = getattr(event, self.rhoBranchName) for jet in jets: #jet pt and mass corrections jet_pt = jet.pt jet_mass = jet.mass #redo JECs if desired if hasattr(jet, "rawFactor"): jet_rawpt = jet_pt * (1 - jet.rawFactor) jet_rawmass = jet_mass * (1 - jet.rawFactor) else: jet_rawpt = -1.0 * jet_pt #If factor not present factor will be saved as -1 jet_rawmass = -1.0 * jet_mass #If factor not present factor will be saved as -1 if self.redoJEC: (jet_pt, jet_mass) = self.jetReCalibrator.correct(jet, rho) jets_pt_raw.append(jet_rawpt) jets_mass_raw.append(jet_rawmass) jets_corr_JEC.append(jet_pt / jet_rawpt) jet_pt_nom = jet_pt # don't smear resolution in data if jet_pt_nom < 0.0: jet_pt_nom *= -1.0 jets_pt_nom.append(jet_pt_nom) jet_mass_nom = jet_mass if jet_mass_nom < 0.0: jet_mass_nom *= -1.0 jets_mass_nom.append(jet_mass_nom) if jet_pt_nom > 15.: jet_cosPhi = math.cos(jet.phi) jet_sinPhi = math.sin(jet.phi) met_px_nom = met_px_nom - (jet_pt_nom - jet.pt) * jet_cosPhi met_py_nom = met_py_nom - (jet_pt_nom - jet.pt) * jet_sinPhi self.out.fillBranch("%s_pt_raw" % self.jetBranchName, jets_pt_raw) self.out.fillBranch("%s_pt_nom" % self.jetBranchName, jets_pt_nom) self.out.fillBranch("%s_mass_raw" % self.jetBranchName, jets_mass_raw) self.out.fillBranch("%s_mass_nom" % self.jetBranchName, jets_mass_nom) self.out.fillBranch("MET_pt_nom", math.sqrt(met_px_nom**2 + met_py_nom**2)) self.out.fillBranch("MET_phi_nom", math.atan2(met_py_nom, met_px_nom)) self.out.fillBranch("%s_corr_JEC" % self.jetBranchName, jets_corr_JEC) return True
class jetRecalib(Module): # Module based on https://github.com/cms-nanoAOD/nanoAOD-tools/blob/master/python/postprocessing/modules/jme/jetRecalib.py def __init__(self, globalTag, jetCollections = ["CleanJet"], metCollections = ["MET"], jetType = "AK4PFchs"): if "AK4" in jetType : self.jetBranchName = "Jet" elif "AK8" in jetType : self.jetBranchName = "FatJet" self.subJetBranchName = "SubJet" else: raise ValueError("ERROR: Invalid jet type = '%s'!" % jetType) self.otherJetBranches = jetCollections # Any jet collections based on full Jet-collection self.metCollections = metCollections self.rhoBranchName = "fixedGridRhoFastjetAll" # To do : change to real values self.jmsVals = [1.00, 0.99, 1.01] self.jesInputFilePath = os.environ['CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/" self.jetReCalibrator = JetReCalibrator(globalTag, jetType , True, self.jesInputFilePath, calculateSeparateCorrections = False, calculateType1METCorrection = False) # load libraries for accessing JES scale factors and uncertainties from txt files for library in [ "libCondFormatsJetMETObjects", "libPhysicsToolsNanoAODTools" ]: if library not in ROOT.gSystem.GetLibraries(): print("Load Library '%s'" % library.replace("lib", "")) ROOT.gSystem.Load(library) def beginJob(self): pass def endJob(self): pass def beginFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): self.out = wrappedOutputTree self.out.branch("%s_pt" % self.jetBranchName, "F", lenVar="n"+self.jetBranchName) self.out.branch("%s_rawFactor" % self.jetBranchName, "F", lenVar="n"+self.jetBranchName) for jname in self.otherJetBranches: self.out.branch("%s_pt" % jname, "F", lenVar="n"+jname) for met in self.metCollections: self.out.branch("%s_pt" % met, "F") self.out.branch("%s_phi" % met, "F") def endFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): pass def analyze(self, event): """process event, return True (go to next module) or False (fail, go to next event)""" jets = Collection(event, self.jetBranchName ) mets = [] met_px = [] met_py = [] for mid,met in enumerate(self.metCollections): mets.append(Object(event, met)) met_px.append(mets[mid].pt*math.cos(mets[mid].phi)) met_py.append(mets[mid].pt*math.sin(mets[mid].phi)) jets_pt_newlist = [] rawFactor_newlist = [] otherjets = [] otherjets_pt_newlist = [] for jname in self.otherJetBranches: otherjets.append(Collection(event, jname )) otherjets_pt_newlist.append([]) rho = getattr(event, self.rhoBranchName) for jid,jet in enumerate(jets): # Apply new correction (this includes undoing previous correction first) newjet_pt = self.jetReCalibrator.correct(jet,rho) # Rewrite new correction factor rawFactor_newlist.append(1. - ((jet.pt * (1. - jet.rawFactor))/newjet_pt)) if newjet_pt < 0.0: newjet_pt *= -1.0 jets_pt_newlist.append(newjet_pt) if newjet_pt > 15.: jet_cosPhi = math.cos(jet.phi) jet_sinPhi = math.sin(jet.phi) for mid,met in enumerate(self.metCollections): met_px[mid] = met_px[mid] - (newjet_pt - jet.pt)*jet_cosPhi met_py[mid] = met_py[mid] - (newjet_pt - jet.pt)*jet_sinPhi for oj,jname in enumerate(self.otherJetBranches): for ojet in otherjets[oj]: if jid == ojet.jetIdx: otherjets_pt_newlist[oj].append(newjet_pt) self.out.fillBranch("%s_pt" % self.jetBranchName, jets_pt_newlist) self.out.fillBranch("%s_rawFactor" % self.jetBranchName, rawFactor_newlist) for oj,jname in enumerate(self.otherJetBranches): self.out.fillBranch("%s_pt" % jname, otherjets_pt_newlist[oj]) for mid,met in enumerate(self.metCollections): self.out.fillBranch("%s_pt" % met, math.sqrt(met_px[mid]**2 + met_py[mid]**2)) self.out.fillBranch("%s_phi" % met, math.atan2(met_py[mid], met_px[mid])) return True
class jetmetUncertaintiesProducer(Module): def __init__(self, era, globalTag, jesUncertainties=["Total"], jetType="AK4PFchs", redoJEC=False, noGroom=False, reducedJECsystematics=False): self.era = era self.redoJEC = redoJEC self.noGroom = noGroom #-------------------------------------------------------------------------------------------- # CV: globalTag and jetType not yet used in the jet smearer, as there is no consistent set of # txt files for JES uncertainties and JER scale factors and uncertainties yet #-------------------------------------------------------------------------------------------- self.jesUncertainties = jesUncertainties # smear jet pT to account for measured difference in JER between data and simulation. if era == "2016": self.jerInputFileName = "Summer16_25nsV1b_MC/Summer16_25nsV1b_MC_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = "Summer16_25nsV1b_MC/Summer16_25nsV1b_MC_SF_" + jetType + ".txt" elif era == "2017": # use Fall17 as temporary placeholder until post-Moriond 2019 JERs are out self.jerInputFileName = "Fall17_V3b_MC/Fall17_V3b_MC_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = "Fall17_V3b_MC/Fall17_V3b_MC_SF_" + jetType + ".txt" elif era == "2018": ## (see https://hypernews.cern.ch/HyperNews/CMS/get/JetMET/1994.html) self.jerInputFileName = "Autumn18_V7b_MC/Autumn18_V7b_MC_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = "Autumn18_V7b_MC/Autumn18_V7b_MC_SF_" + jetType + ".txt" #jet mass resolution: https://twiki.cern.ch/twiki/bin/view/CMS/JetWtagging if self.era == "2016" or self.era == "2018": #update when 2018 values available self.jmrVals = [1.0, 1.2, 0.8] #nominal, up, down else: self.jmrVals = [1.09, 1.14, 1.04] self.jetSmearer = jetSmearer(globalTag, jetType, self.jerInputFileName, self.jerUncertaintyInputFileName, self.jmrVals) if "AK4" in jetType: self.jetBranchName = "Jet" self.genJetBranchName = "GenJet" self.genSubJetBranchName = None self.doGroomed = False self.corrMET = True elif "AK8" in jetType: self.jetBranchName = "FatJet" self.subJetBranchName = "SubJet" self.genJetBranchName = "GenJetAK8" self.genSubJetBranchName = "SubGenJetAK8" if not self.noGroom: self.doGroomed = True else: self.doGroomed = False self.corrMET = False else: raise ValueError("ERROR: Invalid jet type = '%s'!" % jetType) self.metBranchName = "MET" self.rhoBranchName = "fixedGridRhoFastjetAll" self.lenVar = "n" + self.jetBranchName #jet mass scale #W-tagging PUPPI softdrop JMS values: https://twiki.cern.ch/twiki/bin/view/CMS/JetWtagging #2016 values - use for 2018 until new values available self.jmsVals = [1.00, 0.9906, 1.0094] #nominal, down, up if self.era == "2017": self.jmsVals = [0.982, 0.978, 0.986] # read jet energy scale (JES) uncertainties # (downloaded from https://twiki.cern.ch/twiki/bin/view/CMS/JECDataMC ) self.jesInputArchivePath = os.environ[ 'CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/" # Text files are now tarred so must extract first into temporary directory (gets deleted during python memory management at script exit) self.jesArchive = tarfile.open( self.jesInputArchivePath + globalTag + ".tgz", "r:gz") self.jesInputFilePath = tempfile.mkdtemp() self.jesArchive.extractall(self.jesInputFilePath) if len(jesUncertainties) == 1 and jesUncertainties[0] == "Total": if self.era == "2016": self.jesUncertaintyInputFileName = "Summer16_07Aug2017_V11_MC_Uncertainty_" + jetType + ".txt" elif self.era == "2017": self.jesUncertaintyInputFileName = "Fall17_17Nov2017_V32_MC_Uncertainty_" + jetType + ".txt" elif self.era == "2018": self.jesUncertaintyInputFileName = "Autumn18_V19_MC_Uncertainty_" + jetType + ".txt" else: raise ValueError("ERROR: Invalid era = '%s'!" % self.era) else: if self.era == "2016": self.jesUncertaintyInputFileName = "Summer16_07Aug2017_V11_MC_UncertaintySources_" + jetType + ".txt" elif self.era == "2017": self.jesUncertaintyInputFileName = "Fall17_17Nov2017_V32_MC_UncertaintySources_" + jetType + ".txt" elif self.era == "2018": self.jesUncertaintyInputFileName = "Autumn18_V19_MC_UncertaintySources_" + jetType + ".txt" else: raise ValueError("ERROR: Invalid era = '%s'!" % self.era) if reducedJECsystematics: ## Use https://twiki.cern.ch/twiki/bin/viewauth/CMS/JECUncertaintySources#Run_2_reduced_set_of_uncertainty ## self.jesUncertaintyInputFileName = "Regrouped_" + self.jesUncertaintyInputFileName # read all uncertainty source names from the loaded file if jesUncertainties[0] == "All": with open(self.jesInputFilePath + '/' + self.jesUncertaintyInputFileName) as f: lines = f.read().split("\n") sources = filter( lambda x: x.startswith("[") and x.endswith("]"), lines) sources = map(lambda x: x[1:-1], sources) self.jesUncertainties = sources if self.redoJEC: self.jetReCalibrator = JetReCalibrator( globalTag, jetType, True, self.jesInputFilePath, calculateSeparateCorrections=False, calculateType1METCorrection=False) # define energy threshold below which jets are considered as "unclustered energy" # (cf. JetMETCorrections/Type1MET/python/correctionTermsPfMetType1Type2_cff.py ) self.unclEnThreshold = 15. # load libraries for accessing JES scale factors and uncertainties from txt files for library in [ "libCondFormatsJetMETObjects", "libPhysicsToolsNanoAODTools" ]: if library not in ROOT.gSystem.GetLibraries(): print("Load Library '%s'" % library.replace("lib", "")) ROOT.gSystem.Load(library) def beginJob(self): print("Loading jet energy scale (JES) uncertainties from file '%s'" % os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName)) #self.jesUncertainty = ROOT.JetCorrectionUncertainty(os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName)) self.jesUncertainty = {} # implementation didn't seem to work for factorized JEC, try again another way for jesUncertainty in self.jesUncertainties: jesUncertainty_label = jesUncertainty if jesUncertainty == 'Total' and len(self.jesUncertainties) == 1: jesUncertainty_label = '' pars = ROOT.JetCorrectorParameters( os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName), jesUncertainty_label) self.jesUncertainty[ jesUncertainty] = ROOT.JetCorrectionUncertainty(pars) self.jetSmearer.beginJob() def endJob(self): self.jetSmearer.endJob() def beginFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): self.out = wrappedOutputTree self.out.branch("%s_pt_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_pt_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_pt_newJEC" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_mass_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JEC" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JER" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JMS" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_corr_JMR" % self.jetBranchName, "F", lenVar=self.lenVar) if self.doGroomed: self.out.branch("%s_msoftdrop_raw" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_nom" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_corr_JMR" % self.jetBranchName, "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_corr_JMS" % self.jetBranchName, "F", lenVar=self.lenVar) if self.corrMET: self.out.branch("%s_pt_nom" % self.metBranchName, "F") self.out.branch("%s_phi_nom" % self.metBranchName, "F") for shift in ["Up", "Down"]: self.out.branch("%s_pt_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jmr%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jms%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) if self.doGroomed: self.out.branch("%s_msoftdrop_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_jmr%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) self.out.branch("%s_msoftdrop_jms%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar) if self.corrMET: self.out.branch("%s_pt_jer%s" % (self.metBranchName, shift), "F") self.out.branch("%s_phi_jer%s" % (self.metBranchName, shift), "F") for jesUncertainty in self.jesUncertainties: self.out.branch("%s_pt_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) self.out.branch("%s_mass_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) if self.doGroomed: self.out.branch( "%s_msoftdrop_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar) if self.corrMET: self.out.branch( "%s_pt_jes%s%s" % (self.metBranchName, jesUncertainty, shift), "F") self.out.branch( "%s_phi_jes%s%s" % (self.metBranchName, jesUncertainty, shift), "F") if self.corrMET: self.out.branch( "%s_pt_unclustEn%s" % (self.metBranchName, shift), "F") self.out.branch( "%s_phi_unclustEn%s" % (self.metBranchName, shift), "F") def endFile(self, inputFile, outputFile, inputTree, wrappedOutputTree): pass def analyze(self, event): """process event, return True (go to next module) or False (fail, go to next event)""" jets = Collection(event, self.jetBranchName) genJets = Collection(event, self.genJetBranchName) if self.doGroomed: subJets = Collection(event, self.subJetBranchName) genSubJets = Collection(event, self.genSubJetBranchName) genSubJetMatcher = matchObjectCollectionMultiple(genJets, genSubJets, dRmax=0.8) jets_pt_newJEC = [] jets_pt_raw = [] jets_pt_nom = [] jets_mass_raw = [] jets_mass_nom = [] jets_corr_JEC = [] jets_corr_JER = [] jets_corr_JMS = [] jets_corr_JMR = [] jets_pt_jerUp = [] jets_pt_jerDown = [] jets_pt_jesUp = {} jets_pt_jesDown = {} jets_mass_jerUp = [] jets_mass_jerDown = [] jets_mass_jmrUp = [] jets_mass_jmrDown = [] jets_mass_jesUp = {} jets_mass_jesDown = {} jets_mass_jmsUp = [] jets_mass_jmsDown = [] for jesUncertainty in self.jesUncertainties: jets_pt_jesUp[jesUncertainty] = [] jets_pt_jesDown[jesUncertainty] = [] jets_mass_jesUp[jesUncertainty] = [] jets_mass_jesDown[jesUncertainty] = [] if self.corrMET: met = Object(event, self.metBranchName) (met_px, met_py) = (met.pt * math.cos(met.phi), met.pt * math.sin(met.phi)) (met_px_nom, met_py_nom) = (met_px, met_py) (met_px_jerUp, met_py_jerUp) = (met_px, met_py) (met_px_jerDown, met_py_jerDown) = (met_px, met_py) (met_px_jesUp, met_py_jesUp) = ({}, {}) (met_px_jesDown, met_py_jesDown) = ({}, {}) for jesUncertainty in self.jesUncertainties: met_px_jesUp[jesUncertainty] = met_px met_py_jesUp[jesUncertainty] = met_py met_px_jesDown[jesUncertainty] = met_px met_py_jesDown[jesUncertainty] = met_py if self.doGroomed: jets_msdcorr_raw = [] jets_msdcorr_nom = [] jets_msdcorr_corr_JMR = [] jets_msdcorr_corr_JMS = [] jets_msdcorr_jerUp = [] jets_msdcorr_jerDown = [] jets_msdcorr_jmrUp = [] jets_msdcorr_jmrDown = [] jets_msdcorr_jesUp = {} jets_msdcorr_jesDown = {} jets_msdcorr_jmsUp = [] jets_msdcorr_jmsDown = [] for jesUncertainty in self.jesUncertainties: jets_msdcorr_jesUp[jesUncertainty] = [] jets_msdcorr_jesDown[jesUncertainty] = [] rho = getattr(event, self.rhoBranchName) # match reconstructed jets to generator level ones # (needed to evaluate JER scale factors and uncertainties) pairs = matchObjectCollection(jets, genJets) for jet in jets: genJet = pairs[jet] if self.doGroomed: genGroomedSubJets = genSubJetMatcher[ genJet] if genJet != None else None genGroomedJet = genGroomedSubJets[0].p4() + genGroomedSubJets[ 1].p4() if genGroomedSubJets != None and len( genGroomedSubJets) >= 2 else None if jet.subJetIdx1 >= 0 and jet.subJetIdx2 >= 0: groomedP4 = subJets[jet.subJetIdx1].p4() + subJets[ jet.subJetIdx2].p4() #check subjet jecs else: groomedP4 = None #jet pt and mass corrections jet_pt = jet.pt jet_mass = jet.mass #redo JECs if desired if hasattr(jet, "rawFactor"): jet_rawpt = jet_pt * (1 - jet.rawFactor) jet_rawmass = jet_mass * (1 - jet.rawFactor) else: jet_rawpt = -1.0 * jet_pt #If factor not present factor will be saved as -1 jet_rawmass = -1.0 * jet_mass #If factor not present factor will be saved as -1 if self.redoJEC: (jet_pt, jet_mass) = self.jetReCalibrator.correct(jet, rho) jet.pt = jet_pt jet.mass = jet_mass jets_pt_raw.append(jet_rawpt) jets_mass_raw.append(jet_rawmass) jets_corr_JEC.append(jet_pt / jet_rawpt) jets_pt_newJEC.append(jet_pt) # evaluate JER scale factors and uncertainties # (cf. https://twiki.cern.ch/twiki/bin/view/CMS/JetResolution and https://twiki.cern.ch/twiki/bin/view/CMSPublic/WorkBookJetEnergyResolution ) (jet_pt_jerNomVal, jet_pt_jerUpVal, jet_pt_jerDownVal) = self.jetSmearer.getSmearValsPt( jet, genJet, rho) jets_corr_JER.append(jet_pt_jerNomVal) #if jet_pt_jerNomVal > 2 : print "jet_pt_jerNomVal ", jet_pt_jerNomVal jet_pt_nom = jet_pt_jerNomVal * jet_pt if jet_pt_nom < 0.0: jet_pt_nom *= -1.0 jet_pt_jerUp = jet_pt_jerUpVal * jet_pt jet_pt_jerDown = jet_pt_jerDownVal * jet_pt jets_pt_nom.append(jet_pt_nom) jets_pt_jerUp.append(jet_pt_jerUpVal * jet_pt) jets_pt_jerDown.append(jet_pt_jerDownVal * jet_pt) # evaluate JES uncertainties jet_pt_jesUp = {} jet_pt_jesDown = {} jet_mass_jesUp = {} jet_mass_jesDown = {} jet_mass_jmsUp = [] jet_mass_jmsDown = [] # Evaluate JMS and JMR scale factors and uncertainties jmsNomVal = self.jmsVals[0] jmsDownVal = self.jmsVals[1] jmsUpVal = self.jmsVals[2] (jet_mass_jmrNomVal, jet_mass_jmrUpVal, jet_mass_jmrDownVal) = self.jetSmearer.getSmearValsM(jet, genJet) jets_corr_JMS.append(jmsNomVal) jets_corr_JMR.append(jet_mass_jmrNomVal) jet_mass_nom = jet_pt_jerNomVal * jet_mass_jmrNomVal * jmsNomVal * jet_mass if jet_mass_nom < 0.0: jet_mass_nom *= -1.0 jets_mass_nom.append(jet_mass_nom) jets_mass_jerUp.append(jet_pt_jerUpVal * jet_mass_jmrNomVal * jmsNomVal * jet_mass) jets_mass_jerDown.append(jet_pt_jerDownVal * jet_mass_jmrNomVal * jmsNomVal * jet_mass) jets_mass_jmrUp.append(jet_pt_jerNomVal * jet_mass_jmrUpVal * jmsNomVal * jet_mass) jets_mass_jmrDown.append(jet_pt_jerNomVal * jet_mass_jmrDownVal * jmsNomVal * jet_mass) jets_mass_jmsUp.append(jet_pt_jerNomVal * jet_mass_jmrNomVal * jmsUpVal * jet_mass) jets_mass_jmsDown.append(jet_pt_jerNomVal * jet_mass_jmrNomVal * jmsDownVal * jet_mass) if self.doGroomed: # evaluate JES uncertainties jet_msdcorr_jesUp = {} jet_msdcorr_jesDown = {} # Evaluate JMS and JMR scale factors and uncertainties (jet_msdcorr_jmrNomVal, jet_msdcorr_jmrUpVal, jet_msdcorr_jmrDownVal) = self.jetSmearer.getSmearValsM( groomedP4, genGroomedJet ) if groomedP4 != None and genGroomedJet != None else (0., 0., 0.) jet_msdcorr_raw = groomedP4.M() if groomedP4 != None else 0.0 jets_msdcorr_corr_JMS.append(jmsNomVal) jets_msdcorr_corr_JMR.append(jet_msdcorr_jmrNomVal) if jet_msdcorr_raw < 0.0: jet_msdcorr_raw *= -1.0 jet_msdcorr_nom = jet_pt_jerNomVal * jet_msdcorr_jmrNomVal * jmsNomVal * jet_msdcorr_raw jets_msdcorr_raw.append( jet_msdcorr_raw) #fix later so jec's not applied jets_msdcorr_nom.append(jet_msdcorr_nom) jets_msdcorr_jerUp.append(jet_pt_jerUpVal * jet_msdcorr_jmrNomVal * jmsNomVal * jet_msdcorr_raw) jets_msdcorr_jerDown.append(jet_pt_jerDownVal * jet_msdcorr_jmrNomVal * jmsNomVal * jet_msdcorr_raw) jets_msdcorr_jmrUp.append(jet_pt_jerNomVal * jet_msdcorr_jmrUpVal * jmsNomVal * jet_msdcorr_raw) jets_msdcorr_jmrDown.append( jet_pt_jerNomVal * jet_msdcorr_jmrDownVal * jmsNomVal * jet_msdcorr_raw) jets_msdcorr_jmsUp.append(jet_pt_jerNomVal * jet_msdcorr_jmrNomVal * jmsUpVal * jet_msdcorr_raw) jets_msdcorr_jmsDown.append( jet_pt_jerNomVal * jet_msdcorr_jmrNomVal * jmsDownVal * jet_msdcorr_raw) for jesUncertainty in self.jesUncertainties: # (cf. https://twiki.cern.ch/twiki/bin/view/CMSPublic/WorkBookJetEnergyCorrections#JetCorUncertainties ) self.jesUncertainty[jesUncertainty].setJetPt(jet_pt_nom) self.jesUncertainty[jesUncertainty].setJetEta(jet.eta) delta = self.jesUncertainty[jesUncertainty].getUncertainty( True) jet_pt_jesUp[jesUncertainty] = jet_pt_nom * (1. + delta) jet_pt_jesDown[jesUncertainty] = jet_pt_nom * (1. - delta) jets_pt_jesUp[jesUncertainty].append( jet_pt_jesUp[jesUncertainty]) jets_pt_jesDown[jesUncertainty].append( jet_pt_jesDown[jesUncertainty]) jet_mass_jesUp[jesUncertainty] = jet_mass_nom * (1. + delta) jet_mass_jesDown[jesUncertainty] = jet_mass_nom * (1. - delta) jets_mass_jesUp[jesUncertainty].append( jet_mass_jesUp[jesUncertainty]) jets_mass_jesDown[jesUncertainty].append( jet_mass_jesDown[jesUncertainty]) if self.doGroomed: jet_msdcorr_jesUp[jesUncertainty] = jet_msdcorr_nom * ( 1. + delta) jet_msdcorr_jesDown[jesUncertainty] = jet_msdcorr_nom * ( 1. - delta) jets_msdcorr_jesUp[jesUncertainty].append( jet_msdcorr_jesUp[jesUncertainty]) jets_msdcorr_jesDown[jesUncertainty].append( jet_msdcorr_jesDown[jesUncertainty]) # propagate JER and JES corrections and uncertainties to MET if self.corrMET and jet_pt_nom > self.unclEnThreshold: jet_cosPhi = math.cos(jet.phi) jet_sinPhi = math.sin(jet.phi) met_px_nom = met_px_nom - (jet_pt_nom - jet_pt) * jet_cosPhi met_py_nom = met_py_nom - (jet_pt_nom - jet_pt) * jet_sinPhi met_px_jerUp = met_px_jerUp - (jet_pt_jerUp - jet_pt_nom) * jet_cosPhi met_py_jerUp = met_py_jerUp - (jet_pt_jerUp - jet_pt_nom) * jet_sinPhi met_px_jerDown = met_px_jerDown - (jet_pt_jerDown - jet_pt_nom) * jet_cosPhi met_py_jerDown = met_py_jerDown - (jet_pt_jerDown - jet_pt_nom) * jet_sinPhi for jesUncertainty in self.jesUncertainties: met_px_jesUp[jesUncertainty] = met_px_jesUp[ jesUncertainty] - (jet_pt_jesUp[jesUncertainty] - jet_pt_nom) * jet_cosPhi met_py_jesUp[jesUncertainty] = met_py_jesUp[ jesUncertainty] - (jet_pt_jesUp[jesUncertainty] - jet_pt_nom) * jet_sinPhi met_px_jesDown[jesUncertainty] = met_px_jesDown[ jesUncertainty] - (jet_pt_jesDown[jesUncertainty] - jet_pt_nom) * jet_cosPhi met_py_jesDown[jesUncertainty] = met_py_jesDown[ jesUncertainty] - (jet_pt_jesDown[jesUncertainty] - jet_pt_nom) * jet_sinPhi # propagate "unclustered energy" uncertainty to MET if self.corrMET: (met_px_unclEnUp, met_py_unclEnUp) = (met_px, met_py) (met_px_unclEnDown, met_py_unclEnDown) = (met_px, met_py) met_deltaPx_unclEn = getattr( event, self.metBranchName + "_MetUnclustEnUpDeltaX") met_deltaPy_unclEn = getattr( event, self.metBranchName + "_MetUnclustEnUpDeltaY") met_px_unclEnUp = met_px_unclEnUp + met_deltaPx_unclEn met_py_unclEnUp = met_py_unclEnUp + met_deltaPy_unclEn met_px_unclEnDown = met_px_unclEnDown - met_deltaPx_unclEn met_py_unclEnDown = met_py_unclEnDown - met_deltaPy_unclEn # propagate effect of jet energy smearing to MET met_px_jerUp = met_px_jerUp + (met_px_nom - met_px) met_py_jerUp = met_py_jerUp + (met_py_nom - met_py) met_px_jerDown = met_px_jerDown + (met_px_nom - met_px) met_py_jerDown = met_py_jerDown + (met_py_nom - met_py) for jesUncertainty in self.jesUncertainties: met_px_jesUp[jesUncertainty] = met_px_jesUp[jesUncertainty] + ( met_px_nom - met_px) met_py_jesUp[jesUncertainty] = met_py_jesUp[jesUncertainty] + ( met_py_nom - met_py) met_px_jesDown[jesUncertainty] = met_px_jesDown[ jesUncertainty] + (met_px_nom - met_px) met_py_jesDown[jesUncertainty] = met_py_jesDown[ jesUncertainty] + (met_py_nom - met_py) met_px_unclEnUp = met_px_unclEnUp + (met_px_nom - met_px) met_py_unclEnUp = met_py_unclEnUp + (met_py_nom - met_py) met_px_unclEnDown = met_px_unclEnDown + (met_px_nom - met_px) met_py_unclEnDown = met_py_unclEnDown + (met_py_nom - met_py) self.out.fillBranch("%s_pt_raw" % self.jetBranchName, jets_pt_raw) self.out.fillBranch("%s_pt_nom" % self.jetBranchName, jets_pt_nom) self.out.fillBranch("%s_pt_newJEC" % self.jetBranchName, jets_pt_newJEC) self.out.fillBranch("%s_corr_JEC" % self.jetBranchName, jets_corr_JEC) self.out.fillBranch("%s_corr_JER" % self.jetBranchName, jets_corr_JER) self.out.fillBranch("%s_pt_jerUp" % self.jetBranchName, jets_pt_jerUp) self.out.fillBranch("%s_pt_jerDown" % self.jetBranchName, jets_pt_jerDown) self.out.fillBranch("%s_mass_raw" % self.jetBranchName, jets_mass_raw) self.out.fillBranch("%s_mass_nom" % self.jetBranchName, jets_mass_nom) self.out.fillBranch("%s_corr_JMS" % self.jetBranchName, jets_corr_JMS) self.out.fillBranch("%s_corr_JMR" % self.jetBranchName, jets_corr_JMR) self.out.fillBranch("%s_mass_jerUp" % self.jetBranchName, jets_mass_jerUp) self.out.fillBranch("%s_mass_jerDown" % self.jetBranchName, jets_mass_jerDown) self.out.fillBranch("%s_mass_jmrUp" % self.jetBranchName, jets_mass_jmrUp) self.out.fillBranch("%s_mass_jmrDown" % self.jetBranchName, jets_mass_jmrDown) self.out.fillBranch("%s_mass_jmsUp" % self.jetBranchName, jets_mass_jmsUp) self.out.fillBranch("%s_mass_jmsDown" % self.jetBranchName, jets_mass_jmsDown) if self.doGroomed: self.out.fillBranch("%s_msoftdrop_raw" % self.jetBranchName, jets_msdcorr_raw) self.out.fillBranch("%s_msoftdrop_nom" % self.jetBranchName, jets_msdcorr_nom) self.out.fillBranch("%s_msoftdrop_corr_JMS" % self.jetBranchName, jets_msdcorr_corr_JMS) self.out.fillBranch("%s_msoftdrop_corr_JMR" % self.jetBranchName, jets_msdcorr_corr_JMR) self.out.fillBranch("%s_msoftdrop_jerUp" % self.jetBranchName, jets_msdcorr_jerUp) self.out.fillBranch("%s_msoftdrop_jerDown" % self.jetBranchName, jets_msdcorr_jerDown) self.out.fillBranch("%s_msoftdrop_jmrUp" % self.jetBranchName, jets_msdcorr_jmrUp) self.out.fillBranch("%s_msoftdrop_jmrDown" % self.jetBranchName, jets_msdcorr_jmrDown) self.out.fillBranch("%s_msoftdrop_jmsUp" % self.jetBranchName, jets_msdcorr_jmsUp) self.out.fillBranch("%s_msoftdrop_jmsDown" % self.jetBranchName, jets_msdcorr_jmsDown) if self.corrMET: self.out.fillBranch("%s_pt_nom" % self.metBranchName, math.sqrt(met_px_nom**2 + met_py_nom**2)) self.out.fillBranch("%s_phi_nom" % self.metBranchName, math.atan2(met_py_nom, met_px_nom)) self.out.fillBranch("%s_pt_jerUp" % self.metBranchName, math.sqrt(met_px_jerUp**2 + met_py_jerUp**2)) self.out.fillBranch("%s_phi_jerUp" % self.metBranchName, math.atan2(met_py_jerUp, met_px_jerUp)) self.out.fillBranch( "%s_pt_jerDown" % self.metBranchName, math.sqrt(met_px_jerDown**2 + met_py_jerDown**2)) self.out.fillBranch("%s_phi_jerDown" % self.metBranchName, math.atan2(met_py_jerDown, met_px_jerDown)) for jesUncertainty in self.jesUncertainties: self.out.fillBranch( "%s_pt_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_pt_jesUp[jesUncertainty]) self.out.fillBranch( "%s_pt_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_pt_jesDown[jesUncertainty]) self.out.fillBranch( "%s_mass_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_mass_jesUp[jesUncertainty]) self.out.fillBranch( "%s_mass_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_mass_jesDown[jesUncertainty]) if self.doGroomed: self.out.fillBranch( "%s_msoftdrop_jes%sUp" % (self.jetBranchName, jesUncertainty), jets_msdcorr_jesUp[jesUncertainty]) self.out.fillBranch( "%s_msoftdrop_jes%sDown" % (self.jetBranchName, jesUncertainty), jets_msdcorr_jesDown[jesUncertainty]) if self.corrMET: self.out.fillBranch( "%s_pt_jes%sUp" % (self.metBranchName, jesUncertainty), math.sqrt(met_px_jesUp[jesUncertainty]**2 + met_py_jesUp[jesUncertainty]**2)) self.out.fillBranch( "%s_phi_jes%sUp" % (self.metBranchName, jesUncertainty), math.atan2(met_py_jesUp[jesUncertainty], met_px_jesUp[jesUncertainty])) self.out.fillBranch( "%s_pt_jes%sDown" % (self.metBranchName, jesUncertainty), math.sqrt(met_px_jesDown[jesUncertainty]**2 + met_py_jesDown[jesUncertainty]**2)) self.out.fillBranch( "%s_phi_jes%sDown" % (self.metBranchName, jesUncertainty), math.atan2(met_py_jesDown[jesUncertainty], met_px_jesDown[jesUncertainty])) if self.corrMET: self.out.fillBranch( "%s_pt_unclustEnUp" % self.metBranchName, math.sqrt(met_px_unclEnUp**2 + met_py_unclEnUp**2)) self.out.fillBranch("%s_phi_unclustEnUp" % self.metBranchName, math.atan2(met_py_unclEnUp, met_px_unclEnUp)) self.out.fillBranch( "%s_pt_unclustEnDown" % self.metBranchName, math.sqrt(met_px_unclEnDown**2 + met_py_unclEnDown**2)) self.out.fillBranch( "%s_phi_unclustEnDown" % self.metBranchName, math.atan2(met_py_unclEnDown, met_px_unclEnDown)) return True