Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LowPtElectrons: NanoAOD integration (back port of #33817) #33992

Merged
5 changes: 3 additions & 2 deletions DataFormats/EgammaCandidates/interface/GsfElectron.h
Expand Up @@ -836,6 +836,7 @@ class GsfElectron : public RecoCandidate
// setters
void setCorrectedEcalEnergyError( float newEnergyError ) ;
void setCorrectedEcalEnergy( float newEnergy ) ;
void setCorrectedEcalEnergy(float newEnergy, bool rescaleDependentValues);
void setTrackMomentumError( float trackMomentumError ) ;
void setP4( P4Kind kind, const LorentzVector & p4, float p4Error, bool setCandidate ) ;
using RecoCandidate::setP4 ;
Expand All @@ -861,9 +862,9 @@ class GsfElectron : public RecoCandidate
//bool isMomentumCorrected() const { return corrections_.isMomentumCorrected ; }
float caloEnergy() const { return correctedEcalEnergy() ; }
bool isEnergyScaleCorrected() const { return isEcalEnergyCorrected() ; }
void correctEcalEnergy( float newEnergy, float newEnergyError )
void correctEcalEnergy(float newEnergy, float newEnergyError, bool corrEovP = true)
{
setCorrectedEcalEnergy(newEnergy) ;
setCorrectedEcalEnergy(newEnergy, corrEovP) ;
setEcalEnergyError(newEnergyError) ;
}
void correctMomentum( const LorentzVector & p4, float trackMomentumError, float p4Error )
Expand Down
27 changes: 15 additions & 12 deletions DataFormats/EgammaCandidates/src/GsfElectron.cc
Expand Up @@ -179,18 +179,21 @@ bool GsfElectron::ecalDriven() const
void GsfElectron::setCorrectedEcalEnergyError( float energyError )
{ corrections_.correctedEcalEnergyError = energyError ; }

void GsfElectron::setCorrectedEcalEnergy( float newEnergy )
{
math::XYZTLorentzVectorD momentum = p4() ;
momentum *= newEnergy/momentum.e() ;
setP4(momentum) ;
showerShape_.hcalDepth1OverEcal *= corrections_.correctedEcalEnergy/newEnergy ;
showerShape_.hcalDepth2OverEcal *= corrections_.correctedEcalEnergy/newEnergy ;
trackClusterMatching_.eSuperClusterOverP *= newEnergy/corrections_.correctedEcalEnergy ;
corrections_.correctedEcalEnergyError *= newEnergy/corrections_.correctedEcalEnergy ;
corrections_.correctedEcalEnergy = newEnergy ;
corrections_.isEcalEnergyCorrected = true ;
}
void GsfElectron::setCorrectedEcalEnergy(float newEnergy) { setCorrectedEcalEnergy(newEnergy, true); }

void GsfElectron::setCorrectedEcalEnergy(float newEnergy, bool rescaleDependentValues) {
math::XYZTLorentzVectorD momentum = p4();
momentum *= newEnergy / momentum.e();
setP4(momentum);
if (corrections_.correctedEcalEnergy > 0. && rescaleDependentValues) {
showerShape_.hcalDepth1OverEcal *= corrections_.correctedEcalEnergy / newEnergy;
showerShape_.hcalDepth2OverEcal *= corrections_.correctedEcalEnergy / newEnergy;
trackClusterMatching_.eSuperClusterOverP *= newEnergy / corrections_.correctedEcalEnergy;
corrections_.correctedEcalEnergyError *= newEnergy / corrections_.correctedEcalEnergy;
}
corrections_.correctedEcalEnergy = newEnergy;
corrections_.isEcalEnergyCorrected = true;
}

void GsfElectron::setTrackMomentumError( float trackErr )
{ corrections_.trackMomentumError = trackErr ; }
Expand Down
211 changes: 211 additions & 0 deletions PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
@@ -0,0 +1,211 @@
import FWCore.ParameterSet.Config as cms
from PhysicsTools.NanoAOD.nano_eras_cff import *
from PhysicsTools.NanoAOD.common_cff import *

################################################################################
# Modules
################################################################################

from RecoEgamma.EgammaTools.lowPtElectronModifier_cfi import lowPtElectronModifier
from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cff import lowPtRegressionModifier
modifiedLowPtElectrons = cms.EDProducer(
"ModifiedElectronProducer",
src = cms.InputTag("slimmedLowPtElectrons"),
modifierConfig = cms.PSet(
modifications = cms.VPSet(lowPtElectronModifier,lowPtRegressionModifier)
)
)

import PhysicsTools.PatAlgos.producersLayer1.electronProducer_cfi
updatedLowPtElectrons = cms.EDProducer(
"PATElectronUpdater",
src = cms.InputTag("modifiedLowPtElectrons"),
vertices = cms.InputTag("offlineSlimmedPrimaryVertices"),
computeMiniIso = cms.bool(True),
fixDxySign = cms.bool(False),
pfCandsForMiniIso = cms.InputTag("packedPFCandidates"),
miniIsoParamsB = PhysicsTools.PatAlgos.producersLayer1.electronProducer_cfi.patElectrons.miniIsoParamsB,
miniIsoParamsE = PhysicsTools.PatAlgos.producersLayer1.electronProducer_cfi.patElectrons.miniIsoParamsE,
)

from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronID_cff import lowPtGsfElectronID
lowPtPATElectronID = lowPtGsfElectronID.clone(
usePAT = True,
electrons = "updatedLowPtElectrons",
unbiased = "",
ModelWeights = [
'RecoEgamma/ElectronIdentification/data/LowPtElectrons/LowPtElectrons_ID_2020Nov28.root',
],
Version = cms.string('V1'),
rho = "fixedGridRhoFastjetAll",
)

isoForLowPtEle = cms.EDProducer(
"EleIsoValueMapProducer",
src = cms.InputTag("updatedLowPtElectrons"),
relative = cms.bool(True),
rho_MiniIso = cms.InputTag("fixedGridRhoFastjetAll"),
rho_PFIso = cms.InputTag("fixedGridRhoFastjetAll"),
EAFile_MiniIso = cms.FileInPath("RecoEgamma/ElectronIdentification/data/Fall17/effAreaElectrons_cone03_pfNeuHadronsAndPhotons_94X.txt"),
EAFile_PFIso = cms.FileInPath("RecoEgamma/ElectronIdentification/data/Fall17/effAreaElectrons_cone03_pfNeuHadronsAndPhotons_94X.txt"),
)

updatedLowPtElectronsWithUserData = cms.EDProducer(
"PATElectronUserDataEmbedder",
src = cms.InputTag("updatedLowPtElectrons"),
userFloats = cms.PSet(
ID = cms.InputTag("lowPtPATElectronID"),
miniIsoChg = cms.InputTag("isoForLowPtEle:miniIsoChg"),
miniIsoAll = cms.InputTag("isoForLowPtEle:miniIsoAll"),
),
userIntFromBools = cms.PSet(),
userInts = cms.PSet(),
userCands = cms.PSet(),
)

finalLowPtElectrons = cms.EDFilter(
"PATElectronRefSelector",
src = cms.InputTag("updatedLowPtElectronsWithUserData"),
cut = cms.string("pt > 1. && userFloat('ID') > -0.25"),
)

################################################################################
# electronTable
################################################################################

lowPtElectronTable = cms.EDProducer(
"SimpleCandidateFlatTableProducer",
src = cms.InputTag("finalLowPtElectrons"),
cut = cms.string(""),
name= cms.string("LowPtElectron"),
doc = cms.string("slimmedLowPtElectrons after basic selection (" + finalLowPtElectrons.cut.value()+")"),
singleton = cms.bool(False), # the number of entries is variable
extension = cms.bool(False), # this is the main table for the electrons
variables = cms.PSet(
# Basic variables
CandVars,
# BDT scores and WPs
embeddedID = Var("electronID('ID')",float,doc="ID, BDT (raw) score"),
ID = Var("userFloat('ID')",float,doc="New ID, BDT (raw) score"),
unbiased = Var("electronID('unbiased')",float,doc="ElectronSeed, pT- and dxy- agnostic BDT (raw) score"),
ptbiased = Var("electronID('ptbiased')",float,doc="ElectronSeed, pT- and dxy- dependent BDT (raw) score"),
# Isolation
miniPFRelIso_chg = Var("userFloat('miniIsoChg')",float,
doc="mini PF relative isolation, charged component"),
miniPFRelIso_all = Var("userFloat('miniIsoAll')",float,
doc="mini PF relative isolation, total (with scaled rho*EA PU corrections)"),
# Conversions
convVeto = Var("passConversionVeto()",bool,doc="pass conversion veto"),
convWP = Var("userInt('convOpen')*1 + userInt('convLoose')*2 + userInt('convTight')*4",
int,doc="conversion flag bit map: 1=Veto, 2=Loose, 3=Tight"),
convVtxRadius = Var("userFloat('convVtxRadius')",float,doc="conversion vertex radius (cm)",precision=7),
# Tracking
lostHits = Var("gsfTrack.hitPattern.numberOfLostHits('MISSING_INNER_HITS')","uint8",doc="number of missing inner hits"),
# Cluster-related
energyErr = Var("p4Error('P4_COMBINATION')",float,doc="energy error of the cluster-track combination",precision=6),
deltaEtaSC = Var("superCluster().eta()-eta()",float,doc="delta eta (SC,ele) with sign",precision=10),
r9 = Var("full5x5_r9()",float,doc="R9 of the SC, calculated with full 5x5 region",precision=10),
sieie = Var("full5x5_sigmaIetaIeta()",float,doc="sigma_IetaIeta of the SC, calculated with full 5x5 region",precision=10),
eInvMinusPInv = Var("(1-eSuperClusterOverP())/ecalEnergy()",float,doc="1/E_SC - 1/p_trk",precision=10),
scEtOverPt = Var("(superCluster().energy()/(pt*cosh(superCluster().eta())))-1",float,doc="(SC energy)/pt-1",precision=8),
hoe = Var("hadronicOverEm()",float,doc="H over E",precision=8),
# Displacement
dxy = Var("dB('PV2D')",float,doc="dxy (with sign) wrt first PV, in cm",precision=10),
dxyErr = Var("edB('PV2D')",float,doc="dxy uncertainty, in cm",precision=6),
dz = Var("dB('PVDZ')",float,doc="dz (with sign) wrt first PV, in cm",precision=10),
dzErr = Var("abs(edB('PVDZ'))",float,doc="dz uncertainty, in cm",precision=6),
# Cross-referencing
#jetIdx
#photonIdx
),
)

################################################################################
# electronTable (MC)
################################################################################

from PhysicsTools.NanoAOD.particlelevel_cff import particleLevel
particleLevelForMatchingLowPt = particleLevel.clone(
lepMinPt = cms.double(1.),
phoMinPt = cms.double(1),
)

tautaggerForMatchingLowPt = cms.EDProducer(
"GenJetTauTaggerProducer",
src = cms.InputTag('particleLevelForMatchingLowPt:leptons')
)

matchingLowPtElecPhoton = cms.EDProducer(
"GenJetGenPartMerger",
srcJet =cms.InputTag("particleLevelForMatchingLowPt:leptons"),
srcPart=cms.InputTag("particleLevelForMatchingLowPt:photons"),
hasTauAnc=cms.InputTag("tautaggerForMatchingLowPt"),
)

lowPtElectronsMCMatchForTableAlt = cms.EDProducer(
"GenJetMatcherDRPtByDR", # cut on deltaR, deltaPt/Pt; pick best by deltaR
src = lowPtElectronTable.src, # final reco collection
matched = cms.InputTag("matchingLowPtElecPhoton:merged"), # final mc-truth particle collection
mcPdgId = cms.vint32(11,22), # one or more PDG ID (11 = el, 22 = pho); absolute values (see below)
checkCharge = cms.bool(False), # True = require RECO and MC objects to have the same charge
mcStatus = cms.vint32(),
maxDeltaR = cms.double(0.3), # Minimum deltaR for the match
maxDPtRel = cms.double(0.5), # Minimum deltaPt/Pt for the match
resolveAmbiguities = cms.bool(True), # Forbid two RECO objects to match to the same GEN object
resolveByMatchQuality = cms.bool(True), # False = just match input in order; True = pick lowest deltaR pair first
)

lowPtElectronsMCMatchForTable = cms.EDProducer(
"MCMatcher", # cut on deltaR, deltaPt/Pt; pick best by deltaR
src = lowPtElectronTable.src, # final reco collection
matched = cms.InputTag("finalGenParticles"), # final mc-truth particle collection
mcPdgId = cms.vint32(11), # one or more PDG ID (11 = ele); absolute values (see below)
checkCharge = cms.bool(False), # True = require RECO and MC objects to have the same charge
mcStatus = cms.vint32(1), # PYTHIA status code (1 = stable, 2 = shower, 3 = hard scattering)
maxDeltaR = cms.double(0.3), # Minimum deltaR for the match
maxDPtRel = cms.double(0.5), # Minimum deltaPt/Pt for the match
resolveAmbiguities = cms.bool(True), # Forbid two RECO objects to match to the same GEN object
resolveByMatchQuality = cms.bool(True), # False = just match input in order; True = pick lowest deltaR pair first
)

from PhysicsTools.NanoAOD.electrons_cff import electronMCTable
lowPtElectronMCTable = cms.EDProducer(
"CandMCMatchTableProducer",
src = lowPtElectronTable.src,
mcMapDressedLep = cms.InputTag("lowPtElectronsMCMatchForTableAlt"),
mcMap = cms.InputTag("lowPtElectronsMCMatchForTable"),
mapTauAnc = cms.InputTag("matchingLowPtElecPhoton:hasTauAnc"),
objName = lowPtElectronTable.name,
objType = electronMCTable.objType,
branchName = cms.string("genPart"),
docString = cms.string("MC matching to status==1 electrons or photons"),
genparticles = cms.InputTag("finalGenParticles"),
)

################################################################################
# Sequences
################################################################################

lowPtElectronSequence = cms.Sequence(modifiedLowPtElectrons
+updatedLowPtElectrons
+lowPtPATElectronID
+isoForLowPtEle
+updatedLowPtElectronsWithUserData
+finalLowPtElectrons)
lowPtElectronTables = cms.Sequence(lowPtElectronTable)
lowPtElectronMC = cms.Sequence(
particleLevelForMatchingLowPt
+tautaggerForMatchingLowPt
+matchingLowPtElecPhoton
+lowPtElectronsMCMatchForTable
+lowPtElectronsMCMatchForTableAlt
+lowPtElectronMCTable)

################################################################################
# Modifiers
################################################################################

_modifiers = ~(run2_nanoAOD_106Xv2 | run2_nanoAOD_devel)
_modifiers.toReplaceWith(lowPtElectronSequence,cms.Sequence())
_modifiers.toReplaceWith(lowPtElectronTables,cms.Sequence())
_modifiers.toReplaceWith(lowPtElectronMC,cms.Sequence())
1 change: 1 addition & 0 deletions PhysicsTools/NanoAOD/python/nanoDQM_cff.py
Expand Up @@ -146,6 +146,7 @@
## MC
nanoDQMMC = nanoDQM.clone()
nanoDQMMC.vplots.Electron.sels.Prompt = cms.string("genPartFlav == 1")
nanoDQMMC.vplots.LowPtElectron.sels.Prompt = cms.string("genPartFlav == 1")
nanoDQMMC.vplots.Muon.sels.Prompt = cms.string("genPartFlav == 1")
nanoDQMMC.vplots.Photon.sels.Prompt = cms.string("genPartFlav == 1")
nanoDQMMC.vplots.Tau.sels.Prompt = cms.string("genPartFlav == 5")
Expand Down
45 changes: 45 additions & 0 deletions PhysicsTools/NanoAOD/python/nanoDQM_cfi.py
Expand Up @@ -112,6 +112,51 @@
Plot1D('dEsigmaDown', 'dEsigmaDown', 100, -0.1, 0.1, '#Delta E sigmaDown'),
)
),

