Exemple #1
0
def makestack(lep_, reg_, variabile_, samples_, cut_tag_, syst_, lumi):
    os.system('set LD_PRELOAD=libtcmalloc.so')
    if variabile_._name == 'WprAK8_tau2/WprAK8_tau1':
        variabile_._name = 'WprAK8_tau21'
    elif variabile_._name == 'WprAK8_tau3/WprAK8_tau2':
        variabile_._name = 'WprAK8_tau32'
    blind = False
    infile = {}
    histo = []
    tmp = ROOT.TH1F()
    h = ROOT.TH1F()
    hdata = ROOT.TH1F('h', 'h', variabile_._nbins, variabile_._xmin,
                      variabile_._xmax)
    h_sig = []
    h_err = ROOT.TH1F()
    h_bkg_err = ROOT.TH1F()
    print "Variabile:", variabile_._name
    ROOT.gROOT.SetStyle('Plain')
    ROOT.gStyle.SetPalette(1)
    ROOT.gStyle.SetOptStat(0)
    ROOT.TH1.SetDefaultSumw2()
    if (cut_tag_ == ""):
        histoname = "h_" + reg_ + "_" + variabile_._name
        stackname = "stack_" + reg_ + "_" + variabile_._name
        canvasname = "stack_" + reg_ + "_" + variabile_._name + "_" + lep_ + "_" + str(
            samples_[0].year)
    else:
        histoname = "h_" + reg_ + "_" + variabile_._name + "_" + cut_tag_
        stackname = "stack_" + reg_ + "_" + variabile_._name + "_" + cut_tag_
        canvasname = "stack_" + reg_ + "_" + variabile_._name + "_" + cut_tag_ + "_" + lep_ + "_" + str(
            samples_[0].year)
    if ("selection_AND_best_Wpjet_isbtag_AND_best_topjet_isbtag" in cut_tag_
        ) or ("selection_AND_best_topjet_isbtag_AND_best_Wpjet_isbtag"
              in cut_tag_):
        blind = True
    stack = ROOT.THStack(stackname, variabile_._name)
    leg_stack = ROOT.TLegend(0.33, 0.62, 0.91, 0.87)
    signal = False

    print samples_
    for s in samples_:
        if ('WP' in s.label):
            signal = True
        if (syst_ == ""):
            outfile = plotrepo + "stack_" + str(lep_).strip('[]') + ".root"
            infile[s.label] = ROOT.TFile.Open(plotrepo + "plot/" + lep + "/" +
                                              s.label + "_" + lep + ".root")
        else:
            outfile = plotrepo + "stack_" + syst_ + "_" + str(lep_).strip(
                '[]') + ".root"
            infile[s.label] = ROOT.TFile.Open(plotrepo + "plot/" + lep + "/" +
                                              s.label + "_" + lep + "_" +
                                              syst_ + ".root")
    i = 0

    for s in samples_:
        infile[s.label].cd()
        print "opening file: ", infile[s.label].GetName()
        if ('Data' in s.label):
            if ("GenPart" in variabile_._name) or ("MC_" in variabile_._name):
                continue
        tmp = (ROOT.TH1F)(infile[s.label].Get(histoname))
        tmp.SetLineColor(ROOT.kBlack)
        tmp.SetName(s.leglabel)
        if ('Data' in s.label):
            if ("GenPart" in variabile_._name) or ("MC_" in variabile_._name):
                continue
            hdata.Add(ROOT.TH1F(tmp.Clone("")))
            hdata.SetMarkerStyle(20)
            hdata.SetMarkerSize(0.9)
            if (i == 0 and
                    not blind):  # trick to add Data flag to legend only once
                leg_stack.AddEntry(hdata, "Data", "ep")
            i += 1
        elif ('WP' in s.label):
            #tmp.SetLineStyle(9)
            if opt.tostack:
                tmp.SetLineColor(s.color)
            else:
                tmp.SetLineColor(s.color)
            #tmp.SetLineWidth(3)
            tmp.SetMarkerSize(0.)
            tmp.SetMarkerColor(s.color)
            h_sig.append(ROOT.TH1F(tmp.Clone("")))
        else:
            tmp.SetOption("HIST SAME")
            tmp.SetTitle("")
            if opt.tostack:
                tmp.SetFillColor(s.color)
            else:
                tmp.SetLineColor(s.color)
            histo.append(tmp.Clone(""))
            stack.Add(tmp.Clone(""))
        tmp.Reset("ICES")
    for hist in reversed(histo):
        if not ('Data' in hist.GetName()):
            leg_stack.AddEntry(hist, hist.GetName(), "f")
    #style options
    print "Is it blind? " + str(blind)
    leg_stack.SetNColumns(2)
    leg_stack.SetFillColor(0)
    leg_stack.SetFillStyle(0)
    leg_stack.SetTextFont(42)
    leg_stack.SetBorderSize(0)
    leg_stack.SetTextSize(0.05)
    c1 = ROOT.TCanvas(canvasname, "c1", 50, 50, 700, 600)
    c1.SetFillColor(0)
    c1.SetBorderMode(0)
    c1.SetFrameFillStyle(0)
    c1.SetFrameBorderMode(0)
    c1.SetLeftMargin(0.12)
    c1.SetRightMargin(0.9)
    c1.SetTopMargin(1)
    c1.SetBottomMargin(-1)
    c1.SetTickx(1)
    c1.SetTicky(1)
    c1.cd()

    pad1 = ROOT.TPad("pad1", "pad1", 0, 0.31, 1, 1)
    pad1.SetTopMargin(0.1)
    pad1.SetBottomMargin(0.02)
    pad1.SetLeftMargin(0.12)
    pad1.SetRightMargin(0.05)
    pad1.SetBorderMode(0)
    pad1.SetTickx(1)
    pad1.SetTicky(1)
    pad1.Draw()
    pad1.cd()
    if not blind:
        maximum = max(stack.GetMaximum(), hdata.GetMaximum())
    else:
        maximum = stack.GetMaximum()
    logscale = True  # False #
    if (logscale):
        pad1.SetLogy()
        stack.SetMaximum(maximum * 1000)
    else:
        stack.SetMaximum(maximum * 1.6)
    stack.SetMinimum(0.01)
    if opt.tostack:
        stack.Draw("HIST")
    else:
        stack.Draw("HIST NOSTACK")
    step = float(variabile_._xmax - variabile_._xmin) / float(
        variabile_._nbins)
    print str(step)
    if "GeV" in variabile_._title:
        if step.is_integer():
            ytitle = "Events / %.0f GeV" % step
        else:
            ytitle = "Events / %.2f GeV" % step
    else:
        if step.is_integer():
            ytitle = "Events / %.0f units" % step
        else:
            ytitle = "Events / %.2f units" % step
    stack.GetYaxis().SetTitle(ytitle)
    stack.GetYaxis().SetTitleFont(42)
    stack.GetXaxis().SetLabelOffset(1.8)
    stack.GetYaxis().SetTitleOffset(0.85)
    stack.GetXaxis().SetLabelSize(0.15)
    stack.GetYaxis().SetLabelSize(0.07)
    stack.GetYaxis().SetTitleSize(0.07)
    stack.SetTitle("")
    if (signal):
        for hsig in h_sig:
            #hsig.Scale(1000)
            hsig.Draw("same")
            leg_stack.AddEntry(hsig, hsig.GetName(), "l")
    h_err = stack.GetStack().Last().Clone("h_err")
    h_err.SetLineWidth(100)
    h_err.SetFillStyle(3154)
    h_err.SetMarkerSize(0)
    h_err.SetFillColor(ROOT.kGray + 2)
    h_err.Draw("e2same0")
    leg_stack.AddEntry(h_err, "Stat. Unc.", "f")
    if not blind:
        print(hdata.Integral())
        hdata.Draw("eSAMEpx0")
    else:
        hdata = stack.GetStack().Last().Clone("h_data")
    leg_stack.Draw("same")

    CMS_lumi.writeExtraText = 1
    CMS_lumi.extraText = ""
    if str(lep_).strip('[]') == "muon":
        lep_tag = "#mu+"
    elif str(lep_).strip('[]') == "electron":
        lep_tag = "e+"

    lumi_sqrtS = "%s fb^{-1}  (13 TeV)" % (lumi)

    iPeriod = 0
    iPos = 11
    CMS_lumi(pad1, lumi_sqrtS, iPos, lep_tag + str(reg_))
    hratio = stack.GetStack().Last()

    c1.cd()
    pad2 = ROOT.TPad("pad2", "pad2", 0, 0.01, 1, 0.30)
    pad2.SetTopMargin(0.05)
    pad2.SetBottomMargin(0.45)
    pad2.SetLeftMargin(0.12)
    pad2.SetRightMargin(0.05)
    ROOT.gStyle.SetHatchesSpacing(2)
    ROOT.gStyle.SetHatchesLineWidth(2)
    c1.cd()
    pad2.Draw()
    pad2.cd()
    ratio = hdata.Clone("ratio")
    ratio.SetLineColor(ROOT.kBlack)
    ratio.SetMaximum(2)
    ratio.SetMinimum(0)
    ratio.Sumw2()
    ratio.SetStats(0)

    ratio.Divide(hratio)
    ratio.SetMarkerStyle(20)
    ratio.SetMarkerSize(0.9)
    ratio.Draw("epx0e0")
    ratio.SetTitle("")

    h_bkg_err = hratio.Clone("h_err")
    h_bkg_err.Reset()
    h_bkg_err.Sumw2()
    for i in range(1, hratio.GetNbinsX() + 1):
        h_bkg_err.SetBinContent(i, 1)
        if (hratio.GetBinContent(i)):
            h_bkg_err.SetBinError(
                i, (hratio.GetBinError(i) / hratio.GetBinContent(i)))
        else:
            h_bkg_err.SetBinError(i, 10 ^ (-99))
    h_bkg_err.SetLineWidth(100)

    h_bkg_err.SetMarkerSize(0)
    h_bkg_err.SetFillColor(ROOT.kGray + 1)
    h_bkg_err.Draw("e20same")

    f1 = ROOT.TLine(variabile_._xmin, 1., variabile_._xmax, 1.)
    f1.SetLineColor(ROOT.kBlack)
    f1.SetLineStyle(ROOT.kDashed)
    f1.Draw("same")

    ratio.GetYaxis().SetTitle("Data / MC")
    ratio.GetYaxis().SetNdivisions(503)
    ratio.GetXaxis().SetLabelFont(42)
    ratio.GetYaxis().SetLabelFont(42)
    ratio.GetXaxis().SetTitleFont(42)
    ratio.GetYaxis().SetTitleFont(42)
    ratio.GetXaxis().SetTitleOffset(1.1)
    ratio.GetYaxis().SetTitleOffset(0.35)
    ratio.GetXaxis().SetLabelSize(0.15)
    ratio.GetYaxis().SetLabelSize(0.15)
    ratio.GetXaxis().SetTitleSize(0.16)
    ratio.GetYaxis().SetTitleSize(0.16)
    ratio.GetYaxis().SetRangeUser(0., 2.0)
    ratio.GetXaxis().SetTitle(variabile_._title)
    ratio.GetXaxis().SetLabelOffset(0.04)
    ratio.GetYaxis().SetLabelOffset(0.02)
    ratio.Draw("epx0e0same")

    c1.cd()
    #ROOT.TGaxis.SetMaxDigits(3)
    c1.RedrawAxis()
    pad2.RedrawAxis()
    c1.Update()
    c1.Print(plotrepo + "stack/" + canvasname + ".png")
    c1.Print(plotrepo + "stack/" + canvasname + ".pdf")
    del histo
    tmp.Delete()
    h.Delete()
    del tmp
    del h
    del h_sig
    h_err.Delete()
    del h_err
    h_bkg_err.Delete()
    del h_bkg_err
    hratio.Delete()
    del hratio
    stack.Delete()
    del stack
    pad1.Delete()
    del pad1
    pad2.Delete()
    del pad2
    c1.Delete()
    del c1
    for s in samples_:
        infile[s.label].Close()
        infile[s.label].Delete()
    os.system('set LD_PRELOAD=libtcmalloc.so')
