Go to the U of M home page
School of Physics & Astronomy
Physics Intranet

computing:contrib:root:localdoc

What is ROOT?

The ROOT team says that the main features of ROOT are:

  • The Runtime Type Information System
  • The Object I/O System
  • Automatic Documentation Generation

This is completely unhelpful and totally misleading. Only somewhat more to the point, they describe the mission of ROOT thusly:

The ROOT system provides a set of OO frameworks with all the functionality needed to handle and analyse large amounts of data in a very efficient way. Having the data defined as a set of objects, specialised storage methods are used to get direct access to the separate attributes of the selected objects, without having to touch the bulk of the data. Included are histograming methods in 1, 2 and 3 dimensions, curve fitting, function evaluation, minimisation, graphics and visualization classes to allow the easy setup of an analysis system that can query and process the data interactively or in batch mode.

This description is still singularly unhelpful for the novice trying to make sense of how to accomplish his or her first task. A more useful description would be to say that, depending on the user, ROOT is:

  • A program that can make more attractive histograms than is possible with familiar programs
  • A program to make and read files in a format that stores data in a way optimized for physics experiments (i.e. experiments with a large number of independent events)
  • A set of libraries that contain many functions useful for physicists
  • [probably more]

ROOT and C++

The ROOT team goes on to say:

It also provides a good environment to learn C++.

In fact, ROOT is a terrible environment to learn C++. ROOT was designed before the Standard Template Libraries, which provide the bulk of the familiar C++ functionality, were implemented. It therefore has separate, totally incompatible implementations of things like vectors and strings. Code written using ROOT does not translate easily to the 99.999% of C++ world that does not use ROOT.

ROOT also makes heavy use of “interpreted C++” (using Cint, the C++ Interpreter), a concept which essentially does not exist outside of ROOT. It encourages sloppiness with pointers and other woes.

ROOT is written without the use of namespaces, a C++ concept that helps organize code. This is why all ROOT classes start with “T”, as in TTree, TFile, etc. The correct (or perhaps “modern”) way to do this would be to make a ROOT namespace so that the objects are called ROOT::Tree and ROOT::File. Or with “using namespace ROOT;” at the top of your code, they would be simply Tree and File.

ROOT's Object Orientation

The ROOT code shows evidence of having been developed without a good understanding of object-oriented ideas. The most commonly cited example is the wonky use of inheritance. The notion of one class inheriting from another is supposed to mean that the inheriting object has the relationship “is a kind of” with its parent. For instance, an object that represents squares should inherit from one that represents quadrilaterals, which should inherit from one that represents polygons. However, the root class TH2, a 2-dimensional histogram, inherits from a TH1, a 1-dimensional histogram. Most people would agree that a 1-dimensional histogram is a kind of 2-dimensional histogram; it's one where the second dimension is 1 bin wide. The ROOT inheritance is exactly backwards here.

ROOT's internal objects often do not correspond well to the user's idea of what an object should be. For instance, suppose there's a window on the screen that contains several histograms. In ROOT-speak, the window is called a “canvas” or a “pad” (all fine and good). Since, in the user's mind, the canvas has several attributes (size, background color), the histogram has several more (number of bins, bin contents) and the axis yet more (range, number of tick marks), it's clear that to modify the behaviour of the axis, one would do something to the axis object. Yet in fact, to switch an axis between logarithmic and linear, the user must call a function on the canvas.

Recommendations to ROOT users

Programming Style

Use the STL, not ROOT Classes

The Standard Template Library, which defines the usual versions of strings, vectors, lists, maps, and so on, is documented at sgi.com and in many books and other webpages. Always use these instead of the incompatible versions that ROOT provides (i.e. TString, TVector) unless you are forced not to by pre-existing code.

If you follow this, your code will be easier to port to a different system if you decide to move away from ROOT. You will also be able to communicate better with people outside of the ROOT world and you will have better programming skills for when you are outside of the ROOT world.

Resist Overuse of Objects

ROOT's documentation encourages use of objects when writing procedural code would be easier and more clear. For instance, they push you to load your data into an object which has functions defined on it that print and loop over the data (abbreviated from this Fermilab page):

class MyClass {
   public :
   TTree          *fTree; //pointer to the analyzed TTree
   //Declaration of leaves types
   Event           *event;

  //List of branches
  TBranch        *b_event;

   MyClass(TTree *tree=0);
   ~MyClass() {;}
   Int_t GetEntry(Int_t entry);
   void  Init(TTree *tree);
   void  Loop();
   void  Show(Int_t entry = -1);
};

