The concept of Tree has been introduced at Input/Output with Trees. An example of Tree is given in the documentation of the class TTree. A more complete example is given in Event and its I/O performance in ROOT Input/Output Benchmarks.
In this chapter, we will use variants of the program Event to illustrate the advantages of Trees compared to the object serialization method described in How to Write Objects.
It is very important to note that alternating between the methods described below implies NO changes in the object model.
The serialization method is perfectly appropriate when it does not make sense to access sub-objects components. For example, you write an histogram object and you read it as an histogram. It is unlikely that you want to read back only one part of an histogram.
This case does not use a Tree at all. Once the event is filled, it is serialized (via event->Write in one buffer and written on the file using a key (in this case the string "event" followed by the event number).
This technique is, may be, the one to use in the context of a data acquisition system where you want to be as fast as possible in producing the output file. It is likely that the reconstruction program will need access to the totality of the event information. One variant of this scheme could be to create several keys per event, one key for each of the main detector components.
A system with several keys per event may be interesting during the DST(Data Summary Tape) analysis if you really insist in remaining very conventional. (see also picture below where different levels of event granularity are suggested for each application domain).
However, based on experience and results of benchmarking with different configurations, we strongly encourage users to use Trees instead.
//________________________The Serialization method_________________________ main(int argc, char **argv) { TFile hfile("Event.root","RECREATE"); // Fill event, header and tracks with some random numbers Event *event; char keyname[16]; for (Int_t ev = 0; ev < 1000; ev++) { Float_t sigmat, sigmas; gRandom->Rannor(sigmat,sigmas); Int_t ntrack = Int_t(600 +5*sigmat); Float_t random = gRandom->Rndm(1); event = new Event(); // Create one event event->SetHeader(ev, 200, 960312, random); event->SetNseg(Int_t(10*ntrack+20*sigmas)); event->SetNvertex(1); event->SetFlag(UInt_t(random+0.5)); event->SetTemperature(random+20.); // Create and Fill the Track objects for (Int_t t = 0; t < ntrack; t++) event->AddTrack(random); // Encode key name (see TKey) using the event serial number sprintf(keyname,"event%d",ev); // Serialize event into one single buffer event->Write(keyname); delete event; } hfile.Close(); return 0; } //______________________________________________________________________________
The program below is slightly modified compared to the program above. It creates a Tree with one single branch (variable split is set to 0).
When the program will terminate, only one key will be written to the file, corresponding to the Tree header. The tree->Fill statement generates several buffers (baskets) dumped to the file when they are full. The variable bsize specifies the initial basket size. If this size is not enough to accomodate at least one event, it will be resized accordingly.
This method offers only slight advantages compared to the first method.
//_______________________A Tree with one single branch_____________________ main(int argc, char **argv) { TFile hfile("Event.root","RECREATE"); // Create a ROOT Tree TTree *tree = new TTree("T","An example of a ROOT tree"); Int_t split = 0; Int_t bsize = 64000; Event *event = 0; // Create one branch. If splitlevel is set, event is a superbranch // creating a sub branch for each data member of the Event object. tree->Branch("event", "Event", &event, bsize,split); // Fill event, header and tracks with some random numbers char keyname[16]; for (Int_t ev = 0; ev < 1000; ev++) { Float_t sigmat, sigmas; gRandom->Rannor(sigmat,sigmas); Int_t ntrack = Int_t(600 +5*sigmat); Float_t random = gRandom->Rndm(1); event = new Event(); // Create one event event->SetHeader(ev, 200, 960312, random); event->SetNseg(Int_t(10*ntrack+20*sigmas)); event->SetNvertex(1); event->SetFlag(UInt_t(random+0.5)); event->SetTemperature(random+20.); // Create and Fill the Track objects for (Int_t t = 0; t < ntrack; t++) event->AddTrack(random); // Serialize single branch of event into one single basket tree->Fill(); delete event; } hfile.Close(); return 0; } //______________________________________________________________________________
If you run the program above and set split = 1, then the statement:
tree->Branch("event", "Event", &event, bsize,split);will generate several branches, one for each data member of the Event class. The following rules are applied:
This method provides the highest possible level of granularity. You continue obviously to have direct access to any single event. In addition, you can now read selectively a subset of all the branches in your analysis program. This is the subject of the next chapter.
This method requires more memory, because one buffer must be created for each branch. In case, you have memory problems, you should decrease the buffer size (parameter bsize in the above program.
If the file compression is activated (default), ROOT will compress the branch buffers depending on the level of file compression (set by TFile:SetCompressionLevel).
Do not forget, if you do so, to set the addresses for every created branch in the filling loop, in case the addresses change.