Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
rs102_hypotestwithshapes.py
Go to the documentation of this file.
1# \file
2# \ingroup tutorial_roostats
3# \notebook -js
4# A typical search for a new particle by studying an invariant mass distribution
5#
6# The macro creates a simple signal model and two background models,
7# which are added to a RooWorkspace.
8# The macro creates a toy dataset, and then uses a RooStats
9# ProfileLikleihoodCalculator to do a hypothesis test of the
10# background-only and signal+background hypotheses.
11# In this example, shape uncertainties are not taken into account, but
12# normalization uncertainties are.
13#
14# \macro_image
15# \macro_output
16# \macro_code
17#
18# \author Kyle Cranmer (C++ version), and P. P. (Python translation)
19
20# use this order for safety on library loading
21import ROOT
22
23
24# see below for implementation
25# AddModel(RooWorkspace )
26# AddData(RooWorkspace )
27# DoHypothesisTest(RooWorkspace )
28# MakePlots(RooWorkspace )
29
30
31# ____________________________________
32def AddModel(wks):
33 # wks has to be a Workspace
34
35 # Make models for signal (Higgs) and background (Z+jets and QCD)
36 # In real life, this part requires an intelligent modeling
37 # of signal and background -- this is only an example.
38
39 # set range of observable
40 lowRange = 60
41 highRange = 200
42
43 # make a RooRealVar for the observable
44 invMass = ROOT.RooRealVar("invMass", "M_inv", lowRange, highRange, "GeV")
45
46 # --------------------------------------
47 # make a simple signal model.
48 mH = ROOT.RooRealVar("mH", "Higgs Mass", 130, 90, 160)
49 sigma1 = ROOT.RooRealVar("sigma1", "Width of Gaussian", 12.0, 2, 100)
50 sigModel = ROOT.RooGaussian("sigModel", "Signal Model", invMass, mH, sigma1)
51 # we will test this specific mass point for the signal
52 mH.setConstant()
53 # and we assume we know the mass resolution
54 sigma1.setConstant()
55
56 # --------------------------------------
57 # make zjj model. Just like signal model
58 mZ = ROOT.RooRealVar("mZ", "Z Mass", 91.2, 0, 100)
59 sigma1_z = ROOT.RooRealVar("sigma1_z", "Width of Gaussian", 10.0, 6, 100)
60 zjjModel = ROOT.RooGaussian("zjjModel", "Z+jets Model", invMass, mZ, sigma1_z)
61 # we know Z mass
62 mZ.setConstant()
63 # assume we know resolution too
64 sigma1_z.setConstant()
65
66 # --------------------------------------
67 # make QCD model
68 a0 = ROOT.RooRealVar("a0", "a0", 0.26, -1, 1)
69 a1 = ROOT.RooRealVar("a1", "a1", -0.17596, -1, 1)
70 a2 = ROOT.RooRealVar("a2", "a2", 0.018437, -1, 1)
71 a3 = ROOT.RooRealVar("a3", "a3", 0.02, -1, 1)
72 qcdModel = ROOT.RooChebychev("qcdModel", "A Polynomial for QCD", invMass, [a0, a1, a2])
73
74 # let's assume this shape is known, but the normalization is not
75 a0.setConstant()
76 a1.setConstant()
77 a2.setConstant()
78
79 # --------------------------------------
80 # combined model
81
82 # Setting the fraction of Zjj to be 40% for initial guess.
83 fzjj = ROOT.RooRealVar("fzjj", "fraction of zjj background events", 0.4, 0.0, 1)
84
85 # Set the expected fraction of signal to 20%.
86 fsigExpected = ROOT.RooRealVar("fsigExpected", "expected fraction of signal events", 0.2, 0.0, 1)
87 fsigExpected.setConstant() # use mu as main parameter, so fix this.
88
89 # Introduce mu: the signal strength in units of the expectation.
90 # eg. mu = 1 is the SM, mu = 0 is no signal, mu=2 is 2x the SM
91 mu = ROOT.RooRealVar("mu", "signal strength in units of SM expectation", 1, 0.0, 2)
92
93 # Introduce ratio of signal efficiency to nominal signal efficiency.
94 # This is useful if you want to do limits on cross section.
95 ratioSigEff = ROOT.RooRealVar("ratioSigEff", "ratio of signal efficiency to nominal signal efficiency", 1.0, 0.0, 2)
96 ratioSigEff.setConstant(True)
97
98 # finally the signal fraction is the product of the terms above.
99 fsig = ROOT.RooProduct("fsig", "fraction of signal events", [mu, ratioSigEff, fsigExpected])
100
101 # full model
102 model = ROOT.RooAddPdf("model", "sig+zjj+qcd background shapes", [sigModel, zjjModel, qcdModel], [fsig, fzjj])
103
104 # interesting for debugging and visualizing the model
105 # model.printCompactTree("","fullModel.txt");
106 # model.graphVizTree("fullModel.dot");
107
108 wks.Import(model)
109
110
111# ____________________________________
112def AddData(wks):
113 # wks has to be a Workspace
114 # Add a toy dataset
115
116 nEvents = 150
117 model = wks.pdf("model")
118 invMass = wks.var("invMass")
119
120 data = model.generate(invMass, nEvents)
121
122 wks.Import(data, Rename="data")
123
124
125# ____________________________________
126def DoHypothesisTest(wks):
127
128 # Use a RooStats ProfileLikleihoodCalculator to do the hypothesis test.
129 model = ROOT.RooFit.ModelConfig()
130 model.SetWorkspace(wks)
131 model.SetPdf("model")
132
133 # plc.SetData("data");
134
135 plc = ROOT.RooStats.ProfileLikelihoodCalculator()
136 plc.SetData((wks.data("data")))
137
138 # here we explicitly set the value of the parameters for the null.
139 # We want no signal contribution, eg. mu = 0
140 mu = wks.var("mu")
141 # nullParams = RooArgSet("nullParams")
142 # nullParams.addClone(mu)
143 poi = ROOT.RooArgSet(mu)
144 nullParams = poi.snapshot()
145 nullParams.setRealValue("mu", 0)
146
147 # plc.SetNullParameters(*nullParams);
148 plc.SetModel(model)
149 # NOTE: using snapshot will import nullparams
150 # in the WS and merge with existing "mu"
151 # model.SetSnapshot(*nullParams);
152
153 # use instead setNuisanceParameters
154 plc.SetNullParameters(nullParams)
155
156 # We get a HypoTestResult out of the calculator, and we can query it.
157 htr = plc.GetHypoTest()
158 print(f"-------------------------------------------------")
159 print(f"The p-value for the null is ", htr.NullPValue())
160 print(f"Corresponding to a significance of ", htr.Significance())
161 print(f"-------------------------------------------------\n\n")
162
163
164# ____________________________________
165def MakePlots(wks):
166 # wks has to be a RooWorkspace
167
168 # Make plots of the data and the best fit model in two cases:
169 # first the signal+background case
170 # second the background-only case.
171
172 # get some things out of workspace
173 model = wks.pdf("model")
174 sigModel = wks.pdf("sigModel")
175 zjjModel = wks.pdf("zjjModel")
176 qcdModel = wks.pdf("qcdModel")
177
178 mu = wks.var("mu")
179 invMass = wks.var("invMass")
180 data = wks.data("data")
181
182 # --------------------------------------
183 # Make plots for the Alternate hypothesis, eg. let mu float
184
185 mu.setConstant(False)
186
187 model.fitTo(data, Save=True, Minos=False, Hesse=False, PrintLevel=-1)
188
189 # plot sig candidates, full model, and individual components
190 c1 = ROOT.TCanvas()
191 frame = invMass.frame()
192 data.plotOn(frame)
193 model.plotOn(frame)
194 model.plotOn(frame, Components=sigModel, LineStyle="--", LineColor="r")
195 model.plotOn(frame, Components=zjjModel, LineStyle="--", LineColor="b")
196 model.plotOn(frame, Components=qcdModel, LineStyle="--", LineColor="g")
197
198 frame.SetTitle("An example fit to the signal + background model")
199 frame.Draw()
200 c1.Update()
201 c1.Draw()
202 c1.SaveAs("rs102_hypotestwithshapes.1.png")
203 # cdata.SaveAs("alternateFit.gif");
204
205 # --------------------------------------
206 # Do Fit to the Null hypothesis. Eg. fix mu=0
207
208 mu.setVal(0) # set signal fraction to 0
209 mu.setConstant(True) # set constant
210
211 model.fitTo(data, Save=True, Minos=False, Hesse=False, PrintLevel=-1)
212
213 # plot signal candidates with background model and components
214 c2 = ROOT.TCanvas()
215 xframe2 = invMass.frame()
216 data.plotOn(xframe2, DataError="SumW2")
217 model.plotOn(xframe2)
218 model.plotOn(xframe2, Components=zjjModel, LineStyle="--", LineColor="b")
219 model.plotOn(xframe2, Components=qcdModel, LineStyle="--", LineColor="g")
220
221 xframe2.SetTitle("An example fit to the background-only model")
222 xframe2.Draw()
223 c2.Update()
224 c2.Draw()
225 c2.SaveAs("rs102_hypotestwithshapes.1.png")
226 # cbkgonly.SaveAs("nullFit.gif");
227
228
229# ____________________________________
231
232 # The main macro.
233
234 # Create a workspace to manage the project.
235 wspace = ROOT.RooWorkspace("myWS")
236
237 # add the signal and background models to the workspace
238 AddModel(wspace)
239
240 # add some toy data to the workspace
241 AddData(wspace)
242
243 # inspect the workspace if you wish
244 # wspace->Print();
245
246 # do the hypothesis test
247 DoHypothesisTest(wspace)
248
249 # make some plots
250 MakePlots(wspace)
251
252 # cleanup
253 del wspace
254
255