FitResult::Chi2() and FitResult::Ndf() not valid after fit?

Dear ROOT experts,

I’m trying to fit two 1D slices of a 2D distribution simultaneously using ROOT::Fit::Fitter with a custom chi^2 minimization function. The fit appears to work fine, and the FitResult is valid, but for some reason afterwards neither the FitResult nor the fit function know anything about things like the chi^2 or the Ndof. That is: they return either 0 or -1, depending on where one tries to get them.

I have attached a small (albeit not minimal) technically working example and would appreciate any hints on how to make the FitResult return these kind of fit results. I must be doing something wrong, obviously. (And the current approach may be a bit clumsy overall, I admit. I’m open for more elegant solutions there too.)

The whole thing is in PyROOT (but I wonder if that matters here(?)), in ROOT 5.99/01 (built from SVN).

The core bit of the fitting looks like:

    fitter = Fit.Fitter()
    fit_fun = TF2("fit_fun",
                  "[0] * (" \
                      "[1]/(2*pi) * exp(-.5*((x-[2])/[4])**2) * exp(-.5*((y-[3])/[5])**2)" \
                      " + " \
                      "(1. - [1])/(2*pi) * exp(-.5*((x-[2])/[6])**2) * exp(-.5*((y-[3])/[7])**2)" \
                      ")",
                  x_min, x_max, y_min, y_max)
    pars = []
    for ipar in xrange(fit_fun.GetNpar()):
        pars.append(fit_fun.GetParameter(ipar))
    pars = array.array("d", pars)
    fitter.Config().SetParamsSettings(fit_fun.GetNpar(), pars);
    fitter.FitFCN(CreateCombinedFitFun(graph_x, graph_y, fit_fun))
    fit_res = fitter.Result()

where CreateCombinedFitFun() is the Python minimization function.

Best regards,
Jeroen
test_fit.py (4.27 KB)

Ok, reading the documentation over a fresh pot of coffee actually helped. The snippet below does the trick. One simply needs to call Fitter.FitFCN() in the right way, providing the number of parameters and the number of data points. The parameter names can be set using Fitter.Config().ParSettings(ipar).SetName().

    fitter = Fit.Fitter()
    fit_fun = TF2("fit_fun",
                  "[0] * (" \
                      "[1]/(2*pi) * exp(-.5*((x-[2])/[4])**2) * exp(-.5*((y-[3])/[5])**2)" \
                      " + " \
                      "(1. - [1])/(2*pi) * exp(-.5*((x-[2])/[6])**2) * exp(-.5*((y-[3])/[7])**2)" \
                      ")",
                  x_min, x_max, y_min, y_max)
    pars = []
    for ipar in xrange(fit_fun.GetNpar()):
        pars.append(fit_fun.GetParameter(ipar))
    pars = array.array("d", pars)
    fitter.Config().SetParamsSettings(fit_fun.GetNpar(), pars);
    for ipar in xrange(fit_fun.GetNpar()):
        fitter.Config().ParSettings(ipar).SetName(fit_fun.GetParName(ipar))
    fitter.FitFCN(CreateCombinedFitFun(graph_x, graph_y, fit_fun),
                  len(pars), pars, graph_x.GetN() + graph_y.GetN(), True)
    fit_res = fitter.Result()
    fit_res.Print(ROOT.cout)

Jeroen