def plotSpectrum(): useMalbek = False eName = "energy_keV" eName = "trapENFCal" # load workspace f = TFile("./data/fitWorkspace.root") fitWorkspace = f.Get("fitWorkspace") fData = fitWorkspace.allData().front() fitResult = fitWorkspace.allGenericObjects().front() nPars = fitResult.floatParsFinal().getSize() fEnergy = fitWorkspace.var(eName) modelPDF = fitWorkspace.pdf("model") # fitWorkspace.Print() # get a list of pdf names pdfNames = [] pdfList = fitWorkspace.allPdfs() # goddamn RooArgSet itr = pdfList.createIterator() var = itr.Next() while var: name = var.GetName() if "ext" in name: pdfNames.append(name) var = itr.Next() pdfNames = sorted(pdfNames) # -- create spectrum rooplot -- nCol = float(gStyle.GetNumberOfColors()) binSize = 0.2 nBins = int((eHi - eLo) / binSize + 0.5) fSpec = fEnergy.frame(RF.Range(eLo, eHi), RF.Bins(nBins)) fData.plotOn(fSpec) modelPDF.plotOn(fSpec, RF.LineColor(ROOT.kRed), RF.Name("FullModel")) chiSquare = fSpec.chiSquare(nPars) # draw components leg = TLegend(0.83, 0.1, 0.97, 0.9) leg.AddEntry(fSpec.findObject("FullModel"), "model #chi^{2}=%.3f" % chiSquare, "l") for idx in range(len(pdfNames)): name = pdfNames[idx] lineCol = gStyle.GetColorPalette(int(nCol / len(pdfNames) * idx)) modelPDF.plotOn(fSpec, RF.Components(name), RF.LineColor(lineCol), RF.Name(name)) plotName = name if "ext-" in name: plotName = plotName[4:] leg.AddEntry(fSpec.findObject(name), plotName, "l") # create normalized residual ("pull") rooplot # (draw full model again so residuals are calculated against it) modelPDF.plotOn(fSpec, RF.LineColor(ROOT.kRed), RF.Name("FullModel")) res = fSpec.pullHist() fRes = fEnergy.frame(RF.Title(" ")) fRes.addPlotable(res, "P") # -- print fit results -- print "-- SPECFIT RESULTS -- " print "%-10s = %.3f" % ("chiSq", chiSquare) fitValsFinal = getRooArgDict(fitResult.floatParsFinal()) for name in sorted(fitValsFinal): fitVal = fitValsFinal[name] if "amp-" in name: error = fitWorkspace.var(name).getError() print "%-10s = best %-7.3f error %.3f (w/o profile)" % ( name, fitVal, error) elif "mu-" in name: # compare the energy offset pkName = name[3:] pct = 100 * (1 - fitVal / pkList[pkName]) print "%-10s : fit %-6.3f lit %-6.3f (%.3f%%)" % ( name, fitVal, pkList[pkName], pct) elif "sig-" in name: # compare the sigma difference pkName = name[4:] pct = 100 * (1 - fitVal / getSigma(pkList[pkName])) print "%-10s : fit %-6.3f func %-6.3f (%.3f%%)" % ( name, fitVal, getSigma(pkList[pkName]), pct) else: print "%s = %.4f" % (name, fitVal) continue # -- make spectrum plot w/ residual, w/ all the formatting crap -- gStyle.SetOptStat(0) gStyle.SetPalette( ROOT.kRainBow) # https://root.cern.ch/doc/master/classTColor.html#C06 c = TCanvas("c", "Bob Ross's Canvas", 1400, 1000) p1 = TPad("p1", "spectrum", 0., 0.3, 1., 1.) p2 = TPad("p2", "residual", 0., 0., 1., 0.3) p1.Draw() p2.Draw() p1.SetRightMargin(0.2) p1.SetBottomMargin(0.) p2.SetRightMargin(0.2) p2.SetTopMargin(0.) p2.SetBottomMargin(0.4) p1.cd() # p1.SetGrid() fSpec.SetTitle("") fSpec.GetXaxis().SetLabelSize(0.) fSpec.GetYaxis().SetLabelSize(0.05) fSpec.GetYaxis().SetTitleSize(0.06) fSpec.GetYaxis().SetTitleOffset(0.6) fSpec.GetYaxis().SetTitle("Events / (%.1f keV)" % binSize) fSpec.Draw() leg.Draw("same") p2.cd() fRes.GetXaxis().SetLabelSize(0.1) fRes.GetYaxis().SetLabelSize(0.1) fRes.GetXaxis().SetTitleSize(0.15) fRes.GetYaxis().SetTitle("Normalized Resid.") fRes.GetYaxis().SetTitleOffset(0.3) fRes.GetYaxis().SetTitleSize(0.1) fRes.Draw() zeroLine = ROOT.TLine(eLo, 0., eHi, 0.) zeroLine.SetLineColor(ROOT.kBlack) zeroLine.Draw("same") c.Print("./plots/spectrum.pdf") # -- correlation matrix -- c2 = TCanvas("c2", "Bob Ross's Canvas", 1100, 800) c2.SetLeftMargin(0.15) c2.SetRightMargin(0.12) gStyle.SetPalette(ROOT.kDarkBodyRadiator) corrMat = fitResult.correlationHist("Correlation Matrix") corrMat.SetTitle("") corrMat.GetXaxis().SetLabelSize(0.02) corrMat.Draw("colz") c2.Print("./plots/corrMatrix.pdf")
def fitModel(makePlots=False): from ROOT import TFile, TH1D, TCanvas, TLegend, gStyle # === load data into workspace === tf = TFile("%s/data/latDS%s.root" % (dsi.latSWDir, ''.join([str(d) for d in dsList]))) tt = tf.Get("skimTree") tCut = "isEnr==1" if enr is True else "isEnr==0" hitE = ROOT.RooRealVar("trapENFCal", "Energy", eLo, eHi, "keV") hEnr = ROOT.RooRealVar("isEnr", "isEnr", 0, 1, "") # hitW = ROOT.RooRealVar("weight", "weight", 1, 1000, "") fData = ROOT.RooDataSet("data", "data", tt, ROOT.RooArgSet(hitE, hEnr), tCut) # fData = ROOT.RooDataSet("data", "data", tt, ROOT.RooArgSet(hitE, hEnr, hitW), "", "weight") fitWorkspace = ROOT.RooWorkspace("fitWorkspace", "Fit Workspace") getattr(fitWorkspace, 'import')(hitE) getattr(fitWorkspace, 'import')(fData) # getattr(fitWorkspace,'import')(fWeight) tf2 = TFile("%s/data/specPDFs.root" % dsi.latSWDir) pdfList = ROOT.RooArgList("shapes") # === background model === hList = getHistList() bkgList = [] for h in hList: hB, name = h[0], h[1] pars = bkgVals[name] bkN = ROOT.RooRealVar("amp-" + name, "amp-" + name, pars[1], pars[2], pars[3]) bkDH = ROOT.RooDataHist("dh-" + name, "dh-" + name, ROOT.RooArgList(hitE), RF.Import(hB)) bkPDF = ROOT.RooHistPdf("pdf-" + name, "pdf-" + name, ROOT.RooArgSet(hitE), bkDH, 2) bkExt = ROOT.RooExtendPdf("ext-" + name, "ext-" + name, bkPDF, bkN) hitE.setRange(eLo, eHi) bkgList.append([bkExt, name, bkN, bkDH, bkPDF]) # this is separate b/c all the RooVars have to remain in memory for bkg in bkgList: pdfList.add(bkg[0]) model = ROOT.RooAddPdf("model", "total PDF", pdfList) if makePlots: # === make a rooplot of the initial guess parameters, don't let roofit normalize automatically leg = TLegend(0.83, 0.5, 0.97, 0.9) gStyle.SetPalette(ROOT.kRainBow) nCol = float(gStyle.GetNumberOfColors()) fSpec = hitE.frame(RF.Range(eLo, eHi), RF.Bins(nB)) fData.plotOn(fSpec) # wouter's note: DON'T DELETE # "the default behavior is when you plot a p.d.f. on an empty frame it is # plotted with unit normalization. When you plot it on a frame with data in # it, it will normalize to the number of events in that dataset." # (then after you do a fit, the pdf normalization changes again ...) nData = fData.numEntries() nTot = 0 for i, ext in enumerate(bkgList): extPDF, name = ext[0], ext[1] col = gStyle.GetColorPalette(int(nCol / len(bkgList) * i)) extPDF.plotOn( fSpec, RF.LineColor(col), RF.Normalization(bkgVals[name][1], ROOT.RooAbsReal.Raw), RF.Name(name)) leg.AddEntry(fSpec.findObject(name), name, "l") nTot += bkgVals[name][1] model.plotOn(fSpec, RF.LineColor(ROOT.kRed), RF.Name("fmodel"), RF.Normalization(nTot, ROOT.RooAbsReal.Raw)) leg.AddEntry(fSpec.findObject("fmodel"), "Full Model", "l") c = TCanvas("c", "c", 1400, 1000) fSpec.SetTitle("") fSpec.Draw() leg.Draw("same") c.Print("%s/plots/sf-before.pdf" % dsi.latSWDir) c.Clear() # === make a pyplot of the same thing tCut = "isEnr" if enr else "!isEnr" tCut += " && trapENFCal >= %.1f && trapENFCal <= %.1f" % (eLo, eHi) n = tt.Draw("trapENFCal", tCut, "goff") trapE = tt.GetV1() trapE = [trapE[i] for i in range(n)] x, hData = wl.GetHisto(trapE, eLo, eHi, epb) hErr = np.asarray([np.sqrt(h) for h in hData]) plt.errorbar(x, hData, yerr=hErr, c='k', ms=5, linewidth=0.5, fmt='.', capsize=1, zorder=1) # pretty convincing rooplot fake cmap = plt.cm.get_cmap('jet', len(hList) + 2) pdfs = [] for i, h in enumerate(hList): name = h[1] x, y, xpb = wl.npTH1D(h[0]) x, y = normPDF(x, y, eLo, eHi) nCts = bkgVals[h[1]][1] # the initial guess if abs(nCts - np.sum(y * nCts)) > 2: print("norm error, %s nCts %d y*nCts %d" % (name, nCts, np.sum(y * nCts))) # plt.step(x, y * nCts / xpb, c=cmap(i), lw=2, label="%s init cts: %d" % (name, nCts)) # plot the histo xS = np.arange(eLo, eHi, 0.001) # plot a smoothed version yS = spline(x - xpb / 2, y, xS) plt.plot(xS, yS * nCts / xpb, c=cmap(i), lw=2, label="%s init cts: %d" % (name, nCts)) pdfs.append([x, y, xpb, nCts]) xT, yT = getTotalModel(pdfs, eLo, eHi, epb, smooth=True) plt.step(xT, yT / epb, c='r', lw=2, label="Raw (no eff. corr): %d cts" % nTot) plt.xlabel("Energy (keV)", ha='right', x=1) plt.ylabel("Counts / %.1f keV" % epb, ha='right', y=1) plt.legend(loc=1, fontsize=12) plt.xlim(eLo, eHi) plt.ylim(ymin=0) plt.tight_layout() # plt.show() plt.savefig("%s/plots/sf-before-mpl.pdf" % dsi.latSWDir) # === alright, now run the fit and output to the workspace minimizer = ROOT.RooMinimizer( model.createNLL(fData, RF.NumCPU(2, 0), RF.Extended(True))) minimizer.setPrintLevel(-1) minimizer.setStrategy(2) minimizer.migrad() fitResult = minimizer.save() # according to the internet, covQual==3 is a good indicator that it converged print("Fitter is done. Fit Cov Qual:", fitResult.covQual()) # save workspace to a TFile getattr(fitWorkspace, 'import')(fitResult) getattr(fitWorkspace, 'import')(model) tf3 = TFile("%s/data/fitWorkspace.root" % dsi.latSWDir, "RECREATE") fitWorkspace.Write() tf3.Close()
def plotFit(plotRate=False): from ROOT import TFile, TCanvas, TH1D, TLegend, gStyle f = TFile("%s/data/fitWorkspace.root" % dsi.latSWDir) fitWorkspace = f.Get("fitWorkspace") fData = fitWorkspace.allData().front() fitResult = fitWorkspace.allGenericObjects().front() nPars = fitResult.floatParsFinal().getSize() hitE = fitWorkspace.var("trapENFCal") model = fitWorkspace.pdf("model") nData = fData.numEntries() fCov = fitResult.covQual() # fitWorkspace.Print() # === get fit results: {name : [nCts, err]} === fitVals = {} for i in range(nPars): fp = fitResult.floatParsFinal() name = fp.at(i).GetName() fitVal, fitErr = fp.at(i).getValV(), fp.at(i).getError() if "amp" in name: fitVals[name.split('-')[1]] = [fitVal, fitErr] # for f in fitVals: # print(f, fitVals[f]) # === make a rooplot of the fit === leg = TLegend(0.83, 0.5, 0.97, 0.9) gStyle.SetPalette(ROOT.kRainBow) nCol = float(gStyle.GetNumberOfColors()) fSpec = hitE.frame(RF.Range(eLo, eHi), RF.Bins(nB)) fData.plotOn(fSpec) for i, name in enumerate(bkgModel): pdfName = "ext-" + name col = gStyle.GetColorPalette(int(nCol / len(bkgModel) * i)) model.plotOn(fSpec, RF.Components(pdfName), RF.LineColor(col), RF.LineStyle(ROOT.kDashed), RF.Name(name)) leg.AddEntry(fSpec.findObject(name), name, "l") chiSquare = fSpec.chiSquare(nPars) model.plotOn(fSpec, RF.LineColor(ROOT.kRed), RF.Name("fmodel")) leg.AddEntry(fSpec.findObject("fmodel"), "Full Model, #chi^{2}/NDF = %.3f" % chiSquare, "l") c = TCanvas("c", "c", 1400, 1000) fSpec.SetTitle("") fSpec.Draw() leg.Draw("same") c.Print("%s/plots/sf-after.pdf" % dsi.latSWDir) c.Clear() # === duplicate the rooplot in matplotlib === plt.close() tf = TFile("%s/data/latDS%s.root" % (dsi.latSWDir, ''.join([str(d) for d in dsList]))) tt = tf.Get("skimTree") tCut = "isEnr==1" if enr is True else "isEnr==0" tCut = "isEnr" if enr else "!isEnr" tCut += " && trapENFCal >= %.1f && trapENFCal <= %.1f" % (eLo, eHi) n = tt.Draw("trapENFCal", tCut, "goff") trapE = tt.GetV1() trapE = [trapE[i] for i in range(n)] x, hData = wl.GetHisto(trapE, eLo, eHi, epb) if plotRate: hErr = np.asarray([np.sqrt(h) / detExp for h in hData]) # statistical error plt.errorbar(x, hData / detExp, yerr=hErr, c='k', ms=5, linewidth=0.5, fmt='.', capsize=1, zorder=1) else: hErr = np.asarray([np.sqrt(h) for h in hData]) # statistical error plt.errorbar(x, hData, yerr=hErr, c='k', ms=5, linewidth=0.5, fmt='.', capsize=1, zorder=1) # get the list of histograms and plot the components hList = getHistList() cmap = plt.cm.get_cmap('jet', len(hList) + 2) pdfs, pdfsCorr, nTot, nTotC = [], [], 0, 0 for i, h in enumerate(hList): name = h[1] x, y, xpb = wl.npTH1D(h[0]) x, y = normPDF(x, y, eLo, eHi) nCts, nErr = fitVals[name] # final result yc = nCts * getEffCorr(x, y, inv=True) if abs(nCts - np.sum(y * nCts)) > 2: print("norm error, %s nCts %d y*nCts %d" % (name, nCts, np.sum(y * nCts))) # plt.step(x, y * nCts * (epb/xpb), c=cmap(i), lw=2, label="%s cts: %.2f±%.2f" % (name, nCts, nErr)) # plot the histo xS = np.arange(eLo, eHi, 0.001) # plot a smoothed version yS = spline(x - xpb / 2, y, xS) if plotRate: plt.plot(xS, yS * nCts * (epb / xpb) / detExp, "--", c=cmap(i), lw=2, label="%s %.2f ± %.2f" % (name, nCts / detExp, nErr / detExp)) else: plt.plot(xS, yS * nCts * (epb / xpb), c=cmap(i), lw=2, label="%s cts: %d" % (name, nCts)) pdfs.append([x, y, xpb, nCts]) pdfsCorr.append([x, yc, xpb, nCts]) nTot += nCts nTotC += np.sum( yc ) # reverse the efficiency correction to get the "true" number of counts # get the fit model, and the efficiency-corrected final model xT, yT = getTotalModel(pdfs, eLo, eHi, epb, smooth=True) xTc, yTc = getTotalModel(pdfsCorr, eLo, eHi, epb, smooth=True, amp=False) if plotRate: plt.plot(xT, yT / detExp, 'r', lw=2, alpha=0.7, label="Rate: %.2f" % (nTot / detExp)) plt.plot(xTc, yTc / detExp, c='m', lw=3, alpha=0.7, label="Eff.corr: %.2f " % (nTotC / detExp)) plt.ylabel("Counts / keV / kg-d", ha='right', y=1) else: plt.plot(xT, yT, 'r', lw=2, alpha=0.7, label="Raw (no eff. corr): %d cts" % nTot) plt.plot(xTc, yTc, c='m', lw=3, alpha=0.7, label="Efficiency corrected: %d cts" % nTotC) plt.ylabel("Counts / %.1f keV" % epb, ha='right', y=1) plt.xlabel("Energy (keV)", ha='right', x=1) plt.legend(loc=1, fontsize=12) plt.xticks(np.arange(int(eLo) - 1, eHi + 1, 5)) plt.xlim(eLo, eHi) plt.ylim(ymin=0) plt.tight_layout() # plt.show() plt.savefig("%s/plots/sf-after-mpl.pdf" % dsi.latSWDir)
def plotFit(): axPeak = 2.464 # load workspace f = TFile("./data/splitWorkspace.root") fitWorkspace = f.Get("fitWorkspace") fData = fitWorkspace.allData().front() fitResult = fitWorkspace.allGenericObjects().front() nPars = fitResult.floatParsFinal().getSize() fEnergy = fitWorkspace.var("energy_keV") modelPDF = fitWorkspace.pdf("model") # fitWorkspace.Print() pdfNames = ["ext-axion"] # -- create spectrum rooplot -- nCol = float(gStyle.GetNumberOfColors()) binSize = 0.04 nBins = int((eHi - eLo) / binSize + 0.5) fSpec = fEnergy.frame(RF.Range(eLo, eHi), RF.Bins(nBins)) fData.plotOn(fSpec) modelPDF.plotOn(fSpec, RF.LineColor(ROOT.kRed), RF.Name("FullModel")) chiSquare = fSpec.chiSquare(nPars) modelPDF.plotOn(fSpec, RF.Components(pdfNames[0]), RF.LineColor(ROOT.kBlue), RF.Name(pdfNames[0])) # -- make a fake Gaussian -- # gaus = ROOT.TF1("g","gaus",-3, eHi) # gaus.SetParameters(10., axPeak, getSigma(axPeak)) # gaus.SetParameter(0,10.) # gaus.SetParameter(1, axPeak) # gaus.SetParameter(2, getSigma(axPeak)) # erf # rrvGaus = ROOT.RooRealVar("ax","ax",-3.,3.) # rarGaus = RF.bindFunction("gaus", ROOT.TMath.Erf, rrvGaus) # rarGaus.Print() # frame2 = rrvGaus.frame(RF.Title("mygaus")) # rarGaus.plotOn(frame2, RF.LineColor(ROOT.kGreen), RF.LineStyle(ROOT.kDashed), RF.Name("axGaus")) # c0 = TCanvas("c0","",800,600) # frame2.Draw() # c0.Print("./plots/testGaus.pdf") # -- print fit results -- print "-- SHIFTFIT RESULTS -- " print "%-10s = %.3f" % ("chiSq", chiSquare) fitValsFinal = getRooArgDict(fitResult.floatParsFinal()) bkgVal = 0. for name in sorted(fitValsFinal): fitVal = fitValsFinal[name] if "amp-" in name: error = fitWorkspace.var(name).getError() print "%-10s = best %-7.3f error %.3f (w/o profile)" % ( name, fitVal, error) elif "mu-" in name: # compare the energy offset pkName = name[3:] pct = 100 * (1 - fitVal / axPeak) print "%-10s : fit %-6.3f lit %-6.3f (%.3f%%)" % (name, fitVal, axPeak, pct) elif "sig-" in name: # compare the sigma difference pkName = name[4:] pct = 100 * (1 - fitVal / getSigma(axPeak)) print "%-10s : fit %-6.3f func %-6.3f (%.3f%%)" % ( name, fitVal, getSigma(axPeak), pct) else: print "%s = %.4f (%.4f)" % (name, fitVal, fitVal / nBins) bkgVal = fitVal / nBins continue # -- make spectrum plot -- c = TCanvas("c", "Bob Ross's Canvas", 1400, 1000) c.SetRightMargin(0.2) fSpec.SetTitle(" ") fSpec.Draw() ymax = fSpec.GetMaximum() l1 = ROOT.TLine(axPeak, 0., axPeak, ymax) l1.SetLineColor(ROOT.kBlue) l1.SetLineWidth(2) l1.Draw("same") leg = TLegend(0.83, 0.6, 0.97, 0.9) leg.AddEntry(fSpec.findObject("FullModel"), "model #chi^{2}=%.3f" % chiSquare, "l") leg.AddEntry(fSpec.findObject("FullModel"), "cts/bin=%.2f" % bkgVal, "") leg.AddEntry(fSpec.findObject(pdfNames[0]), "axion gaussian", "l") leg.AddEntry(l1, "axion-%.2f" % axPeak, "l") leg.Draw("same") c.Print("./plots/shiftFit.pdf") # -- get FC Limit -- # Calculate the confidence interval for the axion peak, which is too low to use profile likelihood. # TFeldmanCousins version: # https://root.cern.ch/root/html/tutorials/math/FeldmanCousins.C.html # RooStats version: # https://root.cern.ch/root/html/tutorials/roostats/StandardFeldmanCousinsDemo.C.html # FC Paper: https://arxiv.org/pdf/physics/9711021.pdf f = TFeldmanCousins(0.95) N_obs = 1. N_bkg = bkgVal / 3. ul = f.CalculateUpperLimit(N_obs, N_bkg) ll = f.GetLowerLimit() print "For %.2f events observed, and %.2f background events," % (N_obs, N_bkg) print "F-C method gives UL: %.2f and LL %.2f (90%% CL)" % (ul, ll)