Basic ROOT Tutorial Collaboration Meeting Jun2019

From Mu2eWiki
Jump to navigation Jump to search

Tutorial Session Goal

In this Tutorial you will learn the basics of ROOT. The final aim being to learn how to analyze TTree data produced by the Mu2e Offline software.

Session Prerequisites and Advance Preparation

As this is a basic introduction, few prerequisites are necessary, however, it would be beneficial if attendees:

  • Take a look at [1].
  • Have the tutorial docker installed and able to use root and view root GUI

ROOT: An introduction

ROOT is a modular scientific software toolkit used extensive in High Energy and Particle Physics. ROOT provides a platform for data processing, statistical analyses, visualisation and data storage.

ROOT is an object-orientated framework predominately written in c++.

For more information: https://root.cern.ch/

The Basics

ROOT uses a C++ interpreter, you can use it on command line, no need to use ";" at the end of every line.

Some basic commands:

  • -".q" - quits ROOT
  • - ".!" as a prefix allows you to use linux commands in root
  • - ".?" - displays special commands
  • - ".x Example.C" - executes the macro in Example
  • -".L Example.C" - Loads Example and the associated classes within

What is a TTree?

TTrees are used through out particle physics as data containers. They can be form both input and output files in a ROOT macro.

Exercises

The majority of the session time should be spent performing exercises, which you link or embed in the session page.

For these exericses please download the .root file located here "/mu2e/app/users/sophie/tutorial" if you do not have Fermilab access let me know before the session.

Exercise 1: Open the and look at content with a TBrowser

  • Once you have downloaded and installed root, open a new terminal.
  • Type "root" - this will open up root in interactive mode (you should see the root logo flash up and you are then in the root environment)
  • Open up the file by typing:
TFile::Open("$FullPathToFile/FileName.root")


This file will contain the TTree, this is a data container used by root and by the Mu2e Offline software.

  • View the contents of the File by typing:
.ls

You should see a list of the contents of the file.

  • You can create a TBrowser called "a" using the following in the command line:
 TBrowser a 

A GUI browser should appear which lists files in your current directory. You should see the .root file. You can select the file. You will see the TTree and various associated TBranches. Select one - a histogram should appear.

You can project a histogram in the TBrowser.

Exercise 2: Reading a TTree

You can find all the methods which can be applied to a TTree Class object here: https://root.cern.ch/doc/master/classTTree.html

There are a few ways to Read a TTree, for example:

 TFile *f = new TFile("Hits1.root")
 TTree *tree = (TTree*)f->Get("CHD/chdiag")
 tree->Print()

Exercise 3: Plotting from command line

You can use root to plot functions for example:

TF1 f1("f1","sin(x)/x",-10,10)
f1.Draw()

This command displays the function in a window which should pop up after you typed the above two lines.

You can also build histograms on the command line:

TF1 func("efunc","exp([0]+[1]*x)",0.,5.)
func.SetParameter(0,1)
func.SetParameter(1,-1)
TH1F h("h","example histogram",100,0.,5.)
for (int i=0;i<1000;i++) {h.Fill(efunc->GetRandom());}
h.Draw()

Exercise 3: Write a macro to produce histograms

This example is a standalone exercise to illustrate use of macros. We will go back to existing root files later.

If you have a number of lines which you were able to execute at the ROOT prompt, they can be turned into a ROOT macro by giving them a name which corresponds to the file name without extension, for example, if you create a file ExampleMacro.C which contains:

void ExampleMacro() {
       ....YOUR FUNCTION....
}

You can execute the macro by typing:

.x ExampleMacro.C

You can load the macro into the root session by typing:

.L ExampleMacro.C

Now we shall create a simple macro to build some histograms and play with styling options.

Here is an example macro, this is a basic plotting function which plots a function then builds a histogram based on that function:

First you will need to include the relevant ROOT libraries. Here we state explicitly which we use:

// 1-D histogram drawing options
#include "TCanvas.h"
#include "TPad.h"
#include "TFormula.h"
#include "TPaveLabel.h"
#include "TFile.h"
#include "TBenchmark.h"
#include "TLegend.h"

