BlockD.cc
1 /*
2  * MoMEMta: a modular implementation of the Matrix Element Method
3  * Copyright (C) 2016 Universite catholique de Louvain (UCL), Belgium
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 
20 #include <momemta/ParameterSet.h>
21 #include <momemta/Module.h>
22 #include <momemta/Solution.h>
23 #include <momemta/Types.h>
24 #include <momemta/Math.h>
25 
87 class BlockD: public Module {
88  public:
89 
90  BlockD(PoolPtr pool, const ParameterSet& parameters): Module(pool, parameters.getModuleName()) {
91  sqrt_s = parameters.globalParameters().get<double>("energy");
92  pT_is_met = parameters.get<bool>("pT_is_met", false);
93 
94  m1 = parameters.get<double>("m1", 0.);
95  m2 = parameters.get<double>("m2", 0.);
96 
97  s13 = get<double>(parameters.get<InputTag>("s13"));
98  s134 = get<double>(parameters.get<InputTag>("s134"));
99  s25 = get<double>(parameters.get<InputTag>("s25"));
100  s256 = get<double>(parameters.get<InputTag>("s256"));
101 
102  m_particles.push_back(get<LorentzVector>(parameters.get<InputTag>("p3")));
103  m_particles.push_back(get<LorentzVector>(parameters.get<InputTag>("p4")));
104  m_particles.push_back(get<LorentzVector>(parameters.get<InputTag>("p5")));
105  m_particles.push_back(get<LorentzVector>(parameters.get<InputTag>("p6")));
106 
107  if (parameters.exists("branches")) {
108  auto branches_tags = parameters.get<std::vector<InputTag>>("branches");
109  for (auto& t: branches_tags)
110  m_branches.push_back(get<LorentzVector>(t));
111  }
112 
113  // If the met input is specified, get it, otherwise retrieve default
114  // one ("met::p4")
115  InputTag met_tag;
116  if (parameters.exists("met")) {
117  met_tag = parameters.get<InputTag>("met");
118  } else {
119  met_tag = InputTag({"met", "p4"});
120  }
121 
122  m_met = get<LorentzVector>(met_tag);
123  };
124 
125  virtual Status work() override {
126 
127  solutions->clear();
128 
129  const LorentzVector& p3 = *m_particles[0];
130  const LorentzVector& p4 = *m_particles[1];
131  const LorentzVector& p5 = *m_particles[2];
132  const LorentzVector& p6 = *m_particles[3];
133 
134  const double p11 = SQ(m1);
135  const double p22 = SQ(m2);
136  const double p33 = p3.M2();
137  const double p44 = p4.M2();
138  const double p55 = p5.M2();
139  const double p66 = p6.M2();
140 
141  // Don't spend time on unphysical corner of the phase-space
142  if (*s13 + p44 >= *s134 || *s25 + p66 >= *s256 || *s134 + *s256 >= SQ(sqrt_s) || *s13 <= p11 + p33 || *s25 <= p22 + p55)
143  return Status::NEXT;
144 
145  // pT will be used to fix the transverse momentum of the reconstructed neutrinos
146  // We can either enforce momentum conservation by disregarding the MET, ie:
147  // pT = sum of all the visible particles,
148  // Or we can fix it using the MET given as input:
149  // pT = -MET
150  // In the latter case, it is the user's job to ensure momentum conservation at
151  // the matrix element level (by using the Boost module, for instance).
152 
153  LorentzVector pT;
154  if (pT_is_met) {
155  pT = - *m_met;
156  } else {
157  pT = p3 + p4 + p5 + p6;
158  for (size_t i = 0; i < m_branches.size(); i++) {
159  pT += *m_branches[i];
160  }
161  }
162 
163  const double p34 = p3.Dot(p4);
164  const double p56 = p5.Dot(p6);
165 
166  // A1 p1x + B1 p1y + C1 = 0, with C1(E1,E2)
167  // A2 p1y + B2 p2y + C2 = 0, with C2(E1,E2)
168  // ==> express p1x and p1y as functions of E1, E2
169 
170  const double A1 = 2.*( -p3.Px() + p3.Pz()*p4.Px()/p4.Pz() );
171  const double A2 = 2.*( p5.Px() - p5.Pz()*p6.Px()/p6.Pz() );
172 
173  const double B1 = 2.*( -p3.Py() + p3.Pz()*p4.Py()/p4.Pz() );
174  const double B2 = 2.*( p5.Py() - p5.Pz()*p6.Py()/p6.Pz() );
175 
176  const double Dx = B2*A1 - B1*A2;
177  const double Dy = A2*B1 - A1*B2;
178 
179  const double X = 2*( pT.Px()*p5.Px() + pT.Py()*p5.Py() - p5.Pz()/p6.Pz()*( 0.5*(*s25 - *s256 + p66) + p56 + pT.Px()*p6.Px() + pT.Py()*p6.Py() ) ) + p55 + p22 - *s25;
180  const double Y = p3.Pz()/p4.Pz()*( *s13 - *s134 + 2*p34 + p44 ) - p33 - p11 + *s13;
181 
182  // p1x = alpha1 E1 + beta1 E2 + gamma1
183  // p1y = ...(2)
184  // p1z = ...(3)
185  // p2z = ...(4)
186  // p2x = ...(5)
187  // p2y = ...(6)
188 
189  const double alpha1 = -2*B2*(p3.E() - p4.E()*p3.Pz()/p4.Pz())/Dx;
190  const double beta1 = 2*B1*(p5.E() - p6.E()*p5.Pz()/p6.Pz())/Dx;
191  const double gamma1 = B1*X/Dx + B2*Y/Dx;
192 
193  const double alpha2 = -2*A2*(p3.E() - p4.E()*p3.Pz()/p4.Pz())/Dy;
194  const double beta2 = 2*A1*(p5.E() - p6.E()*p5.Pz()/p6.Pz())/Dy;
195  const double gamma2 = A1*X/Dy + A2*Y/Dy;
196 
197  const double alpha3 = (p4.E() - alpha1*p4.Px() - alpha2*p4.Py())/p4.Pz();
198  const double beta3 = -(beta1*p4.Px() + beta2*p4.Py())/p4.Pz();
199  const double gamma3 = ( 0.5*(*s13 - *s134 + p44) + p34 - gamma1*p4.Px() - gamma2*p4.Py() )/p4.Pz();
200 
201  const double alpha4 = (alpha1*p6.Px() + alpha2*p6.Py())/p6.Pz();
202  const double beta4 = (p6.E() + beta1*p6.Px() + beta2*p6.Py())/p6.Pz();
203  const double gamma4 = ( 0.5*(*s25 - *s256 + p66) + p56 + (gamma1 + pT.Px())*p6.Px() + (gamma2 + pT.Py())*p6.Py() )/p6.Pz();
204 
205  const double alpha5 = -alpha1;
206  const double beta5 = -beta1;
207  const double gamma5 = -pT.Px() - gamma1;
208 
209  const double alpha6 = -alpha2;
210  const double beta6 = -beta2;
211  const double gamma6 = -pT.Py() - gamma2;
212 
213  // a11 E1^2 + a22 E2^2 + a12 E1E2 + a10 E1 + a01 E2 + a00 = 0
214  // id. with bij
215 
216  const double a11 = -1 + ( SQ(alpha1) + SQ(alpha2) + SQ(alpha3) );
217  const double a22 = SQ(beta1) + SQ(beta2) + SQ(beta3);
218  const double a12 = 2.*( alpha1*beta1 + alpha2*beta2 + alpha3*beta3 );
219  const double a10 = 2.*( alpha1*gamma1 + alpha2*gamma2 + alpha3*gamma3 );
220  const double a01 = 2.*( beta1*gamma1 + beta2*gamma2 + beta3*gamma3 );
221  const double a00 = SQ(gamma1) + SQ(gamma2) + SQ(gamma3) + p11;
222 
223  const double b11 = SQ(alpha5) + SQ(alpha6) + SQ(alpha4);
224  const double b22 = -1 + ( SQ(beta5) + SQ(beta6) + SQ(beta4) );
225  const double b12 = 2.*( alpha5*beta5 + alpha6*beta6 + alpha4*beta4 );
226  const double b10 = 2.*( alpha5*gamma5 + alpha6*gamma6 + alpha4*gamma4 );
227  const double b01 = 2.*( beta5*gamma5 + beta6*gamma6 + beta4*gamma4 );
228  const double b00 = SQ(gamma5) + SQ(gamma6) + SQ(gamma4) + p22;
229 
230  // Find the intersection of the 2 conics (at most 4 real solutions for (E1,E2))
231  std::vector<double> E1, E2;
232  solve2Quads(a11, a22, a12, a10, a01, a00, b11, b22, b12, b10, b01, b00, E1, E2, false);
233 
234  // For each solution (E1,E2), find the neutrino 4-momenta p1,p2
235 
236  if (E1.size() == 0)
237  return Status::NEXT;
238 
239  for(unsigned int i = 0; i < E1.size(); i++){
240  const double e1 = E1.at(i);
241  const double e2 = E2.at(i);
242 
243  if (e1 <= 0 || e2 <= 0)
244  continue;
245 
246  LorentzVector p1(
247  alpha1*e1 + beta1*e2 + gamma1,
248  alpha2*e1 + beta2*e2 + gamma2,
249  alpha3*e1 + beta3*e2 + gamma3,
250  e1);
251 
252  LorentzVector p2(
253  alpha5*e1 + beta5*e2 + gamma5,
254  alpha6*e1 + beta6*e2 + gamma6,
255  alpha4*e1 + beta4*e2 + gamma4,
256  e2);
257 
258  // Check if solutions are physical
259  LorentzVector tot = p1 + p2 + p3 + p4 + p5 + p6;
260  for (size_t i = 0; i < m_branches.size(); i++) {
261  tot += *m_branches[i];
262  }
263  double q1Pz = std::abs(tot.Pz() + tot.E()) / 2.;
264  double q2Pz = std::abs(tot.Pz() - tot.E()) / 2.;
265  if (q1Pz > sqrt_s / 2 || q2Pz > sqrt_s / 2)
266  continue;
267 
268  if (!ApproxComparison((p1 + p2 + pT).Pt(), 0.)) {
269 #ifndef NDEBUG
270  LOG(trace) << "[BlockD] Throwing solution because neutrino balance is incorrect. "
271  << "Expected " << pT.Pt() << ", got " <<(p1 + p2).Pt();
272 #endif
273  continue;
274  }
275 
276  if (!ApproxComparison(p1.M() / p1.E(), m1 / p1.E())) {
277 #ifndef NDEBUG
278  LOG(trace) << "[BlockD] Throwing solution because p1 has an invalid mass. " <<
279  "Expected " << m1 << ", got " << p1.M();
280 #endif
281  continue;
282  }
283 
284  if (!ApproxComparison(p2.M() / p2.E(), m2 / p2.E())) {
285 #ifndef NDEBUG
286  LOG(trace) << "[BlockD] Throwing solution because p2 has an invalid mass. " <<
287  "Expected " << m2 << ", got " << p2.M();
288 #endif
289  continue;
290  }
291 
292  if (!ApproxComparison((p1 + p3).M2(), *s13)) {
293 #ifndef NDEBUG
294  LOG(trace) << "[BlockD] Throwing solution because of invalid invariant mass. " <<
295  "Expected " << *s13 << ", got " << (p1 + p3).M2();
296 #endif
297  continue;
298  }
299 
300  if (!ApproxComparison((p1 + p3 + p4).M2(), *s134)) {
301 #ifndef NDEBUG
302  LOG(trace) << "[BlockD] Throwing solution because of invalid invariant mass. " <<
303  "Expected " << *s134 << ", got " << (p1 + p3 + p4).M2();
304 #endif
305  continue;
306  }
307 
308  if (!ApproxComparison((p2 + p5).M2(), *s25)) {
309 #ifndef NDEBUG
310  LOG(trace) << "[BlockD] Throwing solution because of invalid invariant mass. " <<
311  "Expected " << *s25 << ", got " << (p2 + p5).M2();
312 #endif
313  continue;
314  }
315 
316  if (!ApproxComparison((p2 + p5 + p6).M2(), *s256)) {
317 #ifndef NDEBUG
318  LOG(trace) << "[BlockD] Throwing solution because of invalid invariant mass. " <<
319  "Expected " << *s256 << ", got " << (p2 + p5 + p6).M2();
320 #endif
321  continue;
322  }
323 
324 
325  double jacobian = computeJacobian(p1, p2, p3, p4, p5, p6);
326  Solution s { {p1, p2}, jacobian, true };
327  solutions->push_back(s);
328  }
329 
330  return solutions->size() > 0 ? Status::OK : Status::NEXT;
331  }
332 
333  double computeJacobian(const LorentzVector& p1, const LorentzVector& p2, const LorentzVector& p3, const LorentzVector& p4, const LorentzVector& p5, const LorentzVector& p6) {
334 
335  const double E1 = p1.E();
336  const double p1x = p1.Px();
337  const double p1y = p1.Py();
338  const double p1z = p1.Pz();
339 
340  const double E2 = p2.E();
341  const double p2x = p2.Px();
342  const double p2y = p2.Py();
343  const double p2z = p2.Pz();
344 
345  const double E3 = p3.E();
346  const double p3x = p3.Px();
347  const double p3y = p3.Py();
348  const double p3z = p3.Pz();
349 
350  const double E4 = p4.E();
351  const double p4x = p4.Px();
352  const double p4y = p4.Py();
353  const double p4z = p4.Pz();
354 
355  const double E5 = p5.E();
356  const double p5x = p5.Px();
357  const double p5y = p5.Py();
358  const double p5z = p5.Pz();
359 
360  const double E6 = p6.E();
361  const double p6x = p6.Px();
362  const double p6y = p6.Py();
363  const double p6z = p6.Pz();
364 
365  const double E34 = E3 + E4;
366  const double p34x = p3x + p4x;
367  const double p34y = p3y + p4y;
368  const double p34z = p3z + p4z;
369 
370  const double E56 = E5 + E6;
371  const double p56x = p5x + p6x;
372  const double p56y = p5y + p6y;
373  const double p56z = p5z + p6z;
374 
375  // copied from Source/MadWeight/blocks/class_d.f
376 
377  double inv_jac = E3*(E5*
378  (p34z*(p1y*p2z*p56x - p1x*p2z*p56y - p1y*p2x*p56z +
379  p1x*p2y*p56z) +
380  p1z*(-(p2z*p34y*p56x) + p2z*p34x*p56y -
381  p2y*p34x*p56z + p2x*p34y*p56z)) +
382  (E56*p2z - E2*p56z)*
383  (p1z*p34y*p5x - p1y*p34z*p5x - p1z*p34x*p5y +
384  p1x*p34z*p5y) +
385  (E56*(p1z*p2y*p34x - p1z*p2x*p34y + p1y*p2x*p34z -
386  p1x*p2y*p34z) +
387  E2*(p1z*p34y*p56x - p1y*p34z*p56x - p1z*p34x*p56y +
388  p1x*p34z*p56y))*p5z) +
389  E34*(E5*p2z*(p1z*p3y*p56x - p1y*p3z*p56x - p1z*p3x*p56y +
390  p1x*p3z*p56y) +
391  E5*(p1z*p2y*p3x - p1z*p2x*p3y + p1y*p2x*p3z -
392  p1x*p2y*p3z)*p56z -
393  (E56*p2z - E2*p56z)*
394  (p1z*p3y*p5x - p1y*p3z*p5x - p1z*p3x*p5y + p1x*p3z*p5y)
395  - (E56*(p1z*p2y*p3x - p1z*p2x*p3y + p1y*p2x*p3z -
396  p1x*p2y*p3z) +
397  E2*(p1z*p3y*p56x - p1y*p3z*p56x - p1z*p3x*p56y +
398  p1x*p3z*p56y))*p5z) +
399  E1*(E5*(p2z*(-(p34z*p3y*p56x) + p34y*p3z*p56x +
400  p34z*p3x*p56y - p34x*p3z*p56y) +
401  (-(p2y*p34z*p3x) + p2x*p34z*p3y + p2y*p34x*p3z -
402  p2x*p34y*p3z)*p56z) +
403  (E56*p2z - E2*p56z)*
404  (p34z*p3y*p5x - p34y*p3z*p5x - p34z*p3x*p5y +
405  p34x*p3z*p5y) +
406  (E56*(p2y*p34z*p3x - p2x*p34z*p3y - p2y*p34x*p3z +
407  p2x*p34y*p3z) +
408  E2*(p34z*p3y*p56x - p34y*p3z*p56x - p34z*p3x*p56y +
409  p34x*p3z*p56y))*p5z);
410 
411  inv_jac *= 8.*16.*SQ(M_PI*sqrt_s);
412 
413  return 1. / std::abs(inv_jac);
414  }
415 
416  private:
417  double sqrt_s;
418  bool pT_is_met;
419 
420  double m1;
421  double m2;
422 
423  // Inputs
424  Value<double> s13;
425  Value<double> s134;
426  Value<double> s25;
427  Value<double> s256;
428  std::vector<Value<LorentzVector>> m_particles;
429  std::vector<Value<LorentzVector>> m_branches;
430  Value<LorentzVector> m_met;
431 
432  // Outputs
433  std::shared_ptr<SolutionCollection> solutions = produce<SolutionCollection>("solutions");
434 };
435 
436 REGISTER_MODULE(BlockD)
437  .Input("s13")
438  .Input("s134")
439  .Input("s25")
440  .Input("s256")
441  .Input("p3")
442  .Input("p4")
443  .Input("p5")
444  .Input("p6")
445  .OptionalInputs("branches")
446  .Input("met=met::p4")
447  .Output("solutions")
448  .GlobalAttr("energy:double")
449  .Attr("pT_is_met:bool=false")
450  .Attr("m1:double=0.")
451  .Attr("m2:double=0.");
bool ApproxComparison(double value, double expected)
Compare two doubles and return true if they are approximatively equal.
Definition: Math.cc:409
Generic solution structure representing a set of particles, along with its jacobian.
Definition: Solution.h:28
Mathematical functions.
An identifier of a module&#39;s output.
Definition: InputTag_fwd.h:37
Parent class for all the modules.
Definition: Module.h:37
A class encapsulating a lua table.
Definition: ParameterSet.h:82
#define SQ(x)
Compute .
Definition: Math.h:25
Module(PoolPtr pool, const std::string &name)
Constructor.
Definition: Module.h:61
virtual Status work() override
Main function.
Definition: BlockD.cc:125
Final (main) Block D, describing
Definition: BlockD.cc:87
bool solve2Quads(const double a20, const double a02, const double a11, const double a10, const double a01, const double a00, const double b20, const double b02, const double b11, const double b10, const double b01, const double b00, std::vector< double > &E1, std::vector< double > &E2, bool verbose=false)
Solve a system of two quadratic equations.
Definition: Math.cc:192