3.5.2 Event Loop in FWLite
Complete:
Contents
Detailed Review status
The goals of this page:
In this page you will learn how to
- write ROOT macros that analyze CMS data directly
- compile these ROOT macros to make them faster and safer
Those users who like command line options, can have a look at
SWGuideCommandLineParsing for command line parsing instructions.
The structure of a FWLite macro
Let us illustrate the access to
EDM collections in FWLite with an example. But before we begin, the following lines need to be executed from within ROOT
{
gSystem->Load("libFWCoreFWLite.so");
FWLiteEnabler::enable();
gSystem->Load("libDataFormatsFWLite.so");
gSystem->Load("libDataFormatsPatCandidates.so");
}
as discussed in the previous section, it is best to place these lines inside the
rootlogon.C file, so that they are executed automatically and you don't need to worry about them.
Here's a sketch of a macro that loops over events, and in each event retrieves an ED object:
void print_data()
{
#include "DataFormats/FWLite/interface/Handle.h"
TFile file("MYCOPY.root");
fwlite::Event ev(&file);
for( ev.toBegin(); ! ev.atEnd(); ++ev) {
fwlite::Handle<std::vector<...> > objs;
objs.getByLabel(ev,"....");
// now can access data
std::cout <<" size "<<objs.ptr()->size()<<std::endl;
...
}
}
Note:
When building a macro, there is a particular order of calls to ROOT methods which are required in order for ROOT to function properly:
- Start the autoloader
- Create the TFile
- Load in the helper library
gSystem->Load("libDataFormatsFWLite")
- Include "DataFormats/FWLite/interface/Handle.h" to get the
fwlite::Handle
- Create an
fwlite::Event
by passing to the constructor a pointer to the TFile. The fwlite::Event allows you to use the same information you use when accessing data in cmsRun using the edm::Event.
- Create a for loop over events
- Start loop by calling
toBegin()
on the fwlite::Event
- For each iteration of the loop call
atEnd
on the fwlite::Event
- At the end of each iteration, increment the
fwlite::Event
by using the operator++ method
- When looping over the events
- Create an
fwlite::Handle< ... >
where the template argument is the C++ class you want to get from the event
- Call the
getByLabel
method of the fwlite::Handle
passing the event and the strings or edm::InputTag used to specify the object
- The object retrieved from the handle is either a single number, or a collection of CMS objects (e.g. Tracks). In the latter case, you can loop the vector of objects using the standard iterator notation (same as in the FullFramework).
If you save the above macro in a file called print_data.C, you can load it and execute it from the interactive ROOT session with
root[] .L print_data.C
print_data()
or simply
.x print_data.C
However, note that in the above case the macro
print_data.C
is
interpreted by
CINT. This is okay for rapid prototyping, however, for real data processing one should always
compile it with ACliC for several reasons:
- The macro will be significantly faster, as ACliC invokes the native compiler (so on Linux it's the same version of GCC used to build ROOT)
- CINT is good, but ii is not 100% reliable in its computations, and it is better not to risk running into one of the rare cases which are not correctly handled by CINT
- Compiled code can be debugged, profiled, or processed with valgrind (especially when linked into a standalone executable)
Compling the macro with ACliC
Compiling the macro on the fly (while it's being loaded) is trivial. One simply adds a "+" at the end of the macro name:
TFile f("....root");
.x print_data.C+
ROOT then first invokes GCC from the release, makes a shared library, and loads it. Note that "++" would always cause compilation, while "+" would recompile an already existing macro only if the source code is newer than the compiled code, just like make.
Note:
There are several pieces of information you need to know about how ROOT compiles a macro:
- ROOT runs the CINT interpreter over the macro before compiling it in order to determine if it needs to generate dictionaries for some of the classes/functions mentioned in the macro. Unfortunately many of CMS's header files contain code that CINT can not parse. This means we must hide those headers from CINT but make sure they are visible to the compiler.
- After compiling the code, ROOT links the compiled macro against all libraries which have been loaded. If the macro uses a function or variable from a library which has not yet been loaded, then ROOT will issue a 'missing symbol' error.
With the above in mind, here are the steps needed to arrive at a compiled ROOT macro:
- Protect all headers using a
#if !defined(__CINT__) && !defined(__MAKECINT__) #endif
block
- Introduce the macro block as a function with the same name as the file (this keeps CINT from trying to fully parse the internals of the routine)
- In ROOT
- Load and start the autoloader
- Load
libDataFormatsFWLite
- Create a
TFile
from one of the files you want to read. This will cause all the libraries for every class in the file to be loaded.
- Compile/link/execute the macro by doing
.x <filename>++
An example macro template is shown below. The example assumes that the file is named print_data.C
#if !defined(__CINT__) && !defined(__MAKECINT__)
#include "DataFormats/FWLite/interface/Handle.h"
#include "DataFormats/FWLite/interface/Event.h"
//Headers for the data items
...
#endif
void print_data() {
TFile file("....root");
fwlite::Event ev(&file);
for( ev.toBegin(); ! ev.atEnd(); ++ev) {
fwlite::Handle<std::vector<...> > objs;
objs.getByLabel(ev,"....");
//now can access data
std::cout <<" size "<<objs.ptr()->size()<<std::endl;
...
}
}
Note:
A note on the preprocessor switches (testing on
CINT and
MAKECINT): The first step that ROOT takes when compiling a macro is to run the
CINT interpreter over the macro in order to determine what class or function 'dictionaries' it must create. After that step, the regular C++ compiler is used to build the code. Unfortunately,
CINT is incapable of properly parsing many of our header files. However, it turns out the headers are not needed by
CINT but only by the compiler, therefore adding
#if !defined(__CINT__) && !defined(__MAKECINT__)
...
#endif
around the header files avoids the problem with
CINT.
However, the compiler still needs to know where to find our header files. FWLite pre-configures ROOT to find CMS headers from the environment variables
CMSSW_BASE
and
CMSSW_RELEASE_BASE
. FWLite pre-configures ROOT to find standard externals header files (e.g. boost and CLHEP).
Running over a list of files
The fwlite::ChainEvent allows you to use the same information you use when accessing data in
cmsRun using
edm::Event:
- Start the autoloader
- Load in the helper library
gSystem->Load("libDataFormatsFWLite")
- Create a
std::vector<std::string>
to hold the list of file names
- Include "DataFormats/FWLite/interface/Handle.h" to get the
fwlite::Handle
-
push_back
each file name into the std::vector<std::string>
- Create an
fwlite::ChainEvent
by passing to the constructor the vector
- Create a for loop
- Start loop by calling
toBegin()
on the fwlite::ChainEvent
- For each iteration of the loop call
atEnd
on the fwlite::ChainEvent
- At the end of each iteration, increment the
fwlite::Event
by using the operator++ method
- When looping over the events
- Create an
fwlite::Handle< ... >
where the template argument is the C++ class you want to get from the event
- Call the
getByLabel
method of the fwlite::Handle
passing it the event and the strings used to denote the object
An example macro template is shown below:
{
gSystem->Load("libFWCoreFWLite.so");
AutoLibraryLoader::enable();
gSystem->Load("libDataFormatsFWLite.so");
#include "DataFormats/FWLite/interface/Handle.h"
vector<string> fileNames;
fileNames.push_back("....root");
fwlite::ChainEvent ev(fileNames);
for( ev.toBegin(); ! ev.atEnd(); ++ev) {
fwlite::Handle<std::vector<...> > objs;
objs.getByLabel(ev,"....");
//now can access data
std::cout <<" size "<<objs.ptr()->size()<<std::endl;
...
}
}
The compilation is identical to what is done for the case of a single file above, except in the macro you replace the use of
fwlite::Event
with
fwlite::ChainEvent
.
Note: You must still pick one of the files to be used in the
fwlite::ChainEvent
and open it with a
TFile
on the ROOT command line in order to force the proper dictionaries to be opened.
3.5.2.4 Using edm::EventBase
It is now possible to use the
edm::EventBase
such that the user does not need to use the
fwlite::Handle any more. This allows the user to write functions that can be used directly in FWLite or in the full framework and use the same "get" methods. Note that this does not function if one uses
CINT to interprete the macros. A snippet of how to do this is given below:
for (ev.toBegin(); ! ev.atEnd(); ++ev) {
edm::EventBase const & event = ev;
// This snippet can be used in EITHER FWLite or the Full Framework
edm::Handle<vector<reco::Vertex> > vertices;
event.getByLabel( edm::InputTag("offlinePrimaryVertices"), vertices);
// ...
}
In the meantime FWLite and full framework can indeed be used in parallel on tha bases of truely compiled executables! If you want to know more about this have a look for a bunch of examples of compiled executables with FWLite on
WorkBookFWLiteExamples.
Review status