blallen / MitFlat

Flat ntuples definition / production

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

<< Introduction >>

MitFlat contains tools to create fully flat (simple C types and arrays only) ROOT
ntuples. The ntuples are extremely slim, fast, and fully portable (no libraries needed) because of
its dumbness. However, by loading the MitFlat libraries, the flat ntuples can be read as C++ objects
with inheritance, so the analysis code does not have to be flat and dumb.

Tree structures are defined in a tree definition file and then converted to C++ code by bin/makeFlat.py.
Example definition files can be found in config/ directory.

<< Definition file syntax >>

Tree definition file is made of various sections and blocks. A comment line starts with a '%' character.
All typedefs, enums, constants, and classes will be defined under a namespace whose name is taken from the
name of the definition file (e.g. simpletree.def --> namespace simpletree).

1. Includes
Define all headers to be included in the object definition headers.

2. Typedefs
Similarly, define all typedefs.

3. Constants
Constants defined in this section can be used in the object definitions.

4. Objects
This section is the core of the definition file. Each object definition is started with a line

[Obj:SPEC]

where Obj is the name of the object, and SPEC is either

 . "MAX=n" (n integer) -> define a dynamic collectable object. A class ObjCollection will be automatically created. The collection class behaves like std::vector but has a maximum size of n. 
 . "SIZE=n" (n integer) -> define a static collectable object. A class ObjCollection will be automatically created. The collection class behaves like a C array of Objs with array size n.
 . "SINGLE" -> define a non-collectable object (e.g. MET).
 . Name of the base class -> define a class that inherits from another class. Collection multiplicity (max size or SINGLE) is inherited.

Everything that follows after the object name until next object or tree declaration are parsed as
the definition of the object. Within this block, object variables (i.e. ROOT branches) and
single-line methods can be defined. Variables are defined by syntax

name/type
or
name[size]/type

where size is either a integer literal or one of the constants defined in the constants section.

Branch types follow the ROOT convention:
 O -> Bool_t (capital o)
 b -> UChar_t
 B -> Chart_t
 s -> UShort_t
 S -> Short_t
 i -> UInt_t
 I -> Int_t
 l -> ULong64_t
 L -> Long64_t
 F -> Float_t
 D -> Double_t
 C -> Chart_t*

5. Trees

Tree names are specified by {Name}. Everything that follows a tree declaration until the next tree
declaration is considered as tree definition.

Syntax for tree blocks are identical to that in object blocks.

<< Tree input/output >>

Each object variable is booked an independent TBranch. The objects know how to write out and read in
the branch values from TTree. Collections use variable names in the tree definition to book branches.
For example, consider a file particles.def with the following content:
===========================
[Particle:MAX=10]
pt/F
eta/F
phi/F

{Event}
myparts/ParticleCollection
===========================

bin/makeFlat.py particles.def
will result in the following TTree structure:
tree (name not fixed)
 L myparts.size/i
 L myparts.pt[myparts.size]/F
 L myparts.eta[myparts.size]/F
 L myparts.phi[myparts.size]/F

<< Analysis code >>

bin/makeFlat.py <conf>.def

will produce multiple files under DataFormats/interface and DataFormats/src. In the analysis code,
it suffices to include only

#include "MitFlat/DataFormats/interface/TreeEntries_<conf>.h"

as all relevant headers are included in this file.

Using the example above, one can fill the tree like

  TTree* tree = new TTree("mytree", "My Tree");
  particles::Event event;
  event.book(*tree); // creates branches

  ...
  for each event
    for (auto&& inPart : inParticles) {
      event.myparts.resize(event.myparts.size() + 1); // create one particle at the end - throws if maximum size has been reached
      particles::Particle& outPart(event.myparts.back());
  
      outPart.pt = inPart.pt();
      outPart.eta = inPart.eta();
      outPart.phi = inPart.phi();
    }
  
    tree->Fill();

or process the tree written above like

  TTree* tree = static_cast<TTree*>(inputFile->Get("mytree"));
  particles::Event event;
  event.setInput(*tree);

  long iEntry = 0;
  while (tree->GetEntry(iEntry++) > 0) {
    histogram1->Fill(event.particles.size());
    for (auto& part : event.particles) {
      histogram2->Fill(part.pt);
    }
  }

<< Inheritance >>

When a collectable object class is declared as derived from another class, both the element and the
collection have inheritance relations. For example, if

[Particle:SIZE=10]
pt/F
eta/F
phi/F

[ParticleM:Particle]
mass/F

then in the resulting C++ code, ParticleM is a subclass of Particle, and ParticleMCollection is a
subclass of ParticleCollection. This is in contrast to e.g. having a std::vector of inheriting classes;
std::vector<ParticleM> and std::vector<Particle> are not related.

These inheritance relations can be used effectively to make the analysis code compact.

About

Flat ntuples definition / production


Languages

Language:C++ 80.5%Language:Python 17.7%Language:Objective-C 1.5%Language:Makefile 0.3%Language:C 0.1%Language:Shell 0.0%