CMS 3D CMS Logo

Public Member Functions | Public Attributes

ModelTools::ModelBuilder Class Reference

Inheritance diagram for ModelTools::ModelBuilder:
ModelTools::ModelBuilderBase ModelTools::CountingModelBuilder

List of all members.

Public Member Functions

def __init__
def doCombination
def doExpectedEvents
def doIndividualModels
def doModel
def doModelConfigs
def doNuisances
def doObservables
def isShapeSystematic
def setPhysics

Public Attributes

 DC
 physics

Detailed Description

This class defines the actual methods to build a model

Definition at line 57 of file ModelTools.py.


Constructor & Destructor Documentation

def ModelTools::ModelBuilder::__init__ (   self,
  datacard,
  options 
)

Reimplemented in ModelTools::CountingModelBuilder.

Definition at line 59 of file ModelTools.py.

00060                                        :
00061         ModelBuilderBase.__init__(self,options) 
        self.DC = datacard

Member Function Documentation

def ModelTools::ModelBuilder::doCombination (   self)
create model_s and model_b pdfs

Reimplemented in ModelTools::CountingModelBuilder.

Definition at line 265 of file ModelTools.py.

00266                            :
00267         """create model_s and model_b pdfs"""
        raise RuntimeError, "Not implemented in ModelBuilder"
def ModelTools::ModelBuilder::doExpectedEvents (   self)

Definition at line 199 of file ModelTools.py.

00200                               :
00201         self.doComment(" --- Expected events in each bin, for each process ----")
00202         for b in self.DC.bins:
00203             for p in self.DC.exp[b].keys(): # so that we get only self.DC.processes contributing to this bin
00204                 # if it's a zero background, write a zero and move on
00205                 if self.DC.exp[b][p] == 0:
00206                     self.doVar("n_exp_bin%s_proc_%s[%g]" % (b, p, self.DC.exp[b][p]))
00207                     continue
00208                 # get model-dependent scale factor
00209                 scale = self.physics.getYieldScale(b,p)
00210                 if scale == 0:  
00211                     self.doVar("n_exp_bin%s_proc_%s[%g]" % (b, p, 0))
00212                     continue
00213                 # collect multiplicative corrections
00214                 nominal   = self.DC.exp[b][p]
00215                 gamma     = None; #  gamma normalization (if present, DC.exp[b][p] is ignored)
00216                 factors   = [] # RooAbsReal multiplicative factors (including gmN)
00217                 logNorms  = [] # (kappa, RooAbsReal) lnN (or lnN)
00218                 alogNorms = [] # (kappaLo, kappaHi, RooAbsReal) asymm lnN
00219                 if scale == 1:
00220                     pass
00221                 elif type(scale) == str: 
00222                     factors.append(scale)
00223                 else:
00224                     raise RuntimeError, "Physics model returned something which is neither a name, nor 0, nor 1."
00225                 for (n,nofloat,pdf,args,errline) in self.DC.systs:
00226                     if pdf == "param" or pdf.startswith("shape"): continue
00227                     if not errline[b].has_key(p): continue
00228                     if errline[b][p] == 0.0: continue
00229                     if pdf.startswith("shape") and pdf.endswith("?"): # might be a lnN in disguise
00230                         if not self.isShapeSystematic(b,p,n): pdf = "lnN"
00231                     if pdf == "lnN" and errline[b][p] == 1.0: continue
00232                     if pdf == "lnN" or pdf == "lnU":
00233                         if type(errline[b][p]) == list:
00234                             elow, ehigh = errline[b][p];
00235                             alogNorms.append((elow, ehigh, n))
00236                         else:
00237                             logNorms.append((errline[b][p], n))
00238                     elif pdf == "gmM":
00239                         factors.append(n)
00240                     elif pdf == "trG" or pdf == "unif":
00241                         myname = "n_exp_shift_bin%s_proc_%s_%s" % (b,p,n)
00242                         self.doObj(myname, ROOFIT_EXPR, "'1+%f*@0', %s" % (errline[b][p], n));
00243                         factors.append(myname)
00244                     elif pdf == "gmN":
00245                         factors.append(n)
00246                         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]):
00247                             raise RuntimeError, "Values of N = %d, alpha = %g don't match with expected rate %g for systematics %s " % (
00248                                                     args[0], errline[b][p], self.DC.exp[b][p], n)
00249                         if gamma != None:
00250                             raise RuntimeError, "More than one gmN uncertainty for the same bin and process (second one is %s)" % n
00251                         gamma = n; nominal = errline[b][p]; 
00252                     else: raise RuntimeError, "Unsupported pdf %s" % pdf
00253                 # optimize constants
00254                 if len(factors) + len(logNorms) + len(alogNorms) == 0:
00255                     self.doVar("n_exp_bin%s_proc_%s[%g]" % (b, p, self.DC.exp[b][p]))
00256                 else:
00257                     #print "Process %s of bin %s depends on:\n\tlog-normals: %s\n\tasymm log-normals: %s\n\tother factors: %s\n" % (p,b,logNorms, alogNorms, factors)
00258                     procNorm = ROOT.ProcessNormalization("n_exp_bin%s_proc_%s" % (b,p), "", nominal)
00259                     for kappa, thetaName in logNorms: procNorm.addLogNormal(kappa, self.out.function(thetaName))
00260                     for kappaLo, kappaHi, thetaName in alogNorms: procNorm.addAsymmLogNormal(kappaLo, kappaHi, self.out.function(thetaName))
00261                     for factorName in factors: procNorm.addOtherFactor(self.out.function(factorName))
                    self.out._import(procNorm)
