CMS 3D CMS Logo

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
ShapeTools.py
Go to the documentation of this file.
1 from sys import stdout, stderr
2 import os.path
3 import ROOT
4 
5 from HiggsAnalysis.CombinedLimit.ModelTools import ModelBuilder
6 
7 class ShapeBuilder(ModelBuilder):
8  def __init__(self,datacard,options):
9  ModelBuilder.__init__(self,datacard,options)
10  if not datacard.hasShapes:
11  raise RuntimeError, "You're using a ShapeBuilder for a model that has no shapes"
12  if options.libs:
13  for lib in options.libs:
14  ROOT.gSystem.Load(lib)
15  self.wspnames = {}
16  self.wsp = None
17  ## ------------------------------------------
18  ## -------- ModelBuilder interface ----------
19  ## ------------------------------------------
20  def doObservables(self):
21  if (self.options.verbose > 1): stderr.write("Using shapes: qui si parra' la tua nobilitate\n")
22  self.prepareAllShapes();
23  if len(self.DC.bins) > 1 or self.options.forceSimPdf:
24  ## start with just a few channels
25  strexpr="CMS_channel[" + ",".join(["%s=%d" % (l,i) for i,l in enumerate(self.DC.bins[:5])]) + "]";
26  self.doVar(strexpr);
27  self.out.binCat = self.out.cat("CMS_channel");
28  ## then add all the others, to avoid a too long factory string
29  for i,l in enumerate(self.DC.bins[5:]): self.out.binCat.defineType(l,i+5)
30  if self.options.verbose: stderr.write("Will use category 'CMS_channel' to identify the %d channels\n" % self.out.binCat.numTypes())
31  self.out.obs = ROOT.RooArgSet()
32  self.out.obs.add(self.out.binVars)
33  self.out.obs.add(self.out.binCat)
34  else:
35  self.out.obs = self.out.binVars
36  self.doSet("observables",self.out.obs)
37  if len(self.DC.obs) != 0:
38  self.doCombinedDataset()
39  def doIndividualModels(self):
40  for b in self.DC.bins:
41  pdfs = ROOT.RooArgList(); bgpdfs = ROOT.RooArgList()
42  coeffs = ROOT.RooArgList(); bgcoeffs = ROOT.RooArgList()
43  for p in self.DC.exp[b].keys(): # so that we get only self.DC.processes contributing to this bin
44  if self.DC.exp[b][p] == 0: continue
45  (pdf,coeff) = (self.getPdf(b,p), self.out.function("n_exp_bin%s_proc_%s" % (b,p)))
46  extranorm = self.getExtraNorm(b,p)
47  if extranorm:
48  self.doObj("n_exp_final_bin%s_proc_%s" % (b,p), "prod", "n_exp_bin%s_proc_%s, %s" % (b,p, extranorm))
49  coeff = self.out.function("n_exp_final_bin%s_proc_%s" % (b,p))
50  pdfs.add(pdf); coeffs.add(coeff)
51  if not self.DC.isSignal[p]:
52  bgpdfs.add(pdf); bgcoeffs.add(coeff)
53  if self.options.verbose: print "Creating RooAddPdf %s with %s elements" % ("pdf_bin"+b, coeffs.getSize())
54  sum_s = ROOT.RooAddPdf("pdf_bin%s" % b, "", pdfs, coeffs)
55  sum_b = ROOT.RooAddPdf("pdf_bin%s_bonly" % b, "", bgpdfs, bgcoeffs)
56  if b in self.pdfModes:
57  sum_s.setAttribute('forceGen'+self.pdfModes[b].title())
58  sum_b.setAttribute('forceGen'+self.pdfModes[b].title())
59  if len(self.DC.systs):
60  ## rename the pdfs
61  sum_s.SetName("pdf_bin%s_nuis" % b); sum_b.SetName("pdf_bin%s_bonly_nuis" % b)
62  # now we multiply by all the nuisances, but avoiding nested products
63  # so we first make a list of all nuisances plus the RooAddPdf
64  sumPlusNuis_s = ROOT.RooArgList(self.out.nuisPdfs); sumPlusNuis_s.add(sum_s)
65  sumPlusNuis_b = ROOT.RooArgList(self.out.nuisPdfs); sumPlusNuis_b.add(sum_b)
66  # then make RooProdPdf and import it
67  pdf_s = ROOT.RooProdPdf("pdf_bin%s" % b, "", sumPlusNuis_s)
68  pdf_b = ROOT.RooProdPdf("pdf_bin%s_bonly" % b, "", sumPlusNuis_b)
69  if b in self.pdfModes:
70  pdf_s.setAttribute('forceGen'+self.pdfModes[b].title())
71  pdf_b.setAttribute('forceGen'+self.pdfModes[b].title())
72  self.out._import(pdf_s, ROOT.RooFit.RenameConflictNodes(b))
73  self.out._import(pdf_b, ROOT.RooFit.RecycleConflictNodes(), ROOT.RooFit.Silence())
74  else:
75  self.out._import(sum_s, ROOT.RooFit.RenameConflictNodes(b))
76  self.out._import(sum_b, ROOT.RooFit.RecycleConflictNodes(), ROOT.RooFit.Silence())
77  def doCombination(self):
78  ## Contrary to Number-counting models, here each channel PDF already contains the nuisances
79  ## So we just have to build the combined pdf
80  if len(self.DC.bins) > 1 or self.options.forceSimPdf:
81  for (postfixIn,postfixOut) in [ ("","_s"), ("_bonly","_b") ]:
82  simPdf = ROOT.RooSimultaneous("model"+postfixOut, "model"+postfixOut, self.out.binCat)
83  for b in self.DC.bins:
84  pdfi = self.out.pdf("pdf_bin%s%s" % (b,postfixIn))
85  simPdf.addPdf(pdfi, b)
86  self.out._import(simPdf)
87  else:
88  self.out._import(self.out.pdf("pdf_bin%s" % self.DC.bins[0]).clone("model_s"), ROOT.RooFit.Silence())
89  self.out._import(self.out.pdf("pdf_bin%s_bonly" % self.DC.bins[0]).clone("model_b"), ROOT.RooFit.Silence())
90  if self.options.fixpars:
91  pars = self.out.pdf("model_s").getParameters(self.out.obs)
92  iter = pars.createIterator()
93  while True:
94  arg = iter.Next()
95  if arg == None: break;
96  if arg.InheritsFrom("RooRealVar") and arg.GetName() != "r":
97  arg.setConstant(True);
98  ## --------------------------------------
99  ## -------- High level helpers ----------
100  ## --------------------------------------
101  def prepareAllShapes(self):
102  shapeTypes = []; shapeBins = []; shapeObs = {}
103  self.pdfModes = {}
104  for ib,b in enumerate(self.DC.bins):
105  databins = {}; bgbins = {}
106  for p in [self.options.dataname]+self.DC.exp[b].keys():
107  if len(self.DC.obs) == 0 and p == self.options.dataname: continue
108  if p != self.options.dataname and self.DC.exp[b][p] == 0: continue
109  shape = self.getShape(b,p); norm = 0;
110  if shape == None: # counting experiment
111  if not self.out.var("CMS_fakeObs"):
112  self.doVar("CMS_fakeObs[0,1]");
113  self.out.var("CMS_fakeObs").setBins(1);
114  self.doSet("CMS_fakeObsSet","CMS_fakeObs");
115  shapeObs["CMS_fakeObsSet"] = self.out.set("CMS_fakeObsSet")
116  if p == self.options.dataname:
117  self.pdfModes[b] = 'binned'
118  shapeTypes.append("RooDataHist")
119  else:
120  shapeTypes.append("RooAbsPdf");
121  elif shape.ClassName().startswith("TH1"):
122  shapeTypes.append("TH1"); shapeBins.append(shape.GetNbinsX())
123  norm = shape.Integral()
124  if p == self.options.dataname:
125  if self.options.poisson > 0 and norm > self.options.poisson:
126  self.pdfModes[b] = 'poisson'
127  else:
128  self.pdfModes[b] = 'binned'
129  for i in xrange(1, shape.GetNbinsX()+1):
130  if shape.GetBinContent(i) > 0: databins[i] = True
131  elif not self.DC.isSignal[p]:
132  for i in xrange(1, shape.GetNbinsX()+1):
133  if shape.GetBinContent(i) > 0: bgbins[i] = True
134  elif shape.InheritsFrom("RooDataHist"):
135  shapeTypes.append("RooDataHist");
136  shapeBins.append(shape.numEntries())
137  shapeObs[self.argSetToString(shape.get())] = shape.get()
138  norm = shape.sumEntries()
139  if p == self.options.dataname:
140  if self.options.poisson > 0 and norm > self.options.poisson:
141  self.pdfModes[b] = 'poisson'
142  else:
143  self.pdfModes[b] = 'binned'
144  elif shape.InheritsFrom("RooDataSet"):
145  shapeTypes.append("RooDataSet");
146  shapeObs[self.argSetToString(shape.get())] = shape.get()
147  norm = shape.sumEntries()
148  if p == self.options.dataname: self.pdfModes[b] = 'unbinned'
149  elif shape.InheritsFrom("TTree"):
150  shapeTypes.append("TTree");
151  if p == self.options.dataname: self.pdfModes[b] = 'unbinned'
152  elif shape.InheritsFrom("RooAbsPdf"):
153  shapeTypes.append("RooAbsPdf");
154  else: raise RuntimeError, "Currently supporting only TH1s, RooDataHist and RooAbsPdfs"
155  if norm != 0:
156  if p == self.options.dataname:
157  if len(self.DC.obs):
158  if self.DC.obs[b] == -1: self.DC.obs[b] = norm
159  elif abs(norm-self.DC.obs[b]) > 0.01:
160  if not self.options.noCheckNorm: raise RuntimeError, "Mismatch in normalizations for observed data in bin %s: text %f, shape %f" % (b,self.DC.obs[b],norm)
161  else:
162  if self.DC.exp[b][p] == -1: self.DC.exp[b][p] = norm
163  elif self.DC.exp[b][p] > 0 and abs(norm-self.DC.exp[b][p]) > 0.01*max(1,self.DC.exp[b][p]):
164  if not self.options.noCheckNorm: raise RuntimeError, "Mismatch in normalizations for bin %s, process %s: rate %f, shape %f" % (b,p,self.DC.exp[b][p],norm)
165  if len(databins) > 0:
166  for i in databins.iterkeys():
167  if i not in bgbins: stderr.write("Channel %s has bin %d fill in data but empty in all backgrounds\n" % (b,i))
168  if shapeTypes.count("TH1"):
169  self.out.maxbins = max(shapeBins)
170  if self.options.verbose: stderr.write("Will use binning variable CMS_th1x with %d bins\n" % self.out.maxbins)
171  self.doVar("CMS_th1x[0,%d]" % self.out.maxbins); self.out.var("CMS_th1x").setBins(self.out.maxbins)
172  self.out.binVar = self.out.var("CMS_th1x")
173  shapeObs['CMS_th1x'] = self.out.binVar
174  if shapeTypes.count("TH1") == len(shapeTypes):
175  self.out.mode = "binned"
176  self.out.binVars = ROOT.RooArgSet(self.out.binVar)
177  elif shapeTypes.count("RooDataSet") > 0 or shapeTypes.count("TTree") > 0 or len(shapeObs.keys()) > 1:
178  self.out.mode = "unbinned"
179  if self.options.verbose: stderr.write("Will try to work with unbinned datasets\n")
180  if self.options.verbose: stderr.write("Observables: %s\n" % str(shapeObs.keys()))
181  if len(shapeObs.keys()) != 1:
182  self.out.binVars = ROOT.RooArgSet()
183  for obs in shapeObs.values():
184  self.out.binVars.add(obs, False)
185  else:
186  self.out.binVars = shapeObs.values()[0]
187  self.out._import(self.out.binVars)
188  else:
189  self.out.mode = "binned"
190  if self.options.verbose: stderr.write("Will try to make a binned dataset\n")
191  if self.options.verbose: stderr.write("Observables: %s\n" % str(shapeObs.keys()))
192  if len(shapeObs.keys()) != 1:
193  raise RuntimeError, "There's more than once choice of observables: %s\n" % str(shapeObs.keys())
194  self.out.binVars = shapeObs.values()[0]
195  self.out._import(self.out.binVars)
196  def doCombinedDataset(self):
197  if len(self.DC.bins) == 1 and not self.options.forceSimPdf:
198  data = self.getData(self.DC.bins[0],self.options.dataname).Clone(self.options.dataname)
199  self.out._import(data)
200  return
201  if self.out.mode == "binned":
202  combiner = ROOT.CombDataSetFactory(self.out.obs, self.out.binCat)
203  for b in self.DC.bins: combiner.addSetBin(b, self.getData(b,self.options.dataname))
204  self.out.data_obs = combiner.done(self.options.dataname,self.options.dataname)
205  self.out._import(self.out.data_obs)
206  elif self.out.mode == "unbinned":
207  combiner = ROOT.CombDataSetFactory(self.out.obs, self.out.binCat)
208  for b in self.DC.bins: combiner.addSetAny(b, self.getData(b,self.options.dataname))
209  self.out.data_obs = combiner.doneUnbinned(self.options.dataname,self.options.dataname)
210  self.out._import(self.out.data_obs)
211  else: raise RuntimeException, "Only combined datasets are supported"
212  #print "Created combined dataset with ",self.out.data_obs.numEntries()," entries, out of:"
213  #for b in self.DC.bins: print " bin", b, ": entries = ", self.getData(b,self.options.dataname).numEntries()
214  ## -------------------------------------
215  ## -------- Low level helpers ----------
216  ## -------------------------------------
217  def getShape(self,channel,process,syst="",_fileCache={},_cache={},allowNoSyst=False):
218  if _cache.has_key((channel,process,syst)):
219  if self.options.verbose > 1: print "recyling (%s,%s,%s) -> %s\n" % (channel,process,syst,_cache[(channel,process,syst)].GetName())
220  return _cache[(channel,process,syst)];
221  postFix="Sig" if (process in self.DC.isSignal and self.DC.isSignal[process]) else "Bkg"
222  bentry = None
223  if self.DC.shapeMap.has_key(channel): bentry = self.DC.shapeMap[channel]
224  elif self.DC.shapeMap.has_key("*"): bentry = self.DC.shapeMap["*"]
225  else: raise KeyError, "Shape map has no entry for channel '%s'" % (channel)
226  names = []
227  if bentry.has_key(process): names = bentry[process]
228  elif bentry.has_key("*"): names = bentry["*"]
229  elif self.DC.shapeMap["*"].has_key(process): names = self.DC.shapeMap["*"][process]
230  elif self.DC.shapeMap["*"].has_key("*"): names = self.DC.shapeMap["*"]["*"]
231  else: raise KeyError, "Shape map has no entry for process '%s', channel '%s'" % (process,channel)
232  if len(names) == 1 and names[0] == "FAKE": return None
233  if syst != "":
234  if len(names) == 2:
235  if allowNoSyst: return None
236  raise RuntimeError, "Can't find systematic "+syst+" for process '%s', channel '%s'" % (process,channel)
237  names = [names[0], names[2]]
238  else:
239  names = [names[0], names[1]]
240  strmass = "%d" % self.options.mass if self.options.mass % 1 == 0 else str(self.options.mass)
241  finalNames = [ x.replace("$PROCESS",process).replace("$CHANNEL",channel).replace("$SYSTEMATIC",syst).replace("$MASS",strmass) for x in names ]
242  if not _fileCache.has_key(finalNames[0]):
243  trueFName = finalNames[0]
244  if not os.path.exists(trueFName) and not os.path.isabs(trueFName) and os.path.exists(self.options.baseDir+"/"+trueFName):
245  trueFName = self.options.baseDir+"/"+trueFName;
246  _fileCache[finalNames[0]] = ROOT.TFile.Open(trueFName)
247  file = _fileCache[finalNames[0]]; objname = finalNames[1]
248  if not file: raise RuntimeError, "Cannot open file %s (from pattern %s)" % (finalNames[0],names[0])
249  if ":" in objname: # workspace:obj or ttree:xvar or th1::xvar
250  (wname, oname) = objname.split(":")
251  if (file,wname) not in self.wspnames :
252  self.wspnames[(file,wname)] = file.Get(wname)
253  self.wsp = self.wspnames[(file,wname)]
254  if not self.wsp: raise RuntimeError, "Failed to find %s in file %s (from pattern %s, %s)" % (wname,finalNames[0],names[1],names[0])
255  if self.wsp.ClassName() == "RooWorkspace":
256  ret = self.wsp.data(oname)
257  if not ret: ret = self.wsp.pdf(oname)
258  if not ret: raise RuntimeError, "Object %s in workspace %s in file %s does not exist or it's neither a data nor a pdf" % (oname, wname, finalNames[0])
259  # Fix the fact that more than one entry can refer to the same object
260  ret = ret.Clone()
261  ret.SetName("shape%s_%s_%s%s" % (postFix,process,channel, "_"+syst if syst else ""))
262  _cache[(channel,process,syst)] = ret
263  if not syst:
264  normname = "%s_norm" % (oname)
265  norm = self.wsp.arg(normname)
266  if norm:
267  if normname in self.DC.flatParamNuisances:
268  self.DC.flatParamNuisances[normname] = False # don't warn if not found
269  norm.setAttribute("flatParam")
270  norm.SetName("shape%s_%s_%s%s_norm" % (postFix,process,channel, "_"))
271  self.out._import(norm, ROOT.RooFit.RecycleConflictNodes())
272  if self.options.verbose > 1: print "import (%s,%s) -> %s\n" % (finalNames[0],objname,ret.GetName())
273  return ret;
274  elif self.wsp.ClassName() == "TTree":
275  ##If it is a tree we will convert it in RooDataSet . Then we can decide if we want to build a
276  ##RooKeysPdf or if we want to use it as an unbinned dataset
277  if not self.wsp: raise RuntimeError, "Failed to find %s in file %s (from pattern %s, %s)" % (wname,finalNames[0],names[1],names[0])
278  self.doVar("%s[%f,%f]" % (oname,self.wsp.GetMinimum(oname),self.wsp.GetMaximum(oname)))
279  #Check if it is weighted
280  self.doVar("__WEIGHT__[0.,1000.]")
281  rds = ROOT.RooDataSet("shape%s_%s_%s%s" % (postFix,process,channel, "_"+syst if syst else ""), "shape%s_%s_%s%s" % (postFix,process,channel, "_"+syst if syst else ""),self.wsp,ROOT.RooArgSet(self.out.var(oname)),"","__WEIGHT__")
282  rds.var = oname
283  _cache[(channel,process,syst)] = rds
284  if self.options.verbose > 1: print "import (%s,%s) -> %s\n" % (finalNames[0],wname,rds.GetName())
285  return rds
286  elif self.wsp.InheritsFrom("TH1"):
287  ##If it is a Histogram we will convert it in RooDataSet preserving the bins
288  if not self.wsp: raise RuntimeError, "Failed to find %s in file %s (from pattern %s, %s)" % (wname,finalNames[0],names[1],names[0])
289  name = "shape%s_%s_%s%s" % (postFix,process,channel, "_"+syst if syst else "")
290  # don't make it twice
291  for X in _neverDelete:
292  if X.InheritsFrom("TNamed") and X.GetName() == name: return X
293  self.doVar("%s[%f,%f]" % (oname,self.wsp.GetXaxis().GetXmin(),self.wsp.GetXaxis().GetXmax()))
294  rds = ROOT.RooDataHist(name, name, ROOT.RooArgList(self.out.var(oname)), self.wsp)
295  rds.var = oname
296  if self.options.verbose > 1: stderr.write("import (%s,%s) -> %s\n" % (finalNames[0],wname,rds.GetName()))
297  _neverDelete.append(rds)
298  return rds
299  else:
300  raise RuntimeError, "Object %s in file %s has unrecognized type %s" (wname, finalNames[0], self.wsp.ClassName())
301  else: # histogram
302  ret = file.Get(objname);
303  if not ret:
304  if allowNoSyst: return None
305  raise RuntimeError, "Failed to find %s in file %s (from pattern %s, %s)" % (objname,finalNames[0],names[1],names[0])
306  ret.SetName("shape%s_%s_%s%s" % (postFix,process,channel, "_"+syst if syst else ""))
307  if self.options.verbose > 1: print "import (%s,%s) -> %s\n" % (finalNames[0],objname,ret.GetName())
308  _cache[(channel,process,syst)] = ret
309  return ret
310  def getData(self,channel,process,syst="",_cache={}):
311  return self.shape2Data(self.getShape(channel,process,syst),channel,process)
312  def getPdf(self,channel,process,_cache={}):
313  postFix="Sig" if (process in self.DC.isSignal and self.DC.isSignal[process]) else "Bkg"
314  if _cache.has_key((channel,process)): return _cache[(channel,process)]
315  shapeNominal = self.getShape(channel,process)
316  nominalPdf = self.shape2Pdf(shapeNominal,channel,process)
317  if shapeNominal == None: return nominalPdf # no point morphing a fake shape
318  morphs = []; shapeAlgo = None
319  for (syst,nofloat,pdf,args,errline) in self.DC.systs:
320  if not "shape" in pdf: continue
321  if errline[channel][process] == 0: continue
322  allowNoSyst = (pdf[-1] == "?")
323  pdf = pdf.replace("?","")
324  if shapeAlgo == None: shapeAlgo = pdf
325  elif pdf != shapeAlgo:
326  errmsg = "ERROR for channel %s, process %s. " % (channel,process)
327  errmsg += "Requesting morphing %s for systematic %s after having requested %s. " % (pdf, syst, shapeAlgo)
328  raise RuntimeError, errmsg+" One can use only one morphing algorithm for a given shape";
329  if errline[channel][process] != 0:
330  if allowNoSyst and not self.isShapeSystematic(channel,process,syst): continue
331  shapeUp = self.getShape(channel,process,syst+"Up")
332  shapeDown = self.getShape(channel,process,syst+"Down")
333  if shapeUp.ClassName() != shapeNominal.ClassName(): raise RuntimeError, "Mismatched shape types for channel %s, process %s, syst %s" % (channel,process,syst)
334  if shapeDown.ClassName() != shapeNominal.ClassName(): raise RuntimeError, "Mismatched shape types for channel %s, process %s, syst %s" % (channel,process,syst)
335  morphs.append((syst,errline[channel][process],self.shape2Pdf(shapeUp,channel,process),self.shape2Pdf(shapeDown,channel,process)))
336  if len(morphs) == 0: return nominalPdf
337  if shapeAlgo == "shapeN": stderr.write("Warning: the shapeN implementation in RooStats and L&S are different\n")
338  pdfs = ROOT.RooArgList(nominalPdf)
339  coeffs = ROOT.RooArgList()
340  minscale = 1
341  for (syst,scale,pdfUp,pdfDown) in morphs:
342  pdfs.add(pdfUp); pdfs.add(pdfDown);
343  if scale == 1:
344  coeffs.add(self.out.var(syst))
345  else: # must scale it :-/
346  coeffs.add(self.doObj("%s_scaled_%s_%s" % (syst,channel,process), "prod","%s, %s" % (scale,syst)))
347  if scale < minscale: minscale = scale
348  qrange = minscale; qalgo = 0;
349  if shapeAlgo[-1] == "*":
350  qalgo = 100
351  shapeAlgo = shapeAlgo[:-1]
352  if shapeAlgo == "shape": shapeAlgo = self.options.defMorph
353  if "shapeL" in shapeAlgo: qrange = 0;
354  elif "shapeN" in shapeAlgo: qalgo = -1;
355  if "2a" in shapeAlgo: # old shape2
356  if not nominalPdf.InheritsFrom("RooHistPdf"): raise RuntimeError, "Algorithms 'shape2', 'shapeL2', shapeN2' only work with histogram templates"
357  if nominalPdf.dataHist().get().getSize() != 1: raise RuntimeError, "Algorithms 'shape2', 'shapeL2', shapeN2' only work in one dimension"
358  xvar = nominalPdf.dataHist().get().first()
359  _cache[(channel,process)] = ROOT.VerticalInterpHistPdf("shape%s_%s_%s_morph" % (postFix,channel,process), "", xvar, pdfs, coeffs, qrange, qalgo)
360  elif "2" in shapeAlgo: # new faster shape2
361  if not nominalPdf.InheritsFrom("RooHistPdf"): raise RuntimeError, "Algorithms 'shape2', 'shapeL2', shapeN2' only work with histogram templates"
362  if nominalPdf.dataHist().get().getSize() != 1: raise RuntimeError, "Algorithms 'shape2', 'shapeL2', shapeN2' only work in one dimension"
363  xvar = nominalPdf.dataHist().get().first()
364  _cache[(channel,process)] = ROOT.FastVerticalInterpHistPdf("shape%s_%s_%s_morph" % (postFix,channel,process), "", xvar, pdfs, coeffs, qrange, qalgo)
365  else:
366  _cache[(channel,process)] = ROOT.VerticalInterpPdf("shape%s_%s_%s_morph" % (postFix,channel,process), "", pdfs, coeffs, qrange, qalgo)
367  return _cache[(channel,process)]
368  def isShapeSystematic(self,channel,process,syst):
369  shapeUp = self.getShape(channel,process,syst+"Up",allowNoSyst=True)
370  return shapeUp != None
371  def getExtraNorm(self,channel,process):
372  postFix="Sig" if (process in self.DC.isSignal and self.DC.isSignal[process]) else "Bkg"
373  terms = []
374  shapeNominal = self.getShape(channel,process)
375  if shapeNominal == None:
376  # FIXME no extra norm for dummy pdfs (could be changed)
377  return None
378  if shapeNominal.InheritsFrom("RooAbsPdf"):
379  # return nominal multiplicative normalization constant
380  normname = "shape%s_%s_%s%s_norm" % (postFix,process,channel, "_")
381  if self.out.arg(normname): return normname
382  else: return None
383  normNominal = 0
384  if shapeNominal.InheritsFrom("TH1"): normNominal = shapeNominal.Integral()
385  elif shapeNominal.InheritsFrom("RooDataHist"): normNominal = shapeNominal.sumEntries()
386  else: return None
387  if normNominal == 0: raise RuntimeError, "Null norm for channel %s, process %s" % (channel,process)
388  for (syst,nofloat,pdf,args,errline) in self.DC.systs:
389  if "shape" not in pdf: continue
390  if errline[channel][process] != 0:
391  if pdf[-1] == "?" and not self.isShapeSystematic(channel,process,syst): continue
392  shapeUp = self.getShape(channel,process,syst+"Up")
393  shapeDown = self.getShape(channel,process,syst+"Down")
394  if shapeUp.ClassName() != shapeNominal.ClassName(): raise RuntimeError, "Mismatched shape types for channel %s, process %s, syst" % (channel,process,syst)
395  if shapeDown.ClassName() != shapeNominal.ClassName(): raise RuntimeError, "Mismatched shape types for channel %s, process %s, syst" % (channel,process,syst)
396  kappaUp,kappaDown = 1,1
397  if shapeNominal.InheritsFrom("TH1"):
398  kappaUp,kappaDown = shapeUp.Integral(),shapeDown.Integral()
399  elif shapeNominal.InheritsFrom("RooDataHist"):
400  kappaUp,kappaDown = shapeUp.sumEntries(),shapeDown.sumEntries()
401  if not kappaUp > 0: raise RuntimeError, "Bogus norm %r for channel %s, process %s, systematic %s Up" % (kappaUp, channel,process,syst)
402  if not kappaDown > 0: raise RuntimeError, "Bogus norm %r for channel %s, process %s, systematic %s Down" % (kappaDown, channel,process,syst)
403  kappaUp /=normNominal; kappaDown /= normNominal
404  if abs(kappaUp-1) < 1e-3 and abs(kappaDown-1) < 1e-3: continue
405  # if errline[channel][process] == <x> it means the gaussian should be scaled by <x> before doing pow
406  # for convenience, we scale the kappas
407  kappasScaled = [ pow(x, errline[channel][process]) for x in kappaDown,kappaUp ]
408  terms.append("AsymPow(%f,%f,%s)" % (kappasScaled[0], kappasScaled[1], syst))
409  return ",".join(terms) if terms else None;
410  def shape2Data(self,shape,channel,process,_cache={}):
411  postFix="Sig" if (process in self.DC.isSignal and self.DC.isSignal[process]) else "Bkg"
412  if shape == None:
413  name = "shape%s_%s_%s" % (postFix,channel,process)
414  if not _cache.has_key(name):
415  obs = ROOT.RooArgSet(self.out.var("CMS_fakeObs"))
416  obs.setRealValue("CMS_fakeObs",0.5);
417  if self.out.mode == "binned":
418  self.out.var("CMS_fakeObs").setBins(1)
419  rdh = ROOT.RooDataHist(name, name, obs)
420  rdh.set(obs, self.DC.obs[channel])
421  _cache[name] = rdh
422  else:
423  rds = ROOT.RooDataSet(name, name, obs)
424  if self.DC.obs[channel] == float(int(self.DC.obs[channel])):
425  for i in range(int(self.DC.obs[channel])): rds.add(obs)
426  else:
427  rds.add(obs, self.DC.obs[channel])
428  _cache[name] = rds
429  return _cache[name]
430  if not _cache.has_key(shape.GetName()):
431  if shape.ClassName().startswith("TH1"):
432  rebinh1 = ROOT.TH1F(shape.GetName()+"_rebin", "", self.out.maxbins, 0.0, float(self.out.maxbins))
433  for i in range(1,min(shape.GetNbinsX(),self.out.maxbins)+1):
434  rebinh1.SetBinContent(i, shape.GetBinContent(i))
435  rdh = ROOT.RooDataHist(shape.GetName(), shape.GetName(), ROOT.RooArgList(self.out.binVar), rebinh1)
436  self.out._import(rdh)
437  _cache[shape.GetName()] = rdh
438  elif shape.ClassName() in ["RooDataHist", "RooDataSet"]:
439  return shape
440  else: raise RuntimeError, "shape2Data not implemented for %s" % shape.ClassName()
441  return _cache[shape.GetName()]
442  def shape2Pdf(self,shape,channel,process,_cache={}):
443  postFix="Sig" if (process in self.DC.isSignal and self.DC.isSignal[process]) else "Bkg"
444  if shape == None:
445  name = "shape%s_%s_%s" % (postFix,channel,process)
446  if not _cache.has_key(name):
447  _cache[name] = ROOT.RooUniform(name, name, ROOT.RooArgSet(self.out.var("CMS_fakeObs")))
448  return _cache[name]
449  if not _cache.has_key(shape.GetName()+"Pdf"):
450  if shape.ClassName().startswith("TH1"):
451  rdh = self.shape2Data(shape,channel,process)
452  rhp = self.doObj("%sPdf" % shape.GetName(), "HistPdf", "{%s}, %s" % (self.out.binVar.GetName(), shape.GetName()))
453  _cache[shape.GetName()+"Pdf"] = rhp
454  elif shape.InheritsFrom("RooAbsPdf"):
455  _cache[shape.GetName()+"Pdf"] = shape
456  elif shape.InheritsFrom("RooDataHist"):
457  rhp = ROOT.RooHistPdf("%sPdf" % shape.GetName(), "", shape.get(), shape)
458  self.out._import(rhp)
459  _cache[shape.GetName()+"Pdf"] = rhp
460  elif shape.InheritsFrom("RooDataSet"):
461  rkp = ROOT.RooKeysPdf("%sPdf" % shape.GetName(), "", self.out.var(shape.var), shape,3,1.5);
462  self.out._import(rkp)
463  _cache[shape.GetName()+"Pdf"] = rkp
464  else:
465  raise RuntimeError, "shape2Pdf not implemented for %s" % shape.ClassName()
466  return _cache[shape.GetName()+"Pdf"]
467  def argSetToString(self,argset):
468  names = []
469  it = argset.createIterator()
470  while True:
471  arg = it.Next()
472  if not arg: break
473  names.append(arg.GetName())
474  return ",".join(names)
475 
def prepareAllShapes
-----— High level helpers -------—
Definition: ShapeTools.py:101
#define abs(x)
Definition: mlp_lapack.h:159
#define min(a, b)
Definition: mlp_lapack.h:161
def getShape
-----— Low level helpers -------—
Definition: ShapeTools.py:217
const T & max(const T &a, const T &b)
def doObservables
-----— ModelBuilder interface -------—
Definition: ShapeTools.py:20
bool first
Definition: L1TdeRCT.cc:94
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
TEveGeoShape * clone(const TEveElement *element, TEveElement *parent)
Definition: eve_macros.cc:135
Power< A, B >::type pow(const A &a, const B &b)
Definition: Power.h:40
T get(const Candidate &c)
Definition: component.h:56