Esempio n. 1
0
def createWorkspace(options):
    """
	Reads out the histograms from the pickle file and converts them
	to a RooDataHist
	Prepare PDFs
	Save all to a RooWorkspace
	"""

    # Read file
    cachefile = open(options.input, 'r')
    masshistos = pickle.load(cachefile)
    cachefile.close()
    print '>>> Read mass histos from (%s)' % options.input

    ## Scale all the mass histograms in one go:
    cachefile = open(".xsecweights.pck", 'r')
    xsecweights = pickle.load(cachefile)
    cachefile.close()
    print '>>> Read xsec weights from cache (.xsecweights.pck)'

    from extractNtrkWeights import extractNTrkWeights
    ntkWeights = extractNTrkWeights()

    for key, hist in masshistos.iteritems():
        ## Xsection scaling
        hist.Scale(LUMI * xsecweights[CHANMASSTOPROCNAME[(key[1], key[2])]])

        ## Ntrack bin reweighting to data
        try:
            hist.Scale(ntkWeights['inclusive'][key[4]])
        except IndexError:
            pass

    bkgmasshistos = None
    try:
        cachefile = open(options.inputBkg, 'r')
        bkgmasshistos = pickle.load(cachefile)
        ## Note that these are already scaled to xs*lumi
        ## and weighted to data ntrack multiplicities
        cachefile.close()
        print '>>> Read background shapes from (%s)' % options.inputBkg
    except IOError:
        print '>>> No valid background shapes file found'

    # Extract the configurations from the diffhistos dictionary
    config = readConfig(masshistos)
    chselList, massList, trkMultList, combList, procList = config
    print 'Selected channels available :', chselList
    print 'Mass points available: ', massList
    print 'Track multiplicities available: ', trkMultList
    print 'Combinations available: ', combList
    print 'Processes available: ', procList

    # Initiate a workspace where the observable is the SVLMass
    # and the variable to fit is mtop
    ws = ROOT.RooWorkspace('w')
    SVLmass = ws.factory('SVLMass[100,0,300]')
    mtop = ws.factory('mtop[172.5,100,200]')
    sigStrength = ws.factory('mu[1.0,0.0,5.0]')

    # Import binned PDFs from histograms read from file
    for chsel in chselList:
        for trk in trkMultList:

            #backgrounds
            try:
                # These are already properly scaled by xsec
                hbkg = bkgmasshistos[(chsel, trk)]
                name = 'SVLMass_unm_%s_bg_%d' % (chsel, trk)
                getattr(ws,
                        'import')(ROOT.RooDataHist(name, name,
                                                   ROOT.RooArgList(SVLmass),
                                                   hbkg))
                ws.factory('%s_bgexp_%d[%f]' % (chsel, trk, hbkg.Integral()))
            except Exception, e:
                raise e

            #signal
            for mass in massList:
                # ttbar
                for comb in ['cor', 'wro']:
                    htt = masshistos[(chsel, 'tt', mass, comb, trk)]
                    getattr(ws, 'import')(ROOT.RooDataHist(
                        htt.GetName(), htt.GetTitle(),
                        ROOT.RooArgList(SVLmass), htt))

                # Only correct combinations for single top
                ht = None
                for stProc in ['t', 'tbar', 'tW', 'tbarW']:
                    try:  # get all available single top templates
                        h = masshistos[(chsel, stProc, mass, 'cor', trk)]
                        if ht is None:
                            ht = h.Clone("SVLMass_%s_%s_%d_t_%d" %
                                         ('cor', chsel, 10 * mass, trk))
                        else:
                            ht.Add(h)
                    except KeyError:
                        pass

                if ht:
                    getattr(ws, 'import')(ROOT.RooDataHist(
                        ht.GetName(), ht.GetTitle(), ROOT.RooArgList(SVLmass),
                        ht))

            # Unmatched for tt and wrong+unmatched for single top are merged
            htt_unm, ht_wrounm = None, None
            for mass in massList:
                htt = masshistos[(chsel, 'tt', mass, 'unm', trk)]
                if htt_unm is None:
                    htt_unm = htt.Clone("SVLMass_unm_%s_tt_%d" % (chsel, trk))
                else:
                    htt_unm.Add(htt)

                for comb in ['unm', 'wro']:
                    for stProc in ['t', 'tbar', 'tW', 'tbarW']:
                        try:  # get all available single top templates
                            ht = masshistos[(chsel, stProc, mass, comb, trk)]
                            if ht_wrounm is None:
                                ht_wrounm = ht.Clone("SVLMass_wrounm_%s_t_%d" %
                                                     (chsel, trk))
                            else:
                                ht_wrounm.Add(ht)
                        except KeyError:
                            pass
            if htt_unm:
                getattr(ws,
                        'import')(ROOT.RooDataHist(htt_unm.GetName(),
                                                   htt_unm.GetTitle(),
                                                   ROOT.RooArgList(SVLmass),
                                                   htt_unm))
            if ht_wrounm:
                getattr(ws,
                        'import')(ROOT.RooDataHist(ht_wrounm.GetName(),
                                                   ht_wrounm.GetTitle(),
                                                   ROOT.RooArgList(SVLmass),
                                                   ht_wrounm))
