diff --git a/PhysicsTools/NanoAOD/python/electrons_cff.py b/PhysicsTools/NanoAOD/python/electrons_cff.py index 79acf3d0126aa..021092bbb7062 100644 --- a/PhysicsTools/NanoAOD/python/electrons_cff.py +++ b/PhysicsTools/NanoAOD/python/electrons_cff.py @@ -395,12 +395,12 @@ def _get_bitmapVIDForEle_docstring(modules,WorkingPoints): # Produce and add time-life info (e.g. for electrons from tau decays) from PhysicsTools.PatAlgos.patElectronTimeLifeInfoUpdater_cfi import patElectronTimeLifeInfoUpdater -from PhysicsTools.NanoAOD.leptonTimeLifeInfo_common_cff import * +import PhysicsTools.NanoAOD.leptonTimeLifeInfo_common_cff as tli from TrackingTools.TransientTrack.TransientTrackBuilder_cfi import * electronsWithTimeLifeInfo = patElectronTimeLifeInfoUpdater.clone( src = electronTable.src, - pvSource = "offlineSlimmedPrimaryVerticesWithBS", - pvChoice = 0 #0: PV[0], 1: smallest dz + pvSource = tli.prod_common.pvSource, + pvChoice = tli.prod_common.pvChoice ) electronTimeLifeInfoTable = simpleCandidateFlatTableProducer.clone( @@ -409,8 +409,8 @@ def _get_bitmapVIDForEle_docstring(modules,WorkingPoints): doc = cms.string("Additional time-life info for non-prompt electrons"), extension = True, variables = cms.PSet( - ipVars, - trackVars + tli.ipVars, + tli.trackVars ) ) @@ -477,3 +477,7 @@ def _get_bitmapVIDForEle_docstring(modules,WorkingPoints): # Revert back to AK4 CHS jets for Run2 inputs run2_nanoAOD_ANY.toModify( ptRatioRelForEle,srcJet="updatedJets") +# Refit PV with beam-spot constraint that is not present in Run-2 samples +_electronTimeLifeInfoTaskRun2 = electronTimeLifeInfoTask.copy() +_electronTimeLifeInfoTaskRun2.add(tli.refittedPV) +run2_nanoAOD_ANY.toReplaceWith(electronTimeLifeInfoTask,_electronTimeLifeInfoTaskRun2) diff --git a/PhysicsTools/NanoAOD/python/leptonTimeLifeInfo_common_cff.py b/PhysicsTools/NanoAOD/python/leptonTimeLifeInfo_common_cff.py index 6c4bc5836fe7b..35c85bcf43482 100644 --- a/PhysicsTools/NanoAOD/python/leptonTimeLifeInfo_common_cff.py +++ b/PhysicsTools/NanoAOD/python/leptonTimeLifeInfo_common_cff.py @@ -4,6 +4,14 @@ # import FWCore.ParameterSet.Config as cms from PhysicsTools.NanoAOD.common_cff import * +from PhysicsTools.NanoAOD.nano_eras_cff import * +from PhysicsTools.PatAlgos.miniAODRefitVertexProducer_cfi import miniAODRefitVertexProducer + +# common settings of lepton life-time info producer +prod_common = cms.PSet( + pvSource = cms.InputTag("offlineSlimmedPrimaryVerticesWithBS"), + pvChoice = cms.int32(0) #0: PV[0], 1: smallest dz +) # impact parameter ipVars = cms.PSet( @@ -48,3 +56,10 @@ flightLength = Var("?hasUserFloat('flightLength')?userFloat('flightLength'):0", float, doc="flight-length,i.e. the PV to SV distance", precision=16), flightLengthSig = Var("?hasUserFloat('flightLength_sig')?userFloat('flightLength_sig'):0", float, doc="Significance of flight-length", precision=16) ) + +# Module to refit PV with beam-spot constraint that is not present in Run-2 samples +refittedPV = miniAODRefitVertexProducer.clone( + srcVertices = "offlineSlimmedPrimaryVertices", +) +run2_nanoAOD_ANY.toModify( + prod_common, pvSource = "refittedPV") diff --git a/PhysicsTools/NanoAOD/python/muons_cff.py b/PhysicsTools/NanoAOD/python/muons_cff.py index 5ff1766916b77..70084a93e0017 100644 --- a/PhysicsTools/NanoAOD/python/muons_cff.py +++ b/PhysicsTools/NanoAOD/python/muons_cff.py @@ -6,7 +6,7 @@ import PhysicsTools.PatAlgos.producersLayer1.muonProducer_cfi from PhysicsTools.PatAlgos.patMuonTimeLifeInfoUpdater_cfi import patMuonTimeLifeInfoUpdater -from PhysicsTools.NanoAOD.leptonTimeLifeInfo_common_cff import * +import PhysicsTools.NanoAOD.leptonTimeLifeInfo_common_cff as tli # this below is used only in some eras slimmedMuonsUpdated = cms.EDProducer("PATMuonUpdater", @@ -191,8 +191,8 @@ # Produce and add time-life info (e.g. for muons from tau decays) muonsWithTimeLifeInfo = patMuonTimeLifeInfoUpdater.clone( src = muonTable.src, - pvSource = "offlineSlimmedPrimaryVerticesWithBS", - pvChoice = 0 #0: PV[0], 1: smallest dz + pvSource = tli.prod_common.pvSource, + pvChoice = tli.prod_common.pvChoice ) muonTimeLifeInfoTable = simpleCandidateFlatTableProducer.clone( @@ -201,8 +201,8 @@ doc = cms.string("Additional time-life info for non-prompt muons"), extension = True, variables = cms.PSet( - ipVars, - trackVars + tli.ipVars, + tli.trackVars ) ) @@ -245,5 +245,9 @@ muonMCTask = cms.Task(muonsMCMatchForTable,muonMCTable) muonTablesTask = cms.Task(muonMVATTH,muonMVALowPt,muonBSConstrain,muonTable,muonMVAID) muonTimeLifeInfoTask = cms.Task(muonsWithTimeLifeInfo,muonTimeLifeInfoTable) +# Refit PV with beam-spot constraint that is not present in Run-2 samples +_muonTimeLifeInfoTaskRun2 = muonTimeLifeInfoTask.copy() +_muonTimeLifeInfoTaskRun2.add(tli.refittedPV) +run2_nanoAOD_ANY.toReplaceWith(muonTimeLifeInfoTask,_muonTimeLifeInfoTaskRun2) muonTablesTask.add(muonTimeLifeInfoTask) diff --git a/PhysicsTools/NanoAOD/python/taus_cff.py b/PhysicsTools/NanoAOD/python/taus_cff.py index a52181a1b8073..c2a7da94a5daa 100644 --- a/PhysicsTools/NanoAOD/python/taus_cff.py +++ b/PhysicsTools/NanoAOD/python/taus_cff.py @@ -1,6 +1,6 @@ import FWCore.ParameterSet.Config as cms from PhysicsTools.NanoAOD.common_cff import * -from PhysicsTools.NanoAOD.nano_eras_cff import run3_nanoAOD_124 +from PhysicsTools.NanoAOD.nano_eras_cff import * from PhysicsTools.NanoAOD.simpleCandidateFlatTableProducer_cfi import simpleCandidateFlatTableProducer from PhysicsTools.JetMCAlgos.TauGenJets_cfi import tauGenJets @@ -8,7 +8,7 @@ from PhysicsTools.PatAlgos.patTauSignalCandidatesProducer_cfi import patTauSignalCandidatesProducer from PhysicsTools.PatAlgos.patTauTimeLifeInfoUpdater_cfi import patTauTimeLifeInfoUpdater -from PhysicsTools.NanoAOD.leptonTimeLifeInfo_common_cff import * +import PhysicsTools.NanoAOD.leptonTimeLifeInfo_common_cff as tli ##################### Updated tau collection with MVA-based tau-Ids rerun ####### # Used only in some eras @@ -178,8 +178,8 @@ def _tauIdWPMask(pattern, choices, doc="", from_raw=False, wp_thrs=None): from TrackingTools.TransientTrack.TransientTrackBuilder_cfi import * tausWithTimeLifeInfo = patTauTimeLifeInfoUpdater.clone( src = tauTable.src, - pvSource = "offlineSlimmedPrimaryVerticesWithBS", - pvChoice = 0 #0: PV[0], 1: smallest dz + pvSource = tli.prod_common.pvSource, + pvChoice = tli.prod_common.pvChoice ) tauTimeLifeInfoTable = simpleCandidateFlatTableProducer.clone( @@ -188,9 +188,9 @@ def _tauIdWPMask(pattern, choices, doc="", from_raw=False, wp_thrs=None): doc = cms.string("Additional tau time-life info"), extension = True, variables = cms.PSet( - svVars, - ipVars, - trackVars + tli.svVars, + tli.ipVars, + tli.trackVars ) ) @@ -264,6 +264,10 @@ def _tauIdWPMask(pattern, choices, doc="", from_raw=False, wp_thrs=None): tauSignalCandsTask = cms.Task(tauSignalCands,tauSignalCandsTable) tauTablesTask.add(tauSignalCandsTask) tauTimeLifeInfoTask = cms.Task(tausWithTimeLifeInfo,tauTimeLifeInfoTable) +# Refit PV with beam-spot constraint that is not present in Run-2 samples +_tauTimeLifeInfoTaskRun2 = tauTimeLifeInfoTask.copy() +_tauTimeLifeInfoTaskRun2.add(tli.refittedPV) +run2_nanoAOD_ANY.toReplaceWith(tauTimeLifeInfoTask,_tauTimeLifeInfoTaskRun2) tauTablesTask.add(tauTimeLifeInfoTask) diff --git a/PhysicsTools/NanoAOD/python/vertices_cff.py b/PhysicsTools/NanoAOD/python/vertices_cff.py index 481bb3e614043..b0e5965ed915f 100644 --- a/PhysicsTools/NanoAOD/python/vertices_cff.py +++ b/PhysicsTools/NanoAOD/python/vertices_cff.py @@ -2,6 +2,8 @@ from PhysicsTools.NanoAOD.common_cff import * from PhysicsTools.NanoAOD.simpleCandidateFlatTableProducer_cfi import simpleCandidateFlatTableProducer from PhysicsTools.NanoAOD.simpleVertexFlatTableProducer_cfi import simpleVertexFlatTableProducer +from PhysicsTools.NanoAOD.nano_eras_cff import * +from PhysicsTools.NanoAOD.leptonTimeLifeInfo_common_cff import prod_common, refittedPV ##################### User floats producers, selectors ########################## @@ -47,7 +49,7 @@ vertexTable.optionalPvVariables = covarianceVars -verticesWithBS = "offlineSlimmedPrimaryVerticesWithBS" +verticesWithBS = prod_common.pvSource pvbsTable = simpleVertexFlatTableProducer.clone( src = verticesWithBS, name = "PVBS", @@ -61,12 +63,16 @@ ndof = Var("ndof()", float, doc = "number of degrees of freedom", precision = 8), chi2 = Var("normalizedChi2()", float, doc = "reduced chi2, i.e. chi2/ndof", precision = 8), ), - externalVariables = cms.PSet( - score = ExtVar(cms.InputTag(verticesWithBS), float, doc="vertex score, i.e. sum pt2 of clustered objects", precision = 8) - ) ) #before cross linking vertexTask = cms.Task() #after cross linkining vertexTablesTask = cms.Task( vertexTable, svCandidateTable, pvbsTable ) + +#refit PV with beam-spot constraint that is not present in Run-2 samples +_vertexTablesTaskRun2 = vertexTablesTask.copy() +_vertexTablesTaskRun2.add( refittedPV ) +run2_nanoAOD_ANY.toReplaceWith( + vertexTablesTask, _vertexTablesTaskRun2 +) diff --git a/PhysicsTools/PatAlgos/plugins/MiniAODRefitVertexProducer.cc b/PhysicsTools/PatAlgos/plugins/MiniAODRefitVertexProducer.cc new file mode 100644 index 0000000000000..e141e2758bbe5 --- /dev/null +++ b/PhysicsTools/PatAlgos/plugins/MiniAODRefitVertexProducer.cc @@ -0,0 +1,189 @@ +/** + \class MiniAODRefitVertexProducer + + This producer is intended to take packedCandidates with tracks associated to + the PV and refit the PV (applying or not) BeamSpot constraint + + \autor Michal Bluj, NCBJ Warsaw (and then others) + + **/ + +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "DataFormats/PatCandidates/interface/PackedCandidate.h" +#include "DataFormats/TrackReco/interface/Track.h" +#include "DataFormats/VertexReco/interface/Vertex.h" +#include "DataFormats/BeamSpot/interface/BeamSpot.h" +#include "TrackingTools/TransientTrack/interface/TransientTrackBuilder.h" +#include "TrackingTools/Records/interface/TransientTrackRecord.h" +#include "RecoVertex/VertexPrimitives/interface/TransientVertex.h" +#include "RecoVertex/AdaptiveVertexFit/interface/AdaptiveVertexFitter.h" + +#include + +class MiniAODRefitVertexProducer : public edm::stream::EDProducer<> { +public: + explicit MiniAODRefitVertexProducer(const edm::ParameterSet&); + ~MiniAODRefitVertexProducer() override{}; + + void produce(edm::Event&, const edm::EventSetup&) override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + //--- utility methods + + //--- configuration parameters + edm::EDGetTokenT > srcCands_, srcLostTracks_, srcEleKfTracks_; + edm::EDGetTokenT srcVertices_; + edm::EDGetTokenT srcBeamSpot_; + edm::ESGetToken transTrackBuilderToken_; + bool useBeamSpot_; + bool useLostTracks_; + bool useEleKfTracks_; +}; + +MiniAODRefitVertexProducer::MiniAODRefitVertexProducer(const edm::ParameterSet& cfg) + : srcCands_(consumes >(cfg.getParameter("srcCands"))), + srcLostTracks_(consumes >(cfg.getParameter("srcLostTracks"))), + srcEleKfTracks_(consumes >(cfg.getParameter("srcEleKfTracks"))), + srcVertices_(consumes(cfg.getParameter("srcVertices"))), + srcBeamSpot_(consumes(cfg.getParameter("srcBeamSpot"))), + transTrackBuilderToken_(esConsumes(edm::ESInputTag("", "TransientTrackBuilder"))), + useBeamSpot_(cfg.getParameter("useBeamSpot")), + useLostTracks_(cfg.getParameter("useLostTracks")), + useEleKfTracks_(cfg.getParameter("useEleKfTracks")) { + produces(); +} + +void MiniAODRefitVertexProducer::produce(edm::Event& evt, const edm::EventSetup& es) { + // Obtain collections + edm::Handle > cands; + evt.getByToken(srcCands_, cands); + + edm::Handle > lostTrackCands; + if (useLostTracks_) + evt.getByToken(srcLostTracks_, lostTrackCands); + + edm::Handle > eleKfTrackCands; + if (useEleKfTracks_) + evt.getByToken(srcEleKfTracks_, eleKfTrackCands); + + edm::Handle vertices; + evt.getByToken(srcVertices_, vertices); + const reco::Vertex& pv = vertices->front(); + size_t vtxIdx = 0; + + edm::Handle beamSpot; + if (useBeamSpot_) + evt.getByToken(srcBeamSpot_, beamSpot); + + // Get transient track builder + const TransientTrackBuilder& transTrackBuilder = es.getData(transTrackBuilderToken_); + + // Output collection + auto outputVertices = std::make_unique(); + outputVertices->reserve(1); + + // Create a new track collection for vertex refit + std::vector transTracks; + + // loop over the PFCandidates + for (const auto& cand : (*cands)) { + if (cand.charge() == 0 || cand.vertexRef().isNull()) + continue; + if (cand.bestTrack() == nullptr) + continue; + auto key = cand.vertexRef().key(); + auto quality = cand.pvAssociationQuality(); + if (key != vtxIdx || + (quality != pat::PackedCandidate::UsedInFitTight && quality != pat::PackedCandidate::UsedInFitLoose)) + continue; + if (useEleKfTracks_ && std::abs(cand.pdgId()) == 11) + continue; + transTracks.push_back(transTrackBuilder.build(cand.bestTrack())); + } + + // loop over the lostTracks + if (useLostTracks_) { + for (const auto& cand : (*lostTrackCands)) { + if (cand.charge() == 0 || cand.vertexRef().isNull()) + continue; + if (cand.bestTrack() == nullptr) + continue; + auto key = cand.vertexRef().key(); + auto quality = cand.pvAssociationQuality(); + if (key != vtxIdx || + (quality != pat::PackedCandidate::UsedInFitTight && quality != pat::PackedCandidate::UsedInFitLoose)) + continue; + transTracks.push_back(transTrackBuilder.build(cand.bestTrack())); + } + } + + // loop over the electronKfTracks + if (useEleKfTracks_) { + for (const auto& cand : (*eleKfTrackCands)) { + if (cand.charge() == 0 || cand.vertexRef().isNull()) + continue; + if (cand.bestTrack() == nullptr) + continue; + auto key = cand.vertexRef().key(); + auto quality = cand.pvAssociationQuality(); + if (key != vtxIdx || + (quality != pat::PackedCandidate::UsedInFitTight && quality != pat::PackedCandidate::UsedInFitLoose)) + continue; + transTracks.push_back(transTrackBuilder.build(cand.bestTrack())); + } + } + + // Refit the vertex + TransientVertex transVtx; + reco::Vertex refitPV(pv); // initialized to the original PV + + bool fitOK = true; + if (transTracks.size() >= 3) { + AdaptiveVertexFitter avf; + avf.setWeightThreshold(0.1); // weight per track, allow almost every fit, else --> exception + try { + if (!useBeamSpot_) { + transVtx = avf.vertex(transTracks); + } else { + transVtx = avf.vertex(transTracks, *beamSpot); + } + } catch (...) { + fitOK = false; + } + } else + fitOK = false; + if (fitOK) { + refitPV = transVtx; + } + + outputVertices->push_back(refitPV); + + evt.put(std::move(outputVertices)); +} + +void MiniAODRefitVertexProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + // miniAODRefitVertexProducer + edm::ParameterSetDescription desc; + + desc.add("srcVertices", edm::InputTag("offlineSlimmedPrimaryVertices")); + desc.add("srcCands", edm::InputTag("packedPFCandidates")); + desc.add("srcLostTracks", edm::InputTag("lostTracks")); + desc.add("srcEleKfTracks", edm::InputTag("lostTracks:eleTracks")); + desc.add("srcBeamSpot", edm::InputTag("offlineBeamSpot")); + desc.add("useBeamSpot", true); + desc.add("useLostTracks", true); + desc.add("useEleKfTracks", true) + ->setComment("Use collection of electron KF-tracks instead of GSF-tracks of electron PF-candidates"); + + descriptions.addWithDefaultLabel(desc); +} + +#include "FWCore/Framework/interface/MakerMacros.h" +DEFINE_FWK_MODULE(MiniAODRefitVertexProducer);