diff --git a/Configuration/ProcessModifiers/python/phase2L3MuonsOIFirst_cff.py b/Configuration/ProcessModifiers/python/phase2L3MuonsOIFirst_cff.py new file mode 100644 index 0000000000000..715f86fef6af3 --- /dev/null +++ b/Configuration/ProcessModifiers/python/phase2L3MuonsOIFirst_cff.py @@ -0,0 +1,4 @@ +import FWCore.ParameterSet.Config as cms + +# this modifier is for enabling Phase 2 L3 Tracker Muon Outside-In first reconstruction +phase2L3MuonsOIFirst = cms.Modifier() diff --git a/Configuration/PyReleaseValidation/python/upgradeWorkflowComponents.py b/Configuration/PyReleaseValidation/python/upgradeWorkflowComponents.py index 31543ebd3b380..e4a2cef035865 100644 --- a/Configuration/PyReleaseValidation/python/upgradeWorkflowComponents.py +++ b/Configuration/PyReleaseValidation/python/upgradeWorkflowComponents.py @@ -814,6 +814,70 @@ def condition(self, fragment, stepList, key, hasHarvest): upgradeWFs['ticl_v5_superclustering_mustache_pf'].step3 = {'--procModifiers': 'ticl_v5,ticl_superclustering_mustache_pf'} upgradeWFs['ticl_v5_superclustering_mustache_pf'].step4 = {'--procModifiers': 'ticl_v5,ticl_superclustering_mustache_pf'} +# Improved L2 seeding from L1Tk Muons and L3 Tracker Muon Inside-Out reconstruction first (Phase-2 Muon default) +class UpgradeWorkflow_phase2L2AndL3Muons(UpgradeWorkflow): + def setup_(self, step, stepName, stepDict, k, properties): + if ('Digi' in step and 'NoHLT' not in step) or ('HLTOnly' in step): + stepDict[stepName][k] = merge([self.step2, stepDict[step][k]]) + if 'RecoGlobal' in step: + stepDict[stepName][k] = merge([self.step3, stepDict[step][k]]) + if 'HARVESTGlobal' in step: + stepDict[stepName][k] = merge([self.step4, stepDict[step][k]]) + def condition(self, fragment, stepList, key, hasHarvest): + return (fragment=="ZMM_14" or 'SingleMu' in fragment or 'TTbar_14' in fragment) and 'Run4' in key + +upgradeWFs['phase2L2AndL3Muons'] = UpgradeWorkflow_phase2L2AndL3Muons( + steps = [ + 'HLTOnly', + 'DigiTrigger', + 'RecoGlobal', + 'HARVESTGlobal' + ], + PU = [ + 'HLTOnly', + 'DigiTrigger', + 'RecoGlobal', + 'HARVESTGlobal' + ], + suffix = '_phase2L2AndL3MuonsIOFirst', + offset = 0.777, +) +upgradeWFs['phase2L2AndL3Muons'].step2 = {'--procModifiers':'phase2L2AndL3Muons'} +upgradeWFs['phase2L2AndL3Muons'].step3 = {'--procModifiers':'phase2L2AndL3Muons'} +upgradeWFs['phase2L2AndL3Muons'].step4 = {'--procModifiers':'phase2L2AndL3Muons'} + +# Improved L2 seeding from L1Tk Muons and L3 Tracker Muon Outside-In reconstruction first +class UpgradeWorkflow_phase2L3MuonsOIFirst(UpgradeWorkflow): + def setup_(self, step, stepName, stepDict, k, properties): + if ('Digi' in step and 'NoHLT' not in step) or ('HLTOnly' in step): + stepDict[stepName][k] = merge([self.step2, stepDict[step][k]]) + if 'RecoGlobal' in step: + stepDict[stepName][k] = merge([self.step3, stepDict[step][k]]) + if 'HARVESTGlobal' in step: + stepDict[stepName][k] = merge([self.step4, stepDict[step][k]]) + def condition(self, fragment, stepList, key, hasHarvest): + return (fragment=="ZMM_14" or 'SingleMu' in fragment or 'TTbar_14' in fragment) and 'Run4' in key + +upgradeWFs['phase2L3MuonsOIFirst'] = UpgradeWorkflow_phase2L3MuonsOIFirst( + steps = [ + 'HLTOnly', + 'DigiTrigger', + 'RecoGlobal', + 'HARVESTGlobal' + ], + PU = [ + 'HLTOnly', + 'DigiTrigger', + 'RecoGlobal', + 'HARVESTGlobal' + ], + suffix = '_phase2L2AndL3MuonsOIFirst', + offset = 0.778, +) +upgradeWFs['phase2L3MuonsOIFirst'].step2 = {'--procModifiers':'phase2L2AndL3Muons,phase2L3MuonsOIFirst'} +upgradeWFs['phase2L3MuonsOIFirst'].step3 = {'--procModifiers':'phase2L2AndL3Muons,phase2L3MuonsOIFirst'} +upgradeWFs['phase2L3MuonsOIFirst'].step4 = {'--procModifiers':'phase2L2AndL3Muons,phase2L3MuonsOIFirst'} + # Track DNN workflows class UpgradeWorkflow_trackdnn(UpgradeWorkflow): def setup_(self, step, stepName, stepDict, k, properties): diff --git a/HLTrigger/Configuration/python/HLT_75e33/modules/hltIter2Phase2L3FromL1TkMuonPixelSeedsFiltered_cfi.py b/HLTrigger/Configuration/python/HLT_75e33/modules/hltIter2Phase2L3FromL1TkMuonPixelSeedsFiltered_cfi.py index deff17f466795..2ca7951fd4860 100644 --- a/HLTrigger/Configuration/python/HLT_75e33/modules/hltIter2Phase2L3FromL1TkMuonPixelSeedsFiltered_cfi.py +++ b/HLTrigger/Configuration/python/HLT_75e33/modules/hltIter2Phase2L3FromL1TkMuonPixelSeedsFiltered_cfi.py @@ -33,3 +33,8 @@ nSeedsMax_E = cms.int32(20), src = cms.InputTag("hltIter2Phase2L3FromL1TkMuonPixelSeeds") ) + +from Configuration.ProcessModifiers.phase2L2AndL3Muons_cff import phase2L2AndL3Muons +from Configuration.ProcessModifiers.phase2L3MuonsOIFirst_cff import phase2L3MuonsOIFirst +(phase2L2AndL3Muons & phase2L3MuonsOIFirst).toModify(hltIter2Phase2L3FromL1TkMuonPixelSeedsFiltered, L1TkMu = cms.InputTag("hltPhase2L3MuonFilter", "L1TkMuToReuse")) + diff --git a/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3FromL1TkMuonPixelTracksTrackingRegions_cfi.py b/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3FromL1TkMuonPixelTracksTrackingRegions_cfi.py index 0a3b8c684c74c..b08c6a6a0df84 100644 --- a/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3FromL1TkMuonPixelTracksTrackingRegions_cfi.py +++ b/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3FromL1TkMuonPixelTracksTrackingRegions_cfi.py @@ -22,3 +22,7 @@ zErrorVetex = cms.double(0.2) ) ) + +from Configuration.ProcessModifiers.phase2L2AndL3Muons_cff import phase2L2AndL3Muons +from Configuration.ProcessModifiers.phase2L3MuonsOIFirst_cff import phase2L3MuonsOIFirst +(phase2L2AndL3Muons & phase2L3MuonsOIFirst).toModify(hltPhase2L3FromL1TkMuonPixelTracksTrackingRegions.RegionPSet, input = cms.InputTag("hltPhase2L3MuonFilter", "L1TkMuToReuse")) \ No newline at end of file diff --git a/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3MuonFilter_cfi.py b/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3MuonFilter_cfi.py new file mode 100644 index 0000000000000..5f5ddb4bad2a6 --- /dev/null +++ b/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3MuonFilter_cfi.py @@ -0,0 +1,25 @@ +import FWCore.ParameterSet.Config as cms + +hltPhase2L3MuonFilter = cms.EDProducer( + "phase2HLTMuonSelectorForL3", + l1TkMuons=cms.InputTag("l1tTkMuonsGmt"), + l2MuonsUpdVtx=cms.InputTag("hltL2MuonsFromL1TkMuon", "UpdatedAtVtx"), + l3Tracks=cms.InputTag("hltIter2Phase2L3FromL1TkMuonMerged"), + IOFirst=cms.bool(True), + matchingDr=cms.double(0.02), + applyL3Filters=cms.bool(True), + MinNhits=cms.int32(1), + MaxNormalizedChi2=cms.double(5.0), + MinNhitsMuons=cms.int32(0), + MinNhitsPixel=cms.int32(1), + MinNhitsTracker=cms.int32(6), + MaxPtDifference=cms.double(999.0), +) + +from Configuration.ProcessModifiers.phase2L2AndL3Muons_cff import phase2L2AndL3Muons +from Configuration.ProcessModifiers.phase2L3MuonsOIFirst_cff import phase2L3MuonsOIFirst +(phase2L2AndL3Muons & phase2L3MuonsOIFirst).toModify( + hltPhase2L3MuonFilter, + l3Tracks=cms.InputTag("hltPhase2L3OIMuonTrackSelectionHighPurity"), + IOFirst=cms.bool(False), +) diff --git a/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3MuonMerged_cfi.py b/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3MuonMerged_cfi.py index 710bf4a1eaac2..dea49b722b7b8 100644 --- a/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3MuonMerged_cfi.py +++ b/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3MuonMerged_cfi.py @@ -1,25 +1,55 @@ import FWCore.ParameterSet.Config as cms -hltPhase2L3MuonMerged = cms.EDProducer("TrackListMerger", - Epsilon = cms.double(-0.001), - FoundHitBonus = cms.double(5.0), - LostHitPenalty = cms.double(20.0), - MaxNormalizedChisq = cms.double(1000.0), - MinFound = cms.int32(3), - MinPT = cms.double(0.05), - ShareFrac = cms.double(0.19), - TrackProducers = cms.VInputTag("hltPhase2L3OIMuonTrackSelectionHighPurity", "hltIter2Phase2L3FromL1TkMuonMerged"), - allowFirstHitShare = cms.bool(True), - copyExtras = cms.untracked.bool(True), - copyMVA = cms.bool(False), - hasSelector = cms.vint32(0, 0), - indivShareFrac = cms.vdouble(1.0, 1.0), - newQuality = cms.string('confirmed'), - selectedTrackQuals = cms.VInputTag("hltPhase2L3OIMuonTrackSelectionHighPurity", "hltIter2Phase2L3FromL1TkMuonMerged"), - setsToMerge = cms.VPSet(cms.PSet( - pQual = cms.bool(False), - tLists = cms.vint32(0, 1) - )), - trackAlgoPriorityOrder = cms.string('hltESPTrackAlgoPriorityOrder'), - writeOnlyTrkQuals = cms.bool(False) +hltPhase2L3MuonMerged = cms.EDProducer( + "TrackListMerger", + Epsilon=cms.double(-0.001), + FoundHitBonus=cms.double(5.0), + LostHitPenalty=cms.double(20.0), + MaxNormalizedChisq=cms.double(1000.0), + MinFound=cms.int32(3), + MinPT=cms.double(0.05), + ShareFrac=cms.double(0.19), + TrackProducers=cms.VInputTag( + "hltPhase2L3OIMuonTrackSelectionHighPurity", + "hltIter2Phase2L3FromL1TkMuonMerged", + ), + allowFirstHitShare=cms.bool(True), + copyExtras=cms.untracked.bool(True), + copyMVA=cms.bool(False), + hasSelector=cms.vint32(0, 0), + indivShareFrac=cms.vdouble(1.0, 1.0), + newQuality=cms.string("confirmed"), + selectedTrackQuals=cms.VInputTag( + "hltPhase2L3OIMuonTrackSelectionHighPurity", + "hltIter2Phase2L3FromL1TkMuonMerged", + ), + setsToMerge=cms.VPSet(cms.PSet(pQual=cms.bool(False), tLists=cms.vint32(0, 1))), + trackAlgoPriorityOrder=cms.string("hltESPTrackAlgoPriorityOrder"), + writeOnlyTrkQuals=cms.bool(False), +) + +from Configuration.ProcessModifiers.phase2L2AndL3Muons_cff import phase2L2AndL3Muons +phase2L2AndL3Muons.toModify( + hltPhase2L3MuonMerged, + TrackProducers=cms.VInputTag( + "hltPhase2L3OIMuonTrackSelectionHighPurity", + cms.InputTag("hltPhase2L3MuonFilter", "L3IOTracksFiltered"), + ), + selectedTrackQuals=cms.VInputTag( + "hltPhase2L3OIMuonTrackSelectionHighPurity", + cms.InputTag("hltPhase2L3MuonFilter", "L3IOTracksFiltered"), + ), +) + +from Configuration.ProcessModifiers.phase2L3MuonsOIFirst_cff import phase2L3MuonsOIFirst +(phase2L2AndL3Muons & phase2L3MuonsOIFirst).toModify( + hltPhase2L3MuonMerged, + TrackProducers=cms.VInputTag( + cms.InputTag("hltPhase2L3MuonFilter", "L3OITracksFiltered"), + "hltIter2Phase2L3FromL1TkMuonMerged", + ), + selectedTrackQuals=cms.VInputTag( + cms.InputTag("hltPhase2L3MuonFilter", "L3OITracksFiltered"), + "hltIter2Phase2L3FromL1TkMuonMerged", + ), ) diff --git a/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3OISeedsFromL2Muons_cfi.py b/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3OISeedsFromL2Muons_cfi.py index 3ec2fa2b86f9f..4c121b3dccb3a 100644 --- a/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3OISeedsFromL2Muons_cfi.py +++ b/HLTrigger/Configuration/python/HLT_75e33/modules/hltPhase2L3OISeedsFromL2Muons_cfi.py @@ -39,3 +39,9 @@ tsosDiff1 = cms.double(0.2), tsosDiff2 = cms.double(0.02) ) + +from Configuration.ProcessModifiers.phase2L2AndL3Muons_cff import phase2L2AndL3Muons +phase2L2AndL3Muons.toModify(hltPhase2L3OISeedsFromL2Muons, src = cms.InputTag("hltPhase2L3MuonFilter", "L2MuToReuse")) + +from Configuration.ProcessModifiers.phase2L3MuonsOIFirst_cff import phase2L3MuonsOIFirst +(phase2L2AndL3Muons & phase2L3MuonsOIFirst).toModify(hltPhase2L3OISeedsFromL2Muons, src = cms.InputTag("hltL2MuonsFromL1TkMuon","UpdatedAtVtx")) diff --git a/HLTrigger/Configuration/python/HLT_75e33/sequences/HLTMuonsSequence_cfi.py b/HLTrigger/Configuration/python/HLT_75e33/sequences/HLTMuonsSequence_cfi.py index 22434b77a2ab3..0a0bb00ecc56b 100644 --- a/HLTrigger/Configuration/python/HLT_75e33/sequences/HLTMuonsSequence_cfi.py +++ b/HLTrigger/Configuration/python/HLT_75e33/sequences/HLTMuonsSequence_cfi.py @@ -7,4 +7,44 @@ from ..sequences.HLTPhase2L3MuonsSequence_cfi import * from ..sequences.HLTPhase2L3OISequence_cfi import * -HLTMuonsSequence = cms.Sequence(HLTL2MuonsFromL1TkSequence+HLTPhase2L3OISequence+HLTPhase2L3FromL1TkSequence+HLTIter0Phase2L3FromL1TkSequence+HLTIter2Phase2L3FromL1TkSequence+HLTPhase2L3MuonsSequence) +HLTMuonsSequence = cms.Sequence( + HLTL2MuonsFromL1TkSequence + + HLTPhase2L3OISequence + + HLTPhase2L3FromL1TkSequence + + HLTIter0Phase2L3FromL1TkSequence + + HLTIter2Phase2L3FromL1TkSequence + + HLTPhase2L3MuonsSequence +) + +from ..modules.hltPhase2L3MuonFilter_cfi import * + +# The IO first HLT Muons sequence +Phase2HLTMuonsSequenceIOFirst = cms.Sequence( + HLTL2MuonsFromL1TkSequence + + HLTPhase2L3FromL1TkSequence + + HLTIter0Phase2L3FromL1TkSequence + + HLTIter2Phase2L3FromL1TkSequence + + hltPhase2L3MuonFilter + + HLTPhase2L3OISequence + + HLTPhase2L3MuonsSequence +) +# The OI first HLT Muons sequence +Phase2HLTMuonsSequenceOIFirst = cms.Sequence( + HLTL2MuonsFromL1TkSequence + + HLTPhase2L3OISequence + + hltPhase2L3MuonFilter + + HLTPhase2L3FromL1TkSequence + + HLTIter0Phase2L3FromL1TkSequence + + HLTIter2Phase2L3FromL1TkSequence + + HLTPhase2L3MuonsSequence +) + +from Configuration.ProcessModifiers.phase2L2AndL3Muons_cff import phase2L2AndL3Muons + +phase2L2AndL3Muons.toReplaceWith(HLTMuonsSequence, Phase2HLTMuonsSequenceIOFirst) + +from Configuration.ProcessModifiers.phase2L3MuonsOIFirst_cff import phase2L3MuonsOIFirst + +(phase2L2AndL3Muons & phase2L3MuonsOIFirst).toReplaceWith( + HLTMuonsSequence, Phase2HLTMuonsSequenceOIFirst +) diff --git a/RecoMuon/L3TrackFinder/interface/phase2HLTMuonSelectorForL3.h b/RecoMuon/L3TrackFinder/interface/phase2HLTMuonSelectorForL3.h new file mode 100644 index 0000000000000..9a807f7df1c28 --- /dev/null +++ b/RecoMuon/L3TrackFinder/interface/phase2HLTMuonSelectorForL3.h @@ -0,0 +1,81 @@ +#ifndef RecoMuon_L3TrackFinder_phase2HLTMuonSelectorForL3_H +#define RecoMuon_L3TrackFinder_phase2HLTMuonSelectorForL3_H + +/** \class phase2HLTMuonSelectorForL3 + * + * Phase-2 L3 selector for Muons + * This module allows to choose whether to perform + * Inside-Out or Outside-In reconstruction first for L3 Muons, + * performing the second pass only on candidates that were not + * reconstructed or whose quality was not good enough. Required + * quality criteria are configurable, the default parameters + * match the requests of HLT Muon ID. + * When Inside-Out reconstruction is performed first, the resulting + * L3 Tracks are filtered and geometrically matched with L2 + * Standalone Muons. If either the match is unsuccessful, or + * the L3 track is not of good-enough quality, the associated + * Standalone Muon will be re-used to seed the Outside-In step. + * The Outside-In first approach follows a similar logic by + * matching the L3 tracks directly with L1 Tracker Muons. + * Then, when either the match fails or the track is not of + * good-enough quality, the L1 Tracker Muon is re-used to seed + * the Inside-Out reconstruction. + * + * \author Luca Ferragina (INFN BO), 2024 + */ + +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/ConsumesCollector.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/Utilities/interface/ESGetToken.h" +#include "FWCore/Utilities/interface/InputTag.h" + +#include "DataFormats/L1TMuonPhase2/interface/TrackerMuon.h" +#include "DataFormats/MuonReco/interface/MuonFwd.h" +#include "DataFormats/MuonSeed/interface/L2MuonTrajectorySeedCollection.h" +#include "DataFormats/TrackReco/interface/Track.h" +#include "DataFormats/TrackReco/interface/TrackFwd.h" + +namespace edm { + class ParameterSet; + class Event; + class EventSetup; +} // End namespace edm + +class phase2HLTMuonSelectorForL3 : public edm::stream::EDProducer<> { +public: + // Constructor + phase2HLTMuonSelectorForL3(const edm::ParameterSet&); + + // Destructor + ~phase2HLTMuonSelectorForL3() override = default; + + // Default values + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + + // Select objects to be reused + void produce(edm::Event&, const edm::EventSetup&) override; + +private: + const edm::EDGetTokenT l1TkMuCollToken_; + const edm::EDGetTokenT l2MuCollectionToken_; + const edm::EDGetTokenT l3TrackCollectionToken_; + + const bool IOFirst_; + const double matchingDr_; + const bool applyL3Filters_; + const double maxNormalizedChi2_, maxPtDifference_; + const int minNhits_, minNhitsMuons_, minNhitsPixel_, minNhitsTracker_; + + // Check L3 inner track quality parameters + const bool rejectL3Track(l1t::TrackerMuonRef l1TkMuRef, reco::TrackRef l3TrackRef) const; +}; + +#endif diff --git a/RecoMuon/L3TrackFinder/src/phase2HLTMuonSelectorForL3.cc b/RecoMuon/L3TrackFinder/src/phase2HLTMuonSelectorForL3.cc new file mode 100644 index 0000000000000..a8090c8d149ac --- /dev/null +++ b/RecoMuon/L3TrackFinder/src/phase2HLTMuonSelectorForL3.cc @@ -0,0 +1,206 @@ +/** \class phase2HLTMuonSelectorForL3 + * See header file for a description of this class + * \author Luca Ferragina (INFN BO), 2024 + */ +#include "RecoMuon/L3TrackFinder/interface/phase2HLTMuonSelectorForL3.h" + +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "DataFormats/Math/interface/deltaR.h" + +#include + +// Constructor +phase2HLTMuonSelectorForL3::phase2HLTMuonSelectorForL3(const edm::ParameterSet& iConfig) + : l1TkMuCollToken_(consumes(iConfig.getParameter("l1TkMuons"))), + l2MuCollectionToken_(consumes(iConfig.getParameter("l2MuonsUpdVtx"))), + l3TrackCollectionToken_(consumes(iConfig.getParameter("l3Tracks"))), + IOFirst_(iConfig.getParameter("IOFirst")), + matchingDr_(iConfig.getParameter("matchingDr")), + applyL3Filters_(iConfig.getParameter("applyL3Filters")), + maxNormalizedChi2_(iConfig.getParameter("MaxNormalizedChi2")), + maxPtDifference_(iConfig.getParameter("MaxPtDifference")), + minNhits_(iConfig.getParameter("MinNhits")), + minNhitsMuons_(iConfig.getParameter("MinNhitsMuons")), + minNhitsPixel_(iConfig.getParameter("MinNhitsPixel")), + minNhitsTracker_(iConfig.getParameter("MinNhitsTracker")) { + if (IOFirst_) { + produces("L2MuToReuse"); + produces("L3IOTracksFiltered"); + } else { + produces("L1TkMuToReuse"); + produces("L3OITracksFiltered"); + } +} + +void phase2HLTMuonSelectorForL3::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("l1TkMuons", edm::InputTag("l1tTkMuonsGmt")); + desc.add("l2MuonsUpdVtx", edm::InputTag("hltL2MuonsFromL1TkMuon", "UpdatedAtVtx")); + desc.add("l3Tracks", edm::InputTag("hltIter2Phase2L3FromL1TkMuonMerged")); + desc.add("IOFirst", true); + desc.add("matchingDr", 0.02); + desc.add("applyL3Filters", true); + desc.add("MinNhits", 1); + desc.add("MaxNormalizedChi2", 5.0); + desc.add("MinNhitsMuons", 0); + desc.add("MinNhitsPixel", 1); + desc.add("MinNhitsTracker", 6); + desc.add("MaxPtDifference", 999.0); //relative difference + descriptions.add("phase2HLTMuonSelectorForL3", desc); +} + +// IO first -> collection of L2 muons not already matched to a L3 inner track +// OI first -> collection of L1Tk Muons not matched to a L3 track +void phase2HLTMuonSelectorForL3::produce(edm::Event& iEvent, const edm::EventSetup& iSetup) { + const std::string metname = "RecoMuon|phase2HLTMuonSelectorForL3"; + + // L3 tracks (IO or OI) + auto l3TracksCollectionH = iEvent.getHandle(l3TrackCollectionToken_); + + if (IOFirst_) { + LogDebug(metname) << "Inside-Out reconstruction done first, looping over L2 muons"; + + // L2 Muons collection + auto const l2MuonsCollectionH = iEvent.getHandle(l2MuCollectionToken_); + + // Output + std::unique_ptr L2MuToReuse = std::make_unique(); + std::unique_ptr L3IOTracksFiltered = std::make_unique(); + + // Indexes of good L3 Tracks + std::unordered_set goodL3Indexes; + + // Loop over L2 Muons + for (size_t l2MuIndex = 0; l2MuIndex != l2MuonsCollectionH->size(); ++l2MuIndex) { + reco::TrackRef l2MuRef(l2MuonsCollectionH, l2MuIndex); + bool reuseL2 = true; + + // Extract L1TkMu from L2 Muon + edm::RefToBase seedRef = l2MuRef->seedRef(); + edm::Ref l2Seed = seedRef.castTo>(); + l1t::TrackerMuonRef l1TkMuRef = l2Seed->l1TkMu(); + + // Check validity of cast (actually found a L1TkMu) + if (l1TkMuRef.isNonnull()) { + // Loop over L3 tracks + LogDebug(metname) << "Looping over L3 tracks"; + for (size_t l3MuIndex = 0; l3MuIndex != l3TracksCollectionH->size(); ++l3MuIndex) { + reco::TrackRef l3TrackRef(l3TracksCollectionH, l3MuIndex); + bool rejectL3 = true; + // Filter L3 Tracks + if (applyL3Filters_) { + LogDebug(metname) << "Checking L3 Track quality"; + rejectL3 = rejectL3Track(l1TkMuRef, l3TrackRef); + if (!rejectL3) { + LogDebug(metname) << "Adding good quality L3 IO track to filtered collection"; + goodL3Indexes.insert(l3MuIndex); + } + } + // Check match in dR + float dR2 = deltaR2(l1TkMuRef->phEta(), l1TkMuRef->phPhi(), l3TrackRef->eta(), l3TrackRef->phi()); + LogDebug(metname) << "deltaR2: " << dR2; + if (dR2 < matchingDr_ * matchingDr_) { + LogDebug(metname) << "Found L2 muon that matches the L3 track"; + reuseL2 = applyL3Filters_ ? rejectL3 : false; + LogDebug(metname) << "Reuse L2: " << reuseL2; + } + } // End loop over L3 Tracks + } else { + LogDebug(metname) << "Found L2 muon without an associated L1TkMu"; + } + if (reuseL2) { + LogDebug(metname) << "Found a L2 muon to be reused"; + L2MuToReuse->push_back(*l2MuRef); + } + } // End loop over L2 Muons + + // Fill L3 IO Tracks Filtered + for (const size_t index : goodL3Indexes) { + L3IOTracksFiltered->push_back(*(reco::TrackRef(l3TracksCollectionH, index))); + } + + LogDebug(metname) << "Placing L2 Muons to be reused in the event"; + iEvent.put(std::move(L2MuToReuse), "L2MuToReuse"); + LogDebug(metname) << "Placing good quality L3 IO Tracks in the event"; + iEvent.put(std::move(L3IOTracksFiltered), "L3IOTracksFiltered"); + } else { + LogDebug(metname) << "Outside-In reconstruction done first, looping over L1Tk muons"; + + // L1Tk Muons collection + auto const l1TkMuonsCollectionH = iEvent.getHandle(l1TkMuCollToken_); + + // Output + std::unique_ptr L1TkMuToReuse = std::make_unique(); + std::unique_ptr L3OITracksFiltered = std::make_unique(); + + // Indexes of good L3 Tracks + std::unordered_set goodL3Indexes; + + // Loop over L1Tk Muons + for (size_t l1TkMuIndex = 0; l1TkMuIndex != l1TkMuonsCollectionH->size(); ++l1TkMuIndex) { + l1t::TrackerMuonRef l1TkMuRef(l1TkMuonsCollectionH, l1TkMuIndex); + bool reuseL1TkMu = true; + + // Loop over L3 tracks + LogDebug(metname) << "Looping over L3 tracks"; + for (size_t l3MuIndex = 0; l3MuIndex != l3TracksCollectionH->size(); ++l3MuIndex) { + reco::TrackRef l3TrackRef(l3TracksCollectionH, l3MuIndex); + bool rejectL3 = true; + // Filter L3 Tracks + if (applyL3Filters_) { + LogDebug(metname) << "Checking L3 Track quality"; + rejectL3 = rejectL3Track(l1TkMuRef, l3TrackRef); + if (!rejectL3) { + LogDebug(metname) << "Adding good quality L3 OI track to filtered collection"; + goodL3Indexes.insert(l3MuIndex); + } + } + // Check match in dR + float dR2 = deltaR2(l1TkMuRef->phEta(), l1TkMuRef->phPhi(), l3TrackRef->eta(), l3TrackRef->phi()); + LogDebug(metname) << "deltaR2: " << dR2; + if (dR2 < matchingDr_ * matchingDr_) { + LogDebug(metname) << "Found L1TkMu that matches the L3 track"; + reuseL1TkMu = applyL3Filters_ ? rejectL3 : false; + LogDebug(metname) << "Reuse L1TkMu: " << reuseL1TkMu; + } + } // End loop over L3 Tracks + if (reuseL1TkMu) { + LogDebug(metname) << "Found a L1TkMu to be reused"; + L1TkMuToReuse->push_back(*l1TkMuRef); + } + } // End loop over L1Tk Muons + + // Fill L3 OI Tracks Filtered + for (const size_t index : goodL3Indexes) { + L3OITracksFiltered->push_back(*(reco::TrackRef(l3TracksCollectionH, index))); + } + + LogDebug(metname) << "Placing L1Tk Muons to be reused in the event"; + iEvent.put(std::move(L1TkMuToReuse), "L1TkMuToReuse"); + LogDebug(metname) << "Placing good quality L3 OI Tracks in the event"; + iEvent.put(std::move(L3OITracksFiltered), "L3OITracksFiltered"); + } +} + +const bool phase2HLTMuonSelectorForL3::rejectL3Track(l1t::TrackerMuonRef l1TkMuRef, reco::TrackRef l3TrackRef) const { + const std::string metname = "RecoMuon|phase2HLTMuonSelectorForL3"; + + bool nHitsCut = l3TrackRef->numberOfValidHits() < minNhits_; + bool chi2Cut = l3TrackRef->normalizedChi2() > maxNormalizedChi2_; + bool nHitsMuonsCut = l3TrackRef->hitPattern().numberOfValidMuonHits() < minNhitsMuons_; + bool nHitsPixelCut = l3TrackRef->hitPattern().numberOfValidPixelHits() < minNhitsPixel_; + bool nHitsTrackerCut = l3TrackRef->hitPattern().trackerLayersWithMeasurement() < minNhitsTracker_; + bool ptCut = std::abs(l3TrackRef->pt() - l1TkMuRef->phPt()) > maxPtDifference_ * l3TrackRef->pt(); + + bool reject = nHitsCut or chi2Cut or nHitsMuonsCut or nHitsPixelCut or nHitsTrackerCut or ptCut; + + LogDebug(metname) << "nHits: " << l3TrackRef->numberOfValidHits() << " | chi2: " << l3TrackRef->normalizedChi2() + << " | nHitsMuon: " << l3TrackRef->hitPattern().numberOfValidMuonHits() + << " | nHitsPixel: " << l3TrackRef->hitPattern().numberOfValidPixelHits() + << " | nHitsTracker: " << l3TrackRef->hitPattern().trackerLayersWithMeasurement(); + LogDebug(metname) << "Reject L3 Track: " << reject; + return reject; +} + +DEFINE_FWK_MODULE(phase2HLTMuonSelectorForL3);