Analysis tutorial #6: Event selection

The data files we use in this tutorial contain simulated events of the Higgs boson decaying into pair of tau leptons that consequently decay into light leptons: electrons or muons. In the final state we can therefore have either two electrons (ee), two muons (μμ), or a mixed state (eμ or μe).

Let’s now modify the algorithm class to allow selection of either same-flavour (ee or μμ) or different flavour (eμ or μe) combinations. The lepton flavour is encoded in the n-tuple using variables “tau_0” and “tau_1” for leading and sub-leading lepton, respectively. When the variable is equal to 1 it means the tau lepton decayed into muon (+neutrinos) when it’s equal to 2 it means it decayed into electron (+neutrinos). So to select same-flavour or different-flavour  combinations, we just need to require the following conditions:

tau_0 == tau_1
tau_0 != tau_1

Because we do not want to hard-code anything into our c++ code but allow everything to be controlled in Python, we need to add switched that will control which of the two conditions (if any) should be applied. So let’s modify the header file of the c++ class “Algorithm.h” like this:


#include "TString.h"
#include "TH1D.h"
#include "Data.h"

class Algorithm {
  * @brief Construct a new Event Loop object

  * @brief Initialize the algorithm
  * @param data - pointer to the data-access class instance
  void initialize(Data* data);

  * @brief Execute. Here the stuff happens
  void execute();

  * @brief Pointer to the histogram class instance defined as public attribute
  TH1D* h_ditau_m = 0; // must be initialized to 0

  * @brief Selection cuts
  bool cut_selectSF = false;
  bool cut_selectDF = false;


  * @brief Apply the selection
  * @return true when event passed the selection.
  bool passedSelection();

  * @brief Fill the histograms
  void fillPlots();

  * @brief Instance of the data-access class
  Data* m_data = 0;


  • We have added two public boolean attributes “cut_selectSF” and “cut_selectDF”. They are set to false by default, so it means the same/different-flavour selection will not be applied by default.
  • To make code more structured, we have also added two new protected methods: “passedSelection” where we will implement the event selection and “fillPlots” where we will put the histogram-filling code.

In the source file “Algorithm.cpp” we implement the new methods:

#include "Algorithm.h"

Algorithm::Algorithm() {

void Algorithm::initialize(Data* data) {
 m_data = data;

void Algorithm::execute() {

 // apply selection. Exit if didn't pass
 if(!passedSelection()) return;

 // fill the plots


bool Algorithm::passedSelection() {
 // select specific flavor combinations
 if(cut_selectSF && m_data->tau_0!=m_data->tau_1) return false;
 if(cut_selectDF && m_data->tau_0==m_data->tau_1) return false;

 // passed all the cuts
 return true;

void Algorithm::fillPlots() {
 // here we fill the histograms. We protect the code against the empty pointers.
 if(h_ditau_m) h_ditau_m->Fill( m_data->ditau_mmc_mlm_m );
  • We have moved the “h_ditau_m->Fill” code from “execute” into “fillPlots” method. The “fillPlots” is now called from “execute”. This will make the code more organised once we add more plots.
  • In the “passedSelection” method we have two conditions for the “cut_selectSF” and “cut_selectDF” cuts. Note that they are implemented as negative conditions! Meaning that the run is interrupted if we require same-flavour combination but the leptons have different flavour. If program reaches the end of the method it means that the selection passed.
  • The “passedSelection” is called from “execute” just before the “fillPlots”.

Now that we have the c++ code updated, let’s recompile everything. When you do large modifications to the c++ code like this, it’s good to also clean all the compiled objects. It can be done using command:

> make clean
> make

Now we also update the python classes in the “” file. We will remove the “AlgFineBinning” class which is not that useful and add two new classes for same-flavour and different-flavour selection:

from Algorithm import Algorithm
from ROOT import TH1D

# -----------------------------------
class AlgDefault(Algorithm):
 """ Default set of histograms
 def __init__(self, name = "AlgDefault"):
  # call inherited constructor
  Algorithm.__init__(self, name)

  # create histograms
  self.alg.h_ditau_m = TH1D("ditau_m", ";#it{m}_{#tau#tau} [GeV];Events", 30, 0, 300)

# -----------------------------------
class AlgSF(AlgDefault):
 """ Select only same-flavor leptons
 def __init__(self, name = "AlgSF"):
  # call inherited constructor
  AlgDefault.__init__(self, name)

  # apply selection for the same-flavor lepton combination
  self.alg.cut_selectSF = True

# -----------------------------------
class AlgDF(AlgDefault):
 """ Select only different-flavor leptons
 def __init__(self, name = "AlgDF"):
  # call inherited constructor
  AlgDefault.__init__(self, name)

  # apply selection for the different-flavor lepton combination
  self.alg.cut_selectDF = True
  • The new classes “AlgSF” and “AlgDF” inherit from “AlgDefault”. This way again we minimise amount of duplicated code. One just have to make sure that in the new classes’ constructors one calls the constructor of the immediate parent, i.e.  “AlgDefault” in this case.
  • The cuts are enabled by setting their value to “True”. Unlike in c++, the Python keyword for True and False is spelled with capital first character.

Finally, we update the “”. We will run the three algorithms in the event loop. One without the selection, one with the same-flavour selection and one with the different-flavour selection:

# here we load the shared library created using the Makefile
from ROOT import gSystem

# now we can create instance of the class EventLoop
from Samples import *
eventLoop = SampleGGH()

# create algorithm
from Algorithms import * 
algs = []
algs += [ AlgDefault() ]
algs += [ AlgSF() ]
algs += [ AlgDF() ]

# add the algorithm into the event loop
eventLoop.addAlgorithms( algs )

# initialize and execute the event loop

# save plots from all algorithms in the loop

After the execution, you will have three output files in your work folder: histograms.ggH.AlgDefault.root, histograms.ggH.AlgDF.root, and histograms.ggH.AlgSF.root. Now you can open them in ROOT and check that the number of entries in histogram without the selection matches the sum of entries in other two:

> root -l histograms.ggH.AlgDefault.root
> ditau_m->GetEntries()
(double) 10757.000
> .q

> root -l histograms.ggH.AlgSF.root 
> ditau_m->GetEntries()
(double) 4522.0000
> .q

> root -l histograms.ggH.AlgDF.root 
> ditau_m->GetEntries()
(double) 6235.0000
> 4522.0000+6235.0000
(double) 10757.000

We are starting to see how the object-oriented design of the analysis program starts to bear fruits. By modifying few lines and adding few items into the list we can create histograms for many different configurations at once. Imagine that we would have everything hardcoded in a single linear macro. We would have to rewrite, recompile and re-execute the macro many times to get the same result.

In the next example we will show how to use the operator overloading technique to implement more complex selection cuts that can be enabled/disabled at will.

Next section →