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)
class jetmetUncertaintiesProducer(Module): def __init__(self, era, globalTag, jerTag="", jesUncertainties=["Total"], jetType="AK4PFchs", redoJEC=False, noGroom=False, doSmearing=True, doL2L3=True): self.era = era self.redoJEC = redoJEC self.noGroom = noGroom self.doSmearing = doSmearing #-------------------------------------------------------------------------------------------- # 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 = jerTag + "_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = jerTag + "_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/" self.jesUncertaintyInputFileName = self.jesInputFilePath + "/" + globalTag + "_Uncertainty_" + 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, doL2L3, 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" % 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_mass" % 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" % self.metBranchName, "F") self.out.branch("%s_phi" % self.metBranchName, "F") for shift in ["Up", "Down"]: self.out.branch("%s_pt_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar, limitedPrecision=12) self.out.branch("%s_mass_jer%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar, limitedPrecision=12) self.out.branch("%s_mass_jmr%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar, limitedPrecision=12) self.out.branch("%s_mass_jms%s" % (self.jetBranchName, shift), "F", lenVar=self.lenVar, limitedPrecision=12) 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", limitedPrecision=12) self.out.branch("%s_phi_jer%s" % (self.metBranchName, shift), "F", limitedPrecision=12) for jesUncertainty in self.jesUncertainties: self.out.branch("%s_pt_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar, limitedPrecision=12) self.out.branch("%s_mass_jes%s%s" % (self.jetBranchName, jesUncertainty, shift), "F", lenVar=self.lenVar, limitedPrecision=12) 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", limitedPrecision=12) self.out.branch( "%s_phi_jes%s%s" % (self.metBranchName, jesUncertainty, shift), "F", limitedPrecision=12) if self.corrMET: self.out.branch("%s_pt_unclustEn%s" % (self.metBranchName, shift), "F", limitedPrecision=12) self.out.branch("%s_phi_unclustEn%s" % (self.metBranchName, shift), "F", limitedPrecision=12) 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_corr_JEC = [] jets_corr_JER = [] 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 hasattr(jet, "rawFactor"): jet_rawpt = jet.pt * (1 - jet.rawFactor) else: jet_rawpt = -1.0 * jet.pt #If factor not present factor will be saved as -1 if self.redoJEC: jet_pt = self.jetReCalibrator.correct(jet, rho) jets_corr_JEC.append(jet_pt / jet_rawpt) jets_corr_JER.append(jet_pt_jerNomVal) jet_pt_nom = jet_pt if self.doSmearing: jet_pt_nom *= jet_pt_jerNomVal 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 jet_mass_nom = 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" % 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" % 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" % self.metBranchName, math.sqrt(met_px_nom**2 + met_py_nom**2)) self.out.fillBranch("%s_phi" % 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, archive, jetType="AK4PFchs", redoJEC=False): self.redoJEC = redoJEC if "AK4" in jetType: self.jetBranchName = "Jet" self.subJetBranchName = "" 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) if self.subJetBranchName != "": 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 hasattr(jet, "subJetIdx1"): 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()) else: jets_msoftdrop_raw.append(0.0) jets_msoftdrop_nom.append(0.0) 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("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) self.out.fillBranch("%s_msoftdrop_raw" % self.jetBranchName, jets_msoftdrop_raw) self.out.fillBranch("%s_msoftdrop_nom" % self.jetBranchName, jets_msoftdrop_nom) return True
def beginJob(self): # set up JEC if self.jec or self.jes in ['up', 'down']: for library in ["libCondFormatsJetMETObjects", "libPhysicsToolsNanoAODTools"]: if library not in ROOT.gSystem.GetLibraries(): logging.info("Load Library '%s'" % library.replace("lib", "")) ROOT.gSystem.Load(library) self.jesInputFilePath = os.environ['CMSSW_BASE'] + "/src/PhysicsTools/NanoHRTTools/data/2016/jme/" # updating JEC if self.jec: logging.info('Loading JEC parameters...') self.jetReCalibratorMC = None # self.jetReCalibratorMC = JetReCalibrator(globalTag='Summer16_23Sep2016V4_MC', # jetFlavour=self.jetType, # doResidualJECs=False, # jecPath=self.jesInputFilePath + 'Summer16_23Sep2016V4_MC/', # calculateSeparateCorrections=False, # calculateType1METCorrection=False) self.jetReCalibratorDATA_BCD = JetReCalibrator(globalTag='Summer16_23Sep2016BCDV4_DATA', jetFlavour=self.jetType, doResidualJECs=True, jecPath=self.jesInputFilePath + 'Summer16_23Sep2016BCDV4_DATA/', calculateSeparateCorrections=False, calculateType1METCorrection=False) self.jetReCalibratorDATA_EF = JetReCalibrator(globalTag='Summer16_23Sep2016EFV4_DATA', jetFlavour=self.jetType, doResidualJECs=True, jecPath=self.jesInputFilePath + 'Summer16_23Sep2016EFV4_DATA/', calculateSeparateCorrections=False, calculateType1METCorrection=False) self.jetReCalibratorDATA_G = JetReCalibrator(globalTag='Summer16_23Sep2016GV4_DATA', jetFlavour=self.jetType, doResidualJECs=True, jecPath=self.jesInputFilePath + 'Summer16_23Sep2016GV4_DATA/', calculateSeparateCorrections=False, calculateType1METCorrection=False) self.jetReCalibratorDATA_H = JetReCalibrator(globalTag='Summer16_23Sep2016HV4_DATA', jetFlavour=self.jetType, doResidualJECs=True, jecPath=self.jesInputFilePath + 'Summer16_23Sep2016HV4_DATA/', calculateSeparateCorrections=False, calculateType1METCorrection=False) # JES uncertainty if self.jes in ['up', 'down']: if not self.jes_source: # total unc. self.jesUncertaintyInputFileName = "Summer16_23Sep2016V4_MC_Uncertainty_" + self.jetType + ".txt" else: # unc. by source self.jesUncertaintyInputFileName = "Summer16_23Sep2016V4_MC_UncertaintySources_" + self.jetType + ".txt" pars = ROOT.JetCorrectorParameters(os.path.join(self.jesInputFilePath, self.jesUncertaintyInputFileName), self.jes_source) self.jesUncertainty = ROOT.JetCorrectionUncertainty(pars) # set up JER self.jetSmearer = None if self.jer is not None or self.jmr is not None: self.jetSmearer = jetSmearer(globalTag='2016', jetType=self.jetType, jerInputFileName="Spring16_25nsV10a_MC_PtResolution_%s.txt" % self.jetType, jerUncertaintyInputFileName="Spring16_25nsV10a_MC_SF_%s.txt" % self.jetType ) self.jetSmearer.jerInputFilePath = os.environ['CMSSW_BASE'] + "/src/PhysicsTools/NanoHRTTools/data/2016/jme/" self.jetSmearer.beginJob()
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 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_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 #2016 values 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 #W-tagging PUPPI softdrop JMS values: https://twiki.cern.ch/twiki/bin/view/CMS/JetWtagging #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)
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) # 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
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)
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
def OpenJECcalibrator(self, jetType = "AK4PF", doRes = True): # For jet re-calibrating fileNameJEC = self.filenameJEC jesInputFilePath = os.environ['CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/" # By default print 'Using the file: ', jesInputFilePath+fileNameJEC return JetReCalibrator(fileNameJEC, jetType , doRes, jesInputFilePath, upToLevel=1)
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
import math, os,re import numpy as np ROOT.PyConfig.IgnoreCommandLineOptions = True from PhysicsTools.NanoAODTools.postprocessing.framework.datamodel import Collection, Object from PhysicsTools.NanoAODTools.postprocessing.framework.eventloop import Module from PhysicsTools.NanoAODTools.postprocessing.tools import matchObjectCollection, matchObjectCollectionMultiple from PhysicsTools.NanoAODTools.postprocessing.modules.jme.JetReCalibrator import JetReCalibrator fileName = GetFileName(isData, run): def GetEraForRun(run): era = '' if (run <= 297019): era = 'A'; elif(run <= 299329 && run > 297019): era = 'B'; elif(run <= 302029 && run > 299336): era = 'C'; elif(run <= 303434 && run > 302029): era = 'D'; elif(run <= 304826 && run > 303434): era = 'E'; elif(run <= 306462 && run > 304910): era = 'F'; return era def GetFileName(isData, run): if not isData: return "Fall17_17Nov2017_V6_MC" else return 'Fall17_17Nov2017' + GetEraForRun(run) + '_V6_DATA' jetType = "AK4PFchs" jesInputFilePath = os.environ['CMSSW_BASE'] + "/src/PhysicsTools/NanoAODTools/data/jme/" jetReCalibrator = JetReCalibrator(fileMC, jetType , True, jesInputFilePath, upToLevel=1) corr = jetReCalibrator.getCorrection(jet, rho)
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. 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" 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.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_23Sep2016V4_MC_Uncertainty_" + jetType + ".txt" # Latest is Summer16_07Aug2017_V11 but affected by Formula Evaluator bug elif self.era == "2017": self.jesUncertaintyInputFileName = "Fall17_17Nov2017_V32_MC_Uncertainty_" + jetType + ".txt" elif self.era == "2018": self.jesUncertaintyInputFileName = "Autumn18_V8_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" # Latest is Summer16_07Aug2017_V11 but affected by Formula Evaluator bug elif self.era == "2017": self.jesUncertaintyInputFileName = "Fall17_17Nov2017_V32_MC_UncertaintySources_" + jetType + ".txt" elif self.era == "2018": self.jesUncertaintyInputFileName = "Autumn18_V8_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 __init__(self, era, globalTag, jerTag="", jesUncertainties=["Total"], jetType="AK4PFchs", redoJEC=False, noGroom=False, doSmearing=True, doL2L3=True): self.era = era self.redoJEC = redoJEC self.noGroom = noGroom self.doSmearing = doSmearing #-------------------------------------------------------------------------------------------- # 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 = jerTag + "_PtResolution_" + jetType + ".txt" self.jerUncertaintyInputFileName = jerTag + "_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/" self.jesUncertaintyInputFileName = self.jesInputFilePath + "/" + globalTag + "_Uncertainty_" + 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, doL2L3, 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)
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: if self.isData: self.jesUncertaintyInputFileName = globalTag + "_UncertaintySources_" + jetType + ".txt" else: # Using special regrouped uncertainties for MC 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 # 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") 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. only correct the non-mu fraction of the jet. if the corrected pt>15, use the corrected jet, otherwise use raw jet_pt_noMuL1L2L3 = jet.pt * jec if jet.pt * jec > self.unclEnThreshold else jet.pt jet_pt_noMuL1 = jet.pt * jecL1 if jet.pt * jec > self.unclEnThreshold else jet.pt # 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 if jet_pt_L1L2L3 > 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
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: if self.isData: self.jesUncertaintyInputFileName = globalTag + "_UncertaintySources_" + jetType + ".txt" else: # Using special regrouped uncertainties for MC 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 # 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)
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 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) 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) 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 #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) 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 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