KAHIBARO
Discord Login Register

10.4. Evaluating Functions

`Eval()`

Once you have a TF1, you can evaluate it at any point in its range using the member function Eval(). Conceptually, Eval() takes an $x$ value and returns the function value $f(x)$, using either a built‑in formula or your custom definition.

Suppose you created a simple Gaussian function like this in ROOT:

cpp
TF1 *fgaus = new TF1("fgaus", "gaus", -5.0, 5.0);
fgaus->SetParameters(1.0, 0.0, 1.0); // amplitude, mean, sigma

You can now evaluate it at a single point:

cpp
double x  = 1.0;
double fx = fgaus->Eval(x);

After this call, fx holds the numerical value of the Gaussian at $x = 1$. You can use this value in further calculations, print it, or store it in a variable or TTree.

You are not restricted to using variables. You can also call Eval() directly with literals when you just want to check a value quickly in an interactive session:

cpp
fgaus->Eval(0.0);  // value at x = 0
fgaus->Eval(2.5);  // value at x = 2.5

By default, TF1 is used for one‑dimensional functions of a single variable. In more advanced cases, TF1 can also represent functions of several variables or functions with extra parameters, but the central idea remains the same. Eval() receives the $x$ coordinate and returns the corresponding $y$ value according to the definition stored in the TF1.

Key rule: For a TF1 representing a function of one variable, y = f->Eval(x) always gives the numerical value of the function at that point, independent of whether the function is built‑in, given as a formula string, or implemented as a C++ function.

Finding values

Evaluating a function at one or more points is the basis for many simple numerical tasks, such as scanning a function, locating approximate maxima or minima, and checking the value at special locations.

To evaluate a function on a grid of $x$ values, you can loop over the range of the function and repeatedly call Eval():

cpp
TF1 *fexp = new TF1("fexp", "exp(-x)", 0.0, 5.0);
for (int i = 0; i <= 10; ++i) {
  double x  = 0.5 * i;          // x from 0.0 to 5.0 in steps of 0.5
  double fx = fexp->Eval(x);
  std::cout << "x = " << x << ", f(x) = " << fx << std::endl;
}

This pattern is useful when you want to:

Check how a function behaves in a certain region before fitting data.

Tabulate function values to compare with another calculation.

Compute approximate summaries such as the maximum in a range, by keeping track of the best value seen so far:

cpp
double xmax = 0.0;
double fmax = -1e30;
for (int i = 0; i <= 100; ++i) {
  double x  = 0.1 * i;       // 0.0 to 10.0
  double fx = fexp->Eval(x);
  if (fx > fmax) {
    fmax = fx;
    xmax = x;
  }
}
std::cout << "Approximate maximum f(x) = " << fmax
          << " at x = " << xmax << std::endl;

You can also evaluate at the boundaries or at special positions such as the mean of a Gaussian or the center of the function range. Since each TF1 stores its own $x$ limits, you can retrieve them and use them directly in your evaluation:

cpp
double xmin = fgaus->GetXmin();
double xmax = fgaus->GetXmax();
double f_left  = fgaus->Eval(xmin);
double f_right = fgaus->Eval(xmax);

For functions that are expensive to compute, it can be more efficient to evaluate them a limited number of times or to reuse results. In many practical analyses, though, calling Eval() in a loop over a moderate number of points is perfectly fine.

Important practice: Use Eval() whenever you need the numerical value of a TF1 at a given point, for example when scanning a function, tabulating values, or feeding function results into your own calculations.

Plotting functions

In ROOT, a TF1 is both a numerical function and a drawable object. To display the function as a curve, you usually draw it on a canvas using its Draw() method. Internally, ROOT evaluates the function at many $x$ values and uses those Eval() results to build the plotted curve.

A minimal example looks like this:

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

ROOT automatically creates a default canvas if none exists. The Draw() method samples the function in the range from GetXmin() to GetXmax() and joins the points with a line. The number of sampling points, and therefore the smoothness of the curve, can be controlled with:

cpp
fsin->SetNpx(500);  // increase number of points used for drawing
fsin->Draw();

Here, SetNpx() determines how many times ROOT calls Eval() internally when constructing the curve. A larger number gives a smoother curve but takes slightly more time.

You can also draw several functions on the same canvas and compare them. The usual way is to draw the first function, then draw others with the "SAME" option:

cpp
TF1 *f1 = new TF1("f1", "sin(x)", 0.0, TMath::Pi());
TF1 *f2 = new TF1("f2", "0.5*sin(x)", 0.0, TMath::Pi());
f1->SetLineColor(kBlue);
f2->SetLineColor(kRed);
f1->Draw();          // first function
f2->Draw("SAME");    // second function on same axes

Since the x axis and y axis come from the first object drawn, it is often helpful to choose an appropriate range and style for that first function. You can then add titles, axis labels, and legends using the plotting tools discussed elsewhere in the course.

Although you usually do not call Eval() directly when plotting, it is useful to keep in mind that the curve you see is a visual representation of a large number of Eval() calls. Whenever you adjust the function parameters or change its range, a new Draw() call will cause ROOT to re‑evaluate the function at many x values and update the plot.

Essential idea: Draw() uses internal calls to Eval() to turn a TF1 into a visible curve on a canvas. You control how the function looks by setting its range, parameters, and number of sampling points with SetNpx(), then redrawing.

Views: 11

Comments

Please login to add a comment.

Don't have an account? Register now!