Exemple #2
0
def run(model,m1,res,lumi):

  filepath = 'dispjets_jsons/'
  rootfile = ROOT.TFile(filepath+"plot_m"+m1+"_r"+str(res)+"_L"+str(lumi)+".root","RECREATE")
  rootfile.cd()
  #m2  = [1, 3, 10, 30, 100, 300, 1000, 3000]
  m2 = [1,10,100,1000,10000]
  wgt = 0.001/13.19  #0.001 b/c i injected this xsec in analysis.cpp 13.19 divided b/c theo presents limits in terms of BR

  # read in theoretical xsec
  if (model=="2HDM"): xsecfile = open('crosssectionZp2HDM.txt','r')
  if (model=="BARY"): xsecfile = open('crosssectionZpBaryonic.txt','r')
  if (model=="DISP"): xsecfile = open('crosssectionDispJets.txt','r') 
  mZ = []
  mA = []
  mstr = []
  xsecs = {} 
  xsec = []
  for line in xsecfile:
    line = line.rstrip('\n')
    line = line.split(' ')
    mZ.append(line[0])
    mA.append(line[1])
    massstr = line[0]+'_'+line[1]
    mstr.append(massstr)
    xsecs[massstr] = float(line[2])

  xsec = array( 'd' )
  mass = array( 'd' )
  exp_raw = array( 'd' ) 
  obs_raw = array( 'd' ) 
  up1_raw = array( 'd' )
  do1_raw = array( 'd' )
  up2_raw = array( 'd' )
  do2_raw = array( 'd' )

  # pick up jsons with limits
  for m in m2:
    newmassstr = str(m)+'_'+m1
    if newmassstr not in mstr: continue
    
    # make correct array of xsecs
    mass.append(m)
    xsec.append(xsecs[str(m)+'_'+m1])   
    if (model=="2HDM"): filename=filepath+'Zprime'+str(m)+'A'+m1+'.json'
    if (model=="BARY"): filename=filepath+'Zprime'+str(m)+'DM'+m1+'.json'
    if (model=="DISP"): filename=filepath+'datacard_dispjets_ct'+str(m)+'mm_res'+res+'_lumi'+lumi+'pb.json'
    print filename
    if os.path.isfile(filename):
      with open(filename) as jsonfile:
        data = json.load(jsonfile)
        for key in data: 
          exp_raw.append(data[key][u'exp0'])
          obs_raw.append(data[key][u'obs'])
          up1_raw.append(data[key][u'exp+1'])
          do1_raw.append(data[key][u'exp-1'])
          up2_raw.append(data[key][u'exp+2'])
          do2_raw.append(data[key][u'exp-2'])
          print data[key][u'exp0']
    else:  print 'File '+filename+' NOT found'

  exp = scaleBR(exp_raw,wgt) 
  obs = scaleBR(obs_raw,wgt)
  up1 = scaleBR(up1_raw,wgt)
  do1 = scaleBR(do1_raw,wgt)
  up2 = scaleBR(up2_raw,wgt)
  do2 = scaleBR(do2_raw,wgt)

  numpts = len(mass)          
  print numpts
  print xsec
  limitPlotExp = ROOT.TGraph(numpts,mass,exp)
  limitPlotObs = ROOT.TGraph(numpts,mass,obs)
  limitPlotXsc = ROOT.TGraph(numpts,mass,xsec)
  limitPlot1sig   = ROOT.TGraph(2*numpts)
  limitPlot2sig   = ROOT.TGraph(2*numpts)

  for i in range(0,numpts):
    limitPlot1sig.SetPoint(i,mass[i],up1[i])
    limitPlot1sig.SetPoint(numpts+i,mass[numpts-i-1],do1[numpts-i-1])
    limitPlot2sig.SetPoint(i,mass[i],up2[i])
    limitPlot2sig.SetPoint(numpts+i,mass[numpts-i-1],do2[numpts-i-1])
  

  ROOT.gStyle.SetOptStat(0)
  c = ROOT.TCanvas('','')
  c.SetLogx(1)
  c.SetLogy(1)
  c.SetGrid();
 
  limitPlotObs.SetTitle("")
  limitPlotObs.GetXaxis().SetTitle("LL particle c#tau [mm]")
  limitPlotObs.GetYaxis().SetTitle("BR(H#rightarrowXX)")

  # styling
  limitPlotXsc.SetLineColor(4)
  limitPlotXsc.SetLineStyle(7)
  limitPlotXsc.SetLineWidth(2)

  limitPlotExp.SetLineStyle(7)
  limitPlotExp.SetLineWidth(2)
  limitPlotObs.SetLineWidth(2)

  limitPlot1sig.SetFillColor(5)
  limitPlot1sig.SetFillStyle(3013)

  limitPlot2sig.SetFillColor(8)
  limitPlot2sig.SetFillStyle(3013)

  limitPlotObs.SetMaximum(10)
  limitPlotObs.SetMinimum(0.0000001)

  limitPlotObs.Draw("ACP")
  limitPlot2sig.Draw("F SAME")
  limitPlot1sig.Draw("F SAME")
  limitPlotExp.Draw("CP SAME")
  limitPlotObs.Draw("CP SAME")
  limitPlotXsc.Draw("CP SAME")
  limitPlotExp.SetName("expected_curve")
  limitPlotExp.Write()
  
  CMS_lumi(c,4,0)
  c.RedrawAxis() 
  c.Print("~/www/DispJets/Limits/lim1D_m"+m1+"_r"+res+"_L"+lumi+".pdf")
  c.Print("~/www/DispJets/Limits/lim1D_m"+m1+"_r"+res+"_L"+lumi+".png")
