Go to Notes and Further References, Exercises, top,
Writing Root Macros
  • CINT : ROOT's interactive C++ compiler
    • Supports most of the C++ language.
  • Can process C++ statements
    • Interactively at ROOT Prompt.
    • From a stored file (called a ROOT macro).
  • Can interact with JobC
    • To create configure and run paths
    • See $SRT_PUBLIC_CONTEXT/macros for examples
  • Useful for more than trivial tasks
    • Can develop significant analysis tasks without compile/link!
  • Loading libraries at execution time
    • Can customise ROOT with application libraries e.g.
      • system_prompt> root
        root [0] .x minos.C
      turns ROOT into loon!

Notes and Further References

Exercises

Before running any exercises see
tutorial preparation.

In this tutorial we will go behind the job user interface and start to directly use the object data structure with the following:-

  1. Looping over events printing out headers
  2. Looping over events printing out RawDataBlocks
  3. Looping over events printing out hits from RawDigitDataBlock

Looping over events printing out headers

To demonstrate the power of ROOT macros, and to show how they give you interactive access to both ROOT and Minossoft (loon) classes consider the following simple macro look_at_data_header.C which reads and prints 10 events:- Let's break it apart to understand it. The outer level consists of:-
look_at_data_header()
{
..
}
void myAnaDataHeader(const MomNavigator* mom) 
{
...
}
As it explains in the summary of the Macros page of the Companion's First Steps: In C++ and ROOT macros can be one of two sorts:- In general named macros are to be preferred. as local (stack) objects are cleaned up just as they are in compiled C++ and the functions they define are added to ROOT and extend it for the remainder of the job. Our example is a named macro with two functions:-

look_at_data_header

Now we will dissect look_at_data_header() starting with:-
  JobC j;
This gets us a JobC object with which we can create a JobCPath:-
  j.Path.Create("Demo","");
You ought to be able to figure out what its doing from the Configuring Jobs slide. The path is empty, but the input module is effectively added at the start so that running the path will read in records.

If you are new to C++ the syntax may have you puzzled. The first part:-

  j.Path.
selects the public data member Path, which is a JobCPathModule object. The second part:-
   .Create("Demo","");
requests that JobCPathModule creates a path called Demo.

Next we add another, special RootCommand module, selecting its Ana method:-

  j.Path.Add("Demo","RootCommand::Ana");
and configure it so, when the Ana method is called, it in turn calls our myAnaDataHeader function and passes it something called mom.
  j.Path("Demo").Mod("RootCommand").Cmd("AddLine/Ana myAnaDataHeader(mom)");
mom is a MomNavigator. If you want to know more then you to look at the MinosObjectMap slide. For now though, all you need to know is, as always, Mom knows where everything is!

You might just be wondering how, when the RootCommand module runs, it knows where to find mom. This is a bit of ROOT magic, which registers the name of every object that gets created within its framework. When presented with an unknown name, it locates its address and type from this is the registry. If you don't like to rely on ROOT magic - which is wise if you plan to write plain C++ - you can mom from the JobC object:-

  MinosObjectMap& mom = j.Mom;

Finally run the path 10 times

  j.Path("Demo").RunNin(10);

myAnaDataHeader

Now let's turn to the analysis function myAnaDataHeader. This starts by asking Mom for the RawRecord:-
 RawRecord* raw = dynamic_cast<RawRecord*>(mom->GetFragment("RawRecord"));
Again, if you are new to C++ lines like this may look a bit daunting. There are two things going on:-
  1. mom is asked to find the fragment called RawRecord:-
        mom->GetFragment("RawRecord");
    
    Now if you look at MomNavigator.h you will see that GetFragment() is declared like this:-
      TObject *GetFragment(const char *classname = 0,
                           const char *username = 0,
    	               const char *streamname = 0 ) ) const;
    
    it returns a TObject* not at RawRecord* which inherits from it.
  2. So its necessary to convert a TObject pointer to a RawRecord pointer which is what:-
        RawRecord* raw = dynamic_cast<RawRecord*>(...);
    
    does and then stores it in the variable raw. The dynamic_cast operator is a recent addition to C++ and you will certainly come across the older, C-style, casting which simply places the type you want to convert to in parenthesis before the expression to be converted. In this case it would be written:-
        RawRecord* raw = (RawRecord*)mom->GetFragment("RawRecord");
    
    which may be easier to read but is more dangerous which is why that form is deprecated in item DANG-12 of our coding standards. Converting a pointer to move up the inheritance tree is always potentially dangerous. For example if the TObject that mom returned was not embedded in a RawRecord what would happen? Well the C-style cast would happily return a non-zero totally corrupt pointer! At least dynamic_cast can do a check at run time and, if the TObject isn't part of a RawRecord, return zero.

Having got the RawRecord,we ask it for its RawHeader and lastly we ask RawHeader to print itself:-
  const RawHeader* head = raw->GetRawHeader();
  cout << "Found a RawRecord header: " << endl;
  head->Print();
  cout << endl;
