RootFAQ

From Mu2eWiki
Jump to navigation Jump to search

Introduction

Most of your questions about ROOT can be answered using the documentation found at the ROOT website. This FAQ answers a few specfic questions that come up often and for which the answers are hard to find on the ROOT web site.


Porting root v5 scripts to root v6

This section holds some hints on how migrating root scripts from ROOT 5 to ROOT 6. It is not comprehensive; if you learn somethinig - add it to this page.

Introduction

Until the end of the ROOT v5 series of releases, ROOT interactive command lines and ROOT macros were parsed and executed by a custom written interpreter named CINT . CINT was advertised as an interpreter for code written in C and C++. A more accurate description is that the CINT language shares many features with C and C++ but it misses many important features and adds other features that are not part of either language. Indeed there is code that is both legal CINT and legal C++ but they do different things.

Starting with ROOT v6, CINT has been replaced by a new interpretter named cling . Quoting from the Cling web site:


Cling is an interactive C++ interpreter, built on the top of LLVM and Clang libraries. Its advantages over the standard interpreters are that it has command line prompt and uses just-in-time (JIT) compiler for compilation.

In addition, Cling can also parse and execute some Cling-specific extensions to C++. These extensions are much less extensive than the extensions supported by CINT.


General Comments and Guidelines

  1. Consider running a ROOT macro from the command line:
  2. root -l myMacro.C
    The file "myMacro.C" may contain either a macro named myMacro or it may contain an unnamed macro. In both ROOT 5 and ROOT 6, objects instantiated in named macros are destroyed on return from the macro (normal C++ lifetime and scope rules). On the other hand, objects instantiated in an unnamed macro have their lifetime and scope extended; they remain accessible on the command line after return from the unnamed macro.
  3. In ROOT 5 the CINT command to compile and load externally supplied code is:
  4. gROOT->LoadMacro("file.C+");
    

    In ROOT 6 this has been replaced with the syntax.

    #include "file.C+"
    

    I have not learned the rules for resolving user supplied header files that are included in file.C; they are not what I expected.

  5. CINT automatically created many variables that are pointers to certain TObjects and that are visible at the ROOT prompt. For example if your compiled ROOT script created a TCanvas object with the TName of mycan, then a variable mycan, of type TCanvas*, was created in the scope of the ROOT prompt. Variables visible at the ROOT prompt were also visible in top level ROOT macros. ROOT 6 does not do this. If you create a TObject inside a class or function called from the command line and if you want a pointer to it to be available at the command line, then it is your responsibity to provide a mechanism to communicate the pointer to the command line.


Case Study 1

The two ROOT scripts that are used to make one of the standard sets of tracking efficiency and resolution plots are:

TrkDiag/test/KalFit.C
TrkDiag/test/ce.C

KalFit.C does all of the physics work; ce.C manages I/O and calls KalFit.C.

These scripts operate on a ROOT TTree created by TrkDiag/src/ReadKalFits_module.cc; for an example you can use Analyses/test/genReco.fcl; running it for 2000 events will take about 15 minutes and will produce enough reconstructed tracks to explore.

To run these scripts, setup the Mu2e run-time environment and:

root -l TrkDiag/test/ce.C

This will pop up two TCanvas windows and return control to the ROOT prompt.

These files are available in Mu2e Offline and copies are available here:

  • From tag art-v2-migration-base (ROOT 5): ce.C, KalFit.C .
  • From commit fc088c0c on branch art-v2-migration-branch (ROOT 6): ce.C , KalFit.C.

The following text is the output of: diff -wbB v5/KalFit.C v6/KalFit.C

23a25,26
>   TCanvas* rcan;
>   TCanvas* acan;
695c699
<   TCanvas* acan = new TCanvas("acan","Acceptance",1200,800);
---
>   acan = new TCanvas("acan","Acceptance",1200,800);
753c757
<   TCanvas* rcan = new TCanvas("rcan","Momentum Resolution",1200,800);
---
>   rcan = new TCanvas("rcan","Momentum Resolution",1200,800);

In the ROOT 5 version, variables acan and rcan were function-local; in the ROOT 6 version they are public member data.

The following text is the output of: diff -wbB v5/ce.C v6/ce.C

2c2
< // Version for use with root v5.
---
> // Version for use with root v6.
30,31d29
<
<   gROOT->Reset();
38c36
<   gROOT->LoadMacro("TrkDiag/test/KalFit.C+");
---
> #include "TrkDiag/test/KalFit.C+"
45,46c43,45
<   acan->Print("acan_ce.pdf");
<   rcan->Print("rcan_ce.pdf");
---
>
>   fit.acan->Print("acan_ce.pdf");
>   fit.rcan->Print("rcan_ce.pdf");

