Version: SMASH-1.6
distributions.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2013-2019
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 #include "smash/distributions.h"
10 
11 #include <gsl/gsl_sf_bessel.h>
12 #include <cmath>
13 #include <cstdio>
14 #include <cstdlib>
15 
16 #include "smash/constants.h"
17 #include "smash/logging.h"
18 #include "smash/random.h"
19 
20 namespace smash {
21 
22 // Relativistic Breit-Wigner distribution
23 double breit_wigner(const double m, const double pole, const double width) {
24  const double msqr = m * m;
25  const double dmsqr = msqr - pole * pole;
26  return 2. * msqr * width / (M_PI * (dmsqr * dmsqr + msqr * width * width));
27 }
28 
29 // Non-relativistic Breit-Wigner distribution
30 double breit_wigner_nonrel(double m, double pole, double width) {
31  return cauchy(m, pole, width / 2.);
32 }
33 
34 // Cauchy distribution used in non-relativistic Breit-Wigner above
35 double cauchy(double x, double pole, double width) {
36  const double dm = x - pole;
37  return width / (M_PI * (dm * dm + width * width));
38 }
39 
40 // density_integrand - Maxwell-Boltzmann distribution
41 double density_integrand(const double energy, const double momentum_sqr,
42  const double temperature) {
43  return 4.0 * M_PI * momentum_sqr * std::exp(-energy / temperature);
44 }
45 
46 double density_integrand_mass(const double energy, const double momentum_sqr,
47  const double temperature) {
48  return momentum_sqr * std::sqrt(momentum_sqr) *
49  std::exp(-energy / temperature);
50 }
51 
52 double density_integrand_1M_IC(const double energy, const double momentum_sqr,
53  const double temperature) {
54  return ((3.0 / 20.0) * (momentum_sqr / (temperature * temperature)) -
55  (6.0 / 5.0) * (energy / temperature) + (14.0 / 5.0)) *
56  std::exp(-energy / temperature) * momentum_sqr;
57 }
58 
59 double density_integrand_2M_IC(const double energy, const double momentum_sqr,
60  const double temperature) {
61  return (0.75 + 0.125 * (momentum_sqr / (temperature * temperature)) -
62  (1.0 / 30.0) * (momentum_sqr * energy /
63  (temperature * temperature * temperature)) +
64  (1.0 / 480.0) *
65  (momentum_sqr * momentum_sqr /
66  (temperature * temperature * temperature * temperature))) *
67  std::exp(-energy / temperature) * momentum_sqr;
68 }
69 
70 double juttner_distribution_func(const double momentum_radial,
71  const double mass, const double temperature,
72  const double baryon_chemical_potential,
73  const double lam) {
74  return 1.0 /
75  (std::exp((std::sqrt(momentum_radial * momentum_radial + mass * mass) -
76  baryon_chemical_potential) /
77  temperature) +
78  lam);
79 }
80 
81 double sample_momenta_non_eq_mass(const double temperature, const double mass) {
82  const auto &log = logger<LogArea::Distributions>();
83  log.debug("Sample momenta with mass ", mass, " and T ", temperature);
84 
85  /* Calculate range on momentum values to use
86  * ideally 0.0 and as large as possible but we want to be efficient! */
87  const double mom_min = 0.0;
88  const double mom_max =
89  std::sqrt(50. * 50. * temperature * temperature - mass * mass);
90  /* Calculate momentum and energy values that will give
91  * maxima of density_integrand_mass, verified by differentiation */
92  const double p_non_eq_sq =
93  0.5 * (9 * temperature * temperature +
94  temperature *
95  std::sqrt(81 * temperature * temperature + 36 * mass * mass));
96  const double e_non_eq = std::sqrt(p_non_eq_sq + mass * mass);
97  const double probability_max =
98  2. * density_integrand_mass(e_non_eq, p_non_eq_sq, temperature);
99 
100  /* sample by rejection method: (see numerical recipes for more efficient)
101  * random momenta and random probability need to be below the distribution */
102  double energy, momentum_radial, probability;
103  do {
104  // sample uniformly in momentum, DONT sample uniformly in energy!
105  momentum_radial = random::uniform(mom_min, mom_max);
106  // Energy by on-shell condition
107  energy = std::sqrt(momentum_radial * momentum_radial + mass * mass);
108  probability = density_integrand_mass(
109  energy, momentum_radial * momentum_radial, temperature);
110  } while (random::uniform(0., probability_max) > probability);
111 
112  return momentum_radial;
113 }
114 
115 double sample_momenta_1M_IC(const double temperature, const double mass) {
116  const auto &log = logger<LogArea::Distributions>();
117  log.debug("Sample momenta with mass ", mass, " and T ", temperature);
118  // Maxwell-Boltzmann average E <E>=3T + m * K_1(m/T) / K_2(m/T)
119  double energy_average;
120  if (mass > 0.) {
121  // massive particles
122  const double m_over_T = mass / temperature;
123  energy_average = 3 * temperature + mass * gsl_sf_bessel_K1(m_over_T) /
124  gsl_sf_bessel_Kn(2, m_over_T);
125  } else {
126  // massless particles
127  energy_average = 3 * temperature;
128  }
129  const double momentum_average_sqr =
130  (energy_average - mass) * (energy_average + mass);
131  const double energy_min = mass;
132  const double energy_max = 50. * temperature;
133  /* 16 * the massless peak value to be well above maximum of the
134  * distribution */
135  const double probability_max =
136  16. * density_integrand_1M_IC(energy_average, momentum_average_sqr,
137  temperature);
138 
139  /* sample by rejection method: (see numerical recipes for more efficient)
140  * random momenta and random probability need to be below the distribution */
141  double momentum_radial_sqr, probability;
142  do {
143  double energy = random::uniform(energy_min, energy_max);
144  momentum_radial_sqr = (energy - mass) * (energy + mass);
145  probability =
146  density_integrand_1M_IC(energy, momentum_radial_sqr, temperature);
147  } while (random::uniform(0., probability_max) > probability);
148 
149  return std::sqrt(momentum_radial_sqr);
150 }
151 
152 double sample_momenta_2M_IC(const double temperature, const double mass) {
153  const auto &log = logger<LogArea::Distributions>();
154  log.debug("Sample momenta with mass ", mass, " and T ", temperature);
155  /* Maxwell-Boltzmann average E <E>=3T + m * K_1(m/T) / K_2(m/T) */
156  double energy_average;
157  if (mass > 0.) {
158  // massive particles
159  const double m_over_T = mass / temperature;
160  energy_average = 3 * temperature + mass * gsl_sf_bessel_K1(m_over_T) /
161  gsl_sf_bessel_Kn(2, m_over_T);
162  } else {
163  // massless particles
164  energy_average = 3 * temperature;
165  }
166  const double momentum_average_sqr =
167  (energy_average - mass) * (energy_average + mass);
168  const double energy_min = mass;
169  const double energy_max = 50. * temperature;
170  /* 16 * the massless peak value to be well above maximum of the
171  * distribution */
172  const double probability_max =
173  16. * density_integrand_2M_IC(energy_average, momentum_average_sqr,
174  temperature);
175 
176  /* sample by rejection method: (see numerical recipes for more efficient)
177  * random momenta and random probability need to be below the distribution */
178  double momentum_radial_sqr, probability;
179  do {
180  double energy = random::uniform(energy_min, energy_max);
181  momentum_radial_sqr = (energy - mass) * (energy + mass);
182  probability =
183  density_integrand_2M_IC(energy, momentum_radial_sqr, temperature);
184  } while (random::uniform(0., probability_max) > probability);
185 
186  return std::sqrt(momentum_radial_sqr);
187 }
188 
189 /* A much faster sampler for thermal distribution from Scott
190  * (see \iref{Pratt:2014vja}) APPENDIX: ALGORITHM FOR GENERATING PARTICLES
191  * math trick: for \f$ x^{n-1}e^{-x} \f$ distribution, sample x by:
192  * \f$ x = -ln(r_1 r_2 r_3 ... r_n) \f$
193  * where \f$ r_i \f$ are uniform random numbers between [0,1)
194  * for \f$ T/m > 0.6 \f$: \f$ p^2 e^{-E/T} = p^2 e^{-p/T} * e^{(p-E)/T} \f$,
195  * where \f$ e^{(p-E)/T}\f$ is used as rejection weight.
196  * Since \f$T/m > 0.6 \f$, \f$ e^{(p-E)/T}\f$ is close to 1.
197  * for \f$ T/m < 0.6 \f$, there are many rejections
198  * another manipulation is used:
199  * \f$ p^2 e^{-E/T} dp = dE \frac{E}{p} p^2 e^{-E/T} \f$
200  * \f$ = dK \frac{p}{E} (K+m)^2 e^{-K/T} e^{-m/T} \f$
201  * \f$ = dK (K^2 + 2mK + m^2) e^{-K/T} \frac{p}{E}\f$
202  * where \frac{p}{E} is used as rejection weight.
203  * return: themal momenta */
204 double sample_momenta_from_thermal(const double temperature,
205  const double mass) {
206  const auto &log = logger<LogArea::Distributions>();
207  log.debug("Sample momenta with mass ", mass, " and T ", temperature);
208  double momentum_radial, energy;
209  // when temperature/mass
210  if (temperature > 0.6 * mass) {
211  while (true) {
212  const double a = -std::log(random::canonical_nonzero());
213  const double b = -std::log(random::canonical_nonzero());
214  const double c = -std::log(random::canonical_nonzero());
215  momentum_radial = temperature * (a + b + c);
216  energy = std::sqrt(momentum_radial * momentum_radial + mass * mass);
217  if (random::canonical() <
218  std::exp((momentum_radial - energy) / temperature)) {
219  break;
220  }
221  }
222  } else {
223  while (true) {
224  const double r0 = random::canonical();
225  const double I1 = mass * mass;
226  const double I2 = 2.0 * mass * temperature;
227  const double I3 = 2.0 * temperature * temperature;
228  const double Itot = I1 + I2 + I3;
229  double K;
230  if (r0 < I1 / Itot) {
231  const double r1 = random::canonical_nonzero();
232  K = -temperature * std::log(r1);
233  } else if (r0 < (I1 + I2) / Itot) {
234  const double r1 = random::canonical_nonzero();
235  const double r2 = random::canonical_nonzero();
236  K = -temperature * std::log(r1 * r2);
237  } else {
238  const double r1 = random::canonical_nonzero();
239  const double r2 = random::canonical_nonzero();
240  const double r3 = random::canonical_nonzero();
241  K = -temperature * std::log(r1 * r2 * r3);
242  }
243  energy = K + mass;
244  momentum_radial = std::sqrt((energy + mass) * (energy - mass));
245  if (random::canonical() < momentum_radial / energy) {
246  break;
247  }
248  }
249  }
250  return momentum_radial;
251 }
252 
253 double sample_momenta_IC_ES(const double temperature) {
254  double momentum_radial;
255  const double a = -std::log(random::canonical_nonzero());
256  const double b = -std::log(random::canonical_nonzero());
257  const double c = -std::log(random::canonical_nonzero());
258  const double d = -std::log(random::canonical_nonzero());
259  momentum_radial = (3.0 / 4.0) * temperature * (a + b + c + d);
260 
261  return momentum_radial;
262 }
263 
264 } // namespace smash
double juttner_distribution_func(const double momentum_radial, const double mass, const double temperature, const double baryon_chemical_potential, const double lam)
Relativistic Juttner distribution function.
double breit_wigner_nonrel(double m, double pole, double width)
Returns a non-relativistic Breit-Wigner distribution, which is essentially a Cauchy distribution with...
double sample_momenta_2M_IC(const double temperature, const double mass)
Samples a momentum from the non-equilibrium distribution 2M_IC from Bazow:2016oky ...
Collection of useful constants that are known at compile time.
double cauchy(double x, double pole, double width)
Returns a Cauchy distribution (sometimes also called Lorentz or non-relativistic Breit-Wigner distrib...
T canonical()
Definition: random.h:113
double sample_momenta_IC_ES(const double temperature)
Sample momenta according to the momentum distribution in Bazow:2016oky
double density_integrand_1M_IC(const double energy, const double momentum_sqr, const double temperature)
density integrand - 1M_IC massless particles for expanding metric initialization, see Bazow:2016oky ...
double density_integrand_2M_IC(const double energy, const double momentum_sqr, const double temperature)
density integrand - 2M_IC massless particles for expanding metric initialization, see Bazow:2016oky ...
double breit_wigner(double m, double pole, double width)
Returns a relativistic Breit-Wigner distribution.
double sample_momenta_from_thermal(const double temperature, const double mass)
Samples a momentum from the Maxwell-Boltzmann (thermal) distribution in a faster way, given by Scott Pratt.
double density_integrand(const double energy, const double momentum_sqr, const double temperature)
Returns the Maxwell-Boltzmann distribution.
T canonical_nonzero()
Definition: random.h:122
T uniform(T min, T max)
Definition: random.h:88
double sample_momenta_non_eq_mass(const double temperature, const double mass)
Samples a momentum via rejection method from the non-equilibrium distribution .
double sample_momenta_1M_IC(const double temperature, const double mass)
Samples a momentum from the non-equilibrium distribution 1M_IC from Bazow:2016oky ...
Definition: action.h:24
double density_integrand_mass(const double energy, const double momentum_sqr, const double temperature)
density_integrand_mass - off_equilibrium distribution for massive particles