SLAC PEP-II
BABAR
SLAC<->RAL
Babar logo CM2 logo
HEPIC E,S & H Databases PDG HEP preprints
Organization Detector Computing Physics Documentation
Personnel Glossary Sitemap Search Hypernews

User Defined Data

Last Updated: 17 August 2004
Guglielmo De Nardo, SLAC


Overview

The Computing Model 2 Event Store provides, among the others, an user component, in which the Analysis Working Groups or the single analysts are allowed to store high level physics quantities, specific to their analyses, and for this reason not available among the basic variables accessible from the BtaCandidate interface. These user defined variables may be related to a specific composite BtaCandidate or to the reconstructed event as a whole. Familiar examples of the former class are the mES and the deltaE of a fully reconstructed B candidate; examples of the second class may be event classification bit-masks or inclusive quantities like a Fischer discriminant or event shape variables under specific track and neutral selections. The creative analyst may easily find variables definitions appropriate to her analysis.

Basic Concepts and C++ interface

The fundamental ingredients of the User Data are generic type variables, blocks aggregating event-level variables, and blocks aggregating candidate-level variables.

In order to provide a generic and uniform interface, the user variables are implemented as a C++ templated class. Therefore, a variable of type T is of class UsrVariable<T>, where T is the template parameter. To give a concrete example, an integer variable "multiplicity" and a floating point variable "deltaE" would be declared as

UsrVariable<int> multi("multiplicity");
UsrVariable<float> dE("deltaE");

Note that the process of creating a variable requires that a name is assigned to the variable.

The UsrVariable object has the same interface as any regular C++ variable of C++ built-in type ( like regular int and float types ), supporting all the usual arithmetic C++ built-in operations. The only restriction is, of course, that the operations have to make sense for the type T to which the template is specialized. For example a value che be assigned with the " = " operator

dE = 0.1 ;
two variables may be summed
UsrVariable<int> multiCh("multi_charged");
UsrVariable<int> multiNeut("multi_neutral");
multi = multiCh + multiNeut ;
and so on.

Note: UsrVariable<bool> is not allowed.
Try using UsrVariable<int>, or UsrVariable<unsigned char> instead.

The simplest way to keep together a set of UsrVariables, even of heterogeneous type, is to group them in a UsrEventBlock, which is used to contain in the same place event level variables, and to provide the interface to access them.

The interface to read or write a variable in the block is very simple. For example, having declared an object myEventBlock of type EventBlock, read and write operations are performed by means of a get() and put() method of the class UsrEventBlock:

myEventBlock.get( multi );
will fill the user variable multi with the value currently stored in the block for the variable "multiplicity" ( the name of the user variable multi was initialized to "multiplicity"), while
myEventBlock.put( multi ) 

In order to put a block in the Event or to fetch from the Event an already existing block, two static functions are used, as in the following lines

IfdStrKey blockName("Name_of_the_Block");
UsrIfd<UsrEventBlock>::put( event, &myEventBlock, blockName )

UsrEventBlock * myEventBlock =
UsrIfd<UsrEventBlock>::get( event, blockName);

Note that a name ( i.e. a key ) has to be given to the block when putting it into the Event, and with the same the block may be retrieved.

The interface for candidate level user variables is the same taking into account that the variables are attached to candidates. Thus, having declared a user candidate block myCandBlock

UsrCandBlock myCandBlock;
The following two statement associate to the BtaCandidate c the value of the variable dE, or fill dE with value of the variable "deltaE" associated to the BtaCandidate c
myCandBlock.put( c, dE );
myCandBlock.get( c, dE );
IfdStrKey blockName("Name_of_the_Block");
UsrIfd<UsrCandBlock>::put( event, &myCandBlock, blockName )

UsrCandBlock * myEventBlock =
UsrIfd<UsrCandBlock>::get( event, blockName);

How to write User Data in skim production ( SkimMiniApp )

What an user needs to do for a new skim to be made in production ( Framework modules and configuration files) is described elsewhere. Assuming that the skim has been already defined and the necessary configurations has been performed to write the list of composite candidates in the skim, here follow the recommended procedure to write the User data component of the skim.

Step 0

Let's say the name of the skim is XYZ. You need to add a framework module, whose name is UsrWriteXYZ in the UsrTools package.

Step 1.

In UsrWriteXYZ.hh include the header files necessary to let the compiler know about the interfaces of the user defined variables (UsrVariable class), the data block containing the variables relevant to the entire event (UsrEventBlock class) and the data block containing the variables associated to BtaCandidates.
For the user commodity the necessary include files have been grouped in a single header file. Therefore add