Esempio n. 2
0
def createWorkspace(options):
	"""
	Reads out the histograms from the pickle file and converts them
	to a RooDataHist
	Prepare PDFs
	Save all to a RooWorkspace
	"""

	# Read file
	cachefile = open(options.input,'r')
	masshistos = pickle.load(cachefile)
	cachefile.close()
	print '>>> Read mass histos from (%s)' % options.input

	## Scale all the mass histograms in one go:
	cachefile = open(".xsecweights.pck", 'r')
	xsecweights = pickle.load(cachefile)
	cachefile.close()
	print '>>> Read xsec weights from cache (.xsecweights.pck)'

	from extractNtrkWeights import extractNTrkWeights
	ntkWeights = extractNTrkWeights()

	for key, hist in masshistos.iteritems():
		## Xsection scaling
		hist.Scale(LUMI*xsecweights[CHANMASSTOPROCNAME[(key[1], key[2])]])

		## Ntrack bin reweighting to data
		try: hist.Scale(ntkWeights['inclusive'][key[4]])
		except IndexError: pass

	bkgmasshistos=None
	try:
		cachefile = open(options.inputBkg,'r')
		bkgmasshistos = pickle.load(cachefile)
		## Note that these are already scaled to xs*lumi
		## and weighted to data ntrack multiplicities
		cachefile.close()
		print '>>> Read background shapes from (%s)' % options.inputBkg
	except IOError:
		print '>>> No valid background shapes file found'


	# Extract the configurations from the diffhistos dictionary
	config = readConfig(masshistos)
	chselList, massList, trkMultList, combList, procList = config
	print 'Selected channels available :', chselList
	print 'Mass points available: ', massList
	print 'Track multiplicities available: ', trkMultList
	print 'Combinations available: ', combList
	print 'Processes available: ' , procList

	# Initiate a workspace where the observable is the SVLMass
	# and the variable to fit is mtop
	ws          = ROOT.RooWorkspace('w')
	SVLmass     = ws.factory('SVLMass[100,0,300]')
	mtop        = ws.factory('mtop[172.5,100,200]')
	sigStrength = ws.factory('mu[1.0,0.0,5.0]')

	# Import binned PDFs from histograms read from file
	for chsel in chselList:
		for trk in trkMultList:

			#backgrounds
			try:
				# These are already properly scaled by xsec
				hbkg = bkgmasshistos[(chsel,trk)]
				name = 'SVLMass_unm_%s_bg_%d'%(chsel,trk)
				getattr(ws,'import')(ROOT.RooDataHist(name,name, ROOT.RooArgList(SVLmass), hbkg))
				ws.factory('%s_bgexp_%d[%f]'%(chsel,trk,hbkg.Integral()))
			except Exception, e:
				raise e

			#signal
			for mass in massList:
				# ttbar
				for comb in ['cor','wro']:
					htt = masshistos[(chsel,'tt',mass,comb,trk)]
					getattr(ws,'import')(ROOT.RooDataHist(htt.GetName(), htt.GetTitle(), ROOT.RooArgList(SVLmass), htt))

				# Only correct combinations for single top
				ht = None
				for stProc in ['t','tbar','tW','tbarW']:
					try: # get all available single top templates
						h = masshistos[(chsel,stProc,mass,'cor',trk)]
						if ht is None:
							ht = h.Clone("SVLMass_%s_%s_%d_t_%d"%('cor',chsel,10*mass,trk))
						else:
							ht.Add(h)
					except KeyError: pass

				if ht:
					getattr(ws,'import')(ROOT.RooDataHist(ht.GetName(), ht.GetTitle(), ROOT.RooArgList(SVLmass), ht))

			# Unmatched for tt and wrong+unmatched for single top are merged
			htt_unm, ht_wrounm = None, None
			for mass in massList:
				htt = masshistos[(chsel,'tt',mass,'unm',trk)]
				if htt_unm is None : htt_unm=htt.Clone("SVLMass_unm_%s_tt_%d"%(chsel,trk))
				else               : htt_unm.Add(htt)

				for comb in ['unm','wro']:
					for stProc in ['t','tbar','tW','tbarW']:
						try: # get all available single top templates
							ht = masshistos[(chsel,stProc,mass,comb,trk)]
							if ht_wrounm is None : ht_wrounm=ht.Clone("SVLMass_wrounm_%s_t_%d"%(chsel,trk))
							else                 : ht_wrounm.Add(ht)
						except KeyError: pass
			if htt_unm:
				getattr(ws,'import')(ROOT.RooDataHist(htt_unm.GetName(), htt_unm.GetTitle(),
									 ROOT.RooArgList(SVLmass), htt_unm))
			if ht_wrounm:
				getattr(ws,'import')(ROOT.RooDataHist(ht_wrounm.GetName(), ht_wrounm.GetTitle(),
									 ROOT.RooArgList(SVLmass), ht_wrounm))