Exemple #3
0
def run(model, m1, res, lumi):

    filepath = 'dispjets_jsons/'
    #m2  = [1, 3, 10, 30, 100, 300, 1000, 3000]
    m2 = [100]
    wgt = 1

    # read in theoretical xsec
    if (model == "2HDM"): xsecfile = open('crosssectionZp2HDM.txt', 'r')
    if (model == "BARY"): xsecfile = open('crosssectionZpBaryonic.txt', 'r')
    if (model == "DISP"): xsecfile = open('crosssectionDispJets.txt', 'r')
    mZ = []
    mA = []
    mstr = []
    xsecs = {}
    xsec = []
    for line in xsecfile:
        line = line.rstrip('\n')
        line = line.split(' ')
        mZ.append(line[0])
        mA.append(line[1])
        massstr = line[0] + '_' + line[1]
        mstr.append(massstr)
        xsecs[massstr] = float(line[2])

    xsec = array('d')
    mass = array('d')
    exp_raw = array('d')
    obs_raw = array('d')
    up1_raw = array('d')
    do1_raw = array('d')
    up2_raw = array('d')
    do2_raw = array('d')

    # pick up jsons with limits
    for m in m2:
        newmassstr = str(m) + '_' + m1
        if newmassstr not in mstr: continue

        # make correct array of xsecs
        mass.append(m)
        xsec.append(xsecs[str(m) + '_' + m1])
        if (model == "2HDM"):
            filename = filepath + 'Zprime' + str(m) + 'A' + m1 + '.json'
        if (model == "BARY"):
            filename = filepath + 'Zprime' + str(m) + 'DM' + m1 + '.json'
        if (model == "DISP"):
            filename = filepath + 'XXto4Q_M' + m1 + '_CT' + str(
                m) + '_r' + res + '_L' + lumi + '.json'

        if os.path.isfile(filename):
            with open(filename) as jsonfile:
                data = json.load(jsonfile)
                for key in data:
                    exp_raw.append(data[key][u'exp0'])
                    obs_raw.append(data[key][u'obs'])
                    up1_raw.append(data[key][u'exp+1'])
                    do1_raw.append(data[key][u'exp-1'])
                    up2_raw.append(data[key][u'exp+2'])
                    do2_raw.append(data[key][u'exp-2'])
        else:
            print 'File ' + filename + ' NOT found'

    exp = scaleBR(exp_raw, wgt)
    obs = scaleBR(obs_raw, wgt)
    up1 = scaleBR(up1_raw, wgt)
    do1 = scaleBR(do1_raw, wgt)
    up2 = scaleBR(up2_raw, wgt)
    do2 = scaleBR(do2_raw, wgt)

    numpts = len(mass)
    limitPlotExp = ROOT.TGraph(numpts, mass, exp)
    limitPlotObs = ROOT.TGraph(numpts, mass, obs)
    limitPlotXsc = ROOT.TGraph(numpts, mass, xsec)
    limitPlot1sig = ROOT.TGraph(2 * numpts)
    limitPlot2sig = ROOT.TGraph(2 * numpts)
    for i in range(0, numpts):
        limitPlot1sig.SetPoint(i, mass[i], up1[i])
        limitPlot1sig.SetPoint(numpts + i, mass[numpts - i - 1],
                               do1[numpts - i - 1])
        limitPlot2sig.SetPoint(i, mass[i], up2[i])
        limitPlot2sig.SetPoint(numpts + i, mass[numpts - i - 1],
                               do2[numpts - i - 1])

    ROOT.gStyle.SetOptStat(0)
    c = ROOT.TCanvas('', '')
    c.SetLogx(1)
    c.SetLogy(1)
    c.SetGrid()

    limitPlotObs.SetTitle("")
    limitPlotObs.GetXaxis().SetTitle("LL particle c#tau [mm]")
    limitPlotObs.GetYaxis().SetTitle("#sigma [pb]")

    # styling
    limitPlotXsc.SetLineColor(4)
    limitPlotXsc.SetLineStyle(7)
    limitPlotXsc.SetLineWidth(2)

    limitPlotExp.SetLineStyle(7)
    limitPlotExp.SetLineWidth(2)
    limitPlotObs.SetLineWidth(2)

    limitPlot1sig.SetFillColor(5)
    limitPlot1sig.SetFillStyle(3013)

    limitPlot2sig.SetFillColor(8)
    limitPlot2sig.SetFillStyle(3013)

    limitPlotObs.SetMaximum(10)
    limitPlotObs.SetMinimum(0.1)

    limitPlotObs.Draw("ACP")
    limitPlot2sig.Draw("F SAME")
    limitPlot1sig.Draw("F SAME")
    limitPlotExp.Draw("CP SAME")
    limitPlotObs.Draw("CP SAME")
    limitPlotXsc.Draw("CP SAME")

    CMS_lumi(c, 4, 0)
    c.RedrawAxis()
    c.Print("~/www/Plots/DispJets/GenLevelPlots/Limits/lim1D_m" + m1 + "_r" +
            res + "_L" + lumi + ".pdf")
    c.Print("~/www/Plots/DispJets/GenLevelPlots/Limits/lim1D_m" + m1 + "_r" +
            res + "_L" + lumi + ".png")
