Hello eveybody,
I need to fit a TH1D using two different functions. Each one is defined on its own subrange. I found a similar topic, but I didn’t get help from that. Sorry if I am posting an already posted issue.

After the definition of the “total” function (parabola_1+parabola_2) the relative subranges are ignored in the “total” fit and I see only one parabola that spans over all the xaxis on my histo. The two separate fits on their subranges are working nicely. Here is the code of the fit. TH1D (named “sliceY0”) is defined between 0 and 20:

Double_t param[6];
TF1 f1 = new TF1(“f1”,"[0](x-[1])(x-[1]) + [2]",0,10);
TF1 f2 = new TF1(“f2”,"[0](x-[1])(x-[1]) + [2]",10,20);

TF1 total = new TF1(“total”,"[0](x-[1])(x-[1]) + [2] + [3](x-[4])*(x-[4]) + [5]",0,20);

I would like to refresh this topic, because I came to the same problem like Tino.

I define two fit functions: for signal and for background, then I create another function being a sum of two previous. And fit of this fails. But when I define one functions containing signal and background inside then fit works very good. Minimum working example attached.

Another issue I found. When I have this two separated functions for sig and bg, and I set parameters for them, then this parameters are also set for new function which contain this functions (parameters are copied, right?). But the same doesn’t work for parameter limits, I have to set them additionally to the new function. Why?

Your fit fails because you ave specified that your background pol1 first parameter has index 0 as does your signal fit’s first gaussian.
Simply change your bg tf1 to start with parameter index 6.

TF1 * fbg = new TF1("fbg", "pol1(6)", 1100, 1150);

After changing to pol1(6) it works, as you suggested, but then I am thinking whether we should consider this as a bug.

First of all, I would expect that after summing two or more functions the parameters are renumbered. This allows me to combine many functions despite fact how many parameters they have.

Secondly, if you look for the output of Print() function of both functions you will see

fit_sum : fsig+fbg Ndim= 1, Npar= 8, Noper= 5
fExpr[0] = gaus(0) action = 110 action param = 0
fExpr[1] = gaus(3) action = 110 action param = 3
fExpr[2] = + action = 1 action param = 0
fExpr[3] = pol1(0) action = 130 action param = 101
fExpr[4] = + action = 1 action param = 0
Optimized expression
fExpr[0] = gaus(0) action = 110 action param = 0
fExpr[1] = gaus(3) action = 110 action param = 3
fExpr[2] = + action = 1 action param = 0
fExpr[3] = pol1(0) action = 130 action param = 101
fExpr[4] = + action = 1 action param = 0
Par 0 p0 = -511724
Par 1 p1 = 457.055
Par 2 p2 = -1.96031
Par 3 p3 = 37854.2
Par 4 p4 = 1112.65
Par 5 p5 = -8.50551
Par 6 p6 = 100
Par 7 p7 = -0.1
fit_full : gaus(0)+gaus(3)+pol1(6) Ndim= 1, Npar= 8, Noper= 5
fExpr[0] = gaus(0) action = 110 action param = 0
fExpr[1] = gaus(3) action = 110 action param = 3
fExpr[2] = + action = 1 action param = 0
fExpr[3] = pol1(6) action = 130 action param = 107
fExpr[4] = + action = 1 action param = 0
Optimized expression
fExpr[0] = gaus(0) action = 110 action param = 0
fExpr[1] = gaus(3) action = 110 action param = 3
fExpr[2] = + action = 1 action param = 0
fExpr[3] = pol1(6) action = 130 action param = 107
fExpr[4] = + action = 1 action param = 0
Par 0 p0 = 39159.4
Par 1 p1 = 1115.23
Par 2 p2 = 1.70004
Par 3 p3 = 19312.2
Par 4 p4 = 1114.46
Par 5 p5 = 4.0956
Par 6 p6 = 128158
Par 7 p7 = -103.258
You can find that for fit_sum we have eight parameters, so parameter counting is proper but parameter numbering in raw or optimized expression is not. If you look then for the same output when background function is defined as pol1(6) we get

