You are here

Accessing a TTree With a TSelector

What is a Selector?

To benefit from many ROOT features (e.g. PROOF and PROOF-lite) you should use a class deriving from the TSelector to analyze a TTree. The selector's code will be used by calling TTree::Process(). We provide a TSelector derived class in a simple source file. So please download the following source file and save it where you can find it: EventSelector.C. This is a skeleton of our selector.

N.B. If you are using ROOT via ssh, you have to download the file with wget from your remote directory. E.g.: wget http://root-mirror.github.io/training/intro/EventSelector.C

Preparation

Now quit and restart ROOT (to make sure everybody starts from the same point). Then open the file again:

root[0] TFile::Open("http://root.cern/files/introtutorials/eventdata.root");

Processing a Selector on a Tree

To apply this selector on the tree you do not call the selector directly; instead, the selector is passed to the tree which then runs it (see the documentation of TTree::Process()):

root[1] EventTree->Process("EventSelector.C")

As you can see, nothing happens because our selector is empty. So lets see what we can do with this selector.

Editing the Selector Skeleton

Open the downloaded selector file (EventSelector.C) in a text editor, e.g. with vi, emacs, wordpad, Eclipse... At the beginning of the file is the EventSelector class definition with the data members declaration etc. This is the place where to add new member definitions, e.g. TH1 *fMyHist;. Then come the function implementations.

Creating a Data Member

Lets start with something simple: count the number of events in our tree. To do so, we need to create a data member of type int or Int_t, to store (increment) the event number. Lets call it fNumberOfEvents. Add its declaration in the class definition, and initialize it to 0 in the constructor:

class EventSelector : public TSelector {
public :
   // Variables used to store the data
   Int_t       fNumberOfEvents; // Total number of events

   EventSelector(TTree * = 0): fNumberOfEvents(0) { }
   virtual ~EventSelector() { }

   virtual void    Init(TTree *tree);
   ...

The code : fNumberOfEvents(0) after the constructor is another way of initializing a data member. Then we have to count the total number of entries (events in our case) in the tree. This has to be done in TSelector::Process(Long64_t entry). We will print also the current entry number, to see what's happening:

Bool_t EventSelector::Process(Long64_t entry)
{
   // ...

   // print some information about the current entry
   printf("Processing Entry number %ld\n", entry);
   // increase the total number of entries
   ++fNumberOfEvents;

   return kTRUE;
}

And finally, at the end of the process, in TSelector::Terminate(), print the total number of entries:

void EventSelector::Terminate()
{
   // ...

   // print the result
   printf("\nTotal Number of Events: %d\n", fNumberOfEvents);
}

As you can see, the fNumberOfEvents member is accessed in Process() and Terminate(), hence the reason of making it a data member.

More Complex Task

We want to calculate the sum of all event sizes. For this we need a new data member of type int. Create it - like the other data members in the class declaration. Don't forget to initialize it to 0 in the constructor. You also have to declare the reader variables used to access the tree's data. At the end, the code in EventSelector.h should look like this:

class EventSelector : public TSelector {
public :
   Int_t fTotalDataSize;   // Sum of data size (in bytes) of all events

   // Variables used to access and store the data
   TTreeReader fReader;                       // The tree reader 
   TTreeReaderValue<Int_t> fCurrentEventSize; // Size of the current event

   EventSelector(TTree * = 0):
      fTotalDataSize(0),
      fCurrentEventSize(fReader, "fEventSize") { }
   virtual ~EventSelector() { }

   virtual void    Init(TTree *tree);
   ...

Connecting a Tree Branch with the Data Member

As already explained in the Using a Macro to Read a TTree tutorial, here again we have to "connect" the TTreeReader to the tree we want to read by calling TTreeReader::SetTree(). This has to be done in the TSelector::Init() method, as shown below:

void EventSelector::Init(TTree *tree)
{
   //...

   // Associate the TTreeReader with the tree we want to read
   fReader.SetTree(tree);
}

Loading TTree's Data

For the analysis example we need to get the events' size, which is accessible through the selector's data member fCurrentEventSize. But the TTreeReader first needs know which TTree entry is the current one, i.e. which entry Process() is called on. So instead of looping yourself using TTreeReader::Next() you set an explicit entry number using TTreeReader::SetLocalEntry(entry) in your Process() method, passing the TTree entry number from the argument of Process() to SetLocalEntry(). (We call SetLocalEntry() and not SetEntry() because Process() hands us the entry number of the current tree.)

Bool_t EventSelector::Process(Long64_t entry)
{
   //...

   // Tell the TTreeReader to get the data for
   // the entry number "entry" in the current tree:
   fReader.SetLocalEntry(entry);

   ...

Analyzing the TTree Entries

For each TTree entry, you will add the current event's size to the data member. This, too, happens in Process() - after you have set the current event from the TTree. After the TSelector has processed the tree it will thus contain the sum of all event sizes.

Bool_t EventSelector::Process(Long64_t entry)
{
   //...

   // Tell the TTree reader to get the data for
   // the entry number "entry" in the current tree:
   fReader.SetLocalEntry(entry);

   // We can still print some informations about the current event
   //printf("Size of Event %ld = %d Bytes\n", entry, *fCurrentEventSize);

   // compute the total size of all events; dereference the TTreeReaderValue
   // using '*' to get the value it refers to, just like an iterator.
   fTotalDataSize += *fCurrentEventSize;

   return kTRUE;
}

Accessing the Analysis Result

In your selector's Terminate() function, print the sum of all event sizes. This sum shows you the real power of a TTree: even though you can analyze large amounts of data (our example tree with 22MB is tiny!) ROOT needs just a few MB of your RAM, no matter how many events you analyze. Imagine what it would be like if you had to load all data into memory, e.g. using a simple vector!

void EventSelector::Terminate()
{
   // ...

   int sizeInMB = fTotalDataSize/1024/1024;
   printf("Total size of all events: %d MB\n", sizeInMB);
}

If, for any reason, you don't manage to get it to work, you can download a working selector file here: CountEventSelector.C

Here again, if you are using ROOT via ssh, you have to download the file with wget from your remote directory: wget http://root-mirror.github.io/training/intro/CountEventSelector.C

Note: You will have to use it that way:

EventTree->Process("CountEventSelector.C")