/*******************************************************************************
* 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
/* Some standard headers. */
#include
#include
/* Local headers. */
#include "swift.h"
#if defined(COOLING_EAGLE) && defined(CHEMISTRY_EAGLE) && defined(GADGET2_SPH)
#include "cooling/EAGLE/cooling_rates.h"
#include "cooling/EAGLE/cooling_tables.h"
/* Flag used for printing cooling rate contribution from each
* element. For testing only. Incremented by 1/(number of elements)
* until reaches 1 after which point append to files instead of
* writing new file. */
static float print_cooling_rate_contribution_flag = 0;
/**
* @brief Wrapper function used to calculate cooling rate and dLambda_du.
* Writes to file contribution from each element to cooling rate for testing
* purposes (this function is not used when running SWIFT). Table indices
* and offsets for redshift, hydrogen number density and helium fraction are
* passed in so as to compute them only once per particle.
*
* @param n_h_i Particle hydrogen number density index
* @param d_n_h Particle hydrogen number density offset
* @param He_i Particle helium fraction index
* @param d_He Particle helium fraction offset
* @param p Particle structure
* @param cooling #cooling_function_data structure
* @param cosmo #cosmology structure
* @param phys_const #phys_const structure
* @param abundance_ratio Ratio of element abundance to solar
*/
INLINE static double eagle_print_metal_cooling_rate(
int n_h_i, float d_n_h, int He_i, float d_He, const struct part *restrict p,
const struct xpart *restrict xp,
const struct cooling_function_data *restrict cooling,
const struct cosmology *restrict cosmo, const struct phys_const *phys_const,
float *abundance_ratio) {
/* array to store contributions to cooling rates from each of the
* elements */
double *element_lambda;
element_lambda = malloc((eagle_cooling_N_metal + 2) * sizeof(double));
/* Get the H and He mass fractions */
const float XH = p->chemistry_data.metal_mass_fraction[chemistry_element_H];
/* convert Hydrogen mass fraction in Hydrogen number density */
const double n_h = hydro_get_physical_density(p, cosmo) * XH /
phys_const->const_proton_mass *
cooling->number_density_to_cgs;
/* cooling rate, derivative of cooling rate and internal energy */
double lambda_net = 0.0;
double u = hydro_get_physical_internal_energy(p, xp, cosmo) *
cooling->internal_energy_to_cgs;
/* Open files for writing contributions to cooling rate. Each element
* gets its own file. */
char output_filename[32];
FILE **output_file = malloc((eagle_cooling_N_metal + 2) * sizeof(FILE *));
/* Once this flag reaches 1 we stop overwriting and start appending. */
print_cooling_rate_contribution_flag += 1.0 / (eagle_cooling_N_metal + 2);
/* Loop over each element */
for (int element = 0; element < eagle_cooling_N_metal + 2; element++) {
sprintf(output_filename, "%s%d%s", "cooling_element_", element, ".dat");
if (print_cooling_rate_contribution_flag < 1) {
/* If this is the first time we're running this function, overwrite the
* output files */
output_file[element] = fopen(output_filename, "w");
print_cooling_rate_contribution_flag += 1.0 / (eagle_cooling_N_metal + 2);
} else {
/* append to existing files */
output_file[element] = fopen(output_filename, "a");
}
if (output_file == NULL) {
error("Error opening file!\n");
}
}
/* calculate cooling rates */
for (int j = 0; j < eagle_cooling_N_metal + 2; j++) element_lambda[j] = 0.0;
lambda_net =
eagle_metal_cooling_rate(log10(u), cosmo->z, n_h, abundance_ratio, n_h_i,
d_n_h, He_i, d_He, cooling, element_lambda);
/* write cooling rate contributions to their own files. */
for (int j = 0; j < eagle_cooling_N_metal + 2; j++) {
fprintf(output_file[j], "%.5e\n", element_lambda[j]);
}
for (int i = 0; i < eagle_cooling_N_metal + 2; i++) fclose(output_file[i]);
free(output_file);
free(element_lambda);
return lambda_net;
}
/**
* @brief Assign particle density and entropy corresponding to the
* hydrogen number density and internal energy specified.
*
* @param p Particle data structure
* @param cooling Cooling function data structure
* @param cosmo Cosmology data structure
* @param internal_const Physical constants data structure
* @param nh Hydrogen number density (cgs units)
* @param u Internal energy (cgs units)
*/
void set_quantities(struct part *restrict p, struct xpart *restrict xp,
const struct unit_system *restrict us,
const struct cooling_function_data *restrict cooling,
const struct cosmology *restrict cosmo,
const struct phys_const *restrict internal_const, float nh,
double u) {
double hydrogen_number_density =
nh * pow(units_cgs_conversion_factor(us, UNIT_CONV_LENGTH), 3);
p->rho = hydrogen_number_density * internal_const->const_proton_mass /
p->chemistry_data.metal_mass_fraction[chemistry_element_H];
float pressure = (u * cosmo->a * cosmo->a) *
cooling->internal_energy_from_cgs * p->rho *
(hydro_gamma_minus_one);
p->entropy = pressure * (pow(p->rho, -hydro_gamma));
xp->entropy_full = p->entropy;
}
/**
* @brief Produces contributions to cooling rates for different
* hydrogen number densities, from different metals,
* tests 1d and 4d table interpolations produce
* same results for cooling rate, dlambda/du and temperature.
*/
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 internal_const;
struct hydro_props hydro_properties;
struct cooling_function_data cooling;
struct cosmology cosmo;
struct space s;
const char *parametersFileName = "./cooling_rates.yml";
/* Initialize CPU frequency, this also starts time. */
unsigned long long cpufreq = 0;
clocks_set_cpufreq(cpufreq);
/* Choke on FP-exceptions */
#ifdef HAVE_FE_ENABLE_EXCEPT
feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
#endif
const int npts = 250; // number of values for the internal energy at which
// cooling rate is evaluated
// Set some default values
float redshift = 0.0, log_10_nh = -1;
// Read options
int param;
while ((param = getopt(argc, argv, "z:d:")) != -1) switch (param) {
case 'z':
// read redshift
redshift = atof(optarg);
break;
case 'd':
// read log10 of hydrogen number density
log_10_nh = atof(optarg);
break;
case '?':
if (optopt == 'z')
printf("Option -%c requires an argument.\n", optopt);
else
printf("Unknown option character `\\x%x'.\n", optopt);
error("invalid option(s) to cooling_rates");
}
// 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");
// Init physical constants
phys_const_init(&us, params, &internal_const);
// Init porperties of hydro
hydro_props_init(&hydro_properties, &internal_const, &us, params);
// Init chemistry
chemistry_init(params, &us, &internal_const, &chem_data);
chemistry_first_init_part(&internal_const, &us, &cosmo, &chem_data, &p, &xp);
chemistry_print(&chem_data);
// Init cosmology
cosmology_init(params, &us, &internal_const, &cosmo);
// Init pressure floor
struct pressure_floor_props pressure_floor;
// Set redshift and associated quantities
const float scale_factor = 1.0 / (1.0 + redshift);
integertime_t ti_current =
log(scale_factor / cosmo.a_begin) / cosmo.time_base;
cosmology_update(&cosmo, &internal_const, ti_current);
message("Redshift is %f", cosmo.z);
// Init cooling
cooling_init(params, &us, &internal_const, &hydro_properties, &cooling);
cooling.H_reion_done = 1;
cooling_print(&cooling);
cooling_update(&cosmo, &pressure_floor, &cooling, &s);
// Copy over the raw metals into the smoothed metals
memcpy(&p.chemistry_data.smoothed_metal_mass_fraction,
&p.chemistry_data.metal_mass_fraction,
chemistry_element_count * sizeof(float));
p.chemistry_data.smoothed_metal_mass_fraction_total =
p.chemistry_data.metal_mass_fraction_total;
// Calculate abundance ratios
float abundance_ratio[(chemistry_element_count + 2)];
abundance_ratio_to_solar(&p, &cooling, abundance_ratio);
// extract mass fractions, calculate table indices and offsets
float XH = p.chemistry_data.smoothed_metal_mass_fraction[chemistry_element_H];
float XHe =
p.chemistry_data.smoothed_metal_mass_fraction[chemistry_element_He];
float HeFrac = XHe / (XH + XHe);
int He_i, n_h_i;
float d_He, d_n_h;
get_index_1d(cooling.HeFrac, eagle_cooling_N_He_frac, HeFrac, &He_i, &d_He);
// Calculate contributions from metals to cooling rate
// open file
FILE *output_file = fopen("cooling_output.dat", "w");
if (output_file == NULL) {
error("Error opening output file!\n");
}
// set hydrogen number density
const float nh = exp(M_LN10 * log_10_nh);
/* Initial internal energy */
double u = 1.0e14;
// set internal energy to dummy value, will get reset when looping over
// internal energies
set_quantities(&p, &xp, &us, &cooling, &cosmo, &internal_const, nh, u);
float inn_h = hydro_get_physical_density(&p, &cosmo) * XH /
internal_const.const_proton_mass *
cooling.number_density_to_cgs;
get_index_1d(cooling.nH, eagle_cooling_N_density, log10(inn_h), &n_h_i,
&d_n_h);
// Loop over internal energy
for (int j = 0; j < npts; j++) {
// Update the particle with the new values
set_quantities(&p, &xp, &us, &cooling, &cosmo, &internal_const, nh,
pow(10.0, 10.0 + j * 8.0 / npts));
// New internal energy
u = hydro_get_physical_internal_energy(&p, &xp, &cosmo) *
cooling.internal_energy_to_cgs;
// calculate cooling rates
const double temperature = eagle_convert_u_to_temp(
log10(u), cosmo.z, n_h_i, He_i, d_n_h, d_He, &cooling);
const double cooling_du_dt = eagle_print_metal_cooling_rate(
n_h_i, d_n_h, He_i, d_He, &p, &xp, &cooling, &cosmo, &internal_const,
abundance_ratio);
// Dump...
fprintf(output_file, "%.5e %.5e\n", exp(M_LN10 * temperature),
cooling_du_dt);
}
fclose(output_file);
message("done cooling rates test");
/* Clean everything */
cosmology_clean(&cosmo);
cooling_clean(&cooling);
free(params);
return 0;
}
#else
int main(int argc, char **argv) {
/* Initialize CPU frequency, this also starts time. */
unsigned long long cpufreq = 0;
clocks_set_cpufreq(cpufreq);
message(
"This test is only defined for the EAGLE cooling model and with Gadget-2 "
"SPH.");
return 0;
}
#endif