See MinosObjectMap for a warning if there is more than one RawRecord currently available.

It should produce 10 lots of output similar to this:-

Found a RawRecord header:
RawDaqSnarlHeader {   Far|  Data|2002-06-28 15:18:28.123984581Z}
 Run 5963 SubRun 0 RunType 2 Snarl 2 TrigSrc 4 ErrorCode 0

Adding Printout

In the above exercise the RawHeader object obligingly provided a Print() member function so you can print out its contents. But what if you want to print the contents of some less thoughtful object or some base type like int? For that you have to use the standard output object cout. For example the code:- int answer = 42; cout << "The answer is " << answer << endl; produces:- The answer is 42 The statement consists of cout, followed by one or more expressions separated by shift operators
To run this example:-
  loon -q $MINOS_TUTORIAL_MACROS/look_at_data_header.C $MINOS_TUTORIAL_DATA/F00018143_0000.mdaq.root
It should produce 10 lots of output similar to this:-
Found a RawRecord header:
RawDaqHeader {   Far|  Data|2003-08-01 02:48:29.183670000Z}
 Run 18143 SubRun 0 RunType 2 TimeFrame 0
Take a look at the file look_at_data_header.C and try to understand it.

Code like:-

    RawRecord* raw = dynamic_cast<RawRecord*>(mom->GetFragment("RawRecord"));
    const RawHeader* head = dynamic_cast<RawHeader*>(raw->GetRawHeader());
    head->Print();
which follows from object to object via pointers has a very serious flaw. What is it and how is it fixed?

Looking at what Mom has to offer

In look_at_data_header.C we simply asked Mom for the first RawData block it had:-
    RawRecord* raw = (RawRecord*)mom->GetFragment("RawRecord");
which is risky - it could have several. In our next example: look_at_data_header_2.C we do something a bit better:-
  // Get an iterator over the fragments
  TIter iter = mom->FragmentIter();

  // Use TIter::Next() to get each TObject mom owns.
  TObject* tobj = 0;
  while ( tobj = iter.Next() ) {
    // Convert TObject to a RecMinos.
    RecMinos* rec = dynamic_cast<RecMinos*>(tobj);
    if (rec) {
     ...  process RecMinos objects
   }
    else {
     ...  deal with everything else
    }
  }
so it loops over all of Mom's fragments and asks each it's type. In this case we just see what RecMinos objects there are. These will include RawRecord which inherit from RecMinos. If you want to be convinced of that then one way would be to browse the code.

Run it in the same way:-

  loon -q $MINOS_TUTORIAL_MACROS/look_at_data_header_2.C $MINOS_TUTORIAL_DATA/F00018143_0000.mdaq.root
Looping over events printing out RawDataBlocks Our next macro: look_at_data_blocks.C goes beyond the printing of a of record headers to look at the data blocks held in a RawDataBlock which it does with:-
   TIter itr = raw->GetRawBlockIter();
   RawDataBlock* rawBlk = dynamic_cast<RawDataBlock*>(itr.Next());
   while ( rawBlk ) {
     cout << "    " << rawBlk->GetName() << endl;;
     rawBlk = dynamic_cast<RawDataBlock*>(itr.Next());
   }
Run it:-
  loon -q $MINOS_TUTORIAL_MACROS/look_at_data_blocks.C $MINOS_TUTORIAL_DATA/F00018143_0000.mdaq.root
Looping over events printing out hits from RawDigitDataBlock In our final macro look_at_data_hits.C we finally get to the hits from a RawDigitDataBlock. In order to do this we have to do 2 things:-
  1. Look for RawDataBlock that are, or inherit from, RawDigitDataBlock.
       //  Does the RawDataBlock inherits from or ir a RawDigitDataBlock
       //  (note: use this ROOT function because CINT's dynamic_cast
       //         doesn't return 0 when it should fail!)
         if ( rawBlk->InheritsFrom("RawDigitDataBlock") ) {
    
  2. Access the digits within the RawDigitDataBlock.
          RawDigitDataBlock* rddb = dynamic_cast<RawDigitDataBlock*>(rawBlk);
    
       //     .... See how many digitizations there are ...
           Int_t numDigits = rddb->GetNumberOfDigits();
           if ( numDigits > 10 ) numDigits = 10;
    
       //     ... and print out the first 10 at most.
           for (unsigned idigit = 0; idigit < numDigits; ++idigit) {
             const RawDigit* rd = rddb->At(idigit);
             rd->Print();
           }
    
Run it:-
  loon -q $MINOS_TUTORIAL_MACROS/look_at_data_hits.C $MINOS_TUTORIAL_DATA/F00018143_0000.mdaq.root

Note to Maintainers: When making changes please follow the maintenance rules

Contact: Nick West <n.west1@physics.ox.ac.uk>
Fermilab
Security, Privacy, Legal Fermi National Accelerator Laboratory