diff --git a/DataFormats/L1DTTrackFinder/BuildFile.xml b/DataFormats/L1DTTrackFinder/BuildFile.xml
index a4b5fbf1485f5..2a83a0228a182 100644
--- a/DataFormats/L1DTTrackFinder/BuildFile.xml
+++ b/DataFormats/L1DTTrackFinder/BuildFile.xml
@@ -2,4 +2,5 @@
+
diff --git a/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhDigi.h b/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhDigi.h
index 0364990377b19..0f9f2a720b148 100644
--- a/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhDigi.h
+++ b/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhDigi.h
@@ -56,7 +56,9 @@ class L1Phase2MuDTExtPhDigi : public L1Phase2MuDTPhDigi {
int tdc[8] = nullptr,
int lat[8] = nullptr);
- L1Phase2MuDTExtPhDigi(const L1Phase2MuDTExtPhDigi &digi);
+ L1Phase2MuDTExtPhDigi(const L1Phase2MuDTExtPhDigi& digi);
+ L1Phase2MuDTExtPhDigi& operator=(const L1Phase2MuDTExtPhDigi&) = default;
+ L1Phase2MuDTExtPhDigi& operator=(L1Phase2MuDTExtPhDigi&&) = default;
~L1Phase2MuDTExtPhDigi() override {}
diff --git a/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhiThetaPair.h b/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhiThetaPair.h
new file mode 100644
index 0000000000000..105a9cd820860
--- /dev/null
+++ b/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhiThetaPair.h
@@ -0,0 +1,43 @@
+#ifndef DataFormats_L1DTTrackFinder_L1Phase2MuDTExtPhiThetaPair_h
+#define DataFormats_L1DTTrackFinder_L1Phase2MuDTExtPhiThetaPair_h
+
+/** \class L1Phase2MuDTExtPhiThetaPair
+ *
+ * Data container for a matched pair of Phase-2 DT Phi and Theta digis.
+ * Provides utilities to retrieve the closest-N time-position pairs per chamber ordered by Phi quality.
+ *
+ * \author J. Fernandez
+ * \date 2025-11-19
+ */
+
+#include "DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhDigi.h"
+#include "DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtThDigi.h"
+#include
+#include
+
+class L1Phase2MuDTExtPhiThetaPair {
+public:
+ /// Default constructor
+ L1Phase2MuDTExtPhiThetaPair() = default;
+
+ /// Constructor with digis and quality
+ L1Phase2MuDTExtPhiThetaPair(const L1Phase2MuDTExtPhDigi& phi, const L1Phase2MuDTExtThDigi& theta, int quality);
+
+ // Explicitly allow copy/move
+ L1Phase2MuDTExtPhiThetaPair(const L1Phase2MuDTExtPhiThetaPair&) = default;
+ L1Phase2MuDTExtPhiThetaPair& operator=(const L1Phase2MuDTExtPhiThetaPair&) = default;
+ L1Phase2MuDTExtPhiThetaPair(L1Phase2MuDTExtPhiThetaPair&&) = default;
+ L1Phase2MuDTExtPhiThetaPair& operator=(L1Phase2MuDTExtPhiThetaPair&&) = default;
+
+ /// Accessors
+ const L1Phase2MuDTExtPhDigi& phiDigi() const { return phi_; }
+ const L1Phase2MuDTExtThDigi& thetaDigi() const { return theta_; }
+ int quality() const { return quality_; }
+
+private:
+ L1Phase2MuDTExtPhDigi phi_;
+ L1Phase2MuDTExtThDigi theta_;
+ int quality_;
+};
+
+#endif
diff --git a/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhiThetaPairContainer.h b/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhiThetaPairContainer.h
new file mode 100644
index 0000000000000..74d9cf5bf076f
--- /dev/null
+++ b/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhiThetaPairContainer.h
@@ -0,0 +1,50 @@
+//-------------------------------------------------
+//
+// Class L1Phase2MuDTExtPhiThetaPairContainer
+//
+// Description: trigger primtive data for the
+// muon barrel Phase2 trigger
+//
+//
+// Author List: J. Fernandez - Oviedo ICTEA
+//
+//
+//--------------------------------------------------
+#ifndef L1Phase2MuDTExtPhiThetaPairContainer_H
+#define L1Phase2MuDTExtPhiThetaPairContainer_H
+
+//------------------------------------
+// Collaborating Class Declarations --
+//------------------------------------
+#include "DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhiThetaPair.h"
+
+//----------------------
+// Base Class Headers --
+//----------------------
+#include
+
+//---------------
+// C++ Headers --
+//---------------
+
+//---------------------
+//-- Class Interface --
+//---------------------
+
+class L1Phase2MuDTExtPhiThetaPairContainer {
+public:
+ typedef std::vector Segment_Container;
+ typedef Segment_Container::const_iterator Segment_iterator;
+
+ // Constructor
+ L1Phase2MuDTExtPhiThetaPairContainer();
+
+ void setContainer(const Segment_Container& inputSegments);
+
+ Segment_Container const& getContainer() const;
+
+private:
+ Segment_Container m_segments;
+};
+
+#endif
diff --git a/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtThDigi.h b/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtThDigi.h
index 75fcc10478063..f6e392cdd33e9 100644
--- a/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtThDigi.h
+++ b/DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtThDigi.h
@@ -54,7 +54,9 @@ class L1Phase2MuDTExtThDigi : public L1Phase2MuDTThDigi {
int tdc[4] = nullptr,
int lat[4] = nullptr);
- L1Phase2MuDTExtThDigi(const L1Phase2MuDTExtThDigi &digi);
+ L1Phase2MuDTExtThDigi(const L1Phase2MuDTExtThDigi& digi);
+ L1Phase2MuDTExtThDigi& operator=(const L1Phase2MuDTExtThDigi&) = default;
+ L1Phase2MuDTExtThDigi& operator=(L1Phase2MuDTExtThDigi&&) = default;
~L1Phase2MuDTExtThDigi() override {}
diff --git a/DataFormats/L1DTTrackFinder/src/L1Phase2MuDTExtPhiThetaPair.cc b/DataFormats/L1DTTrackFinder/src/L1Phase2MuDTExtPhiThetaPair.cc
new file mode 100644
index 0000000000000..aafaee24912f7
--- /dev/null
+++ b/DataFormats/L1DTTrackFinder/src/L1Phase2MuDTExtPhiThetaPair.cc
@@ -0,0 +1,6 @@
+#include "DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhiThetaPair.h"
+
+L1Phase2MuDTExtPhiThetaPair::L1Phase2MuDTExtPhiThetaPair(const L1Phase2MuDTExtPhDigi& phi,
+ const L1Phase2MuDTExtThDigi& theta,
+ int quality)
+ : phi_(phi), theta_(theta), quality_(quality) {}
diff --git a/DataFormats/L1DTTrackFinder/src/L1Phase2MuDTExtPhiThetaPairContainer.cc b/DataFormats/L1DTTrackFinder/src/L1Phase2MuDTExtPhiThetaPairContainer.cc
new file mode 100644
index 0000000000000..e6dd96c594f61
--- /dev/null
+++ b/DataFormats/L1DTTrackFinder/src/L1Phase2MuDTExtPhiThetaPairContainer.cc
@@ -0,0 +1,46 @@
+//-------------------------------------------------
+//
+// Class L1Phase2MuDTExtPhiThetaPairContainer
+//
+// Description: trigger primtive data for the
+// muon barrel Phase2 trigger
+//
+//
+// Author List: J. Fernandez - Oviedo ICTEA
+//
+//
+//--------------------------------------------------
+
+//-----------------------
+// This Class's Header --
+//-----------------------
+#include "DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhiThetaPairContainer.h"
+
+//-------------------------------
+// Collaborating Class Headers --
+//-------------------------------
+
+//---------------
+// C++ Headers --
+//---------------
+
+//-------------------
+// Initializations --
+//-------------------
+
+//----------------
+// Constructors --
+//----------------
+L1Phase2MuDTExtPhiThetaPairContainer::L1Phase2MuDTExtPhiThetaPairContainer() {}
+
+//--------------
+// Operations --
+//--------------
+void L1Phase2MuDTExtPhiThetaPairContainer::setContainer(const Segment_Container& inputSegments) {
+ m_segments = inputSegments;
+}
+
+L1Phase2MuDTExtPhiThetaPairContainer::Segment_Container const& L1Phase2MuDTExtPhiThetaPairContainer::getContainer()
+ const {
+ return m_segments;
+}
diff --git a/DataFormats/L1DTTrackFinder/src/classes.h b/DataFormats/L1DTTrackFinder/src/classes.h
index efcd0d5612165..b78a137cdbce1 100644
--- a/DataFormats/L1DTTrackFinder/src/classes.h
+++ b/DataFormats/L1DTTrackFinder/src/classes.h
@@ -14,4 +14,6 @@
#include
#include
#include
+#include
+#include
#include
diff --git a/DataFormats/L1DTTrackFinder/src/classes_def.xml b/DataFormats/L1DTTrackFinder/src/classes_def.xml
index 7e68549b00265..b2fedfc4e5985 100644
--- a/DataFormats/L1DTTrackFinder/src/classes_def.xml
+++ b/DataFormats/L1DTTrackFinder/src/classes_def.xml
@@ -24,6 +24,7 @@
+
@@ -34,6 +35,7 @@
+
@@ -56,7 +58,7 @@
-
+
@@ -67,10 +69,12 @@
+
+
diff --git a/L1Trigger/Configuration/python/L1Trigger_EventContent_cff.py b/L1Trigger/Configuration/python/L1Trigger_EventContent_cff.py
index a5ee86d334285..d5e9562eaf37f 100644
--- a/L1Trigger/Configuration/python/L1Trigger_EventContent_cff.py
+++ b/L1Trigger/Configuration/python/L1Trigger_EventContent_cff.py
@@ -258,6 +258,9 @@ def _appendPhase2Digis(obj):
'keep *_l1tPhase2L1CaloEGammaEmulator_*_*',
'keep *_l1tGTProducer_*_*',
'keep *_l1tGTAlgoBlockProducer_*_*',
+ 'keep *_dtTriggerPhase2PrimitiveDigis_*_*',
+ 'keep *_dtTriggerPhase2Showers_*_*',
+ 'keep *_dtTriggerPhase2PrimitivePairDigis_*_*'
]
obj.outputCommands += l1Phase2Digis
diff --git a/L1Trigger/Configuration/python/SimL1Emulator_cff.py b/L1Trigger/Configuration/python/SimL1Emulator_cff.py
index 7c7b348875993..1ca6967ff2a25 100644
--- a/L1Trigger/Configuration/python/SimL1Emulator_cff.py
+++ b/L1Trigger/Configuration/python/SimL1Emulator_cff.py
@@ -79,6 +79,8 @@
_phase2_siml1emulator.add(dtTriggerPhase2PrimitiveDigis)
from L1Trigger.DTTriggerPhase2.dtTriggerPhase2Showers_cfi import *
_phase2_siml1emulator.add(dtTriggerPhase2Shower)
+from L1Trigger.DTTriggerPhase2.dtTriggerPhase2PrimitivePairDigis_cfi import *
+_phase2_siml1emulator.add(dtTriggerPhase2PrimitivePairDigis)
# HGCAL TP
# ########################################################################
diff --git a/L1Trigger/DTTriggerPhase2/plugins/DTTrigPhase2PairsProd.cc b/L1Trigger/DTTriggerPhase2/plugins/DTTrigPhase2PairsProd.cc
new file mode 100644
index 0000000000000..47d9ac05f8e18
--- /dev/null
+++ b/L1Trigger/DTTriggerPhase2/plugins/DTTrigPhase2PairsProd.cc
@@ -0,0 +1,371 @@
+#include "FWCore/Framework/interface/MakerMacros.h"
+#include "FWCore/Framework/interface/stream/EDProducer.h"
+#include "FWCore/Utilities/interface/ESGetToken.h"
+#include "FWCore/Framework/interface/ModuleFactory.h"
+#include "FWCore/Framework/interface/ESProducer.h"
+#include "FWCore/Framework/interface/ESHandle.h"
+#include "FWCore/Framework/interface/ESProducts.h"
+#include "FWCore/Framework/interface/ConsumesCollector.h"
+
+#include "FWCore/Framework/interface/Event.h"
+#include "FWCore/Framework/interface/Frameworkfwd.h"
+#include "FWCore/Framework/interface/EventSetup.h"
+#include "FWCore/Framework/interface/Run.h"
+#include "FWCore/ParameterSet/interface/ParameterSet.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+
+#include "Geometry/Records/interface/MuonGeometryRecord.h"
+#include "Geometry/DTGeometry/interface/DTGeometry.h"
+#include "Geometry/DTGeometry/interface/DTLayer.h"
+
+#include "L1Trigger/DTTriggerPhase2/interface/constants.h"
+
+//inputs
+#include "DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhContainer.h"
+#include "DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhDigi.h"
+#include "DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtThContainer.h"
+#include "DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtThDigi.h"
+
+//outputs
+#include "DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhiThetaPair.h"
+#include "DataFormats/L1DTTrackFinder/interface/L1Phase2MuDTExtPhiThetaPairContainer.h"
+
+#include
+#include
+#include
+#include
+
+// Plugin which takes Phi and Theta DT Phase2 digi Extended collections(*) and
+// creates a new class container of the 4 best closest Phi-Theta digi pairs per DT chamber
+// based on the time-position phase-space distance and ordered by Phi quality.
+
+// For those cases where there is no Phi/Theta partner, 4 best quality single Phi/Theta digis are saved
+// and counterpart in pair is set to default values of the constructor digi class
+//
+// (*) Requires df_extended>0 in L1Trigger/DTTriggerPhase2/plugins/DTTrigPhase2Prod.cc
+
+using namespace std;
+using namespace cmsdt;
+
+namespace {
+ struct {
+ bool operator()(const L1Phase2MuDTExtPhDigi& mp1, const L1Phase2MuDTExtPhDigi& mp2) const {
+ int sector1 = mp1.scNum();
+ int wheel1 = mp1.whNum();
+ int station1 = mp1.stNum();
+
+ int sector2 = mp2.scNum();
+ int wheel2 = mp2.whNum();
+ int station2 = mp2.stNum();
+
+ // First, compare by chamber
+ if (sector1 != sector2)
+ return sector1 < sector2;
+ if (wheel1 != wheel2)
+ return wheel1 < wheel2;
+ if (station1 != station2)
+ return station1 < station2;
+
+ // If they are in the same chamber, sort by quality
+ return mp1.quality() > mp2.quality();
+ }
+ } const comparePhiDigis;
+
+ struct {
+ bool operator()(const L1Phase2MuDTThDigi& mp1, const L1Phase2MuDTThDigi& mp2) const {
+ int sector1 = mp1.scNum();
+ int wheel1 = mp1.whNum();
+ int station1 = mp1.stNum();
+
+ int sector2 = mp2.scNum();
+ int wheel2 = mp2.whNum();
+ int station2 = mp2.stNum();
+
+ // First, compare by chamber
+ if (sector1 != sector2)
+ return sector1 < sector2;
+ if (wheel1 != wheel2)
+ return wheel1 < wheel2;
+ if (station1 != station2)
+ return station1 < station2;
+
+ // If they are in the same chamber, sort by quality
+ return mp1.quality() > mp2.quality();
+ }
+ } const compareThetaDigis;
+
+ struct {
+ bool operator()(const L1Phase2MuDTExtPhiThetaPair& mp1, const L1Phase2MuDTExtPhiThetaPair& mp2) const {
+ return mp1.quality() > mp2.quality();
+ }
+ } const comparePairs;
+
+} //namespace
+
+class DTTrigPhase2PairsProd : public edm::stream::EDProducer<> {
+public:
+ //! Constructor
+ DTTrigPhase2PairsProd(const edm::ParameterSet& pset);
+
+ //! Destructor
+ ~DTTrigPhase2PairsProd() override;
+
+ //! Create Trigger Units before starting event processing
+ void beginRun(edm::Run const& iRun, const edm::EventSetup& iEventSetup) override;
+
+ //! Producer: process every event and generates trigger data
+ void produce(edm::Event& iEvent, const edm::EventSetup& iEventSetup) override;
+
+ //! endRun: finish things
+ void endRun(edm::Run const& iRun, const edm::EventSetup& iEventSetup) override;
+
+ // Methods
+ std::vector bestPairsPerChamber(const std::vector& phiDigis,
+ const std::vector& thetaDigis,
+ unsigned int maxPairs,
+ int wheel,
+ int sector,
+ int station);
+ float computeTimePosDistance(
+ const L1Phase2MuDTExtPhDigi& phiDigi, const L1Phase2MuDTExtThDigi& thetaDigi, int sector, int wheel, int station);
+
+ // Setter-methods
+
+ static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);
+
+ // data-members
+
+ double shift_back;
+
+private:
+ // Debug Flag
+ bool debug_;
+ int scenario_;
+ int max_index_;
+
+ // ParameterSet
+ edm::EDGetTokenT digiPhToken_;
+ edm::EDGetTokenT digiThToken_;
+};
+
+DTTrigPhase2PairsProd::DTTrigPhase2PairsProd(const edm::ParameterSet& pset) {
+ produces();
+
+ debug_ = pset.getUntrackedParameter("debug");
+ scenario_ = pset.getParameter("scenario");
+ max_index_ = 4; //Not configurable due to hardware capacities
+
+ digiPhToken_ = consumes(pset.getParameter("digiPhTag"));
+ digiThToken_ = consumes(pset.getParameter("digiThTag"));
+
+ double temp_shift = 0;
+ if (scenario_ == MC)
+ temp_shift = 400; // value used in standard CMSSW simulation
+ else if (scenario_ == DATA)
+ temp_shift = 0;
+ else if (scenario_ == SLICE_TEST)
+ temp_shift = 400; // slice test mimics simulation
+
+ shift_back = temp_shift;
+}
+
+DTTrigPhase2PairsProd::~DTTrigPhase2PairsProd() {
+ if (debug_)
+ LogDebug("DTTrigPhase2PairsProd") << "calling destructor" << std::endl;
+}
+
+void DTTrigPhase2PairsProd::beginRun(edm::Run const& iRun, const edm::EventSetup& iEventSetup) {
+ if (debug_)
+ LogDebug("DTTrigPhase2PairsProd") << "beginRun " << iRun.id().run();
+}
+
+void DTTrigPhase2PairsProd::produce(edm::Event& iEvent, const edm::EventSetup& iEventSetup) {
+ if (debug_)
+ LogDebug("DTTrigPhase2PairsProd") << "produce";
+
+ edm::Handle thePhiDigis;
+ iEvent.getByToken(digiPhToken_, thePhiDigis);
+
+ edm::Handle theThetaDigis;
+ iEvent.getByToken(digiThToken_, theThetaDigis);
+
+ if (!thePhiDigis || !theThetaDigis) {
+ throw cms::Exception("DTTrigPhase2PairsProd") << "Phi or Theta container is null!";
+ }
+
+ if (debug_)
+ LogDebug("DTTrigPhase2PairsProd") << " Containers read";
+
+ using ChamberKey = std::tuple; // (wheel, sector, station)
+
+ std::map> phiByChamber;
+ std::map> thetaByChamber;
+
+ if (debug_)
+ LogDebug("DTTrigPhase2PairsProd") << "Variables declared";
+
+ // Group phi digis
+ for (auto phiIte = thePhiDigis->getContainer()->begin(); phiIte != thePhiDigis->getContainer()->end(); ++phiIte) {
+ ChamberKey key(phiIte->whNum(), phiIte->scNum(), phiIte->stNum());
+ phiByChamber[key].push_back(*phiIte);
+ }
+
+ // Group theta digis
+ for (auto thetaIte = theThetaDigis->getContainer()->begin(); thetaIte != theThetaDigis->getContainer()->end();
+ ++thetaIte) {
+ ChamberKey key(thetaIte->whNum(), thetaIte->scNum(), thetaIte->stNum());
+ thetaByChamber[key].push_back(*thetaIte);
+ }
+
+ if (debug_)
+ LogDebug("DTTrigPhase2PairsProd") << "Grouping per chamber";
+
+ std::vector allPairs;
+
+ // Process each chamber key from phi digis
+ for (const auto& [key, phiList] : phiByChamber) {
+ std::vector chamberPairs;
+ std::vector phiDigis;
+ std::vector thetaDigis;
+
+ auto thetaListIt = thetaByChamber.find(key);
+
+ for (const auto& phi : phiList)
+ phiDigis.emplace_back(phi);
+
+ if (thetaListIt != thetaByChamber.end()) { // Both phi and theta exist
+ for (const auto& theta : thetaListIt->second)
+ thetaDigis.emplace_back(theta);
+ }
+
+ if (debug_)
+ LogDebug("DTTrigPhase2PairsProd") << "Working on chamber:";
+
+ std::sort(phiDigis.begin(), phiDigis.end(), comparePhiDigis);
+ std::sort(thetaDigis.begin(), thetaDigis.end(), compareThetaDigis);
+
+ if (debug_)
+ LogDebug("DTTrigPhase2PairsProd") << "Sorting";
+
+ auto [wheel, sector, station] = key;
+ chamberPairs = bestPairsPerChamber(phiDigis, thetaDigis, max_index_, wheel, sector, station);
+
+ if (debug_)
+ LogDebug("DTTrigPhase2PairsProd") << "Saving top 4";
+
+ for (const auto& pair : chamberPairs)
+ allPairs.emplace_back(pair);
+ }
+
+ if (debug_)
+ LogDebug("DTTrigPhase2PairsProd") << "Saving products";
+
+ // Storing results in the event
+ std::unique_ptr resultPhiThetaPair(new L1Phase2MuDTExtPhiThetaPairContainer);
+ resultPhiThetaPair->setContainer(allPairs);
+ iEvent.put(std::move(resultPhiThetaPair));
+ if (debug_)
+ LogDebug("DTTrigPhase2PairsProd") << "Saved in the event";
+
+ allPairs.clear();
+ allPairs.erase(allPairs.begin(), allPairs.end());
+}
+
+void DTTrigPhase2PairsProd::endRun(edm::Run const& iRun, const edm::EventSetup& iEventSetup) {};
+
+void DTTrigPhase2PairsProd::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
+ // dtTriggerPhase2PrimitivePairDigis
+ edm::ParameterSetDescription desc;
+ desc.add("digiPhTag", edm::InputTag("dtTriggerPhase2PrimitiveDigis"));
+ desc.add("digiThTag", edm::InputTag("dtTriggerPhase2PrimitiveDigis"));
+ desc.add("scenario", 0);
+ desc.addUntracked("debug", false);
+ descriptions.add("dtTriggerPhase2PrimitivePairDigis", desc);
+}
+
+DEFINE_FWK_MODULE(DTTrigPhase2PairsProd);
+
+std::vector DTTrigPhase2PairsProd::bestPairsPerChamber(
+ const std::vector& phiDigis,
+ const std::vector& thetaDigis,
+ unsigned int maxPairs,
+ int wheel,
+ int sector,
+ int station) {
+ std::vector pairs;
+
+ if (station == 4 || thetaDigis.empty()) { // no theta digis in chamber
+
+ L1Phase2MuDTExtThDigi emptyTheta;
+ for (const auto& phi : phiDigis) {
+ int phiQuality = phi.quality();
+ pairs.emplace_back(phi, emptyTheta, phiQuality);
+ }
+
+ }
+
+ else if (phiDigis.empty() && !(thetaDigis.empty())) { // no phi digis in chamber
+
+ L1Phase2MuDTExtPhDigi emptyPhi;
+ for (const auto& theta : thetaDigis) {
+ int thetaQuality = theta.quality();
+ pairs.emplace_back(emptyPhi, theta, thetaQuality);
+ }
+
+ }
+
+ else { // phi and theta digis in chamber
+
+ for (const auto& phi : phiDigis) {
+ float closestDistance = numeric_limits::infinity();
+ const L1Phase2MuDTExtThDigi* closestTheta = nullptr;
+
+ for (const auto& theta : thetaDigis) {
+ float currentDistance = computeTimePosDistance(phi, theta, sector, wheel, station);
+ if (closestDistance > currentDistance) {
+ closestDistance = currentDistance;
+ closestTheta = θ
+ }
+ }
+ int phiQuality = phi.quality();
+ if (closestTheta)
+ pairs.emplace_back(phi, *closestTheta, phiQuality);
+ }
+ }
+ // Sort by quality descending
+ std::sort(pairs.begin(), pairs.end(), comparePairs);
+
+ // Keep only top-N
+ if (pairs.size() > maxPairs)
+ pairs.resize(maxPairs);
+
+ return pairs;
+}
+
+float DTTrigPhase2PairsProd::computeTimePosDistance(
+ const L1Phase2MuDTExtPhDigi& phiDigi, const L1Phase2MuDTExtThDigi& thetaDigi, int sector, int wheel, int station) {
+ float t01 = ((int)round(thetaDigi.t0() / (float)LHC_CLK_FREQ)) - shift_back;
+ float posRefZ = zFE[wheel + 2];
+
+ if (wheel == 0 && (sector == 1 || sector == 4 || sector == 5 || sector == 8 || sector == 9 || sector == 12))
+ posRefZ = -posRefZ;
+
+ float posZ = abs(thetaDigi.z());
+
+ float t02 = ((int)round(phiDigi.t0() / (float)LHC_CLK_FREQ)) - shift_back;
+
+ float tphi = t02 - abs(posZ / ZRES_CONV - posRefZ) / vwire;
+
+ int LR = -1;
+ if (wheel == 0 && (sector == 3 || sector == 4 || sector == 7 || sector == 8 || sector == 11 || sector == 12))
+ LR = +1;
+ else if (wheel > 0)
+ LR = pow(-1, wheel + sector + 1);
+ else if (wheel < 0)
+ LR = pow(-1, -wheel + sector);
+
+ float posRefX = LR * xFE[station - 1];
+ float ttheta = t01 - (phiDigi.xLocal() / 1000 - posRefX) / vwire;
+
+ return abs(tphi - ttheta);
+}
diff --git a/L1Trigger/DTTriggerPhase2/plugins/DTTrigPhase2Prod.cc b/L1Trigger/DTTriggerPhase2/plugins/DTTrigPhase2Prod.cc
index 5d31059221cba..da6d871bdd4ce 100644
--- a/L1Trigger/DTTriggerPhase2/plugins/DTTrigPhase2Prod.cc
+++ b/L1Trigger/DTTriggerPhase2/plugins/DTTrigPhase2Prod.cc
@@ -122,7 +122,6 @@ class DTTrigPhase2Prod : public edm::stream::EDProducer<> {
// data-members
const DTGeometry* dtGeo_;
edm::ESGetToken dtGeomH;
- std::vector> primitives_;
private:
// Trigger Configuration Manager CCB validity flag
@@ -177,9 +176,9 @@ class DTTrigPhase2Prod : public edm::stream::EDProducer<> {
bool activateBuffer_;
int superCellhalfspacewidth_;
float superCelltimewidth_;
- std::vector distribDigis(std::queue>& inQ);
+ std::vector distribDigis(std::queue>& inQ);
void processDigi(std::queue>& inQ,
- std::vector>*>& vec);
+ std::vector>>& vec);
// RPC
std::unique_ptr rpc_integrator_;
@@ -375,14 +374,14 @@ void DTTrigPhase2Prod::produce(Event& iEvent, const EventSetup& iEventSetup) {
tmpvec.clear();
// Distribute the digis from the queue into supercells
- std::vector superCells;
+ std::vector superCells;
superCells = distribDigis(timequeue);
// Process each supercell & collect the resulting muonpaths (as the muonpaths std::vector is only enlarged each time
// the groupings access it, it's not needed to "collect" the final products).
while (!superCells.empty()) {
- grouping_obj_->run(iEvent, iEventSetup, *(superCells.back()), muonpaths[chid.rawId()]);
+ grouping_obj_->run(iEvent, iEventSetup, superCells.back(), muonpaths[chid.rawId()]);
superCells.pop_back();
}
} else {
@@ -1247,35 +1246,36 @@ int DTTrigPhase2Prod::assignQualityOrder(const metaPrimitive& mP) const {
return qmap_.find(mP.quality)->second;
}
-std::vector DTTrigPhase2Prod::distribDigis(std::queue>& inQ) {
- std::vector>*> tmpVector;
+std::vector DTTrigPhase2Prod::distribDigis(std::queue>& inQ) {
+ // Use value-based containers to avoid returning pointers to local variables.
+ std::vector>> tmpVector;
tmpVector.clear();
- std::vector collVector;
+ std::vector collVector;
collVector.clear();
while (!inQ.empty()) {
processDigi(inQ, tmpVector);
}
for (auto& sQ : tmpVector) {
DTDigiCollection tmpColl;
- while (!sQ->empty()) {
- tmpColl.insertDigi((sQ->front().first), (sQ->front().second));
- sQ->pop();
+ while (!sQ.empty()) {
+ tmpColl.insertDigi((sQ.front().first), (sQ.front().second));
+ sQ.pop();
}
- collVector.push_back(&tmpColl);
+ collVector.push_back(std::move(tmpColl));
}
return collVector;
}
void DTTrigPhase2Prod::processDigi(std::queue>& inQ,
- std::vector>*>& vec) {
+ std::vector>>& vec) {
bool classified = false;
if (!vec.empty()) {
for (auto& sC : vec) { // Conditions for entering a super cell.
- if ((sC->front().second.time() + superCelltimewidth_) > inQ.front().second.time()) {
+ if ((sC.front().second.time() + superCelltimewidth_) > inQ.front().second.time()) {
// Time requirement
- if (std::abs(sC->front().second.wire() - inQ.front().second.wire()) <= superCellhalfspacewidth_) {
+ if (std::abs(sC.front().second.wire() - inQ.front().second.wire()) <= superCellhalfspacewidth_) {
// Spatial requirement
- sC->push(std::move(inQ.front()));
+ sC.push(std::move(inQ.front()));
classified = true;
}
}
@@ -1293,7 +1293,7 @@ void DTTrigPhase2Prod::processDigi(std::queue>& inQ
newQueue.push(tmpPair);
inQ.pop();
- vec.push_back(&newQueue);
+ vec.push_back(std::move(newQueue));
}
void DTTrigPhase2Prod::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
diff --git a/L1Trigger/DTTriggerPhase2/python/dtTriggerPhase2PrimitiveDigis_cfi.py b/L1Trigger/DTTriggerPhase2/python/dtTriggerPhase2PrimitiveDigis_cfi.py
index b50d2fb012fb3..c0c1ae9bc3077 100644
--- a/L1Trigger/DTTriggerPhase2/python/dtTriggerPhase2PrimitiveDigis_cfi.py
+++ b/L1Trigger/DTTriggerPhase2/python/dtTriggerPhase2PrimitiveDigis_cfi.py
@@ -23,7 +23,7 @@
allow_confirmation = cms.bool(True),
minx_match_2digis = cms.double(1.),
scenario = cms.int32(0), #0 for mc, 1 for data, 2 for slice test
- df_extended = cms.int32(0), # DF: 0 for standard, 1 for extended, 2 for both
+ df_extended = cms.int32(2), # DF: 0 for standard, 1 for extended, 2 for both
co_option = cms.int32(1), # coincidence w.r.t. : -1 = off, 0 = co all, 1 = co phi, 2 = co theta
co_quality = cms.int32(1), # quality cut (>X) for coincidence TP: request TPs to be above this cut
co_wh2option = cms.int32(1),#0 (filter all stations in Wh2), 1(pass Wh2 St1), 2(pass Wh2 St1+2)
diff --git a/L1Trigger/DTTriggerPhase2/python/dtTriggerPhase2PrimitivePairDigis_cfi.py b/L1Trigger/DTTriggerPhase2/python/dtTriggerPhase2PrimitivePairDigis_cfi.py
new file mode 100644
index 0000000000000..27bb153d8dded
--- /dev/null
+++ b/L1Trigger/DTTriggerPhase2/python/dtTriggerPhase2PrimitivePairDigis_cfi.py
@@ -0,0 +1,11 @@
+
+import FWCore.ParameterSet.Config as cms
+
+dtTriggerPhase2PrimitivePairDigis = cms.EDProducer("DTTrigPhase2PairsProd",
+ scenario = cms.int32(0), #0 for mc, 1 for data, 2 for slice test
+ digiPhTag = cms.InputTag("dtTriggerPhase2PrimitiveDigis",""),
+ digiThTag = cms.InputTag("dtTriggerPhase2PrimitiveDigis",""),
+
+ #debugging
+ debug = cms.untracked.bool(False),
+)
diff --git a/L1Trigger/DTTriggerPhase2/src/MPThetaMatching.cc b/L1Trigger/DTTriggerPhase2/src/MPThetaMatching.cc
index 74e7439a19815..a01d569d453b2 100644
--- a/L1Trigger/DTTriggerPhase2/src/MPThetaMatching.cc
+++ b/L1Trigger/DTTriggerPhase2/src/MPThetaMatching.cc
@@ -8,10 +8,12 @@ namespace {
struct {
bool operator()(const metaPrimitive &mp1, const metaPrimitive &mp2) const {
DTChamberId chId1(mp1.rawId);
+ DTSuperLayerId slId1(mp1.rawId);
int sector1 = chId1.sector();
int wheel1 = chId1.wheel();
int station1 = chId1.station();
+ int sl1 = slId1.superLayer();
DTChamberId chId2(mp2.rawId);
DTSuperLayerId slId2(mp2.rawId);
@@ -19,6 +21,7 @@ namespace {
int sector2 = chId2.sector();
int wheel2 = chId2.wheel();
int station2 = chId2.station();
+ int sl2 = slId2.superLayer();
// First, compare by chamber
if (sector1 != sector2)
@@ -27,11 +30,47 @@ namespace {
return wheel1 < wheel2;
if (station1 != station2)
return station1 < station2;
+ if (sl1 != sl2)
+ return sl1 < sl2;
- // If they are in the same category, sort by the value (4th index)
+ // If they are in the same chamber and SL, sort by the quality
return mp1.quality > mp2.quality;
}
} const compareMPs;
+
+ struct {
+ bool operator()(const metaPrimitive &mp1, const metaPrimitive &mp2) const {
+ // Use main characteristics to know if it is the same metaPrimitive
+ if (mp1.rawId != mp2.rawId)
+ return mp1.rawId < mp2.rawId;
+
+ if (mp1.quality != mp2.quality)
+ return mp1.quality > mp2.quality;
+
+ if (mp1.phi != mp2.phi)
+ return mp1.phi < mp2.phi;
+
+ if (mp1.phiB != mp2.phiB)
+ return mp1.phiB < mp2.phiB;
+
+ if (mp1.chi2 != mp2.chi2)
+ return mp1.chi2 < mp2.chi2;
+
+ return mp1.t0 < mp2.t0;
+ }
+ } const deepCompareMPs;
+
+ struct {
+ bool operator()(const metaPrimitive &mp1, const metaPrimitive &mp2) const {
+ // Use main characteristics to know if it is the same metaPrimitive
+ if (mp1.rawId != mp2.rawId || mp1.quality != mp2.quality || mp1.phi != mp2.phi || mp1.phiB != mp2.phiB ||
+ mp1.chi2 != mp2.chi2 || mp1.t0 != mp2.t0)
+ return false;
+ else
+ return true;
+ }
+ } const sameMPs;
+
} //namespace
// ============================================================================
@@ -80,6 +119,13 @@ void MPThetaMatching::run(edm::Event &iEvent,
<< std::endl;
outMPaths = inMPaths; //no filter at all
}
+
+ ///////////////////////////
+ // Filter out duplicates
+ // Sort by features so that equal keys become adjacent
+ std::sort(outMPaths.begin(), outMPaths.end(), deepCompareMPs);
+ // Unique-erase adjacent duplicates
+ outMPaths.erase(std::unique(outMPaths.begin(), outMPaths.end(), sameMPs), outMPaths.end());
}
void MPThetaMatching::finish() {};
@@ -298,7 +344,7 @@ void MPThetaMatching::orderAndSave(std::vector(p).t0 = std::get<1>(p).t0; //replace t0 by associated phi t0
+ //std::get<0>(p).t0 = std::get<1>(p).t0; //replace t0 by associated phi t0
outMPs->push_back(std::get<1>(p)); //add PhiMP
outMPs->push_back(std::get<0>(p)); //add ThetaMP
savedThetaMPs->push_back(std::get<0>(p)); //for accounting
@@ -312,9 +358,8 @@ void MPThetaMatching::orderAndSave(std::vector(p).rawId);
if (count < th_option_ || (abs(chId.wheel()) == 2 && chId.station() < 3 && count < (th_option_ + 1))) {
if (std::get<1>(p).quality > th_quality_) {
- //if (abs(chId.wheel())!=0)
- std::get<0>(p).t0 = std::get<1>(p).t0; //replace t0 by associated phi t0
- outMPs->push_back(std::get<0>(p)); //add ThetaMP
+ //std::get<0>(p).t0 = std::get<1>(p).t0; //replace t0 by associated phi t0
+ outMPs->push_back(std::get<0>(p)); //add ThetaMP
savedThetaMPs->push_back(std::get<0>(p));
count++;
}