TF1
In ROOT you can fit histograms (or TEfficiency objects) using formulas that you provide, and that have free parameters. For an efficiency curve a TMath::Erf() is usually a good fit function. But you can't really use it as is. Let's see what it looks like:
// This lambda defines the values of the function. For now we don't use p:
auto myErf0Lambda = [](double* x, double* p) { return TMath::Erf(x[0]); };
// When drawing, show between -10..10
TF1* myErf0 = new TF1("myErf0", myErf0Lambda, -10, 10);
myErf0->Draw();
This is the same one but moved around to match the concept of efficiencies:
auto myErf1Lambda = [](double* x, double* p)
{ return (TMath::Erf((x[0]-5)/3) + 1)/2 * 0.8; };
TF1* myErf1 = new TF1("myErf1", myErf1Lambda, -10, 10);
myErf1->Draw();
We can re-write this using parameter notation:
auto myErf2Lambda = [](double* x, double* p) {
return (TMath::Erf((x[0]-p[0])/p[1]) + 1)/2 * p[2]; };
TF1* myErf2 = new TF1("myErf2", myErf2Lambda,
-10, 10, 3 /*number of parameters*/);
myErf2->SetParameter(0, 5.);
myErf2->SetParameter(1, 3.);
myErf2->SetParameter(2, 0.8);
myErf2->Draw();
Try out what happens when you change the values for 5, 3 and 0.8 (simply by calling myErf2->SetParameter(...);)!
Fitting a TF1
Now that we have a nice TF1 we can fit it to the TEfficiency object by calling eff->Fit(). Here is the complete code you need to add to the end of effX():
...
void effX() {
...
auto effErf = [](double* x, double* p) {
return (TMath::Erf((x[0] - p[0]) / p[1]) + 1) / 2. * p[2];
};
TF1* myErf = new TF1("myErf", effErf, 0., 10., 3);
myErf->SetParameter(0, 5.);
myErf->SetParameter(1, 5.);
myErf->SetParameter(2, 1.);
eff->Fit(myErf);
}
Parameter Initialization and Fitting
You must initialize the parameters to sensible values - this is especially important for higher dimensional fits, e.g. with a TF2. Fitting is magic, but only within limits. Try with completely bogus parameters and you'll see how the fit fails!