ntupleWriterSvc
Introduction

Obtaining the code

Running the test program

Using the service
JobOptions

Reading the ntuple

Introduction

Want to write out to a ROOT ntuple?  Then ntupleWriterSvc is for you!  This Gaudi Service is designed to handle the ins and outs of adding and updating an ntuple.  User algorithms just provide a name to identify the ntuple they desire to write to, the tag name for the ntuple entry, and a value.  ntupleWriterSvc does the rest!

Obtaining the Code

ntupleWriterSvc is available from your neighborhood SLAC cvs repository.  You can find the most recent tagged version by checking out this web page that lists the latest tags.

Running the test program

To become acquainted with the ntupleWriterSvc, try the test application that is provided with the ntupleWriterSvc package.  Once ntupleWriterSvc has been set up using cmt, compile the test project to generate the test application.  Now check the jobOptions.txt file in the src/test directory.  You may need to update the following entry, to point to a valid directory on your system:
// Output root ntuple file, specify by logical variable
NTupleSvc.Output = {"TESTALG DATAFILE='/NTUPLES/testAlg.root' OPT='NEW'"};
Now you are ready to run the test application.  When it is complete, there will be a new Root ntuple in the directory name you provided in the NTupleSvc.Output job options entry.  The ntuple will contain one column called "MyFirstItem".

Using the service

  1. First, add "use ntupleWriterSvc" to the requirements file for the application you wish to run.

  2. Next setup the package, so that the settings are reset to include ntupleWriterSvc

  3. Update the jobOptions file:

    1. Add ntupleWriterSvc to ApplicationMgr.ExtSvc

    2. Add ntupleWriterSvc to ApplicationMgr.DLLs

    3. Add ApplicationMgr.HistogramPersistency = "ROOT"; if it is not already in your jobOptions

    4. Provide a list of ntuple names to the service, for example:  ntupleWriterSvc.tuple_name = { "ACD" };

      1. Here we will create one ntuple, identified by the tag "ACD".

    5. Setup the output parameters for the NTupleSvc, using the tag name(s) provided in your ntupleWriterSvc.tuple_name list.  Note that the NTupleSvc as well as ntupleWriterSvc can accomodate multiple ntuples in one run.  Here is an example for settiing up the "ACD" ntuple:
      NTupleSvc.Output = {"ACD DATAFILE='/NTUPLES/acdAlg.root' OPT='NEW'"};

    6. Finally, for the algorithm you wish to use to write to an ntuple, setup a new input parmeter to specify the ntuple logical name.  Here is an example for the acdReconAlg:
      acdReconAlg.tuple_name = "ACD";

  4. Next update your algorithm to use the ntupleWriterSvc

    1. Add the include file:   #include "ntupleWriterSvc/INTupleWriterSvc.h"

    2. Setup a member variable to access the ntupleWriterSvc:
      INTupleWriterSvc *ntupleWriteSvc;

    3. Add a member variable to store the ntuple logical name parameter that we just added to the job options file.
      std::string m_tupleName;

    4. In your algorithm's constructor, add a declareProperty call for the logical name parameter:
      declareProperty("tupleName", m_tupleName="");

    5. In your algorithm's initialize routine, make sure there is a call to "setProperties( )".  This is a call to the Gaudi routine that will extract your parameters from the job options file.

    6. In your algorithms's initialize routine, access the ntupleWriterSvc:
      // get a pointer to our ntupleWriterSvc
      sc = service("ntupleWriterSvc", ntupleWriteSvc);

      if( sc.isFailure() ) {
          log << MSG::ERROR << "acdReconAlg failed to get the ntupleWriterSvc" << endreq;
          return sc;
      }

    7. Lastly, during your algorithm's execute method, call a routine to fill quantities in an ntuple.  For example, the acdReconAlg has a method called writeNTuple( ).  This routine, uses the ntupleWriterSvc to add items and their values to a named ntuple.  This example, adds 2 entries to the ntuple, "ACD_TotEnergy" and "ACD_TileCount".
      StatusCode acdReconAlg::writeNTuple() {
          StatusCode sc = StatusCode::SUCCESS;
          /// we must add our ntuples items to the ntuple here:
          sc = ntupleWriteSvc->addItem(m_tupleName.c_str(), "ACD_TotEnergy", m_totEnergy);
          sc = ntupleWriteSvc->addItem(m_tupleName.c_str(), "ACD_TileCount", m_tileCount);
          return sc;
      }

    8. NOTE:  It is imperative that all items that you expect to fill during the course of a run are setup via an "addItem" call for the first and every event.  An ntuple will contain entries for all columns for every event.  We cannot add new columns during the course of a run.  So...if your addItem calls are dependent upon certain variables being defined - provide alternate calls to addItem to fill in dummy values for the columns in the ntuple.  These dummy values may be zero, or some outlandish value to flag this event as one that does not have certain variables defined.

    The only method users of ntupleWriterSvc need to be concerned with is the addItem routine.  The method is defined as follows:  StatusCode addItem(const char *tupleName, const char *itemName, const float value);  The first parameter is the tag that identifies the ntuple that we wish to write to, such as "ACD".  The second parameter is the column name for the item we wish to update, such as "ACD_TotEnergy", this will be the name as it will appear in the Root ntuple.  The third parameter is a float, which contains the value to be stored in this item for this event.

    Reading the ntuple

    Reading the ntuple is actually fairly easy.  The ntuple only contains simple branches which contain a simple data type..currently just doubles.  So to read the ntuple in Root, here is an example script:

    {
    gROOT->Reset(); // roll back CINT context to last Save

    // open the file
    TFile *myTup = new TFile("I:/NTUPLES/testAlg.root");
    TTree *myTree = (TTree*) myTup->Get("TESTALG/t1");

    // open up a TBrowser
    TBrowser tb;

    // Draw a histogram of the MC X0, 
    // and apply cuts based on Y0 and Z0
    myTree->Draw("MC_X0", "MC_Y0<10 && MC_Z0>40");

    }

    To use this script for your particular root file - you will have to modify 2 lines:
    TFile *myTup = new TFile("I:/NTUPLES/testAlg.root");
    TTree *myTree = (TTree*) myTup->Get("TESTALG/t1");

    First you need to open the correct root file in the TFile *myTup = new TFile( ) call.
    Secondly, you need to access the correct name of the tree.  Our ntuple will be located within a directory named according to the logical name we used in the job options file, this case "TESTALG".  Then the actual name of the tree will be "t1".  If you are in doubt about the name of the directory containing the TTree, then open the TFile and the do "myTup->ls( )", that should print the contents of the file.