LowPtElectron = cms.PSet(
sels = cms.PSet(
Good = cms.string('pt > 1. && ID > 5.')
),
plots = cms.VPSet(
#
Count1D('_size', 8, -0.5, 7.5, 'slimmedLowPtElectrons after basic selection'),
# CandVars
Plot1D('charge', 'charge', 3, -1.5, 1.5, 'electric charge'),
Plot1D('eta', 'eta', 20, -3., 3., 'eta'),
NoPlot('mass'),
Plot1D('pdgId', 'pdgId', 101, -50.5, 50.5, 'PDG code assigned by the event reconstruction (not by MC truth)'),
Plot1D('phi', 'phi', 20, -3.14159, 3.14159, 'phi'),
Plot1D('pt', 'pt', 40, 0., 20., 'pt (corrected)'),
# BDT scores and WPs
Plot1D('embeddedID', 'embeddedID', 40, -10., 10., 'Embedded ID, BDT (raw) score'),
Plot1D('ID', 'ID', 40, -10., 10., 'ID, BDT (raw) score'),
Plot1D('unbiased', 'unbiased', 40, -10., 10., 'ElectronSeed, pT- and dxy- agnostic BDT (raw) score'),
Plot1D('ptbiased', 'ptbiased', 40, -10., 10., 'ElectronSeed, pT- and dxy- dependent BDT (raw) score'),
# Isolation
Plot1D('miniPFRelIso_chg', 'miniPFRelIso_chg', 20, 0., 1., 'mini PF relative isolation, charged component'),
Plot1D('miniPFRelIso_all', 'miniPFRelIso_all', 20, 0., 1., 'mini PF relative isolation, total (with scaled rho*EA PU corrections)'),
# Conversions
Plot1D('convVeto', 'convVeto', 2, -0.5, 1.5, 'pass conversion veto'),
Plot1D('convWP', 'convWP', 8, -0.5, 7.5, 'conversion flag bit map: 1=Veto, 2=Loose, 3=Tight'),
Plot1D('convVtxRadius', 'convVtxRadius', 40, 0., 20.0, 'conversion vertex radius (cm)'),
# Tracking
Plot1D('lostHits', 'lostHits', 4, -0.5, 3.5, 'number of missing inner hits'),
# Cluster-related
Plot1D('energyErr', 'energyErr', 40, 0., 20., 'energy error of the cluster from regression'),
Plot1D('deltaEtaSC', 'deltaEtaSC', 20, -0.2, 0.2, 'delta eta (SC,ele) with sign'),
Plot1D('r9', 'r9', 20, 0, 1.1, 'R9 of the supercluster, calculated with full 5x5 region'),
Plot1D('sieie', 'sieie', 20, 0, 0.05, 'sigma_IetaIeta of the supercluster, calculated with full 5x5 region'),
Plot1D('eInvMinusPInv', 'eInvMinusPInv', 20, -0.1, 0.1, '1/E_SC - 1/p_trk'),
Plot1D('scEtOverPt', 'scEtOverPt', 20, -0.5, 0.5, '(supercluster transverse energy)/pt - 1'),
Plot1D('hoe', 'hoe', 20, 0, 0.6, 'H over E'),
# Displacement
Plot1D('dxy', 'dxy', 20, -0.1, 0.1, 'dxy (with sign) wrt first PV, in cm'),
Plot1D('dz', 'dz', 20, -0.3, 0.3, 'dz (with sign) wrt first PV, in cm'),
Plot1D('dxyErr', 'dxyErr', 20, 0., 0.2, 'dxy uncertainty, in cm'),
Plot1D('dzErr', 'dzErr', 20, 0., 0.2, 'dz uncertainty, in cm'),
),
),