#include "UsrData/UsrData.hh"

Step 2.

Add to the module's private data members the user variables.
If, for example, you wish to add an integer variable multiplicity (an event level variable), and two float variables deltaE and M_ES ( BtaCandidate level variables), add

class UsrWriteXYZ : public AppModule
{
...
private:
...
    UsrVariable<int> multiplicity;
    UsrVariable<float> deltaE,  m_ES;
};

Note: UsrVariable<bool> is not allowed.

Step 3.
Add the blocks that will contain the user data among the data members in the private section of the UsrWriteXYZ class.
Use UsrEventBlock objects to hold UsrVariables associated to the entire event, and UsrCandBlock objects to hold UsrVariables associated to BtaCandidates.

class UsrWriteXYZ : public AppModule
{
...
private:
...
   UsrCandBlock   myCandBlock;
   UsrEventBlock  myEventBlock;
};

Step 4
Set the names of the variables and assign the variables to User Data blocks in the UsrWriteXYZ constructor:

UsrWriteXYZ::UsrWriteXYZ( const char * const name, const char * const description ) :
// initialization section
...
// setting the names
multiplicity("multiplicity"),
deltaE("deltaE").
m_ES("mES")
{
    // body of the constructor
    // adding the multiplicity variable to the Event Data
    myEventBlock.addVariable( multiplicity );
   // adding the m_ES and deltaE variables to the Candidate Data
    myCandBlock.addVariable( deltaE );
    myCandBlock.addVariable( m_ES );
}

Step 5
For each event the User Data Blocks must be put in the AbsEvent.
Therefore add at the beginning of the UsrWriteXYZ event() method

UsrWriteXYZ::event( AbsEvent* anEvent )
{
    ...
    // putting the Event Data in the AbsEvent , giving the name "myEventData"
    UsrIfd<UsrEventBlock>::put( anEvent, &myEventBlock, IfdStrKey("myEventData") );
    // putting the Candidate Data in the AbsEvent , giving the name "myCandData"
    UsrIfd<UsrCandBlock>::put( anEvent, &myCandBlock, IfdStrKey("myCandData") );
}

Note that
1. This also defines the name of the block.
2. The interface with the AbsEvent is mediated by special static functions defined in the class UsrIfd. So not just the usual Ifd<T> .

Step 6
 In the event() method values are assigned to the variables and the variables are put in the block.

 In the case of Event Data,

UsrWriteXYZ::event( AbsEvent* anEvent )
{
    // set the UsrVariable multiplicity to some_value
   multiplicity = the_value;
    // add the data in the EventBlock
    bool status = eventBlock.put( multiplicity ) ;
}

If the operation fails, for example if the user tries to put a variable in the wrong block, the put method returns false.
If everything is fine the return value is true.

 In the case of Candidate data:

