Exemple #1
0
    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)
Exemple #2
0
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
Exemple #3
0
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)
Exemple #6
0
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
Exemple #7
0
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)
Exemple #9
0
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
Exemple #10
0
 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)
Exemple #11
0
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
Exemple #12
0
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)
Exemple #13
0
    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)
Exemple #14
0
    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
Exemple #19
0
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