def ModelTools::ModelBuilder::doIndividualModels (   self)
create pdf_bin<X> and pdf_bin<X>_bonly for each bin

Reimplemented in ModelTools::CountingModelBuilder.

Definition at line 262 of file ModelTools.py.

00263                                 :
00264         """create pdf_bin<X> and pdf_bin<X>_bonly for each bin"""
        raise RuntimeError, "Not implemented in ModelBuilder"
def ModelTools::ModelBuilder::doModel (   self)

Definition at line 65 of file ModelTools.py.

00066                      :
00067         self.doObservables()
00068         self.physics.doParametersOfInterest()
00069         self.physics.preProcessNuisances(self.DC.systs)
00070         self.doNuisances()
00071         self.doExpectedEvents()
00072         self.doIndividualModels()
00073         self.doCombination()
00074         if self.options.bin:
00075             self.doModelConfigs()
00076             if self.options.verbose > 1: self.out.Print("V")
00077             if self.options.verbose > 2: 
00078                 print "Wrote GraphVizTree of model_s to ",self.options.out+".dot"
                self.out.pdf("model_s").graphVizTree(self.options.out+".dot", "\\n")
def ModelTools::ModelBuilder::doModelConfigs (   self)

Definition at line 268 of file ModelTools.py.

00269                             :
00270         if not self.options.bin: raise RuntimeException
00271         if self.options.out == None: raise RuntimeException
00272         for nuis,warn in self.DC.flatParamNuisances.iteritems():
00273             if self.out.var(nuis): self.out.var(nuis).setAttribute("flatParam")
00274             elif warn: stderr.write("Missing variable %s declared as flatParam\n" % nuis)
00275         mc_s = ROOT.RooStats.ModelConfig("ModelConfig",       self.out)
00276         mc_b = ROOT.RooStats.ModelConfig("ModelConfig_bonly", self.out)
00277         for (l,mc) in [ ('s',mc_s), ('b',mc_b) ]:
00278             mc.SetPdf(self.out.pdf("model_"+l))
00279             mc.SetParametersOfInterest(self.out.set("POI"))
00280             mc.SetObservables(self.out.set("observables"))
00281             if len(self.DC.systs):  mc.SetNuisanceParameters(self.out.set("nuisances"))
00282             if self.out.set("globalObservables"): mc.SetGlobalObservables(self.out.set("globalObservables"))
00283             if self.options.verbose > 1: mc.Print("V")
00284             self.out._import(mc, mc.GetName())
        self.out.writeToFile(self.options.out)
def ModelTools::ModelBuilder::doNuisances (   self)

Definition at line 82 of file ModelTools.py.