UsrWriteXYZ::event( AbsEvent* anEvent )
{
         ...
    // within a loop over candidates, which will be persisted
    const BtaCandidate  * candidate;
    while ( candidate = an_iterator_over_a_container ) {
        ...
    // set the UsrVariables m_ES  to for the candidate
    m_ES = the_value_of_mES_for_this_candidate   ;
    // add the data in the EventBlock
   bool status;
    status = candBlock.put( *c, m_ES ) ;
    if ( ! status ) {  // error! }

    deltaE = the_value_of_deltaE_for_this_candidate   ;
    // add the data in the EventBlock
    bool status;
    status = candBlock.put( *c, m_ES ) ;
    if ( ! status ) {  // error! }
    }
}

Also in the UsrCandBlock case, the return value of the put() method is true if the operation was successful or false if there were an error condition.

At this point the module fills event by event one or more User Data blocks, that can be fetched by other modules down in the sequence for further operations, if needed.

Step 7

Having made the module to write the user data, you need now to write a tcl file to configure it. The name of the file must be UsrWriteXYZ.tcl, where XYZ is the name of the skim, and must be put in the UsrTools package. It contains the necessary configuration to write User Data in the skim.
Its basic function is to declare the User Data blocks to store in the skim.

In order to add an User Data block of event level variables, whose name is "XYZEventBlock", to the skim XYZ, add the following command
    writeEventUsrData XYZ XYZEventBlock

In order to add an User Data block of candidate level variables, whose name is "XYZCandBlock", to the skim XYZ, add the following command
    writeCandUsrData XYZ XYZCandBlock

Then add the line
    lappend modulesToAdd UsrWriteXYZ

Step 8

Add the UsrWriteXYZ module to the User Data write sequence. Look in the UsrTools package for the UsrWriteSequence.cc file. Add to the include files your module header file

    #include "UsrTools/UsrWriteXYZ.hh"

add the module to the sequence like this:
    forWho->add(  new UsrWriteXYZ ( "UsrWriteXYZ","User Data for XYZ skim" );

Step 9

Enable writing the User Data for your skim. Look in FilterTools package for the skim configuration file XYZPath.tcl Insert the following lines
    global writeUsrDataForSkim
    set writeUsrDataForSkim 1

How to make an user analysis module read User Data

This section describes the steps that you may follow in order to add to your analysis module ( AnalysisModule in the following ) read back User Data previously stored in a skim.

Step 1.
In AnalysisModule.hh, include the header files necessary to let the compiler know about the interfaces of the user defined variables (UsrVariable class), the data block containing the variables relevant to the entire event (UsrEventBlock class) and the data block containing the variables associated to BtaCandidates.
For the user commodity the necessary include files have been grouped in a single header file.
#include "UsrData/UsrData.hh"

Step 2.
Add the user variables you are intersted in among the module's private data members.
If, for example, the variables which have to be read are an integer variable multiplicity , and two float variables deltaE and M_ES of  B meson candidates,  the following code has to be added in the private section of AnalysisModule

class UsrWriteXYZ : public AppModule
{
...
private:
...
    UsrVariable<int> multiplicity;
    UsrVariable<float> deltaE,  m_ES;
};

Step 3
In AnalysisModule.cc set the variable's names in the AnalysisModule constructor

AnalysisModule::AnalysisModule(const char * const name, const char *const description ) :
// initializationsection
...
// setting variables' names
multiplicity("multiplicity"),
deltaE("deltaE").
m_ES("mES")
{
   // body of the constructor
}

Step 4
For each event you have to fetch the Candidate and Event Block the AbsEvent, known the name of the Block (i.e. the name which has been given to the user data block when this skim was written in the skim).
At the beginning of the AnalysisModule event() method

AnalysisModule::event(AbsEvent* anEvent )
{
   ...
   // getting the Event Data from the AbsEvent , knowing that the block name is "myEventData"
   UsrEventBlock * myEventBlock =
   UsrIfd<UsrEventBlock>::get( anEvent, IfdStrKey("myEventData"));
   // Data from the AbsEvent , knowing that block name is "myCandData"
   UsrcandBlock * myCandBlock =
   UsrIfd<UsrCandBlock>::get( anEvent, IfdStrKey("myCandData"));
   ...
}

Step 5
In the event() method of the module, fetch the user variable values from the user data block.

In the case of Event Data,

AnalysisModule::event(AbsEvent* anEvent )
{
    ...
    // set the UsrVariable multiplicity to the current value for this Event
    bool status = myEventBlock->get( multiplicity );
    if ( ! status ) {} // error !
    ...
}

If the operation fails, for example if the user tries to get a non existent variable from the block, the get() method returns the bool value false.
If everything is fine the return value is true.

 In the case of Candidate data:

AnalysisModule::event( AbsEvent* anEvent )
{
         ...
    // loop over candidates
    const BtaCandidate * candidate;
    while ( candidate = an_iterator_over_a_container ) {
        ...
    bool status;
    // get the UsrVariables m_ES and deltaE for this candidate
    status = myCandBlock->get( *candidate, m_ES ) ;
    if ( ! status ) {}   // error!

    status = myCandBlock->get( *candidate, m_ES ) ;
    if ( ! status ) {}  // error!
    }
    ...
}

Also in the case UsrCandBlock a status condition ( true = OK , false = error ) is returned by the get() method.

Step 6
Configure your analysis tcl file adding the following two lines after the standard Mini set-up (i.e. after sourceFoundFile btaMini.tcl )

    sourceFoundFile UsrTools/UsrDataProcs.tcl
    enableReadUsrData

which enable reading the User Data from the input kanga file to read event level User Data block, whose name is "myEventData"
    readEventUsrData myEventData

To read a candidate level User Data block, whose name is "myCandData" add
    readCandUsrData  myCandData

Step 6 is the way it can be done right now. It can be reduced to do the same in one line ( I'll do it soon ).

FAQ

Notes