Hsimple

hsimple[edit | edit source]

This example creates a binary file, in a ROOT-persistency format, containing a few objects.

We will develop the example in two different (albeit very similar) versions:

  • hsimple.cxx
  • hsimple.cpp

The first can be directly used in the interactive shell provided by the ROOT interpreter (it behaves like a script). The second will be a C++ program, that can be compiled as a stand-alone executable.

Let's start with hsimple.cxx. The first step is to create an empty scope with a text editor and then populate it with C++ statements that the interpreter will parse and execute:

1 {
2 }

It is good practice to begin a ROOT script with the following statement:

1 {
2  gROOT->Reset() ;
3 }

Two comments here:

  • gROOT is a global variable, instantiated by the interpreter and always available: it is a pointer to TROOT, an object which is the entry point to the ROOT system. Using the gROOT pointer one has access to basically every object created in a ROOT based program. The TROOT object is essentially a container of several lists pointing to the main ROOT objects.
  • When running an un-named script over again and this is frequently the case since un-named scripts are used to prototype, one should reset the global environment to clear the variables. This is done by calling gROOT->Reset(). It is good practice, and you will see this in the examples, to begin an un-named script with gROOT->Reset(). It clears the global scope to the state just before executing the previous script (not including any logon scripts). The gROOT->Reset() calls the destructor of the objects if the object was created on the stack. If the object was created on the heap (via new) it is not deleted, but the variable is no longer associated with it. Creating variables on the heap in un-named scripts and calling gROOT->Reset() without you calling the destructor explicitly will cause a memory leak. This may be surprising, but it follows the scope rules. For example, creating an object on the heap in a function (in a named script) without explicitly deleting it will also cause a memory leak. Since when exiting the function only the stack variables are deleted. The code below shows gROOT->Reset() calling the destructor for the stack variable, but not for the heap variable. In the end, neither variable is available, but the memory for the heap variable is not released.

Next step will be the creation of a binary file aimed at containing the data and the histograms that will be generated by this code:

1 {
2  gROOT->Reset() ;
3  auto filename = "hsimple.root";
4  TFile hfile(filename,"RECREATE","Demo ROOT file with histograms");
5 }

The C++-11 keyword auto automatically qualifies the variable filename to be of type const char*.

The statement at line 4 instantiates a TFile object on the stack, named hfile: input arguments are the name of the file we want to create, a string specifying the READ/WRITE mode we want (WRITE in out case) and an optional title.

 1 {
 2  gROOT->Reset() ;
 3  auto filename = "hsimple.root";
 4  TFile    hfile (filename,"RECREATE","Demo ROOT file with histograms"     );
 5 
 6  TH1F     hpx   ("hpx",   "This is the px distribution",   64,-4,4        );
 7  TH2F     hpxpy ("hpxpy", "py vs px",                      64,-4,4,64,-4,4);
 8  TProfile hprof ("hprof", "Profile of pz versus px",       64,-4,4,0,20   );
 9  TNtuple  ntuple("ntuple","Demo ntuple","px:py:pz:random:i"               );
10  TCanvas  c1    ("c1",    "Filling Example",               800,600        );
11 }

The hpx object is an instance of a TH1F class, basically a simple mono-dimensional histogram, built with 64 bins ranging from -4 to +4 on the abscissa. The name of the histogram is the first variable in the calling sequence ("hpx"), while its title is the second one ("This is the px distribution").

The same principle applies to the hpxpy object, a bi-dimensional histogram of type TH2F.

A TProfile is an excellent alternative to represent a scatter plot (a 2-dim histogram, see the rererence here

A TNtuple is object capable of representing (guess what?) a n-tuple: an n-tuple is a sequence (or ordered list) of n elements, where n is a non-negative integer where each element can be, in our case, an array of values of type int, float, double, bool or char.

In this example we would like, in particular, to store a vector of the following quantities, associated to a progressive number i:

element[0]     element[1]    element[2]     ...
double (px)    double (px)   double (px)
double (py)    double (py)   ...
double (pz)    double (pz)   ...
double (r )    double (r )
int    (i )    int    (i )

The constructor of a TNtuple (reference here) accepts the list of variables that will be saved for each progressive number i.

Finally we prepare a canvas (line 10) to hold the graphical representation of the histograms.

Let's now add a component to measure the performance of a snippet of code by means of a selective benchmark (TBenchmark)

 1 {
 2  gROOT->Reset() ;
 3  auto filename = "hsimple.root";
 4  TFile    hfile (filename,"RECREATE","Demo ROOT file with histograms"     );
 5 
 6  TH1F     hpx   ("hpx",   "This is the px distribution",   64,-4,4        );
 7  TH2F     hpxpy ("hpxpy", "py vs px",                      64,-4,4,64,-4,4);
 8  TProfile hprof ("hprof", "Profile of pz versus px",       64,-4,4,0,20   );
 9  TNtuple  ntuple("ntuple","Demo ntuple","px:py:pz:random:i"               );
10  TCanvas  c1    ("c1",    "Filling Example",               800,600        );
11 
12  auto benchmarkName = "hsimple";
13  gBenchmark->Start(benchmarkName);
14 }