ROOT/Getting Started/Many Ways to Use ROOT

In this section we'll have a look at the many ways to execute code in ROOT.

The ROOT Command Line edit

If you start a ROOT session on a terminal, you'll get an interpreter that takes your code and executes it line after line. For example, let's try the “hello world” program:

root [0] cout << "hello world!" << endl;
hello world!

Well, wasn't that simple? Let's see what happened. As you hit enter, line 0 (indicated by the root [0] at the beginning) was sent to the CINT who executed it immediately. CINT is a C++ interpreter developed by Masaharu Goto (Agilent Technologies), Philippe Canal and Paul Russo (Fermilab) and Leandro Franco, Diego Marcos, and Axel Naumann (CERN)[1]. An interpreter is a software that executes code line after line rather than compiling it all at once. This has the advantage that you needn't wait for the compiler and can see / check the result immediately. On the other hand, interpreting code is much slower than executing compiled code.

The interpreter is smart enough to wait for you to finish your command, even if you hit enter before. This is useful when entering loops as we can see in the following example:

root [1] Float_t u = 0.0;
root [2] for (Int_t i = 0; i < 10; i++) {
root [3] u = 2.0 * cos(u) - sin(u);
root [3] if (u >= 0) {
root [4] cout << u << " is positive" << endl; }
root [5] else {
root [6] cout << u << " is negative" << endl; }
root [7] }
2 is positive
-1.74159 is negative
0.645519 is positive
0.995963 is positive
0.248108 is positive
1.69319 is positive
-1.23669 is negative
1.60055 is positive
-1.05906 is negative
1.85128 is positive

Note that I have used the machine independent types Float_t and Int_t here that are granted to be the same on any machine.

You should also have noted that cout, sin() and cos() worked as expected even though we never included the respective header files nor resolved the namespace. This is because the code isn't compiled and CINT knows the standard C++ functions.

(Making and) Correcting Mistakes edit

An other nice thing about using the ROOT command line is that if you make a mistake, you'll be informed immediately and can correct it easily.

root [8] cout < "watch out!" << endl;
Error: operator< not defined for basic_ostream<char,char_traits<char> > (tmpfile):1:
*** Interpreter error recovered ***
root [9] cout << "watch out!" << endl;
watch out!

In this example I have forgotten the second < in line 0. The interpreter told me and I could recall the last line by pressing uparrow, correcting the mistake and executing it again.

It should be said that CINT is quite tolerant and allows you to use input that normally wouldn't be valid C++. However, I clearly discourage to use this option since it will make your code look weired and harder to understand, more likely to fail and impossible to compile if wished later. Therefore this “features” will not be presented here.

Auto Completion edit

An other very useful tool is auto completion. For example, try typing TRa and then hit Tab. The word will be completed to TRandom which is still ambiguous. If you hit Tab again, you'll be shown the four possibilities the interpreter knows. Those are the random classes TRandom, TRandom1, TRandom2 and TRandom3. We'll see later what they are good for.

Especially if you want to try something new and are not very sure about the names to use this can be an enormous helpful support.

Exercise: Trying Interactive ROOT
On an interactive ROOT session: Fill an array of 100 000 000 64Bit floating point numbers of the machine independent ROOT type Double_t with random numbers. Compute the mean of the array and print it to the console. Note the computation time.

Hint: You can get random real numbers by creating a pointer to an instance of the TRandom class. (The constructor takes one arbitrary integer as initialization.) Then you can call the method TRandom::Rndm() to get a simple pseudo random number uniformly distributed between 0 and 1. For example:

TRandom *R = new TRandom(time(0));  // create a pointer to a new instance of TRandom in the heap
cout << R->Rndm() << endl;
[Solution]

ROOT Macros edit

You have now seen how to use the ROOT command line. It is a nice thing, especially if you want to try something out and are not absolutely sure about the syntax. However, if you're solving a more complex problem, you'll probably want to keep your code stored in a file you can edit and execute multiple times. So what would be more convenient than storing the commands in a script file and telling ROOT to execute them all? Here we go:

Create a text file named helloscript.cc. In this file you can put any commands you could have been typing during a session. To start simple, let's do the hello world thing. In this case you would write

void helloscript()
{
  cout << "hello world!" << endl;
}

The script file must contain a void function that is named the same as the file. When ROOT executes your script file, it will call this function. To do so, open a ROOT session in the directory you've saved the script and say

root [10] .x helloscript.cc
hello world!

Please note that the code is still interpreted line by line and not compiled. Except that you have to put everything in the void function there is no difference to using the command line. In particular, it is not necessary to resolve namespaces (as e.g. for cout in iostream).

Passing Parameters edit

You can pass on parameters to the macro you call. Consider the following script:

