SLAC PEP-II
BABAR
SLAC<->RAL
Babar logo
HEPIC E,S & H Databases PDG HEP preprints
Organization Detector Computing Physics Documentation
Personnel Glossary Sitemap Search Hypernews
Workbook Home Introduction Account Setup QuickTour Packages Modules Unwrap page!
Event Information Tcl Commands Editing Compile and Link Run the Job Debugging
Check this page with the
W3C Validator
(More checks...)
Parameters Tcl Files Find Data Batch Analysis ROOT Tutorial

Editing module code

Contents:

The aim of this and the following section is to introduce you to some of the most common C++ code methods in BaBar modules. With just a few basic syntax snippets, you will be well on your way to being able to edit a BaBar module for your analysis - or even to write your own module.


Prerequisite: C++

BaBar code is written in C++, and modules are implemented as a C++ class. So editing module code means working with C++.

If you have not yet learned C++, now is the time to do it. You do not need to become an expert programmer, but you will should try to become familiar with how C++ variables, functions and classes work.

Here are some useful references for beginners:

There are also many many C++ books available from libraries and bookstores. Your colleagues probably have some lying around.


The basic module

A module named MyModule consists of two files: the header file MyModule.hh, and the implementation file MyModule.cc. The header file contains the declarations of all the module's member objects and member functions. That is, it provides the compiler with a list of module's member functions and objects. The implementation file contains the definitions of the member functions. That is, it tells the compiler what the module's functions do.

The following are the skeleton .hh and .cc files for a module. This is the absolute minimum code that you need in a module. Right now, this module does precisely nothing. To make a useful module, you need to add code into the skeleton framework.

Skeleton header file: MyModule.hh

#ifndef MYMODULE_HH
#define MYMODULE_HH

#include "Framework/AppModule.hh"
#include "AbsEvent/AbsEvent.hh"

class MyModule : public AppModule {

public:

  // Constructors
  MyModule( const char* const theName, const char* const theDescription );
  // Destructor
  virtual ~MyModule( );

  // Operations

  virtual AppResult  beginJob( AbsEvent* anEvent );
  virtual AppResult  event( AbsEvent* anEvent );
  virtual AppResult  endJob  ( AbsEvent* anEvent );

protected:

private:

};

#endif

Skeleton implementation file: MyModule.cc

#include "BaBar/BaBar.hh"
#include "MyPackage/MyModule.hh"

// Constructors

MyModule::MyModule( const char* const theName,
                  const char* const theDescription )
  : AppModule( theName, theDescription )
{
}

// Destructor
MyModule::~MyModule( )
{
}

// Operations

AppResult MyModule::beginJob( AbsEvent* anEvent )
{
  return AppResult::OK;
}

AppResult MyModule::endJob( AbsEvent* anEvent )
{
  return AppResult::OK;
}

AppResult MyModule::event( AbsEvent* anEvent )
{
  return AppResult::OK;
}

There are 5 main locations where you will add code:

In the .hh file:

In the .cc file:

Histograms and Ntuples: the HepTuple Package

The BaBar environment package for histograms and ntuples is the HepTuple package.
The histogram class is HepHistogram, and the ntuple class is HepTuple.

To create and use either a histogram or an ntuple from within the BaBar framework, you also need a HepTupleManager.


Histograms

The analysis module QExample class (from the sample analysis job) books a histogram of the number of tracks per event. If an analysis module is to use classes from the HepTuple histogramming package, its header file must #include the Histogram header file. In QExample.hh, you have:

#include "HepTuple/Histogram.h"
An analysis module that will book a histogram needs to have a (preferably private) histogram data member.
     HepHistogram* _numTracksHisto;

The next step is to create a histogram manager. First, the analysis module's .cc file needs to #include the defining header files:

   #include "AbsEnv/AbsEnv.hh"
   #include "GenEnv/GenEnv.hh"
   #include "HepTuple/TupleManager.h"
From within the beginJob function a histogram manager needs to be declared via:
   HepTupleManager* manager = gblEnv->getGen()->ntupleManager();
