/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl). * 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk). * * 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 . * ******************************************************************************/ #ifndef SWIFT_TILLOTSON_EQUATION_OF_STATE_H #define SWIFT_TILLOTSON_EQUATION_OF_STATE_H /** * @file equation_of_state/planetary/tillotson.h * * Contains the Tillotson EOS functions for * equation_of_state/planetary/equation_of_state.h * */ /* Some standard headers. */ #include /* Local headers. */ #include "adiabatic_index.h" #include "common_io.h" #include "equation_of_state.h" #include "inline.h" #include "physical_constants.h" #include "sesame.h" #include "units.h" // Tillotson parameters struct Til_params { float rho_0, a, b, A, B, u_0, u_iv, u_cv, alpha, beta; float eta_min, eta_zero, P_min, C_V; float *A1_u_cold; float rho_cold_min, rho_cold_max; float rho_min, rho_max; enum eos_planetary_material_id mat_id; }; // Parameter values for each material (SI units) INLINE static void set_Til_iron(struct Til_params *mat, enum eos_planetary_material_id mat_id) { mat->mat_id = mat_id; mat->rho_0 = 7800.0f; mat->a = 0.5f; mat->b = 1.5f; mat->A = 1.28e11f; mat->B = 1.05e11f; mat->u_0 = 9.5e6f; mat->u_iv = 2.4e6f; mat->u_cv = 8.67e6f; mat->alpha = 5.0f; mat->beta = 5.0f; mat->eta_min = 0.0f; mat->eta_zero = 0.0f; mat->P_min = 0.01f; mat->C_V = 449.0f; mat->rho_cold_min = 100.0f; mat->rho_cold_max = 1.0e5f; mat->rho_min = 1.0f; mat->rho_max = 1.0e5f; } INLINE static void set_Til_granite(struct Til_params *mat, enum eos_planetary_material_id mat_id) { mat->mat_id = mat_id; mat->rho_0 = 2680.0f; mat->a = 0.5f; mat->b = 1.3f; mat->A = 1.8e10f; mat->B = 1.8e10f; mat->u_0 = 1.6e7f; mat->u_iv = 3.5e6f; mat->u_cv = 1.8e7f; mat->alpha = 5.0f; mat->beta = 5.0f; mat->eta_min = 0.0f; mat->eta_zero = 0.0f; mat->P_min = 0.01f; mat->C_V = 790.0f; mat->rho_cold_min = 100.0f; mat->rho_cold_max = 1.0e5f; mat->rho_min = 1.0f; mat->rho_max = 1.0e5f; } INLINE static void set_Til_basalt(struct Til_params *mat, enum eos_planetary_material_id mat_id) { mat->mat_id = mat_id; mat->rho_0 = 2700.0f; mat->a = 0.5f; mat->b = 1.5f; mat->A = 2.67e10f; mat->B = 2.67e10f; mat->u_0 = 4.87e8f; mat->u_iv = 4.72e6f; mat->u_cv = 1.82e7f; mat->alpha = 5.0f; mat->beta = 5.0f; mat->eta_min = 0.0f; mat->eta_zero = 0.0f; mat->P_min = 0.01f; mat->C_V = 790.0f; mat->rho_cold_min = 100.0f; mat->rho_cold_max = 1.0e5f; mat->rho_min = 1.0f; mat->rho_max = 1.0e5f; } INLINE static void set_Til_water(struct Til_params *mat, enum eos_planetary_material_id mat_id) { mat->mat_id = mat_id; mat->rho_0 = 998.0f; mat->a = 0.7f; mat->b = 0.15f; mat->A = 2.18e9f; mat->B = 1.325e10f; mat->u_0 = 7.0e6f; mat->u_iv = 4.19e5f; mat->u_cv = 2.69e6f; mat->alpha = 10.0f; mat->beta = 5.0f; mat->eta_min = 0.925f; mat->eta_zero = 0.875f; mat->P_min = 0.01f; mat->C_V = 4186.0f; mat->rho_cold_min = 100.0f; mat->rho_cold_max = 1.0e5f; mat->rho_min = 1.0f; mat->rho_max = 1.0e5f; } INLINE static void set_Til_ice(struct Til_params *mat, enum eos_planetary_material_id mat_id) { mat->mat_id = mat_id; mat->rho_0 = 1293.0f; mat->a = 0.3f; mat->b = 0.1f; mat->A = 1.07e10f; mat->B = 6.5e10f; mat->u_0 = 1.0e7f; mat->u_iv = 7.73e5f; mat->u_cv = 3.04e6f; mat->alpha = 10.0f; mat->beta = 5.0f; mat->eta_min = 0.925f; mat->eta_zero = 0.875f; mat->P_min = 0.0f; mat->C_V = 2093.0f; mat->rho_cold_min = 100.0f; mat->rho_cold_max = 1.0e5f; mat->rho_min = 1.0f; mat->rho_max = 1.0e5f; } /* Read the parameters from a file. File contents ------------- # header (5 lines) rho_0 (kg/m3) a (-) b (-) A (Pa) B (Pa) u_0 (J) u_iv (J) u_cv (J) alpha (-) beta (-) eta_min (-) eta_zero (-) P_min (Pa) C_V (J kg^-1 K^-1) rho_cold_min (kg/m3) rho_cold_max (kg/m3) rho_min (kg/m3) rho_max (kg/m3) */ INLINE static void set_Til_custom(struct Til_params *mat, enum eos_planetary_material_id mat_id, char *param_file) { mat->mat_id = mat_id; // Load table contents from file FILE *f = fopen(param_file, "r"); if (f == NULL) error("Failed to open the Tillotson EoS file '%s'", param_file); // Skip header lines skip_lines(f, 5); // Read parameters (SI) int c; c = fscanf(f, "%f %f %f %f %f", &mat->rho_0, &mat->a, &mat->b, &mat->A, &mat->B); if (c != 5) error("Failed to read the Tillotson EoS file %s", param_file); c = fscanf(f, "%f %f %f %f %f", &mat->u_0, &mat->u_iv, &mat->u_cv, &mat->alpha, &mat->beta); if (c != 5) error("Failed to read the Tillotson EoS file %s", param_file); c = fscanf(f, "%f %f %f %f", &mat->eta_min, &mat->eta_zero, &mat->P_min, &mat->C_V); if (c != 4) error("Failed to read the Tillotson EoS file %s", param_file); c = fscanf(f, "%f %f %f %f", &mat->rho_cold_min, &mat->rho_cold_max, &mat->rho_min, &mat->rho_max); if (c != 4) error("Failed to read the Tillotson EoS file %s", param_file); } // Convert to internal units INLINE static void convert_units_Til(struct Til_params *mat, const struct unit_system *us) { struct unit_system si; units_init_si(&si); const int N = 10000; // SI to cgs mat->rho_0 *= units_cgs_conversion_factor(&si, UNIT_CONV_DENSITY); mat->A *= units_cgs_conversion_factor(&si, UNIT_CONV_PRESSURE); mat->B *= units_cgs_conversion_factor(&si, UNIT_CONV_PRESSURE); mat->u_0 *= units_cgs_conversion_factor(&si, UNIT_CONV_ENERGY_PER_UNIT_MASS); mat->u_iv *= units_cgs_conversion_factor(&si, UNIT_CONV_ENERGY_PER_UNIT_MASS); mat->u_cv *= units_cgs_conversion_factor(&si, UNIT_CONV_ENERGY_PER_UNIT_MASS); mat->P_min *= units_cgs_conversion_factor(&si, UNIT_CONV_PRESSURE); for (int i = 0; i < N; i++) { mat->A1_u_cold[i] *= units_cgs_conversion_factor(&si, UNIT_CONV_ENERGY_PER_UNIT_MASS); } mat->C_V *= units_cgs_conversion_factor(&si, UNIT_CONV_ENERGY_PER_UNIT_MASS); // Entropy units don't work? using internal kelvin mat->rho_cold_min *= units_cgs_conversion_factor(&si, UNIT_CONV_DENSITY); mat->rho_cold_max *= units_cgs_conversion_factor(&si, UNIT_CONV_DENSITY); mat->rho_min *= units_cgs_conversion_factor(&si, UNIT_CONV_DENSITY); mat->rho_max *= units_cgs_conversion_factor(&si, UNIT_CONV_DENSITY); // cgs to internal mat->rho_0 /= units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); mat->A /= units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE); mat->B /= units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE); mat->u_0 /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); mat->u_iv /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); mat->u_cv /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); mat->P_min /= units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE); for (int i = 0; i < N; i++) { mat->A1_u_cold[i] /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); } mat->C_V /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); // Entropy units don't work? using internal kelvin mat->rho_cold_min /= units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); mat->rho_cold_max /= units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); mat->rho_min /= units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); mat->rho_max /= units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); } // gas_internal_energy_from_entropy INLINE static float Til_internal_energy_from_entropy( const float density, const float entropy, const struct Til_params *mat) { error("This EOS function is not yet implemented!"); return 0.f; } // gas_pressure_from_entropy INLINE static float Til_pressure_from_entropy(const float density, const float entropy, const struct Til_params *mat) { error("This EOS function is not yet implemented!"); return 0.f; } // gas_entropy_from_pressure INLINE static float Til_entropy_from_pressure(const float density, const float pressure, const struct Til_params *mat) { error("This EOS function is not yet implemented!"); return 0.f; } // gas_soundspeed_from_entropy INLINE static float Til_soundspeed_from_entropy(const float density, const float entropy, const struct Til_params *mat) { error("This EOS function is not yet implemented!"); return 0.f; } // gas_entropy_from_internal_energy INLINE static float Til_entropy_from_internal_energy( const float density, const float u, const struct Til_params *mat) { return 0.f; } // gas_pressure_from_internal_energy INLINE static float Til_pressure_from_internal_energy( const float density, const float u, const struct Til_params *mat) { const float eta = density / mat->rho_0; const float eta_sq = eta * eta; const float mu = eta - 1.f; const float nu = 1.f / eta - 1.f; const float w = u / (mat->u_0 * eta_sq) + 1.f; const float w_inv = 1.f / w; // Condensed or cold float P_c = (mat->a + mat->b * w_inv) * density * u + mat->A * mu + mat->B * mu * mu; if (eta < mat->eta_zero) { P_c = 0.f; } else if (eta < mat->eta_min) { P_c *= (eta - mat->eta_zero) / (mat->eta_min - mat->eta_zero); } // Expanded and hot float P_e = mat->a * density * u + (mat->b * density * u * w_inv + mat->A * mu * expf(-mat->beta * nu)) * expf(-mat->alpha * nu * nu); // Condensed or cold state float P; if ((1.f < eta) || (u < mat->u_iv)) { P = P_c; } // Expanded and hot state else if ((eta < 1.f) && (mat->u_cv < u)) { P = P_e; } // Hybrid state else { P = ((u - mat->u_iv) * P_e + (mat->u_cv - u) * P_c) / (mat->u_cv - mat->u_iv); } // Minimum pressure if (P < mat->P_min) { P = mat->P_min; } return P; } // gas_internal_energy_from_pressure INLINE static float Til_internal_energy_from_pressure( const float density, const float P, const struct Til_params *mat) { error("This EOS function is not yet implemented!"); return 0.f; } // gas_soundspeed_from_internal_energy INLINE static float Til_soundspeed_from_internal_energy( const float density, const float u, const struct Til_params *mat) { const float rho_0_inv = 1.f / mat->rho_0; const float eta = density * rho_0_inv; const float rho_inv = 1.f / density; const float eta_sq = eta * eta; const float mu = eta - 1.f; const float nu = 1.f / eta - 1.f; const float w = u / (mat->u_0 * eta_sq) + 1.f; const float w_inv = 1.f / w; const float w_inv_sq = w_inv * w_inv; const float exp_beta = expf(-mat->beta * nu); const float exp_alpha = expf(-mat->alpha * nu * nu); // Condensed or cold float P_c = (mat->a + mat->b * w_inv) * density * u + mat->A * mu + mat->B * mu * mu; if (eta < mat->eta_zero) { P_c = 0.f; } else if (eta < mat->eta_min) { P_c *= (eta - mat->eta_zero) / (mat->eta_min - mat->eta_zero); } float c_sq_c = P_c * rho_inv * (1.f + mat->a + mat->b * w_inv) + mat->b * (w - 1.f) * w_inv_sq * (2.f * u - P_c * rho_inv) + rho_inv * (mat->A + mat->B * (eta_sq - 1.f)); // Expanded and hot float P_e = mat->a * density * u + (mat->b * density * u * w_inv + mat->A * mu * exp_beta) * exp_alpha; float c_sq_e = P_e * rho_inv * (1.f + mat->a + mat->b * w_inv * exp_alpha) + (mat->b * density * u * w_inv_sq / eta_sq * (rho_inv / mat->u_0 * (2.f * u - P_e * rho_inv) + 2.f * mat->alpha * nu * w * rho_0_inv) + mat->A * rho_0_inv * (1.f + mu / eta_sq * (mat->beta + 2.f * mat->alpha * nu - eta)) * exp_beta) * exp_alpha; // Condensed or cold state float c_sq; if ((1.f < eta) || (u < mat->u_iv)) { c_sq = c_sq_c; } // Expanded and hot state else if ((eta < 1.f) && (mat->u_cv < u)) { c_sq = c_sq_e; } // Hybrid state else { c_sq = ((u - mat->u_iv) * c_sq_e + (mat->u_cv - u) * c_sq_c) / (mat->u_cv - mat->u_iv); } c_sq = fmaxf(c_sq, mat->A * rho_0_inv); return sqrtf(c_sq); } // gas_soundspeed_from_pressure INLINE static float Til_soundspeed_from_pressure(const float density, const float P, const struct Til_params *mat) { error("This EOS function is not yet implemented!"); return 0.f; } // Compute u cold INLINE static float compute_u_cold( const float density, const struct Til_params *mat, const enum eos_planetary_material_id mat_id) { const int N = 10000; const float rho_0 = mat->rho_0; const float drho = (density - rho_0) / N; float x = rho_0; float u_cold = 1e-9; for (int i = 0; i < N; i++) { x += drho; u_cold += Til_pressure_from_internal_energy(x, u_cold, mat) * drho / (x * x); } return u_cold; } // Compute A1_u_cold INLINE static void set_Til_u_cold(struct Til_params *mat, enum eos_planetary_material_id mat_id) { const int N = 10000; const float rho_min = 100.f; const float rho_max = 100000.f; // Allocate table memory mat->A1_u_cold = (float *)malloc(N * sizeof(float)); float rho = rho_min; const float drho = (rho_max - rho_min) / (N - 1); for (int i = 0; i < N; i++) { mat->A1_u_cold[i] = compute_u_cold(rho, mat, mat_id); rho += drho; } } // Compute u cold fast from precomputed values INLINE static float compute_fast_u_cold(const float density, const struct Til_params *mat) { const int N = 10000; const float rho_min = mat->rho_cold_min; const float rho_max = mat->rho_cold_max; const float drho = (rho_max - rho_min) / (N - 1); const int a = (int)((density - rho_min) / drho); const int b = a + 1; float u_cold; if (a >= 0 && a < (N - 1)) { u_cold = mat->A1_u_cold[a]; u_cold += ((mat->A1_u_cold[b] - mat->A1_u_cold[a]) / drho) * (density - rho_min - a * drho); } else if (density < rho_min) { u_cold = mat->A1_u_cold[0]; } else { u_cold = mat->A1_u_cold[N - 1]; u_cold += ((mat->A1_u_cold[N - 1] - mat->A1_u_cold[N - 2]) / drho) * (density - rho_max); } return u_cold; } // gas_temperature_from_internal_energy INLINE static float Til_temperature_from_internal_energy( const float density, const float u, const struct Til_params *mat) { const float u_cold = compute_fast_u_cold(density, mat); float T = (u - u_cold) / (mat->C_V); if (T < 0.f) { T = 0.f; } return T; } // gas_pressure_from_density_and_temperature INLINE static float Til_pressure_from_temperature( const float density, const float T, const struct Til_params *mat) { const float u = compute_fast_u_cold(density, mat) + mat->C_V * T; const float P = Til_pressure_from_internal_energy(density, u, mat); return P; } // gas_density_from_pressure_and_temperature INLINE static float Til_density_from_pressure_and_temperature( const float P, const float T, const struct Til_params *mat) { float rho_min = mat->rho_min; float rho_max = mat->rho_max; float rho_mid = (rho_min + rho_max) / 2.f; // Check for P == 0 or T == 0 float P_des; if (P <= mat->P_min) { P_des = mat->P_min; } else { P_des = P; } float P_min = Til_pressure_from_temperature(rho_min, T, mat); float P_mid = Til_pressure_from_temperature(rho_mid, T, mat); float P_max = Til_pressure_from_temperature(rho_max, T, mat); // quick fix? if (P_des < P_min) { P_des = P_min; } const float tolerance = 0.001 * rho_min; int counter = 0; const int max_counter = 200; if (P_des >= P_min && P_des <= P_max) { while ((rho_max - rho_min) > tolerance && counter < max_counter) { P_min = Til_pressure_from_temperature(rho_min, T, mat); P_mid = Til_pressure_from_temperature(rho_mid, T, mat); P_max = Til_pressure_from_temperature(rho_max, T, mat); const float f0 = P_des - P_min; const float f2 = P_des - P_mid; if ((f0 * f2) > 0) { rho_min = rho_mid; } else { rho_max = rho_mid; } rho_mid = (rho_min + rho_max) / 2.f; counter += 1; } } else { error("Error in Til_density_from_pressure_and_temperature"); return 0.f; } return rho_mid; } // gas_density_from_pressure_and_internal_energy INLINE static float Til_density_from_pressure_and_internal_energy( const float P, const float u, const float rho_ref, const float rho_sph, const struct Til_params *mat) { if (P <= mat->P_min || u == 0) { return rho_sph; } // We start search on the same curve as rho_ref, since this is most likely // curve to find rho on float eta_ref = rho_ref / mat->rho_0; /* There are 5 possible curves: 1: cold_min 2: cold 3: hybrid_min 4: hybrid 5: hot These curves cover different eta ranges within three different regions of u: u REGION 1 (u < u_iv): eta < eta_min: cold_min eta_min < eta: cold u REGION 2 (u_iv < u < u_cv): eta < eta_min: hybrid_min eta_min < eta < 1: hybrid 1 < eta: cold u REGION 3 (u_cv < u): eta < 1: hot 1 < eta: cold NOTE: for a lot of EoS, eta_min = 0, so search this region last if given the option to save time for most EoS */ enum Til_region { Til_region_none, Til_region_cold_min, Til_region_cold, Til_region_hybrid_min, Til_region_hybrid, Til_region_hot }; // Based on our u region, what possible curves can rho be on? Ordered based on // order we search for roots. Numbers correspond to curves in order given // above. 0 is a dummy which breaks the loop. enum Til_region possible_curves[3]; // u REGION 1 if (u <= mat->u_iv) { if (eta_ref <= mat->eta_min) { possible_curves[0] = Til_region_cold_min; possible_curves[1] = Til_region_cold; possible_curves[2] = Til_region_none; } else { possible_curves[0] = Til_region_cold; possible_curves[1] = Til_region_cold_min; possible_curves[2] = Til_region_none; } // u REGION 2 } else if (u <= mat->u_cv) { if (eta_ref <= mat->eta_min) { possible_curves[0] = Til_region_hybrid_min; possible_curves[1] = Til_region_hybrid; possible_curves[2] = Til_region_cold; } else if (eta_ref <= 1) { possible_curves[0] = Til_region_hybrid; possible_curves[1] = Til_region_cold; possible_curves[2] = Til_region_hybrid_min; } else { possible_curves[0] = Til_region_cold; possible_curves[1] = Til_region_hybrid; possible_curves[2] = Til_region_hybrid_min; } // u REGION 3 } else { if (eta_ref <= 1) { possible_curves[0] = Til_region_hot; possible_curves[1] = Til_region_cold; possible_curves[2] = Til_region_none; } else { possible_curves[0] = Til_region_cold; possible_curves[1] = Til_region_hot; possible_curves[2] = Til_region_none; } } // Newton-Raphson const int max_iter = 10; const float tol = 1e-5; // loops over possible curves for (int i = 0; i < 3; i++) { enum Til_region curve = possible_curves[i]; // if there are only two possible curves, break when we get to three and // haven't found a root if (curve == Til_region_none) { break; } // Start iteration at reference value float rho_iter = rho_ref; // Constrain our initial guess to be on the curve we're currently looking at // in the first loop, this is already satisfied. if (i > 0) { // u REGION 1 if (u <= mat->u_iv) { if (curve == Til_region_cold_min) { if (rho_iter > mat->eta_min * mat->rho_0) { rho_iter = mat->eta_min * mat->rho_0; } } else if (curve == Til_region_cold) { if (rho_iter < mat->eta_min * mat->rho_0) { rho_iter = mat->eta_min * mat->rho_0; } } else { error("Error in Til_density_from_pressure_and_internal_energy"); } // u REGION 2 } else if (u <= mat->u_cv) { if (curve == Til_region_hybrid_min) { if (rho_iter > mat->eta_min * mat->rho_0) { rho_iter = mat->eta_min * mat->rho_0; } } else if (curve == Til_region_hybrid) { if (rho_iter < mat->eta_min * mat->rho_0) { rho_iter = mat->eta_min * mat->rho_0; } if (rho_iter > mat->rho_0) { rho_iter = mat->rho_0; } } else if (curve == Til_region_cold) { if (rho_iter < mat->rho_0) { rho_iter = mat->rho_0; } } else { error("Error in Til_density_from_pressure_and_internal_energy"); } // u REGION 3 } else { if (curve == Til_region_hot) { if (rho_iter > mat->rho_0) { rho_iter = mat->rho_0; } } else if (curve == Til_region_cold) { if (rho_iter < mat->rho_0) { rho_iter = mat->rho_0; } } else { error("Error in Til_density_from_pressure_and_internal_energy"); } } } // Set this to an arbitrary number so we definitely dont't think we converge // straigt away float last_rho_iter = -1e5; // Now iterate for (int j = 0; j < max_iter; j++) { const float eta_iter = rho_iter / mat->rho_0; const float eta_iter_sq = eta_iter * eta_iter; const float mu_iter = eta_iter - 1.0f; const float nu_iter = 1.0f / eta_iter - 1.0f; const float w_iter = u / (mat->u_0 * eta_iter_sq) + 1.0f; const float w_iter_inv = 1.0f / w_iter; const float exp1 = expf(-mat->beta * nu_iter); const float exp2 = expf(-mat->alpha * nu_iter * nu_iter); // Derivatives const float dw_inv_drho_iter = (2.f * mat->u_0 * u * eta_iter / mat->rho_0) / ((u + mat->u_0 * eta_iter_sq) * (u + mat->u_0 * eta_iter_sq)); const float dmu_drho_iter = 1.f / mat->rho_0; const float dmu_sq_drho_iter = 2.f * rho_iter / (mat->rho_0 * mat->rho_0) - 2.f / mat->rho_0; const float dexp1_drho_iter = mat->beta * mat->rho_0 * exp1 / (rho_iter * rho_iter); const float dexp2_drho_iter = 2.f * mat->alpha * mat->rho_0 * (mat->rho_0 - rho_iter) * exp2 / (rho_iter * rho_iter * rho_iter); // Use P_fraction to determine whether we've converged on a root float P_fraction; // Newton-Raphson float P_c_iter, dP_c_drho_iter, P_h_iter, dP_h_drho_iter; // if "cold" or "hybrid" if (curve == Til_region_cold || curve == Til_region_hybrid) { P_c_iter = (mat->a + mat->b * w_iter_inv) * rho_iter * u + mat->A * mu_iter + mat->B * mu_iter * mu_iter - P; dP_c_drho_iter = (mat->a + mat->b * w_iter_inv) * u + mat->b * u * rho_iter * dw_inv_drho_iter + mat->A * dmu_drho_iter + mat->B * dmu_sq_drho_iter; P_fraction = P_c_iter / P; // if curve is cold then we've got everything we need if (curve == Til_region_cold) { rho_iter -= P_c_iter / dP_c_drho_iter; // Don't use these: P_h_iter = 0.f; dP_h_drho_iter = 0.f; } // if "cold_min" or "hybrid_min" // Can only have one version of the cold curve, therefore either use the // min version or the normal version hence "else if" } else if (curve == Til_region_cold_min || curve == Til_region_hybrid_min) { P_c_iter = ((mat->a + mat->b * w_iter_inv) * rho_iter * u + mat->A * mu_iter + mat->B * mu_iter * mu_iter) * (eta_iter - mat->eta_zero) / (mat->eta_min - mat->eta_zero) - P; dP_c_drho_iter = ((mat->a + mat->b * w_iter_inv) * u + mat->b * u * rho_iter * dw_inv_drho_iter + mat->A * dmu_drho_iter + mat->B * dmu_sq_drho_iter) * (eta_iter - mat->eta_zero) / (mat->eta_min - mat->eta_zero) + ((mat->a + mat->b * w_iter_inv) * rho_iter * u + mat->A * mu_iter + mat->B * mu_iter * mu_iter) * (1 / (mat->rho_0 * (mat->eta_min - mat->eta_zero))); P_fraction = P_c_iter / P; // if curve is cold_min then we've got everything we need if (curve == Til_region_cold_min) { rho_iter -= P_c_iter / dP_c_drho_iter; // Don't use these: P_c_iter = 0.f; dP_c_drho_iter = 0.f; } } // if "hybrid_min" or "hybrid" or "hot" if (curve == Til_region_hybrid_min || curve == Til_region_hybrid || curve == Til_region_hot) { P_h_iter = mat->a * rho_iter * u + (mat->b * rho_iter * u * w_iter_inv + mat->A * mu_iter * exp1) * exp2 - P; dP_h_drho_iter = mat->a * u + (mat->b * u * w_iter_inv + mat->b * u * rho_iter * dw_inv_drho_iter + mat->A * mu_iter * dexp1_drho_iter + mat->A * exp1 * dmu_drho_iter) * exp2 + (mat->b * rho_iter * u * w_iter_inv + mat->A * mu_iter * exp1) * dexp2_drho_iter; P_fraction = P_h_iter / P; // if curve is hot then we've got everything we need if (curve == Til_region_hot) { rho_iter -= P_h_iter / dP_h_drho_iter; } } // If we are on a hybrid or hybrid_min curve, we combie hot and cold // curves if (curve == Til_region_hybrid_min || curve == Til_region_hybrid) { const float P_hybrid_iter = ((u - mat->u_iv) * P_h_iter + (mat->u_cv - u) * P_c_iter) / (mat->u_cv - mat->u_iv); const float dP_hybrid_drho_iter = ((u - mat->u_iv) * dP_h_drho_iter + (mat->u_cv - u) * dP_c_drho_iter) / (mat->u_cv - mat->u_iv); rho_iter -= P_hybrid_iter / dP_hybrid_drho_iter; P_fraction = P_hybrid_iter / P; } // Now we have to constrain the new rho_iter to the curve we're on // u REGION 1 if (u <= mat->u_iv) { if (curve == Til_region_cold_min) { if (rho_iter > mat->eta_min * mat->rho_0) { rho_iter = mat->eta_min * mat->rho_0; } } else if (curve == Til_region_cold) { if (rho_iter < mat->eta_min * mat->rho_0) { rho_iter = mat->eta_min * mat->rho_0; } } else { error("Error in Til_density_from_pressure_and_internal_energy"); } // u REGION 2 } else if (u <= mat->u_cv) { if (curve == Til_region_hybrid_min) { if (rho_iter > mat->eta_min * mat->rho_0) { rho_iter = mat->eta_min * mat->rho_0; } } else if (curve == Til_region_hybrid) { if (rho_iter < mat->eta_min * mat->rho_0) { rho_iter = mat->eta_min * mat->rho_0; } if (rho_iter > mat->rho_0) { rho_iter = mat->rho_0; } } else if (curve == Til_region_cold) { if (rho_iter < mat->rho_0) { rho_iter = mat->rho_0; } } else { error("Error in Til_density_from_pressure_and_internal_energy"); } // u REGION 3 } else { if (curve == Til_region_hot) { if (rho_iter > mat->rho_0) { rho_iter = mat->rho_0; } } else if (curve == Til_region_cold) { if (rho_iter < mat->rho_0) { rho_iter = mat->rho_0; } } else { error("Error in Til_density_from_pressure_and_internal_energy"); } } // Either we've converged ... if (fabsf(P_fraction) < tol) { return rho_iter; // ... or we're stuck at the boundary ... } else if (rho_iter == last_rho_iter) { break; } // ... or we loop again last_rho_iter = rho_iter; } } return rho_sph; } #endif /* SWIFT_TILLOTSON_EQUATION_OF_STATE_H */