for s in signalsPP: counters[s] = Counter('Hpp3l') counters[s].addProcess(s,sigMap[s],signal=True) for s in signalsPPR: counters[s] = Counter('Hpp3l') counters[s].addProcess(s,sigMap[s],signal=True) counters['data'] = Counter('Hpp3l') counters['data'].addProcess('data',sigMap['data']) for mode in modes: for mass in masses: logging.info('Producing datacard for {0} - {1} GeV'.format(mode,mass)) results = {} limits = Limits() limits.addEra('Era13TeV2016') limits.addAnalysis('Hpp3l') limits.addAnalysis('Hpp3lAP') limits.addAnalysis('Hpp3lPP') limits.addAnalysis('Hpp3lPPR') recoChans = getRecoChans(mode) for reco in recoChans: limits.addChannel(reco) limits.addChannel(reco+'_SB') signalsAP = ['HppHm{0}GeV'.format(mass)] signalsPP = ['HppHmm{0}GeV'.format(mass)] signalsPPR = ['HppHmmR{0}GeV'.format(mass)]
for proc in samples: hists += [histMap[proc]] hist = sumHists('obs',*hists) for b in range(hist.GetNbinsX()+1): val = int(hist.GetBinContent(b)) if val<0: val = 0 err = val**0.5 hist.SetBinContent(b,val) #hist.SetBinError(b,err) histMap['data'] = hist else: hist = getBinned('data') histMap['data'] = hist # create limit object limits = Limits(wsname) limits.addEra('Run2016') limits.addAnalysis('ThreePhoton') limits.addChannel('ggg') if doParametric: limits.addMH(*binning[1:]) limits.addX(*binning[1:]) if doParametric: limits.addProcess('sig',signal=True) limits.addProcess('bg') else: for signal in signals: limits.addProcess(signal,signal=True)
for s in signals: counters[s] = Counter('Hpp4l') counters[s].addProcess(s, sigMap[s], signal=True) for s in signalsR: counters[s] = Counter('Hpp4l') counters[s].addProcess(s, sigMap[s], signal=True) counters['data'] = Counter('Hpp4l') counters['data'].addProcess('data', sigMap['data']) for mode in modes: for mass in masses: logging.info('Producing datacard for {0} - {1} GeV'.format(mode, mass)) results = {} limits = Limits() limits.addEra('Era13TeV2016') limits.addAnalysis('Hpp4l') recoChans = getRecoChans(mode) for reco in recoChans: limits.addChannel(reco) limits.addChannel(reco + '_SB') signals = ['HppHmm{0}GeV'.format(mass)] signalsR = ['HppHmmR{0}GeV'.format(mass)] for sig in signals + signalsR: limits.addProcess(sig, signal=True) for background in backgrounds:
for s in signalsPP: counters[s] = Counter('Hpp3l') counters[s].addProcess(s, sigMap[s], signal=True) for s in signalsPPR: counters[s] = Counter('Hpp3l') counters[s].addProcess(s, sigMap[s], signal=True) counters['data'] = Counter('Hpp3l') counters['data'].addProcess('data', sigMap['data']) for mode in modes: for mass in masses: logging.info('Producing datacard for {0} - {1} GeV'.format(mode, mass)) results = {} limits = Limits() limits.addEra('13TeV80X') limits.addAnalysis('Hpp3l') limits.addAnalysis('Hpp3lAP') limits.addAnalysis('Hpp3lPP') limits.addAnalysis('Hpp3lPPR') recoChans = getRecoChans(mode) for reco in recoChans: limits.addChannel(reco) limits.addChannel(reco + '_SB') signalsAP = ['HppHm{0}GeV'.format(mass)] signalsPP = ['HppHmm{0}GeV'.format(mass)] signalsPPR = ['HppHmmR{0}GeV'.format(mass)]
for proc in samples: hists += [histMap[proc]] hist = sumHists('obs', *hists) for b in range(hist.GetNbinsX() + 1): val = int(hist.GetBinContent(b)) if val < 0: val = 0 err = val**0.5 hist.SetBinContent(b, val) #hist.SetBinError(b,err) histMap['data'] = hist else: hist = getBinned('data') histMap['data'] = hist # create limit object limits = Limits(wsname) limits.addEra('Run2016') limits.addAnalysis('ThreePhoton') limits.addChannel('ggg') if doParametric: limits.addMH(*binning[1:]) limits.addX(*binning[1:]) if doParametric: limits.addProcess('sig', signal=True) limits.addProcess('bg') else: for signal in signals: limits.addProcess(signal, signal=True)
def create_datacard(args): doMatrix = False doParametric = args.parametric doUnbinned = args.unbinned do2D = len(args.fitVars) == 2 blind = not args.unblind addSignal = args.addSignal signalParams = {'h': args.higgs, 'a': args.pseudoscalar} wsname = 'w' var = args.fitVars if do2D and doParametric: logging.error('Parametric 2D fits are not yet supported') raise if doUnbinned and not doParametric: logging.error('Unbinned only supported with parametric option') raise ############# ### Setup ### ############# sampleMap = getSampleMap() backgrounds = ['datadriven'] data = ['data'] signals = [signame.format(h=h, a=a) for h in hmasses for a in amasses] signalToAdd = signame.format(**signalParams) signalSplines = [splinename.format(h=h) for h in hmasses] wrappers = {} for proc in backgrounds + signals + data: if proc == 'datadriven': continue for sample in sampleMap[proc]: wrappers[sample] = NtupleWrapper('MuMuTauTau', sample, new=True, version='80X') for shift in shifts: wrappers[sample + shift] = NtupleWrapper('MuMuTauTau', sample, new=True, version='80X', shift=shift) ############################## ### Create/read histograms ### ############################## histMap = {} # The definitons of which regions match to which arguments # PP can take a fake rate datadriven estimate from PF, but PF can only take the observed values regionArgs = { 'PP': { 'region': 'A', 'fakeRegion': 'B', 'source': 'B', 'sources': ['A', 'C'], 'fakeSources': ['B', 'D'], }, 'PF': { 'region': 'B', 'sources': ['B', 'D'], }, } for mode in ['PP', 'PF']: histMap[mode] = {} for shift in [''] + shifts: histMap[mode][shift] = {} for proc in backgrounds + signals: logging.info('Getting {} {}'.format(proc, shift)) if proc == 'datadriven': # TODO: unbinned, get the RooDataHist from flattenener first if mode == 'PP': if doMatrix: histMap[mode][shift][ proc] = getMatrixDatadrivenHist( var=var, wrappers=wrappers, shift=shift, do2D=do2D, **regionArgs[mode]) else: histMap[mode][shift][proc] = getDatadrivenHist( var=var, wrappers=wrappers, shift=shift, do2D=do2D, **regionArgs[mode]) else: if doMatrix: histMap[mode][shift][proc] = getMatrixHist( 'data', var=var, wrappers=wrappers, shift=shift, do2D=do2D, **regionArgs[mode]) else: histMap[mode][shift][proc] = getHist( 'data', var=var, wrappers=wrappers, shift=shift, do2D=do2D, **regionArgs[mode]) else: if doMatrix: histMap[mode][shift][proc] = getMatrixHist( proc, var=var, wrappers=wrappers, shift=shift, do2D=do2D, **regionArgs[mode]) else: histMap[mode][shift][proc] = getHist( proc, var=var, wrappers=wrappers, shift=shift, do2D=do2D, **regionArgs[mode]) if do2D: pass # TODO, figure out how to rebin 2D else: histMap[mode][shift][proc].Rebin(rebinning[var[0]]) if shift: continue logging.info('Getting observed') if blind: samples = backgrounds if addSignal: samples = backgrounds + [signalToAdd] hists = [] for proc in samples: hists += [histMap[mode][shift][proc]] hist = sumHists('obs', *hists) #for b in range(hist.GetNbinsX()+1): # val = int(hist.GetBinContent(b)) # if val<0: val = 0 # err = val**0.5 # hist.SetBinContent(b,val) # #hist.SetBinError(b,err) histMap[mode][shift]['data'] = hist else: hist = getHist('data', var=var, wrappers=wrappers, do2D=do2D, **regionArgs[mode]) histMap[mode][shift]['data'] = hist if do2D: pass else: histMap[mode][shift]['data'].Rebin(rebinning[var[0]]) ##################### ### Create Limits ### ##################### limits = Limits(wsname) limits.addEra('Run2016') limits.addAnalysis('HAA') era = 'Run2016' analysis = 'HAA' reco = 'mmmt' for mode in ['PP', 'PF']: limits.addChannel(mode) if doParametric: binning = varBinning[var[0]] limits.addMH(*binning[1:]) limits.addX(*binning[1:], unit='GeV', label='m_{#mu#mu}') for h in hmasses: limits.addProcess(splinename.format(h=h), signal=True) for background in backgrounds: limits.addProcess(background) # add models for h in hmasses: model = getSpline(histMap[mode][''], h, tag=mode) limits.setExpected(splinename.format(h=h), era, analysis, mode, model) if doUnbinned: bg = buildModel(limits, tag=mode) limits.setExpected('datadriven', era, analysis, mode, bg) else: # add histograms for background if not using an unbinned model for bg in backgrounds: limits.setExpected(bg, era, analysis, mode, histMap[mode][''][bg]) # get roodatahist limits.setObserved(era, analysis, mode, histMap[mode]['']['data']) else: for signal in signals: limits.addProcess(signal, signal=True) for background in backgrounds: limits.addProcess(background) for proc in backgrounds: limits.setExpected(proc, era, analysis, mode, histMap[mode][''][proc]) for proc in signals: limits.setExpected(proc, era, analysis, mode, histMap[mode][''][proc]) limits.setObserved(era, analysis, mode, histMap[mode]['']['data']) ######################### ### Add uncertainties ### ######################### systproc = tuple( [proc for proc in signals + backgrounds if 'datadriven' not in proc]) allproc = tuple([proc for proc in signals + backgrounds]) systsplineproc = tuple([ proc for proc in signalSplines + backgrounds if 'datadriven' not in proc ]) allsplineproc = tuple([proc for proc in signalSplines + backgrounds]) bgproc = tuple([proc for proc in backgrounds]) sigsplineproc = tuple([proc for proc in signalSplines]) sigproc = tuple([proc for proc in signals]) ############ ### stat ### ############ def getStat(hist, direction): newhist = hist.Clone('{0}{1}'.format(hist.GetName(), direction)) nb = hist.GetNbinsX() * hist.GetNbinsY() for b in range(nb + 1): val = hist.GetBinContent(b + 1) err = hist.GetBinError(b + 1) newval = val + err if direction == 'Up' else val - err if newval < 0: newval = 0 newhist.SetBinContent(b + 1, newval) newhist.SetBinError(b + 1, 0) return newhist logging.info('Adding stat systematic') statMapUp = {} statMapDown = {} for proc in backgrounds + signals: statMapUp[proc] = getStat(histMap[mode][''][proc], 'Up') statMapDown[proc] = getStat(histMap[mode][''][proc], 'Down') statsyst = {} for mode in ['PP', 'PF']: # background if doUnbinned: # TODO: add errors on params pass else: for proc in bgproc: statsyst[((proc, ), (era, ), (analysis, ), (mode, ))] = (statMapUp[proc], statMapDown[proc]) # signal if doParametric: for h in hmasses: statsyst[((splinename.format(h=h), ), (era, ), (analysis, ), (mode, ))] = (getSpline(statMapUp, h, tag=mode + 'StatUp'), getSpline(statMapDown, h, tag=mode + 'StatDown')) else: for proc in sigproc: statsyst[((proc, ), (era, ), (analysis, ), (mode, ))] = (statMapUp[proc], statMapDown[proc]) limits.addSystematic('stat_{process}_{channel}', 'shape', systematics=statsyst) ############## ### shifts ### ############## for shift in shiftTypes: logging.info('Adding {} systematic'.format(shift)) shiftsyst = {} for mode in ['PP', 'PF']: # background if doUnbinned: # TODO rateParams on bg model pass else: for proc in bgproc: shiftsyst[((proc, ), (era, ), (analysis, ), (mode, ))] = (histMap[mode][shift + 'Up'][proc], histMap[mode][shift + 'Down'][proc]) # signal if doParametric: for h in hmasses: shiftsyst[((splinename.format(h=h), ), (era, ), (analysis, ), (mode, ))] = (getSpline( histMap[mode][shift + 'Up'], h, tag=mode + shift + 'Up'), getSpline( histMap[mode][shift + 'Down'], h, tag=mode + shift + 'Down')) else: for proc in sigproc: shiftsyst[((proc, ), (era, ), (analysis, ), (mode, ))] = (histMap[mode][shift + 'Up'][proc], histMap[mode][shift + 'Down'][proc]) limits.addSystematic(shift, 'shape', systematics=shiftsyst) ############ ### Lumi ### ############ # lumi 2.3% for 2015 and 2.5% for 2016 # https://twiki.cern.ch/twiki/bin/view/CMS/TWikiLUM#CurRec logging.info('Adding lumi systematic') lumiproc = systsplineproc if doParametric else systproc lumisyst = { (lumiproc, (era, ), ('all', ), ('all', )): 1.025, } limits.addSystematic('lumi', 'lnN', systematics=lumisyst) ############ ### muon ### ############ # from z: 1 % + 0.5 % + 0.5 % per muon for id + iso + trig (pt>20) logging.info('Adding mu id+iso systematic') muproc = systsplineproc if doParametric else systproc musyst = { (muproc, (era, ), ('all', ), ('all', )): 1 + math.sqrt(sum([0.01**2, 0.005**2] * 2 + [0.01**2])), # 2 lead have iso, tau_mu doesnt } limits.addSystematic('muid', 'lnN', systematics=musyst) logging.info('Adding mu trig systematic') musyst = { (muproc, (era, ), ('all', ), ('all', )): 1.005, # 1 triggering muon } limits.addSystematic('mutrig', 'lnN', systematics=musyst) ########### ### tau ### ########### # 5% on sf 0.99 (VL/L) or 0.97 (M) logging.info('Adding mu id+iso systematic') tauproc = systsplineproc if doParametric else systproc tausyst = { (tauproc, (era, ), ('all', ), ('all', )): 1.05, } limits.addSystematic('tauid', 'lnN', systematics=tausyst) ###################### ### Print datacard ### ###################### directory = 'datacards_shape/{0}'.format('MuMuTauTau') python_mkdir(directory) datacard = '{0}/mmmt_{1}'.format( directory, args.tag) if args.tag else '{}/mmmt'.format(directory) processes = {} if doParametric: for h in hmasses: processes[signame.format( h=h, a='X')] = [splinename.format(h=h)] + backgrounds else: for signal in signals: processes[signal] = [signal] + backgrounds limits.printCard(datacard, processes=processes, blind=False, saveWorkspace=doParametric)
def create_datacard(args): doMatrix = False doParametric = args.parametric doUnbinned = args.unbinned do2D = len(args.fitVars)==2 blind = not args.unblind addSignal = args.addSignal signalParams = {'h': args.higgs, 'a': args.pseudoscalar} wsname = 'w' var = args.fitVars if do2D and doParametric: logging.error('Parametric 2D fits are not yet supported') raise if doUnbinned and not doParametric: logging.error('Unbinned only supported with parametric option') raise ############# ### Setup ### ############# sampleMap = getSampleMap() backgrounds = ['datadriven'] data = ['data'] signals = [signame.format(h=h,a=a) for h in hmasses for a in amasses] signalToAdd = signame.format(**signalParams) signalSplines = [splinename.format(h=h) for h in hmasses] wrappers = {} for proc in backgrounds+signals+data: if proc=='datadriven': continue for sample in sampleMap[proc]: wrappers[sample] = NtupleWrapper('MuMuTauTau',sample,new=True,version='80X') for shift in shifts: wrappers[sample+shift] = NtupleWrapper('MuMuTauTau',sample,new=True,version='80X',shift=shift) ############################## ### Create/read histograms ### ############################## histMap = {} # The definitons of which regions match to which arguments # PP can take a fake rate datadriven estimate from PF, but PF can only take the observed values regionArgs = { 'PP': {'region':'A','fakeRegion':'B','source':'B','sources':['A','C'],'fakeSources':['B','D'],}, 'PF': {'region':'B','sources':['B','D'],}, } for mode in ['PP','PF']: histMap[mode] = {} for shift in ['']+shifts: histMap[mode][shift] = {} for proc in backgrounds+signals: logging.info('Getting {} {}'.format(proc,shift)) if proc=='datadriven': # TODO: unbinned, get the RooDataHist from flattenener first if mode=='PP': if doMatrix: histMap[mode][shift][proc] = getMatrixDatadrivenHist(var=var,wrappers=wrappers,shift=shift,do2D=do2D,**regionArgs[mode]) else: histMap[mode][shift][proc] = getDatadrivenHist(var=var,wrappers=wrappers,shift=shift,do2D=do2D,**regionArgs[mode]) else: if doMatrix: histMap[mode][shift][proc] = getMatrixHist('data',var=var,wrappers=wrappers,shift=shift,do2D=do2D,**regionArgs[mode]) else: histMap[mode][shift][proc] = getHist('data',var=var,wrappers=wrappers,shift=shift,do2D=do2D,**regionArgs[mode]) else: if doMatrix: histMap[mode][shift][proc] = getMatrixHist(proc,var=var,wrappers=wrappers,shift=shift,do2D=do2D,**regionArgs[mode]) else: histMap[mode][shift][proc] = getHist(proc,var=var,wrappers=wrappers,shift=shift,do2D=do2D,**regionArgs[mode]) if do2D: pass # TODO, figure out how to rebin 2D else: histMap[mode][shift][proc].Rebin(rebinning[var[0]]) if shift: continue logging.info('Getting observed') if blind: samples = backgrounds if addSignal: samples = backgrounds + [signalToAdd] hists = [] for proc in samples: hists += [histMap[mode][shift][proc]] hist = sumHists('obs',*hists) #for b in range(hist.GetNbinsX()+1): # val = int(hist.GetBinContent(b)) # if val<0: val = 0 # err = val**0.5 # hist.SetBinContent(b,val) # #hist.SetBinError(b,err) histMap[mode][shift]['data'] = hist else: hist = getHist('data',var=var,wrappers=wrappers,do2D=do2D,**regionArgs[mode]) histMap[mode][shift]['data'] = hist if do2D: pass else: histMap[mode][shift]['data'].Rebin(rebinning[var[0]]) ##################### ### Create Limits ### ##################### limits = Limits(wsname) limits.addEra('Run2016') limits.addAnalysis('HAA') era = 'Run2016' analysis = 'HAA' reco = 'mmmt' for mode in ['PP','PF']: limits.addChannel(mode) if doParametric: binning = varBinning[var[0]] limits.addMH(*binning[1:]) limits.addX(*binning[1:],unit='GeV',label='m_{#mu#mu}') for h in hmasses: limits.addProcess(splinename.format(h=h),signal=True) for background in backgrounds: limits.addProcess(background) # add models for h in hmasses: model = getSpline(histMap[mode][''],h,tag=mode) limits.setExpected(splinename.format(h=h),era,analysis,mode,model) if doUnbinned: bg = buildModel(limits,tag=mode) limits.setExpected('datadriven', era, analysis, mode, bg) else: # add histograms for background if not using an unbinned model for bg in backgrounds: limits.setExpected(bg,era,analysis,mode,histMap[mode][''][bg]) # get roodatahist limits.setObserved(era,analysis,mode,histMap[mode]['']['data']) else: for signal in signals: limits.addProcess(signal,signal=True) for background in backgrounds: limits.addProcess(background) for proc in backgrounds: limits.setExpected(proc,era,analysis,mode,histMap[mode][''][proc]) for proc in signals: limits.setExpected(proc,era,analysis,mode,histMap[mode][''][proc]) limits.setObserved(era,analysis,mode,histMap[mode]['']['data']) ######################### ### Add uncertainties ### ######################### systproc = tuple([proc for proc in signals + backgrounds if 'datadriven' not in proc]) allproc = tuple([proc for proc in signals + backgrounds]) systsplineproc = tuple([proc for proc in signalSplines + backgrounds if 'datadriven' not in proc]) allsplineproc = tuple([proc for proc in signalSplines + backgrounds]) bgproc = tuple([proc for proc in backgrounds]) sigsplineproc = tuple([proc for proc in signalSplines]) sigproc = tuple([proc for proc in signals]) ############ ### stat ### ############ def getStat(hist,direction): newhist = hist.Clone('{0}{1}'.format(hist.GetName(),direction)) nb = hist.GetNbinsX()*hist.GetNbinsY() for b in range(nb+1): val = hist.GetBinContent(b+1) err = hist.GetBinError(b+1) newval = val+err if direction=='Up' else val-err if newval<0: newval = 0 newhist.SetBinContent(b+1,newval) newhist.SetBinError(b+1,0) return newhist logging.info('Adding stat systematic') statMapUp = {} statMapDown = {} for proc in backgrounds+signals: statMapUp[proc] = getStat(histMap[mode][''][proc],'Up') statMapDown[proc] = getStat(histMap[mode][''][proc],'Down') statsyst = {} for mode in ['PP','PF']: # background if doUnbinned: # TODO: add errors on params pass else: for proc in bgproc: statsyst[((proc,),(era,),(analysis,),(mode,))] = (statMapUp[proc],statMapDown[proc]) # signal if doParametric: for h in hmasses: statsyst[((splinename.format(h=h),),(era,),(analysis,),(mode,))] = (getSpline(statMapUp,h,tag=mode+'StatUp'),getSpline(statMapDown,h,tag=mode+'StatDown')) else: for proc in sigproc: statsyst[((proc,),(era,),(analysis,),(mode,))] = (statMapUp[proc],statMapDown[proc]) limits.addSystematic('stat_{process}_{channel}','shape',systematics=statsyst) ############## ### shifts ### ############## for shift in shiftTypes: logging.info('Adding {} systematic'.format(shift)) shiftsyst = {} for mode in ['PP','PF']: # background if doUnbinned: # TODO rateParams on bg model pass else: for proc in bgproc: shiftsyst[((proc,),(era,),(analysis,),(mode,))] = (histMap[mode][shift+'Up'][proc], histMap[mode][shift+'Down'][proc]) # signal if doParametric: for h in hmasses: shiftsyst[((splinename.format(h=h),),(era,),(analysis,),(mode,))] = (getSpline(histMap[mode][shift+'Up'],h,tag=mode+shift+'Up'),getSpline(histMap[mode][shift+'Down'],h,tag=mode+shift+'Down')) else: for proc in sigproc: shiftsyst[((proc,),(era,),(analysis,),(mode,))] = (histMap[mode][shift+'Up'][proc], histMap[mode][shift+'Down'][proc]) limits.addSystematic(shift,'shape',systematics=shiftsyst) ############ ### Lumi ### ############ # lumi 2.3% for 2015 and 2.5% for 2016 # https://twiki.cern.ch/twiki/bin/view/CMS/TWikiLUM#CurRec logging.info('Adding lumi systematic') lumiproc = systsplineproc if doParametric else systproc lumisyst = { (lumiproc,(era,),('all',),('all',)): 1.025, } limits.addSystematic('lumi','lnN',systematics=lumisyst) ############ ### muon ### ############ # from z: 1 % + 0.5 % + 0.5 % per muon for id + iso + trig (pt>20) logging.info('Adding mu id+iso systematic') muproc = systsplineproc if doParametric else systproc musyst = { (muproc,(era,),('all',),('all',)): 1+math.sqrt(sum([0.01**2,0.005**2]*2+[0.01**2])), # 2 lead have iso, tau_mu doesnt } limits.addSystematic('muid','lnN',systematics=musyst) logging.info('Adding mu trig systematic') musyst = { (muproc,(era,),('all',),('all',)): 1.005, # 1 triggering muon } limits.addSystematic('mutrig','lnN',systematics=musyst) ########### ### tau ### ########### # 5% on sf 0.99 (VL/L) or 0.97 (M) logging.info('Adding mu id+iso systematic') tauproc = systsplineproc if doParametric else systproc tausyst = { (tauproc,(era,),('all',),('all',)): 1.05, } limits.addSystematic('tauid','lnN',systematics=tausyst) ###################### ### Print datacard ### ###################### directory = 'datacards_shape/{0}'.format('MuMuTauTau') python_mkdir(directory) datacard = '{0}/mmmt_{1}'.format(directory, args.tag) if args.tag else '{}/mmmt'.format(directory) processes = {} if doParametric: for h in hmasses: processes[signame.format(h=h,a='X')] = [splinename.format(h=h)] + backgrounds else: for signal in signals: processes[signal] = [signal]+backgrounds limits.printCard(datacard,processes=processes,blind=False,saveWorkspace=doParametric)