|
|
|
||||||||||||||||||||||||||
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);
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
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
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 ).