Creating own p.d.f. in RooFit

Dear experts,

I’m trying to create my own roofit p.d.f. with PyROOT to fit some distribution. It should be a two-body phase space multiplied by a polynomial function, which does not look to be easily written in one line, so I’ve chosen to do the following:

  • write a python function
  • make a TF1 out of it
  • turn it in a RooFit p.d.f.

I’m attaching a code example and a small dataset in the end of this post, but briefly it looks as the following:

import ROOT, math

...

def my_function(x, par):
    xx = x[0]
    a1 = par[0]
    a2 = par[1]
    ...
    f = f(x, a1, a2,...)
    return f

## name, function, left limit, right limit, number of parameters
f_ps = ROOT.TF1("f_ps", my_function, mass_left, mass_right, 3)

m_x = ROOT.RooRealVar("m_x", "m_x", mass_left, mass_right)

roo_ps_func = ROOT.RooFit.bindFunction(f_ps_pol2, m_x, pars)
roo_ps_pdf1 = ROOT.RooFit.bindPdf("roo_ps_pdf1",f_ps_pol2, m_x, a1, a2, a3)
roo_ps_pdf2 = ROOT.RooFit.bindPdf(f_ps_pol2, m_x)

where a1, a2 and a3 are RooRealVars for model parameters. I tried several methods but unfortunately none of them seems to allow fitting:

  • bound as a function
    roo_ps_func.fitTo(ds)
    leads to an error
    AttributeError: 'RooTFnBinding' object has no attribute 'fitTo'

  • bound as a pdf
    roo_ps_pdf1.fitTo(ds)
    leads to an error

File /snap/root-framework/954/usr/local/lib/ROOT/_pythonization/_roofit/_rooabspdf.py:64, in RooAbsPdf.fitTo(self, *args, **kwargs)
    60 The RooAbsPdf::fitTo() function is pythonized with the command argument pythonization.
    61 The keywords must correspond to the CmdArgs of the function.
    62 
    63 # Redefinition of `RooAbsPdf.fitTo` for keyword arguments.
--> 64 return self._fitTo["RooLinkedList const&"](args[0], _pack_cmd_args(*args[1:], **kwargs))

TypeError: Could not find "fitTo<RooLinkedList const&>" (set cppyy.set_debug() for C++ errors):
  Template method resolution failed:
  math domain error
  math domain error
  Failed to instantiate "operator()(double,double,double,double)"
  • other way of binding pdf
    roo_ps_pdf2.fitTo(ds)
    does not seem to see the parameters:
[#1] INFO:Fitting -- RooAbsPdf::fitTo(f_ps_pol2_over_f_ps_pol2_Int[m_x]) fixing normalization set for coefficient determination to observables in data
[#1] INFO:Fitting -- Creation of NLL object took 9.0357 ms
[#1] INFO:Fitting -- RooAddition::defaultErrorLevel(nll_f_ps_pol2_over_f_ps_pol2_Int[m_x]_background) Summation contains a RooNLLVar, using its error level
[#1] INFO:Minimization -- RooAbsMinimizerFcn::setOptimizeConst: activating const optimization
[#0] ERROR:Minimization -- RooMinimizer::fitFCN(): FCN function has zero parameters
[#0] ERROR:Minimization -- RooMinimizer: all function calls during minimization gave invalid NLL values!
[#0] WARNING:Minimization -- RooMinimizer::hesse: Error, run Migrad before Hesse!
[#1] INFO:Minimization -- RooAbsMinimizerFcn::setOptimizeConst: deactivating const optimization

So, it seems I’m missing something here. Please, could you give a hint of how to write a model to fit a distribution? Any other workaround would also be very appriciated.

If it is a necessary information, I’m using ROOT 6.36.04.

pdf_from_tf1.py (3.1 KB)
ds_bkg.root (59.8 KB)

Cheers,
Dasha.