Version: SMASH-3.1
hadgas_eos.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2016-2020,2022
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/hadgas_eos.h"
11 
12 #include <filesystem>
13 #include <fstream>
14 #include <iomanip>
15 #include <iostream>
16 
17 #include "gsl/gsl_sf_bessel.h"
18 
19 #include "smash/constants.h"
20 #include "smash/integrate.h"
21 #include "smash/interpolation.h"
22 #include "smash/logging.h"
23 #include "smash/random.h"
24 
25 namespace smash {
26 static constexpr int LResonances = LogArea::Resonances::id;
27 
28 EosTable::EosTable(double de, double dnb, double dq, size_t n_e, size_t n_nb,
29  size_t n_q)
30  : de_(de), dnb_(dnb), dq_(dq), n_e_(n_e), n_nb_(n_nb), n_q_(n_q) {
31  table_.resize(n_e_ * n_nb_ * n_q_);
32 }
33 
35  const std::string &eos_savefile_name) {
36  bool table_read_success = false, table_consistency = true;
37  if (std::filesystem::exists(eos_savefile_name)) {
38  std::cout << "Reading table from file " << eos_savefile_name << std::endl;
39  std::ifstream file;
40  file.open(eos_savefile_name, std::ios::in);
41  file >> de_ >> dnb_ >> dq_;
42  file >> n_e_ >> n_nb_ >> n_q_;
43  table_.resize(n_e_ * n_nb_ * n_q_);
44  for (size_t ie = 0; ie < n_e_; ie++) {
45  for (size_t inb = 0; inb < n_nb_; inb++) {
46  for (size_t iq = 0; iq < n_q_; iq++) {
47  double p, T, mub, mus, muq;
48  file >> p >> T >> mub >> mus >> muq;
49  table_[index(ie, inb, iq)] = {p, T, mub, mus, muq};
50  }
51  }
52  }
53  table_read_success = true;
54  std::cout << "Table consumed successfully." << std::endl;
55  }
56 
57  if (table_read_success) {
58  // Check if the saved table is consistent with the current particle table
59  std::cout << "Checking consistency of the table... " << std::endl;
60  constexpr size_t number_of_steps = 50;
61  const size_t ie_step = 1 + n_e_ / number_of_steps;
62  const size_t inb_step = 1 + n_nb_ / number_of_steps;
63  const size_t iq_step = 1 + n_q_ / number_of_steps;
64  for (size_t ie = 0; ie < n_e_; ie += ie_step) {
65  for (size_t inb = 0; inb < n_nb_; inb += inb_step) {
66  for (size_t iq = 0; iq < n_q_; iq += iq_step) {
67  const table_element x = table_[index(ie, inb, iq)];
68  const bool w = eos.account_for_resonance_widths();
69  const double e_comp = eos.energy_density(x.T, x.mub, x.mus, x.muq);
70  const double nb_comp =
71  eos.net_baryon_density(x.T, x.mub, x.mus, x.muq, w);
72  const double ns_comp =
73  eos.net_strange_density(x.T, x.mub, x.mus, x.muq, w);
74  const double p_comp = eos.pressure(x.T, x.mub, x.mus, x.muq, w);
75  const double nq_comp =
76  eos.net_charge_density(x.T, x.mub, x.mus, x.muq, w);
77  // Precision is just 10^-3, this is precision of saved data in the
78  // file
79  const double eps = 1.e-3;
80  // Only check the physical region, hence T > 0 condition
81  if ((std::abs(de_ * ie - e_comp) > eps ||
82  std::abs(dnb_ * inb - nb_comp) > eps ||
83  std::abs(ns_comp) > eps || std::abs(x.p - p_comp) > eps ||
84  std::abs(dq_ * iq - nq_comp) > eps) &&
85  (x.T > 0.0)) {
86  std::cout << "discrepancy: " << de_ * ie << " = " << e_comp << ", "
87  << dnb_ * inb << " = " << nb_comp << ", " << x.p << " = "
88  << p_comp << ", 0 = " << ns_comp << ", " << dq_ * iq
89  << " = " << nq_comp << std::endl;
90  table_consistency = false;
91  goto finish_consistency_check;
92  }
93  }
94  }
95  }
96  }
97 finish_consistency_check:
98 
99  if (!table_read_success || !table_consistency) {
100  std::cout << "Compiling an EoS table..." << std::endl;
101  const double ns = 0.0;
102  for (size_t ie = 0; ie < n_e_; ie++) {
103  std::cout << ie << "/" << n_e_ << "\r" << std::flush;
104  const double e = de_ * ie;
105  for (size_t inb = 0; inb < n_nb_; inb++) {
106  const double nb = dnb_ * inb;
107  for (size_t iq = 0; iq < n_q_; iq++) {
108  const double q = dq_ * iq;
109  // It is physically impossible to have energy density > nucleon
110  // mass*nb, therefore eqns have no solutions.
111  if (nb >= e || q >= e) {
112  table_[index(ie, inb, iq)] = {0.0, 0.0, 0.0, 0.0, 0.0};
113  continue;
114  }
115  // Take extrapolated (T, mub, mus, muq) as initial approximation
116  std::array<double, 4> init_approx;
117  if (inb >= 2) {
118  const table_element y = table_[index(ie, inb - 2, iq)];
119  const table_element x = table_[index(ie, inb - 1, iq)];
120  init_approx = {2.0 * x.T - y.T, 2.0 * x.mub - y.mub,
121  2.0 * x.mus - y.mus, 2.0 * x.muq - y.muq};
122  } else if (iq >= 2) {
123  const table_element y = table_[index(ie, inb, iq - 2)];
124  const table_element x = table_[index(ie, inb, iq - 1)];
125  init_approx = {2.0 * x.T - y.T, 2.0 * x.mub - y.mub,
126  2.0 * x.mus - y.mus, 2.0 * x.muq - y.muq};
127  } else {
128  init_approx = eos.solve_eos_initial_approximation(e, nb, q);
129  }
130  const std::array<double, 4> res =
131  eos.solve_eos(e, nb, ns, q, init_approx);
132  const double T = res[0];
133  const double mub = res[1];
134  const double mus = res[2];
135  const double muq = res[3];
136  const bool w = eos.account_for_resonance_widths();
137  table_[index(ie, inb, iq)] = {eos.pressure(T, mub, mus, muq, w), T,
138  mub, mus, muq};
139  }
140  }
141  }
142  // Save table to file
143  std::cout << "Saving table to file " << eos_savefile_name << std::endl;
144  std::ofstream file;
145  file.open(eos_savefile_name, std::ios::out);
146  file << de_ << " " << dnb_ << " " << dq_ << std::endl;
147  file << n_e_ << " " << n_nb_ << " " << n_q_ << std::endl;
148  file << std::setprecision(7);
149  file << std::fixed;
150  for (size_t ie = 0; ie < n_e_; ie++) {
151  for (size_t inb = 0; inb < n_nb_; inb++) {
152  for (size_t iq = 0; iq < n_q_; iq++) {
153  const EosTable::table_element x = table_[index(ie, inb, iq)];
154  file << x.p << " " << x.T << " " << x.mub << " " << x.mus << " "
155  << x.muq << std::endl;
156  }
157  }
158  }
159  }
160 }
161 
162 void EosTable::get(EosTable::table_element &res, double e, double nb,
163  double q) const {
164  const size_t ie = static_cast<size_t>(std::floor(e / de_));
165  const size_t inb = static_cast<size_t>(std::floor(nb / dnb_));
166  const size_t iq = static_cast<size_t>(std::floor(q / dq_));
167 
168  if (ie >= n_e_ - 1 || inb >= n_nb_ - 1 || iq >= n_q_ - 1) {
169  res = {-1.0, -1.0, -1.0, -1.0, -1.0};
170  } else {
171  // 1st order interpolation
172  const double ae = e / de_ - ie;
173  const double an = nb / dnb_ - inb;
174  const double aq = q / dq_ - iq;
175  const EosTable::table_element s1 = table_[index(ie, inb, iq)];
176  const EosTable::table_element s2 = table_[index(ie + 1, inb, iq)];
177  const EosTable::table_element s3 = table_[index(ie, inb + 1, iq)];
178  const EosTable::table_element s4 = table_[index(ie + 1, inb + 1, iq)];
179  const EosTable::table_element s5 = table_[index(ie, inb, iq + 1)];
180  const EosTable::table_element s6 = table_[index(ie + 1, inb, iq + 1)];
181  const EosTable::table_element s7 = table_[index(ie, inb + 1, iq + 1)];
182  const EosTable::table_element s8 = table_[index(ie + 1, inb + 1, iq + 1)];
183 
184  res.p = interpolate_trilinear(ae, an, aq, s1.p, s2.p, s3.p, s4.p, s5.p,
185  s6.p, s7.p, s8.p);
186  res.T = interpolate_trilinear(ae, an, aq, s1.T, s2.T, s3.T, s4.T, s5.T,
187  s6.T, s7.T, s8.T);
188  res.mub = interpolate_trilinear(ae, an, aq, s1.mub, s2.mub, s3.mub, s4.mub,
189  s5.mub, s6.mub, s7.mub, s8.mub);
190  res.mus = interpolate_trilinear(ae, an, aq, s1.mus, s2.mus, s3.mus, s4.mus,
191  s5.mus, s6.mus, s7.mus, s8.mus);
192  res.muq = interpolate_trilinear(ae, an, aq, s1.muq, s2.muq, s3.muq, s4.muq,
193  s5.muq, s6.muq, s7.muq, s8.muq);
194  }
195 }
196 
197 HadronGasEos::HadronGasEos(bool tabulate, bool account_for_width)
198  : x_(gsl_vector_alloc(n_equations_)),
199  tabulate_(tabulate),
200  account_for_resonance_widths_(account_for_width) {
201  const gsl_multiroot_fsolver_type *solver_type;
202  solver_type = gsl_multiroot_fsolver_hybrid;
203  solver_ = gsl_multiroot_fsolver_alloc(solver_type, n_equations_);
205  logg[LResonances].error(
206  "Compilation of hadron gas EoS table requested with"
207  " account of resonance spectral functions. This is not "
208  "advised, as it will likely take a few days to finish."
209  " Besides, the effect of resonance widths is currently not "
210  "implemented for energy density computation, so the computed"
211  " table will be inconsistent anyways.");
212  }
213  if (tabulate_) {
214  eos_table_.compile_table(*this);
215  }
216 }
217 
219  gsl_multiroot_fsolver_free(solver_);
220  gsl_vector_free(x_);
221 }
222 
224  double mu_over_T) {
225  double x = mu_over_T - m_over_T;
226  if (x < -500.0) {
227  return 0.0;
228  }
229  x = std::exp(x);
230  // In the case of small masses: K_n(z) -> (n-1)!/2 *(2/z)^n, z -> 0,
231  // z*z*K_2(z) -> 2
232  return (m_over_T < really_small)
233  ? 2.0 * x
234  : m_over_T * m_over_T * x * gsl_sf_bessel_Kn_scaled(2, m_over_T);
235 }
236 
238  double beta, double mub, double mus,
239  double muq,
240  bool account_for_width) {
241  const double m_over_T = ptype.mass() * beta;
242  double mu_over_T = beta * (ptype.baryon_number() * mub +
243  ptype.strangeness() * mus + ptype.charge() * muq);
244  const double g = ptype.spin() + 1;
245  if (ptype.is_stable() || !account_for_width) {
246  return g * scaled_partial_density_auxiliary(m_over_T, mu_over_T);
247  } else {
248  // Integral \int_{threshold}^{\infty} A(m) N_{thermal}(m) dm,
249  // where A(m) is the spectral function of the resonance.
250  const double m0 = ptype.mass();
251  const double w0 = ptype.width_at_pole();
252  const double mth = ptype.min_mass_spectral();
253  const double u_min = std::atan(2.0 * (mth - m0) / w0);
254  const double u_max = 0.5 * M_PI;
256  const double result =
257  g * integrate(u_min, u_max, [&](double u) {
258  // One of many possible variable substitutions. Not clear if it has
259  // any advantages, except transforming (m_th, inf) to finite interval.
260  const double tanu = std::tan(u);
261  const double m = m0 + 0.5 * w0 * tanu;
262  const double jacobian = 0.5 * w0 * (1.0 + tanu * tanu);
263  return ptype.spectral_function(m) * jacobian *
264  scaled_partial_density_auxiliary(m * beta, mu_over_T);
265  });
266  return result;
267  }
268 }
269 
270 double HadronGasEos::partial_density(const ParticleType &ptype, double T,
271  double mub, double mus, double muq,
272  bool account_for_width) {
273  if (T < really_small) {
274  return 0.0;
275  }
276  return prefactor_ * T * T * T *
277  scaled_partial_density(ptype, 1.0 / T, mub, mus, muq,
278  account_for_width);
279 }
280 
281 double HadronGasEos::energy_density(double T, double mub, double mus,
282  double muq) {
283  if (T < really_small) {
284  return 0.0;
285  }
286  const double beta = 1.0 / T;
287  double e = 0.0;
288  for (const ParticleType &ptype : ParticleType::list_all()) {
289  if (!is_eos_particle(ptype)) {
290  continue;
291  }
292  const double z = ptype.mass() * beta;
293  double x = beta * (mub * ptype.baryon_number() + mus * ptype.strangeness() +
294  muq * ptype.charge() - ptype.mass());
295  if (x < -500.0) {
296  return 0.0;
297  }
298  x = std::exp(x);
299  const size_t g = ptype.spin() + 1;
300  // Small mass case, z*z*K_2(z) -> 2, z*z*z*K_1(z) -> 0 at z->0
301  e += (z < really_small) ? 3.0 * g * x
302  : z * z * g * x *
303  (3.0 * gsl_sf_bessel_Kn_scaled(2, z) +
304  z * gsl_sf_bessel_K1_scaled(z));
305  }
306  e *= prefactor_ * T * T * T * T;
307  return e;
308 }
309 
310 double HadronGasEos::density(double T, double mub, double mus, double muq,
311  bool account_for_width) {
312  if (T < really_small) {
313  return 0.0;
314  }
315  const double beta = 1.0 / T;
316  double rho = 0.0;
317  for (const ParticleType &ptype : ParticleType::list_all()) {
318  if (!is_eos_particle(ptype)) {
319  continue;
320  }
321  rho +=
322  scaled_partial_density(ptype, beta, mub, mus, muq, account_for_width);
323  }
324  rho *= prefactor_ * T * T * T;
325  return rho;
326 }
327 
328 double HadronGasEos::net_baryon_density(double T, double mub, double mus,
329  double muq, bool account_for_width) {
330  if (T < really_small) {
331  return 0.0;
332  }
333  const double beta = 1.0 / T;
334  double rho = 0.0;
335  for (const ParticleType &ptype : ParticleType::list_all()) {
336  if (!ptype.is_baryon() || !is_eos_particle(ptype)) {
337  continue;
338  }
339  rho +=
340  scaled_partial_density(ptype, beta, mub, mus, muq, account_for_width) *
341  ptype.baryon_number();
342  }
343  rho *= prefactor_ * T * T * T;
344  return rho;
345 }
346 
347 double HadronGasEos::net_strange_density(double T, double mub, double mus,
348  double muq, bool account_for_width) {
349  if (T < really_small) {
350  return 0.0;
351  }
352  const double beta = 1.0 / T;
353  double rho = 0.0;
354  for (const ParticleType &ptype : ParticleType::list_all()) {
355  if (ptype.strangeness() == 0 || !is_eos_particle(ptype)) {
356  continue;
357  }
358  rho +=
359  scaled_partial_density(ptype, beta, mub, mus, muq, account_for_width) *
360  ptype.strangeness();
361  }
362  rho *= prefactor_ * T * T * T;
363  return rho;
364 }
365 
366 double HadronGasEos::net_charge_density(double T, double mub, double mus,
367  double muq, bool account_for_width) {
368  if (T < really_small) {
369  return 0.0;
370  }
371  const double beta = 1.0 / T;
372  double rho = 0.0;
373  for (const ParticleType &ptype : ParticleType::list_all()) {
374  if (ptype.charge() == 0 || !is_eos_particle(ptype)) {
375  continue;
376  }
377  rho +=
378  scaled_partial_density(ptype, beta, mub, mus, muq, account_for_width) *
379  ptype.charge();
380  }
381  rho *= prefactor_ * T * T * T;
382  return rho;
383 }
384 
386  double beta) {
387  if (ptype.is_stable()) {
388  return ptype.mass();
389  }
390  // Sampling mass m from A(m) x^2 BesselK_2(x), where x = beta m.
391  // Strategy employs the idea of importance sampling:
392  // -- Sample mass from the simple Breit-Wigner first, then
393  // reject by the ratio.
394  // -- Note that f(x) = x^2 BesselK_2(x) monotonously decreases
395  // and has maximum at x = xmin. That is why instead of f(x)
396  // the ratio f(x)/f(xmin) is used.
397 
398  const double max_mass = 5.0; // GeV
399  double m, q;
400  {
401  // Allow underflows in exponentials
402  DisableFloatTraps guard(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
403  const double w0 = ptype.width_at_pole();
404  const double mth = ptype.min_mass_spectral();
405  const double m0 = ptype.mass();
406  double max_ratio =
407  m0 * m0 * std::exp(-beta * m0) * gsl_sf_bessel_Kn_scaled(2, m0 * beta) *
408  ptype.spectral_function(m0) / ptype.spectral_function_simple(m0);
409  // Heuristic adaptive maximum search to find max_ratio
410  constexpr int npoints = 31;
411  double m_lower = mth, m_upper = max_mass, m_where_max = m0;
412 
413  for (size_t n_iterations = 0; n_iterations < 2; n_iterations++) {
414  const double dm = (m_upper - m_lower) / npoints;
415  for (size_t i = 1; i < npoints; i++) {
416  m = m_lower + dm * i;
417  const double thermal_factor =
418  m * m * std::exp(-beta * m) * gsl_sf_bessel_Kn_scaled(2, m * beta);
419  q = ptype.spectral_function(m) * thermal_factor /
420  ptype.spectral_function_simple(m);
421  if (q > max_ratio) {
422  max_ratio = q;
423  m_where_max = m;
424  }
425  }
426  m_lower = m_where_max - (m_where_max - m_lower) * 0.1;
427  m_upper = m_where_max + (m_upper - m_where_max) * 0.1;
428  }
429  // Safety factor
430  max_ratio *= 1.5;
431 
432  do {
433  // sample mass from A(m)
434  do {
435  m = random::cauchy(m0, 0.5 * w0, mth, max_mass);
436  const double thermal_factor =
437  m * m * std::exp(-beta * m) * gsl_sf_bessel_Kn_scaled(2, m * beta);
438  q = ptype.spectral_function(m) * thermal_factor /
439  ptype.spectral_function_simple(m);
440  } while (q < random::uniform(0., max_ratio));
441  if (q > max_ratio) {
442  logg[LResonances].warn(
443  ptype.name(), " - maximum increased in",
444  " sample_mass_thermal from ", max_ratio, " to ", q, ", mass = ", m,
445  " previously assumed maximum at m = ", m_where_max);
446  max_ratio = q;
447  m_where_max = m;
448  } else {
449  break;
450  }
451  } while (true);
452  }
453  return m;
454 }
455 
456 double HadronGasEos::mus_net_strangeness0(double T, double mub, double muq) {
457  // Binary search
458  double mus_u = mub + T;
459  double mus_l = 0.0;
460  double mus, rhos;
461  size_t iteration = 0;
462  // 50 iterations should give precision 2^-50 ~ 10^-15
463  const size_t max_iteration = 50;
464  do {
465  mus = 0.5 * (mus_u + mus_l);
466  rhos = net_strange_density(T, mub, mus, muq);
467  if (rhos > 0.0) {
468  mus_u = mus;
469  } else {
470  mus_l = mus;
471  }
472  iteration++;
473  } while (std::abs(rhos) > tolerance_ && iteration < max_iteration);
474  if (iteration == max_iteration) {
475  throw std::runtime_error("Solving rho_s = 0: too many iterations.");
476  }
477  return mus;
478 }
479 
480 int HadronGasEos::set_eos_solver_equations(const gsl_vector *x, void *params,
481  gsl_vector *f) {
482  double e = reinterpret_cast<struct rparams *>(params)->e;
483  double nb = reinterpret_cast<struct rparams *>(params)->nb;
484  double ns = reinterpret_cast<struct rparams *>(params)->ns;
485  double nq = reinterpret_cast<struct rparams *>(params)->nq;
486  bool w = reinterpret_cast<struct rparams *>(params)->account_for_width;
487 
488  const double T = gsl_vector_get(x, 0);
489  const double mub = gsl_vector_get(x, 1);
490  const double mus = gsl_vector_get(x, 2);
491  const double muq = gsl_vector_get(x, 3);
492 
493  gsl_vector_set(f, 0, energy_density(T, mub, mus, muq) - e);
494  gsl_vector_set(f, 1, net_baryon_density(T, mub, mus, muq, w) - nb);
495  gsl_vector_set(f, 2, net_strange_density(T, mub, mus, muq, w) - ns);
496  gsl_vector_set(f, 3, net_charge_density(T, mub, mus, muq, w) - nq);
497 
498  return GSL_SUCCESS;
499 }
500 
501 double HadronGasEos::e_equation(double T, void *params) {
502  const double edens = reinterpret_cast<struct eparams *>(params)->edens;
503  return edens - energy_density(T, 0.0, 0.0, 0.0);
504 }
505 
506 std::array<double, 4> HadronGasEos::solve_eos_initial_approximation(double e,
507  double nb,
508  double nq) {
509  assert(e >= 0.0);
510  // 1. Get temperature from energy density assuming zero chemical potentials
511  int degeneracies_sum = 0.0;
512  for (const ParticleType &ptype : ParticleType::list_all()) {
513  if (is_eos_particle(ptype)) {
514  degeneracies_sum += ptype.spin() + 1;
515  }
516  }
517  // Temperature in case of massless gas. For massive it should be larger.
518  const double T_min = std::pow(e / prefactor_ / 6 / degeneracies_sum, 1. / 4.);
519  // Simply assume that the temperature is not higher than 2 GeV.
520  const double T_max = 2.0;
521 
522  struct eparams parameters = {e};
523  gsl_function F = {&e_equation, &parameters};
524  const gsl_root_fsolver_type *T = gsl_root_fsolver_brent;
525  gsl_root_fsolver *e_solver;
526  e_solver = gsl_root_fsolver_alloc(T);
527  gsl_root_fsolver_set(e_solver, &F, T_min, T_max);
528 
529  int iter = 0, status, max_iter = 100;
530  double T_init = 0.0;
531 
532  do {
533  iter++;
534  status = gsl_root_fsolver_iterate(e_solver);
535  if (status != GSL_SUCCESS) {
536  break;
537  }
538  T_init = gsl_root_fsolver_root(e_solver);
539  double x_lo = gsl_root_fsolver_x_lower(e_solver);
540  double x_hi = gsl_root_fsolver_x_upper(e_solver);
541  status = gsl_root_test_interval(x_lo, x_hi, 0.0, 0.001);
542  } while (status == GSL_CONTINUE && iter < max_iter);
543 
544  if (status != GSL_SUCCESS) {
545  std::stringstream err_msg;
546  err_msg << "Solver of equation for temperature with e = " << e
547  << " failed to converge. Maybe Tmax = " << T_max << " is too small?"
548  << std::endl;
549  throw std::runtime_error(gsl_strerror(status) + err_msg.str());
550  }
551 
552  gsl_root_fsolver_free(e_solver);
553 
554  // 2. Get the baryon chemical potential for muS = muQ = 0 with previously
555  // obtained T
556  double n_only_baryons = 0.0;
557  for (const ParticleType &ptype : ParticleType::list_all()) {
558  if (is_eos_particle(ptype) && ptype.baryon_number() == 1) {
559  n_only_baryons +=
560  scaled_partial_density(ptype, 1.0 / T_init, 0.0, 0.0, 0.0);
561  }
562  }
563  const double nb_scaled = nb / prefactor_ / (T_init * T_init * T_init);
564  double mub_init = T_init * std::asinh(nb_scaled / n_only_baryons / 2.0);
565 
566  // 3. Get the charge chemical potential assuming muB = muS = 0 with previously
567  // obtained T
568  double n_only_charge_1_particles = 0.0;
569  for (const ParticleType &ptype : ParticleType::list_all()) {
570  if (is_eos_particle(ptype) && ptype.charge() == 1) {
571  n_only_charge_1_particles +=
572  scaled_partial_density(ptype, 1.0 / T_init, 0.0, 0.0, 0.0);
573  }
574  }
575  const double q_scaled = nq / prefactor_ / (T_init * T_init * T_init);
576  double muq_init =
577  T_init * std::asinh(q_scaled / n_only_charge_1_particles / 2.0);
578 
579  // 4. Get the strange chemical potential, where mus = 0 is typically a good
580  // initial approximation
581  std::array<double, 4> initial_approximation = {T_init, mub_init, 0.0,
582  muq_init};
583  return initial_approximation;
584 }
585 
586 std::array<double, 4> HadronGasEos::solve_eos(
587  double e, double nb, double ns, double nq,
588  std::array<double, 4> initial_approximation) {
589  int residual_status = GSL_SUCCESS;
590  size_t iter = 0;
591 
592  struct rparams p = {e, nb, ns, nq, account_for_resonance_widths_};
593  gsl_multiroot_function f = {&HadronGasEos::set_eos_solver_equations,
594  n_equations_, &p};
595 
596  gsl_vector_set(x_, 0, initial_approximation[0]);
597  gsl_vector_set(x_, 1, initial_approximation[1]);
598  gsl_vector_set(x_, 2, initial_approximation[2]);
599  gsl_vector_set(x_, 3, initial_approximation[3]);
600 
601  gsl_multiroot_fsolver_set(solver_, &f, x_);
602  do {
603  iter++;
604  const auto iterate_status = gsl_multiroot_fsolver_iterate(solver_);
605  // std::cout << print_solver_state(iter);
606 
607  // Avoiding too low temperature
608  if (gsl_vector_get(solver_->x, 0) < 0.015) {
609  return {0.0, 0.0, 0.0, 0.0};
610  }
611 
612  // check if solver is stuck
613  if (iterate_status) {
614  break;
615  }
616  residual_status = gsl_multiroot_test_residual(solver_->f, tolerance_);
617  } while (residual_status == GSL_CONTINUE && iter < 1000);
618 
619  if (residual_status != GSL_SUCCESS) {
620  std::stringstream solver_parameters;
621  solver_parameters << "\nSolver run with "
622  << "e = " << e << ", nb = " << nb << ", ns = " << ns
623  << ", nq = " << nq
624  << ", init. approx.: " << initial_approximation[0] << " "
625  << initial_approximation[1] << " "
626  << initial_approximation[2] << " "
627  << initial_approximation[3] << std::endl;
628  logg[LResonances].warn(gsl_strerror(residual_status) +
629  solver_parameters.str() + print_solver_state(iter));
630  }
631 
632  return {gsl_vector_get(solver_->x, 0), gsl_vector_get(solver_->x, 1),
633  gsl_vector_get(solver_->x, 2), gsl_vector_get(solver_->x, 3)};
634 }
635 
636 std::string HadronGasEos::print_solver_state(size_t iter) const {
637  std::stringstream s;
638  // clang-format off
639  s << "iter = " << iter << ","
640  << " x = " << gsl_vector_get(solver_->x, 0) << " "
641  << gsl_vector_get(solver_->x, 1) << " "
642  << gsl_vector_get(solver_->x, 2) << ", "
643  << gsl_vector_get(solver_->x, 3) << ", "
644  << "f(x) = " << gsl_vector_get(solver_->f, 0) << " "
645  << gsl_vector_get(solver_->f, 1) << " "
646  << gsl_vector_get(solver_->f, 2) << " "
647  << gsl_vector_get(solver_->f, 3) << std::endl;
648  // clang-format on
649  return s.str();
650 }
651 
652 } // namespace smash
Guard type that safely disables floating point traps for the scope in which it is placed.
Definition: fpenvironment.h:79
std::vector< table_element > table_
Storage for the tabulated equation of state.
Definition: hadgas_eos.h:97
void compile_table(HadronGasEos &eos, const std::string &eos_savefile_name="hadgas_eos.dat")
Computes the actual content of the table (for EosTable description see documentation of the construct...
Definition: hadgas_eos.cc:34
size_t index(size_t ie, size_t inb, size_t inq) const
proper index in a 1d vector, where the 3d table is stored
Definition: hadgas_eos.h:93
double dnb_
Step in net-baryon density.
Definition: hadgas_eos.h:101
size_t n_q_
Number of steps in net-charge density.
Definition: hadgas_eos.h:109
double de_
Step in energy density.
Definition: hadgas_eos.h:99
EosTable(double de, double dnb, double dq, size_t n_e, size_t n_b, size_t n_q)
Sets up a table p/T/muB/mus/muQ versus (e, nb, nq), where e - energy density, nb - net baryon density...
Definition: hadgas_eos.cc:28
size_t n_e_
Number of steps in energy density.
Definition: hadgas_eos.h:105
double dq_
Step in net-charge density.
Definition: hadgas_eos.h:103
void get(table_element &res, double e, double nb, double nq) const
Obtain interpolated p/T/muB/muS/muQ from the tabulated equation of state given energy density,...
Definition: hadgas_eos.cc:162
size_t n_nb_
Number of steps in net-baryon density.
Definition: hadgas_eos.h:107
Class to handle the equation of state (EoS) of the hadron gas, consisting of all hadrons included in ...
Definition: hadgas_eos.h:125
gsl_multiroot_fsolver * solver_
Definition: hadgas_eos.h:452
static double net_charge_density(double T, double mub, double mus, double muq, bool account_for_resonance_widths=false)
Compute net charge density.
Definition: hadgas_eos.cc:366
static constexpr double prefactor_
Constant factor, that appears in front of many thermodyn. expressions.
Definition: hadgas_eos.h:431
static double partial_density(const ParticleType &ptype, double T, double mub, double mus, double muq, bool account_for_resonance_widths=false)
Compute partial density of one hadron sort.
Definition: hadgas_eos.cc:270
static double sample_mass_thermal(const ParticleType &ptype, double beta)
Sample resonance mass in a thermal medium.
Definition: hadgas_eos.cc:385
std::array< double, 4 > solve_eos(double e, double nb, double ns, double nq, std::array< double, 4 > initial_approximation)
Compute temperature and chemical potentials given energy-, net baryon-, net strangeness- and net char...
Definition: hadgas_eos.cc:586
static double scaled_partial_density_auxiliary(double m_over_T, double mu_over_T)
Function used to avoid duplications in density calculations.
Definition: hadgas_eos.cc:223
EosTable eos_table_
EOS Table to be used.
Definition: hadgas_eos.h:441
gsl_vector * x_
Variables used by gnu equation solver.
Definition: hadgas_eos.h:449
static double mus_net_strangeness0(double T, double mub, double muq)
Compute strangeness chemical potential, requiring that net strangeness = 0.
Definition: hadgas_eos.cc:456
bool account_for_resonance_widths() const
If resonance spectral functions are taken into account.
Definition: hadgas_eos.h:363
static double density(double T, double mub, double mus, double muq, bool account_for_resonance_widths=false)
Compute particle number density.
Definition: hadgas_eos.cc:310
const bool tabulate_
Create an EoS table or not?
Definition: hadgas_eos.h:455
static bool is_eos_particle(const ParticleType &ptype)
Check if a particle belongs to the EoS.
Definition: hadgas_eos.h:355
static int set_eos_solver_equations(const gsl_vector *x, void *params, gsl_vector *f)
Interface EoS equations to be solved to gnu library.
Definition: hadgas_eos.cc:480
std::string print_solver_state(size_t iter) const
Helpful printout, useful for debugging if gnu equation solving goes crazy.
Definition: hadgas_eos.cc:636
static double e_equation(double T, void *params)
Definition: hadgas_eos.cc:501
static double net_baryon_density(double T, double mub, double mus, double muq, bool account_for_resonance_widths=false)
Compute net baryon density.
Definition: hadgas_eos.cc:328
static double energy_density(double T, double mub, double mus, double muq)
Compute energy density.
Definition: hadgas_eos.cc:281
static double net_strange_density(double T, double mub, double mus, double muq, bool account_for_resonance_widths=false)
Compute net strangeness density.
Definition: hadgas_eos.cc:347
const bool account_for_resonance_widths_
Use pole masses of resonances or integrate over spectral functions.
Definition: hadgas_eos.h:458
std::array< double, 4 > solve_eos_initial_approximation(double e, double nb, double nq)
Compute a reasonable initial approximation for solve_eos.
Definition: hadgas_eos.cc:506
static constexpr size_t n_equations_
Number of equations in the system of equations to be solved.
Definition: hadgas_eos.h:438
HadronGasEos(bool tabulate, bool account_for_widths)
Constructor of HadronGasEos.
Definition: hadgas_eos.cc:197
static double pressure(double T, double mub, double mus, double muq, bool account_for_resonance_widths=false)
Compute pressure .
Definition: hadgas_eos.h:195
static constexpr double tolerance_
Precision of equation solving.
Definition: hadgas_eos.h:435
static double scaled_partial_density(const ParticleType &ptype, double beta, double mub, double mus, double muq, bool account_for_width=false)
Compute (unnormalized) density of one hadron sort - helper functions used to reduce code duplication.
Definition: hadgas_eos.cc:237
A C++ interface for numerical integration in one dimension with the GSL CQUAD integration functions.
Definition: integrate.h:106
Particle type contains the static properties of a particle species.
Definition: particletype.h:98
double min_mass_spectral() const
The minimum mass of the resonance, where the spectral function is non-zero.
double spectral_function(double m) const
Full spectral function of the resonance (relativistic Breit-Wigner distribution with mass-dependent ...
int strangeness() const
Definition: particletype.h:213
const std::string & name() const
Definition: particletype.h:142
int32_t charge() const
The charge of the particle.
Definition: particletype.h:189
static const ParticleTypeList & list_all()
Definition: particletype.cc:51
bool is_stable() const
Definition: particletype.h:246
double spectral_function_simple(double m) const
This one is the most simple form of the spectral function, using a Cauchy distribution (non-relativis...
double width_at_pole() const
Definition: particletype.h:151
double mass() const
Definition: particletype.h:145
unsigned int spin() const
Definition: particletype.h:192
int baryon_number() const
Definition: particletype.h:210
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
constexpr int p
Proton.
T beta(T a, T b)
Draws a random number from a beta-distribution, where probability density of is .
Definition: random.h:329
T uniform(T min, T max)
Definition: random.h:88
T cauchy(T pole, T width, T min, T max)
Draws a random number from a Cauchy distribution (sometimes also called Lorentz or non-relativistic B...
Definition: random.h:307
Definition: action.h:24
static Integrator integrate
Definition: decaytype.cc:143
T interpolate_trilinear(T ax, T ay, T az, T f1, T f2, T f3, T f4, T f5, T f6, T f7, T f8)
Perform a trilinear 1st order interpolation.
static constexpr int LResonances
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:37
Define the data structure for one element of the table.
Definition: hadgas_eos.h:58
double mub
Net baryochemical potential.
Definition: hadgas_eos.h:64
double muq
Net charge chemical potential.
Definition: hadgas_eos.h:68
double T
Temperature.
Definition: hadgas_eos.h:62
double mus
Net strangeness potential.
Definition: hadgas_eos.h:66
Another structure for passing energy density to the gnu library.
Definition: hadgas_eos.h:383
A structure for passing equation parameters to the gnu library.
Definition: hadgas_eos.h:369