KAHIBARO
Discord Login Register

10.1. Introduction to TF1

Creating functions

In ROOT, one-dimensional mathematical functions are represented by the class TF1. A TF1 object is a function of a single variable, usually called x, and can optionally depend on a set of parameters. You use TF1 both to visualize functions and to use them as models for fitting histograms or graphs.

The most common way for beginners to create a TF1 is by giving a formula as a string. In an interactive ROOT session you can write, for example:

cpp
TF1 *f1 = new TF1("f1", "sin(x)", 0.0, TMath::Pi());
f1->Draw();

Here "f1" is the internal name of the function, "sin(x)" is the formula, and 0.0 and TMath::Pi() are the lower and upper limits of the range where this function is defined for plotting and fitting. ROOT uses its own expression parser to interpret the formula string, so you can use many standard mathematical functions such as sin, cos, exp, log, sqrt, and constants like TMath::Pi().

You can also create constant functions and simple polynomials in the same way:

cpp
TF1 *fconst = new TF1("fconst", "3.0", 0.0, 10.0);
TF1 *fpoly  = new TF1("fpoly",  "1 + 2*x + 0.5*x*x", -5.0, 5.0);

Whenever you create a TF1 with a string expression, ROOT tries to infer how many parameters it has from symbols like [0], [1], and so on. If you do not use such symbols, ROOT assumes the function has no free parameters.

It is also possible to define functions using C++ functions instead of a string expression, which is useful when formulas become complicated or when you want maximum performance. This is discussed in more detail in the chapter on custom functions, but the essential idea is:

cpp
double myFunc(double *x, double *p) {
   double xx = x[0];
   return p[0] * TMath::Exp( -xx*xx / (2 * p[1]*p[1]) );
}
TF1 *fgaus = new TF1("fgaus", myFunc, -5.0, 5.0, 2);

Here myFunc is a normal C++ function, and the TF1 constructor receives a pointer to it, the range, and the number of parameters. Inside the function, the independent variable is read from x[0] and the parameters from the array p.

Once a TF1 exists, you can draw it, evaluate it, or use it as a fit model. For example:

cpp
double value = f1->Eval(1.0);  // evaluate at x = 1.0
f1->Draw("same");              // overlay on the current canvas

A TF1 created with a string expression must use ROOT's expression syntax. Use sin(x), exp(x), log(x), TMath::Pi(), and parameter placeholders like [0], [1]. C-style functions that ROOT does not know or undeclared variables will cause parsing errors.

Function ranges

Every TF1 has an associated range in the variable x. This range controls where ROOT samples the function for drawing and where it can be used as a default fit range.

When you construct a TF1 with the standard constructors, you usually specify the lower and upper limits explicitly:

cpp
TF1 *f = new TF1("f", "exp(-x)", 0.0, 10.0);

In this example, the function is defined conceptually for all x, but ROOT will only draw and use it from x = 0 to x = 10 unless you change the range later. This is often exactly what you want in data analysis, because you only care about a finite interval where you have data.

You can change the range after creation using SetRange:

cpp
f->SetRange(-5.0, 5.0);

and you can query the current range with:

cpp
double xmin, xmax;
f->GetRange(xmin, xmax);

If you evaluate the function with Eval(x) at a point outside the current range, ROOT still returns a value using the formula. The range is mainly a hint for plotting, sampling and default fitting, not a strict mathematical limitation.

When you use a TF1 as a fit function for a histogram or graph, the fit range can be set explicitly in the Fit call. If you do not specify a range there, ROOT will usually use the TF1 range. For example:

cpp
hist->Fit(f);                        // uses f's current range
hist->Fit(f, "", "", 2.0, 8.0);      // override with explicit fit range

The TF1 range controls where ROOT samples the function for plotting and where it is used by default for fitting. Always check that the function range matches the region of your data that you want to model, and adjust it with SetRange or in the Fit call if necessary.

Parameters

Most functions used in data analysis have adjustable parameters. In TF1, parameters are free numbers that you can set manually or let a fitting procedure determine from data.

There are two main ways parameters appear in a TF1:

  1. As explicit placeholders in a string expression, for example:
cpp
   TF1 *f = new TF1("f", "[0] + [1]*x", 0.0, 10.0);

Here [0] and [1] are the first and second parameters of the function. ROOT automatically sets the number of parameters to 2.

  1. As elements of the p array in a C++ function used to define the TF1, for example:
cpp
   double myLinear(double *x, double *p) {
      return p[0] + p[1]*x[0];
   }
   TF1 *f = new TF1("f", myLinear, 0.0, 10.0, 2);

In this case the last argument 2 tells ROOT that the function has two parameters, p[0] and p[1].

You can set parameter values explicitly using SetParameter or SetParameters:

cpp
f->SetParameter(0, 1.0);             // [0] = 1.0
f->SetParameter(1, 0.5);             // [1] = 0.5
f->SetParameters(1.0, 0.5);          // set both at once

and you can retrieve them with GetParameter:

cpp
double a = f->GetParameter(0);
double b = f->GetParameter(1);

It is also very common to give parameters human-readable names using SetParName, especially if the function has a physical interpretation:

cpp
f->SetParName(0, "offset");
f->SetParName(1, "slope");

These names appear in fit result printouts and on plots with fitted parameters.

ROOT allows you to fix some parameters to constant values and let others float during a fit. To fix a parameter, set it and then call FixParameter:

cpp
f->SetParameter(0, 0.0);
f->FixParameter(0, 0.0);   // parameter 0 will stay fixed during fits

To free a parameter again use ReleaseParameter:

cpp
f->ReleaseParameter(0);

You can also restrict parameters to lie within given bounds, for example to enforce that a width stays positive:

cpp
f->SetParLimits(1, 0.0, 10.0);  // parameter 1 constrained between 0 and 10

When you later use f to fit data, the fitting algorithm will start from the current parameter values, obey any limits you set, and try to improve them to best describe the data. After a fit, you can inspect the fitted parameter values, errors, and correlations using methods described in the fitting chapters.

During simple evaluations, the parameters just behave as constants in the formula. For example:

cpp
f->SetParameters(1.0, 0.5);       // f(x) = 1.0 + 0.5*x
double y = f->Eval(2.0);          // returns 1.0 + 0.5*2.0 = 2.0

Use parameter indices [0], [1], [2], ... consistently in your function definition and always initialize parameters with reasonable starting values before fitting. Good initial parameters and sensible limits can make the difference between a successful fit and a failed or misleading result.

Views: 9

Comments

Please login to add a comment.

Don't have an account? Register now!