HackAnalysis  2
lester_mt2_bisect.h
1 #define ENABLE_INLINING
2 /*
3  * Copyright 2014, Christopher Lester, University of Cambridge
4  *
5  * version 7: arXiv:1411.4312v7
6  * * Allowed users who define ENABLE_INLINING to put the inlines back
7  * that were removed in version 5.
8  * * Allowed users who define DISABLE_COPYRIGHT_PRINTING to disable
9  * the copyright message.
10  * This feature is to allow people who wrap the calculator in
11  * another library to use other ways of communicating the copyright
12  * information to end users that are more appropriate in their
13  * system. It is expected that developers doing so will ensure
14  * that *somehow* a message pointing users back to make citations
15  * to the original MT2 papers will be presented at some stage to
16  * each end user.
17  * * Both of the above changes are made to support
18  * https://pypi.org/project/mt2/
19  * * Minor changes to spelling/typos in comments and whitespace formatting.
20  *
21  * version 6: arXiv:1411.4312v6
22  * * identical to v5
23  *
24  * version 5: arXiv:1411.4312v5
25  * * made more portable by removal of use of __FILE__ and __LINE__ macros in debug statement
26  * * made fewer demands on poor C++ compilers (ROOT5/CINT) by removal of certain inline statements
27  * * added this changelog!
28  *
29  * version 4: arXiv:1411.4312v4
30  * * added copyright information
31  *
32  * version 3: arXiv:1411.4312v3
33  * * added option to turn on/off deci-sectioning
34  * * made code slightly slower for readability gain
35  *
36  * version 2: arXiv:1411.4312v2
37  * * no changes w.r.t. version 1
38  *
39  * version 1: arXiv:1411.4312v1
40  * * initial public release
41  *
42  * This file will let you calculate MT2 or Asymmetric MT2 relatively easily.
43  * An example showing how to do so, may be found below this copyright message.
44  *
45  * (Note that this is a low-level library. Various wrappers exist around
46  * it to allow easier interfacing to ROOT or ATLAS code.)
47  *
48  * If you use this implementation, please cite:
49  *
50  * http://arxiv.org/abs/1411.4312
51  *
52  * as the paper documenting this particular implementation.
53  *
54  * You might also need to cite:
55  *
56  * http://arxiv.org/abs/hep-ph/9906349
57  * Journal reference: Phys.Lett.B463:99-103,1999
58  * DOI: 10.1016/S0370-2693(99)00945-4
59  *
60  * as the paper defining MT2.
61  *
62  * Here is an example of it's use:
63 
64 
65 double mVisA = 10; // mass of visible object on side A. Must be >=0.
66 double pxA = 20; // x momentum of visible object on side A.
67 double pyA = 30; // y momentum of visible object on side A.
68 
69 double mVisB = 10; // mass of visible object on side B. Must be >=0.
70 double pxB = -20; // x momentum of visible object on side B.
71 double pyB = -30; // y momentum of visible object on side B.
72 
73 double pxMiss = -5; // x component of missing transverse momentum.
74 double pyMiss = -5; // y component of missing transverse momentum.
75 
76 double chiA = 4; // hypothesised mass of invisible on side A. Must be >=0.
77 double chiB = 7; // hypothesised mass of invisible on side B. Must be >=0.
78 
79 double desiredPrecisionOnMt2 = 0; // Must be >=0. If 0 alg aims for machine precision.
80  // If >0, MT2 computed to supplied absolute precision.
81 
82 // asymm_mt2_lester_bisect::disableCopyrightMessage();
83 
84 double MT2 = asymm_mt2_lester_bisect::get_mT2(
85  mVisA, pxA, pyA,
86  mVisB, pxB, pyB,
87  pxMiss, pyMiss,
88  chiA, chiB,
89  desiredPrecisionOnMt2);
90 
91  */
92 
93 
94 #ifndef LESTER_TESTWHETHERELLIPSESAREDISJOINT_H
95 #define LESTER_TESTWHETHERELLIPSESAREDISJOINT_H
96 
97 #include <cmath> // for fabs( ... )
98 
99 /*
100  * The
101  *
102  * bool ellipsesAreDisjoint(const EllipseParams & e1, const EllipseParams & e2);
103  *
104  * function determines whether two ellipses (not both singular) are disjoint.
105  * Ellipses are assumed to be solid objects with a filled interior.
106  * They are disjoint it no part of their interiors overlap.
107  * Singular (in this context) is defined below.
108  *
109  * It uses the method of:
110 
111 Computer Aided Geometric Design 23 (2006) 350324
112 A new approach to characterizing the relative position of two ellipses depending on one parameter
113 Fernando Etayo 1,3, Laureano ,2,3, Natalia del Rio 3Vega
114 Departamento de Matematicas, Estadistica y Computacion, Universidad de Cantabria, Spain
115 Received 15 September 2004; received in revised form 2 November 2005; accepted 10 January 2006 Available online 28 February 2006
116 
117 pointed out to me by Gary B. Huges and Mohcine Chraibi authors of
118 
119  Comput Visual Sci (2012) 15:301 DOI 10.1007/s00791-013-0214-3291
120  Calculating ellipse overlap areas Gary B. Mohcine ChraibiHughes
121 
122  * Note:
123  *
124  * Though the paper above talks only about ellipses, from playing with some test cases, I (CGL) have conjectured that the algorithm actually works well even if the conics are parabolas provided that the axx>0&&ayy>0 test is reduced to axx>=0&&ayy>=0&&axx*ayy!=0 ... which is true is good news for the similicity of the MT2 calculator ... as the MT2 calculator will not need to distinguish these two possibilities. In a private communication between me (CGL) and the authors of Computer Aided Geometric Design 23 (2006) 350, the authors have indicated that it is not unreasonable to believe that the code does indeed work on the parabolica cases too. This algorithm relies on that generalisation, which may be the subject of a paper (to appear) from Etayo and Gonzalez-Vega.324
125  *
126  *
127  * Definition: an ellipse is defined with respect to cartesian co-ordinates (x,y) by an equation of the form;
128  *
129  * xVec^T A xVec = 0 (1)
130  *
131  * where xVec is a columnar three vec containing (x,y,1) and where A is a symmetric matrix having elements:
132  *
133  * [ axx axy ax ]
134  * A = [ axy ayy ay ]
135  * [ ax ay a ].
136  *
137  * Therefore the ellipse equation would look like:
138  *
139  * axx x^2 + 2 axy x y + ayy y^2 + 2 ax x + 2 ay y + a = 0.
140  *
141  * Note that this parametrisation has one parameter too many ... the "A"-matrix can be multiplied by a non-zero constant, and the ellipse is not changed.
142  * Etayo et al's implementation REQUIRES that axx and ayy be strictly positive.
143  * The implementation herein doesn't quite enforce that. The implementation herein allows axx or ayy to be non-negative .... and it is left to the user to ensure that axx and ayy are not exactly zero.
144  * Note also that (1) is general enough to contain all conic sections, so it is left to the user to ensure that only values of A consistent
145  * with (non-singular) ellipses are fed into the program below. For our purposes, an ellipse is "singular" iff coeffLamPow3 (see below) is zero.
146  */
147 
148 namespace Lester {
149 
150 
151 
152 
154 
155 
156  // Constructor for non-degenerate ellipses:
157  /*
158  * Ellipse is represented algebraically by:
159  * c_xx x^2 + 2 c_xy x y + c_yy y^2 + 2 c_x x + 2 c_y y + c = 0.
160  */
161 #ifdef ENABLE_INLINING
162  inline
163 #endif
165  const double c_xx2,
166  const double c_yy2,
167  const double c_xy2,
168  const double c_x2,
169  const double c_y2,
170  const double c2) :
171  c_xx(c_xx2),
172  c_yy(c_yy2),
173  c_xy(c_xy2),
174  c_x(c_x2),
175  c_y(c_y2),
176  c(c2) {
177  //Etayo et al REQUIRE that c_xx and c_yy are non-negative, so:
178  if (c_xx<0 || c_yy<0) {
179  throw "precondition violation";
180  }
181  setDet();
182  }
183  EllipseParams() {
184  }
185 #ifdef ENABLE_INLINING
186  inline
187 #endif
188  void setDet() {
189  det = (2.0*c_x*c_xy*c_y + c*c_xx*c_yy - c_yy*c_x*c_x - c*c_xy*c_xy - c_xx*c_y*c_y) ;
190  }
191  // Constructor for degenerate ellipse (i.e. a "dot" at (x0,y0) ).
193  const double x0,
194  const double y0) :
195  c_xx(1),
196  c_yy(1),
197  c_xy(0),
198  c_x(-x0),
199  c_y(-y0),
200  c(x0*x0 + y0*y0),
201  det(0) {
202  }
203  inline double lesterFactor(const EllipseParams & e2) const {
204  const EllipseParams & e1 = *this;
205  const double ans = e1.c_xx*e1.c_yy*e2.c + 2.0*e1.c_xy*e1.c_y*e2.c_x - 2.0*e1.c_x*e1.c_yy*e2.c_x + e1.c*e1.c_yy*e2.c_xx - 2.0*e1.c*e1.c_xy*e2.c_xy + 2.0*e1.c_x*e1.c_y*e2.c_xy + 2.0*e1.c_x*e1.c_xy*e2.c_y - 2.0*e1.c_xx*e1.c_y*e2.c_y + e1.c*e1.c_xx*e2.c_yy - e2.c_yy*(e1.c_x*e1.c_x) - e2.c*(e1.c_xy*e1.c_xy) - e2.c_xx*(e1.c_y*e1.c_y);
206  return ans;
207  }
208 #ifdef ENABLE_INLINING
209  inline
210 #endif
211  bool operator==(const EllipseParams & other) const {
212  return
213  c_xx == other.c_xx &&
214  c_yy == other.c_yy &&
215  c_xy == other.c_xy &&
216  c_x == other.c_x &&
217  c_y == other.c_y &&
218  c == other.c;
219  }
220  public:
221  // Data
222  double c_xx;
223  double c_yy;
224  double c_xy; // note factor of 2 above
225  double c_x; // note factor of 2 above
226  double c_y; // note factor of 2 above
227  double c;
228  double det; // The determinant of the 3x3 conic matrix
229 };
230 
231 // This is the interface: users should call this function:
232 //bool ellipsesAreDisjoint(const EllipseParams & e1, const EllipseParams & e2);
233 
234 // This is an implementation thing: users should not call it:
235 inline bool __private_ellipsesAreDisjoint(const double coeffLamPow3, const double coeffLamPow2, const double coeffLamPow1, const double coeffLamPow0);
236 
237 inline bool ellipsesAreDisjoint(const EllipseParams & e1, const EllipseParams & e2) {
238  /* We want to construct the polynomial "Det(lambda A + B)" where A and B are the 3x3 matrices associated with e1 and e2, and we want to get that
239  polynomial in the form lambda^3 + a lambda^2 + b lambda + c.
240 
241 
242  Note that by default we will not have unity as the coefficient of the lambda^3 term, however the redundancy in the parametrisation of A and B allows us to scale the whole ply until the first term does have a unit coefficient.
243  */
244 
245  if (e1==e2) {
246  return false; // Probably won't catch many cases, but may as well have it here.
247  }
248 
249  // first get unscaled terms:
250  const double coeffLamPow3 = e1.det; // Note that this is the determinant of the symmetric matrix associated with e1.
251  const double coeffLamPow2 = e1.lesterFactor(e2);
252  const double coeffLamPow1 = e2.lesterFactor(e1);
253  const double coeffLamPow0 = e2.det; // Note that this is the determinant of the symmetric matrix associated with e2.
254 
255  // Since question is "symmetric" and since we need to divide by coeffLamPow3 ... do this the way round that involves dividing by the largest number:
256 
257  if (fabs(coeffLamPow3) >= fabs(coeffLamPow0)) {
258  return __private_ellipsesAreDisjoint(coeffLamPow3, coeffLamPow2, coeffLamPow1, coeffLamPow0); // normal order
259  } else {
260  return __private_ellipsesAreDisjoint(coeffLamPow0, coeffLamPow1, coeffLamPow2, coeffLamPow3); // reversed order
261  }
262 }
263 inline bool __private_ellipsesAreDisjoint(const double coeffLamPow3, const double coeffLamPow2, const double coeffLamPow1, const double coeffLamPow0) {
264 
265  // precondition of being called:
266  //assert(fabs(coeffLamPow3)>=fabs(coeffLamPow0));
267 
268  if(coeffLamPow3==0) {
269  // The ellipses were singular in some way.
270  // Cannot determine whether they are overlapping or not.
271  throw 1;
272  }
273 
274  // now scale terms to monomial form:
275  const double a = coeffLamPow2 / coeffLamPow3;
276  const double b = coeffLamPow1 / coeffLamPow3;
277  const double c = coeffLamPow0 / coeffLamPow3;
278 
279 #ifdef LESTER_DEEP_FIDDLE
280  {
281  const double thing1 = -3.0*b + a*a;
282  const double thing2 = -27.0*c*c + 18.0*c*a*b + a*a*b*b - 4.0*a*a*a*c - 4.0*b*b*b;
283  std::cout
284  << (thing1>0) << " && " << (thing2>0) << " && [[ " << (a>=0) << " " << (3.0*a*c + b*a*a - 4.0*b*b<0) << " ] or "
285  << "[ " << (a< 0) << " ] =("<< ((a >= 0 /*&& thing1 > 0*/ && 3.0*a*c + b*a*a - 4.0*b*b< 0 /*&& thing2 > 0*/) ||
286  (a < 0 /*&& thing1 > 0*/ /*&& thing2 > 0*/)) << ")] " << (
287  ( (a >= 0 && thing1 > 0 && 3.0*a*c + b*a*a - 4.0*b*b< 0 && thing2 > 0) ||
288  (a < 0 && thing1 > 0 && thing2 > 0))
289 
290  ) << std::endl;
291  }
292 #endif
293 
294  // Use the main result of the above paper:
295  const double thing1 = -3.0*b + a*a;
296  if (thing1<=0) return false;
297  const double thing2 = -27.0*c*c + 18.0*c*a*b + a*a*b*b - 4.0*a*a*a*c - 4.0*b*b*b;
298  if (thing2<=0) return false;
299 
300  // ans true means ellipses are disjoint:
301  const bool ans = ( (a >= 0 /*&& thing1 > 0*/ && 3.0*a*c + b*a*a - 4.0*b*b< 0 /*&& thing2 > 0*/) ||
302  (a < 0 /*&& thing1 > 0*/ /*&& thing2 > 0*/));
303  return ans;
304 
305 }
306 
307 }
308 
309 #endif
310 
311 
312 
313 
314 
315 
316 
317 
318 #ifndef ASYMM_MT2_BISECT_H
319 #define ASYMM_MT2_BISECT_H
320 
321 #include <iostream>
322 #include <iomanip>
323 #include <cmath>
324 #include <cassert>
325 
326 
328  public:
329 
330  static const int MT2_ERROR=-1;
331 
332  static double get_mT2( // returns asymmetric mT2 (which is >=0), or returns a negative number (such as MT2_ERROR) in the case of an error.
333  const double mVis1, const double pxVis1, const double pyVis1,
334  const double mVis2, const double pxVis2, const double pyVis2,
335  const double pxMiss, const double pyMiss,
336  const double mInvis1, const double mInvis2,
337  const double desiredPrecisionOnMT2=0, // This must be non-negative. If set to zero (default) MT2 will be calculated to the highest precision available on the machine (or as close to that as the algorithm permits). If set to a positive value, MT2 (note that is MT2, not its square) will be calculated to within +- desiredPrecisionOnMT2. Note that by requesting precision of +- 0.01 GeV on an MT2 value of 100 GeV can result in speedups of a factor of two or three.
338  const bool useDeciSectionsInitially=true // If true, interval is cut at the 10% point until first acceptance, which gives factor 3 increase in speed calculating kinematic min, but 3% slowdown for events in the bulk. Is on (true) by default, but can be turned off by setting to false.
339  ) {
340 
341  const double mT2_Sq = get_mT2_Sq(
342  mVis1, pxVis1, pyVis1,
343  mVis2, pxVis2, pyVis2,
344  pxMiss,pyMiss,
345  mInvis1, mInvis2,
346  desiredPrecisionOnMT2,
347  useDeciSectionsInitially);
348  if (mT2_Sq==MT2_ERROR) {
349  return MT2_ERROR;
350  }
351  return sqrt(mT2_Sq);
352  }
353 
354  static void disableCopyrightMessage(const bool printIfFirst=false) {
355  static bool first = true;
356  if (first && printIfFirst) {
357  std::cout
358  << "\n\n"
359  << "#=========================================================\n"
360  << "# To disable this message, place a call to \n"
361  << "# \n"
362  << "# asymm_mt2_lester_bisect::disableCopyrightMessage();\n"
363  << "# \n"
364  << "# somewhere before you begin to calculate your MT2 values.\n"
365  << "#=========================================================\n"
366  << "# You are calculating symmetric or asymmetric MT2 using\n"
367  << "# the implementation defined in:\n"
368  << "# \n"
369  << "# http://arxiv.org/abs/1411.4312\n"
370  << "# \n"
371  << "# Please cite the paper above if you use the MT2 values\n"
372  << "# for a scholarly purpose. For the variable MT2 itself,\n"
373  << "# please also cite:\n"
374  << "# \n"
375  << "# http://arxiv.org/abs/hep-ph/9906349\n"
376  << "#=========================================================\n"
377  << "\n\n" << std::flush;
378  }
379  first = false;
380  }
381 
382  static double get_mT2_Sq( // returns square of asymmetric mT2 (which is >=0), or returns a negative number (such as MT2_ERROR) in the case of an error.
383  const double mVis1, const double pxVis1, const double pyVis1,
384  const double mVis2, const double pxVis2, const double pyVis2,
385  const double pxMiss, const double pyMiss,
386  const double mInvis1, const double mInvis2,
387  const double desiredPrecisionOnMT2=0, // This must be non-negative. If set to zero (default) MT2 will be calculated to the highest precision available on the machine (or as close to that as the algorithm permits). If set to a positive value, MT2 (note that is MT2, not its square) will be calculated to within +- desiredPrecisionOnMT2. Note that by requesting precision of +- 0.01 GeV on an MT2 value of 100 GeV can resJult in speedups of a factor of ..
388  const bool useDeciSectionsInitially=true // If true, interval is cut at the 10% point until first acceptance, which gives factor 3 increase in speed calculating kinematic min, but 3% slowdown for events in the bulk. Is on (true) by default, but can be turned off by setting to false.
389  ) {
390 
391 #ifndef DISABLE_COPYRIGHT_PRINTING
392  disableCopyrightMessage(true); // By supplying an argument to disable, we actually ask for the message to be printed, if printing is not already disabled. This counterintuitive function naming is to avoid the need to introduce static variable initialisations ....
393 #endif
394 
395  const double m1Min = mVis1+mInvis1; // when parent has this mass, ellipse 1 has smallest physical size
396  const double m2Min = mVis2+mInvis2; // when parent has this mass, ellipse 2 has smallest physical size
397 
398  if (m1Min>m2Min) {
399  // swap 1 and 2
400  return asymm_mt2_lester_bisect::get_mT2_Sq(
401  mVis2, pxVis2, pyVis2,
402  mVis1, pxVis1, pyVis1,
403  pxMiss, pyMiss,
404  mInvis2, mInvis1,
405  desiredPrecisionOnMT2
406  );
407  }
408 
409  // By now, we can be sure that m1Min <= m2Min
410  assert(m1Min<=m2Min);
411 
412  const double mMin = m2Min; // when parent has this mass, both ellipses are physical, and at least one has zero size. Note that the name "min" expresses that it is the minimum potential parent mass we should consider, not that it is the min of m1Min and m2Min. It is in fact the MAX of them!
413 
414  // TODO: What about rounding? What about idiots who give us mVis values that have been computed from E^2-p^2 terms that are perilously close to zero, or perilously degenerate?
415 
416  const double msSq = mVis1*mVis1;
417  const double sx = pxVis1;
418  const double sy = pyVis1;
419  const double mpSq = mInvis1*mInvis1;
420 
421  const double mtSq = mVis2*mVis2;
422  const double tx = pxVis2;
423  const double ty = pyVis2;
424  const double mqSq = mInvis2*mInvis2;
425 
426  const double sSq = sx*sx + sy*sy;
427  const double tSq = tx*tx + ty*ty;
428  const double pMissSq = pxMiss*pxMiss + pyMiss*pyMiss;
429  const double massSqSum = msSq + mtSq + mpSq + mqSq;
430  const double scaleSq = (massSqSum + sSq + tSq + pMissSq)/8.0;
431 
432 // #define LESTER_DBG 1
433 
434 #ifdef LESTER_DBG
435  std::cout <<"\nMOO ";
436 #endif
437  // Check for an easy MT2 zero, not because we think it will speed up many cases, but because it will allow us to, ever after, assume that scaleSq>0.
438  if (scaleSq==0) {
439  return 0;
440  }
441  const double scale = sqrt(scaleSq);
442 
443  // disjoint at mMin. So find an mUpper at which they are not disjoint:
444  double mLower = mMin;
445  double mUpper = mMin + scale; // since scaleSq is guaranteed to be >0 at this stage, the adition of scaleSq quarantees that mUpperSq is also >0, so it can be exponentially grown (later) by doubling.
446  unsigned int attempts=0;
447  const unsigned int maxAttempts=10000;
448  while (true) {
449  ++attempts;
450 
451  const double mUpperSq = mUpper*mUpper;
452  const Lester::EllipseParams & side1=helper(mUpperSq, msSq, -sx, -sy, mpSq, 0, 0 ); // see side1Coeffs in mathematica notebook
453  const Lester::EllipseParams & side2=helper(mUpperSq, mtSq, +tx, +ty, mqSq, pxMiss, pyMiss); // see side2Coeffs in mathematica notebook
454 
455  bool disjoint;
456  try {
457  disjoint = Lester::ellipsesAreDisjoint(side1, side2);
458  } catch (...) {
459  return MT2_ERROR;
460  }
461 
462  if (!disjoint) {
463  break;
464  }
465 
466  if (attempts>=maxAttempts) {
467  std::cerr << "MT2 algorithm failed to find upper bound to MT2" << std::endl;
468  return MT2_ERROR;
469  }
470 
471 #ifdef LESTER_DBG
472  std::cout << " - ";
473 #endif
474  mUpper *= 2; // grow mUpper exponentially
475  }
476 
477  //const double tol = relativeTolerance * sqrt(scaleSq);
478 
479  // Now begin the bisection:
480  bool goLow = useDeciSectionsInitially;
481  while(desiredPrecisionOnMT2<=0 || mUpper-mLower>desiredPrecisionOnMT2) {
482 
483  const double trialM = ( goLow ?
484  (mLower*15+mUpper)/16 // bias low until evidence this is not a special case
485  :
486  (mUpper + mLower)/2.0 // bisect
487  ); // worry about this not being between mUpperSq and mLowerSq! TODO
488 
489  if (trialM<=mLower || trialM>=mUpper) {
490  // We reached a numerical precision limit: the interval can no longer be bisected!
491 #ifdef LESTER_DBG
492  std::cout << " MACHINE_PREC " << std::setprecision(10) << mLower << " " << trialM << " " << mUpper << " " << mUpper-mLower << " " << desiredPrecisionOnMT2 << std::endl;
493 #endif
494  return trialM*trialM;
495  }
496  const double trialMSq = trialM * trialM;
497  const Lester::EllipseParams & side1 = helper(trialMSq, msSq, -sx, -sy, mpSq, 0, 0 ); // see side1Coeffs in mathematica notebook
498  const Lester::EllipseParams & side2 = helper(trialMSq, mtSq, +tx, +ty, mqSq, pxMiss, pyMiss); // see side2Coeffs in mathematica notebook
499 
500  try {
501  const bool disjoint = Lester::ellipsesAreDisjoint(side1, side2);
502  if (disjoint) {
503  mLower = trialM;
504  goLow = false;
505 #ifdef LESTER_DBG
506  std::cout << "UP " ;
507 #endif
508  } else {
509  mUpper = trialM;
510 #ifdef LESTER_DBG
511  std::cout << "== ";
512 #endif
513  }
514  } catch (...) {
515  // The test for ellipses being disjoint failed ... this means the ellipses became degenerate, which can only happen right at the bottom of the MT2 search range (subject to numerical precision). So:
516 #ifdef LESTER_DBG
517  std::cout << " THROW " << std::endl;
518 #endif
519  return mLower*mLower;
520  }
521  }
522 
523  const double mAns = (mLower+mUpper)/2.0;
524 
525 #ifdef LESTER_DBG
526  std::cout << " USER_PREC " << std::endl;
527 #endif
528  return mAns*mAns;
529  };
530  private:
531 #ifdef ENABLE_INLINING
532  inline
533 #endif
534  static double lestermax(const double x, const double y) {
535  return (x>y)?x:y;
536  }
537  static const Lester::EllipseParams helper(const double mSq, // The test parent-mass value (squared)
538  const double mtSq, const double tx, const double ty, // The visible particle transverse momentum
539  const double mqSq, // The mass of the invisible particle
540  const double pxmiss, const double pymiss
541  ) {
542  const double txSq = tx*tx;
543  const double tySq = ty*ty;
544  const double pxmissSq = pxmiss*pxmiss;
545  const double pymissSq = pymiss*pymiss;
546 
547 
548  const double c_xx = +4.0* mtSq + 4.0* tySq;
549 
550  const double c_yy = +4.0* mtSq + 4.0* txSq;
551 
552  const double c_xy = -4.0* tx*ty;
553 
554  const double c_x = -4.0* mtSq*pxmiss - 2.0* mqSq*tx + 2.0* mSq*tx - 2.0* mtSq*tx +
555  4.0* pymiss*tx*ty - 4.0* pxmiss*tySq;
556 
557  const double c_y = -4.0* mtSq*pymiss - 4.0* pymiss*txSq - 2.0* mqSq*ty + 2.0* mSq*ty - 2.0* mtSq*ty +
558  4.0* pxmiss*tx*ty;
559 
560  const double c = - mqSq*mqSq + 2*mqSq*mSq - mSq*mSq + 2*mqSq*mtSq + 2*mSq*mtSq - mtSq*mtSq +
561  4.0* mtSq*pxmissSq + 4.0* mtSq*pymissSq + 4.0* mqSq*pxmiss*tx -
562  4.0* mSq*pxmiss*tx + 4.0* mtSq*pxmiss*tx + 4.0* mqSq*txSq +
563  4.0* pymissSq*txSq + 4.0* mqSq*pymiss*ty - 4.0* mSq*pymiss*ty +
564  4.0* mtSq*pymiss*ty - 8.0* pxmiss*pymiss*tx*ty + 4.0* mqSq*tySq +
565  4.0* pxmissSq*tySq;
566 
567  return Lester::EllipseParams(c_xx, c_yy, c_xy, c_x, c_y, c);
568  }
569 };
570 
571 
572 #endif
Definition: lester_mt2_bisect.h:327
Definition: lester_mt2_bisect.h:153