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