fit_sum : fsig+fbg Ndim= 1, Npar= 14, Noper= 5
fExpr[0] = gaus(0) action = 110 action param = 0
fExpr[1] = gaus(3) action = 110 action param = 3
fExpr[2] = + action = 1 action param = 0
fExpr[3] = pol1(6) action = 130 action param = 107
fExpr[4] = + action = 1 action param = 0
Optimized expression
fExpr[0] = gaus(0) action = 110 action param = 0
fExpr[1] = gaus(3) action = 110 action param = 3
fExpr[2] = + action = 1 action param = 0
fExpr[3] = pol1(6) action = 130 action param = 107
fExpr[4] = + action = 1 action param = 0
Par 0 p0 = 39159.4
Par 1 p1 = 1115.23
Par 2 p2 = 1.70004
Par 3 p3 = 19312.2
Par 4 p4 = 1114.46
Par 5 p5 = 4.0956
Par 6 p6 = 128158
Par 7 p7 = -103.258
Par 8 p8 = 0
Par 9 p9 = 0
Par 10 p10 = 0
Par 11 p11 = 0
Par 12 p12 = 0
Par 13 p13 = 0
Here you have proper numerating of the parameters, but improper number of parameters: 14 instead of 8. And also Fit is manipulating parameters p6 and p7 which in respekt to bg functions should be parameters 0 and 1 of bg function, but pol1 starts from 6th, so p12 and p13 should be in fact adjusted.

TF1::Analyze uses the values within the parenthesis as the parameter start number for that predefined function, the parameters are not renumbered. The signal function, fsig, has fNpar = 6, since the background method starts on parameter 6 it’s fNpar = 8. The TF1::Analyse method then simply adds these fNpar values to get fit_sum.fNpar = 14.

Parameters 6 and 7 should be manipulated for the background function as that was indicated in the definition of fbg. Parameters 8-13 should not be in the parameter list as they do not impact the formula in anyway, but are there due to the summing of fNpar as described above.

This explains the resulting behavior, but I agree that one would expect the parameters to be renumbered when summing the values, this would need to have clear documentation. The downside to this renumbering would be that one can not simply take the fit_sum parameters and plug them into fbg to get only the background contribution, as in the following:

Instead, one would have to determine which parameters of fit_sum were background and map them onto the fbg parameters to get the proper values.

The fact that fbg.fNpar = 8 even though only two parameters have an impact is also misleading.

Thank you for this comprehensive explanation. I think that we both have feeling that something s there is not consistent. But now I know how to handle this.

[quote=“ksmith”]The downside to this renumbering would be that one can not simply take the fit_sum parameters and plug them into fbg to get only the background contribution, as in the following:

Instead, one would have to determine which parameters of fit_sum were background and map them onto the fbg parameters to get the proper values.[/quote]

This will not work see the documentation for SetParameters(): [url]http://root.cern.ch/root/htmldoc/TFormula.html#TFormula:SetParameters[/url]
I was just stating that [b]if[/b] ROOT reindexed the parameters for fbg when summing them together to make fit_sum that the background parameters would be then p6 and p7 and if you used the the same array of parameters from fit_sum for fbg you would not get the background contribution since fbg uses parameters p0 and p1. This is [b]only[/b] a concern if TFormula is changed in ROOT, this is not the way it is now!

I was just stating that if ROOT reindexed the parameters for fbg when summing them together to make fit_sum that the background parameters would be then p6 and p7 and if you used the the same array of parameters from fit_sum for fbg you would not get the background contribution since fbg uses parameters p0 and p1. This is only a concern if TFormula is changed in ROOT, this is not the way it is now!

[quote=“ksmith”]fbg->SetParameters(fit_sum->GetParameters() + fsig->GetNpar());
This will not work see the documentation for SetParameters(): http://root.cern.ch/root/htmldoc/TFormula.html#TFormula:SetParameters[/quote]
Why? “fit_sum->GetParameters()” returns “double *” which is a pointer and then " + fsig->GetNpar()" shifts the pointer value to address of par6 (of fit_sum) which is the first value of fbg parameters and will fit to pol1(0).

Sorry for the confusion. I meant that this will not work in your current application. If ROOT reindexed the parameters then your method of getting the parameters should be fine.