Thermal-FIST  1.3
Package for hadron resonance gas model applications
RandomGenerators.cpp
Go to the documentation of this file.
1 /*
2  * Thermal-FIST package
3  *
4  * Copyright (c) 2015-2019 Volodymyr Vovchenko
5  *
6  * GNU General Public License (GPLv3 or later)
7  */
9 
10 #include "HRGBase/xMath.h"
13 
14 namespace thermalfist {
15 
16  namespace RandomGenerators {
17 
18  MTRand randgenMT;
19 
20  void SetSeed(const unsigned int seed) {
21  randgenMT.seed(seed);
22  }
23 
24  int RandomPoisson(double mean) {
25  int n;
26  if (mean <= 0) return 0;
27  if (mean < 25) {
28  double expmean = exp(-mean);
29  double pir = 1;
30  n = -1;
31  while (1) {
32  n++;
33  pir *= randgenMT.rand();
34  if (pir <= expmean) break;
35  }
36  return n;
37  }
38  // for large value we use inversion method
39  else {//if (mean < 1E9) {
40  double em, t, y;
41  double sq, alxm, g;
42  double pi = xMath::Pi();
43 
44  sq = sqrt(2.0*mean);
45  alxm = log(mean);
46  g = mean * alxm - xMath::LogGamma(mean + 1.0);
47 
48  do {
49  do {
50  y = tan(pi*randgenMT.rand());
51  em = sq * y + mean;
52  } while (em < 0.0);
53 
54  em = floor(em);
55  t = 0.9*(1.0 + y * y)* exp(em*alxm - xMath::LogGamma(em + 1.0) - g);
56  } while (randgenMT.rand() > t);
57 
58  return static_cast<int> (em);
59 
60  }
61  //else {
62  // // use Gaussian approximation vor very large values
63  // n = Int_t(Gaus(0,1)*TMath::Sqrt(mean) + mean + 0.5);
64  // return n;
65  //}
66  }
67 
68 
69  int RandomPoisson(double mean, MTRand &rangen) {
70  int n;
71  if (mean <= 0) return 0;
72  if (mean < 25) {
73  double expmean = exp(-mean);
74  double pir = 1;
75  n = -1;
76  while (1) {
77  n++;
78  pir *= rangen.rand();
79  if (pir <= expmean) break;
80  }
81  return n;
82  }
83  // for large value we use inversion method
84  else {//if (mean < 1E9) {
85  double em, t, y;
86  double sq, alxm, g;
87  double pi = xMath::Pi();
88 
89  sq = sqrt(2.0*mean);
90  alxm = log(mean);
91  g = mean * alxm - xMath::LogGamma(mean + 1.0);
92 
93  do {
94  do {
95  y = tan(pi*rangen.rand());
96  em = sq * y + mean;
97  } while (em < 0.0);
98 
99  em = floor(em);
100  t = 0.9*(1.0 + y * y)* exp(em*alxm - xMath::LogGamma(em + 1.0) - g);
101  } while (rangen.rand() > t);
102 
103  return static_cast<int> (em);
104 
105  }
106  //else {
107  // // use Gaussian approximation vor very large values
108  // n = Int_t(Gaus(0,1)*TMath::Sqrt(mean) + mean +0.5);
109  // return n;
110  //}
111  }
112 
113  double SkellamProbability(int k, double mu1, double mu2)
114  {
115  return exp(-(mu1 + mu2)) * pow(sqrt(mu1 / mu2), k) * xMath::BesselI(k, 2. * sqrt(mu1 * mu2));
116  }
117 
118 
119  double SiemensRasmussenMomentumGenerator::g(double x, double mass) const {
120  if (mass < 0.)
121  mass = m_Mass;
122  double tp = -log(x);
123  double talpha = alpha(tp);
124  //double en = w(tp);
125  double en = sqrt(tp * tp + mass * mass);
126  double sh = sinh(talpha);
127  double shtalpha = 1.;
128  if (talpha != 0.0)
129  shtalpha = sh / talpha;
130  double ch = sqrt(1. + sh * sh);
131  return tp * tp * exp(-m_Gamma * en / m_T) * ((1. + m_T / m_Gamma / en)*shtalpha - m_T / m_Gamma / en * ch) / x;
132  }
133 
134  double SiemensRasmussenMomentumGenerator::g2(double x, double tp, double mass) const {
135  if (mass < 0.)
136  mass = m_Mass;
137  double talpha = alpha(tp);
138  //double en = w(tp);
139  double en = sqrt(tp * tp + mass * mass);
140  double sh = sinh(talpha);
141  double shtalpha = 1.;
142  if (talpha != 0.0)
143  shtalpha = sh / talpha;
144  double ch = sqrt(1. + sh * sh);
145  return tp * tp * exp(-m_Gamma * en / m_T) * ((1. + m_T / m_Gamma / en)*shtalpha - m_T / m_Gamma / en * ch) / x;
146  }
147 
148  void SiemensRasmussenMomentumGenerator::FixParameters() {
149  double eps = 1e-8;
150  double l = 0., r = 1.;
151  double m1 = l + (r - l) / 3.;
152  double m2 = r - (r - l) / 3.;
153  int MAXITERS = 200;
154  int iter = 0;
155  while (fabs(m2 - m1) > eps && iter < MAXITERS) {
156  if (g(m1) < g(m2)) {
157  l = m1;
158  }
159  else {
160  r = m2;
161  }
162  m1 = l + (r - l) / 3.;
163  m2 = r - (r - l) / 3.;
164  iter++;
165  }
166  m_Max = g((m1 + m2) / 2.);
167  }
168 
169  double SiemensRasmussenMomentumGenerator::GetRandom(double mass) const {
170  if (mass < 0.)
171  mass = m_Mass;
172  while (1) {
173  double x0 = randgenMT.randDblExc();
174  double y0 = m_Max * randgenMT.randDblExc();
175  double mn = 1.;
176  if (mass != m_Mass)
177  mn = 10.;
178  if (y0 < mn * g(x0)) return -log(x0);
179  }
180  return 0.;
181  }
182 
183  std::vector<double> SiemensRasmussenMomentumGenerator::GetMomentum(double mass) const {
184  std::vector<double> ret(0);
185  double tp = GetRandom(mass);
186  double tphi = 2. * xMath::Pi() * randgenMT.rand();
187  double cthe = 2. * randgenMT.rand() - 1.;
188  double sthe = sqrt(1. - cthe * cthe);
189  ret.push_back(tp*cos(tphi)*sthe); //px
190  ret.push_back(tp*sin(tphi)*sthe); //py
191  ret.push_back(tp*cthe); //pz
192  return ret;
193  }
194 
195 
196  double ThermalMomentumGenerator::g(double x, double mass) const
197  {
198  if (mass < 0.)
199  mass = m_Mass;
200  double tp = -log(x);
201  double texp = exp((sqrt(mass * mass + tp * tp) - m_Mu) / m_T);
202  return tp * tp / (texp + m_Statistics) / x;
203  }
204 
205  void ThermalMomentumGenerator::FixParameters()
206  {
207  double eps = 1e-8;
208  double l = 0., r = 1.;
209  double m1 = l + (r - l) / 3.;
210  double m2 = r - (r - l) / 3.;
211  int MAXITERS = 200;
212  int iter = 0;
213  while (fabs(m2 - m1) > eps && iter < MAXITERS) {
214  if (g(m1) < g(m2)) {
215  l = m1;
216  }
217  else {
218  r = m2;
219  }
220  m1 = l + (r - l) / 3.;
221  m2 = r - (r - l) / 3.;
222  iter++;
223  }
224  m_Max = g((m1 + m2) / 2.);
225  }
226 
227  double ThermalMomentumGenerator::ComputeMaximum(double mass) const
228  {
229  double eps = 1e-8;
230  double l = 0., r = 1.;
231  double m1 = l + (r - l) / 3.;
232  double m2 = r - (r - l) / 3.;
233  int MAXITERS = 200;
234  int iter = 0;
235  while (fabs(m2 - m1) > eps && iter < MAXITERS) {
236  if (g(m1, mass) < g(m2, mass)) {
237  l = m1;
238  }
239  else {
240  r = m2;
241  }
242  m1 = l + (r - l) / 3.;
243  m2 = r - (r - l) / 3.;
244  iter++;
245  }
246  return g((m1 + m2) / 2., mass);
247  }
248 
249  double ThermalMomentumGenerator::GetP(double mass) const
250  {
251  if (mass < 0.)
252  mass = m_Mass;
253  while (1) {
254  double x0 = randgenMT.randDblExc();
255 
256  if (mass < m_Mu && m_Statistics == -1)
257  printf("**WARNING** ThermalMomentumGenerator::GetP: Bose-condensation mu %lf > mass %lf\n", m_Mu, mass);
258 
259  double M = m_Max;
260  if (mass != m_Mass)
261  M = ComputeMaximum(mass);
262 
263  double prob = g(x0, mass) / M;
264 
265  if (prob > 1.)
266  printf("**WARNING** ThermalMomentumGenerator::GetP: Probability exceeds unity by %E\n", prob - 1.);
267 
268  if (randgenMT.randDblExc() < prob) return -log(x0);
269  }
270  return 0.;
271  }
272 
273 
275  double Tkin, double etamax, double mass, int statistics, double mu) :
276  m_FreezeoutModel(FreezeoutModel),
277  m_Tkin(Tkin), m_EtaMax(etamax), m_Mass(mass),
278  m_Generator(mass, statistics, Tkin, mu)
279  {
280  if (m_FreezeoutModel == NULL) {
281  //m_FreezeoutModel = new BoostInvariantFreezeoutParametrization();
282  }
283  }
284 
286  {
287  if (m_FreezeoutModel != NULL)
288  delete m_FreezeoutModel;
289  }
290 
291  std::vector<double> BoostInvariantMomentumGenerator::GetMomentum(double mass) const
292  {
293  if (mass < 0.)
294  mass = Mass();
295 
296  std::vector<double> ret(3, 0.);
297  while (1) {
298  double zetacand = GetRandomZeta(RandomGenerators::randgenMT);
299  double eta = -EtaMax() + 2. * EtaMax() * RandomGenerators::randgenMT.rand();
300  double ph = 2. * xMath::Pi() * RandomGenerators::randgenMT.rand();
301 
302  double betar = m_FreezeoutModel->tanhetaperp(zetacand);
303  double cosheta = cosh(eta);
304  double sinheta = sinh(eta);
305 
306  double cosphi = cos(ph);
307  double sinphi = sin(ph);
308 
309  double vx = betar * cosphi / cosheta;
310  double vy = betar * sinphi / cosheta;
311  double vz = tanh(eta);
312 
313  SimpleParticle part(0., 0., 0., mass, 0);
314 
315  double tp = m_Generator.GetP(mass);
316  double tphi = 2. * xMath::Pi() * RandomGenerators::randgenMT.rand();
317  double cthe = 2. * RandomGenerators::randgenMT.rand() - 1.;
318  double sthe = sqrt(1. - cthe * cthe);
319  part.px = tp * cos(tphi) * sthe;
320  part.py = tp * sin(tphi) * sthe;
321  part.pz = tp * cthe;
322  part.p0 = sqrt(mass * mass + tp * tp);
323 
324  double p0LRF = part.p0;
325 
326  part = ParticleDecaysMC::LorentzBoost(part, -vx, -vy, -vz);
327 
328  //double prob = (cosheta * part.p0 - sinheta * part.pz) /
329  // (2. * (1. / (1 - betar * betar)) * (cosheta * part.p0 - sinheta * part.pz - betar * (part.px * cos(ph) + part.py * sin(ph))));
330 
331  double dRdZeta = m_FreezeoutModel->dRdZeta(zetacand);
332  double dtaudZeta = m_FreezeoutModel->dtaudZeta(zetacand);
333 
334  double coshetaperp = m_FreezeoutModel->coshetaperp(zetacand);
335  double sinhetaperp = m_FreezeoutModel->sinhetaperp(zetacand);
336 
337  double prob = (dRdZeta * (cosheta * part.p0 - sinheta * part.pz)
338  - dtaudZeta * (cosphi * part.px + sinphi * part.py)) /
339  (coshetaperp * dRdZeta - sinhetaperp * dtaudZeta) / p0LRF
340  / 2.;
341 
342  if (RandomGenerators::randgenMT.rand() < prob) {
343  ret[0] = part.px;
344  ret[1] = part.py;
345  ret[2] = part.pz;
346  break;
347  }
348  }
349  return ret;
350  }
351 
353  {
354  if (m_FreezeoutModel->InverseZetaDistributionIsExplicit())
355  return m_FreezeoutModel->InverseZetaDistribution(rangen.rand());
356 
357  while (1) {
358  double zetacand = rangen.rand();
359 
360  double prob = m_FreezeoutModel->ZetaProbability(zetacand) / m_FreezeoutModel->ProbabilityMaximum();
361 
362  if (rangen.rand() < prob)
363  return zetacand;
364  }
365  return 0.;
366  }
367 
368 
369  double BreitWignerGenerator::f(double x) const {
370  return x / ((x*x - m_M * m_M)*(x*x - m_M * m_M) + m_M * m_M*m_Gamma*m_Gamma);
371  }
372 
373  void BreitWignerGenerator::FixParameters() {
374  double eps = 1e-8;
375  double l = m_Mthr, r = m_M + 2.*m_Gamma;
376  double m1 = l + (r - l) / 3.;
377  double m2 = r - (r - l) / 3.;
378  int MAXITERS = 200;
379  int iter = 0;
380  while (fabs(m2 - m1) < eps && iter < MAXITERS) {
381  if (f(m1) < f(m2)) {
382  l = m1;
383  }
384  else {
385  r = m2;
386  }
387  m1 = l + (r - l) / 3.;
388  m2 = r - (r - l) / 3.;
389  iter++;
390  }
391  m_Max = f((m1 + m2) / 2.);
392  }
393 
395  //bool fl = true;
396  if (m_Gamma < 1e-7) return m_M;
397  while (1) {
398  double x0 = m_Mthr + (m_M + 2.*m_Gamma - m_Mthr) * randgenMT.rand();
399  double y0 = m_Max * randgenMT.rand();
400  if (y0 < f(x0)) return x0;
401  }
402  return 0.;
403  }
404 
405  void BreitWignerGenerator::SetParameters(double M, double gamma, double mthr) {
406  m_M = M;
407  m_Gamma = gamma;
408  m_Mthr = mthr;
409  FixParameters();
410  }
411 
413  {
414  m_part = part;
415  m_T = T;
416  m_Mu = Mu;
417  FixParameters();
418  }
419 
421  {
422  double Threshold = m_part->DecayThresholdMass();
423  double Width = m_part->ResonanceWidth();
424  double Mass = m_part->Mass();
425 
426  double a = std::max(Threshold, Mass - 2.*Width);
427  double b = Mass + 2.*Width;
428 
429  m_Xmin = a;
430  m_Xmax = b + (b - a)*0.2;
431 
432  m_Max = 0.;
433 
434  int iters = 1000;
435  double dM = (m_Xmax - m_Xmin) / (iters - 1.);
436  for (int i = 0; i < iters; ++i) {
437  double tM = m_Xmin + dM * i;
438  m_Max = std::max(m_Max, f(tM));
439  }
440  m_Max *= 1.2;
441  }
442 
443  double ThermalBreitWignerGenerator::f(double M) const
444  {
445  return m_part->ThermalMassDistribution(M, m_T, m_Mu, m_part->ResonanceWidth());
446  }
447 
449  {
450  if (m_part->ResonanceWidth() / m_part->Mass() < 1.e-2)
451  return m_part->Mass();
452  while (true) {
453  double x0 = m_Xmin + (m_Xmax - m_Xmin) * randgenMT.rand();
454  double y0 = m_Max * randgenMT.rand();
455  if (y0 < f(x0)) return x0;
456  }
457  return 0.;
458  }
459 
461  {
462  double Threshold = m_part->DecayThresholdMass();
463  double Width = m_part->ResonanceWidth();
464  double Mass = m_part->Mass();
465 
466  double a = std::max(Threshold, Mass - 2.*Width);
467  double b = Mass + 2.*Width;
468 
469  if (m_part->Decays().size() == 0)
470  a = Mass - 2.*Width + 1.e-6;
471  else
472  a = m_part->DecayThresholdMassDynamical();
473 
474  b = Mass + 2.*Width;
475 
476  m_Xmin = a;
477  m_Xmax = b + 0.2 * (b - a);
478 
479  //if (m_part->PdgId() == 32214)
480  // printf("%lf %lf\n", m_Xmin, m_Xmax);
481 
482  m_Max = 0.;
483 
484  int iters = 1000;
485  double dM = (m_Xmax - m_Xmin) / (iters - 1.);
486  for (int i = 0; i < iters; ++i) {
487  double tM = m_Xmin + dM * i;
488  m_Max = std::max(m_Max, f(tM));
489  }
490  m_Max *= 1.2;
491  }
492 
494  {
495  return m_part->ThermalMassDistribution(M, m_T, m_Mu, m_part->TotalWidtheBW(M));
496  }
497 
498  //double BesselDistributionGenerator::pn(int n, double a, int nu)
499  //{
500  // if (nu < 0 || n < 0) return 0.0;
501  // double nfact = 1., nnufact = 1.;
502  // for (int i = 1; i <= n; ++i)
503  // nfact *= i;
504 
505  // nnufact = nfact;
506  // for (int i = n + 1; i <= n + nu; ++i)
507  // nnufact *= i;
508 
509  // return pow(a / 2., 2 * n + nu) / xMath::BesselI(nu, a) / nfact / nnufact;
510  //}
511 
512  double BesselDistributionGenerator::pn(int n, double a, int nu)
513  {
514  if (nu < 0 || n < 0) return 0.0;
515 
516  double logret = (2. * n + nu) * log(a/2.) - a;
517  for (int i = 1; i <= n; ++i)
518  logret -= 2. * log(i);
519  for (int i = n + 1; i <= n + nu; ++i)
520  logret -= log(i);
521 
522  return exp(logret) / xMath::BesselIexp(nu, a);
523  }
524 
525  double BesselDistributionGenerator::R(double x, int nu)
526  {
527  double hn2 = 1., hn1 = 0., hn = 0.;
528  double kn2 = 0., kn1 = 1., kn = 0.;
529  int nmax = 20;
530  for (int n = 1; n <= nmax; ++n) {
531  double an = 2. * (nu + n) / x;
532  hn = an * hn1 + hn2;
533  kn = an * kn1 + kn2;
534 
535  hn2 = hn1;
536  hn1 = hn;
537 
538  kn2 = kn1;
539  kn1 = kn;
540 
541  if (n == nmax && nmax <= 1000 && abs(hn2 / kn2 - hn1 / kn1) > 1.e-9)
542  nmax *= 2;
543 
544  if (n == 1000) {
545  printf("**WARNING** BesselDistributionGenerator::R(x,nu): Reached maximum iterations...\n");
546  }
547  }
548  return hn / kn;
549  }
550 
551  double BesselDistributionGenerator::chi2(double a, int nu)
552  {
553  return mu(a, nu) + (1. / 4.) * a * a * R(a, nu) * (R(a,nu+1) - R(a,nu));
554  }
555 
556  double BesselDistributionGenerator::sig2(double a, int nu)
557  {
558  double tmp = m(a, nu) - mu(a, nu);
559  return chi2(a, nu) + tmp * tmp;
560  }
561 
562  double BesselDistributionGenerator::Q2(double a, int nu)
563  {
564  double A = sqrt(a*a + nu*nu);
565  double B = sqrt(a*a + (nu+1.)*(nu+1.));
566  double tmp = 1. + a * a * (1. + B - A) / 2. / (nu + A) / (nu + 1. + B);
567  return a*a / 2. / (nu + A) + tmp * tmp;
568  }
569 
570  int BesselDistributionGenerator::RandomBesselPoisson(double a, int nu, MTRand & rangen)
571  {
572  if (nu < 0 || a <= 0.) return 0;
573 
574  while (true) {
575  int n1 = RandomPoisson(a / 2., rangen);
576  int n2 = RandomPoisson(a / 2., rangen);
577  if (n1 - n2 == nu)
578  return n2;
579  }
580 
581  return 0;
582  }
583 
584  //double BesselDistributionGenerator::pmXmOverpm(int X, int tm, double a, int nu) {
585  // double tf1 = 1., tf2 = 1.;
586  // if (X > 0) {
587  // for (int i = 1; i <= X; ++i) {
588  // tf1 *= tm + i;
589  // tf2 *= tm + nu + i;
590  // }
591  // }
592  // else if (X < 0) {
593  // for (int i = 1; i <= -X; ++i) {
594  // tf1 *= tm + X + i;
595  // tf2 *= tm + nu + X + i;
596  // }
597  // tf1 = 1. / tf1;
598  // tf2 = 1. / tf2;
599  // }
600 
601  // return pow(a / 2., 2 * X) / tf1 / tf2;
602  //}
603 
604  double BesselDistributionGenerator::pmXmOverpm(int X, int tm, double a, int nu) {
605  double ret = 1.;
606  if (X > 0) {
607  for (int i = 1; i <= X; ++i) {
608  ret *= (a / 2.)/(tm + i);
609  ret *= (a / 2.)/(tm + nu + i);
610  }
611  }
612  else if (X < 0) {
613  for (int i = 1; i <= -X; ++i) {
614  ret *= (tm + X + i) / (a / 2.);
615  ret *= (tm + nu + X + i) / (a / 2.);
616  }
617  }
618 
619  return ret;
620  }
621 
622  int BesselDistributionGenerator::RandomBesselDevroye1(double a, int nu, MTRand & rangen)
623  {
624  int tm = m(a, nu);
625  double pm = pn(tm, a, nu);
626  double w = 1. + pm / 2.;
627  while (true) {
628  double U = rangen.rand();
629  double W = rangen.rand();
630  int S = 1;
631  if (rangen.rand() < 0.5)
632  S = -1;
633 
634  double Y = 0.;
635  if (U <= w / (1. + w)) {
636  double V = rangen.rand();
637  Y = V * w / pm;
638  }
639  else {
640  double E = -log(rangen.randDblExc());
641  Y = (w + E) / pm;
642  }
643  #if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
644  int X = S * std::lround(Y);
645  #else
646  int X = S * ((Y > 0.0) ? std::floor(Y + 0.5) : std::ceil(Y - 0.5));
647  #endif
648 
649  if (tm + X < 0)
650  continue;
651 
652  double pratio = pmXmOverpm(X, tm, a, nu);
653 
654  if (pratio != pratio) {
655  printf("**WARNING** BesselDistributionGenerator::RandomBesselDevroye1: Float problem!");
656  continue;
657  }
658 
659  if (W * std::min(1., exp(w - pm * Y)) <= pratio)
660  return tm + X;
661  }
662  return 0;
663  }
664 
665  int BesselDistributionGenerator::RandomBesselDevroye2(double a, int nu, MTRand & rangen)
666  {
667  int tm = m(a, nu);
668  double tsig = sqrt(sig2(a, nu));
669  double q = std::min(1. / tsig / sqrt(648.), 1. / 3.);
670  while (true) {
671  double U = rangen.rand();
672  double W = rangen.rand();
673  int S = 1;
674  if (rangen.rand() < 0.5)
675  S = -1;
676 
677  double Y = 0.;
678  if (U <= (1. + 2. / q) / (1. + 4. / q)) {
679  double V = rangen.rand();
680  Y = V * (1. / 2. + 1. / q);
681  }
682  else {
683  double E = -log(rangen.randDblExc());
684  Y = 1. / 2. + 1. / q + E / q;
685  }
686  #if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
687  int X = S * std::lround(Y);
688  #else
689  int X = S * ((Y > 0.0) ? std::floor(Y + 0.5) : std::ceil(Y - 0.5));
690  #endif
691 
692  if (tm + X < 0)
693  continue;
694 
695  double pratio = pmXmOverpm(X, tm, a, nu);
696 
697  if (pratio != pratio) {
698  printf("**WARNING** BesselDistributionGenerator::RandomBesselDevroye2: Float problem!");
699  continue;
700  }
701 
702  if (W * std::min(1., exp(1. + q/2. - q*Y)) <= pratio)
703  return tm + X;
704  }
705  return 0;
706  }
707 
708  int BesselDistributionGenerator::RandomBesselDevroye3(double a, int nu, MTRand & rangen)
709  {
710  int tm = m(a, nu);
711  double tQ = sqrt(Q2(a, nu));
712  double q = std::min(1. / tQ / sqrt(648.), 1. / 3.);
713  while (true) {
714  double U = rangen.rand();
715  double W = rangen.rand();
716  int S = 1;
717  if (rangen.rand() < 0.5)
718  S = -1;
719 
720  double Y = 0.;
721  if (U <= (1. + 2. / q) / (1. + 4. / q)) {
722  double V = rangen.rand();
723  Y = V * (1. / 2. + 1. / q);
724  }
725  else {
726  double E = -log(rangen.randDblExc());
727  Y = 1. / 2. + 1. / q + E / q;
728  }
729  #if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
730  int X = S * std::lround(Y);
731  #else
732  int X = S * ((Y > 0.0) ? std::floor(Y + 0.5) : std::ceil(Y - 0.5));
733  #endif
734 
735  if (tm + X < 0)
736  continue;
737 
738  double pratio = pmXmOverpm(X, tm, a, nu);
739 
740  if (pratio != pratio) {
741  printf("**WARNING** BesselDistributionGenerator::RandomBesselDevroye3: Float problem!");
742  continue;
743  }
744 
745 
746  if (W * std::min(1., exp(1. + q/2. - q*Y)) <= pratio)
747  return tm + X;
748  }
749  return 0;
750  }
751 
752  int BesselDistributionGenerator::RandomBesselNormal(double a, int nu, MTRand & rangen)
753  {
754  double ret = -1.;
755  while (ret <= -0.5) {
756  ret = rangen.randNorm(mu(a,nu), sqrt(chi2(a,nu)));
757  }
758  return static_cast<int>(ret + 0.5);
759  }
760 
761  int BesselDistributionGenerator::RandomBesselCombined(double a, int nu, MTRand & rangen)
762  {
763  int tm = m(a, nu);
764  if (tm < 6) {
765  if (nu <= a)
766  return RandomBesselPoisson(a, nu, rangen);
767  else
768  return RandomBesselDevroye3(a, nu, rangen);
769  }
770  else
771  return RandomBesselNormal(a, nu, rangen);
772  }
773 
774  std::vector<double> SiemensRasmussenMomentumGeneratorGeneralized::GetMomentum(double mass) const
775  {
776  if (mass < 0.)
777  mass = GetMass();
778 
779  double ph = 2. * xMath::Pi() * RandomGenerators::randgenMT.rand();
780  double costh = 2. * RandomGenerators::randgenMT.rand() - 1.;
781  double sinth = sqrt(1. - costh * costh);
782 
783  double vx = GetBeta() * sinth * cos(ph);
784  double vy = GetBeta() * sinth * sin(ph);
785  double vz = GetBeta() * costh;
786 
787  SimpleParticle part(0., 0., 0., mass, 0);
788 
789  double tp = m_Generator.GetP(mass);
790  double tphi = 2. * xMath::Pi() * RandomGenerators::randgenMT.rand();
791  double cthe = 2. * RandomGenerators::randgenMT.rand() - 1.;
792  double sthe = sqrt(1. - cthe * cthe);
793  part.px = tp * cos(tphi) * sthe;
794  part.py = tp * sin(tphi) * sthe;
795  part.pz = tp * cthe;
796  part.p0 = sqrt(mass * mass + tp * tp);
797 
798  part = ParticleDecaysMC::LorentzBoost(part, -vx, -vy, -vz);
799 
800  std::vector<double> ret(3);
801  ret[0] = part.px;
802  ret[1] = part.py;
803  ret[2] = part.pz;
804 
805  return ret;
806  }
807 
808 }
809 
810 } // namespace thermalfist
811 
812 
814 // Deprecated code below
815 
816 namespace thermalfist {
817 
818  namespace RandomGenerators {
819 
820  void SSHMomentumGenerator::FixParameters() {
821  {
822  double eps = 1e-8;
823  double l = 0., r = 1.;
824  double m1 = l + (r - l) / 3.;
825  double m2 = r - (r - l) / 3.;
826  int MAXITERS = 200;
827  int iter = 0;
828  while (fabs(m2 - m1) > eps && iter < MAXITERS) {
829  if (g(m1) < g(m2)) {
830  l = m1;
831  }
832  else {
833  r = m2;
834  }
835  m1 = l + (r - l) / 3.;
836  m2 = r - (r - l) / 3.;
837  iter++;
838  }
839  m_MaxPt = g((m1 + m2) / 2.);
840 
841  m_dndpt.clear();
842  double dx = m_dPt;
843  for (double x = 0.5 * dx; x <= 1.; x += dx) {
844  m_dndpt.add_val(x, g(x));
845  }
846  }
847 
848  {
849  m_dndy.resize(0);
850  m_MaxYs.resize(0);
851  double dx = m_dPt;
852  for (double x = 0.5 * dx; x <= 1.; x += dx) {
853 
854  double pt = -log(x);
855 
856  double eps = 1e-8;
857  double l = -4. - m_EtaMax, r = 4. + m_EtaMax;
858  double m1 = l + (r - l) / 3.;
859  double m2 = r - (r - l) / 3.;
860  int MAXITERS = 200;
861  int iter = 0;
862  while (fabs(m2 - m1) > eps && iter < MAXITERS) {
863  if (m_distr.dndy(m1, pt) < m_distr.dndy(m2, pt)) {
864  l = m1;
865  }
866  else {
867  r = m2;
868  }
869  m1 = l + (r - l) / 3.;
870  m2 = r - (r - l) / 3.;
871  iter++;
872  }
873  m_MaxYs.push_back(m_distr.dndy((m1 + m2) / 2., pt));
874 
875  m_dndy.push_back(SplineFunction());
876  double dy = m_dy;
877  for (double ty = -4. - m_EtaMax + 0.5 * dy; ty <= 4. + m_EtaMax; ty += dy) {
878  m_dndy[m_dndy.size() - 1].add_val(ty, m_distr.dndy(ty, pt));
879  }
880  }
881  }
882  }
883 
884  void SSHMomentumGenerator::FixParameters2() {
885  {
886  double eps = 1e-8;
887  double l = 0., r = 1.;
888  double m1 = l + (r - l) / 3.;
889  double m2 = r - (r - l) / 3.;
890  int MAXITERS = 200;
891  int iter = 0;
892  while (fabs(m2 - m1) > eps && iter < MAXITERS) {
893  if (g(m1) < g(m2)) {
894  l = m1;
895  }
896  else {
897  r = m2;
898  }
899  m1 = l + (r - l) / 3.;
900  m2 = r - (r - l) / 3.;
901  iter++;
902  }
903  m_MaxPt = g((m1 + m2) / 2.);
904 
905  m_dndpt.clear();
906  double dx = m_dPt;
907  for (double x = 0.5 * dx; x <= 1.; x += dx) {
908  m_dndpt.add_val(x, g(x));
909  }
910  }
911 
912  {
913  m_dndy.resize(0);
914  m_MaxYs.resize(0);
915  double dx = m_dPt;
916  for (double x = 0.5 * dx; x <= 1.; x += dx) {
917 
918  double pt = -log(x);
919 
920  double eps = 1e-8;
921  double l = -4. - m_EtaMax, r = 4. + m_EtaMax;
922  double m1 = l + (r - l) / 3.;
923  double m2 = r - (r - l) / 3.;
924  int MAXITERS = 200;
925  int iter = 0;
926  while (fabs(m2 - m1) > eps && iter < MAXITERS) {
927  if (m_distr.dndysingle(m1, pt) < m_distr.dndysingle(m2, pt)) {
928  l = m1;
929  }
930  else {
931  r = m2;
932  }
933  m1 = l + (r - l) / 3.;
934  m2 = r - (r - l) / 3.;
935  iter++;
936  }
937  m_MaxYs.push_back(m_distr.dndysingle((m1 + m2) / 2., pt));
938 
939  m_dndy.push_back(SplineFunction());
940  double dy = m_dy;
941  for (double ty = -4. + 0.5 * dy; ty <= 4.; ty += dy) {
942  m_dndy[m_dndy.size() - 1].add_val(ty, m_distr.dndysingle(ty, pt));
943  }
944  }
945  }
946 
947  }
948 
949  void SSHMomentumGenerator::FindMaximumPt() {
950 
951  double eps = 1e-8;
952  double l = 0., r = 1.;
953  double m1 = l + (r - l) / 3.;
954  double m2 = r - (r - l) / 3.;
955  int MAXITERS = 200;
956  int iter = 0;
957  while (fabs(m2 - m1) > eps && iter < MAXITERS) {
958  if (g(m1) < g(m2)) {
959  l = m1;
960  }
961  else {
962  r = m2;
963  }
964  m1 = l + (r - l) / 3.;
965  m2 = r - (r - l) / 3.;
966  iter++;
967  }
968  m_MaxPt = g((m1 + m2) / 2.);
969 
970  m_dndpt.clearall();
971  double dx = 0.05;
972  for (double x = 0.5 * dx; x <= 1.; x += dx) {
973  m_dndpt.add_val(x, g(x));
974  }
975  }
976 
977  void SSHMomentumGenerator::FindMaximumY(double pt) {
978  double eps = 1e-8;
979  double l = -4. - m_EtaMax, r = 4. + m_EtaMax;
980  double m1 = l + (r - l) / 3.;
981  double m2 = r - (r - l) / 3.;
982  int MAXITERS = 200;
983  int iter = 0;
984  while (fabs(m2 - m1) > eps && iter < MAXITERS) {
985  if (m_distr.dndy(m1, pt) < m_distr.dndy(m2, pt)) {
986  l = m1;
987  }
988  else {
989  r = m2;
990  }
991  m1 = l + (r - l) / 3.;
992  m2 = r - (r - l) / 3.;
993  iter++;
994  }
995  m_MaxY = m_distr.dndy((m1 + m2) / 2., pt);
996  }
997 
998  void SSHMomentumGenerator::FindMaximumY2(double pt) {
999  double eps = 1e-8;
1000  double l = -4. - m_EtaMax, r = 4. + m_EtaMax;
1001  double m1 = l + (r - l) / 3.;
1002  double m2 = r - (r - l) / 3.;
1003  int MAXITERS = 200;
1004  int iter = 0;
1005  while (fabs(m2 - m1) > eps && iter < MAXITERS) {
1006  if (m_distr.dndysingle(m1, pt) < m_distr.dndysingle(m2, pt)) {
1007  l = m1;
1008  }
1009  else {
1010  r = m2;
1011  }
1012  m1 = l + (r - l) / 3.;
1013  m2 = r - (r - l) / 3.;
1014  iter++;
1015  }
1016  m_MaxY = m_distr.dndysingle((m1 + m2) / 2., pt);
1017  }
1018 
1019  std::pair<double, double> SSHMomentumGenerator::GetRandom(double mass) {
1020  double tpt = 0., ty = 0.;
1021  while (1) {
1022  double x0 = randgenMT.randDblExc();
1023  double y0 = m_MaxPt * randgenMT.randDblExc();
1024  if (y0 < g2(x0)) {
1025  tpt = -log(x0);
1026  break;
1027  }
1028  }
1029  while (1) {
1030  int ind = (int)(exp(-tpt) / m_dPt);
1031  if (ind < 0) ind = 0;
1032  if (ind >= static_cast<int>(m_dndy.size())) ind = m_dndy.size() - 1;
1033  double x0 = -4. - m_EtaMax + (8. + 2. * m_EtaMax) * randgenMT.randDblExc();
1034  double y0 = m_MaxYs[ind] * randgenMT.randDblExc();
1035  if (y0 < m_dndy[ind].f(x0)) {
1036  ty = x0;
1037  break;
1038  }
1039  }
1040  return std::make_pair(tpt, ty);
1041  }
1042 
1043  std::pair<double, double> SSHMomentumGenerator::GetRandom2(double mass) const {
1044  double tpt = 0., ty = 0., teta = 0.;
1045  while (1) {
1046  double x0 = randgenMT.randDblExc();
1047  double y0 = m_MaxPt * randgenMT.randDblExc();
1048  if (y0 < g2(x0)) {
1049  tpt = -log(x0);
1050  break;
1051  }
1052  }
1053  while (1) {
1054  int ind = (int)(exp(-tpt) / m_dPt);
1055  if (ind < 0) ind = 0;
1056  if (ind >= static_cast<int>(m_dndy.size())) ind = m_dndy.size() - 1;
1057  double x0 = -4. + (8.) * randgenMT.randDblExc();
1058  double y0 = m_MaxYs[ind] * randgenMT.randDblExc();
1059 
1060  if (y0 < m_dndy[ind].f(x0)) {
1061  ty = x0;
1062  teta = -m_EtaMax + 2. * m_EtaMax * randgenMT.randDblExc();
1063  break;
1064  }
1065  }
1066  return std::make_pair(tpt, ty - teta);
1067  }
1068 
1069  std::vector<double> SSHMomentumGenerator::GetMomentum(double mass) const {
1070  std::vector<double> ret(0);
1071  std::pair<double, double> pty = GetRandom2(mass);
1072  double tpt = pty.first;
1073  double ty = pty.second;
1074  double tphi = 2. * xMath::Pi() * randgenMT.rand();
1075  ret.push_back(tpt * cos(tphi)); //px
1076  ret.push_back(tpt * sin(tphi)); //py
1077  ret.push_back(sqrt(tpt * tpt + m_Mass * m_Mass) * sinh(ty)); //pz
1078  return ret;
1079  }
1080 
1081  }
1082 
1083 } // namespace thermalfist
int RandomPoisson(double mean)
Generates random integer distributed by Poisson with specified mean Uses randgenMT.
virtual void FixParameters()
Computes some auxiliary stuff needed for sampling.
double Pi()
Pi constant.
Definition: xMath.h:24
virtual double tanhetaperp(double zeta) const
static int RandomBesselCombined(double a, int nu, MTRand &rangen)
Class implementing a simple linear spline.
virtual double f(double M) const
Unnormalized resonance mass probability density.
static int RandomBesselDevroye2(double a, int nu, MTRand &rangen)
virtual double ZetaProbability(double zeta) const
Proportional to probability of having given value.
static int RandomBesselDevroye1(double a, int nu, MTRand &rangen)
void SetParameters(ThermalParticle *part, double T, double Mu)
Sets the parameters of the distribution.
virtual double dtaudZeta(double zeta) const
d/d
double LogGamma(double)
Definition: xMath.cpp:964
double BesselIexp(int n, double x)
Definition: xMath.cpp:795
Contains some extra mathematical functions used in the code.
void SetSeed(const unsigned int seed)
Set the seed of the random number generator randgenMT.
double BesselI(int n, double x)
integer order modified Bessel function I_n(x)
Definition: xMath.cpp:191
virtual double InverseZetaDistribution(double xi) const
Inverse function of variable distribution used in random number generation.
virtual double f(double M) const
Unnormalized resonance mass probability density.
MTRand randgenMT
The Mersenne Twister random number generator.
Class containing all information about a particle specie.
static int RandomBesselNormal(double a, int nu, MTRand &rangen)
virtual std::vector< double > GetMomentum(double mass=-1.) const
Structure holding information about a single particle in the event generator.
virtual void FixParameters()
Computes some auxiliary stuff needed for sampling.
virtual ~BoostInvariantMomentumGenerator()
BoostInvariantMomentumGenerator desctructor.
double SkellamProbability(int k, double mu1, double mu2)
Probability of a Skellam distributed random variable with Poisson means mu1 and mu2 to have the value...
virtual std::vector< double > GetMomentum(double mass=-1.) const
BoostInvariantMomentumGenerator(BoostInvariantFreezeoutParametrization *FreezeoutModel=NULL, double Tkin=0.100, double etamax=3.0, double mass=0.938, int statistics=0, double mu=0)
Construct a new BoostInvariantMomentumGenerator object.
std::vector< double > GetMomentum(double mass=-1.) const
virtual std::vector< double > GetMomentum(double mass=-1.) const
virtual double dRdZeta(double zeta) const
dR/d
double GetRandom() const
Samples the resonance mass from a relativistic Breit-Wigner distribution with a constant width...
Base class implementing a longitudinally boost-invariant azimuthally symmetric freeze-out parametriza...
double p0
Energy (in GeV)
double pz
3-momentum components (in GeV)
virtual bool InverseZetaDistributionIsExplicit() const
Samples zeta for use in Monte Carlo event generator.
virtual double GetRandomZeta(MTRand &rangen=RandomGenerators::randgenMT) const
Samples zeta for use in Monte Carlo event generator.
virtual double sinhetaperp(double zeta) const
static double pmXmOverpm(int X, int tm, double a, int nu)
static int RandomBesselPoisson(double a, int nu, MTRand &rangen)
double GetP(double mass=-1.) const
Samples the momentum of a particle.
virtual double coshetaperp(double zeta) const
SimpleParticle LorentzBoost(const SimpleParticle &part, double vx, double vy, double vz)
Lorentz boost of the 4-momentum of a particle.
The main namespace where all classes and functions of the Thermal-FIST library reside.
static int RandomBesselDevroye3(double a, int nu, MTRand &rangen)
void SetParameters(double M, double gamma, double mthr)
Set the Breit-Wigner spectral function parameters.