/*******************************************************************************
* This file is part of SWIFT.
* Copyright (C) 2015 Matthieu Schaller (schaller@strw.leidenuniv.nl).
*
* 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 .
*
******************************************************************************/
#include
/* Local headers. */
#include "swift.h"
#if defined(CHEMISTRY_EAGLE) && defined(COOLING_EAGLE) && defined(GADGET2_SPH)
#include "../src/cooling/EAGLE/cooling_rates.h"
/*
* @brief Assign particle density and entropy corresponding to the
* hydrogen number density and internal energy specified.
*
* @param p Particle data structure
* @param xp extra particle structure
* @param us unit system struct
* @param cooling Cooling function data structure
* @param cosmo Cosmology data structure
* @param phys_const Physical constants data structure
* @param nh_cgs Hydrogen number density (cgs units)
* @param u Internal energy (cgs units)
* @param ti_current integertime to set cosmo quantities
*/
void set_quantities(struct part *restrict p, struct xpart *restrict xp,
const struct unit_system *restrict us,
const struct cooling_function_data *restrict cooling,
struct cosmology *restrict cosmo,
const struct phys_const *restrict phys_const, float nh_cgs,
double u_cgs, integertime_t ti_current) {
/* calculate density */
double hydrogen_number_density = nh_cgs / cooling->number_density_to_cgs;
p->rho = hydrogen_number_density * phys_const->const_proton_mass /
p->chemistry_data.metal_mass_fraction[chemistry_element_H];
/* update entropy based on internal energy */
float pressure = (u_cgs)*cooling->internal_energy_from_cgs * p->rho *
(hydro_gamma_minus_one);
p->entropy = pressure * (pow(p->rho, -hydro_gamma));
xp->entropy_full = p->entropy;
p->entropy_dt = 0.f;
}
/*
* @brief Tests cooling integration scheme by comparing EAGLE
* integration to subcycled explicit equation.
*/
int main(int argc, char **argv) {
// Declare relevant structs
struct swift_params *params = malloc(sizeof(struct swift_params));
struct unit_system us;
struct chemistry_global_data chem_data;
struct part p;
struct xpart xp;
struct phys_const phys_const;
struct hydro_props hydro_properties;
struct entropy_floor_properties floor_props;
struct pressure_floor_props pressure_floor;
struct cooling_function_data cooling;
struct cosmology cosmo;
char *parametersFileName = "./testCooling.yml";
float nh_cgs; // hydrogen number density
double u_cgs; // internal energy
const float seconds_per_year = 3.154e7;
/* Number of values to test for in redshift,
* hydrogen number density and internal energy */
const int n_z = 10;
const int n_nh = 10;
const int n_u = 10;
/* Number of subcycles and tolerance used to compare
* subcycled and implicit solution. Note, high value
* of tolerance due to mismatch between explicit and
* implicit solution for large timesteps */
const int n_subcycle = 1000;
const float integration_tolerance = 0.2;
/* Read the parameter file */
if (params == NULL) error("Error allocating memory for the parameter file.");
message("Reading runtime parameters from file '%s'", parametersFileName);
parser_read_file(parametersFileName, params);
/* Init units */
units_init_from_params(&us, params, "InternalUnitSystem");
phys_const_init(&us, params, &phys_const);
/* Init chemistry */
chemistry_init(params, &us, &phys_const, &chem_data);
chemistry_first_init_part(&phys_const, &us, &cosmo, &chem_data, &p, &xp);
chemistry_part_has_no_neighbours(&p, &xp, &chem_data, &cosmo);
chemistry_print(&chem_data);
/* Init cosmology */
cosmology_init(params, &us, &phys_const, &cosmo);
cosmology_print(&cosmo);
/* Init hydro_props */
hydro_props_init(&hydro_properties, &phys_const, &us, params);
/* Init entropy floor */
entropy_floor_init(&floor_props, &phys_const, &us, &hydro_properties, params);
/* Init the pressure floor */
pressure_floor_init(&pressure_floor, &phys_const, &us, &hydro_properties,
params);
/* Set dt */
const int timebin = 38;
float dt_cool, dt_therm;
/* Init cooling */
cooling_init(params, &us, &phys_const, &hydro_properties, &cooling);
cooling_print(&cooling);
cooling_update(&cosmo, &pressure_floor, &cooling, 0);
/* Cooling function needs to know the minimal energy. Set it to the lowest
* internal energy in the cooling table. */
hydro_properties.minimal_internal_energy =
exp(M_LN10 * cooling.Therm[0]) * cooling.internal_energy_from_cgs;
/* Calculate abundance ratios */
float *abundance_ratio;
abundance_ratio = malloc((chemistry_element_count + 2) * sizeof(float));
abundance_ratio_to_solar(&p, &cooling, abundance_ratio);
/* extract mass fractions, calculate table indices and offsets */
float XH = p.chemistry_data.metal_mass_fraction[chemistry_element_H];
float HeFrac =
p.chemistry_data.metal_mass_fraction[chemistry_element_He] /
(XH + p.chemistry_data.metal_mass_fraction[chemistry_element_He]);
int He_i;
float d_He;
get_index_1d(cooling.HeFrac, eagle_cooling_N_He_frac, HeFrac, &He_i, &d_He);
/* calculate spacing in nh and u */
const float log_u_min_cgs = 11, log_u_max_cgs = 17;
const float log_nh_min_cgs = -6, log_nh_max_cgs = 3;
const float delta_log_nh_cgs = (log_nh_max_cgs - log_nh_min_cgs) / n_nh;
const float delta_log_u_cgs = (log_u_max_cgs - log_u_min_cgs) / n_u;
/* Declare variables we will be checking */
double du_dt_implicit, du_dt_check;
integertime_t ti_current;
/* Loop over values of nh and u */
for (int nh_i = 0; nh_i < n_nh; nh_i++) {
nh_cgs = exp(M_LN10 * log_nh_min_cgs + delta_log_nh_cgs * nh_i);
for (int u_i = 0; u_i < n_u; u_i++) {
u_cgs = exp(M_LN10 * log_u_min_cgs + delta_log_u_cgs * u_i);
/* Loop over z */
for (int z_i = 0; z_i <= n_z; z_i++) {
ti_current = max_nr_timesteps / n_z * z_i + 1;
/* update nh, u, z */
cosmology_update(&cosmo, &phys_const, ti_current);
cooling_init(params, &us, &phys_const, &hydro_properties, &cooling);
cooling_update(&cosmo, &pressure_floor, &cooling, 0);
set_quantities(&p, &xp, &us, &cooling, &cosmo, &phys_const, nh_cgs,
u_cgs, ti_current);
/* Set dt */
const integertime_t ti_step = get_integer_timestep(timebin);
const integertime_t ti_begin =
get_integer_time_begin(ti_current - 1, timebin);
dt_cool =
cosmology_get_delta_time(&cosmo, ti_begin, ti_begin + ti_step);
dt_therm = cosmology_get_therm_kick_factor(&cosmo, ti_begin,
ti_begin + ti_step);
/* calculate subcycled solution */
for (int t_subcycle = 0; t_subcycle < n_subcycle; t_subcycle++) {
p.entropy_dt = 0;
cooling_cool_part(&phys_const, &us, &cosmo, &hydro_properties,
&floor_props, &pressure_floor, &cooling, &p, &xp,
dt_cool / n_subcycle, dt_therm / n_subcycle, 0.);
xp.entropy_full += p.entropy_dt * dt_therm / n_subcycle;
}
du_dt_check = hydro_get_physical_internal_energy_dt(&p, &cosmo);
/* reset quantities to nh, u, and z that we want to test */
cosmology_update(&cosmo, &phys_const, ti_current);
set_quantities(&p, &xp, &us, &cooling, &cosmo, &phys_const, nh_cgs,
u_cgs, ti_current);
/* compute implicit solution */
cooling_cool_part(&phys_const, &us, &cosmo, &hydro_properties,
&floor_props, &pressure_floor, &cooling, &p, &xp,
dt_cool, dt_therm, 0.);
du_dt_implicit = hydro_get_physical_internal_energy_dt(&p, &cosmo);
/* check if the two solutions are consistent */
if (fabs((du_dt_implicit - du_dt_check) / du_dt_check) >
integration_tolerance ||
(du_dt_check == 0.0 && du_dt_implicit != 0.0))
error(
"Solutions do not match. scale factor %.5e z %.5e nh_cgs %.5e "
"u_cgs %.5e dt (years) %.5e du cgs implicit %.5e reference %.5e "
"error %.5e",
cosmo.a, cosmo.z, nh_cgs, u_cgs,
dt_cool * units_cgs_conversion_factor(&us, UNIT_CONV_TIME) /
seconds_per_year,
du_dt_implicit *
units_cgs_conversion_factor(&us,
UNIT_CONV_ENERGY_PER_UNIT_MASS) *
dt_therm,
du_dt_check *
units_cgs_conversion_factor(&us,
UNIT_CONV_ENERGY_PER_UNIT_MASS) *
dt_therm,
fabs((du_dt_implicit - du_dt_check) / du_dt_check));
}
}
}
message("done explicit subcycling cooling test");
free(params);
return 0;
}
#else
int main(int argc, char **argv) { return 0; }
#endif