CMS 3D CMS Logo

L1MetPfProducer.cc
Go to the documentation of this file.
1 #include <vector>
2 #include <string>
3 #include <ap_int.h>
4 #include <ap_fixed.h>
5 #include <TVector2.h>
6 
12 
16 
17 #include "hls4ml/emulator.h"
18 
19 using namespace l1t;
20 
22 public:
23  explicit L1MetPfProducer(const edm::ParameterSet&);
24  ~L1MetPfProducer() override;
25  static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);
26 
27 private:
28  void produce(edm::StreamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const override;
30 
31  int maxCands_ = 128;
32 
33  // quantization controllers
34  typedef ap_ufixed<14, 12, AP_RND, AP_WRAP> pt_t; // LSB is 0.25 and max is 4 TeV
35  typedef ap_int<12> phi_t; // LSB is pi/720 ~ 0.0044 and max is +/-8.9
36  static constexpr float ptLSB_ = 0.25; // GeV
37  static constexpr float phiLSB_ = M_PI / 720; // rad
38 
39  // derived, helper types
40  typedef ap_fixed<pt_t::width + 1, pt_t::iwidth + 1, AP_RND, AP_SAT> pxy_t;
41  typedef ap_fixed<2 * pt_t::width, 2 * pt_t::iwidth, AP_RND, AP_SAT> pt2_t;
42  // derived, helper constants
43  static constexpr float maxPt_ = ((1 << pt_t::width) - 1) * ptLSB_;
44  const phi_t hwPi_ = round(M_PI / phiLSB_);
45  const phi_t hwPiOverTwo_ = round(M_PI / (2 * phiLSB_));
46 
47  typedef ap_ufixed<pt_t::width, 0> inv_t; // can't easily use the MAXPT/pt trick with ap_fixed
48 
49  // to make configurable...
50  static constexpr int dropBits_ = 2;
51  static constexpr int dropFactor_ = (1 << dropBits_);
52  static constexpr int invTableBits_ = 10;
53  static constexpr int invTableSize_ = (1 << invTableBits_);
54 
55  // hls4ml emulator objects
57  std::shared_ptr<hls4mlEmulator::Model> model;
59  typedef ap_fixed<32, 16> input_t;
60  typedef ap_fixed<32, 16> result_t;
61  static constexpr int numContInputs_ = 4;
62  static constexpr int numPxPyInputs_ = 2;
63  static constexpr int numCatInputs_ = 2;
64  static constexpr int numInputs_ = numContInputs_ + numPxPyInputs_ + numCatInputs_;
65 
66  void Project(pt_t pt, phi_t phi, pxy_t& pxy, bool isX, bool debug = false) const;
67  void PhiFromXY(pxy_t px, pxy_t py, phi_t& phi, bool debug = false) const;
68 
69  int EncodePdgId(int pdgId) const;
70 
71  void CalcMetHLS(const std::vector<float>& pt,
72  const std::vector<float>& phi,
73  reco::Candidate::PolarLorentzVector& metVector) const;
74  void CalcMlMet(const std::vector<float>& pt,
75  const std::vector<float>& eta,
76  const std::vector<float>& phi,
77  const std::vector<float>& puppiWeight,
78  const std::vector<int>& pdgId,
79  const std::vector<int>& charge,
80  reco::Candidate::PolarLorentzVector& metVector) const;
81 };
82 
84  : _l1PFToken(consumes<std::vector<l1t::PFCandidate>>(cfg.getParameter<edm::InputTag>("L1PFObjects"))),
85  maxCands_(cfg.getParameter<int>("maxCands")),
86  modelVersion_(cfg.getParameter<std::string>("modelVersion")) {
87  produces<std::vector<l1t::EtSum>>();
88  useMlModel_ = (modelVersion_.length() > 0);
89  if (useMlModel_) {
90  hls4mlEmulator::ModelLoader loader(modelVersion_);
91  model = loader.load_model();
92  }
93 }
94 
97  desc.add<edm::InputTag>("L1PFObjects", edm::InputTag("L1PFProducer", "l1pfCandidates"));
98  desc.add<int>("maxCands", 128);
99  desc.add<std::string>("modelVersion", "");
100  descriptions.add("L1MetPfProducer", desc);
101 }
102 
105  iEvent.getByToken(_l1PFToken, l1PFCandidates);
106 
107  std::vector<float> pt;
108  std::vector<float> eta;
109  std::vector<float> phi;
110  std::vector<float> puppiWeight;
111  std::vector<int> pdgId;
112  std::vector<int> charge;
113 
114  for (int i = 0; i < int(l1PFCandidates->size()) && (i < maxCands_ || maxCands_ < 0); i++) {
115  const auto& l1PFCand = l1PFCandidates->at(i);
116  pt.push_back(l1PFCand.pt());
117  eta.push_back(l1PFCand.eta());
118  phi.push_back(l1PFCand.phi());
119  puppiWeight.push_back(l1PFCand.puppiWeight());
120  pdgId.push_back(l1PFCand.pdgId());
121  charge.push_back(l1PFCand.charge());
122  }
123 
125 
126  if (useMlModel_) {
127  CalcMlMet(pt, eta, phi, puppiWeight, pdgId, charge, metVector);
128  } else {
129  CalcMetHLS(pt, phi, metVector);
130  }
131 
132  l1t::EtSum theMET(metVector, l1t::EtSum::EtSumType::kTotalHt, 0, 0, 0, 0);
133 
134  auto metCollection = std::make_unique<std::vector<l1t::EtSum>>(0);
135  metCollection->push_back(theMET);
137 }
138 
140  switch (abs(pdgId)) {
141  case 211: // charged hadron (pion)
142  return 1;
143  case 130: // neutral hadron (kaon)
144  return 2;
145  case 22: // photon
146  return 3;
147  case 13: // muon
148  return 4;
149  case 11: // electron
150  return 5;
151  default:
152  return 0;
153  }
154 }
155 
156 void L1MetPfProducer::CalcMlMet(const std::vector<float>& pt,
157  const std::vector<float>& eta,
158  const std::vector<float>& phi,
159  const std::vector<float>& puppiWeight,
160  const std::vector<int>& pdgId,
161  const std::vector<int>& charge,
162  reco::Candidate::PolarLorentzVector& metVector) const {
163  const int inputSize = maxCands_ * numInputs_;
164 
165  input_t input[800];
166  result_t result[2];
167 
168  // initialize with zeros (for padding)
169  for (int i = 0; i < inputSize; i++) {
170  input[i] = 0;
171  }
172 
173  for (uint i = 0; i < pt.size(); i++) {
174  // input_cont
175  input[i * numContInputs_] = pt[i];
176  input[i * numContInputs_ + 1] = eta[i];
177  input[i * numContInputs_ + 2] = phi[i];
178  input[i * numContInputs_ + 3] = puppiWeight[i];
179  // input_pxpy
181  input[(maxCands_ * numContInputs_) + (i * numPxPyInputs_) + 1] = pt[i] * sin(phi[i]);
182  // input_cat0
184  // input_cat1
185  input[maxCands_ * (numContInputs_ + numPxPyInputs_ + 1) + i] = (abs(charge[i]) <= 1) ? (charge[i] + 2) : 0;
186  }
187 
188  model->prepare_input(input);
189  model->predict();
190  model->read_result(result);
191 
192  double met_px = -result[0].to_double();
193  double met_py = -result[1].to_double();
194  metVector.SetPt(hypot(met_px, met_py));
195  metVector.SetPhi(atan2(met_py, met_px));
196  metVector.SetEta(0);
197 }
198 
199 void L1MetPfProducer::CalcMetHLS(const std::vector<float>& pt,
200  const std::vector<float>& phi,
201  reco::Candidate::PolarLorentzVector& metVector) const {
202  pxy_t hw_px = 0;
203  pxy_t hw_py = 0;
204  pxy_t hw_sumx = 0;
205  pxy_t hw_sumy = 0;
206 
207  for (uint i = 0; i < pt.size(); i++) {
208  pt_t hw_pt = min(pt[i], maxPt_);
209  phi_t hw_phi = float(TVector2::Phi_mpi_pi(phi[i]) / phiLSB_);
210 
211  Project(hw_pt, hw_phi, hw_px, true);
212  Project(hw_pt, hw_phi, hw_py, false);
213 
214  hw_sumx = hw_sumx - hw_px;
215  hw_sumy = hw_sumy - hw_py;
216  }
217 
218  pt2_t hw_met = pt2_t(hw_sumx) * pt2_t(hw_sumx) + pt2_t(hw_sumy) * pt2_t(hw_sumy);
219  hw_met = sqrt(int(hw_met)); // stand-in for HLS::sqrt
220 
221  phi_t hw_met_phi = 0;
222  PhiFromXY(hw_sumx, hw_sumy, hw_met_phi);
223 
224  metVector.SetPt(hw_met.to_double());
225  metVector.SetPhi(hw_met_phi.to_double() * phiLSB_);
226  metVector.SetEta(0);
227 }
228 
229 void L1MetPfProducer::Project(pt_t pt, phi_t phi, pxy_t& pxy, bool isX, bool debug) const {
230  /*
231  Convert pt and phi to px (py)
232  1) Map phi to the first quadrant to reduce LUT size
233  2) Lookup sin(phiQ1), where the result is in [0,maxPt]
234  which is used to encode [0,1].
235  3) Multiply pt by sin(phiQ1) to get px. Result will be px*maxPt, but
236  wrapping multiplication is 'mod maxPt' so the correct value is returned.
237  4) Check px=-|px|.
238  */
239 
240  // set phi to first quadrant
241  phi_t phiQ1 = (phi > 0) ? phi : phi_t(-phi); // Q1/Q4
242  if (phiQ1 >= hwPiOverTwo_)
243  phiQ1 = hwPi_ - phiQ1;
244 
245  if (phiQ1 > hwPiOverTwo_) {
246  edm::LogWarning("L1MetPfProducer") << "unexpected phi (high)";
247  phiQ1 = hwPiOverTwo_;
248  } else if (phiQ1 < 0) {
249  edm::LogWarning("L1MetPfProducer") << "unexpected phi (low)";
250  phiQ1 = 0;
251  }
252  if (isX) {
253  typedef ap_ufixed<14, 12, AP_RND, AP_WRAP> pt_t; // LSB is 0.25 and max is 4 TeV
254  ap_ufixed<pt_t::width, 0> cosPhi = cos(phiQ1.to_double() / hwPiOverTwo_.to_double() * M_PI / 2);
255  pxy = pt * cosPhi;
256  if (phi > hwPiOverTwo_ || phi < -hwPiOverTwo_)
257  pxy = -pxy;
258  } else {
259  ap_ufixed<pt_t::width, 0> sinPhi = sin(phiQ1.to_double() / hwPiOverTwo_.to_double() * M_PI / 2);
260  pxy = pt * sinPhi;
261  if (phi < 0)
262  pxy = -pxy;
263  }
264 }
265 
267  if (px == 0 && py == 0) {
268  phi = 0;
269  return;
270  }
271  if (px == 0) {
272  phi = py > 0 ? hwPiOverTwo_ : phi_t(-hwPiOverTwo_);
273  return;
274  }
275  if (py == 0) {
276  phi = px > 0 ? phi_t(0) : phi_t(-hwPi_);
277  return;
278  }
279 
280  // get q1 coordinates
281  pt_t x = px > 0 ? pt_t(px) : pt_t(-px); //px>=0 ? px : -px;
282  pt_t y = py > 0 ? pt_t(py) : pt_t(-py); //px>=0 ? px : -px;
283  // transform so a<b
284  pt_t a = x < y ? x : y;
285  pt_t b = x < y ? y : x;
286 
287  if (b.to_double() > maxPt_ / dropFactor_)
288  b = maxPt_ / dropFactor_;
289  // map [0,max/4) to inv table size
290  int index = round((b.to_double() / (maxPt_ / dropFactor_)) * invTableSize_);
291  float bcheck = (float(index) / invTableSize_) * (maxPt_ / dropFactor_);
292  inv_t inv_b = 1. / ((float(index) / invTableSize_) * (maxPt_ / dropFactor_));
293 
294  inv_t a_over_b = a * inv_b;
295 
296  if (debug) {
297  printf(" a, b = %f, %f; index, inv = %d, %f; ratio = %f \n",
298  a.to_double(),
299  b.to_double(),
300  index,
301  inv_b.to_double(),
302  a_over_b.to_double());
303  printf(" bcheck, 1/bc = %f, %f -- %d %f %d \n", bcheck, 1. / bcheck, invTableSize_, maxPt_, dropFactor_);
304  }
305 
306  int atanTableBits_ = 7;
307  int atanTableSize_ = (1 << atanTableBits_);
308  index = round(a_over_b.to_double() * atanTableSize_);
309  phi = atan(float(index) / atanTableSize_) / phiLSB_;
310 
311  if (debug) {
312  printf(" atan index, phi = %d, %f (%f rad) real atan(a/b)= %f \n",
313  index,
314  phi.to_double(),
315  phi.to_double() * (M_PI / hwPi_.to_double()),
316  atan(a.to_double() / b.to_double()));
317  }
318 
319  // rotate from (0,pi/4) to full quad1
320  if (y > x)
321  phi = hwPiOverTwo_ - phi; //phi = pi/2 - phi
322  // other quadrants
323  if (px < 0 && py > 0)
324  phi = hwPi_ - phi; // Q2 phi = pi - phi
325  if (px > 0 && py < 0)
326  phi = -phi; // Q4 phi = -phi
327  if (px < 0 && py < 0)
328  phi = -(hwPi_ - phi); // Q3 composition of both
329 
330  if (debug) {
331  printf(" phi hw, float, real = %f, %f (%f rad from x,y = %f, %f) \n",
332  phi.to_double(),
333  phi.to_double() * (M_PI / hwPi_.to_double()),
334  atan2(py.to_double(), px.to_double()),
335  px.to_double(),
336  py.to_double());
337  }
338 }
339 
341 
~L1MetPfProducer() override
std::shared_ptr< hls4mlEmulator::Model > model
MPlex< T, D1, D2, N > hypot(const MPlex< T, D1, D2, N > &a, const MPlex< T, D1, D2, N > &b)
Definition: Matriplex.h:616
static constexpr float phiLSB_
static constexpr int numPxPyInputs_
Sin< T >::type sin(const T &t)
Definition: Sin.h:22
static constexpr float maxPt_
delete x;
Definition: CaloConfig.h:22
void Project(pt_t pt, phi_t phi, pxy_t &pxy, bool isX, bool debug=false) const
ap_ufixed< 14, 12, AP_RND, AP_WRAP > pt_t
static constexpr int dropFactor_
ap_fixed< 2 *pt_t::width, 2 *pt_t::iwidth, AP_RND, AP_SAT > pt2_t
void CalcMetHLS(const std::vector< float > &pt, const std::vector< float > &phi, reco::Candidate::PolarLorentzVector &metVector) const
static std::string const input
Definition: EdmProvDump.cc:50
ap_fixed< 32, 16 > input_t
int iEvent
Definition: GenABIO.cc:224
void PhiFromXY(pxy_t px, pxy_t py, phi_t &phi, bool debug=false) const
const phi_t hwPi_
T sqrt(T t)
Definition: SSEVec.h:23
const phi_t hwPiOverTwo_
Cos< T >::type cos(const T &t)
Definition: Cos.h:22
std::string modelVersion_
static constexpr int invTableSize_
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
int EncodePdgId(int pdgId) const
#define DEFINE_FWK_MODULE(type)
Definition: MakerMacros.h:16
void produce(edm::StreamID, edm::Event &iEvent, const edm::EventSetup &iSetup) const override
ap_int< 12 > phi_t
static constexpr int numInputs_
#define M_PI
static void fillDescriptions(edm::ConfigurationDescriptions &descriptions)
ap_ufixed< pt_t::width, 0 > inv_t
#define debug
Definition: HDRShower.cc:19
static void fillDescriptions(edm::ConfigurationDescriptions &descriptions)
void CalcMlMet(const std::vector< float > &pt, const std::vector< float > &eta, const std::vector< float > &phi, const std::vector< float > &puppiWeight, const std::vector< int > &pdgId, const std::vector< int > &charge, reco::Candidate::PolarLorentzVector &metVector) const
double b
Definition: hdecay.h:120
void add(std::string const &label, ParameterSetDescription const &psetDescription)
HLT enums.
double a
Definition: hdecay.h:121
edm::EDGetTokenT< vector< l1t::PFCandidate > > _l1PFToken
Log< level::Warning, false > LogWarning
L1MetPfProducer(const edm::ParameterSet &)
ap_fixed< pt_t::width+1, pt_t::iwidth+1, AP_RND, AP_SAT > pxy_t
ap_fixed< 32, 16 > result_t
def move(src, dest)
Definition: eostools.py:511
static constexpr int numContInputs_
MPlex< T, D1, D2, N > atan2(const MPlex< T, D1, D2, N > &y, const MPlex< T, D1, D2, N > &x)
Definition: Matriplex.h:648
math::PtEtaPhiMLorentzVector PolarLorentzVector
Lorentz vector.
Definition: Candidate.h:38