This is insanity. Instead, simply prepare your TTree object (probably loaded from a file) and write ordinary functions that operate on it:

void Loop(TTree * awesomedata)
{
  yourdataobject * onedatum = 0;
  awesomedata->SetBranchAddress("yourdataobject", &onedatum);
  for(int i = 0; i < awesomedata->GetEntries(); i++){
    awesomedata->GetEntry(i);
    // brilliant analysis code goes here
  }
}

void analysis()
{
  TFile * datafile = new TFile("newdata.root");
  TTree * awesomedata = (TTree *)datafile->Get("fish");
  Loop(awesomedata);
}

The class “yourdataobject” needs only contain the data and not any functions.

Use Good Names

Root encourages or uses the following names:

  • For a class you wrote: MyClass (in examples)
  • For a histogram: h (in examples)
  • For a mathematical function: f (in examples)
  • For a canvas: c1 (the default for a canvas not named by the user)

These names are extremely problematic. “MyClass” is bad because it is totally undescriptive. Use a name that actually represents what data your class holds. (It also may give the impression that the names of classes have to start with “My”. This is not the case.) The other, very short, names are bad both because they are undescriptive and because it is nearly impossible to search for them in files.

C++ has, in practical terms, no limit on the length of variable or function names. While it's not good to use extremely long names either, it's perfectly fine to name your histogram “december_data_hist”. If you totally can't think of a good name for the new variable you just made, run:

/local/minos/bin/variablename

It outputs two short nouns stuck together. It probably won't be very descriptive, but at least it will be searchable.

Avoid Unnecessary Pointers

ROOT examples often declare every object as a pointer (with '*') and with dynamically allocated memory (with 'new'). For example:

TH1F * queengrid = new TH1F("it's a grid", "with queens", 10, 0, 10);

This is dangerous because if you do not call:

delete queengrid;

when you're done with the object, the memory is never released. This is fine if you're just drawing one plot, but in a large program, you can quickly lose track of things like this and you will then wonder why it uses 3GB of RAM. The alternative is:

TH1F queengrid("it's a grid", "with queens", 10, 0, 10);

which, besides being safer, is also less typing.

Now! Graphical objects like histograms that are meant to appear on the screen do need to be allocated with 'new', because otherwise they will disappear from the screen when their function ends. However, suppose you want to make a histogram, fill it and do a fit, but all you care about is the result of the fit; you never need to see the histogram. Then declare the histogram without '*' and 'new'.

For non-graphical objects like TRandom3, it's an easier call. Don't make them pointers unless you have a particular reason to. Despite the example you see that says to do this:

TRandom3 * laserfocus = new TRandom3();

instead do simply:

TRandom3 laserfocus;

These generate the same random numbers, but the first one stays in memory until you delete it explicitly, while the second stays in memory until the bottom of the function. By the way, to pass each of these to another function, the functions are declared as:

// If you declared with '*' and 'new'
void IWantRandomNumbers(TRandom3 * laserfocus)

// If you simply declared the object:
// This copies the object: the sequence of random
// numbers will not be advanced in the calling function
void IWantRandomNumbers(TRandom3 laserfocus)

// This uses the object itself: the sequence of random
// numbers *will* be advanced in the calling function
void IWantRandomNumbers(TRandom3 & laserfocus)

How to Run Code

There are three generic options for running ROOT-based code:

  1. Interpreted in the ROOT (Cint) shell via running the program “root”
  2. Compiled via running the program “root”
  3. Compiled into a standalone executable

The first two options are widely used and well documented (?) elsewhere. Briefly, if one has the program “domyanalysis.C”, it must contain the function domyanalysis(). It is run interpreted with

root 'domyanalysis.C("datafile1.root", 47)'

where the single quotes protect the parentheses and double quotes from the shell, which otherwise assigns special meaning to them. To run compiled:

root 'domyanalysis.C+("datafile1.root", 47)'

or

root 'domyanalysis.C++("datafile1.root", 47)'

where the second form forces recompilation (sometimes necessary if you move to a new machine, for instance).

The third is rarely used, but probably should be used more. In this case, you write an ordinary C++ program with the function main() that uses some ROOT objects, i.e.:

using namespace std;
#include "TRandom3.h"
#include <iostream>
#include <string>

int main(int argc, char ** argv)
{
   int seed = 0;
   string filename;

   if(argc >= 2) seed = atoi(argv[1]);
   TRandom3 atomlitre(seed);
   cout << "Your random number is " << atomlitre.Rndm() << endl;
   
   if(argc >= 3){
     filename = argv[2];
     cout << "Your file name is " << filename << endl;
   }

   return 0;
} 