and then used to book a histogram, which initializes the histogram data member of the class.
  _numTrkHisto = manager->histogram("Tracks per Event",  20, 0., 20. );
The histogram is declared with four arguments:
  1. the title ("Tracks per Event"),
  2. the number of bins (20),
  3. the lower limit of the histogram (0.), and
  4. the upper limit of the histogram (20.).
This completes the declaration and the definition of the histogram. The histogram can be filled for each event (from within the event function of the analysis module) with a call to the accumulate member function of the histogram object.
 _numTrkHisto->accumulate( trkList->length() );

Ntuples

Managing ntuples is very similar to managing histograms. Once again, the first step is to #include the required header in the module's .hh file,

#include "HepTuple/Tuple.h" in the header file.
and declare a private ntuple pointer:
HepTuple* _ntuple;

In the .cc file, #include the header files needed for the HepTupleManager:

#include "HepTuple/TupleManager.h"
#include "AbsEnv/AbsEnv.hh"
#include "GenEnv/GenEnv.hh"

In beginJob(), create a HepTuple manager:

HepTupleManager* manager = gblEnv->getGen()->ntupleManager();

In beginJob(), initialize your ntuple:

_ntuple = manager->ntuple("MyTuple");

This ntuple is declared with only one argument: the name of the ntuple. This completes the declaration and the definition of the ntuple. The ntuple can be filled for each event (from within the event function of the analysis module) with a call to the column function:

_ntuple->column("Benergy", Benergy, -99.0);
The column function has three arguments:
  1. The name of the variable ("Benergy").
  2. The value of the variable. (Benergy)
  3. A default value (-99.0)

The variable can be an integer, a double, a float, or a boolean. There are also several other supported types. In this example, Benergy is a double, the B meson energy that was (presumably) calculated or determined earlier in the event() function.

If in some event the column function is not called for some reason (but has been called in previous events), the ntuple will be filled with the default value for that event. So it is a good idea to set it to a non-physical value, so that this case easy to identify. Here, the default is set to -99.0, an very unphysical energy.

The name of the variable is the name that will be used in the ntuple to refer to the variable. You should always give your variables names that make sense - like Benergy for B meson energy, pleptonCM for lepton momentum in the center-of-mass frame, and so on.

The last but very important step is to dump all of the event's data into the ntuple. Add the line:

_ntuple->dumpData();
somewhere after your last call to the ntuple's column function, but before the end of the module's event function.
Don't forget this step, or else your ntuple will not be written!

Getting HepALists of BtaCandidates

As described in the Event Information section of the Workbook, one of the most common data structures is a list of particle candidates. The list could be a standard list from the Event Store, or a run-time list created at run-time. Either way, one of the most common tasks that a module needs to perform is to access and use a particle candidate list.

BaBar's C++ class for lists is called HepAList. You can have a HepAList of lots of types of pointers, but the most important type of HepAList is the HepAList of BtaCandidate*s (pointers to BtaCandidates):

HepAList<BtaCandidate>*

To begin, #include the required header files in the module's .cc file:

#include "Beta/BtaCandidate.h"
#include "CLHEP/Alist/AList.h"
#include "ProxyDict/Ifd.hh"

