3 from sys
import stderr, stdout
9 """This class defines the basic stuff for a model builder, and it's an interface on top of RooWorkspace::factory or HLF files"""
14 if options.out ==
None: options.out = re.sub(
".txt$",
"",options.fileName)+
".root"
15 options.baseDir = os.path.dirname(options.fileName)
16 ROOT.gSystem.Load(
"libHiggsAnalysisCombinedLimit.so")
17 self.
out = ROOT.RooWorkspace(
"w",
"w");
18 self.out._import = getattr(self.
out,
"import")
19 self.out.dont_delete = []
20 if options.verbose == 0:
21 ROOT.RooMsgService.instance().setGlobalKillBelow(ROOT.RooFit.ERROR)
22 elif options.verbose < 2:
23 ROOT.RooMsgService.instance().setGlobalKillBelow(ROOT.RooFit.WARNING)
24 if os.environ.has_key(
'ROOFITSYS'):
25 ROOT.gSystem.AddIncludePath(
" -I%s/include " % os.environ[
'ROOFITSYS'])
26 elif options.out !=
None:
28 self.
out = open(options.out,
"w");
30 stderr.write(
"\nWARNING: You're not using binary mode. This is is DEPRECATED and NOT SUPPORTED anymore, and can give WRONG results.\n\n")
36 print "Executing factory with a string of length ",len(X),
" > 1000, could trigger a bug: ",X
37 ret = self.out.factory(X);
39 self.out.dont_delete.append(ret)
42 print "ERROR parsing '%s'" % X
44 raise RuntimeError,
"Error in factory statement"
46 if not self.options.bin: self.out.write(
"// "+X+
"\n");
48 if self.options.bin: self.
factory_(vardef);
49 else: self.out.write(vardef+
";\n");
51 if self.options.bin: self.out.defineSet(name,vars)
52 else: self.out.write(
"%s = set(%s);\n" % (name,vars));
54 if self.options.bin:
return self.
factory_(
"%s::%s(%s)" % (type, name, X));
55 else: self.out.write(
"%s = %s(%s);\n" % (name, type, X))
58 """This class defines the actual methods to build a model"""
60 ModelBuilderBase.__init__(self,options)
64 self.physics.setModelBuilder(self)
67 self.physics.doParametersOfInterest()
68 self.physics.preProcessNuisances(self.DC.systs)
75 if self.options.verbose > 1: self.out.Print(
"V")
76 if self.options.verbose > 2:
77 print "Wrote GraphVizTree of model_s to ",self.options.out+
".dot"
78 self.out.pdf(
"model_s").graphVizTree(self.options.out+
".dot",
"\\n")
80 """create pdf_bin<X> and pdf_bin<X>_bonly for each bin"""
81 raise RuntimeError,
"Not implemented in ModelBuilder"
83 if len(self.DC.systs) == 0:
return
86 for (n,nofloat,pdf,args,errline)
in self.DC.systs:
87 if pdf ==
"lnN" or pdf.startswith(
"shape"):
88 r =
"-4,4" if pdf ==
"shape" else "-7,7"
89 self.
doObj(
"%s_Pdf" % n,
"Gaussian",
"%s[%s], %s_In[0,%s], 1" % (n,r,n,r));
90 globalobs.append(
"%s_In" % n)
92 self.out.var(
"%s_In" % n).setConstant(
True)
95 for c
in errline.values():
98 if val != 0
and v != val:
99 raise RuntimeError,
"Error: line %s contains two different uncertainties %g, %g, which is not supported for gmM" % (n,v,val)
101 if val == 0:
raise RuntimeError,
"Error: line %s contains all zeroes"
102 theta = val*val; kappa = 1/theta
103 self.
doObj(
"%s_Pdf" % n,
"Gamma",
"%s[1,%f,%f], %s_In[%g,%g,%g], %s_scaling[%g], 0" % (n,
max(0.01,1-5*val), 1+5*val, n, kappa, 1, 2*kappa+4, n, theta))
104 globalobs.append(
"%s_In" % n)
105 if self.options.bin: self.out.var(
"%s_In" % n).setConstant(
True)
109 self.
doObj(
"%s_Pdf" % n,
"Poisson",
"%s_In[%d,0,%d], %s[0,%d], 1" % (n,args[0],2*args[0]+5,n,2*args[0]+5))
113 minExp = args[0]+1
if args[0] > 0
else 0;
114 while (ROOT.TMath.Poisson(args[0], minExp) > 1e-12)
and minExp > 0:
118 while (ROOT.TMath.Poisson(args[0], maxExp) > 1e-12):
122 while minObs > 0
and (ROOT.TMath.Poisson(minObs, args[0]+1) > 1e-12):
124 minObs -= (
sqrt(args[0])
if args[0] > 10
else 1);
126 while (ROOT.TMath.Poisson(maxObs, args[0]+1) > 1e-12):
128 maxObs += (
sqrt(args[0])
if args[0] > 10
else 2);
129 self.
doObj(
"%s_Pdf" % n,
"Poisson",
"%s_In[%d,%f,%f], %s[%f,%f,%f], 1" % (n,args[0],minObs,maxObs,n,args[0]+1,minExp,maxExp))
130 globalobs.append(
"%s_In" % n)
131 if self.options.bin: self.out.var(
"%s_In" % n).setConstant(
True)
133 trG_min = -7; trG_max = +7;
134 for b
in errline.keys():
135 for v
in errline[b].
values():
136 if v > 0
and 1.0 + trG_min * v < 0: trG_min = -1.0/v;
137 if v < 0
and 1.0 + trG_max * v < 0: trG_max = -1.0/v;
138 r =
"%f,%f" % (trG_min,trG_max);
139 self.
doObj(
"%s_Pdf" % n,
"Gaussian",
"%s[0,%s], %s_In[0,%s], 1" % (n,r,n,r));
140 globalobs.append(
"%s_In" % n)
142 self.out.var(
"%s_In" % n).setConstant(
True)
144 self.
doObj(
"%s_Pdf" % n,
"Uniform",
"%s[-1,1]" % n);
146 self.
doObj(
"%s_Pdf" % n,
"Uniform",
"%s[%f,%f]" % (n,args[0],args[1]))
148 mean = float(args[0])
150 sigmaL,sigmaR = args[1].
split(
"/")
151 if sigmaL[0] !=
"-" or sigmaR[0] !=
"+":
raise RuntimeError,
"Asymmetric parameter uncertainties should be entered as -x/+y"
152 sigmaL = sigmaL[1:]; sigmaR = sigmaR[1:]
154 self.
doVar(
"%s%s" % (n,args[2]))
156 sigma = float(args[1])
158 self.out.var(n).setConstant(
False)
159 self.out.var(n).setRange(mean-4*float(sigmaL),mean+4*float(sigmaR))
161 self.
doVar(
"%s[%g,%g]" % (n, mean-4*float(sigmaL), mean+4*float(sigmaR)))
162 self.out.var(n).setVal(mean)
163 self.
doObj(
"%s_Pdf" % n,
"BifurGauss",
"%s, %s_In[%s,%g,%g], %s, %s" % (n, n, args[0], self.out.var(n).getMin(), self.out.var(n).getMax(), sigmaL, sigmaR))
164 self.out.var(
"%s_In" % n).setConstant(
True)
167 self.
doVar(
"%s%s" % (n,args[2]))
169 sigma = float(args[1])
171 self.out.var(n).setConstant(
False)
172 self.out.var(n).setRange(mean-4*sigma, mean+4*sigma)
174 self.
doVar(
"%s[%g,%g]" % (n, mean-4*sigma, mean+4*sigma))
175 self.out.var(n).setVal(mean)
176 self.
doObj(
"%s_Pdf" % n,
"Gaussian",
"%s, %s_In[%s,%g,%g], %s" % (n, n, args[0], self.out.var(n).getMin(), self.out.var(n).getMax(), args[1]))
177 self.out.var(
"%s_In" % n).setConstant(
True)
178 globalobs.append(
"%s_In" % n)
179 else:
raise RuntimeError,
"Unsupported pdf %s" % pdf
181 self.out.var(
"%s" % n).setAttribute(
"globalConstrained",
True)
183 nuisPdfs = ROOT.RooArgList()
184 nuisVars = ROOT.RooArgSet()
185 for (n,nf,p,a,e)
in self.DC.systs:
186 nuisVars.add(self.out.var(n))
187 nuisPdfs.add(self.out.pdf(n+
"_Pdf"))
188 self.out.defineSet(
"nuisances", nuisVars)
189 self.out.nuisPdf = ROOT.RooProdPdf(
"nuisancePdf",
"nuisancePdf", nuisPdfs)
190 self.out._import(self.out.nuisPdf)
191 self.out.nuisPdfs = nuisPdfs
192 gobsVars = ROOT.RooArgSet()
193 for g
in globalobs: gobsVars.add(self.out.var(g))
194 self.out.defineSet(
"globalObservables", gobsVars)
196 self.
doSet(
"nuisances",
",".
join([
"%s" % n
for (n,nf,p,a,e)
in self.DC.systs]))
197 self.
doObj(
"nuisancePdf",
"PROD",
",".
join([
"%s_Pdf" % n
for (n,nf,p,a,e)
in self.DC.systs]))
198 self.
doSet(
"globalObservables",
",".
join(globalobs))
200 self.
doComment(
" --- Expected events in each bin, for each process ----")
201 for b
in self.DC.bins:
202 for p
in self.DC.exp[b].
keys():
204 if self.DC.exp[b][p] == 0:
205 self.
doVar(
"n_exp_bin%s_proc_%s[%g]" % (b, p, self.DC.exp[b][p]))
208 scale = self.physics.getYieldScale(b,p)
210 self.
doVar(
"n_exp_bin%s_proc_%s[%g]" % (b, p, 0))
213 nominal = self.DC.exp[b][p]
220 elif type(scale) == str:
221 factors.append(scale)
223 raise RuntimeError,
"Physics model returned something which is neither a name, nor 0, nor 1."
224 for (n,nofloat,pdf,args,errline)
in self.DC.systs:
225 if pdf ==
"param" or pdf.startswith(
"shape"):
continue
226 if not errline[b].has_key(p):
continue
227 if errline[b][p] == 0.0:
continue
228 if pdf.startswith(
"shape")
and pdf.endswith(
"?"):
230 if pdf ==
"lnN" and errline[b][p] == 1.0:
continue
231 if pdf ==
"lnN" or pdf ==
"lnU":
232 if type(errline[b][p]) == list:
233 elow, ehigh = errline[b][p];
234 alogNorms.append((elow, ehigh, n))
236 logNorms.append((errline[b][p], n))
239 elif pdf ==
"trG" or pdf ==
"unif":
240 myname =
"n_exp_shift_bin%s_proc_%s_%s" % (b,p,n)
241 self.
doObj(myname, ROOFIT_EXPR,
"'1+%f*@0', %s" % (errline[b][p], n));
242 factors.append(myname)
245 if abs(errline[b][p] * args[0] - self.DC.exp[b][p]) >
max(0.05 *
max(self.DC.exp[b][p],1), errline[b][p]):
246 raise RuntimeError,
"Values of N = %d, alpha = %g don't match with expected rate %g for systematics %s " % (
247 args[0], errline[b][p], self.DC.exp[b][p], n)
249 raise RuntimeError,
"More than one gmN uncertainty for the same bin and process (second one is %s)" % n
250 gamma = n; nominal = errline[b][p];
251 else:
raise RuntimeError,
"Unsupported pdf %s" % pdf
253 if len(factors) + len(logNorms) + len(alogNorms) == 0:
254 self.
doVar(
"n_exp_bin%s_proc_%s[%g]" % (b, p, self.DC.exp[b][p]))
257 procNorm = ROOT.ProcessNormalization(
"n_exp_bin%s_proc_%s" % (b,p),
"", nominal)
258 for kappa, thetaName
in logNorms: procNorm.addLogNormal(kappa, self.out.function(thetaName))
259 for kappaLo, kappaHi, thetaName
in alogNorms: procNorm.addAsymmLogNormal(kappaLo, kappaHi, self.out.function(thetaName))
260 for factorName
in factors: procNorm.addOtherFactor(self.out.function(factorName))
261 self.out._import(procNorm)
263 """create pdf_bin<X> and pdf_bin<X>_bonly for each bin"""
264 raise RuntimeError,
"Not implemented in ModelBuilder"
266 """create model_s and model_b pdfs"""
267 raise RuntimeError,
"Not implemented in ModelBuilder"
269 if not self.options.bin:
raise RuntimeException
270 if self.options.out ==
None:
raise RuntimeException
271 for nuis,warn
in self.DC.flatParamNuisances.iteritems():
272 if self.out.var(nuis): self.out.var(nuis).setAttribute(
"flatParam")
273 elif warn: stderr.write(
"Missing variable %s declared as flatParam\n" % nuis)
274 mc_s = ROOT.RooStats.ModelConfig(
"ModelConfig", self.
out)
275 mc_b = ROOT.RooStats.ModelConfig(
"ModelConfig_bonly", self.
out)
276 for (l,mc)
in [ (
's',mc_s), (
'b',mc_b) ]:
277 mc.SetPdf(self.out.pdf(
"model_"+l))
278 mc.SetParametersOfInterest(self.out.set(
"POI"))
279 mc.SetObservables(self.out.set(
"observables"))
280 if len(self.DC.systs): mc.SetNuisanceParameters(self.out.set(
"nuisances"))
281 if self.out.set(
"globalObservables"): mc.SetGlobalObservables(self.out.set(
"globalObservables"))
282 if self.options.verbose > 1: mc.Print(
"V")
283 self.out._import(mc, mc.GetName())
284 self.out.writeToFile(self.options.out)
289 """ModelBuilder to make a counting experiment"""
291 ModelBuilder.__init__(self,datacard,options)
292 if datacard.hasShapes:
293 raise RuntimeError,
"You're using a CountingModelBuilder for a model that has shapes"
296 self.
doComment(
" ----- observables (already set to observed values) -----")
297 for b
in self.DC.bins: self.
doVar(
"n_obs_bin%s[%f,0,%d]" % (b,self.DC.obs[b],N_OBS_MAX))
299 self.
doComment(
" ----- observables -----")
300 for b
in self.DC.bins: self.
doVar(
"n_obs_bin%s[0,%d]" % (b,N_OBS_MAX))
301 self.
doSet(
"observables",
",".
join([
"n_obs_bin%s" % b
for b
in self.DC.bins]))
304 self.out.data_obs = ROOT.RooDataSet(self.options.dataname,
"observed data", self.out.set(
"observables"))
305 self.out.data_obs.add( self.out.set(
"observables") )
306 self.out._import(self.out.data_obs)
308 self.
doComment(
" --- Expected events in each bin, total (S+B and B) ----")
309 for b
in self.DC.bins:
310 self.
doObj(
"n_exp_bin%s_bonly" % b,
"sum",
", ".
join([
"n_exp_bin%s_proc_%s" % (b,p)
for p
in self.DC.exp[b].
keys()
if self.DC.isSignal[p] ==
False]) )
311 self.
doObj(
"n_exp_bin%s" % b,
"sum",
", ".
join([
"n_exp_bin%s_proc_%s" % (b,p)
for p
in self.DC.exp[b].
keys() ]) )
312 self.
doObj(
"pdf_bin%s" % b,
"Poisson",
"n_obs_bin%s, n_exp_bin%s, 1" % (b,b))
313 self.
doObj(
"pdf_bin%s_bonly" % b,
"Poisson",
"n_obs_bin%s, n_exp_bin%s_bonly, 1" % (b,b))
315 prefix =
"modelObs" if len(self.DC.systs)
else "model"
316 nbins = len(self.DC.bins)
318 from math
import ceil
319 nblocks = int(ceil(nbins/10.))
320 for i
in range(nblocks):
321 self.
doObj(
"%s_s_%d" % (prefix,i),
"PROD",
",".
join([
"pdf_bin%s" % self.DC.bins[j]
for j
in range(10*i,
min(nbins,10*i+10))]))
322 self.
doObj(
"%s_b_%d" % (prefix,i),
"PROD",
",".
join([
"pdf_bin%s_bonly" % self.DC.bins[j]
for j
in range(10*i,
min(nbins,10*i+10))]))
323 self.
doObj(
"%s_s" % prefix,
"PROD",
",".
join([prefix+
"_s_%d" % i
for i
in range(nblocks)]))
324 self.
doObj(
"%s_b" % prefix,
"PROD",
",".
join([prefix+
"_b_%d" % i
for i
in range(nblocks)]))
326 self.
doObj(
"%s_s" % prefix,
"PROD",
",".
join([
"pdf_bin%s" % b
for b
in self.DC.bins]))
327 self.
doObj(
"%s_b" % prefix,
"PROD",
",".
join([
"pdf_bin%s_bonly" % b
for b
in self.DC.bins]))
328 if len(self.DC.systs):
329 self.
doObj(
"model_s",
"PROD",
"modelObs_s, nuisancePdf")
330 self.
doObj(
"model_b",
"PROD",
"modelObs_b, nuisancePdf")
const T & max(const T &a, const T &b)
static std::string join(char **cmd)