This ensures you have included all the relevent ROOT libraries to allow you to continue with the root macros.

Now create a function with the same name as your ".C" file:

void ExampleMacro()

Create a root TCanvas and TPads:

{
  //create a TCanvass
  TCanvas *c1 = new TCanvas("c1","Histogram Example",200,10,700,900);
  //Set up 2 TPads:
  auto pad1 = new TPad("pad1","The pad with the function",0.05,0.50,0.95,0.95);
  auto pad2 = new TPad("pad2","The pad with the histogram",0.05,0.05,0.95,0.45);
  pad1->Draw();
  pad2->Draw();
  pad1->cd();

Create a Benchmark to monitor your usages etc. (this is good for when you are trying to optmize your code's performance:

  gBenchmark->Start("fillrandom");

Create some math functions:

  //Any function can be used here but we have chosen this one
  auto form1 = new TFormula("form1","abs(sin(x)/x)");
  auto sqroot = new TF1("sqroot","x*gaus(0) + [3]*form1",0,10);
  sqroot->SetParameters(10,4,1,20);

Add styling options, you can play with thse later to see what other options you could use. These tend to depend on the purpose of your plots:

  //Some Styling:
  pad1->SetGridx();
  pad1->SetGridy();
  pad1->GetFrame()->SetBorderMode(-1);
  pad1->GetFrame()->SetBorderSize(5);

Set styling options for the maths:

  sqroot->SetLineColor(4);
  sqroot->SetLineWidth(6);
  //Draw the function in pad1
  sqroot->Draw();

Text labels can be placed on your plot to label it, try:

  auto lfunction = new TPaveLabel(5,39,9.8,46,"The sqroot function");
  lfunction->Draw();
  //Update the TCanvas to add this plot to that canvas.

To get the various drawn objects to appear on the canvas you must do:

  c1->Update();
  
  // Create a one dimensional histogram (one float per bin) and fill it following the distribution in function sqroot.
  //This will take place in the second TPad:
  pad2->cd();
  pad2->GetFrame()->SetBorderMode(-1);
  pad2->GetFrame()->SetBorderSize(5);
  auto h1f = new TH1F("h1f","Test random numbers",200,0,10);
  h1f->SetFillColor(45);
  h1f->FillRandom("sqroot",10000);
  h1f->Draw();
  c1->Update();

Now save the file, more details on this later:

  TFile myfile("fillrandom.root","RECREATE");
  form1->Write();
  sqroot->Write();
  h1f->Write();
  gBenchmark->Show("fillrandom");

End the macro with a bracket:

  }

Exercise 4: Styling

Root has a diverse range of histogram and graph styles. Many options are discussed here: https://root.cern.ch/doc/master/classTAttMarker.html

You can fill histograms with various colours via

 h1f->SetFillColor(...)

Similarly, you can change the line color using

  h1f->SetLineColor(...)

Have a play with the various options.

One important thing that is useful when plotting for analysis purposes it to know how to SetStat options. To remove the stats box:

   SetOptStat(0);

Things that can be displayed in the stats box are: Name, Entries, Mean, RMS, Underflow, Overflow, Integral, Skewness, Kurtosis. The various options are listed in here: https://root.cern.ch/doc/master/classTStyle.html#a0ae6f6044b6d7a32756d7e98bb210d6c.

Useful Tip:

Not covered here but for future reference.Sometimes it is useful to set a global style option. "gStyle" can help you streamline your code and gives your plots a consistent look. A good idea is to add/define a global style in a rootlogon.C file. This is an example:

  // rootlogon.C
  {// Add my own options here: 
     TStyle* myStyle = new TStyle("mcStyle","My Root Styles"); 
     myStyle->SetPalette(1,0); 
     myStyle->SetOptStat(0); 
     myStyle->SetOptTitle(0);
   ...... you can add whatever here.....
   }

Exercise 5: Axes, Legends and Labels

Axes of histograms have there own class "TAxis". You will want to label you axes. This can be done via:

 h1f->GetXaxis()->SetTitle("......")

Add this to your macro. Likewise for the y-axis. If you want to add a special character (like a symbol/greek letter) you can add using the "#" e.g.type "#theta."

You can switch to a log y-axis via:

TCanvas c1; 
c1.Setlogy();

In the case when you have multiple plots or fit lines on you histogram you may wish to add a legend. To do this use the TLegend class. You can add code like this to produce a legend and add it to your canvas:

  auto legend = new TLegend(0.1,0.7,0.48,0.9);//places the histogram
  legend->SetHeader("The Legend Title","C"); // option "C" allows to center the header
  legend->AddEntry(h1f,"Histogram filled with random numbers","f");
  legend->Draw("same");

The "same" option is useful when plotting more than one object on a canvas.

Exercise 6: Compiling Code

Code can be compiled a number of ways.

1) Generate an object library from the macro code, inside the interpreter type:

