Program Listing for File ElectronSelector.cxx¶
↰ Return to documentation for file (Root/ElectronSelector.cxx
)
// c++ include(s):
#include <iostream>
#include <typeinfo>
#include <sstream>
#include <tuple>
// EL include(s):
#include <EventLoop/Job.h>
#include <EventLoop/StatusCode.h>
#include <EventLoop/Worker.h>
// EDM include(s):
#include "xAODEventInfo/EventInfo.h"
#include "xAODEgamma/ElectronContainer.h"
#include "xAODEgamma/EgammaDefs.h"
#include "xAODTracking/VertexContainer.h"
#include "xAODTracking/TrackParticlexAODHelpers.h"
// #include "PATCore/TAccept.h"
// package include(s):
#include "xAODAnaHelpers/ElectronSelector.h"
#include "xAODAnaHelpers/HelperClasses.h"
#include "xAODAnaHelpers/HelperFunctions.h"
#include "ElectronPhotonSelectorTools/AsgElectronLikelihoodTool.h"
#include "ElectronPhotonSelectorTools/AsgElectronIsEMSelector.h"
// tools
#include "IsolationSelection/IsolationSelectionTool.h"
#include "TrigDecisionTool/TrigDecisionTool.h"
#include "TriggerMatchingTool/MatchingTool.h"
#include "TriggerMatchingTool/MatchFromCompositeTool.h"
// ROOT include(s):
#include "TFile.h"
#include "TObjArray.h"
#include "TObjString.h"
// this is needed to distribute the algorithm to the workers
ClassImp(ElectronSelector)
ElectronSelector :: ElectronSelector () :
Algorithm("ElectronSelector")
{
}
ElectronSelector::~ElectronSelector() {}
EL::StatusCode ElectronSelector :: setupJob (EL::Job& job)
{
// Here you put code that sets up the job on the submission object
// so that it is ready to work with your algorithm, e.g. you can
// request the D3PDReader service or add output files. Any code you
// put here could instead also go into the submission script. The
// sole advantage of putting it here is that it gets automatically
// activated/deactivated when you add/remove the algorithm from your
// job, which may or may not be of value to you.
ANA_MSG_INFO( "Calling setupJob");
job.useXAOD ();
xAOD::Init( "ElectronSelector" ).ignore(); // call before opening first file
return EL::StatusCode::SUCCESS;
}
EL::StatusCode ElectronSelector :: histInitialize ()
{
// Here you do everything that needs to be done at the very
// beginning on each worker node, e.g. create histograms and output
// trees. This method gets called before any input files are
// connected.
ANA_MSG_INFO( "Calling histInitialize");
ANA_CHECK( xAH::Algorithm::algInitialize());
if ( this->numInstances() > 1 ) { // 1 for this instance
m_isUsedBefore = true;
ANA_MSG_INFO( "\t An algorithm of the same type has been already used " << numInstances() << " times" );
}
return EL::StatusCode::SUCCESS;
}
EL::StatusCode ElectronSelector :: fileExecute ()
{
// Here you do everything that needs to be done exactly once for every
// single file, e.g. collect a list of all lumi-blocks processed
ANA_MSG_INFO( "Calling fileExecute");
return EL::StatusCode::SUCCESS;
}
EL::StatusCode ElectronSelector :: changeInput (bool /*firstFile*/)
{
// Here you do everything you need to do when we change input files,
// e.g. resetting branch addresses on trees. If you are using
// D3PDReader or a similar service this method is not needed.
ANA_MSG_INFO( "Calling changeInput");
return EL::StatusCode::SUCCESS;
}
EL::StatusCode ElectronSelector :: initialize ()
{
// Here you do everything that you need to do after the first input
// file has been connected and before the first event is processed,
// e.g. create additional histograms based on which variables are
// available in the input files. You can also create all of your
// histograms and trees in here, but be aware that this method
// doesn't get called if no events are processed. So any objects
// you create here won't be available in the output if you have no
// input events.
ANA_MSG_INFO( "Initializing ElectronSelector Interface... ");
// Let's see if the algorithm has been already used before:
// if yes, will write object cutflow in a different histogram!
//
// This is the case when the selector algorithm is used for
// preselecting objects, and then again for the final selection
//
ANA_MSG_INFO( "Algorithm name: " << m_name << " - of type " << m_className );
if ( m_useCutFlow ) {
// retrieve the file in which the cutflow hists are stored
//
TFile *file = wk()->getOutputFile (m_cutFlowStreamName);
// retrieve the event cutflows
//
m_cutflowHist = (TH1D*)file->Get("cutflow");
m_cutflowHistW = (TH1D*)file->Get("cutflow_weighted");
m_cutflow_bin = m_cutflowHist->GetXaxis()->FindBin(m_name.c_str());
m_cutflowHistW->GetXaxis()->FindBin(m_name.c_str());
// retrieve the object cutflow
//
m_el_cutflowHist_1 = (TH1D*)file->Get("cutflow_electrons_1");
m_el_cutflow_all = m_el_cutflowHist_1->GetXaxis()->FindBin("all");
m_el_cutflow_author_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("author_cut");
m_el_cutflow_OQ_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("OQ_cut");
m_el_cutflow_deadHVCell_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("deadHVCell_cut");
m_el_cutflow_ptmax_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("ptmax_cut");
m_el_cutflow_ptmin_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("ptmin_cut");
m_el_cutflow_eta_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("eta_cut"); // including crack veto, if applied
m_el_cutflow_z0sintheta_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("z0sintheta_cut");
m_el_cutflow_d0_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("d0_cut");
m_el_cutflow_d0sig_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("d0sig_cut");
m_el_cutflow_BL_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("BL_cut");
m_el_cutflow_PID_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("PID_cut");
m_el_cutflow_iso_cut = m_el_cutflowHist_1->GetXaxis()->FindBin("iso_cut");
if ( m_isUsedBefore ) {
m_el_cutflowHist_2 = (TH1D*)file->Get("cutflow_electrons_2");
m_el_cutflow_all = m_el_cutflowHist_2->GetXaxis()->FindBin("all");
m_el_cutflow_author_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("author_cut");
m_el_cutflow_OQ_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("OQ_cut");
m_el_cutflow_deadHVCell_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("deadHVCell_cut");
m_el_cutflow_ptmax_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("ptmax_cut");
m_el_cutflow_ptmin_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("ptmin_cut");
m_el_cutflow_eta_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("eta_cut"); // including crack veto, if applied
m_el_cutflow_z0sintheta_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("z0sintheta_cut");
m_el_cutflow_d0_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("d0_cut");
m_el_cutflow_d0sig_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("d0sig_cut");
m_el_cutflow_BL_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("BL_cut");
m_el_cutflow_PID_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("PID_cut");
m_el_cutflow_iso_cut = m_el_cutflowHist_2->GetXaxis()->FindBin("iso_cut");
}
}
m_event = wk()->xaodEvent();
m_store = wk()->xaodStore();
ANA_MSG_INFO( "Number of events in file: " << m_event->getEntries() );
m_outAuxContainerName = m_outContainerName + "Aux."; // the period is very important!
// Compatible with Efficiency Nomenclature
if( m_LHOperatingPoint == "LooseAndBLayer" )
m_LHOperatingPoint = "LooseBL";
if ( m_LHOperatingPoint != "VeryLoose" &&
m_LHOperatingPoint != "Loose" &&
m_LHOperatingPoint != "LooseBL" &&
m_LHOperatingPoint != "Medium" &&
m_LHOperatingPoint != "Tight" &&
m_LHOperatingPoint != "VeryLooseLLP" && m_LHOperatingPoint != "VeryLooseNoPix" &&
m_LHOperatingPoint != "LooseLLP" && m_LHOperatingPoint != "LooseNoPix" &&
m_LHOperatingPoint != "MediumLLP" && m_LHOperatingPoint != "MediumNoPix" &&
m_LHOperatingPoint != "TightLLP" && m_LHOperatingPoint != "TightNoPix" ) {
ANA_MSG_ERROR( "Unknown electron likelihood PID requested " << m_LHOperatingPoint);
return EL::StatusCode::FAILURE;
}
if ( m_CutBasedOperatingPoint != "Loose" &&
m_CutBasedOperatingPoint != "Medium" &&
m_CutBasedOperatingPoint != "Tight" ) {
ANA_MSG_ERROR( "Unknown electron cut-based PID requested " << m_CutBasedOperatingPoint);
return EL::StatusCode::FAILURE;
}
// Parse input isolation WP list, split by comma, and put into a vector for later use
// Make sure it's not empty!
//
if ( m_IsoWPList.empty() ) {
ANA_MSG_ERROR("Empty isolation WP list");
}
std::string token;
std::istringstream ss(m_IsoWPList);
while ( std::getline(ss, token, ',') ) {
m_IsoKeys.push_back(token);
}
if ( m_inContainerName.empty() ) {
ANA_MSG_ERROR( "InputContainer is empty!");
return EL::StatusCode::FAILURE;
}
m_numEvent = 0;
m_numObject = 0;
m_numEventPass = 0;
m_weightNumEventPass = 0;
m_numObjectPass = 0;
// ****************************
//
// Initialise Electron ID tools
//
// ****************************
// Assume no local BLayer track quality cut unless doing LooseBL and reading decision from the derivation
m_doBLTrackQualityCut = false;
// initialise PID tool(s) using classes defined in ParticlePIDManager.h
//
// if not using cut-based PID, make sure all the decorations will be set ... by choosing the loosest WP!
if( m_doCutBasedPID ){
std::string cutbasedWP = ( m_doCutBasedPIDcut ) ? m_CutBasedOperatingPoint : "Loose";
m_el_CutBased_PIDManager = new ElectronCutBasedPIDManager( cutbasedWP, msgLvl(MSG::DEBUG) );
if ( m_doCutBasedPIDcut ) {
ANA_MSG_INFO( "Cutting on Electron Cut-Based PID! \n ********************" );
ANA_MSG_INFO( "Selected cut-based WP: " << m_el_CutBased_PIDManager->getSelectedWP() );
} else {
ANA_MSG_INFO( "Will decorate each electron with all Electron Cut-Based PID WPs decision (pass/not pass)!" );
}
if ( m_readIDFlagsFromDerivation ) {
ANA_MSG_INFO( "Reading Electron cut-based ID from DAODs ..." );
ANA_CHECK( m_el_CutBased_PIDManager->setupWPs( false ));
} else {
ANA_MSG_INFO( "Reading Electron cut-based ID from CP Tool ..." );
ANA_CHECK( m_el_CutBased_PIDManager->setupWPs( true, this->m_name ));
}
}// if m_doCutBasedPID
if( m_doLHPID ){
// if not using LH PID, make sure all the decorations will be set ... by choosing the loosest WP!
std::string likelihoodWP = ( m_doLHPIDcut ) ? m_LHOperatingPoint : "VeryLoose";
m_el_LH_PIDManager = new ElectronLHPIDManager( likelihoodWP, msgLvl(MSG::DEBUG) );
if ( m_doLHPIDcut ) {
ANA_MSG_INFO( "Cutting on Electron Likelihood PID! \n ********************" );
ANA_MSG_INFO( "\t Input WP: " << likelihoodWP << " corresponding to actual LikeEnum::Menu WP: " << m_el_LH_PIDManager->getSelectedWP() );
} else {
ANA_MSG_INFO( "Will decorate each electron with all Electron Likelihood PID WPs decision (pass/not pass)!" );
}
if ( m_readIDFlagsFromDerivation ) {
ANA_MSG_INFO( "Reading Electron LH ID from DAODs ..." );
ANA_CHECK( m_el_LH_PIDManager->setupWPs( false ));
} else {
ANA_MSG_INFO( "Reading Electron LH ID from CP Tool ..." );
ANA_CHECK( m_el_LH_PIDManager->setupWPs( true, this->m_name));
}
}// if m_doLHPID
// *************************************
//
// Initialise CP::IsolationSelectionTool
//
// *************************************
// Do this only for the first WP in the list
ANA_MSG_DEBUG( "Adding isolation WP " << m_IsoKeys.at(0) << " to IsolationSelectionTool" );
ANA_CHECK( m_isolationSelectionTool_handle.setProperty("ElectronWP", (m_IsoKeys.at(0)).c_str()));
ANA_CHECK( m_isolationSelectionTool_handle.setProperty("OutputLevel", msg().level()));
if (m_isoDecSuffix!="") ANA_CHECK ( m_isolationSelectionTool_handle.setProperty("IsoDecSuffix", m_isoDecSuffix) );
ANA_CHECK( m_isolationSelectionTool_handle.retrieve());
ANA_MSG_DEBUG("Retrieved tool: " << m_isolationSelectionTool_handle);
m_isolationSelectionTool = dynamic_cast<CP::IsolationSelectionTool*>(m_isolationSelectionTool_handle.get() ); // see header file for why
// Add the remaining input WPs to the tool
// (start from 2nd element)
//
for ( auto WP_itr = std::next(m_IsoKeys.begin()); WP_itr != m_IsoKeys.end(); ++WP_itr ) {
ANA_MSG_DEBUG( "Adding extra isolation WP " << *WP_itr << " to IsolationSelectionTool" );
if ( (*WP_itr).find("UserDefined") != std::string::npos ) {
HelperClasses::EnumParser<xAOD::Iso::IsolationType> isoParser;
std::vector< std::pair<xAOD::Iso::IsolationType, std::string> > myCuts;
myCuts.push_back(std::make_pair<xAOD::Iso::IsolationType, std::string>(isoParser.parseEnum(m_TrackBasedIsoType), m_TrackIsoEff.c_str() ));
myCuts.push_back(std::make_pair<xAOD::Iso::IsolationType, std::string>(isoParser.parseEnum(m_CaloBasedIsoType) , m_CaloIsoEff.c_str() ));
CP::IsolationSelectionTool::IsoWPType iso_type(CP::IsolationSelectionTool::Efficiency);
if ( (*WP_itr).find("Cut") != std::string::npos ) { iso_type = CP::IsolationSelectionTool::Cut; }
ANA_CHECK( m_isolationSelectionTool->addUserDefinedWP((*WP_itr).c_str(), xAOD::Type::Electron, myCuts, "", iso_type));
} else {
ANA_CHECK( m_isolationSelectionTool->addElectronWP( (*WP_itr).c_str() ));
}
}
// ***************************************
//
// Initialise Trig::MatchingTool
//
// ***************************************
//
// NB: need to retrieve the TrigDecisionTool from asg::ToolStore to configure the tool!
// do not initialise if there are no input trigger chains
if( !( m_singleElTrigChains.empty() && m_diElTrigChains.empty() ) ) {
// grab the TrigDecTool from the ToolStore
if(!m_trigDecTool_handle.isUserConfigured()){
ANA_MSG_FATAL("A configured " << m_trigDecTool_handle.typeAndName() << " must have been previously created! Are you creating one in xAH::BasicEventSelection?" );
return EL::StatusCode::FAILURE;
}
ANA_CHECK( m_trigDecTool_handle.retrieve());
ANA_MSG_DEBUG("Retrieved tool: " << m_trigDecTool_handle);
// in AB we need to explicitly retrieve this tool, see https://twiki.cern.ch/twiki/bin/viewauth/Atlas/R22TriggerAnalysis
ANA_CHECK( m_scoreTool.retrieve());
// Run3 got a new trigger navigation
if (m_useRun3navigation) {
m_trigElectronMatchTool_handle = asg::AnaToolHandle<Trig::IMatchingTool>("Trig::R3MatchingTool/TrigR3MatchingTool");
ANA_CHECK( m_trigElectronMatchTool_handle.setProperty( "TrigDecisionTool", m_trigDecTool_handle ));
ANA_CHECK( m_trigElectronMatchTool_handle.setProperty( "ScoringTool", m_scoreTool ));
ANA_CHECK( m_trigElectronMatchTool_handle.setProperty("OutputLevel", msg().level() ));
ANA_CHECK( m_trigElectronMatchTool_handle.retrieve());
ANA_MSG_DEBUG("Retrieved tool: " << m_trigElectronMatchTool_handle);
}
// otherwise we have to configure the Run2-style navigation
else {
if( !isPHYS() ) {
m_trigElectronMatchTool_handle = asg::AnaToolHandle<Trig::IMatchingTool>("Trig::MatchingTool/MatchingTool");;
ANA_CHECK( m_trigElectronMatchTool_handle.setProperty( "TrigDecisionTool", m_trigDecTool_handle ));
ANA_CHECK( m_trigElectronMatchTool_handle.setProperty( "ScoringTool", m_scoreTool ));
ANA_CHECK( m_trigElectronMatchTool_handle.setProperty( "OutputLevel", msg().level() ));
ANA_CHECK( m_trigElectronMatchTool_handle.retrieve());
ANA_MSG_DEBUG("Retrieved tool: " << m_trigElectronMatchTool_handle);
}
else { // For DAOD_PHYS samples
m_trigElectronMatchTool_handle = asg::AnaToolHandle<Trig::IMatchingTool>("Trig::MatchFromCompositeTool/MatchFromCompositeTool");;
ANA_CHECK( m_trigElectronMatchTool_handle.setProperty( "OutputLevel", msg().level() ));
if (!m_trigInputPrefix.empty()){
ANA_CHECK( m_trigElectronMatchTool_handle.setProperty( "InputPrefix", m_trigInputPrefix ));
ANA_CHECK( m_trigElectronMatchTool_handle.setProperty( "RemapBrokenLinks", true) );
}
ANA_CHECK( m_trigElectronMatchTool_handle.retrieve());
ANA_MSG_DEBUG("Retrieved tool: " << m_trigElectronMatchTool_handle);
}
}
} else {
m_doTrigMatch = false;
ANA_MSG_WARNING("***********************************************************");
ANA_MSG_WARNING( "Will not perform any electron trigger matching at this stage b/c :");
ANA_MSG_WARNING("\t -) could not find the TrigDecisionTool in asg::ToolStore");
ANA_MSG_WARNING("\t AND/OR");
ANA_MSG_WARNING("\t -) all input HLT trigger chain lists are empty");
ANA_MSG_WARNING("However, if you really didn't want to do the matching now, it's all good!");
ANA_MSG_WARNING("***********************************************************");
}
// **********************************************************************************************
// Set up the dead HV Removal Tool
if (m_applyDeadHVCellVeto) {
m_deadHVTool.setTypeAndName("AsgDeadHVCellRemovalTool/deadHVTool");
if (m_deadHVTool.retrieve().isFailure()){
ANA_MSG_ERROR("Failed to retrieve DeadHVTool, aborting");
return StatusCode::FAILURE;
}
}
else {
ANA_MSG_WARNING("Not applying veto of dead HV cells although it's recommended - please double check!");
}
ANA_MSG_INFO( "ElectronSelector Interface succesfully initialized!" );
return EL::StatusCode::SUCCESS;
}
EL::StatusCode ElectronSelector :: execute ()
{
// Here you do everything that needs to be done on every single
// events, e.g. read input variables, apply cuts, and fill
// histograms and trees. This is where most of your actual analysis
// code will go.
ANA_MSG_DEBUG( "Applying Electron Selection... ");
const xAOD::EventInfo* eventInfo(nullptr);
ANA_CHECK( HelperFunctions::retrieve(eventInfo, m_eventInfoContainerName, m_event, m_store, msg()) );
// MC event weight
//
float mcEvtWeight(1.0);
static SG::AuxElement::Accessor< float > mcEvtWeightAcc("mcEventWeight");
if ( ! mcEvtWeightAcc.isAvailable( *eventInfo ) ) {
ANA_MSG_ERROR( "mcEventWeight is not available as decoration! Aborting" );
return EL::StatusCode::FAILURE;
}
mcEvtWeight = mcEvtWeightAcc( *eventInfo );
m_numEvent++;
// QUESTION: why this must be done in execute(), and does not work in initialize()?
//
if ( m_numEvent == 1 && m_trigElectronMatchTool_handle.isInitialized() ) {
// parse input electron trigger chain list, split by comma and fill vector
//
std::string singleel_trig;
std::istringstream ss_singleel_trig(m_singleElTrigChains);
while ( std::getline(ss_singleel_trig, singleel_trig, ',') ) {
m_singleElTrigChainsList.push_back(singleel_trig);
}
std::string diel_trig;
std::istringstream ss_diel_trig(m_diElTrigChains);
while ( std::getline(ss_diel_trig, diel_trig, ',') ) {
m_diElTrigChainsList.push_back(diel_trig);
}
ANA_MSG_INFO( "Input single electron trigger chains that will be considered for matching:\n");
for ( auto const &chain : m_singleElTrigChainsList ) { ANA_MSG_INFO( "\t " << chain); }
ANA_MSG_INFO( "\n");
ANA_MSG_INFO( "Input di-electron trigger chains that will be considered for matching:\n");
for ( auto const &chain : m_diElTrigChainsList ) { ANA_MSG_INFO( "\t " << chain); }
ANA_MSG_INFO( "\n");
}
// did any collection pass the cuts?
//
bool eventPass(false);
bool countPass(true); // for cutflow: count for the 1st collection in the syst container - could be better as should only count for the nominal
const xAOD::ElectronContainer* inElectrons(nullptr);
// if input comes from xAOD, or just running one collection,
// then get the one collection and be done with it
//
if ( m_inputAlgoSystNames.empty() ) {
// this will be the collection processed - no matter what!!
//
ANA_CHECK( HelperFunctions::retrieve(inElectrons, m_inContainerName, m_event, m_store, msg()) );
// create output container (if requested)
ConstDataVector<xAOD::ElectronContainer>* selectedElectrons(nullptr);
if ( m_createSelectedContainer ) { selectedElectrons = new ConstDataVector<xAOD::ElectronContainer>(SG::VIEW_ELEMENTS); }
// find the selected electrons, and return if event passes object selection
//
eventPass = executeSelection( inElectrons, mcEvtWeight, countPass, selectedElectrons );
if ( m_createSelectedContainer) {
if ( eventPass ) {
// add ConstDataVector to TStore
ANA_CHECK( m_store->record( selectedElectrons, m_outContainerName ));
} else {
// if the event does not pass the selection, CDV won't be ever recorded to TStore, so we have to delete it!
delete selectedElectrons; selectedElectrons = nullptr;
}
}
} else { // get the list of systematics to run over
// get vector of string giving the syst names of the upstream algo from TStore (rememeber: 1st element is a blank string: nominal case!)
//
std::vector< std::string >* systNames(nullptr);
ANA_CHECK( HelperFunctions::retrieve(systNames, m_inputAlgoSystNames, 0, m_store, msg()) );
// prepare a vector of the names of CDV containers for usage by downstream algos
// must be a pointer to be recorded in TStore
//
auto vecOutContainerNames = std::make_unique< std::vector< std::string > >();
ANA_MSG_DEBUG( " input list of syst size: " << static_cast<int>(systNames->size()) );
// loop over systematic sets
//
bool eventPassThisSyst(false);
for ( auto systName : *systNames ) {
ANA_MSG_DEBUG( " syst name: " << systName << " input container name: " << m_inContainerName+systName );
ANA_CHECK( HelperFunctions::retrieve(inElectrons, m_inContainerName + systName, m_event, m_store, msg()) );
// create output container (if requested) - one for each systematic
//
ConstDataVector<xAOD::ElectronContainer>* selectedElectrons(nullptr);
if ( m_createSelectedContainer ) { selectedElectrons = new ConstDataVector<xAOD::ElectronContainer>(SG::VIEW_ELEMENTS); }
// find the selected electrons, and return if event passes object selection
//
eventPassThisSyst = executeSelection( inElectrons, mcEvtWeight, countPass, selectedElectrons );
if ( countPass ) { countPass = false; } // only count objects/events for 1st syst collection in iteration (i.e., nominal)
if ( eventPassThisSyst ) {
// save the string of syst set under question if event is passing the selection
vecOutContainerNames->push_back( systName );
}
// if for at least one syst set the event passes selection, this will remain true!
//
eventPass = ( eventPass || eventPassThisSyst );
ANA_MSG_DEBUG( " syst name: " << systName << " output container name: " << m_outContainerName+systName );
if ( m_createSelectedContainer ) {
if ( eventPassThisSyst ) {
// add ConstDataVector to TStore
ANA_CHECK( m_store->record( selectedElectrons, m_outContainerName+systName ));
} else {
// if the event does not pass the selection for this syst, CDV won't be ever recorded to TStore, so we have to delete it!
delete selectedElectrons; selectedElectrons = nullptr;
}
}
} // close loop over syst sets
ANA_MSG_DEBUG(" output list of syst size: " << static_cast<int>(vecOutContainerNames->size()) );
// record in TStore the list of systematics names that should be considered down stream
//
ANA_CHECK( m_store->record( std::move(vecOutContainerNames), m_outputAlgoSystNames));
}
// look what we have in TStore
//
if(msgLvl(MSG::VERBOSE)) m_store->print();
if( !eventPass ) {
wk()->skipEvent();
return EL::StatusCode::SUCCESS;
}
return EL::StatusCode::SUCCESS;
}
bool ElectronSelector :: executeSelection ( const xAOD::ElectronContainer* inElectrons, float mcEvtWeight, bool countPass,
ConstDataVector<xAOD::ElectronContainer>* selectedElectrons )
{
const xAOD::VertexContainer* vertices(nullptr);
ANA_CHECK( HelperFunctions::retrieve(vertices, m_vertexContainerName, m_event, m_store, msg()) );
const xAOD::Vertex *pvx = HelperFunctions::getPrimaryVertex(vertices, msg());
int nPass(0); int nObj(0);
static SG::AuxElement::Decorator< char > passSelDecor( "passSel" );
bool passCrackVetoCleaning = true;
const static SG::AuxElement::ConstAccessor<char> acc_CrackVetoCleaning("DFCommonCrackVetoCleaning");
for ( auto el_itr : *inElectrons ) { // duplicated of basic loop
// if only looking at a subset of electrons make sure all are decorated
//
if ( m_nToProcess > 0 && nObj >= m_nToProcess ) {
if ( m_decorateSelectedObjects ) {
passSelDecor( *el_itr ) = -1;
} else {
break;
}
continue;
}
nObj++;
bool passSel = this->passCuts( el_itr, pvx );
if ( m_decorateSelectedObjects ) {
passSelDecor( *el_itr ) = passSel;
}
if ( passSel ) {
// check DFCommonCrackVetoCleaning flag for topocluster association bugfix
if (m_applyCrackVetoCleaning){
if ( !acc_CrackVetoCleaning( *el_itr ) ) passCrackVetoCleaning = false;
}
nPass++;
if ( m_createSelectedContainer ) {
selectedElectrons->push_back( el_itr );
}
}
}
// Fix to EGamma Crack-Electron topocluster association bug for MET (PFlow)
// https://twiki.cern.ch/twiki/bin/view/AtlasProtected/HowToCleanJetsR21#Muons_Reconstructed_as_Jets_in_P
if (m_applyCrackVetoCleaning) {
if (!passCrackVetoCleaning) return false; // skip event
}
// for cutflow: make sure to count passed objects only once (i.e., this flag will be true only for nominal)
//
if ( countPass ) {
m_numObject += nObj;
m_numObjectPass += nPass;
}
ANA_MSG_DEBUG( "Initial electrons: " << nObj << " - Selected electrons: " << nPass );
// apply event selection based on minimal/maximal requirements on the number of objects per event passing cuts
//
if ( m_pass_min > 0 && nPass < m_pass_min ) {
return false;
}
if ( m_pass_max >= 0 && nPass > m_pass_max ) {
return false;
}
// for cutflow: make sure to count passed events only once (i.e., this flag will be true only for nominal)
//
if ( countPass ){
m_numEventPass++;
m_weightNumEventPass += mcEvtWeight;
}
// Perform trigger matching on the "good" (selected) electrons
//
// NB: this part will be skipped if:
//
// 1. the user didn't pass any trigger chains to the algo (see initialize(): in that case, the tool is not even initialised!)
// 2. there are no selected muons in the event
//
if ( m_doTrigMatch && selectedElectrons ) {
unsigned int nSelectedElectrons = selectedElectrons->size();
static SG::AuxElement::Decorator< std::map<std::string,char> > isTrigMatchedMapElDecor( "isTrigMatchedMapEl" );
if ( nSelectedElectrons > 0 ) {
ANA_MSG_DEBUG( "Doing single electron trigger matching...");
for ( auto const &chain : m_singleElTrigChainsList ) {
ANA_MSG_DEBUG( "\t checking trigger chain " << chain);
for ( auto const electron : *selectedElectrons ) {
// For each electron, decorate w/ a map<string,char> with the 'isMatched' info associated
// to each trigger chain in the input list.
// If decoration map doesn't exist for this electron yet, create it (will be done only for the 1st iteration on the chain names)
//
if ( !isTrigMatchedMapElDecor.isAvailable( *electron ) ) {
isTrigMatchedMapElDecor( *electron ) = std::map<std::string,char>();
}
char matched = false;
if (!m_merged_electrons){
matched = ( m_trigElectronMatchTool_handle->match( *electron, chain, m_minDeltaR ) );
} else {
static const SG::AuxElement::ConstAccessor<ElementLink<xAOD::ElectronContainer>> originalElectronLink("originalElectronLink");
auto originalElectron = const_cast<xAOD::Electron*>(*originalElectronLink( *electron ));
matched = ( m_trigElectronMatchTool_handle->match(*originalElectron, chain, m_minDeltaR) );
}
ANA_MSG_DEBUG( "\t\t is electron trigger matched? " << matched);
( isTrigMatchedMapElDecor( *electron ) )[chain] = matched;
}
}
}// if nSelectedElectrons > 0
// If checking dilepton trigger, form lepton pairs and test matching for each one.
// Save a:
//
// multimap< chain, pair< pair<idx_i, idx_j>, ismatched > >
//
// as *event* decoration to store which
// pairs are matched (to a given chain) and which aren't.
// A multimap is used b/c a given key (i.e., a chain) can be associated to more than one pair. This is the case for e.g., trilepton events.
//
// By retrieving this map later on, user can decide what to do with the event
// (Generally one could just loop over the map and save a flag if there's at least one pair that matches a given chain)
if ( nSelectedElectrons > 1 && !m_diElTrigChains.empty() ) {
ANA_MSG_DEBUG( "Doing di-electron trigger matching...");
const xAOD::EventInfo* eventInfo(nullptr);
ANA_CHECK( HelperFunctions::retrieve(eventInfo, m_eventInfoContainerName, m_event, m_store, msg()) );
typedef std::pair< std::pair<unsigned int,unsigned int>, char> dielectron_trigmatch_pair;
typedef std::multimap< std::string, dielectron_trigmatch_pair > dielectron_trigmatch_pair_map;
static SG::AuxElement::Decorator< dielectron_trigmatch_pair_map > diElectronTrigMatchPairMapDecor( "diElectronTrigMatchPairMap" );
for ( auto const &chain : m_diElTrigChainsList ) {
ANA_MSG_DEBUG( "\t checking trigger chain " << chain);
// If decoration map doesn't exist for this event yet, create it (will be done only for the 1st iteration on the chain names)
if ( !diElectronTrigMatchPairMapDecor.isAvailable( *eventInfo ) ) {
diElectronTrigMatchPairMapDecor( *eventInfo ) = dielectron_trigmatch_pair_map();
}
std::vector<const xAOD::IParticle*> myElectrons;
for ( unsigned int iel = 0; iel < selectedElectrons->size()-1; ++iel ) {
for ( unsigned int jel = iel+1; jel < selectedElectrons->size(); ++jel ) {
// test a new pair
myElectrons.clear();
myElectrons.push_back( selectedElectrons->at(iel) );
myElectrons.push_back( selectedElectrons->at(jel) );
// check whether the pair is matched
char matched = m_trigElectronMatchTool_handle->match( myElectrons, chain, m_minDeltaR );
ANA_MSG_DEBUG( "\t\t is the electron pair ("<<iel<<","<<jel<<") trigger matched? " << matched);
// set basic matching information
( isTrigMatchedMapElDecor( *myElectrons[0] ) )[chain] = matched;
( isTrigMatchedMapElDecor( *myElectrons[1] ) )[chain] = matched;
// set pair decision information
std::pair <unsigned int, unsigned int> chain_idxs = std::make_pair(iel,jel);
dielectron_trigmatch_pair chain_decision = std::make_pair(chain_idxs,matched);
diElectronTrigMatchPairMapDecor( *eventInfo ).insert( std::pair< std::string, dielectron_trigmatch_pair >(chain,chain_decision) );
}
}
}//for m_diElTrigChainsList
} // if electrons
}// if m_doTrigMatch
return true;
}
EL::StatusCode ElectronSelector :: postExecute ()
{
// Here you do everything that needs to be done after the main event
// processing. This is typically very rare, particularly in user
// code. It is mainly used in implementing the NTupleSvc.
ANA_MSG_DEBUG( "Calling postExecute");
return EL::StatusCode::SUCCESS;
}
EL::StatusCode ElectronSelector :: finalize ()
{
// This method is the mirror image of initialize(), meaning it gets
// called after the last event has been processed on the worker node
// and allows you to finish up any objects you created in
// initialize() before they are written to disk. This is actually
// fairly rare, since this happens separately for each worker node.
// Most of the time you want to do your post-processing on the
// submission node after all your histogram outputs have been
// merged. This is different from histFinalize() in that it only
// gets called on worker nodes that processed input events.
ANA_MSG_INFO( "Deleting tool instances...");
if ( m_el_CutBased_PIDManager ) { delete m_el_CutBased_PIDManager; m_el_CutBased_PIDManager = nullptr; }
if ( m_el_LH_PIDManager ) { delete m_el_LH_PIDManager; m_el_LH_PIDManager = nullptr; }
if ( m_useCutFlow ) {
ANA_MSG_INFO( "Filling cutflow");
m_cutflowHist ->SetBinContent( m_cutflow_bin, m_numEventPass );
m_cutflowHistW->SetBinContent( m_cutflow_bin, m_weightNumEventPass );
}
return EL::StatusCode::SUCCESS;
}
EL::StatusCode ElectronSelector :: histFinalize ()
{
// This method is the mirror image of histInitialize(), meaning it
// gets called after the last event has been processed on the worker
// node and allows you to finish up any objects you created in
// histInitialize() before they are written to disk. This is
// actually fairly rare, since this happens separately for each
// worker node. Most of the time you want to do your
// post-processing on the submission node after all your histogram
// outputs have been merged. This is different from finalize() in
// that it gets called on all worker nodes regardless of whether
// they processed input events.
ANA_MSG_INFO( "Calling histFinalize");
ANA_CHECK( xAH::Algorithm::algFinalize());
return EL::StatusCode::SUCCESS;
}
int ElectronSelector :: passCuts( const xAOD::Electron* electron, const xAOD::Vertex *primaryVertex ) {
float et = electron->pt();
// all the eta cuts are done using the measurement of the cluster position with the 2nd layer cluster,
// as for Egamma CP recommendation
//
float eta = ( electron->caloCluster() ) ? electron->caloCluster()->etaBE(2) : -999.0;
// fill cutflow bin 'all' before any cut
if( !m_isUsedBefore && m_useCutFlow) m_el_cutflowHist_1->Fill( m_el_cutflow_all, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_all, 1 ); }
// *********************************************************************************************************************************************************************
//
// author cut
//
if ( m_doAuthorCut ) {
if ( !( electron->author(xAOD::EgammaParameters::AuthorElectron) || electron->author(xAOD::EgammaParameters::AuthorAmbiguous) ) ) {
ANA_MSG_DEBUG( "Electron failed author kinematic cut." );
return 0;
}
}
if (!m_isUsedBefore && m_useCutFlow) m_el_cutflowHist_1->Fill( m_el_cutflow_author_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_author_cut, 1 ); }
// *********************************************************************************************************************************************************************
//
// Object Quality cut
//
if ( m_doOQCut ) {
if( !electron->isGoodOQ(xAOD::EgammaParameters::BADCLUSELECTRON) ){
ANA_MSG_DEBUG( "Electron failed Object Quality cut BADCLUSELECTRON." );
return 0;
}
}
if (!m_isUsedBefore && m_useCutFlow) m_el_cutflowHist_1->Fill( m_el_cutflow_OQ_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_OQ_cut, 1 ); }
// *********************************************************************************************************************************************************************
//
// Dead HV Cell veto (affects only 2016 data)
//
if ( m_applyDeadHVCellVeto ) {
if( !m_deadHVTool->accept(electron) ){
ANA_MSG_DEBUG( "Electron failed dead HV cell veto." );
return 0;
}
}
if (!m_isUsedBefore && m_useCutFlow) m_el_cutflowHist_1->Fill( m_el_cutflow_deadHVCell_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_deadHVCell_cut, 1 ); }
// *********************************************************************************************************************************************************************
//
// pT max cut
//
if ( m_pT_max != 1e8 ) {
if ( et > m_pT_max ) {
ANA_MSG_DEBUG( "Electron failed pT max cut." );
return 0;
}
}
if (!m_isUsedBefore && m_useCutFlow) m_el_cutflowHist_1->Fill( m_el_cutflow_ptmax_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_ptmax_cut, 1 ); }
// *********************************************************************************************************************************************************************
//
// pT min cut
//
if ( m_pT_min != 1e8 ) {
if ( et < m_pT_min ) {
ANA_MSG_DEBUG( "Electron failed pT min cut." );
return 0;
}
}
if (!m_isUsedBefore && m_useCutFlow) m_el_cutflowHist_1->Fill( m_el_cutflow_ptmin_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_ptmin_cut, 1 ); }
// *********************************************************************************************************************************************************************
//
// eta cuts
//
// |eta| max cut
//
if ( m_eta_max != 1e8 ) {
if ( fabs(eta) > m_eta_max ) {
ANA_MSG_DEBUG( "Electron failed |eta| max cut." );
return 0;
}
}
// |eta| crack veto
//
if ( m_vetoCrack ) {
if ( fabs( eta ) > 1.37 && fabs( eta ) < 1.52 ) {
ANA_MSG_DEBUG( "Electron failed |eta| crack veto cut." );
return 0;
}
}
if (!m_isUsedBefore && m_useCutFlow) m_el_cutflowHist_1->Fill( m_el_cutflow_eta_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_eta_cut, 1 ); }
// *********************************************************************************************************************************************************************
//
// Tracking cuts AFTER acceptance, in case of derivation reduction.
//
const xAOD::TrackParticle* tp = electron->trackParticle();
if ( !tp ) {
ANA_MSG_DEBUG("Electron has no TrackParticle. Won't be selected.");
return 0;
}
const xAOD::EventInfo* eventInfo(nullptr);
ANA_CHECK( HelperFunctions::retrieve(eventInfo, m_eventInfoContainerName, m_event, m_store, msg()) );
double d0_significance = xAOD::TrackingHelpers::d0significance( tp, eventInfo->beamPosSigmaX(), eventInfo->beamPosSigmaY(), eventInfo->beamPosSigmaXY() );
// Take distance between z0 and zPV ( after referring the PV z coordinate to the beamspot position, given by vz() ), multiplied by sin(theta)
// see https://twiki.cern.ch/twiki/bin/view/AtlasProtected/InDetTrackingDC14 for further reference
//
float z0sintheta = 1e8;
if (primaryVertex) z0sintheta = ( tp->z0() + tp->vz() - primaryVertex->z() ) * sin( tp->theta() );
// z0*sin(theta) cut
//
if ( m_z0sintheta_max != 1e8 ) {
if ( !( fabs(z0sintheta) < m_z0sintheta_max ) ) {
ANA_MSG_DEBUG( "Electron failed z0*sin(theta) cut." );
return 0;
}
}
if ( !m_isUsedBefore && m_useCutFlow ) m_el_cutflowHist_1->Fill( m_el_cutflow_z0sintheta_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_z0sintheta_cut, 1 ); }
// decorate electron w/ z0*sin(theta) info
static SG::AuxElement::Decorator< float > z0sinthetaDecor("z0sintheta");
z0sinthetaDecor( *electron ) = z0sintheta;
// d0 cut
//
if ( m_d0_max != 1e8 ) {
if ( !( tp->d0() < m_d0_max ) ) {
ANA_MSG_DEBUG( "Electron failed d0 cut.");
return 0;
}
}
if ( !m_isUsedBefore && m_useCutFlow ) m_el_cutflowHist_1->Fill( m_el_cutflow_d0_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_d0_cut, 1 ); }
// d0sig cut
//
if ( m_d0sig_max != 1e8 ) {
if ( !( fabs(d0_significance) < m_d0sig_max ) ) {
ANA_MSG_DEBUG( "Electron failed d0 significance cut.");
return 0;
}
}
if ( !m_isUsedBefore && m_useCutFlow) m_el_cutflowHist_1->Fill( m_el_cutflow_d0sig_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_d0sig_cut, 1 ); }
// decorate electron w/ d0sig info
static SG::AuxElement::Decorator< float > d0SigDecor("d0sig");
d0SigDecor( *electron ) = static_cast<float>(d0_significance);
// BLayer track quality
static SG::AuxElement::Decorator< bool > bLayerDecor("bLayerPass");
// this is taken from ElectronPhotonSelectorTools/Root/AsgElectronLikelihoodTool.cxx
uint8_t expectBlayer(true);
uint8_t nBlayerHits(0);
uint8_t nBlayerOutliers(0);
tp->summaryValue(expectBlayer, xAOD::expectBLayerHit);
tp->summaryValue(nBlayerHits, xAOD::numberOfBLayerHits);
tp->summaryValue(nBlayerOutliers, xAOD::numberOfBLayerOutliers);
bool bLayerPass = expectBlayer && (nBlayerHits+nBlayerOutliers) >= 1;
bLayerDecor( *electron ) = bLayerPass;
if ( m_doBLTrackQualityCut ) {
if ( !bLayerPass ) {
ANA_MSG_DEBUG( "Electron failed BL track quality cut.");
return 0;
}
if ( !m_isUsedBefore && m_useCutFlow ) m_el_cutflowHist_1->Fill( m_el_cutflow_BL_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_BL_cut, 1 ); }
}
// *********************************************************************************************************************************************************************
//
// electron PID cuts
//
//
// likelihood PID
//
// set default values for *this* electron decorations
//
if( m_doLHPID ) {
m_el_LH_PIDManager->setDecorations( electron );
if ( m_readIDFlagsFromDerivation ) {
const static SG::AuxElement::ConstAccessor<char> acc_EG_Loose("DFCommonElectronsLHLoose");
const static SG::AuxElement::ConstAccessor<char> acc_EG_Medium("DFCommonElectronsLHMedium");
const static SG::AuxElement::ConstAccessor<char> acc_EG_Tight("DFCommonElectronsLHTight");
if ( m_doLHPIDcut ) {
bool passSelID(false);
SG::AuxElement::ConstAccessor< char > LHDecision( "DFCommonElectronsLH" + m_LHOperatingPoint );
if( LHDecision.isAvailable( *electron ) ){
if (m_doModifiedEleId){
if(m_LHOperatingPoint == "Tight"){
passSelID = acc_EG_Medium( *electron ) && acc_EG_Tight( *electron );
} else if(m_LHOperatingPoint == "Medium"){
passSelID = ( acc_EG_Loose( *electron ) && acc_EG_Medium( *electron ) ) || acc_EG_Tight( *electron );
} else if (m_LHOperatingPoint == "Loose") {
passSelID = acc_EG_Medium( *electron ) || acc_EG_Loose( *electron );
} else { passSelID = LHDecision( *electron ); }
}
else {
passSelID = LHDecision( *electron );
}
}
if ( !passSelID ) {
ANA_MSG_DEBUG( "Electron failed likelihood PID cut w/ operating point " << m_LHOperatingPoint );
return 0;
}
}
const std::set<std::string> myLHWPs = m_el_LH_PIDManager->getValidWPs();
for ( auto it : (myLHWPs) ) {
const std::string decorWP = "LH"+it;
bool passThisID(false);
SG::AuxElement::ConstAccessor< char > LHDecisionAll( "DFCommonElectrons" + decorWP );
if( LHDecisionAll.isAvailable( *electron ) ){
if (m_doModifiedEleId){
if(decorWP == "LHTight"){
passThisID = acc_EG_Medium( *electron ) && acc_EG_Tight( *electron );
} else if(decorWP == "LHMedium"){
passThisID = ( acc_EG_Loose( *electron ) && acc_EG_Medium( *electron ) ) || acc_EG_Tight( *electron );
} else if (decorWP == "LHLoose") {
passThisID = acc_EG_Medium( *electron ) || acc_EG_Loose( *electron );
} else { passThisID = LHDecisionAll( *electron ); }
}
else {
passThisID = LHDecisionAll( *electron );
}
}
ANA_MSG_DEBUG( "Decorating electron with decision for LH WP : " << decorWP );
ANA_MSG_DEBUG( "\t does electron pass " << decorWP << "? " << passThisID );
electron->auxdecor<char>(decorWP) = static_cast<char>( passThisID );
}
}
else {
// retrieve only tools with WP >= selected WP, cut electrons if not satisfying selected WP, and decorate w/ tool decision all the others
//
typedef std::multimap< std::string, AsgElectronLikelihoodTool* > LHToolsMap;
LHToolsMap myLHTools = m_el_LH_PIDManager->getValidWPTools();
if ( m_doLHPIDcut && !( ( myLHTools.find( m_LHOperatingPoint )->second )->accept( electron ) ) ) {
ANA_MSG_DEBUG( "Electron failed likelihood PID cut w/ operating point " << m_LHOperatingPoint );
return 0;
}
for ( auto it : (myLHTools) ) {
const std::string decorWP = "LH" + it.first;
ANA_MSG_DEBUG( "Decorating electron with decision for LH WP : " << decorWP );
ANA_MSG_DEBUG( "\t does electron pass " << decorWP << " ? " << static_cast<bool>( it.second->accept( electron ) ) );
electron->auxdecor<char>(decorWP) = static_cast<bool>( it.second->accept( electron ) );
}
}
}// if m_doLHPID
//
// cut-based PID
//
// set default values for *this* electron decorations
//
if( m_doCutBasedPID ) {
m_el_CutBased_PIDManager->setDecorations( electron );
if ( m_readIDFlagsFromDerivation ) {
if ( m_doCutBasedPIDcut ) {
bool passSelID(false);
static SG::AuxElement::ConstAccessor< char > CutDecision( "DFCommonElectronsIsEM" + m_CutBasedOperatingPoint );
if( CutDecision.isAvailable( *electron ) )
passSelID = CutDecision( *electron );
if ( !passSelID ) {
ANA_MSG_DEBUG( "Electron failed cut-based PID cut w/ operating point " << m_CutBasedOperatingPoint );
return 0;
}
}
const std::set<std::string> myCutBasedWPs = m_el_CutBased_PIDManager->getValidWPs();
for ( auto it : (myCutBasedWPs) ) {
const std::string decorWP = "IsEM"+it;
bool passThisID(false);
SG::AuxElement::ConstAccessor< char > CutDecisionAll( "DFCommonElectrons" + decorWP );
if( CutDecisionAll.isAvailable( *electron ) )
passThisID = CutDecisionAll( *electron );
ANA_MSG_DEBUG( "Decorating electron with decision for cut-based WP : " << decorWP );
ANA_MSG_DEBUG( "\t does electron pass " << decorWP << "? " << passThisID );
electron->auxdecor<char>(decorWP) = static_cast<char>( passThisID );
}
} else {
// retrieve only tools with WP >= selected WP, cut electrons if not satisfying selected WP, and decorate w/ tool decision all the others
//
typedef std::multimap< std::string, AsgElectronIsEMSelector* > CutBasedToolsMap;
CutBasedToolsMap myCutBasedTools = m_el_CutBased_PIDManager->getValidWPTools();
if ( m_doCutBasedPIDcut && !( ( myCutBasedTools.find( m_CutBasedOperatingPoint )->second )->accept( electron ) ) ) {
ANA_MSG_DEBUG( "Electron failed cut-based PID cut." );
return 0;
}
for ( auto it : (myCutBasedTools) ) {
const std::string decorWP = "IsEM"+it.second->getOperatingPointName( );
ANA_MSG_DEBUG( "Decorating electron with decision for cut-based WP : " << decorWP );
ANA_MSG_DEBUG( "\t does electron pass " << decorWP << "? " << static_cast<bool>( it.second->accept( electron ) ) );
electron->auxdecor<char>(decorWP) = static_cast<bool>( it.second->accept( electron ) );
}
}
}// if m_doCutBasedPID
if (!m_isUsedBefore && m_useCutFlow) m_el_cutflowHist_1->Fill( m_el_cutflow_PID_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_PID_cut, 1 ); }
// *********************************************************************************************************************************************************************
//
// isolation cut
//
// Get the "list" of input WPs with the accept() decision from the tool
//
auto accept_list = m_isolationSelectionTool_handle->accept( *electron );
// Decorate w/ decision for all input WPs
//
std::string base_decor("isIsolated");
for ( auto WP_itr : m_IsoKeys ) {
std::string decorWP = base_decor + "_" + WP_itr;
ANA_MSG_DEBUG( "Decorate electron with " << decorWP << " - accept() ?" << accept_list.getCutResult( WP_itr.c_str()) );
electron->auxdecor<char>(decorWP) = static_cast<bool>( accept_list.getCutResult( WP_itr.c_str() ) );
}
// Apply the cut if needed
//
if ( !m_MinIsoWPCut.empty() && !accept_list.getCutResult( m_MinIsoWPCut.c_str() ) ) {
ANA_MSG_DEBUG( "Electron failed isolation cut " << m_MinIsoWPCut );
return 0;
}
if (!m_isUsedBefore && m_useCutFlow) m_el_cutflowHist_1->Fill( m_el_cutflow_iso_cut, 1 );
if ( m_isUsedBefore && m_useCutFlow ) { m_el_cutflowHist_2->Fill( m_el_cutflow_iso_cut, 1 ); }
return 1;
}