00083                          :
00084         if len(self.DC.systs) == 0: return
00085         self.doComment(" ----- nuisances -----")
00086         globalobs = []
00087         for (n,nofloat,pdf,args,errline) in self.DC.systs: 
00088             if pdf == "lnN" or pdf.startswith("shape"):
00089                 r = "-4,4" if pdf == "shape" else "-7,7"
00090                 self.doObj("%s_Pdf" % n, "Gaussian", "%s[%s], %s_In[0,%s], 1" % (n,r,n,r));
00091                 globalobs.append("%s_In" % n)
00092                 if self.options.bin:
00093                   self.out.var("%s_In" % n).setConstant(True)
00094             elif pdf == "gmM":
00095                 val = 0;
00096                 for c in errline.values(): #list channels
00097                   for v in c.values():     # list effects in each channel 
00098                     if v != 0:
00099                         if val != 0 and v != val: 
00100                             raise RuntimeError, "Error: line %s contains two different uncertainties %g, %g, which is not supported for gmM" % (n,v,val)
00101                         val = v;
00102                 if val == 0: raise RuntimeError, "Error: line %s contains all zeroes"
00103                 theta = val*val; kappa = 1/theta
00104                 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))
00105                 globalobs.append("%s_In" % n)
00106                 if self.options.bin: self.out.var("%s_In" % n).setConstant(True)
00107             elif pdf == "gmN":
00108                 if False:
00109                     # old version, that creates a poisson with a very large range
00110                     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))
00111                 else:
00112                     # new version, that creates a poisson with a narrower range (but still +/- 7 sigmas)
00113                     #print "Searching for bounds for",n,"poisson with obs",args[0]
00114                     minExp = args[0]+1 if args[0] > 0 else 0;
00115                     while (ROOT.TMath.Poisson(args[0], minExp) > 1e-12) and minExp > 0: 
00116                         #print "Poisson(%d, minExp = %f) = %g > 1e-12" % (args[0], minExp, ROOT.TMath.Poisson(args[0], minExp))
00117                         minExp *= 0.8;
00118                     maxExp = args[0]+1;
00119                     while (ROOT.TMath.Poisson(args[0], maxExp) > 1e-12): 
00120                         #print "Poisson(%d, maxExp = %f) = %g > 1e-12" % (args[0], maxExp, ROOT.TMath.Poisson(args[0], maxExp))
00121                         maxExp *= 1.2;
00122                     minObs = args[0];
00123                     while minObs > 0 and (ROOT.TMath.Poisson(minObs, args[0]+1) > 1e-12): 
00124                         #print "Poisson(minObs = %d, %f) = %g > 1e-12" % (minObs, args[0]+1, ROOT.TMath.Poisson(minObs, args[0]+1))
00125                         minObs -= (sqrt(args[0]) if args[0] > 10 else 1);
00126                     maxObs = args[0]+2;
00127                     while (ROOT.TMath.Poisson(maxObs, args[0]+1) > 1e-12): 
00128                         #print "Poisson(maxObs = %d, %f) = %g > 1e-12" % (maxObs, args[0]+1, ROOT.TMath.Poisson(maxObs, args[0]+1))
00129                         maxObs += (sqrt(args[0]) if args[0] > 10 else 2);
00130                     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))
00131                 globalobs.append("%s_In" % n)
00132                 if self.options.bin: self.out.var("%s_In" % n).setConstant(True)
00133             elif pdf == "trG":
00134                 trG_min = -7; trG_max = +7;
00135                 for b in errline.keys():
00136                     for v in errline[b].values():
00137                         if v > 0 and 1.0 + trG_min * v < 0: trG_min = -1.0/v;
00138                         if v < 0 and 1.0 + trG_max * v < 0: trG_max = -1.0/v;
00139                 r = "%f,%f" % (trG_min,trG_max);
00140                 self.doObj("%s_Pdf" % n, "Gaussian", "%s[0,%s], %s_In[0,%s], 1" % (n,r,n,r));
00141                 globalobs.append("%s_In" % n)
00142                 if self.options.bin:
00143                   self.out.var("%s_In" % n).setConstant(True)
00144             elif pdf == "lnU":
00145                 self.doObj("%s_Pdf" % n, "Uniform", "%s[-1,1]" % n);
00146             elif pdf == "unif":
00147                 self.doObj("%s_Pdf" % n, "Uniform", "%s[%f,%f]" % (n,args[0],args[1]))
00148             elif pdf == "param":
00149                 mean = float(args[0])
00150                 if "/" in args[1]: 
00151                     sigmaL,sigmaR = args[1].split("/")
00152                     if sigmaL[0] != "-" or sigmaR[0] != "+": raise RuntimeError, "Asymmetric parameter uncertainties should be entered as -x/+y"
00153                     sigmaL = sigmaL[1:]; sigmaR = sigmaR[1:]
00154                     if len(args) == 3: # mean, sigma, range
00155                         self.doVar("%s%s" % (n,args[2]))
00156                     else:
00157                         sigma = float(args[1])
00158                         if self.out.var(n):
00159                           self.out.var(n).setConstant(False)
00160                           self.out.var(n).setRange(mean-4*float(sigmaL),mean+4*float(sigmaR))
00161                         else:
00162                           self.doVar("%s[%g,%g]" % (n, mean-4*float(sigmaL), mean+4*float(sigmaR)))
00163                     self.out.var(n).setVal(mean)
00164                     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))
00165                     self.out.var("%s_In" % n).setConstant(True)
00166                 else:
00167                     if len(args) == 3: # mean, sigma, range
00168                         self.doVar("%s%s" % (n,args[2]))
00169                     else:
00170                         sigma = float(args[1])
00171                         if self.out.var(n):
00172                           self.out.var(n).setConstant(False)
00173                           self.out.var(n).setRange(mean-4*sigma, mean+4*sigma)
00174                         else:
00175                           self.doVar("%s[%g,%g]" % (n, mean-4*sigma, mean+4*sigma))
00176                     self.out.var(n).setVal(mean)
00177                     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]))
00178                     self.out.var("%s_In" % n).setConstant(True)
00179                 globalobs.append("%s_In" % n)
00180             else: raise RuntimeError, "Unsupported pdf %s" % pdf
00181             if nofloat: 
00182               self.out.var("%s" % n).setAttribute("globalConstrained",True)
00183         if self.options.bin:
00184             nuisPdfs = ROOT.RooArgList()
00185             nuisVars = ROOT.RooArgSet()
00186             for (n,nf,p,a,e) in self.DC.systs:
00187                 nuisVars.add(self.out.var(n))
00188                 nuisPdfs.add(self.out.pdf(n+"_Pdf"))
00189             self.out.defineSet("nuisances", nuisVars)
00190             self.out.nuisPdf = ROOT.RooProdPdf("nuisancePdf", "nuisancePdf", nuisPdfs)
00191             self.out._import(self.out.nuisPdf)
00192             self.out.nuisPdfs = nuisPdfs
00193             gobsVars = ROOT.RooArgSet()
00194             for g in globalobs: gobsVars.add(self.out.var(g))
00195             self.out.defineSet("globalObservables", gobsVars)
00196         else: # doesn't work for too many nuisances :-(
00197             self.doSet("nuisances", ",".join(["%s"    % n for (n,nf,p,a,e) in self.DC.systs]))
00198             self.doObj("nuisancePdf", "PROD", ",".join(["%s_Pdf" % n for (n,nf,p,a,e) in self.DC.systs]))
            self.doSet("globalObservables", ",".join(globalobs))
def ModelTools::ModelBuilder::doObservables (   self)
create pdf_bin<X> and pdf_bin<X>_bonly for each bin

Reimplemented in ModelTools::CountingModelBuilder.

Definition at line 79 of file ModelTools.py.

00080                            :
00081         """create pdf_bin<X> and pdf_bin<X>_bonly for each bin"""
        raise RuntimeError, "Not implemented in ModelBuilder"
def ModelTools::ModelBuilder::isShapeSystematic (   self,
  channel,
  process,
  syst 
)

Definition at line 285 of file ModelTools.py.

00286                                                     :
00287         return False

def ModelTools::ModelBuilder::setPhysics (   self,
  physicsModel 
)

Definition at line 62 of file ModelTools.py.

00063                                      :
00064         self.physics = physicsModel
        self.physics.setModelBuilder(self)

Member Data Documentation

Definition at line 59 of file ModelTools.py.

Definition at line 62 of file ModelTools.py.