def _get_mctruth_scale_and_resolution(self): ## Enlarge the range of the observable to get vanishing tails for ## the photon energy scale resolution # savrange = (self.phoERes.getMin(), self.phoERes.getMax()) # self.phoERes.setRange(savrange[0] - 10, savrange[1] + 10) ## Build the model for the photon energy resolution. self.phoEResPdf = ParametrizedKeysPdf('phoEResPdf', 'phoEResPdf', self.phoERes, self.s, self.r, self.data, ROOT.RooKeysPdf.NoMirror, self.rho) # self.phoERes.setRange(*savrange) ## Set sensible initial values self.s.setVal(self.phoEResPdf.shapemode) self.r.setVal(self.phoEResPdf.shapewidth) ## Extract the MC truth scale and resolution from MC self.fitresult_mctruth = self.phoEResPdf.fitTo( self.data, roo.PrintLevel(self.printlevel), roo.SumW2Error(False), roo.Range(-50, 50), roo.Save(), roo.Strategy(2)) self.w.Import(self.fitresult_mctruth) ## Store the MC truth scale and resolution for source, target in zip([self.s, self.r], [self.s0, self.r0]): target.setVal(source.getVal()) target.setError(source.getError()) target.setAsymError(source.getErrorLo(), source.getErrorHi()) # self.s0.setVal(self.s.getVal()) # self.r0.setVal(self.r.getVal()) self.w.saveSnapshot('sr_mctruth', self.sr) self.w.saveSnapshot('sr0_mctruth', self.sr0) self.s0.setConstant(True) self.r0.setConstant(True)
def loop_over_smearings(): "Extract scale and resolution for mass and energy." ## Set initial value for the mode calculation of the phoERes shape. phoERes.setVal(0) ## Set initial value for the mode calculation of the mmgMass shape. mmgMass.setVal(91.2) ## Define then nominal model for the photon energy smearing function. phoEResPdf = ParametrizedKeysPdf('phoEResPdf_nominal', 'phoEResPdf_nominal', phoERes, phoScale, phoRes, data, ROOT.RooKeysPdf.NoMirror, 1.5) ## Define the nominal mmg mass model. mmgMassPdf = ParametrizedKeysPdf('mmgMassPdf_nominal', 'mmgMassPdf_nominal', mmgMass, massPeak, massWidth, data, ROOT.RooKeysPdf.NoMirror, 1.5) w.Import(phoEResPdf) w.Import(mmgMassPdf) w.Import(phoEResPdf.shape) w.Import(mmgMassPdf.shape) for i, (s, r) in enumerate(zip(stargets, rtargets)): ## Get the smeared data. phoScaleTarget.setVal(s) phoResTarget.setVal(r) sdata = calibrator.get_smeared_data(s, r) ## Save the smeared data in the workspace. sdata.SetName('sdata_%d' % i) sdata.SetTitle('smeared mmg data %d' % i) w.Import(sdata) ## Build the energy and mass models for each smearing since the ## shapes may have changed. ## Guess the right values of the fit parameters. phoScale.setVal(s) phoRes.setVal(phoEResPdf.shapewidth) massScale.setVal(100 * (mmgMassPdf.shapemode / mZ.getVal() - 1)) massRes.setVal(100 * mmgMassPdf.shapewidth / mmgMassPdf.shapemode) ## Fit the models. phoFit = phoEResPdf.fitTo(sdata, roo.Range(-50, 50), # roo.Range(s - 5*r, s + 5*r), # roo.PrintLevel(-1), roo.Save()) massFit = mmgMassPdf.fitTo(sdata, roo.Range(60, 120), # roo.PrintLevel(-1), roo.Save()) ## Store results in the workspace. w.Import(phoFit) w.Import(massFit) w.saveSnapshot('smear_%d' % i, params, True)
def plot_mmgmass_with_fit_for_multiple_smearings(name, stargets, rtargets, colors, plotrange=(60, 105)): """Plot the smeared mmg mass for a number of different smearings.""" canvases.next(name).SetGrid() mmgMass.setRange('plot', *plotrange) plot = mmgMass.frame(roo.Range('plot')) plot.SetTitle("") slabels = [] rlabels = [] ## Loop over the various smearings. for starget, rtarget, color in zip(stargets, rtargets, colors): mydata = calibrator.get_smeared_data(starget, rtarget) model = ParametrizedKeysPdf('model', 'model', mmgMass, mmgMassSmearPeak, mmgMassSmearWidth, mydata, ROOT.RooKeysPdf.NoMirror, 1.5) model.fitTo(mydata, roo.PrintLevel(-1), roo.Range(60, 120), roo.SumW2Error(False)) mydata.plotOn(plot, roo.LineColor(color), roo.MarkerColor(color)) model.plotOn(plot, roo.LineColor(color), roo.Range('plot'), roo.NormRange('plot')) slabels.append([ 's\' = % 3.f %%, ' % starget + '#Delta m_{#mu#mu#gamma} = %.2f #pm %.2f %%' % ( 100 * (mmgMassSmearPeak.getVal() / 91.2 - 1.), 100 * mmgMassSmearPeak.getError() / 91.2 ), ]) rlabels.append([ 'r\' = %.1f %%, ' % rtarget + '#sigma_{eff}/#mu(m_{\mu\mu\gamma}) = % .2f #pm %.2f %%' % ( 100 * mmgMassSmearWidth.getVal() / mmgMassSmearPeak.getVal(), 100 * mmgMassSmearWidth.getError() / mmgMassSmearPeak.getVal(), ), ]) ## End of loop over the various smearings. plot.Draw() for i, (labels, color) in enumerate(zip(slabels, colors)): latex = Latex(labels, position=(0.18, 0.85 - i*0.055)) latex.SetTextColor(color) latex.draw() for i, (labels, color) in enumerate(zip(rlabels, colors)): latex = Latex(labels, position=(0.18, 0.85 - (len(slabels)+1) * 0.055 - i*0.055)) latex.SetTextColor(color) latex.draw()
def plot_nominal_mmgmass_with_shape_and_fit(): """Plot the nominal MC mmg mass data overlayed with the pdf shape and fit.""" canvases.next('NominalMmgMassWithShapeAndFit') plot = mmgMass.frame(roo.Range(75, 105)) plot.SetTitle("m(#mu#mu#gamma) overlayed with PDF shape (blue) " "and it's parametrized fit (dashed red)") data.plotOn(plot) ## Define the mmg mass model. mmgMassPdf = ParametrizedKeysPdf('mmgMassPdf', 'mmgMassPdf', mmgMass, massPeak, massWidth, data, ROOT.RooKeysPdf.NoMirror, 1.5) ## PDF shape mmgMassPdf.shape.plotOn(plot) ## Parametrized fit of the PDF shape mmgMassPdf.fitTo(data, roo.Range(60, 120), roo.PrintLevel(-1)) mmgMassPdf.plotOn(plot, roo.LineColor(ROOT.kRed), roo.LineStyle(ROOT.kDashed)) plot.Draw() sshape = 100 * (mmgMassPdf.shapemode / mZ.getVal() - 1) rshape = 100 * mmgMassPdf.shapewidth / mmgMassPdf.shapemode Latex([ 's_{shape}: %.3f %%' % sshape, 's_{fit}: %.3f #pm %.3f %%' % (massScale.getVal(), massScale.getError()), 's_{fit} - s_{shape}: %.4f #pm %.4f %%' % (massScale.getVal() - sshape, massScale.getError()), 'r_{shape}: %.3f %%' % rshape, 'r_{fit}: %.3f #pm %.3f %%' % (massRes.getVal(), massRes.getError()), 'r_{fit} - r_{shape}: %.4f #pm %.4f %%' % (massRes.getVal() - rshape, massRes.getError()), 'r_{fit}/r_{shape}: %.4f #pm %.4f' % (massRes.getVal() / rshape, massRes.getError() / rshape), ], position=(0.2, 0.8)).draw()
def plot_nominal_mmgmass_with_shape_and_fit(): """Plot the nominal MC mmg mass data overlayed with the pdf shape and fit.""" canvases.next('NominalMmgMassWithShapeAndFit') plot = mmgMass.frame(roo.Range(75, 105)) plot.SetTitle("m(#mu#mu#gamma) overlayed with PDF shape (blue) " "and it's parametrized fit (dashed red)") data.plotOn(plot) ## Define the mmg mass model. mmgMassPdf = ParametrizedKeysPdf('mmgMassPdf', 'mmgMassPdf', mmgMass, massPeak, massWidth, data, ROOT.RooKeysPdf.NoMirror, 1.5) ## PDF shape mmgMassPdf.shape.plotOn(plot) ## Parametrized fit of the PDF shape mmgMassPdf.fitTo(data, roo.Range(60, 120), roo.PrintLevel(-1)) mmgMassPdf.plotOn(plot, roo.LineColor(ROOT.kRed), roo.LineStyle(ROOT.kDashed)) plot.Draw() sshape = 100 * (mmgMassPdf.shapemode / mZ.getVal() - 1) rshape = 100 * mmgMassPdf.shapewidth / mmgMassPdf.shapemode Latex([ 's_{shape}: %.3f %%' % sshape, 's_{fit}: %.3f #pm %.3f %%' % (massScale.getVal(), massScale.getError()), 's_{fit} - s_{shape}: %.4f #pm %.4f %%' % ( massScale.getVal() - sshape, massScale.getError() ), 'r_{shape}: %.3f %%' % rshape, 'r_{fit}: %.3f #pm %.3f %%' % ( massRes.getVal(), massRes.getError() ), 'r_{fit} - r_{shape}: %.4f #pm %.4f %%' % ( massRes.getVal() - rshape, massRes.getError()), 'r_{fit}/r_{shape}: %.4f #pm %.4f' % ( massRes.getVal() / rshape, massRes.getError() / rshape), ], position=(0.2, 0.8)).draw()
def plot_training_phoeres_with_shape_and_fit(): """Plot the nominal MC photon energy smearing overlayed with the pdf shape and fit.""" canvases.next('TrainingPhoEResWithShapeAndFit') plot = phoERes.frame(roo.Range(-7.5, 5)) plot.SetTitle("Photon energy smearing overlayed with PDF shape (blue) " "and it's parametrized fit (dashed red)") data.plotOn(plot) ## Define model for the photon energy smearing function Ereco/Etrue - 1. phoEResPdf = ParametrizedKeysPdf('phoEResPdf', 'phoEResPdf', phoERes, phoScale, phoRes, data, ROOT.RooKeysPdf.NoMirror, 1.5) ## PDF shape phoEResPdf.shape.plotOn(plot) ## Parametrized fit of the PDF shape phoEResPdf.fitTo(data, roo.Range(-50, 50), roo.PrintLevel(-1)) phoEResPdf.plotOn(plot, roo.LineColor(ROOT.kRed), roo.LineStyle(ROOT.kDashed)) plot.Draw() Latex([ 's_{shape}: %.3f %%' % phoEResPdf.shapemode, 's_{fit}: %.3f #pm %.3f %%' % (phoScale.getVal(), phoScale.getError()), 's_{fit} - s_{shape}: %.4f #pm %.4f %%' % (phoScale.getVal() - phoEResPdf.shapemode, phoScale.getError()), 'r_{shape}: %.3f %%' % phoEResPdf.shapewidth, 'r_{fit}: %.3f #pm %.3f %%' % (phoRes.getVal(), phoRes.getError()), 'r_{fit} - r_{shape}: %.4f #pm %.4f %%' % (phoRes.getVal() - phoEResPdf.shapewidth, phoRes.getError()), 'r_{fit}/r_{shape}: %.4f #pm %.4f' % (phoRes.getVal() / phoEResPdf.shapewidth, phoRes.getError() / phoEResPdf.shapewidth), ], position=(0.2, 0.8)).draw()
def plot_training_phoeres_with_shape_and_fit(): """Plot the nominal MC photon energy smearing overlayed with the pdf shape and fit.""" canvases.next('TrainingPhoEResWithShapeAndFit') plot = phoERes.frame(roo.Range(-7.5, 5)) plot.SetTitle("Photon energy smearing overlayed with PDF shape (blue) " "and it's parametrized fit (dashed red)") data.plotOn(plot) ## Define model for the photon energy smearing function Ereco/Etrue - 1. phoEResPdf = ParametrizedKeysPdf('phoEResPdf', 'phoEResPdf', phoERes, phoScale, phoRes, data, ROOT.RooKeysPdf.NoMirror, 1.5) ## PDF shape phoEResPdf.shape.plotOn(plot) ## Parametrized fit of the PDF shape phoEResPdf.fitTo(data, roo.Range(-50, 50), roo.PrintLevel(-1)) phoEResPdf.plotOn(plot, roo.LineColor(ROOT.kRed), roo.LineStyle(ROOT.kDashed)) plot.Draw() Latex([ 's_{shape}: %.3f %%' % phoEResPdf.shapemode, 's_{fit}: %.3f #pm %.3f %%' % (phoScale.getVal(), phoScale.getError()), 's_{fit} - s_{shape}: %.4f #pm %.4f %%' % ( phoScale.getVal() - phoEResPdf.shapemode, phoScale.getError() ), 'r_{shape}: %.3f %%' % phoEResPdf.shapewidth, 'r_{fit}: %.3f #pm %.3f %%' % (phoRes.getVal(), phoRes.getError()), 'r_{fit} - r_{shape}: %.4f #pm %.4f %%' % ( phoRes.getVal() - phoEResPdf.shapewidth, phoRes.getError()), 'r_{fit}/r_{shape}: %.4f #pm %.4f' % ( phoRes.getVal() / phoEResPdf.shapewidth, phoRes.getError() / phoEResPdf.shapewidth), ], position=(0.2, 0.8)).draw()
def loop_over_smearings(): "Extract scale and resolution for mass and energy." ## Set initial value for the mode calculation of the phoERes shape. phoERes.setVal(0) ## Set initial value for the mode calculation of the mmgMass shape. mmgMass.setVal(91.2) ## Define then nominal model for the photon energy smearing function. phoEResPdf = ParametrizedKeysPdf('phoEResPdf_nominal', 'phoEResPdf_nominal', phoERes, phoScale, phoRes, data, ROOT.RooKeysPdf.NoMirror, 1.5) ## Define the nominal mmg mass model. mmgMassPdf = ParametrizedKeysPdf('mmgMassPdf_nominal', 'mmgMassPdf_nominal', mmgMass, massPeak, massWidth, data, ROOT.RooKeysPdf.NoMirror, 1.5) w.Import(phoEResPdf) w.Import(mmgMassPdf) w.Import(phoEResPdf.shape) w.Import(mmgMassPdf.shape) for i, (s, r) in enumerate(zip(stargets, rtargets)): ## Get the smeared data. phoScaleTarget.setVal(s) phoResTarget.setVal(r) sdata = calibrator.get_smeared_data(s, r) ## Save the smeared data in the workspace. sdata.SetName('sdata_%d' % i) sdata.SetTitle('smeared mmg data %d' % i) w.Import(sdata) ## Build the energy and mass models for each smearing since the ## shapes may have changed. ## Guess the right values of the fit parameters. phoScale.setVal(s) phoRes.setVal(phoEResPdf.shapewidth) massScale.setVal(100 * (mmgMassPdf.shapemode / mZ.getVal() - 1)) massRes.setVal(100 * mmgMassPdf.shapewidth / mmgMassPdf.shapemode) ## Fit the models. phoFit = phoEResPdf.fitTo( sdata, roo.Range(-50, 50), # roo.Range(s - 5*r, s + 5*r), # roo.PrintLevel(-1), roo.Save()) massFit = mmgMassPdf.fitTo( sdata, roo.Range(60, 120), # roo.PrintLevel(-1), roo.Save()) ## Store results in the workspace. w.Import(phoFit) w.Import(massFit) w.saveSnapshot('smear_%d' % i, params, True)
def _get_mctruth_scale_and_resolution(self): ## Enlarge the range of the observable to get vanishing tails for ## the photon energy scale resolution # savrange = (self.phoERes.getMin(), self.phoERes.getMax()) # self.phoERes.setRange(savrange[0] - 10, savrange[1] + 10) ## Build the model for the photon energy resolution. self.phoEResPdf = ParametrizedKeysPdf( "phoEResPdf", "phoEResPdf", self.phoERes, self.s, self.r, self.data, ROOT.RooKeysPdf.NoMirror, self.rho ) # self.phoERes.setRange(*savrange) ## Set sensible initial values self.s.setVal(self.phoEResPdf.shapemode) self.r.setVal(self.phoEResPdf.shapewidth) ## Extract the MC truth scale and resolution from MC self.fitresult_mctruth = self.phoEResPdf.fitTo( self.data, roo.PrintLevel(self.printlevel), roo.SumW2Error(False), roo.Range(-50, 50), roo.Save(), roo.Strategy(2), ) self.w.Import(self.fitresult_mctruth) ## Store the MC truth scale and resolution for source, target in zip([self.s, self.r], [self.s0, self.r0]): target.setVal(source.getVal()) target.setError(source.getError()) target.setAsymError(source.getErrorLo(), source.getErrorHi()) # self.s0.setVal(self.s.getVal()) # self.r0.setVal(self.r.getVal()) self.w.saveSnapshot("sr_mctruth", self.sr) self.w.saveSnapshot("sr0_mctruth", self.sr0) self.s0.setConstant(True) self.r0.setConstant(True)
t1xt2func.SetName('t1xt2func') tfunc.SetName('t') data.addColumn(tfunc) tfunc.SetName('tfunc') t1xt2vtdata = data.reduce(ROOT.RooArgSet(t, t1xt2)) data = data.reduce(ROOT.RooArgSet(mmgMass, mmMass, phoERes, mmgMassPhoGenE)) ##------------------------------------------------------------------------------ ## Build the model fT1(t1) for log(mmgMassPhoGenE^2 - mmMass^2) t.setRange(5, 10) # t.setVal(8.3) nomirror = ROOT.RooKeysPdf.NoMirror ## t1pdf = ROOT.RooKeysPdf('t1pdf', 't1pdf', t, t1data, nomirror, 1.5) t1mode = w.factory('t1mode[8.3,5,10]') t1width = w.factory('t1width[0.2,0.01,5]') t1pdf = ParametrizedKeysPdf('t1pdf', 't1pdf', t, t1mode, t1width, t1data, nomirror, 1.5) t1pdf.fitTo(t1data) t1mode.setConstant(True) t1width.setConstant(True) ## TODO: use parametrized KEYS PDF with forced ranges and fit it to data. ## Build the model fT2(t2|s,r) for log(Ereco/Egen) ft2(t2|r,s) t.setRange(-1, 1) t.setVal(0) t2pdf = LogPhoeresKeysPdf('t2pdf', 't2pdf', phoERes, t, phoScale, phoRes, data, rho=1.5) ## Build the model for fT(t|s,r) = fT1(t1) * fT2(t2|s,r) t.setRange(5, 10) t.setBins(1000, "cache") tpdf = ROOT.RooFFTConvPdf('tpdf', 'tpdf', t, t1pdf, t2pdf)
data = dataset.get(tree=tree, weight=weight, cuts=cuts, variables=[mmgMass, mmMass, phoERes,]) ## Give the titles the original meaning phoERes.SetTitle('E_{reco}^{#gamma}/E_{gen}^{#gamma} - 1') phoERes.setUnit('%') ##------------------------------------------------------------------------------ ## Build model phoScale = w.factory('phoScale[0,-50,50]') phoRes = w.factory('phoRes[5,0.01,50]') range_save = (phoERes.getMin(), phoERes.getMax()) ## Enlarge the range of the observable to get vanishing tails. phoERes.setRange(-90, 150) phoEResPdf = ParametrizedKeysPdf('phoEResPdf', 'phoEResPdf', phoERes, phoScale, phoRes, data, ROOT.RooKeysPdf.NoMirror, 1.5) phoERes.setRange(*range_save) ##------------------------------------------------------------------------------ ## Plot the phoEResPdf for various values of the scale canvases.next('ShapeScaleScan').SetGrid() phoRes.setVal(1) plot = phoERes.frame(roo.Range(-10, 10)) latexlabels = [] for i, color in enumerate('Red Yellow Green Blue Black'.split()): scale = -4 + 2*i phoScale.setVal(scale) phoEResPdf.plotOn(plot, roo.LineColor(getattr(ROOT, 'k' + color))) label = Latex(['s: %d %%' % scale,], position=(0.8, 0.75 - (i+1) * 0.055))
def __init__(self, name, title, mass, phos, phor, data, workspace, phostarget, phortargets, rho=1.5, mirror=ROOT.RooKeysPdf.NoMirror, mrangetrain=(40,140), mrangenorm=(50,130), mrangefit=(60,120)): '''PhosphorModel4(str name, str title, RooRealVar mass, RooRealVar phos, RooRealVar phor, RooDataSet data, float phostarget, [float] phortargets, float rho=1.5, int mirror=ROOT.RooKeysPdf.NoMirror) name - PDF name title - PDF title mass - mumugamma invariant mass (GeV), observable phos - photon energy scale (%), parameter phor - photon energy resolution (%), paramter data - (mmMass, mmgMass, phoERes = 100*(phoEreco/phoEgen - 1), dataset on which the shapes of reference PDFs are trained. phostarget - target reference value of the photon energy scale at which the model is being trained phortargetss - a list of target photon energy resolution values for the moment morphing rho - passed to trained RooKeysPdfs mirror - passed to trained RooKeysPdfs ''' ## Attach args. self._name = name self._title = title self._mass = mass self._phos = phos self._phor = phor self._data = data self._phostarget = phostarget self._phortargets = phortargets[:] self._rho = rho self._mirror = mirror ## Attach other attributes. self._massrange = (mass.getMin(), mass.getMax()) self._sdata_list = [] self._phostrue_list = [] self._phortrue_list = [] self._dlogm_dphos_list = [] self._msubs_list = [] self._keys_pdfs = [] self._keys_modes = [] self._keys_effsigmas = [] self._keys_fitresults = [] self._pdfs = [] self._custs = [] self._pdfrefs = [] self._mrefs = [] self._workspace = workspace ## self._workspace = ROOT.RooWorkspace(name + '_workspace', ## title + ' workspace') w = self._workspace self.w = w ## Import important args in workspace. # w.Import(ROOT.RooArgSet(mass, phos, phor)) w.Import(mass) w.Import(phos) w.Import(phor) w.Import(self._data) w.factory( 'ConstVar::{name}_phostarget({value})'.format( name=name, value=self._phostarget ) ) ## Define the morphing parameter. This an identity with the ## photon resolution for now. mpar = self._mpar = w.factory( 'expr::{name}_mpar("{phor}", {{{phor}}})'.format( ## 'expr::{name}_mpar("sqrt(3^2 + {phor}^2)", {{{phor}}})'.format( ## 'expr::{name}_mpar("2.325+sqrt(0.4571 + (0.1608*{phor})^2)", {{{phor}}})'.format( name=name, phor=phor.GetName() ) ) ## Define the formula for dlog(m)/dphos. self._dlogm_dphos_func = w.factory(''' expr::dlogm_dphos_func("0.5 * (1 - mmMass^2 / mmgMass^2) * mmgMass", {mmMass, mmgMass}) ''') ## Get the calibrator. self._calibrator = MonteCarloCalibrator(self._data) ## Loop over target reference photon energy resolutions in phortargets. for index, phortarget in enumerate(self._phortargets): ## Store the target photon resolution value. w.factory( 'ConstVar::{name}_phortarget_{index}({value})'.format( name=name, index=index, value=phortarget ) ) ## Get the corresponding smeared RooDataSet sdata named ## {name}_sdata_{index} and attach it to self._sdata sdata = self._calibrator.get_smeared_data( self._phostarget, phortarget, name + '_sdata_%d' % index, title + ' sdata %d' % index, ## Get the true scale and resolution with errors. (This can be ## added to the calibrator and snapshots of ## self._calibrator.s and self._calibrator.r stored as ## {name}_sdata_{index}_sr in self._calibrator.w) dofit=True ) self._sdata_list.append(sdata) w.Import(sdata) phostrue = ROOT.RooRealVar(self._calibrator.s, name + '_phostrue_%d' % index) phortrue = ROOT.RooRealVar(self._calibrator.r, name + '_phortrue_%d' % index) phostrue.setConstant(True) phortrue.setConstant(True) self._phostrue_list.append(phostrue) self._phortrue_list.append(phortrue) w.Import(phostrue) w.Import(phortrue) ## Calculate the dlogm/dphos {name}_dlogm_dphos_{index} ## for the smeared dataset. sdata.addColumn(self._dlogm_dphos_func) dlogm_dphos = w.factory( ''' {name}_dlogm_dphos_{index}[{mean}, 0, 1] '''.format(name=name, index=index, mean=sdata.mean(sdata.get()['dlogm_dphos_func'])) ) dlogm_dphos.setConstant(True) self._dlogm_dphos_list.append(dlogm_dphos) ## Define the mass scaling {name}_msubs{i} introducing ## the dependence on phos. This needs self._calibrator.s and ## dlogm_dphos. ## msubs = w.factory( ## ''' ## cexpr::{msubs}( ## "{mass}*(1 - 0.01 * {dlogm_dphos} * ({phos} - {phostrue}))", ## {{ {mass}, {dlogm_dphos}, {phos}, {phostrue} }} ## ) ## '''.format(msubs = name + '_msubs_%d' % index, ## mass = self._mass.GetName(), ## dlogm_dphos = dlogm_dphos.GetName(), ## phos = self._phos.GetName(), ## phostrue = phostrue.GetName()) ## ) ## msubs = w.factory( ## ''' ## LinearVar::{msubs}( ## {mass}, ## expr::{slope}( ## "(1 - 0.01 * {dlogm_dphos} * ({phos} - {phostrue}))", ## {{ {dlogm_dphos}, {phos}, {phostrue} }} ## ), ## 0 ## ) ## '''.format(msubs = name + '_msubs_%d' % index, ## slope = name + '_msubs_slope_%d' % index, ## mass = self._mass.GetName(), ## dlogm_dphos = dlogm_dphos.GetName(), ## phos = self._phos.GetName(), ## phostrue = phostrue.GetName()) ## ) msubs = w.factory( ''' LinearVar::{msubs}( {mass}, 1, expr::{offset}( "- 0.01 * {dlogm_dphos} * ({phos} - {phostrue})", {{ {dlogm_dphos}, {phos}, {phostrue} }} ) ) '''.format(msubs = name + '_msubs_%d' % index, offset = name + '_msubs_offset_%d' % index, mass = self._mass.GetName(), dlogm_dphos = dlogm_dphos.GetName(), phos = self._phos.GetName(), phostrue = phostrue.GetName()) ) self._msubs_list.append(msubs) ## Build the corresponding parametrized KEYS PDF {name}_kyes_{index} ## with {name}_keys_mode_{index} and ## {name}_keys_effsigma_{index}. keys_mode = w.factory( '{name}_keys_mode_{index}[91.2, 60, 120]'.format( name=name, index=index ) ) keys_effsigma = w.factory( '{name}_keys_effsigma_{index}[3, 0.1, 60]'.format( name=name, index=index ) ) mass.setRange(*mrangetrain) keys_pdf = ParametrizedKeysPdf(name + '_keys_pdf_%d' % index, name + '_keys_pdf_%d' % index, mass, keys_mode, keys_effsigma, sdata) self._keys_modes.append(keys_mode) self._keys_effsigmas.append(keys_effsigma) self._keys_pdfs.append(keys_pdf) ## Fit the KEYS PDF to the training data and save the result ## {name}_keys_fitresult_{index} and parameter snapshots ## {name}_keys_mctrue_{index}. mass.setRange(*mrangenorm) keys_fitresult = keys_pdf.fitTo(sdata, roo.Range(*mrangefit), roo.Strategy(2), roo.NumCPU(8), roo.Save(True)) self._keys_fitresults.append(keys_fitresult) w.Import(keys_fitresult, name + '_keys_fitresult_%d' % index) w.saveSnapshot(name + '_mctrue_%d' % index, ','.join([phostrue.GetName(), phortrue.GetName(), keys_mode.GetName(), keys_effsigma.GetName()])) ## Sample the fitted KEYS PDF to a histogram {name}_hist_{index}. mass.setRange(*mrangetrain) hist = keys_pdf.createHistogram(name + '_hist_%d' % index, mass, roo.Binning(1000)) ## Build a RooDataHist {name}_dhist{index} of the sampled histogram. dhist = ROOT.RooDataHist(name + '_dhist_%d' % index, name + '_dhist_%d' % index, ROOT.RooArgList(mass), hist) w.Import(dhist) ## Build a RooHistPdf {name}_pdf_{index} using the dhist and msubs. ## pdf = w.factory( ## 'HistPdf::{name}({{{msubs}}}, {{{mass}}}, {dhist}, 1)'.format( ## name = name + '_pdf_%d' % index, msubs = msubs.GetName(), ## mass = mass.GetName(), dhist = dhist.GetName() ## ) ## ) pdf = w.factory( 'HistPdf::{name}({{{mass}}}, {dhist}, 1)'.format( name = name + '_pdf_%d' % index, msubs = msubs.GetName(), mass = mass.GetName(), dhist = dhist.GetName() ) ) self._pdfs.append(pdf) mass.setRange(*self._massrange) ## Supstitute for mass using customizer. cust = ROOT.RooCustomizer(pdf, 'msubs_%d' % index) self._custs.append(cust) cust.replaceArg(mass, msubs) pdfref = cust.build() pdfref.addOwnedComponents(ROOT.RooArgSet(msubs)) pdfref.SetName(name + '_pdfref_%d' % index) pdfref.SetTitle(name + '_pdfref_%d' % index) w.Import(pdfref) self._pdfrefs.append(pdfref) ## Calculate morphing parameter reference values float mref[index]. phorval = phor.getVal() phor.setVal(phortrue.getVal()) self._mrefs.append(mpar.getVal()) phor.setVal(phorval) ## End of loop over target phortargets ## Define the RooMomentMorph model. model = w.factory( ''' MomentMorph::{name}({mpar}, {{{mass}}}, {{{pdfs}}}, {{{mrefs}}}) '''.format(name=name, mpar=mpar.GetName(), mass=mass.GetName(), pdfs=','.join([f.GetName() for f in self._pdfs]), # pdfs=','.join([f.GetName() for f in self._pdfrefs]), mrefs=','.join([str(m) for m in self._mrefs])) ) ## Quick hack to make things work. cust = ROOT.RooCustomizer(model, 'msub') cust.replaceArg(mass, self._msubs_list[0]) model2 = cust.build() ROOT.RooMomentMorph.__init__(self, model2) self.SetName(name) self.SetTitle(title)
##------------------------------------------------------------------------------ init() get_data() fitdata = calibrator.get_smeared_data(sfit, rfit) traindata = calibrator.get_smeared_data(strain, rtrain) ## Get x traindata.addColumn(xfunc) xmean.setVal(traindata.mean(traindata.get()['xfunc'])) xmean.setConstant() mmgMassPdf = ParametrizedKeysPdf('mmgMassPdf', 'mmgMassPdf', mmgMass, mmgMassPeak, mmgMassWidth, traindata, ROOT.RooKeysPdf.NoMirror, 1.5, forcerange=True) calibrator.phoEResPdf.fitTo(traindata, roo.Range(-50, 50), roo.Strategy(2), roo.SumW2Error(True)) mmgMass.setRange(50, 130) mmgMassPdf.fitTo(data, roo.Range(60,120), roo.SumW2Error(True)) phoEResPdf = ParametrizedKeysPdf('phoEResPdf', 'phoEResPdf', phoERes, phoScaleTrue, phoResTrue, data, ROOT.RooKeysPdf.NoMirror, 1.5) phoEResPdf2 = ParametrizedNDKeysPdf('phoEResPdf2', 'phoEResPdf2', phoERes, phoScaleTrue, phoResTrue, data,
class MonteCarloCalibrator: def __init__(self, data, printlevel=-1, rho=1.5): self.w = ROOT.RooWorkspace('mccworkspace', 'MonteCarloCalibrator Workspace') self.datarow = ROOT.RooArgSet() for x in 'mmMass mmgMass phoERes'.split(): setattr(self, x, data.get()[x]) self.datarow.add(data.get()[x]) # self.w.Import(getattr(self, x)) self.data = data.reduce(self.datarow) self.data.SetName('data') self.rho = rho self.w.Import(data) self.printlevel = printlevel ## Define reference (0) and target scale (s) and resolution (r) self.s0 = self.w.factory('s0[0, -50, 50]') self.r0 = self.w.factory('r0[1, 0.01, 50]') self.s = self.w.factory('s[0, -50, 50]') self.r = self.w.factory('r[1, 0.01, 50]') for x in 's0 r0 s r'.split(): getattr(self, x).setUnit('%') ## Define a set of the fit and reference parameters, ## store them in the workspace. self.sr = ROOT.RooArgSet(self.s, self.r) self.sr0 = ROOT.RooArgSet(self.s0, self.r0) self.w.defineSet('fit', self.sr) self.w.defineSet('ref', self.sr0) self.w.saveSnapshot('sr_init', self.sr, True) self.w.saveSnapshot('sr0_init', self.sr0, True) self._get_mctruth_scale_and_resolution() self._define_smearing_functions() ## end of __init__ def _get_mctruth_scale_and_resolution(self): ## Enlarge the range of the observable to get vanishing tails for ## the photon energy scale resolution # savrange = (self.phoERes.getMin(), self.phoERes.getMax()) # self.phoERes.setRange(savrange[0] - 10, savrange[1] + 10) ## Build the model for the photon energy resolution. self.phoEResPdf = ParametrizedKeysPdf('phoEResPdf', 'phoEResPdf', self.phoERes, self.s, self.r, self.data, ROOT.RooKeysPdf.NoMirror, self.rho) # self.phoERes.setRange(*savrange) ## Set sensible initial values self.s.setVal(self.phoEResPdf.shapemode) self.r.setVal(self.phoEResPdf.shapewidth) ## Extract the MC truth scale and resolution from MC self.fitresult_mctruth = self.phoEResPdf.fitTo( self.data, roo.PrintLevel(self.printlevel), roo.SumW2Error(False), roo.Range(-50, 50), roo.Save(), roo.Strategy(2)) self.w.Import(self.fitresult_mctruth) ## Store the MC truth scale and resolution for source, target in zip([self.s, self.r], [self.s0, self.r0]): target.setVal(source.getVal()) target.setError(source.getError()) target.setAsymError(source.getErrorLo(), source.getErrorHi()) # self.s0.setVal(self.s.getVal()) # self.r0.setVal(self.r.getVal()) self.w.saveSnapshot('sr_mctruth', self.sr) self.w.saveSnapshot('sr0_mctruth', self.sr0) self.s0.setConstant(True) self.r0.setConstant(True) ## end of _get_mctruth_scale_and_resolution def _define_smearing_functions(self): ## Define the smearing formulas for photon energy and mmg invariant mass # self.phoEResSmear = w.factory('phoEResSmear[-100,200]') self.phoEResSmear = self.w.factory('''expr::phoEResSmear( "s + r * (phoERes - s0) / r0", {phoERes, s, r, s0, r0} )''') # self.mmgMassSmear = w.factory('mmgMassSmear[0, 200]') self.mmgMassSmear = self.w.factory('''expr::mmgMassSmear( "sqrt({m2} + (1 + 0.01 * {f}) / (1 + 0.01 * {f0}) * ({M2} - {m2}))", {{{m}, {M}, {f}, {f0}}} )'''.format(m='mmMass', m2='mmMass*mmMass', M='mmgMass', M2='mmgMass*mmgMass', f='phoEResSmear', f0='phoERes')) ## end of _define_smearing_functions def _reduce_and_rename(self, oldname, newname): ## Remove the ranges from the oldvar. oldvar = self.data.get()[oldname] oldvar.removeMin() oldvar.removeMax() ## Drop everything except the oldvar. newdata = self.data.reduce(ROOT.RooArgSet(oldvar)) ## Define the renaming identity newvar = oldvar newfunc = ROOT.RooFormulaVar(newname, newname, oldname, ROOT.RooArgList(oldvar)) ## Add a column with the identical values but with the new name newvar = newdata.addColumn(newfunc) ## New data now contains two columns of identical values, one labeled ## with the old name and the other with the new name. Keep only the one ## with the new name. return newdata.reduce(ROOT.RooArgSet(newvar)) ## end of _reduce_and_rename def _fit_smeared_data(self, name): 'Fit the current smeared data to get the smeared s and r.' self.fitresult_sdata = self.phoEResPdf.fitTo( self.sdata, roo.PrintLevel(self.printlevel), roo.SumW2Error(False), roo.Range(-50, 50), roo.Save(), roo.Strategy(2)) self.w.saveSnapshot(name + '_sr', self.sr) self.w.Import(self.fitresult_sdata, name + '_fitresult') ## end of _fit_smeared_data() def get_smeared_data(self, starget, rtarget, name='default', title='default', dofit=False): if name == 'default': name = self.data.GetName() + '_smeared' if title == 'default': title = self.data.GetTitle() + ' smeared' ## Make sure that the reference scale and resolution ## are equal to the MC truth. self.w.loadSnapshot('sr0_mctruth') ## Check if any of the parameters is to be set to the nominal values. if starget == 'nominal': starget = self.s0.getVal() if rtarget == 'nominal': rtarget = self.r0.getVal() self.s.setVal(starget) self.r.setVal(rtarget) ## Make sure that the reference scale and resolution ## are equal to the MC truth. self.w.loadSnapshot('sr0_mctruth') ## Calculate the smeared photon energy resolution and mmg mass. self.data.addColumn(self.phoEResSmear) self.data.addColumn(self.mmgMassSmear) ## Build a new dataset with the smeared data self.sdata = self.data.reduce(ROOT.RooArgSet(self.mmMass)) self.sdata.merge(self._reduce_and_rename('phoEResSmear', 'phoERes')) self.sdata.merge(self._reduce_and_rename('mmgMassSmear', 'mmgMass')) ## Drop the smearing variables from the nominal data self.data = self.data.reduce( ROOT.RooArgSet(self.mmgMass, self.mmMass, self.phoERes)) ## Set the name and title of the smeared data. self.sdata.SetName(name) self.sdata.SetTitle(title) ## Fit the smeared data if dofit: self._fit_smeared_data(name) ## Return the smeared data return self.sdata
## Give the titles the original meaning phoERes.SetTitle('E_{reco}^{#gamma}/E_{gen}^{#gamma} - 1') phoERes.setUnit('%') ##------------------------------------------------------------------------------ ## Build model phoScale = w.factory('phoScale[0,-50,50]') phoRes = w.factory('phoRes[5,0.01,50]') for x in [phoScale, phoRes]: x.setUnit('%') range_save = (phoERes.getMin(), phoERes.getMax()) ## Enlarge the range of the observable to get vanishing tails. phoERes.setRange(-90, 150) phoEResPdf = ParametrizedKeysPdf('phoEResPdf', 'phoEResPdf', phoERes, phoScale, phoRes, data, ROOT.RooKeysPdf.NoMirror, 1.5) phoERes.setRange(*range_save) ##------------------------------------------------------------------------------ ## Extract the MC truth scale and resolution from MC phoEResPdf.fitTo(data, roo.PrintLevel(-1), roo.SumW2Error(False)) phoScaleRef = phoScale.getVal() phoResRef = phoRes.getVal() ##------------------------------------------------------------------------------ ## Define the smearing formulas for photon energy and mmg invariant mass phoEResSmear = w.factory('phoEResSmear[-100,200]') phoEResSmearFunc = w.factory('''expr::phoEResSmearFunc( "{m} + {s} * ({x} - {m0}) / {s0}", {{{x}}} )'''.format(x='phoERes',
phoERes.setRange(*range_save) ## Get the smeared data sdata = calibrator.get_smeared_data(targets, targetr) ## Get the parametrized model for the photon energy resolution phoEResPdf = calibrator.phoEResPdf phoScale = calibrator.s phoRes = calibrator.r calibrator.w.loadSnapshot('sr0_mctruth') phoScaleRef = calibrator.s0.getVal() phoResRef = calibrator.r0.getVal() ## Get the parametrized model for the mmg mass mmgMassPdf = ParametrizedKeysPdf('mmgMassPdf', 'mmgMassPdf', mmgMass, mmgMassPeak, mmgMassWidth, data, ROOT.RooKeysPdf.NoMirror, 1.5) mmgMassPdf.fitTo(data, roo.Range(60,120), roo.SumW2Error(False)) ## Get the parametrized model for the smeared mmg mass mmgMass.Print() mmgMassSmearPdf = ParametrizedKeysPdf('mmgMassSmearPdf', 'mmgMassSmearPdf', mmgMass, mmgMassSmearPeak, mmgMassSmearWidth, sdata, ROOT.RooKeysPdf.NoMirror, 1.5) mmgMassSmearPdf.fitTo(sdata, roo.Range(60,120), roo.SumW2Error(False)) ##------------------------------------------------------------------------------ def plot_training_phoeres_with_shape_and_fit(): """Plot the nominal MC data overlayed with the pdf shape and fit.""" canvases.next('TrainingSampleWithShapeAndFit')
canvases.update() sw.Stop() print 'CPU time:', sw.CpuTime(), 's, real time:', sw.RealTime(), 's' ## End of main() ##------------------------------------------------------------------------------ sw = ROOT.TStopwatch() sw.Start() init() get_data() phoEResPdf = ParametrizedKeysPdf('phoEResPdf', 'phoEResPdf', phoERes, phoScale, phoRes, data, ROOT.RooKeysPdf.NoMirror, 1.5) phoEResPdf.fitTo(data, roo.Range(-50, 50)) canvases.next('phoEResPdf').SetGrid() plot = phoERes.frame(roo.Range(-10, 10)) data.plotOn(plot) phoEResPdf.plotOn(plot) phoEResPdf.paramOn(plot) plot.Draw() t = w.factory('t[0,-1,1]') t.SetTitle('log(E_{reco}^{#gamma}/E_{gen}^{#gamma})') tfunc = w.factory('expr::tfunc("log(0.01 * phoERes + 1)", {phoERes})') tfunc.SetName('t') data.addColumn(tfunc)
def __init__(self, name, title, mass, phos, phor, data, workspace, phostarget, phortargets, rho=1.5, mirror=ROOT.RooKeysPdf.NoMirror, mrangetrain=(40, 140), mrangenorm=(50, 130), mrangefit=(60, 120)): '''PhosphorModel4(str name, str title, RooRealVar mass, RooRealVar phos, RooRealVar phor, RooDataSet data, float phostarget, [float] phortargets, float rho=1.5, int mirror=ROOT.RooKeysPdf.NoMirror) name - PDF name title - PDF title mass - mumugamma invariant mass (GeV), observable phos - photon energy scale (%), parameter phor - photon energy resolution (%), paramter data - (mmMass, mmgMass, phoERes = 100*(phoEreco/phoEgen - 1), dataset on which the shapes of reference PDFs are trained. phostarget - target reference value of the photon energy scale at which the model is being trained phortargetss - a list of target photon energy resolution values for the moment morphing rho - passed to trained RooKeysPdfs mirror - passed to trained RooKeysPdfs ''' ## Attach args. self._name = name self._title = title self._mass = mass self._phos = phos self._phor = phor self._data = data self._phostarget = phostarget self._phortargets = phortargets[:] self._rho = rho self._mirror = mirror ## Attach other attributes. self._massrange = (mass.getMin(), mass.getMax()) self._sdata_list = [] self._phostrue_list = [] self._phortrue_list = [] self._dm_dphos_list = [] self._msubs_list = [] self._keys_pdfs = [] self._keys_modes = [] self._keys_effsigmas = [] self._keys_fitresults = [] self._pdfs = [] self._custs = [] # self._pdfrefs = [] self._mrefs = [] self._phormorphs = [] self._phorhists = [] self._workspace = workspace ## self._workspace = ROOT.RooWorkspace(name + '_workspace', ## title + ' workspace') w = self._workspace self.w = w ## Import important args in workspace. # w.Import(ROOT.RooArgSet(mass, phos, phor)) w.Import(mass) w.Import(phos) w.Import(phor) w.Import(self._data) w.factory('ConstVar::{name}_phostarget({value})'.format( name=name, value=self._phostarget)) ## Define the morphing parameter. This an identity with the ## photon resolution for now. mpar = self._mpar = w.factory( 'expr::{name}_mpar("{phor}", {{{phor}}})'.format( ## 'expr::{name}_mpar("2 + sqrt(0.5^2 + 0.05 * {phor}^2)", {{{phor}}})'.format( ## 'expr::{name}_mpar("2.325+sqrt(0.4571 + (0.1608*{phor})^2)", {{{phor}}})'.format( name=name, phor=phor.GetName())) ## Define the formula for dlog(m)/dphos. self._dm_dphos_func = w.factory(''' expr::dm_dphos_func("0.5 * (1 - mmMass^2 / mmgMass^2) * mmgMass", {mmMass, mmgMass}) ''') ## Get the calibrator. self._calibrator = MonteCarloCalibrator(self._data) ## Loop over target reference photon energy resolutions in phortargets. for index, phortarget in enumerate(self._phortargets): ## Store the target photon resolution value. w.factory('ConstVar::{name}_phortarget_{index}({value})'.format( name=name, index=index, value=phortarget)) ## Get the corresponding smeared RooDataSet sdata named ## {name}_sdata_{index} and attach it to self._sdata sdata = self._calibrator.get_smeared_data( self._phostarget, phortarget, name + '_sdata_%d' % index, title + ' sdata %d' % index, ## Get the true scale and resolution with errors. (This can be ## added to the calibrator and snapshots of ## self._calibrator.s and self._calibrator.r stored as ## {name}_sdata_{index}_sr in self._calibrator.w) dofit=True) self._sdata_list.append(sdata) w.Import(sdata) phostrue = ROOT.RooRealVar(self._calibrator.s, name + '_phostrue_%d' % index) phortrue = ROOT.RooRealVar(self._calibrator.r, name + '_phortrue_%d' % index) phostrue.setConstant(True) phortrue.setConstant(True) self._phostrue_list.append(phostrue) self._phortrue_list.append(phortrue) w.Import(phostrue) w.Import(phortrue) ## Calculate the dlogm/dphos {name}_dm_dphos_{index} ## for the smeared dataset. sdata.addColumn(self._dm_dphos_func) dm_dphos = w.factory(''' {name}_dm_dphos_{index}[{mean}, 0, 1] '''.format(name=name, index=index, mean=sdata.mean(sdata.get()['dm_dphos_func']))) dm_dphos.setConstant(True) self._dm_dphos_list.append(dm_dphos) ## Define the mass scaling {name}_msubs{i} introducing ## the dependence on phos. This needs self._calibrator.s and ## dm_dphos. ## msubs = w.factory( ## ''' ## LinearVar::{msubs}( ## {mass}, 1, ## expr::{offset}( ## "- 0.01 * {dm_dphos} * ({phos} - {phostrue})", ## {{ {dm_dphos}, {phos}, {phostrue} }} ## ) ## ) ## '''.format(msubs = name + '_msubs_%d' % index, ## offset = name + '_msubs_offset_%d' % index, ## mass = self._mass.GetName(), ## dm_dphos = dm_dphos.GetName(), ## phos = self._phos.GetName(), ## phostrue = phostrue.GetName()) ## ) ## LinearVar cannot be persisted. msubs = w.factory(''' expr::{msubs}( "{mass} - 0.01 * {dm_dphos} * ({phos} - {phostrue})", {{ {mass}, {dm_dphos}, {phos}, {phostrue} }} ) '''.format(msubs=name + '_msubs_%d' % index, mass=self._mass.GetName(), dm_dphos=dm_dphos.GetName(), phos=self._phos.GetName(), phostrue=phostrue.GetName())) self._msubs_list.append(msubs) ## Build the corresponding parametrized KEYS PDF {name}_kyes_{index} ## with {name}_keys_mode_{index} and ## {name}_keys_effsigma_{index}. keys_mode = w.factory( '{name}_keys_mode_{index}[91.2, 60, 120]'.format(name=name, index=index)) keys_effsigma = w.factory( '{name}_keys_effsigma_{index}[3, 0.1, 60]'.format(name=name, index=index)) mass.setRange(*mrangetrain) keys_pdf = ParametrizedKeysPdf(name + '_keys_pdf_%d' % index, name + '_keys_pdf_%d' % index, mass, keys_mode, keys_effsigma, sdata, rho=self._rho) self._keys_modes.append(keys_mode) self._keys_effsigmas.append(keys_effsigma) self._keys_pdfs.append(keys_pdf) ## Fit the KEYS PDF to the training data and save the result ## {name}_keys_fitresult_{index} and parameter snapshots ## {name}_keys_mctrue_{index}. mass.setRange(*mrangenorm) keys_fitresult = keys_pdf.fitTo(sdata, roo.Range(*mrangefit), roo.Strategy(2), roo.NumCPU(8), roo.Save(True)) self._keys_fitresults.append(keys_fitresult) w.Import(keys_fitresult, name + '_keys_fitresult_%d' % index) w.saveSnapshot( name + '_mctrue_%d' % index, ','.join([ phostrue.GetName(), phortrue.GetName(), keys_mode.GetName(), keys_effsigma.GetName() ])) ## Sample the fitted KEYS PDF to a histogram {name}_hist_{index}. mass.setRange(*mrangetrain) hist = keys_pdf.createHistogram(name + '_hist_%d' % index, mass, roo.Binning('cache')) ## Build a RooDataHist {name}_dhist{index} of the sampled histogram. dhist = ROOT.RooDataHist(name + '_dhist_%d' % index, name + '_dhist_%d' % index, ROOT.RooArgList(mass), hist) w.Import(dhist) ## Build a RooHistPdf {name}_pdf_{index} using the dhist and msubs. ## pdf = w.factory( pdf = w.factory('HistPdf::{name}({{{mass}}}, {dhist}, 1)'.format( name=name + '_pdf_%d' % index, msubs=msubs.GetName(), mass=mass.GetName(), dhist=dhist.GetName())) self._pdfs.append(pdf) mass.setRange(*self._massrange) ## Calculate morphing parameter reference values float mref[index]. phorval = phor.getVal() phor.setVal(phortrue.getVal()) self._mrefs.append(mpar.getVal()) phor.setVal(phorval) ## End of loop over target phortargets ## Make the reference and cache binnings in phor self._check_phor_ranges() partitions = self._partition_binning(self._phor, 'cache', 'reference') ## Loop over phor reference bins and define pairwise RooMomentMorphs. for ilo in range(len(self._mrefs) - 1): ihi = ilo + 1 mlo = self._mrefs[ilo] mhi = self._mrefs[ihi] pdflo = self._pdfs[ilo] pdfhi = self._pdfs[ihi] phormorph = w.factory(''' MomentMorph::{name}_phormorph_{ilo}to{ihi}( {mpar}, {{{mass}}}, {{{pdfs}}}, {{{mrefs}}} ) '''.format(name=name, ilo=ilo, ihi=ihi, mpar=mpar.GetName(), mass=mass.GetName(), pdfs='%s, %s' % (pdflo.GetName(), pdfhi.GetName()), mrefs='%f, %f' % (mlo, mhi))) self._phormorphs.append(phormorph) ## Sample the morph in a 2D histogram in mass and phor. phorhist = phormorph.createHistogram( name + '_phorhist_%dto%d' % (ilo, ihi), mass, roo.Binning('cache'), roo.YVar(phor, roo.Binning(partitions[ilo]))) self._phorhists.append(phorhist) ## End of loop over phor reference bins. self._stitch_phorhists() self._phor_dhist = phor_dhist = ROOT.RooDataHist( name + '_phor_dhist', name + '_phor_dhist', ROOT.RooArgList(mass, phor), self._phorhist) average_index = (len(self._msubs_list) + 1) / 2 msubs = self._msubs_list[average_index] ## self._model = model = ROOT.RooHistPdf( ## name + '_phor_histpdf', name + '_phor_histpdf', ## ROOT.RooArgList(msubs, phor), ROOT.RooArgList(mass, phor), phor_dhist, 2 ## ) ## self._model = model = ROOT.RooPhosphorPdf( ## name + '_phor_histpdf', name + '_phor_histpdf', mass, msubs, phor, phor_dhist, 2 ## ) ## ## Quick hack to make things work. ## self._customizer = customizer = ROOT.RooCustomizer(model, 'msub') ## customizer.replaceArg(mass, msubs) ## model = customizer.build() # ROOT.RooHistPdf.__init__(self, model) ROOT.RooPhosphorPdf.__init__(self, name, title, mass, msubs, phos, phor, phor_dhist, 2) self.SetName(name) self.SetTitle(title)
canvases.update() sw.Stop() print 'CPU time:', sw.CpuTime(), 's, real time:', sw.RealTime(), 's' ## End of main() ##------------------------------------------------------------------------------ sw = ROOT.TStopwatch() sw.Start() init() get_data() phoEResPdf = ParametrizedKeysPdf( 'phoEResPdf', 'phoEResPdf', phoERes, phoScale, phoRes, data, ROOT.RooKeysPdf.NoMirror, 1.5 ) phoEResPdf.fitTo(data, roo.Range(-50, 50)) canvases.next('phoEResPdf').SetGrid() plot = phoERes.frame(roo.Range(-10, 10)) data.plotOn(plot) phoEResPdf.plotOn(plot) phoEResPdf.paramOn(plot) plot.Draw() t = w.factory('t[0,-1,1]') t.SetTitle('log(E_{reco}^{#gamma}/E_{gen}^{#gamma})') tfunc = w.factory('expr::tfunc("log(0.01 * phoERes + 1)", {phoERes})') tfunc.SetName('t')
class MonteCarloCalibrator: def __init__(self, data, printlevel=-1, rho=1.5): self.w = ROOT.RooWorkspace("mccworkspace", "MonteCarloCalibrator Workspace") self.datarow = ROOT.RooArgSet() for x in "mmMass mmgMass phoERes".split(): setattr(self, x, data.get()[x]) self.datarow.add(data.get()[x]) # self.w.Import(getattr(self, x)) self.data = data.reduce(self.datarow) self.data.SetName("data") self.rho = rho self.w.Import(data) self.printlevel = printlevel ## Define reference (0) and target scale (s) and resolution (r) self.s0 = self.w.factory("s0[0, -50, 50]") self.r0 = self.w.factory("r0[1, 0.01, 50]") self.s = self.w.factory("s[0, -50, 50]") self.r = self.w.factory("r[1, 0.01, 50]") for x in "s0 r0 s r".split(): getattr(self, x).setUnit("%") ## Define a set of the fit and reference parameters, ## store them in the workspace. self.sr = ROOT.RooArgSet(self.s, self.r) self.sr0 = ROOT.RooArgSet(self.s0, self.r0) self.w.defineSet("fit", self.sr) self.w.defineSet("ref", self.sr0) self.w.saveSnapshot("sr_init", self.sr, True) self.w.saveSnapshot("sr0_init", self.sr0, True) self._get_mctruth_scale_and_resolution() self._define_smearing_functions() ## end of __init__ def _get_mctruth_scale_and_resolution(self): ## Enlarge the range of the observable to get vanishing tails for ## the photon energy scale resolution # savrange = (self.phoERes.getMin(), self.phoERes.getMax()) # self.phoERes.setRange(savrange[0] - 10, savrange[1] + 10) ## Build the model for the photon energy resolution. self.phoEResPdf = ParametrizedKeysPdf( "phoEResPdf", "phoEResPdf", self.phoERes, self.s, self.r, self.data, ROOT.RooKeysPdf.NoMirror, self.rho ) # self.phoERes.setRange(*savrange) ## Set sensible initial values self.s.setVal(self.phoEResPdf.shapemode) self.r.setVal(self.phoEResPdf.shapewidth) ## Extract the MC truth scale and resolution from MC self.fitresult_mctruth = self.phoEResPdf.fitTo( self.data, roo.PrintLevel(self.printlevel), roo.SumW2Error(False), roo.Range(-50, 50), roo.Save(), roo.Strategy(2), ) self.w.Import(self.fitresult_mctruth) ## Store the MC truth scale and resolution for source, target in zip([self.s, self.r], [self.s0, self.r0]): target.setVal(source.getVal()) target.setError(source.getError()) target.setAsymError(source.getErrorLo(), source.getErrorHi()) # self.s0.setVal(self.s.getVal()) # self.r0.setVal(self.r.getVal()) self.w.saveSnapshot("sr_mctruth", self.sr) self.w.saveSnapshot("sr0_mctruth", self.sr0) self.s0.setConstant(True) self.r0.setConstant(True) ## end of _get_mctruth_scale_and_resolution def _define_smearing_functions(self): ## Define the smearing formulas for photon energy and mmg invariant mass # self.phoEResSmear = w.factory('phoEResSmear[-100,200]') self.phoEResSmear = self.w.factory( """expr::phoEResSmear( "s + r * (phoERes - s0) / r0", {phoERes, s, r, s0, r0} )""" ) # self.mmgMassSmear = w.factory('mmgMassSmear[0, 200]') self.mmgMassSmear = self.w.factory( """expr::mmgMassSmear( "sqrt({m2} + (1 + 0.01 * {f}) / (1 + 0.01 * {f0}) * ({M2} - {m2}))", {{{m}, {M}, {f}, {f0}}} )""".format( m="mmMass", m2="mmMass*mmMass", M="mmgMass", M2="mmgMass*mmgMass", f="phoEResSmear", f0="phoERes" ) ) ## end of _define_smearing_functions def _reduce_and_rename(self, oldname, newname): ## Remove the ranges from the oldvar. oldvar = self.data.get()[oldname] oldvar.removeMin() oldvar.removeMax() ## Drop everything except the oldvar. newdata = self.data.reduce(ROOT.RooArgSet(oldvar)) ## Define the renaming identity newvar = oldvar newfunc = ROOT.RooFormulaVar(newname, newname, oldname, ROOT.RooArgList(oldvar)) ## Add a column with the identical values but with the new name newvar = newdata.addColumn(newfunc) ## New data now contains two columns of identical values, one labeled ## with the old name and the other with the new name. Keep only the one ## with the new name. return newdata.reduce(ROOT.RooArgSet(newvar)) ## end of _reduce_and_rename def _fit_smeared_data(self, name): "Fit the current smeared data to get the smeared s and r." self.fitresult_sdata = self.phoEResPdf.fitTo( self.sdata, roo.PrintLevel(self.printlevel), roo.SumW2Error(False), roo.Range(-50, 50), roo.Save(), roo.Strategy(2), ) self.w.saveSnapshot(name + "_sr", self.sr) self.w.Import(self.fitresult_sdata, name + "_fitresult") ## end of _fit_smeared_data() def get_smeared_data(self, starget, rtarget, name="default", title="default", dofit=False): if name == "default": name = self.data.GetName() + "_smeared" if title == "default": title = self.data.GetTitle() + " smeared" ## Make sure that the reference scale and resolution ## are equal to the MC truth. self.w.loadSnapshot("sr0_mctruth") ## Check if any of the parameters is to be set to the nominal values. if starget == "nominal": starget = self.s0.getVal() if rtarget == "nominal": rtarget = self.r0.getVal() self.s.setVal(starget) self.r.setVal(rtarget) ## Make sure that the reference scale and resolution ## are equal to the MC truth. self.w.loadSnapshot("sr0_mctruth") ## Calculate the smeared photon energy resolution and mmg mass. self.data.addColumn(self.phoEResSmear) self.data.addColumn(self.mmgMassSmear) ## Build a new dataset with the smeared data self.sdata = self.data.reduce(ROOT.RooArgSet(self.mmMass)) self.sdata.merge(self._reduce_and_rename("phoEResSmear", "phoERes")) self.sdata.merge(self._reduce_and_rename("mmgMassSmear", "mmgMass")) ## Drop the smearing variables from the nominal data self.data = self.data.reduce(ROOT.RooArgSet(self.mmgMass, self.mmMass, self.phoERes)) ## Set the name and title of the smeared data. self.sdata.SetName(name) self.sdata.SetTitle(title) ## Fit the smeared data if dofit: self._fit_smeared_data(name) ## Return the smeared data return self.sdata
sdata.SetName(dataname) w.Import(sdata) pdfname = '_'.join(['mmgMassPdf', name, bintag]) pdfname = pdfname.replace('-', 'to') ## KEYS PDF Dilemma: RooKeysPdf is deprecated, ## RooNDKeysPdf cannot be stored in a workspace, ## RooMomentMorph constructor works in workspace factory only ## pdf = ROOT.RooNDKeysPdf(pdfname, pdfname, ## ROOT.RooArgList(mmgMass), sdata, "a", 1.5) ## -> Stick to deprecated RooKeysPdf for now. ## mmgMass.setRange(40, 140) peak = w.factory('%s_mode[91.2, 60, 120]' % pdfname) width = w.factory('%s_effsigma[3, 0.1, 20]' % pdfname) pdf = ParametrizedKeysPdf(pdfname + '_pkeys', pdfname + '_pkyes', mmgMass, peak, width, sdata, ROOT.RooKeysPdf.NoMirror, 1.5) savrange = (mmgMass.getMin(), mmgMass.getMax()) normrange = (40, 130) fitrange = (60, 120) mmgMass.setRange(*fitrange) peak.setVal(pdf.shapemode) width.setVal(pdf.shapewidth) pdf.fitTo(sdata, roo.Range(*fitrange), roo.Strategy(2)) w.Import(pdf) ## peak.setConstant() ## width.setConstant() hist = pdf.createHistogram(pdfname + '_hist', mmgMass, roo.Binning(1000)) dhist = ROOT.RooDataHist(pdfname + '_dhist', pdfname + '_hist', ROOT.RooArgList(mmgMass), hist)
data.addColumn(t1xt2func) t1xt2func.SetName('t1xt2func') tfunc.SetName('t') data.addColumn(tfunc) tfunc.SetName('tfunc') t1xt2vtdata = data.reduce(ROOT.RooArgSet(t, t1xt2)) data = data.reduce(ROOT.RooArgSet(mmgMass, mmMass, phoERes, mmgMassPhoGenE)) ## Build the model fT1(t1) for log(mmgMassPhoGenE^2 - mmMass^2) t.setRange(5, 10) # t.setVal(8.3) nomirror = ROOT.RooKeysPdf.NoMirror ## t1pdf = ROOT.RooKeysPdf('t1pdf', 't1pdf', t, t1data, nomirror, 1.5) t1mode = w.factory('t1mode[8.3,5,10]') t1width = w.factory('t1width[0.2,0.01,5]') t1pdf = ParametrizedKeysPdf('t1pdf', 't1pdf', t, t1mode, t1width, t1data, nomirror, 1.5) t1pdf.fitTo(t1data) t1mode.setConstant(True) t1width.setConstant(True) ## TODO: use parametrized KEYS PDF with forced ranges and fit it to data. ## Build the model fT2(t2|s,r) for log(Ereco/Egen) ft2(t2|r,s) t.setRange(-1, 1) t.setVal(0) t2pdf = LogPhoeresKeysPdf('t2pdf', 't2pdf', phoERes, t, phoScale, phoRes, data,
init() get_data() sdata4 = calibrator.get_smeared_data('nominal', 4) sdata6 = calibrator.get_smeared_data('nominal', 5) sdata8 = calibrator.get_smeared_data('nominal', 6) mirror = ROOT.RooKeysPdf.NoMirror mode4 = w.factory('mode4[91,60,120]') mode8 = w.factory('mode8[91,60,120]') seff4 = w.factory('seff4[4,0.1,50]') seff8 = w.factory('seff8[4,0.1,50]') pdf4 = ParametrizedKeysPdf('pdf4', 'pdf4', mmgMass, mode4, seff4, sdata4, mirror, 1.5) pdf8 = ParametrizedKeysPdf('pdf8', 'pdf8', mmgMass, mode8, seff8, sdata8, mirror, 1.5) mmgMass.setRange(50, 130) pdf4.fitTo(sdata4, roo.Range(60, 120)) h4 = pdf4.createHistogram('h4', mmgMass, roo.Binning(1000)) pdf8.fitTo(sdata8, roo.Range(60, 120)) h8 = pdf8.createHistogram('h8', mmgMass, roo.Binning(1000)) d4 = ROOT.RooDataHist('d4', 'd4', ROOT.RooArgList(mmgMass), h4) d8 = ROOT.RooDataHist('d8', 'd8', ROOT.RooArgList(mmgMass), h8) f4 = ROOT.RooHistPdf('f4', 'f4', ROOT.RooArgSet(mmgMass), d4, 1)