/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2019 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 #ifdef HAVE_HDF5 #include #endif /* Local includes */ #include "cosmology.h" #include "entropy_floor.h" #include "hydro.h" #include "parser.h" /** * @brief Compute the pressure from the entropy floor at a given density * * @param rho_phys The physical density (internal units). * @param rho_com The comoving density (internal units). * @param cosmo The cosmological model. * @param props The properties of the entropy floor. */ float entropy_floor_gas_pressure(const float rho_phys, const float rho_com, const struct cosmology *cosmo, const struct entropy_floor_properties *props) { /* Mean baryon density in co-moving internal units for over-density condition * (Recall cosmo->critical_density_0 is 0 in a non-cosmological run, * making the over-density condition a no-op) */ const float rho_crit_0 = cosmo->critical_density_0; const float rho_crit_baryon = cosmo->Omega_b * rho_crit_0; /* Physical pressure */ float pressure = 0.f; /* Are we in the regime of the Jeans equation of state? */ if ((rho_com >= rho_crit_baryon * props->Jeans_over_density_threshold) && (rho_phys >= props->Jeans_density_threshold)) { const float pressure_Jeans = props->Jeans_pressure_norm * powf(rho_phys * props->Jeans_density_threshold_inv, props->Jeans_gamma_effective); pressure = max(pressure, pressure_Jeans); } /* Are we in the regime of the Cool equation of state? */ if ((rho_com >= rho_crit_baryon * props->Cool_over_density_threshold) && (rho_phys >= props->Cool_density_threshold)) { const float pressure_Cool = props->Cool_pressure_norm * powf(rho_phys * props->Cool_density_threshold_inv, props->Cool_gamma_effective); pressure = max(pressure, pressure_Cool); } return pressure; } /** * @brief Compute the entropy floor of a given #part. * * Note that the particle is not updated!! * * @param p The #part. * @param cosmo The cosmological model. * @param props The properties of the entropy floor. */ float entropy_floor(const struct part *p, const struct cosmology *cosmo, const struct entropy_floor_properties *props) { /* Comoving density in internal units */ const float rho_com = hydro_get_comoving_density(p); /* Physical density in internal units */ const float rho_phys = hydro_get_physical_density(p, cosmo); const float pressure = entropy_floor_gas_pressure(rho_phys, rho_com, cosmo, props); /* Convert to an entropy. * (Recall that the entropy is the same in co-moving and physical frames) */ return gas_entropy_from_pressure(rho_phys, pressure); } /** * @brief Compute the temperature from the entropy floor at a given density * * This is the temperature exactly corresponding to the imposed EoS shape. * It only matches the entropy returned by the entropy_floor() function * for a neutral gas with primoridal abundance. * * @param rho_phys The physical density (internal units). * @param rho_com The comoving density (internal units). * @param cosmo The cosmological model. * @param props The properties of the entropy floor. */ float entropy_floor_gas_temperature( const float rho_phys, const float rho_com, const struct cosmology *cosmo, const struct entropy_floor_properties *props) { /* Mean baryon density in co-moving internal units for over-density condition * (Recall cosmo->critical_density_0 is 0 in a non-cosmological run, * making the over-density condition a no-op) */ const float rho_crit_0 = cosmo->critical_density_0; const float rho_crit_baryon = cosmo->Omega_b * rho_crit_0; /* Physical */ float temperature = 0.f; /* Are we in the regime of the Jeans equation of state? */ if ((rho_com >= rho_crit_baryon * props->Jeans_over_density_threshold) && (rho_phys >= props->Jeans_density_threshold)) { const float jeans_slope = props->Jeans_gamma_effective - 1.f; const float temperature_Jeans = props->Jeans_temperature_norm * pow(rho_phys * props->Jeans_density_threshold_inv, jeans_slope); temperature = max(temperature, temperature_Jeans); } /* Are we in the regime of the Cool equation of state? */ if ((rho_com >= rho_crit_baryon * props->Cool_over_density_threshold) && (rho_phys >= props->Cool_density_threshold)) { const float cool_slope = props->Cool_gamma_effective - 1.f; const float temperature_Cool = props->Cool_temperature_norm * pow(rho_phys * props->Cool_density_threshold_inv, cool_slope); temperature = max(temperature, temperature_Cool); } return temperature; } /** * @brief Compute the temperature from the entropy floor for a given #part * * Calculate the EoS temperature, the particle is not updated. * This is the temperature exactly corresponding to the imposed EoS shape. * It only matches the entropy returned by the entropy_floor() function * for a neutral gas with primoridal abundance. * * @param p The #part. * @param cosmo The cosmological model. * @param props The properties of the entropy floor. */ float entropy_floor_temperature(const struct part *p, const struct cosmology *cosmo, const struct entropy_floor_properties *props) { /* Comoving density in internal units */ const float rho_com = hydro_get_comoving_density(p); /* Physical density in internal units */ const float rho_phys = hydro_get_physical_density(p, cosmo); return entropy_floor_gas_temperature(rho_phys, rho_com, cosmo, props); } /** * @brief Initialise the entropy floor by reading the parameters and converting * to internal units. * * The input temperatures and number densities are converted to entropy and * density assuming a neutral gas of primoridal abundance. * * @param params The YAML parameter file. * @param us The system of units used internally. * @param phys_const The physical constants. * @param hydro_props The propoerties of the hydro scheme. * @param props The entropy floor properties to fill. */ void entropy_floor_init(struct entropy_floor_properties *props, const struct phys_const *phys_const, const struct unit_system *us, const struct hydro_props *hydro_props, struct swift_params *params) { /* Read the parameters in the units they are set */ props->Jeans_density_threshold_H_p_cm3 = parser_get_param_float( params, "EAGLEEntropyFloor:Jeans_density_threshold_H_p_cm3"); props->Jeans_over_density_threshold = parser_get_param_float( params, "EAGLEEntropyFloor:Jeans_over_density_threshold"); props->Jeans_temperature_norm_K = parser_get_param_float( params, "EAGLEEntropyFloor:Jeans_temperature_norm_K"); props->Jeans_gamma_effective = parser_get_param_float(params, "EAGLEEntropyFloor:Jeans_gamma_effective"); props->Cool_density_threshold_H_p_cm3 = parser_get_param_float( params, "EAGLEEntropyFloor:Cool_density_threshold_H_p_cm3"); props->Cool_over_density_threshold = parser_get_param_float( params, "EAGLEEntropyFloor:Cool_over_density_threshold"); props->Cool_temperature_norm_K = parser_get_param_float( params, "EAGLEEntropyFloor:Cool_temperature_norm_K"); props->Cool_gamma_effective = parser_get_param_float(params, "EAGLEEntropyFloor:Cool_gamma_effective"); /* Cross-check that the input makes sense */ if (props->Cool_density_threshold_H_p_cm3 >= props->Jeans_density_threshold_H_p_cm3) { error( "Invalid values for the entrop floor density thresholds. The 'Jeans' " "threshold (%e cm^-3) should be at a higher density than the 'Cool' " "threshold (%e cm^-3)", props->Jeans_density_threshold_H_p_cm3, props->Cool_density_threshold_H_p_cm3); } /* Initial Hydrogen abundance (mass fraction) */ const double X_H = hydro_props->hydrogen_mass_fraction; /* Now convert to internal units assuming primodial Hydrogen abundance */ props->Jeans_temperature_norm = props->Jeans_temperature_norm_K / units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); props->Jeans_density_threshold = props->Jeans_density_threshold_H_p_cm3 / units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY) * phys_const->const_proton_mass / X_H; props->Cool_temperature_norm = props->Cool_temperature_norm_K / units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); props->Cool_density_threshold = props->Cool_density_threshold_H_p_cm3 / units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY) * phys_const->const_proton_mass / X_H; /* We assume neutral gas */ const float mean_molecular_weight = hydro_props->mu_neutral; /* Get the common terms */ props->Jeans_density_threshold_inv = 1.f / props->Jeans_density_threshold; props->Cool_density_threshold_inv = 1.f / props->Cool_density_threshold; /* P_norm = (k_B * T) / (m_p * mu) * rho_threshold */ props->Jeans_pressure_norm = ((phys_const->const_boltzmann_k * props->Jeans_temperature_norm) / (phys_const->const_proton_mass * mean_molecular_weight)) * props->Jeans_density_threshold; props->Cool_pressure_norm = ((phys_const->const_boltzmann_k * props->Cool_temperature_norm) / (phys_const->const_proton_mass * mean_molecular_weight)) * props->Cool_density_threshold; } /** * @brief Print the properties of the entropy floor to stdout. * * @param props The entropy floor properties. */ void entropy_floor_print(const struct entropy_floor_properties *props) { message("Entropy floor is 'EAGLE' with:"); message("Jeans limiter with slope n=%.3f at rho=%e (%e H/cm^3) and T=%.1f K", props->Jeans_gamma_effective, props->Jeans_density_threshold, props->Jeans_density_threshold_H_p_cm3, props->Jeans_temperature_norm); message(" Cool limiter with slope n=%.3f at rho=%e (%e H/cm^3) and T=%.1f K", props->Cool_gamma_effective, props->Cool_density_threshold, props->Cool_density_threshold_H_p_cm3, props->Cool_temperature_norm); } #ifdef HAVE_HDF5 /** * @brief Writes the current model of entropy floor to the file * @param h_grp The HDF5 group in which to write */ void entropy_floor_write_flavour(hid_t h_grp) { io_write_attribute_s(h_grp, "Entropy floor", "EAGLE"); } #endif /** * @brief Write an entropy floor struct to the given FILE as a stream of bytes. * * @param props the struct * @param stream the file stream */ void entropy_floor_struct_dump(const struct entropy_floor_properties *props, FILE *stream) { restart_write_blocks((void *)props, sizeof(struct entropy_floor_properties), 1, stream, "entropy floor", "entropy floor properties"); } /** * @brief Restore a entropy floor struct from the given FILE as a stream of * bytes. * * @param props the struct * @param stream the file stream */ void entropy_floor_struct_restore(struct entropy_floor_properties *props, FILE *stream) { restart_read_blocks((void *)props, sizeof(struct entropy_floor_properties), 1, stream, NULL, "entropy floor properties"); }