void hello(Int_t times)
{
  for (Int_t i = 0; i < times; i++)
  {
    cout << "hello world!" << endl;
  }
}

You can call it via

root [11] .x hello.cc(3)
hello world!
hello world!
hello world!

It is even possible to overload the function. For example, we could define both void hello() and void hello(Int_t times). Now either of the two calls

root [12] .x hello.cc

and

root [13] .x hello.cc(3)

would be legal.

Exercise: Interpreted ROOT Macros
  1. Consider the problem solved in the previous exercise. Now write a script that does the same thing and execute it as interpreted macro. Does it run faster?
  2. Modify the script in a way that the user can pass the amount of random numbers to be created as a parameter.
  3. Overload the macro such that the amount of numbers can still be passed but if no parameter is given, 100 000 000 numbers will be generated.
[Solution]

Compiling Code on the Fly edit

Macros will do well for almost anything you'll want to do at the beginning. But they have a huge disadvantage: They run slow! (And some people also observed strange behavior under some circumstances&#133;) ROOT however has very sophisticated routines implemented that ensure to make the most out of your machine's resources. To use them you must compile your code.

We just look at the script we've written before. To make it valid C++ we must be aware of namespaces and header files now. (If you were using messy code that was still accepted by CINT—I've warned you—then you'll have to correct it now. Otherwise AcLiC won't be able to compile.) So our modified file should look like this:

# include <iostream>

using namespace std;

void helloscript()
{
  cout << "hello world!" << endl;
}

Now we can execute it via

root [14] .x helloscript.cc+

The output is of course the same. The “+” at the end told ROOT to compile the code before running it. This was done by a program called ACLiC („The Automatic Compiler of Libraries for CINT“). ACLiC is a smart tool that makes use of your installed compiler and builds up and reuses a library from your compiled code. You can, however, tell ACLiC to build up the library from scratch even if not necessary by adding “++” at the end.

Another good news is that even if you adopted your script in a way that it can be compiled by ACLiC, it is still possible to

  • pass on parameters. Syntax: .x <script>.cc+(<parameter>)
  • interpret it by CINT without compiling.
Exercise: Compiled ROOT Macros
Use the script you've written before and modify it so it can be run as compiled macro via ACLiC.
  • Make sure that both, calling with and without parameter, will still work.
  • Can your script still be interpreted by CINT after you've changed it?
  • How is the performance of the compiled macro for 100 000 000 numbers? What is faster for 5 numbers?
[Solution]

Building a Stand-Alone Application edit

The most advanced way to run your ROOT code is to make it a stand alone application. Remember that ROOT basically is a collection of C++ classes. These can be used to build brand new applications that may be compiled and executed without depending on the original ROOT install anymore. I will show how to compile using g++.

To run g++ on our file it must meet the C++ standard. That is in particular, it must contain an int main() function. One could of course simply rename the former void method but there is a more elegant way to go. If we'd rename the function then the code can be compiled but no longer interpreted. (No function named int main() is allowed in interpreted code since it would conflict with the internal functions from CINT.) On a happy note, CINT defines the preprocessor variable __CINT__. Knowing this we can add a main function that will only be visible when g++ processes the code but not when CINT does. Here is an example:

# include <iostream>

using namespace std;

void hello()
{
  cout << "hello world!" << endl;
}

# ifndef __CINT__
int main()
{
  hello();
  return 0;
}
# endif

From CINT's point of view, the file didn't change at all. When g++ compiles it, it will make main call the void function—just what CINT otherwise would have done.

To compile the file, say

g++ -o hello hello.cc `root-config --cflags --glibs`

and then execute it with

./hello

without caring about ROOT at all. On compiling time, ROOT will take care for you that all links are set correctly.

However, you might be a little disappointed because what we actually wrote is nothing but a hello world program that doesn't make use of any ROOT resources at all. Okay, here is another simple example that makes use of the build in data type Double_t and the random class TRandom.

# include <iostream>
# include "TRandom.h"

using namespace std;

void test()
{
  TRandom *rnd = new TRandom(time(0));
  Double_t x = rnd->Rndm();
  cout << "x = " << x << endl;
}

# ifndef __CINT__
int main()
{
  test();
  return 0;
}
# endif
Exercise: A Simple Stand-Alone Application
Once again, consider the code you should have previously written to compute the mean of an array of random numbers. Now, last but not least, make it a stand-alone application and run it independently of any ROOT session. Make sure that passing parameters will still work. Check the performance of your application. Take also care that your modifications don't disturb interpretation or on-the-fly compilation by CINT or ACLiC.
[Solution]

References edit

  1. CERN: CINT. http://root.cern.ch/drupal/content/cint