GAUDI User Guide

Chapter 10
N-tuple facilities

10.1  Overview

User data - so called n-tuples - are very similar to event data. Of course, the scope may be different: a row of an n-tuple may correspond to a track, an event or complete runs. Nevertheless, user data must be accessible by interactive tools such as PAW or Root.

Gaudi n-tuples allow to freely format structures. Later during the running phase of the program data are accumulated and written to disk.

The transient image of an n-tuple is stored in a Gaudi data store which is connected to the n-tuple service. Its purpose is to store user created objects that have a lifetime of more than a single event.

As with the other data stores, all access to data is via a service interface. In this case it is via the INTupleSvc interface which extends the IDataProviderSvc interface. In addition the interface to the n-tuple service provides methods for creating n-tuples, saving the current row of an n-tuple or retrieving n-tuples from a file. The n-tuples are derived from DataObject in order to be storable, and are stored in the same type of tree structure as the event data. This inheritance allows to load and locate n-tuples on the store with the same smart pointer mechanism as is availible for event data items (c.f. Chapter 6 ).

10.2  Access to the N-tuple Service from an Algorithm.

The Algorithm base class defines a member function

 

 
INTupleSvc* ntupleService()

which returns a pointer to the INTupleSvc interface.

The n-tuple service provides methods for the creation and manipulation of n-tuples and the location of n-tuples within the database. The database reflects the tree structure of the n-tuple data store. Please refer to the online documentation for a description of this interface.

The top level directory of the n-tuple data store is called "/NTUPLES". The next directory layer is connected to the different output streams: e.g. "/NTuples/FILE1", where FILE1 is the logical name of the requested output file for a given stream. There can be several output streams connected to the service. In case of Persistency using HBOOK "FILE1" corresponds to the top level RZ Directory of the file (...the name given to HROPEN). From then on the tree structure is reflected with normal RZ directories.

10.3  Using the n-tuple service.

When defining an n-tuple the following steps must be performed:

In the following an attempt is made to explain the different steps. Please note that the n-tuple number must be unique and, in particular, that it must be different from any histogram number. This is a limitation imposed by the fact that Gaudi is currently using HBOOK for histogram and n-tuple persistency.

10.3.1  Defining n-tuple tags

When creating an n-tuple it is necessary to first define the tags to be filled in the n-tuple. Typically the tags belong to the filling algorithm and hence should be provided in the Algorithm's header file. Currently the following data types are supported: bool, long, float and double. double types (Fortran REAL*8) need special attention: the n-tuple structure must be defined in a way that aligns double types to 8 byte boundaries. In addition PAW cannot understand double types. The code fragment below illustrates how to define n-tuple items:

 

Listing 26 Definition of n-tuple tags from the Ntuples.WriteAlg.h example header file.

1: NTuple::Item m_ntrk; // A scalar item (number)
2: NTuple::Array m_flag; // Vector items
3: NTuple::Array m_index;
4: NTuple::Array m_px, m_py, m_pz;
5: NTuple::Matrix m_hits; // Two dimensional tag

10.3.2  Booking and Declaring Tags to the N-tuple

When booking the n-tuple, the previously defined tags must be declared to the the n-tuple. Before booking, the proper output stream (file) must be accessed and the target directory defined.

Listing 27 Creation of an n-tuple in a specified directory and file.

1: // Access the output file
2: NTupleFilePtr file1(ntupleService(), "/NTUPLES/FILE1");
3: if ( file1 ) {
4: if ( !ntupleService()->createDirectory("/NTUPLES/FILE1/MC") ) {
5: log << MSG::ERROR << "Cannot create directory" << endreq;
6: }
7: else {
8: // First: A column wise N tuple
9: NTuplePtr nt1(ntupleService(), "/NTUPLES/FILE1/MC/1");
10: if ( !nt ) { // Check if already booked
11: nt = ntupleService()->book (col, 1, CLID_ColumnWiseTuple, "Hello World");
12: if ( nt ) {
13: // Add an index column
14: status = nt->addItem ("Ntrack", m_ntrk, 0, 5000 );
15: // Add a variable size column of type float (length=length of index col)
16: status = nt->addItem ("px", m_ntrk, m_px);
17: status = nt->addItem ("py", m_ntrk, m_py);
18: status = nt->addItem ("pz", m_ntrk, m_pz);
19: // Another one, but this time of type bool
20: status = nt->addItem ("flg",m_ntrk, m_flag);
21: // Another one, type integer, numerical numbers must be within [0, 5000]
22: status = nt->addItem ("idx",m_ntrk, m_index, 0, 5000 );
23: // Add 2-dim column: [0:m_ntrk][0:2]; numerical numbers within [0, 8]
24: status = nt->addItem ("hit",m_ntrk, m_hits, 2, 0, 8 );
25: }
26: else { // did not manage to book the N tuple....
27: return StatusCode::FAILURE;
28: }
29: }
30: }

Tags which are not declared to the n-tuple are invalid and will cause an access violation at run-time. Later these tags can be used like ordinary numbers, arrays or matrices. However, there is one caveat: double numbers must be 8-byte aligned, otherwise HBOOK complains.

10.3.3  Filling the N-tuple

The tags should be usable just like normal data items, where

There is no implicit bounds checking possible without a rather big overhead at run-time. Hence it is up to the user to ensure the arrays do not overflow.

When all entries are filled, the row must be committed, ie. the record of the n-tuple must be written.

Listing 28 Filling an n-tuple.

31: m_ntrk = 0;
32: for( MCParticleVector::iterator i = mctracks->begin(); i != mctracks->end(); i++ ) {
33: const HepLorentzVector& mom4 = (*i)->fourMomentum();
34: m_px[m_ntrk] = mom4.px();
35: m_py[m_ntrk] = mom4.py();
36: m_pz[m_ntrk] = mom4.pz();
37: m_index[m_ntrk] = cnt;
38: m_flag[m_ntrk] = (m_ntrk%2 == 0) ? true : false;
39: m_hits[m_ntrk][0] = 0;
40: m_hits[m_ntrk][1] = 1;
41: m_ntrk++;
42: // Make sure the array(s) do not overflow.
43: if ( m_ntrk > m_ntrk->range().distance() ) break;
44: }
45: // Commit N tuple row.
46: status = ntupleService()->writeRecord("/NTUPLES/FILE1/MC/1");
47: if ( !status.isSuccess() ) {
48: log << MSG::ERROR << "Cannot fill id 1" << endreq;
49: }
50: }

10.3.4  N-tuple Persistency

A conversion service exists which can convert NTuple objects into a form suitable for storage in a standard HBOOK file. In order to use this facility it is necessary to add the following line in the job options file:

 
NTupleSvc.Output     = { "FILE1#<tuples.hbook>" }; 
//  Perstency type of the N tuple service: 6=HBOOK 
NTupleSvc.Type       = 6;

where <tuples.hbook> should be replaced by the name of the file to which you wish to write the n-tuple. FILE1 one is the logical name of the output file - it could be any other string (Caveat: HBOOK only accepts directory names with less than 8 characters!).

The handling of row wise n-tuples does not differ. However, only individual items (class NTuple::Item) can be filled, no arrays and no matrices. Since the persistent representation of row wise n-tuples is done by floats only, the first row of each row wise n-tuple contains the type information - when looking at a row wise n-tuple with PAW make sure to start at the second event!