The following section will walk you through scheduling the electron analysis sequence, applying selection cuts, and writing them to the output ntuple.
Begin by scheduling the electron analysis sequence. Do this by adding
the following lines to makeSequence()
in MyAnalysisAlgorithms.py
after the GRL and pileup sequences:
# Include and set up the electron analysis sequence:
from EgammaAnalysisAlgorithms.ElectronAnalysisSequence import makeElectronAnalysisSequence
workingpoint = 'LooseLHElectron.Loose_VarRad'
electronSequence = makeElectronAnalysisSequence( dataType, workingpoint, postfix = 'loose',
recomputeLikelihood=False, shallowViewOutput = False )
electronSequence.configure( inputName = 'Electrons',
outputName = 'AnaElectrons_%SYS%' )
# Add the electron analysis sequence to the job:
algSeq += electronSequence
This schedules electron calibration as well as implementing requirements
on the electron identification. It adds a decoration to each electron
named baselineSelection_loose
, where loose
is set by the postfix
argument we pass to makeElectronAnalysisSequence
. It is saved as a
char
with a value of 0 or 1, indicating whether the electron passes
the selection. The use of a char
is to save disk space compared to
using an int
or bool
.
Rerun to make sure this is correctly implemented.
Now let’s apply some basic selection criteria for our electrons. We
will not use electrons with pT < 27 GeV or |η| > 2.47,
so we will use a CP::AsgPtEtaSelectionTool
to apply these requirements.
First, we need to set the configurations for these cuts. Add the
following lines to MyAnalysisAlgorithms.py
. It is useful, but not
required, to add them before makeSequence()
is defined:
electronMinPt = 27e3 # Minimum pt in MeV
electronMaxEta = 2.47
Put the following lines in makeSequence()
after the electron analysis
sequence:
# Schedule selection algorithms for all objects
# Include and set up electron selection algorithm:
selAlgEl = createAlgorithm( 'CP::AsgSelectionAlg', 'UserElectronSelectionAlg' )
addPrivateTool( selAlgEl, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
if electronMinPt is not None :
selAlgEl.selectionTool.minPt = electronMinPt
if electronMaxEta is not None :
selAlgEl.selectionTool.maxEta = electronMaxEta
selAlgEl.selectionDecoration = 'selectPtEta,as_char'
selAlgEl.particles = 'AnaElectrons_%SYS%'
# Add the electron selection algorithm to the job:
algSeq += selAlgEl
This algorithm checks whether each electron passes the pT
and η requirements and decorates them with a flag names selectPtEta
that we can access later to easily determine whether the electron passes
the requirements. The ,as_char
causes the decoration to be saved as
a char
with values of 0 or 1.
As indicated by the comments in the code, all of the object selection algorithms will be placed together here. All analysis sequences for objects (except overlap removal which will be introduced later) are recommended to be before this block of selection algorithms.
Rerun to make sure this is correctly implemented.
Now that we have run the electron analysis sequence and applied some basic selection cuts, we need to write the resulting electron information to the output ntuple. In order to do this, we need to access the electron container.
First, we need to add the correct library to the CMakeLists.txt
file.
For electrons and photons, the proper library is xAODEgamma
. Add this to
the LINK_LIBRARIES
section where you previously added xAODEventInfo
.
Next, make sure the algorithm itself recognizes the type of container we need.
At the top of MyxAODAnalysis.cxx
, add the following include statement:
#include <xAODEgamma/ElectronContainer.h>
Now that our algorithm knows what electrons are, we can retrieve the
electron container (just like the Events
container). However, we
shouldn’t use the Electrons
container. Instead, we want to use the
AnaElectrons_NOSYS
container that was created by the algorithms we
scheduled. Add the following lines to execute()
before the TTree is
filled.
// Retrieve electron container from the event store
const xAOD::ElectronContainer* allElectrons = 0;
ANA_CHECK (evtStore()->retrieve(allElectrons, "AnaElectrons_NOSYS"));
![]()
NOSYS
refers to the nominal (i.e., no systematic variations applied) collection. Later, we will look at the collections that result from systematic variations being applied.
Finally, we can write information about our electrons to the output ntuple so we can look at them and use them later in the analysis. This will be an extension of the ntuple that you previously created. We are just adding electron information to it so it has a more complete set of information about the event.
In any data or MC event, there is a variable number of electrons (as well as any other physics object), so we need to use vectors to store information about them. We can write a wide variety of information about the electrons to the ntuple, but for now we will store their four momenta.
First, declare the variables we want to use for our branches in the header file:
std::vector<float> *m_elEta = nullptr;
std::vector<float> *m_elPhi = nullptr;
std::vector<float> *m_elPt = nullptr;
std::vector<float> *m_elM = nullptr;
Notice these are all pointers to vectors and therefore are initialized
to nullptr
.
Now we can instantiate our branches. We are going to be writing the
vectors to the output. For each vector pointer, we need to create a
new vector. Add the following lines in initialize()
:
m_elEta = new std::vector<float>();
mytree->Branch ("el_eta", &m_elEta);
m_elPhi = new std::vector<float>();
mytree->Branch ("el_phi", &m_elPhi);
m_elPt = new std::vector<float>();
mytree->Branch ("el_pt", &m_elPt);
m_elM = new std::vector<float>();
mytree->Branch ("el_m", &m_elM);
Before we start filling these vectors with values, we want to be sure the
vectors are empty since we reuse the same vector for every event. In execute()
add the following:
// Clear electron 4-momenta vectors
m_elEta->clear();
m_elPhi->clear();
m_elPt->clear();
m_elM->clear();
Once the vectors are cleared, we can start filling them with the values for
each electron in the event. We can use a simple for-loop over the electron
container to accomplish this. Here, the data type of the iterator el
is
defined as an xAOD::Electron*
. You can also use auto
as the data type,
but you need to be wary that this comes with its own set of potential pitfalls.
// Loop over electrons and store four-momentum information
for (const xAOD::Electron* el : *allElectrons) {
m_elEta->push_back (el->eta ());
m_elPhi->push_back (el->phi ());
m_elPt-> push_back (el->pt ());
m_elM-> push_back (el->m ());
}
In the initialize()
function we created new vectors for our objects.
As we continue to add more object information, the memory management of
all the vectors can become overburdened. It is important to delete these
vectors to ensure the algorithm runs smoothly. To do this, we need to
define a custom destructor for our algorithm. Add the following to your
header (MyxAODAnalysis.h
):
public:
~MyxAODAnalysis ();
In your source code (MyxAODAnalysis.cxx
) add the destructor (given
below) after the finalize()
function:
MyxAODAnalysis :: ~MyxAODAnalysis () {
delete m_elEta;
delete m_elPhi;
delete m_elPt;
delete m_elM;
}
Now recompile and rerun your job. Look at the ntuple output to see
if the changes are as expected. In particular, look at the ElPt
distribution.
Congrats, we have electrons!
Follow the instructions in Making Histograms to book and fill a histogram for each of the four-momentum components.
You will likely need several iterations of adjusting the binning to show the information optimally. Note that some of the variables may also have negative values. Also, for pT and mass, it is usually useful to divide by 1000 when filling histograms to convert from MeV to GeV.
Now that we are able to write electron information to the ntuple,
let’s cut on the baselineSelection_loose
as well as the pT
and η selection criteria that were applied by the algorithms.
Recall that the selection algorithm applied a decoration called
selectPtEta
. We will use this flag to skip electrons that failed
the criteria.
To select only electrons that pass these criteria, let’s add a check in the loop over electrons that skips any that fail the criteria:
for (const xAOD::Electron *el : *allElectrons) {
if(!el->auxdata< char >("baselineSelection_loose")) continue;
if(!el->auxdata< char >("selectPtEta")) continue;
...
}
Note that it is necessary to cast the
char
as anint
if you want to print it to screen to check its value.
Recompile and rerun your job. Check the output ntuple to see how the selection criteria changed the electron pT distribution.
Commit and push your code changes.
Add an ElectronPtCut
as a property of the algorithm, set it to 30 GeV, and add it to
your electron selection in MyxAODAnalysis.cxx
. This is an alternate way of implementing
object-level selection criteria. Run again and look at your output ntuple to see if the
cut has an effect. What happens if you apply a cut of 20 GeV? Think about how this cut
is applied and what other cuts are applied in the electeron algorithms.