and compile it with:

g++ -o printrandomnumber printrandomnumber.C `root-config --cflags --libs`

and run it with:

printrandomnumber 17 datafile.root

This has two big advantages. First, you do not need to type all of those quotation marks and parentheses. Second, your compiled program can run on machines that don't have ROOT installed. Third, your code is less likely to stop working when the ROOT version changes. Fourth, it probably runs faster because it doesn't have all the overhead of running root itself. Fifth, your code looks much more like the ordinary C++ that most programmers are familiar with. Sixth, the compile error messages from g++ are much more helpful than those from the ROOT compiler. Ok, more than two.

Making Attractive Plots

The default ROOT plot is quite nice for screen display. However, when you print it or use it in a talk, you will discover that the background is grey, which is ugly and wastes toner. The lines used for histograms and axes are often too thin for easy reading when shown in a talk or paper. Canvases have beveled edges and legends have drop shadows; whiz-bang, but not really suitable for many environments.

These things can all be modified either by fiddling with the individual objects or by setting things in the active TStyle. This code sets things up in a sensible way, although you probably want to pick and choose to suit your own tastes:

void SetOKStyle()
{
  TStyle* OKStyle = new  TStyle("OKStyle", "OK Default Style");

  // Colors

  //set the background color to white
  OKStyle->SetFillColor(10);
  OKStyle->SetFrameFillColor(10);
  OKStyle->SetFrameFillStyle(0);
  OKStyle->SetFillStyle(0);
  OKStyle->SetCanvasColor(10);
  OKStyle->SetPadColor(10);
  OKStyle->SetTitleFillColor(0);
  OKStyle->SetStatColor(10);

  // Get rid of drop shadow on legends
  // This doesn't seem to work.  Call SetBorderSize(1) directly on your TLegends
  OKStyle->SetLegendBorderSize(1);

  //don't put a colored frame around the plots
  OKStyle->SetFrameBorderMode(0);
  OKStyle->SetCanvasBorderMode(0);
  OKStyle->SetPadBorderMode(0);

  //use the primary color palette
  OKStyle->SetPalette(1,0);

  //set the default line color for a histogram to be black
  OKStyle->SetHistLineColor(kBlack);

  //set the default line color for a fit function to be red
  OKStyle->SetFuncColor(kRed);

  //make the axis labels black
  OKStyle->SetLabelColor(kBlack,"xyz");

  //set the default title color to be black
  OKStyle->SetTitleColor(kBlack);

  //set the margins
  OKStyle->SetPadBottomMargin(0.15);
  OKStyle->SetPadLeftMargin(0.15);
  OKStyle->SetPadTopMargin(0.075);
  OKStyle->SetPadRightMargin(0.15);

  //set axis label and title text sizes
  OKStyle->SetLabelSize(0.05,"xyz");
  OKStyle->SetTitleSize(0.07,"xyz");
  OKStyle->SetTitleOffset(0.9,"xyz");
  OKStyle->SetStatFontSize(0.05);
  OKStyle->SetTextSize(0.07);
  OKStyle->SetTitleBorderSize(0);

  //set line widths
  OKStyle->SetHistLineWidth(2);
  OKStyle->SetFrameLineWidth(2);
  OKStyle->SetFuncWidth(2);

  // Misc

  //align the titles to be centered
  //OKStyle->SetTextAlign(22);

  //turn off xy grids
  OKStyle->SetPadGridX(0);
  OKStyle->SetPadGridY(0);

  //set the tick mark style
  OKStyle->SetPadTickX(1);
  OKStyle->SetPadTickY(1);

  //don't show the fit parameters in a box
  OKStyle->SetOptFit(0000);

  //set the default stats shown
  OKStyle->SetOptStat("neimr");

  //marker settings
  OKStyle->SetMarkerStyle(8);
  OKStyle->SetMarkerSize(0.7);

  // Fonts
  OKStyle->SetStatFont(12);
  OKStyle->SetLabelFont(12,"xyz");
  OKStyle->SetTitleFont(12,"xyz");
  OKStyle->SetTextFont(12);

  // Set the paper size for output
  OKStyle->SetPaperSize(TStyle::kUSLetter);

  //done
  OKStyle->cd();

  cout << "Using OKStyle" << endl;
}
computing/contrib/root/localdoc.txt · Last modified: 2009/06/01 15:47 by strait