Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
rs401d_FeldmanCousins.py
Go to the documentation of this file.
1# \file
2# \ingroup tutorial_roostats
3# \notebook
4# Neutrino Oscillation Example from Feldman & Cousins
5#
6# This tutorial shows a more complex example using the FeldmanCousins utility
7# to create a confidence interval for a toy neutrino oscillation experiment.
8# The example attempts to faithfully reproduce the toy example described in Feldman & Cousins'
9# original paper, Phys.Rev.D57:3873-3889,1998.
10#
11# \macro_image
12# \macro_output
13# \macro_code
14#
15# \author Kyle Cranmer (C++ version), and P. P. (Python translation)
16
17import ROOT
18
19
20def rs401d_FeldmanCousins(doFeldmanCousins=False, doMCMC=True):
21
22 # to time the macro
23 t = ROOT.TStopwatch()
24 t.Start()
25
26 # Taken from Feldman & Cousins paper, Phys.Rev.D57:3873-3889,1998.
27 # e-Print: physics/9711021 (see page 13.)
28 #
29 # Quantum mechanics dictates that the probability of such a transformation is given by the formula
30 # $P (\nu\mu \rightarrow \nu e ) = sin^2 (2\theta) sin^2 (1.27 \Delta m^2 L /E )$
31 # where P is the probability for a $\nu\mu$ to transform into a $\nu e$ , L is the distance in km between
32 # the creation of the neutrino from meson decay and its interaction in the detector, E is the
33 # neutrino energy in GeV, and $\Delta m^2 = |m^2 - m^2 |$ in $(eV/c^2 )^2$ .
34 #
35 # To demonstrate how this works in practice, and how it compares to alternative approaches
36 # that have been used, we consider a toy model of a typical neutrino oscillation experiment.
37 # The toy model is defined by the following parameters: Mesons are assumed to decay to
38 # neutrinos uniformly in a region 600 m to 1000 m from the detector. The expected background
39 # from conventional $\nu e$ interactions and misidentified $\nu\mu$ interactions is assumed to be 100
40 # events in each of 5 energy bins which span the region from 10 to 60 GeV. We assume that
41 # the $\nu\mu$ flux is such that if $P (\nu\mu \rightarrow \nu e ) = 0.01$ averaged over any bin, then that bin
42 # would
43 # have an expected additional contribution of 100 events due to $\nu\mu \rightarrow \nu e$ oscillations.
44
45 # Make signal model model
46 E = ROOT.RooRealVar("E", "", 15, 10, 60, "GeV")
47 L = ROOT.RooRealVar("L", "", 0.800, 0.600, 1.0, "km") # need these units in formula
48 deltaMSq = ROOT.RooRealVar("deltaMSq", "#Delta m^{2}", 40, 1, 300, "eV/c^{2}")
49 sinSq2theta = ROOT.RooRealVar("sinSq2theta", "sin^{2}(2#theta)", 0.006, 0.0, 0.02)
50 # RooRealVar deltaMSq("deltaMSq","#Delta m^{2}",40,20,70,"eV/c^{2}");
51 # RooRealVar sinSq2theta("sinSq2theta","sin^{2}(2#theta)", .006,.001,.01);
52 # PDF for oscillation only describes deltaMSq dependence, sinSq2theta goes into sigNorm
53 oscillationFormula = "std::pow(std::sin(1.27 * x[2] * x[0] / x[1]), 2)"
54 PnmuTone = ROOT.RooGenericPdf("PnmuTone", "P(#nu_{#mu} #rightarrow #nu_{e}", oscillationFormula, [L, E, deltaMSq])
55
56 # only E is observable, so create the signal model by integrating out L
57 sigModel = PnmuTone.createProjection(L)
58
59 # create $ \int dE' dL' P(E',L' | \Delta m^2)$.
60 # Given RooFit will renormalize the PDF in the range of the observables,
61 # the average probability to oscillate in the experiment's acceptance
62 # needs to be incorporated into the extended term in the likelihood.
63 # Do this by creating a RooAbsReal representing the integral and divide by
64 # the area in the E-L plane.
65 # The integral should be over "primed" observables, so we need
66 # an independent copy of PnmuTone not to interfere with the original.
67
68 # Independent copy for Integral
69 EPrime = ROOT.RooRealVar("EPrime", "", 15, 10, 60, "GeV")
70 LPrime = ROOT.RooRealVar("LPrime", "", 0.800, 0.600, 1.0, "km") # need these units in formula
71 PnmuTonePrime = ROOT.RooGenericPdf(
72 "PnmuTonePrime", "P(#nu_{#mu} #rightarrow #nu_{e}", oscillationFormula, [LPrime, EPrime, deltaMSq]
73 )
74 intProbToOscInExp = PnmuTonePrime.createIntegral([EPrime, LPrime])
75
76 # Getting the flux is a bit tricky. It is more clear to include a cross section term that is not
77 # explicitly referred to in the text, eg.
78 # number events in bin = flux * cross-section for nu_e interaction in E bin * average prob nu_mu osc. to nu_e in bin
79 # let maxEventsInBin = flux * cross-section for nu_e interaction in E bin
80 # maxEventsInBin * 1% chance per bin = 100 events / bin
81 # therefore maxEventsInBin = 10,000.
82 # for 5 bins, this means maxEventsTot = 50,000
83 maxEventsTot = ROOT.RooConstVar("maxEventsTot", "maximum number of sinal events", 50000)
84 inverseArea = ROOT.RooConstVar(
85 "inverseArea",
86 "1/(#Delta E #Delta L)",
87 1.0 / (EPrime.getMax() - EPrime.getMin()) / (LPrime.getMax() - LPrime.getMin()),
88 )
89
90 # $sigNorm = maxEventsTot \cdot \int dE dL \frac{P_{oscillate\ in\ experiment}}{Area} \cdot {sin}^2(2\theta)$
91 sigNorm = ROOT.RooProduct("sigNorm", "", [maxEventsTot, intProbToOscInExp, inverseArea, sinSq2theta])
92 # bkg = 5 bins * 100 events / bin
93 bkgNorm = ROOT.RooConstVar("bkgNorm", "normalization for background", 500)
94
95 # flat background (0th order polynomial, so no arguments for coefficients)
96 bkgEShape = ROOT.RooPolynomial("bkgEShape", "flat bkg shape", E)
97
98 # total model
99 model = ROOT.RooAddPdf("model", "", [sigModel, bkgEShape], [sigNorm, bkgNorm])
100
101 # for debugging, check model tree
102 # model.printCompactTree();
103 # model.graphVizTree("model.dot");
104
105 # turn off some messages
106 ROOT.RooMsgService.instance().setStreamStatus(0, False)
107 ROOT.RooMsgService.instance().setStreamStatus(1, False)
108 ROOT.RooMsgService.instance().setStreamStatus(2, False)
109
110 # --------------------------------------
111 # n events in data to data, simply sum of sig+bkg
112 nEventsData = bkgNorm.getVal() + sigNorm.getVal()
113 print("generate toy data with nEvents = ", nEventsData)
114 # adjust random seed to get a toy dataset similar to one in paper.
115 # Found by trial and error (3 trials, so not very "fine tuned")
116 ROOT.RooRandom.randomGenerator().SetSeed(3)
117 # create a toy dataset
118 data = model.generate(E, nEventsData)
119
120 # --------------------------------------
121 # make some plots
122 dataCanvas = ROOT.TCanvas("dataCanvas")
123 dataCanvas.Divide(2, 2)
124
125 # plot the PDF
126 dataCanvas.cd(1)
127 hh = PnmuTone.createHistogram("hh", E, ROOT.RooFit.YVar(L, ROOT.RooFit.Binning(40)), Binning=40, Scaling=False)
128 hh.SetLineColor(ROOT.kBlue)
129 hh.SetTitle("True Signal Model")
130 hh.Draw("surf")
131 dataCanvas.Update()
132 dataCanvas.Draw()
133 # dataCanvas.SaveAs("rs.1.png")
134 # plot the data with the best fit
135 dataCanvas.cd(2)
136 Eframe = E.frame()
137 data.plotOn(Eframe)
138 model.fitTo(data, Extended=True)
139 model.plotOn(Eframe)
140 model.plotOn(Eframe, Components=sigModel, LineColor="r")
141 model.plotOn(Eframe, Components=bkgEShape, LineColor="g")
142 model.plotOn(Eframe)
143 Eframe.SetTitle("toy data with best fit model (and sig+bkg components)")
144 Eframe.Draw()
145 dataCanvas.Update()
146 dataCanvas.Draw()
147 # dataCanvas.SaveAs("rs.2.png")
148
149 # plot the likelihood function
150 dataCanvas.cd(3)
151 nll = model.createNLL(data, Extended=True)
152 pll = ROOT.RooProfileLL("pll", "", nll, [deltaMSq, sinSq2theta])
153 # hhh = nll.createHistogram("hhh",sinSq2theta, Binning(40), YVar(deltaMSq,Binning(40)))
154 hhh = pll.createHistogram(
155 "hhh", sinSq2theta, ROOT.RooFit.YVar(deltaMSq, ROOT.RooFit.Binning(40)), Binning=40, Scaling=False
156 )
157 hhh.SetLineColor(ROOT.kBlue)
158 hhh.SetTitle("Likelihood Function")
159 hhh.Draw("surf")
160
161 dataCanvas.Update()
162 dataCanvas.Draw()
163 dataCanvas.SaveAs("3.png")
164
165 # --------------------------------------------------------------
166 # show use of Feldman-Cousins utility in RooStats
167 # set the distribution creator, which encodes the test statistic
168 parameters = ROOT.RooArgSet(deltaMSq, sinSq2theta)
169 w = ROOT.RooWorkspace()
170
171 modelConfig = ROOT.RooFit.ModelConfig()
172 modelConfig.SetWorkspace(w)
173 modelConfig.SetPdf(model)
174 modelConfig.SetParametersOfInterest(parameters)
175
176 fc = RooStats.FeldmanCousins(data, modelConfig)
177 fc.SetTestSize(0.1) # set size of test
178 fc.UseAdaptiveSampling(True)
179 fc.SetNBins(10) # number of points to test per parameter
180
181 # use the Feldman-Cousins tool
182 interval = 0
183 if doFeldmanCousins:
184 interval = fc.GetInterval()
185
186 # ---------------------------------------------------------
187 # show use of ProfileLikeihoodCalculator utility in RooStats
188 plc = RooStats.ProfileLikelihoodCalculator(data, modelConfig)
189 plc.SetTestSize(0.1)
190
191 plcInterval = plc.GetInterval()
192
193 # --------------------------------------------
194 # show use of MCMCCalculator utility in RooStats
195 mcInt = ROOT.kNone
196
197 if doMCMC:
198 # turn some messages back on
199 RooMsgService.instance().setStreamStatus(0, True)
200 RooMsgService.instance().setStreamStatus(1, True)
201
202 mcmcWatch = ROOT.TStopwatch()
203 mcmcWatch.Start()
204
205 axisList = ROOT.RooArgList(deltaMSq, sinSq2theta)
206 mc = ROOT.RooStats.MCMCCalculator(data, modelConfig)
207 mc.SetNumIters(5000)
208 mc.SetNumBurnInSteps(100)
209 mc.SetUseKeys(True)
210 mc.SetTestSize(0.1)
211 mc.SetAxes(axisList) # set which is x and y axis in posterior histogram
212 # mc.SetNumBins(50);
213 mcInt = mc.GetInterval()
214
215 mcmcWatch.Stop()
216 mcmcWatch.Print()
217
218 # -------------------------------
219 # make plot of resulting interval
220
221 dataCanvas.cd(4)
222
223 # first plot a small dot for every point tested
224 if doFeldmanCousins:
225 parameterScan = fc.GetPointsToScan()
226 hist = parameterScan.createHistogram(deltaMSq, sinSq2theta, 30, 30)
227 # hist.Draw()
228 forContour = hist.Clone()
229
230 # now loop through the points and put a marker if it's in the interval
231 tmpPoint = RooArgSet()
232 # loop over points to test
233 for i in range(parameterScan.numEntries):
234 # get a parameter point from the list of points to test.
235 tmpPoint = parameterScan.get(i).clone("temp")
236
237 if interval:
238 if interval.IsInInterval(tmpPoint):
239 forContour.SetBinContent(
240 hist.FindBin(tmpPoint.getRealValue("sinSq2theta"), tmpPoint.getRealValue("deltaMSq")), 1
241 )
242 else:
243 forContour.SetBinContent(
244 hist.FindBin(tmpPoint.getRealValue("sinSq2theta"), tmpPoint.getRealValue("deltaMSq")), 0
245 )
246
247 del tmpPoint
248
249 if interval:
250 level = 0.5
251 forContour.SetContour(1, level)
252 forContour.SetLineWidth(2)
253 forContour.SetLineColor(ROOT.kRed)
254 forContour.Draw("cont2,same")
255
256 mcPlot = ROOT.kNone
257 if mcInt:
258 print(f"MCMC actual confidence level: ", mcInt.GetActualConfidenceLevel())
259 mcPlot = MCMCIntervalPlot(mcInt)
260 mcPlot.SetLineColor(kMagenta)
261 mcPlot.Draw()
262
263 dataCanvas.Update()
264
265 plotInt = LikelihoodIntervalPlot(plcInterval)
266
267 plotInt.SetTitle("90% Confidence Intervals")
268 if mcInt:
269 plotInt.Draw("same")
270 else:
271 plotInt.Draw()
272
273 dataCanvas.Update()
274
275 dataCanvas.SaveAs("rs401d_FeldmanCousins.1.pdf")
276
277 # print timing info
278 t.Stop()
279 t.Print()
280
281
282rs401d_FeldmanCousins(doFeldmanCousins=False, doMCMC=True)
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
static RooMsgService & instance()
Return reference to singleton instance.
The FeldmanCousins class (like the Feldman-Cousins technique) is essentially a specific configuration...
The ProfileLikelihoodCalculator is a concrete implementation of CombinedCalculator (the interface cla...