def run(model, m1):

    filepath = 'gg_BARY_results/'
    m2 = [
        10, 20, 30, 40, 50, 75, 100, 150, 200, 250, 300, 350, 400, 450, 500,
        550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1100
    ]
    wgt = 1

    # read in theoretical xsec
    if (model == "2HDM"): xsecfile = open('crosssectionZp2HDM.txt', 'r')
    if (model == "BARY"): xsecfile = open('crosssectionZpBaryonic.txt', 'r')
    mZ = []
    mA = []
    mstr = []
    xsecs = {}
    xsec = []
    for line in xsecfile:
        line = line.rstrip('\n')
        line = line.split(' ')
        mZ.append(line[0])
        mA.append(line[1])
        massstr = line[0] + '_' + line[1]
        mstr.append(massstr)
        xsecs[massstr] = float(line[2])

    xsec = array('d')
    mass = array('d')
    exp_raw = array('d')
    obs_raw = array('d')
    up1_raw = array('d')
    do1_raw = array('d')
    up2_raw = array('d')
    do2_raw = array('d')

    # pick up jsons with limits
    for m in m2:
        newmassstr = str(m) + '_' + m1
        if newmassstr not in mstr: continue

        # make correct array of xsecs
        mass.append(m)
        xsec.append(xsecs[str(m) + '_' + m1])
        if (model == "2HDM"):
            filename = filepath + 'Zprime' + str(m) + 'A' + m1 + '.json'
        if (model == "BARY"):
            filename = filepath + 'Zprime' + str(m) + 'DM' + m1 + '.json'
        if os.path.isfile(filename):
            with open(filename) as jsonfile:
                data = json.load(jsonfile)
                for key in data:
                    exp_raw.append(data[key][u'exp0'])
                    obs_raw.append(data[key][u'obs'])
                    up1_raw.append(data[key][u'exp+1'])
                    do1_raw.append(data[key][u'exp-1'])
                    up2_raw.append(data[key][u'exp+2'])
                    do2_raw.append(data[key][u'exp-2'])
        else:
            print 'File ' + filename + ' NOT found'

    exp = scaleBR(exp_raw, wgt)
    obs = scaleBR(obs_raw, wgt)
    up1 = scaleBR(up1_raw, wgt)
    do1 = scaleBR(do1_raw, wgt)
    up2 = scaleBR(up2_raw, wgt)
    do2 = scaleBR(do2_raw, wgt)

    numpts = len(mass)
    limitPlotExp = ROOT.TGraph(numpts, mass, exp)
    limitPlotObs = ROOT.TGraph(numpts, mass, obs)
    limitPlotXsc = ROOT.TGraph(numpts, mass, xsec)
    limitPlot1sig = ROOT.TGraph(2 * numpts)
    limitPlot2sig = ROOT.TGraph(2 * numpts)
    for i in range(0, numpts):
        limitPlot1sig.SetPoint(i, mass[i], up1[i])
        limitPlot1sig.SetPoint(numpts + i, mass[numpts - i - 1],
                               do1[numpts - i - 1])
        limitPlot2sig.SetPoint(i, mass[i], up2[i])
        limitPlot2sig.SetPoint(numpts + i, mass[numpts - i - 1],
                               do2[numpts - i - 1])

    ROOT.gStyle.SetOptStat(0)
    c = ROOT.TCanvas('', '')
    c.SetLogx(1)
    c.SetLogy(1)
    c.SetGrid()

    limitPlotObs.SetTitle("")
    limitPlotObs.GetXaxis().SetTitle("M_{Z'} [GeV]")
    limitPlotObs.GetYaxis().SetTitle("#sigma(Z' #rightarrow#chi#chi h) [pb]")

    # styling
    limitPlotXsc.SetLineColor(4)
    limitPlotXsc.SetLineStyle(7)
    limitPlotXsc.SetLineWidth(2)

    limitPlotExp.SetLineStyle(7)
    limitPlotExp.SetLineWidth(2)
    limitPlotObs.SetLineWidth(2)

    limitPlot1sig.SetFillColor(5)
    limitPlot1sig.SetFillStyle(3013)

    limitPlot2sig.SetFillColor(8)
    limitPlot2sig.SetFillStyle(3013)

    limitPlotObs.SetMaximum(10)
    limitPlotObs.SetMinimum(0.1)

    limitPlotObs.Draw("ACP")
    limitPlot2sig.Draw("F SAME")
    limitPlot1sig.Draw("F SAME")
    limitPlotExp.Draw("CP SAME")
    limitPlotObs.Draw("CP SAME")
    limitPlotXsc.Draw("CP SAME")

    CMS_lumi(c, 4, 0)
    c.RedrawAxis()
    c.Print("~/www/Plots/13TeV_v80X_moriond17/MonoHCombo/test_1Dlimit.pdf")
    c.Print("~/www/Plots/13TeV_v80X_moriond17/MonoHCombo/test_1Dlimit.png")