Like all event information, the Event Store's HepALists of BtaCandidates must be accessed from the module's event function. The syntax is:

  QExample::event(AbsEvent* anEvent) {
  ...
  HepAList<BtaCandidate> *trkList  =
    Ifd<HepAList< BtaCandidate > >::get(anEvent, "ChargedTracks");

The Ifd<T>::get function requires two arguments:

The name of the list must be one of the available lists in the Event Store. (In other words, this is not a name that you assign - it is one that you use to access the list.) To see the particle candidate lists available, see the Table of BtaCandidate lists from the Workbook's Event Information section.


Looping over HepALists of BtaCandidates

The most common way to use a HepAList of BtaCandidates is to loop over it. That is, you examine each BtaCandidate on the HepAlist, one at a time.

To loop over a HepAList, you need another C++ object, called a HepAListIterator.

The first step is to #include the required header files in the module's .cc file:

#include "CLHEP/Alist/AIterator.h"

The syntax to create the HepAListIterator is:

   HepAListIterator<BtaCandidate> iterTrk(*trkList);

Here, iterTrk is the name you assign to your iterator, and trkList is the (pointer to) the HepAList of BtaCandidates. (By the way, if trkList were just a HepAList, rather than a pointer to a HepAList, then you would not need the dereferencing asterix * above.)

To loop over your HepAList<BtaCandidate>*, you need a "placeholder" BtaCandidate*. This BtaCandidate* will take on the identity of each BtaCandidate* in the list, one at a time.

   BtaCandidate* trk(0);
Finally, you are ready to loop over the HepAList:
   int nTrkHard(0);
   while ( trk = iterTrk()) {
   double etrk = trk->energy();
   if (etrk > 1.0) nTrkHard++;
   }
Within the while loop, trk is the current value of the BtaCandidate*. You can treat it like any other BtaCandidate, which means that you can access its energy, momentum, charge, and other particle information. In the above code snippet, you get the energy of each track, and count the number of tracks with energy greater than 1.0 GeV. For more information about how to use BtaCandidates, see the Workbook's BtaCandidates page.

Adding a module to the Framework

There are 2 steps to add a module into the Framework:

Add the module in AppUserBuild

Every analysis package comes with a file called AppUserBuild.cc. The AppUserBuild object gives the Framework its list of modules. So to put your module on this list, you need to add it in the AppUserBuild file.

Unfortunately, the BetaMiniUser package (which is the one we are using) has its own peculiar AppUserBuild method, and this method is different from the method used for all other BaBar packages. So first I will show you the syntax used in most packages, and then I will show you how to add your module in BetaMiniUser.

For most packages

In most packages, there are two lines to add in AppUserBuild.cc. First, you need to #include the module's header file:

#include "ThePackage/MyModule.hh"
where "ThePackage" is whatever package you put your module in.

Next, you need to add the module in the AppUserBuild constructor:

  add( new MyModule( "MyModule", "Skeleton module" ) );
The add function takes two arguments: The description of the module is the description you will see when you use the path list or module list commands in a framework session.

For BetaMiniUser only

In BetaMiniUser, AppUserBuild's job has been divided among four AppUserBuild files:

The file that you need to modify to add your module to the Framework is AppUserBuildBase.cc. First, #include your module's header file:

#include "BetaMiniUser/MyModule.hh"

Then, in the AppUserBuildBase constructor, add the module:

void AppUserBuildBase(AppUserBuild* theBuild, AppFramework* theFramework)
{
   ...
  theBuild->add(new MyModule("MyModule", "Skeleton module"));
Again, the add function takes two arguments:

Append the module to the path

Now you have created your new module and added it to the Framework's list of modules. But you must also add it to the path, or else the Framework will ignore it.

To add your module to the analysis path, you need to modify one of the package's tcl files. In the tcl file, you must append your module to the main analysis path, or to one of the smaller paths or sequences that make up the main analysis path.

The basic syntax to add (append) your module to a path is:

path append path_name MyModule
Similarly, for a sequence, the syntax is:
sequence append sequence_name MyModule

Exactly where to put your new module depends on the package. The best way to proceed is to examine how another module in the package is appended, and follow the same procedure for your module.

For example, the BetaMiniUser package comes with the module MyMiniAnalysis. To find the tcl file where MyMiniAnalysis is appended, use grep to search the BetaMiniUser package:

BetaMiniUser > grep MyMiniAnalysis * | grep append

MyMiniAnalysis.tcl:path append Everything MyMiniAnalysis

You see that MyMiniAnalysis is appended to a path called "Everything" in the tcl file MyMiniAnalysis.tcl. So now you know how to edit MyMiniAnalysis.tcl to append your module as well:

path append Everything MyMiniAnalysis
path append Everything MyModule

This procedure ensures that if MyMiniAnalysis is on the analysis path, then MyModule will be there, too. A word of warning, however: for a given package there is usually more than one possible analysis path. (Different tcl configurations lead to different paths.) So if you run one of the analysis jobs that does NOT use MyMiniAnalysis, then MyModule will not be there, either.


BaBar Analysis Examples

Now it is time to put together everything you've learned. The following examples will give you some practice in modifying and writing code in modules.


Example 1: Annotated Quick Tour Analysis Code

As a first step in becoming familiar with some of the analysis code, you can look at the annotated header and implementation files for the Quicktour's QExample analysis module. This module is used to generate the number-of-tracks-per-event histogram. The comments inserted for these purposes are in blue.

Example 2, Part 1: Add a momentum histogram

Quick links:

The easiest way to edit analysis code is to modify an existing module. As a second example, you will modify the QExample module and add a track momentum histogram in addition to its number-of-tracks histogram.

Open your QExample file in a text editor, such as emacs. (Don't worry about making mistakes as you edit the code. If you get stuck and can't find or fix your error, you can always re-copy the original code from $BFROOT/www/doc/workbook/examples/ex0/.)

When you modify an existing module, the required header files are often already #included. Normally, the first step in adding a histogram would be to #include "HepTuple/Histogram.h". However, QExample.hh already has that #include statement. So you can move onto the next step: declaring the histogram.

Declare the new HepHistogram as a private data member in the header file:

   
HepHistogram*            _pHisto;

The reason that histograms and ntuples are added as class members is because you want the histograms to retain all the information they store over multiple events, so the histogram needs to be of greater scope than the event( ) function. Making them private ensures that other modules cannot interfere with them.

Now QExample now has a new HepHistogram pointer, but it has not yet been initialized. Member objects, like the histogram and the histogram manager, should be initialized in the module's beginJob function.

In our case, a histogram manager has already been initialized for QExample's _numTracksHisto. You can use the same histogram manager for _pHisto:

   _pHisto = manager->histogram("Momentum",  25,  0.,  1. ); 

The first argument (in quotes) is the title of the histogram, the second argument (an integer) is the number of bins, the third and fourth arguments (doubles) are the low and high values of the x-axis.

Finally, you are ready to fill the momentum histogram. In your number-of-tracks histogram, all you needed was the length of the tracks list, which is a property of the tracks list as a whole. But momentum is a property of a single track. So you need to iterate (loop) over the BtaCandidates in trkList, and store the momentum for each one:

  // Loop over track candidates to plot momentum 
   HepAListIterator<BtaCandidate> iterTrk(*trkList);
   BtaCandidate* trk(0);
   while ( trk = iterTrk()) {
    _pHisto->accumulate( trk->p() );
   }

The original QExample did not use a HepAListIterator, so the HepAListIterator header still needs to be included:

#include "CLHEP/Alist/AIterator.h"

CLHEP, Class Library for High Energy Physics, is a package that contains general utility classes. If you are looking for the home of a class or function named HEPsomething, it's probably there.

That's all there is to it! Working examples of the modified QExample module can be found in:

$BFROOT/www/doc/workbook/examples/ex2/

or viewed here, where the added code is in blue.

However, the C++ code must be re-compiled and re-linked (gmake all) before the changes will be incorporated into the executable. You will do this in the next section of the workbook: Compile and Link.


Example 3: Creating a new module

This third example will take you step-by-step through the process of creating a new module from the basic module skeleton.

This example is independent of the rest of the Workbook, and it is optional. If you choose to try it, you have two options: you can just read through the example, or you can copy the skeleton code and follow along. Either way, this will be a useful reference if someday you need to create your own module.

If you want to follow along, then copy the skeleton code for a module to your analysis package:

Package> cp -r $BFROOT/www/doc/workbook/examples/ex3/skeleton/* .

(For the Quicktour and the QExample module, the analysis package is BetaMiniUser, but this example is intended to be general so that it will work in any analysis package.)

Now you are ready to begin:

Example 3: Creating a new module


Conclusion

At this point you have learned the basics of what you need to know to modify BaBar code, and maybe even write your own module. You have seen how to manage the two main data-storage objects, HepHistograms and HepTuples, and how to access the main data-storage object, the HepAList of BtaCandidates*.

In the next section, you will learn how to compile and link your code, so that your modified or new module will be incorporated into a new BetaMiniApp.


Related Documents:

Back to Workbook Front Page

Send comments to Workbook Team.