### FFT ConvPdf # original_binnning = old_workspace.var("rrv_mass_lvj").getBin(); # old_workspace.var("rrv_mass_lvj").setBins(1000,"cache"); # model_pdf = RooFFTConvPdf("BulkWW_xww_%s_%s"%(options.channel,options.category),"BulkWW_xww_%s_%s"%(options.channel,options.category),old_workspace.var("rrv_mass_lvj"),bw,new_signal); # model_pdf.setBufferFraction(1.0); # old_workspace.var("rrv_mass_lvj").setBins(int(original_binnning)); # model_pdf.SetName("BulkWW_xww_%s_%s"%(options.channel,options.category)); # getattr(new_workspace,"import")(model_pdf); getattr(new_workspace, "import")(new_signal_ggH) getattr(new_workspace, "import")(new_signal_vbfH) ### iterate on the workspace element parameters print "----------- Parameter Workspace -------------" parameters_workspace = new_workspace.allVars() par = parameters_workspace.createIterator() par.Reset() param = par.Next() while param: param.Print() param = par.Next() print "---------------------------------------------" new_workspace.Print() new_workspace.writeToFile(new_file.GetName()) new_file.Close() """ ## create a frame with data mplot = old_workspace.var("rrv_mass_lvj").frame(RooFit.Title(""), RooFit.Bins(int(old_workspace.var("rrv_mass_lvj").getBins()/10)),RooFit.Range(700,3000));
class Wjj2DFitter: def __init__ (self, pars): self.pars = pars self.ws = RooWorkspace('wjj2dfitter') self.utils = Wjj2DFitterUtils(self.pars) self.useImportPars = False self.rangeString = None obs = [] for v in self.pars.var: try: vName = self.pars.varNames[v] except AttributeError: vName = v obs.append(vName) var1 = self.ws.factory('%s[%f,%f]' % (vName, self.pars.varRanges[v][1], self.pars.varRanges[v][2]) ) var1.setUnit('GeV') try: var1.SetTitle(self.pars.varTitles[v]) except AttributeError: var1.SetTitle('m_{jj}') var1.setPlotLabel(var1.GetTitle()) if len(self.pars.varRanges[v][3]) > 1: vbinning = RooBinning(len(self.pars.varRanges[v][3]) - 1, array('d', self.pars.varRanges[v][3]), '%sBinning' % vName) var1.setBinning(vbinning) else: var1.setBins(self.pars.varRanges[v][0]) var1.Print() if v in self.pars.exclude: var1.setRange('signalRegion', self.pars.exclude[v][0], self.pars.exclude[v][1]) var1.setRange('lowSideband', var1.getMin(), self.pars.exclude[v][0]) var1.setRange('highSideband', self.pars.exclude[v][1], var1.getMax()) self.rangeString = 'lowSideband,highSideband' if hasattr(self.pars, 'plotRanges'): var1.setRange('plotRange', self.pars.plotRanges[v][1], self.pars.plotRanges[v][2]) var1.setBins(self.pars.plotRanges[v][0], 'plotBins') else: var1.setRange('plotRange', var1.getMin(), var1.getMax()) var1.setBins(var1.getBins(), 'plotBins') self.ws.defineSet('obsSet', ','.join(obs)) def loadDataFromWorkspace(self, other, cut = None): #pull unbinned data from other workspace unbinnedData = other.data('data_unbinned') if not unbinnedData: unbinnedData = other.data('data_obs') if cut: unbinnedData = unbinnedData.reduce(cut) unbinnedData.Print() if self.pars.binData: #bin and import data unbinnedData.SetName('data_unbinned') getattr(self.ws, 'import')(unbinnedData) data = RooDataHist('data_obs', 'data_obs', other.set('obsSet'), unbinnedData) getattr(self.ws, 'import')(data) else: #just import data unbinnedData.SetName('data_obs') getattr(self.ws, 'import')(unbinnedData) def loadHistogramsFromWorkspace(self, other): #pull RooHist pdfs from other workspace pdfs = other.allPdfs() pdfIter = pdfs.createIterator() pdf = pdfIter.Next() while pdf: if pdf.IsA().InheritsFrom('RooHistPdf'): print 'importing',pdf.GetName(),'from old workspace' getattr(self.ws, 'import')(pdf) pdf = pdfIter.Next() def loadWorkspaceFromFile(self, filename, wsname = 'w', getFloatPars = True): print 'loading data workspace %s from file %s' % (wsname, filename) fin = TFile.Open(filename) if not fin: print 'failed to open the file',filename import os print 'cwd:',os.getcwd() print 'access of',filename,os.access(filename, os.R_OK) print 'list of root files in cwd' for f in os.listdir(os.getcwd()): if f[-5:] == '.root': print f,len(f),len(filename) fin = TFile.Open(os.getcwd() + '/' + filename) assert(fin) other = fin.Get(wsname) #pull unbinned data from other workspace self.loadDataFromWorkspace(other) #pull in histogram pdfs to save time self.loadHistogramsFromWorkspace(other) if getFloatPars and other.loadSnapshot('fitPars'): self.useImportPars = True self.ws.saveSnapshot('importParams', other.set('floatingParams'), True) # self.ws.Print() # put together a fitting model and return the pdf def makeFitter(self, useAlternateModels = False): if self.ws.pdf('total'): return self.ws.pdf('total') compPdfs = [] for component in self.pars.backgrounds: # print 'getting compModels' compModels = getattr(self.pars, '%sModels' % component) if hasattr(self.pars, '%sConvModels' % component): convModels = getattr(self.pars, '%sConvModels' % component) else: convModels = None if useAlternateModels: print 'loading Alternate Models' compModels = getattr(self.pars, '%sModelsAlt' % component) convModels = getattr(self.pars, '%sConvModelsAlt' % component) # print 'compModels = %s' % compModels compFiles = getattr(self.pars, '%sFiles' % component) compPdf = self.makeComponentPdf(component, compFiles, compModels, useAlternateModels, convModels) norm = self.ws.factory('prod::f_%s_norm' % component + \ '(n_%s[0.,1e6],' % component + \ '%s_nrm[1.,-0.5,5.])' % component) self.ws.var('n_%s' % component).setConstant(True) if hasattr(self, '%sExpected' % component): self.ws.var('n_%s' % component).setVal( getattr(self, '%sExpected' % component)) compPdfs.append( self.ws.factory('RooExtendPdf::%s_extended(%s,%s)' % \ (compPdf.GetName(), compPdf.GetName(), norm.GetName()) ) ) self.ws.factory('r_signal[0., -200., 200.]') self.ws.var('r_signal').setConstant(False) try: obs = [ self.pars.varNames[x] for x in self.pars.var ] except AttributeError: obs = self.pars.var for component in self.pars.signals: compFile = getattr(self.pars, '%sFiles' % component) compModels = getattr(self.pars, '%sModels' % component) if hasattr(self.pars, '%sConvModels' % component): convModels = getattr(self.pars, '%sConvModels' % component) else: convModels = None compPdf = self.makeComponentPdf(component, compFiles, compModels, useAlternateModels, convModels) norm = self.ws.factory( "prod::f_%s_norm(n_%s[0., 1e6],r_signal)" % \ (component, component) ) self.ws.var('n_%s' % component).setConstant(True) if hasattr(self, '%sExpected' % component): self.ws.var('n_%s' % component).setVal( getattr(self, '%sExpected' % component)) pdf = self.ws.factory('RooExtendPdf::%s_extended(%s,%s)' % \ (compPdf.GetName(), compPdf.GetName(), norm.GetName()) ) if (hasattr(self.pars, '%sInterference' % component)) and \ getattr(self.pars, '%sInterference' % component): getattr(self.ws, 'import') \ (pdf, RooFit.RenameAllNodes('interf_%sUp' % component), RooFit.RenameAllVariablesExcept('interf_%sUp' % component, ','.join(obs)), RooFit.Silence() ) getattr(self.ws, 'import') \ (pdf, RooFit.RenameAllNodes('interf_%sDown' % component), RooFit.RenameAllVariablesExcept('interf_%sDown'%component, ','.join(obs)), RooFit.Silence() ) if self.pars.includeSignal: compPdfs.append(pdf) #print compPdfs prodList = [ '%s' % (pdf.GetName()) \ for (idx, pdf) in enumerate(compPdfs) ] comps = RooArgList(self.ws.argSet(','.join(prodList))) getattr(self.ws, 'import')(RooAddPdf('total', 'total', comps)) return self.ws.pdf('total') # define the constraints on the yields, etc that will be part of the fit. def makeConstraints(self): if self.ws.set('constraintSet'): return self.ws.set('constraintSet') constraints = [] constrainedParameters = [] for constraint in self.pars.yieldConstraints: theYield = self.ws.var('%s_nrm' % constraint) if not theYield.isConstant(): self.ws.factory('RooGaussian::%s_const(%s, 1.0, %f)' % \ (constraint, theYield.GetName(), self.pars.yieldConstraints[constraint]) ) constraints.append('%s_const' % constraint) constrainedParameters.append(theYield.GetName()) if hasattr(self.pars, 'constrainShapes'): for component in self.pars.constrainShapes: pc = self.ws.pdf(component).getParameters(self.ws.set('obsSet')) parIter = pc.createIterator() par = parIter.Next() while par: if not par.isConstant(): theConst = self.ws.factory('RooGaussian::%s_const' % \ (par.GetName()) + \ '(%s, %f, %f)' % \ (par.GetName(), par.getVal(), par.getError()) ) constraints.append(theConst.GetName()) constrainedParameters.append(par.GetName()) par = parIter.Next() pc.IsA().Destructor(pc) self.ws.defineSet('constraintSet', ','.join(constraints)) self.ws.defineSet('constrainedSet', ','.join(constrainedParameters)) return self.ws.set('constraintSet') # make the constrained fitter def makeConstrainedFitter(self): if self.ws.pdf('totalFit_const'): return self.ws.pdf('totalFit_const') constraintSet = self.makeConstraints() fitter = self.makeFitter() print '\nfit constraints' constIter = constraintSet.createIterator() constraint = constIter.Next() constraints = [] while constraint: constraint.Print() constraints.append(constraint.GetName()) constraint = constIter.Next() if constraintSet.getSize() > 0: constraints.append(fitter.GetName()) fitter = self.ws.factory('PROD::totalFit_const(%s)' % \ (','.join(constraints)) ) return fitter # fit the data using the pdf def fit(self, keepParameterValues = False, overrideRangeCmd = False): print 'construct fit pdf ...' fitter = self.makeFitter() print 'load data ...' data = self.loadData() self.resetYields() constraintSet = self.makeConstraints() if not keepParameterValues: self.readParametersFromFile() self.resetYields() # print constraints, self.pars.yieldConstraints constraintCmd = RooCmdArg.none() if constraintSet.getSize() > 0: fitter = self.makeConstrainedFitter() constraintCmd = RooFit.Constrained() # constraintCmd = RooFit.ExternalConstraints(self.ws.set('constraintSet')) if self.useImportPars: self.ws.loadSnapshot('importParams') self.ws.Print() # for constraint in pars.constraints: # self.ws.pdf(constraint).Print() # print rangeCmd = RooCmdArg.none() if self.rangeString and self.pars.doExclude and not overrideRangeCmd: rangeCmd = RooFit.Range(self.rangeString) # print 'scanning parameter values...' # fitter.fitTo(data, RooFit.Minos(False), # RooFit.PrintEvalErrors(-1), # RooFit.Warnings(False), # RooFit.Minimizer("Minuit2", "scan"), # RooFit.PrintLevel(0), # constraintCmd, # rangeCmd) print 'fitting ...' fr = fitter.fitTo(data, RooFit.Save(True), # RooFit.Extended(True), RooFit.Minos(False), RooFit.PrintEvalErrors(-1), RooFit.Warnings(False), RooFit.Minimizer("Minuit2", "minimize"), constraintCmd, rangeCmd ) fr.Print('v') return fr # determine the fitting model for each component and return them def makeComponentPdf(self, component, files, models, useAlternateModels, convModels): print 'making ComponentPdf %s' % component # print 'models = %s' % models # print 'files = %s' % files if convModels and not (convModels[0] == -1): thePdf = self.makeConvolvedPdf(component, files, models, useAlternateModels, convModels) elif (models[0] == -1): thePdf = self.makeComponentHistPdf(component, files) elif (models[0] == -2): thePdf = self.makeMorphingPdf(component, useAlternateModels, convModels) elif (models[0] == -3): pass else: thePdf = self.makeComponentAnalyticPdf(component, models, useAlternateModels) return thePdf #create a simple 2D histogram pdf def makeComponentHistPdf(self, component, files): if self.ws.pdf(component): return self.ws.pdf(component) compHist = self.utils.newEmptyHist('hist%s' % component) sumYields = 0. sumxsec = 0. sumExpected = 0. for (idx,fset) in enumerate(files): if hasattr(self.pars, '%scuts' % component): cutOverride = getattr(self.pars, '%scuts' % component) else: cutOverride = None filename = fset[0] tmpHist = self.utils.File2Hist(filename, 'hist%s_%i' % (component, idx), False,cutOverride,False,True,0) sumYields += tmpHist.Integral() sumxsec += fset[2] compHist.Add(tmpHist, self.pars.integratedLumi*fset[2]/fset[1]) sumExpected += tmpHist.Integral()*fset[2]* \ self.pars.integratedLumi/fset[1] print filename,'acc x eff: %.3g' % (tmpHist.Integral()/fset[1]) print filename,'N_expected: %.1f' % \ (tmpHist.Integral()*fset[2]*self.pars.integratedLumi/fset[1]) #tmpHist.Print() #compHist.Print() print '%s acc x eff: %.3g' % \ (component, sumExpected/sumxsec/self.pars.integratedLumi) print 'Number of expected %s events: %.1f' % (component, sumExpected) setattr(self, '%sExpected' % component, sumExpected) return self.utils.Hist2Pdf(compHist, component, self.ws, self.pars.order) #create a pdf which is a convolution of any two pdf def makeConvolvedPdf(self, component, files, models, useAlternateModels, convModels): if self.ws.pdf(component): return self.ws.pdf(component) #If a morphing model is selected, then convolve each individual component first and then morph if (models[0] == -2): return self.makeMorphingPdf(component, useAlternateModels, convModels) basePdf = self.makeComponentPdf('%s_base' % component, files, models, useAlternateModels, [-1]) convComponent = 'Global' ##Overwrite to use the same convolution model for all Pdfs convModel = getattr(self.pars, '%sConvModels' % convComponent) if useAlternateModels: convModel = getattr(self.pars, '%sConvModelsAlt' % convComponent) convPdf = self.makeComponentPdf('%s_conv' % convComponent, files, convModel, useAlternateModels, [-1]) var = self.pars.var[0] try: vName = self.pars.varNames[var] except AttributeError: vName = var self.ws.factory('RooFFTConvPdf::%s(%s,%s,%s)' % \ (component, vName, basePdf.GetName(), convPdf.GetName())) return self.ws.pdf(component) # create a pdf using the "template morphing" technique def makeMorphingPdf(self, component, useAlternateModels, convModels): if self.ws.pdf(component): return self.ws.pdf(component) filesNom = getattr(self.pars, '%s_NomFiles' % component) modelsNom = getattr(self.pars, '%s_NomModels' % component) filesMU = getattr(self.pars, '%s_MUFiles' % component) modelsMU = getattr(self.pars, '%s_MUModels' % component) filesMD = getattr(self.pars, '%s_MDFiles' % component) modelsMD = getattr(self.pars, '%s_MDModels' % component) filesSU = getattr(self.pars, '%s_SUFiles' % component) modelsSU = getattr(self.pars, '%s_SUModels' % component) filesSD = getattr(self.pars, '%s_SDFiles' % component) modelsSD = getattr(self.pars, '%s_SDModels' % component) if useAlternateModels: modelsNom = getattr(self.pars, '%s_NomModelsAlt' % component) modelsMU = getattr(self.pars, '%s_MUModelsAlt' % component) modelsMD = getattr(self.pars, '%s_MDModelsAlt' % component) modelsSU = getattr(self.pars, '%s_SUModelsAlt' % component) modelsSD = getattr(self.pars, '%s_SDModelsAlt' % component) # Adds five (sub)components for the component with suffixes Nom, MU, MD, SU, SD NomPdf = self.makeComponentPdf('%s_Nom' % component, filesNom, modelsNom, False, convModels) if hasattr(self, '%s_NomExpected' % component): setattr(self, '%sExpected' % component, getattr(self, '%s_NomExpected' % component)) MUPdf = self.makeComponentPdf('%s_MU' % component, filesMU, modelsMU, False, convModels) MDPdf = self.makeComponentPdf('%s_MD' % component, filesMD, modelsMD, False, convModels) SUPdf = self.makeComponentPdf('%s_SU' % component, filesSU, modelsSU, False, convModels) SDPdf = self.makeComponentPdf('%s_SD' % component, filesSD, modelsSD, False, convModels) fMU_comp = self.ws.factory("fMU_%s[0., -1., 1.]" % component) fSU_comp = self.ws.factory("fSU_%s[0., -1., 1.]" % component) fMU = RooFormulaVar("f_fMU_%s" % component, "1.0*@0*(@0 >= 0.)", RooArgList( fMU_comp ) ) fMD = RooFormulaVar("f_fMD_%s" % component, "-1.0*@0*(@0 < 0.)", RooArgList( fMU_comp ) ) fSU = RooFormulaVar("f_fSU_%s" % component, "@0*(@0 >= 0.)", RooArgList( fSU_comp ) ) fSD = RooFormulaVar("f_fSD_%s" % component, "@0*(-1)*(@0 < 0.)", RooArgList( fSU_comp ) ) fNom = RooFormulaVar("f_fNom_%s" % component, "(1.-abs(@0)-abs(@1))", RooArgList(fMU_comp,fSU_comp) ) morphPdf = RooAddPdf(component,component, RooArgList(MUPdf,MDPdf,SUPdf,SDPdf,NomPdf), RooArgList(fMU, fMD, fSU, fSD, fNom)) morphPdf.SetName(component) getattr(self.ws, 'import')(morphPdf) return self.ws.pdf(component) # create a pdf using an analytic function. def makeComponentAnalyticPdf(self, component, models, useAlternateModels): if self.ws.pdf(component): return self.ws.pdf(component) pdfList = [] systMult = None if ( hasattr(self.pars, '%sInterference' % component) and \ getattr(self.pars, '%sInterference' % component) and \ hasattr(self.pars, "%sdoSystMult" % component) and \ getattr(self.pars, "%sdoSystMult" % component) ): systMult = getattr(self.pars, "%sSystMult" % component) for (idx,model) in enumerate(models): var = self.pars.var[idx] try: vName = self.pars.varNames[var] except AttributeError: vName = var auxModel = None if useAlternateModels: if hasattr(self.pars, '%sAuxModelsAlt' % component): auxModel = getattr(self.pars, '%sAuxModelsAlt' % component)[idx] else: if hasattr(self.pars, '%sAuxModels' % component): auxModel = getattr(self.pars, '%sAuxModels' % component)[idx] pdfList.append(self.utils.analyticPdf(self.ws, vName, model, '%s_%s'%(component,vName), '%s_%s'%(component,vName), auxModel, systMult ) ) pdfListNames = [ pdf.GetName() for pdf in pdfList ] if len(pdfList) > 1: self.ws.factory('PROD::%s(%s)' % \ (component, ','.join(pdfListNames))) else: pdfList[0].SetName(component) return self.ws.pdf(component) def loadData(self, weight = False): if self.ws.data('data_obs'): return self.ws.data('data_obs') unbinnedName = 'data_obs' if self.pars.binData: unbinnedName = 'data_unbinned' data = self.utils.File2Dataset(self.pars.DataFile, unbinnedName, self.ws, weighted = weight) if self.pars.binData: data = RooDataHist('data_obs', 'data_obs', self.ws.set('obsSet'), data) getattr(self.ws, 'import')(data) data = self.ws.data('data_obs') return data def stackedPlot(self, var, logy = False, pdfName = None, Silent = False): if not pdfName: pdfName = 'total' xvar = self.ws.var(var) nbins = xvar.getBins() # if hasattr(self.pars, 'plotRanges') and not xvar.hasRange('plotRange'): # xvar.setRange('plotRange', self.pars.plotRanges[var][1], # self.pars.plotRanges[var][2]) # xvar.setBins(self.pars.plotRanges[var][0], 'plotBins') # elif not xvar.hasRange('plotRange'): # xvar.setRange('plotRange', xvar.getMin(), xvar.getMax()) # xvar.setBins(nbins, 'plotBins') sframe = xvar.frame(RooFit.Range('plotRange'), RooFit.Bins(xvar.getBins('plotBins'))) sframe.SetName("%s_stacked" % var) pdf = self.ws.pdf(pdfName) if isinstance(pdf, RooAddPdf): compList = RooArgList(pdf.pdfList()) else: compList = None data = self.ws.data('data_obs') nexp = pdf.expectedEvents(self.ws.set('obsSet')) if not Silent: print pdf.GetName(),'expected: %.0f' % (nexp) print 'data events: %.0f' % (data.sumEntries()) if nexp < 1: nexp = data.sumEntries() theComponents = [] if self.pars.includeSignal: theComponents += self.pars.signals theComponents += self.pars.backgrounds data.plotOn(sframe, RooFit.Invisible(), RooFit.Binning('plotBins')) # dataHist = RooAbsData.createHistogram(data,'dataHist_%s' % var, xvar, # RooFit.Binning('%sBinning' % var)) # #dataHist.Scale(1., 'width') # invData = RooHist(dataHist, 1., 1, RooAbsData.SumW2, 1.0, False) # #invData.Print('v') # sframe.addPlotable(invData, 'pe', True, True) for (idx,component) in enumerate(theComponents): if not Silent: print 'plotting',component,'...', if hasattr(self.pars, '%sPlotting' % (component)): plotCharacteristics = getattr(self.pars, '%sPlotting' % \ (component)) else: plotCharacteristics = {'color' : colorwheel[idx%6], 'title' : component } compCmd = RooCmdArg.none() if compList: compSet = RooArgSet(compList) if compSet.getSize() > 0: compCmd = RooFit.Components(compSet) removals = compList.selectByName('%s*' % component) compList.remove(removals) if not Silent: print 'events', self.ws.function('f_%s_norm' % component).getVal() sys.stdout.flush() if abs(self.ws.function('f_%s_norm' % component).getVal()) >= 1.: pdf.plotOn(sframe, #RooFit.ProjWData(data), RooFit.DrawOption('LF'), RooFit.FillStyle(1001), RooFit.FillColor(plotCharacteristics['color']), RooFit.LineColor(plotCharacteristics['color']), RooFit.VLines(), RooFit.Range('plotRange'), RooFit.NormRange('plotRange'), RooFit.Normalization(nexp, RooAbsReal.NumEvent), compCmd ) tmpCurve = sframe.getCurve() tmpCurve.SetName(component) tmpCurve.SetTitle(plotCharacteristics['title']) if 'visible' in plotCharacteristics: sframe.setInvisible(component, plotCharacteristics['visible']) data.plotOn(sframe, RooFit.Name('theData'), RooFit.Binning('plotBins')) sframe.getHist('theData').SetTitle('data') # theData = RooHist(dataHist, 1., 1, RooAbsData.SumW2, 1.0, True) # theData.SetName('theData') # theData.SetTitle('data') # sframe.addPlotable(theData, 'pe') if (logy): sframe.SetMinimum(0.01) sframe.SetMaximum(1.0e6) else: sframe.SetMaximum(sframe.GetMaximum()*1.35) pass excluded = (var in self.pars.exclude) bname = var if not excluded: for v in self.pars.exclude: if hasattr(self.pars, 'varNames') and \ (self.pars.varNames[v] == var): excluded = True bname = v if excluded: blinder = TBox(self.pars.exclude[bname][0], sframe.GetMinimum(), self.pars.exclude[bname][1], sframe.GetMaximum()) # blinder.SetName('blinder') # blinder.SetTitle('signal region') blinder.SetFillColor(kBlack) if self.pars.blind: blinder.SetFillStyle(1001) else: blinder.SetFillStyle(0) blinder.SetLineStyle(2) sframe.addObject(blinder) elif self.pars.blind: if not Silent: print "blind but can't find exclusion region for", var print 'excluded',excluded,self.pars.exclude print 'hiding data points' sframe.setInvisible('theData', True) else: sframe.setInvisible('theData', False) #sframe.GetYaxis().SetTitle('Events / GeV') # dataHist.IsA().Destructor(dataHist) if not Silent: print xvar.setBins(nbins) return sframe def readParametersFromFile(self, fname=None): if (not fname): fname = self.pars.initialParametersFile if isinstance(fname, str): flist = [ fname ] else: flist = fname for tmpName in flist: if len(tmpName) > 0: print 'loading parameters from file',tmpName self.ws.allVars().readFromFile(tmpName) def expectedFromPars(self): components = self.pars.signals + self.pars.backgrounds for component in components: theYield = self.ws.var('n_%s' % component) setattr(self, '%sExpected' % component, theYield.getVal()) def initFromExplicitVals(self,opts): #,init_diboson= -1.0,init_WpJ=-1.0,init_top=-1.0,init_ZpJ=-1.0,init_QCD=-1.0 components = ['diboson', 'top', 'WpJ', 'ZpJ', 'QCD', 'WHbb'] for component in components: #double init init = getattr(opts, 'ext%s' % component) #init = -2.0 #setattr(self,init, 'init_%s' % component) #init = init_%s % component #print "init=", init #init = self.ws.var('init_%s' % component) #init.setVal(100.0) #init.setVal('init_%s' % component) #init = theYield.getVal() if (init>0.): print 'setting initial value for ',component,' to ',init setattr(self, '%sInitial' % component, init) def resetYields(self): if self.ws.data('data_obs'): Ndata = self.ws.data('data_obs').sumEntries() else: Ndata = 10000. print 'resetting yields...' components = self.pars.signals + self.pars.backgrounds for component in components: theYield = self.ws.var('n_%s' % component) theNorm = self.ws.var('%s_nrm' % component) if hasattr(self, '%sInitial' % component): print 'explicitly setting initial value for ',component theYield.setVal(getattr(self, '%sInitial' % component)) theNorm.setVal(1.0) theNorm.setConstant() else: fracofdata = -1. if hasattr(self.pars, '%sFracOfData' % component): fracofdata = getattr(self.pars, '%sFracOfData' % component) if (fracofdata >= 0.): print 'explicitly setting ', component,' yield to be', fracofdata,' of data' theYield.setVal(fracofdata*Ndata) elif hasattr(self, '%sExpected' % component): theYield.setVal(getattr(self, '%sExpected' % component)) else: print 'no expected value for',component theYield.setVal(Ndata/len(components)) if theNorm and not theNorm.isConstant(): theNorm.setVal(1.0) if component in self.pars.yieldConstraints: theYield.setError(theYield.getVal() * \ self.pars.yieldConstraints[component]) if theNorm: theNorm.setError(self.pars.yieldConstraints[component]) else: theYield.setError(sqrt(theYield.getVal())) theYield.Print() def generateToyMCSet(self,var,inputPdf,outFileName,NEvts): fMC = TFile(outFileName, "RECREATE"); # thevar = self.ws.var(var); print 'thevar=' print var # print thevar print '...' # varList = RooArgList() # varList.add(self.ws.var(var)) toymc = inputPdf.generate(RooArgSet(self.ws.var(var)),NEvts); tMC = toymc.tree(); fMC.cd(); tMC.Write(); fMC.Close(); def legend4Plot(plot, left = False): if left: theLeg = TLegend(0.2, 0.62, 0.55, 0.92, "", "NDC") else: theLeg = TLegend(0.60, 0.62, 0.92, 0.92, "", "NDC") theLeg.SetName('theLegend') theLeg.SetBorderSize(0) theLeg.SetLineColor(0) theLeg.SetFillColor(0) theLeg.SetFillStyle(0) theLeg.SetLineWidth(0) theLeg.SetLineStyle(0) theLeg.SetTextFont(42) theLeg.SetTextSize(.045) entryCnt = 0 for obj in range(0, int(plot.numItems())): objName = plot.nameOf(obj) if (not plot.getInvisible(objName)): theObj = plot.getObject(obj) objTitle = theObj.GetTitle() if len(objTitle) < 1: objTitle = objName dopts = plot.getDrawOptions(objName).Data() # print 'obj:',theObj,'title:',objTitle,'opts:',dopts,'type:',type(dopts) if theObj.IsA().InheritsFrom('TNamed'): theLeg.AddEntry(theObj, objTitle, dopts) entryCnt += 1 theLeg.SetY1NDC(0.9 - 0.05*entryCnt - 0.005) theLeg.SetY1(theLeg.GetY1NDC()) return theLeg legend4Plot = staticmethod(legend4Plot)
def main(options,args): cfg = options.config workspaceName = cfg.get('Global','workspace') ws = RooWorkspace(workspaceName) #ws.Print("v") setupWorkspace(ws,options) #create -log(likelihood) theNLL = ws.pdf('TopLevelPdf').createNLL(ws.data('allcountingdata'), RooFit.NumCPU(1), RooFit.ConditionalObservables(ws.set('condObs')), RooFit.Verbose(True)) ws.saveSnapshot('standardmodel',ws.allVars()) minuit = ROOT.RooMinuit(theNLL) minuit.setPrintLevel(1) minuit.setPrintEvalErrors(-1) minuit.setErrorLevel(.5) #find the values of the parameters that minimize the likelihood minuit.setStrategy(2) minuit.simplex() minuit.migrad() minuit.hesse() #ws.var('err_gl').setConstant(True) #ws.var('err_gs').setConstant(True) #ws.var('err_gb').setConstant(True) ws.defineSet('POI', ROOT.RooArgSet(ws.var('%s_%s'%(cfg.get('Global','par1Name'),cfg.get('Global','couplingType'))), ws.var('%s_%s'%(cfg.get('Global','par2Name'),cfg.get('Global','couplingType'))))) ws.saveSnapshot('%s_fitresult'%cfg.get('Global','couplingType'), ws.allVars()) #create profile likelihood level_68 = ROOT.TMath.ChisquareQuantile(.68,2)/2.0 # delta NLL for 68% confidence level for -log(LR) level_95 = ROOT.TMath.ChisquareQuantile(.95,2)/2.0 # delta NLL for 95% confidence level for -log(LR) print print '68% CL Delta-NLL 2 DOF=',level_68 print '95% CL Delta-NLL 2 DOF=',level_95 minuit.setPrintLevel(1) minuit.setPrintEvalErrors(-1) minuit.migrad() minuit.minos(ws.set('POI')) thePlot = minuit.contour(ws.var('%s_%s'%(cfg.get('Global','par1Name'),cfg.get('Global','couplingType'))), ws.var('%s_%s'%(cfg.get('Global','par2Name'),cfg.get('Global','couplingType'))), sqrt(2*level_95),sqrt(2*level_68)) # here the error is in sigmas thePlot.SetName('%s_%s_%s_contour'%(cfg.get('Global','par1Name'), cfg.get('Global','par2Name'), cfg.get('Global','couplingType'))) thePlot.SetTitle('68% & 95% CL on the Best Fit Values of '+cfg.get('Global','par1Name')+' and '+cfg.get('Global','par2Name')) legend = ROOT.TLegend(2.01612903225806439e-01,7.86016949152542388e-01, 7.15725806451612989e-01,9.13135593220338992e-01) legend.SetNColumns(2) thePlot.addObject(legend) # 1-D Limits level_95 = ROOT.TMath.ChisquareQuantile(.95,1)/2.0 # delta NLL for -log(LR) with 1 dof print '95% CL Delta-NLL 1 DOF=',level_95 minuit.setErrorLevel(level_95) #set 1-D limits on parameter 1 with parameter 2 == 0 ws.var('%s_%s'%(cfg.get('Global','par2Name'),cfg.get('Global','couplingType'))).setVal(0.0) ws.var('%s_%s'%(cfg.get('Global','par2Name'),cfg.get('Global','couplingType'))).setConstant(True) minuit.minos(ws.set('POI')) parm1 = ws.var('%s_%s'%(cfg.get('Global','par1Name'),cfg.get('Global','couplingType'))) print 'parameter 1 value: '+str(parm1.getVal()) if not (0 < parm1.getVal()+parm1.getErrorHi() and 0 > parm1.getVal()+parm1.getErrorLo()): print '95% CL does not cover SM for parameter 1' else: print '95% CL covers SM for parameter 1' par1Line = ROOT.TLine(parm1.getVal()+parm1.getErrorLo(),0, parm1.getVal()+parm1.getErrorHi(),0) par1Line.SetLineWidth(2) par1Line.SetLineColor(ROOT.kRed) thePlot.addObject(par1Line) #set 1-D limits on parameter 2 with parameter 1 == 0 ws.var('%s_%s'%(cfg.get('Global','par2Name'),cfg.get('Global','couplingType'))).setConstant(False) ws.var('%s_%s'%(cfg.get('Global','par1Name'),cfg.get('Global','couplingType'))).setVal(0.0) ws.var('%s_%s'%(cfg.get('Global','par1Name'),cfg.get('Global','couplingType'))).setConstant(True) minuit.minos(ws.set('POI')) parm2 = ws.var('%s_%s'%(cfg.get('Global','par2Name'),cfg.get('Global','couplingType'))) print 'parameter 2 value: '+str(parm2.getVal()) if not (0 < parm2.getVal()+parm2.getErrorHi() and 0 > parm2.getVal()+parm2.getErrorLo()): print '95% CL does not cover SM for parameter 2' else: print '95% CL covers SM for parameter 2' par2Line = ROOT.TLine(0,parm2.getVal()+parm2.getErrorLo(), 0,parm2.getVal()+parm2.getErrorHi()) par2Line.SetLineWidth(2) par2Line.SetLineColor(ROOT.kRed) thePlot.addObject(par2Line) ws.var('%s_%s'%(cfg.get('Global','par1Name'),cfg.get('Global','couplingType'))).setConstant(False) #construct likelihood scan histograms plot = parm1.frame() parm1.setBins(200) parm2.setBins(200) scanHist = ROOT.TH2F('scan2d_plot','2D Scan of the Likelihood', 200,parm1.getMin(),parm1.getMax(), 200,parm2.getMin(),parm2.getMax()) for i in range(200): for j in range(200): parm1.setVal(parm1.getMin() + (i+.5)*(parm1.getMax()-parm1.getMin())/200) parm2.setVal(parm2.getMin() + (j+.5)*(parm2.getMax()-parm2.getMin())/200) scanHist.SetBinContent(i+1,j+1,theNLL.getVal()) profNLL_par1 = theNLL.createProfile(RooArgSet(parm1)) profNLL_par1_plot = parm1.frame() profNLL_par1.plotOn(profNLL_par1_plot) profNLL_par2 = theNLL.createProfile(RooArgSet(parm2)) profNLL_par2_plot = parm2.frame() profNLL_par2.plotOn(profNLL_par2_plot) initCMSStyle() output = TFile.Open(workspaceName+'.root','RECREATE') ws.Write() contCanvas = ROOT.TCanvas('contour_canvas','',500,500) thePlot.Draw() prettyContour(contCanvas,cfg) contCanvas.Write() thePlot.Write() scanCanvas2D = ROOT.TCanvas('scan2d_canvas','',500,500) scanHist.Draw('colz') prettyScan(scanCanvas2D,cfg) scanCanvas2D.Write() scanHist.Write() par1ScanCanvas = ROOT.TCanvas('scan1d_par1','',500,500) par1ScanCanvas.cd() profNLL_par1_plot.Draw() par1ScanCanvas.Write() profNLL_par1_plot.Write() par2ScanCanvas = ROOT.TCanvas('scan1d_par2','',500,500) par2ScanCanvas.cd() profNLL_par2_plot.Draw() par2ScanCanvas.Write() profNLL_par2_plot.Write() prettyObsPlots(ws,cfg) output.Close() if options.makeCards: print print "Creating cards for Higgs Combined Limit calculator!" makeHCLCards(ws,cfg) return 0
class WspaceReader(Path): """Read objects from RooWorkspace""" def __init__(self, cfg): """Init""" super(WspaceReader, self).__init__(cfg) self.reset() return def reset(self): super(WspaceReader, self).reset() self.ifile = None self.wspace = None @classmethod def templateConfig(cls): cfg = { 'name': "WspaceReader", 'fileName': "wspace.root", 'wspaceTag': "DEFAULT", 'obj': OrderedDict([ # ('source_key', 'wspace_key'), ]), } return cfg def _runPath(self): # Hook to registerd file fileName = self.cfg.get('fileName', "") if os.path.exists(fileName): self.ifile = self.process.filemanager.open(str(hex(id(self))), fileName, 'READ') # Load wspace wspaceName = "wspace.{0}".format(self.cfg['wspaceTag']) if wspaceName in self.process.sourcemanager.keys(): self.wspace = self.process.sourcemanager.get(wspaceName) elif not self.ifile == None: self.wspace = self.ifile.Get(wspaceName) # Load desired objects if self.wspace == None: self.logger.logINFO("RooWorkspace {0} not found. Create new workspace without booking target objects.".format(wspaceName)) self.wspace = RooWorkspace(wspaceName) self.cfg['source'][wspaceName] = self.wspace # Update results only for new workspace. # By design you CANNOT overwrite items in RooWorkspace by `Write` or `writeToFile`. self.bookEndSeq() else: self.logger.logINFO("RooWorkspace {0} found. Loading objects...".format(wspaceName)) self.cfg['source'][wspaceName] = self.wspace def readObjs(): """Exactly define how to read varaiables, read `wspace_key` from wspace and book as `source_key`""" for source_key, wspace_key in self.cfg['obj'].items(): if source_key in self.process.sourcemanager.keys(): self.cfg['source'][source_key] = self.process.sourcemanager.get(source_key) else: obj = self.wspace.obj(wspace_key) if obj == None: self.logger.logWARNING("No Variable {0} is found.".format(wspace_key)) else: self.cfg['source'][source_key] = obj def readAll(iArgs): def bookOne(arg): argName = arg.GetName() if argName in self.process.sourcemanager: self.cfg['source'][argName] = self.process.sourcemanager.get(argName) else: self.cfg['source'][argName] = arg FitterCore.ArgLooper(iArgs, bookOne) if self.cfg['obj']: readObjs() else: readAll(self.wspace.allFunctions()) readAll(self.wspace.allPdfs()) readAll(self.wspace.allVars()) def bookEndSeq(self): """Book to p.endSeq stack. Assign new order in case of multiple calls.""" if hex(id(self.wspace)) in self.process._services.keys(): del self.process._services[id(self.wspace)] self.process._services[hex(id(self.wspace))] = self def _endSeq(self): """Write wspace to registered file""" if self.ifile == None and 'fileName' in self.cfg.keys(): self.wspace.writeToFile(self.cfg['fileName'], True)
class Wjj2DFitter: def __init__ (self, pars): self.pars = pars self.ws = RooWorkspace('wjj2dfitter') self.utils = Wjj2DFitterUtils(self.pars) self.useImportPars = False self.rangeString = None obs = [] for v in self.pars.var: try: vName = self.pars.varNames[v] except AttributeError: vName = v obs.append(vName) var1 = self.ws.factory('%s[%f,%f]' % (vName, self.pars.varRanges[v][1], self.pars.varRanges[v][2]) ) var1.setUnit('GeV') try: var1.SetTitle(self.pars.varTitles[v]) except AttributeError: var1.SetTitle('m_{jj}') var1.setPlotLabel(var1.GetTitle()) if len(self.pars.varRanges[v][3]) > 1: vbinning = RooBinning(len(self.pars.varRanges[v][3]) - 1, array('d', self.pars.varRanges[v][3]), '%sBinning' % vName) var1.setBinning(vbinning) else: var1.setBins(self.pars.varRanges[v][0]) var1.Print() if v in self.pars.exclude: var1.setRange('signalRegion', self.pars.exclude[v][0], self.pars.exclude[v][1]) var1.setRange('lowSideband', var1.getMin(), self.pars.exclude[v][0]) var1.setRange('highSideband', self.pars.exclude[v][1], var1.getMax()) self.rangeString = 'lowSideband,highSideband' self.ws.defineSet('obsSet', ','.join(obs)) def loadDataFromWorkspace(self, other, cut = None): #pull unbinned data from other workspace unbinnedData = other.data('data_unbinned') if not unbinnedData: unbinnedData = other.data('data_obs') if cut: unbinnedData = unbinnedData.reduce(cut) unbinnedData.Print() if self.pars.binData: #bin and import data unbinnedData.SetName('data_unbinned') getattr(self.ws, 'import')(unbinnedData) data = RooDataHist('data_obs', 'data_obs', other.set('obsSet'), unbinnedData) getattr(self.ws, 'import')(data) else: #just import data unbinnedData.SetName('data_obs') getattr(self.ws, 'import')(unbinnedData) def loadHistogramsFromWorkspace(self, other): #pull RooHist pdfs from other workspace pdfs = other.allPdfs() pdfIter = pdfs.createIterator() pdf = pdfIter.Next() while pdf: if pdf.IsA().InheritsFrom('RooHistPdf'): print 'importing',pdf.GetName(),'from old workspace' getattr(self.ws, 'import')(pdf) pdf = pdfIter.Next() def loadWorkspaceFromFile(self, filename, wsname = 'w', getFloatPars = True): print 'loading data workspace %s from file %s' % (wsname, filename) fin = TFile.Open(filename) if not fin: print 'failed to open the file',filename import os print 'cwd:',os.getcwd() print 'access of',filename,os.access(filename, os.R_OK) print 'list of root files in cwd' for f in os.listdir(os.getcwd()): if f[-5:] == '.root': print f,len(f),len(filename) fin = TFile.Open(os.getcwd() + '/' + filename) assert(fin) other = fin.Get(wsname) #pull unbinned data from other workspace self.loadDataFromWorkspace(other) #pull in histogram pdfs to save time self.loadHistogramsFromWorkspace(other) if getFloatPars and other.loadSnapshot('fitPars'): self.useImportPars = True self.ws.saveSnapshot('importParams', other.set('floatingParams'), True) # self.ws.Print() # put together a fitting model and return the pdf def makeFitter(self, useAlternateModels = False): if self.ws.pdf('total'): return self.ws.pdf('total') compPdfs = [] for component in self.pars.backgrounds: # print 'getting compModels' compModels = getattr(self.pars, '%sModels' % component) if hasattr(self.pars, '%sConvModels' % component): convModels = getattr(self.pars, '%sConvModels' % component) else: convModels = None if useAlternateModels: print 'loading Alternate Models' compModels = getattr(self.pars, '%sModelsAlt' % component) convModels = getattr(self.pars, '%sConvModelsAlt' % component) # print 'compModels = %s' % compModels compFiles = getattr(self.pars, '%sFiles' % component) compPdf = self.makeComponentPdf(component, compFiles, compModels, useAlternateModels, convModels) norm = self.ws.factory('prod::f_%s_norm' % component + \ '(n_%s[0.,1e6],' % component + \ '%s_nrm[1.,-0.5,5.])' % component) self.ws.var('n_%s' % component).setConstant(True) if hasattr(self, '%sExpected' % component): self.ws.var('n_%s' % component).setVal( getattr(self, '%sExpected' % component)) compPdfs.append( self.ws.factory('RooExtendPdf::%s_extended(%s,%s)' % \ (compPdf.GetName(), compPdf.GetName(), norm.GetName()) ) ) self.ws.factory('r_signal[0., -200., 200.]') self.ws.var('r_signal').setConstant(False) try: obs = [ self.pars.varNames[x] for x in self.pars.var ] except AttributeError: obs = self.pars.var for component in self.pars.signals: compFile = getattr(self.pars, '%sFiles' % component) compModels = getattr(self.pars, '%sModels' % component) if hasattr(self.pars, '%sConvModels' % component): convModels = getattr(self.pars, '%sConvModels' % component) else: convModels = None compPdf = self.makeComponentPdf(component, compFiles, compModels, useAlternateModels, convModels) norm = self.ws.factory( "prod::f_%s_norm(n_%s[0., 1e6],r_signal)" % \ (component, component) ) self.ws.var('n_%s' % component).setConstant(True) if hasattr(self, '%sExpected' % component): self.ws.var('n_%s' % component).setVal( getattr(self, '%sExpected' % component)) pdf = self.ws.factory('RooExtendPdf::%s_extended(%s,%s)' % \ (compPdf.GetName(), compPdf.GetName(), norm.GetName()) ) if (hasattr(self.pars, '%sInterference' % component)) and \ getattr(self.pars, '%sInterference' % component): getattr(self.ws, 'import') \ (pdf, RooFit.RenameAllNodes('interf_%sUp' % component), RooFit.RenameAllVariablesExcept('interf_%sUp' % component, ','.join(obs)), RooFit.Silence() ) getattr(self.ws, 'import') \ (pdf, RooFit.RenameAllNodes('interf_%sDown' % component), RooFit.RenameAllVariablesExcept('interf_%sDown'%component, ','.join(obs)), RooFit.Silence() ) if self.pars.includeSignal: compPdfs.append(pdf) #print compPdfs prodList = [ '%s' % (pdf.GetName()) \ for (idx, pdf) in enumerate(compPdfs) ] comps = RooArgList(self.ws.argSet(','.join(prodList))) getattr(self.ws, 'import')(RooAddPdf('total', 'total', comps)) return self.ws.pdf('total') # define the constraints on the yields, etc that will be part of the fit. def makeConstraints(self): if self.ws.set('constraintSet'): return self.ws.set('constraintSet') constraints = [] constrainedParameters = [] for constraint in self.pars.yieldConstraints: theYield = self.ws.var('%s_nrm' % constraint) if not theYield.isConstant(): self.ws.factory('RooGaussian::%s_const(%s, 1.0, %f)' % \ (constraint, theYield.GetName(), self.pars.yieldConstraints[constraint]) ) constraints.append('%s_const' % constraint) constrainedParameters.append(theYield.GetName()) if hasattr(self.pars, 'constrainShapes'): for component in self.pars.constrainShapes: pc = self.ws.pdf(component).getParameters(self.ws.set('obsSet')) parIter = pc.createIterator() par = parIter.Next() while par: if not par.isConstant(): theConst = self.ws.factory('RooGaussian::%s_const' % \ (par.GetName()) + \ '(%s, %f, %f)' % \ (par.GetName(), par.getVal(), par.getError()) ) constraints.append(theConst.GetName()) constrainedParameters.append(par.GetName()) par = parIter.Next() pc.IsA().Destructor(pc) self.ws.defineSet('constraintSet', ','.join(constraints)) self.ws.defineSet('constrainedSet', ','.join(constrainedParameters)) return self.ws.set('constraintSet') # fit the data using the pdf def fit(self, keepParameterValues = False): print 'construct fit pdf ...' fitter = self.makeFitter() print 'load data ...' data = self.loadData() self.resetYields() constraintSet = self.makeConstraints() if not keepParameterValues: self.readParametersFromFile() self.resetYields() # print constraints, self.pars.yieldConstraints print '\nfit constraints' constIter = constraintSet.createIterator() constraint = constIter.Next() constraints = [] while constraint: constraint.Print() constraints.append(constraint.GetName()) constraint = constIter.Next() constraintCmd = RooCmdArg.none() if constraintSet.getSize() > 0: constraints.append(fitter.GetName()) fitter = self.ws.pdf('totalFit_const') if not fitter: fitter = self.ws.factory('PROD::totalFit_const(%s)' % \ (','.join(constraints)) ) constraintCmd = RooFit.Constrained() # constraintCmd = RooFit.ExternalConstraints(self.ws.set('constraintSet')) if self.useImportPars: self.ws.loadSnapshot('importParams') self.ws.Print() # for constraint in pars.constraints: # self.ws.pdf(constraint).Print() # print rangeCmd = RooCmdArg.none() if self.rangeString and self.pars.doExclude: rangeCmd = RooFit.Range(self.rangeString) print 'fitting ...' fr = fitter.fitTo(data, RooFit.Save(True), RooFit.Extended(True), RooFit.Minos(False), RooFit.PrintEvalErrors(-1), RooFit.Warnings(False), constraintCmd, rangeCmd) fr.Print() return fr # determine the fitting model for each component and return them def makeComponentPdf(self, component, files, models, useAlternateModels, convModels): print 'making ComponentPdf %s' % component # print 'models = %s' % models # print 'files = %s' % files if convModels and not (convModels[0] == -1): thePdf = self.makeConvolvedPdf(component, files, models, useAlternateModels, convModels) elif (models[0] == -1): thePdf = self.makeComponentHistPdf(component, files) elif (models[0] == -2): thePdf = self.makeMorphingPdf(component, useAlternateModels, convModels) elif (models[0] == -3): pass else: thePdf = self.makeComponentAnalyticPdf(component, models, useAlternateModels) return thePdf #create a simple 2D histogram pdf def makeComponentHistPdf(self, component, files): if self.ws.pdf(component): return self.ws.pdf(component) compHist = self.utils.newEmptyHist('hist%s' % component) sumYields = 0. sumxsec = 0. sumExpected = 0. for (idx,fset) in enumerate(files): if hasattr(self.pars, '%scuts' % component): cutOverride = getattr(self.pars, '%scuts' % component) else: cutOverride = None filename = fset[0] tmpHist = self.utils.File2Hist(filename, 'hist%s_%i' % (component, idx), False,cutOverride,False,True,0) sumYields += tmpHist.Integral() sumxsec += fset[2] compHist.Add(tmpHist, self.pars.integratedLumi*fset[2]/fset[1]) sumExpected += tmpHist.Integral()*fset[2]* \ self.pars.integratedLumi/fset[1] print filename,'acc x eff: %.3g' % (tmpHist.Integral()/fset[1]) print filename,'N_expected: %.1f' % \ (tmpHist.Integral()*fset[2]*self.pars.integratedLumi/fset[1]) #tmpHist.Print() #compHist.Print() print '%s acc x eff: %.3g' % \ (component, sumExpected/sumxsec/self.pars.integratedLumi) print 'Number of expected %s events: %.1f' % (component, sumExpected) setattr(self, '%sExpected' % component, sumExpected) return self.utils.Hist2Pdf(compHist, component, self.ws, self.pars.order) #create a pdf which is a convolution of any two pdf def makeConvolvedPdf(self, component, files, models, useAlternateModels, convModels): if self.ws.pdf(component): return self.ws.pdf(component) #If a morphing model is selected, then convolve each individual component first and then morph if (models[0] == -2): return self.makeMorphingPdf(component, useAlternateModels, convModels) basePdf = self.makeComponentPdf('%s_base' % component, files, models, useAlternateModels, [-1]) convComponent = 'Global' ##Overwrite to use the same convolution model for all Pdfs convModel = getattr(self.pars, '%sConvModels' % convComponent) if useAlternateModels: convModel = getattr(self.pars, '%sConvModelsAlt' % convComponent) convPdf = self.makeComponentPdf('%s_conv' % convComponent, files, convModel, useAlternateModels, [-1]) var = self.pars.var[0] try: vName = self.pars.varNames[var] except AttributeError: vName = var self.ws.factory('RooFFTConvPdf::%s(%s,%s,%s)' % \ (component, vName, basePdf.GetName(), convPdf.GetName())) return self.ws.pdf(component) # create a pdf using the "template morphing" technique def makeMorphingPdf(self, component, useAlternateModels, convModels): if self.ws.pdf(component): return self.ws.pdf(component) filesNom = getattr(self.pars, '%s_NomFiles' % component) modelsNom = getattr(self.pars, '%s_NomModels' % component) filesMU = getattr(self.pars, '%s_MUFiles' % component) modelsMU = getattr(self.pars, '%s_MUModels' % component) filesMD = getattr(self.pars, '%s_MDFiles' % component) modelsMD = getattr(self.pars, '%s_MDModels' % component) filesSU = getattr(self.pars, '%s_SUFiles' % component) modelsSU = getattr(self.pars, '%s_SUModels' % component) filesSD = getattr(self.pars, '%s_SDFiles' % component) modelsSD = getattr(self.pars, '%s_SDModels' % component) if useAlternateModels: modelsNom = getattr(self.pars, '%s_NomModelsAlt' % component) modelsMU = getattr(self.pars, '%s_MUModelsAlt' % component) modelsMD = getattr(self.pars, '%s_MDModelsAlt' % component) modelsSU = getattr(self.pars, '%s_SUModelsAlt' % component) modelsSD = getattr(self.pars, '%s_SDModelsAlt' % component) # Adds five (sub)components for the component with suffixes Nom, MU, MD, SU, SD NomPdf = self.makeComponentPdf('%s_Nom' % component, filesNom, modelsNom, False, convModels) if hasattr(self, '%s_NomExpected' % component): setattr(self, '%sExpected' % component, getattr(self, '%s_NomExpected' % component)) MUPdf = self.makeComponentPdf('%s_MU' % component, filesMU, modelsMU, False, convModels) MDPdf = self.makeComponentPdf('%s_MD' % component, filesMD, modelsMD, False, convModels) SUPdf = self.makeComponentPdf('%s_SU' % component, filesSU, modelsSU, False, convModels) SDPdf = self.makeComponentPdf('%s_SD' % component, filesSD, modelsSD, False, convModels) fMU_comp = self.ws.factory("fMU_%s[0., -1., 1.]" % component) fSU_comp = self.ws.factory("fSU_%s[0., -1., 1.]" % component) fMU = RooFormulaVar("f_fMU_%s" % component, "1.0*@0*(@0 >= 0.)", RooArgList( fMU_comp ) ) fMD = RooFormulaVar("f_fMD_%s" % component, "-1.0*@0*(@0 < 0.)", RooArgList( fMU_comp ) ) fSU = RooFormulaVar("f_fSU_%s" % component, "@0*(@0 >= 0.)", RooArgList( fSU_comp ) ) fSD = RooFormulaVar("f_fSD_%s" % component, "@0*(-1)*(@0 < 0.)", RooArgList( fSU_comp ) ) fNom = RooFormulaVar("f_fNom_%s" % component, "(1.-abs(@0)-abs(@1))", RooArgList(fMU_comp,fSU_comp) ) morphPdf = RooAddPdf(component,component, RooArgList(MUPdf,MDPdf,SUPdf,SDPdf,NomPdf), RooArgList(fMU, fMD, fSU, fSD, fNom)) morphPdf.SetName(component) getattr(self.ws, 'import')(morphPdf) return self.ws.pdf(component) # create a pdf using an analytic function. def makeComponentAnalyticPdf(self, component, models, useAlternateModels): if self.ws.pdf(component): return self.ws.pdf(component) pdfList = [] for (idx,model) in enumerate(models): var = self.pars.var[idx] try: vName = self.pars.varNames[var] except AttributeError: vName = var auxModel = None if useAlternateModels: if hasattr(self.pars, '%sAuxModelsAlt' % component): auxModel = getattr(self.pars, '%sAuxModelsAlt' % component)[idx] else: if hasattr(self.pars, '%sAuxModels' % component): auxModel = getattr(self.pars, '%sAuxModels' % component)[idx] pdfList.append(self.utils.analyticPdf(self.ws, vName, model, '%s_%s'%(component,vName), '%s_%s'%(component,vName), auxModel ) ) pdfListNames = [ pdf.GetName() for pdf in pdfList ] if len(pdfList) > 1: self.ws.factory('PROD::%s(%s)' % \ (component, ','.join(pdfListNames))) else: pdfList[0].SetName(component) return self.ws.pdf(component) def loadData(self, weight = False): if self.ws.data('data_obs'): return self.ws.data('data_obs') unbinnedName = 'data_obs' if self.pars.binData: unbinnedName = 'data_unbinned' data = self.utils.File2Dataset(self.pars.DataFile, unbinnedName, self.ws, weighted = weight) if self.pars.binData: data = RooDataHist('data_obs', 'data_obs', self.ws.set('obsSet'), data) getattr(self.ws, 'import')(data) data = self.ws.data('data_obs') return data def stackedPlot(self, var, logy = False, pdfName = None, Silent = False): if not pdfName: pdfName = 'total' xvar = self.ws.var(var) nbins = xvar.getBins() if hasattr(self.pars, 'plotRanges'): xvar.setRange('plotRange', self.pars.plotRanges[var][1], self.pars.plotRanges[var][2]) xvar.setBins(self.pars.plotRanges[var][0], 'plotBins') else: xvar.setRange('plotRange', xvar.getMin(), xvar.getMax()) xvar.setBins(nbins, 'plotBins') sframe = xvar.frame() sframe.SetName("%s_stacked" % var) pdf = self.ws.pdf(pdfName) if isinstance(pdf, RooAddPdf): compList = RooArgList(pdf.pdfList()) else: compList = None data = self.ws.data('data_obs') nexp = pdf.expectedEvents(self.ws.set('obsSet')) if not Silent: print pdf.GetName(),'expected: %.0f' % (nexp) print 'data events: %.0f' % (data.sumEntries()) if nexp < 1: nexp = data.sumEntries() theComponents = [] if self.pars.includeSignal: theComponents += self.pars.signals theComponents += self.pars.backgrounds data.plotOn(sframe, RooFit.Invisible(), RooFit.Binning('plotBins')) # dataHist = RooAbsData.createHistogram(data,'dataHist_%s' % var, xvar, # RooFit.Binning('%sBinning' % var)) # #dataHist.Scale(1., 'width') # invData = RooHist(dataHist, 1., 1, RooAbsData.SumW2, 1.0, False) # #invData.Print('v') # sframe.addPlotable(invData, 'pe', True, True) for (idx,component) in enumerate(theComponents): if not Silent: print 'plotting',component,'...', if hasattr(self.pars, '%sPlotting' % (component)): plotCharacteristics = getattr(self.pars, '%sPlotting' % \ (component)) else: plotCharacteristics = {'color' : colorwheel[idx%6], 'title' : component } compCmd = RooCmdArg.none() if compList: compSet = RooArgSet(compList) if compSet.getSize() > 0: compCmd = RooFit.Components(compSet) removals = compList.selectByName('%s*' % component) compList.remove(removals) if not Silent: print 'events', self.ws.function('f_%s_norm' % component).getVal() sys.stdout.flush() if abs(self.ws.function('f_%s_norm' % component).getVal()) >= 1.: pdf.plotOn(sframe, #RooFit.ProjWData(data), RooFit.DrawOption('LF'), RooFit.FillStyle(1001), RooFit.FillColor(plotCharacteristics['color']), RooFit.LineColor(plotCharacteristics['color']), RooFit.VLines(), RooFit.Range('plotRange'), RooFit.NormRange('plotRange'), RooFit.Normalization(nexp, RooAbsReal.NumEvent), compCmd ) tmpCurve = sframe.getCurve() tmpCurve.SetName(component) tmpCurve.SetTitle(plotCharacteristics['title']) if 'visible' in plotCharacteristics: sframe.setInvisible(component, plotCharacteristics['visible']) data.plotOn(sframe, RooFit.Name('theData'), RooFit.Binning('plotBins')) sframe.getHist('theData').SetTitle('data') # theData = RooHist(dataHist, 1., 1, RooAbsData.SumW2, 1.0, True) # theData.SetName('theData') # theData.SetTitle('data') # sframe.addPlotable(theData, 'pe') if (logy): sframe.SetMinimum(0.01) sframe.SetMaximum(1.0e6) else: sframe.SetMaximum(sframe.GetMaximum()*1.35) pass excluded = (var in self.pars.exclude) bname = var if not excluded: for v in self.pars.exclude: if hasattr(self.pars, 'varNames') and \ (self.pars.varNames[v] == var): excluded = True bname = v if excluded: blinder = TBox(self.pars.exclude[bname][0], sframe.GetMinimum(), self.pars.exclude[bname][1], sframe.GetMaximum()) # blinder.SetName('blinder') # blinder.SetTitle('signal region') blinder.SetFillColor(kBlack) if self.pars.blind: blinder.SetFillStyle(1001) else: blinder.SetFillStyle(0) blinder.SetLineStyle(2) sframe.addObject(blinder) elif self.pars.blind: if not Silent: print "blind but can't find exclusion region for", var print 'excluded',excluded,self.pars.exclude print 'hiding data points' sframe.setInvisible('theData', True) #sframe.GetYaxis().SetTitle('Events / GeV') # dataHist.IsA().Destructor(dataHist) if not Silent: print xvar.setBins(nbins) return sframe def readParametersFromFile(self, fname=None): if (not fname): fname = self.pars.initialParametersFile if isinstance(fname, str): flist = [ fname ] else: flist = fname for tmpName in flist: if len(tmpName) > 0: print 'loading parameters from file',tmpName self.ws.allVars().readFromFile(tmpName) def expectedFromPars(self): components = self.pars.signals + self.pars.backgrounds for component in components: theYield = self.ws.var('n_%s' % component) setattr(self, '%sExpected' % component, theYield.getVal()) def initFromExplicitVals(self,opts): #,init_diboson= -1.0,init_WpJ=-1.0,init_top=-1.0,init_ZpJ=-1.0,init_QCD=-1.0 components = ['diboson', 'top', 'WpJ', 'ZpJ', 'QCD', 'WHbb'] for component in components: #double init init = getattr(opts, 'ext%s' % component) #init = -2.0 #setattr(self,init, 'init_%s' % component) #init = init_%s % component #print "init=", init #init = self.ws.var('init_%s' % component) #init.setVal(100.0) #init.setVal('init_%s' % component) #init = theYield.getVal() if (init>0.): print 'setting initial value for ',component,' to ',init setattr(self, '%sInitial' % component, init) def resetYields(self): if self.ws.data('data_obs'): Ndata = self.ws.data('data_obs').sumEntries() else: Ndata = 10000. print 'resetting yields...' components = self.pars.signals + self.pars.backgrounds for component in components: theYield = self.ws.var('n_%s' % component) theNorm = self.ws.var('%s_nrm' % component) if hasattr(self, '%sInitial' % component): print 'explicitly setting initial value for ',component theYield.setVal(getattr(self, '%sInitial' % component)) theNorm.setVal(1.0) theNorm.setConstant() else: fracofdata = -1. if hasattr(self.pars, '%sFracOfData' % component): fracofdata = getattr(self.pars, '%sFracOfData' % component) if (fracofdata >= 0.): print 'explicitly setting ', component,' yield to be', fracofdata,' of data' theYield.setVal(fracofdata*Ndata) elif hasattr(self, '%sExpected' % component): theYield.setVal(getattr(self, '%sExpected' % component)) else: print 'no expected value for',component theYield.setVal(Ndata/len(components)) if theNorm and not theNorm.isConstant(): theNorm.setVal(1.0) if component in self.pars.yieldConstraints: theYield.setError(theYield.getVal() * \ self.pars.yieldConstraints[component]) if theNorm: theNorm.setError(self.pars.yieldConstraints[component]) else: theYield.setError(sqrt(theYield.getVal())) theYield.Print() def generateToyMCSet(self,var,inputPdf,outFileName,NEvts): fMC = TFile(outFileName, "RECREATE"); # thevar = self.ws.var(var); print 'thevar=' print var # print thevar print '...' # varList = RooArgList() # varList.add(self.ws.var(var)) toymc = inputPdf.generate(RooArgSet(self.ws.var(var)),NEvts); tMC = toymc.tree(); fMC.cd(); tMC.Write(); fMC.Close(); def legend4Plot(plot, left = False): if left: theLeg = TLegend(0.2, 0.62, 0.55, 0.92, "", "NDC") else: theLeg = TLegend(0.60, 0.62, 0.92, 0.92, "", "NDC") theLeg.SetName('theLegend') theLeg.SetBorderSize(0) theLeg.SetLineColor(0) theLeg.SetFillColor(0) theLeg.SetFillStyle(0) theLeg.SetLineWidth(0) theLeg.SetLineStyle(0) theLeg.SetTextFont(42) theLeg.SetTextSize(.045) entryCnt = 0 for obj in range(0, int(plot.numItems())): objName = plot.nameOf(obj) if (not plot.getInvisible(objName)): theObj = plot.getObject(obj) objTitle = theObj.GetTitle() if len(objTitle) < 1: objTitle = objName dopts = plot.getDrawOptions(objName).Data() # print 'obj:',theObj,'title:',objTitle,'opts:',dopts,'type:',type(dopts) if theObj.IsA().InheritsFrom('TNamed'): theLeg.AddEntry(theObj, objTitle, dopts) entryCnt += 1 theLeg.SetY1NDC(0.9 - 0.05*entryCnt - 0.005) theLeg.SetY1(theLeg.GetY1NDC()) return theLeg legend4Plot = staticmethod(legend4Plot)
def main(options, args): cfg = options.config workspaceName = cfg.get('Global', 'workspace') ws = RooWorkspace(workspaceName) #ws.Print("v") setupWorkspace(ws, options) #create -log(likelihood) theNLL = ws.pdf('TopLevelPdf').createNLL( ws.data('allcountingdata'), RooFit.NumCPU(1), RooFit.ConditionalObservables(ws.set('condObs')), RooFit.Verbose(True)) ws.saveSnapshot('standardmodel', ws.allVars()) minuit = ROOT.RooMinuit(theNLL) minuit.setPrintLevel(1) minuit.setPrintEvalErrors(-1) minuit.setErrorLevel(.5) #find the values of the parameters that minimize the likelihood minuit.setStrategy(2) minuit.simplex() minuit.migrad() minuit.hesse() #ws.var('err_gl').setConstant(True) #ws.var('err_gs').setConstant(True) #ws.var('err_gb').setConstant(True) ws.defineSet( 'POI', ROOT.RooArgSet( ws.var('%s_%s' % (cfg.get( 'Global', 'par1Name'), cfg.get('Global', 'couplingType'))), ws.var('%s_%s' % (cfg.get( 'Global', 'par2Name'), cfg.get('Global', 'couplingType'))))) ws.saveSnapshot('%s_fitresult' % cfg.get('Global', 'couplingType'), ws.allVars()) #create profile likelihood level_68 = ROOT.TMath.ChisquareQuantile( .68, 2) / 2.0 # delta NLL for 68% confidence level for -log(LR) level_95 = ROOT.TMath.ChisquareQuantile( .95, 2) / 2.0 # delta NLL for 95% confidence level for -log(LR) print print '68% CL Delta-NLL 2 DOF=', level_68 print '95% CL Delta-NLL 2 DOF=', level_95 minuit.setPrintLevel(1) minuit.setPrintEvalErrors(-1) minuit.migrad() minuit.minos(ws.set('POI')) thePlot = minuit.contour( ws.var('%s_%s' % (cfg.get( 'Global', 'par1Name'), cfg.get('Global', 'couplingType'))), ws.var('%s_%s' % (cfg.get( 'Global', 'par2Name'), cfg.get('Global', 'couplingType'))), sqrt(2 * level_95), sqrt(2 * level_68)) # here the error is in sigmas thePlot.SetName( '%s_%s_%s_contour' % (cfg.get('Global', 'par1Name'), cfg.get( 'Global', 'par2Name'), cfg.get('Global', 'couplingType'))) thePlot.SetTitle('68% & 95% CL on the Best Fit Values of ' + cfg.get('Global', 'par1Name') + ' and ' + cfg.get('Global', 'par2Name')) legend = ROOT.TLegend(2.01612903225806439e-01, 7.86016949152542388e-01, 7.15725806451612989e-01, 9.13135593220338992e-01) legend.SetNColumns(2) thePlot.addObject(legend) # 1-D Limits level_95 = ROOT.TMath.ChisquareQuantile( .95, 1) / 2.0 # delta NLL for -log(LR) with 1 dof print '95% CL Delta-NLL 1 DOF=', level_95 minuit.setErrorLevel(level_95) #set 1-D limits on parameter 1 with parameter 2 == 0 ws.var('%s_%s' % (cfg.get( 'Global', 'par2Name'), cfg.get('Global', 'couplingType'))).setVal(0.0) ws.var('%s_%s' % (cfg.get('Global', 'par2Name'), cfg.get('Global', 'couplingType'))).setConstant(True) minuit.minos(ws.set('POI')) parm1 = ws.var( '%s_%s' % (cfg.get('Global', 'par1Name'), cfg.get('Global', 'couplingType'))) print 'parameter 1 value: ' + str(parm1.getVal()) if not (0 < parm1.getVal() + parm1.getErrorHi() and 0 > parm1.getVal() + parm1.getErrorLo()): print '95% CL does not cover SM for parameter 1' else: print '95% CL covers SM for parameter 1' par1Line = ROOT.TLine(parm1.getVal() + parm1.getErrorLo(), 0, parm1.getVal() + parm1.getErrorHi(), 0) par1Line.SetLineWidth(2) par1Line.SetLineColor(ROOT.kRed) thePlot.addObject(par1Line) #set 1-D limits on parameter 2 with parameter 1 == 0 ws.var('%s_%s' % (cfg.get('Global', 'par2Name'), cfg.get('Global', 'couplingType'))).setConstant(False) ws.var('%s_%s' % (cfg.get( 'Global', 'par1Name'), cfg.get('Global', 'couplingType'))).setVal(0.0) ws.var('%s_%s' % (cfg.get('Global', 'par1Name'), cfg.get('Global', 'couplingType'))).setConstant(True) minuit.minos(ws.set('POI')) parm2 = ws.var( '%s_%s' % (cfg.get('Global', 'par2Name'), cfg.get('Global', 'couplingType'))) print 'parameter 2 value: ' + str(parm2.getVal()) if not (0 < parm2.getVal() + parm2.getErrorHi() and 0 > parm2.getVal() + parm2.getErrorLo()): print '95% CL does not cover SM for parameter 2' else: print '95% CL covers SM for parameter 2' par2Line = ROOT.TLine(0, parm2.getVal() + parm2.getErrorLo(), 0, parm2.getVal() + parm2.getErrorHi()) par2Line.SetLineWidth(2) par2Line.SetLineColor(ROOT.kRed) thePlot.addObject(par2Line) ws.var('%s_%s' % (cfg.get('Global', 'par1Name'), cfg.get('Global', 'couplingType'))).setConstant(False) #construct likelihood scan histograms plot = parm1.frame() parm1.setBins(200) parm2.setBins(200) scanHist = ROOT.TH2F('scan2d_plot', '2D Scan of the Likelihood', 200, parm1.getMin(), parm1.getMax(), 200, parm2.getMin(), parm2.getMax()) for i in range(200): for j in range(200): parm1.setVal(parm1.getMin() + (i + .5) * (parm1.getMax() - parm1.getMin()) / 200) parm2.setVal(parm2.getMin() + (j + .5) * (parm2.getMax() - parm2.getMin()) / 200) scanHist.SetBinContent(i + 1, j + 1, theNLL.getVal()) profNLL_par1 = theNLL.createProfile(RooArgSet(parm1)) profNLL_par1_plot = parm1.frame() profNLL_par1.plotOn(profNLL_par1_plot) profNLL_par2 = theNLL.createProfile(RooArgSet(parm2)) profNLL_par2_plot = parm2.frame() profNLL_par2.plotOn(profNLL_par2_plot) initCMSStyle() output = TFile.Open(workspaceName + '.root', 'RECREATE') ws.Write() contCanvas = ROOT.TCanvas('contour_canvas', '', 500, 500) thePlot.Draw() prettyContour(contCanvas, cfg) contCanvas.Write() thePlot.Write() scanCanvas2D = ROOT.TCanvas('scan2d_canvas', '', 500, 500) scanHist.Draw('colz') prettyScan(scanCanvas2D, cfg) scanCanvas2D.Write() scanHist.Write() par1ScanCanvas = ROOT.TCanvas('scan1d_par1', '', 500, 500) par1ScanCanvas.cd() profNLL_par1_plot.Draw() par1ScanCanvas.Write() profNLL_par1_plot.Write() par2ScanCanvas = ROOT.TCanvas('scan1d_par2', '', 500, 500) par2ScanCanvas.cd() profNLL_par2_plot.Draw() par2ScanCanvas.Write() profNLL_par2_plot.Write() prettyObsPlots(ws, cfg) output.Close() if options.makeCards: print print "Creating cards for Higgs Combined Limit calculator!" makeHCLCards(ws, cfg) return 0