ROOTPWA
diffractivePhaseSpace.cc
Go to the documentation of this file.
1 
2 //
3 // Copyright 2009 Sebastian Neubert
4 //
5 // This file is part of rootpwa
6 //
7 // rootpwa is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation, either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // rootpwa is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with rootpwa. If not, see <http://www.gnu.org/licenses/>.
19 //
21 
22 
23 #include <iomanip>
24 #include <fstream>
25 #include <limits>
26 
27 #include "TVector3.h"
28 #include "TH1D.h"
29 #include "TH2D.h"
30 #include "TH3D.h"
31 #include "TH1.h"
32 #include "TCanvas.h"
33 #include "TRandom3.h"
34 #include "TFile.h"
35 
36 #include "reportingUtils.hpp"
37 #include "physUtils.hpp"
38 #include "diffractivePhaseSpace.h"
39 
40 using namespace std;
41 using namespace rpwa;
42 
43 
44 // /////////////////////////////////////////////////////////////////////////////////
45 // // global constants
46 
47 
48 // // target position
49 // const double _targetZPos = -300.0; // [cm]
50 
51 // // beam parameters:
52 // const double _beamMomSigma = 1.2; // [GeV/c]
53 // const double _beamMom = 189; // [GeV/c]
54 // // 2004 beam:
55 // // const double _beamDxDz = 0.00026; // tilt from Quirin was in mrad
56 // // const double _beamDxDzSigma = 0.00010;
57 // // const double _beamDyDz = 0.00001; // tilt from Quirin was in mrad
58 // // const double _beamDyDzSigma = 0.00018;
59 // // ideal beam:
60 // const double _beamDxDz = 0.0;
61 // const double _beamDxDzSigma = 0.0;
62 // const double _beamDyDz = 0.0;
63 // const double _beamDyDzSigma = 0.0;
64 
65 // // cut on t-distribution
66 // const double _tMin = 0.001; // [(GeV/c)^2]
67 
68 
69 diffractivePhaseSpace::diffractivePhaseSpace()
70  : _tMin(0.),
71  _tprimeMin(0.),
72  _tprimeMax(numeric_limits<double>::max()),
73  _xMassMin(0),
74  _xMassMax(0),
75  _protonMass(0.938272013),
76  _pionMass(0.13957018),
77  _pionMass2(_pionMass * _pionMass)
78 {
79  _primaryVertexGen = NULL;
82  _tprime = 0;
83  _invSlopePar = NULL;
84  _invM = NULL;
85 }
86 
88  delete _primaryVertexGen;
89 }
90 
91 
92 // /////////////////////////////////////////////////////////////////////////////////
93 // helper functions
94 
95 // constructs beam Lorentz vector
96 TLorentzVector
98 {
99  // throw magnituide of beam momentum
100  const double pBeam = gRandom->Gaus(_beamMom, _beamMomSigma);
101  // throw beam inclination
102  const double dxdz = gRandom->Gaus(_beamDxDz, _beamDxDzSigma);
103  const double dydz = gRandom->Gaus(_beamDyDz, _beamDyDzSigma);
104  // construct tilted beam momentum Lorentz vector
105  const double pz = pBeam / sqrt(1 + dxdz * dxdz + dydz * dydz);
106  const double px = dxdz * pz;
107  const double py = dydz * pz;
108  const double EBeam = sqrt(pBeam * pBeam + _pionMass2);
109  return TLorentzVector(px, py, pz, EBeam);
110 }
111 
112 
113 // writes event to ascii file read by gamp
114 bool
116  const int beamGeantId,
117  const int beamCharge)
118 {
119  if(!out) {
120  printErr << "output stream is not writable." << endl;
121  return false;
122  }
123  unsigned int nmbDaughters = _decayProducts.size();
124  out << nmbDaughters + 1 << endl;
125  // beam particle: geant ID, charge, p_x, p_y, p_z, E
126  out << beamGeantId << " " << beamCharge
127  << setprecision(numeric_limits<double>::digits10 + 1)
128  << " " << _beamLab.Px() << " " << _beamLab.Py() << " " << _beamLab.Pz()
129  << " " << _beamLab.E() << endl;
130  for(unsigned int i = 0; i < nmbDaughters; ++i) {
131  const TLorentzVector& hadron = _phaseSpace.daughter(i);
132  // hadron: geant ID, charge, p_x, p_y, p_z, E
133  out << _decayProducts[i]._gId << " " << _decayProducts[i]._charge
134  << setprecision(numeric_limits<double>::digits10 + 1)
135  << " " << hadron.Px() << " " << hadron.Py() << " " << hadron.Pz()
136  << " " << hadron.E() << endl;
137  }
138  return true;
139 }
140 
141 bool
142 diffractivePhaseSpace::writeComGeantAscii(ostream& out, bool formated) {
143 
144  if(!out) {
145  cerr << "Output stream is not writable." << endl;
146  return false;
147  }
148 
149  if(formated) {
150  // total number of particles including recoil proton and beam particle
151  unsigned int nmbDaughters = _decayProducts.size();
152  out << nmbDaughters+1+1 << endl;
153  // vertex position in cm
154  // note that Comgeant's coordinate system is different
155  out << _vertex.Z() << " " << _vertex.X() << " " << _vertex.Y() << endl;
156  // beam particle: geant ID , -p_z, -p_x, -p_y must go the opposite direction upstream and should be defined as mulike with PID 44 in Comgeant
157  out << setprecision(numeric_limits<double>::digits10 + 1)
158  << "44 " << -_beamLab.Pz() << " " << -_beamLab.Px() << " " << -_beamLab.Py() << endl;// << " " << beam.E() << endl;
159  // the recoil proton
160  out << setprecision(numeric_limits<double>::digits10 + 1)
161  << "14 " << _recoilprotonLab.Pz() << " " << _recoilprotonLab.Px() << " " << _recoilprotonLab.Py() << endl;// << " " << beam.E() << endl;
162  for (unsigned int i = 0; i < nmbDaughters; ++i) {
163  const TLorentzVector& hadron = _phaseSpace.daughter(i);
164  // hadron: geant ID, p_z, p_x, p_y
165  out << setprecision(numeric_limits<double>::digits10 + 1)
166  << _decayProducts[i]._gId << " "
167  << hadron.Pz() << " "
168  << hadron.Px() << " "
169  << hadron.Py() << endl;// << " " << hadron->E() << endl;
170  }
171  } else {
172  int intval;
173  float floatval;
174  // total number of particles including recoil proton and beam particle
175  unsigned int nmbDaughters = _decayProducts.size();
176  //out << nmbDaughters+1+1 << endl;
177  intval = (int)nmbDaughters+1+1; out.write((char*)&intval,4);
178  // vertex position in cm
179  // note that Comgeant's coordinate system is different
180  floatval = (float)_vertex.Z(); out.write((char*)&floatval,4);
181  floatval = (float)_vertex.X(); out.write((char*)&floatval,4);
182  floatval = (float)_vertex.Y(); out.write((char*)&floatval,4);
183  //out << _vertex.Z() << " " << _vertex.X() << " " << _vertex.Y() << endl;
184  // beam particle: geant ID , -p_z, -p_x, -p_y must go the opposite direction upstream and should be defined as mulike with PID 44 in Comgeant
185  intval = 44; out.write((char*)&intval,4);
186  floatval = (float)-_beamLab.Pz(); out.write((char*)&floatval,4);
187  floatval = (float)-_beamLab.Px(); out.write((char*)&floatval,4);
188  floatval = (float)-_beamLab.Py(); out.write((char*)&floatval,4);
189  //out << setprecision(numeric_limits<double>::digits10 + 1)
190  // << "44 " << -_beamLab.Pz() << " " << -_beamLab.Px() << " " << -_beamLab.Py() << endl;// << " " << beam.E() << endl;
191  // the recoil proton
192  intval = 14; out.write((char*)&intval,4);
193  floatval = (float)_recoilprotonLab.Pz(); out.write((char*)&floatval,4);
194  floatval = (float)_recoilprotonLab.Px(); out.write((char*)&floatval,4);
195  floatval = (float)_recoilprotonLab.Py(); out.write((char*)&floatval,4);
196  //out << setprecision(numeric_limits<double>::digits10 + 1)
197  // << "14 " << _recoilprotonLab.Pz() << " " << _recoilprotonLab.Px() << " " << _recoilprotonLab.Py() << endl;// << " " << beam.E() << endl;
198  for (unsigned int i = 0; i < nmbDaughters; ++i) {
199  const TLorentzVector& hadron = _phaseSpace.daughter(i);
200  // hadron: geant ID, p_z, p_x, p_y
201  intval = (int)_decayProducts[i]._gId; out.write((char*)&intval,4);
202  floatval = (float)hadron.Pz(); out.write((char*)&floatval,4);
203  floatval = (float)hadron.Px(); out.write((char*)&floatval,4);
204  floatval = (float)hadron.Py(); out.write((char*)&floatval,4);
205  //out << setprecision(numeric_limits<double>::digits10 + 1)
206  //<< _decayProducts[i]._gId << " "
207  //<< hadron.Pz() << " "
208  //<< hadron.Px() << " "
209  //<< hadron.Py() << endl;// << " " << hadron->E() << endl;
210  }
211  }
212  return true;
213 }
214 
215 
216 void
218 {
219  gRandom->SetSeed(seed);
220  _phaseSpace.setSeed(seed);
221 }
222 
223 
224 void
225 diffractivePhaseSpace::SetDecayProducts(const vector<particleInfo>& info)
226 {
227  _decayProducts.clear();
230 }
231 
232 
233 void
235 {
236  _decayProducts.push_back(info);
238 }
239 
240 
241 void
243 {
244  const unsigned int nmbDaughters = _decayProducts.size();
245  vector<double> daughterMasses(nmbDaughters, 0);
246  for(unsigned int i = 0; i < nmbDaughters; ++i) {
247  daughterMasses[i] = _decayProducts[i]._mass;
248  }
249  if(nmbDaughters > 1) {
250  _phaseSpace.setDecay(daughterMasses);
251  if(_xMassMax == 0) {
252  printErr << "mass range [" << _xMassMin << ", " << _xMassMin << "] GeV/c^2 "
253  << "does mot make sense. exiting." << endl;
254  throw;
255  } else {
256  printInfo << "calculating max weight (" << nmbDaughters << " FS particles) "
257  << "for m = " << _xMassMax << " GeV/c^2" << endl;
259  cout << " max weight = " << _phaseSpace.maxWeight() << endl;
260  }
261  }
262 }
263 
264 
266 // main routine
267 // void
268 // diffractivePhaseSpace::genPhaseSpaceData(const double xMassMin = 2.100, // lower bound of mass bin [GeV/c^2]
269 // const double xMassMax = 2.140, // upper bound of mass bin [GeV/c^2]
270 // const TString& outFileName = "2100.2140.genbod.evt",
271 // const TString& thetaHistFileName = "./hTheta.root", // histogram with experimental distribution of scattering angle
272 // const int nmbEvent = 2000,
273 // const bool plot = false)
274 // {
275 // Double_t daughterMasses[3] = {_pionMass, _pionMass, _pionMass};
276 
277 // gRandom->SetSeed(12345);
278 
279 
280 // // setup histograms
281 // TH1D* ht;
282 // TH1D* hm;
283 // TH1D* hTheta;
284 // TH3D* hVertex3D;
285 // TH2D* hVertex;
286 // TH1D* hVz;
287 // TH1D* hE;
288 // if (plot) {
289 // ht = new TH1D("ht", "t", 10000, -0.1, 1);
290 // hm = new TH1D("hm", "3pi mass", 1000, 0.5, 2.5);
291 // hTheta = new TH1D("hThetaGen", "cos theta", 100, 0.99985, 1);
292 // hVertex3D = new TH3D("hVertex3D", "Vertex xyz", 100, -2, 2, 100, -2, 2, 200, _targetZPos - 5, _targetZPos + 40);
293 // hVertex = new TH2D("hVertex", "Vertex xy", 100, -2, 2, 100, -2, 2);
294 // hVz = new TH1D("hVz","Vertex z", 1000, _targetZPos - 40, _targetZPos + 40);
295 // hE = new TH1D("hE", "E", 100, 180, 200);
296 // }
297 
298 // // open output file
299 // ofstream outFile(outFileName);
300 // cout << "Writing " << nmbEvent << " events to file '" << outFileName << "'." << endl;
301 
302 // // get theta histogram
303 // TH1* thetaDist = NULL;
304 // {
305 // TFile* thetaHistFile = TFile::Open(thetaHistFileName, "READ");
306 // if (!thetaHistFile || thetaHistFile->IsZombie()) {
307 // cerr << "Cannot open histogram file '" << thetaHistFileName << "'. exiting." << endl;
308 // return;
309 // }
310 // thetaHistFile->GetObject("h1", thetaDist);
311 // if (!thetaDist) {
312 // cerr << "Cannot find theta histogram in file '" << thetaHistFileName << "'. exiting." << endl;
313 // return;
314 // }
315 // }
316 
317 // int countEvent = 0;
318 // int attempts = 0;
319 // int tenpercent = (int)(nmbEvent * 0.1);
320 // while (countEvent < nmbEvent) { // loop over events
321 // ++attempts;
322 
323 
324 // }
325 // }
326 
327 double
329  double result = 1.;
330  if(!_invSlopePar) {
331  return result;
332  }
333  if(_ninvSlopePar == 1) {
334  return _invSlopePar[0];
335  }
336  if(invariant_M < 0.) {
337  return _invSlopePar[0];
338  }
339  // assuming to have sorted entries
340  // case of linear extrapolation
341  int i_1 = -1;
342  int i_2 = -1;
343  if(invariant_M < _invM[0]) {
344  i_1 = 0;
345  i_2 = 1;
346  }
347  if(invariant_M >= _invM[_ninvSlopePar - 2]) {
348  i_1 = _ninvSlopePar - 2;
349  i_2 = _ninvSlopePar - 1;
350  }
351  // case of linear interpolation
352  if(i_1 < 0 || i_2 < 0) {
353  // search for the matching two points
354  for(int i = 0; i < _ninvSlopePar-2; i++) {
355  if(_invM[i] <= invariant_M && invariant_M < _invM[i+1]) {
356  i_1=i;
357  i_2=i+1;
358  break;
359  }
360  }
361  }
362  // extra/interpolate lineary
363  double m_1 = _invM[i_1];
364  double m_2 = _invM[i_2];
365  double invt_1 = _invSlopePar[i_1];
366  double invt_2 = _invSlopePar[i_2];
367  result = invt_1 + (((invt_2-invt_1) / (m_2-m_1)) * (invariant_M-m_1));
368  return result;
369 }
370 
371 
372 // based on Dima's prod_decay_split.f
373 unsigned int
375 {
376  unsigned long int attempts = 0;
377  // construct primary vertex and beam
378  // use the primary Vertex Generator if available
379  if(_primaryVertexGen) {
380  // as documented one may need several attempts to get a vertex position
381  // which is valid also for the beam direction measurement
382  while(attempts < 1000) {
384  TVector3 beam_dir = _primaryVertexGen->getBeamDir(_vertex);
385  if(beam_dir.Mag() == 0) {
386  ++attempts;
387  //cout << " skipping " << endl;
388  continue;
389  }
391  break;
392  }
393  // Just a few events should contain a vertex position with no beam direction information
394  if(attempts == 999) {
395  cerr << " Error in beam construction. Please check the beam properties loaded correctly!" << endl;
396  _beamLab = makeBeam();
397  }
398  } else {
399  _vertex.SetXYZ(0, 0,
400  _targetZPos+gRandom->Uniform(-_targetZLength * 0.5,
401  _targetZLength * 0.5));
402  _beamLab = makeBeam();
403  }
404 
405  const TLorentzVector targetLab(0, 0, 0, _targetMass);
406  const TLorentzVector overallCm = _beamLab + targetLab; // beam-target center-of-mass system
407  // check
408  if(_xMassMax + _targetMass > overallCm.M()) {
409  printErr << "Max Mass out of kinematic range." << endl
410  << "Limit = " << overallCm.M() - _targetMass << "GeV/c2" << endl
411  << " ABORTING " << flush<< endl;
412  throw;
413  }
414 
415  bool done = false;
416  while(!done) {
417 
418  // _xMassMin == _xMassMax
419  double xMass = _xMassMin;
420  if(_xMassMin < _xMassMax) {
421  xMass = gRandom->Uniform(_xMassMin, _xMassMax); // pick random X mass
422  } else if(_xMassMin > _xMassMax) {
423  xMass = gRandom->Gaus( _xMassMin, _xMassMax); // pick random X mass arround _xMassMin
424  }
425 
426  double tPrime = _tprimeMin;
427  if(_tprimeMax < _tprimeMin) {
428  // calculate the slope parameter depending on the invariant mass
429  const double calc_invSlopePar = Get_inv_SlopePar(xMass);
430  tPrime = -gRandom->Exp(calc_invSlopePar); // pick random t'
431  //cout << " inv slope par " << _invSlopePar << " gradient " << _invSlopeParGradient << " t' is " << tPrime << endl;
432  }
433 
434  // make sure that X mass is not larger than maximum allowed mass
435  if(xMass + _recoilMass > overallCm.M()) {
436  continue;
437  }
438 
439  // calculate t from t' in center-of-mass system of collision
440  const double s = overallCm.Mag2();
441  const double sqrtS = sqrt(s);
442  const double recoilMass2 = _recoilMass * _recoilMass;
443  const double xMass2 = xMass * xMass;
444  const double xEnergyCM = (s - recoilMass2 + xMass2) / (2 * sqrtS); // breakup energy
445  const double xMomCM = sqrt(xEnergyCM * xEnergyCM - xMass2); // breakup momentum
446  const double beamMass2 = _beamLab.M2();
447  const double targetMass2 = _targetMass * _targetMass;
448  const double beamEnergyCM = (s - targetMass2 + beamMass2) / (2 * sqrtS); // breakup energy
449  const double beamMomCM = sqrt(beamEnergyCM * beamEnergyCM - beamMass2); // breakup momentum
450  const double t0 = (xEnergyCM - beamEnergyCM) * (xEnergyCM - beamEnergyCM) -
451  (xMomCM - beamMomCM) * (xMomCM - beamMomCM);
452  const double t = t0 + tPrime;
453  // reject events outside of allowed kinematic region
454  if(t > t0) {
455  continue;
456  }
457 
458  // construct X Lorentz-vector in lab frame (= target RF)
459  // convention used here: Reggeon = X - beam = target - recoil (momentum transfer from target to beam vertex)
460  const double reggeonEnergyLab = (targetMass2 - recoilMass2 + t) / (2 * _targetMass); // breakup energy
461  const double xEnergyLab = _beamLab.E() + reggeonEnergyLab;
462  const double xMomLab = sqrt(xEnergyLab * xEnergyLab - xMass2);
463  const double xCosThetaLab = (t - xMass2 - beamMass2 + 2 * _beamLab.E() * xEnergyLab) / (2 * _beamLab.P() * xMomLab);
464  const double xSinThetaLab = sqrt(1 - xCosThetaLab * xCosThetaLab);
465  const double xPtLab = xMomLab * xSinThetaLab;
466  const double xPhiLab = gRandom->Uniform(0., TMath::TwoPi());
467 
468  // xSystemLab is defined w.r.t. beam direction
469  TLorentzVector xSystemLab = TLorentzVector(xPtLab * cos(xPhiLab),
470  xPtLab * sin(xPhiLab),
471  xMomLab * xCosThetaLab,
472  xEnergyLab);
473  // rotate according to beam tilt
474  TVector3 beamDir = _beamLab.Vect().Unit();
475  xSystemLab.RotateUz(beamDir);
476  // calculate the recoil proton properties
477  _recoilprotonLab = (_beamLab + targetLab) - xSystemLab; // targetLab
478 
479  /* check for coplanarity
480  cout << " Energy balance is " << (_beamLab + targetLab).E() << " vs. " << (_recoilprotonLab+xSystemLab).E() << endl;
481  cout << " Momentum balance is " << (_beamLab + targetLab).Mag() << " vs. " << (_recoilprotonLab+xSystemLab).Mag() << endl;
482  cout << " Direction X balance is " << (_beamLab + targetLab).Px() << " vs. " << (_recoilprotonLab+xSystemLab).Px() << endl;
483  cout << " Direction Y balance is " << (_beamLab + targetLab).Py() << " vs. " << (_recoilprotonLab+xSystemLab).Py() << endl;
484  cout << " Direction Z balance is " << (_beamLab + targetLab).Pz() << " vs. " << (_recoilprotonLab+xSystemLab).Pz() << endl;
485 
486  // own rotation
487  TVector3 vec_direction = _beamLab.Vect().Unit();
488  TVector3 vec_origin(0.,0.,1.);
489  // get the angle of the vector
490  double angle = vec_origin.Angle(vec_direction);
491  // get the rotation axis perpendicular to the plane between these both
492  TVector3 vec_rotation = vec_origin.Cross(vec_direction);
493  vec_rotation = vec_rotation.Unit();
494  // rotate around this axis by the given angle
495  //particle_null.Rotate (-angle, vec_rotation);
496  _recoilprotonLab.Rotate(-angle, vec_rotation);
497  xSystemLab.Rotate(-angle, vec_rotation);
498 
499  cout << " delta phi is " << (_recoilprotonLab.Phi()-xSystemLab.Phi())/3.141592654 << endl;
500  */
501 
502  // recalculate t' for xcheck or save the generated
503  // number directly if you change if (1) to (0) to
504  // speed up the process a bit
505  if(0) {
506  _tprime = Calc_t_prime(_beamLab, xSystemLab);
507  } else {
508  _tprime = tPrime;
509  }
510 
511  // apply t cut
512  if(t > _tMin || _tprime > _tprimeMin || _tprime < _tprimeMax) {
513  continue;
514  }
515 
516  // generate n-body phase space for X system
517  ++attempts;
518  {
519  _phaseSpace.pickMasses(xMass);
520 
521  // correct weight for phase space splitting
522  // and for 2-body phase space beam-target
523  // (1 / 4pi) * q(sqrt(s), m_x, m_recoil) / sqrt(s)
524  const double ps2bodyWMax = breakupMomentum(sqrtS,_xMassMax,_targetMass)/sqrtS;
525  const double ps2bodyW = breakupMomentum(sqrtS,xMass,_targetMass)/sqrtS;
526 
527  const double maxPsWeight = _phaseSpace.maxWeight() * _xMassMax * ps2bodyWMax;
528  const double psWeight = _phaseSpace.calcWeight() * xMass* ps2bodyW;
529 
530  if((psWeight / maxPsWeight) < _phaseSpace.random()) {
531  continue;
532  }
534  _phaseSpace.calcEventKinematics(xSystemLab);
535  }
536 
537  done = true;
538  }
539  // event was accepted
540 
541  return attempts;
542 }
543 
544 
545 unsigned int
546 diffractivePhaseSpace::event(ostream& stream)
547 {
548  unsigned int attempts = event();
549  //writePwa2000Ascii(stream, 9, -1); // use pi^- beam
550  // use the first particle as the beam particle
551  writePwa2000Ascii(stream, _decayProducts[0]._gId, _decayProducts[0]._charge);
552  return attempts;
553 }
554 
555 unsigned int
556 diffractivePhaseSpace::event(ostream& stream, ostream& streamComGeant)
557 {
558  unsigned int attempts = event();
559  //writePwa2000Ascii(stream, 9, -1); // use pi^- beam
560  // use the first particle as the beam particle
561  writePwa2000Ascii(stream, _decayProducts[0]._gId, _decayProducts[0]._charge);
562  writeComGeantAscii(streamComGeant, true);
563  return attempts;
564 }
565 
566 
567 void
568 diffractivePhaseSpace::SetBeam(double Mom, double MomSigma,
569  double DxDz, double DxDzSigma,
570  double DyDz, double DyDzSigma)
571 {
572  _beamMomSigma=MomSigma;
573  _beamMom=Mom;
574  _beamDxDz=DxDz;
575  _beamDxDzSigma=DxDzSigma;
576  _beamDyDz=DyDz;
577  _beamDyDzSigma=DyDzSigma;
578 }
579 
580 float
581 diffractivePhaseSpace::Calc_t_prime(const TLorentzVector& particle_In, const TLorentzVector& particle_Out){
582  float result = 0.;
583  result = (particle_Out.M2()-particle_In.M2());
584  result = pow(result,2);
585  result /= 4*pow(particle_In.P(),2);
586  result = fabs((particle_In-particle_Out).M2())-fabs(result);
587  return result;
588 }
589