/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2019 Willem Elbers (whe@willemelbers.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
******************************************************************************/
/* Config parameters. */
#include
/* Some standard headers. */
#include
#include
/* Includes. */
#include "swift.h"
#define N_CHECK 20
#define TOLERANCE 1e-6
#define EXTERNAL_TOLERANCE 1e-5 // comparing against CLASS
void test_params_init(struct swift_params *params, int testnr) {
switch (testnr) {
/* One doubly degenerate light neutrino (total 0.10 eV) and one massless */
case 0:
parser_init("", params);
parser_set_param(params, "Cosmology:Omega_cdm:0.263069938");
parser_set_param(params, "Cosmology:Omega_lambda:6.853646e-01");
parser_set_param(params, "Cosmology:Omega_b:0.049198926683");
parser_set_param(params, "Cosmology:h:0.6737");
parser_set_param(params, "Cosmology:a_begin:1e-2");
parser_set_param(params, "Cosmology:a_end:1.0");
parser_set_param(params, "Cosmology:T_nu_0:1.95176");
parser_set_param(params, "Cosmology:N_nu:1");
parser_set_param(params, "Cosmology:N_ur:1.0196");
parser_set_param(params, "Cosmology:M_nu_eV:0.0486");
parser_set_param(params, "Cosmology:deg_nu:2");
break;
/* Three very massive neutrinos */
case 1:
parser_init("", params);
parser_set_param(params, "Cosmology:Omega_cdm:0.241989232");
parser_set_param(params, "Cosmology:Omega_lambda:0.685365");
parser_set_param(params, "Cosmology:Omega_b:0.049199");
parser_set_param(params, "Cosmology:h:0.6737");
parser_set_param(params, "Cosmology:a_begin:1e-2");
parser_set_param(params, "Cosmology:a_end:1.0");
parser_set_param(params, "Cosmology:N_nu:3");
parser_set_param(params, "Cosmology:M_nu_eV:0.2,0.3,0.4");
break;
/* One light massive neutrino (0.05 eV) + two massless */
case 2:
parser_init("", params);
parser_set_param(params, "Cosmology:Omega_cdm:0.261887181");
parser_set_param(params, "Cosmology:Omega_lambda:0.685365");
parser_set_param(params, "Cosmology:Omega_b:0.049199");
parser_set_param(params, "Cosmology:h:0.6737");
parser_set_param(params, "Cosmology:a_begin:1e-2");
parser_set_param(params, "Cosmology:a_end:1.0");
parser_set_param(params, "Cosmology:T_nu_0:1.95176");
parser_set_param(params, "Cosmology:N_ur:2");
parser_set_param(params, "Cosmology:N_nu:1");
parser_set_param(params, "Cosmology:M_nu_eV:0.05");
break;
/* Three very massive neutrinos (same as 1), but starting earlier */
case 3:
parser_init("", params);
parser_set_param(params, "Cosmology:Omega_cdm:0.241989232");
parser_set_param(params, "Cosmology:Omega_lambda:0.685365");
parser_set_param(params, "Cosmology:Omega_b:0.049199");
parser_set_param(params, "Cosmology:h:0.6737");
parser_set_param(params, "Cosmology:a_begin:1e-3");
parser_set_param(params, "Cosmology:a_end:1.0");
parser_set_param(params, "Cosmology:N_nu:3");
parser_set_param(params, "Cosmology:M_nu_eV:0.2,0.3,0.4");
break;
}
parser_set_param(params, "InternalUnitSystem:UnitMass_in_cgs:1.988435e+43");
parser_set_param(params,
"InternalUnitSystem:UnitLength_in_cgs:3.0856775815e+24");
parser_set_param(params,
"InternalUnitSystem:UnitVelocity_in_cgs:9.7846194238e+07");
parser_set_param(params, "InternalUnitSystem:UnitCurrent_in_cgs:1");
parser_set_param(params, "InternalUnitSystem:UnitTemp_in_cgs:1");
}
int main(int argc, char *argv[]) {
/* Load a CLASS data table of background quantities for cosmology 0 */
const int rows = 51;
const int cols = 3;
double CLASS_table[rows * cols];
FILE *stream = fopen(argv[1], "r");
if (stream == NULL) error("Could not open reference solution file!");
char line[1024];
int row = 0;
while (fgets(line, 1024, stream)) {
if (line[0] == '#') continue;
char *tmp = strdup(line);
char *ptr = NULL;
CLASS_table[0 + row * 3] = strtod(tmp, &ptr);
CLASS_table[1 + row * 3] = strtod(ptr + 1, &ptr);
CLASS_table[2 + row * 3] = strtod(ptr + 1, &ptr);
row++;
free(tmp);
}
fclose(stream);
/* Initialize CPU frequency, this also starts time. */
unsigned long long cpufreq = 0;
clocks_set_cpufreq(cpufreq);
/* Test a number of different cosmologies */
int N_cosmos = 4;
double times1[N_CHECK]; // compare t(a) for cosmology 1
double times2[N_CHECK]; // with t(a) for cosmology 3
for (int testnr = 0; testnr < N_cosmos; testnr++) {
message("Initialization of cosmology %i", testnr);
/* pseudo initialization of params */
struct swift_params params;
test_params_init(¶ms, testnr);
/* initialization of unit system */
struct unit_system us;
units_init_from_params(&us, ¶ms, "InternalUnitSystem");
/* initialization of phys_const */
struct phys_const phys_const;
phys_const_init(&us, ¶ms, &phys_const);
/* initialization of cosmo */
struct cosmology cosmo;
cosmology_init(¶ms, &us, &phys_const, &cosmo);
message("Start checking computation...");
for (int i = 0; i < N_CHECK; i++) {
double a = 0.1 + 0.9 * i / (N_CHECK - 1.);
/* Compute a(t(a)) and check if same results */
double time = cosmology_get_time_since_big_bang(&cosmo, a);
/* Store the value for cosmologies 1 and 3 to compare later */
if (testnr == 1) {
times1[i] = time;
} else if (testnr == 3) {
times2[i] = time;
}
double my_a = cosmology_get_scale_factor(&cosmo, time);
/* check accuracy */
double rel_err = (my_a - a) / a;
message("Accuracy of %g at a=%g", rel_err, a);
assert(fabs(rel_err) < TOLERANCE);
}
if (testnr == 0) {
/* For cosmology 0, compare against independently computed array */
message("Relative to CLASS values (a, t, Omega_nu(a))");
const double delta_a = (cosmo.log_a_end - cosmo.log_a_begin) / 10000;
for (int j = 0; j < 50; j++) {
double a1 = exp(cosmo.log_a_begin + delta_a * (j * 200 + 1));
double time1 = cosmology_get_time_since_big_bang(&cosmo, a1);
// double time1 = cosmo.time_interp_table[j*200] +
// cosmo.time_interp_table_offset;
double Onu1 = cosmology_get_neutrino_density(&cosmo, a1);
double a2 = CLASS_table[0 + 3 * j];
double time2 = CLASS_table[1 + 3 * j];
double Onu2 = CLASS_table[2 + 3 * j];
/* Class defines years as 365.25 days, we use 365 days (in this test).
*/
time2 *= 365.25 / 365.0;
assert(fabs(a1 - a2) / (a1 + a2) < EXTERNAL_TOLERANCE);
assert(fabs(time1 - time2) / (time1 + time2) < EXTERNAL_TOLERANCE);
assert(fabs(Onu1 - Onu2) / (Onu1 + Onu2) < EXTERNAL_TOLERANCE);
message("At a = %e: (%e, %e, %e)", a1, a1 / a2, time1 / time2,
Onu1 / Onu2);
}
}
message("Everything seems fine with this cosmology.");
cosmology_clean(&cosmo);
}
/* Compare cosmologies 0 and 3 */
for (int j = 0; j < N_CHECK; j++) {
/* check accuracy */
double err = (times1[j] - times2[j]) / times1[j];
message("Agreement of %g at step %i", err, j);
assert(fabs(err) < TOLERANCE);
}
/* For the massive neutrino cosmology 3, check that it reproduces
* the relativistic (early) and non-relativistic (late) limits */
message("Initialization...");
/* pseudo initialization of params */
struct swift_params params;
test_params_init(¶ms, 3);
/* initialization of unit system */
struct unit_system us;
units_init_cgs(&us);
/* initialization of phys_const */
struct phys_const phys_const;
phys_const_init(&us, ¶ms, &phys_const);
/* initialization of cosmo */
struct cosmology cosmo;
cosmology_init(¶ms, &us, &phys_const, &cosmo);
message("Start checking the fermion integration...");
/* Relativistic limit */
double Omega_nu_early = cosmology_get_neutrino_density(&cosmo, 1e-10);
double Omega_nu_r =
cosmo.Omega_g * cosmo.N_eff * (7. / 8.) * pow(4. / 11., 4. / 3.);
double err = (Omega_nu_early - Omega_nu_r) / Omega_nu_early;
message("Accuracy of %g of the relativistic limit", err);
assert(fabs(err) < TOLERANCE);
/* Zeta function and other constants */
const double zeta3 = 1.202056903159594;
const double kb = phys_const.const_boltzmann_k;
const double hbar = phys_const.const_planck_h / (2 * M_PI);
const double cvel = phys_const.const_speed_light_c;
const double eV = phys_const.const_electron_volt;
/* Non-relativistic limit (limit not reached, so lower tolerance is fine)*/
double Omega_nu_late = cosmology_get_neutrino_density(&cosmo, 1.0);
double Omega_nu_nr = 0.;
for (int i = 0; i < cosmo.N_nu; i++) {
Omega_nu_nr += 6 * zeta3 / (11 * M_PI * M_PI) * pow(kb * cosmo.T_CMB_0, 3) /
pow(cvel * hbar, 3) * cosmo.M_nu_eV[i] * eV /
(cosmo.critical_density_0 * cvel * cvel);
}
double err2 = (Omega_nu_late - Omega_nu_nr) / Omega_nu_late;
message("Accuracy of %g of the non-relativistic limit", err2);
assert(fabs(err2) < 1e-5);
message("Neutrino cosmology test successful.");
cosmology_clean(&cosmo);
return 0;
}