.L ExampleMacro.C+

Note the use of the "+" here. Once this operation is accomplished, the macro functions are in memory and you will be able to execute them by calling from inside the interpreter:

ExampleMacro()

We will not got through this part but it is useful to know:

2)Compile with a c++ compiler e.g. gcc. In order to make the code executable stand-alone, you must have a main function defined, for example:

int main() {
 ExampleMacro();
 return 0;
}

Can be added to the end of your macro. Now compile using:

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

or similar if you use another platform.

You can now execute the macro by typing:

./ExampleMacro

Exercise 7: Saving the histograms to a file (.root)

You will have seen in the example macro the final few lines:

  TFile myfile("fillrandom.root","RECREATE");
  form1->Write();
  sqroot->Write();
  h1f->Write();
  

This wrote the various functions and histograms to a .root file named "fill random.root". To save the histogram as a .root file. This will allow you to use it in other analyses in root, type:

MyHist->Write()

Any root object can be written to a .root file in this manner.

Exercise 8: Chaining multiple input files together

A TChain is a collection of files containing TTree objects. Use TChain::Add to add a new element to this chain, for example, if three files each contain a TTree called "Position" then you could write:

TChain chain("Position")
chain.Add("particle1.root");
chain.Add("particle2.root");
chain.Add("particle3.root");
chain.Draw("x"); 

the last line processes variable "x" from the "Position" TTree.

You can see an example of a file chain in the file /mu2e/app/users/sophie/AnalysisExample.C

Exercise 9: Basic event loop

Most analyses will require more processing than can be done with a simple Draw command. In these cases we will use an event loop, where we iterate over every entry in our TTree and can do arbitrary operations on all the branches for that entry. We will create a simple ROOT macro to try this.

  • Note that there are several ways to do this! In this exercise we will go over the old way, you can look up TTreeReader if you want to see a newer method. The end result is practically the same.
  1. First we will generate a very simple TTree. Run scripts/treeBuild.C
  2. >.x scripts/treeBuild.C
  3. This should create a file treeBuild.root in your directory. First lets quickly look at the script you ran. You will see that this script creates a tree with two branches: a single float num1, and an array of 10 floats num2. You can also check the root file created in the TBrowser to see if it looks like you expect.
  4. Lets create a script to check how many events in the TTree have num1 != any of the values in num2. We could try writing a Draw command and cut on "num1 != num2[0] && num1 != num2[1] &&..." but this way we can use a normal "for" loop in our script.
    • We will start our script by creating a TFile to read in our existing root file:
    TFile *fin = new TFile("treeBuild.root","READ");
    • Next we will create a variable pointing at the TTree, which from treeBuild.C we can see is named "T":
    TTree *t = (TTree*) fin->Get("T");
    • We next have to create variables to hold the values of each branch, in this case a float and an array of 10 floats
    • Finally, we point the tree at these variables for each branch using the command:
    t->SetBranchAddress("<name of branch>",&<name of variable>);
    • We can now loop over the entries in our tree. We can get the total number of entries with the function
    t->GetEntries() and for each entry, in order to load the values of num1 and num2 into our float variables we must call t->GetEntry(<entry number>)
  5. Try writing a script to print out the index of the entries where num1 != any num2. If you need help, you can look at solutions/treeRead.C

Exercise 10: MakeClass

