Program Listing for File MuonInFatJetCorrector.cxx¶
↰ Return to documentation for file (Root/MuonInFatJetCorrector.cxx
)
#include <iostream>
#include <typeinfo>
#include <EventLoop/Job.h>
#include <EventLoop/Algorithm.h>
#include <EventLoop/Worker.h>
#include "xAODJet/JetContainer.h"
#include "xAODMuon/MuonContainer.h"
#include "xAODTruth/TruthParticleContainer.h"
#include "MuonSelectorTools/MuonSelectionTool.h"
#include "xAODAnaHelpers/MuonInFatJetCorrector.h"
#include "xAODAnaHelpers/HelperClasses.h"
#include "xAODAnaHelpers/HelperFunctions.h"
// Needed to distribute the algorithm to the workers
ClassImp(MuonInFatJetCorrector)
MuonInFatJetCorrector :: MuonInFatJetCorrector() :
Algorithm("MuonInFatJetCorrector")
{
}
EL::StatusCode MuonInFatJetCorrector :: setupJob(EL::Job& job)
{
ANA_MSG_DEBUG("Calling setupJob");
job.useXAOD();
xAOD::Init("MuonInFatJetCorrector").ignore();
return EL::StatusCode::SUCCESS;
}
EL::StatusCode MuonInFatJetCorrector :: histInitialize()
{
ANA_MSG_DEBUG("Calling histInitialize");
ANA_CHECK(xAH::Algorithm::algInitialize());
return EL::StatusCode::SUCCESS;
}
EL::StatusCode MuonInFatJetCorrector :: fileExecute()
{
ANA_MSG_DEBUG("Calling fileExecute");
return EL::StatusCode::SUCCESS;
}
EL::StatusCode MuonInFatJetCorrector :: changeInput(bool /*firstFile*/)
{
ANA_MSG_DEBUG("Calling changeInput");
return EL::StatusCode::SUCCESS;
}
EL::StatusCode MuonInFatJetCorrector :: initialize()
{
ANA_MSG_DEBUG("Calling initialize");
m_event = wk()->xaodEvent();
m_store = wk()->xaodStore();
//
// Automatically determine calibrated mass decorators, if asked
m_calibratedMassDecorator=(isMC())?m_calibratedMassDecoratorFullSim:m_calibratedMassDecoratorData;
return EL::StatusCode::SUCCESS;
}
EL::StatusCode MuonInFatJetCorrector :: execute()
{
//
// Do muon matching
ANA_CHECK(matchTrackJetsToMuons());
//
// Loop over large-R jets (all systematics) and calculate correction
std::vector<std::string>* systNames(nullptr);
if ( !m_inputAlgo.empty() )
{
ANA_CHECK( HelperFunctions::retrieve(systNames, m_inputAlgo, 0, m_store, msg()) );
}
else
{
systNames=new std::vector<std::string>({""});
}
// Decorator holding muon in fatjet corrected fatjets.
static SG::AuxElement::Decorator<TLorentzVector> dec_correctedFatJets_tlv("correctedFatJets_tlv");
for(const std::string& systName : *systNames)
{
// Retrieve calibrated fatjets.
const xAOD::JetContainer *fatJets(nullptr);
ANA_CHECK(HelperFunctions::retrieve(fatJets, m_fatJetContainerName+systName, m_event, m_store, msg()));
// Loop over fatjets
for(const xAOD::Jet *fatJet : *fatJets)
{
// Get corrected fatjet.
TLorentzVector correctedVector = getHbbCorrectedVector(*fatJet);
dec_correctedFatJets_tlv(*fatJet) = correctedVector;
}
}
// Clean up systematics list if none exists
if(m_inputAlgo.empty())
delete systNames;
return EL::StatusCode::SUCCESS;
}
EL::StatusCode MuonInFatJetCorrector :: postExecute ()
{
ANA_MSG_DEBUG("Calling postExecute");
return EL::StatusCode::SUCCESS;
}
EL::StatusCode MuonInFatJetCorrector :: finalize()
{
return EL::StatusCode::SUCCESS;
}
EL::StatusCode MuonInFatJetCorrector :: histFinalize ()
{
ANA_MSG_DEBUG( "Calling histFinalize");
ANA_CHECK( xAH::Algorithm::algFinalize());
return EL::StatusCode::SUCCESS;
}
TLorentzVector MuonInFatJetCorrector::getHbbCorrectedVector(const xAOD::Jet& jet)
{
/* Steps:
1. Get all track jets asssociated with the ungroomed jet
2. Match muons to these b-tagged track-jets
- if more than 1 muon matches a track jet, only use the muon closest in DR
3. Correct the fat-jet mass by putting the matched muon back
*/
//
// Step 1
std::vector<const xAOD::Jet*> associated_trackJets;
// get the element links to the parent, ungroomed jet
static const SG::AuxElement::ConstAccessor<ElementLink<xAOD::JetContainer>> acc_Parent("Parent");
if (!acc_Parent.isAvailable(jet))
{
ANA_MSG_FATAL("Parent (ungroomed) jet collection does not exist.");
return TLorentzVector();
}
ElementLink<xAOD::JetContainer> parentEL = acc_Parent(jet);
if (!parentEL.isValid())
{
ANA_MSG_FATAL("Parent link is not valid.");
return TLorentzVector();
}
// access the track jets
const xAOD::Jet* parentJet = *parentEL;
if (!parentJet->getAssociatedObjects<xAOD::Jet>(m_trackJetLinkName, associated_trackJets))
{
ANA_MSG_FATAL("No associated track jets found on parent jet.");
return TLorentzVector();
}
// get trackjets of interest
std::vector<const xAOD::Jet*> associated_trackJets_filtered;
for (const xAOD::Jet* trackJet : associated_trackJets)
{
if (trackJet->pt() < m_trackJetPtMin) continue;
if (fabs(trackJet->eta()) > m_trackJetEtaMax) continue;
if (trackJet->numConstituents() < m_trackJetNConst) continue;
associated_trackJets_filtered.push_back(trackJet);
}
std::sort(associated_trackJets_filtered.begin(), associated_trackJets_filtered.end(), [](const xAOD::Jet* lhs, const xAOD::Jet* rhs) -> bool { return (lhs->pt() > rhs->pt()); });
//
// Step 2
std::vector<const xAOD::Muon*> matched_muons;
for (const xAOD::Jet* trackJet : associated_trackJets_filtered)
{
const xAOD::Muon* closest_muon=nullptr;
float maxDR=m_muonDrMax;
// get muons from jet decoration
static const SG::AuxElement::Accessor<std::vector<ElementLink<xAOD::MuonContainer>>> acc_MuonsInTrackJet("MuonsInTrackJet");
if(!acc_MuonsInTrackJet.isAvailable(*trackJet))
{
ANA_MSG_FATAL("No muons associated to track jet.");
return TLorentzVector();
}
std::vector<ElementLink<xAOD::MuonContainer>> associated_muons=acc_MuonsInTrackJet(*trackJet);
for (const ElementLink<xAOD::MuonContainer>& muonEL : associated_muons)
{
const xAOD::Muon* muon=(*muonEL);
// muon quality selection
if (muon->pt() < m_muonPtMin) continue;
if (muon->quality() > xAOD::Muon::Medium) continue;
if (fabs(muon->eta()) > m_muonEtaMax) continue;
// find clostest muon
float DR = trackJet->p4().DeltaR(muon->p4());
float cutDR=std::min(0.4,0.04 + 10000.0/muon->pt());
if (DR > cutDR) continue;
if (DR > maxDR) continue;
maxDR = DR;
closest_muon = muon;
}
// check if the closest muon was already selected
if(std::find(matched_muons.begin(),matched_muons.end(),closest_muon)!=matched_muons.end())
{
closest_muon = nullptr;
ANA_MSG_DEBUG("Muon duplicate found! Skipping.");
break;
}
if (closest_muon)
matched_muons.push_back(closest_muon);
}
//
// Step 3
xAOD::JetFourMom_t corrected_jet_p4 = getMuonCorrectedJetFourMom(jet, matched_muons, Scheme::Combined);
TLorentzVector corrected_jet(corrected_jet_p4.x(), corrected_jet_p4.y(), corrected_jet_p4.z(), corrected_jet_p4.t());
return corrected_jet;
}
EL::StatusCode MuonInFatJetCorrector::matchTrackJetsToMuons() const
{
// retrieve muons from StoreGate
const xAOD::MuonContainer *muons(nullptr);
ANA_CHECK(HelperFunctions::retrieve(muons, m_muonContainerName, m_event, m_store, msg()));
// retrieve track jets from StoreGate
const xAOD::JetContainer *trackJets(nullptr);
ANA_CHECK(HelperFunctions::retrieve(trackJets, m_trackJetContainerName, m_event, m_store, msg()));
// decorate all track jets by default, no selection, no muon overlap removal (will be done later)
static SG::AuxElement::Decorator<std::vector<ElementLink<xAOD::MuonContainer>>> dec_MuonsInTrackJet("MuonsInTrackJet");
for (const xAOD::Jet* trackJet : *trackJets)
{
std::vector<ElementLink<xAOD::MuonContainer>> muons_in_jet;
uint32_t idx=0;
for (const xAOD::Muon* muon : *muons)
{
float DR=trackJet->p4().DeltaR(muon->p4());
if (DR < m_muonDrMax)
{
ElementLink<xAOD::MuonContainer> muonEL(*muons, idx);
muons_in_jet.push_back(muonEL);
}
++idx;
}
dec_MuonsInTrackJet(*trackJet) = muons_in_jet;
ANA_MSG_DEBUG("Found " << muons_in_jet.size() << " muons within R < " << m_muonDrMax << " of associated track jet.");
}
return StatusCode::SUCCESS;
}
const xAOD::JetFourMom_t MuonInFatJetCorrector::getMuonCorrectedJetFourMom(const xAOD::Jet &jet, std::vector<const xAOD::Muon*> muons, Scheme scheme, bool useJMSScale) const
{
xAOD::JetFourMom_t JetCorr_tlv = jet.jetP4();
if (muons.size() == 0)
return JetCorr_tlv;
ANA_MSG_DEBUG("Derive muon-in-jet correction: nMuons = " << (int) muons.size() << "\tMuonCorrectionScheme = " << scheme << "\tuseJMSScale = " << useJMSScale);
switch(scheme)
{
case Scheme::Calorimeter:
{
// muon-in-jet correction for jets calibrated using calorimeter mass
xAOD::JetFourMom_t CaloJet_tlv = jet.jetP4();
if (useJMSScale) CaloJet_tlv = jet.jetP4(m_calibratedMassDecorator+"Calo");
for(const xAOD::Muon* muon : muons)
{
// get energy loss of muon in the calorimeter
float eLoss=0.0;
muon->parameter(eLoss, xAOD::Muon::EnergyLoss);
ANA_MSG_DEBUG("Energy loss in calorimter = " << eLoss);
// use muon tlv to get x/y/z compontent of energy loss
TLorentzVector muon_tlv = muon->p4();
double eLossX = eLoss * sin(muon_tlv.Theta()) * cos(muon_tlv.Phi());
double eLossY = eLoss * sin(muon_tlv.Theta()) * sin(muon_tlv.Phi());
double eLossZ = eLoss * cos(muon_tlv.Theta());
TLorentzVector mLoss(eLossX, eLossY, eLossZ, eLoss);
TLorentzVector muonCorr_tlv = muon_tlv - mLoss;
// apply muon-in-jet correction for track-assisted mass calibration to output jet TLV
CaloJet_tlv += xAOD::JetFourMom_t(muonCorr_tlv.Pt(), muonCorr_tlv.Eta(), muonCorr_tlv.Phi(), muonCorr_tlv.M());
}
JetCorr_tlv = CaloJet_tlv;
break;
}
case Scheme::TrackAssisted:
{
// muon-in-jet correction for jets calibrated using track-assisted mass
xAOD::JetFourMom_t TAJet_tlv = jet.jetP4();
if (useJMSScale) TAJet_tlv = jet.jetP4(m_calibratedMassDecorator+"TA");
xAOD::JetFourMom_t CaloJet_tlv = jet.jetP4(m_calibratedMassDecorator+"Calo");
xAOD::JetFourMom_t CaloJetCorr_tlv = getMuonCorrectedJetFourMom(jet, muons, Scheme::Calorimeter, true);
float TAJetCorr_m = TAJet_tlv.M() / CaloJet_tlv.Pt() * CaloJetCorr_tlv.Pt() ;
float TAJetCorr_pt = sqrt((CaloJetCorr_tlv.E() * CaloJetCorr_tlv.E()) - (TAJetCorr_m * TAJetCorr_m)) / cosh(CaloJetCorr_tlv.Eta());
// apply muon-in-jet correction for track-assisted mass calibration to output jet TLV
JetCorr_tlv = xAOD::JetFourMom_t(TAJetCorr_pt, CaloJetCorr_tlv.Eta(), CaloJetCorr_tlv.Phi(), TAJetCorr_m);
break;
}
case Scheme::Combined:
{
// muon-in-jet correction for jets calibrated using combined mass
xAOD::JetFourMom_t TAJet_tlv = jet.jetP4(m_calibratedMassDecorator+"TA");
xAOD::JetFourMom_t TAJetCorr_tlv = getMuonCorrectedJetFourMom(jet, muons, Scheme::TrackAssisted, true);
xAOD::JetFourMom_t CaloJet_tlv = jet.jetP4(m_calibratedMassDecorator+"Calo");
xAOD::JetFourMom_t CaloJetCorr_tlv = getMuonCorrectedJetFourMom(jet, muons, Scheme::Calorimeter , true);
xAOD::JetFourMom_t CombJet_tlv = jet.jetP4();
float CaloWeight = (CombJet_tlv.M() - TAJet_tlv.M()) / (CaloJet_tlv.M() - TAJet_tlv.M());
float TAWeight = (CaloJet_tlv.M() - CombJet_tlv.M()) / (CaloJet_tlv.M() - TAJet_tlv.M());
ANA_MSG_DEBUG("CaloWeight = " << CaloWeight << "\tTAWeight = " << TAWeight);
float CombJetCorr_m = CaloWeight * CaloJetCorr_tlv.M() + TAWeight * TAJetCorr_tlv.M();
float CombJetCorr_pt = sqrt((CaloJetCorr_tlv.E() * CaloJetCorr_tlv.E()) - (CombJetCorr_m * CombJetCorr_m)) / cosh(CaloJetCorr_tlv.Eta());
// apply muon-in-jet correction for track-assisted mass calibration to output jet TLV
JetCorr_tlv = xAOD::JetFourMom_t(CombJetCorr_pt, CaloJetCorr_tlv.Eta(), CaloJetCorr_tlv.Phi(), CombJetCorr_m);
break;
}
case SimpleMuon:
{
// unknown mass calibration; just add muon 4-momentum
for(const xAOD::Muon* muon : muons)
{
JetCorr_tlv += xAOD::JetFourMom_t(muon->pt(), muon->eta(), muon->phi(), muon->m());
}
break;
}
default:
{
ANA_MSG_FATAL("Unknown muon correction scheme.");
}
}
ANA_MSG_DEBUG("Before muon-in-jet: pt = " << jet.pt() << "\t eta = " << jet.eta()
<< "\tphi = " << jet.phi() << "\tm = " << jet.m());
ANA_MSG_DEBUG("After muon-in-jet: pt = " << JetCorr_tlv.pt() << "\teta = " << JetCorr_tlv.eta()
<< "\tphi = " << JetCorr_tlv.phi() << "\tm = " << JetCorr_tlv.M());
for(const xAOD::Muon* muon : muons)
{
ANA_MSG_DEBUG("muons: pt = " << muon->pt() << "\teta = " << muon->eta()
<< "\tphi = " << muon->phi() << "\tm = " << muon->m());
}
return JetCorr_tlv;
}