def doTheMassFit(ws,
                 data=None,
                 CandTypes=None,
                 showResult=False,
                 outD='unfolded',
                 postfixForOutputs=''):

    #fit the mass spectrum
    ws.pdf('model').fitTo(data, ROOT.RooFit.Extended())

    if not showResult: return

    #get the main parameters of the fit
    nsig = ws.var("nsig").getVal()
    nsigErr = ws.var("nsig").getError()
    nbkg = ws.var("nbkg").getVal()
    nbkgErr = ws.var("nbkg").getError()
    mass = ws.var("sig_mu").getVal()
    massErr = ws.var("sig_mu").getError()
    try:
        width = ws.var("sig_sigma").getVal()
        widthErr = ws.var("sig_sigma").getError()
    except:
        width = ws.var("sig_Gauss1_sigma").getVal()
        widthErr = ws.var("sig_Gauss1_sigma").getError()

    #show result of the fit
    cfit = ROOT.TCanvas("cfit", "cfit", 500, 500)
    cfit.cd()
    frame = ws.var("mass").frame()  #ROOT.RooFit.Bins(50))
    data.plotOn(frame, ROOT.RooFit.DrawOption("p"),
                ROOT.RooFit.MarkerStyle(20), ROOT.RooFit.Name("data"))
    ws.pdf("model").plotOn(frame, ROOT.RooFit.FillStyle(0),
                           ROOT.RooFit.MoveToBack(), ROOT.RooFit.Name("total"))
    # ws.pdf("model").plotOn(frame,
    #                           ROOT.RooFit.Components('sig_model'),
    #                           ROOT.RooFit.LineColor(5),
    #                           ROOT.RooFit.LineWidth(1),
    #                           ROOT.RooFit.FillStyle(1001),
    #                           ROOT.RooFit.FillColor(5),
    #                           ROOT.RooFit.DrawOption("LF"),
    #                           ROOT.RooFit.MoveToBack(),
    #                           ROOT.RooFit.Name("sig"))
    ws.pdf("model").plotOn(frame, ROOT.RooFit.Components('bkg_model'),
                           ROOT.RooFit.LineColor(1), ROOT.RooFit.LineWidth(2),
                           ROOT.RooFit.FillStyle(1001),
                           ROOT.RooFit.FillColor(920),
                           ROOT.RooFit.DrawOption("LF"),
                           ROOT.RooFit.MoveToBack(), ROOT.RooFit.Name("bkg"))
    frame.Draw()

    try:
        bkg_lambda = ws.var('bkg_lambda').getVal()
        ymin = nbkg * math.exp(frame.GetXaxis().GetXmax() * bkg_lambda) * 0.2
        ymax = 1.4 * frame.GetMaximum()
    except TypeError:
        ymin = 0
        ymax = 1.1 * frame.GetMaximum()

    frame.GetYaxis().SetRangeUser(ymin, ymax)
    frame.GetYaxis().SetTitle("Candidates")
    frame.GetYaxis().SetTitleOffset(1.6)
    if '411' in str(CandTypes):
        frame.GetXaxis().SetTitle(XAXIS['Dpm'])
    if '421' in str(CandTypes):
        frame.GetXaxis().SetTitle(XAXIS['D0'])
    if '443' in str(CandTypes):
        frame.GetXaxis().SetTitle(XAXIS['JPsi'])
    if '413' in str(CandTypes):
        frame.GetXaxis().SetTitle(XAXIS['Dsm'])
    frame.GetXaxis().SetTitleOffset(0.9)
    frame.GetYaxis().SetTitleSize(0.05)
    frame.GetYaxis().SetLabelSize(0.04)
    frame.GetXaxis().SetTitleSize(0.05)
    frame.GetXaxis().SetLabelSize(0.04)
    cfit.Modified()
    cfit.Update()

    #build a legend
    leg = ROOT.TLegend(0.7, 0.79, 0.9, 0.94, "", "brNDC")
    # leg=ROOT.TLegend(0.7,0.72,0.9,0.95,"","brNDC")
    leg.SetFillStyle(0)
    leg.SetBorderSize(0)
    leg.SetTextFont(42)
    leg.SetTextSize(0.04)
    leg.AddEntry("data", "Data", "p")
    leg.AddEntry("bkg", "Background", "f")
    # leg.AddEntry("sig",   "Signal",     "f")
    leg.AddEntry("total", "Total", "f")
    leg.Draw()

    #display fit results on the canvas
    CMS_lumi(cfit, 2, 10)
    if not '413' in str(CandTypes):
        pt = ROOT.TPaveText(0.17, 0.85, 0.5, 0.6, "brNDC")
    else:
        pt = ROOT.TPaveText(0.50, 0.77, 0.89, 0.52, "brNDC")
    pt.SetFillStyle(0)
    pt.SetBorderSize(0)
    pt.SetTextFont(42)
    pt.SetTextAlign(12)
    pt.SetTextSize(0.04)

    if not '413' in str(CandTypes):
        pt.AddText("m = %3.4f #pm %3.4f GeV" % (mass, massErr))
        pt.AddText("#sigma = %3.4f #pm %3.4f GeV" % (width, widthErr))
        pt.AddText("N_{signal} = %3.0f #pm %3.0f" % (nsig, nsigErr))
        pt.AddText("N_{bkg} = %3.0f #pm %3.0f" % (nbkg, nbkgErr))
    else:
        pt.AddText("dm = %3.2f #pm %3.2f MeV" % (mass * 1000, massErr * 1000))
        pt.AddText("#sigma = %3.2f #pm %3.2f MeV" %
                   (width * 1000, widthErr * 1000))
        pt.AddText("N_{signal} = %3.0f #pm %3.0f" % (nsig, nsigErr))
        pt.AddText("N_{bkg} = %3.0f #pm %3.0f" % (nbkg, nbkgErr))
    pt.Draw()

    #save to file
    outF = 'cfit%s' % postfixForOutputs

    for ext in ['.pdf', '.png', '.C']:
        #for ext in ['.pdf']:
        cfit.SaveAs(os.path.join(outD, outF + ext))