The above method gets more difficult when your TTree branches are not simple integers and floats. For example, the branches in the TrkAna TTree are structs. If you know the exact makeup of the branches you want to look at, you can manually construct the objects and do the same procedure as in exercise 9 (see for example in https://mu2ewiki.fnal.gov/wiki/TrkAna_Tutorial_Session).

ROOT also provides a helper script that can produce boilerplate code for you to analyze complicated trees. Lets try analyzing a TrkAna TTree this way.


  1. The first step is making ROOT aware of the special classes and structs in your TTrees. In TrkAna some of the structs are converted explicitly into a list of basic integers and floats, but some are stored as objects. When you setup your Offline environment, you are automatically telling ROOT about these objects. For example, setup Offline and then try
  2. > mu2e::CrvHitInfoMC chiMC; You will see that ROOT knows about and can create objects of this type, defined in CRVAnalysis/inc/CrvHitInfoMC.hh.
  3. Once ROOT is aware of the types of objects in your TTree, open the file with the tree you are interested in and create a TTree pointer to it
  4. > TFile *f = new TFile("data/trkana.root"); > TTree *t = (TTree*) f->Get("TrkAnaPos/trkana");
  5. Now we call the MakeClass function of this TTree, and tell it the name of the files it will create
  6. > t->MakeClass("myMakeClass"); If you exit ROOT, you will see two new files, myMakeClass.C and myMakeClass.h. If you look at the header you will see it calls SetBranchAddress several times, and you can see the names of the variables it is attaching to all the branches. In the source file you will see a Loop function that performs an event loop like in the previous example.
  7. Lets modify the Loop() function to print out the momentum of the reconstructed track at the tracker entrance for every entry
  8. To use your MakeClass, load the script in ROOT, create an object of your myMakeClass, and call the Loop() function on it:
  9. > .L myMakeClass.C > myMakeClass mmc; > mmc.Loop();

Exercise 11: Pyroot

It is possible to use ROOT in python, which can make some analysis scripting easier, especially if you already are experienced in python over C++. Once you have setup Offline, you can start the python interpreter, and then just call

 import ROOT

The basic syntax for all your ROOT functions and classes are the same, so for example to create and plot a histogram:

 import ROOT
 h = ROOT.TH1F("h","h",100,0,1)
 for i in range(10):
   h.Fill(i)
 h.Draw()

Python also has some nice features for interacting with TTrees. For example, lets look at the TTree from exercise 9. Once you create your TTree variable, pyroot automatically figures out the branches and binds them for you so they are accessible as <tree name>.<variable name>, so for example you can do:

 import ROOT
 f = ROOT.TFile("treeBuild.root")
 t = f.Get("T")
 for i in range(10):
   t.GetEntry(i)
   print t.num1,t.num2[0],t.num2[1]

There is no need for any SetBranchAddress, etc. For complicated TTrees, if ROOT knows about the classes then pyroot will handle it automatically as well, without the need for any MakeClass etc. So for a TrkAna TTree:

 import ROOT
 f = ROOT.TFile("data/trkana.root")
 t = f.Get("TrkAnaPos/trkana")
 t.GetEntry(0)
 print t.crvinfomc

will give a "ROOT.vector<mu2e::CrvHitInfoMC>" object. Try getting some other branches of TrkAna. You will notice that some don't seem to work, like deent. Since the deent branch is set up as a list of leaves, to pyroot it looks like a series of branches with names like "deent/mom", and python doesn't allow you to do "t.deent/mom" as a member name. Instead, you can use the getattr method, so try this:

 getattr(t,"deent/mom")

You can use the same method to work directly with .art files with dataproducts. The Events TTree in these files will have branches with names like "mu2e::StepPointMCs_compressRecoMCs_tracker_recomcdigistrig". Additionally, the branch objects are actually art::wrapper objects, so to get something useful we can do:

 import ROOT
 f = ROOT.TFile("data/dataproducts.art")
 t = f.Get("Events")
 t.GetEntry(0)
 print getattr(t,"mu2e::StepPointMCs_compressRecoMCs_tracker_recomcdigistrig.obj")

which will give you the dataproduct class object.

Reference Materials