In the ROOT 6 code, the LoadMacro call is replaced as described in item 2 from General Comments section . In the ROOT 6 code, the TCanvas* pointers acan and rcan are accessed as public member data of the class KalFit.C; in the ROOT 5 code they were automagicaly provided by CINT.

I am not sure if it was necessary to remove the gROOT->Reset() or if it is an artifact of debugging.



TFile, TDirectory and the ROOT Current Working Directory

When one opens a ROOT file that holds, or will hold, histograms, ntuples etc, that file is seen inside the program as a TFile object. Inside an executing program, there may be zero, one or many TFiles open at any given time. Inside each of these TFiles there may be a hierarchy of many directories; each directory is represented inside ROOT as a TDirectory object. In addition there may be TDirectory objects that are only inside the memory of an executing program and are not associated with any disk file.

At any given time ROOT has the concept of the current working directory; this is available to the user as the global variable gDirectory, from the header TDirectory.h. When the user codes

TH1F* hist = new TH1F( "hist", "Title", nx, xlow, xhigh);

the following actions are implied:

  1. Upon instantiation, the object hist is appended to the list of objects inside the current working directory.
  2. If the current working directory is associated with a TFile, then, when the TFile is closed, a snapshot of hist at close-time will be written to the TFile.

This behaviour happens for objects of type TH* and TTree; the latter includes objects of type TNtuple. To be a little more specific, objects of type TTree are periodically written to the output file as their internal memory buffers fill; at close-time the buffers are flushed.

Objects of other types are not automatically written to the output file; for many other types, the user may take additional steps to cause selected objects to be written to the output file, see below.


Why is my TCanvas, TGraph etc not written to the ROOT output file?

While ROOT automatically writes histograms, ntuples and trees to output files, it does not do so for objects of other types, in particular for objects of type TGraph, TCanvas and TText. This FAQ item describes how to write these items to a ROOT output file; it presumes that the reader is already familiar with the FAQ item about TFile, TDirectory and the ROOT current working directory . This FAQ item discusses two methods:

Method 1: Using TDirectory::Append

One may add an object to the current working directory using Append(). For example,

TGraph* graph = new TGraph();
graph->SetName("myGraph");
graph->SetTitle("My Graph");
gDirectory->Append(graph);
// ... code to fill the graph

In this example the unnamed, untitled object graph is given a name and a title and is added to the current working directory. If the current working directory is associated with a TFile, then, when the TFile is closed, a snapshot of graph will be written to the file. The call to Append may be made at any time after the object is created and before the TFile is closed ( of course gDirectory might change frequently during that time). Alternatively, one might have added graph to any other TDirectory by getting a pointer to that TDirectory and calling its Append method.


I am not sure what happens if the call to SetName is skipped; I think that the object appears in the output file with a name automatically generated by ROOT. The call to SetTitle is optional.


Bug Report, Septemeber 2010: This method does not work when one creates a TCanvas inside a framework job. For reasons that are not yet understood, one must use the second method. We suspect that both the framework and the root are trying to delete the objects that are posted to the TCanvas.

Method 2: Using TObject::Write

TGraph* graph = new TGraph();
graph->SetName("myGraph");
graph->SetTitle("My Graph");
// ... code to fill the graph
graph->Write();

In this example, a snapshot of graph will be written to the directory that is current at the time of the call to Write(); so, normally, one would call Write in the endJob method. One may make multiple calls to Write throughout a job; each call will write a snapshot to the directory that is current at the time of the call; if there is already an object named "myGraph" in that directory, then ROOT will automatically generate a new name and both objects will be present in the directory; the automatic name is generated by by appending ";1" to the original name of the object; and so on, for versions ";2", ";3" etc ).

Bug Report, September 2010: The framework TFileSerivce creates a TDirectory with a unique name for each module in a framework job. It also does a cd to the correct TDirectory before calling most methods of each module. The exception is that it fails to do the cd for the endJob method. This will be fixed in a new release of the TFileService. One can work around this by saving the value of gDirectory in beginJob and doing a cd() to it during endJob.



Showing Full Histogram Statistics

We strongly recommend that when you make a histogram to show at a group meeting or in an internal report, that you tell root to show the underflows and overflows in the statistics box. While there will be reasonable exceptions to this, we strongly encourage it as the default practice.

To enable this behaviour by default, you can add a line like one of the following lines to your root script or to your .rootrc file.

gStyle->SetOptStat("emruoi");   // Pick one or the other.
gStyle->SetOptStat(1111110);

Both of these lines tell root to do the same thing; the first uses mnemonic letters while the second uses the paw style bit map. In both cases the statistics box will contain the number of entries in the histogram (including underflows and overflow), the mean value, the rms, the number of underflows, the number of overflows and the sum of all entries inside the histogram limits. For a full description of the available options to SetOptStat, see the root documentation for the class TStyle and search for SetOptStat.