FatJet = cms.PSet(
sels = cms.PSet(),
plots = cms.VPSet(
Expand Down
7 changes: 4 additions & 3 deletions PhysicsTools/NanoAOD/python/nano_cff.py
Expand Up @@ -7,6 +7,7 @@
from PhysicsTools.NanoAOD.taus_cff import *
from PhysicsTools.NanoAOD.boostedTaus_cff import *
from PhysicsTools.NanoAOD.electrons_cff import *
from PhysicsTools.NanoAOD.lowPtElectrons_cff import *
from PhysicsTools.NanoAOD.photons_cff import *
from PhysicsTools.NanoAOD.globals_cff import *
from PhysicsTools.NanoAOD.extraflags_cff import *
Expand Down Expand Up @@ -107,10 +108,10 @@
(run2_miniAOD_80XLegacy | run2_nanoAOD_94X2016 | run2_nanoAOD_94XMiniAODv1 | run2_nanoAOD_94XMiniAODv2 | run2_nanoAOD_102Xv1).toModify(l1bits, storeUnprefireableBit=False)

nanoSequenceCommon = cms.Sequence(
nanoMetadata + jetSequence + muonSequence + tauSequence + boostedTauSequence + electronSequence+photonSequence+vertexSequence+
nanoMetadata + jetSequence + muonSequence + tauSequence + boostedTauSequence + electronSequence + lowPtElectronSequence + photonSequence+vertexSequence+
isoTrackSequence + jetLepSequence + # must be after all the leptons
linkedObjects +
jetTables + muonTables + tauTables + boostedTauTables + electronTables + photonTables + globalTables +vertexTables+ metTables+simpleCleanerTable + isoTrackTables
jetTables + muonTables + tauTables + boostedTauTables + electronTables + lowPtElectronTables + photonTables + globalTables +vertexTables+ metTables+simpleCleanerTable + isoTrackTables
)
#remove boosted tau from previous eras
(run2_miniAOD_80XLegacy | run2_nanoAOD_92X | run2_nanoAOD_94XMiniAODv1 | run2_nanoAOD_94X2016 | run2_nanoAOD_94XMiniAODv2 | run2_nanoAOD_102Xv1 | run2_nanoAOD_106Xv1).toReplaceWith(nanoSequenceCommon, nanoSequenceCommon.copyAndExclude([boostedTauSequence, boostedTauTables]))
Expand All @@ -123,7 +124,7 @@

( run2_nanoAOD_106Xv1 & ~run2_nanoAOD_devel).toReplaceWith(nanoSequence, nanoSequence.copyAndExclude([nanoSequenceOnlyData]))

nanoSequenceFS = cms.Sequence(genParticleSequence + genVertexTables + particleLevelSequence + nanoSequenceCommon + jetMC + muonMC + electronMC + photonMC + tauMC + boostedTauMC + metMC + ttbarCatMCProducers + globalTablesMC + btagWeightTable + genWeightsTable + genVertexTable + genParticleTables + particleLevelTables + lheInfoTable + ttbarCategoryTable )
nanoSequenceFS = cms.Sequence(genParticleSequence + genVertexTables + particleLevelSequence + nanoSequenceCommon + jetMC + muonMC + electronMC + lowPtElectronMC + photonMC + tauMC + boostedTauMC + metMC + ttbarCatMCProducers + globalTablesMC + btagWeightTable + genWeightsTable + genVertexTable + genParticleTables + particleLevelTables + lheInfoTable + ttbarCategoryTable )

(run2_nanoAOD_92X | run2_miniAOD_80XLegacy | run2_nanoAOD_94X2016 | run2_nanoAOD_94X2016 | \
run2_nanoAOD_94XMiniAODv1 | run2_nanoAOD_94XMiniAODv2 | \
Expand Down