diff --git a/L1Trigger/Configuration/python/L1Trigger_EventContent_cff.py b/L1Trigger/Configuration/python/L1Trigger_EventContent_cff.py index 80ad9390f2292..c5fe7085fa66f 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..185667d7d81a9 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; @@ -48,16 +52,50 @@ class L1MetPfProducer : public edm::global::EDProducer<> { const int invTableBits_ = 10; const 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; + int numContInputs_ = 4; + int numPxPyInputs_ = 2; + int numCatInputs_ = 2; + 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; + int EncodePdgId(int pdgId) const; + void CalcMetHLS(std::vector pt, std::vector phi, reco::Candidate::PolarLorentzVector& metVector) const; + void CalcMlMet(std::vector pt, + std::vector eta, + std::vector phi, + std::vector puppiWeight, + std::vector pdgId, + 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,17 +103,29 @@ 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); @@ -84,6 +134,66 @@ void L1MetPfProducer::produce(edm::StreamID, edm::Event& iEvent, const edm::Even iEvent.put(std::move(metCollection)); } +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(std::vector pt, + std::vector eta, + std::vector phi, + std::vector puppiWeight, + std::vector pdgId, + 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(std::vector pt, std::vector phi, reco::Candidate::PolarLorentzVector& metVector) const { 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"), )