diff --git a/L1Trigger/Configuration/python/L1Trigger_EventContent_cff.py b/L1Trigger/Configuration/python/L1Trigger_EventContent_cff.py
index 873f8deff4088..60c01724072fe 100644
--- a/L1Trigger/Configuration/python/L1Trigger_EventContent_cff.py
+++ b/L1Trigger/Configuration/python/L1Trigger_EventContent_cff.py
@@ -218,6 +218,7 @@ def _appendPhase2Digis(obj):
'keep *_l1tLayer1EG_*_*',
'keep *_l1tLayer2EG_*_*',
'keep *_l1tMETPFProducer_*_*',
+ 'keep *_l1tMETMLProducer_*_*',
'keep *_l1tNNTauProducer_*_*',
'keep *_l1tNNTauProducerPuppi_*_*',
'keep *_l1tHPSPFTauProducerPF_*_*',
diff --git a/L1Trigger/Configuration/python/SimL1Emulator_cff.py b/L1Trigger/Configuration/python/SimL1Emulator_cff.py
index 99c9ded41520e..59bc34d267031 100644
--- a/L1Trigger/Configuration/python/SimL1Emulator_cff.py
+++ b/L1Trigger/Configuration/python/SimL1Emulator_cff.py
@@ -223,6 +223,7 @@
from L1Trigger.Phase2L1ParticleFlow.l1tMETPFProducer_cfi import *
_phase2_siml1emulator.add(l1tMETPFProducer)
+_phase2_siml1emulator.add(l1tMETMLProducer)
# NNTaus
diff --git a/L1Trigger/Phase2L1ParticleFlow/BuildFile.xml b/L1Trigger/Phase2L1ParticleFlow/BuildFile.xml
index a01f8facb498a..9c3b1442767df 100644
--- a/L1Trigger/Phase2L1ParticleFlow/BuildFile.xml
+++ b/L1Trigger/Phase2L1ParticleFlow/BuildFile.xml
@@ -13,6 +13,8 @@
+
+
diff --git a/L1Trigger/Phase2L1ParticleFlow/plugins/L1MetPfProducer.cc b/L1Trigger/Phase2L1ParticleFlow/plugins/L1MetPfProducer.cc
index d9bcdbb96e192..20eb233ddf235 100644
--- a/L1Trigger/Phase2L1ParticleFlow/plugins/L1MetPfProducer.cc
+++ b/L1Trigger/Phase2L1ParticleFlow/plugins/L1MetPfProducer.cc
@@ -1,4 +1,5 @@
#include
+#include
#include
#include
#include
@@ -13,12 +14,15 @@
#include "DataFormats/L1Trigger/interface/EtSum.h"
#include "DataFormats/Math/interface/LorentzVector.h"
+#include "hls4ml/emulator.h"
+
using namespace l1t;
class L1MetPfProducer : public edm::global::EDProducer<> {
public:
explicit L1MetPfProducer(const edm::ParameterSet&);
~L1MetPfProducer() override;
+ static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);
private:
void produce(edm::StreamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const override;
@@ -29,35 +33,71 @@ class L1MetPfProducer : public edm::global::EDProducer<> {
// quantization controllers
typedef ap_ufixed<14, 12, AP_RND, AP_WRAP> pt_t; // LSB is 0.25 and max is 4 TeV
typedef ap_int<12> phi_t; // LSB is pi/720 ~ 0.0044 and max is +/-8.9
- const float ptLSB_ = 0.25; // GeV
- const float phiLSB_ = M_PI / 720; // rad
+ static constexpr float ptLSB_ = 0.25; // GeV
+ static constexpr float phiLSB_ = M_PI / 720; // rad
// derived, helper types
typedef ap_fixed pxy_t;
typedef ap_fixed<2 * pt_t::width, 2 * pt_t::iwidth, AP_RND, AP_SAT> pt2_t;
// derived, helper constants
- const float maxPt_ = ((1 << pt_t::width) - 1) * ptLSB_;
+ static constexpr float maxPt_ = ((1 << pt_t::width) - 1) * ptLSB_;
const phi_t hwPi_ = round(M_PI / phiLSB_);
const phi_t hwPiOverTwo_ = round(M_PI / (2 * phiLSB_));
typedef ap_ufixed inv_t; // can't easily use the MAXPT/pt trick with ap_fixed
// to make configurable...
- const int dropBits_ = 2;
- const int dropFactor_ = (1 << dropBits_);
- const int invTableBits_ = 10;
- const int invTableSize_ = (1 << invTableBits_);
+ static constexpr int dropBits_ = 2;
+ static constexpr int dropFactor_ = (1 << dropBits_);
+ static constexpr int invTableBits_ = 10;
+ static constexpr int invTableSize_ = (1 << invTableBits_);
+
+ // hls4ml emulator objects
+ bool useMlModel_;
+ std::shared_ptr model;
+ std::string modelVersion_;
+ typedef ap_fixed<32, 16> input_t;
+ typedef ap_fixed<32, 16> result_t;
+ static constexpr int numContInputs_ = 4;
+ static constexpr int numPxPyInputs_ = 2;
+ static constexpr int numCatInputs_ = 2;
+ static constexpr int numInputs_ = numContInputs_ + numPxPyInputs_ + numCatInputs_;
void Project(pt_t pt, phi_t phi, pxy_t& pxy, bool isX, bool debug = false) const;
void PhiFromXY(pxy_t px, pxy_t py, phi_t& phi, bool debug = false) const;
- void CalcMetHLS(std::vector pt, std::vector phi, reco::Candidate::PolarLorentzVector& metVector) const;
+ int EncodePdgId(int pdgId) const;
+
+ void CalcMetHLS(const std::vector& pt,
+ const std::vector& phi,
+ reco::Candidate::PolarLorentzVector& metVector) const;
+ void CalcMlMet(const std::vector& pt,
+ const std::vector& eta,
+ const std::vector& phi,
+ const std::vector& puppiWeight,
+ const std::vector& pdgId,
+ const std::vector& charge,
+ reco::Candidate::PolarLorentzVector& metVector) const;
};
L1MetPfProducer::L1MetPfProducer(const edm::ParameterSet& cfg)
: _l1PFToken(consumes>(cfg.getParameter("L1PFObjects"))),
- maxCands_(cfg.getParameter("maxCands")) {
+ maxCands_(cfg.getParameter("maxCands")),
+ modelVersion_(cfg.getParameter("modelVersion")) {
produces>();
+ useMlModel_ = (modelVersion_.length() > 0);
+ if (useMlModel_) {
+ hls4mlEmulator::ModelLoader loader(modelVersion_);
+ model = loader.load_model();
+ }
+}
+
+void L1MetPfProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
+ edm::ParameterSetDescription desc;
+ desc.add("L1PFObjects", edm::InputTag("L1PFProducer", "l1pfCandidates"));
+ desc.add("maxCands", 128);
+ desc.add("modelVersion", "");
+ descriptions.add("L1MetPfProducer", desc);
}
void L1MetPfProducer::produce(edm::StreamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const {
@@ -65,27 +105,99 @@ void L1MetPfProducer::produce(edm::StreamID, edm::Event& iEvent, const edm::Even
iEvent.getByToken(_l1PFToken, l1PFCandidates);
std::vector pt;
+ std::vector eta;
std::vector phi;
+ std::vector puppiWeight;
+ std::vector pdgId;
+ std::vector charge;
for (int i = 0; i < int(l1PFCandidates->size()) && (i < maxCands_ || maxCands_ < 0); i++) {
const auto& l1PFCand = l1PFCandidates->at(i);
pt.push_back(l1PFCand.pt());
+ eta.push_back(l1PFCand.eta());
phi.push_back(l1PFCand.phi());
+ puppiWeight.push_back(l1PFCand.puppiWeight());
+ pdgId.push_back(l1PFCand.pdgId());
+ charge.push_back(l1PFCand.charge());
}
reco::Candidate::PolarLorentzVector metVector;
- CalcMetHLS(pt, phi, metVector);
+ if (useMlModel_) {
+ CalcMlMet(pt, eta, phi, puppiWeight, pdgId, charge, metVector);
+ } else {
+ CalcMetHLS(pt, phi, metVector);
+ }
l1t::EtSum theMET(metVector, l1t::EtSum::EtSumType::kTotalHt, 0, 0, 0, 0);
- std::unique_ptr> metCollection(new std::vector(0));
+ auto metCollection = std::make_unique>(0);
metCollection->push_back(theMET);
iEvent.put(std::move(metCollection));
}
-void L1MetPfProducer::CalcMetHLS(std::vector pt,
- std::vector phi,
+int L1MetPfProducer::EncodePdgId(int pdgId) const {
+ switch (abs(pdgId)) {
+ case 211: // charged hadron (pion)
+ return 1;
+ case 130: // neutral hadron (kaon)
+ return 2;
+ case 22: // photon
+ return 3;
+ case 13: // muon
+ return 4;
+ case 11: // electron
+ return 5;
+ default:
+ return 0;
+ }
+}
+
+void L1MetPfProducer::CalcMlMet(const std::vector& pt,
+ const std::vector& eta,
+ const std::vector& phi,
+ const std::vector& puppiWeight,
+ const std::vector& pdgId,
+ const std::vector& charge,
+ reco::Candidate::PolarLorentzVector& metVector) const {
+ const int inputSize = maxCands_ * numInputs_;
+
+ input_t input[800];
+ result_t result[2];
+
+ // initialize with zeros (for padding)
+ for (int i = 0; i < inputSize; i++) {
+ input[i] = 0;
+ }
+
+ for (uint i = 0; i < pt.size(); i++) {
+ // input_cont
+ input[i * numContInputs_] = pt[i];
+ input[i * numContInputs_ + 1] = eta[i];
+ input[i * numContInputs_ + 2] = phi[i];
+ input[i * numContInputs_ + 3] = puppiWeight[i];
+ // input_pxpy
+ input[(maxCands_ * numContInputs_) + (i * numPxPyInputs_)] = pt[i] * cos(phi[i]);
+ input[(maxCands_ * numContInputs_) + (i * numPxPyInputs_) + 1] = pt[i] * sin(phi[i]);
+ // input_cat0
+ input[maxCands_ * (numContInputs_ + numPxPyInputs_) + i] = EncodePdgId(pdgId[i]);
+ // input_cat1
+ input[maxCands_ * (numContInputs_ + numPxPyInputs_ + 1) + i] = (abs(charge[i]) <= 1) ? (charge[i] + 2) : 0;
+ }
+
+ model->prepare_input(input);
+ model->predict();
+ model->read_result(result);
+
+ double met_px = -result[0].to_double();
+ double met_py = -result[1].to_double();
+ metVector.SetPt(hypot(met_px, met_py));
+ metVector.SetPhi(atan2(met_py, met_px));
+ metVector.SetEta(0);
+}
+
+void L1MetPfProducer::CalcMetHLS(const std::vector& pt,
+ const std::vector& phi,
reco::Candidate::PolarLorentzVector& metVector) const {
pxy_t hw_px = 0;
pxy_t hw_py = 0;
diff --git a/L1Trigger/Phase2L1ParticleFlow/python/l1tMETPFProducer_cfi.py b/L1Trigger/Phase2L1ParticleFlow/python/l1tMETPFProducer_cfi.py
index 52e45f324d441..7f047d3dfb675 100644
--- a/L1Trigger/Phase2L1ParticleFlow/python/l1tMETPFProducer_cfi.py
+++ b/L1Trigger/Phase2L1ParticleFlow/python/l1tMETPFProducer_cfi.py
@@ -3,4 +3,11 @@
l1tMETPFProducer = cms.EDProducer("L1MetPfProducer",
L1PFObjects = cms.InputTag("l1tLayer1","Puppi"),
maxCands = cms.int32(128),
+ modelVersion = cms.string(""),
+)
+
+l1tMETMLProducer = cms.EDProducer("L1MetPfProducer",
+ L1PFObjects = cms.InputTag("l1tLayer1","Puppi"),
+ maxCands = cms.int32(100),
+ modelVersion = cms.string("L1METML_v1"),
)