def main(args, opt):
	os.system('mkdir -p %s'%opt.outDir)
	mcfiles = {}   # procname -> filename
	datafiles = {} # procname -> filename
	try:
		for fname in os.listdir(args[0]):
			if not osp.splitext(fname)[1] == '.root': continue
			isdata,procname,splitno = resolveFilename(fname)
			if isdata:
				if not procname in datafiles:
					datafiles[procname] = []
				datafiles[procname].append(osp.join(args[0],fname))
			else:
				if 'QCD' in procname:                   continue ## exclude QCD
				if procname == 'TTJets_MSDecays_172v5': continue ## have those already
				if 'SingleT' in procname:               continue ## have those already

				if not procname in mcfiles:
					mcfiles[procname] = []
				mcfiles[procname].append(osp.join(args[0],fname))

	except IndexError:
		print "Please provide a valid input directory"
		exit(-1)


	## Produce (or read) the histogram data
	bghistos = makeBackgroundHistos(mcfiles, opt)

	cachefile = open(".xsecweights.pck", 'r')
	xsecweights = pickle.load(cachefile)
	cachefile.close()
	print '>>> Read xsec weights from cache (.xsecweights.pck)'

	cachefile = open(".svldyscalefactors.pck", 'r')
	dySFs = pickle.load(cachefile)
	cachefile.close()
	print '>>> Read DY scale factors from cache (.svldyscalefactors.pck)'

	cachefile = open(".svlqcdtemplates.pck", 'r')
	qcdTemplates = pickle.load(cachefile)
	cachefile.close()
	print '>>> Read QCD templates from cache (.svlqcdtemplates.pck)'

	## Read SV Track multiplicity weights:
	from extractNtrkWeights import extractNTrkWeights
	ntkWeights = extractNTrkWeights()

	## Now add them up with proper scales
	mcprocesses = [k for k in mcfiles.keys() if not 'Data8TeV' in k]
	bghistos_added = sumBGHistos(processes=mcprocesses,
		                         bghistos=bghistos,
		                         xsecweights=xsecweights,
		                         ntkWeights=ntkWeights,
		                         dySFs=dySFs,
		                         qcdTemplates=qcdTemplates,
		                         opt=opt)

	bghistos_added_dyup = sumBGHistos(processes=mcprocesses,
		                         bghistos=bghistos,
		                         xsecweights=xsecweights,
		                         ntkWeights=ntkWeights,
		                         dySFs=dySFs,
		                         qcdTemplates=qcdTemplates,
		                         opt=opt,
		                         dyScale=1.3)
	bghistos_added_dydn = sumBGHistos(processes=mcprocesses,
		                         bghistos=bghistos,
		                         xsecweights=xsecweights,
		                         ntkWeights=ntkWeights,
		                         dySFs=dySFs,
		                         qcdTemplates=qcdTemplates,
		                         opt=opt,
		                         dyScale=0.7)
	bghistos_added_qcdup = sumBGHistos(processes=mcprocesses,
		                         bghistos=bghistos,
		                         xsecweights=xsecweights,
		                         ntkWeights=ntkWeights,
		                         dySFs=dySFs,
		                         qcdTemplates=qcdTemplates,
		                         opt=opt,
		                         qcdScale=1.1)
	bghistos_added_qcddn = sumBGHistos(processes=mcprocesses,
		                         bghistos=bghistos,
		                         xsecweights=xsecweights,
		                         ntkWeights=ntkWeights,
		                         dySFs=dySFs,
		                         qcdTemplates=qcdTemplates,
		                         opt=opt,
		                         qcdScale=0.9)

	## Produce data histograms
	datahistos = makeDataHistos(datafiles, opt)
	datahistos_added = sumDataHistos(datafiles.keys(), datahistos)
	# Rebin also data, if required:
	if opt.rebin>0:
		for hist in datahistos_added.values():
			hist.Rebin(opt.rebin)

	## Save the background only shapes separately as templates for the fit
	cachefile = open(".svlbgtemplates.pck", 'w')
	pickle.dump(bghistos_added, cachefile, pickle.HIGHEST_PROTOCOL)
	print '>>> Dumped bg templates to cache (.svlbgtemplates.pck)'
	cachefile.close()

	## Read syst histos:
	cachefile = open(".svlsysthistos.pck", 'r')
	systhistos = pickle.load(cachefile)
	print '>>> Read systematics histograms from cache (.svlsysthistos.pck)'
	cachefile.close()

	## Read mass scan histos:
	cachefile = open(".svlmasshistos.pck", 'r')
	masshistos = pickle.load(cachefile)
	print '>>> Read mass scan histograms from cache (.svlmasshistos.pck)'
	# (tag, chan, mass, comb)      -> histo
	# (tag, chan, mass, comb, ntk) -> histo
	cachefile.close()

	ofi = ROOT.TFile.Open(osp.join(opt.outDir,'pe_inputs.root'),'RECREATE')
	ofi.cd()

	#####################################################
	## Central mass point and syst samples
	for syst in ([s for s,_,_,_ in ALLSYSTS] +
	             ['dyup','dydown','qcdup','qcddown','ntkmult']):
		odir = ofi.mkdir(syst + '_172v5')
		odir.cd()
		for tag,_,_ in SELECTIONS:
			for ntk,_ in NTRKBINS:
				hname = "SVLMass_%s_%s_%s" % (tag,syst+'_172v5',ntk)
				if not syst in ['dyup','dydown','qcdup','qcddown','ntkmult',
				                'tchscaleup','tchscaledown',
				                'twchscaleup','twchscaledown']:
					hfinal = systhistos[(tag,syst,'tot',ntk)].Clone(hname)
				else:
					hfinal = systhistos[(tag,'nominal','tot',ntk)].Clone(hname)
				try:
					## Systs from separate samples
					if syst in ['tchscaleup','tchscaledown',
					            'twchscaleup','twchscaledown']:
						scale = LUMI*xsecweights[CHANMASSTOPROCNAME[('tt', 172.5)]]
					else:
						scale = LUMI*xsecweights[SYSTTOPROCNAME[syst][0]]
				except KeyError:
					## Systs from event weights
					scale = LUMI*xsecweights[CHANMASSTOPROCNAME[('tt', 172.5)]]
				hfinal.Scale(scale)

				## Renormalize some variations with event weights
				if syst in SYSTSTOBERENORMALIZED:
					normintegral = systhistos[(tag,'nominal','tot',ntk)].Integral()
					normintegral *= LUMI*xsecweights[CHANMASSTOPROCNAME[('tt', 172.5)]]
					normintegral /= hfinal.Integral()
					hfinal.Scale(normintegral)

				## Add single top
				stProcs=['t', 'tbar', 'tW', 'tbarW']
				stSystProcs=[]
				if 'tchscale' in syst:
					stProcs=['tW', 'tbarW']
					stSystProcs=['t', 'tbar']
				if 'twchscale' in syst:
					stProcs=['t', 'tbar']
					stSystProcs=['tW', 'tbarW']
				for st in stProcs:
					hsinglet = masshistos[(tag, st, 172.5,'tot',ntk)].Clone('%s_%s'%(hname,st))
					hsinglet.Scale(LUMI*xsecweights[CHANMASSTOPROCNAME[(st, 172.5)]])
					hfinal.Add(hsinglet)
				for st in stSystProcs:
					hsinglet = systhistos[(tag, syst, 'tot', ntk)].Clone('%s_%s'%(hname,st))
					hsinglet.Scale(LUMI*xsecweights[CHANMASSTOPROCNAME[(st, 172.5)]])
					hfinal.Add(hsinglet)


				## Add the backgrounds
				if not syst in ['dyup','dydown','qcdup','qcddown']:
					hfinal.Add(bghistos_added[(tag,ntk)])
				else: ## From the scaled bghistos if necessary
					bghistos_added_scaled = {
						'dyup'    : bghistos_added_dyup,
						'dydown'  : bghistos_added_dydn,
						'qcdup'   : bghistos_added_qcdup,
						'qcddown' : bghistos_added_qcddn,
					}[syst]
					hfinal.Add(bghistos_added_scaled[(tag,ntk)])

				## Rebin if requested
				if opt.rebin>0:
					hfinal.Rebin(opt.rebin)

				## Scale by SV track multiplicity weights:
				if not syst == 'ntkmult':
					hfinal.Scale(ntkWeights['inclusive'][ntk])

				## Write out to file
				hfinal.Write(hname, ROOT.TObject.kOverwrite)

	#####################################################
	## Non-central mass points
	ROOT.gSystem.Load('libUserCodeTopMassSecVtx.so')
	from ROOT import th1fmorph
	# extract mass points from dictionary
	mass_points = sorted(list(set([key[2] for key in masshistos.keys()])))
	mass_points = mass_points[1:-1] # remove outermost points
	debughistos = []
	for mass in mass_points:
		if mass == 172.5: continue
		mname = 'nominal_%s' % str(mass).replace('.','v')
		odir = ofi.mkdir(mname)
		odir.cd()
		for tag,_,_ in SELECTIONS:
			for ntk,_ in NTRKBINS:
				hname = "SVLMass_%s_%s_%s" % (tag,mname,ntk)
				hfinal = masshistos[(tag,'tt',mass,'tot',ntk)].Clone(hname)
				hfinal.Scale(LUMI*xsecweights[CHANMASSTOPROCNAME[('tt', mass)]])

				## Add single top (t-channel, for which we have the samples)
				for st in ['t', 'tbar']:
					hsinglet = masshistos[(tag, st, mass,'tot',ntk)].Clone('%s_%s'%(hname,st))
					hsinglet.Scale(LUMI*xsecweights[CHANMASSTOPROCNAME[(st, mass)]])
					hfinal.Add(hsinglet)

				## Add single top (tW-channel, for which we don't have samples)
				## Morph between the two extreme mass points to get
				## the non existing ones
				for st in ['tW', 'tbarW']:
					if mass not in [166.5, 178.5]:
						hsingletW = th1fmorph('%s_%s_morph'%(hname,st),
							                  '%s_%s_morphed'%(hname,st),
							                   masshistos[(tag, 'tW', 166.5,'tot',ntk)],
							                   masshistos[(tag, 'tW', 178.5,'tot',ntk)],
							                   166.5, 178.5, mass,
							                   masshistos[(tag, 'tW', 166.5,'tot',ntk)].Integral())
						hsingletW.Scale(LUMI*xsecweights[CHANMASSTOPROCNAME[(st, 166.5)]]
							                * TWXSECS[mass]/TWXSECS[166.5])
						hsingletW.SetDirectory(0)
					else:
						hsingletW = masshistos[(tag, st, mass,'tot',ntk)].Clone('%s_%s'%(hname,st))
						hsingletW.Scale(LUMI*xsecweights[CHANMASSTOPROCNAME[(st, mass)]])
					hfinal.Add(hsingletW)

				## Add the combined backgrounds
				hfinal.Add(bghistos_added[(tag,ntk)])

				## Rebin if requested
				if opt.rebin>0:
					hfinal.Rebin(opt.rebin)

				## Scale by SV track multiplicity weights:
				hfinal.Scale(ntkWeights['inclusive'][ntk])

				## Write out to file
				hfinal.Write(hname, ROOT.TObject.kOverwrite)

	## Write also data histos
	ofi.cd()
	odir = ofi.mkdir('data')
	odir.cd()
	for tag,_,_ in SELECTIONS:
		for ntk,_ in NTRKBINS:
			hname = "SVLMass_%s_data_%s" % (tag,ntk)
			datahistos_added[(tag,ntk)].Write(hname, ROOT.TObject.kOverwrite)


	print ('>>> Wrote pseudo experiment inputs to file (%s)' %
		                      osp.join(opt.outDir,'pe_inputs.root'))

	ofi.Write()
	ofi.Close()

	return 0