Exemple #6
0
#limitPlotDown2.Draw("CONT3 SAME")

limitPlotObsCopy = limitPlotObs.Clone()
limitPlotObsCopy.SetMinimum(1)
limitPlotObsCopy.SetContour(1)
limitPlotObsCopy.SetLineWidth(2)
limitPlotObsCopy.Draw("CONT3 SAME")

limitPlot.SetMinimum(1)
limitPlot.SetContour(1)
limitPlot.SetLineStyle(7)
limitPlot.SetLineWidth(3)
limitPlot.Draw("CONT3 SAME")

leg = ROOT.TLegend(.35, .75, .90, .90)
leg.SetBorderSize(0)
leg.SetFillColor(0)
leg.SetFillStyle(0)
leg.SetTextFont(42)
leg.SetTextSize(0.030)
leg.AddEntry(limitPlotObsCopy, "Observed Limit (95% CL)", "L")
leg.AddEntry(limitPlot, "Expected Limit (95% CL)", "L")
leg.AddEntry(limitPlotUp, "Expected Limit +/- 1 \sigma", "L")
#leg.AddEntry(limitPlotUp2,"Expected Limit +/- 2\sigma r = 1 (95% CL)","L")
leg.Draw()

CMS_lumi(canv, 4, 0)

limitPlot.SaveAs("test.root")
canv.Print("test.pdf")
def run(opts):

    # --- read in options
    model = opts.model
    which = opts.which
    outdir = opts.outdir
    do90 = opts.do90
    dowgt = opts.dowgt
    dosmth = opts.dosmth
    smthfnc = opts.smthfnc
    #if dosmth: addtxt = '_smth'

    # --- read in files
    indir = '/eos/cms/store/group/phys_exotica/MonoHgg/MonoH-COMBO-2016/' + model + '_jsons/'
    if dowgt: wfile = ''
    else: wfile = '_weighted'
    if do90: indir += which + '_' + model + wfile + '_results_90CL/'
    else: indir += which + '_' + model + wfile + '_results/'

    # --- options for plot averaging
    doFillAvgLow = True  # do averaging below mMed = 2*mDM line
    doFillAvgHigh = True  # do averaging above mMed = 2*mDM line
    if model == "2HDM": doFillAvgLow = False
    if model == "2HDM": doFillAvgHigh = False
    doFillAvgRest = True  # do averaging at line or average normally
    doFillAvgAll = True  # do averaging for all plots not just observed

    # --- setup general style
    ROOT.gROOT.SetBatch(ROOT.kTRUE)
    ROOT.gStyle.SetOptStat(0)
    plot.ModTDRStyle()

    canv = ROOT.TCanvas()
    canv.SetLogz()
    canv.SetTicks()
    canv.SetRightMargin(0.16)  # allow enough space for z axis label
    canv.cd()

    # --- setup palette
    ROOT.gStyle.SetPalette(57)  # palette normal
    InvertPalette()  # palette inverted
    ROOT.gStyle.SetNumberContours(255)

    A = []
    Z = []
    # --- mass points
    if model == "2HDM":
        A = [
            300, 325, 350, 375, 400, 425, 450, 475, 500, 525, 550, 575, 600,
            625, 650, 675
        ]
        Z = [
            450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1050,
            1100, 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600,
            1650, 1700, 1750, 1800, 1850, 1900, 1950
        ]
    if model == "BARY":
        A = [
            1, 35, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325, 350, 375,
            400, 425, 450, 475, 500, 525, 550, 575, 600, 625, 650, 675, 700,
            725, 750, 775, 800, 825, 850, 875, 900, 925, 950, 975, 1000
        ]
        Z = [
            10, 50, 100, 200, 250, 300, 350, 400, 450, 500, 550, 600, 675, 750,
            800, 850, 900, 950, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700,
            1800, 1900, 2000
        ]

    # --- binning for BARY model
    # Y axis
    BinningA = [0.5, 1.5]
    BinAAxis = [1.0, 47.5]
    for i in range(1, len(A) - 1):
        BinningA.append((A[i] + A[i + 1]) / 2.0)
        BinAAxis.append((A[i] + A[i + 1]) / 2.0)
    BinningA.append((A[-1] + A[-1] - ((A[-1] + A[-2]) / 2.0)))
    BinAAxis.append((A[-1] + A[-1] - ((A[-1] + A[-2]) / 2.0)))
    # X axis
    BinningZ = [9, 11]
    BinZAxis = [10, 75]
    for i in range(1, len(Z) - 1):
        BinningZ.append((Z[i] + Z[i + 1]) / 2.0)
        BinZAxis.append((Z[i] + Z[i + 1]) / 2.0)
    BinningZ.append((Z[-1] + Z[-1] - ((Z[-1] + Z[-2]) / 2.0)))
    BinZAxis.append((Z[-1] + Z[-1] - ((Z[-1] + Z[-2]) / 2.0)))

    # --- setup histograms (different models have different binning)
    if model == "2HDM":
        limitPlotAxis = ROOT.TH2F("lplotAxis", "lplotAxis", len(Z), Z[0],
                                  Z[-1] + 50, len(A), A[0], A[-1] + 25)
        limitPlot = ROOT.TH2F("lplot", "lplot", len(Z), Z[0] - 25, Z[-1] + 50,
                              len(A), A[0] - 12.5, A[-1] + 25)
        limitPlotObs = ROOT.TH2F("lplotObs", "lplotObs", len(Z), Z[0] - 25,
                                 Z[-1] + 50, len(A), A[0] - 12.5, A[-1] + 25)
        limitPlotUp = ROOT.TH2F("lplotU", "lplotU", len(Z), Z[0] - 25,
                                Z[-1] + 50, len(A), A[0] - 12.5, A[-1] + 25)
        limitPlotDown = ROOT.TH2F("lplotDown", "lplotDown", len(Z), Z[0] - 25,
                                  Z[-1] + 50, len(A), A[0] - 12.5, A[-1] + 25)
        limitPlotUp2 = ROOT.TH2F("lplotU2", "lplotU2", len(Z), Z[0] - 25,
                                 Z[-1] + 50, len(A), A[0] - 12.5, A[-1] + 25)
        limitPlotDown2 = ROOT.TH2F("lplotDown2",
                                   "lplotDown2", len(Z), Z[0] - 25, Z[-1] + 50,
                                   len(A), A[0] - 12.5, A[-1] + 25)
    if model == "BARY":  # variable binning
        limitPlotAxis = ROOT.TH2F("lplotAxis", "lplotAxis",
                                  len(Z) - 1, array('d', BinZAxis),
                                  len(A) - 1, array('d', BinAAxis))
        limitPlot = ROOT.TH2F("lplot", "lplot",
                              len(BinningZ) - 1, array('d', BinningZ),
                              len(BinningA) - 1, array('d', BinningA))
        limitPlotObs = ROOT.TH2F("lplotObs", "lplotObs",
                                 len(BinningZ) - 1, array('d', BinningZ),
                                 len(BinningA) - 1, array('d', BinningA))
        limitPlotUp = ROOT.TH2F("lplotU", "lplotU",
                                len(BinningZ) - 1, array('d', BinningZ),
                                len(BinningA) - 1, array('d', BinningA))
        limitPlotDown = ROOT.TH2F("lplotDown", "lplotDown",
                                  len(BinningZ) - 1, array('d', BinningZ),
                                  len(BinningA) - 1, array('d', BinningA))
        limitPlotUp2 = ROOT.TH2F("lplotU2", "lplotU2",
                                 len(BinningZ) - 1, array('d', BinningZ),
                                 len(BinningA) - 1, array('d', BinningA))
        limitPlotDown2 = ROOT.TH2F("lplotDown2", "lplotDown2",
                                   len(BinningZ) - 1, array('d', BinningZ),
                                   len(BinningA) - 1, array('d', BinningA))

    # --- read in json files
    for a in A:
        for z in Z:
            data = {}
            filename = indir + 'Zprime' + str(z) + 'A' + str(a) + '.json'
            if which == 'gg' and model == 'BARY':  # BARY gg ONLY has DM instead of A in filename
                filename = indir + 'Zprime' + str(z) + 'DM' + str(a) + '.json'
            scale = 1.
            if dowgt: scale = scaleXS(model, z, a)
            if os.path.isfile(filename) and scale != "99999":
                with open(filename) as jsonfile:
                    data = json.load(jsonfile)
                    for key in data:  # fill plots from json
                        limitPlot.SetBinContent(
                            limitPlot.GetXaxis().FindBin(float(z)),
                            limitPlot.GetYaxis().FindBin(float(a)),
                            float(scale) * data[key][u'exp0'])
                        limitPlotUp.SetBinContent(
                            limitPlotUp.GetXaxis().FindBin(float(z)),
                            limitPlot.GetYaxis().FindBin(float(a)),
                            float(scale) * data[key][u'exp+1'])
                        limitPlotDown.SetBinContent(
                            limitPlotDown.GetXaxis().FindBin(float(z)),
                            limitPlot.GetYaxis().FindBin(float(a)),
                            float(scale) * data[key][u'exp-1'])
                        limitPlotUp2.SetBinContent(
                            limitPlotUp2.GetXaxis().FindBin(float(z)),
                            limitPlot.GetYaxis().FindBin(float(a)),
                            float(scale) * data[key][u'exp+2'])
                        limitPlotDown2.SetBinContent(
                            limitPlotDown2.GetXaxis().FindBin(float(z)),
                            limitPlot.GetYaxis().FindBin(float(a)),
                            float(scale) * data[key][u'exp-2'])
                        limitPlotObs.SetBinContent(
                            limitPlotObs.GetXaxis().FindBin(float(z)),
                            limitPlot.GetYaxis().FindBin(float(a)),
                            float(scale) * data[key][u'obs'])

    # --- average plots to make smooth contours
    fillAvg(limitPlotObs, A, Z, doFillAvgLow, False, False)
    fillAvg(limitPlotObs, A, Z, False, doFillAvgHigh, False)
    fillAvg(limitPlotObs, A, Z, False, False, doFillAvgRest)
    if doFillAvgAll:
        fillAvg(limitPlot, A, Z, doFillAvgLow, False, False)
        fillAvg(limitPlotUp, A, Z, doFillAvgLow, False, False)
        fillAvg(limitPlotDown, A, Z, doFillAvgLow, False, False)
        fillAvg(limitPlotUp2, A, Z, doFillAvgLow, False, False)
        fillAvg(limitPlotDown2, A, Z, doFillAvgLow, False, False)
        fillAvg(limitPlot, A, Z, False, doFillAvgHigh, False)
        fillAvg(limitPlotUp, A, Z, False, doFillAvgHigh, False)
        fillAvg(limitPlotDown, A, Z, False, doFillAvgHigh, False)
        fillAvg(limitPlotUp2, A, Z, False, doFillAvgHigh, False)
        fillAvg(limitPlotDown2, A, Z, False, doFillAvgHigh, False)
        fillAvg(limitPlot, A, Z, False, False, doFillAvgRest)
        fillAvg(limitPlotUp, A, Z, False, False, doFillAvgRest)
        fillAvg(limitPlotDown, A, Z, False, False, doFillAvgRest)
        fillAvg(limitPlotUp2, A, Z, False, False, doFillAvgRest)
        fillAvg(limitPlotDown2, A, Z, False, False, doFillAvgRest)

    # --- axis labels
    limitPlotAxis.GetXaxis().SetTitle("m_{Z'} [GeV]")
    limitPlotObs.GetZaxis().SetTitle("#sigma_{95% CL}/#sigma_{th}")
    if model == "2HDM": limitPlotAxis.GetYaxis().SetTitle("m_{A} [GeV]")
    if model == "BARY": limitPlotAxis.GetYaxis().SetTitle("m_{#chi} [GeV]")

    # --- clone obs to get contour
    limitPlotObsCopy = limitPlotObs.Clone()

    # --- set up min and max of z axis
    limitPlotObs.SetMaximum(100)
    limitPlotObs.SetMinimum(0.3)
    # --- set range of x and y axis
    if model == "BARY": limitPlotObs.GetXaxis().SetRangeUser(10, 2001)
    if model == "BARY": limitPlotObs.GetYaxis().SetRangeUser(1, 1001)
    if model == "2HDM": limitPlotObs.GetXaxis().SetRangeUser(450, 2000)
    if model == "2HDM": limitPlotObs.GetYaxis().SetRangeUser(300, 700)

    # --- style plot
    limitPlotObs.GetYaxis().SetTitleOffset(0.95)  # format axis labels
    limitPlotObs.GetZaxis().SetTitleOffset(0.95)
    limitPlotObs.GetXaxis().SetLabelSize(0.035)  # format axis ticks
    limitPlotObs.GetYaxis().SetLabelSize(0.035)
    if model == "2HDM": limitPlotAxis.GetXaxis().SetNdivisions(9)
    if model == "2HDM": limitPlotAxis.GetYaxis().SetNdivisions(8)
    if model == "BARY": limitPlotAxis.GetXaxis().SetNdivisions(10)
    if model == "BARY": limitPlotAxis.GetYaxis().SetNdivisions(16)

    # --- get and style each contour
    # 1 sigma up
    limitPlotUp.SetMinimum(1)
    limitPlotUp.SetContour(1)
    limitPlotUp.SetLineWidth(1)
    # 1 sigma down
    limitPlotDown.SetMinimum(1)
    limitPlotDown.SetContour(1)
    limitPlotDown.SetLineWidth(1)
    # observed
    limitPlotObs.SetLineWidth(3)
    limitPlotObs.SetLineColor(2)
    limitPlotObsCopy.SetMinimum(1)
    limitPlotObsCopy.SetContour(1)
    limitPlotObsCopy.SetLineWidth(3)
    limitPlotObsCopy.SetLineColor(2)
    # expected
    limitPlot.SetMinimum(1)
    limitPlot.SetContour(1)
    limitPlot.SetLineStyle(7)
    limitPlot.SetLineWidth(3)

    # --- smooth
    if dosmth:
        limitPlot.Smooth(1, smthfnc)
        #limitPlotObs.Smooth(1,smthfnc)
        limitPlotObsCopy.Smooth(1, smthfnc)
        limitPlotUp.Smooth(1, smthfnc)
        limitPlotDown.Smooth(1, smthfnc)

    # --- draw plots
    limitPlotAxis.Draw("COLZ")
    limitPlotObs.Draw("COLZ SAME")
    limitPlotUp.Draw("CONT3 SAME")
    limitPlotDown.Draw("CONT3 SAME")
    limitPlotObsCopy.Draw("CONT3 SAME")
    limitPlot.Draw("CONT3 SAME")

    # --- legend and extra text box
    x1 = 0.18
    y1 = 0.65
    x2 = x1 + 0.45
    y2 = y1 + 0.25
    # --- latex
    if model == "2HDM": txt1 = "#bf{Z'-2HDM}"
    if model == "BARY": txt1 = "#bf{Baryonic Z'}"
    txt1 += "#bf{, Z' #rightarrow DM + h"
    if which == 'gg': txt1 += "(#gamma#gamma)}"
    if which == 'tt': txt1 += "(#tau#tau)} "
    if which == 'combo': txt1 += "(#gamma#gamma + #tau#tau)}"
    if model == "2HDM":
        txt2 = "#bf{Dirac DM, m_{#chi} = 100 GeV, g_{Z'} = 0.8, g_{#chi} = 1.0}"
    if model == "BARY": txt2 = "#bf{Dirac DM, g_{q} = 0.25, g_{#chi} = 1.0 }"
    txt = ROOT.TPaveText(x1, y1 + 0.15, x2, y2, "NDC")
    txt.AddText(txt1)
    txt.AddText(txt2)
    txt.SetFillColor(0)
    txt.SetTextAlign(12)
    txt.SetTextSize(0.03)
    txt.Draw("SAME")
    # --- legend
    leg = ROOT.TLegend(x1, y1, x2, y1 + 0.15)
    #leg.SetHeader(text)
    leg.SetBorderSize(0)
    leg.SetFillColor(0)
    #leg.SetFillStyle(0)
    leg.SetTextFont(42)
    leg.SetTextSize(0.030)
    leg.AddEntry(limitPlotObs, "Observed 95% CL", "L")
    leg.AddEntry(limitPlot, "Expected 95% CL", "L")
    leg.AddEntry(limitPlotUp, "#pm 1 s.d.", "L")
    leg.Draw()

    canv.cd()
    canv.Update()
    CMS_lumi(canv, 4, 0)
    canv.RedrawAxis()
    canv.Update()

    # --- save
    outname = outdir + 'contours_'
    if do90: outname += '90CL'
    else: outname += '95CL'
    outname += '_' + model + '_' + which + '.root'
    outfile = ROOT.TFile(outname, 'RECREATE')
    outfile.cd()
    limitPlot.Write()
    limitPlotObs.Write()
    if do90:
        canv.Print(outdir + 'limits2D_' + model + '_' + which + '_90CL.pdf')
        canv.Print(outdir + 'limits2D_' + model + '_' + which + '_90CL.png')
    else:
        canv.Print(outdir + 'limits2D_' + model + '_' + which + '.pdf')
        canv.Print(outdir + 'limits2D_' + model + '_' + which + '.png')