def main(args, opt):
    os.system('mkdir -p %s' % opt.outDir)
    mcfiles = {}  # procname -> filename
    datafiles = {}  # procname -> filename
    try:
        for fname in os.listdir(args[0]):
            if not osp.splitext(fname)[1] == '.root': continue
            isdata, procname, splitno = resolveFilename(fname)
            if isdata:
                if not procname in datafiles:
                    datafiles[procname] = []
                datafiles[procname].append(osp.join(args[0], fname))
            else:
                if 'QCD' in procname: continue  ## exclude QCD
                if procname == 'TTJets_MSDecays_172v5':
                    continue  ## have those already
                if 'SingleT' in procname: continue  ## have those already

                if not procname in mcfiles:
                    mcfiles[procname] = []
                mcfiles[procname].append(osp.join(args[0], fname))

    except IndexError:
        print "Please provide a valid input directory"
        exit(-1)

    ## Produce (or read) the histogram data
    bghistos = makeBackgroundHistos(mcfiles, opt)

    cachefile = open(".xsecweights.pck", 'r')
    xsecweights = pickle.load(cachefile)
    cachefile.close()
    print '>>> Read xsec weights from cache (.xsecweights.pck)'

    cachefile = open(".svldyscalefactors.pck", 'r')
    dySFs = pickle.load(cachefile)
    cachefile.close()
    print '>>> Read DY scale factors from cache (.svldyscalefactors.pck)'

    cachefile = open(".svlqcdtemplates.pck", 'r')
    qcdTemplates = pickle.load(cachefile)
    cachefile.close()
    print '>>> Read QCD templates from cache (.svlqcdtemplates.pck)'

    ## Read SV Track multiplicity weights:
    from extractNtrkWeights import extractNTrkWeights
    ntkWeights = extractNTrkWeights()

    ## Now add them up with proper scales
    mcprocesses = [k for k in mcfiles.keys() if not 'Data8TeV' in k]
    bghistos_added = sumBGHistos(processes=mcprocesses,
                                 bghistos=bghistos,
                                 xsecweights=xsecweights,
                                 ntkWeights=ntkWeights,
                                 dySFs=dySFs,
                                 qcdTemplates=qcdTemplates,
                                 opt=opt)

    bghistos_added_dyup = sumBGHistos(processes=mcprocesses,
                                      bghistos=bghistos,
                                      xsecweights=xsecweights,
                                      ntkWeights=ntkWeights,
                                      dySFs=dySFs,
                                      qcdTemplates=qcdTemplates,
                                      opt=opt,
                                      dyScale=1.3)
    bghistos_added_dydn = sumBGHistos(processes=mcprocesses,
                                      bghistos=bghistos,
                                      xsecweights=xsecweights,
                                      ntkWeights=ntkWeights,
                                      dySFs=dySFs,
                                      qcdTemplates=qcdTemplates,
                                      opt=opt,
                                      dyScale=0.7)
    bghistos_added_qcdup = sumBGHistos(processes=mcprocesses,
                                       bghistos=bghistos,
                                       xsecweights=xsecweights,
                                       ntkWeights=ntkWeights,
                                       dySFs=dySFs,
                                       qcdTemplates=qcdTemplates,
                                       opt=opt,
                                       qcdScale=1.1)
    bghistos_added_qcddn = sumBGHistos(processes=mcprocesses,
                                       bghistos=bghistos,
                                       xsecweights=xsecweights,
                                       ntkWeights=ntkWeights,
                                       dySFs=dySFs,
                                       qcdTemplates=qcdTemplates,
                                       opt=opt,
                                       qcdScale=0.9)

    ## Produce data histograms
    datahistos = makeDataHistos(datafiles, opt)
    datahistos_added = sumDataHistos(datafiles.keys(), datahistos)
    # Rebin also data, if required:
    if opt.rebin > 0:
        for hist in datahistos_added.values():
            hist.Rebin(opt.rebin)

    ## Save the background only shapes separately as templates for the fit
    cachefile = open(".svlbgtemplates.pck", 'w')
    pickle.dump(bghistos_added, cachefile, pickle.HIGHEST_PROTOCOL)
    print '>>> Dumped bg templates to cache (.svlbgtemplates.pck)'
    cachefile.close()

    ## Read syst histos:
    cachefile = open(".svlsysthistos.pck", 'r')
    systhistos = pickle.load(cachefile)
    print '>>> Read systematics histograms from cache (.svlsysthistos.pck)'
    cachefile.close()

    ## Read mass scan histos:
    cachefile = open(".svlmasshistos.pck", 'r')
    masshistos = pickle.load(cachefile)
    print '>>> Read mass scan histograms from cache (.svlmasshistos.pck)'
    # (tag, chan, mass, comb)      -> histo
    # (tag, chan, mass, comb, ntk) -> histo
    cachefile.close()

    ## Signal only (tt+t+tW) shapes
    signalonly = {}

    ofi = ROOT.TFile.Open(osp.join(opt.outDir, 'pe_inputs.root'), 'RECREATE')
    ofi.cd()

    #####################################################
    ## Central mass point and syst samples
    to_be_processed = ([s for s, _, _, _ in ALLSYSTS] +
                       ['dyup', 'dydown', 'qcdup', 'qcddown', 'ntkmult'])
    if opt.skip_systs: to_be_processed = ['nominal']

    for syst in to_be_processed:
        odir = ofi.mkdir(syst + '_172v5')
        odir.cd()
        for tag, _, _ in SELECTIONS:
            for ntk, _ in NTRKBINS:
                hname = "SVLMass_%s_%s_%s" % (tag, syst + '_172v5', ntk)
                if not syst in [
                        'dyup', 'dydown', 'qcdup', 'qcddown', 'ntkmult',
                        'tchscaleup', 'tchscaledown', 'twchscaleup',
                        'twchscaledown'
                ]:
                    hfinal = systhistos[(tag, syst, 'tot', ntk)].Clone(hname)
                else:
                    hfinal = systhistos[(tag, 'nominal', 'tot',
                                         ntk)].Clone(hname)
                try:
                    ## Systs from separate samples
                    if syst in [
                            'tchscaleup', 'tchscaledown', 'twchscaleup',
                            'twchscaledown'
                    ]:
                        scale = LUMI * xsecweights[CHANMASSTOPROCNAME[('tt',
                                                                       172.5)]]
                    else:
                        scale = LUMI * xsecweights[SYSTTOPROCNAME[syst][0]]
                except KeyError:
                    ## Systs from event weights
                    scale = LUMI * xsecweights[CHANMASSTOPROCNAME[('tt',
                                                                   172.5)]]
                hfinal.Scale(scale)

                ## Renormalize some variations with event weights
                if syst in SYSTSTOBERENORMALIZED:
                    normintegral = systhistos[(tag, 'nominal', 'tot',
                                               ntk)].Integral()
                    normintegral *= LUMI * xsecweights[CHANMASSTOPROCNAME[
                        ('tt', 172.5)]]
                    normintegral /= hfinal.Integral()
                    hfinal.Scale(normintegral)

                ## Add single top
                stProcs = ['t', 'tbar', 'tW', 'tbarW']
                stSystProcs = []
                if 'tchscale' in syst:
                    stProcs = ['tW', 'tbarW']
                    stSystProcs = ['t', 'tbar']
                if 'twchscale' in syst:
                    stProcs = ['t', 'tbar']
                    stSystProcs = ['tW', 'tbarW']
                for st in stProcs:
                    hsinglet = masshistos[(tag, st, 172.5, 'tot',
                                           ntk)].Clone('%s_%s' % (hname, st))
                    hsinglet.Scale(
                        LUMI * xsecweights[CHANMASSTOPROCNAME[(st, 172.5)]])
                    hfinal.Add(hsinglet)
                for st in stSystProcs:
                    hsinglet = systhistos[(tag, syst, 'tot',
                                           ntk)].Clone('%s_%s' % (hname, st))
                    hsinglet.Scale(
                        LUMI * xsecweights[CHANMASSTOPROCNAME[(st, 172.5)]])
                    hfinal.Add(hsinglet)

                ## Save signal only shapes
                if syst == 'nominal':
                    signalonly[(tag, 172.5,
                                ntk)] = hfinal.Clone('%s_sigonly' % hname)
                    signalonly[(tag, 172.5,
                                ntk)].Scale(ntkWeights['inclusive'][ntk])
                    if opt.rebin > 0:
                        signalonly[(tag, 172.5, ntk)].Rebin(opt.rebin)

                ## Add the backgrounds
                if not syst in ['dyup', 'dydown', 'qcdup', 'qcddown']:
                    hfinal.Add(bghistos_added[(tag, ntk)])
                else:  ## From the scaled bghistos if necessary
                    bghistos_added_scaled = {
                        'dyup': bghistos_added_dyup,
                        'dydown': bghistos_added_dydn,
                        'qcdup': bghistos_added_qcdup,
                        'qcddown': bghistos_added_qcddn,
                    }[syst]
                    hfinal.Add(bghistos_added_scaled[(tag, ntk)])

                ## Rebin if requested
                if opt.rebin > 0:
                    hfinal.Rebin(opt.rebin)

                ## Scale by SV track multiplicity weights:
                if not syst == 'ntkmult':
                    hfinal.Scale(ntkWeights['inclusive'][ntk])

                ## Write out to file
                hfinal.Write(hname, ROOT.TObject.kOverwrite)

    #####################################################
    ## Non-central mass points
    ROOT.gSystem.Load('libUserCodeTopMassSecVtx.so')
    from ROOT import th1fmorph
    # extract mass points from dictionary
    mass_points = sorted(list(set([key[2] for key in masshistos.keys()])))
    mass_points = mass_points[1:-1]  # remove outermost points
    debughistos = []
    for mass in mass_points:
        if mass == 172.5: continue
        mname = 'nominal_%s' % str(mass).replace('.', 'v')
        odir = ofi.mkdir(mname)
        odir.cd()
        for tag, _, _ in SELECTIONS:
            for ntk, _ in NTRKBINS:
                hname = "SVLMass_%s_%s_%s" % (tag, mname, ntk)
                hfinal = masshistos[(tag, 'tt', mass, 'tot', ntk)].Clone(hname)
                hfinal.Scale(LUMI *
                             xsecweights[CHANMASSTOPROCNAME[('tt', mass)]])

                ## Add single top (t-channel, for which we have the samples)
                for st in ['t', 'tbar']:
                    hsinglet = masshistos[(tag, st, mass, 'tot',
                                           ntk)].Clone('%s_%s' % (hname, st))
                    hsinglet.Scale(LUMI *
                                   xsecweights[CHANMASSTOPROCNAME[(st, mass)]])
                    hfinal.Add(hsinglet)

                ## Add single top (tW-channel, for which we don't have samples)
                ## Morph between the two extreme mass points to get
                ## the non existing ones
                for st in ['tW', 'tbarW']:
                    if mass not in [166.5, 178.5]:
                        hsingletW = th1fmorph(
                            '%s_%s_morph' % (hname, st),
                            '%s_%s_morphed' % (hname, st),
                            masshistos[(tag, 'tW', 166.5, 'tot', ntk)],
                            masshistos[(tag, 'tW', 178.5, 'tot', ntk)], 166.5,
                            178.5, mass, masshistos[(tag, 'tW', 166.5, 'tot',
                                                     ntk)].Integral())
                        hsingletW.Scale(
                            LUMI *
                            xsecweights[CHANMASSTOPROCNAME[(st, 166.5)]] *
                            TWXSECS[mass] / TWXSECS[166.5])
                        hsingletW.SetDirectory(0)
                    else:
                        hsingletW = masshistos[(tag, st, mass, 'tot',
                                                ntk)].Clone('%s_%s' %
                                                            (hname, st))
                        hsingletW.Scale(
                            LUMI * xsecweights[CHANMASSTOPROCNAME[(st, mass)]])
                    hfinal.Add(hsingletW)

                ## Save signal only shapes
                signalonly[(tag, mass,
                            ntk)] = hfinal.Clone('%s_sigonly' % hname)
                signalonly[(tag, mass,
                            ntk)].Scale(ntkWeights['inclusive'][ntk])
                if opt.rebin > 0:
                    signalonly[(tag, mass, ntk)].Rebin(opt.rebin)

                ## Add the combined backgrounds
                hfinal.Add(bghistos_added[(tag, ntk)])

                ## Rebin if requested
                if opt.rebin > 0:
                    hfinal.Rebin(opt.rebin)

                ## Scale by SV track multiplicity weights:
                hfinal.Scale(ntkWeights['inclusive'][ntk])

                ## Write out to file
                hfinal.Write(hname, ROOT.TObject.kOverwrite)

    ## Save the signal only shapes (tt+t+tW) as input for the combined plot
    cachefile = open(".svlsignalshapes.pck", 'w')
    pickle.dump(signalonly, cachefile, pickle.HIGHEST_PROTOCOL)
    print '>>> Dumped signal only shapes to cache (.svlsignalshapes.pck)'
    cachefile.close()

    ## Write also data histos
    ofi.cd()
    odir = ofi.mkdir('data')
    odir.cd()
    for tag, _, _ in SELECTIONS:
        for ntk, _ in NTRKBINS:
            hname = "SVLMass_%s_data_%s" % (tag, ntk)
            datahistos_added[(tag, ntk)].Write(hname, ROOT.TObject.kOverwrite)

    print('>>> Wrote pseudo experiment inputs to file (%s)' %
          osp.join(opt.outDir, 'pe_inputs.root'))

    ofi.Write()
    ofi.Close()

    return 0