From 5b379910c7141ac4524ea06e6edba9544a2b6e34 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Sat, 8 Nov 2025 13:09:17 +0000 Subject: [PATCH 01/37] Begin Kiara rebase from https://github.com/romeeld/swiftsim/tree/master --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index f9975dd2d2..6cd850b646 100644 --- a/configure.ac +++ b/configure.ac @@ -2279,6 +2279,9 @@ case "$with_hydro" in gasoline) AC_DEFINE([GASOLINE_SPH], [1], [Gasoline SPH]) ;; + magma2) + AC_DEFINE([MAGMA2_SPH], [1], [MAGMA2 SPH]) + ;; anarchy-pu) AC_DEFINE([ANARCHY_PU_SPH], [1], [ANARCHY (PU) SPH]) ;; From f0e6d35c22b0b76dd545352cee659f205063f980 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Sat, 8 Nov 2025 13:21:07 +0000 Subject: [PATCH 02/37] Add MAGMA2 hydro (including target_neighbours option) --- src/debug.c | 2 + .../roberts_flow_acceleration/forcing.h | 5 + src/hydro.h | 4 + src/hydro/MAGMA2/hydro.h | 2383 +++++++++++++++++ src/hydro/MAGMA2/hydro_debug.h | 66 + src/hydro/MAGMA2/hydro_iact.h | 1549 +++++++++++ src/hydro/MAGMA2/hydro_io.h | 380 +++ src/hydro/MAGMA2/hydro_parameters.h | 297 ++ src/hydro/MAGMA2/hydro_part.h | 366 +++ src/hydro_csds.h | 2 + src/hydro_io.h | 2 + src/hydro_parameters.h | 2 + src/hydro_properties.c | 39 +- src/part.h | 4 + src/sink/GEAR/sink_getters.h | 3 + src/star_formation/GEAR/star_formation.h | 3 + 16 files changed, 5105 insertions(+), 2 deletions(-) create mode 100644 src/hydro/MAGMA2/hydro.h create mode 100644 src/hydro/MAGMA2/hydro_debug.h create mode 100644 src/hydro/MAGMA2/hydro_iact.h create mode 100644 src/hydro/MAGMA2/hydro_io.h create mode 100644 src/hydro/MAGMA2/hydro_parameters.h create mode 100644 src/hydro/MAGMA2/hydro_part.h diff --git a/src/debug.c b/src/debug.c index 2db8f69dff..32db343678 100644 --- a/src/debug.c +++ b/src/debug.c @@ -80,6 +80,8 @@ #include "./hydro/SPHENIX/hydro_debug.h" #elif defined(GASOLINE_SPH) #include "./hydro/Gasoline/hydro_debug.h" +#elif defined(MAGMA2_SPH) +#include "./hydro/MAGMA2/hydro_debug.h" #elif defined(ANARCHY_PU_SPH) #include "./hydro/AnarchyPU/hydro_debug.h" #else diff --git a/src/forcing/roberts_flow_acceleration/forcing.h b/src/forcing/roberts_flow_acceleration/forcing.h index a4115170e1..1d911d2c23 100644 --- a/src/forcing/roberts_flow_acceleration/forcing.h +++ b/src/forcing/roberts_flow_acceleration/forcing.h @@ -87,8 +87,13 @@ __attribute__((always_inline)) INLINE static void forcing_terms_apply( /* Effective viscosity from artificial viscosity, as in eq. 100 from * arXiv:1012.1885 */ +#ifndef MAGMA2_SPH const float nu = terms->nu * p->viscosity.alpha * c_s * p->h / (2.f * (hydro_dimension + 2.f)); +#else + const float nu = terms->nu * p->viscosity.alpha * c_s * p->h / + (2.f * (hydro_dimension + 2.f)); +#endif const float Vz_factor = terms->Vz_factor; const double k0 = (2. * M_PI / L) * terms->kv; diff --git a/src/hydro.h b/src/hydro.h index c82c402e81..8f7b07e554 100644 --- a/src/hydro.h +++ b/src/hydro.h @@ -82,6 +82,10 @@ #include "./hydro/Gasoline/hydro.h" #include "./hydro/Gasoline/hydro_iact.h" #define SPH_IMPLEMENTATION "Gasoline-2 (Wadsley+ 2017)" +#elif defined(MAGMA2_SPH) +#include "./hydro/MAGMA2/hydro.h" +#include "./hydro/MAGMA2/hydro_iact.h" +#define SPH_IMPLEMENTATION "MAGMA2 (Rosswog 2020)" #elif defined(ANARCHY_PU_SPH) #include "./hydro/AnarchyPU/hydro.h" #include "./hydro/AnarchyPU/hydro_iact.h" diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h new file mode 100644 index 0000000000..592e06a6a3 --- /dev/null +++ b/src/hydro/MAGMA2/hydro.h @@ -0,0 +1,2383 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2025 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_MAGMA2_HYDRO_H +#define SWIFT_MAGMA2_HYDRO_H + +/** + * @file MAGMA2/hydro.h + * @brief Density-Energy non-conservative implementation of SPH, + * with added MAGMA2 physics (Rosswog 2020) (Non-neighbour loop + * equations) + */ + +#include "adiabatic_index.h" +#include "approx_math.h" +#include "cosmology.h" +#include "dimension.h" +#include "entropy_floor.h" +#include "equation_of_state.h" +#include "hydro_parameters.h" +#include "hydro_properties.h" +#include "hydro_space.h" +#include "kernel_hydro.h" +#include "minmax.h" +#include "pressure_floor.h" + +#include +#include +#include +#include + +/** + * @brief Returns the comoving internal energy of a particle at the last + * time the particle was kicked. + * + * For implementations where the main thermodynamic variable + * is not internal energy, this function computes the internal + * energy from the thermodynamic variable. + * + * @param p The particle of interest + * @param xp The extended data of the particle of interest. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy(const struct part *restrict p, + const struct xpart *restrict xp) { + return xp->u_full; +} + +/** + * @brief Returns the physical internal energy of a particle at the last + * time the particle was kicked. + * + * For implementations where the main thermodynamic variable + * is not internal energy, this function computes the internal + * energy from the thermodynamic variable and converts it to + * physical coordinates. + * + * @param p The particle of interest. + * @param xp The extended data of the particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy(const struct part *restrict p, + const struct xpart *restrict xp, + const struct cosmology *cosmo) { + return xp->u_full * cosmo->a_factor_internal_energy; +} + +/** + * @brief Returns the comoving internal energy of a particle drifted to the + * current time. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_comoving_internal_energy(const struct part *restrict p) { + return p->u; +} + +/** + * @brief Returns the physical internal energy of a particle drifted to the + * current time. + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_physical_internal_energy(const struct part *restrict p, + const struct cosmology *cosmo) { + return p->u * cosmo->a_factor_internal_energy; +} + +/** + * @brief Returns the comoving pressure of a particle + * + * Computes the pressure based on the particle's properties. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure( + const struct part *restrict p) { + return gas_pressure_from_internal_energy(p->rho, p->u); +} + +/** + * @brief Returns the physical pressure of a particle + * + * Computes the pressure based on the particle's properties and + * convert it to physical coordinates. + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure( + const struct part *restrict p, const struct cosmology *cosmo) { + return cosmo->a_factor_pressure * hydro_get_comoving_pressure(p); +} + +/** + * @brief Returns the comoving entropy of a particle at the last + * time the particle was kicked. + * + * For implementations where the main thermodynamic variable + * is not entropy, this function computes the entropy from + * the thermodynamic variable. + * + * @param p The particle of interest + * @param xp The extended data of the particle of interest. + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( + const struct part *restrict p, const struct xpart *restrict xp) { + return gas_entropy_from_internal_energy(p->rho, xp->u_full); +} + +/** + * @brief Returns the physical entropy of a particle at the last + * time the particle was kicked. + * + * For implementations where the main thermodynamic variable + * is not entropy, this function computes the entropy from + * the thermodynamic variable and converts it to + * physical coordinates. + * + * @param p The particle of interest. + * @param xp The extended data of the particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( + const struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { + /* Note: no cosmological conversion required here with our choice of + * coordinates. */ + return gas_entropy_from_internal_energy(p->rho, xp->u_full); +} + +/** + * @brief Returns the comoving entropy of a particle drifted to the + * current time. + * + * @param p The particle of interest. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_comoving_entropy(const struct part *restrict p) { + return gas_entropy_from_internal_energy(p->rho, p->u); +} + +/** + * @brief Returns the physical entropy of a particle drifted to the + * current time. + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_physical_entropy(const struct part *restrict p, + const struct cosmology *cosmo) { + /* Note: no cosmological conversion required here with our choice of + * coordinates. */ + return gas_entropy_from_internal_energy(p->rho, p->u); +} + +/** + * @brief Returns the comoving sound speed of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_soundspeed(const struct part *restrict p) { + return gas_soundspeed_from_internal_energy(p->rho, p->u); +} + +/** + * @brief Returns the physical sound speed of a particle + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_soundspeed(const struct part *restrict p, + const struct cosmology *cosmo) { + return cosmo->a_factor_sound_speed * hydro_get_comoving_soundspeed(p); +} + +/** + * @brief Returns the comoving density of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_density( + const struct part *restrict p) { + return p->rho; +} + +/** + * @brief Returns the comoving density of a particle. + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_density( + const struct part *restrict p, const struct cosmology *cosmo) { + return cosmo->a3_inv * p->rho; +} + +/** + * @brief Returns the mass of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_mass( + const struct part *restrict p) { + return p->mass; +} + +/** + * @brief Sets the mass of a particle + * + * @param p The particle of interest + * @param m The mass to set. + */ +__attribute__((always_inline)) INLINE static void hydro_set_mass( + struct part *restrict p, float m) { + p->mass = m; +} + +/** + * @brief Returns the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy_dt(const struct part *restrict p) { + return p->u_dt; +} + +/** + * @brief Returns the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest + * @param cosmo Cosmology data structure + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy_dt(const struct part *restrict p, + const struct cosmology *cosmo) { + return p->u_dt * cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest. + * @param du_dt The new time derivative of the internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) { + p->u_dt = du_dt; +} + +/** + * @brief Returns the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param du_dt The new time derivative of the internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy_dt(struct part *restrict p, + const struct cosmology *cosmo, + float du_dt) { + p->u_dt = du_dt / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const float entropy) { + /* Note there is no conversion from physical to comoving entropy */ + const float comoving_entropy = entropy; + xp->u_full = gas_internal_energy_from_entropy(p->rho, comoving_entropy); +} + +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy(struct part *p, struct xpart *xp, + const struct cosmology *cosmo, + const float u) { + xp->u_full = u / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param pressure_floor The properties of the pressure floor. + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_drifted_physical_internal_energy( + struct part *p, const struct cosmology *cosmo, + const struct pressure_floor_props *pressure_floor, const float u) { + /* There is no need to use the floor here as this function is called in the + * feedback, so the new value of the internal energy should be strictly + * higher than the old value. */ + + p->u = u / cosmo->a_factor_internal_energy; + + /* Now recompute the extra quantities */ + + /* Compute the sound speed */ + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + const float pressure_including_floor = + pressure_floor_get_comoving_pressure(p, pressure_floor, pressure, cosmo); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, pressure_including_floor); + + /* Update variables. */ + p->force.soundspeed = soundspeed; + p->force.pressure = pressure_including_floor; + + /* Signal velocity */ + const float v_sig = const_viscosity_alpha_prefactor * soundspeed; + + p->dt_min = min(p->dt_min, p->h_min / v_sig); +} + +/** + * @brief Correct the signal velocity of the particle partaking in + * supernova (kinetic) feedback based on the velocity kick the particle receives + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param dv_phys The velocity kick received by the particle expressed in + * physical units (note that dv_phys must be positive or equal to zero) + */ +__attribute__((always_inline)) INLINE static void +hydro_set_v_sig_based_on_velocity_kick(struct part *p, + const struct cosmology *cosmo, + const float dv_phys) { + /* Compute the velocity kick in comoving coordinates */ + const float dv = dv_phys / cosmo->a_factor_sound_speed; + + /* Sound speed */ + const float soundspeed = hydro_get_comoving_soundspeed(p); + + /* Signal speed */ + const float v_sig_sound = const_viscosity_alpha_prefactor * soundspeed; + const float v_sig_kick = const_viscosity_beta_prefactor * dv; + const float v_sig = v_sig_sound + v_sig_kick; + + p->dt_min = min(p->dt_min, p->h_min / v_sig); +} + +/** + * @brief Update the value of the viscosity alpha for the scheme. + * + * @param p the particle of interest + * @param alpha the new value for the viscosity coefficient. + */ +__attribute__((always_inline)) INLINE static void hydro_set_viscosity_alpha( + struct part *restrict p, float alpha) { } + +/** + * @brief Update the value of the diffusive coefficients to the + * feedback reset value for the scheme. + * + * @param p the particle of interest + */ +__attribute__((always_inline)) INLINE static void +hydro_diffusive_feedback_reset(struct part *restrict p) { + /* Set the viscosity to the max, and the diffusion to the min */ + hydro_set_viscosity_alpha(p, const_viscosity_alpha); +} + +/** + * @brief Computes the hydro time-step of a given particle + * + * This function returns the time-step of a particle given its hydro-dynamical + * state. A typical time-step calculation would be the use of the CFL condition. + * + * @param p Pointer to the particle data + * @param xp Pointer to the extended particle data + * @param hydro_properties The SPH parameters + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_compute_timestep( + const struct part *restrict p, const struct xpart *restrict xp, + const struct hydro_props *restrict hydro_properties, + const struct cosmology *restrict cosmo) { + + //if (p->dt_min == 0.f || p->decoupled || p->feedback_data.decoupling_delay_time > 0.f) return FLT_MAX; + if (p->dt_min == 0.f) return FLT_MAX; + + /* Use full kernel support and physical time */ + const float conv = kernel_gamma * cosmo->a / cosmo->a_factor_sound_speed; + + /* CFL condition */ + const float dt_cfl = 2. * hydro_properties->CFL_condition * conv * p->dt_min; + + return dt_cfl; +} + +/** + * @brief Compute the signal velocity between two gas particles + * + * @param dx Comoving vector separating both particles (pi - pj). + * @brief pi The first #part. + * @brief pj The second #part. + * @brief mu_ij The velocity on the axis linking the particles, or zero if the + * particles are moving away from each other, + * @brief beta The non-linear viscosity constant. + */ +__attribute__((always_inline)) INLINE static float hydro_signal_velocity( + const float dx[3], const struct part *restrict pi, + const struct part *restrict pj, const float mu_ij, const float beta) { + + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + const float c_ij = 0.5 * (ci + cj); + + const float v_sig_alpha = const_viscosity_alpha_prefactor * c_ij; + const float v_sig_beta = const_viscosity_beta_prefactor * mu_ij; + const float v_sig = v_sig_alpha - fmin(v_sig_beta, 0.f); + + return v_sig; +} + +/** + * @brief Returns the physical velocity divergence. + * + * @brief p The particle + */ +__attribute__((always_inline)) INLINE static float hydro_get_div_v( + const struct part *restrict p) { + + return p->gradients.velocity_tensor[0][0] + + p->gradients.velocity_tensor[1][1] + + p->gradients.velocity_tensor[2][2]; +} + +/** + * @brief Returns the physical velocity divergence. + * + * @brief p The particle + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_div_v( + const struct part *restrict p, const struct cosmology* cosmo) { + + const float div_v = hydro_get_div_v(p); + return div_v * cosmo->a2_inv + hydro_dimension * cosmo->H; +} + +/** + * @brief Does some extra hydro operations once the actual physical time step + * for the particle is known. + * + * @param p The particle to act upon. + * @param dt Physical time step of the particle during the next step. + */ +__attribute__((always_inline)) INLINE static void hydro_timestep_extra( + struct part *p, float dt) {} + +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + * @param time The simulation time. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part *p, const struct xpart *xp, const double time) {} + +/** + * @brief Prepares a particle for the density calculation. + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the various density loop over neighbours. Typically, all fields of the + * density sub-structure of a particle get zeroed in here. + * + * @param p The particle to act upon + * @param hs #hydro_space containing hydro specific space information. + */ +__attribute__((always_inline)) INLINE static void hydro_init_part( + struct part *restrict p, const struct hydro_space *hs) { + p->density.wcount = 0.f; + p->density.wcount_dh = 0.f; + p->rho = 0.f; + p->rho_gradient[0] = 0.f; + p->rho_gradient[1] = 0.f; + p->rho_gradient[2] = 0.f; + p->density.rho_dh = 0.f; +#ifdef MAGMA2_DEBUG_CHECKS + p->debug.num_ngb = 0; +#endif + +#ifdef hydro_props_use_adiabatic_correction + p->gradients.adiabatic_f_numerator = 0.; +#endif + + p->gradients.wcount = 0.; + p->gradients.u_well_conditioned = 1; + p->gradients.D_well_conditioned = 1; + + p->gradients.du_min = FLT_MAX; + p->gradients.du_max = -FLT_MAX; + p->gradients.kernel_size = FLT_MIN; + + /* These must be zeroed before the density loop */ + for (int i = 0; i < 3; i++) { + p->gradients.u_aux[i] = 0.; + p->gradients.u_aux_norm[i] = 0.; + + p->gradients.dv_min[i] = FLT_MAX; + p->gradients.dv_max[i] = -FLT_MAX; + + for (int j = 0; j < 3; j++) { + p->gradients.velocity_tensor_aux[i][j] = 0.; + p->gradients.velocity_tensor_aux_norm[i][j] = 0.; + } + } +} + +/** + * @brief Computes the condition number of a matrix A + * + * + * @param A The matrix to compute the condition number of. + */ +__attribute__((always_inline)) INLINE static +double condition_number(gsl_matrix *A) { + + gsl_matrix *A_copy = gsl_matrix_alloc(3, 3); + gsl_matrix_memcpy(A_copy, A); + + gsl_vector *S = gsl_vector_alloc(3); + gsl_vector *work = gsl_vector_alloc(3); + gsl_matrix *V = gsl_matrix_alloc(3, 3); + + gsl_linalg_SV_decomp(A_copy, V, S, work); + + double s_max = gsl_vector_get(S, 0); + double s_min = gsl_vector_get(S, 2); + + gsl_matrix_free(A_copy); + gsl_matrix_free(V); + gsl_vector_free(S); + gsl_vector_free(work); + + return (s_min != 0.) ? s_max / s_min : const_condition_number_upper_limit; +} + +/** + * @brief Vector dot product of two 3D vectors. + * + * + * @param vec_a The first vector. + * @param vec_b The second vector. + * @param result The result of the dot product. + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_vec3_vec3_dot(const hydro_real_t *restrict vec_a, + const hydro_real_t *restrict vec_b) { + + return vec_a[0] * vec_b[0] + vec_a[1] * vec_b[1] + vec_a[2] * vec_b[2]; +} + +/** + * @brief Norm of two 3D vectors. + * + * + * @param vec The vector. + * @param result The result of the norm. + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_vec3_norm(const hydro_real_t *restrict vec) { + + return sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]); +} + +/** + * @brief Unit vector of the given vector. + * + * + * @param vec The vector. + * @param result The unit vector of vec + */ +__attribute__((always_inline)) INLINE static +void hydro_vec3_unit(const hydro_real_t *restrict vec, + hydro_real_t *restrict result) { + + result[0] = 0.; + result[1] = 0.; + result[2] = 0.; + + const hydro_real_t vec_norm = hydro_vec3_norm(vec); + if (vec_norm > 0.) { + result[0] = vec[0] / vec_norm; + result[1] = vec[1] / vec_norm; + result[2] = vec[2] / vec_norm; + } +} + +/** + * @brief The Frobenius inner product of two matrices. + * + * + * @param mat The matrix to contract with the vector. + * @param vec The vector to contract with the matrix. + * @param result The result of the contraction. + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_mat3x3_mat3x3_dot(const hydro_real_t (*restrict mat_a)[3], + const hydro_real_t (*restrict mat_b)[3]) { + + return mat_a[0][0] * mat_b[0][0] + + mat_a[0][1] * mat_b[0][1] + + mat_a[0][2] * mat_b[0][2] + + mat_a[1][0] * mat_b[1][0] + + mat_a[1][1] * mat_b[1][1] + + mat_a[1][2] * mat_b[1][2] + + mat_a[2][0] * mat_b[2][0] + + mat_a[2][1] * mat_b[2][1] + + mat_a[2][2] * mat_b[2][2]; +} + +/** + * @brief Contracts the last index of matrix mat with a vector vec and stores in + * result. + * + * + * @param mat The matrix to contract with the vector. + * @param vec The vector to contract with the matrix. + * @param result The result of the contraction. + */ +__attribute__((always_inline)) INLINE static void hydro_mat3x3_vec3_dot( + const hydro_real_t (*restrict mat)[3], + const hydro_real_t *restrict vec, + hydro_real_t *restrict result) { + + result[0] = mat[0][0] * vec[0] + mat[0][1] * vec[1] + mat[0][2] * vec[2]; + result[1] = mat[1][0] * vec[0] + mat[1][1] * vec[1] + mat[1][2] * vec[2]; + result[2] = mat[2][0] * vec[0] + mat[2][1] * vec[1] + mat[2][2] * vec[2]; +} + +/** + * @brief Contracts the last two indices of the tensor tensor with a matrix + * mat and stores in result. Form: mat^T * tensor * mat. + * + * + * @param tensor The tensor to contract with the matrix. + * @param mat The matrix to contract with the tensor. + * @param result The result of the contraction. + */ +__attribute__((always_inline)) INLINE static +void hydro_tensor3x3x3_matrix3x3_dot( + const hydro_real_t (*restrict tensor)[3][3], + const hydro_real_t (*restrict mat)[3], + hydro_real_t *restrict result) { + + result[0] = tensor[0][0][0] * mat[0][0] + + tensor[0][0][1] * mat[0][1] + + tensor[0][0][2] * mat[0][2] + + tensor[0][1][0] * mat[1][0] + + tensor[0][1][1] * mat[1][1] + + tensor[0][1][2] * mat[1][2] + + tensor[0][2][0] * mat[2][0] + + tensor[0][2][1] * mat[2][1] + + tensor[0][2][2] * mat[2][2]; + + result[1] = tensor[1][0][0] * mat[0][0] + + tensor[1][0][1] * mat[0][1] + + tensor[1][0][2] * mat[0][2] + + tensor[1][1][0] * mat[1][0] + + tensor[1][1][1] * mat[1][1] + + tensor[1][1][2] * mat[1][2] + + tensor[1][2][0] * mat[2][0] + + tensor[1][2][1] * mat[2][1] + + tensor[1][2][2] * mat[2][2]; + + result[2] = tensor[2][0][0] * mat[0][0] + + tensor[2][0][1] * mat[0][1] + + tensor[2][0][2] * mat[0][2] + + tensor[2][1][0] * mat[1][0] + + tensor[2][1][1] * mat[1][1] + + tensor[2][1][2] * mat[1][2] + + tensor[2][2][0] * mat[2][0] + + tensor[2][2][1] * mat[2][1] + + tensor[2][2][2] * mat[2][2]; +} + +/** + * @brief Constructs the outer product of two 3D vectors. + * + * + * @param vec_a The first vector. + * @param vec_b The second vector. + * @param result The result of the outer product. + */ +__attribute__((always_inline)) INLINE static void hydro_vec3_vec3_outer( + const hydro_real_t *restrict vec_a, + const hydro_real_t *restrict vec_b, + hydro_real_t (*restrict result)[3]) { + + result[0][0] = vec_a[0] * vec_b[0]; + result[0][1] = vec_a[0] * vec_b[1]; + result[0][2] = vec_a[0] * vec_b[2]; + + result[1][0] = vec_a[1] * vec_b[0]; + result[1][1] = vec_a[1] * vec_b[1]; + result[1][2] = vec_a[1] * vec_b[2]; + + result[2][0] = vec_a[2] * vec_b[0]; + result[2][1] = vec_a[2] * vec_b[1]; + result[2][2] = vec_a[2] * vec_b[2]; +} + +/** + * @brief Limit gradients to variation across the kernel. + * + * + * @param df_raw Raw value + * @param df_reconstructed Reconstructed value + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_scalar_minmod(const hydro_real_t df_raw, + const hydro_real_t df_reconstructed) { + + if (df_raw * df_reconstructed <= 0.) return 0.; + + return (fabs(df_raw) < fabs(df_reconstructed)) ? df_raw : df_reconstructed; +} + +/** + * @brief Limit gradients to variation across the kernel. + * + * + * @param df_reconstructed Reconstructed estimate of df + * @param df_raw Particle estimate of df + * @param df_min_i Minimum value of df_raw across the kernel. + * @param df_max_i Maximum value of df_raw across the kernel. + * @param df_min_j Minimum value of df_raw across the kernel. + * @param df_max_j Maximum value of df_raw across the kernel + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_scalar_minmod_limiter(const hydro_real_t df_reconstructed, + const hydro_real_t df_raw, + const hydro_real_t df_min_i, + const hydro_real_t df_max_i, + const hydro_real_t df_min_j, + const hydro_real_t df_max_j) { + +#ifdef hydro_props_use_strict_minmod_limiter + hydro_real_t df = hydro_scalar_minmod(df_raw, df_reconstructed); + + const hydro_real_t lo = fmax(df_min_i, -df_max_j); + const hydro_real_t hi = fmin(df_max_i, -df_min_j); + + if (lo > hi) return df_raw; + + if (df < lo) df = lo; + if (df > hi) df = hi; + + return df; +#else + return df_reconstructed; +#endif +} + +/** + * @brief Limit variations across the kernel (for 3-vectors) + * + * + * @param dv_reconstructed Reconstructed estimate of df + * @param dv_raw Particle estimate of df + * @param dv_min_i Minimum value of df_raw across the kernel. + * @param dv_max_i Maximum value of df_raw across the kernel. + * @param dv_min_j Minimum value of df_raw across the kernel. + * @param dv_max_j Maximum value of df_raw across the kernel + * @param dv_ij The vector to return. + */ +__attribute__((always_inline)) INLINE static +void hydro_vec_minmod_limiter(const hydro_real_t *restrict dv_reconstructed, + const hydro_real_t *restrict dv_raw, + const hydro_real_t *restrict dv_min_i, + const hydro_real_t *restrict dv_max_i, + const hydro_real_t *restrict dv_min_j, + const hydro_real_t *restrict dv_max_j, + hydro_real_t *restrict dv_ij) { + + dv_ij[0] = hydro_scalar_minmod_limiter(dv_reconstructed[0], dv_raw[0], + dv_min_i[0], dv_max_i[0], + dv_min_j[0], dv_max_j[0]); + dv_ij[1] = hydro_scalar_minmod_limiter(dv_reconstructed[1], dv_raw[1], + dv_min_i[1], dv_max_i[1], + dv_min_j[1], dv_max_j[1]); + dv_ij[2] = hydro_scalar_minmod_limiter(dv_reconstructed[2], dv_raw[2], + dv_min_i[2], dv_max_i[2], + dv_min_j[2], dv_max_j[2]); + + /* If any of the components are exactly zero, we return a zero vector */ + if (dv_ij[0] == 0. || dv_ij[1] == 0. || dv_ij[2] == 0.) { + dv_ij[0] = 0.; + dv_ij[1] = 0.; + dv_ij[2] = 0.; + } +} + +/** + * @brief Limit gradients to variation across the kernel. + * + * + * @param df_min Minimum value of delta f across the kernel. + * @param df_max Maximum value of delta f across the kernel. + * @param kernel_size Interaction distance across the kernel. + * @param grad The vector gradient to slope limit. + */ +__attribute__((always_inline)) INLINE static +void hydro_vec_slope_limiter(const hydro_real_t df_min, + const hydro_real_t df_max, + const hydro_real_t kernel_size, + hydro_real_t *restrict grad) { + +#ifdef hydro_props_use_extra_slope_limiter + const hydro_real_t grad_norm = hydro_vec3_norm(grad); + const hydro_real_t length = const_grad_overshoot_length * kernel_size; + + /* Nothing to do if there is no gradient or no look-ahead distance */ + if (grad_norm > 0. && length > 0.) { + const hydro_real_t df_min_abs = (df_min < 0.) ? fabs(df_min) : 0.; + const hydro_real_t df_max_abs = (df_max > 0.) ? fabs(df_max) : 0.; + const hydro_real_t df_abs_min = fmin(df_min_abs, df_max_abs); + + hydro_real_t bound = df_abs_min; + +#ifdef const_grad_overshoot_tolerance + const hydro_real_t tolerance = const_grad_overshoot_tolerance; + if (tolerance > 0.) { + const hydro_real_t df_abs_max = fmax(df_min_abs, df_max_abs); + const hydro_real_t extra = tolerance * df_abs_max; + const hydro_real_t cap = + (df_abs_min + extra < df_abs_max) ? df_abs_min + extra : df_abs_max; + bound = cap; + } +#endif + + const hydro_real_t limiter = + (bound > 0.) ? (bound / (length * grad_norm)) : 0.; + + if (limiter < 1.) { + grad[0] *= limiter; + grad[1] *= limiter; + grad[2] *= limiter; + } + } +#endif + +} + +/** + * @brief Computes Phi_ab from Rosswog 2020 21 for a field given A. This is the + * van Leer 1974 slope limiting procedure. + * + * + * @param A_ij The ratio of the gradients of the two particles. + * @param eta_i The normed smoothing length of the first particle. + * @param eta_j The normed smoothing length of the second particle. + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_van_leer_phi(const hydro_real_t A_ij, + const hydro_real_t eta_i, + const hydro_real_t eta_j) { + + hydro_real_t phi_raw = (4. * A_ij) / ((1. + A_ij) * (1. + A_ij)); + phi_raw = fmin(1., phi_raw); + phi_raw = fmax(0., phi_raw); + + /* η_ab and η_crit damping */ + const hydro_real_t eta_ij = min(eta_i, eta_j); + + hydro_real_t damping = 1.; + if (eta_ij <= const_slope_limiter_eta_crit) { + const hydro_real_t diff = + (eta_ij - const_slope_limiter_eta_crit) / const_slope_limiter_eta_fold; + damping = exp(-diff * diff); + } + + phi_raw *= damping; + + /* Handle any edge cases */ + phi_raw = fmin(1., phi_raw); + phi_raw = fmax(0., phi_raw); + + return phi_raw; +} + +/** + * @brief Computes A_ab from Rosswog 2020 Eq. 22 for a scalar field. + * + * + * @param grad_i The gradient of the quantity for the first particle. + * @param grad_j The gradient of the quantity for the second particle. + * @param dx The distance vector between the two particles ( ri - rj ). + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_scalar_van_leer_A(const hydro_real_t *restrict grad_i, + const hydro_real_t *restrict grad_j, + const hydro_real_t *restrict dx) { + + const hydro_real_t grad_dot_x_i = hydro_vec3_vec3_dot(grad_i, dx); + const hydro_real_t grad_dot_x_j = hydro_vec3_vec3_dot(grad_j, dx); + + /* Fall-back to raw estimates */ + if (grad_dot_x_i * grad_dot_x_j <= 0.) return 0.; + + /* Regularize denominator */ + if (fabs(grad_dot_x_j) < 1.e-10) return 0.; + + const hydro_real_t A_ij = grad_dot_x_i / grad_dot_x_j; + + return A_ij; +} + +/** + * @brief Computes A_ab from Rosswog 2020 Eq. 22 for a vector field. + * + * + * @param grad_i The gradient tensor for the first particle. + * @param grad_j The gradient tensor for the second particle. + * @param dx The distance vector between the two particles ( ri - rj ). + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_vector_van_leer_A(const hydro_real_t (*restrict grad_i)[3], + const hydro_real_t (*restrict grad_j)[3], + const hydro_real_t *restrict dx) { + + hydro_real_t delta_ij[3][3] = {0}; + hydro_vec3_vec3_outer(dx, dx, delta_ij); + + const hydro_real_t grad_dot_x_x_i = hydro_mat3x3_mat3x3_dot(grad_i, delta_ij); + const hydro_real_t grad_dot_x_x_j = hydro_mat3x3_mat3x3_dot(grad_j, delta_ij); + + /* Fall-back to raw estimates */ + if (grad_dot_x_x_i * grad_dot_x_x_j <= 0.) return 0.; + + /* Regularize denominator */ + if (fabs(grad_dot_x_x_j) < 1.e-10) return 0.; + + const hydro_real_t A_ij = grad_dot_x_x_i / grad_dot_x_x_j; + + return A_ij; +} + +/** + * @brief Computes Phi_ab from Rosswog 2020 21 for a scalar field. This is the + * van Leer 1974 slope limiting procedure. + * + * + * @param grad_i The gradient of the quantity for the first particle. + * @param grad_j The gradient of the quantity for the second particle. + * @param dx The distance vector between the two particles ( ri - rj ). + * @param eta_i The normed smoothing length of the first particle. + * @param eta_j The normed smoothing length of the second particle. + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_scalar_van_leer_phi(const hydro_real_t *restrict grad_i, + const hydro_real_t *restrict grad_j, + const hydro_real_t *restrict dx, + const hydro_real_t eta_i, + const hydro_real_t eta_j) { + + const hydro_real_t A_ij = hydro_scalar_van_leer_A(grad_i, grad_j, dx); + + return hydro_van_leer_phi(A_ij, eta_i, eta_j); +} + +/** + * @brief Computes Phi_ab from Rosswog 2020 21 for a vector field. This is the + * van Leer 1974 slope limiting procedure. + * + * + * @param grad_i The gradient of the quantity for the first particle. + * @param grad_j The gradient of the quantity for the second particle. + * @param dx The distance vector between the two particles ( ri - rj ). + * @param eta_i The normed smoothing length of the first particle. + * @param eta_j The normed smoothing length of the second particle. + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_vector_van_leer_phi(const hydro_real_t (*restrict grad_i)[3], + const hydro_real_t (*restrict grad_j)[3], + const hydro_real_t *restrict dx, + const hydro_real_t eta_i, + const hydro_real_t eta_j) { + + const hydro_real_t A_ij = hydro_vector_van_leer_A(grad_i, grad_j, dx); + + return hydro_van_leer_phi(A_ij, eta_i, eta_j); +} + +/** + * @brief Reconstructs the scalar field at the mid-point between to the + * two particles to second order. + * + * + * @param phi The slope limiter value from the van Leer 1974 scheme. + * @param dx The distance vector between the two particles ( ri - rj ). + * @param f The field at the particle. + * @param grad The gradient of the field at the particle. + * @param hess The Hessian of the field at the particle. + * @param f_reconstructed The reconstructed field at the mid-point (2nd order). + */ +__attribute__((always_inline)) INLINE static void +hydro_scalar_second_order_reconstruction(const hydro_real_t phi, + const hydro_real_t *restrict dx, + const hydro_real_t f, + const hydro_real_t *restrict grad, + const hydro_real_t (*restrict hess)[3], + hydro_real_t *f_reconstructed) { + + /* Midpoint from Equation 17 Rosswog 2020 */ + const hydro_real_t midpoint[3] = {0.5 * dx[0], + 0.5 * dx[1], + 0.5 * dx[2]}; + hydro_real_t delta_ij[3][3] = {0}; + hydro_vec3_vec3_outer(midpoint, midpoint, delta_ij); + + const hydro_real_t df_dx_dot_r = hydro_vec3_vec3_dot(grad, midpoint); + const hydro_real_t df_dx_dx_dot_r2 = hydro_mat3x3_mat3x3_dot(hess, delta_ij); + + /* Apply limited slope reconstruction */ + *f_reconstructed = f + phi * (df_dx_dot_r + 0.5 * df_dx_dx_dot_r2); +} + +/** + * @brief Reconstructs the vector field at the mid-point between to the + * two particles to second order. + * + * + * @param phi The slope limiter value from the van Leer 1974 scheme. + * @param dx The distance vector between the two particles ( ri - rj ). + * @param f The vector field at the particle. + * @param grad The gradient of the vector field at the particle. + * @param hess The Hessian of the vector field at the particle. + * @param f_reconstructed The reconstructed vector field at the mid-point (2nd order). + */ +__attribute__((always_inline)) INLINE static void +hydro_vector_second_order_reconstruction(const hydro_real_t phi, + const hydro_real_t *restrict dx, + const hydro_real_t *restrict f, + const hydro_real_t (*restrict grad)[3], + const hydro_real_t (*restrict hess)[3][3], + hydro_real_t *restrict f_reconstructed) { + + /* Midpoint from Equation 17 Rosswog 2020 */ + const hydro_real_t midpoint[3] = {0.5 * dx[0], + 0.5 * dx[1], + 0.5 * dx[2]}; + hydro_real_t delta_ij[3][3] = {0}; + hydro_vec3_vec3_outer(midpoint, midpoint, delta_ij); + + hydro_real_t df_dx_dot_r[3] = {0}; + hydro_real_t df_dx_dx_dot_r2[3] = {0}; + + hydro_mat3x3_vec3_dot(grad, midpoint, df_dx_dot_r); + hydro_tensor3x3x3_matrix3x3_dot(hess, delta_ij, df_dx_dx_dot_r2); + + /* Apply limited slope reconstruction */ + f_reconstructed[0] = f[0] + phi * (df_dx_dot_r[0] + 0.5 * df_dx_dx_dot_r2[0]); + f_reconstructed[1] = f[1] + phi * (df_dx_dot_r[1] + 0.5 * df_dx_dx_dot_r2[1]); + f_reconstructed[2] = f[2] + phi * (df_dx_dot_r[2] + 0.5 * df_dx_dx_dot_r2[2]); +} + +/** + * @brief Get the balsara limiter for viscosity. + * + * + * @param p Particle p + * @param cosmo The cosmology structure + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_get_balsara_limiter(const struct part *restrict p, + const struct cosmology* cosmo) { + +#ifdef hydro_props_use_balsara_limiter + const hydro_real_t fac_B = cosmo->a_factor_Balsara_eps; + hydro_real_t balsara = 1.; + + /* Can't trust velocity_tensor when having ill-conditioned matrices */ + if (p->gradients.C_well_conditioned && p->gradients.D_well_conditioned) { + const hydro_real_t div_v_phys = hydro_get_physical_div_v(p, cosmo); + + const hydro_real_t curl_v[3] = { + p->gradients.velocity_tensor[2][1] - p->gradients.velocity_tensor[1][2], + p->gradients.velocity_tensor[0][2] - p->gradients.velocity_tensor[2][0], + p->gradients.velocity_tensor[1][0] - p->gradients.velocity_tensor[0][1] + }; + + const hydro_real_t curl_v_norm_phys = + hydro_vec3_norm(curl_v) * cosmo->a2_inv; + const hydro_real_t Balsara_eps = 1.e-4 * fac_B * p->force.soundspeed / p->h; + balsara = + fabs(div_v_phys) / (fabs(div_v_phys) + curl_v_norm_phys + Balsara_eps); + balsara = min(1., balsara); + balsara = max(0., balsara); + } + + return balsara; +#else + return 1.; +#endif +} + +/** + * @brief Computes the average kernel gradient between pi and pj + * + * + * @param pi Particle pi + * @param pj Particle pj + * @param G_i Kernel gradient at pi + * @param G_j Kernel gradient at pj + * @param G_ij Kernel gradient average to fill + */ +__attribute__((always_inline)) INLINE static +void hydro_get_average_kernel_gradient(const struct part *restrict pi, + const struct part *restrict pj, + const hydro_real_t *restrict G_i, + const hydro_real_t *restrict G_j, + hydro_real_t *restrict G_ij) { + +#if (hydro_props_kernel_gradient_weighting == 0) + G_ij[0] = 0.5 * (G_i[0] + G_j[0]); + G_ij[1] = 0.5 * (G_i[1] + G_j[1]); + G_ij[2] = 0.5 * (G_i[2] + G_j[2]); +#elif (hydro_props_kernel_gradient_weighting == 1) + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; + G_ij[0] = 0.5 * (f_i * G_i[0] + f_j * G_j[0]); + G_ij[1] = 0.5 * (f_i * G_i[1] + f_j * G_j[1]); + G_ij[2] = 0.5 * (f_i * G_i[2] + f_j * G_j[2]); +#elif (hydro_props_kernel_gradient_weighting == 2) + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; + const hydro_real_t f_ij = 0.5 * (f_i + f_j); + G_ij[0] = 0.5 * f_ij * (G_i[0] + G_j[0]); + G_ij[1] = 0.5 * f_ij * (G_i[1] + G_j[1]); + G_ij[2] = 0.5 * f_ij * (G_i[2] + G_j[2]); +#elif (hydro_props_kernel_gradient_weighting == 3) + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; + hydro_real_t f_ij = 0.; + const hydro_real_t f_sum = f_i + f_j; + if (f_sum > 0.) f_ij = 2. * f_i * f_j / f_sum; + + G_ij[0] = 0.5 * f_ij * (G_i[0] + G_j[0]); + G_ij[1] = 0.5 * f_ij * (G_i[1] + G_j[1]); + G_ij[2] = 0.5 * f_ij * (G_i[2] + G_j[2]); +#elif (hydro_props_kernel_gradient_weighting == 4) + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; + const hydro_real_t f_ij = sqrt(f_i * f_j); + G_ij[0] = 0.5 * f_ij * (G_i[0] + G_j[0]); + G_ij[1] = 0.5 * f_ij * (G_i[1] + G_j[1]); + G_ij[2] = 0.5 * f_ij * (G_i[2] + G_j[2]); +#elif (hydro_props_kernel_gradient_weighting == 5) + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; + const hydro_real_t rho_sum = pi->rho + pj->rho; + const hydro_real_t f_ij = (pi->rho * f_i + pj->rho * f_j) / rho_sum; + G_ij[0] = 0.5 * f_ij * (G_i[0] + G_j[0]); + G_ij[1] = 0.5 * f_ij * (G_i[1] + G_j[1]); + G_ij[2] = 0.5 * f_ij * (G_i[2] + G_j[2]); +#elif (hydro_props_kernel_gradient_weighting == 6) + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; + const hydro_real_t rho_f_sum = pi->rho * f_i + pj->rho * f_j; + const hydro_real_t f_ij = 2. * pi->rho * f_i * pj->rho * f_j / rho_f_sum; + G_ij[0] = 0.5 * f_ij * (G_i[0] + G_j[0]); + G_ij[1] = 0.5 * f_ij * (G_i[1] + G_j[1]); + G_ij[2] = 0.5 * f_ij * (G_i[2] + G_j[2]); +#elif (hydro_props_kernel_gradient_weighting == 7) + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; + const hydro_real_t P_sum = pi->force.pressure + pj->force.pressure; + const hydro_real_t f_ij = + (pi->force.pressure * f_i + pj->force.pressure * f_j) / P_sum; + G_ij[0] = 0.5 * f_ij * (G_i[0] + G_j[0]); + G_ij[1] = 0.5 * f_ij * (G_i[1] + G_j[1]); + G_ij[2] = 0.5 * f_ij * (G_i[2] + G_j[2]); +#elif (hydro_props_kernel_gradient_weighting == 8) + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; + const hydro_real_t P_f_sum = + pi->force.pressure * f_i + pj->force.pressure * f_j; + const hydro_real_t f_ij = + 2. * pi->force.pressure * f_i * pj->force.pressure * f_j / P_f_sum; + G_ij[0] = 0.5 * f_ij * (G_i[0] + G_j[0]); + G_ij[1] = 0.5 * f_ij * (G_i[1] + G_j[1]); + G_ij[2] = 0.5 * f_ij * (G_i[2] + G_j[2]); +#elif (hydro_props_kernel_gradient_weighting == 9) + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; + const hydro_real_t mi = hydro_get_mass(pi); + const hydro_real_t mj = hydro_get_mass(pj); + const hydro_real_t rhoi_inv = 1. / hydro_get_comoving_density(pi); + const hydro_real_t rhoj_inv = 1. / hydro_get_comoving_density(pj); + const hydro_real_t Vi = mi * rhoi_inv; + const hydro_real_t Vj = mj * rhoj_inv; + + const hydro_real_t f_ij = 0.5 * (Vi * f_i + Vj * f_j) / (Vi + Vj); + G_ij[0] = 0.5 * f_ij * (G_i[0] + G_j[0]); + G_ij[1] = 0.5 * f_ij * (G_i[1] + G_j[1]); + G_ij[2] = 0.5 * f_ij * (G_i[2] + G_j[2]); +#else + error("Invalid hydro_props_kernel_gradient_weighting value: %d", + hydro_props_kernel_gradient_weighting); +#endif +} + +/** + * @brief Computes the viscosity acceleration and mu_ij terms. + * + * + * @param pi Particle pi + * @param pj Particle pj + * @param dv Second order reconstructed velocity difference + * @param dx Distance vector + * @param r Distance vector norm + * @param fac_mu Cosmological factor for mu_ij + * @param a2_Hubble Hubble flow + * @param mu_i The velocity jump indicator at pi to fill + * @param mu_j The velocity jump indicator at pj to fill + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_get_visc_acc_term(const struct part *restrict pi, + const struct part *restrict pj, + const hydro_real_t *restrict dv, + const hydro_real_t *restrict dx, + const hydro_real_t fac_mu, + const hydro_real_t a2_Hubble) { + + const hydro_real_t rhoi = pi->rho; + const hydro_real_t rhoj = pj->rho; + + const hydro_real_t bi = pi->gradients.balsara; + const hydro_real_t bj = pj->gradients.balsara; + + /* Viscosity direction */ + hydro_real_t dx_hat[3] = {0., 0., 0.}; + hydro_vec3_unit(dx, dx_hat); + + /* Separation and velocity along the gradient vector */ + const hydro_real_t dv_Hubble[3] = {dv[0] + a2_Hubble * dx[0], + dv[1] + a2_Hubble * dx[1], + dv[2] + a2_Hubble * dx[2]}; + const hydro_real_t dv_dot_dx_hat = hydro_vec3_vec3_dot(dv_Hubble, dx_hat); + const hydro_real_t conv = (dv_dot_dx_hat < 0.) ? fac_mu : 0.; + + /* Must be a converging flow */ + if (conv == 0.) return 0.; + + const hydro_real_t mu_ij = conv * dv_dot_dx_hat; + +#ifdef hydro_props_viscosity_weighting_type +#if (hydro_props_viscosity_weighting_type == 0) + + /* Each particle gets its own Q and then weighted by density */ + const hydro_real_t rhoij_inv = 1.0 / (rhoi * rhoj); + + const hydro_real_t q_i_alpha = + -const_viscosity_alpha * pi->force.soundspeed * mu_ij; + const hydro_real_t q_i_beta = const_viscosity_beta * mu_ij * mu_ij; + const hydro_real_t Q_i = rhoi * (q_i_alpha + q_i_beta); + + const hydro_real_t q_j_alpha = + -const_viscosity_alpha * pj->force.soundspeed * mu_ij; + const hydro_real_t q_j_beta = const_viscosity_beta * mu_ij * mu_ij; + const hydro_real_t Q_j = rhoj * (q_j_alpha + q_j_beta); + + return (bi * Q_i + bj * Q_j) * rhoij_inv; + +#elif (hydro_props_viscosity_weighting_type == 1) + + /* Each particle has the same Q but is density weighted */ + const hydro_real_t b_ij = 0.5 * (bi + bj); + const hydro_real_t rhoij_inv = 1. / (rhoi * rhoj); + const hydro_real_t c_ij = 0.5 * (pi->force.soundspeed + pj->force.soundspeed); + const hydro_real_t q_ij_alpha = -const_viscosity_alpha * c_ij * mu_ij; + const hydro_real_t q_ij_beta = const_viscosity_beta * mu_ij * mu_ij; + const hydro_real_t Q_ij = (rhoi + rhoj) * (q_ij_alpha + q_ij_beta); + + return b_ij * Q_ij * rhoij_inv; + +#elif (hydro_props_viscosity_weighting_type == 2) + + /* Particles average symmetrically and arithmetically */ + const hydro_real_t b_ij = 0.5 * (bi + bj); + const hydro_real_t c_ij = 0.5 * (pi->force.soundspeed + pj->force.soundspeed); + const hydro_real_t q_ij_alpha = -const_viscosity_alpha * c_ij * mu_ij; + const hydro_real_t q_ij_beta = const_viscosity_beta * mu_ij * mu_ij; + const hydro_real_t q_ij = 2. * (q_ij_alpha + q_ij_beta) / (rhoi + rhoj); + + return b_ij * q_ij; + +#endif +#else + error("Unknown compiled hydro_props_viscosity_weighting_type value: %d\n" + "Valid values are 0, 1, or 2.\n", + (int)hydro_props_viscosity_weighting_type); +#endif +} + +/** + * @brief Gets the signal velocity for the conduction interaction based on mu_ij. + * + * + * @param dx The separation vector + * @param pi Particle pi + * @param pj Particle pj + * @param mu_i The velocity jump indicator at pi + * @param mu_j The velocity jump indicator at pj + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_get_cond_signal_velocity(const float *restrict dx, + const struct part *restrict pi, + const struct part *restrict pj, + const float mu_i, + const float mu_j, + const float beta) { + + + const float mu_ij = 0.5f * (mu_i + mu_j); + return hydro_signal_velocity(dx, pi, pj, mu_ij, beta); +} + +/** + * @brief Gets the signal velocity for the viscous interaction based on mu_ij. + * + * + * @param dx The separation vector + * @param pi Particle pi + * @param pj Particle pj + * @param mu_i The velocity jump indicator at pi + * @param mu_j The velocity jump indicator at pj + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_get_visc_signal_velocity(const float *restrict dx, + const struct part *restrict pi, + const struct part *restrict pj, + const float mu_i, + const float mu_j, + const float beta) { + +#ifdef hydro_props_viscosity_weighting_type +#if (hydro_props_viscosity_weighting_type == 0) + const float dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; + const float v_sig_visc_i = + hydro_signal_velocity(dx, pi, pj, mu_i, beta); + const float v_sig_visc_j = + hydro_signal_velocity(dx_ji, pj, pi, mu_j, beta); + return 0.5 * (v_sig_visc_i + v_sig_visc_j); +#else + const float mu_ij = 0.5f * (mu_i + mu_j); + return hydro_signal_velocity(dx, pi, pj, mu_ij, beta); +#endif +#else + error("No viscosity weighting type defined at compile time."); +#endif +} + +/** + * @brief Finishes the density calculation. + * + * Multiplies the density and number of neighbours by the appropiate constants + * and add the self-contribution term. + * Additional quantities such as velocity gradients will also get the final + * terms added to them here. + * + * Also adds/multiplies the cosmological terms if need be. + * + * @param p The particle to act upon + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_end_density( + struct part *restrict p, const struct cosmology *cosmo) { + /* Some smoothing length multiples. */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + /* Final operation on the density (add self-contribution). */ + p->rho += p->mass * kernel_root; + p->density.rho_dh -= hydro_dimension * p->mass * kernel_root; + p->density.wcount += kernel_root; + p->density.wcount_dh -= hydro_dimension * kernel_root; + + /* Finish the calculation by inserting the missing h-factors */ + p->rho *= h_inv_dim; + p->density.rho_dh *= h_inv_dim_plus_one; + p->density.wcount *= h_inv_dim; + p->density.wcount_dh *= h_inv_dim_plus_one; + + /* Need this for correct dh/dt */ + p->gradients.wcount = p->density.wcount; + + p->gradients.u_aux[0] *= h_inv_dim_plus_one; + p->gradients.u_aux[1] *= h_inv_dim_plus_one; + p->gradients.u_aux[2] *= h_inv_dim_plus_one; + + p->gradients.u_aux_norm[0] *= h_inv_dim_plus_one; + p->gradients.u_aux_norm[1] *= h_inv_dim_plus_one; + p->gradients.u_aux_norm[2] *= h_inv_dim_plus_one; + + if (p->gradients.u_aux_norm[0] != 0. && + p->gradients.u_aux_norm[1] != 0. && + p->gradients.u_aux_norm[2] != 0.) { + p->gradients.u_aux[0] /= p->gradients.u_aux_norm[0]; + p->gradients.u_aux[1] /= p->gradients.u_aux_norm[1]; + p->gradients.u_aux[2] /= p->gradients.u_aux_norm[2]; + + hydro_vec_slope_limiter(p->gradients.du_min, p->gradients.du_max, + p->gradients.kernel_size, + p->gradients.u_aux); + } + else { + p->gradients.u_well_conditioned = 0; +#ifdef MAGMA2_DEBUG_CHECKS + p->debug.u_aux[0] = p->gradients.u_aux[0]; + p->debug.u_aux[1] = p->gradients.u_aux[1]; + p->debug.u_aux[2] = p->gradients.u_aux[2]; + + p->debug.u_aux_norm[0] = p->gradients.u_aux_norm[0]; + p->debug.u_aux_norm[1] = p->gradients.u_aux_norm[1]; + p->debug.u_aux_norm[2] = p->gradients.u_aux_norm[2]; + + p->debug.u_ill_conditioned_count++; +#endif + + p->gradients.u_aux[0] = 0.; + p->gradients.u_aux[1] = 0.; + p->gradients.u_aux[2] = 0.; + + p->gradients.u_aux_norm[0] = 0.; + p->gradients.u_aux_norm[1] = 0.; + p->gradients.u_aux_norm[2] = 0.; + } + + double aux_norm[3][3] = {0}; + for (int i = 0; i < 3; i++) { + p->gradients.velocity_tensor_aux[i][0] *= h_inv_dim_plus_one; + p->gradients.velocity_tensor_aux[i][1] *= h_inv_dim_plus_one; + p->gradients.velocity_tensor_aux[i][2] *= h_inv_dim_plus_one; + p->gradients.velocity_tensor_aux_norm[i][0] *= h_inv_dim_plus_one; + p->gradients.velocity_tensor_aux_norm[i][1] *= h_inv_dim_plus_one; + p->gradients.velocity_tensor_aux_norm[i][2] *= h_inv_dim_plus_one; + aux_norm[i][0] = p->gradients.velocity_tensor_aux_norm[i][0]; + aux_norm[i][1] = p->gradients.velocity_tensor_aux_norm[i][1]; + aux_norm[i][2] = p->gradients.velocity_tensor_aux_norm[i][2]; + } + + /* Invert the aux_norm matrix */ + gsl_matrix_view A_view = gsl_matrix_view_array((double *)aux_norm, 3, 3); + gsl_matrix *A = &A_view.matrix; + + double cond = condition_number(A); + if (cond < const_condition_number_upper_limit) { + gsl_matrix *A_inv = gsl_matrix_alloc(3, 3); + gsl_permutation *p_perm = gsl_permutation_alloc(3); + int signum; + + gsl_linalg_LU_decomp(A, p_perm, &signum); + gsl_linalg_LU_invert(A, p_perm, A_inv); + + for (int i = 0; i < 3; i++) { + p->gradients.velocity_tensor_aux_norm[i][0] = gsl_matrix_get(A_inv, i, 0); + p->gradients.velocity_tensor_aux_norm[i][1] = gsl_matrix_get(A_inv, i, 1); + p->gradients.velocity_tensor_aux_norm[i][2] = gsl_matrix_get(A_inv, i, 2); + } + + gsl_matrix_free(A_inv); + gsl_permutation_free(p_perm); + + /* aux_norm is equation 20 in Rosswog 2020, finalize the gradient in 19 */ + hydro_real_t aux_matrix[3][3] = {0}; + for (int j = 0; j < 3; j++) { + for (int i = 0; i < 3; i++) { + /** + * The indices of aux_norm and velocity_gradient_aux are important. + * aux_norm j: dx vector direction. + * k: kernel gradient direction + * + * velocity_gradient_aux i: dv vector direction + * k: kernel gradient direction + * + * aux_matrix j: spatial derivative direction + * i: velocity direction + */ + for (int k = 0; k < 3; k++) { + aux_matrix[j][i] += p->gradients.velocity_tensor_aux_norm[j][k] * + p->gradients.velocity_tensor_aux[i][k]; + } + } + } + + /* Copy over the matrices for use later */ + + /* D. Rennehan: For some reason, memcpy does not work here? Could it + * be because of the union in the particle struct? */ + for (int j = 0; j < 3; j++) { + hydro_vec_slope_limiter(p->gradients.dv_min[j], p->gradients.dv_max[j], + p->gradients.kernel_size, + aux_matrix[j]); + + p->gradients.velocity_tensor_aux[j][0] = aux_matrix[j][0]; + p->gradients.velocity_tensor_aux[j][1] = aux_matrix[j][1]; + p->gradients.velocity_tensor_aux[j][2] = aux_matrix[j][2]; + } + } + else { + + p->gradients.D_well_conditioned = 0; +#ifdef MAGMA2_DEBUG_CHECKS + p->debug.D_ill_conditioned_count++; +#endif + + /* Ensure no crazy gradients later */ + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { +#ifdef MAGMA2_DEBUG_CHECKS + p->debug.velocity_tensor_aux[i][j] = + p->gradients.velocity_tensor_aux[i][j]; + p->debug.velocity_tensor_aux_norm[i][j] = + p->gradients.velocity_tensor_aux_norm[i][j]; +#endif + + p->gradients.velocity_tensor_aux[i][j] = 0.; + p->gradients.velocity_tensor_aux_norm[i][j] = 0.; + } + } + } +} + +/** + * @brief Prepare a particle for the gradient calculation. + * + * This function is called after the density loop and before the gradient loop. + * + * We use it to set the physical timestep for the particle and to copy the + * actual velocities, which we need to boost our interfaces during the flux + * calculation. We also initialize the variables used for the time step + * calculation. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + * @param hydro_props Hydrodynamic properties. + * @param pressure_floor The properties of the pressure floor. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct pressure_floor_props *pressure_floor) { + +#ifdef hydro_props_use_adiabatic_correction + /* Prepare the denominator for the adiabatic correction term */ + p->gradients.adiabatic_f_denominator = 0.; +#endif + + /* Compute the sound speed */ + const float pressure = hydro_get_comoving_pressure(p); + const float pressure_including_floor = + pressure_floor_get_comoving_pressure(p, pressure_floor, pressure, cosmo); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, pressure_including_floor); + + /* Update variables. */ + p->force.pressure = pressure_including_floor; + p->force.soundspeed = soundspeed; + + + /* ------ Compute the kernel correction for SPH gradients ------ */ + + + /* Compute the "grad h" term */ + const float common_factor = p->h * hydro_dimension_inv / p->density.wcount; + float grad_W_term = -1.f; + + /* Ignore changing-kernel effects when h ~= h_max */ + if (p->h <= 0.9999f * hydro_props->h_max) { + + grad_W_term = common_factor * p->density.wcount_dh; + + if (grad_W_term < -0.9999f) { + /* if we get here, we either had very small neighbour contributions + (which should be treated as a no neighbour case in the ghost) or + a very weird particle distribution (e.g. particles sitting on + top of each other). Either way, we cannot use the normal + expression, since that would lead to overflow or excessive round + off and cause excessively high accelerations in the force loop */ + grad_W_term = -1.f; + warning( + "grad_W_term very small for particle with ID %lld (h: %g, wcount: " + "%g, wcount_dh: %g)", + p->id, p->h, p->density.wcount, p->density.wcount_dh); + } + } + + /* Update variables. */ + p->force.f = (grad_W_term > -1.f) ? 1.f / (1.f + grad_W_term) : 1.f; + +} + +/** + * @brief Resets the variables that are required for a gradient calculation. + * + * This function is called after hydro_prepare_gradient. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_gradient( + struct part *restrict p) { + + p->rho_gradient[0] = 0.f; + p->rho_gradient[1] = 0.f; + p->rho_gradient[2] = 0.f; + + p->gradients.C_well_conditioned = 1; + + for (int i = 0; i < 3; i++) { + p->gradients.u[i] = 0.; + p->gradients.u_hessian[i][0] = 0.; + p->gradients.u_hessian[i][1] = 0.; + p->gradients.u_hessian[i][2] = 0.; + + p->gradients.correction_matrix[i][0] = 0.; + p->gradients.correction_matrix[i][1] = 0.; + p->gradients.correction_matrix[i][2] = 0.; + + p->gradients.velocity_tensor[i][0] = 0.; + p->gradients.velocity_tensor[i][1] = 0.; + p->gradients.velocity_tensor[i][2] = 0.; + + for (int j = 0; j < 3; j++) { + p->gradients.velocity_hessian[i][j][0] = 0.; + p->gradients.velocity_hessian[i][j][1] = 0.; + p->gradients.velocity_hessian[i][j][2] = 0.; + } + } +} + +/** + * @brief Finishes the gradient calculation. + * + * Just a wrapper around hydro_gradients_finalize, which can be an empty method, + * in which case no gradients are used. + * + * This method also initializes the force loop variables. + * + * @param p The particle to act upon. + */ +__attribute__((always_inline)) INLINE static void hydro_end_gradient( + struct part *p) { + + /* Calculate smoothing length powers */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + const hydro_real_t rho_inv = 1. / hydro_get_comoving_density(p); + p->rho_gradient[0] *= h_inv_dim_plus_one * rho_inv; + p->rho_gradient[1] *= h_inv_dim_plus_one * rho_inv; + p->rho_gradient[2] *= h_inv_dim_plus_one * rho_inv; + + p->gradients.u[0] *= h_inv_dim; + p->gradients.u[1] *= h_inv_dim; + p->gradients.u[2] *= h_inv_dim; + + /* Temporary double for GSL */ + double correction_matrix[3][3] = {0}; + + /* Apply correct normalisation */ + for (int k = 0; k < 3; k++) { + p->gradients.correction_matrix[k][0] *= h_inv_dim; + p->gradients.correction_matrix[k][1] *= h_inv_dim; + p->gradients.correction_matrix[k][2] *= h_inv_dim; + + correction_matrix[k][0] = p->gradients.correction_matrix[k][0]; + correction_matrix[k][1] = p->gradients.correction_matrix[k][1]; + correction_matrix[k][2] = p->gradients.correction_matrix[k][2]; + + p->gradients.u_hessian[k][0] *= h_inv_dim; + p->gradients.u_hessian[k][1] *= h_inv_dim; + p->gradients.u_hessian[k][2] *= h_inv_dim; + + p->gradients.velocity_tensor[k][0] *= h_inv_dim; + p->gradients.velocity_tensor[k][1] *= h_inv_dim; + p->gradients.velocity_tensor[k][2] *= h_inv_dim; + + for (int j = 0; j < 3; j++) { + p->gradients.velocity_hessian[k][j][0] *= h_inv_dim; + p->gradients.velocity_hessian[k][j][1] *= h_inv_dim; + p->gradients.velocity_hessian[k][j][2] *= h_inv_dim; + } + } + + /* Invert the p->gradients.correction_matrix[3][3] matrix */ + gsl_matrix_view C_view = + gsl_matrix_view_array((double *)correction_matrix, 3, 3); + gsl_matrix *C = &C_view.matrix; + + const double cond = condition_number(C); + if (cond < const_condition_number_upper_limit) { + gsl_matrix *C_inv = gsl_matrix_alloc(3, 3); + gsl_permutation *p_perm = gsl_permutation_alloc(3); + int signum; + + gsl_linalg_LU_decomp(C, p_perm, &signum); + gsl_linalg_LU_invert(C, p_perm, C_inv); + + for (int k = 0; k < 3; k++) { + p->gradients.correction_matrix[k][0] = gsl_matrix_get(C_inv, k, 0); + p->gradients.correction_matrix[k][1] = gsl_matrix_get(C_inv, k, 1); + p->gradients.correction_matrix[k][2] = gsl_matrix_get(C_inv, k, 2); + } + + gsl_matrix_free(C_inv); + gsl_permutation_free(p_perm); + } + else { +#ifdef MAGMA2_DEBUG_CHECKS + for (int k = 0; k < 3; k++) { + p->debug.correction_matrix[k][0] = p->gradients.correction_matrix[k][0]; + p->debug.correction_matrix[k][1] = p->gradients.correction_matrix[k][1]; + p->debug.correction_matrix[k][2] = p->gradients.correction_matrix[k][2]; + } + + p->debug.C_ill_conditioned_count++; +#endif + + /* Ill-condition matrix, revert back to normal SPH gradients */ + p->gradients.C_well_conditioned = 0; + for (int k = 0; k < 3; k++) { + p->gradients.correction_matrix[k][0] = 0.; + p->gradients.correction_matrix[k][1] = 0.; + p->gradients.correction_matrix[k][2] = 0.; + } + } + + /* Contract the correction matrix with the internal energy gradient and with + * the velocity tensor */ + hydro_real_t u_gradient[3] = {0}; + hydro_real_t u_hessian[3][3] = {0}; + hydro_real_t velocity_tensor[3][3] = {0}; + hydro_real_t velocity_hessian[3][3][3] = {0}; + for (int j = 0; j < 3; j++) { + for (int i = 0; i < 3; i++) { + u_gradient[j] += p->gradients.correction_matrix[j][i] * + p->gradients.u[i]; + + for (int k = 0; k < 3; k++) { + u_hessian[j][i] += p->gradients.correction_matrix[j][k] * + p->gradients.u_hessian[i][k]; + velocity_tensor[j][i] += p->gradients.correction_matrix[j][k] * + p->gradients.velocity_tensor[i][k]; + + for (int m = 0; m < 3; m++) { + velocity_hessian[j][i][k] += p->gradients.correction_matrix[j][m] * + p->gradients.velocity_hessian[j][i][m]; + } + } + } + } + + /* Slope limiter for internal energy */ + hydro_vec_slope_limiter(p->gradients.du_min, p->gradients.du_max, + p->gradients.kernel_size, + u_gradient); + + /* Copy back over to the particle for later */ + p->gradients.u[0] = u_gradient[0]; + p->gradients.u[1] = u_gradient[1]; + p->gradients.u[2] = u_gradient[2]; + + for (int j = 0; j < 3; j++) { + p->gradients.u_hessian[j][0] = u_hessian[j][0]; + p->gradients.u_hessian[j][1] = u_hessian[j][1]; + p->gradients.u_hessian[j][2] = u_hessian[j][2]; + + hydro_vec_slope_limiter(p->gradients.dv_min[j], p->gradients.dv_max[j], + p->gradients.kernel_size, + velocity_tensor[j]); + + p->gradients.velocity_tensor[j][0] = velocity_tensor[j][0]; + p->gradients.velocity_tensor[j][1] = velocity_tensor[j][1]; + p->gradients.velocity_tensor[j][2] = velocity_tensor[j][2]; + + p->gradients.velocity_hessian[j][0][0] = velocity_hessian[j][0][0]; + p->gradients.velocity_hessian[j][0][1] = velocity_hessian[j][0][1]; + p->gradients.velocity_hessian[j][0][2] = velocity_hessian[j][0][2]; + + p->gradients.velocity_hessian[j][1][0] = velocity_hessian[j][1][0]; + p->gradients.velocity_hessian[j][1][1] = velocity_hessian[j][1][1]; + p->gradients.velocity_hessian[j][1][2] = velocity_hessian[j][1][2]; + + p->gradients.velocity_hessian[j][2][0] = velocity_hessian[j][2][0]; + p->gradients.velocity_hessian[j][2][1] = velocity_hessian[j][2][1]; + p->gradients.velocity_hessian[j][2][2] = velocity_hessian[j][2][2]; + } +} + +/** + * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. + * + * In the desperate case where a particle has no neighbours (likely because + * of the h_max ceiling), set the particle fields to something sensible to avoid + * NaNs in the next calculations. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { + /* Some smoothing length multiples. */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + + /* Decoupled particles might have no neighbours */ + if (!p->decoupled) { + warning( + "Gas particle with ID %lld treated as having no neighbours (h: %g, " + "wcount: %g).", + p->id, h, p->density.wcount); + } + + /* Re-set problematic values */ + p->rho = p->mass * kernel_root * h_inv_dim; + p->rho_gradient[0] = 0.f; + p->rho_gradient[1] = 0.f; + p->rho_gradient[2] = 0.f; + p->h_min = FLT_MAX; + p->dt_min = FLT_MAX; + p->density.wcount = kernel_root * h_inv_dim; + p->density.rho_dh = 0.f; + p->density.wcount_dh = 0.f; + +#ifdef MAGMA2_DEBUG_CHECKS + p->debug.num_ngb = 0; +#endif + p->gradients.C_well_conditioned = 0; + p->gradients.D_well_conditioned = 0; + p->gradients.u_well_conditioned = 0; + p->gradients.du_min = 0.; + p->gradients.du_max = 0.; + p->gradients.kernel_size = (hydro_real_t)h; + + for (int i = 0; i < 3; i++) { + p->gradients.u[i] = 0.; + p->gradients.u_aux[i] = 0.; + p->gradients.u_aux_norm[i] = 0.; + + p->gradients.dv_min[i] = 0.; + p->gradients.dv_max[i] = 0.; + + for (int j = 0; j < 3; j++) { + p->gradients.correction_matrix[i][j] = 0.; + p->gradients.velocity_tensor[i][j] = 0.; + p->gradients.velocity_tensor_aux[i][j] = 0.; + p->gradients.velocity_tensor_aux_norm[i][j] = 0.; + p->gradients.u_hessian[i][j] = 0.; + + for (int k = 0; k < 3; k++) { + p->gradients.velocity_hessian[i][j][k] = 0.; + } + } + } +} + +/** + * @brief Prepare a particle for the force calculation. + * + * This function is called in the ghost task to convert some quantities coming + * from the density loop over neighbours into quantities ready to be used in the + * force loop over neighbours. Quantities are typically read from the density + * sub-structure and written to the force sub-structure. + * Examples of calculations done here include the calculation of viscosity term + * constants, thermal conduction terms, hydro conversions, etc. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. + * @param hydro_props Hydrodynamic properties. + * @param pressure_floor The properties of the pressure floor. + * @param dt_alpha The time-step used to evolve non-cosmological quantities such + * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_force( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct pressure_floor_props *pressure_floor, const float dt_alpha, + const float dt_therm) { + + /* First estimates for the timestepping. Missing the kernel_gamma factors + * for now, but will be added at the end of the force loop. */ + p->h_min = FLT_MAX; + p->dt_min = FLT_MAX; + p->gradients.balsara = hydro_get_balsara_limiter(p, cosmo); + +#ifdef hydro_props_use_adiabatic_correction + const hydro_real_t prev_f = p->force.f; + const hydro_real_t rho_inv = 1. / hydro_get_comoving_density(p); + /* Finish final kernel gradient correction factor */ + if (p->gradients.adiabatic_f_denominator != 0.) { + const hydro_real_t kernel_r2 = p->gradients.adiabatic_f_numerator; + const hydro_real_t weighted_kernel_r2_inv = + 1. / p->gradients.adiabatic_f_denominator; + p->force.f = rho_inv * kernel_r2 * weighted_kernel_r2_inv; + } + else { + p->force.f = 1.; + } + +#ifdef MAGMA2_DEBUG_CHECKS + if (p->force.f > 100.) { + warning("Final force factor is very high for particle with ID %lld" + " (prev f: %g, f: %g, numerator: %g, denominator: %g, rho_inv: %g)", + p->id, prev_f, p->force.f, p->gradients.adiabatic_f_numerator, + p->gradients.adiabatic_f_denominator, rho_inv); + } +#endif +#endif +} + +/** + * @brief Reset acceleration fields of a particle + * + * Resets all hydro acceleration and time derivative fields in preparation + * for the sums taking place in the various force tasks. + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_reset_acceleration( + struct part *restrict p) { + /* Reset the acceleration. */ + p->a_hydro[0] = 0.0f; + p->a_hydro[1] = 0.0f; + p->a_hydro[2] = 0.0f; + + /* Reset the time derivatives. */ + p->u_dt = 0.0f; + p->u_dt_cond = 0.0f; + p->force.h_dt = 0.0f; +} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param p The particle. + * @param xp The extended data of this particle. + * @param cosmo The cosmological model. + * @param pressure_floor The properties of the pressure floor. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( + struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo, + const struct pressure_floor_props *pressure_floor) { + /* Re-set the predicted velocities */ + p->v[0] = xp->v_full[0]; + p->v[1] = xp->v_full[1]; + p->v[2] = xp->v_full[2]; + + /* Re-set the entropy */ + p->u = xp->u_full; + + /* Compute the sound speed */ + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + const float pressure_including_floor = + pressure_floor_get_comoving_pressure(p, pressure_floor, pressure, cosmo); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, pressure_including_floor); + + p->force.pressure = pressure_including_floor; + p->force.soundspeed = soundspeed; + + /* Signal speed */ + const float v_sig = const_viscosity_alpha_prefactor * soundspeed; + + /* Update the signal velocity, if we need to. */ + p->dt_min = min(p->dt_min, p->h_min / v_sig); +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * Additional hydrodynamic quantites are drifted forward in time here. These + * include thermal quantities (thermal energy or total energy or entropy, ...). + * + * Note the different time-step sizes used for the different quantities as they + * include cosmological factors. + * + * @param p The particle. + * @param xp The extended data of the particle. + * @param dt_drift The drift time-step for positions. + * @param dt_therm The drift time-step for thermal quantities. + * @param dt_kick_grav The time-step for gravity quantities. + * @param cosmo The cosmological model. + * @param hydro_props The properties of the hydro scheme. + * @param floor_props The properties of the entropy floor. + * @param pressure_floor The properties of the pressure floor. + */ +__attribute__((always_inline)) INLINE static void hydro_predict_extra( + struct part *restrict p, const struct xpart *restrict xp, float dt_drift, + float dt_therm, float dt_kick_grav, const struct cosmology *cosmo, + const struct hydro_props *hydro_props, + const struct entropy_floor_properties *floor_props, + const struct pressure_floor_props *pressure_floor) { + + /* Wind particles do not need the extra predict evolution */ + if (p->decoupled) return; + + /* Predict the internal energy */ + p->u += p->u_dt * dt_therm; + + const float h_inv = 1.f / p->h; + + /* Predict smoothing length */ + const float w1 = p->force.h_dt * h_inv * dt_drift; + if (fabsf(w1) < 0.2f) + p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */ + else + p->h *= expf(w1); + + /* Predict density and weighted pressure */ + const float w2 = -hydro_dimension * w1; + if (fabsf(w2) < 0.2f) { + const float expf_approx = + approx_expf(w2); /* 4th order expansion of exp(w) */ + p->rho *= expf_approx; + } else { + const float expf_exact = expf(w2); + p->rho *= expf_exact; + } + + /* Check against entropy floor - explicitly do this after drifting the + * density as this has a density dependence. */ + const float floor_A = entropy_floor(p, cosmo, floor_props); + const float floor_u = gas_internal_energy_from_entropy(p->rho, floor_A); + + /* Check against absolute minimum */ + const float min_u = + hydro_props->minimal_internal_energy / cosmo->a_factor_internal_energy; + + p->u = max(p->u, floor_u); + p->u = max(p->u, min_u); + + /* Compute the new sound speed */ + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + const float pressure_including_floor = + pressure_floor_get_comoving_pressure(p, pressure_floor, pressure, cosmo); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, pressure_including_floor); + + p->force.pressure = pressure_including_floor; + p->force.soundspeed = soundspeed; + + /* Signal speed */ + const float v_sig = const_viscosity_alpha_prefactor * soundspeed; + + /* Update signal velocity if we need to */ + p->dt_min = min(p->dt_min, p->h_min / v_sig); +} + +/** + * @brief Returns the sum term for the dh/dt calculation + * + * + * @param m The particle mass of the neighbour + * @param rho_inv The inverse density of the neighbour + * @param r_inv The inverse distance between particles + * @param w_dr The kernel gradient for this particle + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_get_h_dt_sum(const hydro_real_t dv_dot_dx, + const hydro_real_t dv_dot_G, + const hydro_real_t m, + const hydro_real_t rho_inv, + const hydro_real_t r_inv, + const hydro_real_t w_dr) { + + hydro_real_t dvdx = 0.; +#ifdef hydro_props_dh_dt_estimator_type +#if (hydro_props_dh_dt_estimator_type == 0) + const hydro_real_t grad = r_inv * w_dr; + const hydro_real_t wt = m * rho_inv; + dvdx = dv_dot_dx; +#elif (hydro_props_dh_dt_estimator_type == 1) + const hydro_real_t grad = r_inv * w_dr; + const hydro_real_t wt = 1.; + dvdx = dv_dot_dx; +#elif (hydro_props_dh_dt_estimator_type == 2) + const hydro_real_t grad = 1.; + const hydro_real_t wt = m * rho_inv; + dvdx = dv_dot_G; +#else + error("Compiled with an unknown dh/dt estimator type"); +#endif +#else + error("Must compile with hydro_props_dh_dt_estimator_type."); +#endif + + return wt * dvdx * grad; +} + +/** + * @brief Returns the normalization for the dh/dt sum + * + * + * @param p The particle + */ +__attribute__((always_inline)) INLINE static +hydro_real_t hydro_get_h_dt_norm(struct part *restrict p) { + + hydro_real_t renormalization = 1.; + +#ifdef hydro_props_dh_dt_estimator_type +#if (hydro_props_dh_dt_estimator_type == 1) + renormalization = p->force.f / p->gradients.wcount; +#endif +#else + error("Must compile with hydro_props_dh_dt_estimator_type."); +#endif + + return renormalization * p->h * hydro_dimension_inv; +} + +/** + * @brief Finishes the force calculation. + * + * Multiplies the force and accelerations by the appropiate constants + * and add the self-contribution term. In most cases, there is little + * to do here. + * + * Cosmological terms are also added/multiplied here. + * + * @param p The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_end_force( + struct part *restrict p, const struct cosmology *cosmo) { + + p->force.h_dt *= hydro_get_h_dt_norm(p); +} + +/** + * @brief Kick the additional variables + * + * Additional hydrodynamic quantites are kicked forward in time here. These + * include thermal quantities (thermal energy or total energy or entropy, ...). + * + * @param p The particle to act upon. + * @param xp The particle extended data to act upon. + * @param dt_therm The time-step for this kick (for thermodynamic quantities). + * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_grav_mesh The time-step for this kick (mesh gravity). + * @param dt_hydro The time-step for this kick (for hydro quantities). + * @param dt_kick_corr The time-step for this kick (for gravity corrections). + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme + * @param floor_props The properties of the entropy floor. + */ +__attribute__((always_inline)) INLINE static void hydro_kick_extra( + struct part *restrict p, struct xpart *restrict xp, float dt_therm, + float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct entropy_floor_properties *floor_props) { + + /* Wind particles do not need the extra kick evolution */ + if (p->decoupled) return; + + /* Integrate the internal energy forward in time */ + const float delta_u = p->u_dt * dt_therm; + + /* Do not decrease the energy by more than a factor of 2*/ + xp->u_full = max(xp->u_full + delta_u, 0.5f * xp->u_full); + + /* Check against entropy floor */ + const float floor_A = entropy_floor(p, cosmo, floor_props); + const float floor_u = gas_internal_energy_from_entropy(p->rho, floor_A); + + /* Check against absolute minimum */ + const float min_u = + hydro_props->minimal_internal_energy / cosmo->a_factor_internal_energy; + + /* Take highest of both limits */ + const float energy_min = max(min_u, floor_u); + + if (xp->u_full < energy_min) { + xp->u_full = energy_min; + p->u_dt = 0.f; + } +} + +/** + * @brief Converts hydro quantity of a particle at the start of a run + * + * This function is called once at the end of the engine_init_particle() + * routine (at the start of a calculation) after the densities of + * particles have been computed. + * This can be used to convert internal energy into entropy for instance. + * + * @param p The particle to act upon + * @param xp The extended particle to act upon + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme. + * @param pressure_floor The properties of the pressure floor. + */ +__attribute__((always_inline)) INLINE static void hydro_convert_quantities( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct pressure_floor_props *pressure_floor) { + /* Convert the physcial internal energy to the comoving one. */ + /* u' = a^(3(g-1)) u */ + const float factor = 1.f / cosmo->a_factor_internal_energy; + p->u *= factor; + xp->u_full = p->u; + + /* Apply the minimal energy limit */ + const float min_comoving_energy = + hydro_props->minimal_internal_energy / cosmo->a_factor_internal_energy; + if (xp->u_full < min_comoving_energy) { + xp->u_full = min_comoving_energy; + p->u = min_comoving_energy; + p->u_dt = 0.f; + } + + /* Set the initial value of the artificial viscosity based on the non-variable + schemes for safety */ + + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + const float pressure_including_floor = + pressure_floor_get_comoving_pressure(p, pressure_floor, pressure, cosmo); + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, pressure_including_floor); + + p->force.pressure = pressure_including_floor; + p->force.soundspeed = soundspeed; +} + +/** + * @brief Initialises the particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions or assignments between the particle + * and extended particle fields. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_first_init_part( + struct part *restrict p, struct xpart *restrict xp) { + p->time_bin = 0; + + xp->v_full[0] = p->v[0]; + xp->v_full[1] = p->v[1]; + xp->v_full[2] = p->v[2]; + xp->u_full = p->u; + + p->gradients.C_well_conditioned = 1; + p->gradients.D_well_conditioned = 1; + p->gradients.u_well_conditioned = 1; +#ifdef MAGMA2_DEBUG_CHECKS + p->debug.N_force_low_order_grad = 0; + p->debug.N_force_high_order_grad = 0; + p->debug.C_ill_conditioned_count = 0; + p->debug.D_ill_conditioned_count = 0; + p->debug.u_ill_conditioned_count = 0; + for (int i = 0; i < 3; i++) { + p->debug.correction_matrix[i][0] = 0.; + p->debug.correction_matrix[i][1] = 0.; + p->debug.correction_matrix[i][2] = 0.; + + p->debug.velocity_tensor_aux[i][0] = 0.; + p->debug.velocity_tensor_aux[i][1] = 0.; + p->debug.velocity_tensor_aux[i][2] = 0.; + + p->debug.velocity_tensor_aux_norm[i][0] = 0.; + p->debug.velocity_tensor_aux_norm[i][1] = 0.; + p->debug.velocity_tensor_aux_norm[i][2] = 0.; + } +#endif + + hydro_reset_acceleration(p); + hydro_init_part(p, NULL); + + p->decoupled = 0; + p->to_be_decoupled = 0; + p->to_be_recoupled = 0; + +} + +/** + * @brief Overwrite the initial internal energy of a particle. + * + * Note that in the cases where the thermodynamic variable is not + * internal energy but gets converted later, we must overwrite that + * field. The conversion to the actual variable happens later after + * the initial fake time-step. + * + * @param p The #part to write to. + * @param u_init The new initial internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_init_internal_energy(struct part *p, float u_init) { + p->u = u_init; +} + +#endif /* SWIFT_MAGMA2_HYDRO_H */ diff --git a/src/hydro/MAGMA2/hydro_debug.h b/src/hydro/MAGMA2/hydro_debug.h new file mode 100644 index 0000000000..84852bbb73 --- /dev/null +++ b/src/hydro/MAGMA2/hydro_debug.h @@ -0,0 +1,66 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2025 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ + +#ifndef SWIFT_MAGMA2_HYDRO_DEBUG_H +#define SWIFT_MAGMA2_HYDRO_DEBUG_H + +/** + * @file MAGMA2/hydro_debug.h + * @brief Density-Energy non-conservative implementation of SPH, + * with added MAGMA2 physics (Rosswog 2020) (Debugging routines) + */ + +__attribute__((always_inline)) INLINE static void hydro_debug_particle( + const struct part* p, const struct xpart* xp) { + warning("[PID%lld] part:", p->id); + warning("[PID%lld] x=[%.3e,%.3e,%.3e]", p->id, p->x[0], p->x[1], p->x[2]); + warning("[PID%lld] v=[%.3e,%.3e,%.3e]", p->id, p->v[0], p->v[1], p->v[2]); + warning("[PID%lld] a=[%.3e,%.3e,%.3e]", p->id, p->a_hydro[0], p->a_hydro[1], + p->a_hydro[2]); + warning("[PID%lld] u=%.3e, du/dt=%.3e P=%.3e", p->id, p->u, + p->u_dt, hydro_get_comoving_pressure(p)); + warning("[PID%lld] h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e", p->id, + p->h, p->force.h_dt, (int)p->density.wcount, p->mass, + p->density.rho_dh); + warning( + "[PID%lld] time_bin=%d, rho=%.3e, velocity_gradient=[" + "[%.3e,%.3e,%.3e]," + "[%.3e,%.3e,%.3e]," + "[%.3e,%.3e,%.3e]]", + p->id, p->time_bin, p->rho, + p->gradients.velocity_tensor[0][0], + p->gradients.velocity_tensor[0][1], + p->gradients.velocity_tensor[0][2], + p->gradients.velocity_tensor[1][0], + p->gradients.velocity_tensor[1][1], + p->gradients.velocity_tensor[1][2], + p->gradients.velocity_tensor[2][0], + p->gradients.velocity_tensor[2][1], + p->gradients.velocity_tensor[2][2]); + + if (xp != NULL) { + warning("[PID%lld] xpart:", p->id); + warning("[PID%lld] v_full=[%.3e,%.3e,%.3e]", p->id, xp->v_full[0], + xp->v_full[1], xp->v_full[2]); + } +} + +#endif /* SWIFT_MAGMA2_HYDRO_DEBUG_H */ diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h new file mode 100644 index 0000000000..e361424f1c --- /dev/null +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -0,0 +1,1549 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2025 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_MAGMA2_HYDRO_IACT_H +#define SWIFT_MAGMA2_HYDRO_IACT_H + +/** + * @file MAGMA2/hydro_iact.h + * @brief Density-Energy non-conservative implementation of SPH, + * with added MAGMA2 physics (Rosswog 2020) (interaction routines) + */ + +#include "adaptive_softening_iact.h" +#include "adiabatic_index.h" +#include "hydro_parameters.h" +#include "minmax.h" +#include "signal_velocity.h" + +/** + * @brief Density interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of part*icle i. + * @param hj Comoving smoothing-length of part*icle j. + * @param pi First part*icle. + * @param pj Second part*icle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_density( + const float r2, const float dx[3], const float hi, const float hj, + struct part* restrict pi, struct part* restrict pj, const float a, + const float H) { + + const unsigned char decoupled_i = pi->decoupled; + const unsigned char decoupled_j = pj->decoupled; + if ((decoupled_i && !decoupled_j) || (!decoupled_i && decoupled_j)) return; + + /* Kernel weights to be filled */ + float wi, wj, wi_dx, wj_dx; + + const hydro_real_t r = sqrt(r2); + + /* Get the masses. */ + const hydro_real_t mi = pi->mass; + const hydro_real_t mj = pj->mass; + + /* Compute density of pi. */ + const hydro_real_t hi_inv = 1. / hi; + const float xi = r * hi_inv; + + kernel_deval(xi, &wi, &wi_dx); + + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); + + pi->density.wcount += wi; + pi->density.wcount_dh -= (hydro_dimension * wi + xi * wi_dx); + + adaptive_softening_add_correction_term(pi, xi, hi_inv, mj); + + /* Compute density of pj. */ + const hydro_real_t hj_inv = 1. / hj; + const float xj = r * hj_inv; + kernel_deval(xj, &wj, &wj_dx); + + pj->rho += mi * wj; + pj->density.rho_dh -= mi * (hydro_dimension * wj + xj * wj_dx); + + pj->density.wcount += wj; + pj->density.wcount_dh -= (hydro_dimension * wj + xj * wj_dx); + + adaptive_softening_add_correction_term(pj, xj, hj_inv, mi); + + /* For slope limiter */ + pi->gradients.kernel_size = fmax(r, pi->gradients.kernel_size); + pj->gradients.kernel_size = fmax(r, pj->gradients.kernel_size); + + /* Now we need to compute the derivative terms */ + const hydro_real_t r_inv = r ? 1.0 / r : 0.0; + const hydro_real_t faci = mj * wi_dx * r_inv; + const hydro_real_t facj = mi * wj_dx * r_inv; + + /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary + * vector and norm for the gradient */ + const hydro_real_t du = pi->u - pj->u; + + /* For slope limiter */ + pi->gradients.du_min = fmin(-du, pi->gradients.du_min); + pi->gradients.du_max = fmax(-du, pi->gradients.du_max); + + pj->gradients.du_min = fmin(du, pj->gradients.du_min); + pj->gradients.du_max = fmax(du, pj->gradients.du_max); + + pi->gradients.u_aux[0] += du * dx[0] * faci; + pi->gradients.u_aux[1] += du * dx[1] * faci; + pi->gradients.u_aux[2] += du * dx[2] * faci; + + pj->gradients.u_aux[0] += du * dx[0] * facj; + pj->gradients.u_aux[1] += du * dx[1] * facj; + pj->gradients.u_aux[2] += du * dx[2] * facj; + + pi->gradients.u_aux_norm[0] += dx[0] * dx[0] * faci; + pi->gradients.u_aux_norm[1] += dx[1] * dx[1] * faci; + pi->gradients.u_aux_norm[2] += dx[2] * dx[2] * faci; + + pj->gradients.u_aux_norm[0] += dx[0] * dx[0] * facj; + pj->gradients.u_aux_norm[1] += dx[1] * dx[1] * facj; + pj->gradients.u_aux_norm[2] += dx[2] * dx[2] * facj; + + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; + + /* Equations 19 & 20 in Rosswog 2020. Signs are all positive because + * dv * dx always results in a positive sign. */ + for (int i = 0; i < 3; i++) { + + /* For slope limiter */ + pi->gradients.dv_min[i] = fmin(-dv[i], pi->gradients.dv_min[i]); + pi->gradients.dv_max[i] = fmax(-dv[i], pi->gradients.dv_max[i]); + + pj->gradients.dv_min[i] = fmin(dv[i], pj->gradients.dv_min[i]); + pj->gradients.dv_max[i] = fmax(dv[i], pj->gradients.dv_max[i]); + + for (int k = 0; k < 3; k++) { + pi->gradients.velocity_tensor_aux[i][k] += dv[i] * dx[k] * faci; + pj->gradients.velocity_tensor_aux[i][k] += dv[i] * dx[k] * facj; + + pi->gradients.velocity_tensor_aux_norm[i][k] += dx[i] * dx[k] * faci; + pj->gradients.velocity_tensor_aux_norm[i][k] += dx[i] * dx[k] * facj; + } + } + +#ifdef MAGMA2_DEBUG_CHECKS + /* Number of neighbors */ + pi->debug.num_ngb++; + pj->debug.num_ngb++; +#endif + +#ifdef hydro_props_use_adiabatic_correction + /* Needed for the adiabatic kernel correction factor */ + pi->gradients.adiabatic_f_numerator += mj * r2 * wi; + pj->gradients.adiabatic_f_numerator += mi * r2 * wj; +#endif + +} + +/** + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of part*icle i. + * @param hj Comoving smoothing-length of part*icle j. + * @param pi First part*icle. + * @param pj Second part*icle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( + const float r2, const float dx[3], const float hi, const float hj, + struct part* restrict pi, const struct part* restrict pj, const float a, + const float H) { + + const unsigned char decoupled_i = pi->decoupled; + const unsigned char decoupled_j = pj->decoupled; + if ((decoupled_i && !decoupled_j) || (!decoupled_i && decoupled_j)) return; + + /* Kernel weights to be filled */ + float wi, wi_dx; + + /* Get the masses. */ + const hydro_real_t mj = pj->mass; + + /* Get r and r inverse. */ + const hydro_real_t r = sqrt(r2); + + const hydro_real_t h_inv = 1. / hi; + const float xi = r * h_inv; + kernel_deval(xi, &wi, &wi_dx); + + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); + + pi->density.wcount += wi; + pi->density.wcount_dh -= (hydro_dimension * wi + xi * wi_dx); + + adaptive_softening_add_correction_term(pi, xi, h_inv, mj); + + /* For slope limiter */ + pi->gradients.kernel_size = fmax(r, pi->gradients.kernel_size); + + const hydro_real_t r_inv = r ? 1.0 / r : 0.0; + const hydro_real_t faci = mj * wi_dx * r_inv; + + /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary + * vector and norm for the gradient */ + const hydro_real_t du = pi->u - pj->u; + + /* For slope limiter */ + pi->gradients.du_min = fmin(-du, pi->gradients.du_min); + pi->gradients.du_max = fmax(-du, pi->gradients.du_max); + + pi->gradients.u_aux[0] += du * dx[0] * faci; + pi->gradients.u_aux[1] += du * dx[1] * faci; + pi->gradients.u_aux[2] += du * dx[2] * faci; + + pi->gradients.u_aux_norm[0] += dx[0] * dx[0] * faci; + pi->gradients.u_aux_norm[1] += dx[1] * dx[1] * faci; + pi->gradients.u_aux_norm[2] += dx[2] * dx[2] * faci; + + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; + + /* Equations 19 & 20 in Rosswog 2020. Signs are all positive because + * dv * dx always results in a positive sign. */ + for (int i = 0; i < 3; i++) { + + /* For slope limiter */ + pi->gradients.dv_min[i] = fmin(-dv[i], pi->gradients.dv_min[i]); + pi->gradients.dv_max[i] = fmax(-dv[i], pi->gradients.dv_max[i]); + + for (int k = 0; k < 3; k++) { + pi->gradients.velocity_tensor_aux[i][k] += dv[i] * dx[k] * faci; + pi->gradients.velocity_tensor_aux_norm[i][k] += dx[i] * dx[k] * faci; + } + } + +#ifdef MAGMA2_DEBUG_CHECKS + /* Neighbour number */ + pi->debug.num_ngb++; +#endif + +#ifdef hydro_props_use_adiabatic_correction + /* Needed for the adiabatic kernel correction factor */ + pi->gradients.adiabatic_f_numerator += mj * r2 * wi; +#endif + +} + +/** + * @brief Calculate the gradient interaction between particle i and particle j + * + * This method wraps around hydro_gradients_collect, which can be an empty + * method, in which case no gradients are used. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part* restrict pi, struct part* restrict pj, const float a, + const float H) { + + const unsigned char decoupled_i = pi->decoupled; + const unsigned char decoupled_j = pj->decoupled; + if (decoupled_i || decoupled_j) return; + + /* Get particle properties */ + const hydro_real_t mi = hydro_get_mass(pi); + const hydro_real_t mj = hydro_get_mass(pj); + + const hydro_real_t rhoi = hydro_get_comoving_density(pi); + const hydro_real_t rhoj = hydro_get_comoving_density(pj); + + const hydro_real_t rhoi_inv = 1. / rhoi; + const hydro_real_t rhoj_inv = 1. / rhoj; + + const hydro_real_t r = sqrt(r2); + const hydro_real_t r_inv = r ? 1.0 / r : 0.0; + + float wi, wi_dx, wj, wj_dx; + + const float xi = r / hi; + const float xj = r / hj; + + kernel_deval(xi, &wi, &wi_dx); + kernel_deval(xj, &wj, &wj_dx); + + const hydro_real_t faci = mj * rhoj_inv * wi; + const hydro_real_t facj = mi * rhoi_inv * wj; + + /* Compute all of the first-order gradients, and second-order gradients */ + + /* Rosswog 2020 Equation 18 gradients. In the paper he uses (vj - vi) and + * (rj - ri), however this is symmetric so no sign problems. */ + + /* Internal energy gradient */ + const hydro_real_t du = pi->u - pj->u; + + pi->gradients.u[0] += du * dx[0] * faci; + pi->gradients.u[1] += du * dx[1] * faci; + pi->gradients.u[2] += du * dx[2] * faci; + + pj->gradients.u[0] += du * dx[0] * facj; + pj->gradients.u[1] += du * dx[1] * facj; + pj->gradients.u[2] += du * dx[2] * facj; + + /* Velocity gradients */ + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; + + + for (int k = 0; k < 3; k++) { + const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[k]; + + for (int i = 0; i < 3; i++) { + pi->gradients.u_hessian[k][i] += du_k * dx[i] * faci; + pj->gradients.u_hessian[k][i] += du_k * dx[i] * facj; + + /* dx is signed as (pi - pj), but it is symmetric so we add */ + pi->gradients.correction_matrix[k][i] += dx[k] * dx[i] * faci; + pj->gradients.correction_matrix[k][i] += dx[k] * dx[i] * facj; + + /* Indices in Rosswog 2020 are i for dv and k for dx. In this loop, + * they are swapped just because correction_matrix is computed with + * the paper indices. */ + pi->gradients.velocity_tensor[k][i] += dv[k] * dx[i] * faci; + pj->gradients.velocity_tensor[k][i] += dv[k] * dx[i] * facj; + + const hydro_real_t dv_grad_ki = pi->gradients.velocity_tensor_aux[k][i] - + pj->gradients.velocity_tensor_aux[k][i]; + + /* Equation 19 indices: + * Index i: velocity direction + * Index k: gradient direction + * + * Our indices: + * Index k: velocity direction + * Index i: gradient direction + * Index j: second derivative gradient direction + */ + for (int j = 0; j < 3; j++) { + pi->gradients.velocity_hessian[k][i][j] += dv_grad_ki * dx[j] * faci; + pj->gradients.velocity_hessian[k][i][j] += dv_grad_ki * dx[j] * facj; + } + } + } + + /* Density gradients */ + const hydro_real_t drho_ij = rhoi - rhoj; + const hydro_real_t wi_dr = wi_dx * r_inv; + const hydro_real_t wj_dr = wj_dx * r_inv; + + pi->rho_gradient[0] += mj * drho_ij * dx[0] * wi_dr; + pi->rho_gradient[1] += mj * drho_ij * dx[1] * wi_dr; + pi->rho_gradient[2] += mj * drho_ij * dx[2] * wi_dr; + + pj->rho_gradient[0] += mi * drho_ij * dx[0] * wj_dr; + pj->rho_gradient[1] += mi * drho_ij * dx[1] * wj_dr; + pj->rho_gradient[2] += mi * drho_ij * dx[2] * wj_dr; + +#ifdef hydro_props_use_adiabatic_correction + /* Correction terms for div v */ + pi->gradients.adiabatic_f_denominator += mj * rhoj_inv * r2 * wi; + pj->gradients.adiabatic_f_denominator += mi * rhoi_inv * r2 * wj; +#endif + +} + +/** + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version + * + * This method wraps around hydro_gradients_nonsym_collect, which can be an + * empty method, in which case no gradients are used. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part* restrict pi, struct part* restrict pj, const float a, + const float H) { + + const unsigned char decoupled_i = pi->decoupled; + const unsigned char decoupled_j = pj->decoupled; + if (decoupled_i || decoupled_j) return; + + /* Get particle properties */ + const hydro_real_t mj = hydro_get_mass(pj); + const hydro_real_t rhoi = hydro_get_comoving_density(pi); + const hydro_real_t rhoj = hydro_get_comoving_density(pj); + const hydro_real_t rhoj_inv = 1. / rhoj; + const hydro_real_t r = sqrt(r2); + const hydro_real_t r_inv = r ? 1.0 / r : 0.0; + float wi, wi_dx; + const float xi = r / hi; + kernel_deval(xi, &wi, &wi_dx); + const hydro_real_t faci = mj * rhoj_inv * wi; + + /* Compute all of the first-order gradients, and second-order gradients */ + + /* Rosswog 2020 Equation 18 gradients. In the paper he uses (vj - vi) and + * (rj - ri), however this is symmetric so no sign problems. */ + + /* Internal energy gradient */ + const hydro_real_t du = pi->u - pj->u; + + pi->gradients.u[0] += du * dx[0] * faci; + pi->gradients.u[1] += du * dx[1] * faci; + pi->gradients.u[2] += du * dx[2] * faci; + + /* Velocity gradients */ + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; + + + for (int k = 0; k < 3; k++) { + const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[k]; + + for (int i = 0; i < 3; i++) { + pi->gradients.u_hessian[k][i] += du_k * dx[i] * faci; + + /* dx is signed as (pi - pj), but it is symmetric so we add */ + pi->gradients.correction_matrix[k][i] += dx[k] * dx[i] * faci; + + /* Indices in Rosswog 2020 are i for dv and k for dx. In this loop, + * they are swapped just because correction_matrix is computed with + * the paper indices. */ + pi->gradients.velocity_tensor[k][i] += dv[k] * dx[i] * faci; + + const hydro_real_t dv_grad_ki = pi->gradients.velocity_tensor_aux[k][i] - + pj->gradients.velocity_tensor_aux[k][i]; + + /* Equation 19 indices: + * Index i: velocity direction + * Index k: gradient direction + * + * Our indices: + * Index k: velocity direction + * Index i: gradient direction + * Index j: second derivative gradient direction + */ + for (int j = 0; j < 3; j++) { + pi->gradients.velocity_hessian[k][i][j] += dv_grad_ki * dx[j] * faci; + } + } + } + + /* Density gradients */ + const hydro_real_t drho_ij = rhoi - rhoj; + const hydro_real_t wi_dr = wi_dx * r_inv; + + pi->rho_gradient[0] += mj * drho_ij * dx[0] * wi_dr; + pi->rho_gradient[1] += mj * drho_ij * dx[1] * wi_dr; + pi->rho_gradient[2] += mj * drho_ij * dx[2] * wi_dr; + +#ifdef hydro_props_use_adiabatic_correction + /* Correction terms for div v */ + pi->gradients.adiabatic_f_denominator += mj * rhoj_inv * r2 * wi; +#endif + +} + +/** + * @brief Force interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of part*icle i. + * @param hj Comoving smoothing-length of part*icle j. + * @param pi First part*icle. + * @param pj Second part*icle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_force( + const float r2, const float dx[3], const float hi, const float hj, + struct part* restrict pi, struct part* restrict pj, const float a, + const float H) { + + const unsigned char decoupled_i = pi->decoupled; + const unsigned char decoupled_j = pj->decoupled; + if (decoupled_i || decoupled_j) return; + + /* Cosmological factors entering the EoMs */ + const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); + const hydro_real_t a2_Hubble = a * a * H; + + const hydro_real_t r = sqrt(r2); + const hydro_real_t r_inv = r ? 1.0 / r : 0.0; + + /* Recover some data */ + const hydro_real_t mj = pj->mass; + const hydro_real_t mi = pi->mass; + + const hydro_real_t rhoi = pi->rho; + const hydro_real_t rhoj = pj->rho; + const hydro_real_t rhoi_inv = 1. / rhoi; + const hydro_real_t rhoj_inv = 1. / rhoj; + const hydro_real_t rhoij_inv = rhoi_inv * rhoj_inv; + + const hydro_real_t pressurei = pi->force.pressure; + const hydro_real_t pressurej = pj->force.pressure; + + /* Get the kernel for hi. */ + const hydro_real_t hi_inv = 1. / hi; + const hydro_real_t hi_inv_dim = pow_dimension(hi_inv); + const float xi = r * hi_inv; + float wi, wi_dx; + kernel_deval(xi, &wi, &wi_dx); + + /* Get the kernel for hj. */ + const hydro_real_t hj_inv = 1. / hj; + const hydro_real_t hj_inv_dim = pow_dimension(hj_inv); + const float xj = r * hj_inv; + float wj, wj_dx; + kernel_deval(xj, &wj, &wj_dx); + + /* For dh/dt and fall-back SPH */ + const hydro_real_t hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ + const hydro_real_t hj_inv_dim_plus_one = hj_inv * hj_inv_dim; + const hydro_real_t wi_dr = hi_inv_dim_plus_one * wi_dx; + const hydro_real_t wj_dr = hj_inv_dim_plus_one * wj_dx; + + /* Peculiar velocity difference vector */ + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; + const hydro_real_t dv_raw[3] = {dv[0], dv[1], dv[2]}; + + /* For MAGMA2, this is the full anti-symmetric gradient vector. For the + * fallback Gasoline2-style SPH, this will just be the direction vector + * between the two particles (r_i - r_j). */ + hydro_real_t G_ij[3] = {0., 0., 0.}; + hydro_real_t G_rad_ij[3] = {0., 0., 0.}; + hydro_real_t G_ij_norm = 0.; + hydro_real_t G_rad_ij_norm = 0.; + + /* Separation vectors and swapped */ + const hydro_real_t dx_ij[3] = {dx[0], dx[1], dx[2]}; + const hydro_real_t dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; + hydro_real_t dx_ij_hat[3] = {0., 0., 0.}; + hydro_vec3_unit(dx_ij, dx_ij_hat); + + /* These are set whether or not we fall back onto SPH gradients */ + hydro_real_t visc_acc_term = 0.; + hydro_real_t sph_acc_term = (pressurei + pressurej) * rhoij_inv; + hydro_real_t sph_du_term_i = pressurei * rhoij_inv; + hydro_real_t sph_du_term_j = pressurej * rhoij_inv; + hydro_real_t visc_du_term = 0.; + hydro_real_t cond_du_term = 0.; + + /* Correction factor must be well-conditioned. */ + const unsigned char C_well_conditioned = + (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned); + /* First order density-independent velocity gradients */ + const unsigned char D_well_conditioned = + (pi->gradients.D_well_conditioned && pj->gradients.D_well_conditioned); + const unsigned char u_well_conditioned = + (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned); + + /* Flag to revert to use high order gradients */ + unsigned char high_order_gradients_flag = 0; + + /* Always use high order gradients when both pi and pj are well-conditioned */ + if (C_well_conditioned && D_well_conditioned && u_well_conditioned) { + + /* Corrected gradients */ + hydro_real_t G_i[3] = {0., 0., 0.}; + hydro_real_t G_j[3] = {0., 0., 0.}; + /* Rosswog 2020 Eqs 4 & 5 use dx_ji for both */ + hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx_ji, G_i); + hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx_ji, G_j); + + G_i[0] *= wi * hi_inv_dim; + G_i[1] *= wi * hi_inv_dim; + G_i[2] *= wi * hi_inv_dim; + + G_j[0] *= wj * hj_inv_dim; + G_j[1] *= wj * hj_inv_dim; + G_j[2] *= wj * hj_inv_dim; + + /* Averaged correction gradient. Note: antisymmetric, so only need + * a sign flip for pj */ + hydro_get_average_kernel_gradient(pi, pj, G_i, G_j, G_ij); + + /* Compute from j perspective to ensure perfectly anti-symmetric */ + G_j[0] = 0.; + G_j[1] = 0.; + G_j[2] = 0.; + + G_i[0] = 0.; + G_i[1] = 0.; + G_i[2] = 0.; + + /* Swap G_i and G_j */ + hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx_ij, G_j); + hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx_ij, G_i); + + G_j[0] *= wj * hj_inv_dim; + G_j[1] *= wj * hj_inv_dim; + G_j[2] *= wj * hj_inv_dim; + + G_i[0] *= wi * hi_inv_dim; + G_i[1] *= wi * hi_inv_dim; + G_i[2] *= wi * hi_inv_dim; + + hydro_real_t G_ji[3] = {0., 0., 0.}; + hydro_get_average_kernel_gradient(pj, pi, G_j, G_i, G_ji); + + /* Average the two estimators */ + G_ij[0] = 0.5 * (G_ij[0] - G_ji[0]); + G_ij[1] = 0.5 * (G_ij[1] - G_ji[1]); + G_ij[2] = 0.5 * (G_ij[2] - G_ji[2]); + + /* Check if G_ij is extremely misaligned with the radial direction */ + G_ij_norm = hydro_vec3_norm(G_ij); + + /* Get G_ij along the separation vector */ + hydro_real_t G_ij_dot_dx_ij_hat = hydro_vec3_vec3_dot(G_ij, dx_ij_hat); + const hydro_real_t G_ij_dot_dx_ij_hat_abs = fabs(G_ij_dot_dx_ij_hat); + + /* Find the cos(theta) term between G and dx */ + hydro_real_t cosine_G_ij_dx_ij_hat = + G_ij_dot_dx_ij_hat_abs / (G_ij_norm + 1.e-10); + + /* Handle floating point errors */ + if (cosine_G_ij_dx_ij_hat > 1.) cosine_G_ij_dx_ij_hat = 1.; + + const unsigned char G_has_large_angle = + (cosine_G_ij_dx_ij_hat < const_viscosity_cosine_limit); + const unsigned char G_in_wrong_direction = (G_ij_dot_dx_ij_hat > 0.); + + /* Good angle between separation and correct direction, good to go! */ + if (!G_has_large_angle && !G_in_wrong_direction) { + + /* Make sure we use the correct interaction */ + high_order_gradients_flag = 1; + +#ifdef hydro_props_use_radial_artificial_terms + /* G along the separation vector */ + G_rad_ij[0] = G_ij_dot_dx_ij_hat * dx_ij_hat[0]; + G_rad_ij[1] = G_ij_dot_dx_ij_hat * dx_ij_hat[1]; + G_rad_ij[2] = G_ij_dot_dx_ij_hat * dx_ij_hat[2]; +#else + G_rad_ij[0] = G_ij[0]; + G_rad_ij[1] = G_ij[1]; + G_rad_ij[2] = G_ij[2]; +#endif + } + else { + /* Revert back to standard separation vector */ + G_ij[0] = dx[0]; + G_ij[1] = dx[1]; + G_ij[2] = dx[2]; + G_ij_norm = hydro_vec3_norm(G_ij); + + /* Use the separation vector for SPH */ + G_rad_ij[0] = dx[0]; + G_rad_ij[1] = dx[1]; + G_rad_ij[2] = dx[2]; + } + } + + /* MAGMA2-style gradients (Matrix-Inversion-2 SPH) */ + if (high_order_gradients_flag) { + + /* Compute second order reconstruction of velocity between pi & pj */ + hydro_real_t vi_reconstructed[3] = {0., 0., 0.}; + hydro_real_t vj_reconstructed[3] = {0., 0., 0.}; + + /* Important: Rosswog 2020 h_i and h_j are without kernel_gamma. Therefore, + * use xi and xj in the slope limiting procedure. */ + + /* Compute global Van Leer limiter (scalar, not component-wise) */ + const hydro_real_t phi_ij_vec = + hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, + pj->gradients.velocity_tensor, + dx_ij, xi, xj); + + const hydro_real_t phi_ji_vec = + hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, + pi->gradients.velocity_tensor, + dx_ji, xj, xi); + + /* Make sure no floating point problems */ + hydro_real_t phi_vec_sym = fmin(phi_ij_vec, phi_ji_vec); + phi_vec_sym = fmin(1., phi_vec_sym); + phi_vec_sym = fmax(0., phi_vec_sym); + + /* Need these recast in case of switching precision */ + const hydro_real_t v_i[3] = {pi->v[0], pi->v[1], pi->v[2]}; + const hydro_real_t v_j[3] = {pj->v[0], pj->v[1], pj->v[2]}; + + /* dx_ji for particle i and dx_ij for particle j */ + hydro_vector_second_order_reconstruction(phi_vec_sym, dx_ji, v_i, + pi->gradients.velocity_tensor, + pi->gradients.velocity_hessian, + vi_reconstructed); + + hydro_vector_second_order_reconstruction(phi_vec_sym, dx_ij, v_j, + pj->gradients.velocity_tensor, + pj->gradients.velocity_hessian, + vj_reconstructed); + + const hydro_real_t dv_reconstructed[3] = { + vi_reconstructed[0] - vj_reconstructed[0], + vi_reconstructed[1] - vj_reconstructed[1], + vi_reconstructed[2] - vj_reconstructed[2] + }; + + /* Get velocity difference, but limit reconstructed values */ + hydro_real_t dv_ij[3] = {0., 0., 0.}; + hydro_vec_minmod_limiter(dv_reconstructed, dv_raw, + pi->gradients.dv_min, pi->gradients.dv_max, + pj->gradients.dv_min, pj->gradients.dv_max, + dv_ij); + + /* Artificial viscosity */ + + + /* Get the acceleration term, depends on the weighting scheme */ + visc_acc_term = + hydro_get_visc_acc_term(pi, pj, dv_ij, dx_ij, fac_mu, a2_Hubble); + + /* Split heating between the two particles */ + visc_du_term = 0.5 * visc_acc_term; + + + /* Artificial conductivity */ + + + hydro_real_t ui_reconstructed = 0.; + hydro_real_t uj_reconstructed = 0.; + + + /* Compute global Van Leer limiter (scalar, not component-wise) */ + const hydro_real_t phi_ij_scalar = + hydro_scalar_van_leer_phi(pi->gradients.u, + pj->gradients.u, + dx_ij, xi, xj); + const hydro_real_t phi_ji_scalar = + hydro_scalar_van_leer_phi(pj->gradients.u, + pi->gradients.u, + dx_ji, xj, xi); + + /* Make sure no floating point problems */ + hydro_real_t phi_scalar_sym = fmin(phi_ij_scalar, phi_ji_scalar); + phi_scalar_sym = fmin(1., phi_scalar_sym); + phi_scalar_sym = fmax(0., phi_scalar_sym); + + /* dx_ji for particle i and dx_ij for particle j */ + hydro_scalar_second_order_reconstruction(phi_scalar_sym, dx_ji, + (hydro_real_t)pi->u, + pi->gradients.u, + pi->gradients.u_hessian, + &ui_reconstructed); + + hydro_scalar_second_order_reconstruction(phi_scalar_sym, dx_ij, + (hydro_real_t)pj->u, + pj->gradients.u, + pj->gradients.u_hessian, + &uj_reconstructed); + + const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); + const hydro_real_t rho_ij_inv = 1. / rho_ij; + const hydro_real_t dv_Hubble[3] = { + dv[0] + a2_Hubble * dx_ij[0], + dv[1] + a2_Hubble * dx_ij[1], + dv[2] + a2_Hubble * dx_ij[2] + }; + + const hydro_real_t dv_Hubble_dot_dx_ij_hat = + hydro_vec3_vec3_dot(dv_Hubble, dx_ij_hat); + + /* Limit art. cond. to only when information is communicable */ + const hydro_real_t c_ij = + 0.5 * (pi->force.soundspeed + pj->force.soundspeed); + const hydro_real_t v_sig_alpha = c_ij * (1. + 0.75 * const_viscosity_alpha); + + /* Must connect the particles along the LOS */ + hydro_real_t mu_ij = fac_mu * dv_Hubble_dot_dx_ij_hat; + const hydro_real_t v_sig_beta = 0.75 * const_viscosity_beta * mu_ij; + + /* Skip conduction if expansion beats sound speed along LOS */ + if (v_sig_alpha > v_sig_beta) { + + const hydro_real_t dv_ij_Hubble[3] = { + dv_ij[0] + a2_Hubble * dx_ij[0], + dv_ij[1] + a2_Hubble * dx_ij[1], + dv_ij[2] + a2_Hubble * dx_ij[2] + }; + + /* Signal velocity from speed contributions */ +#ifdef hydro_props_use_radial_artificial_terms + const hydro_real_t dv_ij_Hubble_dot_dx_ij_hat = + hydro_vec3_vec3_dot(dv_ij_Hubble, dx_ij_hat); + const hydro_real_t v_sig_speed = + fac_mu * fabs(dv_ij_Hubble_dot_dx_ij_hat); +#else + const hydro_real_t v_sig_speed = fac_mu * hydro_vec3_norm(dv_ij_Hubble); +#endif + + /* Get spec. energy difference, but limit reconstructed values */ + const hydro_real_t du_raw = pi->u - pj->u; + const hydro_real_t du_reconstructed = ui_reconstructed - uj_reconstructed; + const hydro_real_t du_ij = + hydro_scalar_minmod_limiter(du_reconstructed, du_raw, + pi->gradients.du_min, pi->gradients.du_max, + pj->gradients.du_min, pj->gradients.du_max); + + const hydro_real_t alpha_cond = const_conductivity_alpha; + const hydro_real_t delta_P = fabs(pressurei - pressurej); + const hydro_real_t P_lim = delta_P / (pressurei + pressurej); + + /* Add conductivity to the specific energy */ + cond_du_term = alpha_cond * P_lim * v_sig_speed * du_ij * rho_ij_inv; + } + else { + mu_ij = 0.; + } + + + /* Finalize everything with the correct normalizations. */ + + + /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ + const hydro_real_t dv_dot_G_ij = hydro_vec3_vec3_dot(dv_raw, G_ij); + +#ifdef hydro_props_use_radial_artificial_terms + /* Compute Hubble flow along LOS */ + const hydro_real_t dv_Hubble_along_dx_ij[3] = { + dv_Hubble_dot_dx_ij_hat * dx_ij_hat[0], + dv_Hubble_dot_dx_ij_hat * dx_ij_hat[1], + dv_Hubble_dot_dx_ij_hat * dx_ij_hat[2] + }; + + const hydro_real_t dv_Hubble_dot_G_rad_ij = + hydro_vec3_vec3_dot(dv_Hubble_along_dx_ij, G_rad_ij); +#else + const hydro_real_t dv_Hubble_dot_G_rad_ij = + hydro_vec3_vec3_dot(dv_Hubble, G_rad_ij); +#endif + + /* Evolve the heating terms using the velocity divergence */ + sph_du_term_i *= dv_dot_G_ij; + sph_du_term_j *= dv_dot_G_ij; + cond_du_term *= -G_rad_ij_norm; /* Eq. 24 Rosswog 2020 */ + visc_du_term *= dv_Hubble_dot_G_rad_ij; + + if (visc_du_term <= 0.) { + visc_acc_term = 0.; + visc_du_term = 0.; + } + + + /* Get the time derivative for h. */ + + + /* Velocity divergence is from the SPH estimator, not the G_ij vector */ + const hydro_real_t dv_dot_dx_ij = hydro_vec3_vec3_dot(dv_raw, dx_ij); + pi->force.h_dt -= hydro_get_h_dt_sum(dv_dot_dx_ij, dv_dot_G_ij, + mj, rhoj_inv, r_inv, wi_dr); + pj->force.h_dt -= hydro_get_h_dt_sum(dv_dot_dx_ij, dv_dot_G_ij, + mi, rhoi_inv, r_inv, wj_dr); + + + /* Timestepping */ + + + /* Compute based on raw velocities */ + const hydro_real_t v_sig_visc = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); + + const hydro_real_t h_ij = 0.5 * (hi + hj); + pi->h_min = fmin(pi->h_min, h_ij); + pj->h_min = fmin(pj->h_min, h_ij); + + /* New timestep estimate */ + const hydro_real_t dt_min_i = h_ij / v_sig_visc; + const hydro_real_t dt_min_j = h_ij / v_sig_visc; + pi->dt_min = fmin(pi->dt_min, dt_min_i); + pj->dt_min = fmin(pj->dt_min, dt_min_j); + +#ifdef MAGMA2_DEBUG_CHECKS + pi->debug.N_force_high_order_grad++; + pj->debug.N_force_high_order_grad++; +#endif + } + else { /* Gasoline-like SPH fallback */ + + + /* Compute dv dot dr. */ + const hydro_real_t dvdr = hydro_vec3_vec3_dot(dv_raw, G_ij); + + /* Includes the hubble flow term; not used for du/dt */ + const hydro_real_t dvdr_Hubble = dvdr + a2_Hubble * r2; + + /* Are the particles moving towards each others ? */ + const hydro_real_t omega_ij = fmin(dvdr_Hubble, 0.); + + hydro_real_t mu_full_ij = fac_mu * r_inv * dvdr_Hubble; + const hydro_real_t mu_ij = fac_mu * r_inv * omega_ij; + + /* Construct the full viscosity term */ + const hydro_real_t b_ij = + 0.5 * (pi->gradients.balsara + pj->gradients.balsara); + const hydro_real_t rho_ij = rhoi + rhoj; + const hydro_real_t cs_ij = pi->force.soundspeed + pj->force.soundspeed; + const hydro_real_t c_ij = 0.5 * cs_ij; + const hydro_real_t alpha = const_viscosity_alpha; + const hydro_real_t visc = + omega_ij < 0. + ? (-0.25 * alpha * cs_ij * + mu_ij + + const_viscosity_beta * mu_ij * mu_ij) * b_ij / + (0.5 * rho_ij) + : 0.; + + visc_acc_term = const_fallback_reduction_factor * visc; + visc_du_term = 0.5 * visc_acc_term; + + const hydro_real_t v_sig_alpha = c_ij * (1. + 0.75 * const_viscosity_alpha); + const hydro_real_t v_sig_beta = 0.75 * const_viscosity_beta * mu_full_ij; + + if (v_sig_alpha > v_sig_beta) { + const hydro_real_t rho_ij_inv = 2. / rho_ij; + const hydro_real_t du = pi->u - pj->u; + + const hydro_real_t alpha_cond = const_conductivity_alpha; + const hydro_real_t delta_P = fabs(pressurei - pressurej); + const hydro_real_t P_lim = delta_P / (pressurei + pressurej); + + cond_du_term = alpha_cond * P_lim * fabs(mu_full_ij) * du * rho_ij_inv; + cond_du_term *= const_fallback_reduction_factor; + } + + /* New signal velocity */ + const hydro_real_t new_v_sig_visc = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); + + const hydro_real_t h_ij = 0.5 * (hi + hj); + pi->h_min = fmin(pi->h_min, h_ij); + pj->h_min = fmin(pj->h_min, h_ij); + + /* New timestep estimate */ + const hydro_real_t dt_min_i = h_ij / new_v_sig_visc; + const hydro_real_t dt_min_j = h_ij / new_v_sig_visc; + pi->dt_min = fmin(pi->dt_min, dt_min_i); + pj->dt_min = fmin(pj->dt_min, dt_min_j); + + const hydro_real_t kernel_gradient = 0.5 * (wi_dr + wj_dr) * r_inv; + + visc_acc_term *= kernel_gradient; + sph_acc_term *= kernel_gradient; + + sph_du_term_i *= dvdr * kernel_gradient; + sph_du_term_j *= dvdr * kernel_gradient; + visc_du_term *= dvdr_Hubble * kernel_gradient; + cond_du_term *= kernel_gradient; + + /* Get the time derivative for h. */ + pi->force.h_dt -= hydro_get_h_dt_sum(dvdr, 0., mj, rhoj_inv, r_inv, wi_dr); + pj->force.h_dt -= hydro_get_h_dt_sum(dvdr, 0., mi, rhoi_inv, r_inv, wj_dr); + +#ifdef MAGMA2_DEBUG_CHECKS + pi->debug.N_force_low_order_grad++; + pj->debug.N_force_low_order_grad++; +#endif + } + + + /* Get the time derivative for v. */ + + + /* Assemble the acceleration */ + const hydro_real_t acc[3] = { + sph_acc_term * G_ij[0] + visc_acc_term * G_rad_ij[0], + sph_acc_term * G_ij[1] + visc_acc_term * G_rad_ij[1], + sph_acc_term * G_ij[2] + visc_acc_term * G_rad_ij[2] + }; + + /* Use the force Luke ! */ + pi->a_hydro[0] -= mj * acc[0]; + pi->a_hydro[1] -= mj * acc[1]; + pi->a_hydro[2] -= mj * acc[2]; + + pj->a_hydro[0] += mi * acc[0]; + pj->a_hydro[1] += mi * acc[1]; + pj->a_hydro[2] += mi * acc[2]; + + + /* Get the time derivative for u. */ + + + /* Assemble the energy equation term */ + const hydro_real_t du_dt_i = sph_du_term_i + visc_du_term + cond_du_term; + const hydro_real_t du_dt_j = sph_du_term_j + visc_du_term - cond_du_term; + + /* Internal energy time derivative */ + pi->u_dt += du_dt_i * mj; + pj->u_dt += du_dt_j * mi; + + pi->u_dt_cond += cond_du_term * mj; + pj->u_dt_cond -= cond_du_term * mi; +} + +/** + * @brief Force interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of part*icle i. + * @param hj Comoving smoothing-length of part*icle j. + * @param pi First part*icle. + * @param pj Second part*icle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( + const float r2, const float dx[3], const float hi, const float hj, + struct part* restrict pi, const struct part* restrict pj, const float a, + const float H) { + + const unsigned char decoupled_i = pi->decoupled; + const unsigned char decoupled_j = pj->decoupled; + if (decoupled_i || decoupled_j) return; + + /* Cosmological factors entering the EoMs */ + const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); + const hydro_real_t a2_Hubble = a * a * H; + + const hydro_real_t r = sqrt(r2); + const hydro_real_t r_inv = r ? 1.0 / r : 0.0; + + /* Recover some data */ + const hydro_real_t mj = pj->mass; + + const hydro_real_t rhoi = pi->rho; + const hydro_real_t rhoj = pj->rho; + const hydro_real_t rhoi_inv = 1. / rhoi; + const hydro_real_t rhoj_inv = 1. / rhoj; + const hydro_real_t rhoij_inv = rhoi_inv * rhoj_inv; + + const hydro_real_t pressurei = pi->force.pressure; + const hydro_real_t pressurej = pj->force.pressure; + + /* Get the kernel for hi. */ + const hydro_real_t hi_inv = 1.0 / hi; + const hydro_real_t hi_inv_dim = pow_dimension(hi_inv); + const float xi = r * hi_inv; + float wi, wi_dx; + kernel_deval(xi, &wi, &wi_dx); + + /* Get the kernel for hj. */ + const hydro_real_t hj_inv = 1.0 / hj; + const hydro_real_t hj_inv_dim = pow_dimension(hj_inv); + const float xj = r * hj_inv; + float wj, wj_dx; + kernel_deval(xj, &wj, &wj_dx); + + /* For dh/dt and fall-back SPH */ + const hydro_real_t hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ + const hydro_real_t hj_inv_dim_plus_one = hj_inv * hj_inv_dim; + const hydro_real_t wi_dr = hi_inv_dim_plus_one * wi_dx; + const hydro_real_t wj_dr = hj_inv_dim_plus_one * wj_dx; + + /* Peculiar velocity difference vector */ + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; + const hydro_real_t dv_raw[3] = {dv[0], dv[1], dv[2]}; + + /* For MAGMA2, this is the full anti-symmetric gradient vector. For the + * fallback Gasoline2-style SPH, this will just be the direction vector + * between the two particles (r_i - r_j). */ + hydro_real_t G_ij[3] = {0., 0., 0.}; + hydro_real_t G_rad_ij[3] = {0., 0., 0.}; + hydro_real_t G_ij_norm = 0.; + hydro_real_t G_rad_ij_norm = 0.; + + /* Separation vectors and swapped */ + const hydro_real_t dx_ij[3] = {dx[0], dx[1], dx[2]}; + const hydro_real_t dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; + hydro_real_t dx_ij_hat[3] = {0., 0., 0.}; + hydro_vec3_unit(dx_ij, dx_ij_hat); + + /* These are set whether or not we fall back onto SPH gradients */ + hydro_real_t visc_acc_term = 0.; + hydro_real_t sph_acc_term = (pressurei + pressurej) * rhoij_inv; + hydro_real_t sph_du_term_i = pressurei * rhoij_inv; + hydro_real_t visc_du_term = 0.; + hydro_real_t cond_du_term = 0.; + + /* Correction factor must be well-conditioned. */ + const unsigned char C_well_conditioned = + (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned); + /* First order density-independent velocity gradients */ + const unsigned char D_well_conditioned = + (pi->gradients.D_well_conditioned && pj->gradients.D_well_conditioned); + const unsigned char u_well_conditioned = + (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned); + + /* Flag to use second order gradients */ + unsigned char high_order_gradients_flag = 0; + + /* Always use SPH gradients between particles if one of them has an + * ill-conditioned C matrix */ + if (C_well_conditioned && D_well_conditioned && u_well_conditioned) { + + /* Corrected gradients */ + hydro_real_t G_i[3] = {0., 0., 0.}; + hydro_real_t G_j[3] = {0., 0., 0.}; + /* Rosswog 2020 Eqs 4 & 5 use dx_ji for both */ + hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx_ji, G_i); + hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx_ji, G_j); + + G_i[0] *= wi * hi_inv_dim; + G_i[1] *= wi * hi_inv_dim; + G_i[2] *= wi * hi_inv_dim; + + G_j[0] *= wj * hj_inv_dim; + G_j[1] *= wj * hj_inv_dim; + G_j[2] *= wj * hj_inv_dim; + + /* Averaged correction gradient. Note: antisymmetric, so only need + * a sign flip for pj */ + hydro_get_average_kernel_gradient(pi, pj, G_i, G_j, G_ij); + + /* Compute from j perspective to ensure perfectly anti-symmetric */ + G_j[0] = 0.; + G_j[1] = 0.; + G_j[2] = 0.; + + G_i[0] = 0.; + G_i[1] = 0.; + G_i[2] = 0.; + + /* Swap G_i and G_j */ + hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx_ij, G_j); + hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx_ij, G_i); + + G_j[0] *= wj * hj_inv_dim; + G_j[1] *= wj * hj_inv_dim; + G_j[2] *= wj * hj_inv_dim; + + G_i[0] *= wi * hi_inv_dim; + G_i[1] *= wi * hi_inv_dim; + G_i[2] *= wi * hi_inv_dim; + + hydro_real_t G_ji[3] = {0., 0., 0.}; + hydro_get_average_kernel_gradient(pj, pi, G_j, G_i, G_ji); + + /* Average the two estimators */ + G_ij[0] = 0.5 * (G_ij[0] - G_ji[0]); + G_ij[1] = 0.5 * (G_ij[1] - G_ji[1]); + G_ij[2] = 0.5 * (G_ij[2] - G_ji[2]); + + /* Check if G_ij is extremely misaligned with the radial direction */ + G_ij_norm = hydro_vec3_norm(G_ij); + + /* Get G_ij along the separation vector */ + hydro_real_t G_ij_dot_dx_ij_hat = hydro_vec3_vec3_dot(G_ij, dx_ij_hat); + const hydro_real_t G_ij_dot_dx_ij_hat_abs = fabs(G_ij_dot_dx_ij_hat); + + /* Find the cos(theta) term between G and dx */ + hydro_real_t cosine_G_ij_dx_ij_hat = + G_ij_dot_dx_ij_hat_abs / (G_ij_norm + 1.e-10); + + /* Handle floating point errors */ + if (cosine_G_ij_dx_ij_hat > 1.) cosine_G_ij_dx_ij_hat = 1.; + + const unsigned char G_has_large_angle = + (cosine_G_ij_dx_ij_hat < const_viscosity_cosine_limit); + const unsigned char G_in_wrong_direction = (G_ij_dot_dx_ij_hat > 0.); + + /* Good angle between separation and correct direction, good to go! */ + if (!G_has_large_angle && !G_in_wrong_direction) { + + /* Make sure we use the correct gradients below */ + high_order_gradients_flag = 1; + +#ifdef hydro_props_use_radial_artificial_terms + /* G along the separation vector */ + G_rad_ij[0] = G_ij_dot_dx_ij_hat * dx_ij_hat[0]; + G_rad_ij[1] = G_ij_dot_dx_ij_hat * dx_ij_hat[1]; + G_rad_ij[2] = G_ij_dot_dx_ij_hat * dx_ij_hat[2]; +#else + G_rad_ij[0] = G_ij[0]; + G_rad_ij[1] = G_ij[1]; + G_rad_ij[2] = G_ij[2]; +#endif + } + else { + /* Revert back to standard separation vector */ + G_ij[0] = dx[0]; + G_ij[1] = dx[1]; + G_ij[2] = dx[2]; + G_ij_norm = hydro_vec3_norm(G_ij); + + /* Use the separation vector for SPH */ + G_rad_ij[0] = dx[0]; + G_rad_ij[1] = dx[1]; + G_rad_ij[2] = dx[2]; + } + + G_rad_ij_norm = hydro_vec3_norm(G_rad_ij); + } + + /* MAGMA2 style gradients (Matrix-Inversion-2 SPH) */ + if (high_order_gradients_flag) { + + /* Compute second order reconstruction of velocity between pi & pj */ + hydro_real_t vi_reconstructed[3] = {0., 0., 0.}; + hydro_real_t vj_reconstructed[3] = {0., 0., 0.}; + + /* Important: Rosswog 2020 h_i and h_j are without kernel_gamma. Therefore, + * use xi and xj in the slope limiting procedure. */ + + /* Compute global Van Leer limiter (scalar, not component-wise) */ + const hydro_real_t phi_ij_vec = + hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, + pj->gradients.velocity_tensor, + dx_ij, xi, xj); + const hydro_real_t phi_ji_vec = + hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, + pi->gradients.velocity_tensor, + dx_ji, xj, xi); + + /* Make sure no floating point problems */ + hydro_real_t phi_vec_sym = fmin(phi_ij_vec, phi_ji_vec); + phi_vec_sym = fmin(1., phi_vec_sym); + phi_vec_sym = fmax(0., phi_vec_sym); + + /* Need these recast in case of switching precision */ + const hydro_real_t v_i[3] = {pi->v[0], pi->v[1], pi->v[2]}; + const hydro_real_t v_j[3] = {pj->v[0], pj->v[1], pj->v[2]}; + + /* dx_ji for particle i and dx_ij for particle j */ + hydro_vector_second_order_reconstruction(phi_vec_sym, dx_ji, v_i, + pi->gradients.velocity_tensor, + pi->gradients.velocity_hessian, + vi_reconstructed); + + hydro_vector_second_order_reconstruction(phi_vec_sym, dx_ij, v_j, + pj->gradients.velocity_tensor, + pj->gradients.velocity_hessian, + vj_reconstructed); + + const hydro_real_t dv_reconstructed[3] = { + vi_reconstructed[0] - vj_reconstructed[0], + vi_reconstructed[1] - vj_reconstructed[1], + vi_reconstructed[2] - vj_reconstructed[2] + }; + + /* Get velocity difference, but limit reconstructed values */ + hydro_real_t dv_ij[3] = {0., 0., 0.}; + hydro_vec_minmod_limiter(dv_reconstructed, dv_raw, + pi->gradients.dv_min, pi->gradients.dv_max, + pj->gradients.dv_min, pj->gradients.dv_max, + dv_ij); + + + /* Artificial viscosity */ + + + /* Get the acceleration term, depends on the weighting scheme */ + visc_acc_term = + hydro_get_visc_acc_term(pi, pj, dv_ij, dx_ij, fac_mu, a2_Hubble); + + /* Split heating between the two particles */ + visc_du_term = 0.5 * visc_acc_term; + + + /* Artificial conductivity */ + + + hydro_real_t ui_reconstructed = 0.; + hydro_real_t uj_reconstructed = 0.; + + /* Compute global Van Leer limiter (scalar, not component-wise) */ + const hydro_real_t phi_ij_scalar = + hydro_scalar_van_leer_phi(pi->gradients.u, + pj->gradients.u, + dx_ij, xi, xj); + const hydro_real_t phi_ji_scalar = + hydro_scalar_van_leer_phi(pj->gradients.u, + pi->gradients.u, + dx_ji, xj, xi); + + /* Make sure no floating point problems */ + hydro_real_t phi_scalar_sym = fmin(phi_ij_scalar, phi_ji_scalar); + phi_scalar_sym = fmin(1., phi_scalar_sym); + phi_scalar_sym = fmax(0., phi_scalar_sym); + + /* dx_ji for particle i and dx_ij for particle j */ + hydro_scalar_second_order_reconstruction(phi_scalar_sym, dx_ji, + (hydro_real_t)pi->u, + pi->gradients.u, + pi->gradients.u_hessian, + &ui_reconstructed); + + hydro_scalar_second_order_reconstruction(phi_scalar_sym, dx_ij, + (hydro_real_t)pj->u, + pj->gradients.u, + pj->gradients.u_hessian, + &uj_reconstructed); + + const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); + const hydro_real_t rho_ij_inv = 1. / rho_ij; + const hydro_real_t dv_Hubble[3] = { + dv[0] + a2_Hubble * dx_ij[0], + dv[1] + a2_Hubble * dx_ij[1], + dv[2] + a2_Hubble * dx_ij[2] + }; + + const hydro_real_t dv_Hubble_dot_dx_ij_hat = + hydro_vec3_vec3_dot(dv_Hubble, dx_ij_hat); + + /* Limit art. cond. to only when information is communicable */ + const hydro_real_t c_ij = + 0.5 * (pi->force.soundspeed + pj->force.soundspeed); + const hydro_real_t v_sig_alpha = c_ij * (1. + 0.75 * const_viscosity_alpha); + + /* Must connect the particles along the LOS */ + hydro_real_t mu_ij = fac_mu * dv_Hubble_dot_dx_ij_hat; + const hydro_real_t v_sig_beta = 0.75 * const_viscosity_beta * mu_ij; + + /* Skip conduction if expansion beats sound speed along LOS */ + if (v_sig_alpha > v_sig_beta) { + + const hydro_real_t dv_ij_Hubble[3] = { + dv_ij[0] + a2_Hubble * dx_ij[0], + dv_ij[1] + a2_Hubble * dx_ij[1], + dv_ij[2] + a2_Hubble * dx_ij[2] + }; + + /* Signal velocity from speed contributions */ +#ifdef hydro_props_use_radial_artificial_terms + const hydro_real_t dv_ij_Hubble_dot_dx_ij_hat = + hydro_vec3_vec3_dot(dv_ij_Hubble, dx_ij_hat); + const hydro_real_t v_sig_speed = + fac_mu * fabs(dv_ij_Hubble_dot_dx_ij_hat); +#else + const hydro_real_t v_sig_speed = fac_mu * hydro_vec3_norm(dv_ij_Hubble); +#endif + + /* Get spec. energy difference, but limit reconstructed values */ + const hydro_real_t du_raw = pi->u - pj->u; + const hydro_real_t du_reconstructed = ui_reconstructed - uj_reconstructed; + const hydro_real_t du_ij = + hydro_scalar_minmod_limiter(du_reconstructed, du_raw, + pi->gradients.du_min, pi->gradients.du_max, + pj->gradients.du_min, pj->gradients.du_max); + + const hydro_real_t alpha_cond = const_conductivity_alpha; + const hydro_real_t delta_P = fabs(pressurei - pressurej); + const hydro_real_t P_lim = delta_P / (pressurei + pressurej); + + /* Add conductivity to the specific energy */ + cond_du_term = alpha_cond * P_lim * v_sig_speed * du_ij * rho_ij_inv; + } + else { + mu_ij = 0.; + } + + /* Finalize the viscosity and conductivity with correct normalizations. */ + + + /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ + const hydro_real_t dv_dot_G_ij = hydro_vec3_vec3_dot(dv_raw, G_ij); + +#ifdef hydro_props_use_radial_artificial_terms + /* Get Hubble flow along dx */ + const hydro_real_t dv_Hubble_along_dx_ij[3] = { + dv_Hubble_dot_dx_ij_hat * dx_ij_hat[0], + dv_Hubble_dot_dx_ij_hat * dx_ij_hat[1], + dv_Hubble_dot_dx_ij_hat * dx_ij_hat[2] + }; + + /* Get Hubble flow contribution */ + const hydro_real_t dv_Hubble_dot_G_rad_ij = + hydro_vec3_vec3_dot(dv_Hubble_along_dx_ij, G_rad_ij); +#else + const hydro_real_t dv_Hubble_dot_G_rad_ij = + hydro_vec3_vec3_dot(dv_Hubble, G_rad_ij); +#endif + + sph_du_term_i *= dv_dot_G_ij; + cond_du_term *= -G_rad_ij_norm; /* Eq. 24 Rosswog 2020 */ + visc_du_term *= dv_Hubble_dot_G_rad_ij; + + if (visc_du_term <= 0.) { + visc_acc_term = 0.; + visc_du_term = 0.; + } + + + /* Get the time derivative for h. */ + + + const hydro_real_t dv_dot_dx_ij = hydro_vec3_vec3_dot(dv_raw, dx_ij); + pi->force.h_dt -= hydro_get_h_dt_sum(dv_dot_dx_ij, dv_dot_G_ij, + mj, rhoj_inv, r_inv, wi_dr); + + + /* Timestepping */ + + + /* Compute based on raw velocities */ + const hydro_real_t v_sig_visc = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); + + const hydro_real_t h_ij = 0.5 * (hi + hj); + pi->h_min = fmin(pi->h_min, h_ij); + + const hydro_real_t dt_min_i = h_ij / v_sig_visc; + pi->dt_min = fmin(pi->dt_min, dt_min_i); + +#ifdef MAGMA2_DEBUG_CHECKS + pi->debug.N_force_high_order_grad++; +#endif + } + else { /* Gasoline2 SPH fallback*/ + + + /* Compute dv dot dr. */ + const hydro_real_t dvdr = hydro_vec3_vec3_dot(dv_raw, G_ij); + + /* Includes the hubble flow term; not used for du/dt */ + const hydro_real_t dvdr_Hubble = dvdr + a2_Hubble * r2; + + /* Are the particles moving towards each others ? */ + const hydro_real_t omega_ij = fmin(dvdr_Hubble, 0.); + + hydro_real_t mu_full_ij = fac_mu * r_inv * dvdr_Hubble; + const hydro_real_t mu_ij = fac_mu * r_inv * omega_ij; + + /* Construct the full viscosity term */ + const hydro_real_t b_ij = + 0.5 * (pi->gradients.balsara + pj->gradients.balsara); + const hydro_real_t rho_ij = rhoi + rhoj; + const hydro_real_t cs_ij = pi->force.soundspeed + pj->force.soundspeed; + const hydro_real_t c_ij = 0.5 * cs_ij; + const hydro_real_t alpha = const_viscosity_alpha; + const hydro_real_t visc = + omega_ij < 0. + ? (-0.25 * alpha * cs_ij * + mu_ij + + const_viscosity_beta * mu_ij * mu_ij) * b_ij / + (0.5 * rho_ij) + : 0.; + + visc_acc_term = const_fallback_reduction_factor * visc; + visc_du_term = 0.5 * visc_acc_term; + + const hydro_real_t v_sig_alpha = c_ij * (1. + 0.75 * const_viscosity_alpha); + const hydro_real_t v_sig_beta = 0.75 * const_viscosity_beta * mu_full_ij; + + if (v_sig_alpha > v_sig_beta) { + const hydro_real_t rho_ij_inv = 2. / rho_ij; + const hydro_real_t du = pi->u - pj->u; + + const hydro_real_t alpha_cond = const_conductivity_alpha; + const hydro_real_t delta_P = fabs(pressurei - pressurej); + const hydro_real_t P_lim = delta_P / (pressurei + pressurej); + + cond_du_term = alpha_cond * P_lim * fabs(mu_full_ij) * du * rho_ij_inv; + cond_du_term *= const_fallback_reduction_factor; + } + + /* New signal velocity */ + const hydro_real_t new_v_sig_visc = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); + + const hydro_real_t h_ij = 0.5 * (hi + hj); + pi->h_min = fmin(pi->h_min, h_ij); + + /* New time-step estimate */ + const hydro_real_t dt_min_i = h_ij / new_v_sig_visc; + pi->dt_min = fmin(pi->dt_min, dt_min_i); + + /* Variable smoothing length term */ + const hydro_real_t kernel_gradient = 0.5 * (wi_dr + wj_dr) * r_inv; + + visc_acc_term *= kernel_gradient; + sph_acc_term *= kernel_gradient; + sph_du_term_i *= dvdr * kernel_gradient; + visc_du_term *= dvdr_Hubble * kernel_gradient; + cond_du_term *= kernel_gradient; + + /* Get the time derivative for h. */ + pi->force.h_dt -= hydro_get_h_dt_sum(dvdr, 0., mj, rhoj_inv, r_inv, wi_dr); + +#ifdef MAGMA2_DEBUG_CHECKS + pi->debug.N_force_low_order_grad++; +#endif + } + + /* Assemble the acceleration */ + const hydro_real_t acc[3] = { + sph_acc_term * G_ij[0] + visc_acc_term * G_rad_ij[0], + sph_acc_term * G_ij[1] + visc_acc_term * G_rad_ij[1], + sph_acc_term * G_ij[2] + visc_acc_term * G_rad_ij[2] + }; + + /* Use the force Luke ! */ + pi->a_hydro[0] -= mj * acc[0]; + pi->a_hydro[1] -= mj * acc[1]; + pi->a_hydro[2] -= mj * acc[2]; + + /* Assemble the energy equation term */ + const hydro_real_t du_dt_i = sph_du_term_i + visc_du_term + cond_du_term; + + /* Internal energy time derivative */ + pi->u_dt += du_dt_i * mj; + + pi->u_dt_cond += cond_du_term * mj; +} + +#endif /* SWIFT_MAGMA2_HYDRO_IACT_H */ diff --git a/src/hydro/MAGMA2/hydro_io.h b/src/hydro/MAGMA2/hydro_io.h new file mode 100644 index 0000000000..1edd53e043 --- /dev/null +++ b/src/hydro/MAGMA2/hydro_io.h @@ -0,0 +1,380 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2025 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_MAGMA2_HYDRO_IO_H +#define SWIFT_MAGMA2_HYDRO_IO_H + +/** + * @file MAGMA2/hydro_io.h + * @brief Density-Energy non-conservative implementation of SPH, + * with added MAGMA2 physics (Rosswog 2020) (i/o routines) + */ + +#include "adiabatic_index.h" +#include "hydro.h" +#include "hydro_parameters.h" +#include "io_properties.h" +#include "kernel_hydro.h" + +/** + * @brief Specifies which particle fields to read from a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to read. + * @param num_fields The number of i/o fields to read. + */ +INLINE static void hydro_read_particles(struct part* parts, + struct io_props* list, + int* num_fields) { + *num_fields = 8; + + /* List what we want to read */ + list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, + UNIT_CONV_LENGTH, parts, x); + list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, + UNIT_CONV_SPEED, parts, v); + list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + parts, mass); + list[3] = io_make_input_field("SmoothingLength", FLOAT, 1, COMPULSORY, + UNIT_CONV_LENGTH, parts, h); + list[4] = io_make_input_field("InternalEnergy", FLOAT, 1, COMPULSORY, + UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); + list[5] = io_make_input_field("ParticleIDs", ULONGLONG, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, parts, id); + list[6] = io_make_input_field("Accelerations", FLOAT, 3, OPTIONAL, + UNIT_CONV_ACCELERATION, parts, a_hydro); + list[7] = io_make_input_field("Density", FLOAT, 1, OPTIONAL, + UNIT_CONV_DENSITY, parts, rho); +} + +INLINE static void convert_S(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + ret[0] = hydro_get_comoving_entropy(p, xp); +} + +INLINE static void convert_P(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + ret[0] = hydro_get_comoving_pressure(p); +} + +INLINE static void convert_div_v(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + ret[0] = p->gradients.velocity_tensor[0][0] + + p->gradients.velocity_tensor[1][1] + + p->gradients.velocity_tensor[2][2]; +} + +INLINE static void convert_part_pos(const struct engine* e, + const struct part* p, + const struct xpart* xp, double* ret) { + const struct space* s = e->s; + if (s->periodic) { + ret[0] = box_wrap(p->x[0], 0.0, s->dim[0]); + ret[1] = box_wrap(p->x[1], 0.0, s->dim[1]); + ret[2] = box_wrap(p->x[2], 0.0, s->dim[2]); + } else { + ret[0] = p->x[0]; + ret[1] = p->x[1]; + ret[2] = p->x[2]; + } + if (e->snapshot_use_delta_from_edge) { + ret[0] = min(ret[0], s->dim[0] - e->snapshot_delta_from_edge); + ret[1] = min(ret[1], s->dim[1] - e->snapshot_delta_from_edge); + ret[2] = min(ret[2], s->dim[2] - e->snapshot_delta_from_edge); + } +} + +INLINE static void convert_part_vel(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + const float dt_kick_grav_mesh = e->dt_kick_grav_mesh_for_io; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav, dt_kick_hydro; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_hydro -= + cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time (hydro term)*/ + ret[0] = xp->v_full[0] + p->a_hydro[0] * dt_kick_hydro; + ret[1] = xp->v_full[1] + p->a_hydro[1] * dt_kick_hydro; + ret[2] = xp->v_full[2] + p->a_hydro[2] * dt_kick_hydro; + + /* Add the gravity term */ + if (p->gpart != NULL) { + ret[0] += p->gpart->a_grav[0] * dt_kick_grav; + ret[1] += p->gpart->a_grav[1] * dt_kick_grav; + ret[2] += p->gpart->a_grav[2] * dt_kick_grav; + } + + /* And the mesh gravity term */ + if (p->gpart != NULL) { + ret[0] += p->gpart->a_grav_mesh[0] * dt_kick_grav_mesh; + ret[1] += p->gpart->a_grav_mesh[1] * dt_kick_grav_mesh; + ret[2] += p->gpart->a_grav_mesh[2] * dt_kick_grav_mesh; + } + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a_inv; + ret[1] *= cosmo->a_inv; + ret[2] *= cosmo->a_inv; +} + +INLINE static void convert_part_potential(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + if (p->gpart != NULL) + ret[0] = gravity_get_comoving_potential(p->gpart); + else + ret[0] = 0.f; +} + +INLINE static void convert_part_softening(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + if (p->gpart != NULL) { + ret[0] = kernel_gravity_softening_plummer_equivalent_inv * + gravity_get_softening(p->gpart, e->gravity_properties); + } + else { + ret[0] = 0.f; + } +} + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * @param num_fields The number of i/o fields to write. + */ +INLINE static void hydro_write_particles(const struct part* parts, + const struct xpart* xparts, + struct io_props* list, + int* num_fields) { + *num_fields = 0; + int num = 0; + + /* List what we want to write */ + list[num] = io_make_output_field_convert_part( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_pos, "Co-moving positions of the particles"); + num++; + + list[num] = io_make_output_field_convert_part( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, parts, xparts, + convert_part_vel, + "Peculiar velocities of the stars. This is (a * dx/dt) where x is the " + "co-moving positions of the particles"); + num++; + + list[num] = io_make_output_field( + "Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, mass, + "Masses of the particles"); + num++; + + list[num] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + num++; + + list[num] = io_make_output_field( + "InternalEnergies", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, + -3.f * hydro_gamma_minus_one, parts, u, + "Co-moving thermal energies per unit mass of the particles"); + num++; + + list[num] = io_make_physical_output_field( + "ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, parts, id, + /*can convert to comoving=*/0, "Unique IDs of the particles"); + num++; + + list[num] = io_make_output_field( + "Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, parts, rho, + "Co-moving mass densities of the particles"); + num++; + + list[num] = io_make_output_field_convert_part( + "Entropies", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, 0.f, parts, + xparts, convert_S, "Co-moving entropies per unit mass of the particles"); + num++; + + list[num] = io_make_output_field_convert_part( + "Pressures", FLOAT, 1, UNIT_CONV_PRESSURE, -3.f * hydro_gamma, parts, + xparts, convert_P, "Co-moving pressures of the particles"); + num++; + + list[num] = io_make_output_field_convert_part( + "VelocityDivergences", FLOAT, 1, UNIT_CONV_FREQUENCY, 0.f, parts, xparts, + convert_div_v, + "Local velocity divergence field around the particles. Provided without " + "cosmology, as this includes the Hubble flow. To return to a peculiar " + "velocity divergence, div . v_pec = a^2 (div . v - n_D H)"); + num++; + + list[num] = io_make_output_field_convert_part( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, parts, xparts, + convert_part_potential, + "Co-moving gravitational potential at position of the particles"); + num++; + + list[num] = io_make_output_field_convert_part( + "Softenings", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, parts, xparts, + convert_part_softening, + "Co-moving gravitational Plummer-equivalent softenings of the particles"); + num++; + + /* + list[num] = io_make_output_field( + "NumberOfTimesDecoupled", INT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + feedback_data.number_of_times_decoupled, + "The integer number of times a particle was decoupled from " + "the hydro. Black hole wind events are encoded in thousands, " + "jet events in hundreds of thousands."); + num++; + + list[num] = io_make_output_field( + "DecouplingDelayTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, + feedback_data.decoupling_delay_time, + "Time remaining until the particle recouples to the hydro."); + num++; + + list[num] = io_make_output_field( + "CoolingShutOffTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, + feedback_data.cooling_shutoff_delay_time, + "Time remaining until cooling is allowed again."); + num++; + + list[num] = io_make_output_field( + "InternalEnergiesDt", FLOAT, 1, UNIT_CONV_U_DT, + -3.f * hydro_gamma_minus_one, parts, u_dt, + "Comoving rate of change of specific thermal energy (u_dt)."); + num++; + */ + +#ifdef MAGMA2_DEBUG_CHECKS + list[num] = io_make_output_field( + "NumberOfNeighbours", INT, 1, UNIT_CONV_NO_UNITS, + 0.f, parts, debug.num_ngb, + "Number of neighbours."); + num++; + + list[num] = io_make_output_field( + "LowOrderGradientsCount", INT, 1, UNIT_CONV_NO_UNITS, + 0.f, parts, debug.N_force_low_order_grad, + "Cumulative number of low order gradient force interactions."); + num++; + + list[num] = io_make_output_field( + "HighOrderGradientsCount", INT, 1, UNIT_CONV_NO_UNITS, + 0.f, parts, debug.N_force_high_order_grad, + "Cumulative number of high order gradient force interactions."); + num++; + + list[num] = io_make_output_field( + "CorrectionMatrices", FLOAT, 9, UNIT_CONV_LENGTH * UNIT_CONV_LENGTH, + 2.f, parts, debug.correction_matrix, + "Co-moving correction matrices for the particles."); + num++; + + list[num] = io_make_output_field( + "CorrectionIllConditionedCounts", INT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + debug.C_ill_conditioned_count, + "Count for how many times this particle had an ill-conditioned C matrix"); + num++; + + list[num] = io_make_output_field( + "VelocityGradientNumeratorMatrices", FLOAT, 9, + UNIT_CONV_DENSITY * UNIT_CONV_SPEED / UNIT_CONV_LENGTH, + -5.f, parts, debug.velocity_tensor_aux, + "Co-moving numerator matrices for the particles."); + num++; + + list[num] = io_make_output_field( + "VelocityGradientDenominatorMatrices", FLOAT, 9, UNIT_CONV_DENSITY, + -3.f, parts, debug.velocity_tensor_aux_norm, + "Co-moving denominator matrices for the particles."); + num++; + + list[num] = io_make_output_field( + "VelocityGradientIllConditionedCounts", INT, 1, UNIT_CONV_NO_UNITS, + 0.f, parts, debug.D_ill_conditioned_count, + "Count for how many times this particle had an ill-conditioned D matrix"); + num++; + + list[num] = io_make_output_field( + "SpecificEnergyGradientNumerators", FLOAT, 3, + UNIT_CONV_DENSITY * UNIT_CONV_ENERGY_PER_UNIT_MASS / UNIT_CONV_LENGTH, + -6.f, parts, debug.u_aux, + "Co-moving specific energy numerator matrices for the particles."); + num++; + + list[num] = io_make_output_field( + "SpecificEnergyGradientDenominators", FLOAT, 3, UNIT_CONV_DENSITY, + -3.f, parts, debug.u_aux, + "Co-moving specific energy gradient numerator for the particles."); + num++; + + list[num] = io_make_output_field( + "SpecificEnergyIllConditionedCounts", INT, 1, UNIT_CONV_NO_UNITS, + 0.f, parts, debug.u_ill_conditioned_count, + "Count for how many times this particle had an ill-conditioned u_aux_norm"); + num++; +#endif + + *num_fields = num; +} + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +INLINE static void hydro_write_flavour(hid_t h_grpsph) { + /* Viscosity and thermal conduction */ + /* Nothing in this minimal model... */ + io_write_attribute_s(h_grpsph, "Thermal Conductivity Model", + "Simple treatment as in Price (2008)"); + io_write_attribute_s(h_grpsph, "Viscosity Model", + "Gingold & Monaghan (1983)"); +} + +/** + * @brief Are we writing entropy in the internal energy field ? + * + * @return 1 if entropy is in 'internal energy', 0 otherwise. + */ +INLINE static int writeEntropyFlag(void) { return 0; } + +#endif /* SWIFT_MAGMA2_HYDRO_IO_H */ diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h new file mode 100644 index 0000000000..3e99e97207 --- /dev/null +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -0,0 +1,297 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + * 2025 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ + +#ifndef SWIFT_MAGMA2_HYDRO_PARAMETERS_H +#define SWIFT_MAGMA2_HYDRO_PARAMETERS_H + +/* Configuration file */ +#include + +/* Global headers */ +#if defined(HAVE_HDF5) +#include +#endif + +/* Local headers */ +#include "common_io.h" +#include "error.h" +#include "inline.h" + +/** + * @file MAGMA2/hydro_parameters.h + * @brief Density-Energy non-conservative implementation of SPH, + * with added MAGMA2 physics (Rosswog 2020) (default compile-time + * parameters). + * + * This file defines a number of things that are used in + * hydro_properties.c as defaults for run-time parameters + * as well as a number of compile-time parameters. + */ + + +/* ---------- Viscosity & Conductivitiy parameters ---------- */ + + +/*! Alpha viscosity, usually =1.0. For lower N_ngb, should be higher */ +#define const_viscosity_alpha 2.0 + +/*! Alpha conductivity, usually =0.05. At lower N_ngb, should be higher */ +#define const_conductivity_alpha 0.075 + +/*! Desired number of neighbours -- CRITICAL that this matches hydro props */ +#if defined(HYDRO_DIMENSION_1D) +#define const_kernel_target_neighbours 8.0 +#elif defined(HYDRO_DIMENSION_2D) +#define const_kernel_target_neighbours 34.0 +#else +#define const_kernel_target_neighbours 114.0 +#endif + + +/* ---------- These parameters should not be changed ---------- */ + +/*! Use a Swift-like estimator for dh/dt rather than the correct formula + * 0 = Simple mass flow estimator + * 1 = Correct formula based on number density constraint + * 2 = Using v_ij dot G_ij with simple mass flow estimator + */ +#define hydro_props_dh_dt_estimator_type 0 + +/*! Flag to use Balsara limiter */ +#define hydro_props_use_balsara_limiter + +/*! Flag to use additional slope limiting procedures */ +//#define hydro_props_use_extra_slope_limiter + +/* Flag to disallow sign flip in reconstructed quantities */ +//#define hydro_props_use_strict_minmod_limiter + +/* Slope limiter length, fraction of max. distance in kernel */ +#ifdef hydro_props_use_extra_slope_limiter +#define const_grad_overshoot_length 0.25 + +/*! Slope limiter tolerance */ +#define const_grad_overshoot_tolerance 0.0 +#endif + +/* Viscosity floor when G_ij is extremely misaligned with dx_ij */ +#define const_viscosity_cosine_limit 0.1736 + +/* Viscosity weighting scheme: + * 0 = (rho_i * q_i + rho_j * q_j) / (rho_i * rho_j) + * 1 = (rho_i * q_ij + rho_j * q_ij) / (rho_i * rho_j) + * 2 = 2.0 * q_ij / (rho_i + rho_j) */ +#define hydro_props_viscosity_weighting_type 2 + +/* Flag to use radial gradients for viscosity and conductivity */ +//#define hydro_props_use_radial_artificial_terms + +/*! Use the correction terms to make the internal energy match the mass flux */ +//#define hydro_props_use_adiabatic_correction + +/* Kernel gradient weighting scheme: + * 0 = 0.5 * (G_i + G_j) + * 1 = 0.5 * (f_i * G_i + f_j * G_j) + * 2 = 0.5 * f_ij * (G_i + G_j) + * with f_ij = 0.5 * (f_i + f_j) + * 3 = 0.5 * f_ij * (G_i + G_j) + * with f_ij = 2 * f_i * f_j / (f_i + f_j) + * 4 = 0.5 * f_ij * (G_i + G_j) + * with f_ij = sqrt(f_i * f_j) + * 5 = 0.5 * f_ij * (G_i + G_j) + * with f_ij = (f_i * rho_i + f_j * rho_j) / (rho_i + rho_j) + * 6 = 0.5 * f_ij * (G_i + G_j) + * with f_ij = 2 * f_i * f_j / (f_i * rho_i + f_j * rho_j) + * 7 = 0.5 * f_ij * (G_i + G_j) + * with f_ij = (f_i * P_i + f_j * P_j) / (P_i + P_j) + * 8 = 0.5 * f_ij * (G_i + G_j) + * with f_ij = 2 * f_i * f_j / (f_i * P_i + f_j * P_j) + * 9 = 0.5 * f_ij * (G_i + G_j) + * with f_ij = (f_i * V_i + f_j * V_j) / (V_i + V_j) + */ +#define hydro_props_kernel_gradient_weighting 0 + +/*! Use double precision for all matrix/vector operations */ +//#define hydro_props_use_double_precision + +#ifdef hydro_props_use_double_precision +/*! Consider matrix inversion to be ill-conditioned above this limit */ +#define const_condition_number_upper_limit 300. +/*! Mean interparticle spacing for this kernel and neighbour number */ +#define const_kernel_mean_spacing (kernel_gamma*pow(4. * M_PI / (3. * \ + (double)const_kernel_target_neighbours), 1. / 3.)) +#else +/*! Consider matrix inversion to be ill-conditioned above this limit */ +#define const_condition_number_upper_limit 60. +/*! Mean interparticle spacing for this kernel and neighbour number */ +#define const_kernel_mean_spacing (kernel_gamma*powf(4. * M_PI / (3. * \ + (float)const_kernel_target_neighbours), 1. / 3.)) +#endif + +/*! eta_crit Rosswog 2020 Eq 23. Of order the mean interparticle spacing. */ +#define const_slope_limiter_eta_crit (const_kernel_mean_spacing) + +/*! eta_fold from Frontiere+'17 Equation 51 */ +#define const_slope_limiter_eta_fold 0.2 + +/*! Softening squared (epsilon^2) in Eq. 15 Rosswog 2020 */ +#define const_viscosity_epsilon2 0.01 + +/*! Cosmology default const_viscosity_beta=2*const_viscosity_alpha + * Beta is defined as in e.g. Price (2010) Eqn (103) */ +#define const_viscosity_beta (2.0*const_viscosity_alpha) + +/*! Prefactor for alpha term in signal velocity */ +#define const_viscosity_alpha_prefactor (1.25 * (1. + \ + 0.75 * const_viscosity_alpha)) + +/*! Prefactor for beta term in signal velocity */ +#define const_viscosity_beta_prefactor (1.25 * 0.75 * const_viscosity_beta) + +/*! Fallback multiplier for alpha/beta terms to reduce spread */ +#define const_fallback_reduction_factor 0.25 + +/* ---------- Structures for below ---------- */ + + +/*! Artificial viscosity parameters */ +struct viscosity_global_data { }; + +/*! Thermal diffusion parameters */ +struct diffusion_global_data { }; + +/* Functions for reading from parameter file */ + +/* Forward declartions */ +struct swift_params; +struct phys_const; +struct unit_system; + +/* Define float or double depending on hydro_props_use_double_precision */ +#if defined(hydro_props_use_double_precision) +typedef double hydro_real_t; +#else +typedef float hydro_real_t; +#endif + +/* Viscosity */ + +/** + * @brief Initialises the viscosity parameters in the struct from + * the parameter file, or sets them to defaults. + * + * @param params: the pointer to the swift_params file + * @param unit_system: pointer to the unit system + * @param phys_const: pointer to the physical constants system + * @param viscosity: pointer to the viscosity_global_data struct to be filled. + **/ +static INLINE void viscosity_init(struct swift_params* params, + const struct unit_system* us, + const struct phys_const* phys_const, + struct viscosity_global_data* viscosity) { } + +/** + * @brief Initialises a viscosity struct to sensible numbers for mocking + * purposes. + * + * @param viscosity: pointer to the viscosity_global_data struct to be filled. + **/ +static INLINE void viscosity_init_no_hydro( + struct viscosity_global_data* viscosity) { } + +/** + * @brief Prints out the viscosity parameters at the start of a run. + * + * @param viscosity: pointer to the viscosity_global_data struct found in + * hydro_properties + **/ +static INLINE void viscosity_print( + const struct viscosity_global_data* viscosity) { + message("Artificial viscosity alpha set to %.3f", const_viscosity_alpha); + message("Artificial viscosity beta set to %.3f", const_viscosity_beta); +} + +#if defined(HAVE_HDF5) +/** + * @brief Prints the viscosity information to the snapshot when writing. + * + * @param h_grpsph: the SPH group in the ICs to write attributes to. + * @param viscosity: pointer to the viscosity_global_data struct. + **/ +static INLINE void viscosity_print_snapshot( + hid_t h_grpsph, const struct viscosity_global_data* viscosity) { + + io_write_attribute_f(h_grpsph, "Alpha viscosity", const_viscosity_alpha); + io_write_attribute_f(h_grpsph, "Beta viscosity", const_viscosity_beta); +} +#endif + +/* Diffusion */ + +/** + * @brief Initialises the diffusion parameters in the struct from + * the parameter file, or sets them to defaults. + * + * @param params: the pointer to the swift_params file + * @param unit_system: pointer to the unit system + * @param phys_const: pointer to the physical constants system + * @param diffusion_global_data: pointer to the diffusion struct to be filled. + **/ +static INLINE void diffusion_init(struct swift_params* params, + const struct unit_system* us, + const struct phys_const* phys_const, + struct diffusion_global_data* diffusion) { } + +/** + * @brief Initialises a diffusion struct to sensible numbers for mocking + * purposes. + * + * @param diffusion: pointer to the diffusion_global_data struct to be filled. + **/ +static INLINE void diffusion_init_no_hydro( + struct diffusion_global_data* diffusion) { } + +/** + * @brief Prints out the diffusion parameters at the start of a run. + * + * @param diffusion: pointer to the diffusion_global_data struct found in + * hydro_properties + **/ +static INLINE void diffusion_print( + const struct diffusion_global_data* diffusion) { + message("Artificial conductivity alpha set to %.3f", + const_conductivity_alpha); +} + +#ifdef HAVE_HDF5 +/** + * @brief Prints the diffusion information to the snapshot when writing. + * + * @param h_grpsph: the SPH group in the ICs to write attributes to. + * @param diffusion: pointer to the diffusion_global_data struct. + **/ +static INLINE void diffusion_print_snapshot( + hid_t h_grpsph, const struct diffusion_global_data* diffusion) { + io_write_attribute_f(h_grpsph, "Conductivity alpha", + const_conductivity_alpha); +} +#endif + +#endif /* SWIFT_MAGMA2_HYDRO_PARAMETERS_H */ diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h new file mode 100644 index 0000000000..caef3b4ffc --- /dev/null +++ b/src/hydro/MAGMA2/hydro_part.h @@ -0,0 +1,366 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & + * Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2025 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_MAGMA2_HYDRO_PART_H +#define SWIFT_MAGMA2_HYDRO_PART_H + +/** + * @file MAGMA2/hydro_part.h + * @brief Density-Energy non-conservative implementation of SPH, + * with added MAGMA2 physics (Rosswog 2020) (particle definition) + */ + +#include "adaptive_softening_struct.h" +#include "black_holes_struct.h" +#include "chemistry_struct.h" +#include "cooling_struct.h" +#include "csds.h" +#include "feedback_struct.h" +#ifdef WITH_FOF_GALAXIES +#include "fof_struct.h" +#endif +#include "hydro_parameters.h" +#include "mhd_struct.h" +#include "particle_splitting_struct.h" +#include "pressure_floor_struct.h" +#include "rt_struct.h" +#include "sink_struct.h" +#include "star_formation_struct.h" +#include "timestep_limiter_struct.h" +#include "tracers_struct.h" + +/** + * @brief Particle fields not needed during the SPH loops over neighbours. + * + * This structure contains the particle fields that are not used in the + * density or force loops. Quantities should be used in the kick, drift and + * potentially ghost tasks only. + */ +struct xpart { + /*! Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /*! Offset between the current position and position at the last sort. */ + float x_diff_sort[3]; + + /*! Velocity at the last full step. */ + float v_full[3]; + + /*! Gravitational acceleration at the end of the last step */ + float a_grav[3]; + + /*! Internal energy at the last full step. */ + float u_full; + + /*! Additional data used to record particle splits */ + struct particle_splitting_data split_data; + + /*! Additional data used to record cooling information */ + struct cooling_xpart_data cooling_data; + + /* Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + + /* Additional data used by the tracers */ + struct star_formation_xpart_data sf_data; + + /* Additional data used by the feedback */ + struct feedback_xpart_data feedback_data; + + /*! Additional data used by the MHD scheme */ + struct mhd_xpart_data mhd_data; + +#ifdef WITH_CSDS + /* Additional data for the particle csds */ + struct csds_part_data csds_data; +#endif + +} SWIFT_STRUCT_ALIGN; + +/** + * @brief Particle fields for the SPH particles + * + * The density and force substructures are used to contain variables only used + * within the density and force loops over neighbours. All more permanent + * variables should be declared in the main part of the part structure, + */ +struct part { + /*! Particle unique ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + + /*! Particle position. */ + double x[3]; + + /*! Particle predicted velocity. */ + float v[3]; + + /*! Particle acceleration. */ + float a_hydro[3]; + + /*! Particle mass. */ + float mass; + + /*! Particle smoothing length. */ + float h; + + /*! Particle internal energy. */ + float u; + + /*! Time derivative of the internal energy. */ + float u_dt; + + /*! Particle density. */ + float rho; + + /*! Particle density gradient */ + float rho_gradient[3]; + + /*! Minimum smoothing length in the kernel */ + float h_min; + + /*! Minimum time-step amongst neighbours */ + float dt_min; + + /*! Conduction du/dt */ + float u_dt_cond; + + #ifdef MAGMA2_DEBUG_CHECKS + struct { + /*! Correction matrix at the last time it was ill-conditioned */ + hydro_real_t correction_matrix[3][3]; + + /*! Velocity tensor at ill-condition time */ + hydro_real_t velocity_tensor_aux[3][3]; + + /*! Velocity tensor norm ill-conditioned */ + hydro_real_t velocity_tensor_aux_norm[3][3]; + + /*! u_aux tensor at ill-condition time */ + hydro_real_t u_aux[3]; + + /*! u_aux_norm tensor ill-conditioned */ + hydro_real_t u_aux_norm[3]; + + /*! Number of times correction_matrix was ill-conditioned */ + int C_ill_conditioned_count; + + /*! Number of times velocity_tensor_aux_norm was ill-conditioned */ + int D_ill_conditioned_count; + + /*! Number of times u_aux_norm was ill-conditioned */ + int u_ill_conditioned_count; + + /*! How many low-order SPH gradients in force interactions */ + int N_force_low_order_grad; + + /*! How many high-order SPH gradients in force interactions */ + int N_force_high_order_grad; + + /*! Number of neighbors in the kernel */ + int num_ngb; + + } debug; +#endif + + /* Store gradients in a separate struct */ + struct { +#ifdef hydro_props_use_adiabatic_correction + /*! Adiabatic kernel correction factor numerator */ + hydro_real_t adiabatic_f_numerator; + + /*! Adiabatic kernel correction factor denominator */ + hydro_real_t adiabatic_f_denominator; +#endif + + /*! Sum of the kernel weights */ + hydro_real_t wcount; + + /*! Correction matrix (C^ki in Rosswog 2020) */ + hydro_real_t correction_matrix[3][3]; + + /*! Flag for whether C is ill-conditioned */ + char C_well_conditioned; + + /*! Full velocity gradient tensor */ + hydro_real_t velocity_tensor[3][3]; + + /*! Auxiliary full velocity gradient tensor */ + hydro_real_t velocity_tensor_aux[3][3]; + + /*! Flag for whether D (velocity_tensor_aux) is ill-conditioned */ + char D_well_conditioned; + + /*! Normalization for computing velocity_tensor_aux */ + hydro_real_t velocity_tensor_aux_norm[3][3]; + + /*! Hessian tensor */ + hydro_real_t velocity_hessian[3][3][3]; + + /*! Internal energy gradient */ + hydro_real_t u[3]; + + /*! Auxiliary internal energy gradient */ + hydro_real_t u_aux[3]; + + /*! Normalization for computing u_aux */ + hydro_real_t u_aux_norm[3]; + + /*! Flag for whether u_aux_norm is ill-conditioned */ + char u_well_conditioned; + + /*! Internal energy Hessian */ + hydro_real_t u_hessian[3][3]; + + /*! Minimum delta u across kernel */ + hydro_real_t du_min; + + /*! Maximum delta u across kernel */ + hydro_real_t du_max; + + /*! Minimum dv across kernel */ + hydro_real_t dv_min[3]; + + /*! Maximum dv across kernel */ + hydro_real_t dv_max[3]; + + /*! Kernel size for slope limiting */ + hydro_real_t kernel_size; + + /*! Balsara limiter for divergences */ + hydro_real_t balsara; + + } gradients; + + /* Store density/force specific stuff. */ + union { + /** + * @brief Structure for the variables only used in the density loop over + * neighbours. + * + * Quantities in this sub-structure should only be accessed in the density + * loop over neighbours and the ghost task. + */ + struct { + /*! Neighbour number count. */ + float wcount; + + /*! Derivative of the neighbour number with respect to h. */ + float wcount_dh; + + /*! Derivative of density with respect to h */ + float rho_dh; + + } density; + + /** + * @brief Structure for the variables only used in the force loop over + * neighbours. + * + * Quantities in this sub-structure should only be accessed in the force + * loop over neighbours and the ghost, drift and kick tasks. + */ + struct { + /*! "Grad h" term -- only partial in P-U */ + float f; + + /*! Particle pressure. */ + float pressure; + + /*! Particle soundspeed. */ + float soundspeed; + + /*! Time derivative of smoothing length */ + float h_dt; + + } force; + }; + + /*! Flag for decoupling from the hydrodynamics/feedback routines */ + unsigned char decoupled; + + /*! Flag to indicate that the decoupling task will run */ + unsigned char to_be_decoupled; + + /*! Flag to indicate that the recoupling task will run */ + unsigned char to_be_recoupled; + + /*! Additional data used for adaptive softening */ + struct adaptive_softening_part_data adaptive_softening_data; + + /*! Additional data used by the MHD scheme */ + struct mhd_part_data mhd_data; + + /*! Chemistry information */ + struct chemistry_part_data chemistry_data; + + /*! Cooling information */ + struct cooling_part_data cooling_data; + + /*! Additional data used by the feedback */ + struct feedback_part_data feedback_data; + +#ifdef WITH_FOF_GALAXIES + /*! Additional data used by the FoF */ + struct galaxy_data galaxy_data; +#endif + + /*! Black holes information (e.g. swallowing ID) */ + struct black_holes_part_data black_holes_data; + + /* Additional data used by the SF routines */ + struct star_formation_xpart_data sf_data; + + /*! Sink information (e.g. swallowing ID) */ + struct sink_part_data sink_data; + + /*! Additional data used by the pressure floor */ + struct pressure_floor_part_data pressure_floor_data; + + /*! Additional Radiative Transfer Data */ + struct rt_part_data rt_data; + + /*! RT sub-cycling time stepping data */ + struct rt_timestepping_data rt_time_data; + + /*! Time-step length */ + timebin_t time_bin; + + /*! Tree-depth at which size / 2 <= h * gamma < size */ + char depth_h; + + /*! Time-step limiter information */ + struct timestep_limiter_data limiter_data; + +#ifdef SWIFT_DEBUG_CHECKS + + /* Time of the last drift */ + integertime_t ti_drift; + + /* Time of the last kick */ + integertime_t ti_kick; + +#endif + +} SWIFT_STRUCT_ALIGN; + +#endif /* SWIFT_MAGMA2_HYDRO_PART_H */ diff --git a/src/hydro_csds.h b/src/hydro_csds.h index a0593ca7b3..088f9f6341 100644 --- a/src/hydro_csds.h +++ b/src/hydro_csds.h @@ -53,6 +53,8 @@ #include "./hydro/SPHENIX/hydro_csds.h" #elif defined(GASOLINE_SPH) #error TODO +#elif defined(MAGMA2_SPH) +#error TODO #elif defined(ANARCHY_PU_SPH) #error TODO #else diff --git a/src/hydro_io.h b/src/hydro_io.h index 5a64a284cc..5920dd0a9b 100644 --- a/src/hydro_io.h +++ b/src/hydro_io.h @@ -49,6 +49,8 @@ #include "./hydro/SPHENIX/hydro_io.h" #elif defined(GASOLINE_SPH) #include "./hydro/Gasoline/hydro_io.h" +#elif defined(MAGMA2_SPH) +#include "./hydro/MAGMA2/hydro_io.h" #elif defined(ANARCHY_PU_SPH) #include "./hydro/AnarchyPU/hydro_io.h" #else diff --git a/src/hydro_parameters.h b/src/hydro_parameters.h index 46d93ad43a..a370c7df71 100644 --- a/src/hydro_parameters.h +++ b/src/hydro_parameters.h @@ -58,6 +58,8 @@ #include "./hydro/SPHENIX/hydro_parameters.h" #elif defined(GASOLINE_SPH) #include "./hydro/Gasoline/hydro_parameters.h" +#elif defined(MAGMA2_SPH) +#include "./hydro/MAGMA2/hydro_parameters.h" #elif defined(ANARCHY_PU_SPH) #include "./hydro/AnarchyPU/hydro_parameters.h" #else diff --git a/src/hydro_properties.c b/src/hydro_properties.c index f8b547bc56..7051ccea47 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -63,19 +63,54 @@ void hydro_props_init(struct hydro_props *p, /* ------ Smoothing lengths parameters ---------- */ /* Kernel properties */ - p->eta_neighbours = parser_get_param_float(params, "SPH:resolution_eta"); + p->eta_neighbours = + parser_get_opt_param_float(params, "SPH:resolution_eta", 0.f); + + /* Target number of neighbours (optional) */ + p->target_neighbours = + parser_get_opt_param_float(params, "SPH:target_neighbours", 0.f); + + if (p->eta_neighbours <= 0.f && p->target_neighbours <=0.f) { + error("You must set either SPH:resolution_eta or SPH:target_neighbours " + "in the parameter file."); + } /* Tolerance for the smoothing length Newton-Raphson scheme */ p->h_tolerance = parser_get_opt_param_float(params, "SPH:h_tolerance", hydro_props_default_h_tolerance); /* Get derived properties */ - p->target_neighbours = pow_dimension(p->eta_neighbours) * kernel_norm; + /* Target number of neighbours */ + if (p->eta_neighbours > 0.f) { + p->target_neighbours = pow_dimension(p->eta_neighbours) * kernel_norm; + } + else { + p->eta_neighbours = + powf(p->target_neighbours / kernel_norm, hydro_dimension_inv); + } + const float delta_eta = p->eta_neighbours * (1.f + p->h_tolerance); p->delta_neighbours = (pow_dimension(delta_eta) - pow_dimension(p->eta_neighbours)) * kernel_norm; +#ifdef MAGMA2_SPH +#ifndef const_kernel_target_neighbours + error("When using MAGMA2 SPH, the constant " + "const_kernel_target_neighbours must be defined in the header file " + "hydro_parameters.h. This is a compile-time constant and must be set " + "to the desired number of neighbours in the parameter file."); +#else + if (fabsf((float)const_kernel_target_neighbours - p->target_neighbours) > + 0.05f * p->target_neighbours) { + error("When using MAGMA2 SPH, the compiled constant " + "const_kernel_target_neighbours (%g) must be within 5 percent of " + "the desired number of neighbours (%g) in the parameter file.", + (float)const_kernel_target_neighbours, p->target_neighbours); + } +#endif +#endif + #ifdef SHADOWFAX_SPH /* change the meaning of target_neighbours and delta_neighbours */ p->target_neighbours = 1.0f; diff --git a/src/part.h b/src/part.h index 08fb3121f8..9a8d70e95d 100644 --- a/src/part.h +++ b/src/part.h @@ -93,6 +93,10 @@ struct threadpool; #include "./hydro/Gasoline/hydro_part.h" #define hydro_need_extra_init_loop 0 #define EXTRA_HYDRO_LOOP +#elif defined(MAGMA2_SPH) +#include "./hydro/MAGMA2/hydro_part.h" +#define hydro_need_extra_init_loop 0 +#define EXTRA_HYDRO_LOOP #elif defined(ANARCHY_PU_SPH) #include "./hydro/AnarchyPU/hydro_part.h" #define hydro_need_extra_init_loop 0 diff --git a/src/sink/GEAR/sink_getters.h b/src/sink/GEAR/sink_getters.h index bd50ec1f54..1d30fca0f5 100644 --- a/src/sink/GEAR/sink_getters.h +++ b/src/sink/GEAR/sink_getters.h @@ -106,6 +106,9 @@ INLINE static float sink_get_physical_div_v_from_part( div_v = (1. / 3.) * (p->viscosity.velocity_gradient[0][0] + p->viscosity.velocity_gradient[1][1] + p->viscosity.velocity_gradient[2][2]); +#elif MAGMA2_SPH + /* Copy the velocity divergence */ + div_v = hydro_get_physical_div_v(p, cosmo); #elif HOPKINS_PU_SPH div_v = p->density.div_v; #elif defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) diff --git a/src/star_formation/GEAR/star_formation.h b/src/star_formation/GEAR/star_formation.h index 160ee54d67..f8c9ee5ef6 100644 --- a/src/star_formation/GEAR/star_formation.h +++ b/src/star_formation/GEAR/star_formation.h @@ -448,6 +448,9 @@ __attribute__((always_inline)) INLINE static void star_formation_end_density( xp->sf_data.div_v = (1. / 3.) * (p->viscosity.velocity_gradient[0][0] + p->viscosity.velocity_gradient[1][1] + p->viscosity.velocity_gradient[2][2]); +#elif MAGMA2_SPH + /* Copy the velocity divergence */ + xp->sf_data.div_v = hydro_get_physical_div_v(p, cosmo); #elif HOPKINS_PU_SPH xp->sf_data.div_v = p->density.div_v; #elif defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) From 2f1e4defca029cfe2613a16fd48986f06a9b030d Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Sat, 8 Nov 2025 13:48:13 +0000 Subject: [PATCH 03/37] Apply formatting tool --- src/hydro/MAGMA2/hydro.h | 446 ++++++++++++---------------- src/hydro/MAGMA2/hydro_debug.h | 23 +- src/hydro/MAGMA2/hydro_iact.h | 387 ++++++++++-------------- src/hydro/MAGMA2/hydro_io.h | 118 ++++---- src/hydro/MAGMA2/hydro_parameters.h | 76 +++-- src/hydro/MAGMA2/hydro_part.h | 16 +- src/hydro_properties.c | 30 +- 7 files changed, 473 insertions(+), 623 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 592e06a6a3..a082bd3c9b 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -3,7 +3,7 @@ * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2025 Doug Rennehan (douglas.rennehan@gmail.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 @@ -41,10 +41,10 @@ #include "minmax.h" #include "pressure_floor.h" -#include +#include #include +#include #include -#include /** * @brief Returns the comoving internal energy of a particle at the last @@ -417,7 +417,7 @@ hydro_set_v_sig_based_on_velocity_kick(struct part *p, * @param alpha the new value for the viscosity coefficient. */ __attribute__((always_inline)) INLINE static void hydro_set_viscosity_alpha( - struct part *restrict p, float alpha) { } + struct part *restrict p, float alpha) {} /** * @brief Update the value of the diffusive coefficients to the @@ -447,7 +447,8 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const struct hydro_props *restrict hydro_properties, const struct cosmology *restrict cosmo) { - //if (p->dt_min == 0.f || p->decoupled || p->feedback_data.decoupling_delay_time > 0.f) return FLT_MAX; + // if (p->dt_min == 0.f || p->decoupled || + // p->feedback_data.decoupling_delay_time > 0.f) return FLT_MAX; if (p->dt_min == 0.f) return FLT_MAX; /* Use full kernel support and physical time */ @@ -503,7 +504,7 @@ __attribute__((always_inline)) INLINE static float hydro_get_div_v( * @brief p The particle */ __attribute__((always_inline)) INLINE static float hydro_get_physical_div_v( - const struct part *restrict p, const struct cosmology* cosmo) { + const struct part *restrict p, const struct cosmology *cosmo) { const float div_v = hydro_get_div_v(p); return div_v * cosmo->a2_inv + hydro_dimension * cosmo->H; @@ -586,8 +587,8 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( * * @param A The matrix to compute the condition number of. */ -__attribute__((always_inline)) INLINE static -double condition_number(gsl_matrix *A) { +__attribute__((always_inline)) INLINE static double condition_number( + gsl_matrix *A) { gsl_matrix *A_copy = gsl_matrix_alloc(3, 3); gsl_matrix_memcpy(A_copy, A); @@ -617,9 +618,8 @@ double condition_number(gsl_matrix *A) { * @param vec_b The second vector. * @param result The result of the dot product. */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_vec3_vec3_dot(const hydro_real_t *restrict vec_a, - const hydro_real_t *restrict vec_b) { +__attribute__((always_inline)) INLINE static hydro_real_t hydro_vec3_vec3_dot( + const hydro_real_t *restrict vec_a, const hydro_real_t *restrict vec_b) { return vec_a[0] * vec_b[0] + vec_a[1] * vec_b[1] + vec_a[2] * vec_b[2]; } @@ -631,8 +631,8 @@ hydro_real_t hydro_vec3_vec3_dot(const hydro_real_t *restrict vec_a, * @param vec The vector. * @param result The result of the norm. */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_vec3_norm(const hydro_real_t *restrict vec) { +__attribute__((always_inline)) INLINE static hydro_real_t hydro_vec3_norm( + const hydro_real_t *restrict vec) { return sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]); } @@ -644,9 +644,8 @@ hydro_real_t hydro_vec3_norm(const hydro_real_t *restrict vec) { * @param vec The vector. * @param result The unit vector of vec */ -__attribute__((always_inline)) INLINE static -void hydro_vec3_unit(const hydro_real_t *restrict vec, - hydro_real_t *restrict result) { +__attribute__((always_inline)) INLINE static void hydro_vec3_unit( + const hydro_real_t *restrict vec, hydro_real_t *restrict result) { result[0] = 0.; result[1] = 0.; @@ -668,18 +667,14 @@ void hydro_vec3_unit(const hydro_real_t *restrict vec, * @param vec The vector to contract with the matrix. * @param result The result of the contraction. */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_mat3x3_mat3x3_dot(const hydro_real_t (*restrict mat_a)[3], - const hydro_real_t (*restrict mat_b)[3]) { - - return mat_a[0][0] * mat_b[0][0] + - mat_a[0][1] * mat_b[0][1] + - mat_a[0][2] * mat_b[0][2] + - mat_a[1][0] * mat_b[1][0] + - mat_a[1][1] * mat_b[1][1] + - mat_a[1][2] * mat_b[1][2] + - mat_a[2][0] * mat_b[2][0] + - mat_a[2][1] * mat_b[2][1] + +__attribute__((always_inline)) INLINE static hydro_real_t +hydro_mat3x3_mat3x3_dot(const hydro_real_t (*restrict mat_a)[3], + const hydro_real_t (*restrict mat_b)[3]) { + + return mat_a[0][0] * mat_b[0][0] + mat_a[0][1] * mat_b[0][1] + + mat_a[0][2] * mat_b[0][2] + mat_a[1][0] * mat_b[1][0] + + mat_a[1][1] * mat_b[1][1] + mat_a[1][2] * mat_b[1][2] + + mat_a[2][0] * mat_b[2][0] + mat_a[2][1] * mat_b[2][1] + mat_a[2][2] * mat_b[2][2]; } @@ -693,8 +688,7 @@ hydro_real_t hydro_mat3x3_mat3x3_dot(const hydro_real_t (*restrict mat_a)[3], * @param result The result of the contraction. */ __attribute__((always_inline)) INLINE static void hydro_mat3x3_vec3_dot( - const hydro_real_t (*restrict mat)[3], - const hydro_real_t *restrict vec, + const hydro_real_t (*restrict mat)[3], const hydro_real_t *restrict vec, hydro_real_t *restrict result) { result[0] = mat[0][0] * vec[0] + mat[0][1] * vec[1] + mat[0][2] * vec[2]; @@ -711,40 +705,27 @@ __attribute__((always_inline)) INLINE static void hydro_mat3x3_vec3_dot( * @param mat The matrix to contract with the tensor. * @param result The result of the contraction. */ -__attribute__((always_inline)) INLINE static -void hydro_tensor3x3x3_matrix3x3_dot( - const hydro_real_t (*restrict tensor)[3][3], - const hydro_real_t (*restrict mat)[3], - hydro_real_t *restrict result) { - - result[0] = tensor[0][0][0] * mat[0][0] + - tensor[0][0][1] * mat[0][1] + - tensor[0][0][2] * mat[0][2] + - tensor[0][1][0] * mat[1][0] + - tensor[0][1][1] * mat[1][1] + - tensor[0][1][2] * mat[1][2] + - tensor[0][2][0] * mat[2][0] + - tensor[0][2][1] * mat[2][1] + +__attribute__((always_inline)) INLINE static void +hydro_tensor3x3x3_matrix3x3_dot(const hydro_real_t (*restrict tensor)[3][3], + const hydro_real_t (*restrict mat)[3], + hydro_real_t *restrict result) { + + result[0] = tensor[0][0][0] * mat[0][0] + tensor[0][0][1] * mat[0][1] + + tensor[0][0][2] * mat[0][2] + tensor[0][1][0] * mat[1][0] + + tensor[0][1][1] * mat[1][1] + tensor[0][1][2] * mat[1][2] + + tensor[0][2][0] * mat[2][0] + tensor[0][2][1] * mat[2][1] + tensor[0][2][2] * mat[2][2]; - - result[1] = tensor[1][0][0] * mat[0][0] + - tensor[1][0][1] * mat[0][1] + - tensor[1][0][2] * mat[0][2] + - tensor[1][1][0] * mat[1][0] + - tensor[1][1][1] * mat[1][1] + - tensor[1][1][2] * mat[1][2] + - tensor[1][2][0] * mat[2][0] + - tensor[1][2][1] * mat[2][1] + + + result[1] = tensor[1][0][0] * mat[0][0] + tensor[1][0][1] * mat[0][1] + + tensor[1][0][2] * mat[0][2] + tensor[1][1][0] * mat[1][0] + + tensor[1][1][1] * mat[1][1] + tensor[1][1][2] * mat[1][2] + + tensor[1][2][0] * mat[2][0] + tensor[1][2][1] * mat[2][1] + tensor[1][2][2] * mat[2][2]; - result[2] = tensor[2][0][0] * mat[0][0] + - tensor[2][0][1] * mat[0][1] + - tensor[2][0][2] * mat[0][2] + - tensor[2][1][0] * mat[1][0] + - tensor[2][1][1] * mat[1][1] + - tensor[2][1][2] * mat[1][2] + - tensor[2][2][0] * mat[2][0] + - tensor[2][2][1] * mat[2][1] + + result[2] = tensor[2][0][0] * mat[0][0] + tensor[2][0][1] * mat[0][1] + + tensor[2][0][2] * mat[0][2] + tensor[2][1][0] * mat[1][0] + + tensor[2][1][1] * mat[1][1] + tensor[2][1][2] * mat[1][2] + + tensor[2][2][0] * mat[2][0] + tensor[2][2][1] * mat[2][1] + tensor[2][2][2] * mat[2][2]; } @@ -757,8 +738,7 @@ void hydro_tensor3x3x3_matrix3x3_dot( * @param result The result of the outer product. */ __attribute__((always_inline)) INLINE static void hydro_vec3_vec3_outer( - const hydro_real_t *restrict vec_a, - const hydro_real_t *restrict vec_b, + const hydro_real_t *restrict vec_a, const hydro_real_t *restrict vec_b, hydro_real_t (*restrict result)[3]) { result[0][0] = vec_a[0] * vec_b[0]; @@ -781,9 +761,8 @@ __attribute__((always_inline)) INLINE static void hydro_vec3_vec3_outer( * @param df_raw Raw value * @param df_reconstructed Reconstructed value */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_scalar_minmod(const hydro_real_t df_raw, - const hydro_real_t df_reconstructed) { +__attribute__((always_inline)) INLINE static hydro_real_t hydro_scalar_minmod( + const hydro_real_t df_raw, const hydro_real_t df_reconstructed) { if (df_raw * df_reconstructed <= 0.) return 0.; @@ -801,13 +780,13 @@ hydro_real_t hydro_scalar_minmod(const hydro_real_t df_raw, * @param df_min_j Minimum value of df_raw across the kernel. * @param df_max_j Maximum value of df_raw across the kernel */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_scalar_minmod_limiter(const hydro_real_t df_reconstructed, - const hydro_real_t df_raw, - const hydro_real_t df_min_i, - const hydro_real_t df_max_i, - const hydro_real_t df_min_j, - const hydro_real_t df_max_j) { +__attribute__((always_inline)) INLINE static hydro_real_t +hydro_scalar_minmod_limiter(const hydro_real_t df_reconstructed, + const hydro_real_t df_raw, + const hydro_real_t df_min_i, + const hydro_real_t df_max_i, + const hydro_real_t df_min_j, + const hydro_real_t df_max_j) { #ifdef hydro_props_use_strict_minmod_limiter hydro_real_t df = hydro_scalar_minmod(df_raw, df_reconstructed); @@ -838,24 +817,22 @@ hydro_real_t hydro_scalar_minmod_limiter(const hydro_real_t df_reconstructed, * @param dv_max_j Maximum value of df_raw across the kernel * @param dv_ij The vector to return. */ -__attribute__((always_inline)) INLINE static -void hydro_vec_minmod_limiter(const hydro_real_t *restrict dv_reconstructed, - const hydro_real_t *restrict dv_raw, - const hydro_real_t *restrict dv_min_i, - const hydro_real_t *restrict dv_max_i, - const hydro_real_t *restrict dv_min_j, - const hydro_real_t *restrict dv_max_j, - hydro_real_t *restrict dv_ij) { - - dv_ij[0] = hydro_scalar_minmod_limiter(dv_reconstructed[0], dv_raw[0], - dv_min_i[0], dv_max_i[0], - dv_min_j[0], dv_max_j[0]); - dv_ij[1] = hydro_scalar_minmod_limiter(dv_reconstructed[1], dv_raw[1], - dv_min_i[1], dv_max_i[1], - dv_min_j[1], dv_max_j[1]); - dv_ij[2] = hydro_scalar_minmod_limiter(dv_reconstructed[2], dv_raw[2], - dv_min_i[2], dv_max_i[2], - dv_min_j[2], dv_max_j[2]); +__attribute__((always_inline)) INLINE static void hydro_vec_minmod_limiter( + const hydro_real_t *restrict dv_reconstructed, + const hydro_real_t *restrict dv_raw, const hydro_real_t *restrict dv_min_i, + const hydro_real_t *restrict dv_max_i, + const hydro_real_t *restrict dv_min_j, + const hydro_real_t *restrict dv_max_j, hydro_real_t *restrict dv_ij) { + + dv_ij[0] = + hydro_scalar_minmod_limiter(dv_reconstructed[0], dv_raw[0], dv_min_i[0], + dv_max_i[0], dv_min_j[0], dv_max_j[0]); + dv_ij[1] = + hydro_scalar_minmod_limiter(dv_reconstructed[1], dv_raw[1], dv_min_i[1], + dv_max_i[1], dv_min_j[1], dv_max_j[1]); + dv_ij[2] = + hydro_scalar_minmod_limiter(dv_reconstructed[2], dv_raw[2], dv_min_i[2], + dv_max_i[2], dv_min_j[2], dv_max_j[2]); /* If any of the components are exactly zero, we return a zero vector */ if (dv_ij[0] == 0. || dv_ij[1] == 0. || dv_ij[2] == 0.) { @@ -874,11 +851,9 @@ void hydro_vec_minmod_limiter(const hydro_real_t *restrict dv_reconstructed, * @param kernel_size Interaction distance across the kernel. * @param grad The vector gradient to slope limit. */ -__attribute__((always_inline)) INLINE static -void hydro_vec_slope_limiter(const hydro_real_t df_min, - const hydro_real_t df_max, - const hydro_real_t kernel_size, - hydro_real_t *restrict grad) { +__attribute__((always_inline)) INLINE static void hydro_vec_slope_limiter( + const hydro_real_t df_min, const hydro_real_t df_max, + const hydro_real_t kernel_size, hydro_real_t *restrict grad) { #ifdef hydro_props_use_extra_slope_limiter const hydro_real_t grad_norm = hydro_vec3_norm(grad); @@ -891,19 +866,19 @@ void hydro_vec_slope_limiter(const hydro_real_t df_min, const hydro_real_t df_abs_min = fmin(df_min_abs, df_max_abs); hydro_real_t bound = df_abs_min; - + #ifdef const_grad_overshoot_tolerance const hydro_real_t tolerance = const_grad_overshoot_tolerance; if (tolerance > 0.) { const hydro_real_t df_abs_max = fmax(df_min_abs, df_max_abs); const hydro_real_t extra = tolerance * df_abs_max; - const hydro_real_t cap = + const hydro_real_t cap = (df_abs_min + extra < df_abs_max) ? df_abs_min + extra : df_abs_max; bound = cap; } #endif - const hydro_real_t limiter = + const hydro_real_t limiter = (bound > 0.) ? (bound / (length * grad_norm)) : 0.; if (limiter < 1.) { @@ -913,11 +888,10 @@ void hydro_vec_slope_limiter(const hydro_real_t df_min, } } #endif - } /** - * @brief Computes Phi_ab from Rosswog 2020 21 for a field given A. This is the + * @brief Computes Phi_ab from Rosswog 2020 21 for a field given A. This is the * van Leer 1974 slope limiting procedure. * * @@ -925,10 +899,9 @@ void hydro_vec_slope_limiter(const hydro_real_t df_min, * @param eta_i The normed smoothing length of the first particle. * @param eta_j The normed smoothing length of the second particle. */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_van_leer_phi(const hydro_real_t A_ij, - const hydro_real_t eta_i, - const hydro_real_t eta_j) { +__attribute__((always_inline)) INLINE static hydro_real_t hydro_van_leer_phi( + const hydro_real_t A_ij, const hydro_real_t eta_i, + const hydro_real_t eta_j) { hydro_real_t phi_raw = (4. * A_ij) / ((1. + A_ij) * (1. + A_ij)); phi_raw = fmin(1., phi_raw); @@ -939,7 +912,7 @@ hydro_real_t hydro_van_leer_phi(const hydro_real_t A_ij, hydro_real_t damping = 1.; if (eta_ij <= const_slope_limiter_eta_crit) { - const hydro_real_t diff = + const hydro_real_t diff = (eta_ij - const_slope_limiter_eta_crit) / const_slope_limiter_eta_fold; damping = exp(-diff * diff); } @@ -961,10 +934,10 @@ hydro_real_t hydro_van_leer_phi(const hydro_real_t A_ij, * @param grad_j The gradient of the quantity for the second particle. * @param dx The distance vector between the two particles ( ri - rj ). */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_scalar_van_leer_A(const hydro_real_t *restrict grad_i, - const hydro_real_t *restrict grad_j, - const hydro_real_t *restrict dx) { +__attribute__((always_inline)) INLINE static hydro_real_t +hydro_scalar_van_leer_A(const hydro_real_t *restrict grad_i, + const hydro_real_t *restrict grad_j, + const hydro_real_t *restrict dx) { const hydro_real_t grad_dot_x_i = hydro_vec3_vec3_dot(grad_i, dx); const hydro_real_t grad_dot_x_j = hydro_vec3_vec3_dot(grad_j, dx); @@ -988,10 +961,10 @@ hydro_real_t hydro_scalar_van_leer_A(const hydro_real_t *restrict grad_i, * @param grad_j The gradient tensor for the second particle. * @param dx The distance vector between the two particles ( ri - rj ). */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_vector_van_leer_A(const hydro_real_t (*restrict grad_i)[3], - const hydro_real_t (*restrict grad_j)[3], - const hydro_real_t *restrict dx) { +__attribute__((always_inline)) INLINE static hydro_real_t +hydro_vector_van_leer_A(const hydro_real_t (*restrict grad_i)[3], + const hydro_real_t (*restrict grad_j)[3], + const hydro_real_t *restrict dx) { hydro_real_t delta_ij[3][3] = {0}; hydro_vec3_vec3_outer(dx, dx, delta_ij); @@ -1011,7 +984,7 @@ hydro_real_t hydro_vector_van_leer_A(const hydro_real_t (*restrict grad_i)[3], } /** - * @brief Computes Phi_ab from Rosswog 2020 21 for a scalar field. This is the + * @brief Computes Phi_ab from Rosswog 2020 21 for a scalar field. This is the * van Leer 1974 slope limiting procedure. * * @@ -1021,12 +994,11 @@ hydro_real_t hydro_vector_van_leer_A(const hydro_real_t (*restrict grad_i)[3], * @param eta_i The normed smoothing length of the first particle. * @param eta_j The normed smoothing length of the second particle. */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_scalar_van_leer_phi(const hydro_real_t *restrict grad_i, - const hydro_real_t *restrict grad_j, - const hydro_real_t *restrict dx, - const hydro_real_t eta_i, - const hydro_real_t eta_j) { +__attribute__((always_inline)) INLINE static hydro_real_t +hydro_scalar_van_leer_phi(const hydro_real_t *restrict grad_i, + const hydro_real_t *restrict grad_j, + const hydro_real_t *restrict dx, + const hydro_real_t eta_i, const hydro_real_t eta_j) { const hydro_real_t A_ij = hydro_scalar_van_leer_A(grad_i, grad_j, dx); @@ -1034,7 +1006,7 @@ hydro_real_t hydro_scalar_van_leer_phi(const hydro_real_t *restrict grad_i, } /** - * @brief Computes Phi_ab from Rosswog 2020 21 for a vector field. This is the + * @brief Computes Phi_ab from Rosswog 2020 21 for a vector field. This is the * van Leer 1974 slope limiting procedure. * * @@ -1044,12 +1016,11 @@ hydro_real_t hydro_scalar_van_leer_phi(const hydro_real_t *restrict grad_i, * @param eta_i The normed smoothing length of the first particle. * @param eta_j The normed smoothing length of the second particle. */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_vector_van_leer_phi(const hydro_real_t (*restrict grad_i)[3], - const hydro_real_t (*restrict grad_j)[3], - const hydro_real_t *restrict dx, - const hydro_real_t eta_i, - const hydro_real_t eta_j) { +__attribute__((always_inline)) INLINE static hydro_real_t +hydro_vector_van_leer_phi(const hydro_real_t (*restrict grad_i)[3], + const hydro_real_t (*restrict grad_j)[3], + const hydro_real_t *restrict dx, + const hydro_real_t eta_i, const hydro_real_t eta_j) { const hydro_real_t A_ij = hydro_vector_van_leer_A(grad_i, grad_j, dx); @@ -1057,7 +1028,7 @@ hydro_real_t hydro_vector_van_leer_phi(const hydro_real_t (*restrict grad_i)[3], } /** - * @brief Reconstructs the scalar field at the mid-point between to the + * @brief Reconstructs the scalar field at the mid-point between to the * two particles to second order. * * @@ -1068,18 +1039,16 @@ hydro_real_t hydro_vector_van_leer_phi(const hydro_real_t (*restrict grad_i)[3], * @param hess The Hessian of the field at the particle. * @param f_reconstructed The reconstructed field at the mid-point (2nd order). */ -__attribute__((always_inline)) INLINE static void +__attribute__((always_inline)) INLINE static void hydro_scalar_second_order_reconstruction(const hydro_real_t phi, const hydro_real_t *restrict dx, - const hydro_real_t f, + const hydro_real_t f, const hydro_real_t *restrict grad, const hydro_real_t (*restrict hess)[3], hydro_real_t *f_reconstructed) { /* Midpoint from Equation 17 Rosswog 2020 */ - const hydro_real_t midpoint[3] = {0.5 * dx[0], - 0.5 * dx[1], - 0.5 * dx[2]}; + const hydro_real_t midpoint[3] = {0.5 * dx[0], 0.5 * dx[1], 0.5 * dx[2]}; hydro_real_t delta_ij[3][3] = {0}; hydro_vec3_vec3_outer(midpoint, midpoint, delta_ij); @@ -1091,7 +1060,7 @@ hydro_scalar_second_order_reconstruction(const hydro_real_t phi, } /** - * @brief Reconstructs the vector field at the mid-point between to the + * @brief Reconstructs the vector field at the mid-point between to the * two particles to second order. * * @@ -1100,20 +1069,18 @@ hydro_scalar_second_order_reconstruction(const hydro_real_t phi, * @param f The vector field at the particle. * @param grad The gradient of the vector field at the particle. * @param hess The Hessian of the vector field at the particle. - * @param f_reconstructed The reconstructed vector field at the mid-point (2nd order). + * @param f_reconstructed The reconstructed vector field at the mid-point (2nd + * order). */ -__attribute__((always_inline)) INLINE static void -hydro_vector_second_order_reconstruction(const hydro_real_t phi, - const hydro_real_t *restrict dx, - const hydro_real_t *restrict f, - const hydro_real_t (*restrict grad)[3], - const hydro_real_t (*restrict hess)[3][3], - hydro_real_t *restrict f_reconstructed) { +__attribute__((always_inline)) INLINE static void +hydro_vector_second_order_reconstruction( + const hydro_real_t phi, const hydro_real_t *restrict dx, + const hydro_real_t *restrict f, const hydro_real_t (*restrict grad)[3], + const hydro_real_t (*restrict hess)[3][3], + hydro_real_t *restrict f_reconstructed) { /* Midpoint from Equation 17 Rosswog 2020 */ - const hydro_real_t midpoint[3] = {0.5 * dx[0], - 0.5 * dx[1], - 0.5 * dx[2]}; + const hydro_real_t midpoint[3] = {0.5 * dx[0], 0.5 * dx[1], 0.5 * dx[2]}; hydro_real_t delta_ij[3][3] = {0}; hydro_vec3_vec3_outer(midpoint, midpoint, delta_ij); @@ -1136,10 +1103,10 @@ hydro_vector_second_order_reconstruction(const hydro_real_t phi, * @param p Particle p * @param cosmo The cosmology structure */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_get_balsara_limiter(const struct part *restrict p, - const struct cosmology* cosmo) { - +__attribute__((always_inline)) INLINE static hydro_real_t +hydro_get_balsara_limiter(const struct part *restrict p, + const struct cosmology *cosmo) { + #ifdef hydro_props_use_balsara_limiter const hydro_real_t fac_B = cosmo->a_factor_Balsara_eps; hydro_real_t balsara = 1.; @@ -1151,13 +1118,13 @@ hydro_real_t hydro_get_balsara_limiter(const struct part *restrict p, const hydro_real_t curl_v[3] = { p->gradients.velocity_tensor[2][1] - p->gradients.velocity_tensor[1][2], p->gradients.velocity_tensor[0][2] - p->gradients.velocity_tensor[2][0], - p->gradients.velocity_tensor[1][0] - p->gradients.velocity_tensor[0][1] - }; + p->gradients.velocity_tensor[1][0] - + p->gradients.velocity_tensor[0][1]}; - const hydro_real_t curl_v_norm_phys = + const hydro_real_t curl_v_norm_phys = hydro_vec3_norm(curl_v) * cosmo->a2_inv; const hydro_real_t Balsara_eps = 1.e-4 * fac_B * p->force.soundspeed / p->h; - balsara = + balsara = fabs(div_v_phys) / (fabs(div_v_phys) + curl_v_norm_phys + Balsara_eps); balsara = min(1., balsara); balsara = max(0., balsara); @@ -1179,12 +1146,12 @@ hydro_real_t hydro_get_balsara_limiter(const struct part *restrict p, * @param G_j Kernel gradient at pj * @param G_ij Kernel gradient average to fill */ -__attribute__((always_inline)) INLINE static -void hydro_get_average_kernel_gradient(const struct part *restrict pi, - const struct part *restrict pj, - const hydro_real_t *restrict G_i, - const hydro_real_t *restrict G_j, - hydro_real_t *restrict G_ij) { +__attribute__((always_inline)) INLINE static void +hydro_get_average_kernel_gradient(const struct part *restrict pi, + const struct part *restrict pj, + const hydro_real_t *restrict G_i, + const hydro_real_t *restrict G_j, + hydro_real_t *restrict G_ij) { #if (hydro_props_kernel_gradient_weighting == 0) G_ij[0] = 0.5 * (G_i[0] + G_j[0]); @@ -1240,7 +1207,7 @@ void hydro_get_average_kernel_gradient(const struct part *restrict pi, const hydro_real_t f_i = pi->force.f; const hydro_real_t f_j = pj->force.f; const hydro_real_t P_sum = pi->force.pressure + pj->force.pressure; - const hydro_real_t f_ij = + const hydro_real_t f_ij = (pi->force.pressure * f_i + pj->force.pressure * f_j) / P_sum; G_ij[0] = 0.5 * f_ij * (G_i[0] + G_j[0]); G_ij[1] = 0.5 * f_ij * (G_i[1] + G_j[1]); @@ -1248,7 +1215,7 @@ void hydro_get_average_kernel_gradient(const struct part *restrict pi, #elif (hydro_props_kernel_gradient_weighting == 8) const hydro_real_t f_i = pi->force.f; const hydro_real_t f_j = pj->force.f; - const hydro_real_t P_f_sum = + const hydro_real_t P_f_sum = pi->force.pressure * f_i + pj->force.pressure * f_j; const hydro_real_t f_ij = 2. * pi->force.pressure * f_i * pj->force.pressure * f_j / P_f_sum; @@ -1288,14 +1255,14 @@ void hydro_get_average_kernel_gradient(const struct part *restrict pi, * @param a2_Hubble Hubble flow * @param mu_i The velocity jump indicator at pi to fill * @param mu_j The velocity jump indicator at pj to fill - */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_get_visc_acc_term(const struct part *restrict pi, - const struct part *restrict pj, - const hydro_real_t *restrict dv, - const hydro_real_t *restrict dx, - const hydro_real_t fac_mu, - const hydro_real_t a2_Hubble) { + */ +__attribute__((always_inline)) INLINE static hydro_real_t +hydro_get_visc_acc_term(const struct part *restrict pi, + const struct part *restrict pj, + const hydro_real_t *restrict dv, + const hydro_real_t *restrict dx, + const hydro_real_t fac_mu, + const hydro_real_t a2_Hubble) { const hydro_real_t rhoi = pi->rho; const hydro_real_t rhoj = pj->rho; @@ -1325,14 +1292,14 @@ hydro_real_t hydro_get_visc_acc_term(const struct part *restrict pi, /* Each particle gets its own Q and then weighted by density */ const hydro_real_t rhoij_inv = 1.0 / (rhoi * rhoj); - const hydro_real_t q_i_alpha = + const hydro_real_t q_i_alpha = -const_viscosity_alpha * pi->force.soundspeed * mu_ij; - const hydro_real_t q_i_beta = const_viscosity_beta * mu_ij * mu_ij; + const hydro_real_t q_i_beta = const_viscosity_beta * mu_ij * mu_ij; const hydro_real_t Q_i = rhoi * (q_i_alpha + q_i_beta); - const hydro_real_t q_j_alpha = + const hydro_real_t q_j_alpha = -const_viscosity_alpha * pj->force.soundspeed * mu_ij; - const hydro_real_t q_j_beta = const_viscosity_beta * mu_ij * mu_ij; + const hydro_real_t q_j_beta = const_viscosity_beta * mu_ij * mu_ij; const hydro_real_t Q_j = rhoj * (q_j_alpha + q_j_beta); return (bi * Q_i + bj * Q_j) * rhoij_inv; @@ -1344,7 +1311,7 @@ hydro_real_t hydro_get_visc_acc_term(const struct part *restrict pi, const hydro_real_t rhoij_inv = 1. / (rhoi * rhoj); const hydro_real_t c_ij = 0.5 * (pi->force.soundspeed + pj->force.soundspeed); const hydro_real_t q_ij_alpha = -const_viscosity_alpha * c_ij * mu_ij; - const hydro_real_t q_ij_beta = const_viscosity_beta * mu_ij * mu_ij; + const hydro_real_t q_ij_beta = const_viscosity_beta * mu_ij * mu_ij; const hydro_real_t Q_ij = (rhoi + rhoj) * (q_ij_alpha + q_ij_beta); return b_ij * Q_ij * rhoij_inv; @@ -1355,21 +1322,23 @@ hydro_real_t hydro_get_visc_acc_term(const struct part *restrict pi, const hydro_real_t b_ij = 0.5 * (bi + bj); const hydro_real_t c_ij = 0.5 * (pi->force.soundspeed + pj->force.soundspeed); const hydro_real_t q_ij_alpha = -const_viscosity_alpha * c_ij * mu_ij; - const hydro_real_t q_ij_beta = const_viscosity_beta * mu_ij * mu_ij; + const hydro_real_t q_ij_beta = const_viscosity_beta * mu_ij * mu_ij; const hydro_real_t q_ij = 2. * (q_ij_alpha + q_ij_beta) / (rhoi + rhoj); return b_ij * q_ij; #endif #else - error("Unknown compiled hydro_props_viscosity_weighting_type value: %d\n" - "Valid values are 0, 1, or 2.\n", - (int)hydro_props_viscosity_weighting_type); + error( + "Unknown compiled hydro_props_viscosity_weighting_type value: %d\n" + "Valid values are 0, 1, or 2.\n", + (int)hydro_props_viscosity_weighting_type); #endif } /** - * @brief Gets the signal velocity for the conduction interaction based on mu_ij. + * @brief Gets the signal velocity for the conduction interaction based on + * mu_ij. * * * @param dx The separation vector @@ -1378,14 +1347,11 @@ hydro_real_t hydro_get_visc_acc_term(const struct part *restrict pi, * @param mu_i The velocity jump indicator at pi * @param mu_j The velocity jump indicator at pj */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_get_cond_signal_velocity(const float *restrict dx, - const struct part *restrict pi, - const struct part *restrict pj, - const float mu_i, - const float mu_j, - const float beta) { - +__attribute__((always_inline)) INLINE static hydro_real_t +hydro_get_cond_signal_velocity(const float *restrict dx, + const struct part *restrict pi, + const struct part *restrict pj, const float mu_i, + const float mu_j, const float beta) { const float mu_ij = 0.5f * (mu_i + mu_j); return hydro_signal_velocity(dx, pi, pj, mu_ij, beta); @@ -1401,21 +1367,17 @@ hydro_real_t hydro_get_cond_signal_velocity(const float *restrict dx, * @param mu_i The velocity jump indicator at pi * @param mu_j The velocity jump indicator at pj */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_get_visc_signal_velocity(const float *restrict dx, - const struct part *restrict pi, - const struct part *restrict pj, - const float mu_i, - const float mu_j, - const float beta) { +__attribute__((always_inline)) INLINE static hydro_real_t +hydro_get_visc_signal_velocity(const float *restrict dx, + const struct part *restrict pi, + const struct part *restrict pj, const float mu_i, + const float mu_j, const float beta) { #ifdef hydro_props_viscosity_weighting_type #if (hydro_props_viscosity_weighting_type == 0) const float dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; - const float v_sig_visc_i = - hydro_signal_velocity(dx, pi, pj, mu_i, beta); - const float v_sig_visc_j = - hydro_signal_velocity(dx_ji, pj, pi, mu_j, beta); + const float v_sig_visc_i = hydro_signal_velocity(dx, pi, pj, mu_i, beta); + const float v_sig_visc_j = hydro_signal_velocity(dx_ji, pj, pi, mu_j, beta); return 0.5 * (v_sig_visc_i + v_sig_visc_j); #else const float mu_ij = 0.5f * (mu_i + mu_j); @@ -1470,18 +1432,15 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->gradients.u_aux_norm[1] *= h_inv_dim_plus_one; p->gradients.u_aux_norm[2] *= h_inv_dim_plus_one; - if (p->gradients.u_aux_norm[0] != 0. && - p->gradients.u_aux_norm[1] != 0. && + if (p->gradients.u_aux_norm[0] != 0. && p->gradients.u_aux_norm[1] != 0. && p->gradients.u_aux_norm[2] != 0.) { p->gradients.u_aux[0] /= p->gradients.u_aux_norm[0]; p->gradients.u_aux[1] /= p->gradients.u_aux_norm[1]; p->gradients.u_aux[2] /= p->gradients.u_aux_norm[2]; hydro_vec_slope_limiter(p->gradients.du_min, p->gradients.du_max, - p->gradients.kernel_size, - p->gradients.u_aux); - } - else { + p->gradients.kernel_size, p->gradients.u_aux); + } else { p->gradients.u_well_conditioned = 0; #ifdef MAGMA2_DEBUG_CHECKS p->debug.u_aux[0] = p->gradients.u_aux[0]; @@ -1555,7 +1514,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( * i: velocity direction */ for (int k = 0; k < 3; k++) { - aux_matrix[j][i] += p->gradients.velocity_tensor_aux_norm[j][k] * + aux_matrix[j][i] += p->gradients.velocity_tensor_aux_norm[j][k] * p->gradients.velocity_tensor_aux[i][k]; } } @@ -1563,19 +1522,17 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* Copy over the matrices for use later */ - /* D. Rennehan: For some reason, memcpy does not work here? Could it + /* D. Rennehan: For some reason, memcpy does not work here? Could it * be because of the union in the particle struct? */ for (int j = 0; j < 3; j++) { hydro_vec_slope_limiter(p->gradients.dv_min[j], p->gradients.dv_max[j], - p->gradients.kernel_size, - aux_matrix[j]); + p->gradients.kernel_size, aux_matrix[j]); p->gradients.velocity_tensor_aux[j][0] = aux_matrix[j][0]; p->gradients.velocity_tensor_aux[j][1] = aux_matrix[j][1]; p->gradients.velocity_tensor_aux[j][2] = aux_matrix[j][2]; } - } - else { + } else { p->gradients.D_well_conditioned = 0; #ifdef MAGMA2_DEBUG_CHECKS @@ -1636,10 +1593,8 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( p->force.pressure = pressure_including_floor; p->force.soundspeed = soundspeed; - /* ------ Compute the kernel correction for SPH gradients ------ */ - /* Compute the "grad h" term */ const float common_factor = p->h * hydro_dimension_inv / p->density.wcount; float grad_W_term = -1.f; @@ -1661,12 +1616,11 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( "grad_W_term very small for particle with ID %lld (h: %g, wcount: " "%g, wcount_dh: %g)", p->id, p->h, p->density.wcount, p->density.wcount_dh); - } + } } /* Update variables. */ p->force.f = (grad_W_term > -1.f) ? 1.f / (1.f + grad_W_term) : 1.f; - } /** @@ -1704,7 +1658,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( for (int j = 0; j < 3; j++) { p->gradients.velocity_hessian[i][j][0] = 0.; p->gradients.velocity_hessian[i][j][1] = 0.; - p->gradients.velocity_hessian[i][j][2] = 0.; + p->gradients.velocity_hessian[i][j][2] = 0.; } } } @@ -1724,10 +1678,10 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( /* Calculate smoothing length powers */ const float h = p->h; - const float h_inv = 1.0f / h; /* 1/h */ - const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ - + const hydro_real_t rho_inv = 1. / hydro_get_comoving_density(p); p->rho_gradient[0] *= h_inv_dim_plus_one * rho_inv; p->rho_gradient[1] *= h_inv_dim_plus_one * rho_inv; @@ -1766,7 +1720,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( } /* Invert the p->gradients.correction_matrix[3][3] matrix */ - gsl_matrix_view C_view = + gsl_matrix_view C_view = gsl_matrix_view_array((double *)correction_matrix, 3, 3); gsl_matrix *C = &C_view.matrix; @@ -1787,15 +1741,14 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( gsl_matrix_free(C_inv); gsl_permutation_free(p_perm); - } - else { + } else { #ifdef MAGMA2_DEBUG_CHECKS for (int k = 0; k < 3; k++) { p->debug.correction_matrix[k][0] = p->gradients.correction_matrix[k][0]; p->debug.correction_matrix[k][1] = p->gradients.correction_matrix[k][1]; p->debug.correction_matrix[k][2] = p->gradients.correction_matrix[k][2]; } - + p->debug.C_ill_conditioned_count++; #endif @@ -1808,7 +1761,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( } } - /* Contract the correction matrix with the internal energy gradient and with + /* Contract the correction matrix with the internal energy gradient and with * the velocity tensor */ hydro_real_t u_gradient[3] = {0}; hydro_real_t u_hessian[3][3] = {0}; @@ -1816,17 +1769,16 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( hydro_real_t velocity_hessian[3][3][3] = {0}; for (int j = 0; j < 3; j++) { for (int i = 0; i < 3; i++) { - u_gradient[j] += p->gradients.correction_matrix[j][i] * - p->gradients.u[i]; + u_gradient[j] += p->gradients.correction_matrix[j][i] * p->gradients.u[i]; for (int k = 0; k < 3; k++) { - u_hessian[j][i] += p->gradients.correction_matrix[j][k] * - p->gradients.u_hessian[i][k]; + u_hessian[j][i] += + p->gradients.correction_matrix[j][k] * p->gradients.u_hessian[i][k]; velocity_tensor[j][i] += p->gradients.correction_matrix[j][k] * p->gradients.velocity_tensor[i][k]; for (int m = 0; m < 3; m++) { - velocity_hessian[j][i][k] += p->gradients.correction_matrix[j][m] * + velocity_hessian[j][i][k] += p->gradients.correction_matrix[j][m] * p->gradients.velocity_hessian[j][i][m]; } } @@ -1835,8 +1787,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( /* Slope limiter for internal energy */ hydro_vec_slope_limiter(p->gradients.du_min, p->gradients.du_max, - p->gradients.kernel_size, - u_gradient); + p->gradients.kernel_size, u_gradient); /* Copy back over to the particle for later */ p->gradients.u[0] = u_gradient[0]; @@ -1849,8 +1800,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( p->gradients.u_hessian[j][2] = u_hessian[j][2]; hydro_vec_slope_limiter(p->gradients.dv_min[j], p->gradients.dv_max[j], - p->gradients.kernel_size, - velocity_tensor[j]); + p->gradients.kernel_size, velocity_tensor[j]); p->gradients.velocity_tensor[j][0] = velocity_tensor[j][0]; p->gradients.velocity_tensor[j][1] = velocity_tensor[j][1]; @@ -1977,20 +1927,20 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Finish final kernel gradient correction factor */ if (p->gradients.adiabatic_f_denominator != 0.) { const hydro_real_t kernel_r2 = p->gradients.adiabatic_f_numerator; - const hydro_real_t weighted_kernel_r2_inv = + const hydro_real_t weighted_kernel_r2_inv = 1. / p->gradients.adiabatic_f_denominator; p->force.f = rho_inv * kernel_r2 * weighted_kernel_r2_inv; - } - else { + } else { p->force.f = 1.; } #ifdef MAGMA2_DEBUG_CHECKS if (p->force.f > 100.) { - warning("Final force factor is very high for particle with ID %lld" - " (prev f: %g, f: %g, numerator: %g, denominator: %g, rho_inv: %g)", - p->id, prev_f, p->force.f, p->gradients.adiabatic_f_numerator, - p->gradients.adiabatic_f_denominator, rho_inv); + warning( + "Final force factor is very high for particle with ID %lld" + " (prev f: %g, f: %g, numerator: %g, denominator: %g, rho_inv: %g)", + p->id, prev_f, p->force.f, p->gradients.adiabatic_f_numerator, + p->gradients.adiabatic_f_denominator, rho_inv); } #endif #endif @@ -2145,13 +2095,10 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( * @param r_inv The inverse distance between particles * @param w_dr The kernel gradient for this particle */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_get_h_dt_sum(const hydro_real_t dv_dot_dx, - const hydro_real_t dv_dot_G, - const hydro_real_t m, - const hydro_real_t rho_inv, - const hydro_real_t r_inv, - const hydro_real_t w_dr) { +__attribute__((always_inline)) INLINE static hydro_real_t hydro_get_h_dt_sum( + const hydro_real_t dv_dot_dx, const hydro_real_t dv_dot_G, + const hydro_real_t m, const hydro_real_t rho_inv, const hydro_real_t r_inv, + const hydro_real_t w_dr) { hydro_real_t dvdx = 0.; #ifdef hydro_props_dh_dt_estimator_type @@ -2183,8 +2130,8 @@ hydro_real_t hydro_get_h_dt_sum(const hydro_real_t dv_dot_dx, * * @param p The particle */ -__attribute__((always_inline)) INLINE static -hydro_real_t hydro_get_h_dt_norm(struct part *restrict p) { +__attribute__((always_inline)) INLINE static hydro_real_t hydro_get_h_dt_norm( + struct part *restrict p) { hydro_real_t renormalization = 1.; @@ -2361,7 +2308,6 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( p->decoupled = 0; p->to_be_decoupled = 0; p->to_be_recoupled = 0; - } /** diff --git a/src/hydro/MAGMA2/hydro_debug.h b/src/hydro/MAGMA2/hydro_debug.h index 84852bbb73..d540d30c2e 100644 --- a/src/hydro/MAGMA2/hydro_debug.h +++ b/src/hydro/MAGMA2/hydro_debug.h @@ -3,7 +3,7 @@ * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2025 Doug Rennehan (douglas.rennehan@gmail.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 @@ -29,14 +29,14 @@ */ __attribute__((always_inline)) INLINE static void hydro_debug_particle( - const struct part* p, const struct xpart* xp) { + const struct part *p, const struct xpart *xp) { warning("[PID%lld] part:", p->id); warning("[PID%lld] x=[%.3e,%.3e,%.3e]", p->id, p->x[0], p->x[1], p->x[2]); warning("[PID%lld] v=[%.3e,%.3e,%.3e]", p->id, p->v[0], p->v[1], p->v[2]); warning("[PID%lld] a=[%.3e,%.3e,%.3e]", p->id, p->a_hydro[0], p->a_hydro[1], p->a_hydro[2]); - warning("[PID%lld] u=%.3e, du/dt=%.3e P=%.3e", p->id, p->u, - p->u_dt, hydro_get_comoving_pressure(p)); + warning("[PID%lld] u=%.3e, du/dt=%.3e P=%.3e", p->id, p->u, p->u_dt, + hydro_get_comoving_pressure(p)); warning("[PID%lld] h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e", p->id, p->h, p->force.h_dt, (int)p->density.wcount, p->mass, p->density.rho_dh); @@ -45,16 +45,11 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "[%.3e,%.3e,%.3e]," "[%.3e,%.3e,%.3e]," "[%.3e,%.3e,%.3e]]", - p->id, p->time_bin, p->rho, - p->gradients.velocity_tensor[0][0], - p->gradients.velocity_tensor[0][1], - p->gradients.velocity_tensor[0][2], - p->gradients.velocity_tensor[1][0], - p->gradients.velocity_tensor[1][1], - p->gradients.velocity_tensor[1][2], - p->gradients.velocity_tensor[2][0], - p->gradients.velocity_tensor[2][1], - p->gradients.velocity_tensor[2][2]); + p->id, p->time_bin, p->rho, p->gradients.velocity_tensor[0][0], + p->gradients.velocity_tensor[0][1], p->gradients.velocity_tensor[0][2], + p->gradients.velocity_tensor[1][0], p->gradients.velocity_tensor[1][1], + p->gradients.velocity_tensor[1][2], p->gradients.velocity_tensor[2][0], + p->gradients.velocity_tensor[2][1], p->gradients.velocity_tensor[2][2]); if (xp != NULL) { warning("[PID%lld] xpart:", p->id); diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index e361424f1c..bd106922ba 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -3,7 +3,7 @@ * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2025 Doug Rennehan (douglas.rennehan@gmail.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 @@ -47,7 +47,7 @@ */ __attribute__((always_inline)) INLINE static void runner_iact_density( const float r2, const float dx[3], const float hi, const float hj, - struct part* restrict pi, struct part* restrict pj, const float a, + struct part *restrict pi, struct part *restrict pj, const float a, const float H) { const unsigned char decoupled_i = pi->decoupled; @@ -99,7 +99,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( const hydro_real_t faci = mj * wi_dx * r_inv; const hydro_real_t facj = mi * wj_dx * r_inv; - /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary + /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary * vector and norm for the gradient */ const hydro_real_t du = pi->u - pj->u; @@ -126,8 +126,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( pj->gradients.u_aux_norm[1] += dx[1] * dx[1] * facj; pj->gradients.u_aux_norm[2] += dx[2] * dx[2] * facj; - const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], - pi->v[1] - pj->v[1], + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; /* Equations 19 & 20 in Rosswog 2020. Signs are all positive because @@ -161,7 +160,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( pi->gradients.adiabatic_f_numerator += mj * r2 * wi; pj->gradients.adiabatic_f_numerator += mi * r2 * wj; #endif - } /** @@ -178,7 +176,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( const float r2, const float dx[3], const float hi, const float hj, - struct part* restrict pi, const struct part* restrict pj, const float a, + struct part *restrict pi, const struct part *restrict pj, const float a, const float H) { const unsigned char decoupled_i = pi->decoupled; @@ -212,7 +210,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( const hydro_real_t r_inv = r ? 1.0 / r : 0.0; const hydro_real_t faci = mj * wi_dx * r_inv; - /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary + /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary * vector and norm for the gradient */ const hydro_real_t du = pi->u - pj->u; @@ -228,8 +226,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( pi->gradients.u_aux_norm[1] += dx[1] * dx[1] * faci; pi->gradients.u_aux_norm[2] += dx[2] * dx[2] * faci; - const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], - pi->v[1] - pj->v[1], + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; /* Equations 19 & 20 in Rosswog 2020. Signs are all positive because @@ -255,7 +252,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( /* Needed for the adiabatic kernel correction factor */ pi->gradients.adiabatic_f_numerator += mj * r2 * wi; #endif - } /** @@ -276,7 +272,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( */ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float r2, const float dx[3], const float hi, const float hj, - struct part* restrict pi, struct part* restrict pj, const float a, + struct part *restrict pi, struct part *restrict pj, const float a, const float H) { const unsigned char decoupled_i = pi->decoupled; @@ -306,7 +302,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const hydro_real_t faci = mj * rhoj_inv * wi; const hydro_real_t facj = mi * rhoi_inv * wj; - + /* Compute all of the first-order gradients, and second-order gradients */ /* Rosswog 2020 Equation 18 gradients. In the paper he uses (vj - vi) and @@ -324,11 +320,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( pj->gradients.u[2] += du * dx[2] * facj; /* Velocity gradients */ - const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], - pi->v[1] - pj->v[1], + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; - for (int k = 0; k < 3; k++) { const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[k]; @@ -383,7 +377,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( pi->gradients.adiabatic_f_denominator += mj * rhoj_inv * r2 * wi; pj->gradients.adiabatic_f_denominator += mi * rhoi_inv * r2 * wj; #endif - } /** @@ -405,7 +398,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float r2, const float dx[3], const float hi, const float hj, - struct part* restrict pi, struct part* restrict pj, const float a, + struct part *restrict pi, struct part *restrict pj, const float a, const float H) { const unsigned char decoupled_i = pi->decoupled; @@ -437,11 +430,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( pi->gradients.u[2] += du * dx[2] * faci; /* Velocity gradients */ - const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], - pi->v[1] - pj->v[1], + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; - for (int k = 0; k < 3; k++) { const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[k]; @@ -456,7 +447,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( * the paper indices. */ pi->gradients.velocity_tensor[k][i] += dv[k] * dx[i] * faci; - const hydro_real_t dv_grad_ki = pi->gradients.velocity_tensor_aux[k][i] - + const hydro_real_t dv_grad_ki = pi->gradients.velocity_tensor_aux[k][i] - pj->gradients.velocity_tensor_aux[k][i]; /* Equation 19 indices: @@ -486,7 +477,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( /* Correction terms for div v */ pi->gradients.adiabatic_f_denominator += mj * rhoj_inv * r2 * wi; #endif - } /** @@ -503,7 +493,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( */ __attribute__((always_inline)) INLINE static void runner_iact_force( const float r2, const float dx[3], const float hi, const float hj, - struct part* restrict pi, struct part* restrict pj, const float a, + struct part *restrict pi, struct part *restrict pj, const float a, const float H) { const unsigned char decoupled_i = pi->decoupled; @@ -551,12 +541,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t wj_dr = hj_inv_dim_plus_one * wj_dx; /* Peculiar velocity difference vector */ - const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], - pi->v[1] - pj->v[1], + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; const hydro_real_t dv_raw[3] = {dv[0], dv[1], dv[2]}; - /* For MAGMA2, this is the full anti-symmetric gradient vector. For the + /* For MAGMA2, this is the full anti-symmetric gradient vector. For the * fallback Gasoline2-style SPH, this will just be the direction vector * between the two particles (r_i - r_j). */ hydro_real_t G_ij[3] = {0., 0., 0.}; @@ -579,15 +568,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( hydro_real_t cond_du_term = 0.; /* Correction factor must be well-conditioned. */ - const unsigned char C_well_conditioned = + const unsigned char C_well_conditioned = (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned); /* First order density-independent velocity gradients */ - const unsigned char D_well_conditioned = + const unsigned char D_well_conditioned = (pi->gradients.D_well_conditioned && pj->gradients.D_well_conditioned); - const unsigned char u_well_conditioned = + const unsigned char u_well_conditioned = (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned); - - /* Flag to revert to use high order gradients */ + + /* Flag to revert to use high order gradients */ unsigned char high_order_gradients_flag = 0; /* Always use high order gradients when both pi and pj are well-conditioned */ @@ -616,7 +605,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_j[0] = 0.; G_j[1] = 0.; G_j[2] = 0.; - + G_i[0] = 0.; G_i[1] = 0.; G_i[2] = 0.; @@ -649,13 +638,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t G_ij_dot_dx_ij_hat_abs = fabs(G_ij_dot_dx_ij_hat); /* Find the cos(theta) term between G and dx */ - hydro_real_t cosine_G_ij_dx_ij_hat = + hydro_real_t cosine_G_ij_dx_ij_hat = G_ij_dot_dx_ij_hat_abs / (G_ij_norm + 1.e-10); /* Handle floating point errors */ if (cosine_G_ij_dx_ij_hat > 1.) cosine_G_ij_dx_ij_hat = 1.; - const unsigned char G_has_large_angle = + const unsigned char G_has_large_angle = (cosine_G_ij_dx_ij_hat < const_viscosity_cosine_limit); const unsigned char G_in_wrong_direction = (G_ij_dot_dx_ij_hat > 0.); @@ -675,8 +664,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_rad_ij[1] = G_ij[1]; G_rad_ij[2] = G_ij[2]; #endif - } - else { + } else { /* Revert back to standard separation vector */ G_ij[0] = dx[0]; G_ij[1] = dx[1]; @@ -701,15 +689,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( * use xi and xj in the slope limiting procedure. */ /* Compute global Van Leer limiter (scalar, not component-wise) */ - const hydro_real_t phi_ij_vec = - hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, - pj->gradients.velocity_tensor, - dx_ij, xi, xj); + const hydro_real_t phi_ij_vec = + hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, + pj->gradients.velocity_tensor, dx_ij, xi, xj); - const hydro_real_t phi_ji_vec = - hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, - pi->gradients.velocity_tensor, - dx_ji, xj, xi); + const hydro_real_t phi_ji_vec = + hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, + pi->gradients.velocity_tensor, dx_ji, xj, xi); /* Make sure no floating point problems */ hydro_real_t phi_vec_sym = fmin(phi_ij_vec, phi_ji_vec); @@ -721,56 +707,44 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t v_j[3] = {pj->v[0], pj->v[1], pj->v[2]}; /* dx_ji for particle i and dx_ij for particle j */ - hydro_vector_second_order_reconstruction(phi_vec_sym, dx_ji, v_i, - pi->gradients.velocity_tensor, - pi->gradients.velocity_hessian, - vi_reconstructed); + hydro_vector_second_order_reconstruction( + phi_vec_sym, dx_ji, v_i, pi->gradients.velocity_tensor, + pi->gradients.velocity_hessian, vi_reconstructed); - hydro_vector_second_order_reconstruction(phi_vec_sym, dx_ij, v_j, - pj->gradients.velocity_tensor, - pj->gradients.velocity_hessian, - vj_reconstructed); + hydro_vector_second_order_reconstruction( + phi_vec_sym, dx_ij, v_j, pj->gradients.velocity_tensor, + pj->gradients.velocity_hessian, vj_reconstructed); const hydro_real_t dv_reconstructed[3] = { vi_reconstructed[0] - vj_reconstructed[0], vi_reconstructed[1] - vj_reconstructed[1], - vi_reconstructed[2] - vj_reconstructed[2] - }; + vi_reconstructed[2] - vj_reconstructed[2]}; /* Get velocity difference, but limit reconstructed values */ hydro_real_t dv_ij[3] = {0., 0., 0.}; - hydro_vec_minmod_limiter(dv_reconstructed, dv_raw, - pi->gradients.dv_min, pi->gradients.dv_max, - pj->gradients.dv_min, pj->gradients.dv_max, - dv_ij); + hydro_vec_minmod_limiter(dv_reconstructed, dv_raw, pi->gradients.dv_min, + pi->gradients.dv_max, pj->gradients.dv_min, + pj->gradients.dv_max, dv_ij); /* Artificial viscosity */ - /* Get the acceleration term, depends on the weighting scheme */ - visc_acc_term = + visc_acc_term = hydro_get_visc_acc_term(pi, pj, dv_ij, dx_ij, fac_mu, a2_Hubble); /* Split heating between the two particles */ visc_du_term = 0.5 * visc_acc_term; - /* Artificial conductivity */ - hydro_real_t ui_reconstructed = 0.; hydro_real_t uj_reconstructed = 0.; - /* Compute global Van Leer limiter (scalar, not component-wise) */ - const hydro_real_t phi_ij_scalar = - hydro_scalar_van_leer_phi(pi->gradients.u, - pj->gradients.u, - dx_ij, xi, xj); - const hydro_real_t phi_ji_scalar = - hydro_scalar_van_leer_phi(pj->gradients.u, - pi->gradients.u, - dx_ji, xj, xi); + const hydro_real_t phi_ij_scalar = hydro_scalar_van_leer_phi( + pi->gradients.u, pj->gradients.u, dx_ij, xi, xj); + const hydro_real_t phi_ji_scalar = hydro_scalar_van_leer_phi( + pj->gradients.u, pi->gradients.u, dx_ji, xj, xi); /* Make sure no floating point problems */ hydro_real_t phi_scalar_sym = fmin(phi_ij_scalar, phi_ji_scalar); @@ -778,31 +752,25 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( phi_scalar_sym = fmax(0., phi_scalar_sym); /* dx_ji for particle i and dx_ij for particle j */ - hydro_scalar_second_order_reconstruction(phi_scalar_sym, dx_ji, - (hydro_real_t)pi->u, - pi->gradients.u, - pi->gradients.u_hessian, - &ui_reconstructed); - - hydro_scalar_second_order_reconstruction(phi_scalar_sym, dx_ij, - (hydro_real_t)pj->u, - pj->gradients.u, - pj->gradients.u_hessian, - &uj_reconstructed); + hydro_scalar_second_order_reconstruction( + phi_scalar_sym, dx_ji, (hydro_real_t)pi->u, pi->gradients.u, + pi->gradients.u_hessian, &ui_reconstructed); + + hydro_scalar_second_order_reconstruction( + phi_scalar_sym, dx_ij, (hydro_real_t)pj->u, pj->gradients.u, + pj->gradients.u_hessian, &uj_reconstructed); const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); const hydro_real_t rho_ij_inv = 1. / rho_ij; - const hydro_real_t dv_Hubble[3] = { - dv[0] + a2_Hubble * dx_ij[0], - dv[1] + a2_Hubble * dx_ij[1], - dv[2] + a2_Hubble * dx_ij[2] - }; + const hydro_real_t dv_Hubble[3] = {dv[0] + a2_Hubble * dx_ij[0], + dv[1] + a2_Hubble * dx_ij[1], + dv[2] + a2_Hubble * dx_ij[2]}; const hydro_real_t dv_Hubble_dot_dx_ij_hat = hydro_vec3_vec3_dot(dv_Hubble, dx_ij_hat); /* Limit art. cond. to only when information is communicable */ - const hydro_real_t c_ij = + const hydro_real_t c_ij = 0.5 * (pi->force.soundspeed + pj->force.soundspeed); const hydro_real_t v_sig_alpha = c_ij * (1. + 0.75 * const_viscosity_alpha); @@ -813,17 +781,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Skip conduction if expansion beats sound speed along LOS */ if (v_sig_alpha > v_sig_beta) { - const hydro_real_t dv_ij_Hubble[3] = { - dv_ij[0] + a2_Hubble * dx_ij[0], - dv_ij[1] + a2_Hubble * dx_ij[1], - dv_ij[2] + a2_Hubble * dx_ij[2] - }; + const hydro_real_t dv_ij_Hubble[3] = {dv_ij[0] + a2_Hubble * dx_ij[0], + dv_ij[1] + a2_Hubble * dx_ij[1], + dv_ij[2] + a2_Hubble * dx_ij[2]}; /* Signal velocity from speed contributions */ #ifdef hydro_props_use_radial_artificial_terms const hydro_real_t dv_ij_Hubble_dot_dx_ij_hat = hydro_vec3_vec3_dot(dv_ij_Hubble, dx_ij_hat); - const hydro_real_t v_sig_speed = + const hydro_real_t v_sig_speed = fac_mu * fabs(dv_ij_Hubble_dot_dx_ij_hat); #else const hydro_real_t v_sig_speed = fac_mu * hydro_vec3_norm(dv_ij_Hubble); @@ -832,10 +798,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get spec. energy difference, but limit reconstructed values */ const hydro_real_t du_raw = pi->u - pj->u; const hydro_real_t du_reconstructed = ui_reconstructed - uj_reconstructed; - const hydro_real_t du_ij = - hydro_scalar_minmod_limiter(du_reconstructed, du_raw, - pi->gradients.du_min, pi->gradients.du_max, - pj->gradients.du_min, pj->gradients.du_max); + const hydro_real_t du_ij = hydro_scalar_minmod_limiter( + du_reconstructed, du_raw, pi->gradients.du_min, pi->gradients.du_max, + pj->gradients.du_min, pj->gradients.du_max); const hydro_real_t alpha_cond = const_conductivity_alpha; const hydro_real_t delta_P = fabs(pressurei - pressurej); @@ -843,15 +808,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Add conductivity to the specific energy */ cond_du_term = alpha_cond * P_lim * v_sig_speed * du_ij * rho_ij_inv; - } - else { + } else { mu_ij = 0.; } - /* Finalize everything with the correct normalizations. */ - /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ const hydro_real_t dv_dot_G_ij = hydro_vec3_vec3_dot(dv_raw, G_ij); @@ -860,10 +822,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t dv_Hubble_along_dx_ij[3] = { dv_Hubble_dot_dx_ij_hat * dx_ij_hat[0], dv_Hubble_dot_dx_ij_hat * dx_ij_hat[1], - dv_Hubble_dot_dx_ij_hat * dx_ij_hat[2] - }; + dv_Hubble_dot_dx_ij_hat * dx_ij_hat[2]}; - const hydro_real_t dv_Hubble_dot_G_rad_ij = + const hydro_real_t dv_Hubble_dot_G_rad_ij = hydro_vec3_vec3_dot(dv_Hubble_along_dx_ij, G_rad_ij); #else const hydro_real_t dv_Hubble_dot_G_rad_ij = @@ -881,21 +842,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( visc_du_term = 0.; } - /* Get the time derivative for h. */ - /* Velocity divergence is from the SPH estimator, not the G_ij vector */ const hydro_real_t dv_dot_dx_ij = hydro_vec3_vec3_dot(dv_raw, dx_ij); - pi->force.h_dt -= hydro_get_h_dt_sum(dv_dot_dx_ij, dv_dot_G_ij, - mj, rhoj_inv, r_inv, wi_dr); - pj->force.h_dt -= hydro_get_h_dt_sum(dv_dot_dx_ij, dv_dot_G_ij, - mi, rhoi_inv, r_inv, wj_dr); - + pi->force.h_dt -= hydro_get_h_dt_sum(dv_dot_dx_ij, dv_dot_G_ij, mj, + rhoj_inv, r_inv, wi_dr); + pj->force.h_dt -= hydro_get_h_dt_sum(dv_dot_dx_ij, dv_dot_G_ij, mi, + rhoi_inv, r_inv, wj_dr); /* Timestepping */ - /* Compute based on raw velocities */ const hydro_real_t v_sig_visc = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); @@ -914,9 +871,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pi->debug.N_force_high_order_grad++; pj->debug.N_force_high_order_grad++; #endif - } - else { /* Gasoline-like SPH fallback */ - + } else { /* Gasoline-like SPH fallback */ /* Compute dv dot dr. */ const hydro_real_t dvdr = hydro_vec3_vec3_dot(dv_raw, G_ij); @@ -926,28 +881,26 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Are the particles moving towards each others ? */ const hydro_real_t omega_ij = fmin(dvdr_Hubble, 0.); - + hydro_real_t mu_full_ij = fac_mu * r_inv * dvdr_Hubble; const hydro_real_t mu_ij = fac_mu * r_inv * omega_ij; /* Construct the full viscosity term */ - const hydro_real_t b_ij = - 0.5 * (pi->gradients.balsara + pj->gradients.balsara); + const hydro_real_t b_ij = + 0.5 * (pi->gradients.balsara + pj->gradients.balsara); const hydro_real_t rho_ij = rhoi + rhoj; const hydro_real_t cs_ij = pi->force.soundspeed + pj->force.soundspeed; const hydro_real_t c_ij = 0.5 * cs_ij; const hydro_real_t alpha = const_viscosity_alpha; - const hydro_real_t visc = - omega_ij < 0. - ? (-0.25 * alpha * cs_ij * - mu_ij + - const_viscosity_beta * mu_ij * mu_ij) * b_ij / - (0.5 * rho_ij) - : 0.; + const hydro_real_t visc = omega_ij < 0. + ? (-0.25 * alpha * cs_ij * mu_ij + + const_viscosity_beta * mu_ij * mu_ij) * + b_ij / (0.5 * rho_ij) + : 0.; visc_acc_term = const_fallback_reduction_factor * visc; visc_du_term = 0.5 * visc_acc_term; - + const hydro_real_t v_sig_alpha = c_ij * (1. + 0.75 * const_viscosity_alpha); const hydro_real_t v_sig_beta = 0.75 * const_viscosity_beta * mu_full_ij; @@ -970,7 +923,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t h_ij = 0.5 * (hi + hj); pi->h_min = fmin(pi->h_min, h_ij); pj->h_min = fmin(pj->h_min, h_ij); - + /* New timestep estimate */ const hydro_real_t dt_min_i = h_ij / new_v_sig_visc; const hydro_real_t dt_min_j = h_ij / new_v_sig_visc; @@ -997,16 +950,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( #endif } - /* Get the time derivative for v. */ - /* Assemble the acceleration */ const hydro_real_t acc[3] = { sph_acc_term * G_ij[0] + visc_acc_term * G_rad_ij[0], sph_acc_term * G_ij[1] + visc_acc_term * G_rad_ij[1], - sph_acc_term * G_ij[2] + visc_acc_term * G_rad_ij[2] - }; + sph_acc_term * G_ij[2] + visc_acc_term * G_rad_ij[2]}; /* Use the force Luke ! */ pi->a_hydro[0] -= mj * acc[0]; @@ -1017,10 +967,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->a_hydro[1] += mi * acc[1]; pj->a_hydro[2] += mi * acc[2]; - /* Get the time derivative for u. */ - /* Assemble the energy equation term */ const hydro_real_t du_dt_i = sph_du_term_i + visc_du_term + cond_du_term; const hydro_real_t du_dt_j = sph_du_term_j + visc_du_term - cond_du_term; @@ -1047,13 +995,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float r2, const float dx[3], const float hi, const float hj, - struct part* restrict pi, const struct part* restrict pj, const float a, + struct part *restrict pi, const struct part *restrict pj, const float a, const float H) { const unsigned char decoupled_i = pi->decoupled; const unsigned char decoupled_j = pj->decoupled; if (decoupled_i || decoupled_j) return; - + /* Cosmological factors entering the EoMs */ const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); const hydro_real_t a2_Hubble = a * a * H; @@ -1094,12 +1042,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t wj_dr = hj_inv_dim_plus_one * wj_dx; /* Peculiar velocity difference vector */ - const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], - pi->v[1] - pj->v[1], + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; const hydro_real_t dv_raw[3] = {dv[0], dv[1], dv[2]}; - /* For MAGMA2, this is the full anti-symmetric gradient vector. For the + /* For MAGMA2, this is the full anti-symmetric gradient vector. For the * fallback Gasoline2-style SPH, this will just be the direction vector * between the two particles (r_i - r_j). */ hydro_real_t G_ij[3] = {0., 0., 0.}; @@ -1121,12 +1068,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( hydro_real_t cond_du_term = 0.; /* Correction factor must be well-conditioned. */ - const unsigned char C_well_conditioned = + const unsigned char C_well_conditioned = (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned); /* First order density-independent velocity gradients */ - const unsigned char D_well_conditioned = + const unsigned char D_well_conditioned = (pi->gradients.D_well_conditioned && pj->gradients.D_well_conditioned); - const unsigned char u_well_conditioned = + const unsigned char u_well_conditioned = (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned); /* Flag to use second order gradients */ @@ -1155,11 +1102,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( * a sign flip for pj */ hydro_get_average_kernel_gradient(pi, pj, G_i, G_j, G_ij); - /* Compute from j perspective to ensure perfectly anti-symmetric */ + /* Compute from j perspective to ensure perfectly anti-symmetric */ G_j[0] = 0.; G_j[1] = 0.; G_j[2] = 0.; - + G_i[0] = 0.; G_i[1] = 0.; G_i[2] = 0.; @@ -1183,7 +1130,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( G_ij[0] = 0.5 * (G_ij[0] - G_ji[0]); G_ij[1] = 0.5 * (G_ij[1] - G_ji[1]); G_ij[2] = 0.5 * (G_ij[2] - G_ji[2]); - + /* Check if G_ij is extremely misaligned with the radial direction */ G_ij_norm = hydro_vec3_norm(G_ij); @@ -1192,13 +1139,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t G_ij_dot_dx_ij_hat_abs = fabs(G_ij_dot_dx_ij_hat); /* Find the cos(theta) term between G and dx */ - hydro_real_t cosine_G_ij_dx_ij_hat = + hydro_real_t cosine_G_ij_dx_ij_hat = G_ij_dot_dx_ij_hat_abs / (G_ij_norm + 1.e-10); /* Handle floating point errors */ if (cosine_G_ij_dx_ij_hat > 1.) cosine_G_ij_dx_ij_hat = 1.; - const unsigned char G_has_large_angle = + const unsigned char G_has_large_angle = (cosine_G_ij_dx_ij_hat < const_viscosity_cosine_limit); const unsigned char G_in_wrong_direction = (G_ij_dot_dx_ij_hat > 0.); @@ -1218,8 +1165,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( G_rad_ij[1] = G_ij[1]; G_rad_ij[2] = G_ij[2]; #endif - } - else { + } else { /* Revert back to standard separation vector */ G_ij[0] = dx[0]; G_ij[1] = dx[1]; @@ -1246,14 +1192,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( * use xi and xj in the slope limiting procedure. */ /* Compute global Van Leer limiter (scalar, not component-wise) */ - const hydro_real_t phi_ij_vec = - hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, - pj->gradients.velocity_tensor, - dx_ij, xi, xj); - const hydro_real_t phi_ji_vec = - hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, - pi->gradients.velocity_tensor, - dx_ji, xj, xi); + const hydro_real_t phi_ij_vec = + hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, + pj->gradients.velocity_tensor, dx_ij, xi, xj); + const hydro_real_t phi_ji_vec = + hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, + pi->gradients.velocity_tensor, dx_ji, xj, xi); /* Make sure no floating point problems */ hydro_real_t phi_vec_sym = fmin(phi_ij_vec, phi_ji_vec); @@ -1265,56 +1209,44 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t v_j[3] = {pj->v[0], pj->v[1], pj->v[2]}; /* dx_ji for particle i and dx_ij for particle j */ - hydro_vector_second_order_reconstruction(phi_vec_sym, dx_ji, v_i, - pi->gradients.velocity_tensor, - pi->gradients.velocity_hessian, - vi_reconstructed); + hydro_vector_second_order_reconstruction( + phi_vec_sym, dx_ji, v_i, pi->gradients.velocity_tensor, + pi->gradients.velocity_hessian, vi_reconstructed); - hydro_vector_second_order_reconstruction(phi_vec_sym, dx_ij, v_j, - pj->gradients.velocity_tensor, - pj->gradients.velocity_hessian, - vj_reconstructed); + hydro_vector_second_order_reconstruction( + phi_vec_sym, dx_ij, v_j, pj->gradients.velocity_tensor, + pj->gradients.velocity_hessian, vj_reconstructed); const hydro_real_t dv_reconstructed[3] = { vi_reconstructed[0] - vj_reconstructed[0], vi_reconstructed[1] - vj_reconstructed[1], - vi_reconstructed[2] - vj_reconstructed[2] - }; + vi_reconstructed[2] - vj_reconstructed[2]}; /* Get velocity difference, but limit reconstructed values */ hydro_real_t dv_ij[3] = {0., 0., 0.}; - hydro_vec_minmod_limiter(dv_reconstructed, dv_raw, - pi->gradients.dv_min, pi->gradients.dv_max, - pj->gradients.dv_min, pj->gradients.dv_max, - dv_ij); - + hydro_vec_minmod_limiter(dv_reconstructed, dv_raw, pi->gradients.dv_min, + pi->gradients.dv_max, pj->gradients.dv_min, + pj->gradients.dv_max, dv_ij); /* Artificial viscosity */ - /* Get the acceleration term, depends on the weighting scheme */ - visc_acc_term = + visc_acc_term = hydro_get_visc_acc_term(pi, pj, dv_ij, dx_ij, fac_mu, a2_Hubble); /* Split heating between the two particles */ visc_du_term = 0.5 * visc_acc_term; - /* Artificial conductivity */ - hydro_real_t ui_reconstructed = 0.; hydro_real_t uj_reconstructed = 0.; /* Compute global Van Leer limiter (scalar, not component-wise) */ - const hydro_real_t phi_ij_scalar = - hydro_scalar_van_leer_phi(pi->gradients.u, - pj->gradients.u, - dx_ij, xi, xj); - const hydro_real_t phi_ji_scalar = - hydro_scalar_van_leer_phi(pj->gradients.u, - pi->gradients.u, - dx_ji, xj, xi); + const hydro_real_t phi_ij_scalar = hydro_scalar_van_leer_phi( + pi->gradients.u, pj->gradients.u, dx_ij, xi, xj); + const hydro_real_t phi_ji_scalar = hydro_scalar_van_leer_phi( + pj->gradients.u, pi->gradients.u, dx_ji, xj, xi); /* Make sure no floating point problems */ hydro_real_t phi_scalar_sym = fmin(phi_ij_scalar, phi_ji_scalar); @@ -1322,31 +1254,25 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( phi_scalar_sym = fmax(0., phi_scalar_sym); /* dx_ji for particle i and dx_ij for particle j */ - hydro_scalar_second_order_reconstruction(phi_scalar_sym, dx_ji, - (hydro_real_t)pi->u, - pi->gradients.u, - pi->gradients.u_hessian, - &ui_reconstructed); - - hydro_scalar_second_order_reconstruction(phi_scalar_sym, dx_ij, - (hydro_real_t)pj->u, - pj->gradients.u, - pj->gradients.u_hessian, - &uj_reconstructed); + hydro_scalar_second_order_reconstruction( + phi_scalar_sym, dx_ji, (hydro_real_t)pi->u, pi->gradients.u, + pi->gradients.u_hessian, &ui_reconstructed); + + hydro_scalar_second_order_reconstruction( + phi_scalar_sym, dx_ij, (hydro_real_t)pj->u, pj->gradients.u, + pj->gradients.u_hessian, &uj_reconstructed); const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); const hydro_real_t rho_ij_inv = 1. / rho_ij; - const hydro_real_t dv_Hubble[3] = { - dv[0] + a2_Hubble * dx_ij[0], - dv[1] + a2_Hubble * dx_ij[1], - dv[2] + a2_Hubble * dx_ij[2] - }; + const hydro_real_t dv_Hubble[3] = {dv[0] + a2_Hubble * dx_ij[0], + dv[1] + a2_Hubble * dx_ij[1], + dv[2] + a2_Hubble * dx_ij[2]}; const hydro_real_t dv_Hubble_dot_dx_ij_hat = hydro_vec3_vec3_dot(dv_Hubble, dx_ij_hat); /* Limit art. cond. to only when information is communicable */ - const hydro_real_t c_ij = + const hydro_real_t c_ij = 0.5 * (pi->force.soundspeed + pj->force.soundspeed); const hydro_real_t v_sig_alpha = c_ij * (1. + 0.75 * const_viscosity_alpha); @@ -1357,17 +1283,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Skip conduction if expansion beats sound speed along LOS */ if (v_sig_alpha > v_sig_beta) { - const hydro_real_t dv_ij_Hubble[3] = { - dv_ij[0] + a2_Hubble * dx_ij[0], - dv_ij[1] + a2_Hubble * dx_ij[1], - dv_ij[2] + a2_Hubble * dx_ij[2] - }; + const hydro_real_t dv_ij_Hubble[3] = {dv_ij[0] + a2_Hubble * dx_ij[0], + dv_ij[1] + a2_Hubble * dx_ij[1], + dv_ij[2] + a2_Hubble * dx_ij[2]}; /* Signal velocity from speed contributions */ #ifdef hydro_props_use_radial_artificial_terms const hydro_real_t dv_ij_Hubble_dot_dx_ij_hat = hydro_vec3_vec3_dot(dv_ij_Hubble, dx_ij_hat); - const hydro_real_t v_sig_speed = + const hydro_real_t v_sig_speed = fac_mu * fabs(dv_ij_Hubble_dot_dx_ij_hat); #else const hydro_real_t v_sig_speed = fac_mu * hydro_vec3_norm(dv_ij_Hubble); @@ -1376,10 +1300,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get spec. energy difference, but limit reconstructed values */ const hydro_real_t du_raw = pi->u - pj->u; const hydro_real_t du_reconstructed = ui_reconstructed - uj_reconstructed; - const hydro_real_t du_ij = - hydro_scalar_minmod_limiter(du_reconstructed, du_raw, - pi->gradients.du_min, pi->gradients.du_max, - pj->gradients.du_min, pj->gradients.du_max); + const hydro_real_t du_ij = hydro_scalar_minmod_limiter( + du_reconstructed, du_raw, pi->gradients.du_min, pi->gradients.du_max, + pj->gradients.du_min, pj->gradients.du_max); const hydro_real_t alpha_cond = const_conductivity_alpha; const hydro_real_t delta_P = fabs(pressurei - pressurej); @@ -1387,14 +1310,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Add conductivity to the specific energy */ cond_du_term = alpha_cond * P_lim * v_sig_speed * du_ij * rho_ij_inv; - } - else { + } else { mu_ij = 0.; } /* Finalize the viscosity and conductivity with correct normalizations. */ - /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ const hydro_real_t dv_dot_G_ij = hydro_vec3_vec3_dot(dv_raw, G_ij); @@ -1403,11 +1324,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t dv_Hubble_along_dx_ij[3] = { dv_Hubble_dot_dx_ij_hat * dx_ij_hat[0], dv_Hubble_dot_dx_ij_hat * dx_ij_hat[1], - dv_Hubble_dot_dx_ij_hat * dx_ij_hat[2] - }; + dv_Hubble_dot_dx_ij_hat * dx_ij_hat[2]}; /* Get Hubble flow contribution */ - const hydro_real_t dv_Hubble_dot_G_rad_ij = + const hydro_real_t dv_Hubble_dot_G_rad_ij = hydro_vec3_vec3_dot(dv_Hubble_along_dx_ij, G_rad_ij); #else const hydro_real_t dv_Hubble_dot_G_rad_ij = @@ -1423,18 +1343,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( visc_du_term = 0.; } - /* Get the time derivative for h. */ - const hydro_real_t dv_dot_dx_ij = hydro_vec3_vec3_dot(dv_raw, dx_ij); - pi->force.h_dt -= hydro_get_h_dt_sum(dv_dot_dx_ij, dv_dot_G_ij, - mj, rhoj_inv, r_inv, wi_dr); - + pi->force.h_dt -= hydro_get_h_dt_sum(dv_dot_dx_ij, dv_dot_G_ij, mj, + rhoj_inv, r_inv, wi_dr); /* Timestepping */ - /* Compute based on raw velocities */ const hydro_real_t v_sig_visc = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); @@ -1448,9 +1364,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( #ifdef MAGMA2_DEBUG_CHECKS pi->debug.N_force_high_order_grad++; #endif - } - else { /* Gasoline2 SPH fallback*/ - + } else { /* Gasoline2 SPH fallback*/ /* Compute dv dot dr. */ const hydro_real_t dvdr = hydro_vec3_vec3_dot(dv_raw, G_ij); @@ -1465,23 +1379,21 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t mu_ij = fac_mu * r_inv * omega_ij; /* Construct the full viscosity term */ - const hydro_real_t b_ij = + const hydro_real_t b_ij = 0.5 * (pi->gradients.balsara + pj->gradients.balsara); const hydro_real_t rho_ij = rhoi + rhoj; const hydro_real_t cs_ij = pi->force.soundspeed + pj->force.soundspeed; const hydro_real_t c_ij = 0.5 * cs_ij; const hydro_real_t alpha = const_viscosity_alpha; - const hydro_real_t visc = - omega_ij < 0. - ? (-0.25 * alpha * cs_ij * - mu_ij + - const_viscosity_beta * mu_ij * mu_ij) * b_ij / - (0.5 * rho_ij) - : 0.; + const hydro_real_t visc = omega_ij < 0. + ? (-0.25 * alpha * cs_ij * mu_ij + + const_viscosity_beta * mu_ij * mu_ij) * + b_ij / (0.5 * rho_ij) + : 0.; visc_acc_term = const_fallback_reduction_factor * visc; visc_du_term = 0.5 * visc_acc_term; - + const hydro_real_t v_sig_alpha = c_ij * (1. + 0.75 * const_viscosity_alpha); const hydro_real_t v_sig_beta = 0.75 * const_viscosity_beta * mu_full_ij; @@ -1529,8 +1441,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t acc[3] = { sph_acc_term * G_ij[0] + visc_acc_term * G_rad_ij[0], sph_acc_term * G_ij[1] + visc_acc_term * G_rad_ij[1], - sph_acc_term * G_ij[2] + visc_acc_term * G_rad_ij[2] - }; + sph_acc_term * G_ij[2] + visc_acc_term * G_rad_ij[2]}; /* Use the force Luke ! */ pi->a_hydro[0] -= mj * acc[0]; diff --git a/src/hydro/MAGMA2/hydro_io.h b/src/hydro/MAGMA2/hydro_io.h index 1edd53e043..414f519586 100644 --- a/src/hydro/MAGMA2/hydro_io.h +++ b/src/hydro/MAGMA2/hydro_io.h @@ -3,7 +3,7 @@ * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2025 Doug Rennehan (douglas.rennehan@gmail.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 @@ -40,9 +40,9 @@ * @param list The list of i/o properties to read. * @param num_fields The number of i/o fields to read. */ -INLINE static void hydro_read_particles(struct part* parts, - struct io_props* list, - int* num_fields) { +INLINE static void hydro_read_particles(struct part *parts, + struct io_props *list, + int *num_fields) { *num_fields = 8; /* List what we want to read */ @@ -64,27 +64,27 @@ INLINE static void hydro_read_particles(struct part* parts, UNIT_CONV_DENSITY, parts, rho); } -INLINE static void convert_S(const struct engine* e, const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_S(const struct engine *e, const struct part *p, + const struct xpart *xp, float *ret) { ret[0] = hydro_get_comoving_entropy(p, xp); } -INLINE static void convert_P(const struct engine* e, const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_P(const struct engine *e, const struct part *p, + const struct xpart *xp, float *ret) { ret[0] = hydro_get_comoving_pressure(p); } -INLINE static void convert_div_v(const struct engine* e, const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_div_v(const struct engine *e, const struct part *p, + const struct xpart *xp, float *ret) { ret[0] = p->gradients.velocity_tensor[0][0] + p->gradients.velocity_tensor[1][1] + p->gradients.velocity_tensor[2][2]; } -INLINE static void convert_part_pos(const struct engine* e, - const struct part* p, - const struct xpart* xp, double* ret) { - const struct space* s = e->s; +INLINE static void convert_part_pos(const struct engine *e, + const struct part *p, + const struct xpart *xp, double *ret) { + const struct space *s = e->s; if (s->periodic) { ret[0] = box_wrap(p->x[0], 0.0, s->dim[0]); ret[1] = box_wrap(p->x[1], 0.0, s->dim[1]); @@ -101,11 +101,11 @@ INLINE static void convert_part_pos(const struct engine* e, } } -INLINE static void convert_part_vel(const struct engine* e, - const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_part_vel(const struct engine *e, + const struct part *p, + const struct xpart *xp, float *ret) { const int with_cosmology = (e->policy & engine_policy_cosmology); - const struct cosmology* cosmo = e->cosmology; + const struct cosmology *cosmo = e->cosmology; const integertime_t ti_current = e->ti_current; const double time_base = e->time_base; const float dt_kick_grav_mesh = e->dt_kick_grav_mesh_for_io; @@ -152,23 +152,22 @@ INLINE static void convert_part_vel(const struct engine* e, ret[2] *= cosmo->a_inv; } -INLINE static void convert_part_potential(const struct engine* e, - const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_part_potential(const struct engine *e, + const struct part *p, + const struct xpart *xp, float *ret) { if (p->gpart != NULL) ret[0] = gravity_get_comoving_potential(p->gpart); else ret[0] = 0.f; } -INLINE static void convert_part_softening(const struct engine* e, - const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_part_softening(const struct engine *e, + const struct part *p, + const struct xpart *xp, float *ret) { if (p->gpart != NULL) { ret[0] = kernel_gravity_softening_plummer_equivalent_inv * gravity_get_softening(p->gpart, e->gravity_properties); - } - else { + } else { ret[0] = 0.f; } } @@ -180,10 +179,10 @@ INLINE static void convert_part_softening(const struct engine* e, * @param list The list of i/o properties to write. * @param num_fields The number of i/o fields to write. */ -INLINE static void hydro_write_particles(const struct part* parts, - const struct xpart* xparts, - struct io_props* list, - int* num_fields) { +INLINE static void hydro_write_particles(const struct part *parts, + const struct xpart *xparts, + struct io_props *list, + int *num_fields) { *num_fields = 0; int num = 0; @@ -200,9 +199,8 @@ INLINE static void hydro_write_particles(const struct part* parts, "co-moving positions of the particles"); num++; - list[num] = io_make_output_field( - "Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, mass, - "Masses of the particles"); + list[num] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + parts, mass, "Masses of the particles"); num++; list[num] = io_make_output_field( @@ -221,9 +219,9 @@ INLINE static void hydro_write_particles(const struct part* parts, /*can convert to comoving=*/0, "Unique IDs of the particles"); num++; - list[num] = io_make_output_field( - "Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, parts, rho, - "Co-moving mass densities of the particles"); + list[num] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, + -3.f, parts, rho, + "Co-moving mass densities of the particles"); num++; list[num] = io_make_output_field_convert_part( @@ -285,27 +283,26 @@ INLINE static void hydro_write_particles(const struct part* parts, */ #ifdef MAGMA2_DEBUG_CHECKS - list[num] = io_make_output_field( - "NumberOfNeighbours", INT, 1, UNIT_CONV_NO_UNITS, - 0.f, parts, debug.num_ngb, - "Number of neighbours."); + list[num] = + io_make_output_field("NumberOfNeighbours", INT, 1, UNIT_CONV_NO_UNITS, + 0.f, parts, debug.num_ngb, "Number of neighbours."); num++; list[num] = io_make_output_field( - "LowOrderGradientsCount", INT, 1, UNIT_CONV_NO_UNITS, - 0.f, parts, debug.N_force_low_order_grad, + "LowOrderGradientsCount", INT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + debug.N_force_low_order_grad, "Cumulative number of low order gradient force interactions."); num++; list[num] = io_make_output_field( - "HighOrderGradientsCount", INT, 1, UNIT_CONV_NO_UNITS, - 0.f, parts, debug.N_force_high_order_grad, + "HighOrderGradientsCount", INT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + debug.N_force_high_order_grad, "Cumulative number of high order gradient force interactions."); num++; list[num] = io_make_output_field( - "CorrectionMatrices", FLOAT, 9, UNIT_CONV_LENGTH * UNIT_CONV_LENGTH, - 2.f, parts, debug.correction_matrix, + "CorrectionMatrices", FLOAT, 9, UNIT_CONV_LENGTH * UNIT_CONV_LENGTH, 2.f, + parts, debug.correction_matrix, "Co-moving correction matrices for the particles."); num++; @@ -316,41 +313,42 @@ INLINE static void hydro_write_particles(const struct part* parts, num++; list[num] = io_make_output_field( - "VelocityGradientNumeratorMatrices", FLOAT, 9, - UNIT_CONV_DENSITY * UNIT_CONV_SPEED / UNIT_CONV_LENGTH, - -5.f, parts, debug.velocity_tensor_aux, + "VelocityGradientNumeratorMatrices", FLOAT, 9, + UNIT_CONV_DENSITY * UNIT_CONV_SPEED / UNIT_CONV_LENGTH, -5.f, parts, + debug.velocity_tensor_aux, "Co-moving numerator matrices for the particles."); num++; list[num] = io_make_output_field( - "VelocityGradientDenominatorMatrices", FLOAT, 9, UNIT_CONV_DENSITY, - -3.f, parts, debug.velocity_tensor_aux_norm, + "VelocityGradientDenominatorMatrices", FLOAT, 9, UNIT_CONV_DENSITY, -3.f, + parts, debug.velocity_tensor_aux_norm, "Co-moving denominator matrices for the particles."); num++; list[num] = io_make_output_field( - "VelocityGradientIllConditionedCounts", INT, 1, UNIT_CONV_NO_UNITS, - 0.f, parts, debug.D_ill_conditioned_count, + "VelocityGradientIllConditionedCounts", INT, 1, UNIT_CONV_NO_UNITS, 0.f, + parts, debug.D_ill_conditioned_count, "Count for how many times this particle had an ill-conditioned D matrix"); num++; list[num] = io_make_output_field( - "SpecificEnergyGradientNumerators", FLOAT, 3, + "SpecificEnergyGradientNumerators", FLOAT, 3, UNIT_CONV_DENSITY * UNIT_CONV_ENERGY_PER_UNIT_MASS / UNIT_CONV_LENGTH, -6.f, parts, debug.u_aux, "Co-moving specific energy numerator matrices for the particles."); num++; list[num] = io_make_output_field( - "SpecificEnergyGradientDenominators", FLOAT, 3, UNIT_CONV_DENSITY, - -3.f, parts, debug.u_aux, + "SpecificEnergyGradientDenominators", FLOAT, 3, UNIT_CONV_DENSITY, -3.f, + parts, debug.u_aux, "Co-moving specific energy gradient numerator for the particles."); num++; - list[num] = io_make_output_field( - "SpecificEnergyIllConditionedCounts", INT, 1, UNIT_CONV_NO_UNITS, - 0.f, parts, debug.u_ill_conditioned_count, - "Count for how many times this particle had an ill-conditioned u_aux_norm"); + list[num] = io_make_output_field("SpecificEnergyIllConditionedCounts", INT, 1, + UNIT_CONV_NO_UNITS, 0.f, parts, + debug.u_ill_conditioned_count, + "Count for how many times this particle had " + "an ill-conditioned u_aux_norm"); num++; #endif diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 3e99e97207..89228142a1 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -45,10 +45,8 @@ * as well as a number of compile-time parameters. */ - /* ---------- Viscosity & Conductivitiy parameters ---------- */ - /*! Alpha viscosity, usually =1.0. For lower N_ngb, should be higher */ #define const_viscosity_alpha 2.0 @@ -64,7 +62,6 @@ #define const_kernel_target_neighbours 114.0 #endif - /* ---------- These parameters should not be changed ---------- */ /*! Use a Swift-like estimator for dh/dt rather than the correct formula @@ -78,10 +75,10 @@ #define hydro_props_use_balsara_limiter /*! Flag to use additional slope limiting procedures */ -//#define hydro_props_use_extra_slope_limiter +// #define hydro_props_use_extra_slope_limiter /* Flag to disallow sign flip in reconstructed quantities */ -//#define hydro_props_use_strict_minmod_limiter +// #define hydro_props_use_strict_minmod_limiter /* Slope limiter length, fraction of max. distance in kernel */ #ifdef hydro_props_use_extra_slope_limiter @@ -94,34 +91,34 @@ /* Viscosity floor when G_ij is extremely misaligned with dx_ij */ #define const_viscosity_cosine_limit 0.1736 -/* Viscosity weighting scheme: +/* Viscosity weighting scheme: * 0 = (rho_i * q_i + rho_j * q_j) / (rho_i * rho_j) * 1 = (rho_i * q_ij + rho_j * q_ij) / (rho_i * rho_j) * 2 = 2.0 * q_ij / (rho_i + rho_j) */ #define hydro_props_viscosity_weighting_type 2 /* Flag to use radial gradients for viscosity and conductivity */ -//#define hydro_props_use_radial_artificial_terms +// #define hydro_props_use_radial_artificial_terms /*! Use the correction terms to make the internal energy match the mass flux */ -//#define hydro_props_use_adiabatic_correction +// #define hydro_props_use_adiabatic_correction /* Kernel gradient weighting scheme: * 0 = 0.5 * (G_i + G_j) * 1 = 0.5 * (f_i * G_i + f_j * G_j) * 2 = 0.5 * f_ij * (G_i + G_j) * with f_ij = 0.5 * (f_i + f_j) - * 3 = 0.5 * f_ij * (G_i + G_j) + * 3 = 0.5 * f_ij * (G_i + G_j) * with f_ij = 2 * f_i * f_j / (f_i + f_j) - * 4 = 0.5 * f_ij * (G_i + G_j) + * 4 = 0.5 * f_ij * (G_i + G_j) * with f_ij = sqrt(f_i * f_j) - * 5 = 0.5 * f_ij * (G_i + G_j) + * 5 = 0.5 * f_ij * (G_i + G_j) * with f_ij = (f_i * rho_i + f_j * rho_j) / (rho_i + rho_j) - * 6 = 0.5 * f_ij * (G_i + G_j) + * 6 = 0.5 * f_ij * (G_i + G_j) * with f_ij = 2 * f_i * f_j / (f_i * rho_i + f_j * rho_j) - * 7 = 0.5 * f_ij * (G_i + G_j) + * 7 = 0.5 * f_ij * (G_i + G_j) * with f_ij = (f_i * P_i + f_j * P_j) / (P_i + P_j) - * 8 = 0.5 * f_ij * (G_i + G_j) + * 8 = 0.5 * f_ij * (G_i + G_j) * with f_ij = 2 * f_i * f_j / (f_i * P_i + f_j * P_j) * 9 = 0.5 * f_ij * (G_i + G_j) * with f_ij = (f_i * V_i + f_j * V_j) / (V_i + V_j) @@ -129,20 +126,22 @@ #define hydro_props_kernel_gradient_weighting 0 /*! Use double precision for all matrix/vector operations */ -//#define hydro_props_use_double_precision +// #define hydro_props_use_double_precision #ifdef hydro_props_use_double_precision /*! Consider matrix inversion to be ill-conditioned above this limit */ #define const_condition_number_upper_limit 300. /*! Mean interparticle spacing for this kernel and neighbour number */ -#define const_kernel_mean_spacing (kernel_gamma*pow(4. * M_PI / (3. * \ - (double)const_kernel_target_neighbours), 1. / 3.)) +#define const_kernel_mean_spacing \ + (kernel_gamma * \ + pow(4. * M_PI / (3. * (double)const_kernel_target_neighbours), 1. / 3.)) #else /*! Consider matrix inversion to be ill-conditioned above this limit */ #define const_condition_number_upper_limit 60. /*! Mean interparticle spacing for this kernel and neighbour number */ -#define const_kernel_mean_spacing (kernel_gamma*powf(4. * M_PI / (3. * \ - (float)const_kernel_target_neighbours), 1. / 3.)) +#define const_kernel_mean_spacing \ + (kernel_gamma * \ + powf(4. * M_PI / (3. * (float)const_kernel_target_neighbours), 1. / 3.)) #endif /*! eta_crit Rosswog 2020 Eq 23. Of order the mean interparticle spacing. */ @@ -156,11 +155,11 @@ /*! Cosmology default const_viscosity_beta=2*const_viscosity_alpha * Beta is defined as in e.g. Price (2010) Eqn (103) */ -#define const_viscosity_beta (2.0*const_viscosity_alpha) +#define const_viscosity_beta (2.0 * const_viscosity_alpha) /*! Prefactor for alpha term in signal velocity */ -#define const_viscosity_alpha_prefactor (1.25 * (1. + \ - 0.75 * const_viscosity_alpha)) +#define const_viscosity_alpha_prefactor \ + (1.25 * (1. + 0.75 * const_viscosity_alpha)) /*! Prefactor for beta term in signal velocity */ #define const_viscosity_beta_prefactor (1.25 * 0.75 * const_viscosity_beta) @@ -170,12 +169,11 @@ /* ---------- Structures for below ---------- */ - /*! Artificial viscosity parameters */ -struct viscosity_global_data { }; +struct viscosity_global_data {}; /*! Thermal diffusion parameters */ -struct diffusion_global_data { }; +struct diffusion_global_data {}; /* Functions for reading from parameter file */ @@ -202,10 +200,10 @@ typedef float hydro_real_t; * @param phys_const: pointer to the physical constants system * @param viscosity: pointer to the viscosity_global_data struct to be filled. **/ -static INLINE void viscosity_init(struct swift_params* params, - const struct unit_system* us, - const struct phys_const* phys_const, - struct viscosity_global_data* viscosity) { } +static INLINE void viscosity_init(struct swift_params *params, + const struct unit_system *us, + const struct phys_const *phys_const, + struct viscosity_global_data *viscosity) {} /** * @brief Initialises a viscosity struct to sensible numbers for mocking @@ -214,7 +212,7 @@ static INLINE void viscosity_init(struct swift_params* params, * @param viscosity: pointer to the viscosity_global_data struct to be filled. **/ static INLINE void viscosity_init_no_hydro( - struct viscosity_global_data* viscosity) { } + struct viscosity_global_data *viscosity) {} /** * @brief Prints out the viscosity parameters at the start of a run. @@ -223,7 +221,7 @@ static INLINE void viscosity_init_no_hydro( * hydro_properties **/ static INLINE void viscosity_print( - const struct viscosity_global_data* viscosity) { + const struct viscosity_global_data *viscosity) { message("Artificial viscosity alpha set to %.3f", const_viscosity_alpha); message("Artificial viscosity beta set to %.3f", const_viscosity_beta); } @@ -236,7 +234,7 @@ static INLINE void viscosity_print( * @param viscosity: pointer to the viscosity_global_data struct. **/ static INLINE void viscosity_print_snapshot( - hid_t h_grpsph, const struct viscosity_global_data* viscosity) { + hid_t h_grpsph, const struct viscosity_global_data *viscosity) { io_write_attribute_f(h_grpsph, "Alpha viscosity", const_viscosity_alpha); io_write_attribute_f(h_grpsph, "Beta viscosity", const_viscosity_beta); @@ -254,10 +252,10 @@ static INLINE void viscosity_print_snapshot( * @param phys_const: pointer to the physical constants system * @param diffusion_global_data: pointer to the diffusion struct to be filled. **/ -static INLINE void diffusion_init(struct swift_params* params, - const struct unit_system* us, - const struct phys_const* phys_const, - struct diffusion_global_data* diffusion) { } +static INLINE void diffusion_init(struct swift_params *params, + const struct unit_system *us, + const struct phys_const *phys_const, + struct diffusion_global_data *diffusion) {} /** * @brief Initialises a diffusion struct to sensible numbers for mocking @@ -266,7 +264,7 @@ static INLINE void diffusion_init(struct swift_params* params, * @param diffusion: pointer to the diffusion_global_data struct to be filled. **/ static INLINE void diffusion_init_no_hydro( - struct diffusion_global_data* diffusion) { } + struct diffusion_global_data *diffusion) {} /** * @brief Prints out the diffusion parameters at the start of a run. @@ -275,7 +273,7 @@ static INLINE void diffusion_init_no_hydro( * hydro_properties **/ static INLINE void diffusion_print( - const struct diffusion_global_data* diffusion) { + const struct diffusion_global_data *diffusion) { message("Artificial conductivity alpha set to %.3f", const_conductivity_alpha); } @@ -288,7 +286,7 @@ static INLINE void diffusion_print( * @param diffusion: pointer to the diffusion_global_data struct. **/ static INLINE void diffusion_print_snapshot( - hid_t h_grpsph, const struct diffusion_global_data* diffusion) { + hid_t h_grpsph, const struct diffusion_global_data *diffusion) { io_write_attribute_f(h_grpsph, "Conductivity alpha", const_conductivity_alpha); } diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index caef3b4ffc..44c1ec6b7b 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -3,7 +3,7 @@ * Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) & * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2025 Doug Rennehan (douglas.rennehan@gmail.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 @@ -106,7 +106,7 @@ struct part { long long id; /*! Pointer to corresponding gravity part. */ - struct gpart* gpart; + struct gpart *gpart; /*! Particle position. */ double x[3]; @@ -140,11 +140,11 @@ struct part { /*! Minimum time-step amongst neighbours */ float dt_min; - + /*! Conduction du/dt */ float u_dt_cond; - - #ifdef MAGMA2_DEBUG_CHECKS + +#ifdef MAGMA2_DEBUG_CHECKS struct { /*! Correction matrix at the last time it was ill-conditioned */ hydro_real_t correction_matrix[3][3]; @@ -154,7 +154,7 @@ struct part { /*! Velocity tensor norm ill-conditioned */ hydro_real_t velocity_tensor_aux_norm[3][3]; - + /*! u_aux tensor at ill-condition time */ hydro_real_t u_aux[3]; @@ -224,7 +224,7 @@ struct part { /*! Normalization for computing u_aux */ hydro_real_t u_aux_norm[3]; - + /*! Flag for whether u_aux_norm is ill-conditioned */ char u_well_conditioned; @@ -300,7 +300,7 @@ struct part { /*! Flag to indicate that the decoupling task will run */ unsigned char to_be_decoupled; - + /*! Flag to indicate that the recoupling task will run */ unsigned char to_be_recoupled; diff --git a/src/hydro_properties.c b/src/hydro_properties.c index 7051ccea47..ff184a166b 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -68,11 +68,12 @@ void hydro_props_init(struct hydro_props *p, /* Target number of neighbours (optional) */ p->target_neighbours = - parser_get_opt_param_float(params, "SPH:target_neighbours", 0.f); + parser_get_opt_param_float(params, "SPH:target_neighbours", 0.f); - if (p->eta_neighbours <= 0.f && p->target_neighbours <=0.f) { - error("You must set either SPH:resolution_eta or SPH:target_neighbours " - "in the parameter file."); + if (p->eta_neighbours <= 0.f && p->target_neighbours <= 0.f) { + error( + "You must set either SPH:resolution_eta or SPH:target_neighbours " + "in the parameter file."); } /* Tolerance for the smoothing length Newton-Raphson scheme */ @@ -83,8 +84,7 @@ void hydro_props_init(struct hydro_props *p, /* Target number of neighbours */ if (p->eta_neighbours > 0.f) { p->target_neighbours = pow_dimension(p->eta_neighbours) * kernel_norm; - } - else { + } else { p->eta_neighbours = powf(p->target_neighbours / kernel_norm, hydro_dimension_inv); } @@ -96,17 +96,19 @@ void hydro_props_init(struct hydro_props *p, #ifdef MAGMA2_SPH #ifndef const_kernel_target_neighbours - error("When using MAGMA2 SPH, the constant " - "const_kernel_target_neighbours must be defined in the header file " - "hydro_parameters.h. This is a compile-time constant and must be set " - "to the desired number of neighbours in the parameter file."); + error( + "When using MAGMA2 SPH, the constant " + "const_kernel_target_neighbours must be defined in the header file " + "hydro_parameters.h. This is a compile-time constant and must be set " + "to the desired number of neighbours in the parameter file."); #else if (fabsf((float)const_kernel_target_neighbours - p->target_neighbours) > 0.05f * p->target_neighbours) { - error("When using MAGMA2 SPH, the compiled constant " - "const_kernel_target_neighbours (%g) must be within 5 percent of " - "the desired number of neighbours (%g) in the parameter file.", - (float)const_kernel_target_neighbours, p->target_neighbours); + error( + "When using MAGMA2 SPH, the compiled constant " + "const_kernel_target_neighbours (%g) must be within 5 percent of " + "the desired number of neighbours (%g) in the parameter file.", + (float)const_kernel_target_neighbours, p->target_neighbours); } #endif #endif From c577ce601590911f3194e3fb7548003f82e7471b Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Sat, 8 Nov 2025 16:46:55 +0000 Subject: [PATCH 04/37] Add div_v calculation to MAGMA2 to enable unit tests --- src/hydro/MAGMA2/hydro.h | 9 ++++++++- src/hydro/MAGMA2/hydro_iact.h | 35 ++++++++++++++++++++++------------- src/hydro/MAGMA2/hydro_part.h | 2 ++ 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index a082bd3c9b..72641a236b 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -550,6 +550,7 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->rho_gradient[1] = 0.f; p->rho_gradient[2] = 0.f; p->density.rho_dh = 0.f; + p->density.div_v = 0.f; #ifdef MAGMA2_DEBUG_CHECKS p->debug.num_ngb = 0; #endif @@ -1421,6 +1422,12 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.wcount *= h_inv_dim; p->density.wcount_dh *= h_inv_dim_plus_one; + /* Finish calculation of the velocity divergence */ + const float rho_inv = 1.f / p->rho; + const float a_inv2 = cosmo->a2_inv; + p->density.div_v *= h_inv_dim_plus_one * rho_inv * a_inv2; + p->density.div_v += cosmo->H * hydro_dimension; + /* Need this for correct dh/dt */ p->gradients.wcount = p->density.wcount; @@ -1857,7 +1864,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( p->density.wcount = kernel_root * h_inv_dim; p->density.rho_dh = 0.f; p->density.wcount_dh = 0.f; - + p->density.div_v = 0.f; #ifdef MAGMA2_DEBUG_CHECKS p->debug.num_ngb = 0; #endif diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index bd106922ba..0d9b9a10e5 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -90,15 +90,24 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( adaptive_softening_add_correction_term(pj, xj, hj_inv, mi); + /* Now we need to compute the div terms */ + const hydro_real_t r_inv = r ? 1.0 / r : 0.0; + const hydro_real_t faci = mj * wi_dx * r_inv; + const hydro_real_t facj = mi * wj_dx * r_inv; + + /* Compute dv dot r */ + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; + const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]; + + pi->density.div_v -= faci * dvdr; + pj->density.div_v -= facj * dvdr; + /* For slope limiter */ pi->gradients.kernel_size = fmax(r, pi->gradients.kernel_size); pj->gradients.kernel_size = fmax(r, pj->gradients.kernel_size); /* Now we need to compute the derivative terms */ - const hydro_real_t r_inv = r ? 1.0 / r : 0.0; - const hydro_real_t faci = mj * wi_dx * r_inv; - const hydro_real_t facj = mi * wj_dx * r_inv; - /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary * vector and norm for the gradient */ const hydro_real_t du = pi->u - pj->u; @@ -126,9 +135,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( pj->gradients.u_aux_norm[1] += dx[1] * dx[1] * facj; pj->gradients.u_aux_norm[2] += dx[2] * dx[2] * facj; - const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], - pi->v[2] - pj->v[2]}; - /* Equations 19 & 20 in Rosswog 2020. Signs are all positive because * dv * dx always results in a positive sign. */ for (int i = 0; i < 3; i++) { @@ -204,12 +210,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( adaptive_softening_add_correction_term(pi, xi, h_inv, mj); - /* For slope limiter */ - pi->gradients.kernel_size = fmax(r, pi->gradients.kernel_size); - const hydro_real_t r_inv = r ? 1.0 / r : 0.0; const hydro_real_t faci = mj * wi_dx * r_inv; + /* Compute dv dot r */ + const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; + const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]; + pi->density.div_v -= faci * dvdr; + + /* For slope limiter */ + pi->gradients.kernel_size = fmax(r, pi->gradients.kernel_size); + /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary * vector and norm for the gradient */ const hydro_real_t du = pi->u - pj->u; @@ -226,9 +238,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( pi->gradients.u_aux_norm[1] += dx[1] * dx[1] * faci; pi->gradients.u_aux_norm[2] += dx[2] * dx[2] * faci; - const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], - pi->v[2] - pj->v[2]}; - /* Equations 19 & 20 in Rosswog 2020. Signs are all positive because * dv * dx always results in a positive sign. */ for (int i = 0; i < 3; i++) { diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 44c1ec6b7b..f96af92a8c 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -270,6 +270,8 @@ struct part { /*! Derivative of density with respect to h */ float rho_dh; + /*! Particle velocity divergence. */ + float div_v; } density; /** From ac97b179b0be6ce97398b286766ff9ede3c3383e Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Thu, 13 Nov 2025 09:18:33 +0000 Subject: [PATCH 05/37] Add KIARA chemistry and cooling. Compiles but otherwise untested. --- configure.ac | 262 ++- src/Makefile.am | 18 + src/chemistry.h | 3 + src/chemistry/KIARA/chemistry.h | 1569 ++++++++++++++++ src/chemistry/KIARA/chemistry_additions.h | 56 + src/chemistry/KIARA/chemistry_debug.h | 40 + src/chemistry/KIARA/chemistry_iact.h | 934 ++++++++++ src/chemistry/KIARA/chemistry_io.h | 221 +++ src/chemistry/KIARA/chemistry_struct.h | 264 +++ src/chemistry_debug.h | 2 + src/chemistry_io.h | 2 + src/chemistry_struct.h | 2 + src/cooling.h | 2 + src/cooling/KIARA/cooling.c | 2014 +++++++++++++++++++++ src/cooling/KIARA/cooling.h | 609 +++++++ src/cooling/KIARA/cooling_debug.h | 54 + src/cooling/KIARA/cooling_io.h | 380 ++++ src/cooling/KIARA/cooling_properties.h | 139 ++ src/cooling/KIARA/cooling_struct.h | 100 + src/cooling_debug.h | 2 + src/cooling_io.h | 2 + src/cooling_properties.h | 2 + src/cooling_struct.h | 2 + src/hydro/MAGMA2/hydro.h | 21 +- src/hydro/MAGMA2/hydro_iact.h | 30 +- src/hydro/MAGMA2/hydro_io.h | 28 - src/hydro/MAGMA2/hydro_part.h | 17 +- src/runner_doiact_functions_hydro.h | 93 +- src/runner_ghost.c | 2 +- src/runner_others.c | 5 +- swift.c | 24 +- tests/test125cells.c | 7 +- 32 files changed, 6785 insertions(+), 121 deletions(-) create mode 100644 src/chemistry/KIARA/chemistry.h create mode 100644 src/chemistry/KIARA/chemistry_additions.h create mode 100644 src/chemistry/KIARA/chemistry_debug.h create mode 100644 src/chemistry/KIARA/chemistry_iact.h create mode 100644 src/chemistry/KIARA/chemistry_io.h create mode 100644 src/chemistry/KIARA/chemistry_struct.h create mode 100644 src/cooling/KIARA/cooling.c create mode 100644 src/cooling/KIARA/cooling.h create mode 100644 src/cooling/KIARA/cooling_debug.h create mode 100644 src/cooling/KIARA/cooling_io.h create mode 100644 src/cooling/KIARA/cooling_properties.h create mode 100644 src/cooling/KIARA/cooling_struct.h diff --git a/configure.ac b/configure.ac index 6cd850b646..87fb82dbe2 100644 --- a/configure.ac +++ b/configure.ac @@ -319,6 +319,54 @@ if test "$enable_timers" = "yes"; then AC_DEFINE([SWIFT_USE_TIMERS],1,[Enable individual timers]) fi +# Check if Simba expensive debugging is on. +AC_ARG_ENABLE([simba-debugging-checks], + [AS_HELP_STRING([--enable-simba-debugging-checks], + [Activate expensive consistency checks @<:@yes/no@:>@] + )], + [simba_enable_debugging_checks="$enableval"], + [simba_enable_debugging_checks="no"] +) +if test "$enable_simba_debugging_checks" = "yes"; then + AC_DEFINE([SIMBA_DEBUG_CHECKS],1,[Enable Simba expensive debugging]) +fi + +# Check if KIARA expensive debugging is on. +AC_ARG_ENABLE([kiara-debugging-checks], + [AS_HELP_STRING([--enable-kiara-debugging-checks], + [Activate expensive consistency checks @<:@yes/no@:>@] + )], + [kiara_enable_debugging_checks="$enableval"], + [kiara_enable_debugging_checks="no"] +) +if test "$enable_kiara_debugging_checks" = "yes"; then + AC_DEFINE([KIARA_DEBUG_CHECKS],1,[Enable KIARA expensive debugging]) +fi + +# Check if Firehose expensive debugging is on. +AC_ARG_ENABLE([firehose-debugging-checks], + [AS_HELP_STRING([--enable-firehose-debugging-checks], + [Activate expensive consistency checks @<:@yes/no@:>@] + )], + [firehose_enable_debugging_checks="$enableval"], + [firehose_enable_debugging_checks="no"] +) +if test "$enable_firehose_debugging_checks" = "yes"; then + AC_DEFINE([FIREHOSE_DEBUG_CHECKS],1,[Enable firehose expensive debugging]) +fi + +# Check if Obsidian expensive debugging is on. +AC_ARG_ENABLE([obsidian-debugging-checks], + [AS_HELP_STRING([--enable-obsidian-debugging-checks], + [Activate expensive consistency checks @<:@yes/no@:>@] + )], + [obsidian_enable_debugging_checks="$enableval"], + [obsidian_enable_debugging_checks="no"] +) +if test "$enable_obsidian_debugging_checks" = "yes"; then + AC_DEFINE([OBSIDIAN_DEBUG_CHECKS],1,[Enable Obsidian expensive debugging]) +fi + # Check if expensive debugging is on. AC_ARG_ENABLE([debugging-checks], [AS_HELP_STRING([--enable-debugging-checks], @@ -331,6 +379,18 @@ if test "$enable_debugging_checks" = "yes"; then AC_DEFINE([SWIFT_DEBUG_CHECKS],1,[Enable expensive debugging]) fi +# Check if expensive debugging is on. +AC_ARG_ENABLE([magma2-debugging-checks], + [AS_HELP_STRING([--enable-magma2-debugging-checks], + [Activate expensive MAGMA2 debugging checks @<:@yes/no@:>@] + )], + [enable_magma2_debugging_checks="$enableval"], + [enable_magma2_debugging_checks="no"] +) +if test "$enable_magma2_debugging_checks" = "yes"; then + AC_DEFINE([MAGMA2_DEBUG_CHECKS],1,[Enable expensive MAGMA2 debugging]) +fi + # Check if cell graph is on. AC_ARG_ENABLE([cell-graph], [AS_HELP_STRING([--enable-cell-graph], @@ -1476,16 +1536,18 @@ if test "x$with_grackle" != "xno"; then AC_PROG_FC AC_FC_LIBRARY_LDFLAGS if test "x$with_grackle" != "xyes" -a "x$with_grackle" != "x"; then - GRACKLE_LIBS="-L$with_grackle/lib -lgrackle" - GRACKLE_INCS="-I$with_grackle/include" + GRACKLE_LIBS="-L$with_grackle/lib -lgrackle -Wl,-rpath=$with_grackle/lib" + GRACKLE_INCS="-I$with_grackle/include -DOMIT_LEGACY_INTERNAL_GRACKLE_FUNC" else GRACKLE_LIBS="-lgrackle" GRACKLE_INCS="" fi + # AC_MSG_NOTICE([Grackle include is $GRACKLE_INCS and grackle lib is $GRACKLE_LIBS]) + # note that if you have multiple grackle libs included in your LD_LIBRARY_PATH, the code will use the first one it finds, not this specific one defined here! have_grackle="yes" - AS_VAR_APPEND([GRACKLE_LIBS], ["$FCLIBS"]) + # AS_VAR_APPEND([GRACKLE_LIBS], ["$FCLIBS"]) AC_CHECK_LIB( [grackle], @@ -1510,6 +1572,7 @@ if test "x$with_grackle" != "xno"; then [AC_MSG_ERROR(Wrong grackle library version. Please consult the documentation for specifics.)], [$GRACKLE_LIBS]) + # AC_MSG_NOTICE([The value of Grackle include is $GRACKLE_INCS from $with_grackle]) fi AC_SUBST([GRACKLE_LIBS]) @@ -2015,8 +2078,7 @@ AC_SUBST([SUNDIALS_INCS]) # As an example for this, see the call to AC_ARG_WITH for cooling. AC_ARG_WITH([subgrid], [AS_HELP_STRING([--with-subgrid=], - [Master switch for subgrid methods. Inexperienced user should - start here. Options are: @<:@none, GEAR, GEAR-G3, AGORA, QLA, QLA-EAGLE, EAGLE, EAGLE-XL, SPIN_JET_EAGLE default: none@:>@] + [Master switch for subgrid methods. Inexperienced user should start here. Options are: @<:@none, GEAR, GEAR-G3, AGORA, QLA, QLA-EAGLE, EAGLE, EAGLE-XL, SPIN_JET_EAGLE, SIMBA, KIARA default: none@:>@] )], [with_subgrid="$withval"], [with_subgrid=none] @@ -2031,6 +2093,7 @@ with_subgrid_pressure_floor=none with_subgrid_stars=none with_subgrid_star_formation=none with_subgrid_feedback=none +with_subgrid_black_holes=none with_subgrid_sink=none with_subgrid_extra_io=none @@ -2063,6 +2126,7 @@ case "$with_subgrid" in with_subgrid_sink=GEAR with_subgrid_extra_io=none enable_fof=no + enable_fof_galaxies=no ;; AGORA) with_subgrid_cooling=grackle_0 @@ -2088,6 +2152,7 @@ case "$with_subgrid" in with_subgrid_sink=none with_subgrid_extra_io=none enable_fof=no + enable_fof_galaxies=no ;; QLA-EAGLE) with_subgrid_cooling=QLA-EAGLE @@ -2100,6 +2165,7 @@ case "$with_subgrid" in with_subgrid_black_holes=none with_subgrid_sink=none enable_fof=no + enable_fof_galaxies=no ;; EAGLE) with_subgrid_cooling=EAGLE @@ -2113,6 +2179,7 @@ case "$with_subgrid" in with_subgrid_sink=none with_subgrid_extra_io=none enable_fof=yes + enable_fof_galaxies=no ;; EAGLE-XL) with_subgrid_cooling=PS2020 @@ -2126,6 +2193,92 @@ case "$with_subgrid" in with_subgrid_sink=none with_subgrid_extra_io=none enable_fof=yes + enable_fof_galaxies=no + ;; + SIMBA) + with_subgrid_cooling=SIMBA + with_subgrid_chemistry=EAGLE + with_subgrid_tracers=EAGLE + with_subgrid_entropy_floor=SIMBA + with_subgrid_stars=EAGLE + with_subgrid_star_formation=SIMBA + with_subgrid_feedback=SIMBA + with_subgrid_black_holes=SIMBA + with_subgrid_sink=none + with_subgrid_extra_io=none + enable_fof=yes + enable_fof_galaxies=yes + ;; + SIMBART) + with_subgrid_cooling=SIMBA + with_subgrid_chemistry=KIARA + with_subgrid_tracers=none + with_subgrid_entropy_floor=SIMBA + with_subgrid_stars=SIMBA + with_subgrid_star_formation=SIMBA + with_subgrid_feedback=KIARA + with_subgrid_black_holes=SIMBA + with_subgrid_sink=none + with_subgrid_extra_io=none + enable_fof=yes + enable_fof_galaxies=yes + ;; + Obsidian) + with_subgrid_cooling=KIARA + with_subgrid_chemistry=KIARA + with_subgrid_tracers=none + with_subgrid_entropy_floor=none + with_subgrid_stars=SIMBA + with_subgrid_star_formation=KIARA + with_subgrid_feedback=KIARA + with_subgrid_black_holes=Obsidian + with_subgrid_sink=none + with_subgrid_extra_io=none + enable_fof=yes + enable_fof_galaxies=yes + ;; + ObsidianNoBH) + with_subgrid_cooling=KIARA + with_subgrid_chemistry=KIARA + with_subgrid_tracers=none + with_subgrid_entropy_floor=SIMBA + with_subgrid_stars=SIMBA + with_subgrid_star_formation=KIARA + with_subgrid_feedback=KIARA + with_subgrid_black_holes=none + with_subgrid_sink=none + with_subgrid_extra_io=none + enable_fof=yes + enable_fof_galaxies=yes + ;; + KIARA) + with_subgrid_cooling=KIARA + with_subgrid_chemistry=KIARA + with_subgrid_tracers=none + with_subgrid_entropy_floor=none + with_subgrid_stars=EAGLE + with_subgrid_star_formation=EAGLE + with_subgrid_feedback=EAGLE + with_subgrid_black_holes=none + #with_subgrid_black_holes=Obsidian + with_subgrid_sink=none + with_subgrid_extra_io=none + enable_fof=yes + enable_fof_galaxies=yes + ;; + KIARART) + with_subgrid_cooling=KIARA + with_subgrid_chemistry=KIARA + with_subgrid_tracers=none + with_subgrid_entropy_floor=SIMBA + with_subgrid_stars=SIMBA + with_subgrid_star_formation=KIARA + with_subgrid_feedback=KIARA + with_subgrid_black_holes=Obsidian + with_subgrid_sink=none + with_subgrid_extra_io=none + enable_fof=yes + enable_fof_galaxies=yes ;; SPIN_JET_EAGLE) with_subgrid_cooling=EAGLE @@ -2170,6 +2323,18 @@ if test "$enable_fof" = "yes"; then AC_DEFINE([WITH_FOF], 1, [Enable FoF]) fi +# Check if we are looking for galaxies. +AC_ARG_ENABLE([fof-galaxies], + [AS_HELP_STRING([--enable-fof-galaxies], + [Activate the friends-of-friends (FoF) code with galaxy finding.], + )], + [enable_fof_galaxies="$enableval"], + [enable_fof_galaxies="no"] +) +if test "$enable_fof_galaxies" = "yes"; then + AC_DEFINE([WITH_FOF_GALAXIES], 1, [Enable FoF Galaxies]) +fi + # Check if stand-alone FoF is on. AC_ARG_ENABLE([stand-alone-fof], [AS_HELP_STRING([--enable-stand-alone-fof], @@ -2616,6 +2781,15 @@ case "$with_chemistry" in AC_DEFINE([CHEMISTRY_EAGLE], [1], [Chemistry taken from the EAGLE model]) with_chemistry_name="EAGLE (9 elements + smoothing)" ;; + SIMBA) + AC_DEFINE([CHEMISTRY_SIMBA], [1], [Chemistry taken from the SIMBA model]) + with_chemistry_name="SIMBA (11 elements + smoothing)" + ;; + KIARA) + AC_DEFINE([CHEMISTRY_KIARA], [1], [Chemistry taken from the KIARA model]) + with_chemistry_name="KIARA (11 elements + smoothing + diffusion)" + ;; + *) AC_MSG_ERROR([Unknown chemistry function: $with_chemistry]) ;; @@ -2674,6 +2848,16 @@ case "$with_cooling" in with_cooling_name="Grackle $primordial_chemistry" with_cooling="grackle" ;; + SIMBA) + AC_DEFINE([COOLING_SIMBA], [1], [Cooling via the grackle library v3+ with extensions]) + AC_DEFINE([COOLING_GRACKLE_MODE], [1], [Cooling via Grackle mode=1]) + with_cooling_name="SIMBA (Grackle 3+ with extensions)" + ;; + KIARA) + AC_DEFINE([COOLING_KIARA], [1], [Cooling via the grackle library v3.2+ incl. dust+H2 formation]) + AC_DEFINE([COOLING_GRACKLE_MODE], [2], [Cooling via Grackle mode=2]) + with_cooling_name="KIARA (Grackle 3.2+ incl. dust+H2 formation)" + ;; QLA) AC_DEFINE([COOLING_QLA], [1], [Cooling following the Quick-Lyman-alpha model]) with_cooling_name="QLA (Ploeckinger+20 tables) with constant primordial Z" @@ -2769,7 +2953,7 @@ esac # Stellar model. AC_ARG_WITH([stars], [AS_HELP_STRING([--with-stars=], - [Stellar model to use @<:@none, basic, EAGLE, GEAR, default: basic@:>@] + [Stellar model to use @<:@none, basic, EAGLE, GEAR, SIMBA, default: basic@:>@] )], [with_stars="$withval"], [with_stars="basic"] @@ -2789,6 +2973,9 @@ case "$with_stars" in ;; GEAR) AC_DEFINE([STARS_GEAR], [1], [GEAR stellar model]) + ;; + SIMBA) + AC_DEFINE([STARS_SIMBA], [1], [SIMBA stellar model]) ;; basic) AC_DEFINE([STARS_BASIC], [1], [Basic stellar model]) @@ -2804,7 +2991,7 @@ esac # Feedback model AC_ARG_WITH([feedback], [AS_HELP_STRING([--with-feedback=], - [Feedback model to use @<:@none, EAGLE, EAGLE-thermal, EAGLE-kinetic, GEAR, AGORA default: none@:>@] + [Feedback model to use @<:@none, EAGLE, EAGLE-thermal, EAGLE-kinetic, GEAR, SIMBA, KIARA, AGORA default: none@:>@] )], [with_feedback="$withval"], [with_feedback="none"] @@ -2836,6 +3023,14 @@ case "$with_feedback" in AC_DEFINE([FEEDBACK_GEAR], [1], [GEAR stellar feedback and evolution model]) with_feedback_name="GEAR" ;; + SIMBA) + AC_DEFINE([FEEDBACK_SIMBA], [1], [SIMBA stellar feedback and evolution model]) + with_feedback_name="SIMBA decoupled winds feedback" + ;; + KIARA) + AC_DEFINE([FEEDBACK_KIARA], [1], [KIARA stellar feedback and evolution model]) + with_feedback_name="KIARA decoupled winds feedback and Chem5 enrichment" + ;; AGORA) AC_DEFINE([FEEDBACK_AGORA], [1], [AGORA stellar feedback and evolution model]) with_feedback_name="AGORA" @@ -2893,6 +3088,13 @@ case "$with_black_holes" in EAGLE) AC_DEFINE([BLACK_HOLES_EAGLE], [1], [EAGLE black hole model]) ;; + SIMBA) + AC_DEFINE([BLACK_HOLES_SIMBA], [1], [SIMBA black hole model]) + ;; + Obsidian) + AC_DEFINE([BLACK_HOLES_OBSIDIAN], [1], [Obsidian black hole model]) + with_black_holes="Three regime model (Rennehan+24)" + ;; SPIN_JET) AC_DEFINE([BLACK_HOLES_SPIN_JET], [1], [Spin and jet black hole model]) with_black_holes="SPIN_JETS (Husko+22)" @@ -3014,7 +3216,7 @@ esac # Entropy floor AC_ARG_WITH([entropy-floor], [AS_HELP_STRING([--with-entropy-floor=], - [entropy floor @<:@none, QLA, EAGLE, default: none@:>@] + [entropy floor @<:@none, QLA, EAGLE, SIMBA, default: none@:>@] )], [with_entropy_floor="$withval"], [with_entropy_floor="none"] @@ -3037,6 +3239,9 @@ case "$with_entropy_floor" in EAGLE) AC_DEFINE([ENTROPY_FLOOR_EAGLE], [1], [EAGLE entropy floor]) ;; + SIMBA) + AC_DEFINE([ENTROPY_FLOOR_SIMBA], [1], [SIMBA entropy floor]) + ;; *) AC_MSG_ERROR([Unknown entropy floor model]) ;; @@ -3074,7 +3279,7 @@ esac # Star formation AC_ARG_WITH([star-formation], [AS_HELP_STRING([--with-star-formation=], - [star formation @<:@none, QLA, EAGLE, GEAR, default: none@:>@] + [star formation @<:@none, QLA, EAGLE, GEAR, SIMBA, KIARA, default: none@:>@] )], [with_star_formation="$withval"], [with_star_formation="none"] @@ -3092,7 +3297,7 @@ case "$with_star_formation" in AC_DEFINE([STAR_FORMATION_NONE], [1], [No star formation]) ;; QLA) - AC_DEFINE([STAR_FORMATION_QLA], [1], [Quick Lyman-alpha star formation model)]) + AC_DEFINE([STAR_FORMATION_QLA], [1], [Quick Lyman-alpha star formation model]) ;; EAGLE) AC_DEFINE([STAR_FORMATION_EAGLE], [1], [EAGLE star formation model (Schaye and Dalla Vecchia (2008))]) @@ -3100,6 +3305,12 @@ case "$with_star_formation" in GEAR) AC_DEFINE([STAR_FORMATION_GEAR], [1], [GEAR star formation model (Revaz and Jablonka (2018))]) ;; + SIMBA) + AC_DEFINE([STAR_FORMATION_SIMBA], [1], [SIMBA star formation model]) + ;; + KIARA) + AC_DEFINE([STAR_FORMATION_KIARA], [1], [KIARA star formation model]) + ;; *) AC_MSG_ERROR([Unknown star formation model]) ;; @@ -3201,6 +3412,10 @@ case "$with_rt" in AC_MSG_ERROR([GEAR-RT: You need the grackle library for GEAR-RT. (--with-grackle=PATH)]) fi + if test "$with_cooling" = "none"; then + AC_MSG_ERROR([GEAR-RT: You need to select a cooling module (--with-cooling=grackle_1)]) + fi + ;; debug) AC_DEFINE([RT_DEBUG], [1], [debugging scheme]) @@ -3263,12 +3478,24 @@ AM_CONDITIONAL([HAVEEAGLEKINETICFEEDBACK], [test "$with_feedback" = "EAGLE-kinet # check if using grackle cooling AM_CONDITIONAL([HAVEGRACKLECOOLING], [test "$with_cooling" = "grackle"]) +# check if using grackle cooling +AM_CONDITIONAL([HAVESIMBACOOLING], [test "$with_cooling" = "SIMBA"]) + +# check if using grackle cooling +AM_CONDITIONAL([HAVEKIARACOOLING], [test "$with_cooling" = "KIARA"]) + # check if using EAGLE floor AM_CONDITIONAL([HAVEEAGLEFLOOR], [test "$with_entropy_floor" = "EAGLE"]) # check if using gear feedback AM_CONDITIONAL([HAVEGEARFEEDBACK], [test "$with_feedback" = "GEAR"]) +# check if using SIMBA feedback +AM_CONDITIONAL([HAVESIMBAFEEDBACK], [test "$with_feedback" = "SIMBA"]) + +# check if using KIARA feedback +AM_CONDITIONAL([HAVEKIARAFEEDBACK], [test "$with_feedback" = "KIARA"]) + # Check for GEAR and GEAR_MECHANICAL feedback flags to set the common files # variable AS_IF([test "$with_feedback" = "GEAR"], @@ -3294,18 +3521,33 @@ AM_CONDITIONAL([HAVE_CHEMISTRY_GEAR], [test "$with_chemistry" = "GEAR" || test " # check if using AGORA chemistry AM_CONDITIONAL([HAVE_CHEMISTRY_AGORA], [test "$with_chemistry" = "AGORA" || test "$with_chemistry" = "GEAR_DIFFUSION"]) +# check if using SIMBA chemistry +AM_CONDITIONAL([HAVE_CHEMISTRY_SIMBA], [test "$with_chemistry" = "SIMBA"]) + +# check if using KIARA chemistry +AM_CONDITIONAL([HAVE_CHEMISTRY_KIARA], [test "$with_chemistry" = "KIARA"]) + # check if using default stars AM_CONDITIONAL([HAVE_STARS_BASIC], [test "$with_stars" = "basic"]) # check if using GEAR stars AM_CONDITIONAL([HAVE_STARS_GEAR], [test "$with_stars" = "GEAR"]) +# check if using GEAR stars +AM_CONDITIONAL([HAVE_STARS_SIMBA], [test "$with_stars" = "SIMBA"]) + # check if using default star formation AM_CONDITIONAL([HAVE_STAR_FORMATION_DEFAULT], [test "$with_star_formation" = "none"]) # check if using GEAR star formation AM_CONDITIONAL([HAVE_STAR_FORMATION_GEAR], [test "$with_star_formation" = "GEAR"]) +# check if using SIMBA star formation +AM_CONDITIONAL([HAVE_STAR_FORMATION_SIMBA], [test "$with_star_formation" = "SIMBA"]) + +# check if using KIARA star formation +AM_CONDITIONAL([HAVE_STAR_FORMATION_KIARA], [test "$with_star_formation" = "KIARA"]) + # check if using multi softening gravity AM_CONDITIONAL([HAVE_GRAVITY_MULTISOFTENING], [test "$with_gravity" = "with-multi-softening"]) diff --git a/src/Makefile.am b/src/Makefile.am index 3ce99f224b..b3b3328266 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -170,6 +170,18 @@ SPHM1RT_RT_SOURCES += rt/SPHM1RT/rt_rate_equations.c SPHM1RT_RT_SOURCES += rt/SPHM1RT/rt_cooling.c endif +# source files for KIARA cooling +KIARA_COOLING_SOURCES = +if HAVEKIARACOOLING +KIARA_COOLING_SOURCES += cooling/KIARA/cooling.c +endif + +# source files for KIARA feedback +KIARA_FEEDBACK_SOURCES = +if HAVEKIARAFEEDBACK +KIARA_FEEDBACK_SOURCES += feedback/KIARA/feedback.c +endif + # Common source files AM_SOURCES = space.c space_rebuild.c space_regrid.c space_unique_id.c AM_SOURCES += space_sort.c space_split.c space_extras.c space_first_init.c space_init.c @@ -219,6 +231,7 @@ AM_SOURCES += $(AGORA_FEEDBACK_SOURCES) AM_SOURCES += $(PS2020_COOLING_SOURCES) AM_SOURCES += $(SPHM1RT_RT_SOURCES) AM_SOURCES += $(GEAR_RT_SOURCES) +AM_SOURCES += $(KIARA_COOLING_SOURCES) # $(KIARA_FEEDBACK_SOURCES) AM_SOURCES += swift_lustre_api.c # Include files for distribution, not installation. @@ -423,6 +436,8 @@ nobase_noinst_HEADERS += cooling/PS2020/cooling.h cooling/PS2020/cooling_struct. nobase_noinst_HEADERS += cooling/PS2020/cooling_io.h cooling/PS2020/interpolate.h cooling/PS2020/cooling_rates.h nobase_noinst_HEADERS += cooling/PS2020/cooling_tables.h cooling/PS2020/cooling_subgrid.h nobase_noinst_HEADERS += cooling/PS2020/cooling_properties.h cooling/PS2020/cooling_debug.h +nobase_noinst_HEADERS += cooling/KIARA/cooling.h cooling/KIARA/cooling_struct.h +nobase_noinst_HEADERS += cooling/KIARA/cooling_io.h cooling/KIARA/cooling_properties.h cooling/KIARA/cooling_debug.h nobase_noinst_HEADERS += chemistry/none/chemistry.h nobase_noinst_HEADERS += chemistry/none/chemistry_additions.h nobase_noinst_HEADERS += chemistry/none/chemistry_io.h @@ -486,6 +501,9 @@ nobase_noinst_HEADERS += feedback/GEAR/feedback_properties.h feedback/GEAR/feedb nobase_noinst_HEADERS += feedback/GEAR/initial_mass_function.h feedback/GEAR/supernovae_ia.h feedback/GEAR/supernovae_ii.h nobase_noinst_HEADERS += feedback/GEAR/lifetime.h feedback/GEAR/hdf5_functions.h feedback/GEAR/interpolation.h nobase_noinst_HEADERS += feedback/GEAR/feedback_debug.h +#nobase_noinst_HEADERS += feedback/KIARA/feedback.h feedback/KIARA/feedback_struct.h +#nobase_noinst_HEADERS += feedback/KIARA/feedback_properties.h feedback/KIARA/feedback_iact.h +#nobase_noinst_HEADERS += feedback/KIARA/feedback_debug.h nobase_noinst_HEADERS += black_holes/Default/black_holes.h black_holes/Default/black_holes_io.h nobase_noinst_HEADERS += black_holes/Default/black_holes_part.h black_holes/Default/black_holes_iact.h nobase_noinst_HEADERS += black_holes/Default/black_holes_properties.h diff --git a/src/chemistry.h b/src/chemistry.h index 388346f900..e418acd422 100644 --- a/src/chemistry.h +++ b/src/chemistry.h @@ -49,6 +49,9 @@ #elif defined(CHEMISTRY_EAGLE) #include "./chemistry/EAGLE/chemistry.h" #include "./chemistry/EAGLE/chemistry_iact.h" +#elif defined(CHEMISTRY_KIARA) +#include "./chemistry/KIARA/chemistry.h" +#include "./chemistry/KIARA/chemistry_iact.h" #else #error "Invalid choice of chemistry function." #endif diff --git a/src/chemistry/KIARA/chemistry.h b/src/chemistry/KIARA/chemistry.h new file mode 100644 index 0000000000..f3085e43ad --- /dev/null +++ b/src/chemistry/KIARA/chemistry.h @@ -0,0 +1,1569 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 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 . + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_KIARA_H +#define SWIFT_CHEMISTRY_KIARA_H + +/** + * @file src/chemistry/KIARA/chemistry.h + * @brief Empty infrastructure for the cases without chemistry function + */ + +/* Some standard headers. */ +#include +#include +#include + +/* Local includes. */ +#include "chemistry_struct.h" +#include "error.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" +#include "timestep_sync_part.h" + +/** + * @brief Initializes summed particle quantities for the firehose wind model + * + * This is called from chemistry_init_part. + * + * @param p The particle to act upon + * @param cd #chemistry_global_data containing chemistry informations. + */ +__attribute__((always_inline)) INLINE static void +firehose_init_ambient_quantities(struct part* restrict p, + const struct chemistry_global_data* cd) { + + struct chemistry_part_data* cpd = &p->chemistry_data; + + cpd->w_ambient = 0.f; + cpd->rho_ambient = 0.f; + cpd->u_ambient = 0.f; +} + +__attribute__((always_inline)) INLINE static void +logger_windprops_printprops( + struct part *pi, + const struct cosmology *cosmo, const struct chemistry_global_data* cd) { + + /* Ignore COUPLED particles */ + if (!pi->decoupled) return; + +#ifdef CHEMISTRY_OUTPUT_FIREHOSE_LOG + /* Print wind properties */ + const float length_convert = cosmo->a * cd->length_to_kpc; + const float rho_convert = cosmo->a3_inv * cd->rho_to_n_cgs; + const float u_convert = + cosmo->a_factor_internal_energy / cd->temp_to_u_factor; + + message("FIREHOSE: z=%.3f id=%lld Mgal=%g h=%g T=%g rho=%g Rs=%g Z=%g tdel=%g Ndec=%d rhoamb=%g Tamb=%g tcmix=%g\n", + cosmo->z, + pi->id, + (pi->galaxy_data.gas_mass + pi->galaxy_data.stellar_mass) * + cd->mass_to_solar_mass, + pi->h * cosmo->a * cd->length_to_kpc, + hydro_get_drifted_comoving_internal_energy(pi) * u_convert, + pi->rho * rho_convert, + pi->chemistry_data.radius_stream * length_convert, + pi->chemistry_data.metal_mass_fraction_total, + pi->chemistry_data.decoupling_delay_time * cd->time_to_Myr, + pi->chemistry_data.number_of_times_decoupled, + pi->chemistry_data.rho_ambient * cd->rho_to_n_cgs * cosmo->a3_inv, + pi->chemistry_data.u_ambient * + cosmo->a_factor_internal_energy / cd->temp_to_u_factor, + pi->cooling_data.mixing_layer_cool_time); +#endif + + return; +} + +/** + * @brief Finishes up ambient quantity calculation for the firehose wind model + * + * This is called from chemistry_end_density + * + * @param p The particle to act upon + * @param cd #chemistry_global_data containing chemistry informations. + */ +__attribute__((always_inline)) INLINE static void +firehose_end_ambient_quantities(struct part* restrict p, + struct xpart* restrict xp, + const struct chemistry_global_data* cd, + const struct cosmology* cosmo) { + + const float u_floor = cd->firehose_u_floor / cosmo->a_factor_internal_energy; + const float rho_max = + cd->firehose_ambient_rho_max * cosmo->a * cosmo->a * cosmo->a; + + /* No ambient properties for non-wind particles */ + if (p->decoupled) { + + /* Some smoothing length multiples. */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + +#ifdef FIREHOSE_DEBUG_CHECKS + message("FIREHOSE_prelim: id=%lld rhoamb=%g wamb=%g uamb=%g" + "h=%g h_inv=%g h_inv_dim=%g", + p->id, + p->chemistry_data.rho_ambient, + p->chemistry_data.w_ambient, + p->chemistry_data.u_ambient, + h, + h_inv, + h_inv_dim); +#endif + + /* h_inv_dim can sometimes lead to rho_ambient -> 0 after normalization */ + p->chemistry_data.rho_ambient *= h_inv_dim; + + if (p->chemistry_data.rho_ambient > 0.f) { + p->chemistry_data.u_ambient *= h_inv_dim / p->chemistry_data.rho_ambient; + } + else { + p->chemistry_data.rho_ambient = hydro_get_comoving_density(p); + p->chemistry_data.u_ambient = u_floor; + } + +#ifdef FIREHOSE_DEBUG_CHECKS + message("FIREHOSE_lim: id=%lld rhoamb=%g wamb=%g uamb=%g ufloor=%g\n", + p->id, + p->chemistry_data.rho_ambient, + p->chemistry_data.w_ambient, + p->chemistry_data.u_ambient, + cd->firehose_u_floor / cd->temp_to_u_factor); +#endif + } + else { + /* Set them to reasonable values for non-wind, just in case */ + p->chemistry_data.rho_ambient = hydro_get_comoving_density(p); + p->chemistry_data.u_ambient = hydro_get_drifted_comoving_internal_energy(p); + } + + /* Limit ambient density to the user settings */ + p->chemistry_data.rho_ambient = min(p->chemistry_data.rho_ambient, rho_max); + p->chemistry_data.u_ambient = max(p->chemistry_data.u_ambient, u_floor); +#ifdef FIREHOSE_DEBUG_CHECKS + if (p->decoupled) { + message("FIREHOSE_AMB: z=%g id=%lld nH=%g nHamb=%g u=%g uamb=%g T=%g " + "Tamb=%g tcool=%g", + cosmo->z, + p->id, + p->rho * cd->rho_to_n_cgs * cosmo->a3_inv, + p->chemistry_data.rho_ambient * cd->rho_to_n_cgs * cosmo->a3_inv, + hydro_get_drifted_comoving_internal_energy(p), + p->chemistry_data.u_ambient, + hydro_get_drifted_comoving_internal_energy(p) * + cosmo->a_factor_internal_energy / cd->temp_to_u_factor, + p->chemistry_data.u_ambient * + cosmo->a_factor_internal_energy / cd->temp_to_u_factor, + p->cooling_data.mixing_layer_cool_time); + } +#endif + +#ifdef FIREHOSE_DEBUG_CHECKS + logger_windprops_printprops(p, cosmo, cd); +#endif +} + + +/** + * @brief Return a string containing the name of a given #chemistry_element. + */ +__attribute__((always_inline)) INLINE static const char* +chemistry_get_element_name(enum chemistry_element elem) { + + static const char* chemistry_element_names[chemistry_element_count] = { + "Hydrogen", "Helium", "Carbon", "Nitrogen", "Oxygen", + "Neon", "Magnesium", "Silicon", "Sulfur", "Calcium", "Iron"}; + + return chemistry_element_names[elem]; +} + +/** + * @brief Prepares a particle for the smooth metal calculation. + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the various smooth metallicity tasks + * + * @param p The particle to act upon + * @param cd #chemistry_global_data containing chemistry informations. + */ +__attribute__((always_inline)) INLINE static void chemistry_init_part( + struct part* restrict p, const struct chemistry_global_data* cd) { + + struct chemistry_part_data* cpd = &p->chemistry_data; + + /* Reset the shear tensor */ + for (int i = 0; i < 3; i++) { + cpd->shear_tensor[i][0] = 0.f; + cpd->shear_tensor[i][1] = 0.f; + cpd->shear_tensor[i][2] = 0.f; + + /* Accumulated velocity from the firehose model */ + cpd->dv[i] = 0.f; + } + + /* Reset the diffusion. */ + cpd->diffusion_coefficient = 0.f; + + /* Reset the changes for the accumulated properties */ + cpd->dZ_dt_total = 0.f; + cpd->du = 0.; + cpd->dm = 0.f; + cpd->dm_dust = 0.f; + for (int elem = 0; elem < chemistry_element_count; ++elem) { + cpd->dZ_dt[elem] = 0.f; + cpd->dm_Z[elem] = 0.f; + cpd->dm_dust_Z[elem] = 0.f; + } + +#if COOLING_GRACKLE_MODE >= 2 + cpd->local_sfr_density = 0.f; +#endif + + if (cd->use_firehose_wind_model) { + firehose_init_ambient_quantities(p, cd); + } +} + +/** + * @brief Finishes the smooth metal calculation. + * + * Multiplies the metallicity and number of neighbours by the + * appropiate constants and add the self-contribution term. + * + * This function requires the #hydro_end_density to have been called. + * + * @param p The particle to act upon. + * @param cd #chemistry_global_data containing chemistry informations. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void chemistry_end_density( + struct part* restrict p, + struct xpart* restrict xp, + const struct chemistry_global_data* cd, + const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + + struct chemistry_part_data* cpd = &p->chemistry_data; + + /* If diffusion is on, finish up shear tensor & particle's diffusion coeff */ + if (cd->diffusion_flag == 1 && cd->C_Smagorinsky > 0.f) { + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + const float rho = hydro_get_comoving_density(p); + + /* convert the shear factor into physical */ + const float factor_shear = h_inv_dim_plus_one * cosmo->a2_inv / rho; + for (int k = 0; k < 3; k++) { + cpd->shear_tensor[k][0] *= factor_shear; + cpd->shear_tensor[k][1] *= factor_shear; + cpd->shear_tensor[k][2] *= factor_shear; + } + + /* Compute the trace over 3 and add the hubble flow. */ + float trace_3 = 0.f; + for (int i = 0; i < 3; i++) { + cpd->shear_tensor[i][i] += cosmo->H; + trace_3 += cpd->shear_tensor[i][i]; + } + trace_3 /= 3.f; + + float shear_tensor[3][3] = {{0.f}}; + for (int i = 0; i < 3; i++) { + /* Make the tensor symmetric. */ + float avg = 0.5f * (cpd->shear_tensor[i][0] + cpd->shear_tensor[0][i]); + shear_tensor[i][0] = avg; + shear_tensor[0][i] = avg; + + avg = 0.5f * (cpd->shear_tensor[i][1] + cpd->shear_tensor[1][i]); + shear_tensor[i][1] = avg; + shear_tensor[1][i] = avg; + + avg = 0.5f * (cpd->shear_tensor[i][2] + cpd->shear_tensor[2][i]); + shear_tensor[i][2] = avg; + shear_tensor[2][i] = avg; + + /* Remove the trace. */ + shear_tensor[i][i] -= trace_3; + } + + /* Compute the norm. */ + float velocity_gradient_norm = 0.f; + for (int i = 0; i < 3; i++) { + velocity_gradient_norm += shear_tensor[i][0] * shear_tensor[i][0]; + velocity_gradient_norm += shear_tensor[i][1] * shear_tensor[i][1]; + velocity_gradient_norm += shear_tensor[i][2] * shear_tensor[i][2]; + + /* Copy the final values into the particle quantity */ + cpd->shear_tensor[i][0] = shear_tensor[i][0]; + cpd->shear_tensor[i][1] = shear_tensor[i][1]; + cpd->shear_tensor[i][2] = shear_tensor[i][2]; + } + + velocity_gradient_norm = sqrtf(velocity_gradient_norm); + + /* Never set D for wind, or ISM particles */ + if (!(p->decoupled) && + !(p->cooling_data.subgrid_temp > 0.f)) { + + /* Rennehan: Limit to maximum resolvable velocity scale */ + const float v_phys = sqrtf(xp->v_full[0] * xp->v_full[0] + + xp->v_full[1] * xp->v_full[1] + + xp->v_full[2] * xp->v_full[2]) * cosmo->a_inv; + const float h_phys = cosmo->a * p->h * kernel_gamma; + const float vel_norm_phys_max = 0.5f * v_phys / h_phys; + if (velocity_gradient_norm > vel_norm_phys_max) { + velocity_gradient_norm = vel_norm_phys_max; + } + + /* Compute the diffusion coefficient in physical coordinates. + * The norm is already in physical coordinates. + * kernel_gamma is necessary (see Rennehan 2021) + */ + const float rho_phys = hydro_get_physical_density(p, cosmo); + const float smag_length_scale = cd->C_Smagorinsky * h_phys; + const float D_phys = rho_phys * smag_length_scale * smag_length_scale * + velocity_gradient_norm; + + cpd->diffusion_coefficient = D_phys; + } + } /* end Smagorinsky diffusion */ + +#if COOLING_GRACKLE_MODE >= 2 + /* Finish SFR density calculation */ + cpd->local_sfr_density *= h_inv_dim; +#endif + + if (cd->use_firehose_wind_model) { + firehose_end_ambient_quantities(p, xp, cd, cosmo); + } + +} + +/** + * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cd #chemistry_global_data containing chemistry informations. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void +chemistry_part_has_no_neighbours(struct part* restrict p, + struct xpart* restrict xp, + const struct chemistry_global_data* cd, + const struct cosmology* cosmo) { + + /* Just make all the smoothed fields default to the un-smoothed values */ + struct chemistry_part_data* cpd = &p->chemistry_data; + /* Reset the shear tensor */ + for (int i = 0; i < 3; i++) { + cpd->shear_tensor[i][0] = 0.f; + cpd->shear_tensor[i][1] = 0.f; + cpd->shear_tensor[i][2] = 0.f; + } + + /* Reset the diffusion. */ + cpd->diffusion_coefficient = 0.f; + + /* Reset the change in metallicity */ + cpd->dZ_dt_total = 0.f; + for (int elem = 0; elem < chemistry_element_count; ++elem) { + cpd->dZ_dt[elem] = 0.f; + } + +#if COOLING_GRACKLE_MODE >= 2 + /* If there is no nearby SF, set to zero */ + cpd->local_sfr_density = 0.f; +#endif +} + +/** + * @brief Sets the chemistry properties of the (x-)particles to a valid start + * state. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param data The global chemistry information. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE static void chemistry_first_init_part( + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct chemistry_global_data* data, struct part* restrict p, + struct xpart* restrict xp) { + + /* Initialize mass fractions for total metals and each metal individually */ + if (data->initial_metal_mass_fraction_total != -1) { + p->chemistry_data.metal_mass_fraction_total = + data->initial_metal_mass_fraction_total; + + for (int elem = 0; elem < chemistry_element_count; ++elem) { + p->chemistry_data.metal_mass_fraction[elem] = + data->initial_metal_mass_fraction[elem]; + } + } + chemistry_init_part(p, data); + + if (data->use_firehose_wind_model) { + firehose_init_ambient_quantities(p, data); + } +} + +/** + * @brief Sets the chemistry properties of the sparticles to a valid start + * state. + * + * @param data The global chemistry information. + * @param sp Pointer to the sparticle data. + */ +__attribute__((always_inline)) INLINE static void chemistry_first_init_spart( + const struct chemistry_global_data* data, struct spart* restrict sp) { + + /* Initialize mass fractions for total metals and each metal individually */ + if (data->initial_metal_mass_fraction_total != -1) { + sp->chemistry_data.metal_mass_fraction_total = + data->initial_metal_mass_fraction_total; + + for (int elem = 0; elem < chemistry_element_count; ++elem) + sp->chemistry_data.metal_mass_fraction[elem] = + data->initial_metal_mass_fraction[elem]; + } +} + +/** + * @brief Sets the chemistry properties of the sink particles to a valid start + * state. + * + * @param data The global chemistry information. + * @param sink Pointer to the sink particle data. + * Required by space_first_init.c + */ +__attribute__((always_inline)) INLINE static void chemistry_first_init_sink( + const struct chemistry_global_data* data, struct sink* restrict sink) {} + + +/** + * @brief Initialises the chemistry properties. + * + * @param parameter_file The parsed parameter file. + * @param us The current internal system of units. + * @param phys_const The physical constants in internal units. + * @param data The properties to initialise. + */ +static INLINE void chemistry_init_backend(struct swift_params* parameter_file, + const struct unit_system* us, + const struct phys_const* phys_const, + struct chemistry_global_data* data) { + + /* Set some useful unit conversions */ + const double Msun_cgs = phys_const->const_solar_mass * + units_cgs_conversion_factor(us, UNIT_CONV_MASS); + const double unit_mass_cgs = units_cgs_conversion_factor(us, UNIT_CONV_MASS); + data->mass_to_solar_mass = unit_mass_cgs / Msun_cgs; + data->temp_to_u_factor = + phys_const->const_boltzmann_k / + (hydro_gamma_minus_one * phys_const->const_proton_mass * + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE)); + data->T_to_internal = + 1. / units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + const double X_H = 0.752; + data->rho_to_n_cgs = + (X_H / phys_const->const_proton_mass) * + units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); + data->kms_to_internal = + 1.0e5 / units_cgs_conversion_factor(us, UNIT_CONV_SPEED); + data->time_to_Myr = units_cgs_conversion_factor(us, UNIT_CONV_TIME) / + (1.e6 * 365.25 * 24. * 60. * 60.); + data->length_to_kpc = + units_cgs_conversion_factor(us, UNIT_CONV_LENGTH) / 3.08567758e21; + + /* Is metal diffusion turned on? */ + data->diffusion_flag = parser_get_param_int(parameter_file, + "KIARAChemistry:diffusion_on"); + + /* Read the diffusion coefficient */ + data->C_Smagorinsky = + parser_get_opt_param_float(parameter_file, + "KIARAChemistry:diffusion_coefficient", + 0.23f); + + /* Time-step restriction to <= 0.15*rho*h^2 / D from + Parshikov & Medin 2002 equation 41 */ + data->diffusion_beta = + parser_get_opt_param_float(parameter_file, + "KIARAChemistry:diffusion_beta", + 0.1f); + if (data->diffusion_beta < 0.f || data->diffusion_beta > 0.1f) { + error("diffusion_beta must be >= 0 and <= 0.1"); + } + + data->time_step_min = + parser_get_opt_param_float(parameter_file, + "KIARAChemistry:minimum_timestep_Myr", + 0.1f); + data->time_step_min /= data->time_to_Myr; + if (data->time_step_min < 0.f) { + error("time_step_min must be > 0"); + } + + data->max_fractional_Z_transfer = + parser_get_opt_param_float(parameter_file, + "KIARAChemistry:max_fractional_Z_transfer", + 0.25f); + if (data->max_fractional_Z_transfer < 0.f || + data->max_fractional_Z_transfer > 1.f) { + error("diffusion_beta must be >= 0 and <= 1"); + } + + /* Are we using the firehose wind model? */ + data->use_firehose_wind_model = + parser_get_opt_param_int(parameter_file, + "KIARAChemistry:use_firehose_wind_model", + 0); + + if (data->use_firehose_wind_model) { + /* Firehose model parameters */ + data->firehose_ambient_rho_max = + parser_get_opt_param_float(parameter_file, + "KIARAChemistry:firehose_nh_ambient_max_cgs", + 0.1f); + data->firehose_ambient_rho_max /= data->rho_to_n_cgs; + + data->firehose_u_floor = + parser_get_opt_param_float(parameter_file, + "KIARAChemistry:firehose_temp_floor", + 1.e4f); + data->firehose_u_floor *= data->temp_to_u_factor * data->T_to_internal; + + /* Firehose recoupling parameters */ + data->firehose_recoupling_mach = + parser_get_opt_param_float(parameter_file, + "KIARAChemistry:firehose_recoupling_mach", + 0.5f); + + data->firehose_recoupling_u_factor = + parser_get_opt_param_float(parameter_file, + "KIARAChemistry:firehose_recoupling_u_factor", + 0.5f); + + data->firehose_recoupling_fmix = + parser_get_opt_param_float(parameter_file, + "KIARAChemistry:firehose_recoupling_fmix", + 0.9f); + + data->firehose_max_velocity = + parser_get_opt_param_float(parameter_file, + "KIARAChemistry:firehose_max_velocity", + 2000.f); + data->firehose_max_velocity *= data->kms_to_internal; + + data->firehose_max_fmix_per_step = + parser_get_opt_param_float(parameter_file, + "KIARAChemistry:firehose_max_fmix_per_step", + 0.1f); + } + + /* Read the total metallicity */ + data->initial_metal_mass_fraction_total = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:init_abundance_metal", -1); + + if (data->initial_metal_mass_fraction_total != -1) { + /* Read the individual mass fractions */ + for (int elem = 0; elem < chemistry_element_count; ++elem) { + char buffer[50]; + sprintf(buffer, "KIARAChemistry:init_abundance_%s", + chemistry_get_element_name((enum chemistry_element)elem)); + + data->initial_metal_mass_fraction[elem] = + parser_get_param_float(parameter_file, buffer); + } + + /* Let's check that things make sense (broadly) */ + + /* H + He + Z should be ~1 */ + float total_frac = data->initial_metal_mass_fraction[chemistry_element_H] + + data->initial_metal_mass_fraction[chemistry_element_He] + + data->initial_metal_mass_fraction_total; + + if (total_frac < 0.98 || total_frac > 1.02) + error("The abundances provided seem odd! H + He + Z = %f =/= 1.", + total_frac); + + /* Sum of metal elements should be <= Z */ + total_frac = 0.f; + for (int elem = 0; elem < chemistry_element_count; ++elem) { + if (elem != chemistry_element_H && elem != chemistry_element_He) { + total_frac += data->initial_metal_mass_fraction[elem]; + } + } + + if (!data->diffusion_flag) { + if (total_frac > 1.02 * data->initial_metal_mass_fraction_total) { + error( + "The abundances provided seem odd! \\sum metal elements (%f) > Z " + "(%f)", + total_frac, data->initial_metal_mass_fraction_total); + } + } + else { + /* If diffusion is on, need a metallicity floor so reset Z total */ + if (total_frac > 1.02 * data->initial_metal_mass_fraction_total) { + warning("Resetting total Z to the sum of all available metals."); + data->initial_metal_mass_fraction_total = total_frac; + + /* H + He + Z should be ~1 */ + float total_frac_check = + data->initial_metal_mass_fraction[chemistry_element_H] + + data->initial_metal_mass_fraction[chemistry_element_He] + + data->initial_metal_mass_fraction_total; + + if (total_frac_check < 0.98 || total_frac_check > 1.02) { + error("After resetting, the abundances provided seem odd! " + "H + He + Z = %f =/= 1.", + total_frac_check); + } + } + } + + /* Sum of all elements should be <= 1 */ + total_frac = 0.f; + for (int elem = 0; elem < chemistry_element_count; ++elem) { + total_frac += data->initial_metal_mass_fraction[elem]; + } + + if (total_frac > 1.02) { + error("The abundances provided seem odd! \\sum elements (%f) > 1", + total_frac); + } + } + +} + +/** + * @brief Prints the properties of the chemistry model to stdout. + * + * @brief The #chemistry_global_data containing information about the current + * model. + */ +static INLINE void chemistry_print_backend( + const struct chemistry_global_data* data) { + + if (data->use_firehose_wind_model) { + if (data->diffusion_flag) { + message("Chemistry model is 'KIARA' tracking %d elements with the " + "firehose wind model and metal diffusion.", + chemistry_element_count); + } + else { + message("Chemistry model is 'KIARA' tracking %d elements with " + "the firehose wind model.", + chemistry_element_count); + } + } + else { + if (data->diffusion_flag) { + message("Chemistry model is 'KIARA' tracking %d elements with " + " metal diffusion on.", + chemistry_element_count); + } + else { + message("Chemistry model is 'KIARA' tracking %d elements.", + chemistry_element_count); + } + } +} + +/** + * @brief Check recoupling criterion for firehose stream particle . + * Returns negative value if it should recouple. + * Actual recoupling is done in feedback.h. + * + * @param pi Wind particle (not updated). + * @param Mach Stream Mach number vs ambient + * @param r_stream Current radius of stream + * @param cd #chemistry_global_data containing chemistry information. + * + */ +__attribute__((always_inline)) INLINE static float +firehose_recoupling_criterion(struct part *p, + const float Mach, + const float r_stream, + const struct chemistry_global_data* cd) { + + if (!cd->use_firehose_wind_model) return 0.f; + + float rs = r_stream; + const double u = hydro_get_drifted_comoving_internal_energy(p); + const double u_max = max(u, p->chemistry_data.u_ambient); + const double u_diff = fabs(u - p->chemistry_data.u_ambient) / u_max; + if (Mach < cd->firehose_recoupling_mach && + u_diff < cd->firehose_recoupling_u_factor) rs = -1.f; + + const float exchanged_mass_frac = + p->chemistry_data.exchanged_mass / hydro_get_mass(p); + + if (exchanged_mass_frac > cd->firehose_recoupling_fmix) rs = -1.f; + if (r_stream == 0.f) rs = -1.f; + + return rs; +} + +/** + * @brief Finishes the gradient calculation. + * + * Nothing to do here. + * + * @param p The particle to act upon. + * @param cd The global properties of the chemistry scheme. + */ +__attribute__((always_inline)) INLINE static void chemistry_end_gradient( + struct part *p, const struct chemistry_global_data *cd) {} + +/** + * @brief Prepare a particle for the force calculation. + * + * Nothing to do here. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. + * @param dt_alpha The time-step used to evolve non-cosmological quantities such + * as the artificial viscosity. + * @param dt_therm The time-step used to evolve hydrodynamical quantities. + * @param cd The global properties of the chemistry scheme. + */ +__attribute__((always_inline)) INLINE static void chemistry_prepare_force( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const float dt_alpha, const float dt_therm, + const struct chemistry_global_data *cd) {} + +/** + * @brief Updates to the chemistry data after the hydro force loop. + * + * Finish off the diffusion by actually exchanging the metals + * + * @param p The particle to act upon. + * @param cosmo The current cosmological model. + * @param with_cosmology Are we running with the cosmology? + * @param time Current time of the simulation. + * @param dt Time step (in physical units). + */ +__attribute__((always_inline)) INLINE static void chemistry_end_force( + struct part* restrict p, struct xpart *xp, + const struct cosmology* cosmo, + const int with_cosmology, const double time, const double dt, + const struct chemistry_global_data* cd) { + + if (dt == 0.) return; + + struct chemistry_part_data* ch = &p->chemistry_data; + + const float h_inv = 1.f / p->h; + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + /* Missing factors in iact. */ + const float factor = h_inv_dim * h_inv; + + if (cd->use_firehose_wind_model && ch->dm > 0.f) { + struct cooling_part_data* co = &p->cooling_data; + + const float m = hydro_get_mass(p); + const float v = sqrtf(xp->v_full[0] * xp->v_full[0] + + xp->v_full[1] * xp->v_full[1] + + xp->v_full[2] * xp->v_full[2]); + const float dv = sqrtf(ch->dv[0] * ch->dv[0] + + ch->dv[1] * ch->dv[1] + + ch->dv[2] * ch->dv[2]); + float dv_phys = dv * cosmo->a_inv; + + /* Use this to limit energy change in v^2 and u */ + float alpha = 1.f; + + if (dv >= FIREHOSE_EPSILON_TOLERANCE * v) { + const float v_new[3] = { + xp->v_full[0] + ch->dv[0], + xp->v_full[1] + ch->dv[1], + xp->v_full[2] + ch->dv[2] + }; + const float v_new_norm = sqrtf(v_new[0] * v_new[0] + + v_new[1] * v_new[1] + + v_new[2] * v_new[2]); + + /* Apply a kinetic energy limiter */ + const double v2 = v * v; + double dv2 = dv * dv; + const double v_new2 = v_new_norm * v_new_norm; + const double KE_ratio = (v > 0.) ? v_new2 / v2 : 1.; + const int KE_low_flag = (KE_ratio < FIREHOSE_COOLLIM); + const int KE_high_flag = (KE_ratio > FIREHOSE_HEATLIM); + const int KE_out_of_bounds = KE_low_flag || KE_high_flag; + + if (KE_out_of_bounds && dv2 > 0.) { + /* Solve the same scaling equation, just with a different target */ + const float target_KE_factor = + (KE_low_flag) ? FIREHOSE_COOLLIM : FIREHOSE_HEATLIM; + + const float v_dot_dv = xp->v_full[0] * ch->dv[0] + + xp->v_full[1] * ch->dv[1] + + xp->v_full[2] * ch->dv[2]; + + /* How to scale all components equally? Solve quadratic: + * v^2 + 2 * alpha * v * dv + alpha^2 * dv^2 = target_KE_factor * v^2 + * + * Or equivalently: + * A * alpha^2 + B * alpha + C = 0 + * + * where A = 1 + * B = 2 * (v * dv) / (dv^2)) + * C = (v / dv)^2 * (1 - target_KE_factor) + */ + const float B = 2.f * v_dot_dv / dv2; + const float C = (v2 / dv2) * (1.f - target_KE_factor); + const float discriminant = B * B - 4.f * C; + /* For logging */ + const double u_drift = hydro_get_drifted_comoving_internal_energy(p); + + if (discriminant >= 0.) { + const float alpha1 = (-B - sqrtf(discriminant)) / 2.f; + const float alpha2 = (-B + sqrtf(discriminant)) / 2.f; + + /* Minimize alpha1 and alpha2 between (0, 1) */ + if (alpha1 > 0.f && alpha1 < 1.f) alpha = alpha1; + if (alpha2 < alpha && alpha2 > 0.f) alpha = alpha2; + + /* If there is predicted to be no change, just cancel the operation */ + if (alpha == 1.f) alpha = 0.f; + + ch->dv[0] *= alpha; + ch->dv[1] *= alpha; + ch->dv[2] *= alpha; + + message("FIREHOSE_KE_LIMIT p=%lld alpha=%.4g KE_ratio=%.4g v=%.4g " + "dv=%g m=%g dm=%g u=%g du=%g " + "dv[0]=%g dv[1]=%g dv[2]=%g " + "v[0]=%g v[1]=%g v[2]=%g", + p->id, alpha, KE_ratio, v, + dv, m, ch->dm, u_drift, ch->du, + ch->dv[0], ch->dv[1], ch->dv[2], + xp->v_full[0], xp->v_full[1], xp->v_full[2]); + } + else { + ch->dv[0] = 0.f; + ch->dv[1] = 0.f; + ch->dv[2] = 0.f; + + message("FIREHOSE_KE_LIMIT p=%lld alpha=INVALID KE_ratio=%.4g v=%.4g " + "dv=%g m=%g dm=%g u=%g du=%g " + "dv[0]=%g dv[1]=%g dv[2]=%g " + "v[0]=%g v[1]=%g v[2]=%g", + p->id, KE_ratio, v, + dv, m, ch->dm, u_drift, ch->du, + ch->dv[0], ch->dv[1], ch->dv[2], + xp->v_full[0], xp->v_full[1], xp->v_full[2]); + } + + /* Recompute the new updated limited values to set v_sig */ + dv2 = ch->dv[0] * ch->dv[0] + + ch->dv[1] * ch->dv[1] + + ch->dv[2] * ch->dv[2]; + dv_phys = sqrtf(dv2) * cosmo->a_inv; + } + } + else { + /* Cancel everything if the kick is so small it doesn't matter */ + dv_phys = 0.f; + } + + /* Make sure there were also no problems with the KE of the particle. + * Skip all exchanges if there was! */ + if (dv_phys > 0.f) { + + hydro_set_v_sig_based_on_velocity_kick(p, cosmo, dv_phys); + + xp->v_full[0] += ch->dv[0]; + xp->v_full[1] += ch->dv[1]; + xp->v_full[2] += ch->dv[2]; + + /* Grab the comoving internal energy at last kick */ + const double u = hydro_get_drifted_comoving_internal_energy(p); + + /* Reset du based on previously calculated alpha limiter */ + ch->du *= alpha * alpha; + + double u_new = u + ch->du; + const double u_floor = + cd->firehose_u_floor / cosmo->a_factor_internal_energy; + if (u_new < u_floor) { + u_new = u_floor; + ch->du = u_new - u; + } + + /* Ignore small changes to the internal energy */ + const double u_eps = fabs(ch->du) / u; + + if (u_eps > FIREHOSE_EPSILON_TOLERANCE) { + #ifdef FIREHOSE_DEBUG_CHECKS + if (!isfinite(u) || !isfinite(ch->du)) { + message("FIREHOSE_BAD p=%lld u=%g du=%g dv_phys=%g m=%g dm=%g", + p->id, + u, + ch->du, + dv_phys, + m, + ch->dm); + } + #endif + + const double energy_frac = (u > 0.) ? u_new / u : 1.; + if (energy_frac > FIREHOSE_HEATLIM) u_new = FIREHOSE_HEATLIM * u; + if (energy_frac < FIREHOSE_COOLLIM) u_new = FIREHOSE_COOLLIM * u; + + /* If it's in subgrid ISM mode, use additional heat to + * lower ISM cold fraction */ + const int firehose_add_heat_to_ISM = + (p->cooling_data.subgrid_temp > 0.f && + p->cooling_data.subgrid_fcold > 0.f && + ch->du > 0.); + + if (firehose_add_heat_to_ISM) { + + /* 0.8125 is mu for a fully neutral gas with XH=0.75; + * approximate but good enough */ + const double T_conv = + cd->temp_to_u_factor / cosmo->a_factor_internal_energy; + const double u_cold = + 0.8125 * p->cooling_data.subgrid_temp * T_conv; + + const double delta_u = u - u_cold; + double f_evap = 0.; + + if (delta_u > FIREHOSE_EPSILON_TOLERANCE * u) { + f_evap = ch->du / delta_u; + f_evap = min(f_evap, 1.0); + } + else { + f_evap = 1.0; + } + + /* Clip values in case of overflow */ + if (f_evap > 0.) { + + p->cooling_data.subgrid_fcold *= 1. - f_evap; + + /* Make sure any extra heat goes into the particle */ + const double u_remaining = ch->du - f_evap * delta_u; + u_new = u + max(u_remaining, 0.); + + if (p->cooling_data.subgrid_fcold <= 0.f) { + p->cooling_data.subgrid_temp = 0.f; + p->cooling_data.subgrid_dens = + hydro_get_physical_density(p, cosmo); + p->cooling_data.subgrid_fcold = 0.f; + } + } + } + + double u_phys = u_new * cosmo->a_factor_internal_energy; + + hydro_set_physical_internal_energy(p, xp, cosmo, u_phys); + hydro_set_drifted_physical_internal_energy(p, cosmo, NULL, u_phys); + } + else { + ch->du = 0.; + } + + /* Check dust change */ + float dust_eps = 0.f; + + /* Check dust change */ + if (co->dust_mass > 0.f) { + dust_eps = fabs(ch->dm_dust) / co->dust_mass; + } + + float new_dust_mass = co->dust_mass; + if (dust_eps >= FIREHOSE_EPSILON_TOLERANCE) new_dust_mass += ch->dm_dust; + + ch->metal_mass_fraction_total = 0.f; + for (int elem = 0; elem < chemistry_element_count; ++elem) { + const float old_mass_Z = ch->metal_mass_fraction[elem] * m; + if (old_mass_Z > 0.f) { + const float Z_eps = fabs(ch->dm_Z[elem]) / old_mass_Z; + + if (Z_eps >= FIREHOSE_EPSILON_TOLERANCE) { + ch->metal_mass_fraction[elem] = + (old_mass_Z + ch->dm_Z[elem]) / m; + } + } + + /* Recompute Z */ + if (elem != chemistry_element_H && elem != chemistry_element_He) { + ch->metal_mass_fraction_total += ch->metal_mass_fraction[elem]; + } + + if (dust_eps >= FIREHOSE_EPSILON_TOLERANCE) { + const float old_dust_mass_Z = + co->dust_mass_fraction[elem] * co->dust_mass; + co->dust_mass_fraction[elem] = + (old_dust_mass_Z + ch->dm_dust_Z[elem]) / new_dust_mass; + } + } + + /* Set the new dust mass from the exchange */ + co->dust_mass = (new_dust_mass > 0.f) ? new_dust_mass : 0.f; + if (co->dust_mass <= 0.f) { + for (int elem = 0; elem < chemistry_element_count; ++elem) { + co->dust_mass_fraction[elem] = 0.f; + } + + co->dust_mass = 0.f; + } + + /* Make sure that X + Y + Z = 1 */ + const float Y_He = ch->metal_mass_fraction[chemistry_element_He]; + ch->metal_mass_fraction[chemistry_element_H] = + 1.f - Y_He - ch->metal_mass_fraction_total; + + /* Make sure H fraction does not go out of bounds */ + if (ch->metal_mass_fraction[chemistry_element_H] > 1.f || + ch->metal_mass_fraction[chemistry_element_H] < 0.f) { + for (int i = chemistry_element_H; i < chemistry_element_count; i++) { + warning("\telem[%d] is %g", + i, ch->metal_mass_fraction[i]); + } + + error("Hydrogen fraction exeeds unity or is negative for" + " particle id=%lld due to firehose exchange", p->id); + } + + /* Update stream radius for stream particle */ + if (p->decoupled) { + const float stream_growth_factor = + 1.f + ch->dm / hydro_get_mass(p); + ch->radius_stream *= sqrtf(stream_growth_factor); + + const double c_s = + sqrt(ch->u_ambient * hydro_gamma * hydro_gamma_minus_one); + const float Mach = dv_phys / (cosmo->a_factor_sound_speed * c_s); + ch->radius_stream = + firehose_recoupling_criterion(p, Mach, ch->radius_stream, cd); + } + } + } + + /* Are we a decoupled wind? Skip diffusion. */ + if (p->decoupled) return; + + /* Check if we are hypersonic*/ + /* Reset dZ_dt and return? */ + bool reset_time_derivatives = false; + + /* Add diffused metals to particle */ + const float dZ_tot = ch->dZ_dt_total * dt * factor; + const float new_metal_mass_fraction_total + = ch->metal_mass_fraction_total + dZ_tot; + if (ch->metal_mass_fraction_total > 0.f) { + const float abs_fractional_change = + fabs(dZ_tot) / ch->metal_mass_fraction_total; + /* Check if dZ is bigger than 1/4 of the Z */ + if (abs_fractional_change > cd->max_fractional_Z_transfer) { + reset_time_derivatives = true; + } + } + + /* Handle edge case where diffusion leads to negative metallicity */ + if (new_metal_mass_fraction_total < 0.f) { + warning("Metal diffusion led to negative metallicity!\n" + "\tpid=%lld\n\tdt=%g\n\tZ=%g\n\tdZ_dt=%g\n" + "\tdZtot=%g\n\tZnewtot=%g\n\tfactor=%g", + p->id, + dt, + ch->metal_mass_fraction_total, + ch->dZ_dt_total, + dZ_tot, + new_metal_mass_fraction_total, + factor); + reset_time_derivatives = true; + } + + /* Handle edge case where diffusion leads to super-unity metallicity */ + if (new_metal_mass_fraction_total > 1.f) { + warning("Metal diffusion led to metal fractions above unity!\n" + "pid=%lld\n\tdt=%g\n\tZ=%g\n\tdZ_dt=%g\n" + "\tdZtot=%g\n\tZnewtot=%g\n\tfactor=%g", + p->id, + dt, + ch->metal_mass_fraction_total, + ch->dZ_dt_total, + dZ_tot, + new_metal_mass_fraction_total, + factor); + reset_time_derivatives = true; + } + + + /* Add individual element contributions from diffusion */ + for (int elem = 0; elem < chemistry_element_count; elem++) { + const float dZ = ch->dZ_dt[elem] * dt * factor; + const float new_metal_fraction_elem + = ch->metal_mass_fraction[elem] + dZ; + + if (ch->metal_mass_fraction[elem] > 0.f) { + const float abs_fractional_change = + fabs(dZ) / ch->metal_mass_fraction[elem]; + if (abs_fractional_change > cd->max_fractional_Z_transfer) { + reset_time_derivatives = true; + } + } + + /* Make sure that the metallicity is 0 <= x <= 1 */ + if (new_metal_fraction_elem < 0.f) { + warning("Z[elem] < 0! pid=%lld, dt=%g, elem=%d, Z=%g, dZ_dt=%g, dZ=%g, " + "dZtot=%g Ztot=%g Zdust=%g.", + p->id, + dt, + elem, + ch->metal_mass_fraction[elem], + ch->dZ_dt[elem], + dZ, + dZ_tot, + ch->metal_mass_fraction_total, + p->cooling_data.dust_mass_fraction[elem]); + reset_time_derivatives = true; + } + + if (new_metal_fraction_elem > 1.f) { + warning("Z[elem] > 1! pid=%lld, dt=%g, elem=%d, Z=%g, dZ_dt=%g, " + "dZ=%g, dZtot=%g Ztot=%g.", + p->id, + dt, + elem, + ch->metal_mass_fraction[elem], + ch->dZ_dt[elem], + dZ, + dZ_tot, + ch->metal_mass_fraction_total); + reset_time_derivatives = true; + } + } + + /* Found weird dZ_dt values so we should reset everything and exit */ + if (reset_time_derivatives) { + ch->dZ_dt_total = 0.f; + for (int elem = 0; elem < chemistry_element_count; elem++) { + ch->dZ_dt[elem] = 0.f; + } + return; + } + else { +#if COOLING_GRACKLE_MODE >= 2 + if (ch->metal_mass_fraction_total > 0.f) { + /* Add diffused dust to particle, in proportion to added metals */ + p->cooling_data.dust_mass *= 1.f + dZ_tot / ch->metal_mass_fraction_total; + } +#endif + + /* Reset the total metallicity Z */ + ch->metal_mass_fraction_total = new_metal_mass_fraction_total; + + /* Add individual element contributions from diffusion */ + for (int elem = 0; elem < chemistry_element_count; elem++) { + const float dZ = ch->dZ_dt[elem] * dt * factor; + const float new_metal_fraction_elem = + ch->metal_mass_fraction[elem] + dZ; + + #if COOLING_GRACKLE_MODE >= 2 + /* Add diffused dust to particle, in proportion to added metals */ + if (ch->metal_mass_fraction[elem] > 0.f) { + p->cooling_data.dust_mass_fraction[elem] *= + 1.f + dZ / ch->metal_mass_fraction[elem]; + } + #endif + + /* Treating Z like a passive scalar */ + ch->metal_mass_fraction[elem] = new_metal_fraction_elem; + } + } + + /* Make sure that X + Y + Z = 1 */ + const float Y_He = ch->metal_mass_fraction[chemistry_element_He]; + ch->metal_mass_fraction[chemistry_element_H] = + 1.f - Y_He - ch->metal_mass_fraction_total; + + /* Make sure H fraction does not go out of bounds */ + if (ch->metal_mass_fraction[chemistry_element_H] > 1.f || + ch->metal_mass_fraction[chemistry_element_H] < 0.f) { + for (int i = chemistry_element_H; i < chemistry_element_count; i++) { + warning("\telem[%d] is %g", + i, ch->metal_mass_fraction[i]); + } + + error("Hydrogen fraction exeeds unity or is negative for" + " particle id=%lld due to metal diffusion", p->id); + } +} + +/** + * @brief Computes the chemistry-related time-step constraint. + * + * Only constraint in KIARA is the diffusion time-step. + * + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param us The internal system of units. + * @param hydro_props The properties of the hydro scheme. + * @param cd The global properties of the chemistry scheme. + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float chemistry_timestep( + const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, + const struct unit_system* restrict us, + const struct hydro_props* hydro_props, + const struct chemistry_global_data* cd, const struct part* restrict p) { + + float dt_chem = FLT_MAX; + if (cd->diffusion_flag) { + if (p->chemistry_data.diffusion_coefficient > 0.f) { + const struct chemistry_part_data *ch = &p->chemistry_data; + + /* Parshikov & Medin 2002 equation 41 */ + const float h_phys = p->h * cosmo->a * kernel_gamma; + const float D_phys = ch->diffusion_coefficient; + const float rho_phys = hydro_get_physical_density(p, cosmo); + dt_chem = cd->diffusion_beta * rho_phys * h_phys * h_phys / D_phys; + if (dt_chem < cd->time_step_min) { + message( + "Warning! dZ_dt timestep low: id=%lld (%g Myr) is below " + "time_step_min (%g Myr).", + p->id, dt_chem * cd->time_to_Myr, + cd->time_step_min * cd->time_to_Myr); + } + + dt_chem = max(dt_chem, cd->time_step_min); + } + } + + /* Decoupled winds need the hydro time-step for firehose model. */ + if (cd->use_firehose_wind_model) { + if (p->decoupled) { + const float CFL_condition = hydro_props->CFL_condition; + const float h = kernel_gamma * cosmo->a * p->h; + const float v_sig = 2.f * hydro_get_physical_soundspeed(p, cosmo); + const float dt_cfl = 2.f * CFL_condition * h / v_sig; + + /* The actual minimum time-step is handled in the runner file. */ + dt_chem = min(dt_chem, dt_cfl); + } + } + + return dt_chem; +} + +/** + * @brief Initialise the chemistry properties of a black hole with + * the chemistry properties of the gas it is born from. + * + * Black holes don't store fractions so we need to use element masses. + * + * @param bp_data The black hole data to initialise. + * @param p_data The gas data to use. + * @param gas_mass The mass of the gas particle. + */ +__attribute__((always_inline)) INLINE static void chemistry_bpart_from_part( + struct chemistry_bpart_data* bp_data, + const struct chemistry_part_data* p_data, const double gas_mass) { + + bp_data->metal_mass_total = p_data->metal_mass_fraction_total * gas_mass; + for (int i = 0; i < chemistry_element_count; ++i) { + bp_data->metal_mass[i] = p_data->metal_mass_fraction[i] * gas_mass; + } + + bp_data->formation_metallicity = p_data->metal_mass_fraction_total; +} + +/** + * @brief Add the chemistry data of a gas particle to a black hole. + * + * Black holes don't store fractions so we need to add element masses. + * + * @param bp_data The black hole data to add to. + * @param p_data The gas data to use. + * @param gas_mass The mass of the gas particle. + */ +__attribute__((always_inline)) INLINE static void chemistry_add_part_to_bpart( + struct chemistry_bpart_data* bp_data, + const struct chemistry_part_data* p_data, const double gas_mass) { + + bp_data->metal_mass_total += p_data->metal_mass_fraction_total * gas_mass; + for (int i = 0; i < chemistry_element_count; ++i) { + bp_data->metal_mass[i] += p_data->metal_mass_fraction[i] * gas_mass; + } +} + +/** + * @brief Transfer chemistry data of a gas particle to a black hole. + * + * In contrast to `chemistry_add_part_to_bpart`, only a fraction of the + * masses stored in the gas particle are transferred here. Absolute masses + * of the gas particle are adjusted as well. + * Black holes don't store fractions so we need to add element masses. + * + * We expect the nibble_mass to be the gas particle mass multiplied by the + * nibble_fraction. + * + * @param bp_data The black hole data to add to. + * @param p_data The gas data to use. + * @param nibble_mass The mass to be removed from the gas particle. + * @param nibble_fraction The fraction of the (original) mass of the gas + * particle that is removed. + */ +__attribute__((always_inline)) INLINE static void +chemistry_transfer_part_to_bpart(struct chemistry_bpart_data* bp_data, + struct chemistry_part_data* p_data, + const double nibble_mass, + const double nibble_fraction) { + + bp_data->metal_mass_total += p_data->metal_mass_fraction_total * nibble_mass; + for (int i = 0; i < chemistry_element_count; ++i) + bp_data->metal_mass[i] += p_data->metal_mass_fraction[i] * nibble_mass; + +} + +/** + * @brief Add the chemistry data of a black hole to another one. + * + * @param bp_data The black hole data to add to. + * @param swallowed_data The black hole data to use. + */ +__attribute__((always_inline)) INLINE static void chemistry_add_bpart_to_bpart( + struct chemistry_bpart_data* bp_data, + const struct chemistry_bpart_data* swallowed_data) { + + bp_data->metal_mass_total += swallowed_data->metal_mass_total; + for (int i = 0; i < chemistry_element_count; ++i) { + bp_data->metal_mass[i] += swallowed_data->metal_mass[i]; + } +} + +/** + * @brief Split the metal content of a particle into n pieces + * + * We only need to split the fields that are not fractions. + * + * @param p The #part. + * @param n The number of pieces to split into. + */ +__attribute__((always_inline)) INLINE static void chemistry_split_part( + struct part* p, const double n) { } + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * gas particle to be used in feedback/enrichment related routines. + * + * We return the un-smoothed quantity here as the star will smooth + * over its gas neighbours. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_feedback( + const struct part* restrict p) { + + return p->chemistry_data.metal_mass_fraction_total; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * gas particle to be used in feedback/enrichment related routines. + * + * We return the un-smoothed quantity here as the star will smooth + * over its gas neighbours. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_feedback(const struct part* restrict p) { + + return p->chemistry_data.metal_mass_fraction; +} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * star particle to be used in feedback/enrichment related routines. + * + * KIARA uses smooth abundances for everything. + * + * @param sp Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_star_total_metal_mass_fraction_for_feedback( + const struct spart* restrict sp) { + + return sp->chemistry_data.metal_mass_fraction_total; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * star particle to be used in feedback/enrichment related routines. + * + * KIARA uses smooth abundances for everything. + * + * @param sp Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_star_metal_mass_fraction_for_feedback( + const struct spart* restrict sp) { + + return sp->chemistry_data.metal_mass_fraction; +} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * gas particle to be used in cooling related routines. + * + * KIARA uses smooth abundances for everything. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_cooling( + const struct part* restrict p) { + + return p->chemistry_data.metal_mass_fraction_total; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * gas particle to be used in cooling related routines. + * + * KIARA uses smooth abundances for everything. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_cooling(const struct part* restrict p) { + + return p->chemistry_data.metal_mass_fraction; +} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * gas particle to be used in star formation related routines. + * + * KIARA uses smooth abundances for everything. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_star_formation( + const struct part* restrict p) { + + return p->chemistry_data.metal_mass_fraction_total; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * gas particle to be used in star formation related routines. + * + * KIARA uses smooth abundances for everything. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_star_formation( + const struct part* restrict p) { + + return p->chemistry_data.metal_mass_fraction; +} + +/** + * @brief Returns the total metal mass of the + * gas particle to be used in the stats related routines. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_for_stats(const struct part* restrict p) { + + return p->chemistry_data.metal_mass_fraction_total * hydro_get_mass(p); +} + +/** + * @brief Returns the total metal mass of the + * star particle to be used in the stats related routines. + * + * @param sp Pointer to the star particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_star_total_metal_mass_for_stats(const struct spart* restrict sp) { + + return sp->chemistry_data.metal_mass_fraction_total * sp->mass; +} + +/** + * @brief Returns the total metal mass of the + * black hole particle to be used in the stats related routines. + * + * @param bp Pointer to the BH particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_bh_total_metal_mass_for_stats(const struct bpart* restrict bp) { + + return bp->chemistry_data.metal_mass_total; +} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * star particle to be used in the luminosity calculations. + * + * @param sp Pointer to the star particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_star_total_metal_mass_fraction_for_luminosity( + const struct spart* restrict sp) { + + return sp->chemistry_data.metal_mass_fraction_total; +} + +/** + * @brief Extra chemistry operations to be done during the drift. + * + * @param p Particle to act upon. + * @param xp The extended particle data to act upon. + * @param dt_drift The drift time-step for positions. + * @param dt_therm The drift time-step for thermal quantities. + * @param cosmo The current cosmological model. + * @param chem_data The global properties of the chemistry scheme. + */ +__attribute__((always_inline)) INLINE static void chemistry_predict_extra( + struct part *p, struct xpart *xp, float dt_drift, float dt_therm, + const struct cosmology *cosmo, + const struct chemistry_global_data *chem_data) {} + +#endif /* SWIFT_CHEMISTRY_KIARA_H */ diff --git a/src/chemistry/KIARA/chemistry_additions.h b/src/chemistry/KIARA/chemistry_additions.h new file mode 100644 index 0000000000..20f3d05697 --- /dev/null +++ b/src/chemistry/KIARA/chemistry_additions.h @@ -0,0 +1,56 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2023 Yolan Uyttenhove (yolan.uyttenhove@ugent.be) + * + * 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_CHEMISTRY_KIARA_ADDITIONS_H +#define SWIFT_CHEMISTRY_KIARA_ADDITIONS_H + +/** + * @brief Extra operations done during the kick. This needs to be + * done before the particle mass is updated in the hydro_kick_extra. + * + * @param p Particle to act upon. + * @param dt_therm Thermal energy time-step @f$\frac{dt}{a^2}@f$. + * @param dt_grav Gravity time-step @f$\frac{dt}{a}@f$. + * @param dt_hydro Hydro acceleration time-step + * @f$\frac{dt}{a^{3(\gamma{}-1)}}@f$. + * @param dt_kick_corr Gravity correction time-step @f$adt@f$. + * @param cosmo Cosmology. + * @param hydro_props Additional hydro properties. + */ +__attribute__((always_inline)) INLINE static void chemistry_kick_extra( + struct part* p, float dt_therm, float dt_grav, float dt_hydro, + float dt_kick_corr, const struct cosmology* cosmo, + const struct hydro_props* hydro_props) {} + +/** + * @brief update metal mass fluxes between two interacting particles during + * hydro_iact_(non)sym(...) calls. + * + * @param pi first interacting particle + * @param pj second interacting particle + * @param mass_flux the mass flux between these two particles. + * @param flux_dt the time-step over which the fluxes are exchanged + * @param mode 0: non-symmetric interaction, update i only. 1: symmetric + * interaction. + **/ +__attribute__((always_inline)) INLINE static void runner_iact_chemistry_fluxes( + struct part* restrict pi, struct part* restrict pj, float mass_flux, + float flux_dt, int mode) {} + +#endif // SWIFT_CHEMISTRY_KIARA_ADDITIONS_H diff --git a/src/chemistry/KIARA/chemistry_debug.h b/src/chemistry/KIARA/chemistry_debug.h new file mode 100644 index 0000000000..b8720722ee --- /dev/null +++ b/src/chemistry/KIARA/chemistry_debug.h @@ -0,0 +1,40 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_KIARA_DEBUG_H +#define SWIFT_CHEMISTRY_KIARA_DEBUG_H + +__attribute__((always_inline)) INLINE static void chemistry_debug_particle( + const struct part* p, const struct xpart* xp) { + + warning("[PID%lld chemistry_part_data:", p->id); + for (int i = 0; i < chemistry_element_count; i++) { + warning("[PID%lld metal_mass_fraction[%i]: %.3e", p->id, i, + p->chemistry_data.metal_mass_fraction[i]); + } + warning("[PID%lld metal_mass_fraction_total: %.3e", p->id, + p->chemistry_data.metal_mass_fraction_total); + for (int i = 0; i < chemistry_element_count; i++) { + warning("[PID%lld metal_mass_fraction[%i]: %.3e", p->id, i, + p->chemistry_data.metal_mass_fraction[i]); + } + warning("[PID%lld metal_mass_fraction_total: %.3e", p->id, + p->chemistry_data.metal_mass_fraction_total); +} + +#endif /* SWIFT_CHEMISTRY_KIARA_DEBUG_H */ diff --git a/src/chemistry/KIARA/chemistry_iact.h b/src/chemistry/KIARA/chemistry_iact.h new file mode 100644 index 0000000000..b887ea71f2 --- /dev/null +++ b/src/chemistry/KIARA/chemistry_iact.h @@ -0,0 +1,934 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 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 . + * + ******************************************************************************/ +#ifndef SWIFT_KIARA_CHEMISTRY_IACT_H +#define SWIFT_KIARA_CHEMISTRY_IACT_H + +/** + * @file KIARA/chemistry_iact.h + * @brief Smooth metal interaction functions following the KIARA model. + */ + +#include "timestep_sync_part.h" +#include + +/** + * @brief Sums ambient quantities for the firehose wind model + * + * This is called from runner_iact_chemistry, which is called during the density loop + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + */ +__attribute__((always_inline)) INLINE static void firehose_compute_ambient_sym( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj) { + + /* Only decoupled particles need the ambient properties since they + * are in the stream. */ + const int decoupled_i = pi->decoupled; + const int decoupled_j = pj->decoupled; + if (decoupled_i && decoupled_j) return; + + const float r = sqrtf(r2); + const float eint_i = hydro_get_drifted_comoving_internal_energy(pi); + const float eint_j = hydro_get_drifted_comoving_internal_energy(pj); + + /* Accumulate ambient neighbour quantities with an SPH gather operation */ + if (decoupled_i && !decoupled_j) { + struct chemistry_part_data* chi = &pi->chemistry_data; + const float hi_inv = 1. / hi; + const float ui = r * hi_inv; + const float mj = hydro_get_mass(pj); + float wi; + kernel_eval(ui, &wi); + +#ifdef FIREHOSE_DEBUG_CHECKS + if (!isfinite(mj * eint_j * wi)) { + message("FIREHOSE_BAD pi=%lld ui=%g neighbour pj=%lld uj=%g mj=%g hi=%g" + "wi=%g\n", + pi->id, + eint_i, + pj->id, + eint_j, + mj, + hi, + wi); + } +#endif + + chi->u_ambient += mj * eint_j * wi; + chi->rho_ambient += mj * wi; + chi->w_ambient += wi; + } + + if (!decoupled_i && decoupled_j) { + struct chemistry_part_data* chj = &pj->chemistry_data; + const float hj_inv = 1. / hj; + const float uj = r * hj_inv; + const float mi = hydro_get_mass(pi); + float wj; + kernel_eval(uj, &wj); + +#ifdef FIREHOSE_DEBUG_CHECKS + if (!isfinite(mi * eint_i* wj)) { + message("FIREHOSE_BAD pj=%lld uj=%g neighbour pi=%lld ui=%g mi=%g hj=%g" + "wj=%g\n", + pj->id, + eint_j, + pi->id, + eint_i, + mi, + hj, + wj); + } +#endif + + chj->u_ambient += mi * eint_i * wj; + chj->rho_ambient += mi * wj; + chj->w_ambient += wj; + } +} + +/** + * @brief Sums ambient quantities for the firehose wind model + * + * This is called from runner_iact_chemistry, which is called during the density loop + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + */ +__attribute__((always_inline)) INLINE static void +firehose_compute_ambient_nonsym( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, const struct part *restrict pj) { + + /* Only decoupled winds need the ambient quantities computed because + * they are in the stream. */ + const int decoupled_i = pi->decoupled; + const int decoupled_j = pj->decoupled; + + /* A wind particle cannot be in the ambient medium */ + if (!decoupled_i || decoupled_j) return; + + struct chemistry_part_data* chi = &pi->chemistry_data; + + /* Do accumulation of ambient quantities */ + const float eint_j = hydro_get_drifted_comoving_internal_energy(pj); + + /* Compute the kernel function for pi */ + const float r = sqrtf(r2); + const float h_inv = 1. / hi; + const float ui = r * h_inv; + const float mj = hydro_get_mass(pj); + float wi; + kernel_eval(ui, &wi); + +#ifdef FIREHOSE_DEBUG_CHECKS + const float eint_i = hydro_get_drifted_comoving_internal_energy(pi); + if (!isfinite(mj * eint_j * wi)) { + message("FIREHOSE_BAD pi=%lld ui=%g neighbour pj=%lld uj=%g mj=%g hi=%g" + "wi=%g\n", + pi->id, + eint_i, + pj->id, + eint_j, + mj, + hi, + wi); + } +#endif + + /* Accumulate ambient neighbour quantities with an SPH gather operation */ + chi->u_ambient += mj * eint_j * wi; + chi->rho_ambient += mj * wi; + chi->w_ambient += wi; +} + +/** + * @brief do chemistry computation after the runner_iact_density (symmetric + * version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_chemistry( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) { + + /* If in wind mode, compute ambient quantities for firehose wind diffusion */ + firehose_compute_ambient_sym(r2, dx, hi, hj, pi, pj); + + /* Do not need diffusion properties for wind particles */ + if (pi->decoupled || pj->decoupled) return; + + struct chemistry_part_data *chi = &pi->chemistry_data; + struct chemistry_part_data *chj = &pj->chemistry_data; + + float wi, wi_dx; + float wj, wj_dx; + + /* Get the masses. */ + const float mi = hydro_get_mass(pi); + const float mj = hydro_get_mass(pj); + + /* Get r */ + const float r = sqrtf(r2); + const float r_inv = 1.f / r; + + /* Compute the kernel function for pi */ + const float ui = r / hi; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute the kernel function for pj */ + const float uj = r / hj; + kernel_deval(uj, &wj, &wj_dx); + + const float wi_dr = wi_dx * r_inv; + const float mj_wi_dr = mj * wi_dr; + + const float wj_dr = wj_dx * r_inv; + const float mi_wj_dr = mi * wj_dr; + + /* dx[i] is from i -> j, so should dv_ij be from i -> j */ + const float dv_ij[3] = { + pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2] + }; + + /* Compute the shear tensor, i = spatial direction */ + for (int i = 0; i < 3; i++) { + const float dxi_mj_wi_dr = dx[i] * mj_wi_dr; + const float dxi_mi_wj_dr = dx[i] * mi_wj_dr; + + chi->shear_tensor[0][i] += dv_ij[0] * dxi_mj_wi_dr; + chi->shear_tensor[1][i] += dv_ij[1] * dxi_mj_wi_dr; + chi->shear_tensor[2][i] += dv_ij[2] * dxi_mj_wi_dr; + + /* Sign must be positive since dx is always i -> j */ + chj->shear_tensor[0][i] += dv_ij[0] * dxi_mi_wj_dr; + chj->shear_tensor[1][i] += dv_ij[1] * dxi_mi_wj_dr; + chj->shear_tensor[2][i] += dv_ij[2] * dxi_mi_wj_dr; + } + +#if COOLING_GRACKLE_MODE >= 2 + /* Sum up local SFR density for computing G0 */ + chi->local_sfr_density += wi * max(0.f, pj->sf_data.SFR); + chj->local_sfr_density += wj * max(0.f, pi->sf_data.SFR); +#endif +} + +/** + * @brief do chemistry computation after the runner_iact_density (non symmetric + * version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_chemistry( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, const struct part *restrict pj, const float a, + const float H) { + + /* If in wind mode, compute ambient quantities for firehose wind diffusion */ + firehose_compute_ambient_nonsym(r2, dx, hi, hj, pi, pj); + + /* Do not need diffusion properties for wind particles */ + if (pi->decoupled || pj->decoupled) return; + + struct chemistry_part_data *chi = &pi->chemistry_data; + + float wi, wi_dx; + + /* Get the masses. */ + const float mj = hydro_get_mass(pj); + + /* Get r */ + const float r = sqrtf(r2); + const float r_inv = 1.f / r; + + /* Compute the kernel function for pi */ + const float ui = r / hi; + kernel_deval(ui, &wi, &wi_dx); + + const float wi_dr = wi_dx * r_inv; + const float mj_wi_dr = mj * wi_dr; + + const float dv_ij[3] = { + pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2] + }; + + /* Compute the shear tensor */ + for (int i = 0; i < 3; i++) { + const float dxi_mj_wi_dr = dx[i] * mj_wi_dr; + chi->shear_tensor[0][i] += dv_ij[0] * dxi_mj_wi_dr; + chi->shear_tensor[1][i] += dv_ij[1] * dxi_mj_wi_dr; + chi->shear_tensor[2][i] += dv_ij[2] * dxi_mj_wi_dr; + } + +#if COOLING_GRACKLE_MODE >= 2 + /* Sum up local SFR density for computing G0 */ + chi->local_sfr_density += wi * max(0.f, pj->sf_data.SFR); +#endif +} + +/** + * @brief Computes the mass exchanged between the firehose stream + * and the ambient medium. Note that either i or j could be the stream + * particle, with j or i being ambient. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Wind particle. + * @param pj Ambient particle. + * @param xpi The #xpart data of the particle #pi. + * @param xpj The #xpart data of the particle #pj. + * @param time_base The time base used to convert integer to float time. + * @param ti_current Current integer time for seeding random number generator + * @param phys_const Physical constants + * @param cd #chemistry_global_data containing chemistry information. + * @param v2 velocity difference squared between i and j. + * + */ +__attribute__((always_inline)) INLINE static float +firehose_compute_mass_exchange( + const float r2, const float dx[3], const float hi, const float hj, + const struct part *pi, const struct part *pj, + const struct xpart *xpi, const struct xpart *xpj, + const float time_base, const integertime_t ti_current, + const struct phys_const* phys_const, const struct chemistry_global_data* cd, + float *v2, + const struct cosmology *cosmo) { + + const int i_stream = pi->decoupled; + const int j_stream = pj->decoupled; + + /* Both particles cannot be in the stream. The one with >0 delay time is + * the stream particle. At least one particle must be in the stream. */ + if (i_stream && j_stream) return 0.f; + if (!i_stream && !j_stream) return 0.f; + + const double dt_i = get_timestep(pi->time_bin, time_base); + const double dt_j = get_timestep(pj->time_bin, time_base); + + /* Must be a timestep for both */ + if (dt_i <= 0. || dt_j <= 0.) return 0.f; + + /* For stream particle, make sure the stream radius > 0 */ + if (i_stream && pi->chemistry_data.radius_stream <= 0.f) return 0.f; + if (j_stream && pj->chemistry_data.radius_stream <= 0.f) return 0.f; + + /* Stream particle cannot be an AGN jet particle + if (i_stream && + pi->feedback_data.number_of_times_decoupled >= 10000) return 0.f; + if (j_stream && + pj->feedback_data.number_of_times_decoupled >= 10000) return 0.f;*/ + + /* Compute the velocity of the stream relative to ambient gas. + * Order does not matter here because it is symmetric. */ + *v2 = 0.f; + for (int i = 0; i < 3; i++) { + const float dv = xpi->v_full[i] - xpj->v_full[i]; + *v2 += dv * dv; + } + + /* Don't apply above some velocity to avoid jets */ + const float v2_phys = *v2 * cosmo->a2_inv; + const float max_v2_phys = + cd->firehose_max_velocity * cd->firehose_max_velocity; + if (v2_phys > max_v2_phys) return 0.f; + + /* Get mixing layer cooling time, which is negative if cooling */ + const float mixing_layer_time_i = pi->cooling_data.mixing_layer_cool_time; + const float mixing_layer_time_j = pj->cooling_data.mixing_layer_cool_time; + + const float r = sqrtf(r2); + const float gamma_gamma_minus_1 = hydro_gamma * hydro_gamma_minus_one; + + /* Set these based on which is in the stream */ + float mi; + float wi; + float sum_wi; + float chi; + float c_stream; + float c_amb; + float radius_stream; + float mixing_layer_time; + double dt = 0.; + + if (i_stream) { + dt = dt_i; + mi = hydro_get_mass(pi); + const float h_inv = 1. / hi; + const float ui = r * h_inv; + kernel_eval(ui, &wi); + + /* Normalization of the ambient medium */ + sum_wi = pi->chemistry_data.w_ambient; + + /* Compute thermal energy ratio for stream and ambient */ + const float eint_i = hydro_get_drifted_comoving_internal_energy(pi); + chi = pi->chemistry_data.u_ambient / eint_i; + c_stream = sqrtf(eint_i * gamma_gamma_minus_1); + c_amb = sqrtf(pi->chemistry_data.u_ambient * gamma_gamma_minus_1); + radius_stream = pi->chemistry_data.radius_stream; + mixing_layer_time = mixing_layer_time_i; + } + + if (j_stream) { /* j must be the stream here */ + dt = dt_j; + mi = hydro_get_mass(pj); + const float h_inv = 1. / hj; + const float ui = r * h_inv; + kernel_eval(ui, &wi); + + /* Normalization of the ambient medium */ + sum_wi = pj->chemistry_data.w_ambient; + + /* Compute thermal energy ratio for stream and ambient */ + const float eint_j = hydro_get_drifted_comoving_internal_energy(pj); + chi = pj->chemistry_data.u_ambient / eint_j; + c_stream = sqrtf(eint_j * gamma_gamma_minus_1); + c_amb = sqrtf(pj->chemistry_data.u_ambient * gamma_gamma_minus_1); + radius_stream = pj->chemistry_data.radius_stream; + mixing_layer_time = mixing_layer_time_j; + } + + /* This should never happen. */ + if (dt == 0.) return 0.f; + + double dm = 0.; + double t_cool_mix = 1.e10 * dt; + + /* Mass change is growth due to cooling minus loss due to shearing, + kernel-weighted. */ + + /* Must compare internal velocity and soundspeed physically */ + const float a_factor_Mach = cosmo->a_inv / cosmo->a_factor_sound_speed; + const float a_factor_L_over_V = cosmo->a * cosmo->a; + const float a_factor_L_over_Cs = cosmo->a / cosmo->a_factor_sound_speed; + + const float v_stream = sqrtf(*v2); + const float Mach = a_factor_Mach * (v_stream / (c_stream + c_amb)); + const float alpha = 0.21f * (0.8f * exp(-3.f * Mach * Mach) + 0.2f); + + t_cool_mix = + (mixing_layer_time < 0.f) ? fabs(mixing_layer_time) : t_cool_mix; + + const double t_shear = + a_factor_L_over_V * (radius_stream / (alpha * v_stream)); + const double t_sound = + a_factor_L_over_Cs * (2.f * radius_stream / c_stream); + + const double delta_growth = + (4. / (chi * t_sound)) * pow(t_cool_mix / t_sound, -0.25) * dt; + + double delta_shear = 0.; + if (t_shear < t_cool_mix) delta_shear = (1. - exp(-dt / t_shear)); + + dm = mi * (delta_growth - delta_shear) * (wi / sum_wi); + + /* If stream is growing, don't mix */ + if (dm > 0.f) dm = 0.f; + +#ifdef FIREHOSE_DEBUG_CHECKS + if (dm < 0.f && i_stream && pj->cooling_data.subgrid_temp > 0.f) { + message("FIREHOSE: z=%g %lld %lld m=%g nHamb=%g rhoamb/rhoi=%g rhoamb/rhoj=%g" + " Tamb=%g Tj/Tamb=%g cstr/camb=%g M=%g r=%g grow=%g shear=%g tshear=%g" + " tcool=%g fexch=%g", + cosmo->z, + pi->id, + pj->id, + pi->mass, + pi->chemistry_data.rho_ambient * cosmo->a3_inv * cd->rho_to_n_cgs, + pi->chemistry_data.rho_ambient / pi->rho, + pi->chemistry_data.rho_ambient / pj->rho, + pi->chemistry_data.u_ambient * cosmo->a_factor_internal_energy / + cd->temp_to_u_factor, + eint_j / + pi->chemistry_data.u_ambient, + c_stream / c_amb, + Mach, + radius_stream * cd->length_to_kpc * cosmo->a, + delta_growth, + delta_shear, + t_shear, + pi->cooling_data.mixing_layer_cool_time, + dm/pi->mass); + } +#endif + + return dm; +} + + +/** + * @brief Computes the particle interaction via the firehose stream model + * + * This is called from runner_iact_diffusion, which is called during the force + * loop + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Wind particle. + * @param pj Ambient particle. + * @param xpi The #xpart data of the particle #pi. + * @param xpj The #xpart data of the particle #pj. + * @param time_base The time base used to convert integer to float time. + * @param ti_current Current integer time for seeding random number generator. + * @param phys_const Physical constants + * @param cd #chemistry_global_data containing chemistry information. + * + */ +__attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( + const float r2, const float dx[3], const float hi, const float hj, + struct part *pi, struct part *pj, + struct xpart *xpi, struct xpart *xpj, + const float time_base, const integertime_t ti_current, + const struct phys_const* phys_const, const struct chemistry_global_data* cd, + const struct cosmology *cosmo) { + + /* Both particles must be within each others smoothing lengths */ + const float Hi = kernel_gamma * hi; + const float Hj = kernel_gamma * hj; + const int r_in_Hi = (r2 < Hi * Hi) ? 1 : 0; + const int r_in_Hj = (r2 < Hj * Hj) ? 1 : 0; + if (!r_in_Hi || !r_in_Hj) return; + + const int i_stream = pi->decoupled; + const int j_stream = pj->decoupled; + + /* Only one of the particles must be in the stream. */ + if (i_stream && j_stream) return; + if (!i_stream && !j_stream) return; + + if (r2 <= 0.f) return; + + struct chemistry_part_data* chi = &pi->chemistry_data; + struct chemistry_part_data* chj = &pj->chemistry_data; + + /* Compute the amount of mass mixed between stream particle and ambient gas */ + float v2 = 0.f; + const float dm = firehose_compute_mass_exchange(r2, dx, hi, hj, pi, pj, xpi, xpj, + time_base, ti_current, + phys_const, cd, &v2, + cosmo); + float delta_m = fabs(dm); + if (delta_m <= 0.f) return; + + /* Limit mass exchange to some fraction of particles' mass */ + const float mi = hydro_get_mass(pi); + const float mj = hydro_get_mass(pj); + + const float max_fmix_this_step = cd->firehose_max_fmix_per_step; + if (delta_m > max_fmix_this_step * mi) delta_m = max_fmix_this_step * mi; + if (delta_m > max_fmix_this_step * mj) delta_m = max_fmix_this_step * mj; + + /* Track amount of gas mixed in stream particle */ + if (i_stream) chi->exchanged_mass += delta_m; + if (j_stream) chj->exchanged_mass += delta_m; + + chi->dm += delta_m; + chj->dm += delta_m; + + /* set weights for averaging i and j */ + const float pii_weight = (mi - delta_m) / mi; + const float pij_weight = delta_m / mi; + const float pji_weight = delta_m / mj; + const float pjj_weight = (mj - delta_m) / mj; + + /* Mixing is negligibly small, avoid underflows */ + if (pij_weight < 1.e-10f || pji_weight < 1.e-10f) return; + + const float wt_ii = pii_weight * mi; + const float wt_ij = pij_weight * mi; + const float wt_jj = pjj_weight * mj; + const float wt_ji = pji_weight * mj; + + /* Spread dust masses between particles */ + const float pi_dust_mass = pi->cooling_data.dust_mass; + const float pj_dust_mass = pj->cooling_data.dust_mass; + + const float dust_wt_ii = pii_weight * pi_dust_mass; + const float dust_wt_ij = pij_weight * pi_dust_mass; + const float dust_wt_ji = pji_weight * pj_dust_mass; + const float dust_wt_jj = pjj_weight * pj_dust_mass; + + const float new_pi_dust_mass = dust_wt_ii + dust_wt_ij; + const float new_pj_dust_mass = dust_wt_ji + dust_wt_jj; + chi->dm_dust += new_pi_dust_mass - pi_dust_mass; + chj->dm_dust += new_pj_dust_mass - pj_dust_mass; + + /* Spread individual dust elements */ + for (int elem = 0; elem < chemistry_element_count; ++elem) { + /* Exchange metals */ + const float term_ii = wt_ii * chi->metal_mass_fraction[elem]; + const float term_ij = wt_ij * chj->metal_mass_fraction[elem]; + const float term_jj = wt_jj * chj->metal_mass_fraction[elem]; + const float term_ji = wt_ji * chi->metal_mass_fraction[elem]; + + const float old_pi_Z_mass = mi * chi->metal_mass_fraction[elem]; + const float new_pi_Z_mass = term_ii + term_ij; + const float old_pj_Z_mass = mj * chj->metal_mass_fraction[elem]; + const float new_pj_Z_mass = term_jj + term_ji; + + chi->dm_Z[elem] += new_pi_Z_mass - old_pi_Z_mass; + chj->dm_Z[elem] += new_pj_Z_mass - old_pj_Z_mass; + + /* Exchange dust metals */ + const float pi_dust_frac = pi->cooling_data.dust_mass_fraction[elem]; + const float pj_dust_frac = pj->cooling_data.dust_mass_fraction[elem]; + + /* Particle i */ + const float old_pi_dust_mass_Z = pi_dust_frac * pi_dust_mass; + + const float dust_term_ii = dust_wt_ii * pi_dust_frac; + const float dust_term_ij = dust_wt_ij * pj_dust_frac; + const float new_pi_dust_mass_Z = dust_term_ii + dust_term_ij; + + chi->dm_dust_Z[elem] += new_pi_dust_mass_Z - old_pi_dust_mass_Z; + + /* Particle j */ + const float old_pj_dust_mass_Z = pj_dust_frac * pj_dust_mass; + + const float dust_term_jj = dust_wt_jj * pj_dust_frac; + const float dust_term_ji = dust_wt_ji * pi_dust_frac; + const float new_pj_dust_mass_Z = dust_term_jj + dust_term_ji; + + chj->dm_dust_Z[elem] += new_pj_dust_mass_Z - old_pj_dust_mass_Z; + } + + /* Update particles' internal energy per unit mass */ + const float old_pi_u = hydro_get_drifted_comoving_internal_energy(pi); + const float old_pj_u = hydro_get_drifted_comoving_internal_energy(pj); + + float new_pi_u = (wt_ii * old_pi_u + wt_ij * old_pj_u) / mi; + float new_pj_u = (wt_ji * old_pi_u + wt_jj * old_pj_u) / mj; + + /* Accumulate the velocity exchange due to the mass, conserving the + * momentum */ + float new_v2 = 0.f; + for (int i = 0; i < 3; i++) { + const float new_pi_v_full_i = + (wt_ii * xpi->v_full[i] + wt_ij * xpj->v_full[i]) / mi; + /* Keep track of the final new velocity */ + chi->dv[i] += new_pi_v_full_i - xpi->v_full[i]; + + const float new_pj_v_full_i = + (wt_ji * xpi->v_full[i] + wt_jj * xpj->v_full[i]) / mj; + chj->dv[i] += new_pj_v_full_i - xpj->v_full[i]; + + const float dv_i = new_pi_v_full_i - new_pj_v_full_i; + new_v2 += dv_i * dv_i; + } + + /* 4) Split excess energy between stream and ambient particle */ + float delta_KE = 0.5f * delta_m * (v2 - new_v2); + const float min_KE = min(mi * new_pi_u, mj * new_pj_u); + + /* Limit to minimum of the two particles */ + if (delta_KE > min_KE) delta_KE = min_KE; + + const float dKE_split = 0.5f * delta_KE; + + /* Add in the delta KE difference */ + new_pi_u += dKE_split / mi; + new_pj_u += dKE_split / mj; + + /* Accumulate the changes in energy in all interactions */ + chi->du += new_pi_u - old_pi_u; + chj->du += new_pj_u - old_pj_u; + +#ifdef FIREHOSE_DEBUG_CHECKS + message("FIREHOSE_EXCHANGE: pi=%lld pj=%lld si=%d sj=%d npi_u=%g npj_u=%g " + "opi_u=%g opj_u=%g dKE=%g nv2=%g ov2=%g", + pi->id, + pj->id, + i_stream, + j_stream, + new_pi_u, + new_pj_u, + old_pi_u, + old_pj_u, + delta_KE, + new_v2, + v2); +#endif + +} + +/** + * @brief do metal diffusion computation in the + * (symmetric version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param xpi The #xpart data of the particle #pi. + * @param xpj The #xpart data of the particle #pj. + * @param a Current scale factor. + * @param H Current Hubble parameter. + * @param time_base The time base used to convert integer to float time. + * @param ti_current The current time (in integer) + * @param cosmo The cosmology information. + * @param with_cosmology Are we running with cosmology? + * + */ +__attribute__((always_inline)) INLINE static void runner_iact_diffusion( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, + struct xpart *restrict xpi, struct xpart *restrict xpj, + const float a, + const float H, const float time_base, const integertime_t t_current, + const struct cosmology *cosmo, const int with_cosmology, + const struct phys_const* phys_const, const struct chemistry_global_data *cd) { + + if (pi->decoupled || pj->decoupled) { + if (cd->use_firehose_wind_model) { + /* If in wind mode, do firehose wind diffusion */ + firehose_evolve_particle_sym(r2, dx, hi, hj, pi, pj, xpi, xpj, time_base, + t_current, phys_const, cd, cosmo); + } + + return; + } + + struct chemistry_part_data *chi = &pi->chemistry_data; + struct chemistry_part_data *chj = &pj->chemistry_data; + + /* No need to diffuse if both particles are not diffusing. */ + if (chj->diffusion_coefficient > 0.f && chi->diffusion_coefficient > 0.f) { + + /* Get mass */ + const float mj = hydro_get_mass(pj); + const float mi = hydro_get_mass(pi); + const float rhoj = hydro_get_physical_density(pj, cosmo); + const float rhoi = hydro_get_physical_density(pi, cosmo); + + float wi, wj, dwi_dx, dwj_dx; + + /* Get r */ + const float r = sqrtf(r2); + + /* part j */ + /* Get the kernel for hj */ + const float hj_inv = 1.0f / hj; + + /* Compute the kernel function for pj */ + const float xj = r * hj_inv; + kernel_deval(xj, &wj, &dwj_dx); + + /* part i */ + /* Get the kernel for hi */ + const float hi_inv = 1.0f / hi; + + /* Compute the kernel function for pi */ + const float xi = r * hi_inv; + kernel_deval(xi, &wi, &dwi_dx); + + /* Get 1/r */ + const float r_inv = r > 0.f ? 1.f / r : 0.f; + + const float wi_dr = dwi_dx * r_inv; + const float wj_dr = dwj_dx * r_inv; + + const float mj_dw_r = mj * wi_dr; + const float mi_dw_r = mi * wj_dr; + + const float rhoij_inv = 1.f / (rhoi * rhoj); + + /** + * Compute the diffusion following Eq. 2.14 + * from Monaghan, Huppert, & Worster (2006). + */ + float coef = 4.f * chi->diffusion_coefficient * chj->diffusion_coefficient; + coef /= chi->diffusion_coefficient + chj->diffusion_coefficient; + + const float coef_i = coef * mj_dw_r * rhoij_inv; + const float coef_j = coef * mi_dw_r * rhoij_inv; + + /* Compute the time derivative of metals due to diffusion */ + const float dZ_ij_tot = + chi->metal_mass_fraction_total - chj->metal_mass_fraction_total; + chi->dZ_dt_total += coef_i * dZ_ij_tot; + chj->dZ_dt_total -= coef_j * dZ_ij_tot; + + for (int elem = 0; elem < chemistry_element_count; elem++) { + const float dZ_ij = + chi->metal_mass_fraction[elem] - chj->metal_mass_fraction[elem]; + chi->dZ_dt[elem] += coef_i * dZ_ij; + chj->dZ_dt[elem] -= coef_j * dZ_ij; + } + } +} + +/** + * @brief do metal diffusion computation in the + * (nonsymmetric version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. (not updated) + * @param a Current scale factor. + * @param H Current Hubble parameter. + * @param time_base The time base used to convert integer to float time. + * @param ti_current The current time (in integer) + * @param cosmo The #cosmology. + * @param with_cosmology Are we running with cosmology? + * @param cd #chemistry_global_data containing chemistry information. + * + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_diffusion( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, const struct part *restrict pj, const float a, + const float H, const float time_base, const integertime_t t_current, + const struct cosmology *cosmo, const int with_cosmology, + const struct chemistry_global_data* cd) { + + /* In nonsym case, two cases: depending on whether i is stream or ambient */ + if (pi->decoupled || pj->decoupled) return; + + struct chemistry_part_data *chi = &pi->chemistry_data; + const struct chemistry_part_data *chj = &pj->chemistry_data; + + if (chj->diffusion_coefficient > 0 && chi->diffusion_coefficient > 0) { + + /* Get mass */ + const float mj = hydro_get_mass(pj); + const float rhoj = hydro_get_physical_density(pj, cosmo); + const float rhoi = hydro_get_physical_density(pi, cosmo); + + float wi, dwi_dx; + + /* Get r */ + const float r = sqrtf(r2); + + /* part i */ + /* Get the kernel for hi */ + const float hi_inv = 1.0f / hi; + + /* Compute the kernel function for pi */ + const float xi = r * hi_inv; + kernel_deval(xi, &wi, &dwi_dx); + + /* Get 1/r */ + const float r_inv = 1.f / sqrtf(r2); + const float wi_dr = dwi_dx * r_inv; + + const float mj_dw_r = mj * wi_dr; + + const float rhoij_inv = 1.f / (rhoi * rhoj); + + /** + * Compute the diffusion following Eq. 2.14 + * from Monaghan, Huppert, & Worster (2006). + */ + float coef = 4.f * chi->diffusion_coefficient * chj->diffusion_coefficient; + coef /= chi->diffusion_coefficient + chj->diffusion_coefficient; + + const float coef_i = coef * mj_dw_r * rhoij_inv; + + /* Compute the time derivative */ + const float dZ_ij_tot = + chi->metal_mass_fraction_total - chj->metal_mass_fraction_total; + chi->dZ_dt_total += coef_i * dZ_ij_tot; + + for (int elem = 0; elem < chemistry_element_count; elem++) { + const float dZ_ij = + chi->metal_mass_fraction[elem] - chj->metal_mass_fraction[elem]; + chi->dZ_dt[elem] += coef_i * dZ_ij; + } + } +} + +/** + * @brief do metal diffusion computation in the + * (symmetric version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_gradient_diffusion(const float r2, const float dx[3], + const float hi, const float hj, + struct part *restrict pi, + struct part *restrict pj, const float a, + const float H) {} + +/** + * @brief do metal diffusion computation in the + * (nonsymmetric version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_gradient_diffusion(const float r2, const float dx[3], + const float hi, const float hj, + struct part *restrict pi, + struct part *restrict pj, const float a, + const float H) {} + + +#endif /* SWIFT_KIARA_CHEMISTRY_IACT_H */ diff --git a/src/chemistry/KIARA/chemistry_io.h b/src/chemistry/KIARA/chemistry_io.h new file mode 100644 index 0000000000..d749e5573a --- /dev/null +++ b/src/chemistry/KIARA/chemistry_io.h @@ -0,0 +1,221 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 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 . + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_IO_KIARA_H +#define SWIFT_CHEMISTRY_IO_KIARA_H + +#include "chemistry.h" +#include "io_properties.h" + +/** + * @brief Specifies which particle fields to read from a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to read. + * + * @return Returns the number of fields to read. + */ +INLINE static int chemistry_read_particles(struct part* parts, + struct io_props* list) { + int num = 0; + /* List what we want to read */ + list[num] = io_make_input_field( + "ElementMassFractions", FLOAT, chemistry_element_count, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, chemistry_data.metal_mass_fraction); + num++; + + list[num] = + io_make_input_field("MetalMassFractions", FLOAT, 1, OPTIONAL, UNIT_CONV_NO_UNITS, + parts, chemistry_data.metal_mass_fraction_total); + num++; + + return num; +} + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param xparts The extra particle array. + * @param list The list of i/o properties to write. + * @param with_cosmology Are we running with cosmology? + * + * @return Returns the number of fields to write. + */ +INLINE static int chemistry_write_particles(const struct part* parts, + const struct xpart* xparts, + struct io_props* list, + const int with_cosmology) { + + int num = 0; + + /* List what we want to write */ + list[num] = io_make_output_field( + "ElementMassFractions", FLOAT, chemistry_element_count, + UNIT_CONV_NO_UNITS, 0.f, parts, chemistry_data.metal_mass_fraction, + "Fractions of the particles' masses that are in the gas phase of a given element"); + num++; + + list[num] = io_make_output_field( + "MetalMassFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + chemistry_data.metal_mass_fraction_total, + "Fractions of the particles' masses that are in metals"); + num++; + + list[num] = io_make_output_field( + "DiffusionCoefficients", FLOAT, 1, UNIT_CONV_DIFF_COEFF, 0.f, parts, + chemistry_data.diffusion_coefficient, + "The full diffusion coefficient"); + num++; + +#ifdef KIARA_DEBUG_CHECKS + list[num] = io_make_output_field( + "ElementDiffusionRates", FLOAT, chemistry_element_count, + UNIT_CONV_DIFF_RATE, + 0.f, parts, + chemistry_data.dZ_dt, + "The rate of transfer of metal concentration for each element"); + num++; + + list[num] = io_make_output_field( + "MetalDiffusionRates", FLOAT, 1, UNIT_CONV_DIFF_RATE, + 0.f, parts, + chemistry_data.dZ_dt_total, + "The rate of transfer of total metal concentration"); + num++; +#endif + + return num; +} + +/** + * @brief Specifies which star particle fields to write to a dataset + * + * @param sparts The star particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +INLINE static int chemistry_write_sparticles(const struct spart* sparts, + struct io_props* list) { + + int num = 0; + + /* List what we want to write */ + list[num] = io_make_output_field( + "ElementMassFractions", FLOAT, chemistry_element_count, + UNIT_CONV_NO_UNITS, 0.f, sparts, chemistry_data.metal_mass_fraction, + "Fractions of the particles' masses that are in the given element"); + num++; + + list[num] = io_make_output_field( + "MetalMassFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, + chemistry_data.metal_mass_fraction_total, + "Fractions of the particles' masses that are in metals"); + num++; + + return num; +} + + +/** + * @brief Specifies which sink fields to write to a dataset + * + * @param sinks The #sink array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + * required by src/common_io.c + */ +INLINE static int chemistry_write_sinkparticles(const struct sink* sinks, + struct io_props* list) { + return 0; +} + + +/** + * @brief Specifies which black hole particle fields to write to a dataset + * + * @param bparts The black hole particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +INLINE static int chemistry_write_bparticles(const struct bpart* bparts, + struct io_props* list) { + + int num = 0; + + /* List what we want to write */ + list[num] = io_make_output_field( + "ElementMasses", FLOAT, chemistry_element_count, UNIT_CONV_MASS, 0.f, + bparts, chemistry_data.metal_mass, + "Masses of the BH particles in a given element"); + num++; + + list[num] = io_make_output_field("MetalMasses", FLOAT, chemistry_element_count, + UNIT_CONV_MASS, 0.f, bparts, + chemistry_data.metal_mass_total, + "Masses of the BH particles in a metals"); + num++; + + return num; +} + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of chemistry to the file + * @param h_grp The HDF5 group in which to write + * @param h_grp_columns The HDF5 group containing named columns + * @param e The #engine. + */ +INLINE static void chemistry_write_flavour(hid_t h_grp, hid_t h_grp_columns, + const struct engine* e) { + + /* Write the chemistry model */ + io_write_attribute_s(h_grp, "Chemistry Model", "KIARA"); + + /* Create an array of element names */ + const int element_name_length = 32; + char element_names[chemistry_element_count][element_name_length]; + for (int elem = 0; elem < chemistry_element_count; ++elem) { + sprintf(element_names[elem], "%s", + chemistry_get_element_name((enum chemistry_element)elem)); + } + + /* Add to the named columns */ + hsize_t dims[1] = {chemistry_element_count}; + hid_t type = H5Tcopy(H5T_C_S1); + H5Tset_size(type, element_name_length); + hid_t space = H5Screate_simple(1, dims, NULL); + hid_t dset = H5Dcreate(h_grp_columns, "ElementMassFractions", type, space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, element_names[0]); + H5Dclose(dset); + dset = H5Dcreate(h_grp_columns, "SmoothedElementMassFractions", type, space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, element_names[0]); + H5Dclose(dset); + + H5Tclose(type); + H5Sclose(space); +} +#endif + +#endif /* SWIFT_CHEMISTRY_IO_KIARA_H */ diff --git a/src/chemistry/KIARA/chemistry_struct.h b/src/chemistry/KIARA/chemistry_struct.h new file mode 100644 index 0000000000..e3a3e9bdae --- /dev/null +++ b/src/chemistry/KIARA/chemistry_struct.h @@ -0,0 +1,264 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 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 . + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_STRUCT_KIARA_H +#define SWIFT_CHEMISTRY_STRUCT_KIARA_H + +#define FIREHOSE_COOLLIM 0.1f /* -u_new / u */ +#define FIREHOSE_HEATLIM 10.f /* +u_new / u */ +#define FIREHOSE_EPSILON_TOLERANCE 1.e-6 /* Minimum rel. difference to add */ + +/** + * @brief The individual elements traced in the KIARA model. + */ +enum chemistry_element { + chemistry_element_H = 0, + chemistry_element_He, + chemistry_element_C, + chemistry_element_N, + chemistry_element_O, + chemistry_element_Ne, + chemistry_element_Mg, + chemistry_element_Si, + chemistry_element_S, + chemistry_element_Ca, + chemistry_element_Fe, + chemistry_element_count +}; + +#if COOLING_GRACKLE_MODE >= 2 +/** + * @brief The individual elements traced in the Grackle dust model. + */ +enum dust_element { + dust_element_C, + dust_element_O, + dust_element_Mg, + dust_element_Si, + dust_element_S, + dust_element_Ca, + dust_element_Fe, + dust_element_count +}; +#endif + +/** + * @brief Global chemical abundance information in the KIARA model. + */ +struct chemistry_global_data { + + /*! Fraction of the particle mass in given elements at the start of the run */ + float initial_metal_mass_fraction[chemistry_element_count]; + + /*! Fraction of the particle mass in *all* metals at the start of the run */ + float initial_metal_mass_fraction_total; + + /*! Is metal diffusion turned on? */ + int diffusion_flag; + + /*! The timestep beta value from Parshikov & Medin 2002 equation 41 */ + float diffusion_beta; + + /*! The minimum time step size in internal units for diffusion */ + float time_step_min; + + /*! A limiter for how much Z/Z_init can be transferred (~0.25) */ + float max_fractional_Z_transfer; + + /*! The metal diffusion coefficient (Smag ~0.23) */ + float C_Smagorinsky; + + /*! Use Firehose wind model (1) or standard decoupled winds (0) */ + int use_firehose_wind_model; + + /*! Firehose wind model maximum density */ + float firehose_ambient_rho_max; + + /*! Firehose wind model minimum thermal energy */ + float firehose_u_floor; + + /*! Firehose wind model Mach number threshold for recoupling */ + float firehose_recoupling_mach; + + /*! Firehose wind model thermal energy ratio threshold for recoupling */ + float firehose_recoupling_u_factor; + + /*! Firehose wind model mixing mass threshold for recoupling */ + float firehose_recoupling_fmix; + + /*! Firehose threshold relative velocity (km/s) above which model is turned off */ + float firehose_max_velocity; + + /*! Firehose maximum fraction of particles' mass that can be mixed in a single step */ + float firehose_max_fmix_per_step; + + /*! Dust sputtering constant */ + float dust_sputtering_const; + + /*! Conversion factor from internal mass unit to solar mass */ + double mass_to_solar_mass; + + /*! Conversion factor from density in internal units to Hydrogen number + * density in cgs */ + double rho_to_n_cgs; + + /*! Converts temperature to internal energy */ + float temp_to_u_factor; + + /*! Converst temperature to internal units */ + float T_to_internal; + + /*! Factor to convert km/s to internal units */ + float kms_to_internal; + + /*! Convert internal units to kpc */ + float length_to_kpc; + + /*! Convert internal time to Myr */ + float time_to_Myr; +}; + +/** + * @brief Chemical abundances traced by the #part in the KIARA model. + */ +struct chemistry_part_data { + + /*! Fraction of the particle mass in a given element */ + float metal_mass_fraction[chemistry_element_count]; + + /*! Fraction of the particle mass in *all* metals */ + float metal_mass_fraction_total; + + /*! Mass coming from SNIa */ + float mass_from_SNIa; + + /*! Fraction of total gas mass in metals coming from SNIa */ + float metal_mass_fraction_from_SNIa; + + /*! Mass coming from AGB */ + float mass_from_AGB; + + /*! Fraction of total gas mass in metals coming from AGB */ + float metal_mass_fraction_from_AGB; + + /*! Mass coming from SNII */ + float mass_from_SNII; + + /*! Fraction of total gas mass in metals coming from SNII */ + float metal_mass_fraction_from_SNII; + + /*! Fraction of total gas mass in Iron coming from SNIa */ + float iron_mass_fraction_from_SNIa; + + /*! Diffusion coefficient */ + float diffusion_coefficient; + + /*! Variation of the total metal mass */ + float dZ_dt_total; + + /*! Variation of the metal mass by element */ + float dZ_dt[chemistry_element_count]; + + /*! Velocity shear tensor in internal and physical units. */ + float shear_tensor[3][3]; + +#if COOLING_GRACKLE_MODE >= 2 + /*! SFR density (physical) within smoothing kernel needed for G0 calculation */ + float local_sfr_density; +#endif + + /*! Firehose ambient gas thermal energy */ + float u_ambient; + + /*! Firehose ambient gas density */ + float rho_ambient; + + /*! Weighting factor for ambient thermal energy sum */ + float w_ambient; + + /*! Firehose radius of outflowing stream */ + float radius_stream; + + /*! Firehose initial mass of the stream */ + float exchanged_mass; + + /*! Firehose exchanged mass this step */ + float dm; + + /*! Firehose exchanged metal fractions */ + float dm_Z[chemistry_element_count]; + + /*! Firehose exchanged dust mass */ + float dm_dust; + + /*! Firehose exchanged dust mass metals */ + float dm_dust_Z[chemistry_element_count]; + + /*! Firehose exchanged internal energy, internal units */ + double du; + + /*! Firehose exchanged velocities, internal units */ + float dv[3]; +}; + +#define chemistry_spart_data chemistry_part_data + +/** + * @brief Chemical abundances traced by the #bpart in the KIARA model. + */ +struct chemistry_bpart_data { + + /*! Mass in a given element */ + float metal_mass[chemistry_element_count]; + + /*! Mass in *all* metals */ + float metal_mass_total; + + /*! Mass coming from SNIa */ + float mass_from_SNIa; + + /*! Mass coming from AGB */ + float mass_from_AGB; + + /*! Mass coming from SNII */ + float mass_from_SNII; + + /*! Metal mass coming from SNIa */ + float metal_mass_from_SNIa; + + /*! Metal mass coming from AGB */ + float metal_mass_from_AGB; + + /*! Metal mass coming from SNII */ + float metal_mass_from_SNII; + + /*! Iron mass coming from SNIa */ + float iron_mass_from_SNIa; + + /*! Metallicity of converted part. */ + float formation_metallicity; +}; + +/** + * @brief Chemical abundances traced by the #sink in the KIARA model. + * + * Nothing here. + */ +struct chemistry_sink_data {}; + +#endif /* SWIFT_CHEMISTRY_STRUCT_KIARA_H */ diff --git a/src/chemistry_debug.h b/src/chemistry_debug.h index 077e1a7424..0ebfb139ce 100644 --- a/src/chemistry_debug.h +++ b/src/chemistry_debug.h @@ -35,6 +35,8 @@ #include "./chemistry/QLA/chemistry_debug.h" #elif defined(CHEMISTRY_EAGLE) #include "./chemistry/EAGLE/chemistry_debug.h" +#elif defined(CHEMISTRY_KIARA) +#include "./chemistry/KIARA/chemistry_debug.h" #else #error "Invalid choice of chemistry function." #endif diff --git a/src/chemistry_io.h b/src/chemistry_io.h index 83cbba15c3..bf31ea251a 100644 --- a/src/chemistry_io.h +++ b/src/chemistry_io.h @@ -35,6 +35,8 @@ #include "./chemistry/QLA/chemistry_io.h" #elif defined(CHEMISTRY_EAGLE) #include "./chemistry/EAGLE/chemistry_io.h" +#elif defined(CHEMISTRY_KIARA) +#include "./chemistry/KIARA/chemistry_io.h" #else #error "Invalid choice of chemistry function." #endif diff --git a/src/chemistry_struct.h b/src/chemistry_struct.h index 9089db4df1..ace430c31e 100644 --- a/src/chemistry_struct.h +++ b/src/chemistry_struct.h @@ -40,6 +40,8 @@ #include "./chemistry/QLA/chemistry_struct.h" #elif defined(CHEMISTRY_EAGLE) #include "./chemistry/EAGLE/chemistry_struct.h" +#elif defined(CHEMISTRY_KIARA) +#include "./chemistry/KIARA/chemistry_struct.h" #else #error "Invalid choice of chemistry function." #endif diff --git a/src/cooling.h b/src/cooling.h index 377271cc02..56dd321dd0 100644 --- a/src/cooling.h +++ b/src/cooling.h @@ -53,6 +53,8 @@ #include "./cooling/EAGLE/cooling.h" #elif defined(COOLING_PS2020) #include "./cooling/PS2020/cooling.h" +#elif defined(COOLING_KIARA) +#include "./cooling/KIARA/cooling.h" #else #error "Invalid choice of cooling function." #endif diff --git a/src/cooling/KIARA/cooling.c b/src/cooling/KIARA/cooling.c new file mode 100644 index 0000000000..8ad1b9a482 --- /dev/null +++ b/src/cooling/KIARA/cooling.c @@ -0,0 +1,2014 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 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 . + * + ******************************************************************************/ +/** + * @file src/cooling/KIARA/cooling.c + * @brief Cooling using the GRACKLE 3.x library. + */ + +#include + +/* Some standard headers. */ +#include +#include +#include +#include + +/* Include header */ +#include "cooling.h" + +/* Some standard headers. */ +#include +#include +#include +#include + +/* The grackle library itself */ +#include +extern chemistry_data *grackle_data; + +/* Local includes. */ +#include "chemistry.h" +#include "cooling_io.h" +#include "entropy_floor.h" +#include "star_formation.h" +#include "error.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" +#include "fof.h" + +/** + * @brief Common operations performed on the cooling function at a + * given time-step or redshift. Predominantly used to read cooling tables + * above and below the current redshift, if not already read in. + * + * Also calls the additional H reionisation energy injection if need be. + * + * @param cosmo The current cosmological model. + * @param pressure_floor Properties of the pressure floor. + * @param cooling The #cooling_function_data used in the run. + * @param s The space data, including a pointer to array of particles + * @param time The current system time + */ +void cooling_update(const struct phys_const *phys_const, + const struct cosmology *cosmo, + const struct pressure_floor_props *pressure_floor, + struct cooling_function_data *cooling, struct space *s, + const double time) { + + /* set current time */ + if (cooling->redshift == -1) { + cooling->units.a_value = cosmo->a; + } + else { + cooling->units.a_value = 1. / (1. + cooling->redshift); + } +} + +/** + * @brief Print the chemical network + * + * @param xp The #xpart to print + */ +void cooling_print_fractions(const struct xpart* restrict xp) { + + const struct cooling_xpart_data tmp = xp->cooling_data; +#if COOLING_GRACKLE_MODE > 0 + message("HI %g, HII %g, HeI %g, HeII %g, HeIII %g, e %g", tmp.HI_frac, + tmp.HII_frac, tmp.HeI_frac, tmp.HeII_frac, tmp.HeIII_frac, + tmp.e_frac); +#endif + +#if COOLING_GRACKLE_MODE > 1 + message("HM %g, H2I %g, H2II %g", tmp.HM_frac, tmp.H2I_frac, tmp.H2II_frac); +#endif + +#if COOLING_GRACKLE_MODE > 2 + message("DI %g, DII %g, HDI %g", tmp.DI_frac, tmp.DII_frac, tmp.HDI_frac); +#endif + message("Metal: %g", tmp.metal_frac); +} + +/** + * @brief Initializes grackle particle quantities + * assuming purely neutral gas + * + * Nothing to do here. + * + * @param phys_const The physical constant in internal units. + * @param us The unit system. + * @param hydro_props The properties of the hydro scheme. + * @param cosmo The current cosmological model. + * @param cooling The properties of the cooling function. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE void cooling_grackle_init_part( + const struct cooling_function_data *cooling, struct part *restrict p, + struct xpart *restrict xp) { + +#if COOLING_GRACKLE_MODE >= 1 + gr_float zero = 1.e-20; + zero = 0.f; + + /* primordial chemistry >= 1: Start with everything neutral (as in dark ages) */ + xp->cooling_data.HI_frac = + p->chemistry_data.metal_mass_fraction[chemistry_element_H]; + xp->cooling_data.HII_frac = zero; + xp->cooling_data.HeI_frac = + p->chemistry_data.metal_mass_fraction[chemistry_element_He]; + xp->cooling_data.HeII_frac = zero; + xp->cooling_data.HeIII_frac = zero; + xp->cooling_data.e_frac = xp->cooling_data.HII_frac + + 0.25 * xp->cooling_data.HeII_frac + + 0.5 * xp->cooling_data.HeIII_frac; +#endif // MODE >= 1 + +#if COOLING_GRACKLE_MODE >= 2 + /* primordial chemistry >= 2 */ + xp->cooling_data.HM_frac = zero; + xp->cooling_data.H2I_frac = zero; + xp->cooling_data.H2II_frac = zero; +#endif // MODE >= 2 + +#if COOLING_GRACKLE_MODE >= 3 + /* primordial chemistry >= 3 */ + xp->cooling_data.DI_frac = grackle_data->DeuteriumToHydrogenRatio * + grackle_data->HydrogenFractionByMass; + xp->cooling_data.DII_frac = zero; + xp->cooling_data.HDI_frac = zero; +#endif // MODE >= 3 +} + +/** + * @brief Sets the cooling properties of the (x-)particles to a valid start + * state. + * + * @param phys_const The #phys_const. + * @param us The #unit_system. + * @param hydro_props The #hydro_props. + * @param cosmo The #cosmology. + * @param cooling The properties of the cooling function. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +void cooling_first_init_part(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct hydro_props* hydro_props, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* cooling, + struct part* restrict p, + struct xpart* restrict xp) { + + xp->cooling_data.radiated_energy = 0.f; + xp->cooling_data.time_last_event = -cooling->thermal_time; + + /* Initialize grackle ionization fractions */ + cooling_grackle_init_part(cooling, p, xp); + + p->cooling_data.subgrid_fcold = 0.f; + + /* Initialize dust properties */ +#if COOLING_GRACKLE_MODE >= 2 + p->cooling_data.dust_mass = 0.f; + for (int i = 0; i < chemistry_element_count; i++) { + p->cooling_data.dust_mass_fraction[i] = 0.f; + } + + p->cooling_data.dust_temperature = 0.f; +#endif +} + +/** + * @brief Perform additional init on the cooling properties of the + * (x-)particles that requires the density to be known. + * + * Nothing to do here. + * + * @param phys_const The physical constant in internal units. + * @param us The unit system. + * @param hydro_props The properties of the hydro scheme. + * @param cosmo The current cosmological model. + * @param cooling The properties of the cooling function. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE void cooling_post_init_part( + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct hydro_props *hydro_props, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *cooling, const struct part *restrict p, + struct xpart *restrict xp) {} + + +/** + * @brief Returns the total radiated energy by this particle. + * + * @param xp The extended particle data + */ +float cooling_get_radiated_energy(const struct xpart* restrict xp) { + + return xp->cooling_data.radiated_energy; +} + +/** + * @brief Computes H and H2 self-shielding for G0 calculation. + * Based on Schauer et al. 2015 eqs 8,9. + * + * @param p The particle to act upon. + * @param cooling The properties of the cooling function. + */ +__attribute__((always_inline)) INLINE static float cooling_compute_self_shielding( + const struct part* restrict p, + const struct cooling_function_data *cooling) { + + float fH2_shield = 1.f; +#if COOLING_GRACKLE_MODE >= 2 + float T_ism = p->cooling_data.subgrid_temp; + if (T_ism > 0.f) { + /* Compute self-shielding from H */ + const float a = cooling->units.a_value; + const float a3_inv = 1.f / (a * a * a); + //const double r_in_cm = p->h * a * cooling->units.length_units; + const double rho_grad_norm2 = p->rho_gradient[0] * p->rho_gradient[0] + + p->rho_gradient[1] * p->rho_gradient[1] + + p->rho_gradient[2] * p->rho_gradient[2]; + const double rho_grad_norm_inv = + (rho_grad_norm2 > 0.) ? 1. / sqrt(rho_grad_norm2) : 0.; + const double rho_com = hydro_get_comoving_density(p); + double L_eff_com = rho_com * rho_grad_norm_inv; + const double L_eff_com_max = kernel_gamma * p->h; + const double L_eff_com_min = MIN_SHIELD_H_FRAC * p->h; + L_eff_com = fmin(L_eff_com, L_eff_com_max); + L_eff_com = fmax(L_eff_com, L_eff_com_min); + const double L_eff_in_cm = L_eff_com * a * cooling->units.length_units; + const double rho_to_n_cgs = + cooling->units.density_units * 5.97729e23 * 0.75; + const double rho_cgs_phys = rho_com * a3_inv * rho_to_n_cgs; + const double NH_cgs = rho_cgs_phys * L_eff_in_cm; + const double xH = NH_cgs * 3.50877e-24; + const double fH_shield = pow(1.f + xH, -1.62) * exp(-0.149 * xH); + + /* Extra self-shielding from H2 if present - DON'T DO THIS HERE SINCE IT IS IN CRACKLE + const float fH2 = p->sf_data.H2_fraction; + if (fH2 > 0.f) { + const double NH2_cgs = fH2 * NH_cgs; + const double DH2_cgs = 1.e-5 * sqrt(2.*1.38e-16 * T_ism * 2.98864e23); + const double xH2 = NH2_cgs * 1.18133e-14; + fH2_shield = 0.9379 * pow(1.f + xH2 / DH2_cgs, -1.879) + 0.03465 * pow(1.f + xH2, -0.473) * exp(-2.293e-4 * sqrt(1.f + xH2)); + message("G0 shield: r=%g xH2=%g NH2=%g fH2sh=%g term1=%g term2=%g term3=%g\n",r_in_cm/3.086e21, xH2, NH2_cgs, fH2_shield, pow(1.f + xH2 / DH2_cgs, -1.879), pow(1.f + xH2, -0.473), exp(-2.293e-4 * sqrt(1.f + xH2))); + }*/ + fH2_shield *= fH_shield; + } +#endif + + return fH2_shield; +} + +/** + * @brief Returns the value of G0 for given particle p + * + * @param p Pointer to the particle data. + * @param rho Physical density in system units. + * @param cooling The properties of the cooling function. + * @param dt The cooling timestep. + * + */ +__attribute__((always_inline)) INLINE float cooling_compute_G0( + const struct part *restrict p, + const float rho, + const struct cooling_function_data *cooling, + const float mstar, const float ssfr, + const double dt) { + + float G0 = 0.f; + float fH2_shield = 1.f; + /* Determine ISRF in Habing units based on chosen method */ + if (cooling->G0_computation_method == 0) { + G0 = 0.f; + } + else if (cooling->G0_computation_method == 1) { + fH2_shield = cooling_compute_self_shielding(p, cooling); + G0 = fH2_shield * p->chemistry_data.local_sfr_density * + cooling->G0_factor1; + } + else if (cooling->G0_computation_method == 2) { + G0 = ssfr * cooling->G0_factor2; + } + else if (cooling->G0_computation_method == 3) { + if (ssfr > 0.) { + G0 = ssfr * cooling->G0_factor2; + } + else { + fH2_shield = cooling_compute_self_shielding(p, cooling); + G0 = fH2_shield * p->chemistry_data.local_sfr_density * + cooling->G0_factor1; + } + } +#if COOLING_GRACKLE_MODE >= 2 + else if (cooling->G0_computation_method==4) { + /* Remember SNe_ThisTimeStep stores SN **rate** */ + G0 = p->cooling_data.SNe_ThisTimeStep * cooling->G0_factorSNe * dt; + } + else if (cooling->G0_computation_method == 5) { + float pssfr = max(p->sf_data.SFR, 0.f); + pssfr /= max(mstar, 8. * p->mass); + G0 = max(ssfr, pssfr) * cooling->G0_factor2 + + p->cooling_data.SNe_ThisTimeStep * cooling->G0_factorSNe * dt; + } +#endif + else { + error("G0_computation_method %d not recognized\n", + cooling->G0_computation_method); + } + + /*if (p->galaxy_mstar * 1.e10 > 1.e9 && p->id % 10000 == 0) { + message("G0: id=%lld M*=%g SFR=%g rho_sfr=%g Td=%g fshield=%g G0=%g", + p->id, + mstar * 1.e10, + mstar * 1.e10 * + ssfr / (1.e6 * cooling->time_to_Myr), + p->chemistry_data.local_sfr_density * 0.002 / 1.6, + p->cooling_data.dust_temperature, + fH2_shield, + G0); + }*/ + + return G0; +} + +/** + * @brief Prints the properties of the cooling model to stdout. + * + * @param cooling The properties of the cooling function. + */ +void cooling_print_backend(const struct cooling_function_data* cooling) { + + if (cooling->chemistry.use_grackle == 1) { + message("Cooling function is 'Grackle', mode = %i", + cooling->chemistry.primordial_chemistry); + } + else if (cooling->chemistry.use_grackle == 2) { + message("Cooling function is 'Crackle', mode = %i", + cooling->chemistry.primordial_chemistry); + } + + message("CloudyTable = %s", cooling->cloudy_table); + message("Redshift = %g", cooling->redshift); + message("UV background flag = %d", cooling->with_uv_background); + message("Metal cooling flag = %i", cooling->chemistry.metal_cooling); + message("Self Shielding flag = %i", cooling->self_shielding_method); + message("Max subgrid density (internal units) = %g", + cooling->max_subgrid_density); + message("Thermal time = %g", cooling->thermal_time); + message("Specific Heating Rates flag = %i", + cooling->provide_specific_heating_rates); + message("Volumetric Heating Rates flag = %i", + cooling->provide_volumetric_heating_rates); + message("Units:"); + message("\tComoving = %i", cooling->units.comoving_coordinates); + message("\tLength = %g", cooling->units.length_units); + message("\tNumber Density = %g", cooling->units.density_units); + message("\tTime = %g", cooling->units.time_units); + message("\tScale Factor = %g (units: %g)", cooling->units.a_value, + cooling->units.a_units); +} + +/** + * @brief copy a #xpart to the grackle data + * + * @param data The grackle_field_data structure from grackle. + * @param p The #part + * @param xp The #xpart + * @param rho Particle density + */ +#if COOLING_GRACKLE_MODE >= 1 +void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, + const struct xpart* xp, gr_float rho, + gr_float species_densities[N_SPECIES]) { + /* HI */ + species_densities[0] = xp->cooling_data.HI_frac * rho; + data->HI_density = &species_densities[0]; + + /* HII */ + species_densities[1] = xp->cooling_data.HII_frac * rho; + data->HII_density = &species_densities[1]; + + /* HeI */ + species_densities[2] = xp->cooling_data.HeI_frac * rho; + data->HeI_density = &species_densities[2]; + + /* HeII */ + species_densities[3] = xp->cooling_data.HeII_frac * rho; + data->HeII_density = &species_densities[3]; + + /* HeIII */ + species_densities[4] = xp->cooling_data.HeIII_frac * rho; + data->HeIII_density = &species_densities[4]; + + /* electrons */ + species_densities[5] = xp->cooling_data.e_frac * rho; + data->e_density = &species_densities[5]; +} +#else +void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, + const struct xpart* xp, gr_float rho, + gr_float species_densities[N_SPECIES]) { + data->HI_density = NULL; + data->HII_density = NULL; + data->HeI_density = NULL; + data->HeII_density = NULL; + data->HeIII_density = NULL; + data->e_density = NULL; +} +#endif + +/** + * @brief copy a #xpart to the grackle data + * + * @param data The grackle_field_data structure from grackle. + * @param p The #part + * @param xp The #xpart + * @param rho Particle density + */ +#if COOLING_GRACKLE_MODE >= 2 +void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, + const struct xpart* xp, + const struct cooling_function_data* restrict cooling, + const double dt, gr_float rho, + gr_float species_densities[N_SPECIES]) { + /* HM */ + species_densities[6] = xp->cooling_data.HM_frac * rho; + data->HM_density = &species_densities[6]; + + /* H2I */ + species_densities[7] = xp->cooling_data.H2I_frac * rho; + data->H2I_density = &species_densities[7]; + + /* H2II */ + species_densities[8] = xp->cooling_data.H2II_frac * rho; + data->H2II_density = &species_densities[8]; + + /* Dust model */ + if (cooling->use_grackle_dust_evol == 1) { + /* Load dust and metal info */ + species_densities[20] = + p->cooling_data.dust_mass / p->mass * species_densities[12]; + data->dust_density = &species_densities[20]; + species_densities[21] = + p->cooling_data.SNe_ThisTimeStep * dt / p->mass * species_densities[12]; + /* need to pass the number of SNe per volume of particle; + * recall SNe_ThisTimeStep is the SNe rate */ + + //if (species_densities[21] > 1.e15) message("SNe_density: %g %g %g %g\n",p->mass, species_densities[12], p->cooling_data.SNe_ThisTimeStep, species_densities[21]); + data->SNe_ThisTimeStep = &species_densities[21]; + //if( chemistry_get_total_metal_mass_fraction_for_cooling(p)>0.f) message("Zsm= %g Zp= %g Z= %g Zd= %g",chemistry_get_total_metal_mass_fraction_for_cooling(p), p->chemistry_data.metal_mass_fraction_total, species_densities[19], species_densities[20]); + + /* Determine ISRF in Habing units based on chosen method, -1 == non-ISM */ + if (p->cooling_data.subgrid_temp == 0.f) { + species_densities[22] = -1.f; + } + else { + species_densities[22] = p->cooling_data.G0; + } + + data->isrf_habing = &species_densities[22]; + + const double rho_grad_norm2 = p->rho_gradient[0] * p->rho_gradient[0] + + p->rho_gradient[1] * p->rho_gradient[1] + + p->rho_gradient[2] * p->rho_gradient[2]; + const double rho_grad_norm_inv = + (rho_grad_norm2 > 0.) ? 1. / sqrt(rho_grad_norm2) : 0.; + const double rho_com = hydro_get_comoving_density(p); + double L_eff_com = rho_com * rho_grad_norm_inv; + const double L_eff_com_max = kernel_gamma * p->h; + const double L_eff_com_min = MIN_SHIELD_H_FRAC * p->h; + L_eff_com = fmin(L_eff_com, L_eff_com_max); + L_eff_com = fmax(L_eff_com, L_eff_com_min); + + species_densities[23] = L_eff_com * cooling->units.a_value; + data->H2_self_shielding_length = &species_densities[23]; + + /* Load gas metallicities */ + for (int i = 0; i < chemistry_element_count; i++) { + species_densities[24 + i] = + max(p->chemistry_data.metal_mass_fraction[i] * + species_densities[12], 0.); + species_densities[24 + chemistry_element_count+i] = + max(p->cooling_data.dust_mass_fraction[i] * species_densities[20], 0); + //if (i>0) printf("dust densities: %d %g %g %g\n",i,species_densities[23+chemistry_element_count+i],species_densities[23+chemistry_element_count+i]/data->dust_density[0],p->cooling_data.dust_mass_fraction[i]*p->mass / p->cooling_data.dust_mass); + } + + data->He_gas_metalDensity = &species_densities[25]; + data->C_gas_metalDensity = &species_densities[26]; + data->N_gas_metalDensity = &species_densities[27]; + data->O_gas_metalDensity = &species_densities[28]; + data->Ne_gas_metalDensity = &species_densities[29]; + data->Mg_gas_metalDensity = &species_densities[30]; + data->Si_gas_metalDensity = &species_densities[31]; + data->S_gas_metalDensity = &species_densities[32]; + data->Ca_gas_metalDensity = &species_densities[32]; + data->Fe_gas_metalDensity = &species_densities[34]; + /* Load dust metallicities */ + data->He_dust_metalDensity = &species_densities[36]; + data->C_dust_metalDensity = &species_densities[37]; + data->N_dust_metalDensity = &species_densities[38]; + data->O_dust_metalDensity = &species_densities[39]; + data->Ne_dust_metalDensity = &species_densities[40]; + data->Mg_dust_metalDensity = &species_densities[41]; + data->Si_dust_metalDensity = &species_densities[42]; + data->S_dust_metalDensity = &species_densities[43]; + data->Ca_dust_metalDensity = &species_densities[44]; + data->Fe_dust_metalDensity = &species_densities[45]; + } + else { + data->dust_density = NULL; + data->SNe_ThisTimeStep = NULL; + data->isrf_habing = NULL; + data->He_gas_metalDensity = NULL; + data->C_gas_metalDensity = NULL; + data->N_gas_metalDensity = NULL; + data->O_gas_metalDensity = NULL; + data->Ne_gas_metalDensity = NULL; + data->Mg_gas_metalDensity = NULL; + data->Si_gas_metalDensity = NULL; + data->S_gas_metalDensity = NULL; + data->Ca_gas_metalDensity = NULL; + data->Fe_gas_metalDensity = NULL; + data->He_dust_metalDensity = NULL; + data->C_dust_metalDensity = NULL; + data->N_dust_metalDensity = NULL; + data->O_dust_metalDensity = NULL; + data->Ne_dust_metalDensity = NULL; + data->Mg_dust_metalDensity = NULL; + data->Si_dust_metalDensity = NULL; + data->S_dust_metalDensity = NULL; + data->Ca_dust_metalDensity = NULL; + data->Fe_dust_metalDensity = NULL; + } +} +#else +void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, + const struct xpart* xp, + const struct cooling_function_data* restrict cooling, + const double dt, gr_float rho, + gr_float species_densities[N_SPECIES]) { + data->HM_density = NULL; + data->H2I_density = NULL; + data->H2II_density = NULL; +} +#endif + +/** + * @brief copy a #xpart to the grackle data + * + * @param data The grackle_field_data structure from grackle. + * @param p The #part + * @param xp The #xpart + * @param rho Particle density + */ +#if COOLING_GRACKLE_MODE >= 3 +void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, + const struct xpart* xp, gr_float rho, + gr_float species_densities[N_SPECIES]) { + /* DI */ + species_densities[9] = xp->cooling_data.DI_frac * rho; + data->DI_density = &species_densities[9]; + + /* DII */ + species_densities[10] = xp->cooling_data.DII_frac * rho; + data->DII_density = &species_densities[10]; + + /* HDI */ + species_densities[11] = xp->cooling_data.HDI_frac * rho; + data->HDI_density = &species_densities[11]; +} +#else +void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, + const struct xpart* xp, gr_float rho, + gr_float species_densities[N_SPECIES]) { + data->DI_density = NULL; + data->DII_density = NULL; + data->HDI_density = NULL; +} +#endif + +/** + * @brief copy the grackle data to a #xpart + * + * @param data The grackle_field_data structure from grackle. + * @param p The #part. + * @param xp The #xpart. + * @param rho The particle density. + */ +#if COOLING_GRACKLE_MODE >= 1 +void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + + const double rhoinv = 1.f / rho; + /* HI */ + xp->cooling_data.HI_frac = *data->HI_density * rhoinv; + + /* HII */ + xp->cooling_data.HII_frac = *data->HII_density * rhoinv; + + /* HeI */ + xp->cooling_data.HeI_frac = *data->HeI_density * rhoinv; + + /* HeII */ + xp->cooling_data.HeII_frac = *data->HeII_density * rhoinv; + + /* HeIII */ + xp->cooling_data.HeIII_frac = *data->HeIII_density * rhoinv; + + /* e */ + xp->cooling_data.e_frac = *data->e_density * rhoinv; +} +#else +void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) {} +#endif + +/** + * @brief copy the grackle data to a #xpart. + * + * @param data The grackle_field_data structure from grackle. + * @param p The #part. + * @param xp The #xpart. + * @param rho The particle density. + */ +#if COOLING_GRACKLE_MODE >= 2 +void cooling_copy_from_grackle2(grackle_field_data* data, struct part* p, + struct xpart* xp, + const struct cooling_function_data* restrict cooling, + gr_float rho) { + double rhoinv = 1.f / rho; + + /* HM */ + xp->cooling_data.HM_frac = *data->HM_density * rhoinv; + /* H2I */ + xp->cooling_data.H2I_frac = *data->H2I_density * rhoinv; + /* H2II */ + xp->cooling_data.H2II_frac = *data->H2II_density * rhoinv; + + /* Dust model */ + if (cooling->use_grackle_dust_evol == 1) { + /* Load gas metallicities */ + p->chemistry_data.metal_mass_fraction_total = + *data->metal_density * rhoinv; + p->chemistry_data.metal_mass_fraction[1] = + *data->He_gas_metalDensity * rhoinv; + p->chemistry_data.metal_mass_fraction[2] = + *data->C_gas_metalDensity * rhoinv; + p->chemistry_data.metal_mass_fraction[3] = + *data->N_gas_metalDensity * rhoinv; + p->chemistry_data.metal_mass_fraction[4] = + *data->O_gas_metalDensity * rhoinv; + p->chemistry_data.metal_mass_fraction[5] = + *data->Ne_gas_metalDensity * rhoinv; + p->chemistry_data.metal_mass_fraction[6] = + *data->Mg_gas_metalDensity * rhoinv; + p->chemistry_data.metal_mass_fraction[7] = + *data->Si_gas_metalDensity * rhoinv; + p->chemistry_data.metal_mass_fraction[8] = + *data->S_gas_metalDensity * rhoinv; + p->chemistry_data.metal_mass_fraction[9] = + *data->Ca_gas_metalDensity * rhoinv; + p->chemistry_data.metal_mass_fraction[10] = + *data->Fe_gas_metalDensity * rhoinv; + + /* Load dust metallicities */ + p->cooling_data.dust_mass = *data->dust_density * p->mass * rhoinv; + + if (p->cooling_data.dust_mass > 0.5 * p->mass) { + warning("DUST > METALS Mg=%g Zg=%g mdust=%g mmet=%g\n", + p->mass, + chemistry_get_total_metal_mass_fraction_for_cooling(p), + p->cooling_data.dust_mass, + p->mass * chemistry_get_total_metal_mass_fraction_for_cooling(p)); + } + + p->cooling_data.dust_mass_fraction[0] = 0.f; + if (*data->dust_density > 0.f) { + rhoinv = 1. / *data->dust_density; + + const double rhodust[chemistry_element_count - 1] = { + *data->He_dust_metalDensity, + *data->C_dust_metalDensity, + *data->N_dust_metalDensity, + *data->O_dust_metalDensity, + *data->Ne_dust_metalDensity, + *data->Mg_dust_metalDensity, + *data->Si_dust_metalDensity, + *data->S_dust_metalDensity, + *data->Ca_dust_metalDensity, + *data->Fe_dust_metalDensity + }; + + /* no Helium metal density */ + assert(rhodust[0] == 0.); + + for (int i = 1; i < chemistry_element_count; i++) { + p->cooling_data.dust_mass_fraction[i] = rhodust[i - 1] * rhoinv; + p->cooling_data.dust_mass_fraction[0] += + p->cooling_data.dust_mass_fraction[i]; + } + } + else { + for (int i = 1; i < chemistry_element_count; i++) { + p->cooling_data.dust_mass_fraction[i] = 0.f; + } + } + } +} +#else +void cooling_copy_from_grackle2(grackle_field_data* data, struct part* p, + struct xpart* xp, + const struct cooling_function_data* restrict cooling, + gr_float rho) {} +#endif + +/** + * @brief copy the grackle data to a #xpart + * + * @param data The grackle_field_data structure from grackle. + * @param p The #part. + * @param xp The #xpart. + * @param rho The particle density. + */ +#if COOLING_GRACKLE_MODE > 2 +void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + + const double rhoinv = 1.f / rho; + /* DI */ + xp->cooling_data.DI_frac = *data->DI_density * rhoinv; + + /* DII */ + xp->cooling_data.DII_frac = *data->DII_density * rhoinv; + + /* HDI */ + xp->cooling_data.HDI_frac = *data->HDI_density * rhoinv; +} +#else +void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) {} +#endif + +/** + * @brief copy a #xpart to the grackle data + * + * Warning this function creates some variable, therefore the grackle call + * should be in a block that still has the variables. + * + * @param data The grackle_field_data structure from grackle. + * @param p The #part. + * @param xp The #xpart. + * @param rho The particle density. + */ +void cooling_copy_to_grackle(grackle_field_data* data, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* p, const struct xpart* xp, + const double dt, const double T_warm, + gr_float species_densities[N_SPECIES], + int mode) { + + int i; + /* set values */ + /* grid */ + data->grid_dx = 0.f; + data->grid_rank = GRACKLE_RANK; + + data->grid_dimension = malloc(GRACKLE_RANK * sizeof(int)); + data->grid_start = malloc(GRACKLE_RANK * sizeof(int)); + data->grid_end = malloc(GRACKLE_RANK * sizeof(int)); + for (i = 0; i < 3; i++) { + /* The active dimension not including ghost zones */ + data->grid_dimension[i] = 1; + data->grid_start[i] = 0; + data->grid_end[i] = 0; + } + + data->grid_dimension[0] = GRACKLE_NPART; + data->grid_end[0] = GRACKLE_NPART - 1; + + /* get particle density, internal energy in + physical coordinates (still code units) */ + double T_subgrid = cooling_get_subgrid_temperature(p, xp); + /* mode 1 is cooling time, here we want non-subgrid values in all cases */ + if (mode == 1) { + species_densities[12] = hydro_get_physical_density(p, cosmo); + species_densities[13] = hydro_get_physical_internal_energy(p, xp, cosmo); + species_densities[14] = cooling->T_CMB_0 * (1.f + cosmo->z); + species_densities[15] = 0.f; + data->grid_end[0] = -1; // this signals to crackle to turn off UVB + } + /* non-subgrid case, here we set the floor temperature + * by the EoS (if applicable). Note the cold fraction has a small + * limit otherwise one can get underflows in crackle. */ + else if (p->cooling_data.subgrid_temp == 0. || + p->cooling_data.subgrid_fcold <= 1.e-6) { + species_densities[12] = hydro_get_physical_density(p, cosmo); + species_densities[13] = hydro_get_physical_internal_energy(p, xp, cosmo); + species_densities[14] = T_warm; + /* specific_heating_rate has to be in cgs units; + no unit conversion done within grackle */ + species_densities[15] = + hydro_get_physical_internal_energy_dt(p, cosmo) * cooling->dudt_units; + } + /* subgrid ISM case, use subgrid ISM values and set floor by T_CMB */ + else { + /* Physical sub-grid density*/ + species_densities[12] = + cooling_get_subgrid_density(p, xp) * p->cooling_data.subgrid_fcold; + /* Physical internal energy */ + species_densities[13] = + cooling_convert_temp_to_u(T_subgrid, xp->cooling_data.e_frac, + cooling, p); + /* CMB temp is floor*/ + species_densities[14] = cooling->T_CMB_0 * (1.f + cosmo->z); + /* If tracking H2, turn off specific heating rate in ISM. */ + species_densities[15] = 0.f; + if (cooling->ism_adiabatic_heating_method == 1) { + species_densities[15] += + hydro_get_physical_internal_energy_dt(p, cosmo) * + cooling->dudt_units; + } + } + /* load into grackle structure */ + data->density = &species_densities[12]; + data->internal_energy = &species_densities[13]; + data->temperature_floor = &species_densities[14]; + data->specific_heating_rate = &species_densities[15]; + + /* velocity (maybe not needed?) */ + species_densities[16] = xp->v_full[0] * cosmo->a_inv; + species_densities[17] = xp->v_full[1] * cosmo->a_inv; + species_densities[18] = xp->v_full[2] * cosmo->a_inv; + data->x_velocity = &species_densities[16]; + data->y_velocity = &species_densities[17]; + data->z_velocity = &species_densities[18]; + + cooling_copy_to_grackle1(data, p, xp, + species_densities[12], species_densities); + cooling_copy_to_grackle2(data, p, xp, cooling, dt, + species_densities[12], species_densities); + cooling_copy_to_grackle3(data, p, xp, + species_densities[12], species_densities); + + data->RT_heating_rate = NULL; + data->RT_HI_ionization_rate = NULL; + data->RT_HeI_ionization_rate = NULL; + data->RT_HeII_ionization_rate = NULL; + data->RT_H2_dissociation_rate = NULL; + + species_densities[19] = + chemistry_get_total_metal_mass_fraction_for_cooling(p) * + species_densities[12]; + data->metal_density = &species_densities[19]; + + for (i = 0; i < N_SPECIES; i++) { + if (fpclassify(species_densities[i]) == FP_NAN || + fpclassify(species_densities[i]) == FP_INFINITE) { + error("Passing a non-finite value to grackle! " + "i=%d / %d, species_densities[i]=%g\n", + i, + N_SPECIES, + species_densities[i]); + } + } +} + +/** + * @brief copy a #xpart to the grackle data + * + * Warning this function creates some variable, therefore the grackle call + * should be in a block that still has the variables. + * + * @param data The grackle_field_data structure from grackle. + * @param p The #part. + * @param xp The #xpart. + * @param rho The particle density. + */ +void cooling_copy_from_grackle(grackle_field_data* data, struct part* p, + struct xpart* xp, + const struct cooling_function_data* restrict cooling, + gr_float rho) { + + cooling_copy_from_grackle1(data, p, xp, rho); + cooling_copy_from_grackle2(data, p, xp, cooling, rho); + cooling_copy_from_grackle3(data, p, xp, rho); +} + +/** + * @brief free memory associated with grackle driver + * + * @param data The grackle_field_data structure from grackle. + */ +void cooling_grackle_free_data(grackle_field_data* data) { + + free(data->grid_dimension); + free(data->grid_start); + free(data->grid_end); +} + +/** + * @brief Compute the energy of a particle after dt and update the particle + * chemistry data + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The #cosmology. + * @param hydro_props The #hydro_props. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the particle extra data + * @param dt The time-step of this particle. + * @param mode 0=energy, 1=cooling time, 2=temperature, 3=pressure, 4=gamma + * + * @return desired quantity based on mode + */ +gr_float cooling_grackle_driver( + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, double dt, + double T_warm, int mode) { + + /* set current units for conversion to physical quantities */ + code_units units = cooling->units; + + /* initialize data to send to grackle */ + gr_float *species_densities; + species_densities = (gr_float *)calloc(N_SPECIES, sizeof(gr_float)); + grackle_field_data data; + //cooling_grackle_malloc_fields(&data, 1, cooling->chemistry.use_dust_evol); + + /* load particle information from particle to grackle data */ + cooling_copy_to_grackle(&data, us, cosmo, cooling, p, xp, dt, T_warm, + species_densities, mode); + + /* Run Grackle in desired mode */ + gr_float return_value = 0.f; + double t_dust = 0.f; + + switch (mode) { + case 0: + /* solve chemistry, advance thermal energy by dt */ + if (solve_chemistry(&units, &data, dt) == 0) { + error("Error in Grackle solve_chemistry."); + } + //if (solve_chemistry(&units, &data, -dt) == 0) { + // error("Error in Crackle solve_chemistry."); + //} + /* copy from grackle data to particle */ + cooling_copy_from_grackle(&data, p, xp, cooling, species_densities[12]); + return_value = data.internal_energy[0]; +#if COOLING_GRACKLE_MODE >= 2 + /* Compute dust temperature */ + t_dust = p->cooling_data.dust_temperature; + if (calculate_dust_temperature(&units, &data, &t_dust) == 0) { + error("Error in Grackle calculate dust temperature."); + } + + p->cooling_data.dust_temperature = t_dust; + + /* Reset accumulated local variables to zero */ + p->cooling_data.SNe_ThisTimeStep = 0.f; +#endif + break; + + case 1: + /* compute cooling time */ + if (calculate_cooling_time(&units, &data, &return_value) == 0) { + error("Error in Grackle calculate_cooling_time."); + } + break; + + case 2: + /* compute temperature */ + if (calculate_temperature(&units, &data, &return_value) == 0) { + error("Error in Grackle calculate_temperature."); + } + break; + + case 3: + /* compute pressure */ + if (calculate_pressure(&units, &data, &return_value) == 0) { + error("Error in Grackle calculate_pressure."); + } + break; + case 4: + /* compute gamma */ + if (calculate_gamma(&units, &data, &return_value) == 0) { + error("Error in Grackle calculate_gamma."); + } + break; + } + cooling_grackle_free_data(&data); + free(species_densities); + + return return_value; +} + +/** + * @brief Compute the cooling time. Optionally uses input values + * for rho and u, but leaves all other properties of p the same. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param hydro_props The #hydro_props. + * @param cosmo The #cosmology. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the particle extra data. + * @param rhocool Density used in tcool calculation. + * @param ucool Density used in tcool calculation. + * + * @return cooling time + */ +gr_float cooling_time(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct hydro_props* hydro_properties, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, + struct xpart* restrict xp, + const float rhocool, const float ucool) { + + /* Removes const in declaration*/ + struct part p_temp = *p; + + if (rhocool > 0.f) p_temp.rho = rhocool; + if (ucool > 0.f) p_temp.u = ucool; + + gr_float cooling_time = cooling_grackle_driver( + phys_const, us, cosmo, hydro_properties, cooling, &p_temp, xp, 0., 0., 1); + + return cooling_time; +} + +/** + * @brief Compute the temperature of a #part based on the cooling function. + * + * @param phys_const #phys_const data structure. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cosmo #cosmology data structure. + * @param cooling #cooling_function_data struct. + * @param p #part data. + * @param xp Pointer to the #xpart data. + */ +float cooling_get_temperature( + const struct phys_const* restrict phys_const, + const struct hydro_props* restrict hydro_properties, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, const struct xpart* restrict xp) { + + /* Remove const in declaration*/ + struct part p_temp = *p; + struct xpart xp_temp = *xp; + + const float temperature = + cooling_grackle_driver(phys_const, us, cosmo, hydro_properties, + cooling, &p_temp, &xp_temp, 0., 0., 2); + + return temperature; +} + +/** + * @brief Do dust sputtering and return metals back to gas phase + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the #xpart data. + * @param dt The time-step of this particle. + */ +__attribute__((always_inline)) INLINE void cooling_sputter_dust( + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, const double dt) { + + /* Do dust destruction in stream particle */ + if (cooling->use_grackle_dust_evol && p->cooling_data.dust_mass > 0.f) { + const float u_phys = hydro_get_physical_internal_energy(p, xp, cosmo); + const float Tstream = + cooling_convert_u_to_temp(u_phys, xp->cooling_data.e_frac, cooling, p); + const double Tstream_K = + Tstream * units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + /* Sputtering negligible at low-T */ + if (Tstream_K > 1.e4) { + const double rho_cgs = + hydro_get_physical_density(p, cosmo) * + units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + + /* sputtering timescale, Tsai & Mathews (1995) */ + const double tsp = + 1.7e8 * 3.15569251e7 / units_cgs_conversion_factor(us, UNIT_CONV_TIME) + * (cooling->dust_grainsize / 0.1) * (1.e-27 / rho_cgs) + * (pow(2.e6 / Tstream, 2.5) + 1.0); + + const float dust_mass_old = p->cooling_data.dust_mass; + + /* Update dust mass but limit the destruction */ + p->cooling_data.dust_mass -= + dust_mass_old * (1.f - exp(-3.f * min(dt / tsp, 5.f))); + if (p->cooling_data.dust_mass < 0.5f * dust_mass_old) { + p->cooling_data.dust_mass = 0.5f * dust_mass_old; + } + +#ifdef FIREHOSE_DEBUG_CHECKS + message("FIREHOSE_SPUT: id=%lld mdust=%g mdustnew=%g T=%g rho=%g " + "tsp_dt_ratio=%g", + p->id, + dust_mass_old, + p->cooling_data.dust_mass, + Tstream, + rho_cgs / 1.673e-24, /* units of mp */ + tsp / dt); +#endif + /* factor by which dust mass changed */ + const float dust_mass_new = p->cooling_data.dust_mass; + const float dust_mass_ratio = dust_mass_new / dust_mass_old; + p->chemistry_data.metal_mass_fraction_total = 0.f; + + for (int elem = chemistry_element_He; + elem < chemistry_element_count; ++elem) { + const float Z_dust_elem_old = p->cooling_data.dust_mass_fraction[elem]; + const float Z_dust_elem_new = Z_dust_elem_old * dust_mass_ratio; + const float Z_elem_old = p->chemistry_data.metal_mass_fraction[elem]; + const float elem_mass_old = Z_elem_old * hydro_get_mass(p); + + /* This is the positive amount of metal mass to add since we + * are losing dust mass when sputtering. */ + const float delta_metal_mass_elem = + (Z_dust_elem_old * dust_mass_old - Z_dust_elem_new * dust_mass_new); + const float elem_mass_new = elem_mass_old + delta_metal_mass_elem; + const float Z_elem_new = elem_mass_new / hydro_get_mass(p); + + p->chemistry_data.metal_mass_fraction[elem] = Z_elem_new; + p->cooling_data.dust_mass_fraction[elem] *= dust_mass_ratio; + + /* Sum up to get the new Z value */ + if (elem != chemistry_element_H && elem != chemistry_element_He) { + p->chemistry_data.metal_mass_fraction_total += Z_elem_new; + } + } + + /* Make sure that X + Y + Z = 1 */ + const float Y_He = + p->chemistry_data.metal_mass_fraction[chemistry_element_He]; + p->chemistry_data.metal_mass_fraction[chemistry_element_H] = + 1.f - Y_He - p->chemistry_data.metal_mass_fraction_total; + + /* Make sure H fraction does not go out of bounds */ + if (p->chemistry_data.metal_mass_fraction[chemistry_element_H] > 1.f || + p->chemistry_data.metal_mass_fraction[chemistry_element_H] < 0.f) { + for (int i = chemistry_element_H; i < chemistry_element_count; i++) { + warning("\telem[%d] is %g", + i, p->chemistry_data.metal_mass_fraction[i]); + } + + error("Hydrogen fraction exeeds unity or is negative for" + " particle id=%lld due to dust sputtering", p->id); + } + } + } +} + +/** + * @brief Compute particle quantities for the firehose model + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the #xpart data. + * @param dt The time-step of this particle. + */ +__attribute__((always_inline)) INLINE void firehose_cooling_and_dust( + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* restrict hydro_props, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, const double dt) { + + /* Initialize cooling time */ + p->cooling_data.mixing_layer_cool_time = 0.f; + + const float u = hydro_get_comoving_internal_energy(p, xp); + + /* If it's not a firehose particle, just compute particle cooling time */ + if (p->chemistry_data.radius_stream <= 0.f || + p->chemistry_data.rho_ambient <= 0.f) { + p->cooling_data.mixing_layer_cool_time = + cooling_time(phys_const, us, hydro_props, cosmo, cooling, p, xp, + p->rho, u); + return; + } + + /* It's a firehose particles, so compute the cooling rate + * in the mixing layer */ + const float rhocool = 0.5f * (p->chemistry_data.rho_ambient + p->rho); + const float ucool = 0.5f * (p->chemistry_data.u_ambient + u); + + /* +ive if heating -ive if cooling*/ + p->cooling_data.mixing_layer_cool_time = + cooling_time(phys_const, us, hydro_props, cosmo, cooling, p, xp, + rhocool, ucool); +#ifdef FIREHOSE_DEBUG_CHECKS + message("FIREHOSE_COOLING: id=%lld nH=%g T=%g rhoamb=%g Tamb=%g tcool=%g", + p->id, + hydro_get_physical_density(p, cosmo) * + cooling->units.density_units * 0.75 / 1.673e-24, + u_old * cosmo->a_factor_internal_energy / cooling->temp_to_u_factor, + p->chemistry_data.rho_ambient, + p->chemistry_data.u_ambient * + cosmo->a_factor_internal_energy / cooling->temp_to_u_factor, + p->cooling_data.mixing_layer_cool_time); +#endif + + cooling_sputter_dust(us, cosmo, cooling, p, xp, dt); + +} + +/** + * @brief Apply the cooling function to a particle. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param floor_props Properties of the entropy floor. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the particle' extended data. + * @param dt The time-step of this particle. + * @param dt_therm The time-step operator used for thermal quantities. + * @param time The current time (since the Big Bang or start of the run) in + * internal units. + */ +void cooling_cool_part(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, + const struct pressure_floor_props *pressure_floor_props, + const struct cooling_function_data* restrict cooling, + const struct fof_props* fof_props, + struct part* restrict p, struct xpart* restrict xp, + const double dt, const double dt_therm, + const double time) { + + /* Compute cooling time and other quantities needed for firehose */ + firehose_cooling_and_dust(phys_const, us, cosmo, hydro_props, + cooling, p, xp, dt); + + /* No cooling if particle is decoupled */ + if (p->decoupled) { + /* Make sure these are always set for the wind particles */ + p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); + p->cooling_data.subgrid_temp = 0.; + p->cooling_data.subgrid_fcold = 0.f; + + return; + } + + /* Collect information about galaxy that the particle belongs to */ + size_t group_id = p->gpart->fof_data.group_id; + float galaxy_mstar = fof_props->group_stellar_mass[group_id]; + float galaxy_ssfr = fof_props->group_star_formation_rate[group_id] / galaxy_mstar; + + /* Never cool if there is a cooling shut off, let the hydro do its magic + if (p->feedback_data.cooling_shutoff_delay_time > 0.f) { + p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); + p->cooling_data.subgrid_temp = 0.; + p->cooling_data.subgrid_fcold = 0.f; + + return; + }*/ + + /* Update the subgrid properties */ + cooling_set_particle_subgrid_properties( phys_const, us, + cosmo, hydro_props, floor_props, cooling, p, xp); + + /* No cooling happens over zero time */ + if (dt == 0.f || dt_therm == 0.f) { + return; + } + + /* If less that thermal_time has passed since last cooling, don't cool + * KIARA can't use this because the dust needs to be formed/destroyed over dt_therm + if (time - xp->cooling_data.time_last_event < cooling->thermal_time) { + return; + }*/ + + /* Compute the ISRF */ + p->cooling_data.G0 = fmax(cooling_compute_G0(p, p->cooling_data.subgrid_dens, cooling, galaxy_mstar, galaxy_ssfr, dt), 0.); + + /* Current energy */ + const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo); + //const float T_old = cooling_get_temperature( phys_const, hydro_props, us, cosmo, cooling, p, xp); // for debugging only + + /* Compute the entropy floor */ + //const double T_warm = entropy_floor_temperature(p, cosmo, floor_props); + const double T_warm = warm_ISM_temperature(p, cooling, phys_const, cosmo); + const double u_warm = + cooling_convert_temp_to_u(T_warm, xp->cooling_data.e_frac, cooling, p); + + /* If it's eligible for SF and metal-free, crudely self-enrich to + very small level; needed to kick-start Grackle dust. Arbitrary + amount to kick-start the model */ + const float init_dust_to_gas = 0.2f; + const float total_Z = chemistry_get_total_metal_mass_fraction_for_cooling(p); + const float self_Z = + (1.f - init_dust_to_gas) * cooling->self_enrichment_metallicity; + if (p->cooling_data.subgrid_temp > 0.f && total_Z < self_Z) { + float Z_sun = 0.f; + for (int i = 1; i < 10; i++) { + Z_sun += cooling->chemistry.SolarAbundances[i]; + } + + /* Distribute the self-enrichment metallicity among elements + assuming solar abundance ratios*/ + p->chemistry_data.metal_mass_fraction_total = 0.f; + p->cooling_data.dust_mass = 0.f; + /* Offset index for the SolarAbundaces array (starts at He -> Fe) */ + int j = 1; + for (int i = chemistry_element_C; i < chemistry_element_count; i++) { + /* fraction of gas mass in each element */ + p->chemistry_data.metal_mass_fraction[i] = + (1.f - init_dust_to_gas) * cooling->chemistry.SolarAbundances[j] * + cooling->self_enrichment_metallicity; + p->chemistry_data.metal_mass_fraction[i] /= Z_sun; + + /* Update to the new metal mass fraction */ + p->chemistry_data.metal_mass_fraction_total += + p->chemistry_data.metal_mass_fraction[i]; + + /* fraction of dust mass in each element */ + p->cooling_data.dust_mass_fraction[i] = + init_dust_to_gas * cooling->chemistry.SolarAbundances[j]; + p->cooling_data.dust_mass_fraction[i] /= Z_sun; + + /* Sum up all of the dust mass */ + p->cooling_data.dust_mass += + p->cooling_data.dust_mass_fraction[i] + * p->chemistry_data.metal_mass_fraction[i] * p->mass; + j++; + } + + p->chemistry_data.metal_mass_fraction[chemistry_element_He] = + cooling->chemistry.SolarAbundances[0]; + /* Since He is fixed at SolarAbundances[0], make sure the hydrogen + fraction makes sense, i.e. X_H + Y_He + Z = 1. */ + p->chemistry_data.metal_mass_fraction[chemistry_element_H] = + 1.f - p->chemistry_data.metal_mass_fraction[chemistry_element_He] + - p->chemistry_data.metal_mass_fraction_total; + + if (p->chemistry_data.metal_mass_fraction[chemistry_element_H] > 1.f || + p->chemistry_data.metal_mass_fraction[chemistry_element_H] < 0.f) { + for (int i = chemistry_element_H; i < chemistry_element_count; i++) { + warning("\telem[%d] is %g", + i, p->chemistry_data.metal_mass_fraction[i]); + } + + error("Hydrogen fraction exeeds unity or is negative for" + " particle id=%lld", p->id); + } + } + + /* Do grackle cooling */ + gr_float u_new = u_old; + u_new = cooling_grackle_driver(phys_const, us, cosmo, hydro_props, cooling, + p, xp, dt, T_warm, 0); + + /* Apply simulation-wide minimum temperature */ + u_new = max(u_new, hydro_props->minimal_internal_energy); + + /* Assign new thermal energy to particle */ + /* Calculate the cooling rate */ + float cool_du_dt = (u_new - u_old) / dt_therm; + + if (p->cooling_data.subgrid_temp == 0.) { + /* Normal cooling; check that we are not going to go + * below any of the limits */ + if (u_new > GRACKLE_HEATLIM * u_old) u_new = GRACKLE_HEATLIM * u_old; + if (u_new < GRACKLE_COOLLIM * u_old) u_new = GRACKLE_COOLLIM * u_old; + //u_new = max(u_new, u_warm); + + /* Rennehan: Recompute the actual thermal evolution after setting min/max */ + cool_du_dt = (u_new - u_old) / dt_therm; + + /* Update the internal energy time derivative, + * which will be evolved later */ + hydro_set_physical_internal_energy_dt(p, cosmo, cool_du_dt); + + /* If there is any dust outside of the ISM, sputter it + * back into gas phase metals */ + cooling_sputter_dust(us, cosmo, cooling, p, xp, dt); + } + else { + /* Particle is in subgrid mode; result is stored in subgrid_temp */ + p->cooling_data.subgrid_temp = + cooling_convert_u_to_temp(u_new, xp->cooling_data.e_frac, cooling, p); + + /* Set the subgrid cold ISM fraction for particle */ + /* Get H number density */ + const double rho = hydro_get_physical_density(p, cosmo); + const float X_H = chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; + const double n_H = rho * X_H / phys_const->const_proton_mass; + + const double fcold_max = + cooling_compute_cold_ISM_fraction(n_H, cooling); + + /* Compute cooling time in warm ISM component */ + float rhocool = p->rho * (1.f - p->cooling_data.subgrid_fcold); + rhocool = fmax(rhocool, 0.001f * p->rho); + const float u = hydro_get_comoving_internal_energy(p, xp); + float tcool = + cooling_time(phys_const, us, hydro_props, cosmo, cooling, p, xp, + rhocool, u); + + /* Evolve fcold upwards by cooling from warm ISM on + * relevant cooling timescale */ + if (tcool < 0.f) { + p->cooling_data.subgrid_fcold += + (fcold_max - p->cooling_data.subgrid_fcold) * (1.f - exp(dt / tcool)); + } + + /* Compare the adiabatic heating to the heating required to heat the + * gas back up to the equation of state line, and assume this is the + * fraction of cold cloud mass destroyed. */ + if (cooling->ism_adiabatic_heating_method == 2) { + /* Use adiabatic du/dt to evaporate cold gas clouds, into warm phase */ + const double f_evap = + hydro_get_physical_internal_energy_dt(p, cosmo) * dt_therm / + (hydro_get_physical_internal_energy(p, xp, cosmo) - u_new); + + /* If it's in the ISM of a galaxy, suppress cold fraction */ + if (f_evap > 0.f && galaxy_mstar > 0.f) { + p->cooling_data.subgrid_fcold *= max(1. - f_evap, 0.f); + } + } + + /* Set internal energy time derivative to 0 for overall particle */ + hydro_set_physical_internal_energy_dt(p, cosmo, 0.f); + + /* No cooling in warm phase since it is fixed on the EoS */ + cool_du_dt = 0.f; + + /* Force the overall particle to lie on the equation of state + hydro_set_physical_internal_energy(p, xp, cosmo, u_warm);*/ + + /* set subgrid properties for use in SF routine */ + cooling_set_particle_subgrid_properties( + phys_const, us, cosmo, hydro_props, floor_props, cooling, p, xp); + + /* Overall particle u is combination of EOS u and subgrid u */ + const float u_part = p->cooling_data.subgrid_fcold * u_new + + (1. - p->cooling_data.subgrid_fcold) * u_warm; + hydro_set_physical_internal_energy(p, xp, cosmo, u_part); + } /* subgrid mode */ + + /* Store the radiated energy */ + xp->cooling_data.radiated_energy -= hydro_get_mass(p) * cool_du_dt * dt_therm; + + /* Record this cooling event */ + xp->cooling_data.time_last_event = time; +} + +/** + * @brief Set the subgrid properties (rho, T) of the gas particle for use + * in SF routine + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param hydro_props the hydro_props struct + * @param floor_props Properties of the entropy floor. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +void cooling_set_particle_subgrid_properties( + const struct phys_const *phys_const, const struct unit_system *us, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct entropy_floor_properties *floor_props, + const struct cooling_function_data *cooling, + struct part *p, struct xpart *xp) { + + /* Get temperature of overall particle */ + const double u = hydro_get_physical_internal_energy(p, xp, cosmo); + const float temperature = + cooling_convert_u_to_temp(u, xp->cooling_data.e_frac, cooling, p); + + /* Get density */ + const double rho = hydro_get_physical_density(p, cosmo); + + /* Subgrid model is on if particle is in the Jeans EOS regime */ + const double T_warm = warm_ISM_temperature(p, cooling, phys_const, cosmo); + //entropy_floor_gas_temperature( rho, rho_com, cosmo, floor_props); + const double u_warm = + cooling_convert_temp_to_u(T_warm, xp->cooling_data.e_frac, cooling, p); + + /* Check if it is in subgrid mode: Must be in Jeans EoS regime + * and have nonzero cold gas */ + if (T_warm > 0 && u < u_warm * cooling->entropy_floor_margin) { + /* YES: If first time in subgrid, set temperature to particle T, + otherwise limit to particle T */ + if (p->cooling_data.subgrid_temp == 0.) { + p->cooling_data.subgrid_temp = temperature; + /* Reset grackle subgrid quantities assuming neutral gas */ + cooling_grackle_init_part(cooling, p, xp); + /* Initialize ISM cold fraction */ + p->cooling_data.subgrid_fcold = cooling->cold_ISM_frac; + } + else { + /* Subgrid temperature should be no higher + * than overall particle temperature */ + p->cooling_data.subgrid_temp = + min(p->cooling_data.subgrid_temp, temperature); + } + + /* Compute subgrid density assuming pressure equilibrium */ + const float X_H = chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; + const double n_H = rho * X_H / phys_const->const_proton_mass; + p->cooling_data.subgrid_dens = + cooling_compute_subgrid_density(rho, n_H, temperature, + p->cooling_data.subgrid_temp, + cooling); + } + else { + /* NO: subgrid density is the actual particle's physical density */ + p->cooling_data.subgrid_dens = rho; + + /* set subgrid temperature to 0 indicating it's not in subgrid mode */ + p->cooling_data.subgrid_temp = 0.f; + + /* No more cold gas! */ + p->cooling_data.subgrid_fcold = 0.f; + } +} + +/* + * @brief Returns the subgrid temperature of a particle. + * + * @param p The particle. + * @param xp The extended particle data. + * @return The subgrid temperature in internal units. + */ +float cooling_get_subgrid_temperature(const struct part *p, + const struct xpart *xp) { + return p->cooling_data.subgrid_temp; +} + +/* + * @brief Returns the subgrid density of a particle. + * + * @param p The particle. + * @param xp The extended particle data. + * @return The subgrid density in physical internal units. + */ +float cooling_get_subgrid_density(const struct part *p, + const struct xpart *xp) { + return p->cooling_data.subgrid_dens; +} + +/** + * @brief Compute the y-Compton contribution of a #part based on the cooling + * function. + * + * Does not exist in this model. We return 0. + * + * @param phys_const #phys_const data structure. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cosmo #cosmology data structure. + * @param cooling #cooling_function_data struct. + * @param p #part data. + * @param xp Pointer to the #xpart data. + */ +double Cooling_get_ycompton(const struct phys_const* phys_const, + const struct hydro_props* hydro_props, + const struct unit_system* us, + const struct cosmology* cosmo, + const struct cooling_function_data* cooling, + const struct part* p, const struct xpart* xp) { + + return 0.; +} + +/** + * @brief Computes the cooling time-step. + * + * We return FLT_MAX so as to impose no limit on the time-step, + * since Grackle sub-cycles the cooling as needed. + * + * @param cooling The #cooling_function_data used in the run. + * @param phys_const The physical constants in internal units. + * @param cosmo The #cosmology. + * @param us The internal system of units. + * @param hydro_props The #hydro_props. + * @param p Pointer to the particle data. + * @param xp Pointer to the particle extra data + */ +float cooling_timestep(const struct cooling_function_data* restrict cooling, + const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, + const struct unit_system* restrict us, + const struct hydro_props* hydro_props, + const struct part* restrict p, + const struct xpart* restrict xp) { + + return FLT_MAX; +} + +/** + * @brief Split the coolong content of a particle into n pieces + * + * @param p The #part. + * @param xp The #xpart. + * @param n The number of pieces to split into. + */ +void cooling_split_part(struct part* p, struct xpart* xp, double n) { + + xp->cooling_data.radiated_energy /= n; +} + +/** + * @brief Initialises the cooling unit system. + * + * @param us The current internal system of units. + * @param phys_const The #phys_const. + * @param cooling The cooling properties to initialize + */ +void cooling_init_units(const struct unit_system* us, + const struct phys_const* phys_const, + struct cooling_function_data* cooling) { + + /* These are conversions from code units to cgs. */ + + /* first cosmo: + units for the expansion factor, + use cosmological redshift! + a_value is updated in cooling_update() */ + cooling->units.a_units = 1.0; + if (cooling->redshift == -1) { + cooling->units.a_value = 0.01; + } + else { + cooling->units.a_value = 1.0; + } + + /* We assume here all quantities to be in physical coordinate */ + cooling->units.comoving_coordinates = 0; + + /* CMB temperature */ + cooling->T_CMB_0 = phys_const->const_T_CMB_0 * + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + /* then units */ + /* converts physical density to cgs number density for H */ + cooling->units.density_units = + units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + cooling->units.length_units = + units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); + cooling->units.time_units = units_cgs_conversion_factor(us, UNIT_CONV_TIME); + cooling->units.velocity_units = + units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); + + cooling->temp_to_u_factor = + phys_const->const_boltzmann_k / (hydro_gamma_minus_one * + phys_const->const_proton_mass * + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE)); + cooling->dudt_units = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS) / + units_cgs_conversion_factor(us, UNIT_CONV_TIME); + + /* converts galaxy sSFR into G0 by scaling to MW values */ + const double time_to_yr = units_cgs_conversion_factor(us, UNIT_CONV_TIME) / + (365.25f * 24.f * 60.f * 60.f); + const double mass_to_solar_mass = 1.f / phys_const->const_solar_mass; + const double length_to_pc = + units_cgs_conversion_factor(us, UNIT_CONV_LENGTH) / 3.08567758e18f; + + cooling->time_to_Myr = time_to_yr * 1.e-6; + + /* G0 for MW=1.6 (Parravano etal 2003). */ + /* Scaled to SFR density in solar neighborhood =0.002 Mo/Gyr/pc^3 + (J. Isern 2019) */ + cooling->G0_factor1 = 1.6f * mass_to_solar_mass / + (0.002f * time_to_yr * 1.e-9) / + (length_to_pc * length_to_pc * length_to_pc); + + /* Calibrated to sSFR for MW=2.71e-11 (Licquia etal 2015) */ + cooling->G0_factor2 = 1.6f / (2.71e-11f * time_to_yr); + + /* Calibrated to 0.015 SNe per year for MW (BC Reed 2005) */ + cooling->G0_factorSNe = 1.6f / 0.015; +} + +/** + * @brief Initialises Grackle. + * + * @param cooling The cooling properties to initialize + */ +void cooling_init_grackle(struct cooling_function_data* cooling) { + +#ifdef SWIFT_DEBUG_CHECKS + /* enable verbose for grackle */ + grackle_verbose = 1; +#endif + + chemistry_data* chemistry = &cooling->chemistry; + + /* Create a chemistry object for parameters and rate data. */ + if (set_default_chemistry_parameters(chemistry) == 0) { + error("Error in set_default_chemistry_parameters."); + } + + // Set parameter values for chemistry & cooling + + // Flag to activate the grackle machinery: + chemistry->use_grackle = 2; // grackle on (duh) + /* Flag to include radiative cooling and actually update the thermal energy + * during the chemistry solver. If off, the chemistry species will still be + * updated. The most common reason to set this to off is to iterate the + * chemistry network to an equilibrium state. Default: 1. */ + chemistry->with_radiative_cooling = 1; + + /* Flag to control which primordial chemistry network is used (set by Config + * file) */ + chemistry->primordial_chemistry = COOLING_GRACKLE_MODE; + + /** Flag to enable H2 formation on dust grains, dust cooling, and dust-gas + * heat transfer follow Omukai (2000). This assumes that the dust to gas ratio + * scales with the metallicity. Default: 0. */ + chemistry->h2_on_dust = 0; + + /* Flag to enable metal cooling using the Cloudy tables. If enabled, the + * cooling table to be used must be specified with the grackle_data_file + * parameter. Default: 0. */ + chemistry->metal_cooling = cooling->with_metal_cooling; + + /* Flag to enable an effective CMB temperature floor. This is implemented by + * subtracting the value of the cooling rate at TCMB from the total cooling + * rate. Default: 1. */ + chemistry->cmb_temperature_floor = 1; + + /* Flag to enable a UV background. If enabled, the cooling table to be used + * must be specified with the grackle_data_file parameter. Default: 0. */ + chemistry->UVbackground = cooling->with_uv_background; + + /* Path to the data file containing the metal cooling and UV background + * tables: */ + chemistry->grackle_data_file = cooling->cloudy_table; + + /* The ratio of specific heats for an ideal gas. A direct calculation for the + * molecular component is used if primordial_chemistry > 1. Default: 5/3. */ + chemistry->Gamma = hydro_gamma; + + /* Flag to control which three-body H2 formation rate is used. */ + chemistry->three_body_rate = 0; + + /* Flag to enable H2 collision-induced emission cooling from Ripamonti & Abel + * (2004). Default: 0. */ + chemistry->cie_cooling = 0; + + /* Flag to enable H2 cooling attenuation from Ripamonti & Abel (2004). + * Default: 0 */ + chemistry->h2_optical_depth_approximation = 0; + /* Flag to enable a spatially uniform heating term approximating + * photo-electric heating from dust from Tasker & Bryan (2008). Default: 0. */ + chemistry->photoelectric_heating = + 0; + /* photo-electric on [but not adjusted to local background, beware!] */ + chemistry->photoelectric_heating_rate = 8.5e-26; + + /* Flag to enable Compton heating from an X-ray background following Madau & + * Efstathiou (1999). Default: 0. */ + chemistry->Compton_xray_heating = 0; + + /* Intensity of a constant Lyman-Werner H2 photo-dissociating radiation field, + * in units of 10-21 erg s-1 cm-2 Hz-1 sr-1. Default: 0. */ + chemistry->LWbackground_intensity = 0; + + /* Flag to enable suppression of Lyman-Werner flux due to Lyman-series + * absorption + * (giving a sawtooth pattern), taken from Haiman & Abel, & Rees (2000). + * Default: 0. */ + chemistry->LWbackground_sawtooth_suppression = 0; + + /* volumetric heating rates is being provided in the volumetric_heating_rate + * field of grackle_field_data */ + chemistry->use_volumetric_heating_rate = + cooling->provide_volumetric_heating_rates; + + /* specific heating rates is being provided in the specific_heating_rate field + * of grackle_field_data */ + chemistry->use_specific_heating_rate = + cooling->provide_specific_heating_rates; + + /* Set parameters of temperature floor: 0=none, 1=provide scalar, + * 2=provide array */ + chemistry->use_temperature_floor = 2; + + /* arrays of ionization and heating rates from radiative transfer solutions + * are being provided */ + chemistry->use_radiative_transfer = 0; + + /* must be enabled to couple the passed radiative transfer fields to the + * chemistry solver */ + chemistry->radiative_transfer_coupled_rate_solver = 0; + + /* enable intermediate stepping in applying radiative transfer fields to + * chemistry solver. */ + chemistry->radiative_transfer_intermediate_step = 0; + + /* only use hydrogen ionization and heating rates from the radiative transfer + * solutions. */ + chemistry->radiative_transfer_hydrogen_only = 0; + + /* Use Rahmati+13 self-shielding; 0=none, 1=HI only, 2=HI+HeI, 3=HI+HeI but + * set HeII rates to 0 */ + chemistry->self_shielding_method = cooling->self_shielding_method; + + /* control behaviour of Grackle sub-step integrator */ + chemistry->max_iterations = cooling->max_step; + chemistry->exit_after_iterations_exceeded = 0; + chemistry->accuracy = cooling->timestep_accuracy; + + /* control behaviour of Grackle sub-step integration damping */ + if (cooling->grackle_damping_interval > 0) { + chemistry->use_subcycle_timestep_damping = 1; + chemistry->subcycle_timestep_damping_interval = + cooling->grackle_damping_interval; + } + else { + chemistry->use_subcycle_timestep_damping = 0; + chemistry->subcycle_timestep_damping_interval = 0; + } + /* run on a single thread since Swift sends each particle to a single thread + *chemistry->omp_nthreads = 1; */ + + /* Turn on Li+ 2019 dust evolution model */ + chemistry->use_dust_evol = cooling->use_grackle_dust_evol; + chemistry->use_dust_density_field = cooling->use_grackle_dust_evol; + + /* Load dust evolution parameters */ + if (cooling->use_grackle_dust_evol == 1) { + chemistry->dust_destruction_eff = cooling->dust_destruction_eff; + chemistry->sne_coeff = cooling->dust_sne_coeff; + chemistry->sne_shockspeed = cooling->dust_sne_shockspeed; + chemistry->dust_grainsize = cooling->dust_grainsize; + chemistry->dust_growth_densref = cooling->dust_growth_densref; + chemistry->dust_growth_tauref = cooling->dust_growth_tauref; + /* Enable dust temperature calculation using ISRF */ + chemistry->metal_cooling = 1; + chemistry->dust_chemistry = 1; + chemistry->h2_on_dust = 1; + chemistry->use_isrf_field = 1; + chemistry->H2_self_shielding = 4; + /* 2 means we specify the H2 shielding + length ourselves (the gas smoothing length) */ + chemistry->H2_custom_shielding = 2; + /* Solar abundances to pass to Grackle: + He (10.93 in units where log[H]=12, so photospheric mass fraction + -> Y=0.2485 [Hydrogen X=0.7381]; Anders+Grevesse Y=0.2485, X=0.7314) + C (8.43 -> 2.38e-3, AG=3.18e-3) + N (7.83 -> 0.70e-3, AG=1.15e-3) + O (8.69 -> 5.79e-3, AG=9.97e-3) + Ne (7.93 -> 1.26e-3, AG=1.72e-3) + Mg (7.60 -> 7.14e-4, AG=6.75e-4) + Si (7.51 -> 6.71e-4, AG=7.30e-4) + S (7.12 -> 3.12e-4, AG=3.80e-4) + Ca (6.34 -> 0.65e-4, AG=0.67e-4) + Fe (7.50 -> 1.31e-3, AG=1.92e-3) + */ + chemistry->SolarAbundances[0] = 0.2485; + chemistry->SolarAbundances[1] = 2.38e-3; + chemistry->SolarAbundances[2] = 0.70e-3; + chemistry->SolarAbundances[3] = 5.79e-3; + chemistry->SolarAbundances[4] = 1.26e-3; + chemistry->SolarAbundances[5] = 7.14e-4; + chemistry->SolarAbundances[6] = 6.71e-3; + chemistry->SolarAbundances[7] = 3.12e-4; + chemistry->SolarAbundances[8] = 0.65e-4; + chemistry->SolarAbundances[9] = 1.31e-3; + } else { + chemistry->use_dust_evol = 0; + } + + cooling->use_grackle_h2_form = + cooling->use_grackle_dust_evol && COOLING_GRACKLE_MODE >= 2; + + /* Initialize the chemistry object. */ + if (initialize_chemistry_data(&cooling->units) == 0) { + error("Error in initialize_chemistry_data."); + } +} + +/** + * @brief Initialises the cooling properties. + * + * @param parameter_file The parsed parameter file. + * @param us The current internal system of units. + * @param phys_const The physical constants in internal units. + * @param hydro_props The properties of the hydro scheme. + * @param cooling The cooling properties to initialize + */ +void cooling_init_backend(struct swift_params* parameter_file, + const struct unit_system* us, + const struct phys_const* phys_const, + const struct hydro_props* hydro_props, + struct cooling_function_data* cooling) { + + if (GRACKLE_NPART != 1) + error("Grackle with multiple particles not implemented"); + + /* read parameters */ + cooling_read_parameters(parameter_file, cooling, phys_const, us); + + /* Set up the units system. */ + cooling_init_units(us, phys_const, cooling); + + /* Set up grackle */ + cooling_init_grackle(cooling); +} + +/** + * @brief Clean-up the memory allocated for the cooling routines + * + * @param cooling the cooling data structure. + */ +void cooling_clean(struct cooling_function_data* cooling) { + //_free_chemistry_data(&cooling->chemistry, &grackle_rates); +} + +/** + * @brief Write a cooling struct to the given FILE as a stream of bytes. + * + * Nothing to do beyond writing the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + */ +void cooling_struct_dump(const struct cooling_function_data* cooling, + FILE* stream) { + restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, "cooling", "cooling function"); +} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * Nothing to do beyond reading the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + * @param cosmo #cosmology structure + */ +void cooling_struct_restore(struct cooling_function_data* cooling, FILE* stream, + const struct cosmology* cosmo) { + restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, NULL, "cooling function"); + + /* Set up grackle */ + cooling_init_grackle(cooling); +} diff --git a/src/cooling/KIARA/cooling.h b/src/cooling/KIARA/cooling.h new file mode 100644 index 0000000000..75bb4fcb29 --- /dev/null +++ b/src/cooling/KIARA/cooling.h @@ -0,0 +1,609 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 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 . + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_KIARA_H +#define SWIFT_COOLING_KIARA_H + +/** + * @file src/cooling/KIARA/cooling.h + * @brief Cooling using the GRACKLE 3.1.1 library. + */ + +/* Some standard headers. */ +#include +#include +#include + +/* The grackle library itself */ +#include + +/* Local includes. */ +#include "chemistry.h" +#include "cooling_io.h" +#include "cooling_properties.h" +#include "entropy_floor.h" +#include "error.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" +#include "fof.h" + +/* need to rework (and check) code if changed */ +#define GRACKLE_NPART 1 +#define GRACKLE_RANK 3 +#if COOLING_GRACKLE_MODE >= 2 + #define N_SPECIES 46 /* This further includes properties for dust model */ +#else + #define N_SPECIES 21 /* This includes extra values at end to hold rho,u,dudt,vx,vy,vz,u_floor,mZ,dummyvar */ +#endif + +/* define heating and cooling limits on thermal energy, per timestep */ +#define GRACKLE_HEATLIM 1000.f +#define GRACKLE_COOLLIM 0.01f +#define MAX_COLD_ISM_FRACTION 0.9f +/* Minimum particle column length as a fraction of p->h. + * Should be <= mean interparticle spacing. + * For 48 Ngb mean spacing ~ 0.887 + * For 57 Ngb mean spacing ~ 0.837 + * For 114 Ngb mean spacing ~ 0.664 + * + * Basically a limiter on rho / |grad rho| for a depth + * for shielding, and how bad you think |grad rho| + * really is at estimating the depth. */ +#define MIN_SHIELD_H_FRAC 0.332f + +void cooling_update(const struct phys_const *phys_const, + const struct cosmology *cosmo, + const struct pressure_floor_props *pressure_floor, + struct cooling_function_data *cooling, struct space *s, + const double time); + +void cooling_print_fractions(const struct xpart* restrict xp); +void cooling_first_init_part(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct hydro_props* hydro_properties, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* cooling, + struct part* restrict p, + struct xpart* restrict xp); +void cooling_post_init_part(const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct hydro_props *hydro_props, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + const struct part *restrict p, struct xpart *restrict xp); + +void cooling_print_backend(const struct cooling_function_data* cooling); + +void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, + const struct xpart* xp, gr_float rho, + gr_float species_densities[N_SPECIES]); +void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, + const struct xpart* xp, + const struct cooling_function_data* restrict cooling, + const double dt, gr_float rho, + gr_float species_densities[N_SPECIES]); +void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, + const struct xpart* xp, gr_float rho, + gr_float species_densities[N_SPECIES]); +void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho); +void cooling_copy_from_grackle2(grackle_field_data* data, struct part* p, + struct xpart* xp, + const struct cooling_function_data* restrict cooling, + gr_float rho); +void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho); +void cooling_copy_to_grackle(grackle_field_data* data, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* p, const struct xpart* xp, + const double dt, const double T_floor, + gr_float species_densities[N_SPECIES], + int mode); +void cooling_copy_from_grackle(grackle_field_data* data, struct part* p, + struct xpart* xp, + const struct cooling_function_data* restrict cooling, + gr_float rho); +void cooling_grackle_free_data(grackle_field_data* data); +gr_float cooling_grackle_driver(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_properties, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, + struct xpart* restrict xp, double dt, + double T_floor, + int mode); + +gr_float cooling_time(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct hydro_props* hydro_properties, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, struct xpart* restrict xp, + const float rhocool, const float ucool); + +float cooling_get_temperature( + const struct phys_const* restrict phys_const, + const struct hydro_props* hydro_properties, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, const struct xpart* restrict xp); + +void firehose_cooling_and_dust( + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* restrict hydro_props, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, const double dt); + +void cooling_cool_part(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_properties, + const struct entropy_floor_properties* floor_props, + const struct pressure_floor_props *pressure_floor_props, + const struct cooling_function_data* restrict cooling, + const struct fof_props* fof_props, + struct part* restrict p, struct xpart* restrict xp, + const double dt, const double dt_therm, + const double time); + +void cooling_set_particle_subgrid_properties( + const struct phys_const *phys_const, const struct unit_system *us, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct entropy_floor_properties *floor_props, + const struct cooling_function_data *cooling, + struct part *p, struct xpart *xp); + +float cooling_get_subgrid_temperature(const struct part *p, + const struct xpart *xp); + +float cooling_get_subgrid_density(const struct part *p, const struct xpart *xp); + +float cooling_get_radiated_energy(const struct xpart* restrict xp); + +double cooling_get_ycompton(const struct phys_const* phys_const, + const struct hydro_props* hydro_props, + const struct unit_system* us, + const struct cosmology* cosmo, + const struct cooling_function_data* cooling, + const struct part* p, const struct xpart* xp); + +float cooling_timestep(const struct cooling_function_data* restrict cooling, + const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, + const struct unit_system* restrict us, + const struct hydro_props* hydro_properties, + const struct part* restrict p, + const struct xpart* restrict xp); + +void cooling_split_part(struct part* p, struct xpart* xp, double n); + +void cooling_init_units(const struct unit_system* us, + const struct phys_const* phys_const, + struct cooling_function_data* cooling); +void cooling_init_grackle(struct cooling_function_data* cooling); + +void cooling_init_backend(struct swift_params* parameter_file, + const struct unit_system* us, + const struct phys_const* phys_const, + const struct hydro_props* hydro_props, + struct cooling_function_data* cooling); + +void cooling_clean(struct cooling_function_data* cooling); +void cooling_struct_dump(const struct cooling_function_data* cooling, + FILE* stream); +void cooling_struct_restore(struct cooling_function_data* cooling, FILE* stream, + const struct cosmology* cosmo); +void cooling_sputter_dust( + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, const double dt); + +/** + * @brief Compute the electron pressure of a #part based on the cooling + * function. + * + * Does not exist in this model. We return 0. + * + * @param phys_const #phys_const data structure. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cosmo #cosmology data structure. + * @param cooling #cooling_function_data struct. + * @param p #part data. + * @param xp Pointer to the #xpart data. + */ +INLINE static double cooling_get_electron_pressure( + const struct phys_const* phys_const, const struct hydro_props* hydro_props, + const struct unit_system* us, const struct cosmology* cosmo, + const struct cooling_function_data* cooling, const struct part* p, + const struct xpart* xp) { + return 0; +} + +/** + * @brief Compute the specific thermal energy (physical) for a given temperature. + * + * Converts T to u (internal physical units) for a given particle. + * + * @param temperature Particle temperature in K + * @param ne Electron number density relative to H atom density + * @param cooling #cooling_function_data struct. + * @param p #part data. + */ +INLINE static double cooling_convert_temp_to_u( + const double temperature, const double ne, const struct cooling_function_data* cooling, + const struct part* p) { + + const float X_H = chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; + const float yhelium = (1. - X_H) / (4. * X_H); + const float mu = (1. + yhelium) / (1. + ne + 4. * yhelium); + + return temperature * mu * cooling->temp_to_u_factor; +} + +/** + * @brief Compute the temperature for a given physical specific energy + * + * Converts T to u (internal physical units) for a given particle. + * + * @param u Physical specific energy + * @param ne Electron number density relative to H atom density + * @param cooling #cooling_function_data struct. + * @param p #part data. + */ +INLINE static double cooling_convert_u_to_temp( + const double u, const double ne, const struct cooling_function_data* cooling, + const struct part* p) { + + const float X_H = chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; + const float yhelium = (1. - X_H) / (4. * X_H); + const float mu = (1. + yhelium) / (1. + ne + 4. * yhelium); + + return u / (mu * cooling->temp_to_u_factor); +} + +/** + * @brief Compute the cold ISM fraction at a given factor above subgrid threshold density + * + * Compute the cold ISM fraction at a given factor above subgrid threshold density. + * This uses a fit to the density vs. cold gas fraction relation from Springel+Hernquist 2003. + * + * @param dens_fac Density factor above threshold density + * @param cooling #cooling_function_data struct. + */ +INLINE static double cooling_compute_cold_ISM_fraction( + const double n_H, const struct cooling_function_data* cooling) { + + float fc = cooling->cold_ISM_frac; + float dens_fac = n_H * cooling->subgrid_threshold_n_H_inv; + if (dens_fac > 1.) { + fc = cooling->cold_ISM_frac + (1. - cooling->cold_ISM_frac) * (1. - exp(-log10(dens_fac))); + fc = fmin(fc, MAX_COLD_ISM_FRACTION); + } + return fc; +} + +/** + * @brief Compute the subgrid density based on pressure equilibrium in a 2-phase ISM model + * + * We set the subgrid density based on pressure equilibrium with overall particle. + * The pressure is set by 1-cold_ISM_frac of the mass in the warm phase. + * + * @param rho SPH (non-subgrid) physical particle density. + * @param n_H SPH (non-subgrid) physical particle H number density. + * @param temp SPH (non-subgrid) particle temperature. + * @param subgrid_temp Subgrid particle temperature. + * @param cooling #cooling_function_data struct. + */ +INLINE static double cooling_compute_subgrid_density( + const double rho, + const double n_H, const double temp, const double subgrid_temp, + const struct cooling_function_data* cooling) { + + const double ism_frac = + cooling_compute_cold_ISM_fraction(n_H * cooling->subgrid_threshold_n_H_inv, cooling); + double subgrid_dens = + (1.f - ism_frac) * rho * temp / (ism_frac * subgrid_temp); + + /* Cap at max value which should be something vaguely like GMC densities */ + subgrid_dens = fmin(subgrid_dens, cooling->max_subgrid_density); + return subgrid_dens; +} + +/** + * @brief Return warm ISM temperature if above SF threshold density, otherwise 0. + * + * @param p Pointer to the particle data. + * @param cooling The properties of the cooling function. + * @param us The unit system. + * @param phys_const The physical constant in internal units. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE float warm_ISM_temperature( + const struct part *restrict p, const struct cooling_function_data *cooling, + const struct phys_const* phys_const, const struct cosmology* cosmo) { + + float temperature = 0.f; + +/* 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; + const double rho_com = hydro_get_comoving_density(p); + const double rho_phys = hydro_get_physical_density(p, cosmo); + + if (rho_com >= rho_crit_baryon * 100.f) { + const double n_H = rho_phys * 0.75 / phys_const->const_proton_mass; + if (n_H * cooling->subgrid_threshold_n_H_inv > 1.f ) { + const float EOS_slope = cooling->subgrid_warm_ism_EOS; + temperature = cooling->subgrid_threshold_T * pow(n_H * cooling->subgrid_threshold_n_H_inv, EOS_slope); + } + } + return temperature; +} + +/** + * @brief Copy all grackle fields into a new set. THIS IS ONLY USED FOR DEBUGGING. + * + * @param my_fields The target (new) set of grackle particle properties. + * @param old_fields The original (old) set of grackle particle properties. + * @param field_size Number of particles to copy. + */ +INLINE static void cooling_copy_grackle_fields(grackle_field_data *my_fields, + grackle_field_data *old_fields, + int field_size) { + int i; + + for (i = 0; i < field_size; i++) { + + printf("loop copy_grackle_fields %g %p\n",old_fields->density[0],my_fields->density); + + my_fields->density[i] = old_fields->density[i]; + my_fields->HI_density[i] = old_fields->HI_density[i]; + my_fields->HII_density[i] = old_fields->HII_density[i]; + my_fields->HM_density[i] = old_fields->HM_density[i]; + my_fields->HeI_density[i] = old_fields->HeI_density[i]; + my_fields->HeII_density[i] = old_fields->HeII_density[i]; + my_fields->HeIII_density[i] = old_fields->HeIII_density[i]; + my_fields->H2I_density[i] = old_fields->H2I_density[i]; + my_fields->H2II_density[i] = old_fields->H2II_density[i]; + my_fields->DI_density[i] = old_fields->DI_density[i]; + my_fields->DII_density[i] = old_fields->DII_density[i]; + my_fields->HDI_density[i] = old_fields->HDI_density[i]; + my_fields->e_density[i] = old_fields->e_density[i]; + // solar metallicity + my_fields->metal_density[i] = old_fields->metal_density[i]; + + my_fields->x_velocity[i] = 0.0; + my_fields->y_velocity[i] = 0.0; + my_fields->z_velocity[i] = 0.0; + + // initilize internal energy (here 1000 K for no reason) + my_fields->internal_energy[i] = old_fields->internal_energy[i]; + + my_fields->volumetric_heating_rate[i] = old_fields->volumetric_heating_rate[i]; + my_fields->specific_heating_rate[i] = old_fields->specific_heating_rate[i]; + my_fields->temperature_floor[i] = old_fields->temperature_floor[i]; + + my_fields->isrf_habing[i] = old_fields->isrf_habing[i]; + my_fields->RT_HI_ionization_rate[i] = old_fields->RT_HI_ionization_rate[i]; + my_fields->RT_HeI_ionization_rate[i] = old_fields->RT_HeI_ionization_rate[i]; + my_fields->RT_HeII_ionization_rate[i] = old_fields->RT_HeII_ionization_rate[i]; + my_fields->RT_H2_dissociation_rate[i] = old_fields->RT_H2_dissociation_rate[i]; + my_fields->RT_heating_rate[i] = old_fields->RT_heating_rate[i]; + + if (grackle_data->use_dust_evol) { + my_fields->dust_density[i] = old_fields->dust_density[i]; + my_fields->He_gas_metalDensity[i] = old_fields->He_gas_metalDensity[i]; + my_fields->C_gas_metalDensity[i] = old_fields->C_gas_metalDensity[i]; + my_fields->N_gas_metalDensity[i] = old_fields->N_gas_metalDensity[i]; + my_fields->O_gas_metalDensity[i] = old_fields->O_gas_metalDensity[i]; + my_fields->Ne_gas_metalDensity[i] = old_fields->Ne_gas_metalDensity[i]; + my_fields->Mg_gas_metalDensity[i] = old_fields->Mg_gas_metalDensity[i]; + my_fields->Si_gas_metalDensity[i] = old_fields->Si_gas_metalDensity[i]; + my_fields->S_gas_metalDensity[i] = old_fields->S_gas_metalDensity[i]; + my_fields->Ca_gas_metalDensity[i] = old_fields->Ca_gas_metalDensity[i]; + my_fields->Fe_gas_metalDensity[i] = old_fields->Fe_gas_metalDensity[i]; + my_fields->He_dust_metalDensity[i] = old_fields->He_dust_metalDensity[i]; + my_fields->C_dust_metalDensity[i] = old_fields->C_dust_metalDensity[i]; + my_fields->N_dust_metalDensity[i] = old_fields->N_dust_metalDensity[i]; + my_fields->O_dust_metalDensity[i] = old_fields->O_dust_metalDensity[i]; + my_fields->Ne_dust_metalDensity[i] = old_fields->Ne_dust_metalDensity[i]; + my_fields->Mg_dust_metalDensity[i] = old_fields->Mg_dust_metalDensity[i]; + my_fields->Si_dust_metalDensity[i] = old_fields->Si_dust_metalDensity[i]; + my_fields->S_dust_metalDensity[i] = old_fields->S_dust_metalDensity[i]; + my_fields->Ca_dust_metalDensity[i] = old_fields->Ca_dust_metalDensity[i]; + my_fields->Fe_dust_metalDensity[i] = old_fields->Fe_dust_metalDensity[i]; + my_fields->SNe_ThisTimeStep[i] = old_fields->SNe_ThisTimeStep[i]; + } + } + printf("done copy_grackle_fields\n"); + + return; +} + +/** + * @brief Allocate a new set of grackle fields in memory. THIS IS ONLY USED FOR DEBUGGING. + * + * @param my_fields The target (new) set of grackle particle properties. + * @param field_size Number of particles to copy. + * @param dust_flag Are we using grackle's dust model (Jones, Smith, Dave 2024)? + */ +INLINE static void cooling_grackle_malloc_fields(grackle_field_data *my_fields, int field_size, int dust_flag) +{ + my_fields->density = malloc(field_size * sizeof(gr_float)); + my_fields->internal_energy = malloc(field_size * sizeof(gr_float)); + my_fields->x_velocity = malloc(field_size * sizeof(gr_float)); + my_fields->y_velocity = malloc(field_size * sizeof(gr_float)); + my_fields->z_velocity = malloc(field_size * sizeof(gr_float)); + // for primordial_chemistry >= 1 + my_fields->HI_density = malloc(field_size * sizeof(gr_float)); + my_fields->HII_density = malloc(field_size * sizeof(gr_float)); + my_fields->HeI_density = malloc(field_size * sizeof(gr_float)); + my_fields->HeII_density = malloc(field_size * sizeof(gr_float)); + my_fields->HeIII_density = malloc(field_size * sizeof(gr_float)); + my_fields->e_density = malloc(field_size * sizeof(gr_float)); + // for primordial_chemistry >= 2 + my_fields->HM_density = malloc(field_size * sizeof(gr_float)); + my_fields->H2I_density = malloc(field_size * sizeof(gr_float)); + my_fields->H2II_density = malloc(field_size * sizeof(gr_float)); + // for primordial_chemistry >= 3 + my_fields->DI_density = malloc(field_size * sizeof(gr_float)); + my_fields->DII_density = malloc(field_size * sizeof(gr_float)); + my_fields->HDI_density = malloc(field_size * sizeof(gr_float)); + // for metal_cooling = 1 + my_fields->metal_density = malloc(field_size * sizeof(gr_float)); + + // volumetric heating rate (provide in units [erg s^-1 cm^-3]) + my_fields->volumetric_heating_rate = malloc(field_size * sizeof(gr_float)); + // specific heating rate (provide in units [egs s^-1 g^-1] + my_fields->specific_heating_rate = malloc(field_size * sizeof(gr_float)); + my_fields->temperature_floor = malloc(field_size * sizeof(gr_float)); + + // radiative transfer ionization / dissociation rate fields (provide in units [1/s]) + my_fields->RT_HI_ionization_rate = malloc(field_size * sizeof(gr_float)); + my_fields->RT_HeI_ionization_rate = malloc(field_size * sizeof(gr_float)); + my_fields->RT_HeII_ionization_rate = malloc(field_size * sizeof(gr_float)); + my_fields->RT_H2_dissociation_rate = malloc(field_size * sizeof(gr_float)); + // radiative transfer heating rate field (provide in units [erg s^-1 cm^-3]) + my_fields->RT_heating_rate = malloc(field_size * sizeof(gr_float)); + + // H2 model + my_fields->H2_self_shielding_length = malloc(field_size * sizeof(gr_float)); + my_fields->H2_custom_shielding_factor = malloc(field_size * sizeof(gr_float)); + my_fields->isrf_habing = malloc(field_size * sizeof(gr_float)); + + if (dust_flag) { + my_fields->dust_density = malloc(field_size * sizeof(gr_float)); + my_fields->He_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->C_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->N_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->O_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Ne_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Mg_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Si_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->S_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Ca_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Fe_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->He_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->C_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->N_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->O_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Ne_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Mg_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Si_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->S_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Ca_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Fe_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->SNe_ThisTimeStep = malloc(field_size * sizeof(gr_float)); + } + return; +} + +/** + * @brief Free a set of grackle fields from memory. THIS IS ONLY USED FOR DEBUGGING. + * + * @param my_fields The target (new) set of grackle particle properties. + * @param dust_flag Are we using grackle's dust model (Jones, Smith, Dave 2024)? + */ +INLINE static void cooling_grackle_free_fields(grackle_field_data *my_fields, int dust_flag) +{ + free(my_fields->grid_dimension ); + free(my_fields->grid_start ); + free(my_fields->grid_end ); + + free(my_fields->density ); + free(my_fields->internal_energy ); + free(my_fields->x_velocity ); + free(my_fields->y_velocity ); + free(my_fields->z_velocity ); + // for primordial_chemistry >= 1 + free(my_fields->HI_density ); + free(my_fields->HII_density ); + free(my_fields->HeI_density ); + free(my_fields->HeII_density ); + free(my_fields->HeIII_density ); + free(my_fields->e_density ); + // for primordial_chemistry >= 2 + free(my_fields->HM_density ); + free(my_fields->H2I_density ); + free(my_fields->H2II_density ); + // for primordial_chemistry >= 3 + free(my_fields->DI_density ); + free(my_fields->DII_density ); + free(my_fields->HDI_density ); + // for metal_cooling = 1 + free(my_fields->metal_density ); + + // volumetric heating rate (provide in units [erg s^-1 cm^-3]) + free(my_fields->volumetric_heating_rate ); + // specific heating rate (provide in units [egs s^-1 g^-1] + free(my_fields->specific_heating_rate ); + free(my_fields->temperature_floor ); + + // radiative transfer ionization / dissociation rate fields (provide in units [1/s]) + free(my_fields->RT_HI_ionization_rate ); + free(my_fields->RT_HeI_ionization_rate ); + free(my_fields->RT_HeII_ionization_rate ); + free(my_fields->RT_H2_dissociation_rate ); + // radiative transfer heating rate field (provide in units [erg s^-1 cm^-3]) + free(my_fields->RT_heating_rate ); + + // H2 model + free(my_fields->H2_self_shielding_length ); + free(my_fields->H2_custom_shielding_factor ); + free(my_fields->isrf_habing ); + + if (dust_flag) { + free(my_fields->dust_density ); + free(my_fields->He_gas_metalDensity ); + free(my_fields->C_gas_metalDensity ); + free(my_fields->N_gas_metalDensity ); + free(my_fields->O_gas_metalDensity ); + free(my_fields->Ne_gas_metalDensity ); + free(my_fields->Mg_gas_metalDensity ); + free(my_fields->Si_gas_metalDensity ); + free(my_fields->S_gas_metalDensity ); + free(my_fields->Ca_gas_metalDensity ); + free(my_fields->Fe_gas_metalDensity ); + free(my_fields->He_dust_metalDensity ); + free(my_fields->C_dust_metalDensity ); + free(my_fields->N_dust_metalDensity ); + free(my_fields->O_dust_metalDensity ); + free(my_fields->Ne_dust_metalDensity ); + free(my_fields->Mg_dust_metalDensity ); + free(my_fields->Si_dust_metalDensity ); + free(my_fields->S_dust_metalDensity ); + free(my_fields->Ca_dust_metalDensity ); + free(my_fields->Fe_dust_metalDensity ); + free(my_fields->SNe_ThisTimeStep ); + } + return; +} +#endif /* SWIFT_COOLING_KIARA_H */ diff --git a/src/cooling/KIARA/cooling_debug.h b/src/cooling/KIARA/cooling_debug.h new file mode 100644 index 0000000000..f9d549e129 --- /dev/null +++ b/src/cooling/KIARA/cooling_debug.h @@ -0,0 +1,54 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_KIARA_DEBUG_H +#define SWIFT_COOLING_KIARA_DEBUG_H + +__attribute__((always_inline)) INLINE static void cooling_debug_particle( + const struct part* p, const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] cooling_xpart_data:", p->id); + warning("[PID%lld] radiated_energy = %.3e, time_last_event = %.3e", p->id, + xp->cooling_data.radiated_energy, xp->cooling_data.time_last_event); + +#if COOLING_GRACKLE_MODE >= 1 + warning( + "[PID%lld] HI_frac = %.3e, HII_frac = %.3e, HeI_frac = %.3e, HeII_frac " + "= " + "%.3e, " + "HeIII_frac = %.3e, e_frac = %.3e", + p->id, xp->cooling_data.HI_frac, xp->cooling_data.HII_frac, + xp->cooling_data.HeI_frac, xp->cooling_data.HeII_frac, + xp->cooling_data.HeIII_frac, xp->cooling_data.e_frac); +#if COOLING_GRACKLE_MODE >= 2 + warning("[PID%lld] HM_frac = %.3e, H2I_frac = %.3e, H2II_frac = %.3e", + p->id, xp->cooling_data.HM_frac, xp->cooling_data.H2I_frac, + xp->cooling_data.H2II_frac); +#if COOLING_GRACKLE_MODE >= 3 + warning("[PID%lld] DI_frac = %.3e, DII_frac = %.3e, HDI_frac = %.3e", p->id, + xp->cooling_data.DI_frac, xp->cooling_data.DII_frac, + xp->cooling_data.HDI_frac); +#endif +#endif +#endif + warning("[PID%lld] metal_frac = %.3e", p->id, xp->cooling_data.metal_frac); + } +} + +#endif /* SWIFT_COOLING_KIARA_DEBUG_H */ diff --git a/src/cooling/KIARA/cooling_io.h b/src/cooling/KIARA/cooling_io.h new file mode 100644 index 0000000000..3d6a745b69 --- /dev/null +++ b/src/cooling/KIARA/cooling_io.h @@ -0,0 +1,380 @@ + +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 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 . + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_KIARA_IO_H +#define SWIFT_COOLING_KIARA_IO_H + +/* Local includes */ +#include "cooling_properties.h" +#include "cooling_struct.h" +#include "io_properties.h" +#include "physical_constants.h" + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of cooling to the file + * + * @param h_grp The HDF5 group in which to write + * @param h_grp_columns The HDF5 group containing named columns + * @param cooling The #cooling_function_data + */ +__attribute__((always_inline)) INLINE static void cooling_write_flavour( + hid_t h_grp, hid_t h_grp_columns, + const struct cooling_function_data* cooling) { + +#if COOLING_GRACKLE_MODE == 0 + io_write_attribute_s(h_grp, "Cooling Model", "Grackle0"); +#elif COOLING_GRACKLE_MODE == 1 + io_write_attribute_s(h_grp, "Cooling Model", "Grackle1"); +#elif COOLING_GRACKLE_MODE == 2 + io_write_attribute_s(h_grp, "Cooling Model", "Grackle2"); +#elif COOLING_GRACKLE_MODE == 3 + io_write_attribute_s(h_grp, "Cooling Model", "Grackle3"); +#else + error("This function should be called only with one of the Grackle cooling."); +#endif +} +#endif + +INLINE static void convert_part_HI_mass(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + + *ret = hydro_get_mass(p) * xp->cooling_data.HI_frac; +} + +INLINE static void convert_part_H2_mass(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + + float H2_frac = 0.; + //const float X_H = chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; +#if COOLING_GRACKLE_MODE >= 2 + H2_frac = xp->cooling_data.H2I_frac + xp->cooling_data.H2II_frac; + *ret = hydro_get_mass(p) * p->cooling_data.subgrid_fcold * H2_frac; +#else + if ( p->sf_data.SFR > 0 ) H2_frac = 1. - xp->cooling_data.HI_frac; + *ret = hydro_get_mass(p) * H2_frac; +#endif +} + +INLINE static void convert_part_HII_mass(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + + *ret = hydro_get_mass(p) * xp->cooling_data.HII_frac; +} + +INLINE static void convert_part_HeI_mass(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + + *ret = hydro_get_mass(p) * xp->cooling_data.HeI_frac; +} + +INLINE static void convert_part_HeII_mass(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + + *ret = hydro_get_mass(p) * xp->cooling_data.HeII_frac; +} + +INLINE static void convert_part_HeIII_mass(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + + *ret = hydro_get_mass(p) * xp->cooling_data.HeIII_frac; +} + +INLINE static void convert_part_e_density(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + + *ret = (float)xp->cooling_data.e_frac; +} + +#ifdef RT_NONE +INLINE static void convert_mass_fractions(const struct engine* engine, + const struct part* part, + const struct xpart* xpart, + float* ret) { + + ret[0] = (float)xpart->cooling_data.HI_frac; + ret[1] = (float)xpart->cooling_data.HII_frac; +} +#endif + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param xparts The extra particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +__attribute__((always_inline)) INLINE static int cooling_write_particles( + const struct part* parts, const struct xpart* xparts, + struct io_props* list) { + + int num = 0; + +#if COOLING_GRACKLE_MODE >= 1 + /* List what we want to write */ + list[num] = io_make_output_field_convert_part( + "AtomicHydrogenMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, + convert_part_HI_mass, "Atomic hydrogen (HI) masses."); + num ++; + + list[num] = io_make_output_field_convert_part( + "IonizedHydrogenMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, + convert_part_HII_mass, "Ionized hydrogen (HII) masses."); + num ++; + + list[num] = + io_make_output_field_convert_part( + "MolecularHydrogenMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, + convert_part_H2_mass, "Molecular hydrogen (H2) masses."); + num ++; + + list[num] = + io_make_output_field_convert_part( + "HeIMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, + convert_part_HeII_mass, "HeI masses."); + num ++; + + list[num] = + io_make_output_field_convert_part( + "HeIIMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, + convert_part_HeII_mass, "HeII masses."); + num ++; + + list[num] = + io_make_output_field_convert_part( + "HeIIIMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, + convert_part_HeIII_mass, "HeIII masses."); + num ++; + + list[num] = io_make_output_field_convert_part( + "ElectronNumberDensities", FLOAT, 1, UNIT_CONV_NUMBER_DENSITY, -3.f, + parts, xparts, convert_part_e_density, + "Electron number densities"); + num ++; + +#if COOLING_GRACKLE_MODE >= 2 + list[num] = + io_make_output_field("SubgridTemperatures", + FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + cooling_data.subgrid_temp, + "Temperature of subgrid ISM in K"); + num ++; + + list[num] = + io_make_output_field("SubgridDensities", + FLOAT, 1, UNIT_CONV_DENSITY, -3.f, parts, + cooling_data.subgrid_dens, + "Mass density in physical units of subgrid ISM"); + num ++; + + list[num] = + io_make_output_field("SubgridColdISMFraction", + FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + cooling_data.subgrid_fcold, + "Fraction of particle mass in cold subgrid ISM"); + num ++; + + list[num] = + io_make_output_field("DustMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + cooling_data.dust_mass, "Total mass in dust"); + num ++; + + list[num] = io_make_output_field( + "DustMassFractions", FLOAT, chemistry_element_count, + UNIT_CONV_NO_UNITS, 0.f, parts, cooling_data.dust_mass_fraction, + "Fractions of the particles' masses that are in dust for a given element"); + num++; + + list[num] = + io_make_output_field("DustTemperatures", + FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + cooling_data.dust_temperature, + "Dust temperature in subgrid dust model, in K"); + num ++; + + list[num] = + io_make_output_field("CoolingTime", + FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, + cooling_data.mixing_layer_cool_time, + "Cooling time for particle; if it's currently a firehose wind" + "particle (delay_time>0), this is the mixing layer cooling time"); + num ++; +#endif +#endif + +#ifdef RT_NONE + list[num] = io_make_output_field_convert_part( + "IonMassFractions", FLOAT, 2, UNIT_CONV_NO_UNITS, 0, parts, + xparts, convert_mass_fractions, + "Mass fractions of all constituent species"); + num ++; +#endif + return num; +} + +/** + * @brief Parser the parameter file and initialize the #cooling_function_data + * + * @param parameter_file The parser parameter file + * @param cooling The cooling properties to initialize + * @param phys_const The #phys_const. + */ +__attribute__((always_inline)) INLINE static void cooling_read_parameters( + struct swift_params* parameter_file, struct cooling_function_data* cooling, + const struct phys_const* phys_const, const struct unit_system* us) { + + parser_get_param_string(parameter_file, "KIARACooling:cloudy_table", + cooling->cloudy_table); + + cooling->with_uv_background = + parser_get_param_int(parameter_file, "KIARACooling:with_UV_background"); + + cooling->redshift = + parser_get_param_double(parameter_file, "KIARACooling:redshift"); + + cooling->with_metal_cooling = + parser_get_param_int(parameter_file, "KIARACooling:with_metal_cooling"); + + cooling->provide_volumetric_heating_rates = parser_get_opt_param_int( + parameter_file, "KIARACooling:provide_volumetric_heating_rates", -1); + + cooling->provide_specific_heating_rates = parser_get_opt_param_int( + parameter_file, "KIARACooling:provide_specific_heating_rates", 1); + + /* Use lookup tables when outside ISM */ + cooling->use_tables_outside_ism = parser_get_opt_param_int( + parameter_file, "KIARACooling:use_tables_outside_ism", 0); + + /* Self shielding */ + cooling->self_shielding_method = parser_get_opt_param_int( + parameter_file, "KIARACooling:self_shielding_method", 3); + + /* What to do with adiabatic du/dt when in ISM mode */ + cooling->ism_adiabatic_heating_method = parser_get_opt_param_int( + parameter_file, "KIARACooling:ism_adiabatic_heating_method", 1); + + /* Initial step convergence */ + cooling->max_step = + parser_get_opt_param_int(parameter_file, + "KIARACooling:grackle_max_steps", + 500); + + cooling->timestep_accuracy = + parser_get_opt_param_double(parameter_file, + "KIARACooling:timestep_accuracy", 0.2); + + cooling->grackle_damping_interval = + parser_get_opt_param_double(parameter_file, + "KIARACooling:grackle_damping_interval", 5); + + cooling->thermal_time = + parser_get_opt_param_double(parameter_file, + "KIARACooling:thermal_time_myr", 0.); + cooling->thermal_time *= phys_const->const_year * 1e6; + + /* flag to turn on dust evolution option, only works for GRACKLE_CHEMISTRY>=2 (KIARA) */ + cooling->use_grackle_dust_evol = + parser_get_opt_param_int(parameter_file, + "KIARACooling:use_grackle_dust_evol", 1); +#if COOLING_GRACKLE_MODE <= 1 + message("WARNING: Dust evol not implemented in SIMBA; use KIARA instead."); + cooling->use_grackle_dust_evol = 0; +#endif + + /* These are dust parameters for KIARA's dust model (MODE>=2); irrelevant otherwise */ + cooling->dust_destruction_eff = + parser_get_opt_param_double(parameter_file, + "KIARACooling:dust_destruction_eff", 0.3); + + cooling->dust_sne_coeff = + parser_get_opt_param_double(parameter_file, + "KIARACooling:dust_sne_coeff", 1.0); + + cooling->dust_sne_shockspeed = + parser_get_opt_param_double(parameter_file, + "KIARACooling:dust_sne_shockspeed", 100.0); + + cooling->dust_grainsize = + parser_get_opt_param_double(parameter_file, + "KIARACooling:dust_grainsize", 0.1); + + cooling->dust_growth_densref = + parser_get_opt_param_double(parameter_file, + "KIARACooling:dust_growth_densref", 2.3e-20); + + cooling->dust_growth_tauref = + parser_get_opt_param_double(parameter_file, + "KIARACooling:dust_growth_tauref", 1.0); + + cooling->cold_ISM_frac = + parser_get_opt_param_double(parameter_file, + "KIARACooling:cold_ISM_frac", 1.0); + + cooling->G0_computation_method = + parser_get_opt_param_double(parameter_file, + "KIARACooling:G0_computation_method", 3); + + cooling->max_subgrid_density = + parser_get_opt_param_double(parameter_file, + "KIARACooling:max_subgrid_density_g_p_cm3", + FLT_MAX); + /* convert to internal units */ + cooling->max_subgrid_density /= units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + + cooling->subgrid_threshold_n_H_inv = + parser_get_opt_param_double(parameter_file, + "KIARACooling:subgrid_threshold_n_H_cgs", 0.13); + /* convert to internal units, take inverse to save compute time */ + cooling->subgrid_threshold_n_H_inv /= units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); + cooling->subgrid_threshold_n_H_inv = 1.f / cooling->subgrid_threshold_n_H_inv; + + cooling->subgrid_threshold_T = + parser_get_opt_param_double(parameter_file, + "KIARACooling:subgrid_threshold_T_K", 1.e4); + /* convert to internal units */ + cooling->subgrid_threshold_T /= units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + cooling->subgrid_warm_ism_EOS = + parser_get_opt_param_double(parameter_file, + "KIARACooling:subgrid_warm_ism_EOS", 0.f); + + cooling->entropy_floor_margin = + parser_get_opt_param_double(parameter_file, + "KIARACooling:entropy_floor_margin_dex", + 1.0); + cooling->entropy_floor_margin = pow(10.f, cooling->entropy_floor_margin); + + cooling->self_enrichment_metallicity = + parser_get_opt_param_double(parameter_file, + "KIARACooling:self_enrichment_metallicity", + 0.f); + +} + +#endif /* SWIFT_COOLING_KIARA_IO_H */ diff --git a/src/cooling/KIARA/cooling_properties.h b/src/cooling/KIARA/cooling_properties.h new file mode 100644 index 0000000000..0e48fa1f0a --- /dev/null +++ b/src/cooling/KIARA/cooling_properties.h @@ -0,0 +1,139 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 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 . + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_PROPERTIES_KIARA_H +#define SWIFT_COOLING_PROPERTIES_KIARA_H + + +/* include grackle */ +#include + +/** + * @file src/cooling/grackle/cooling_properties.h + * @brief Empty infrastructure for the cases without cooling function + */ + +/** + * @brief Properties of the cooling function. + */ +struct cooling_function_data { + + /*! Filename of the Cloudy Table */ + char cloudy_table[200]; + + /*! Enable/Disable UV backgroud */ + int with_uv_background; + + /*! Redshift to use for the UV backgroud (-1 to use cosmological one) */ + double redshift; + + /*! unit system */ + code_units units; + + /*! Temperature of the CMB at present day (for quick access) */ + double T_CMB_0; + + /*! k_Boltz/m_p plus conversion factor for converting u<->T */ + double temp_to_u_factor; + + /*! Convert time to Myr */ + double time_to_Myr; + + /*! conversion unit factor for rate of change of thermal energy */ + double dudt_units; + + /*! grackle chemistry data */ + chemistry_data chemistry; + + /*! Enable/Disable metal cooling */ + int with_metal_cooling; + + /*! User provide volumetric heating rates */ + int provide_volumetric_heating_rates; + + /*! User provide specific heating rates */ + int provide_specific_heating_rates; + + /*! Self shielding method (1 -> 3 for grackle's ones, 0 for none and -1 for + * GEAR) */ + int self_shielding_method; + + /*! What to do with adiabatic du/dt when in ISM mode: 0=no adiabatic heating, + * 1=evolve in grackle, 2=use to evaporate cold ism */ + int ism_adiabatic_heating_method; + + /*! number of step max for first init */ + int max_step; + + /*! max fractional change in quantities in single grackle substep */ + double timestep_accuracy; + + /*! parameter to control how fast grackle damps oscillatory behaviour (lower=more aggressive) */ + int grackle_damping_interval; + + /*! Duration for switching off cooling after an event (e.g. supernovae) */ + double thermal_time; + + /*! track dust growth and destruction (only available in KIARA) */ + int use_grackle_dust_evol; + + /*! track H2 formation; this is set within the code based on selection options */ + int use_grackle_h2_form; + + /*! G0 conversion factors, scales to MW value based on local/global galaxy props */ + double G0_factor1; + double G0_factor2; + double G0_factorSNe; + + /*! Dust parameters; see sample yml file */ + double dust_destruction_eff; + double dust_sne_coeff; + double dust_sne_shockspeed; + double dust_grainsize; + double dust_growth_densref; + double dust_growth_tauref; + + /*! For dust model, need self-enrichment up to a small metallicity to kick-start dust */ + double self_enrichment_metallicity; + + /*! For subgrid model (eg KIARA) need a subgrid ISM fraction */ + double cold_ISM_frac; + + /*! For Grackle subgrid model, choose way to determine G0: 1=Local SFR density; 2=Global sSFR */ + int G0_computation_method; + + /*! For Grackle subgrid model, set max density to avoid pointlessly over-iterating in Grackle */ + double max_subgrid_density; + + /*! For Grackle subgrid model, inverse of threshold nH above which multi-phase ISM model kicks in */ + double subgrid_threshold_n_H_inv; + + /*! For Grackle subgrid model, temperature at threshold nH */ + double subgrid_threshold_T; + + /*! For Grackle subgrid model, Power-law eqn of state for warm ISM component above threshold n_H */ + double subgrid_warm_ism_EOS; + + /*! For Grackle subgrid model, factor above entropy floor allowed to be in subgrid mode */ + double entropy_floor_margin; + + /*! Option to use Cloudy lookup tables when outside ISM */ + int use_tables_outside_ism; +}; + +#endif /* SWIFT_COOLING_PROPERTIES_KIARA_H */ diff --git a/src/cooling/KIARA/cooling_struct.h b/src/cooling/KIARA/cooling_struct.h new file mode 100644 index 0000000000..de3e2dcb35 --- /dev/null +++ b/src/cooling/KIARA/cooling_struct.h @@ -0,0 +1,100 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 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 . + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_STRUCT_KIARA_H +#define SWIFT_COOLING_STRUCT_KIARA_H + +/** + * * @brief Properties of the cooling stored in the #part data. + * */ +struct cooling_part_data { + + /*! Subgrid temperature */ + float subgrid_temp; + + /*! Subgrid density (internal units, physical frame) */ + float subgrid_dens; + + /*! Subgrid fraction of cold mass */ + float subgrid_fcold; + +#if COOLING_GRACKLE_MODE >= 2 + /*! Dust properties when use_grackle_dust_evol=1 */ + + /* Total mass in dust */ + float dust_mass; // total mass in dust + + /* Fraction of each metal in dust */ + float dust_mass_fraction[chemistry_element_count]; + + /* Temperature of subgrid ISM */ + float dust_temperature; +#endif + + /*! Cooling time in mixing layer between stream and ambient gas */ + float mixing_layer_cool_time; + + /*! The strength of the interstellar radiation field in Habing units */ + float G0; + +#if COOLING_GRACKLE_MODE >= 2 + /*! Number of SNe (of any type) going off in nearby stars */ + float SNe_ThisTimeStep; +#endif +}; + +/** + * @brief Properties of the cooling stored in the extra particle data + */ +struct cooling_xpart_data { + + /*! Energy radiated away by this particle since the start of the run */ + float radiated_energy; + + /*! Last time the cooling was switch off */ + double time_last_event; + +/*! here all fractions are mass fraction */ +#if COOLING_GRACKLE_MODE >= 1 + float HI_frac; + float HII_frac; + float HeI_frac; + float HeII_frac; + float HeIII_frac; + float e_frac; + +#if COOLING_GRACKLE_MODE >= 2 + float HM_frac; + float H2I_frac; + float H2II_frac; + +#if COOLING_GRACKLE_MODE >= 3 + float DI_frac; + float DII_frac; + float HDI_frac; +#endif // MODE >= 3 + +#endif // MODE >= 2 + +#endif // MODE >= 1 + + /*! metal cooling = 1 */ + float metal_frac; +}; + +#endif /* SWIFT_COOLING_STRUCT_KIARA_H */ diff --git a/src/cooling_debug.h b/src/cooling_debug.h index 37d34e6bd8..ae8770ae4f 100644 --- a/src/cooling_debug.h +++ b/src/cooling_debug.h @@ -41,6 +41,8 @@ #include "./cooling/EAGLE/cooling_debug.h" #elif defined(COOLING_PS2020) #include "./cooling/PS2020/cooling_debug.h" +#elif defined(COOLING_KIARA) +#include "./cooling/KIARA/cooling_debug.h" #else #error "Invalid choice of cooling function." #endif diff --git a/src/cooling_io.h b/src/cooling_io.h index d3439b4d0a..1b84849834 100644 --- a/src/cooling_io.h +++ b/src/cooling_io.h @@ -41,6 +41,8 @@ #include "./cooling/EAGLE/cooling_io.h" #elif defined(COOLING_PS2020) #include "./cooling/PS2020/cooling_io.h" +#elif defined(COOLING_KIARA) +#include "./cooling/KIARA/cooling_io.h" #else #error "Invalid choice of cooling function." #endif diff --git a/src/cooling_properties.h b/src/cooling_properties.h index f48d153137..5383b17e15 100644 --- a/src/cooling_properties.h +++ b/src/cooling_properties.h @@ -46,6 +46,8 @@ #include "./cooling/EAGLE/cooling_properties.h" #elif defined(COOLING_PS2020) #include "./cooling/PS2020/cooling_properties.h" +#elif defined(COOLING_KIARA) +#include "./cooling/KIARA/cooling_properties.h" #else #error "Invalid choice of cooling function." #endif diff --git a/src/cooling_struct.h b/src/cooling_struct.h index b205cbfef3..b977bc0930 100644 --- a/src/cooling_struct.h +++ b/src/cooling_struct.h @@ -46,6 +46,8 @@ #include "./cooling/EAGLE/cooling_struct.h" #elif defined(COOLING_PS2020) #include "./cooling/PS2020/cooling_struct.h" +#elif defined(COOLING_KIARA) +#include "./cooling/KIARA/cooling_struct.h" #else #error "Invalid choice of cooling function." #endif diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 72641a236b..f93d8b35ce 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -447,8 +447,6 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const struct hydro_props *restrict hydro_properties, const struct cosmology *restrict cosmo) { - // if (p->dt_min == 0.f || p->decoupled || - // p->feedback_data.decoupling_delay_time > 0.f) return FLT_MAX; if (p->dt_min == 0.f) return FLT_MAX; /* Use full kernel support and physical time */ @@ -1846,14 +1844,6 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( const float h_inv = 1.0f / h; /* 1/h */ const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ - /* Decoupled particles might have no neighbours */ - if (!p->decoupled) { - warning( - "Gas particle with ID %lld treated as having no neighbours (h: %g, " - "wcount: %g).", - p->id, h, p->density.wcount); - } - /* Re-set problematic values */ p->rho = p->mass * kernel_root * h_inv_dim; p->rho_gradient[0] = 0.f; @@ -2038,9 +2028,6 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( const struct entropy_floor_properties *floor_props, const struct pressure_floor_props *pressure_floor) { - /* Wind particles do not need the extra predict evolution */ - if (p->decoupled) return; - /* Predict the internal energy */ p->u += p->u_dt * dt_therm; @@ -2194,9 +2181,6 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props) { - /* Wind particles do not need the extra kick evolution */ - if (p->decoupled) return; - /* Integrate the internal energy forward in time */ const float delta_u = p->u_dt * dt_therm; @@ -2308,13 +2292,10 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( p->debug.velocity_tensor_aux_norm[i][2] = 0.; } #endif + p->decoupled = 0; hydro_reset_acceleration(p); hydro_init_part(p, NULL); - - p->decoupled = 0; - p->to_be_decoupled = 0; - p->to_be_recoupled = 0; } /** diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 0d9b9a10e5..ce248c3ee8 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -50,10 +50,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( struct part *restrict pi, struct part *restrict pj, const float a, const float H) { - const unsigned char decoupled_i = pi->decoupled; - const unsigned char decoupled_j = pj->decoupled; - if ((decoupled_i && !decoupled_j) || (!decoupled_i && decoupled_j)) return; - /* Kernel weights to be filled */ float wi, wj, wi_dx, wj_dx; @@ -185,10 +181,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( struct part *restrict pi, const struct part *restrict pj, const float a, const float H) { - const unsigned char decoupled_i = pi->decoupled; - const unsigned char decoupled_j = pj->decoupled; - if ((decoupled_i && !decoupled_j) || (!decoupled_i && decoupled_j)) return; - /* Kernel weights to be filled */ float wi, wi_dx; @@ -284,10 +276,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( struct part *restrict pi, struct part *restrict pj, const float a, const float H) { - const unsigned char decoupled_i = pi->decoupled; - const unsigned char decoupled_j = pj->decoupled; - if (decoupled_i || decoupled_j) return; - /* Get particle properties */ const hydro_real_t mi = hydro_get_mass(pi); const hydro_real_t mj = hydro_get_mass(pj); @@ -410,10 +398,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( struct part *restrict pi, struct part *restrict pj, const float a, const float H) { - const unsigned char decoupled_i = pi->decoupled; - const unsigned char decoupled_j = pj->decoupled; - if (decoupled_i || decoupled_j) return; - /* Get particle properties */ const hydro_real_t mj = hydro_get_mass(pj); const hydro_real_t rhoi = hydro_get_comoving_density(pi); @@ -505,10 +489,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( struct part *restrict pi, struct part *restrict pj, const float a, const float H) { - const unsigned char decoupled_i = pi->decoupled; - const unsigned char decoupled_j = pj->decoupled; - if (decoupled_i || decoupled_j) return; - /* Cosmological factors entering the EoMs */ const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); const hydro_real_t a2_Hubble = a * a * H; @@ -959,6 +939,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( #endif } + /* If one or both are decoupled they do not contribute to accel or u_dt */ + if (pi->decoupled || pj->decoupled) return; + /* Get the time derivative for v. */ /* Assemble the acceleration */ @@ -1007,10 +990,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( struct part *restrict pi, const struct part *restrict pj, const float a, const float H) { - const unsigned char decoupled_i = pi->decoupled; - const unsigned char decoupled_j = pj->decoupled; - if (decoupled_i || decoupled_j) return; - /* Cosmological factors entering the EoMs */ const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); const hydro_real_t a2_Hubble = a * a * H; @@ -1446,6 +1425,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( #endif } + /* If one or both are decoupled they do not contribute to accel or u_dt */ + if (pi->decoupled || pj->decoupled) return; + /* Assemble the acceleration */ const hydro_real_t acc[3] = { sph_acc_term * G_ij[0] + visc_acc_term * G_rad_ij[0], diff --git a/src/hydro/MAGMA2/hydro_io.h b/src/hydro/MAGMA2/hydro_io.h index 414f519586..6205774efd 100644 --- a/src/hydro/MAGMA2/hydro_io.h +++ b/src/hydro/MAGMA2/hydro_io.h @@ -254,34 +254,6 @@ INLINE static void hydro_write_particles(const struct part *parts, "Co-moving gravitational Plummer-equivalent softenings of the particles"); num++; - /* - list[num] = io_make_output_field( - "NumberOfTimesDecoupled", INT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, - feedback_data.number_of_times_decoupled, - "The integer number of times a particle was decoupled from " - "the hydro. Black hole wind events are encoded in thousands, " - "jet events in hundreds of thousands."); - num++; - - list[num] = io_make_output_field( - "DecouplingDelayTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, - feedback_data.decoupling_delay_time, - "Time remaining until the particle recouples to the hydro."); - num++; - - list[num] = io_make_output_field( - "CoolingShutOffTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, - feedback_data.cooling_shutoff_delay_time, - "Time remaining until cooling is allowed again."); - num++; - - list[num] = io_make_output_field( - "InternalEnergiesDt", FLOAT, 1, UNIT_CONV_U_DT, - -3.f * hydro_gamma_minus_one, parts, u_dt, - "Comoving rate of change of specific thermal energy (u_dt)."); - num++; - */ - #ifdef MAGMA2_DEBUG_CHECKS list[num] = io_make_output_field("NumberOfNeighbours", INT, 1, UNIT_CONV_NO_UNITS, diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index f96af92a8c..73756a27d9 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -297,15 +297,6 @@ struct part { } force; }; - /*! Flag for decoupling from the hydrodynamics/feedback routines */ - unsigned char decoupled; - - /*! Flag to indicate that the decoupling task will run */ - unsigned char to_be_decoupled; - - /*! Flag to indicate that the recoupling task will run */ - unsigned char to_be_recoupled; - /*! Additional data used for adaptive softening */ struct adaptive_softening_part_data adaptive_softening_data; @@ -321,11 +312,6 @@ struct part { /*! Additional data used by the feedback */ struct feedback_part_data feedback_data; -#ifdef WITH_FOF_GALAXIES - /*! Additional data used by the FoF */ - struct galaxy_data galaxy_data; -#endif - /*! Black holes information (e.g. swallowing ID) */ struct black_holes_part_data black_holes_data; @@ -353,6 +339,9 @@ struct part { /*! Time-step limiter information */ struct timestep_limiter_data limiter_data; + /*! Flag to indicate particle is decoupled */ + int decoupled; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/runner_doiact_functions_hydro.h b/src/runner_doiact_functions_hydro.h index 9795578798..941c537011 100644 --- a/src/runner_doiact_functions_hydro.h +++ b/src/runner_doiact_functions_hydro.h @@ -390,6 +390,7 @@ void DOSELF1_NAIVE(struct runner *r, const struct cell *c, const integertime_t t_current = e->ti_current; const int with_cosmology = (e->policy & engine_policy_cosmology); const struct chemistry_global_data *chem_data = e->chemistry; + struct xpart *xparts = c->hydro.xparts; #endif TIMER_TIC; @@ -420,6 +421,9 @@ void DOSELF1_NAIVE(struct runner *r, const struct cell *c, /* Get a hold of the ith part in ci. */ struct part *restrict pi = &parts[pid]; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *restrict xpi = &xparts[pid]; +#endif /* Skip inhibited particles. */ if (part_is_inhibited(pi, e)) continue; @@ -437,6 +441,9 @@ void DOSELF1_NAIVE(struct runner *r, const struct cell *c, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts[pjd]; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *restrict xpj = &xparts[pjd]; +#endif /* Skip inhibited particles. */ if (part_is_inhibited(pj, e)) continue; @@ -488,8 +495,9 @@ void DOSELF1_NAIVE(struct runner *r, const struct cell *c, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, - t_current, cosmo, with_cosmology, chem_data); + runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, time_base, + t_current, cosmo, with_cosmology, + e->physical_constants, e->chemistry); #endif } else if (doi) { @@ -584,6 +592,9 @@ void DOSELF2_NAIVE(struct runner *r, const struct cell *c, const int count = c->hydro.count; struct part *parts = c->hydro.parts; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *xparts = c->hydro.xparts; +#endif /* Get the depth limits (if any) */ const char min_depth = limit_max_h ? c->depth : 0; @@ -600,6 +611,9 @@ void DOSELF2_NAIVE(struct runner *r, const struct cell *c, /* Get a hold of the ith part in ci. */ struct part *restrict pi = &parts[pid]; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *restrict xpi = &xparts[pid]; +#endif /* Skip inhibited particles. */ if (part_is_inhibited(pi, e)) continue; @@ -617,6 +631,9 @@ void DOSELF2_NAIVE(struct runner *r, const struct cell *c, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts[pjd]; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *restrict xpj = &xparts[pjd]; +#endif /* Skip inhibited particles. */ if (part_is_inhibited(pj, e)) continue; @@ -668,8 +685,9 @@ void DOSELF2_NAIVE(struct runner *r, const struct cell *c, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, - t_current, cosmo, with_cosmology, chem_data); + runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, time_base, + t_current, cosmo, with_cosmology, + e->physical_constants, e->chemistry); #endif } else if (doi) { @@ -1654,6 +1672,10 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, const int count_j = cj->hydro.count; struct part *restrict parts_i = ci->hydro.parts; struct part *restrict parts_j = cj->hydro.parts; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *restrict xparts_i = ci->hydro.xparts; + struct xpart *restrict xparts_j = cj->hydro.xparts; +#endif /* Cosmological terms and physical constants */ const float a = cosmo->a; @@ -1731,6 +1753,9 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, /* Get a hold of the ith part in ci. */ struct part *pi = &parts_i[sort_i[pid].i]; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *xpi = &xparts_i[sort_i[pid].i]; +#endif const char depth_i = pi->depth_h; /* Skip inhibited particles. */ @@ -1855,7 +1880,10 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, /* Recover pj */ struct part *pj = &parts_j[sort_j[pjd].i]; - const char depth_j = pj->depth_h; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *xpj = &xparts_j[sort_j[pjd].i]; +#endif + const char depth_j = pj->depth_h; /* Skip inhibited particles. */ if (part_is_inhibited(pj, e)) continue; @@ -1940,8 +1968,9 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, - t_current, cosmo, with_cosmology, chem_data); + runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, time_base, + t_current, cosmo, with_cosmology, + e->physical_constants, e->chemistry); #endif } else { @@ -1982,6 +2011,9 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, /* Get a hold of the jth part in cj. */ struct part *pj = &parts_j[sort_j[pjd].i]; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *xpj = &xparts_j[sort_j[pjd].i]; +#endif const char depth_j = pj->depth_h; /* Skip inhibited particles. */ @@ -2107,6 +2139,9 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, /* Recover pi */ struct part *pi = &parts_i[sort_i[pid].i]; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *xpi = &xparts_i[sort_i[pid].i]; +#endif const char depth_i = pi->depth_h; /* Skip inhibited particles. */ @@ -2191,8 +2226,9 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, - t_current, cosmo, with_cosmology, chem_data); + runner_iact_diffusion(r2, dx, hj, hi, pj, pi, xpj, xpi, a, H, time_base, + t_current, cosmo, with_cosmology, + e->physical_constants, e->chemistry); #endif } else { @@ -2308,6 +2344,9 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, TIMER_TIC; struct part *parts = c->hydro.parts; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *xparts = c->hydro.xparts; +#endif const int count = c->hydro.count; /* Get the depth limits (if any) */ @@ -2348,6 +2387,9 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, /* Get a pointer to the ith particle. */ struct part *restrict pi = &parts[pid]; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *restrict xpi = &xparts[pid]; +#endif const char depth_i = pi->depth_h; /* Skip inhibited particles. */ @@ -2370,6 +2412,9 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, /* Get a pointer to the jth particle. (by construction pi != pj) */ struct part *restrict pj = &parts[indt[pjd]]; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *restrict xpj = &xparts[indt[pjd]]; +#endif /* This particle's (square of) search radius. */ const float hj = pj->h; @@ -2411,9 +2456,9 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_nonsym_diffusion(r2, dx, hj, hi, pj, pi, a, H, time_base, - t_current, cosmo, with_cosmology, - chem_data); + runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, time_base, + t_current, cosmo, with_cosmology, + e->physical_constants, chem_data); #endif } } /* loop over all the particles we want to update. */ @@ -2434,7 +2479,10 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, /* Get a pointer to the jth particle (by construction pi != pj). */ struct part *restrict pj = &parts[pjd]; - const char depth_j = pj->depth_h; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *restrict xpj = &xparts[pjd]; +#endif + const char depth_j = pj->depth_h; /* Skip inhibited particles. */ if (part_is_inhibited(pj, e)) continue; @@ -2495,8 +2543,9 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, - t_current, cosmo, with_cosmology, chem_data); + runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, time_base, + t_current, cosmo, with_cosmology, + e->physical_constants, chem_data); #endif } else if (doi) { @@ -2632,6 +2681,9 @@ void DOSELF2(struct runner *r, const struct cell *c, const int limit_min_h, TIMER_TIC; struct part *parts = c->hydro.parts; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *xparts = c->hydro.xparts; +#endif const int count = c->hydro.count; /* Get the depth limits (if any) */ @@ -2672,6 +2724,9 @@ void DOSELF2(struct runner *r, const struct cell *c, const int limit_min_h, /* Get a pointer to the ith particle. */ struct part *restrict pi = &parts[pid]; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *restrict xpi = &xparts[pid]; +#endif const char depth_i = pi->depth_h; /* Skip inhibited particles. */ @@ -2758,6 +2813,9 @@ void DOSELF2(struct runner *r, const struct cell *c, const int limit_min_h, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts[pjd]; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) + struct xpart *restrict xpj = &xparts[pjd]; +#endif const char depth_j = pj->depth_h; /* Skip inhibited particles. */ @@ -2820,8 +2878,9 @@ void DOSELF2(struct runner *r, const struct cell *c, const int limit_min_h, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_diffusion(r2, dx, hi, hj, pi, pj, a, H, time_base, - t_current, cosmo, with_cosmology, chem_data); + runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, time_base, + t_current, cosmo, with_cosmology, + e->physical_constants, chem_data); #endif } else if (doi) { diff --git a/src/runner_ghost.c b/src/runner_ghost.c index 9710eadf71..75ab0e6a98 100644 --- a/src/runner_ghost.c +++ b/src/runner_ghost.c @@ -1207,7 +1207,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { hydro_end_density(p, cosmo); adaptive_softening_end_density(p, e->gravity_properties); mhd_end_density(p, cosmo); - chemistry_end_density(p, chemistry, cosmo); + chemistry_end_density(p, xp, chemistry, cosmo); star_formation_end_density(p, xp, star_formation, cosmo); /* Are we using the alternative definition of the diff --git a/src/runner_others.c b/src/runner_others.c index fe61e2039e..3cc9106351 100644 --- a/src/runner_others.c +++ b/src/runner_others.c @@ -132,6 +132,7 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { const struct hydro_props *hydro_props = e->hydro_properties; const struct entropy_floor_properties *entropy_floor_props = e->entropy_floor; const struct pressure_floor_props *pressure_floor = e->pressure_floor_props; + const struct fof_props *fof_props = e->fof_properties; const double time_base = e->time_base; const integertime_t ti_current = e->ti_current; struct part *restrict parts = c->hydro.parts; @@ -180,7 +181,7 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { /* Let's cool ! */ cooling_cool_part(constants, us, cosmo, hydro_props, - entropy_floor_props, pressure_floor, cooling_func, p, + entropy_floor_props, pressure_floor, cooling_func, fof_props, p, xp, dt_cool, dt_therm, time); } } @@ -733,7 +734,7 @@ void runner_do_end_hydro_force(struct runner *r, struct cell *c, int timer) { hydro_end_force(p, cosmo); mhd_end_force(p, cosmo); timestep_limiter_end_force(p); - chemistry_end_force(p, cosmo, with_cosmology, e->time, dt, + chemistry_end_force(p, xp, cosmo, with_cosmology, e->time, dt, e->chemistry); /* Apply the forcing terms (if any) */ diff --git a/swift.c b/swift.c index 50e8399a68..7cb695f090 100644 --- a/swift.c +++ b/swift.c @@ -202,6 +202,7 @@ int main(int argc, char *argv[]) { int with_line_of_sight = 0; int with_rt = 0; int with_power = 0; + int with_kiara = 0; int verbose = 0; int nr_threads = 1; int nr_pool_threads = -1; @@ -300,6 +301,12 @@ int main(int argc, char *argv[]) { "equivalent to --hydro --limiter --sync --self-gravity --stars " "--star-formation --cooling --feedback.", NULL, 0, 0), + OPT_BOOLEAN( + 0, "kiara", &with_kiara, + "Run with all the options needed for the Kiara model. This is " + "equivalent to --hydro --limiter --sync --self-gravity --stars " + "--star-formation --cooling --feedback --black-holes --fof.", + NULL, 0, 0), OPT_GROUP(" Control options:\n"), OPT_BOOLEAN('a', "pin", &with_aff, @@ -413,6 +420,19 @@ int main(int argc, char *argv[]) { with_cooling = 1; with_feedback = 1; } + if (with_kiara) { + with_hydro = 1; + with_timestep_limiter = 1; + with_timestep_sync = 1; + with_self_gravity = 1; + with_stars = 1; + with_star_formation = 1; + with_cooling = 1; + //with_hydro_decoupling = 1; + with_feedback = 1; + with_black_holes = 1; + with_fof = 1; + } #ifdef MOVING_MESH if (with_hydro) { with_grid = 1; @@ -842,7 +862,7 @@ int main(int argc, char *argv[]) { error("Cannot reconstruct m-poles every step over MPI (yet)."); #endif - /* Temporary early aborts for modes not supported with hand-vec. */ + /* Temporary early aborts for modes not supported with hand-vec. */ #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) && \ !defined(CHEMISTRY_NONE) error( @@ -1180,7 +1200,7 @@ int main(int argc, char *argv[]) { } else bzero(&sink_properties, sizeof(struct sink_props)); - /* Initialise the cooling function properties */ + /* Initialise the cooling function properties */ #ifdef COOLING_NONE if (with_cooling) { error( diff --git a/tests/test125cells.c b/tests/test125cells.c index bc6203f772..d2f420d791 100644 --- a/tests/test125cells.c +++ b/tests/test125cells.c @@ -113,7 +113,8 @@ void set_energy_state(struct part *part, enum pressure_field press, float size, part->u = pressure / (hydro_gamma_minus_one * density); #elif defined(MINIMAL_SPH) || defined(HOPKINS_PU_SPH) || \ defined(HOPKINS_PU_SPH_MONAGHAN) || defined(ANARCHY_PU_SPH) || \ - defined(SPHENIX_SPH) || defined(PHANTOM_SPH) || defined(GASOLINE_SPH) + defined(SPHENIX_SPH) || defined(PHANTOM_SPH) || defined(GASOLINE_SPH) || \ + defined(MAGMA2_SPH) part->u = pressure / (hydro_gamma_minus_one * density); #elif defined(PLANETARY_SPH) || defined(REMIX_SPH) set_idg_def(&eos.idg_def, 0); @@ -691,7 +692,7 @@ int main(int argc, char *argv[]) { for (int j = 0; j < 125; ++j) runner_do_hydro_sort(&runner, cells[j], 0x1FFF, 0, 0, 0, 0); - /* Do the density calculation */ + /* Do the density calculation */ /* Initialise the particle cache. */ #ifdef WITH_VECTORIZATION @@ -801,7 +802,7 @@ int main(int argc, char *argv[]) { #endif /* EXTRA_HYDRO_LOOP */ - /* Do the force calculation */ + /* Do the force calculation */ #ifdef WITH_VECTORIZATION /* Initialise the cache. */ From e43a18d7cc5efcf4aa37b5bfc87d114cdcd27076 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Thu, 13 Nov 2025 12:43:43 +0000 Subject: [PATCH 06/37] Add Kiara stars and star_formation routines. Compiles but probably doesn't work. --- configure.ac | 40 +- src/hydro/MAGMA2/hydro_part.h | 4 +- src/part.h | 2 + src/runner_others.c | 3 +- src/star_formation.h | 3 + src/star_formation/KIARA/star_formation.h | 1117 +++++++++++++++++ .../KIARA/star_formation_debug.h | 31 + .../KIARA/star_formation_iact.h | 73 ++ src/star_formation/KIARA/star_formation_io.h | 94 ++ .../KIARA/star_formation_logger.h | 266 ++++ .../KIARA/star_formation_logger_struct.h | 54 + .../KIARA/star_formation_struct.h | 54 + src/star_formation_debug.h | 2 + src/star_formation_iact.h | 2 + src/star_formation_io.h | 2 + src/star_formation_logger.h | 2 + src/star_formation_logger_struct.h | 2 + src/star_formation_struct.h | 2 + src/stars.h | 3 + src/stars/KIARA/stars.h | 378 ++++++ src/stars/KIARA/stars_debug.h | 31 + src/stars/KIARA/stars_iact.h | 112 ++ src/stars/KIARA/stars_io.h | 530 ++++++++ src/stars/KIARA/stars_part.h | 275 ++++ src/stars_io.h | 2 + src/timestep.h | 3 +- 26 files changed, 3049 insertions(+), 38 deletions(-) create mode 100644 src/star_formation/KIARA/star_formation.h create mode 100644 src/star_formation/KIARA/star_formation_debug.h create mode 100644 src/star_formation/KIARA/star_formation_iact.h create mode 100644 src/star_formation/KIARA/star_formation_io.h create mode 100644 src/star_formation/KIARA/star_formation_logger.h create mode 100644 src/star_formation/KIARA/star_formation_logger_struct.h create mode 100644 src/star_formation/KIARA/star_formation_struct.h create mode 100644 src/stars/KIARA/stars.h create mode 100644 src/stars/KIARA/stars_debug.h create mode 100644 src/stars/KIARA/stars_iact.h create mode 100644 src/stars/KIARA/stars_io.h create mode 100644 src/stars/KIARA/stars_part.h diff --git a/configure.ac b/configure.ac index 87fb82dbe2..2732646074 100644 --- a/configure.ac +++ b/configure.ac @@ -2195,34 +2195,6 @@ case "$with_subgrid" in enable_fof=yes enable_fof_galaxies=no ;; - SIMBA) - with_subgrid_cooling=SIMBA - with_subgrid_chemistry=EAGLE - with_subgrid_tracers=EAGLE - with_subgrid_entropy_floor=SIMBA - with_subgrid_stars=EAGLE - with_subgrid_star_formation=SIMBA - with_subgrid_feedback=SIMBA - with_subgrid_black_holes=SIMBA - with_subgrid_sink=none - with_subgrid_extra_io=none - enable_fof=yes - enable_fof_galaxies=yes - ;; - SIMBART) - with_subgrid_cooling=SIMBA - with_subgrid_chemistry=KIARA - with_subgrid_tracers=none - with_subgrid_entropy_floor=SIMBA - with_subgrid_stars=SIMBA - with_subgrid_star_formation=SIMBA - with_subgrid_feedback=KIARA - with_subgrid_black_holes=SIMBA - with_subgrid_sink=none - with_subgrid_extra_io=none - enable_fof=yes - enable_fof_galaxies=yes - ;; Obsidian) with_subgrid_cooling=KIARA with_subgrid_chemistry=KIARA @@ -2256,9 +2228,9 @@ case "$with_subgrid" in with_subgrid_chemistry=KIARA with_subgrid_tracers=none with_subgrid_entropy_floor=none - with_subgrid_stars=EAGLE - with_subgrid_star_formation=EAGLE - with_subgrid_feedback=EAGLE + with_subgrid_stars=KIARA + with_subgrid_star_formation=KIARA + with_subgrid_feedback=none with_subgrid_black_holes=none #with_subgrid_black_holes=Obsidian with_subgrid_sink=none @@ -2953,7 +2925,7 @@ esac # Stellar model. AC_ARG_WITH([stars], [AS_HELP_STRING([--with-stars=], - [Stellar model to use @<:@none, basic, EAGLE, GEAR, SIMBA, default: basic@:>@] + [Stellar model to use @<:@none, basic, EAGLE, GEAR, KIARA, default: basic@:>@] )], [with_stars="$withval"], [with_stars="basic"] @@ -2974,8 +2946,8 @@ case "$with_stars" in GEAR) AC_DEFINE([STARS_GEAR], [1], [GEAR stellar model]) ;; - SIMBA) - AC_DEFINE([STARS_SIMBA], [1], [SIMBA stellar model]) + KIARA) + AC_DEFINE([STARS_KIARA], [1], [KIARA stellar model]) ;; basic) AC_DEFINE([STARS_BASIC], [1], [Basic stellar model]) diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 73756a27d9..0a44e75a42 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -78,7 +78,7 @@ struct xpart { /* Additional data used by the tracers */ struct tracers_xpart_data tracers_data; - /* Additional data used by the tracers */ + /* Additional data used by the SF routines */ struct star_formation_xpart_data sf_data; /* Additional data used by the feedback */ @@ -316,7 +316,7 @@ struct part { struct black_holes_part_data black_holes_data; /* Additional data used by the SF routines */ - struct star_formation_xpart_data sf_data; + struct star_formation_part_data sf_data; /*! Sink information (e.g. swallowing ID) */ struct sink_part_data sink_data; diff --git a/src/part.h b/src/part.h index 9a8d70e95d..73be132823 100644 --- a/src/part.h +++ b/src/part.h @@ -123,6 +123,8 @@ struct threadpool; #include "./stars/EAGLE/stars_part.h" #elif defined(STARS_GEAR) #include "./stars/GEAR/stars_part.h" +#elif defined(STARS_KIARA) +#include "./stars/KIARA/stars_part.h" #else #error "Invalid choice of star particle" #endif diff --git a/src/runner_others.c b/src/runner_others.c index 3cc9106351..3ec26ef090 100644 --- a/src/runner_others.c +++ b/src/runner_others.c @@ -332,6 +332,7 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { const int with_cosmology = (e->policy & engine_policy_cosmology); const int with_feedback = (e->policy & engine_policy_feedback); const struct hydro_props *restrict hydro_props = e->hydro_properties; + const struct fof_props *restrict fof_props = e->fof_properties; const struct unit_system *restrict us = e->internal_units; struct cooling_function_data *restrict cooling = e->cooling_func; const struct entropy_floor_properties *entropy_floor = e->entropy_floor; @@ -414,7 +415,7 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { /* Compute the SF rate of the particle */ star_formation_compute_SFR(p, xp, sf_props, phys_const, hydro_props, - cosmo, dt_star); + fof_props, cosmo, dt_star); /* Add the SFR and SFR*dt to the SFH struct of this cell */ star_formation_logger_log_active_part(p, xp, &c->stars.sfh, dt_star); diff --git a/src/star_formation.h b/src/star_formation.h index c96fc3a53f..f598b56f95 100644 --- a/src/star_formation.h +++ b/src/star_formation.h @@ -43,6 +43,9 @@ #elif defined(STAR_FORMATION_GEAR) #define swift_star_formation_model_creates_stars 1 #include "./star_formation/GEAR/star_formation.h" +#elif defined(STAR_FORMATION_KIARA) +#define swift_star_formation_model_creates_stars 1 +#include "./star_formation/KIARA/star_formation.h" #else #error "Invalid choice of star formation law" #endif diff --git a/src/star_formation/KIARA/star_formation.h b/src/star_formation/KIARA/star_formation.h new file mode 100644 index 0000000000..1942eb473a --- /dev/null +++ b/src/star_formation/KIARA/star_formation.h @@ -0,0 +1,1117 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * 2023 Doug Rennehan (douglas.rennehan@gmail.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 . + * + *******************************************************************************/ +#ifndef SWIFT_KIARA_STAR_FORMATION_H +#define SWIFT_KIARA_STAR_FORMATION_H + +/* Local includes */ +#include "adiabatic_index.h" +#include "chemistry.h" +#include "cooling.h" +#include "cosmology.h" +#include "engine.h" +#include "entropy_floor.h" +#include "equation_of_state.h" +#include "exp10.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "random.h" +#include "stars.h" +#include "units.h" +#include "fof.h" + +#define star_formation_need_update_dx_max 0 + +/** + * @file src/star_formation/KIARA/star_formation.h + * @brief Star formation model used in the KIARA model + */ + +/** + * @brief Functional form of the star formation law and H2 model + */ +enum star_formation_H2_model { + /*z > 6) slope = FIRE_eta_lower_slope_EOR; + if (m_star > FIRE_eta_break) { + slope = FIRE_eta_upper_slope; + } + + float eta = + FIRE_eta_normalization * powf(m_star / FIRE_eta_break, slope); + + const float a_suppress_inv = + (1.f + fabs(wind_eta_suppression_redshift)); + if (wind_eta_suppression_redshift > 0 && + cosmo->z > wind_eta_suppression_redshift) { + eta *= cosmo->a * cosmo->a * a_suppress_inv * a_suppress_inv; + } + else if (wind_eta_suppression_redshift < 0) { + eta *= expf(-powf(cosmo->a * a_suppress_inv, -3.f)); + } + + return fmax(eta, 0.f); +} + +/** + * @brief Calculate if the satisfies the conditions for star formation. + * + * @param starform the star formation law properties to use. + * @param p the gas particles. + * @param xp the additional properties of the gas particles. + * @param phys_const the physical constants in internal units. + * @param cosmo the cosmological parameters and properties. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cooling The cooling data struct. + * @param entropy_floor_props The entropy floor assumed in this run. + */ +INLINE static int star_formation_is_star_forming_subgrid( + const struct part* p, const struct xpart* xp, + const struct star_formation* starform, const struct phys_const* phys_const, + const struct cosmology* cosmo, const struct hydro_props* hydro_props, + const struct unit_system* us, const struct cooling_function_data* cooling, + const struct entropy_floor_properties* entropy_floor_props) { + + const double number_density_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); + + /* Get the Hydrogen mass fraction */ + const double XH = chemistry_get_metal_mass_fraction_for_star_formation( + p)[chemistry_element_H]; + + /* Get the subgrid properties + * Note these are both in physical frame already */ + const double subgrid_T_cgs = cooling_get_subgrid_temperature(p, xp); + const double subgrid_rho = cooling_get_subgrid_density(p, xp); + const double subgrid_n_H = subgrid_rho * XH / phys_const->const_proton_mass; + const double subgrid_n_H_cgs = subgrid_n_H * number_density_to_cgs; + + /* Now, determine whether we are very cold or (cold and dense) enough + * + * This would typically be (T < 10^3 OR (T < 10^4.5 AND n_H > 10)) + * with T and n_H subgrid properties. + * + * Recall that particles above the EoS have T_sub = T and rho_sub = rho. + */ + return ( (subgrid_T_cgs < starform->subgrid_thresh.T_threshold) && + (subgrid_n_H_cgs > starform->subgrid_thresh.nH_threshold) ); +} + +/** + * @brief Calculate if the gas particle satisfies the conditions for star + * formation. + * + * @param starform the star formation law properties to use. + * @param p the gas particles. + * @param xp the additional properties of the gas particles. + * @param phys_const the physical constants in internal units. + * @param cosmo the cosmological parameters and properties. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cooling The cooling data struct. + * @param entropy_floor_props The entropy floor assumed in this run. + */ +INLINE static int star_formation_is_star_forming( + const struct part* p, const struct xpart* xp, + const struct star_formation* starform, const struct phys_const* phys_const, + const struct cosmology* cosmo, const struct hydro_props* hydro_props, + const struct unit_system* us, const struct cooling_function_data* cooling, + const struct entropy_floor_properties* entropy_floor_props) { + + /* Decide whether we should form stars or not */ + + /* No star formation for particles in the wind */ + if (p->decoupled) return 0; + + /* No star formation when outside subgrid model */ + if (cooling_get_subgrid_temperature(p, xp) <= 0.f) return 0; + + /* Minimal density (converted from mean baryonic density) + * for star formation */ + const float rho_mean_b_times_min_over_den = + cosmo->mean_density_Omega_b * starform->min_over_den; + + /* Physical density of the particle */ + const float physical_density = hydro_get_physical_density(p, cosmo); + + /* Check overdensity criterion */ + if (physical_density < rho_mean_b_times_min_over_den) return 0; + + return star_formation_is_star_forming_subgrid( + p, xp, starform, phys_const, cosmo, hydro_props, + us, cooling, entropy_floor_props); +} + +/** + * @brief Compute the star-formation rate of a given particle and store + * it into the #part. The star formation is calculated as a simple + * Schmidt law with an efficiency per free-fall time that can be specified, + * the free-fall time is based on the total SPH density. + * + * @param p #part. + * @param xp the #xpart. + * @param starform the star formation law properties to use + * @param phys_const the physical constants in internal units. + * @param hydro_props The properties of the hydro scheme. + * @param cosmo the cosmological parameters and properties. + * @param dt_star The time-step of this particle. + */ +INLINE static void star_formation_compute_SFR_schmidt_law( + struct part* p, struct xpart* xp, + const struct star_formation* starform, const struct phys_const* phys_const, + const struct hydro_props* hydro_props, const struct cosmology* cosmo, + const double dt_star) { + + /* Mass density of this particle */ + //const float physical_density = cooling_get_subgrid_density(p, xp); + const float physical_density = cooling_get_subgrid_density(p, xp); + + /* Calculate the SFR per gas mass */ + const float SFRpergasmass = + starform->schmidt_law.mdot_const * sqrt(physical_density); + + /* Store the SFR */ + p->sf_data.SFR = p->sf_data.H2_fraction * SFRpergasmass * hydro_get_mass(p); +} + +/** + * @brief Compute the star-formation rate of a given particle and store + * it into the #part. The star formation is calculated based on + * the model of Wada & Norman 2007, eq. 17 + * + * @param p #part. + * @param xp the #xpart. + * @param starform the star formation law properties to use + * @param phys_const the physical constants in internal units. + * @param hydro_props The properties of the hydro scheme. + * @param cosmo the cosmological parameters and properties. + * @param dt_star The time-step of this particle. + */ +INLINE static void star_formation_compute_SFR_wn07( + struct part* p, struct xpart* xp, + const struct star_formation* starform, const struct phys_const* phys_const, + const struct hydro_props* hydro_props, const struct fof_props* fof_props, + const struct cosmology* cosmo, const double dt_star) { + + p->sf_data.SFR = 0.f; + /* Mean density of the gas described by a lognormal PDF (i.e. dense gas). */ + const double rho_V = cooling_get_subgrid_density(p, xp); + const double rho_0 = starform->lognormal.rho0; + + /* Collect information about galaxy that the particle belongs to */ + size_t group_id = p->gpart->fof_data.group_id; + const float galaxy_mstar = fof_props->group_stellar_mass[group_id]; + const float galaxy_sfr = fof_props->group_star_formation_rate[group_id]; + const float galaxy_ssfr = galaxy_sfr / galaxy_mstar; + + /* Density is too low, so no SF */ + if (rho_V <= 1.001 * rho_0) return; + + /* Calculate SFR efficiency, which WN07 suggests should + * scale from 0.1 for starbursts to 0.001 for normal galaxies + */ + double epsc = 0.01; + /* if it's not in a galaxy, assume it's a small proto-galaxy so starburst */ + if (galaxy_mstar == 0.f) { + epsc = 0.1; + } + else if (starform->lognormal.wn07_epsc_method == 0) { /* constant value */ + epsc = 0.01; + } + /* deviation from all-galaxies main sequence taken from Koprowski+24 */ + else if (starform->lognormal.wn07_epsc_method == 1 || + starform->lognormal.wn07_epsc_method == 2) { + const double mstar = + galaxy_mstar * starform->lognormal.to_solar_mass; + const double sfrmax_data = pow(10., 3.69 - 3.81 * exp(-0.47 * cosmo->z)); + const double M0_data = pow(10., 11.91 - 2.48 * exp(-0.44 * cosmo->z)); + const double sfr_data = sfrmax_data / (1. + (M0_data / mstar)); + const double sfr_msun_per_yr = galaxy_sfr * + starform->lognormal.to_msun_per_yr; + if (starform->lognormal.wn07_epsc_method == 1) { + epsc = 0.01 * sfr_msun_per_yr / sfr_data; + } + else { + epsc = 0.01 * sqrtf(sfr_msun_per_yr / sfr_data); + } + } + /* deviation from SF-galaxies main sequence data taken from Koprowski+24 */ + else if (starform->lognormal.wn07_epsc_method == 3 || + starform->lognormal.wn07_epsc_method == 4) { + const double mstar = + galaxy_mstar * starform->lognormal.to_solar_mass; + const double sfrmax_data = pow(10., 3.47 - 3.13 * exp(-0.56 * cosmo->z)); + const double M0_data = pow(10., 11.69 - 1.66 * exp(-0.53 * cosmo->z)); + const double sfr_data = sfrmax_data / (1. + (M0_data / mstar)); + const double sfr_msun_per_yr = galaxy_sfr * + starform->lognormal.to_msun_per_yr; + epsc = 0.01 * sqrt(sfr_msun_per_yr / sfr_data); + } + /* based on direct scaling with sSFR */ + else if (starform->lognormal.wn07_epsc_method == 5) { + epsc = min( + galaxy_ssfr * starform->lognormal.time_to_year_inverse * 1.e7, + 0.1 + ); + } + /* Scale with galaxy SFR instead of SSFR */ + else if (starform->lognormal.wn07_epsc_method == 6) { + const float sfr_msun_per_yr = galaxy_sfr * + starform->lognormal.to_msun_per_yr; + epsc = min(0.001 * cbrt(sfr_msun_per_yr * 300.), 0.1); + epsc = max(epsc, 0.001); + } + else { + error("wn07_epsc_method value of %d not recognised", + starform->lognormal.wn07_epsc_method); + } + + /* Restrict range to that specified in WN07 */ + epsc = min(epsc, 0.1); + epsc = max(epsc, 0.001); + + /* Calculate parameters in WN07 model */ + const double sigma = sqrt(2. * log(rho_V / rho_0)); + const double z_num = + log(starform->lognormal.rhocrit / rho_0) - sigma * sigma; + const double z_den = sqrt(2.) * sigma; + const double z = z_num / z_den; + + /* fraction of dense gas */ + const double fc = 0.5 * erfc(z); + + /* This is the SFR density from eq. 17, except use actual + * density rho_V not estimated density rho_c + */ + const double rhosfr = + epsc * sqrt(phys_const->const_newton_G * rho_V) * fc; + + /* multiply by dense gas effective volume to get SFR */ + const double sfr = + rhosfr * (p->cooling_data.subgrid_fcold * hydro_get_mass(p)); + + /* Multiply by the H2 fraction */ + p->sf_data.SFR = starform->lognormal.epsilon * sfr * p->sf_data.H2_fraction; +} + +/** + * @brief Compute the star-formation rate of a given particle and store + * it into the #part. The star formation is calculated by assuming + * a lognormal density distribution with a mean density given by the + * subgrid density, above a critical density threshold that is an + * input parameter. Lognormal params based on sims by Wada & Norman 2007. + * + * @param p #part. + * @param xp the #xpart. + * @param starform the star formation law properties to use + * @param phys_const the physical constants in internal units. + * @param hydro_props The properties of the hydro scheme. + * @param cosmo the cosmological parameters and properties. + * @param dt_star The time-step of this particle. + */ +INLINE static void star_formation_compute_SFR_lognormal( + struct part* p, struct xpart* xp, + const struct star_formation* starform, const struct phys_const* phys_const, + const struct hydro_props* hydro_props, const struct cosmology* cosmo, + const double dt_star) { + + + /* Limit for Eq. 12 in Wada+Norman 2007 */ + const double rho_limit = 1.001 * starform->lognormal.rho0; + + /* H2 fraction in the particle */ + const double f_H2 = p->sf_data.H2_fraction; + + /* Mean density of the gas described by a lognormal PDF + * (i.e. subgrid ISM gas) PHYSICAL */ + double rho_V = cooling_get_subgrid_density(p, xp); + + /* SF cannot occur below characteristic + * density~1 cm^-3 (formulae below give nans) + */ + if (rho_V < rho_limit || f_H2 <= 0.) { + p->sf_data.SFR = 0.f; + return; + } + + /* Calculate the SFR based on a lognormal density distribution at rho0 with a + * threshold density for star formation rhocrit. sigma comes from WN07 model. + */ + const double rho_0 = starform->lognormal.rho0; + /* Mass-averaged density for cold phase */ + const double sigma = sqrt(log(2. * rho_V / rho_0)); + const double z_num = + log(starform->lognormal.rhocrit / rho_0) - (sigma * sigma); + const double z_den = sqrt(2.) * sigma; + const double z = z_num / z_den; + + /* Calculate lognormal fraction from the WN07 model */ + const double f_c = 0.5 * erfc(z); + + const double rho_phys = hydro_get_physical_density(p, cosmo); + + /* Calculate the SFR per gas mass, using lognormal mass fraction above + * rhocrit as efficiency + */ + const double sSFR = + f_c * starform->lognormal.ff_const_inv * sqrt(rho_phys); + + const double mass = + f_H2 * p->cooling_data.subgrid_fcold * hydro_get_mass(p); + + /* Store the SFR */ + p->sf_data.SFR = starform->lognormal.epsilon * sSFR * mass; + +} + +/** + * @brief Compute the star-formation rate of a given particle and store + * it into the #part. This involves computing H2 fraction, then using + * the chosen SF model to compute the SFR. + * + * @param p #part. + * @param xp the #xpart. + * @param starform the star formation law properties to use + * @param phys_const the physical constants in internal units. + * @param hydro_props The properties of the hydro scheme. + * @param cosmo the cosmological parameters and properties. + * @param dt_star The time-step of this particle. + */ +INLINE static void star_formation_compute_SFR( + struct part* p, struct xpart* xp, + const struct star_formation* starform, const struct phys_const* phys_const, + const struct hydro_props* hydro_props, const struct fof_props* fof_props, + const struct cosmology* cosmo, const double dt_star) { + + /* Abort early if time-step size is 0 */ + if (dt_star == 0.) { + p->sf_data.SFR = 0.f; + return; + } + + /* Physical gas density of the particle */ + const double physical_density = hydro_get_physical_density(p, cosmo); + + /* Compute the H2 fraction of the particle */ + switch (starform->H2_model) { + case kiara_star_formation_density_thresh: + p->sf_data.H2_fraction = 1.f; + break; + case kiara_star_formation_kmt_model: + p->sf_data.H2_fraction = 0.f; + + /* gas_sigma is double because we do some cgs conversions */ + double gas_sigma = 0.f; + float gas_Z = 0.f; + float chi = 0.f; + float s = 0.f; + float clumping_factor = 30.f; + float gas_gradrho_mag = 0.f; + + gas_Z = p->chemistry_data.metal_mass_fraction_total; + gas_Z /= starform->Z_solar; + if (gas_Z < 0.01f) { + gas_Z = 0.01f; + } + + if (physical_density > 0.f) { + gas_gradrho_mag = sqrtf( + p->rho_gradient[0] * p->rho_gradient[0] + + p->rho_gradient[1] * p->rho_gradient[1] + + p->rho_gradient[2] * p->rho_gradient[2] + ); + + if (gas_gradrho_mag > 0) { + gas_sigma = (p->rho * p->rho) / gas_gradrho_mag; + + /* surface density must be in Msun/pc^2 */ + gas_sigma *= starform->surface_rho_to_Msun_per_parsec2 + * cosmo->a2_inv; + + /* Lower clumping factor with higher resolution + (CF = 30 @ ~1 kpc resolution) */ + clumping_factor *= starform->clumping_factor_scaling; + if (clumping_factor < 1.f) { + clumping_factor = 1.f; + } + + /* chi ~ 1/R ~ 1/clump from KG11 eq. 3 */ + chi = 0.756f * (1.f + 3.1f * powf(gas_Z, 0.365f)) + * (30.f / clumping_factor); + s = logf(1.f + 0.6f * chi + 0.01f * chi * chi) + / (0.0396f * powf(clumping_factor, 2.f / 3.f) + * gas_Z * gas_sigma); + + if (s > 0.f && s < 2.f) { + p->sf_data.H2_fraction = 1.f - 0.75f * (s / (1.f + 0.25f * s)); + } + } + } + break; + case kiara_star_formation_grackle_model: +#if COOLING_GRACKLE_MODE >= 2 + p->sf_data.H2_fraction = p->cooling_data.subgrid_fcold * + (xp->cooling_data.H2I_frac + xp->cooling_data.H2II_frac); +#else + p->sf_data.H2_fraction = + 1. - xp->cooling_data.HI_frac; +#endif + break; + default: + error("Invalid H2 model in star formation!"); + break; + } + + /* Now compute the star formation rate and save it to the particle */ + switch (starform->SF_model) { + case kiara_star_formation_SchmidtLaw: + star_formation_compute_SFR_schmidt_law(p, xp, starform, phys_const, + hydro_props, cosmo, dt_star); + break; + case kiara_star_formation_WadaNorman: + star_formation_compute_SFR_wn07(p, xp, starform, phys_const, + hydro_props, fof_props, + cosmo, dt_star); + break; + case kiara_star_formation_lognormal: + star_formation_compute_SFR_lognormal(p, xp, starform, phys_const, + hydro_props, cosmo, dt_star); + break; + default: + error("Invalid SF model in star formation!!!"); + break; + } +} + +/** + * @brief Returns the number of new star particles to create per SF event. + * + * @param p The #part. + * @param xp The #xpart. + * @param starform The properties of the star formation model. + * + * @return The number of extra star particles to generate per gas particles. + * (return 0 if the gas particle itself is to be converted) + */ +INLINE static int star_formation_number_spart_to_spawn( + struct part* p, struct xpart* xp, const struct star_formation* starform) { + + return 0; +} + +/** + * @brief Returns the number of particles to convert per SF event. + * + * @param p The #part. + * @param xp The #xpart. + * @param starform The properties of the star formation model. + * + * @return The number of particles to generate per gas particles. + * (This has to be 0 or 1) + */ +INLINE static int star_formation_number_spart_to_convert( + const struct part* p, const struct xpart* xp, + const struct star_formation* starform) { + + return 1; +} + +/** + * @brief Decides whether a particle should be converted into a + * star or not. + * + * Equation 21 of Schaye & Dalla Vecchia 2008. + * + * @param p The #part. + * @param xp The #xpart. + * @param starform The properties of the star formation model. + * @param e The #engine (for random numbers). + * @param dt_star The time-step of this particle + * @param star_prob The probability of converting to a star particle. + * @return 1 if a conversion should be done, 0 otherwise. + */ +INLINE static int star_formation_should_convert_to_star( + const struct part* p, const struct xpart* xp, + const struct star_formation* starform, const struct engine* e, + const double dt_star) { + + /* Calculate the propability of forming a star */ + const double prob = max(p->sf_data.SFR, 0.f) * dt_star / hydro_get_mass(p); + + /* Get a unique random number between 0 and 1 for star formation */ + const double random_number = + random_unit_interval(p->id, e->ti_current, random_number_star_formation); + + /* Have we been lucky and need to form a star? */ + return (prob > random_number); +} + +/** + * @brief Decides whether a new particle should be created or if the hydro + * particle needs to be transformed. + * + * @param p The #part. + * @param xp The #xpart. + * @param starform The properties of the star formation model. + * + * @return 1 if a new spart needs to be created. + */ +INLINE static int star_formation_should_spawn_spart( + struct part* p, struct xpart* xp, const struct star_formation* starform) { + return 0; +} + +/** + * @brief Update the SF properties of a particle that is not star forming. + * + * @param p The #part. + * @param xp The #xpart. + * @param e The #engine. + * @param starform The properties of the star formation model. + * @param with_cosmology Are we running with cosmology switched on? + */ +INLINE static void star_formation_update_part_not_SFR( + struct part* p, struct xpart* xp, const struct engine* e, + const struct star_formation* starform, const int with_cosmology) { + + /* Check if it is the first time steps after star formation */ + if (p->sf_data.SFR > 0.f) { + + /* Record the current time as an indicator of when this particle was last + star-forming. */ + if (with_cosmology) { + p->sf_data.SFR = -e->cosmology->a; + } else { + p->sf_data.SFR = -e->time; + } + } +} + +/** + * @brief Copies the properties of the gas particle over to the + * star particle + * + * @param e The #engine + * @param p the gas particles. + * @param xp the additional properties of the gas particles. + * @param sp the new created star particle with its properties. + * @param starform the star formation law properties to use. + * @param cosmo the cosmological parameters and properties. + * @param with_cosmology if we run with cosmology. + * @param phys_const the physical constants in internal units. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cooling The cooling data struct. + * @param chem_data The global properties of the chemistry scheme. + * @param convert_part Did we convert a part (or spawned one)? + */ +INLINE static void star_formation_copy_properties( + const struct part *p, const struct xpart *xp, struct spart *sp, + const struct engine *e, const struct star_formation *starform, + const struct cosmology *cosmo, const int with_cosmology, + const struct phys_const *phys_const, const struct hydro_props *hydro_props, + const struct unit_system *us, const struct cooling_function_data *cooling, + const struct chemistry_global_data *chem_data, const int convert_part) { + + /* Store the current mass */ + sp->mass = hydro_get_mass(p); + + /* Store the current mass as the initial mass */ + sp->mass_init = hydro_get_mass(p); + + /* Store either the birth_scale_factor or birth_time depending */ + if (with_cosmology) { + sp->birth_scale_factor = cosmo->a; + } else { + sp->birth_time = e->time; + } + + /* Move over the splitting data */ + sp->split_data = xp->split_data; + + /* Store the chemistry struct in the star particle */ + sp->chemistry_data = p->chemistry_data; + + /* Store the tracers data */ + sp->tracers_data = xp->tracers_data; + + /* Store the birth density in the star particle */ + sp->birth_density = cooling_get_subgrid_density(p, xp); + + /* Store the birth temperature in the star particle */ + sp->birth_temperature = cooling_get_subgrid_temperature(p, xp); + + /* Flag that this particle has not done feedback yet + sp->feedback_data.physical_energy_reservoir = 0.; + sp->feedback_data.N_launched = 0; + sp->feedback_data.mass_to_launch = 0.f; + sp->feedback_data.total_mass_kicked = 0.f; + sp->feedback_data.wind_velocity = 0.f; + sp->feedback_data.eta_suppression_factor = 1.f;*/ + sp->last_enrichment_time = sp->birth_time; + sp->count_since_last_enrichment = 0; +} + +/** + * @brief initialization of the star formation law + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us The current internal system of units. + * @param hydro_props The propertis of the hydro model. + * @param cosmo The current cosmological model. + * @param entropy_floor The properties of the entropy floor used in this + * simulation. + * @param starform the star formation law properties to initialize + */ +INLINE static void starformation_init_backend( + struct swift_params* parameter_file, const struct phys_const* phys_const, + const struct unit_system* us, const struct hydro_props* hydro_props, + const struct cosmology* cosmo, + const struct entropy_floor_properties* entropy_floor, + struct star_formation* starform) { + + /* Get the Gravitational constant */ + const double G_newton = phys_const->const_newton_G; + + /* CGS density conversion */ + const double rho_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + + /* Get the surface density unit Msun / pc^2 in internal units */ + const double Msun_per_pc2 = + phys_const->const_solar_mass / + (phys_const->const_parsec * phys_const->const_parsec); + + starform->surface_rho_to_Msun_per_parsec2 = 1. / Msun_per_pc2; + starform->conv_factor_surface_rho_to_cgs = + rho_to_cgs * units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); + + /* Read the SF model we are using */ + char SF_model[32]; + parser_get_param_string(parameter_file, + "KIARAStarFormation:SF_model", SF_model); + + if (strstr(SF_model, "SchmidtLaw") != NULL) { + starform->SF_model = kiara_star_formation_SchmidtLaw; + } + else if (strstr(SF_model, "WadaNorman") != NULL) { + starform->SF_model = kiara_star_formation_WadaNorman; + } + else if (strstr(SF_model, "lognormal") != NULL) { + starform->SF_model = kiara_star_formation_lognormal; + } + else { + error("Invalid SF model in SF params %s", SF_model); + } + + /* Read the H2 model we are using */ + char H2_model[32]; + parser_get_param_string(parameter_file, + "KIARAStarFormation:H2_model", H2_model); + + if (strstr(H2_model, "Thresh") != NULL) { + starform->H2_model = kiara_star_formation_density_thresh; + } + else if (strstr(H2_model, "KMT") != NULL) { + starform->H2_model = kiara_star_formation_kmt_model; + } + else if (strstr(H2_model, "Grackle") != NULL) { + starform->H2_model = kiara_star_formation_grackle_model; + } + else { + error("Invalid H2 model in SF params %s", H2_model); + } + + /* Read the ISM subgrid clumping factor value at the resolved scale + * (KMT model only) + */ + starform->clumping_factor_scaling = + parser_get_opt_param_double(parameter_file, + "KIARAStarFormation:clumping_factor_scaling", 30.f); + + /* Read the total metal mass fraction of the Sun */ + starform->Z_solar = + parser_get_opt_param_double(parameter_file, + "KIARAStarFormation:Z_solar", 0.0134f); + + /* Read the critical density contrast from the parameter file*/ + starform->min_over_den = parser_get_param_double( + parameter_file, "KIARAStarFormation:min_over_density"); + + /* Read threshold properties */ + starform->subgrid_thresh.T_threshold = parser_get_param_double( + parameter_file, "KIARAStarFormation:threshold_temperature_K"); + + starform->subgrid_thresh.nH_threshold = parser_get_param_double( + parameter_file, "KIARAStarFormation:threshold_number_density_H_p_cm3"); + + /* Calculate the ff constant */ + const double ff_const = sqrt(3. * M_PI / (32. * G_newton)); + + if (starform->SF_model == kiara_star_formation_SchmidtLaw) { + /* Get the star formation efficiency */ + starform->schmidt_law.sfe = parser_get_param_double( + parameter_file, "KIARAStarFormation:star_formation_efficiency"); + + /* Calculate the constant */ + starform->schmidt_law.mdot_const = starform->schmidt_law.sfe / ff_const; + } + else if (starform->SF_model == kiara_star_formation_WadaNorman || + starform->SF_model == kiara_star_formation_lognormal) { + + /* Critical density for SF (in physical cm^-3) for lognormal SF model */ + starform->lognormal.ncrit = parser_get_opt_param_double( + parameter_file, "KIARAStarFormation:lognormal_critical_density", 1.e3); + + /* code units */ + starform->lognormal.rhocrit = starform->lognormal.ncrit * 1.673e-24 / + rho_to_cgs; + + /* Set characeristic density of ISM lognormal (Table 1 of Wada+Norman07) + * in code units */ + starform->lognormal.rho0 = + pow(10., -1.5) * 1.98841e33 / (rho_to_cgs * pow(3.08567758149e18, 3.)); + + /* Like the star formation efficiency but to control for sub-grid factors */ + starform->lognormal.epsilon = parser_get_param_double( + parameter_file, "KIARAStarFormation:lognormal_epsilon"); + + /* used to scale epsilon_c (efficiency) in lognormal model to sSFR */ + starform->lognormal.time_to_year_inverse = + (365.25 * 24. * 60. * 60.) / + units_cgs_conversion_factor(us, UNIT_CONV_TIME); + + /* used to scale epsilon_c (efficiency) in lognormal model to SFR */ + starform->lognormal.to_solar_mass = + units_cgs_conversion_factor(us, UNIT_CONV_MASS) / 1.98841e33; + + /* used to scale epsilon_c (efficiency) in lognormal model to SFR */ + starform->lognormal.to_msun_per_yr = + units_cgs_conversion_factor(us, UNIT_CONV_SFR) / 1.98841e33 + * (365.25 * 24. * 60. * 60.); + + /* Calculate the constant needed for the free-fall time */ + starform->lognormal.ff_const_inv = 1. / ff_const; + + if (starform->SF_model == kiara_star_formation_WadaNorman) { + starform->lognormal.wn07_epsc_method = parser_get_opt_param_int( + parameter_file, "KIARAStarFormation:wn07_epsc_method", 0); + } + } + +} + +/** + * @brief Prints the used parameters of the star formation law + * + * @param starform the star formation law properties. + * */ +INLINE static void starformation_print_backend( + const struct star_formation* starform) { + + message("Star formation model is KIARA"); + + message("Particles are eligible for star formation if their have " + "T < %e K, n_H > %e cm^-3, and overdensity > %e", + starform->subgrid_thresh.T_threshold, + starform->subgrid_thresh.nH_threshold, + starform->min_over_den); + + if (starform->SF_model == kiara_star_formation_SchmidtLaw) { + message("Star formation law is a Schmidt law: Star formation " + "efficiency = %e", + starform->schmidt_law.sfe); + } + else if (starform->SF_model == kiara_star_formation_WadaNorman) { + message("Star formation based on Wada+Norman 2007: critical " + "density (code units) = %e", + starform->lognormal.rhocrit); + } + else if (starform->SF_model == kiara_star_formation_lognormal) { + message("Star formation based on lognormal density pdf: " + "critical density (code units) = %e" + "efficiency = %e", + starform->lognormal.rhocrit, + starform->lognormal.epsilon); + } + else { + error("Invalid SF model in star formation!!!"); + } + + +} + +/** + * @brief Return the star formation rate of a particle. + * Remember that SFR can be <0 because it stores last expansion factor + * when it was SF. + * + * @param p The particle. + * @param xp The extended data of the particle. + */ +INLINE static float star_formation_get_SFR(const struct part* p, + const struct xpart* xp) { + if (p->sf_data.SFR <= 0.) + return 0.f; + else + return p->sf_data.SFR; +} + +/** + * @brief Finishes the density calculation. + * + * Nothing to do here. We do not need to compute any quantity in the hydro + * density loop for the KIARA star formation model. + * + * @param p The particle to act upon + * @param xp The extra particle to act upon + * @param cd The global star_formation information. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void star_formation_end_density( + struct part* p, struct xpart* xp, const struct star_formation* cd, + const struct cosmology* cosmo) {} + +/** + * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. + * + * Nothing to do here. We do not need to compute any quantity in the hydro + * density loop for the KIARA star formation model. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cd #star_formation containing star_formation informations. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void +star_formation_part_has_no_neighbours(struct part* p, struct xpart* xp, + const struct star_formation* cd, + const struct cosmology* cosmo) { +} + +/** + * @brief Sets the star_formation properties of the (x-)particles to a valid + * state to start the density loop. + * + * Nothing to do here. We do not need to compute any quantity in the hydro + * density loop for the KIARA star formation model. + * + * @param data The global star_formation information used for this run. + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static void star_formation_init_part( + struct part* p, const struct star_formation* data) { +} + +/** + * @brief Sets the star_formation properties of the (x-)particles to a valid + * start state at the beginning of the simulation after the ICs have been read. + * + * @param phys_const The physical constant in internal units. + * @param us The unit system. + * @param cosmo The current cosmological model. + * @param data The global star_formation information used for this run. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE static void +star_formation_first_init_part(const struct phys_const* phys_const, + const struct unit_system* us, + const struct cosmology* cosmo, + const struct star_formation* data, + struct part* p, struct xpart* xp) { + /* This may need to be updated elsewhere */ + p->sf_data.H2_fraction = 0.f; + star_formation_init_part(p, data); +} + +/** + * @brief Split the star formation content of a particle into n pieces + * + * We only need to split the SFR if it is positive, i.e. it is not + * storing the redshift/time of last SF event. + * + * @param p The #part. + * @param xp The #xpart. + * @param n The number of pieces to split into. + */ +__attribute__((always_inline)) INLINE static void star_formation_split_part( + struct part* p, struct xpart* xp, const double n) { + + if (p->sf_data.SFR > 0.) p->sf_data.SFR /= n; +} + +/** + * @brief Deal with the case where no spart are available for star formation. + * + * @param e The #engine. + * @param p The #part. + * @param xp The #xpart. + */ +__attribute__((always_inline)) INLINE static void +star_formation_no_spart_available(const struct engine* e, const struct part* p, + const struct xpart* xp) { + /* Nothing to do, we just skip it and deal with it next step */ +} + +/** + * @brief Compute some information for the star formation model based + * on all the particles that were read in. + * + * This is called once on start-up of the code. + * + * Nothing to do here for KIARA. + * + * @param star_form The #star_formation structure. + * @param e The #engine. + */ +__attribute__((always_inline)) INLINE static void +star_formation_first_init_stats(struct star_formation* star_form, + const struct engine* e) {} + +#endif /* SWIFT_KIARA_STAR_FORMATION_H */ diff --git a/src/star_formation/KIARA/star_formation_debug.h b/src/star_formation/KIARA/star_formation_debug.h new file mode 100644 index 0000000000..ecbe430bb6 --- /dev/null +++ b/src/star_formation/KIARA/star_formation_debug.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_STAR_FORMATION_KIARA_DEBUG_H +#define SWIFT_STAR_FORMATION_KIARA_DEBUG_H + +__attribute__((always_inline)) INLINE static void star_formation_debug_particle( + const struct part* p, const struct xpart* xp) { + + if (xp != NULL) { + warning("[PID%lld] sf_data:", p->id); + warning("[PID%lld] SFR = %.3e", p->id, p->sf_data.SFR); + } +} + +#endif /* SWIFT_STAR_FORMATION_KIARA_DEBUG_H */ diff --git a/src/star_formation/KIARA/star_formation_iact.h b/src/star_formation/KIARA/star_formation_iact.h new file mode 100644 index 0000000000..e340fbb2a7 --- /dev/null +++ b/src/star_formation/KIARA/star_formation_iact.h @@ -0,0 +1,73 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 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 . + * + ******************************************************************************/ +#ifndef SWIFT_KIARA_STAR_FORMATION_IACT_H +#define SWIFT_KIARA_STAR_FORMATION_IACT_H + +/** + * @file KIARA/star_formation_iact.h + * @brief Density computation + */ + +/** + * @brief do star_formation computation after the runner_iact_density (symmetric + * version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_star_formation( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) { + + /* Nothing to do here. We do not need to compute any quantity in the hydro + density loop for the KIARA star formation model. */ +} + +/** + * @brief do star_formation computation after the runner_iact_density (non + * symmetric version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_star_formation(const float r2, const float dx[3], + const float hi, const float hj, + struct part *restrict pi, + const struct part *restrict pj, float a, + float H) { + + /* Nothing to do here. We do not need to compute any quantity in the hydro + density loop for the KIARA star formation model. */ +} + +#endif /* SWIFT_KIARA_STAR_FORMATION_IACT_H */ diff --git a/src/star_formation/KIARA/star_formation_io.h b/src/star_formation/KIARA/star_formation_io.h new file mode 100644 index 0000000000..2d880dfd4c --- /dev/null +++ b/src/star_formation/KIARA/star_formation_io.h @@ -0,0 +1,94 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@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 . + * + ******************************************************************************/ +#ifndef SWIFT_STAR_FORMATION_KIARA_IO_H +#define SWIFT_STAR_FORMATION_KIARA_IO_H + +/* Config parameters. */ +#include "../config.h" + +/* Local includes */ +#include "io_properties.h" + +/** + * @brief Specifies which s-particle fields to read from a dataset + * + * @param sparts The s-particle array. + * @param list The list of i/o properties to read. + * + * @return num_fields The number of i/o fields to read. + */ +INLINE static int star_formation_read_particles(struct spart* sparts, + struct io_props* list) { + return 0; +} + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param xparts The extended data particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +__attribute__((always_inline)) INLINE static int star_formation_write_particles( + const struct part* parts, const struct xpart* xparts, + struct io_props* list) { + + int num = 0; + + list[num] = io_make_output_field( + "StarFormationRates", FLOAT, 1, UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, parts, sf_data.SFR, + "If positive, star formation rates of the particles. If negative, stores " + "the last time/scale-factor at which the gas particle was star-forming. " + "If zero, the particle was never star-forming."); + num++; + +#if COOLING_GRACKLE_MODE < 2 + /* If using Kiara/Grackle then this is output in cooling_io since it is tracked in Grackle */ + list[num] = io_make_output_field( + "MolecularHydrogenFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + sf_data.H2_fraction, + "The H2 fraction of the gas particle. "); + num++; +#endif + + list[num] = io_make_output_field( + "InterstellarRadiationField", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + sf_data.G0, + "The interstellar radiation field strength in Habing units. "); + num++; + + return num; +} + +/** + * @brief Specifies which sparticle fields to write to a dataset + * + * @param sparts The star particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +__attribute__((always_inline)) INLINE static int +star_formation_write_sparticles(const struct spart* sparts, + struct io_props* list) { + return 0; +} +#endif /* SWIFT_STAR_FORMATION_KIARA_IO_H */ diff --git a/src/star_formation/KIARA/star_formation_logger.h b/src/star_formation/KIARA/star_formation_logger.h new file mode 100644 index 0000000000..310e0057ff --- /dev/null +++ b/src/star_formation/KIARA/star_formation_logger.h @@ -0,0 +1,266 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Folkert Nobels (nobels@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 . + * + *******************************************************************************/ +#ifndef SWIFT_KIARA_STARFORMATION_LOGGER_H +#define SWIFT_KIARA_STARFORMATION_LOGGER_H + +/* Some standard headers */ +#include + +/* Local includes */ +#include "cell.h" +#include "hydro.h" +#include "part.h" +#include "star_formation_logger_struct.h" +#include "units.h" + +/** + * @brief Update the stellar mass in the current cell after creating + * the new star particle spart sp + * + * @param sp new created star particle + * @param sf the star_formation_history struct of the current cell + */ +INLINE static void star_formation_logger_log_new_spart( + const struct spart *sp, struct star_formation_history *sf) { + + /* Add mass of created sparticle to the total stellar mass in this cell*/ + sf->new_stellar_mass += sp->mass; +} + +/** + * @brief Initialize the star formation history struct in the case the cell is + * inactive + * + * @param sf the star_formation_history struct we want to initialize + */ +INLINE static void star_formation_logger_log_inactive_cell( + struct star_formation_history *sf) { + + /* Initialize the stellar mass to zero*/ + sf->new_stellar_mass = 0.f; + + /* The active SFR becomes the inactive SFR */ + sf->SFR_inactive += sf->SFR_active; + + /* Initialize the active SFR */ + sf->SFR_active = 0.f; + + /* Initialize the SFR*dt active */ + sf->SFRdt_active = 0.f; +} + +/** + * @brief add a star formation history struct to an other star formation history + * struct + * + * @param sf_add the star formation struct which we want to add to the star + * formation history + * @param sf_update the star formation structure which we want to update + */ +INLINE static void star_formation_logger_add( + struct star_formation_history *sf_update, + const struct star_formation_history *sf_add) { + + /* Update the SFH structure */ + sf_update->new_stellar_mass += sf_add->new_stellar_mass; + + sf_update->SFR_active += sf_add->SFR_active; + + sf_update->SFRdt_active += sf_add->SFRdt_active; + + sf_update->SFR_inactive += sf_add->SFR_inactive; +} + +/** + * @brief add a star formation history struct to the engine star formation + * history accumulator struct + * + * @param sf_add the star formation accumulator struct which we want to add to + * the star formation history + * @param sf_update the star formation structure which we want to update + */ +INLINE static void star_formation_logger_add_to_accumulator( + struct star_formation_history_accumulator *sf_update, + const struct star_formation_history *sf_add) { + + /* Update the SFH structure */ + sf_update->new_stellar_mass = sf_add->new_stellar_mass; + + sf_update->SFR_active = sf_add->SFR_active; + + sf_update->SFRdt_active = sf_add->SFRdt_active; + + sf_update->SFR_inactive = sf_add->SFR_inactive; +} + +/** + * @brief Initialize the star formation history structure in the #engine + * + * @param sfh The pointer to the star formation history structure + */ +INLINE static void star_formation_logger_init( + struct star_formation_history *sfh) { + + /* Initialize the collecting SFH structure to zero */ + sfh->new_stellar_mass = 0.f; + + sfh->SFR_active = 0.f; + + sfh->SFRdt_active = 0.f; + + sfh->SFR_inactive = 0.f; +} + +/** + * @brief Initialize the star formation history structure in the #engine + * + * @param sfh The pointer to the star formation history structure + */ +INLINE static void star_formation_logger_accumulator_init( + struct star_formation_history_accumulator *sfh) { + + /* Initialize the collecting SFH structure to zero */ + sfh->new_stellar_mass = 0.f; + + sfh->SFR_active = 0.f; + + sfh->SFRdt_active = 0.f; + + sfh->SFR_inactive = 0.f; +} + +/** + * @brief Write the final SFH to a file + * + * @param fp The file to write to. + * @param time the simulation time (time since Big Bang) in internal units. + * @param a the scale factor. + * @param z the redshift. + * @param sf the #star_formation_history struct. + * @param step The time-step of the simulation. + */ +INLINE static void star_formation_logger_write_to_log_file( + FILE *fp, const double time, const double a, const double z, + const struct star_formation_history_accumulator sf, const int step) { + + /* Calculate the total SFR */ + const float totalSFR = sf.SFR_active + sf.SFR_inactive; + fprintf(fp, "%6d %16e %12.7f %12.7f %14e %14e %14e %14e\n", step, time, a, z, + sf.new_stellar_mass, sf.SFR_active, sf.SFRdt_active, totalSFR); +} + +/** + * @brief Initialize the SFH logger file + * + * @param fp the file pointer + * @param us The current internal system of units. + * @param phys_const Physical constants in internal units + */ +INLINE static void star_formation_logger_init_log_file( + FILE *fp, const struct unit_system *restrict us, + const struct phys_const *phys_const) { + + /* Write some general text to the logger file */ + fprintf(fp, "# Star Formation History Logger file\n"); + fprintf(fp, "######################################################\n"); + fprintf(fp, "# The quantities are all given in internal physical units!\n"); + fprintf(fp, "#\n"); + fprintf(fp, "# (0) Simulation step\n"); + fprintf(fp, "# Unit = dimensionless\n"); + fprintf(fp, + "# (1) Time since Big Bang (cosmological run), Time since start of " + "the simulation (non-cosmological run).\n"); + fprintf(fp, "# Unit = %e s\n", us->UnitTime_in_cgs); + fprintf(fp, "# Unit = %e yr\n", 1.f / phys_const->const_year); + fprintf(fp, "# Unit = %e Myr\n", 1.f / phys_const->const_year / 1e6); + fprintf(fp, "# (2) Scale factor\n"); + fprintf(fp, "# Unit = dimensionless\n"); + fprintf(fp, "# (3) Redshift\n"); + fprintf(fp, "# Unit = dimensionless\n"); + fprintf(fp, "# (4) Total mass stars formed in the current time-step.\n"); + fprintf(fp, "# Unit = %e gram\n", us->UnitMass_in_cgs); + fprintf(fp, "# Unit = %e Msun\n", 1.f / phys_const->const_solar_mass); + fprintf(fp, "# (5) The total SFR of all the active particles.\n"); + fprintf(fp, "# Unit = %e gram/s\n", + us->UnitMass_in_cgs / us->UnitTime_in_cgs); + fprintf(fp, "# Unit = %e Msun/yr\n", + phys_const->const_year / phys_const->const_solar_mass); + fprintf(fp, + "# (6) The star formation rate (SFR) of active particles multiplied " + "by their time-step size.\n"); + fprintf(fp, "# Unit = %e gram\n", us->UnitMass_in_cgs); + fprintf(fp, "# Unit = %e Msun\n", 1.f / phys_const->const_solar_mass); + fprintf(fp, "# (7) The total SFR of all the particles in the simulation.\n"); + fprintf(fp, "# Unit = %e gram/s\n", + us->UnitMass_in_cgs / us->UnitTime_in_cgs); + fprintf(fp, "# Unit = %e Msun/yr\n", + phys_const->const_year / phys_const->const_solar_mass); + fprintf(fp, "#\n"); + fprintf( + fp, + "# (0) (1) (2) (3) (4) " + " (5) (6) (7)\n"); + fprintf( + fp, + "# Time a z total M_stars SFR " + "(active) SFR*dt (active) SFR (total)\n"); +} + +/** + * @brief Add the SFR tracer to the total active SFR of this cell + * + * @param p the #part + * @param xp the #xpart + * @param sf the SFH logger struct + * @param dt_star The length of the time-step in physical internal units. + */ +INLINE static void star_formation_logger_log_active_part( + const struct part *p, const struct xpart *xp, + struct star_formation_history *sf, const double dt_star) { + + /* No SFR logging for wind particles. */ + if (p->decoupled) return; + + /* Add the SFR to the logger file */ + sf->SFR_active += max(p->sf_data.SFR, 0.f); + + /* Update the active SFR*dt */ + sf->SFRdt_active += p->sf_data.SFR * dt_star; +} + +/** + * @brief Add the SFR tracer to the total inactive SFR of this cell as long as + * the SFR tracer is larger than 0 + * + * @param p the #part + * @param xp the #xpart + * @param sf the SFH logger struct + */ +INLINE static void star_formation_logger_log_inactive_part( + const struct part *p, const struct xpart *xp, + struct star_formation_history *sf) { + + /* No SFR logging for wind particles. */ + if (p->decoupled) return; + + /* Add the SFR to the logger file */ + sf->SFR_inactive += max(p->sf_data.SFR, 0.f); +} + +#endif /* SWIFT_KIARA_STARFORMATION_LOGGER_H */ diff --git a/src/star_formation/KIARA/star_formation_logger_struct.h b/src/star_formation/KIARA/star_formation_logger_struct.h new file mode 100644 index 0000000000..c3d4ba0280 --- /dev/null +++ b/src/star_formation/KIARA/star_formation_logger_struct.h @@ -0,0 +1,54 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Folkert Nobels (nobels@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 . + * + ******************************************************************************/ +#ifndef SWIFT_KIARA_STAR_FORMATION_LOGGER_STRUCT_H +#define SWIFT_KIARA_STAR_FORMATION_LOGGER_STRUCT_H + +/* Starformation history struct */ +struct star_formation_history { + /*! Total new stellar mass */ + float new_stellar_mass; + + /*! SFR of all particles */ + float SFR_inactive; + + /*! SFR of active particles */ + float SFR_active; + + /*! SFR*dt of active particles */ + float SFRdt_active; +}; + +/* Starformation history struct for the engine. + Allows to integrate in time some values. + Nothing to do in KIARA => copy of star_formation_history */ +struct star_formation_history_accumulator { + /*! Total new stellar mass */ + float new_stellar_mass; + + /*! SFR of all particles */ + float SFR_inactive; + + /*! SFR of active particles */ + float SFR_active; + + /*! SFR*dt of active particles */ + float SFRdt_active; +}; + +#endif /* SWIFT_KIARA_STAR_FORMATION_LOGGER_STRUCT_H */ diff --git a/src/star_formation/KIARA/star_formation_struct.h b/src/star_formation/KIARA/star_formation_struct.h new file mode 100644 index 0000000000..4b5f01bb74 --- /dev/null +++ b/src/star_formation/KIARA/star_formation_struct.h @@ -0,0 +1,54 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@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 . + * + ******************************************************************************/ +#ifndef SWIFT_KIARA_STAR_FORMATION_STRUCT_H +#define SWIFT_KIARA_STAR_FORMATION_STRUCT_H + +/* Do we need unique IDs (only useful when spawning + new particles, conversion gas->stars does not need unique IDs) */ +#define star_formation_need_unique_id 0 + +/** + * @brief Star-formation-related properties stored in the extended particle + * data. + */ +struct star_formation_xpart_data {}; + +/** + * @brief Star-formation-related properties stored in the particle data. + */ +struct star_formation_part_data { + + /*! Star formation rate (internal units) or (if negative) time/scale-factor of + * last SF episode */ + float SFR; + + /*! The fraction of H2 in this gas particle */ + float H2_fraction; + + /*! The fraction of H2 in this gas particle */ + float G0; +}; + +/** + * @brief Star-formation-related properties stored in the star particle + * data. + */ +struct star_formation_spart_data {}; + +#endif /* SWIFT_KIARA_STAR_FORMATION_STRUCT_H */ diff --git a/src/star_formation_debug.h b/src/star_formation_debug.h index 81b15daa01..70cd022e7b 100644 --- a/src/star_formation_debug.h +++ b/src/star_formation_debug.h @@ -31,6 +31,8 @@ #include "./star_formation/EAGLE/star_formation_debug.h" #elif defined(STAR_FORMATION_GEAR) #include "./star_formation/GEAR/star_formation_debug.h" +#elif defined(STAR_FORMATION_KIARA) +#include "./star_formation/KIARA/star_formation_debug.h" #else #error "Invalid choice of star formation model." #endif diff --git a/src/star_formation_iact.h b/src/star_formation_iact.h index 9959c52d20..9ca3f6a891 100644 --- a/src/star_formation_iact.h +++ b/src/star_formation_iact.h @@ -37,6 +37,8 @@ #include "./star_formation/EAGLE/star_formation_iact.h" #elif defined(STAR_FORMATION_GEAR) #include "./star_formation/GEAR/star_formation_iact.h" +#elif defined(STAR_FORMATION_KIARA) +#include "./star_formation/KIARA/star_formation_iact.h" #else #error "Invalid choice of star formation law" #endif diff --git a/src/star_formation_io.h b/src/star_formation_io.h index 4edd084e66..c58fa00892 100644 --- a/src/star_formation_io.h +++ b/src/star_formation_io.h @@ -36,6 +36,8 @@ #include "./star_formation/EAGLE/star_formation_io.h" #elif defined(STAR_FORMATION_GEAR) #include "./star_formation/GEAR/star_formation_io.h" +#elif defined(STAR_FORMATION_KIARA) +#include "./star_formation/KIARA/star_formation_io.h" #else #error "Invalid choice of star formation model." #endif diff --git a/src/star_formation_logger.h b/src/star_formation_logger.h index fe1bc7d389..91ef0c0edc 100644 --- a/src/star_formation_logger.h +++ b/src/star_formation_logger.h @@ -36,6 +36,8 @@ #include "./star_formation/EAGLE/star_formation_logger.h" #elif defined(STAR_FORMATION_GEAR) #include "./star_formation/GEAR/star_formation_logger.h" +#elif defined(STAR_FORMATION_KIARA) +#include "./star_formation/KIARA/star_formation_logger.h" #else #error "Invalid choice of star formation model." #endif diff --git a/src/star_formation_logger_struct.h b/src/star_formation_logger_struct.h index 5ba6654fd8..3c67d23204 100644 --- a/src/star_formation_logger_struct.h +++ b/src/star_formation_logger_struct.h @@ -36,6 +36,8 @@ #include "./star_formation/EAGLE/star_formation_logger_struct.h" #elif defined(STAR_FORMATION_GEAR) #include "./star_formation/GEAR/star_formation_logger_struct.h" +#elif defined(STAR_FORMATION_KIARA) +#include "./star_formation/KIARA/star_formation_logger_struct.h" #else #error "Invalid choice of star formation structure." #endif diff --git a/src/star_formation_struct.h b/src/star_formation_struct.h index 2c8e72418e..2d5f472a7b 100644 --- a/src/star_formation_struct.h +++ b/src/star_formation_struct.h @@ -36,6 +36,8 @@ #include "./star_formation/EAGLE/star_formation_struct.h" #elif defined(STAR_FORMATION_GEAR) #include "./star_formation/GEAR/star_formation_struct.h" +#elif defined(STAR_FORMATION_KIARA) +#include "./star_formation/KIARA/star_formation_struct.h" #else #error "Invalid choice of star formation structure." #endif diff --git a/src/stars.h b/src/stars.h index 539f9654c1..33f12603c6 100644 --- a/src/stars.h +++ b/src/stars.h @@ -36,6 +36,9 @@ #include "./stars/GEAR/stars.h" #include "./stars/GEAR/stars_iact.h" #include "./stars/GEAR/stars_stellar_type.h" +#elif defined(STARS_KIARA) +#include "./stars/KIARA/stars.h" +#include "./stars/KIARA/stars_iact.h" #else #error "Invalid choice of star model" #endif diff --git a/src/stars/KIARA/stars.h b/src/stars/KIARA/stars.h new file mode 100644 index 0000000000..2e5a2d42d2 --- /dev/null +++ b/src/stars/KIARA/stars.h @@ -0,0 +1,378 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 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 . + * + ******************************************************************************/ +#ifndef SWIFT_SIMBA_STARS_H +#define SWIFT_SIMBA_STARS_H + +#include "exp10.h" + +#include + +/** + * @brief Computes the time-step length of a given star particle from star + * physics + * + * @param sp Pointer to the s-particle data. + * @param stars_properties Properties of the stars model. + * @param feedback_props Properties of the feedback model. + * @param phys_const The #phys_const. + * @param us The #unit_system. + * @param with_cosmology Are we running with cosmological time integration. + * @param cosmo The current cosmological model (used if running with + * cosmology). + * @param ti_current The current time (in integer). + * @param time The current time (used if running without cosmology). + * @param time_base The time base. + */ +__attribute__((always_inline)) INLINE static float stars_compute_timestep( + const struct spart *const sp, const struct stars_props *stars_properties, + //const struct feedback_props *feedback_props, + const struct phys_const *phys_const, const struct unit_system *us, + const int with_cosmology, const struct cosmology *cosmo, + const integertime_t ti_current, const double time, const double time_base) { + + /* Background star particles have no time-step limits */ + if (sp->birth_time == -1.) { + return FLT_MAX; + } + + /* Star age (in internal units) */ + double star_age; + if (with_cosmology) { + + /* Deal with rounding issues */ + if (sp->birth_scale_factor >= cosmo->a) { + star_age = 0.; + } else { + star_age = cosmology_get_delta_time_from_scale_factors( + cosmo, sp->birth_scale_factor, cosmo->a); + } + } else { + star_age = time - sp->birth_time; + } + + if (star_age > stars_properties->age_threshold_unlimited) return FLT_MAX; + + float dt_star = FLT_MAX; + if (star_age < stars_properties->age_threshold) { + dt_star = min(star_age * stars_properties->time_step_factor_young, + stars_properties->max_time_step_young); + } + else { + dt_star = min(star_age * stars_properties->time_step_factor_old, + stars_properties->max_time_step_old); + } + + return max(stars_properties->min_time_step, dt_star); +} + +/** + * @brief Returns the age of a star in internal units + * + * @param sp The star particle. + * @param cosmo The cosmological model. + * @param time The current time (in internal units). + * @param with_cosmology Are we running with cosmological integration? + */ +__attribute__((always_inline)) INLINE static double stars_compute_age( + const struct spart* sp, const struct cosmology* cosmo, double time, + const int with_cosmology) { + + if (with_cosmology) { + if ((double)sp->birth_scale_factor >= cosmo->a) return 0.; + return cosmology_get_delta_time_from_scale_factors( + cosmo, (double)sp->birth_scale_factor, cosmo->a); + } else { + return time - (double)sp->birth_time; + } +} + +/** + * @brief Prepares a s-particle for its interactions + * + * @param sp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void stars_init_spart( + struct spart* sp) { + +#ifdef DEBUG_INTERACTIONS_STARS + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + sp->ids_ngbs_density[i] = -1; + sp->num_ngb_density = 0; +#endif + + sp->density.wcount = 0.f; + sp->density.wcount_dh = 0.f; + +#ifdef SWIFT_STARS_DENSITY_CHECKS + sp->N_density = 0; + sp->N_density_exact = 0; + sp->rho = 0.f; + sp->rho_exact = 0.f; + sp->n = 0.f; + sp->n_exact = 0.f; + sp->inhibited_exact = 0; +#endif +} + +/** + * @brief Initialises the s-particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon. + * @param stars_properties Properties of the stars model. + * @param with_cosmology Are we running with cosmological time integration. + * @param scale_factor The current scale-factor (used if running with + * cosmology). + * @param time The current time (used if running without cosmology). + */ +__attribute__((always_inline)) INLINE static void stars_first_init_spart( + struct spart* sp, const struct stars_props* stars_properties, + const int with_cosmology, const double scale_factor, const double time) { + + sp->time_bin = 0; + sp->count_since_last_enrichment = -1; + + if (stars_properties->overwrite_birth_time) + sp->birth_time = stars_properties->spart_first_init_birth_time; + if (stars_properties->overwrite_birth_density) + sp->birth_density = stars_properties->spart_first_init_birth_density; + if (stars_properties->overwrite_birth_temperature) + sp->birth_temperature = + stars_properties->spart_first_init_birth_temperature; + + if (with_cosmology) + sp->last_enrichment_time = scale_factor; + else + sp->last_enrichment_time = time; + + stars_init_spart(sp); + +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * @param sp The particle + * @param dt_drift The drift time-step for positions. + */ +__attribute__((always_inline)) INLINE static void stars_predict_extra( + struct spart* restrict sp, float dt_drift) {} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param sp The particle. + */ +__attribute__((always_inline)) INLINE static void stars_reset_predicted_values( + struct spart* restrict sp) {} + +/** + * @brief Finishes the calculation of (non-gravity) forces acting on stars + * + * Multiplies the forces and accelerations by the appropiate constants + * + * @param sp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void stars_end_feedback( + struct spart* sp) {} + +/** + * @brief Kick the additional variables + * + * @param sp The particle to act upon + * @param dt The time-step for this kick + */ +__attribute__((always_inline)) INLINE static void stars_kick_extra( + struct spart* sp, float dt) {} + +/** + * @brief Finishes the calculation of density on stars + * + * @param sp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void stars_end_density( + struct spart* sp, const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = sp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + /* Finish the calculation by inserting the missing h-factors */ + sp->density.wcount *= h_inv_dim; + sp->density.wcount_dh *= h_inv_dim_plus_one; + +#ifdef SWIFT_STARS_DENSITY_CHECKS + sp->rho *= h_inv_dim; + sp->n *= h_inv_dim; +#endif +} + +/** + * @brief Sets all particle fields to sensible values when the #spart has 0 + * ngbs. + * + * @param sp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours( + struct spart* restrict sp, const struct cosmology* cosmo) { + + warning( + "Star particle with ID %lld treated as having no neighbours (h: %g, " + "wcount: %g).", + sp->id, sp->h, sp->density.wcount); + + /* Re-set problematic values */ + sp->density.wcount = 0.f; + sp->density.wcount_dh = 0.f; +} + +/** + * @brief Reset acceleration fields of a particle + * + * This is the equivalent of hydro_reset_acceleration. + * We do not compute the acceleration on star, therefore no need to use it. + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void stars_reset_acceleration( + struct spart* restrict p) { + +#ifdef DEBUG_INTERACTIONS_STARS + p->num_ngb_feedback = 0; +#endif +} + +/** + * @brief Reset acceleration fields of a particle + * + * This is the equivalent of hydro_reset_acceleration. + * We do not compute the acceleration on star, therefore no need to use it. + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void stars_reset_feedback( + struct spart* restrict p) { + +#ifdef DEBUG_INTERACTIONS_STARS + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + p->ids_ngbs_feedback[i] = -1; + p->num_ngb_feedback = 0; +#endif +} + +/** + * @brief Compute the luminosities of a particles in different bands. + * + * @param sp The particle. + * @param with_cosmology Are we running a cosmological simulation? + * @param cosmo The #cosmology object. + * @param time The current physical time (internal units). + * @param phys_const The physical constants in internal units. + * @param props The #stars_props of that run. + * @param luminosities (return) The luminosity in each band. + */ +INLINE static void stars_get_luminosities( + const struct spart* sp, const int with_cosmology, + const struct cosmology* cosmo, const double time, + const struct phys_const* phys_const, const struct stars_props* props, + float luminosities[luminosity_bands_count]) { + + const int count_Z = eagle_stars_lum_tables_N_Z; + const int count_ages = eagle_stars_lum_tables_N_ages; + + /* Get star properties (all in internal units */ + const float Z = + chemistry_get_star_total_metal_mass_fraction_for_luminosity(sp); + const float mass = sp->mass_init; + float age; + if (with_cosmology) + age = cosmology_get_delta_time_from_scale_factors( + cosmo, sp->birth_scale_factor, cosmo->a); + else + age = time - sp->birth_time; + + /* Convert to the units of the tables */ + const float mass_Msun = mass / phys_const->const_solar_mass; + const float age_Gyr = age / phys_const->const_year / 1e9; + + for (int i = 0; i < (int)luminosity_bands_count; ++i) { + + /* Log things */ + float log10_Z = log10(Z + FLT_MIN); + float log10_age_Gyr = log10(age_Gyr + FLT_MIN); + + /* Clip the input */ + log10_Z = max(log10_Z, props->lum_tables_Z[i][0]); + log10_Z = min(log10_Z, props->lum_tables_Z[i][count_Z - 1]); + log10_age_Gyr = max(log10_age_Gyr, props->lum_tables_ages[i][0]); + log10_age_Gyr = + min(log10_age_Gyr, props->lum_tables_ages[i][count_ages - 1]); + + /* Get index along the interpolation axis */ + int Z_index = 0, age_index = 0; + for (int j = 0; j < count_Z - 1; ++j) { + if (log10_Z >= props->lum_tables_Z[i][j]) ++Z_index; + } + for (int j = 0; j < count_ages - 1; ++j) { + if (log10_age_Gyr >= props->lum_tables_ages[i][j]) ++age_index; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (age_index == 0) error("Invalid age index!"); + if (Z_index == 0) error("Invalid Z index!"); +#endif + + const float* array = props->lum_tables_luminosities[i]; + + /* 2D interpolation */ + + const float f_11 = array[(Z_index - 1) * count_ages + (age_index - 1)]; + const float f_12 = array[(Z_index - 0) * count_ages + (age_index - 1)]; + const float f_21 = array[(Z_index - 1) * count_ages + (age_index - 0)]; + const float f_22 = array[(Z_index - 0) * count_ages + (age_index - 0)]; + + const float x_diff1 = props->lum_tables_ages[i][age_index] - log10_age_Gyr; + const float x_diff2 = + log10_age_Gyr - props->lum_tables_ages[i][age_index - 1]; + const float x_diff3 = props->lum_tables_ages[i][age_index] - + props->lum_tables_ages[i][age_index - 1]; + + const float y_diff1 = props->lum_tables_Z[i][Z_index] - log10_Z; + const float y_diff2 = log10_Z - props->lum_tables_Z[i][Z_index - 1]; + const float y_diff3 = + props->lum_tables_Z[i][Z_index] - props->lum_tables_Z[i][Z_index - 1]; + + const float f_1 = (f_11 * x_diff1 + f_21 * x_diff2) / x_diff3; + const float f_2 = (f_12 * x_diff1 + f_22 * x_diff2) / x_diff3; + + const float log10_f = (y_diff1 * f_1 + y_diff2 * f_2) / y_diff3; + + /* Final conversion */ + luminosities[i] = exp10f(log10_f) * mass_Msun * props->lum_tables_factor; + } +} + +#endif /* SWIFT_SIMBA_STARS_H */ diff --git a/src/stars/KIARA/stars_debug.h b/src/stars/KIARA/stars_debug.h new file mode 100644 index 0000000000..ed3d15bc40 --- /dev/null +++ b/src/stars/KIARA/stars_debug.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 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 . + * + ******************************************************************************/ +#ifndef SWIFT_SIMBA_STARS_DEBUG_H +#define SWIFT_SIMBA_STARS_DEBUG_H + +__attribute__((always_inline)) INLINE static void stars_debug_particle( + const struct spart* p) { + printf( + "x=[%.3e,%.3e,%.3e], " + "v_full=[%.3e,%.3e,%.3e] p->mass=%.3e \n t_begin=%d, t_end=%d\n", + p->x[0], p->x[1], p->x[2], p->v_full[0], p->v_full[1], p->v_full[2], + p->mass, p->ti_begin, p->ti_end); +} + +#endif /* SWIFT_SIMBA_STARS_DEBUG_H */ diff --git a/src/stars/KIARA/stars_iact.h b/src/stars/KIARA/stars_iact.h new file mode 100644 index 0000000000..fc5db01fbf --- /dev/null +++ b/src/stars/KIARA/stars_iact.h @@ -0,0 +1,112 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 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 . + * + ******************************************************************************/ +#ifndef SWIFT_SIMBA_STARS_IACT_H +#define SWIFT_SIMBA_STARS_IACT_H + +#include "random.h" + +/** + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First sparticle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_stars_density(const float r2, const float dx[3], + const float hi, const float hj, + struct spart *si, const struct part *pj, + const float a, const float H) { + + /* Ignore wind in density computation */ + if (pj->decoupled) return; + + float wi, wi_dx; + + /* Get r. */ + const float r = sqrtf(r2); + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the number of neighbours */ + si->density.wcount += wi; + si->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + +#ifdef SWIFT_STARS_DENSITY_CHECKS + si->rho += pj->mass * wi; + si->n += wi; + si->N_density++; +#endif + +#ifdef DEBUG_INTERACTIONS_STARS + /* Update ngb counters */ + if (si->num_ngb_density < MAX_NUM_OF_NEIGHBOURS_STARS) + si->ids_ngbs_density[si->num_ngb_density] = pj->id; + ++si->num_ngb_density; +#endif +} + +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_stars_prep1(const float r2, const float dx[3], + const float hi, const float hj, struct spart *si, + const struct part *pj, const float a, + const float H) {} + +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_stars_prep2(const float r2, const float dx[3], + const float hi, const float hj, struct spart *si, + const struct part *pj, const float a, + const float H) {} + +/** + * @brief Feedback interaction between two particles (non-symmetric). + * Used for updating properties of gas particles neighbouring a star particle + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (si - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First (star) particle (not updated). + * @param pj Second (gas) particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_stars_feedback(const float r2, const float dx[3], + const float hi, const float hj, + const struct spart *si, struct part *pj, + const float a, const float H) { + +#ifdef DEBUG_INTERACTIONS_STARS + /* Update ngb counters */ + if (si->num_ngb_feedback < MAX_NUM_OF_NEIGHBOURS_STARS) + si->ids_ngbs_feedback[si->num_ngb_feedback] = pj->id; + ++si->num_ngb_feedback; +#endif +} + +#endif /* SWIFT_SIMBA_STARS_IACT_H */ diff --git a/src/stars/KIARA/stars_io.h b/src/stars/KIARA/stars_io.h new file mode 100644 index 0000000000..85288682d5 --- /dev/null +++ b/src/stars/KIARA/stars_io.h @@ -0,0 +1,530 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2018 Folkert Nobels (nobels@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 . + * + ******************************************************************************/ +#ifndef SWIFT_SIMBA_STARS_IO_H +#define SWIFT_SIMBA_STARS_IO_H + +#include "io_properties.h" +#include "kick.h" +#include "stars_part.h" + +/** + * @brief Specifies which s-particle fields to read from a dataset + * + * @param sparts The s-particle array. + * @param list The list of i/o properties to read. + * @param num_fields The number of i/o fields to read. + */ +INLINE static void stars_read_particles(struct spart *sparts, + struct io_props *list, + int *num_fields) { + /* Say how much we want to read */ + *num_fields = 9; + + /* List what we want to read */ + list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, + UNIT_CONV_LENGTH, sparts, x); + list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, + UNIT_CONV_SPEED, sparts, v); + list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + sparts, mass); + list[3] = io_make_input_field("ParticleIDs", LONGLONG, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, sparts, id); + list[4] = io_make_input_field("SmoothingLength", FLOAT, 1, OPTIONAL, + UNIT_CONV_LENGTH, sparts, h); + list[5] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + sparts, mass_init); + float def_value = -1.f; + list[6] = + io_make_input_field_default("StellarFormationTime", FLOAT, 1, OPTIONAL, + UNIT_CONV_NO_UNITS, sparts, birth_time, def_value); + list[7] = io_make_input_field("BirthDensities", FLOAT, 1, OPTIONAL, + UNIT_CONV_DENSITY, sparts, birth_density); + list[8] = + io_make_input_field("BirthTemperatures", FLOAT, 1, OPTIONAL, + UNIT_CONV_TEMPERATURE, sparts, birth_temperature); +} + +INLINE static void convert_spart_pos(const struct engine *e, + const struct spart *sp, double *ret) { + const struct space *s = e->s; + if (s->periodic) { + ret[0] = box_wrap(sp->x[0], 0.0, s->dim[0]); + ret[1] = box_wrap(sp->x[1], 0.0, s->dim[1]); + ret[2] = box_wrap(sp->x[2], 0.0, s->dim[2]); + } else { + ret[0] = sp->x[0]; + ret[1] = sp->x[1]; + ret[2] = sp->x[2]; + } + if (e->snapshot_use_delta_from_edge) { + ret[0] = min(ret[0], s->dim[0] - e->snapshot_delta_from_edge); + ret[1] = min(ret[1], s->dim[1] - e->snapshot_delta_from_edge); + ret[2] = min(ret[2], s->dim[2] - e->snapshot_delta_from_edge); + } +} + +INLINE static void convert_spart_vel(const struct engine *e, + const struct spart *sp, float *ret) { + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology *cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + const float dt_kick_grav_mesh = e->dt_kick_grav_mesh_for_io; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, sp->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, sp->time_bin); + + /* Get time-step since the last kick */ + const float dt_kick_grav = + kick_get_grav_kick_dt(ti_beg, ti_current, time_base, with_cosmology, + cosmo) - + kick_get_grav_kick_dt(ti_beg, (ti_beg + ti_end) / 2, time_base, + with_cosmology, cosmo); + + /* Extrapolate the velocites to the current time */ + const struct gpart *gp = sp->gpart; + ret[0] = gp->v_full[0] + gp->a_grav[0] * dt_kick_grav; + ret[1] = gp->v_full[1] + gp->a_grav[1] * dt_kick_grav; + ret[2] = gp->v_full[2] + gp->a_grav[2] * dt_kick_grav; + + /* Extrapolate the velocites to the current time (mesh forces) */ + ret[0] += gp->a_grav_mesh[0] * dt_kick_grav_mesh; + ret[1] += gp->a_grav_mesh[1] * dt_kick_grav_mesh; + ret[2] += gp->a_grav_mesh[2] * dt_kick_grav_mesh; + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a_inv; + ret[1] *= cosmo->a_inv; + ret[2] *= cosmo->a_inv; +} + +INLINE static void convert_spart_luminosities(const struct engine *e, + const struct spart *sp, + float *ret) { + stars_get_luminosities(sp, e->policy & engine_policy_cosmology, e->cosmology, + e->time, e->physical_constants, e->stars_properties, + ret); +} + +INLINE static void convert_spart_potential(const struct engine *e, + const struct spart *sp, float *ret) { + + if (sp->gpart != NULL) + ret[0] = gravity_get_comoving_potential(sp->gpart); + else + ret[0] = 0.f; +} + +/** + * @brief Specifies which s-particle fields to write to a dataset + * + * @param sparts The s-particle array. + * @param list The list of i/o properties to write. + * @param num_fields The number of i/o fields to write. + * @param with_cosmology Are we running a cosmological simulation? + */ +INLINE static void stars_write_particles(const struct spart *sparts, + struct io_props *list, int *num_fields, + const int with_cosmology) { + /* Say how much we want to write */ + *num_fields = 12; + + /* List what we want to write */ + list[0] = io_make_output_field_convert_spart( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, sparts, + convert_spart_pos, "Co-moving position of the particles"); + + list[1] = io_make_output_field_convert_spart( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, sparts, convert_spart_vel, + "Peculiar velocities of the particles. This is a * dx/dt where x is the " + "co-moving position of the particles."); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + sparts, mass, + "Masses of the particles at the current point " + "in time (i.e. after stellar losses"); + + list[3] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + sparts, id, "Unique ID of the particles"); + + list[4] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, sparts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + + list[5] = io_make_output_field("InitialMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + sparts, mass_init, + "Masses of the star particles at birth time"); + + if (with_cosmology) { + list[6] = io_make_output_field( + "BirthScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, + birth_scale_factor, "Scale-factors at which the stars were born"); + } else { + list[6] = io_make_output_field("BirthTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, + sparts, birth_time, + "Times at which the stars were born"); + } + + list[7] = io_make_output_field( + "BirthDensities", FLOAT, 1, UNIT_CONV_DENSITY, 0.f, sparts, birth_density, + "Physical densities at the time of birth of the gas particles that " + "turned into stars (note that " + "we store the physical density at the birth redshift, no conversion is " + "needed)"); + + list[8] = io_make_output_field("BirthTemperatures", + FLOAT, 1, UNIT_CONV_TEMPERATURE, + 0.f, sparts, birth_temperature, + "Temperatures at the time of birth of the gas " + "particles that turned into stars"); + +/* list[9] = io_make_output_field( + "TotalMassEjected", FLOAT, 1, UNIT_CONV_MASS, 0.f, sparts, + feedback_data.total_mass_kicked, + "Total gas mass kicked by the star over its life-time. ");*/ + + list[10] = io_make_output_field_convert_spart( + "Luminosities", FLOAT, luminosity_bands_count, UNIT_CONV_NO_UNITS, 0.f, + sparts, convert_spart_luminosities, + "Rest-frame dust-free AB-luminosities of the star particles in the GAMA " + "bands. These were computed using the BC03 (GALAXEV) models convolved " + "with different filter bands and interpolated in log-log (f(log(Z), " + "log(age)) = log(flux)) as used in the dust-free modelling of Trayford " + "et al. (2015). The luminosities are given in dimensionless units. They " + "have been divided by 3631 Jy already, i.e. they can be turned into " + "absolute AB-magnitudes (rest-frame absolute maggies) directly by " + "applying -2.5 log10(L) without additional corrections."); + + list[11] = io_make_output_field_convert_spart( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, sparts, + convert_spart_potential, "Gravitational potentials of the particles"); +} + +/** + * @brief Initialize the global properties of the stars scheme. + * + * By default, takes the values provided by the hydro. + * + * @param sp The #stars_props. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param p The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. + */ +INLINE static void stars_props_init(struct stars_props *sp, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct hydro_props *p, + const struct cosmology *cosmo) { + /* Kernel properties */ + sp->eta_neighbours = parser_get_opt_param_float( + params, "Stars:resolution_eta", p->eta_neighbours); + + /* Tolerance for the smoothing length Newton-Raphson scheme */ + sp->h_tolerance = + parser_get_opt_param_float(params, "Stars:h_tolerance", p->h_tolerance); + + /* Get derived properties */ + sp->target_neighbours = pow_dimension(sp->eta_neighbours) * kernel_norm; + const float delta_eta = sp->eta_neighbours * (1.f + sp->h_tolerance); + sp->delta_neighbours = + (pow_dimension(delta_eta) - pow_dimension(sp->eta_neighbours)) * + kernel_norm; + + /* Number of iterations to converge h */ + sp->max_smoothing_iterations = parser_get_opt_param_int( + params, "Stars:max_ghost_iterations", p->max_smoothing_iterations); + + /* Time integration properties */ + const float max_volume_change = + parser_get_opt_param_float(params, "Stars:max_volume_change", -1); + if (max_volume_change == -1) + sp->log_max_h_change = p->log_max_h_change; + else + sp->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv)); + + /* Do we want to overwrite the stars' birth properties? */ + sp->overwrite_birth_time = + parser_get_opt_param_int(params, "Stars:overwrite_birth_time", 0); + sp->overwrite_birth_density = + parser_get_opt_param_int(params, "Stars:overwrite_birth_density", 0); + sp->overwrite_birth_temperature = + parser_get_opt_param_int(params, "Stars:overwrite_birth_temperature", 0); + + /* Read birth time to set all stars in ICs */ + if (sp->overwrite_birth_time) { + sp->spart_first_init_birth_time = + parser_get_param_float(params, "Stars:birth_time"); + } + + /* Read birth density to set all stars in ICs */ + if (sp->overwrite_birth_density) { + sp->spart_first_init_birth_density = + parser_get_param_float(params, "Stars:birth_density"); + } + + /* Read birth temperature to set all stars in ICs */ + if (sp->overwrite_birth_temperature) { + sp->spart_first_init_birth_temperature = + parser_get_param_float(params, "Stars:birth_temperature"); + } + + /* Maximal time-step lengths */ + const double Myr = 1e6 * 365.25 * 24. * 60. * 60.; + const double conv_fac = units_cgs_conversion_factor(us, UNIT_CONV_TIME); + + sp->time_step_factor_young = parser_get_opt_param_float( + params, "Stars:time_step_factor_young", 1.f); + sp->time_step_factor_old = parser_get_opt_param_float( + params, "Stars:time_step_factor_old", 1.f); + const double min_time_step_Myr = parser_get_opt_param_float( + params, "Stars:min_time_step_Myr", 30.); + const double max_time_step_young_Myr = parser_get_opt_param_float( + params, "Stars:max_timestep_young_Myr", FLT_MAX); + const double max_time_step_old_Myr = + parser_get_opt_param_float(params, "Stars:max_timestep_old_Myr", FLT_MAX); + const double age_threshold_Myr = parser_get_opt_param_float( + params, "Stars:timestep_age_threshold_Myr", FLT_MAX); + const double age_threshold_unlimited_Myr = parser_get_opt_param_float( + params, "Stars:timestep_age_threshold_unlimited_Myr", 0.); + + /* Check for consistency */ + if (age_threshold_unlimited_Myr != 0. && age_threshold_Myr != FLT_MAX) { + if (age_threshold_unlimited_Myr < age_threshold_Myr) + error( + "The age threshold for unlimited stellar time-step sizes (%e Myr) is " + "smaller than the transition threshold from young to old ages (%e " + "Myr)", + age_threshold_unlimited_Myr, age_threshold_Myr); + } + + /* Convert to internal units */ + sp->min_time_step = min_time_step_Myr * Myr / conv_fac; + sp->max_time_step_young = max_time_step_young_Myr * Myr / conv_fac; + sp->max_time_step_old = max_time_step_old_Myr * Myr / conv_fac; + sp->age_threshold = age_threshold_Myr * Myr / conv_fac; + sp->age_threshold_unlimited = age_threshold_unlimited_Myr * Myr / conv_fac; + + /* Read luminosity table filepath */ + char base_dir_name[200]; + parser_get_param_string(params, "Stars:luminosity_filename", base_dir_name); + + static const char *luminosity_band_names[luminosity_bands_count] = { + "u", "g", "r", "i", "z", "Y", "J", "H", "K"}; + + /* Luminosity tables */ + for (int i = 0; i < (int)luminosity_bands_count; ++i) { + const int count_Z = eagle_stars_lum_tables_N_Z; + const int count_ages = eagle_stars_lum_tables_N_ages; + const int count_L = count_Z * count_ages; + + sp->lum_tables_Z[i] = (float *)malloc(count_Z * sizeof(float)); + sp->lum_tables_ages[i] = (float *)malloc(count_ages * sizeof(float)); + sp->lum_tables_luminosities[i] = (float *)malloc(count_L * sizeof(float)); + + char fname[256]; + sprintf(fname, "%s/GAMA/%s", base_dir_name, luminosity_band_names[i]); + FILE *file = fopen(fname, "r"); + + if (file != NULL) { + char buffer[200]; + int j = 0, k = 0; + while (fgets(buffer, sizeof(buffer), file) != NULL) { + double z, age, L; + sscanf(buffer, "%le %le %le", &z, &age, &L); + + if (age == 0.) { + sp->lum_tables_Z[i][k++] = log10(z); + } + + if (j < count_ages) { + sp->lum_tables_ages[i][j] = log10(age + FLT_MIN); + } + + sp->lum_tables_luminosities[i][j] = log10(L); + + ++j; + } + } else { + error("Unable to load luminosity table %s", fname); + } + + fclose(file); + } + + /* Luminosity conversion factor */ + const double L_sun = 3.828e26; /* Watt */ + const double pc = 3.08567758149e16; /* m */ + const double A = 4. * M_PI * (10. * pc) * (10 * pc); + const double to_Jansky = 1e26 * L_sun / A; + const double zero_point_AB = 3631; /* Jansky */ + sp->lum_tables_factor = to_Jansky / zero_point_AB; +} + +/** + * @brief Print the global properties of the stars scheme. + * + * @param sp The #stars_props. + */ +INLINE static void stars_props_print(const struct stars_props *sp) { + message("Stars kernel: %s with eta=%f (%.2f neighbours).", kernel_name, + sp->eta_neighbours, sp->target_neighbours); + + message("Stars relative tolerance in h: %.5f (+/- %.4f neighbours).", + sp->h_tolerance, sp->delta_neighbours); + + message( + "Stars integration: Max change of volume: %.2f " + "(max|dlog(h)/dt|=%f).", + pow_dimension(expf(sp->log_max_h_change)), sp->log_max_h_change); + + message("Maximal iterations in ghost task set to %d", + sp->max_smoothing_iterations); + + if (sp->overwrite_birth_time) + message("Stars' birth time read from the ICs will be overwritten to %f", + sp->spart_first_init_birth_time); + + message("Time-step factor for young stars: %e", sp->time_step_factor_young); + message("Time-step factor for old stars: %e", sp->time_step_factor_old); + message("Stars' age threshold for unlimited dt: %e [U_t]", + sp->age_threshold_unlimited); + message("Stars' young/old age threshold: %e [U_t]", sp->age_threshold); + message("Max time-step size of young stars: %e [U_t]", + sp->max_time_step_young); + message("Max time-step size of old stars: %e [U_t]", sp->max_time_step_old); +} + +#if defined(HAVE_HDF5) +INLINE static void stars_props_print_snapshot(hid_t h_grpstars, + hid_t h_grp_columns, + const struct stars_props *sp) { + io_write_attribute_s(h_grpstars, "Kernel function", kernel_name); + io_write_attribute_f(h_grpstars, "Kernel target N_ngb", + sp->target_neighbours); + io_write_attribute_f(h_grpstars, "Kernel delta N_ngb", sp->delta_neighbours); + io_write_attribute_f(h_grpstars, "Kernel eta", sp->eta_neighbours); + io_write_attribute_f(h_grpstars, "Smoothing length tolerance", + sp->h_tolerance); + io_write_attribute_f(h_grpstars, "Volume log(max(delta h))", + sp->log_max_h_change); + io_write_attribute_f(h_grpstars, "Volume max change time-step", + pow_dimension(expf(sp->log_max_h_change))); + io_write_attribute_i(h_grpstars, "Max ghost iterations", + sp->max_smoothing_iterations); + + static const char luminosity_band_names[luminosity_bands_count][32] = { + "GAMA_u", "GAMA_g", "GAMA_r", "GAMA_i", "GAMA_z", + "GAMA_Y", "GAMA_J", "GAMA_H", "GAMA_K"}; + + /* Add to the named columns */ + hsize_t dims[1] = {luminosity_bands_count}; + hid_t type = H5Tcopy(H5T_C_S1); + H5Tset_size(type, 32); + hid_t space = H5Screate_simple(1, dims, NULL); + hid_t dset = H5Dcreate(h_grp_columns, "Luminosities", type, space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, luminosity_band_names[0]); + H5Dclose(dset); + + H5Tclose(type); + H5Sclose(space); +} +#endif + +/** + * @brief Free the memory allocated for the stellar properties. + * + * @param sp The #stars_props structure. + */ +INLINE static void stars_props_clean(struct stars_props *sp) { + for (int i = 0; i < (int)luminosity_bands_count; ++i) { + free(sp->lum_tables_Z[i]); + free(sp->lum_tables_ages[i]); + free(sp->lum_tables_luminosities[i]); + } +} + +/** + * @brief Write a #stars_props struct to the given FILE as a stream of bytes. + * + * @param p the struct + * @param stream the file stream + */ +INLINE static void stars_props_struct_dump(struct stars_props *p, + FILE *stream) { + restart_write_blocks((void *)p, sizeof(struct stars_props), 1, stream, + "starsprops", "stars props"); + + const int count_Z = eagle_stars_lum_tables_N_Z; + const int count_ages = eagle_stars_lum_tables_N_ages; + const int count_L = count_Z * count_ages; + + /* Did we allocate anything? */ + if (p->lum_tables_Z[0]) { + for (int i = 0; i < (int)luminosity_bands_count; ++i) { + restart_write_blocks(p->lum_tables_Z[i], count_Z, sizeof(float), stream, + "luminosity_Z", "stars props"); + restart_write_blocks(p->lum_tables_ages[i], count_ages, sizeof(float), + stream, "luminosity_ages", "stars props"); + restart_write_blocks(p->lum_tables_luminosities[i], count_L, + sizeof(float), stream, "luminosity_L", + "stars props"); + } + } +} + +/** + * @brief Restore a stars_props struct from the given FILE as a stream of + * bytes. + * + * @param p the struct + * @param stream the file stream + */ +INLINE static void stars_props_struct_restore(struct stars_props *p, + FILE *stream) { + restart_read_blocks((void *)p, sizeof(struct stars_props), 1, stream, NULL, + "stars props"); + + /* Did we allocate anything? */ + if (p->lum_tables_Z[0]) { + for (int i = 0; i < (int)luminosity_bands_count; ++i) { + const int count_Z = eagle_stars_lum_tables_N_Z; + const int count_ages = eagle_stars_lum_tables_N_ages; + const int count_L = count_Z * count_ages; + + p->lum_tables_Z[i] = (float *)malloc(count_Z * sizeof(float)); + p->lum_tables_ages[i] = (float *)malloc(count_ages * sizeof(float)); + p->lum_tables_luminosities[i] = (float *)malloc(count_L * sizeof(float)); + + restart_read_blocks((void *)p->lum_tables_Z[i], count_Z, sizeof(float), + stream, NULL, "stars props"); + restart_read_blocks((void *)p->lum_tables_ages[i], count_ages, + sizeof(float), stream, NULL, "stars props"); + restart_read_blocks((void *)p->lum_tables_luminosities[i], count_L, + sizeof(float), stream, NULL, "stars props"); + } + } +} + +#endif /* SWIFT_SIMBA_STAR_IO_H */ diff --git a/src/stars/KIARA/stars_part.h b/src/stars/KIARA/stars_part.h new file mode 100644 index 0000000000..1d1f8dbd1b --- /dev/null +++ b/src/stars/KIARA/stars_part.h @@ -0,0 +1,275 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2018 Folkert Nobels (nobels@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 . + * + ******************************************************************************/ +#ifndef SWIFT_SIMBA_STAR_PART_H +#define SWIFT_SIMBA_STAR_PART_H + +/* Some standard headers. */ +#include + +/* Read additional aubgrid models */ +#include "chemistry_struct.h" +#include "feedback_struct.h" +#ifdef WITH_FOF_GALAXIES +#include "fof_struct.h" +#endif +#include "particle_splitting_struct.h" +#include "rt_struct.h" +#include "star_formation_struct.h" +#include "tracers_struct.h" + +/* Like EAGLE, we define the xpart and spart tracers to be the same */ +#define tracers_spart_data tracers_xpart_data + +/** + * @brief Particle fields for the star particles. + * + * All quantities related to gravity are stored in the associate #gpart. + */ +struct spart { + + /*! Particle ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + + /*! Particle position. */ + double x[3]; + + /* Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /* Offset between current position and position at last tree rebuild. */ + float x_diff_sort[3]; + + /*! Particle velocity. */ + float v[3]; + + /*! Star mass */ + float mass; + + /*! Particle smoothing length. */ + float h; + + struct { + + /* Number of neighbours. */ + float wcount; + + /* Number of neighbours spatial derivative. */ + float wcount_dh; + + } density; + + /*! Union for the birth time and birth scale factor */ + union { + + /*! Birth time */ + float birth_time; + + /*! Birth scale factor */ + float birth_scale_factor; + }; + + /*! Scale-factor / time at which this particle last did enrichment */ + float last_enrichment_time; + + /*! Initial star mass */ + float mass_init; + + /*! The physical birth density */ + float birth_density; + + /*! The birth temperature */ + float birth_temperature; + + /*! Star formation struct */ + struct star_formation_spart_data sf_data; + + /*! Feedback structure */ + struct feedback_spart_data feedback_data; + + /*! Tracer structure */ + struct tracers_spart_data tracers_data; + + /*! Chemistry structure */ + struct chemistry_spart_data chemistry_data; + + /*! Splitting structure */ + struct particle_splitting_data split_data; + + /*! Radiative Transfer data */ + struct rt_spart_data rt_data; + + /*! Particle time bin */ + timebin_t time_bin; + + /*! Number of time-steps since the last enrichment step */ + char count_since_last_enrichment; + + /*! Tree-depth at which size / 2 <= h * gamma < size */ + char depth_h; + +#ifdef SWIFT_DEBUG_CHECKS + + /* Time of the last drift */ + integertime_t ti_drift; + + /* Time of the last kick */ + integertime_t ti_kick; + +#endif + +#ifdef SWIFT_STARS_DENSITY_CHECKS + + /* Integer number of neighbours in the density loop */ + int N_density; + + /* Exact integer number of neighbours in the density loop */ + int N_density_exact; + + /*! Has this particle interacted with any unhibited neighbour? */ + char inhibited_exact; + + float n; + + float n_exact; + + float rho; + + /*! Exact value of the density field obtained via brute-force loop */ + float rho_exact; + + int has_done_feedback; +#endif + +#ifdef DEBUG_INTERACTIONS_STARS + + /*! Number of interactions in the density SELF and PAIR */ + int num_ngb_density; + + /*! List of interacting particles in the density SELF and PAIR */ + long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_STARS]; + + /*! Number of interactions in the feedback SELF and PAIR */ + int num_ngb_feedback; + + /*! List of interacting particles in the feedback SELF and PAIR */ + long long ids_ngbs_feedback[MAX_NUM_OF_NEIGHBOURS_STARS]; +#endif + +} SWIFT_STRUCT_ALIGN; + +#define eagle_stars_lum_tables_N_Z 6 +#define eagle_stars_lum_tables_N_ages 221 + +/** + * @brief The luminosity bands written in snapshots + */ +enum luminosity_bands { + luminosity_GAMA_u_band, + luminosity_GAMA_g_band, + luminosity_GAMA_r_band, + luminosity_GAMA_i_band, + luminosity_GAMA_z_band, + luminosity_GAMA_Y_band, + luminosity_GAMA_J_band, + luminosity_GAMA_H_band, + luminosity_GAMA_K_band, + luminosity_bands_count, +}; + +/** + * @brief Contains all the constants and parameters of the stars scheme + */ +struct stars_props { + + /*! Resolution parameter */ + float eta_neighbours; + + /*! Target weighted number of neighbours (for info only)*/ + float target_neighbours; + + /*! Smoothing length tolerance */ + float h_tolerance; + + /*! Tolerance on neighbour number (for info only)*/ + float delta_neighbours; + + /*! Maximal number of iterations to converge h */ + int max_smoothing_iterations; + + /*! Maximal change of h over one time-step */ + float log_max_h_change; + + /*! Are we overwriting the stars' birth time read from the ICs? */ + int overwrite_birth_time; + + /*! Are we overwriting the stars' birth density read from the ICs? */ + int overwrite_birth_density; + + /*! Are we overwriting the stars' birth temperature read from the ICs? */ + int overwrite_birth_temperature; + + /*! Value to set birth time of stars read from ICs */ + float spart_first_init_birth_time; + + /*! Value to set birth density of stars read from ICs */ + float spart_first_init_birth_density; + + /*! Value to set birth temperature of stars read from ICs */ + float spart_first_init_birth_temperature; + + /*! Factor to multiply age to get the timestep for young stars */ + float time_step_factor_young; + + /*! Factor to multiply age to get the timestep for old stars */ + float time_step_factor_old; + + /*! Maximal time-step length of young stars (internal units) */ + double max_time_step_young; + + /*! Maximal time-step length of old stars (internal units) */ + double max_time_step_old; + + /*! Minimum time-step length of all stars */ + double min_time_step; + + /*! Age threshold for the young/old transition (internal units) */ + double age_threshold; + + /*! Age threshold for the transition to unlimited time-step size (internal + * units) */ + double age_threshold_unlimited; + + /*! The metallicities (metal mass frac) for the luminosity interpolations */ + float* lum_tables_Z[luminosity_bands_count]; + + /*! The age (in Gyr) for the luminosity interpolations */ + float* lum_tables_ages[luminosity_bands_count]; + + /*! The luminosities */ + float* lum_tables_luminosities[luminosity_bands_count]; + + /*! Conversion factor to luminosities */ + double lum_tables_factor; +}; + +#endif /* SWIFT_SIMBA_STAR_PART_H */ diff --git a/src/stars_io.h b/src/stars_io.h index a629235346..a510538552 100644 --- a/src/stars_io.h +++ b/src/stars_io.h @@ -30,6 +30,8 @@ #include "./stars/EAGLE/stars_io.h" #elif defined(STARS_GEAR) #include "./stars/GEAR/stars_io.h" +#elif defined(STARS_KIARA) +#include "./stars/KIARA/stars_io.h" #else #error "Invalid choice of star model" #endif diff --git a/src/timestep.h b/src/timestep.h index 8db2ea2100..684d0697e4 100644 --- a/src/timestep.h +++ b/src/timestep.h @@ -297,7 +297,8 @@ __attribute__((always_inline)) INLINE static integertime_t get_spart_timestep( /* Stellar time-step */ float new_dt_stars = stars_compute_timestep( - sp, e->stars_properties, e->feedback_props, e->physical_constants, + sp, e->stars_properties, //e->feedback_props, + e->physical_constants, e->internal_units, (e->policy & engine_policy_cosmology), e->cosmology, e->ti_current, e->time, e->time_base); From 632fe02875c867be3db948f7bbe879cbf3ffd1b8 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Thu, 20 Nov 2025 15:31:59 +0000 Subject: [PATCH 07/37] Add Kiara feedback and Obsidian BH; full KIARA model now works --- configure.ac | 5 +- src/Makefile.am | 2 +- src/black_holes.h | 2 + src/black_holes/Obsidian/black_holes.h | 1890 ++++++++++++++ src/black_holes/Obsidian/black_holes.h.rad | 1730 +++++++++++++ src/black_holes/Obsidian/black_holes_debug.h | 31 + src/black_holes/Obsidian/black_holes_iact.h | 1408 ++++++++++ src/black_holes/Obsidian/black_holes_io.h | 459 ++++ .../Obsidian/black_holes_parameters.h | 42 + src/black_holes/Obsidian/black_holes_part.h | 327 +++ .../Obsidian/black_holes_properties.h | 1302 ++++++++++ src/black_holes/Obsidian/black_holes_struct.h | 143 ++ src/black_holes_debug.h | 2 + src/black_holes_iact.h | 2 + src/black_holes_io.h | 2 + src/black_holes_properties.h | 2 + src/black_holes_struct.h | 2 + src/cooling/KIARA/cooling.c | 183 +- src/cooling/KIARA/cooling.h | 1 - src/feedback.h | 3 + src/feedback/KIARA/feedback.c | 2266 +++++++++++++++++ src/feedback/KIARA/feedback.h | 1000 ++++++++ src/feedback/KIARA/feedback_debug.h | 26 + src/feedback/KIARA/feedback_iact.h | 894 +++++++ src/feedback/KIARA/feedback_properties.h | 392 +++ src/feedback/KIARA/feedback_struct.h | 127 + .../libswiftsim_la-feedback-335341eb.o.tmp | 0 .../libswiftsim_la-feedback-f98e8954.o.tmp | 0 .../KIARA/mpi-feedback-8dfc2d63.o.tmp | 0 src/feedback_debug.h | 2 + src/feedback_iact.h | 2 + src/feedback_new_stars.h | 2 + src/feedback_properties.h | 2 + src/feedback_struct.h | 2 + src/fof.c | 25 + src/fof_struct.h | 18 + src/hydro/MAGMA2/hydro.h | 7 + src/hydro/MAGMA2/hydro_part.h | 5 + src/part.h | 2 + src/runner_doiact_functions_stars.h | 8 +- src/runner_others.c | 6 +- src/runner_time_integration.c | 4 +- src/space_first_init.c | 3 + src/star_formation/KIARA/star_formation.h | 19 +- src/stars/KIARA/stars_io.h | 11 +- src/stars/KIARA/stars_part.h | 5 + 46 files changed, 12258 insertions(+), 108 deletions(-) create mode 100644 src/black_holes/Obsidian/black_holes.h create mode 100644 src/black_holes/Obsidian/black_holes.h.rad create mode 100644 src/black_holes/Obsidian/black_holes_debug.h create mode 100644 src/black_holes/Obsidian/black_holes_iact.h create mode 100644 src/black_holes/Obsidian/black_holes_io.h create mode 100644 src/black_holes/Obsidian/black_holes_parameters.h create mode 100644 src/black_holes/Obsidian/black_holes_part.h create mode 100644 src/black_holes/Obsidian/black_holes_properties.h create mode 100644 src/black_holes/Obsidian/black_holes_struct.h create mode 100644 src/feedback/KIARA/feedback.c create mode 100644 src/feedback/KIARA/feedback.h create mode 100644 src/feedback/KIARA/feedback_debug.h create mode 100644 src/feedback/KIARA/feedback_iact.h create mode 100644 src/feedback/KIARA/feedback_properties.h create mode 100644 src/feedback/KIARA/feedback_struct.h create mode 100644 src/feedback/KIARA/libswiftsim_la-feedback-335341eb.o.tmp create mode 100644 src/feedback/KIARA/libswiftsim_la-feedback-f98e8954.o.tmp create mode 100644 src/feedback/KIARA/mpi-feedback-8dfc2d63.o.tmp diff --git a/configure.ac b/configure.ac index f4131109f7..ad7fa56e55 100644 --- a/configure.ac +++ b/configure.ac @@ -2199,9 +2199,8 @@ case "$with_subgrid" in with_subgrid_entropy_floor=none with_subgrid_stars=KIARA with_subgrid_star_formation=KIARA - with_subgrid_feedback=none - with_subgrid_black_holes=none - #with_subgrid_black_holes=Obsidian + with_subgrid_feedback=KIARA + with_subgrid_black_holes=Obsidian with_subgrid_sink=none with_subgrid_extra_io=none enable_fof=yes diff --git a/src/Makefile.am b/src/Makefile.am index b3b3328266..30fc8ba522 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -231,7 +231,7 @@ AM_SOURCES += $(AGORA_FEEDBACK_SOURCES) AM_SOURCES += $(PS2020_COOLING_SOURCES) AM_SOURCES += $(SPHM1RT_RT_SOURCES) AM_SOURCES += $(GEAR_RT_SOURCES) -AM_SOURCES += $(KIARA_COOLING_SOURCES) # $(KIARA_FEEDBACK_SOURCES) +AM_SOURCES += $(KIARA_COOLING_SOURCES) $(KIARA_FEEDBACK_SOURCES) AM_SOURCES += swift_lustre_api.c # Include files for distribution, not installation. diff --git a/src/black_holes.h b/src/black_holes.h index 4051727192..b2fa561001 100644 --- a/src/black_holes.h +++ b/src/black_holes.h @@ -29,6 +29,8 @@ #include "./black_holes/EAGLE/black_holes.h" #elif defined(BLACK_HOLES_SPIN_JET) #include "./black_holes/SPIN_JET/black_holes.h" +#elif defined(BLACK_HOLES_OBSIDIAN) +#include "./black_holes/Obsidian/black_holes.h" #else #error "Invalid choice of black hole model" #endif diff --git a/src/black_holes/Obsidian/black_holes.h b/src/black_holes/Obsidian/black_holes.h new file mode 100644 index 0000000000..0083b5434d --- /dev/null +++ b/src/black_holes/Obsidian/black_holes.h @@ -0,0 +1,1890 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_OBSIDIAN_BLACK_HOLES_H +#define SWIFT_OBSIDIAN_BLACK_HOLES_H + +/* Local includes */ +#include "black_holes_properties.h" +#include "black_holes_struct.h" +#include "cooling.h" +#include "star_formation.h" +#include "cosmology.h" +#include "dimension.h" +#include "exp10.h" +#include "gravity.h" +#include "kernel_hydro.h" +#include "minmax.h" +#include "physical_constants.h" +#include "random.h" + +/* Standard includes */ +#include +#include +#include + + +/** + * @brief How much of the feedback actually couples to the medium? + * + * @param bp The black hole particle. + * @param props The properties of the black hole scheme. + * @param BH_state The current state of the black hole. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static double get_black_hole_coupling( + const struct bpart* const bp, const struct black_holes_props* props, + const struct cosmology* cosmo, const struct phys_const* phys_const) { + const int BH_state = bp->state; + switch (BH_state) { + case BH_states_adaf: + { + float scaling = 1.f; + if (props->adaf_coupling < 0.f) { + min(pow(1. + cosmo->z, props->adaf_z_scaling), 1.); + } + return fabs(props->adaf_coupling) * scaling; + break; + } + case BH_states_quasar: + { + float quasar_coupling = fabs(props->quasar_coupling); + const double c = phys_const->const_speed_light_c; + const double luminosity = bp->radiative_efficiency * bp->accretion_rate * c * c; + const float mass_limit = get_black_hole_adaf_mass_limit(bp, props, cosmo); + if (luminosity > props->quasar_luminosity_thresh && props->quasar_luminosity_thresh > 0.f && bp->subgrid_mass * props->mass_to_solar_mass > mass_limit) { + quasar_coupling = fmin(quasar_coupling * luminosity / props->quasar_luminosity_thresh, 1.); + //message("BOOST: z=%g id=%lld MBH=%g LBH=%g boost=%g qcoupling=%g", cosmo->z, bp->id, bp->subgrid_mass * props->mass_to_solar_mass, luminosity * props->conv_factor_energy_rate_to_cgs, luminosity / props->quasar_luminosity_thresh, quasar_coupling); + } + return quasar_coupling; + break; + } + case BH_states_slim_disk: + return fabs(props->slim_disk_coupling); + break; + default: + error("Invalid black hole state."); + return 0.; + break; + } +} + +/** + * @brief Computes the radiative efficiency in the slim disk mode. + * + * @param props The properties of the black hole scheme. + * @param f_Edd M_dot,BH / M_dot,Edd + */ +__attribute__((always_inline)) INLINE static double +get_black_hole_slim_disk_efficiency(const struct black_holes_props* props, + const double f_Edd) { + if (f_Edd <= 0.) return 0.; + const double R = 1. / f_Edd; + /* Efficiency from Lupi et al. (2014), + * super eddington accretion and feedback */ + return (R / 16.) * props->A_sd * + (0.985 / (R + (5. / 8.) * props->B_sd) + 0.015 / + (R + (5. / 8.) * props->C_sd)); +} + +/** + * @brief Computes the radiative efficiency in the ADAF mode. + * + * @param props The properties of the black hole scheme. + * @param f_Edd M_dot,BH / M_dot,Edd + */ +__attribute__((always_inline)) INLINE static +double get_black_hole_adaf_efficiency( + const struct black_holes_props* props, const double f_Edd) { + return props->epsilon_r * f_Edd; /* scales with M_dot,BH */ +} + +/** + * @brief Chooses and calls the proper radiative efficiency function for the + * state. + * + * @param props The properties of the black hole scheme. + * @param f_Edd The accretion rate over the Eddington rate. + * @param BH_state The current state of the BH. + */ +__attribute__((always_inline)) INLINE static +double get_black_hole_radiative_efficiency( + const struct black_holes_props* props, + const double f_Edd, const int BH_state) { + switch(BH_state) { + case BH_states_adaf: + return get_black_hole_adaf_efficiency(props, f_Edd); + case BH_states_quasar: + return props->epsilon_r; + case BH_states_slim_disk: + return get_black_hole_slim_disk_efficiency(props, f_Edd); + default: + error("Invalid black hole state."); + break; + } + + return 0.; +} + +/** + * @brief Compute the wind launch speed for this feedback step. + * + * @param props The properties of the black hole scheme. + * @param phys_const The physical phys_const (in internal units). + * @param bp The black hole particle. + */ +__attribute__((always_inline)) INLINE static double +get_black_hole_wind_speed(const struct black_holes_props* props, + const struct phys_const* phys_const, + const struct bpart *bp) { + + if (bp->accretion_rate < 0.f || bp->m_dot_inflow < 0.f) return 0.f; + + float v_kick = 0.f; + if (props->quasar_wind_speed < 0.f || props->slim_disk_wind_speed < 0.f) { + const float subgrid_mass_Msun = + bp->subgrid_mass * props->mass_to_solar_mass; + + if (bp->subgrid_mass > props->subgrid_seed_mass) { + const float min_BH_mass_Msun = + props->minimum_black_hole_mass_v_kick * props->mass_to_solar_mass; + const float dlog10_BH_mass = + log10f(subgrid_mass_Msun) - log10f(min_BH_mass_Msun); + v_kick = 500.f + (500.f / 3.f) * dlog10_BH_mass; + + /* Sometimes can get very small leading to huge mass loadings */ + if (v_kick < props->minimum_v_kick_km_s) { + v_kick = props->minimum_v_kick_km_s; + } + + v_kick *= props->kms_to_internal; + } + } + + switch (bp->state) { + case BH_states_adaf: + return fabs(props->adaf_wind_speed); + break; + case BH_states_quasar: + if (props->quasar_wind_speed < 0.f && v_kick > 0.f) { + return v_kick; + } + else { + return fabs(props->quasar_wind_speed); + } + break; + case BH_states_slim_disk: + if (props->slim_disk_wind_speed < 0.f && v_kick > 0.f) { + return v_kick; + } + else { + return fabs(props->slim_disk_wind_speed); + } + break; + default: + error("Invalid black hole state."); + return 0.f; + break; + } +} + +/** + * @brief Computes the fraction of M_dot,inflow that should go into the BH. + * + * @param props The properties of the black hole scheme. + * @param phys_const The physical phys_const (in internal units). + * @param cosmo The current cosmological model. + * @param bp The black hole particle. + * @param m_dot_inflow_m_dot_edd M_dot,inflow scaled to M_dot,Edd for the BH. + */ +__attribute__((always_inline)) INLINE static double +get_black_hole_upper_mdot_medd(const struct black_holes_props* props, + const struct phys_const* phys_const, + const struct cosmology* cosmo, + const struct bpart* const bp, + const double m_dot_inflow_m_dot_edd) { + + if (m_dot_inflow_m_dot_edd <= 0.) return 0.; + + double x1, x2, x3; + double a3, a2, a1, a0; + + double phi = props->slim_disk_phi; + if (props->slim_disk_wind_speed < 0.f) { + const float v_kick = get_black_hole_wind_speed(props, phys_const, bp); + + if (v_kick > 0.f) { + /* Set the slim disk mass loading to be continuous at the + * eta upper boundary. Compute the phi term to solve for the + * accretion fraction. + * WARNING: Using the quasar_wind_momentum_flux because that is the + * default way of using the model. Any changes to that require a + * change here. */ + const double c_over_v = phys_const->const_speed_light_c / v_kick; + + /* TODO: Compute once at the beginning of the simulation */ + double mom_flux_times_epsilon_sd = + props->quasar_wind_momentum_flux * get_black_hole_coupling(bp, props, cosmo, phys_const); + phi = mom_flux_times_epsilon_sd * c_over_v; + } + } + + int num_roots; + + a3 = ((5. * 5.) / (8. * 8.)) * props->B_sd * props->C_sd; + a2 = (5. / 8.) * ((props->B_sd + props->C_sd) + (phi / 16.) * + props->A_sd * (0.015 * props->B_sd + 0.985 * props->C_sd) - + (5. / 8.) * props->B_sd * props->C_sd * m_dot_inflow_m_dot_edd); + a1 = 1. + (phi / 16.) * props->A_sd - (5. / 8.) * + (props->B_sd + props->C_sd) * m_dot_inflow_m_dot_edd; + a0 = -m_dot_inflow_m_dot_edd; + + a2 /= a3; + a1 /= a3; + a0 /= a3; + + num_roots = gsl_poly_solve_cubic(a2, a1, a0, &x1, &x2, &x3); + if (num_roots == 1) { + if (x1 >= 0.) { + return x1; + } + else { + warning("num_roots=1 m_dot_inflow_m_dot_edd=%g phi=%g a3=%g a2=%g " + "a1=%g a0=%g", + m_dot_inflow_m_dot_edd, phi, a3, a2, a1, a0); + return 0.; + } + } + if (x3 >= 0.) { + return x3; + } + else { + warning("num_roots=0 m_dot_inflow_m_dot_edd=%g phi=%g a3=%g a2=%g a1=%g " + "a0=%g", + m_dot_inflow_m_dot_edd, phi, a3, a2, a1, a0); + return 0.; + } + + return 0.; +} + +/** + * @brief Computes the fraction of M_dot,inflow that should go into the BH. + * + * @param props The properties of the black hole scheme. + * @param phys_const The physical phys_const (in internal units). + * @param m_dot_inflow M_dot,inflow in internal units. + * @param BH_mass The subgrid mass of the BH in internal units. + * @param BH_state The current state of the BH. + * @param Eddington_rate M_dot,Edd in internal units. + */ +__attribute__((always_inline)) INLINE static +double get_black_hole_accretion_factor(const struct black_holes_props* props, + const struct phys_const* phys_const, + const struct cosmology* cosmo, + const struct bpart* const bp, + const double Eddington_rate) { + + const double m_dot_inflow = bp->m_dot_inflow; + const double BH_mass = bp->subgrid_mass; + const int BH_state = bp->state; + + if (m_dot_inflow <= 0. || BH_mass <= 0.) return 0.; + + switch (BH_state) { + case BH_states_adaf: + return props->adaf_f_accretion; + break; + case BH_states_quasar: + { + float v_kick = 0.f; + float f_accretion = 0.f; + if (props->quasar_wind_speed < 0.f) { + /* Save computation by only computing when specified by the user */ + v_kick = get_black_hole_wind_speed(props, phys_const, bp); + if (v_kick > 0.) { + const double c = phys_const->const_speed_light_c; + const double c_over_v = c / v_kick; + double quasar_coupling = get_black_hole_coupling(bp, props, cosmo, phys_const); + double quasar_wind_mass_loading = + props->quasar_wind_momentum_flux * + quasar_coupling * props->epsilon_r * c_over_v; + f_accretion = 1.f / (1.f + quasar_wind_mass_loading); + } + } + + if (f_accretion > 0.f) { + return f_accretion; + } + else { + return props->quasar_f_accretion; + } + break; + } + case BH_states_slim_disk: + { + /* This is the FRACTION of the total so divide by M_dot,inflow */ + const double f_edd = m_dot_inflow / Eddington_rate; + double mdot_medd = + get_black_hole_upper_mdot_medd(props, phys_const, cosmo, bp, f_edd); + return mdot_medd * Eddington_rate / m_dot_inflow; + break; + } + default: + error("Invalid black hole state."); + return 0.; + break; + } +} + +/** + * @brief Computes the time-step of a given black hole particle. + * + * @param bp Pointer to the s-particle data. + * @param props The properties of the black hole scheme. + * @param phys_const The physical phys_const (in internal units). + */ +__attribute__((always_inline)) INLINE static float black_holes_compute_timestep( + const struct bpart* const bp, const struct black_holes_props* props, + const struct phys_const* phys_const, const struct cosmology* cosmo) { + + /* Allow for finer timestepping if necessary! */ + float dt_accr = FLT_MAX; + float dt_overall = FLT_MAX; + float dt_kick = FLT_MAX; + const float min_subgrid_mass = props->minimum_black_hole_mass_unresolved; + + /* Only limit when in the resolved feedback regime */ + if (bp->accretion_rate > 0.f && bp->subgrid_mass > min_subgrid_mass) { + dt_accr = props->dt_accretion_factor * bp->mass / bp->accretion_rate; + + if (bp->state == BH_states_adaf && bp->jet_mass_loading > 0.f) { + dt_kick = bp->ngb_mass / (bp->jet_mass_loading * bp->accretion_rate); + } + else { + if (bp->f_accretion > 0.f) { + /* Make sure that the wind mass does not exceed the kernel gas mass */ + const float psi = (1.f - bp->f_accretion) / bp->f_accretion; + dt_kick = bp->ngb_mass / (psi * bp->accretion_rate); + } + } + + dt_overall = min(dt_kick, dt_accr); + } + + if (dt_overall < props->time_step_min) { + message( + "Warning! BH_TIMESTEP_LOW: id=%lld (%g Myr) is below time_step_min (%g " + "Myr).", + bp->id, dt_overall * props->time_to_Myr, + props->time_step_min * props->time_to_Myr); + } + + return max(dt_overall, props->time_step_min); +} + +/** + * @brief Initialises the b-particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param bp The particle to act upon + * @param props The properties of the black holes model. + */ +__attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( + struct bpart* bp, const struct black_holes_props* props) { + + bp->time_bin = 0; + if (props->use_subgrid_mass_from_ics == 0) { + bp->subgrid_mass = bp->mass; + } else if (props->with_subgrid_mass_check && bp->subgrid_mass <= 0) { + error( + "Black hole %lld has a subgrid mass of %f (internal units).\n" + "If this is because the ICs do not contain a 'SubgridMass' data " + "set, you should set the parameter " + "'ObsidianAGN:use_subgrid_mass_from_ics' to 0 to initialize the " + "black hole subgrid masses to the corresponding dynamical masses.\n" + "If the subgrid mass is intentionally set to this value, you can " + "disable this error by setting 'ObsidianAGN:with_subgrid_mass_check' " + "to 0.", + bp->id, bp->subgrid_mass); + } + bp->total_accreted_mass = 0.f; + bp->accretion_disk_mass = 0.f; + bp->gas_SFR = 0.f; + bp->accretion_rate = 0.f; + bp->bondi_accretion_rate = 0.f; + bp->formation_time = -1.f; + bp->cumulative_number_seeds = 1; + bp->number_of_mergers = 0; + bp->number_of_gas_swallows = 0; + bp->number_of_direct_gas_swallows = 0; + bp->number_of_repositions = 0; + bp->number_of_reposition_attempts = 0; + bp->number_of_time_steps = 0; + bp->last_minor_merger_time = -1.; + bp->last_major_merger_time = -1.; + bp->swallowed_angular_momentum[0] = 0.f; + bp->swallowed_angular_momentum[1] = 0.f; + bp->swallowed_angular_momentum[2] = 0.f; + bp->accreted_angular_momentum[0] = 0.f; + bp->accreted_angular_momentum[1] = 0.f; + bp->accreted_angular_momentum[2] = 0.f; + bp->last_repos_vel = 0.f; + bp->radiative_luminosity = 0.f; + bp->delta_energy_this_timestep = 0.f; + bp->state = BH_states_slim_disk; + bp->radiative_efficiency = 0.f; + bp->f_accretion = 0.f; + bp->m_dot_inflow = 0.f; + bp->cold_disk_mass = 0.f; + bp->jet_mass_reservoir = 0.f; + /* Default to the original value at fixed jet_velocity */ + bp->jet_mass_loading = props->jet_mass_loading; + bp->jet_mass_kicked_this_step = 0.f; + bp->adaf_energy_to_dump = 0.f; + bp->adaf_energy_used_this_step = 0.f; + +} + +/** + * @brief Prepares a b-particle for its interactions + * + * @param bp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void black_holes_init_bpart( + struct bpart* bp) { + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + bp->ids_ngbs_density[i] = -1; + bp->num_ngb_density = 0; +#endif + + bp->density.wcount = 0.f; + bp->density.wcount_dh = 0.f; + bp->rho_gas = 0.f; + bp->sound_speed_gas = 0.f; + bp->internal_energy_gas = 0.f; + bp->hot_gas_mass = 0.f; + bp->cold_gas_mass = 0.f; + bp->hot_gas_internal_energy = 0.f; + bp->sound_speed_subgrid_gas = -1.f; + bp->velocity_gas[0] = 0.f; + bp->velocity_gas[1] = 0.f; + bp->velocity_gas[2] = 0.f; + bp->circular_velocity_gas[0] = 0.f; + bp->circular_velocity_gas[1] = 0.f; + bp->circular_velocity_gas[2] = 0.f; + bp->angular_momentum_gas[0] = 0.f; + bp->angular_momentum_gas[1] = 0.f; + bp->angular_momentum_gas[2] = 0.f; + bp->stellar_mass = 0.f; + bp->stellar_bulge_mass = 0.f; + bp->radiative_luminosity = 0.f; + bp->ngb_mass = 0.f; + bp->gravitational_ngb_mass = 0.f; + bp->num_ngbs = 0; + bp->num_gravitational_ngbs = 0; + bp->reposition.delta_x[0] = -FLT_MAX; + bp->reposition.delta_x[1] = -FLT_MAX; + bp->reposition.delta_x[2] = -FLT_MAX; + bp->reposition.min_potential = FLT_MAX; + bp->reposition.potential = FLT_MAX; + bp->accretion_rate = 0.f; /* Optionally accumulated ngb-by-ngb */ + bp->bondi_accretion_rate = 0.f; /* Optionally accumulated ngb-by-ngb */ + bp->cold_disk_mass = 0.f; + bp->mass_at_start_of_step = bp->mass; /* bp->mass may grow in nibbling mode */ + bp->m_dot_inflow = 0.f; /* reset accretion rate */ + bp->kernel_wt_sum = 0.f; + + /* update the reservoir */ + bp->jet_mass_reservoir -= bp->jet_mass_kicked_this_step; + bp->jet_mass_kicked_this_step = 0.f; + if (bp->jet_mass_reservoir < 0.f) { + bp->jet_mass_reservoir = 0.f; /* reset reservoir if used up */ + } + /* update the unresolved reservoir */ + bp->unresolved_mass_reservoir -= bp->unresolved_mass_kicked_this_step; + bp->unresolved_mass_kicked_this_step = 0.f; + if (bp->unresolved_mass_reservoir < 0.f) { + bp->unresolved_mass_reservoir = 0.f; + } + /* update the adaf energy reservoir */ + if (bp->adaf_wt_sum > 0.f) { + const double adaf_energy_used = + bp->adaf_energy_used_this_step / bp->adaf_wt_sum; + bp->adaf_energy_to_dump -= adaf_energy_used; + bp->adaf_wt_sum = 0.f; + bp->adaf_energy_used_this_step = 0.f; + if (bp->adaf_energy_to_dump < 0.f) { + bp->adaf_energy_to_dump = 0.f; + } + } + else { + bp->adaf_energy_used_this_step = 0.f; + } +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * The fields do not get predicted but we move the BH to its new position + * if a new one was calculated in the repositioning loop. + * + * @param bp The particle + * @param dt_drift The drift time-step for positions. + */ +__attribute__((always_inline)) INLINE static void black_holes_predict_extra( + struct bpart* restrict bp, float dt_drift) { + + /* Are we doing some repositioning? */ + if (bp->reposition.min_potential != FLT_MAX) { + +#ifdef SWIFT_DEBUG_CHECKS + if (bp->reposition.delta_x[0] == -FLT_MAX || + bp->reposition.delta_x[1] == -FLT_MAX || + bp->reposition.delta_x[2] == -FLT_MAX) { + error("Something went wrong with the new repositioning position"); + } + + const double dx = bp->reposition.delta_x[0]; + const double dy = bp->reposition.delta_x[1]; + const double dz = bp->reposition.delta_x[2]; + const double d = sqrt(dx * dx + dy * dy + dz * dz); + if (d > 1.01 * kernel_gamma * bp->h) + error("Repositioning BH beyond the kernel support!"); +#endif + + /* Move the black hole */ + bp->x[0] += bp->reposition.delta_x[0]; + bp->x[1] += bp->reposition.delta_x[1]; + bp->x[2] += bp->reposition.delta_x[2]; + + /* Move its gravity properties as well */ + bp->gpart->x[0] += bp->reposition.delta_x[0]; + bp->gpart->x[1] += bp->reposition.delta_x[1]; + bp->gpart->x[2] += bp->reposition.delta_x[2]; + + /* Store the delta position */ + bp->x_diff[0] -= bp->reposition.delta_x[0]; + bp->x_diff[1] -= bp->reposition.delta_x[1]; + bp->x_diff[2] -= bp->reposition.delta_x[2]; + + /* Reset the reposition variables */ + bp->reposition.delta_x[0] = -FLT_MAX; + bp->reposition.delta_x[1] = -FLT_MAX; + bp->reposition.delta_x[2] = -FLT_MAX; + bp->reposition.min_potential = FLT_MAX; + + /* Count the jump */ + bp->number_of_repositions++; + } +} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param bp The particle. + */ +__attribute__((always_inline)) INLINE static void +black_holes_reset_predicted_values(struct bpart* bp) {} + +/** + * @brief Kick the additional variables + * + * @param bp The particle to act upon + * @param dt The time-step for this kick + */ +__attribute__((always_inline)) INLINE static void black_holes_kick_extra( + struct bpart* bp, float dt) {} + +/** + * @brief Finishes the calculation of density on black holes + * + * @param bp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void black_holes_end_density( + struct bpart* bp, const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = bp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + /* --- Finish the calculation by inserting the missing h factors --- */ + bp->density.wcount *= h_inv_dim; + bp->density.wcount_dh *= h_inv_dim_plus_one; + bp->rho_gas *= h_inv_dim; + float rho_inv = 1.f; + if (bp->rho_gas > 0.f) rho_inv = 1.f / bp->rho_gas; + + /* For the following, we also have to undo the mass smoothing + * (N.B.: bp->velocity_gas is in BH frame, in internal units). */ + bp->sound_speed_gas *= h_inv_dim * rho_inv; + bp->internal_energy_gas *= h_inv_dim * rho_inv; + + /* Non-weighted (no decoupled winds) properties below. + * All mass-weighted quantities are for the hot & cold gas */ + float m_hot_inv = 1.f; + if (bp->hot_gas_mass > 0.f) m_hot_inv /= bp->hot_gas_mass; + /* Or the total mass */ + float m_tot_inv = 1.f; + if (bp->ngb_mass > 0.f) m_tot_inv /= bp->ngb_mass; + + bp->hot_gas_internal_energy *= m_hot_inv; + bp->velocity_gas[0] *= m_tot_inv; + bp->velocity_gas[1] *= m_tot_inv; + bp->velocity_gas[2] *= m_tot_inv; + bp->circular_velocity_gas[0] *= m_tot_inv; + bp->circular_velocity_gas[1] *= m_tot_inv; + bp->circular_velocity_gas[2] *= m_tot_inv; + + /* Calculate circular velocity at the smoothing radius from specific + * angular momentum (extra h_inv). It is now a VELOCITY. + */ + bp->circular_velocity_gas[0] *= h_inv; + bp->circular_velocity_gas[1] *= h_inv; + bp->circular_velocity_gas[2] *= h_inv; + +} + +/** + * @brief Sets all particle fields to sensible values when the #spart has 0 + * ngbs. + * + * @param bp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void +black_holes_bpart_has_no_neighbours(struct bpart* bp, + const struct cosmology* cosmo) { + + //warning( + // "BH particle with ID %lld treated as having no neighbours (h: %g, " + // "wcount: %g).", + // bp->id, bp->h, bp->density.wcount); + + /* Some smoothing length multiples. */ + const float h = bp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + + /* Re-set problematic values */ + bp->density.wcount = kernel_root * h_inv_dim; + bp->density.wcount_dh = 0.f; + + bp->velocity_gas[0] = FLT_MAX; + bp->velocity_gas[1] = FLT_MAX; + bp->velocity_gas[2] = FLT_MAX; + + bp->internal_energy_gas = -FLT_MAX; + bp->hot_gas_internal_energy = -FLT_MAX; +} + +/** + * @brief Return the current instantaneous accretion rate of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_accretion_rate(const struct bpart* bp) { + return bp->accretion_rate; +} + +/** + * @brief Return the total accreted gas mass of this BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_accreted_mass(const struct bpart* bp) { + return bp->total_accreted_mass; +} + +/** + * @brief Return the subgrid mass of this BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_subgrid_mass(const struct bpart* bp) { + return bp->subgrid_mass; +} + +/** + * @brief Return the current bolometric luminosity of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_bolometric_luminosity(const struct bpart* bp, + const struct phys_const* phys_const) { + const double c = phys_const->const_speed_light_c; + return bp->accretion_rate * bp->radiative_efficiency * c * c; +} + +/** + * @brief Return the current kinetic jet power of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double black_holes_get_jet_power( + const struct bpart* bp, const struct phys_const* phys_const) { + const double c = phys_const->const_speed_light_c; + /* accretion_rate is M_dot,acc from the paper */ + return bp->radiative_efficiency * bp->accretion_rate * c * c; +} + +/** + * @brief Update the properties of a black hole particles by swallowing + * a gas particle. + * + * @param bp The #bpart to update. + * @param p The #part that is swallowed. + * @param xp The #xpart that is swallowed. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void black_holes_swallow_part( + struct bpart* bp, const struct part* p, const struct xpart* xp, + const struct cosmology* cosmo) { + + /* Get the current dynamical masses */ + const float gas_mass = hydro_get_mass(p); + const float BH_mass = bp->mass; + + /* Increase the dynamical mass of the BH. */ + bp->mass += gas_mass; + bp->gpart->mass += gas_mass; + + /* Physical velocity difference between the particles */ + const float dv[3] = {(bp->v[0] - p->v[0]) * cosmo->a_inv, + (bp->v[1] - p->v[1]) * cosmo->a_inv, + (bp->v[2] - p->v[2]) * cosmo->a_inv}; + + /* Physical distance between the particles */ + const float dx[3] = {(bp->x[0] - p->x[0]) * cosmo->a, + (bp->x[1] - p->x[1]) * cosmo->a, + (bp->x[2] - p->x[2]) * cosmo->a}; + + /* Collect the swallowed angular momentum */ + bp->swallowed_angular_momentum[0] += + gas_mass * (dx[1] * dv[2] - dx[2] * dv[1]); + bp->swallowed_angular_momentum[1] += + gas_mass * (dx[2] * dv[0] - dx[0] * dv[2]); + bp->swallowed_angular_momentum[2] += + gas_mass * (dx[0] * dv[1] - dx[1] * dv[0]); + + /* Update the BH momentum */ + const float BH_mom[3] = {BH_mass * bp->v[0] + gas_mass * p->v[0], + BH_mass * bp->v[1] + gas_mass * p->v[1], + BH_mass * bp->v[2] + gas_mass * p->v[2]}; + + bp->v[0] = BH_mom[0] / bp->mass; + bp->v[1] = BH_mom[1] / bp->mass; + bp->v[2] = BH_mom[2] / bp->mass; + bp->gpart->v_full[0] = bp->v[0]; + bp->gpart->v_full[1] = bp->v[1]; + bp->gpart->v_full[2] = bp->v[2]; + + const float dr = sqrt(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]); + message( + "BH %lld swallowing gas particle %lld " + "(Delta_v = [%f, %f, %f] U_V, " + "Delta_x = [%f, %f, %f] U_L, " + "Delta_v_rad = %f)", + bp->id, p->id, -dv[0], -dv[1], -dv[2], -dx[0], -dx[1], -dx[2], + (dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]) / dr); + + /* Update the BH metal masses */ + struct chemistry_bpart_data* bp_chem = &bp->chemistry_data; + const struct chemistry_part_data* p_chem = &p->chemistry_data; + chemistry_add_part_to_bpart(bp_chem, p_chem, gas_mass); + + /* This BH swallowed a gas particle */ + bp->number_of_gas_swallows++; + bp->number_of_direct_gas_swallows++; + + /* This BH lost a neighbour */ + bp->num_ngbs--; + bp->num_gravitational_ngbs--; + bp->ngb_mass -= gas_mass; +} + +/** + * @brief Update the properties of a black hole particles by swallowing + * a BH particle. + * + * @param bpi The #bpart to update. + * @param bpj The #bpart that is swallowed. + * @param cosmo The current cosmological model. + * @param time Time since the start of the simulation (non-cosmo mode). + * @param with_cosmology Are we running with cosmology? + * @param props The properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static void black_holes_swallow_bpart( + struct bpart* bpi, const struct bpart* bpj, const struct cosmology* cosmo, + const double time, const int with_cosmology, + const struct black_holes_props* props, const struct phys_const* phys_const) { + + /* Get the current dynamical masses */ + const float bpi_dyn_mass = bpi->mass; + const float bpj_dyn_mass = bpj->mass; + + /* Is this merger ratio above the threshold for recording? */ + const double merger_ratio = bpj->subgrid_mass / bpi->subgrid_mass; + if (merger_ratio > props->major_merger_threshold) { + if (with_cosmology) { + bpi->last_major_merger_scale_factor = cosmo->a; + } else { + bpi->last_major_merger_time = time; + } + } else if (merger_ratio > props->minor_merger_threshold) { + if (with_cosmology) { + bpi->last_minor_merger_scale_factor = cosmo->a; + } else { + bpi->last_minor_merger_time = time; + } + } + + /* Increase the masses of the BH. */ + bpi->mass += bpj->mass; + bpi->gpart->mass += bpj->mass; + bpi->subgrid_mass += bpj->subgrid_mass; + + /* Collect the swallowed angular momentum */ + bpi->swallowed_angular_momentum[0] += bpj->swallowed_angular_momentum[0]; + bpi->swallowed_angular_momentum[1] += bpj->swallowed_angular_momentum[1]; + bpi->swallowed_angular_momentum[2] += bpj->swallowed_angular_momentum[2]; + + /* Update the BH momentum */ + const float BH_mom[3] = {bpi_dyn_mass * bpi->v[0] + bpj_dyn_mass * bpj->v[0], + bpi_dyn_mass * bpi->v[1] + bpj_dyn_mass * bpj->v[1], + bpi_dyn_mass * bpi->v[2] + bpj_dyn_mass * bpj->v[2]}; + + bpi->v[0] = BH_mom[0] / bpi->mass; + bpi->v[1] = BH_mom[1] / bpi->mass; + bpi->v[2] = BH_mom[2] / bpi->mass; + bpi->gpart->v_full[0] = bpi->v[0]; + bpi->gpart->v_full[1] = bpi->v[1]; + bpi->gpart->v_full[2] = bpi->v[2]; + + /* Update the BH metal masses */ + struct chemistry_bpart_data* bpi_chem = &bpi->chemistry_data; + const struct chemistry_bpart_data* bpj_chem = &bpj->chemistry_data; + chemistry_add_bpart_to_bpart(bpi_chem, bpj_chem); + + /* Update the energy reservoir */ + bpi->jet_mass_reservoir += bpj->jet_mass_reservoir; + + /* Add up all the BH seeds */ + bpi->cumulative_number_seeds += bpj->cumulative_number_seeds; + + /* Add up all the gas particles we swallowed */ + bpi->number_of_gas_swallows += bpj->number_of_gas_swallows; + + /* Add the subgrid angular momentum that we swallowed */ + bpi->accreted_angular_momentum[0] += bpj->accreted_angular_momentum[0]; + bpi->accreted_angular_momentum[1] += bpj->accreted_angular_momentum[1]; + bpi->accreted_angular_momentum[2] += bpj->accreted_angular_momentum[2]; + + /* We had another merger */ + bpi->number_of_mergers++; +} + +/** + * @brief Function to generate a random number from a Gaussian distribution. + * @param mu Mean of Gaussian + * @param sigma Standard deviation of Gaussian + * @param u1 Random number in (0,1) + * @param u2 Random number in (0,1) + */ +__attribute__((always_inline)) INLINE static float gaussian_random_number(float mu, float sigma, double u1, double u2) { + double mag, z0, z1; + + /* Apply the Box-Muller transform */ + mag = sigma * sqrt(-2.0 * log(u1)); + z0 = mag * cos(2.0 * M_PI * u2) + mu; + z1 = mag * sin(2.0 * M_PI * u2) + mu; + if (u1+u2 < 1.f) { + return z0; + } + return z1; +} + +/** + * @brief Compute the accretion rate of the black hole and all the quantities + * required for the feedback loop. + * + * @param bp The black hole particle. + * @param props The properties of the black hole scheme. + * @param phys_const The physical phys_const (in internal units). + * @param cosmo The cosmological model. + * @param cooling Properties of the cooling model. + * @param floor_props Properties of the entropy fllor. + * @param time Time since the start of the simulation (non-cosmo mode). + * @param with_cosmology Are we running with cosmology? + * @param dt The time-step size (in physical internal units). + * @param ti_begin The time at which the step begun (ti_current). + */ +__attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( + struct bpart* restrict bp, const struct black_holes_props* props, + const struct phys_const* phys_const, const struct cosmology* cosmo, + const struct cooling_function_data* cooling, + const struct entropy_floor_properties* floor_props, const double time, + const int with_cosmology, const double dt, const integertime_t ti_begin) { + + /* Record that the black hole has another active time step */ + bp->number_of_time_steps++; + + if (dt == 0. || bp->rho_gas == 0. || bp->h == 0.) return; + +/* Collect information about galaxy that the particle belongs to */ + const float galaxy_mstar = bp->galaxy_data.stellar_mass; + const float galaxy_mgas = bp->galaxy_data.gas_mass; + + /* A black hole should never accrete/feedback if it is not in a galaxy */ + if (galaxy_mstar <= 0.f) return; + + /* Gather some physical phys_const (all in internal units) */ + const double G = phys_const->const_newton_G; + const double c = phys_const->const_speed_light_c; + const double proton_mass = phys_const->const_proton_mass; + const double sigma_Thomson = phys_const->const_thomson_cross_section; + + /* Gather the parameters of the model */ + const double f_Edd_maximum = props->f_Edd_maximum; + +#ifdef OBSIDIAN_DEBUG_CHECKS + if (isnan(bp->subgrid_mass)) error("subgrid_mass nan"); +#endif + /* (Subgrid) mass of the BH (internal units) */ + const double BH_mass = bp->subgrid_mass; + + /* Compute the Eddington rate (internal units). + * IMPORTANT: epsilon_r = 0.1 is the SET value for the Eddington rate. + * It is assumed in all of the derivations for the state model. + */ + const double Eddington_rate = + 4. * M_PI * G * BH_mass * proton_mass / (0.1 * c * sigma_Thomson); + + const double bh_h = kernel_gamma * bp->h; + const double bh_h_inv = 1. / bh_h; + const double volume_bh_inv = + (3. / (4. * M_PI)) * bh_h_inv * bh_h_inv * bh_h_inv; + double gas_rho = 0.; + if (props->bondi_use_all_gas) { + gas_rho = bp->rho_gas; + } + else { + gas_rho = bp->hot_gas_mass * volume_bh_inv; + } + + const double gas_rho_phys = gas_rho * cosmo->a3_inv; + + /* We can now compute the Bondi accretion rate (internal units) + * D. Rennehan: In Simba, we only consider the hot gas within + * the kernel for the Bondi rate, and the cold gas using the + * torque accretion estimator. + */ + double Bondi_rate = 0.; + + /* Use all gas within the kernel or only the hot gas*/ + double gas_internal_energy = 0.; + if (props->bondi_use_all_gas) { + if (bp->internal_energy_gas > 0.) { + gas_internal_energy = bp->internal_energy_gas; + } + } + else { + if (bp->hot_gas_internal_energy > 0.) { + gas_internal_energy = bp->hot_gas_internal_energy; + } + } + + /* Check if there is hot/any gas in the kernel */ + if (gas_internal_energy > 0.) { + double gas_c = + gas_soundspeed_from_internal_energy(gas_rho, gas_internal_energy); + + if (gas_c > 0.) { + const double gas_c_phys_inv = + 1. / (cosmo->a_factor_sound_speed * gas_c); + + Bondi_rate = 4. * M_PI * G * G * BH_mass * BH_mass * gas_rho_phys * + gas_c_phys_inv * gas_c_phys_inv * gas_c_phys_inv; + + /* In the case of standard Bondi, we limit it to the Eddington rate */ + Bondi_rate = fmin(Bondi_rate, props->f_Edd_Bondi_maximum * Eddington_rate); + } + } + + /* The accretion rate estimators give Mdot,inflow + * (Mdot,BH = f_acc * Mdot,inflow) */ + const double bondi_accr_rate = props->bondi_alpha * Bondi_rate; + + /* Compute the torque-limited accretion rate */ + double torque_accr_rate = 0.; + + double f_corr_stellar = 10.; + if (galaxy_mgas > 0.) { + f_corr_stellar = + min(galaxy_mstar / galaxy_mgas, + f_corr_stellar); + } + + /* Torque accretion rate based on some fraction of gas near BH + * falling in on dynamical time. (This is the default.) + * Here the accretion rate is only based on Mgas / tdyn. + * We do not use the DM mass to compute tdyn since it probably + * doesn't contribute much near the core of the system. We also + * assume that the gas fraction is constant in the galaxy in + * order to compute Mstar within the kernel of the black hole. + * Therefore, Mdot = Mgas / tdyn = Mgas / sqrt(3pi/(32 G rho)) + * and rho = (Mgas + Mstar + Mdm) / (4pi h^3 / 3) where + * Mstar = Mgas / fgas, Mdm = 0. Therefore, + * rho = 3 * ((1 + fgas) / fgas) * Mgas / (4 * pi * h^3) + * and + * Mdot = Mgas * sqrt(32 * G * 3 * ((1 + fgas) / fgas) * Mgas)) / + * sqrt(3 * pi * 4 * pi * h^3) + * = sqrt(96 * G * ((1 + fgas) / fgas) * Mgas^3) / + * sqrt(12 * pi^2 * h^3) + * = (1 / pi) * sqrt(8 * G * ((1 + fgas) / fgas) * (Mgas / h)^3)) + */ + float tdyn_inv = FLT_MAX; + const float potential = fabs(gravity_get_comoving_potential(bp->gpart)); + /* Includes dynamical mass of the BH */ + switch (props->dynamical_time_calculation_method) { + /* Assume gas fraction is the same in the kernel and outside */ + case 0: + { + /* Compute correction to total dynamical mass around + * BH contributed by stars */ + const float m_star_gal = galaxy_mstar; + const float m_gas_cold_gal = galaxy_mgas; + const float m_gas_bh = bp->gravitational_ngb_mass; + const float m_bh = bp->mass; + + /* Compute stellar mass assuming a constant cold gas fraction in the + * entire galaxy. If m_gas_cold_bh is zero it doesn't matter since + * the BH won't acrrete in the torque mode anyway. */ + const float m_gas_cold_bh = bp->cold_gas_mass; + float m_star_bh = 0.; + if (m_gas_cold_gal > 0.) { + m_star_bh = fmin(m_star_gal / m_gas_cold_gal, 10.f) * m_gas_cold_bh; + } + + /* Have to assume baryon dominance within the kernel */ + const float rho_est = (m_star_bh + m_gas_bh + m_bh) * volume_bh_inv; + + /* Inverse physical dynamical time */ + tdyn_inv = sqrt(32. * G * rho_est * cosmo->a3_inv / (3. * M_PI)); + break; + } + + /* Assume BH potential */ + case 1: + if (potential >= 0.f) { + tdyn_inv = (sqrt(potential) / bh_h) * cosmo->a2_inv; + } + break; + + /* Assume dynamical time from the kernel mass */ + case 2: + { + /* do not have gravity_props here */ + const float hsml = kernel_gamma * bh_h; + const float volume = (4. * M_PI / 3.) * hsml * hsml * hsml; + const float rho = bp->mass / volume; + tdyn_inv = sqrt(32. * G * rho * cosmo->a3_inv / (3. * M_PI)); + break; + } + + default: + error("Unknown dynamical time calculation method %d", + props->dynamical_time_calculation_method); + break; + } + + /* Compute inverse of accretion time into BH */ + tdyn_inv /= props->dynamical_time_factor; + + /* Limit by max dynamical time, with z=0 value scaled by H0/H */ + if (props->dynamical_time_max > 0.) { + const float t_inv_min = cosmo->H / + (props->dynamical_time_max * cosmo->H0); + tdyn_inv = fmax(tdyn_inv, t_inv_min); + } + + /* Create a spread in accretion times, with minimum at + * free-fall time=0.5*tdyn */ + const float tdyn_sigma = props->tdyn_sigma; + if (tdyn_sigma > 0.f) { + const double ran1 = + random_unit_interval(bp->id, ti_begin, + random_number_BH_swallow); + const double ran2 = + random_unit_interval(bp->id, ti_begin, + random_number_BH_swallow); + const float gaussian_random = + gaussian_random_number(0.f, tdyn_sigma, ran1, ran2); + tdyn_inv /= 0.5 * (1.f + fabs(gaussian_random)); + } + + const float corot_gas_mass = + bp->cold_gas_mass - 2. * (bp->cold_gas_mass - bp->cold_disk_mass); + if (props->torque_accretion_norm > 0.f) { + switch (props->torque_accretion_method) { + case 0: + if (galaxy_mgas > 0.) { + torque_accr_rate = + props->torque_accretion_norm * bp->cold_disk_mass * tdyn_inv; + } + break; + + case 1: + if (corot_gas_mass > 0. && bp->cold_gas_mass > 0.) { + torque_accr_rate = + props->torque_accretion_norm * corot_gas_mass * tdyn_inv; + } + break; + + case 2: + if (corot_gas_mass > 0. && bp->cold_gas_mass > 0.) { + const float m_disk = bp->cold_gas_mass * f_corr_stellar; + const float f_disk = corot_gas_mass / bp->cold_gas_mass; + + const float r0 = bh_h * cosmo->a * (props->length_to_parsec * 0.01); + + const float alpha = 5.; + const float mass_to_1e9solar = props->mass_to_solar_mass * 1.0e-9; + const float mass_to_1e8solar = props->mass_to_solar_mass * 1.0e-8; + + const float f0 = + 0.31 * f_disk * f_disk * + pow(m_disk * mass_to_1e9solar, -1. / 3.); + const float f_gas = corot_gas_mass / m_disk; + const float mass_in_1e8solar = BH_mass * mass_to_1e8solar; + + torque_accr_rate = props->torque_accretion_norm * alpha * + corot_gas_mass * mass_to_1e9solar * + powf(f_disk, 5. / 2.) * + powf(mass_in_1e8solar, 1. / 6.) * + powf(r0, -3. / 2.) / (1. + f0 / f_gas); + torque_accr_rate *= + (props->time_to_yr / props->mass_to_solar_mass); + } + break; + + case 3: + if (galaxy_mgas > 0.) { + torque_accr_rate = + props->torque_accretion_norm * bp->cold_gas_mass * tdyn_inv; + } + break; + + default: + error("Unknown torque_accretion_method=%d", + props->torque_accretion_method); + break; + } + + /* Do suppression of BH growth */ + switch (props->suppress_growth) { + case 1: + { + const double r0 = bh_h * cosmo->a * props->length_to_parsec; + const double sigma_eff = f_corr_stellar * bp->ngb_mass * + props->mass_to_solar_mass / + (M_PI * r0 * r0); + + torque_accr_rate *= sigma_eff / (sigma_eff + 3000.); + break; + } + + case 2: + case 6: + case 7: + { + double m_suppress = fabs(props->bh_characteristic_suppression_mass); + if (props->bh_characteristic_suppression_mass < 0) { + m_suppress *= cosmo->a; + } + + torque_accr_rate *= + 1. - exp(-BH_mass * props->mass_to_solar_mass / m_suppress); + break; + } + + case 4: + case 5: + { + /* compute mass loading factor from SF feedback, + * should be same as used in feedback_mass_loading_factor() + */ + const float galaxy_stellar_mass = galaxy_mstar; + const float eta_norm = props->FIRE_eta_normalization; + const float eta_break = props->FIRE_eta_break; + const float eta_lower_slope = props->FIRE_eta_lower_slope; + const float eta_upper_slope = props->FIRE_eta_upper_slope; + const float eta_lower_slope_EOR = props->FIRE_eta_lower_slope_EOR; + const float eta_minmass = props->minimum_galaxy_stellar_mass; + const float eta_suppress = props->wind_eta_suppression_redshift; + + const double eta = feedback_mass_loading_factor(cosmo, galaxy_stellar_mass, eta_minmass, eta_norm, eta_break, eta_lower_slope, eta_upper_slope, eta_lower_slope_EOR, eta_suppress); + + if (bp->cold_gas_mass * tdyn_inv > 0.f) { + /* star formation efficiency, frac of gas converted + * to stars per tdyn */ + float sf_eff = props->suppression_sf_eff; + if (sf_eff < 0.f) { + /* SF efficiency within BH kernel. Cap at cloud-scale SFE from Leroy+25 */ + sf_eff = fmin(bp->gas_SFR / (tdyn_inv * bp->cold_gas_mass), fabs(sf_eff)); + } + + /* Suppresses accretion by factor accounting for mass + * lost in outflow over accretion time. ODE: + * dM/dt = -eta * sf_eff * M / tdyn */ + torque_accr_rate *= exp(-eta * sf_eff); + //message("BH_SUPPRESS: z=%g id=%lld M*=%g eta=%g eff=%g tfac=%g fsupp=%g", cosmo->z, bp->id, galaxy_stellar_mass * props->mass_to_solar_mass, eta, sf_eff, t_accrete * tdyn_inv, exp(-eta * sf_eff * t_accrete * tdyn_inv)); + } + break; + } + + default: + break; + } + } + +#ifdef OBSIDIAN_DEBUG_CHECKS + if (isnan(bondi_accr_rate)) error("bondi_accr_rate nan"); + if (isnan(torque_accr_rate)) error("torque_accr_rate nan"); +#endif + + /* Right now this is M_dot,inflow. We will multiply by + * f_accretion later to make it M_dot,acc */ + bp->accretion_rate = bondi_accr_rate + torque_accr_rate; + + /* We will use eddington_fraction_lower_boundary and + * eddington_fraction_upper_boundary to divide up the accretion rate + * in three regimes. + * + * In order to switch out of a regime (i.e. a state), it is necessary + * for the true accretion rate (compared to Eddington rate) to switch + * over the boundary. Therefore, before we switch a state we must calculate + * what the previous state predicts the true accretion rate onto the SMBH is, + * and then update the state if it crosses a boundary. + */ + + /* We need to store the full M_dot,inflow rate to calculate the + * fraction at high accretion rate */ + bp->m_dot_inflow = bp->accretion_rate; + const double f_accretion = + get_black_hole_accretion_factor(props, phys_const, cosmo, bp, Eddington_rate); + double predicted_mdot_medd = + bp->accretion_rate * f_accretion / Eddington_rate; + const float my_adaf_mass_limit = get_black_hole_adaf_mass_limit(bp, props, cosmo); + + /* Switch between states depending on the */ + switch (bp->state) { + case BH_states_adaf: + if (predicted_mdot_medd > props->eddington_fraction_upper_boundary) { + bp->state = BH_states_slim_disk; + break; + } + if (predicted_mdot_medd > props->eddington_fraction_lower_boundary) { + bp->state = BH_states_quasar; + } + + break; /* end case ADAF */ + case BH_states_quasar: + if (BH_mass > my_adaf_mass_limit && + predicted_mdot_medd < props->eddington_fraction_lower_boundary) { + bp->state = BH_states_adaf; + break; + } + + if (predicted_mdot_medd > props->eddington_fraction_upper_boundary) { + bp->state = BH_states_slim_disk; + } + + break; /* end case quasar */ + case BH_states_slim_disk: + if (BH_mass > my_adaf_mass_limit && + predicted_mdot_medd < props->eddington_fraction_lower_boundary) { + bp->state = BH_states_adaf; + break; + } + + if (predicted_mdot_medd < props->eddington_fraction_upper_boundary) { + bp->state = BH_states_quasar; + } + + break; /* end case slim disk */ + default: + error("Invalid black hole state."); + break; + } + + /* This depends on the new state */ + bp->f_accretion = + get_black_hole_accretion_factor(props, phys_const, cosmo, bp, Eddington_rate); +#ifdef OBSIDIAN_DEBUG_CHECKS + if (isnan(bp->f_accretion)) error("f_accretion nan"); +#endif + if (bp->f_accretion <= 1.e-10f) bp->f_accretion = 0.f; + + /* bp->accretion_rate is M_dot,acc in Rennehan+24 */ + bp->accretion_rate *= bp->f_accretion; + + /* Track Bondi accretion separately for diagnostics (remainder is torque) */ + bp->bondi_accretion_rate = bondi_accr_rate * bp->f_accretion; + + if (!props->bondi_use_all_gas) { + /* Now we can Eddington limit. */ + bp->accretion_rate = + min(bp->accretion_rate, f_Edd_maximum * Eddington_rate); + } + + /* All accretion is done, now we can set the eddington fraction */ + bp->eddington_fraction = bp->accretion_rate / Eddington_rate; + + /* Get the new radiative efficiency based on the new state */ + bp->radiative_efficiency = + get_black_hole_radiative_efficiency(props, bp->eddington_fraction, + bp->state); +#ifdef OBSIDIAN_DEBUG_CHECKS + if (isnan(bp->radiative_efficiency)) error("radiative_efficiency nan"); +#endif + if (bp->radiative_efficiency < 1.e-10f) bp->radiative_efficiency = 0.f; + + double mass_rate = 0.; + const double luminosity = + bp->radiative_efficiency * bp->accretion_rate * c * c; + + /* Factor in the radiative efficiency, don't subtract + * jet BZ efficiency (spin is fixed) */ + mass_rate = (1. - bp->radiative_efficiency) * bp->accretion_rate; + + /* This is used for X-ray feedback later */ + bp->radiative_luminosity = luminosity; + +#ifdef OBSIDIAN_DEBUG_CHECKS + if (isnan(dt)) error("dt nan"); +#endif + /* Integrate forward in time */ + double delta_mass = mass_rate * dt; + + /* If desired we put mass into accretion disk which feeds BH on some + * frac of tdyn + */ + if (tdyn_inv > 0.f) { + /* Add accreted mass into a reservoir representing BH accretion disk */ + bp->accretion_disk_mass += delta_mass; + + /* Compute mass that will actually go into BH */ + delta_mass = bp->accretion_disk_mass * (1. - exp(-dt * tdyn_inv)); + + /* This mass gets removed from the accretion disk */ + if (bp->accretion_disk_mass > delta_mass) { + bp->accretion_disk_mass -= delta_mass; + } + else { + delta_mass = bp->accretion_disk_mass; + bp->accretion_disk_mass = 0.; + } + + /* Recompute accretion rate based on the reservoir change */ + bp->accretion_rate = delta_mass / (dt * (1. - bp->radiative_efficiency)); + } + + bp->subgrid_mass += delta_mass; + bp->total_accreted_mass += delta_mass; + + /* Note: bp->subgrid_mass has been integrated, so avoid BH_mass variable */ + if (bp->state == BH_states_adaf && BH_mass > my_adaf_mass_limit) { + + /* ergs to dump in a kernel-weighted fashion */ + if (props->adaf_wind_mass_loading == 0.f) { + if (bp->subgrid_mass < my_adaf_mass_limit) { + bp->adaf_energy_to_dump = 0.f; + } + /*else if (bp->subgrid_mass < 1.5f * my_adaf_mass_limit) { + bp->adaf_energy_to_dump *= + 4.f * powf(bp->subgrid_mass / my_adaf_mass_limit - 1.f, 2.f); + }*/ + else { + bp->adaf_energy_to_dump = + get_black_hole_coupling(bp, props, cosmo, phys_const) * + props->adaf_disk_efficiency * bp->accretion_rate * c * c * dt; + } + } + else { + const float adaf_v2 = props->adaf_wind_speed * props->adaf_wind_speed; + const float mass_this_step = + props->adaf_wind_mass_loading * bp->accretion_rate * dt; + bp->adaf_energy_to_dump += 0.5f * mass_this_step * adaf_v2; + } + } + + if (bp->state == BH_states_adaf || + (props->slim_disk_jet_active && bp->state == BH_states_slim_disk)) { + + float jet_velocity = black_hole_compute_jet_velocity(bp, cosmo, props); + + /* If there is a variable jet velocity we must recalculate the mass loading */ + if (jet_velocity != props->jet_velocity) { + const double c_over_v = phys_const->const_speed_light_c / jet_velocity; + + if (props->jet_loading_type == BH_jet_momentum_loaded) { + bp->jet_mass_loading = props->jet_efficiency * c_over_v; + } + else if (props->jet_loading_type == BH_jet_mixed_loaded) { + const double energy_loading = + 2. * props->jet_efficiency * pow(c_over_v, 2.); + const double momentum_loading = props->jet_efficiency * c_over_v; + + /* Divide the contribution between energy and momentum loading */ + const double energy_term = props->jet_frac_energy * energy_loading; + const double momentum_term = + (1. - props->jet_frac_energy) * momentum_loading; + + bp->jet_mass_loading = energy_term + momentum_term; + } + else { + bp->jet_mass_loading = 2. * props->jet_efficiency * pow(c_over_v, 2.); + } + + /* Psi_jet*M_dot,acc*dt is the total mass expected in the jet this step */ + bp->jet_mass_reservoir += bp->jet_mass_loading * bp->accretion_rate * dt; + } + else { + bp->jet_mass_reservoir += props->jet_mass_loading * bp->accretion_rate * dt; + } + } + + if (bp->subgrid_mass < bp->mass) { + /* In this case, the BH is still accreting from its (assumed) subgrid gas + * mass reservoir left over when it was formed. There is some loss in this + * due to radiative losses, so we must decrease the particle mass + * in proportion to its current accretion rate. We do not account for this + * in the swallowing approach, however. */ + bp->mass -= bp->radiative_efficiency * bp->accretion_rate * dt; + + if (bp->mass < 0) { + error("Black hole %lld reached negative mass (%g). Trouble ahead...", + bp->id, bp->mass); + } + + /* Make sure not to destroy low mass galaxies */ + if (bp->subgrid_mass > props->minimum_black_hole_mass_unresolved && + bp->state != BH_states_adaf) { + /* Make sure if many mergers have driven up the dynamical mass at low + * subgrid mass, that we still kick out particles! */ + const float psi = (1.f - bp->f_accretion) / bp->f_accretion; + bp->unresolved_mass_reservoir += psi * bp->accretion_rate * dt; + } + } + + /* Increase the subgrid angular momentum according to what we accreted + * Note that this is already in physical units, a factors from velocity and + * radius cancel each other. Also, the circular velocity contains an extra + * smoothing length factor that we undo here. */ + const double m_times_r = (mass_rate * dt) * bp->h; + /* L = m * r * v */ + bp->accreted_angular_momentum[0] += m_times_r * bp->circular_velocity_gas[0]; + bp->accreted_angular_momentum[1] += m_times_r * bp->circular_velocity_gas[1]; + bp->accreted_angular_momentum[2] += m_times_r * bp->circular_velocity_gas[2]; + + /* Keep v_kick physical, there are a lot of comparisons */ + bp->v_kick = get_black_hole_wind_speed(props, phys_const, bp); + + /* This is always true in the ADAF mode; only heating happens */ + if (bp->state == BH_states_adaf) bp->v_kick = 0.f; + +#ifdef OBSIDIAN_DEBUG_CHECKS + float galaxy_sfr = fof_props->group_star_formation_rate[group_id]; + tdyn_inv = (tdyn_inv > 0.f) ? tdyn_inv : FLT_MIN; + message("BH_ACC: z=%g bid=%lld ms=%g dms=%g sfr=%g mbh=%g dmbh=%g state=%d " + "torque=%g bondi=%g fEdd=%g facc=%g fsupp=%g mcold=%g mhot=%g mdisk=%g" + " tin=%g vkick=%g dmass=%g radeff=%g mres=%g tdyn=%g", + cosmo->z, + bp->id, + galaxy_mstar * props->mass_to_solar_mass, + galaxy_sfr * dt * props->mass_to_solar_mass, + galaxy_sfr * props->mass_to_solar_mass / props->time_to_yr, + bp->subgrid_mass * props->mass_to_solar_mass, + delta_mass * props->mass_to_solar_mass, + bp->state, + torque_accr_rate * props->mass_to_solar_mass / props->time_to_yr, + bondi_accr_rate * props->mass_to_solar_mass / props->time_to_yr, + bp->eddington_fraction, + bp->f_accretion, + 1. - exp(-bp->subgrid_mass * props->mass_to_solar_mass / + fabs(props->bh_characteristic_suppression_mass) * cosmo->a), + bp->cold_gas_mass * props->mass_to_solar_mass, + bp->hot_gas_mass * props->mass_to_solar_mass, + corot_gas_mass * props->mass_to_solar_mass, + props->time_to_Myr / bp->tdyn_inv, + bp->v_kick / props->kms_to_internal, + delta_mass, + bp->radiative_efficiency, + bp->accretion_disk_mass, + (1.f / tdyn_inv) * props->time_to_Myr); + + message("BH_STATES: id=%lld, new_state=%d, predicted_mdot_medd=%g, " + "eps_r=%g, f_Edd=%g, f_acc=%g, " + "luminosity=%g, accr_rate=%g Msun/yr, coupling=%g, v_kick=%g km/s, " + "jet_mass_reservoir=%g Msun unresolved_reservoir=%g Msun " + "jet_mass_loading=%g", + bp->id, + bp->state, + predicted_mdot_medd, + bp->radiative_efficiency, + bp->eddington_fraction, + bp->f_accretion, + bp->radiative_luminosity * props->conv_factor_energy_rate_to_cgs, + bp->accretion_rate * props->mass_to_solar_mass / props->time_to_yr, + get_black_hole_coupling(bp, props, cosmo, phys_const), + bp->v_kick / props->kms_to_internal, + bp->jet_mass_reservoir * props->mass_to_solar_mass, + bp->unresolved_mass_reservoir * props->mass_to_solar_mass, + bp->jet_mass_loading); +#endif + +#define OBSIDIAN_BH_DETAILS +#ifdef OBSIDIAN_BH_DETAILS + printf("BH_DETAILS " + "z=%2.12f bid=%lld " + " Mdyn=%g MBH=%g Mres=%g BHAR=%g Bondi=%g torque=%g dt=%g " + " nH=%g T=%g SFR=%g mgas=%g " + " mhot=%g m*=%g mgbulge=%g msbulge=%g N/A=%g " + " x=%2.10f y=%2.10f z=%2.10f " + " vx=%2.7f vy=%2.7f vz=%2.7f " + " Lx=%g Ly=%g Lz=%g Lx*=%g Ly*=%g Lz*=%g" + " Lrad=%g state=%d facc=%g eff=%g" + " fedd=%g madaf=%g mngb=%g\n", + cosmo->z, + bp->id, + bp->mass * props->mass_to_solar_mass, + bp->subgrid_mass * props->mass_to_solar_mass, + bp->jet_mass_reservoir * props->mass_to_solar_mass, + bp->accretion_rate * props->mass_to_solar_mass / props->time_to_yr, + Bondi_rate * props->mass_to_solar_mass / props->time_to_yr, + torque_accr_rate * props->mass_to_solar_mass / props->time_to_yr, + dt * props->time_to_Myr, + (bp->rho_gas * cosmo->a3_inv) * props->rho_to_n_cgs, + bp->hot_gas_internal_energy * cosmo->a_factor_internal_energy / + (props->T_K_to_int * props->temp_to_u_factor), + bp->gas_SFR * props->mass_to_solar_mass / props->time_to_yr, + bp->ngb_mass * props->mass_to_solar_mass, + bp->hot_gas_mass * props->mass_to_solar_mass, + bp->stellar_mass * props->mass_to_solar_mass, + 0.f /* Mgas,bulge */, + bp->stellar_bulge_mass * props->mass_to_solar_mass, + 0.f, + bp->x[0] * cosmo->a * props->length_to_parsec / 1.0e3f, + bp->x[1] * cosmo->a * props->length_to_parsec / 1.0e3f, + bp->x[2] * cosmo->a * props->length_to_parsec / 1.0e3f, + bp->v[0] * cosmo->a_inv / props->kms_to_internal, + bp->v[1] * cosmo->a_inv / props->kms_to_internal, + bp->v[2] * cosmo->a_inv / props->kms_to_internal, + bp->angular_momentum_gas[0], + bp->angular_momentum_gas[1], + bp->angular_momentum_gas[2], + 0.f, /* specific angular momentum of the stars */ + 0.f, /* specific angular momentum of the stars */ + 0.f, /* specific angular momentum of the stars */ + bp->radiative_luminosity * props->conv_factor_energy_rate_to_cgs, + bp->state, + bp->f_accretion, + bp->radiative_efficiency, + bp->eddington_fraction, + my_adaf_mass_limit * props->mass_to_solar_mass, + bp->gravitational_ngb_mass * props->mass_to_solar_mass); +#endif +} + +/** + * @brief Computes the (maximal) repositioning speed for a black hole. + * + * Calculated as upsilon * (m_BH / m_ref) ^ beta_m * (n_H_BH / n_ref) ^ beta_n + * where m_BH = BH subgrid mass, n_H_BH = physical gas density around BH + * and upsilon, m_ref, beta_m, n_ref, and beta_n are parameters. + * + * @param bp The #bpart. + * @param props The properties of the black hole model. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_repositioning_speed(const struct bpart* restrict bp, + const struct black_holes_props* props, + const struct cosmology* cosmo) { + + const double n_gas_phys = bp->rho_gas * cosmo->a3_inv * props->rho_to_n_cgs; + const double v_repos = + props->reposition_coefficient_upsilon * + pow(bp->subgrid_mass / props->reposition_reference_mass, + props->reposition_exponent_mass) * + pow(n_gas_phys / props->reposition_reference_n_H, + props->reposition_exponent_n_H); + + /* Make sure the repositioning is not back-firing... */ + if (v_repos < 0) + error( + "BH %lld wants to reposition at negative speed (%g U_V). Do you " + "think you are being funny? No-one is laughing.", + bp->id, v_repos); + + return v_repos; +} + +/** + * @brief Finish the calculation of the new BH position. + * + * Here, we check that the BH should indeed be moved in the next drift. + * + * @param bp The black hole particle. + * @param props The properties of the black hole scheme. + * @param phys_const The physical phys_const (in internal units). + * @param cosmo The cosmological model. + * @param dt The black hole particle's time step. + * @param ti_begin The time at the start of the temp + */ +__attribute__((always_inline)) INLINE static void black_holes_end_reposition( + struct bpart* restrict bp, const struct black_holes_props* props, + const struct phys_const* phys_const, const struct cosmology* cosmo, + const double dt, const integertime_t ti_begin) { + + /* First check: did we find any eligible neighbour particle to jump to? */ + if (bp->reposition.min_potential != FLT_MAX) { + + /* Record that we have a (possible) repositioning situation */ + bp->number_of_reposition_attempts++; + + /* Is the potential lower (i.e. the BH is at the bottom already) + * OR is the BH massive enough that we don't reposition? */ + const float potential = gravity_get_comoving_potential(bp->gpart); + if (potential < bp->reposition.min_potential || + bp->subgrid_mass > props->max_reposition_mass) { + + /* No need to reposition */ + bp->reposition.min_potential = FLT_MAX; + bp->reposition.delta_x[0] = -FLT_MAX; + bp->reposition.delta_x[1] = -FLT_MAX; + bp->reposition.delta_x[2] = -FLT_MAX; + + } else if (props->set_reposition_speed) { + + /* If we are re-positioning, move the BH a fraction of delta_x, so + * that we have a well-defined re-positioning velocity (repos_vel + * cannot be negative). */ + double repos_vel = black_holes_get_repositioning_speed(bp, props, cosmo); + + /* Convert target reposition velocity to a fractional reposition + * along reposition.delta_x */ + const double dx = bp->reposition.delta_x[0]; + const double dy = bp->reposition.delta_x[1]; + const double dz = bp->reposition.delta_x[2]; + const double d = sqrt(dx * dx + dy * dy + dz * dz); + + /* Exclude the pathological case of repositioning by zero distance */ + if (d > 0) { + double repos_frac = repos_vel * dt / d; + + /* We should never get negative repositioning fractions... */ + if (repos_frac < 0) + error("Wanting to reposition by negative fraction (%g)?", repos_frac); + + /* ... but fractions > 1 can occur if the target velocity is high. + * We do not want this, because it could lead to overshooting the + * actual potential minimum. */ + if (repos_frac > 1) { + repos_frac = 1.; + repos_vel = repos_frac * d / dt; + } + + bp->last_repos_vel = (float)repos_vel; + bp->reposition.delta_x[0] *= repos_frac; + bp->reposition.delta_x[1] *= repos_frac; + bp->reposition.delta_x[2] *= repos_frac; + } + + /* ends section for fractional repositioning */ + } else { + + /* We _should_ reposition, but not fractionally. Here, we will + * reposition exactly on top of another gas particle - which + * could cause issues, so we add on a small fractional offset + * of magnitude 0.001 h in the reposition delta. */ + + /* Generate three random numbers in the interval [-0.5, 0.5[; id, + * id**2, and id**3 are required to give unique random numbers (as + * random_unit_interval is completely reproducible). */ + const float offset_dx = + random_unit_interval(bp->id, ti_begin, random_number_BH_reposition) - + 0.5f; + const float offset_dy = + random_unit_interval(bp->id * bp->id, ti_begin, + random_number_BH_reposition) - + 0.5f; + const float offset_dz = + random_unit_interval(bp->id * bp->id * bp->id, ti_begin, + random_number_BH_reposition) - + 0.5f; + + const float length_inv = + 1.0f / sqrtf(offset_dx * offset_dx + offset_dy * offset_dy + + offset_dz * offset_dz); + + const float norm = 0.001f * bp->h * length_inv; + + bp->reposition.delta_x[0] += offset_dx * norm; + bp->reposition.delta_x[1] += offset_dy * norm; + bp->reposition.delta_x[2] += offset_dz * norm; + } + } /* ends section if we found eligible repositioning target(s) */ +} + +/** + * @brief Reset acceleration fields of a particle + * + * This is the equivalent of hydro_reset_acceleration. + * We do not compute the acceleration on black hole, therefore no need to use + * it. + * + * @param bp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void black_holes_reset_feedback( + struct bpart* restrict bp) { + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + bp->ids_ngbs_force[i] = -1; + bp->num_ngb_force = 0; +#endif +} + +/** + * @brief Store the gravitational potential of a black hole by copying it from + * its #gpart friend. + * + * @param bp The black hole particle. + * @param gp The black hole's #gpart. + */ +__attribute__((always_inline)) INLINE static void +black_holes_store_potential_in_bpart(struct bpart* bp, const struct gpart* gp) { + +#ifdef SWIFT_DEBUG_CHECKS + if (bp->gpart != gp) error("Copying potential to the wrong black hole!"); +#endif + + bp->reposition.potential = gp->potential; +} + +/** + * @brief Store the gravitational potential of a particle by copying it from + * its #gpart friend. + * + * @param p_data The black hole data of a gas particle. + * @param gp The black hole's #gpart. + */ +__attribute__((always_inline)) INLINE static void +black_holes_store_potential_in_part(struct black_holes_part_data* p_data, + const struct gpart* gp) { + p_data->potential = gp->potential; +} + +/** + * @brief Initialise a BH particle that has just been seeded. + * + * @param bp The #bpart to initialise. + * @param props The properties of the black hole scheme. + * @param phys_const The physical phys_const in internal units. + * @param cosmo The current cosmological model. + * @param p The #part that became a black hole. + * @param xp The #xpart that became a black hole. + */ +INLINE static void black_holes_create_from_gas( + struct bpart* bp, const struct black_holes_props* props, + const struct phys_const* phys_const, const struct cosmology* cosmo, + const struct part* p, const struct xpart* xp, + const integertime_t ti_current) { + + /* All the non-basic properties of the black hole have been zeroed + * in the FOF code. We update them here. + * (i.e. position, velocity, mass, time-step have been set) */ + + /* Birth time and density */ + bp->formation_scale_factor = cosmo->a; + bp->formation_gas_density = hydro_get_physical_density(p, cosmo); + + /* Copy FoF galaxy data from spawning particle */ + bp->galaxy_data.stellar_mass = p->galaxy_data.stellar_mass; + bp->galaxy_data.gas_mass = p->galaxy_data.gas_mass; + bp->galaxy_data.specific_sfr = p->galaxy_data.specific_sfr; + + /* Initial seed mass */ + bp->subgrid_mass = props->subgrid_seed_mass; + + /* We haven't accreted anything yet */ + bp->total_accreted_mass = 0.f; + bp->cumulative_number_seeds = 1; + bp->number_of_mergers = 0; + bp->number_of_gas_swallows = 0; + bp->number_of_direct_gas_swallows = 0; + bp->number_of_time_steps = 0; + + /* We haven't repositioned yet, nor attempted it */ + bp->number_of_repositions = 0; + bp->number_of_reposition_attempts = 0; + bp->last_repos_vel = 0.f; + + /* Copy over the splitting struct */ + bp->split_data = xp->split_data; + + /* Initial metal masses */ + const float gas_mass = hydro_get_mass(p); + struct chemistry_bpart_data* bp_chem = &bp->chemistry_data; + const struct chemistry_part_data* p_chem = &p->chemistry_data; + chemistry_bpart_from_part(bp_chem, p_chem, gas_mass); + + /* No swallowed angular momentum */ + bp->swallowed_angular_momentum[0] = 0.f; + bp->swallowed_angular_momentum[1] = 0.f; + bp->swallowed_angular_momentum[2] = 0.f; + + /* Last time of mergers */ + bp->last_minor_merger_time = -1.; + bp->last_major_merger_time = -1.; + + /* First initialisation */ + black_holes_init_bpart(bp); + + bp->state = BH_states_slim_disk; + + black_holes_mark_bpart_as_not_swallowed(&bp->merger_data); + +} + +#endif /* SWIFT_OBSIDIAN_BLACK_HOLES_H */ diff --git a/src/black_holes/Obsidian/black_holes.h.rad b/src/black_holes/Obsidian/black_holes.h.rad new file mode 100644 index 0000000000..684b896167 --- /dev/null +++ b/src/black_holes/Obsidian/black_holes.h.rad @@ -0,0 +1,1730 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_OBSIDIAN_BLACK_HOLES_H +#define SWIFT_OBSIDIAN_BLACK_HOLES_H + +/* Local includes */ +#include "black_holes_properties.h" +#include "black_holes_struct.h" +#include "cooling.h" +#include "cosmology.h" +#include "dimension.h" +#include "exp10.h" +#include "gravity.h" +#include "kernel_hydro.h" +#include "minmax.h" +#include "physical_constants.h" +#include "random.h" + +/* Standard includes */ +#include +#include +#include + + +/** + * @brief How much of the feedback actually couples to the medium? + * + * @param props The properties of the black hole scheme. + * @param BH_state The current state of the black hole. + */ +__attribute__((always_inline)) INLINE static double get_black_hole_coupling( + const struct black_holes_props* props, const struct cosmology* cosmo, + const int BH_state) { + switch (BH_state) { + case BH_states_adaf: + { + const double scaling = + min(pow(1. + cosmo->z, props->adaf_z_scaling), 1.); + return props->adaf_coupling * scaling; + break; + } + case BH_states_quasar: + return props->quasar_coupling; + break; + case BH_states_slim_disk: + return props->slim_disk_coupling; + break; + default: + error("Invalid black hole state."); + return 0.; + break; + } +} + +/** + * @brief Computes the radiative efficiency in the slim disk mode. + * + * @param props The properties of the black hole scheme. + * @param f_Edd M_dot,BH / M_dot,Edd + */ +__attribute__((always_inline)) INLINE static +double get_black_hole_slim_disk_efficiency( + const struct black_holes_props* props, + const double f_Edd) { + if (f_Edd <= 0.) return 0.; + const double R = 1. / f_Edd; + /* Efficiency from Lupi et al. (2014), + * super eddington accretion and feedback */ + return (R / 16.) * props->A_lupi * + (0.985 / (R + (5. / 8.) * props->B_lupi) + 0.015 / + (R + (5. / 8.) * props->C_lupi)); +} + +/** + * @brief Computes the radiative efficiency in the ADAF mode. + * + * @param props The properties of the black hole scheme. + * @param f_Edd M_dot,BH / M_dot,Edd + */ +__attribute__((always_inline)) INLINE static +double get_black_hole_adaf_efficiency( + const struct black_holes_props* props, const double f_Edd) { + return props->epsilon_r * f_Edd; /* scales with M_dot,BH */ +} + +/** + * @brief Chooses and calls the proper radiative efficiency function for the state. + * + * @param props The properties of the black hole scheme. + * @param f_Edd The accretion rate over the Eddington rate. + * @param BH_state The current state of the BH. + */ +__attribute__((always_inline)) INLINE static +double get_black_hole_radiative_efficiency( + const struct black_holes_props* props, + const double f_Edd, const int BH_state) { + switch(BH_state) { + case BH_states_adaf: + return get_black_hole_adaf_efficiency(props, f_Edd); + case BH_states_quasar: + return props->epsilon_r; + case BH_states_slim_disk: + return get_black_hole_slim_disk_efficiency(props, f_Edd); + default: + error("Invalid black hole state."); + break; + } + + return 0.; +} + +/** + * @brief Computes the fraction of M_dot,inflow that should go into the BH. + * + * @param props The properties of the black hole scheme. + * @param constants The physical constants (in internal units). + * @param m_dot_inflow_m_dot_edd M_dot,inflow scaled to M_dot,Edd for the BH. + */ +__attribute__((always_inline)) INLINE static double +get_black_hole_upper_mdot_medd( + const struct black_holes_props* props, + const struct phys_const* constants, + const double m_dot_inflow_m_dot_edd) { + + if (m_dot_inflow_m_dot_edd <= 0.) return 0.; + + double x1, x2, x3; + double a3, a2, a1, a0; + const double phi = props->slim_disk_phi; + + int num_roots; + + a3 = ((5. * 5.) / (8. * 8.)) * props->B_lupi * props->C_lupi; + a2 = (5. / 8.) * ((props->B_lupi + props->C_lupi) + (phi / 16.) * + props->A_lupi * (0.015 * props->B_lupi + 0.985 * props->C_lupi) - + (5. / 8.) * props->B_lupi * props->C_lupi * m_dot_inflow_m_dot_edd); + a1 = 1. + (phi / 16.) * props->A_lupi - (5. / 8.) * + (props->B_lupi + props->C_lupi) * m_dot_inflow_m_dot_edd; + a0 = -m_dot_inflow_m_dot_edd; + + a2 /= a3; + a1 /= a3; + a0 /= a3; + + num_roots = gsl_poly_solve_cubic(a2, a1, a0, &x1, &x2, &x3); + if (num_roots == 1) { + if (x1 >= 0.) { + return x1; + } + else { + warning("num_roots=1 m_dot_inflow_m_dot_edd=%g phi=%g a3=%g a2=%g " + "a1=%g a0=%g", + m_dot_inflow_m_dot_edd, phi, a3, a2, a1, a0); + return 0.; + } + } + if (x3 >= 0.) { + return x3; + } + else { + warning("num_roots=0 m_dot_inflow_m_dot_edd=%g phi=%g a3=%g a2=%g a1=%g a0=%g", + m_dot_inflow_m_dot_edd, phi, a3, a2, a1, a0); + return 0.; + } + + return 0.; +} + +/** + * @brief Computes the fraction of M_dot,inflow that should go into the BH. + * + * @param props The properties of the black hole scheme. + * @param constants The physical constants (in internal units). + * @param m_dot_inflow M_dot,inflow in internal units. + * @param BH_mass The subgrid mass of the BH in internal units. + * @param BH_state The current state of the BH. + * @param Eddington_rate M_dot,Edd in internal units. + */ +__attribute__((always_inline)) INLINE static +double get_black_hole_accretion_factor( + const struct black_holes_props* props, + const struct phys_const* constants, + const double m_dot_inflow, const double BH_mass, + const int BH_state, + const double Eddington_rate) { + + if (m_dot_inflow <= 0. || BH_mass <= 0.) return 0.; + + + switch (BH_state) { + case BH_states_adaf: + return props->adaf_f_accretion; + break; + case BH_states_quasar: + return props->quasar_f_accretion; + break; + case BH_states_slim_disk: + { + /* This is the FRACTION of the total so divide by M_dot,inflow */ + const double f_edd = m_dot_inflow / Eddington_rate; + double mdot_medd = + get_black_hole_upper_mdot_medd(props, constants, f_edd); + return mdot_medd * Eddington_rate / m_dot_inflow; + break; + } + default: + error("Invalid black hole state."); + return 0.; + break; + } +} + +/** + * @brief Computes the time-step of a given black hole particle. + * + * @param bp Pointer to the s-particle data. + * @param props The properties of the black hole scheme. + * @param constants The physical constants (in internal units). + */ +__attribute__((always_inline)) INLINE static float black_holes_compute_timestep( + const struct bpart* const bp, const struct black_holes_props* props, + const struct phys_const* constants, const struct cosmology* cosmo) { + + /* Allow for finer timestepping if necessary! */ + float dt_accr = FLT_MAX; + float dt_overall = FLT_MAX; + float dt_kick = FLT_MAX; + const float min_subgrid_mass = props->minimum_black_hole_mass_unresolved; + + /* Only limit when in the resolved feedback regime */ + if (bp->accretion_rate > 0.f && bp->subgrid_mass > min_subgrid_mass) { + dt_accr = props->dt_accretion_factor * bp->mass / bp->accretion_rate; + + if (bp->state == BH_states_adaf) { + dt_kick = bp->ngb_mass / (props->jet_mass_loading * bp->accretion_rate); + } + else { + if (bp->f_accretion > 0.f) { + /* Make sure that the wind mass does not exceed the kernel gas mass */ + const float psi = (1.f - bp->f_accretion) / bp->f_accretion; + dt_kick = bp->ngb_mass / (psi * bp->accretion_rate); + } + } + + dt_overall = min(dt_kick, dt_accr); + } + + if (dt_overall < props->time_step_min) { + message( + "Warning! BH_TIMESTEP_LOW: id=%lld (%g Myr) is below time_step_min (%g " + "Myr).", + bp->id, dt_overall * props->time_to_Myr, + props->time_step_min * props->time_to_Myr); + } + + return max(dt_overall, props->time_step_min); +} + +/** + * @brief Initialises the b-particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param bp The particle to act upon + * @param props The properties of the black holes model. + */ +__attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( + struct bpart* bp, const struct black_holes_props* props) { + + bp->time_bin = 0; + if (props->use_subgrid_mass_from_ics == 0) { + bp->subgrid_mass = bp->mass; + } else if (props->with_subgrid_mass_check && bp->subgrid_mass <= 0) { + error( + "Black hole %lld has a subgrid mass of %f (internal units).\n" + "If this is because the ICs do not contain a 'SubgridMass' data " + "set, you should set the parameter " + "'ObsidianAGN:use_subgrid_mass_from_ics' to 0 to initialize the " + "black hole subgrid masses to the corresponding dynamical masses.\n" + "If the subgrid mass is intentionally set to this value, you can " + "disable this error by setting 'ObsidianAGN:with_subgrid_mass_check' " + "to 0.", + bp->id, bp->subgrid_mass); + } + bp->total_accreted_mass = 0.f; + bp->accretion_disk_mass = 0.f; + bp->gas_SFR = 0.f; + bp->accretion_rate = 0.f; + bp->formation_time = -1.f; + bp->cumulative_number_seeds = 1; + bp->number_of_mergers = 0; + bp->number_of_gas_swallows = 0; + bp->number_of_direct_gas_swallows = 0; + bp->number_of_repositions = 0; + bp->number_of_reposition_attempts = 0; + bp->number_of_time_steps = 0; + bp->last_minor_merger_time = -1.; + bp->last_major_merger_time = -1.; + bp->swallowed_angular_momentum[0] = 0.f; + bp->swallowed_angular_momentum[1] = 0.f; + bp->swallowed_angular_momentum[2] = 0.f; + bp->accreted_angular_momentum[0] = 0.f; + bp->accreted_angular_momentum[1] = 0.f; + bp->accreted_angular_momentum[2] = 0.f; + bp->last_repos_vel = 0.f; + bp->radiative_luminosity = 0.f; + bp->delta_energy_this_timestep = 0.f; + bp->state = BH_states_slim_disk; + bp->radiative_efficiency = 0.f; + bp->f_accretion = 0.f; + bp->m_dot_inflow = 0.f; + bp->cold_disk_mass = 0.f; + bp->jet_mass_reservoir = 0.f; + bp->jet_mass_kicked_this_step = 0.f; + bp->adaf_energy_to_dump = 0.f; + bp->adaf_energy_used_this_step = 0.f; + +#ifdef WITH_FOF_GALAXIES + bp->group_data.mass = 0.f; + bp->group_data.stellar_mass = 0.f; + bp->group_data.ssfr = 0.f; +#endif +} + +/** + * @brief Prepares a b-particle for its interactions + * + * @param bp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void black_holes_init_bpart( + struct bpart* bp) { + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + bp->ids_ngbs_density[i] = -1; + bp->num_ngb_density = 0; +#endif + + bp->density.wcount = 0.f; + bp->density.wcount_dh = 0.f; + bp->rho_gas = 0.f; + bp->sound_speed_gas = 0.f; + bp->internal_energy_gas = 0.f; + bp->hot_gas_mass = 0.f; + bp->cold_gas_mass = 0.f; + bp->hot_gas_internal_energy = 0.f; + bp->sound_speed_subgrid_gas = -1.f; + bp->velocity_gas[0] = 0.f; + bp->velocity_gas[1] = 0.f; + bp->velocity_gas[2] = 0.f; + bp->circular_velocity_gas[0] = 0.f; + bp->circular_velocity_gas[1] = 0.f; + bp->circular_velocity_gas[2] = 0.f; + bp->angular_momentum_gas[0] = 0.f; + bp->angular_momentum_gas[1] = 0.f; + bp->angular_momentum_gas[2] = 0.f; + bp->stellar_mass = 0.f; + bp->stellar_bulge_mass = 0.f; + bp->radiative_luminosity = 0.f; + bp->ngb_mass = 0.f; + bp->gravitational_ngb_mass = 0.f; + bp->num_ngbs = 0; + bp->num_gravitational_ngbs = 0; + bp->reposition.delta_x[0] = -FLT_MAX; + bp->reposition.delta_x[1] = -FLT_MAX; + bp->reposition.delta_x[2] = -FLT_MAX; + bp->reposition.min_potential = FLT_MAX; + bp->reposition.potential = FLT_MAX; + bp->accretion_rate = 0.f; /* Optionally accumulated ngb-by-ngb */ + bp->cold_disk_mass = 0.f; + bp->mass_at_start_of_step = bp->mass; /* bp->mass may grow in nibbling mode */ + bp->m_dot_inflow = 0.f; /* reset accretion rate */ + bp->kernel_wt_sum = 0.f; + /* update the reservoir */ + bp->jet_mass_reservoir -= bp->jet_mass_kicked_this_step; + bp->jet_mass_kicked_this_step = 0.f; + if (bp->jet_mass_reservoir < 0.f) { + bp->jet_mass_reservoir = 0.f; /* reset reservoir if used up */ + } + /* update the unresolved reservoir */ + bp->unresolved_mass_reservoir -= bp->unresolved_mass_kicked_this_step; + bp->unresolved_mass_kicked_this_step = 0.f; + if (bp->unresolved_mass_reservoir < 0.f) { + bp->unresolved_mass_reservoir = 0.f; + } + /* update the adaf energy reservoir */ + if (bp->adaf_wt_sum > 0.f) { + const float adaf_energy_used = + bp->adaf_energy_used_this_step / bp->adaf_wt_sum; + bp->adaf_energy_to_dump -= adaf_energy_used; + bp->adaf_wt_sum = 0.f; + bp->adaf_energy_used_this_step = 0.f; + if (bp->adaf_energy_to_dump < 0.f) { + bp->adaf_energy_to_dump = 0.f; + } + } + else { + bp->adaf_energy_used_this_step = 0.f; + } +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * The fields do not get predicted but we move the BH to its new position + * if a new one was calculated in the repositioning loop. + * + * @param bp The particle + * @param dt_drift The drift time-step for positions. + */ +__attribute__((always_inline)) INLINE static void black_holes_predict_extra( + struct bpart* restrict bp, float dt_drift) { + + /* Are we doing some repositioning? */ + if (bp->reposition.min_potential != FLT_MAX) { + +#ifdef SWIFT_DEBUG_CHECKS + if (bp->reposition.delta_x[0] == -FLT_MAX || + bp->reposition.delta_x[1] == -FLT_MAX || + bp->reposition.delta_x[2] == -FLT_MAX) { + error("Something went wrong with the new repositioning position"); + } + + const double dx = bp->reposition.delta_x[0]; + const double dy = bp->reposition.delta_x[1]; + const double dz = bp->reposition.delta_x[2]; + const double d = sqrt(dx * dx + dy * dy + dz * dz); + if (d > 1.01 * kernel_gamma * bp->h) + error("Repositioning BH beyond the kernel support!"); +#endif + + /* Move the black hole */ + bp->x[0] += bp->reposition.delta_x[0]; + bp->x[1] += bp->reposition.delta_x[1]; + bp->x[2] += bp->reposition.delta_x[2]; + + /* Move its gravity properties as well */ + bp->gpart->x[0] += bp->reposition.delta_x[0]; + bp->gpart->x[1] += bp->reposition.delta_x[1]; + bp->gpart->x[2] += bp->reposition.delta_x[2]; + + /* Store the delta position */ + bp->x_diff[0] -= bp->reposition.delta_x[0]; + bp->x_diff[1] -= bp->reposition.delta_x[1]; + bp->x_diff[2] -= bp->reposition.delta_x[2]; + + /* Reset the reposition variables */ + bp->reposition.delta_x[0] = -FLT_MAX; + bp->reposition.delta_x[1] = -FLT_MAX; + bp->reposition.delta_x[2] = -FLT_MAX; + bp->reposition.min_potential = FLT_MAX; + + /* Count the jump */ + bp->number_of_repositions++; + } +} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param bp The particle. + */ +__attribute__((always_inline)) INLINE static void +black_holes_reset_predicted_values(struct bpart* bp) {} + +/** + * @brief Kick the additional variables + * + * @param bp The particle to act upon + * @param dt The time-step for this kick + */ +__attribute__((always_inline)) INLINE static void black_holes_kick_extra( + struct bpart* bp, float dt) {} + +/** + * @brief Finishes the calculation of density on black holes + * + * @param bp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void black_holes_end_density( + struct bpart* bp, const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = bp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + /* --- Finish the calculation by inserting the missing h factors --- */ + bp->density.wcount *= h_inv_dim; + bp->density.wcount_dh *= h_inv_dim_plus_one; + bp->rho_gas *= h_inv_dim; + float rho_inv = 1.f; + if (bp->rho_gas > 0.f) rho_inv = 1.f / bp->rho_gas; + + /* For the following, we also have to undo the mass smoothing + * (N.B.: bp->velocity_gas is in BH frame, in internal units). */ + bp->sound_speed_gas *= h_inv_dim * rho_inv; + bp->internal_energy_gas *= h_inv_dim * rho_inv; + + /* Non-weighted (no decoupled winds) properties below. + * All mass-weighted quantities are for the hot & cold gas */ + float m_hot_inv = 1.f; + if (bp->hot_gas_mass > 0.f) m_hot_inv /= bp->hot_gas_mass; + /* Or the total mass */ + float m_tot_inv = 1.f; + if (bp->ngb_mass > 0.f) m_tot_inv /= bp->ngb_mass; + + bp->hot_gas_internal_energy *= m_hot_inv; + bp->velocity_gas[0] *= m_tot_inv; + bp->velocity_gas[1] *= m_tot_inv; + bp->velocity_gas[2] *= m_tot_inv; + bp->circular_velocity_gas[0] *= m_tot_inv; + bp->circular_velocity_gas[1] *= m_tot_inv; + bp->circular_velocity_gas[2] *= m_tot_inv; + + /* Calculate circular velocity at the smoothing radius from specific + * angular momentum (extra h_inv). It is now a VELOCITY. + */ + bp->circular_velocity_gas[0] *= h_inv; + bp->circular_velocity_gas[1] *= h_inv; + bp->circular_velocity_gas[2] *= h_inv; + +} + +/** + * @brief Sets all particle fields to sensible values when the #spart has 0 + * ngbs. + * + * @param bp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void black_holes_bpart_has_no_neighbours(struct bpart* bp, + const struct cosmology* cosmo) { + + //warning( + // "BH particle with ID %lld treated as having no neighbours (h: %g, " + // "wcount: %g).", + // bp->id, bp->h, bp->density.wcount); + + /* Some smoothing length multiples. */ + const float h = bp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + + /* Re-set problematic values */ + bp->density.wcount = kernel_root * h_inv_dim; + bp->density.wcount_dh = 0.f; + + bp->velocity_gas[0] = FLT_MAX; + bp->velocity_gas[1] = FLT_MAX; + bp->velocity_gas[2] = FLT_MAX; + + bp->internal_energy_gas = -FLT_MAX; + bp->hot_gas_internal_energy = -FLT_MAX; +} + +/** + * @brief Return the current instantaneous accretion rate of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_accretion_rate(const struct bpart* bp) { + return bp->accretion_rate; +} + +/** + * @brief Return the total accreted gas mass of this BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_accreted_mass(const struct bpart* bp) { + return bp->total_accreted_mass; +} + +/** + * @brief Return the subgrid mass of this BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_subgrid_mass(const struct bpart* bp) { + return bp->subgrid_mass; +} + +/** + * @brief Return the current bolometric luminosity of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_bolometric_luminosity(const struct bpart* bp, + const struct phys_const* constants) { + const double c = constants->const_speed_light_c; + return bp->accretion_rate * bp->radiative_efficiency * c * c; +} + +/** + * @brief Return the current kinetic jet power of the BH. + * + * @param bp the #bpart. + */ +__attribute__((always_inline)) INLINE static double black_holes_get_jet_power( + const struct bpart* bp, const struct phys_const* constants, + const struct black_holes_props* props) { + const double c = constants->const_speed_light_c; + double eta_jet = props->jet_efficiency; + if (bp->state != BH_states_adaf) { + eta_jet = FLT_MIN; + } + /* accretion_rate is M_dot,acc from the paper */ + return eta_jet * bp->accretion_rate * c * c; +} + +/** + * @brief Update the properties of a black hole particles by swallowing + * a gas particle. + * + * @param bp The #bpart to update. + * @param p The #part that is swallowed. + * @param xp The #xpart that is swallowed. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void black_holes_swallow_part( + struct bpart* bp, const struct part* p, const struct xpart* xp, + const struct cosmology* cosmo) { + + /* Get the current dynamical masses */ + const float gas_mass = hydro_get_mass(p); + const float BH_mass = bp->mass; + + /* Increase the dynamical mass of the BH. */ + bp->mass += gas_mass; + bp->gpart->mass += gas_mass; + + /* Physical velocity difference between the particles */ + const float dv[3] = {(bp->v[0] - p->v[0]) * cosmo->a_inv, + (bp->v[1] - p->v[1]) * cosmo->a_inv, + (bp->v[2] - p->v[2]) * cosmo->a_inv}; + + /* Physical distance between the particles */ + const float dx[3] = {(bp->x[0] - p->x[0]) * cosmo->a, + (bp->x[1] - p->x[1]) * cosmo->a, + (bp->x[2] - p->x[2]) * cosmo->a}; + + /* Collect the swallowed angular momentum */ + bp->swallowed_angular_momentum[0] += + gas_mass * (dx[1] * dv[2] - dx[2] * dv[1]); + bp->swallowed_angular_momentum[1] += + gas_mass * (dx[2] * dv[0] - dx[0] * dv[2]); + bp->swallowed_angular_momentum[2] += + gas_mass * (dx[0] * dv[1] - dx[1] * dv[0]); + + /* Update the BH momentum */ + const float BH_mom[3] = {BH_mass * bp->v[0] + gas_mass * p->v[0], + BH_mass * bp->v[1] + gas_mass * p->v[1], + BH_mass * bp->v[2] + gas_mass * p->v[2]}; + + bp->v[0] = BH_mom[0] / bp->mass; + bp->v[1] = BH_mom[1] / bp->mass; + bp->v[2] = BH_mom[2] / bp->mass; + bp->gpart->v_full[0] = bp->v[0]; + bp->gpart->v_full[1] = bp->v[1]; + bp->gpart->v_full[2] = bp->v[2]; + + const float dr = sqrt(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]); + message( + "BH %lld swallowing gas particle %lld " + "(Delta_v = [%f, %f, %f] U_V, " + "Delta_x = [%f, %f, %f] U_L, " + "Delta_v_rad = %f)", + bp->id, p->id, -dv[0], -dv[1], -dv[2], -dx[0], -dx[1], -dx[2], + (dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]) / dr); + + /* Update the BH metal masses */ + struct chemistry_bpart_data* bp_chem = &bp->chemistry_data; + const struct chemistry_part_data* p_chem = &p->chemistry_data; + chemistry_add_part_to_bpart(bp_chem, p_chem, gas_mass); + + /* This BH swallowed a gas particle */ + bp->number_of_gas_swallows++; + bp->number_of_direct_gas_swallows++; + + /* This BH lost a neighbour */ + bp->num_ngbs--; + bp->num_gravitational_ngbs--; + bp->ngb_mass -= gas_mass; +} + +/** + * @brief Update the properties of a black hole particles by swallowing + * a BH particle. + * + * @param bpi The #bpart to update. + * @param bpj The #bpart that is swallowed. + * @param cosmo The current cosmological model. + * @param time Time since the start of the simulation (non-cosmo mode). + * @param with_cosmology Are we running with cosmology? + * @param props The properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static void black_holes_swallow_bpart( + struct bpart* bpi, const struct bpart* bpj, const struct cosmology* cosmo, + const double time, const int with_cosmology, + const struct black_holes_props* props, const struct phys_const* constants) { + + /* Get the current dynamical masses */ + const float bpi_dyn_mass = bpi->mass; + const float bpj_dyn_mass = bpj->mass; + + /* Is this merger ratio above the threshold for recording? */ + const double merger_ratio = bpj->subgrid_mass / bpi->subgrid_mass; + if (merger_ratio > props->major_merger_threshold) { + if (with_cosmology) { + bpi->last_major_merger_scale_factor = cosmo->a; + } else { + bpi->last_major_merger_time = time; + } + } else if (merger_ratio > props->minor_merger_threshold) { + if (with_cosmology) { + bpi->last_minor_merger_scale_factor = cosmo->a; + } else { + bpi->last_minor_merger_time = time; + } + } + + /* Increase the masses of the BH. */ + bpi->mass += bpj->mass; + bpi->gpart->mass += bpj->mass; + bpi->subgrid_mass += bpj->subgrid_mass; + + /* Collect the swallowed angular momentum */ + bpi->swallowed_angular_momentum[0] += bpj->swallowed_angular_momentum[0]; + bpi->swallowed_angular_momentum[1] += bpj->swallowed_angular_momentum[1]; + bpi->swallowed_angular_momentum[2] += bpj->swallowed_angular_momentum[2]; + + /* Update the BH momentum */ + const float BH_mom[3] = {bpi_dyn_mass * bpi->v[0] + bpj_dyn_mass * bpj->v[0], + bpi_dyn_mass * bpi->v[1] + bpj_dyn_mass * bpj->v[1], + bpi_dyn_mass * bpi->v[2] + bpj_dyn_mass * bpj->v[2]}; + + bpi->v[0] = BH_mom[0] / bpi->mass; + bpi->v[1] = BH_mom[1] / bpi->mass; + bpi->v[2] = BH_mom[2] / bpi->mass; + bpi->gpart->v_full[0] = bpi->v[0]; + bpi->gpart->v_full[1] = bpi->v[1]; + bpi->gpart->v_full[2] = bpi->v[2]; + + /* Update the BH metal masses */ + struct chemistry_bpart_data* bpi_chem = &bpi->chemistry_data; + const struct chemistry_bpart_data* bpj_chem = &bpj->chemistry_data; + chemistry_add_bpart_to_bpart(bpi_chem, bpj_chem); + + /* Update the energy reservoir */ + bpi->jet_mass_reservoir += bpj->jet_mass_reservoir; + + /* Add up all the BH seeds */ + bpi->cumulative_number_seeds += bpj->cumulative_number_seeds; + + /* Add up all the gas particles we swallowed */ + bpi->number_of_gas_swallows += bpj->number_of_gas_swallows; + + /* Add the subgrid angular momentum that we swallowed */ + bpi->accreted_angular_momentum[0] += bpj->accreted_angular_momentum[0]; + bpi->accreted_angular_momentum[1] += bpj->accreted_angular_momentum[1]; + bpi->accreted_angular_momentum[2] += bpj->accreted_angular_momentum[2]; + + /* We had another merger */ + bpi->number_of_mergers++; +} + +/** + * @brief Compute the wind launch speed for this feedback step. + * + * @param props The properties of the black hole scheme. + * @param constants The physical constants (in internal units). + * @param bp The black hole particle. + * @param Eddington_rate M_dot,Edd for the black hole. + */ +__attribute__((always_inline)) INLINE static double get_black_hole_wind_speed( + const struct black_holes_props* props, + const struct phys_const* constants, + const struct bpart *bp, + const double Eddington_rate) { + + if (bp->accretion_rate < 0.f || bp->m_dot_inflow < 0.f) return 0.f; + + switch (bp->state) { + case BH_states_adaf: + return props->adaf_wind_speed; + break; + case BH_states_quasar: + return props->quasar_wind_speed; + break; + case BH_states_slim_disk: + return props->slim_disk_wind_speed; + break; + default: + error("Invalid black hole state."); + return 0.f; + break; + } +} + +/** + * @brief Compute the accretion rate of the black hole and all the quantities + * required for the feedback loop. + * + * @param bp The black hole particle. + * @param props The properties of the black hole scheme. + * @param constants The physical constants (in internal units). + * @param cosmo The cosmological model. + * @param cooling Properties of the cooling model. + * @param floor_props Properties of the entropy fllor. + * @param time Time since the start of the simulation (non-cosmo mode). + * @param with_cosmology Are we running with cosmology? + * @param dt The time-step size (in physical internal units). + * @param ti_begin The time at which the step begun (ti_current). + */ +__attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( + struct bpart* restrict bp, const struct black_holes_props* props, + const struct phys_const* constants, const struct cosmology* cosmo, + const struct cooling_function_data* cooling, + const struct entropy_floor_properties* floor_props, const double time, + const int with_cosmology, const double dt, const integertime_t ti_begin) { + + /* Record that the black hole has another active time step */ + bp->number_of_time_steps++; + + if (dt == 0. || bp->rho_gas == 0. || bp->h == 0.) return; + + /* A black hole should never accrete/feedback if it is not in a galaxy */ + if (bp->group_data.mass <= 0.f) return; + + /* Gather some physical constants (all in internal units) */ + const double G = constants->const_newton_G; + const double c = constants->const_speed_light_c; + const double proton_mass = constants->const_proton_mass; + const double sigma_Thomson = constants->const_thomson_cross_section; + + /* Gather the parameters of the model */ + const double f_Edd_maximum = props->f_Edd_maximum; + +#ifdef OBSIDIAN_DEBUG_CHECKS + if (isnan(bp->subgrid_mass)) error("subgrid_mass nan"); +#endif + /* (Subgrid) mass of the BH (internal units) */ + const double BH_mass = bp->subgrid_mass; + + /* Compute the Eddington rate (internal units). + * IMPORTANT: epsilon_r = 0.1 is the SET value for the Eddington rate. + * It is assumed in all of the derivations for the state model. + */ + const double Eddington_rate = + 4. * M_PI * G * BH_mass * proton_mass / (0.1 * c * sigma_Thomson); + + const double bh_h = kernel_gamma * bp->h; + const double bh_h_inv = 1. / bh_h; + const double volume_bh_inv = + (3. / (4. * M_PI)) * bh_h_inv * bh_h_inv * bh_h_inv; + double gas_rho = 0.; + if (props->bondi_use_all_gas) { + gas_rho = bp->rho_gas; + } + else { + gas_rho = bp->hot_gas_mass * volume_bh_inv; + } + + const double gas_rho_phys = gas_rho * cosmo->a3_inv; + + /* We can now compute the Bondi accretion rate (internal units) + * D. Rennehan: In Simba, we only consider the hot gas within + * the kernel for the Bondi rate, and the cold gas using the + * torque accretion estimator. + */ + double Bondi_rate = 0.; + + /* Use all gas within the kernel or only the hot gas*/ + double gas_internal_energy = 0.; + if (props->bondi_use_all_gas) { + if (bp->internal_energy_gas > 0.) { + gas_internal_energy = bp->internal_energy_gas; + } + } + else { + if (bp->hot_gas_internal_energy > 0.) { + gas_internal_energy = bp->hot_gas_internal_energy; + } + } + + /* Check if there is hot/any gas in the kernel */ + if (gas_internal_energy > 0.) { + double gas_c = + gas_soundspeed_from_internal_energy(gas_rho, gas_internal_energy); + + if (gas_c > 0.) { + const double gas_c_phys_inv = + 1. / (cosmo->a_factor_sound_speed * gas_c); + + Bondi_rate = 4. * M_PI * G * G * BH_mass * BH_mass * gas_rho_phys * + gas_c_phys_inv * gas_c_phys_inv * gas_c_phys_inv; + + /* In the case of standard Bondi, we limit it to the Eddington rate */ + Bondi_rate = fmin(Bondi_rate, props->f_Edd_Bondi_maximum * Eddington_rate); + } + } + + /* The accretion rate estimators give Mdot,inflow + * (Mdot,BH = f_acc * Mdot,inflow) */ + const double bondi_accr_rate = props->bondi_alpha * Bondi_rate; + + /* Compute the torque-limited accretion rate */ + double torque_accr_rate = 0.; + + double f_corr_stellar = 10.; + const double galaxy_gas_mass = + bp->group_data.mass - bp->group_data.stellar_mass; + if (galaxy_gas_mass > 0.) { + f_corr_stellar = + min(bp->group_data.stellar_mass / galaxy_gas_mass, f_corr_stellar); + } + + /* Torque accretion rate based on some fraction of gas near BH + * falling in on dynamical time. (This is the default.) + * Here the accretion rate is only based on Mgas / tdyn. + * We do not use the DM mass to compute tdyn since it probably + * doesn't contribute much near the core of the system. We also + * assume that the gas fraction is constant in the galaxy in + * order to compute Mstar within the kernel of the black hole. + * Therefore, Mdot = Mgas / tdyn = Mgas / sqrt(3pi/(32 G rho)) + * and rho = (Mgas + Mstar + Mdm) / (4pi h^3 / 3) where + * Mstar = Mgas / fgas, Mdm = 0. Therefore, + * rho = 3 * ((1 + fgas) / fgas) * Mgas / (4 * pi * h^3) + * and + * Mdot = Mgas * sqrt(32 * G * 3 * ((1 + fgas) / fgas) * Mgas)) / + * sqrt(3 * pi * 4 * pi * h^3) + * = sqrt(96 * G * ((1 + fgas) / fgas) * Mgas^3) / + * sqrt(12 * pi^2 * h^3) + * = (1 / pi) * sqrt(8 * G * ((1 + fgas) / fgas) * (Mgas / h)^3)) + */ + double tdyn_inv = FLT_MAX; + const float potential = fabs(gravity_get_comoving_potential(bp->gpart)); + /* Includes dynamical mass of the BH */ + double total_mass = gravity_get_total_mass(bp->gpart); + switch (props->dynamical_time_calculation_method) { + /* Assume gas fraction is the same in the kernel and outside */ + case 0: + { + /* Compute correction to total dynamical mass around + * BH contributed by stars */ + const double m_star_gal = bp->group_data.stellar_mass; + const double m_gas_cold_gal = + bp->group_data.mass - bp->group_data.stellar_mass; + const double m_gas_bh = bp->gravitational_ngb_mass; + const double m_bh = bp->mass; + + /* Compute stellar mass assuming a constant cold gas fraction in the + * entire galaxy. If m_gas_cold_bh is zero it doesn't matter since + * the BH won't acrrete in the torque mode anyway. */ + const double m_gas_cold_bh = bp->cold_gas_mass; + double m_star_bh = 0.; + if (m_gas_cold_gal > 0.) { + m_star_bh = (m_star_gal / m_gas_cold_gal) * m_gas_cold_bh; + } + + /* Have to assume baryon dominance within the kernel */ + const double rho_est = (m_star_bh + m_gas_bh + m_bh) * volume_bh_inv; + + /* Inverse physical dynamical time */ + tdyn_inv = sqrt(32. * G * rho_est * cosmo->a3_inv / (3. * M_PI)); + break; + } + + /* Assume BH potential */ + case 1: + if (potential >= 0.f) { + tdyn_inv = (sqrt(potential) / bh_h) * cosmo->a2_inv; + } + break; + + /* Assume dynamical time from the kernel mass */ + case 2: + { + /* do not have gravity_props here */ + const double eps = gravity_get_softening(bp->gpart, NULL); + const double volume = (4.f * M_PI / 3.f) * eps * eps * eps; + const double rho = total_mass / volume; + tdyn_inv = sqrt(32. * G * rho * cosmo->a3_inv / (3. * M_PI)); + break; + } + + default: + error("Unknown dynamical time calculation method %d", + props->dynamical_time_calculation_method); + break; + } + + /* Compute infall times to BH at this redshift */ + double t_infall = props->bh_accr_dyn_time_fac / tdyn_inv; + + /* If the input value is above 10, assume it is constant in Myr, + * scaled with 1/H */ + if (props->bh_accr_dyn_time_fac > 10.) { + t_infall = + props->bh_accr_dyn_time_fac * cosmo->H0 / + (props->time_to_Myr * cosmo->H); + } + + const double corot_gas_mass = + bp->cold_gas_mass - 2. * (bp->cold_gas_mass - bp->cold_disk_mass); + if (props->torque_accretion_norm > 0.f) { + switch (props->torque_accretion_method) { + case 0: + if (galaxy_gas_mass > 0.) { + torque_accr_rate = + props->torque_accretion_norm * bp->cold_disk_mass * tdyn_inv; + } + break; + + case 1: + if (corot_gas_mass > 0. && bp->cold_gas_mass > 0.) { + torque_accr_rate = + props->torque_accretion_norm * corot_gas_mass * tdyn_inv; + } + break; + + case 2: + if (corot_gas_mass > 0. && bp->cold_gas_mass > 0.) { + const double m_disk = bp->cold_gas_mass * f_corr_stellar; + const double f_disk = corot_gas_mass / bp->cold_gas_mass; + + const double r0 = bh_h * cosmo->a * (props->length_to_parsec * 0.01); + + const double alpha = 5.; + const double mass_to_1e9solar = props->mass_to_solar_mass * 1.0e-9; + const double mass_to_1e8solar = props->mass_to_solar_mass * 1.0e-8; + + const double f0 = + 0.31 * f_disk * f_disk * + pow(m_disk * mass_to_1e9solar, -1. / 3.); + const double f_gas = corot_gas_mass / m_disk; + const double mass_in_1e8solar = bp->subgrid_mass * mass_to_1e8solar; + + torque_accr_rate = props->torque_accretion_norm * alpha * + corot_gas_mass * mass_to_1e9solar * + powf(f_disk, 5. / 2.) * + powf(mass_in_1e8solar, 1. / 6.) * + powf(r0, -3. / 2.) / (1. + f0 / f_gas); + torque_accr_rate *= + (props->time_to_yr / props->mass_to_solar_mass); + } + break; + + case 3: + if (galaxy_gas_mass > 0.) { + torque_accr_rate = + props->torque_accretion_norm * bp->cold_gas_mass * tdyn_inv; + } + break; + + default: + error("Unknown torque_accretion_method=%d", + props->torque_accretion_method); + break; + } + + /* Do suppression of BH growth */ + switch (props->suppress_growth) { + case 1: + { + const double r0 = bh_h * cosmo->a * props->length_to_parsec; + const double sigma_eff = f_corr_stellar * bp->ngb_mass * + props->mass_to_solar_mass / + (M_PI * r0 * r0); + + torque_accr_rate *= sigma_eff / (sigma_eff + 3000.); + break; + } + + case 2: + case 6: + case 7: + { + double m_suppress = fabs(props->bh_characteristic_suppression_mass); + if (props->bh_characteristic_suppression_mass < 0) { + m_suppress *= cosmo->a; + } + + torque_accr_rate *= + 1. - exp(-bp->subgrid_mass * props->mass_to_solar_mass / m_suppress); + break; + } + + case 4: + case 5: + { + double slope = -0.317; + /* compute mass loading factor from SF feedback, + * should be same as used in feedback_mass_loading_factor() + */ + const double galaxy_stellar_mass = + max(bp->group_data.stellar_mass, 5.8e8 / props->mass_to_solar_mass); + + const double min_mass = 5.2e9 / props->mass_to_solar_mass; + if (galaxy_stellar_mass > min_mass) slope = -0.716; + + const double eta = + 12. * pow(galaxy_stellar_mass / min_mass, slope); + + /* compute fraction of mass within kernel going into outflows + * over accretion time + */ + double sfr = 0.; + if (bp->cold_gas_mass > 0.f) { + sfr = bp->group_data.ssfr * bp->group_data.stellar_mass; + sfr *= props->torque_accretion_norm; + } + + /* suppress accretion by factor accounting for mass lost + * in SF-driven outflow + */ + if (sfr > 0.) { + torque_accr_rate *= torque_accr_rate / (torque_accr_rate + eta * sfr); + } + break; + } + + default: + break; + } + } + +#ifdef OBSIDIAN_DEBUG_CHECKS + if (isnan(bondi_accr_rate)) error("bondi_accr_rate nan"); + if (isnan(torque_accr_rate)) error("torque_accr_rate nan"); +#endif + + /* Right now this is M_dot,inflow. We will multiply by + * f_accretion later to make it M_dot,acc */ + bp->accretion_rate = bondi_accr_rate + torque_accr_rate; + + /* We will use eddington_fraction_lower_boundary and + * eddington_fraction_upper_boundary to divide up the accretion rate + * in three regimes. + * + * In order to switch out of a regime (i.e. a state), it is necessary + * for the true accretion rate (compared to Eddington rate) to switch + * over the boundary. Therefore, before we switch a state we must calculate + * what the previous state predicts the true accretion rate onto the SMBH is, + * and then update the state if it crosses a boundary. + */ + double predicted_mdot_medd = 0.; + switch (bp->state) { + case BH_states_adaf: + predicted_mdot_medd + = bp->accretion_rate * props->adaf_f_accretion / Eddington_rate; + + if (predicted_mdot_medd > props->eddington_fraction_upper_boundary) { + bp->state = BH_states_slim_disk; + break; + } + if (predicted_mdot_medd > props->eddington_fraction_lower_boundary) { + bp->state = BH_states_quasar; + } + + break; /* end case ADAF */ + case BH_states_quasar: + predicted_mdot_medd + = bp->accretion_rate * props->quasar_f_accretion / Eddington_rate; + + if (BH_mass > props->adaf_mass_limit && + predicted_mdot_medd < props->eddington_fraction_lower_boundary) { + bp->state = BH_states_adaf; + break; + } + + if (predicted_mdot_medd > props->eddington_fraction_upper_boundary) { + bp->state = BH_states_slim_disk; + } + + break; /* end case quasar */ + case BH_states_slim_disk: + predicted_mdot_medd = + get_black_hole_upper_mdot_medd(props, constants, + bp->accretion_rate / Eddington_rate); + + if (BH_mass > props->adaf_mass_limit && + predicted_mdot_medd < props->eddington_fraction_lower_boundary) { + bp->state = BH_states_adaf; + break; + } + + if (predicted_mdot_medd < props->eddington_fraction_upper_boundary) { + bp->state = BH_states_quasar; + } + + break; /* end case slim disk */ + default: + error("Invalid black hole state."); + break; + } + + /* We need to store the full M_dot,inflow rate to calculate the + * fraction at high accretion rate */ + bp->m_dot_inflow = bp->accretion_rate; + + /* This depends on the new state */ + bp->f_accretion = + get_black_hole_accretion_factor(props, constants, bp->m_dot_inflow, + BH_mass, bp->state, Eddington_rate); +#ifdef OBSIDIAN_DEBUG_CHECKS + if (isnan(bp->f_accretion)) error("f_accretion nan"); +#endif + if (bp->f_accretion <= 1.e-10f) bp->f_accretion = 0.f; + + /* bp->accretion_rate is M_dot,acc in Rennehan+24 */ + bp->accretion_rate *= bp->f_accretion; + + if (!props->bondi_use_all_gas) { + /* Now we can Eddington limit. */ + bp->accretion_rate = + min(bp->accretion_rate, f_Edd_maximum * Eddington_rate); + } + + /* All accretion is done, now we can set the eddington fraction */ + bp->eddington_fraction = bp->accretion_rate / Eddington_rate; + + /* Get the new radiative efficiency based on the new state */ + bp->radiative_efficiency = + get_black_hole_radiative_efficiency(props, bp->eddington_fraction, + bp->state); +#ifdef OBSIDIAN_DEBUG_CHECKS + if (isnan(bp->radiative_efficiency)) error("radiative_efficiency nan"); +#endif + if (bp->radiative_efficiency < 1.e-10f) bp->radiative_efficiency = 0.f; + + double mass_rate = 0.; + const double luminosity = + bp->radiative_efficiency * bp->accretion_rate * c * c; + + /* Factor in the radiative efficiency, don't subtract + * jet BZ efficiency (spin is fixed) */ + mass_rate = (1. - bp->radiative_efficiency) * bp->accretion_rate; + + /* This is used for X-ray feedback later */ + bp->radiative_luminosity = luminosity; + +#ifdef OBSIDIAN_DEBUG_CHECKS + if (isnan(dt)) error("dt nan"); +#endif + /* Integrate forward in time */ + double delta_mass = mass_rate * dt; + + /* If desired we put mass into accretion disk which feeds BH on some + * frac of tdyn + */ + if (t_infall > 0.f) { + /* Add accreted mass into a reservoir representing BH accretion disk */ + bp->accretion_disk_mass += delta_mass; + + /* Compute mass that will actually go into BH */ + delta_mass = bp->accretion_disk_mass * (1. - exp(-dt / t_infall)); + + /* This mass gets removed from the accretion disk */ + if (bp->accretion_disk_mass > delta_mass) { + bp->accretion_disk_mass -= delta_mass; + } + else { + delta_mass = bp->accretion_disk_mass; + bp->accretion_disk_mass = 0.; + } + + /* Recompute accretion rate based on the reservoir change */ + bp->accretion_rate = delta_mass / (dt * (1. - bp->radiative_efficiency)); + } + + bp->subgrid_mass += delta_mass; + bp->total_accreted_mass += delta_mass; + + if (bp->state == BH_states_adaf) { + /* ergs to dump in a kernel-weighted fashion */ + if (props->adaf_wind_mass_loading == 0.f) { + bp->adaf_energy_to_dump = + get_black_hole_coupling(props, cosmo, bp->state) * + props->adaf_disk_efficiency * bp->accretion_rate * c * c * dt; + } + else { + const float adaf_v2 = props->adaf_wind_speed * props->adaf_wind_speed; + float mass_this_step = + props->adaf_wind_mass_loading * bp->accretion_rate * dt; + bp->adaf_energy_to_dump += 0.5f * mass_this_step * adaf_v2; + } + } + + if (bp->state == BH_states_adaf || + (props->slim_disk_jet_active && bp->state == BH_states_slim_disk)) { + /* Psi_jet*M_dot,acc*dt is the total mass expected in the jet this step */ + bp->jet_mass_reservoir += props->jet_mass_loading * bp->accretion_rate * dt; + } + + if (bp->subgrid_mass < bp->mass) { + /* In this case, the BH is still accreting from its (assumed) subgrid gas + * mass reservoir left over when it was formed. There is some loss in this + * due to radiative losses, so we must decrease the particle mass + * in proportion to its current accretion rate. We do not account for this + * in the swallowing approach, however. */ + bp->mass -= bp->radiative_efficiency * bp->accretion_rate * dt; + + if (bp->mass < 0) { + error("Black hole %lld reached negative mass (%g). Trouble ahead...", + bp->id, bp->mass); + } + + /* Make sure not to destroy low mass galaxies */ + if (bp->subgrid_mass > props->minimum_black_hole_mass_unresolved && + bp->state != BH_states_adaf) { + /* Make sure if many mergers have driven up the dynamical mass at low + * subgrid mass, that we still kick out particles! */ + const float psi = (1.f - bp->f_accretion) / bp->f_accretion; + bp->unresolved_mass_reservoir += psi * bp->accretion_rate * dt; + } + } + + /* Increase the subgrid angular momentum according to what we accreted + * Note that this is already in physical units, a factors from velocity and + * radius cancel each other. Also, the circular velocity contains an extra + * smoothing length factor that we undo here. */ + const double m_times_r = (mass_rate * dt) * bp->h; + /* L = m * r * v */ + bp->accreted_angular_momentum[0] += m_times_r * bp->circular_velocity_gas[0]; + bp->accreted_angular_momentum[1] += m_times_r * bp->circular_velocity_gas[1]; + bp->accreted_angular_momentum[2] += m_times_r * bp->circular_velocity_gas[2]; + + /* Keep v_kick physical, there are a lot of comparisons */ + bp->v_kick = + get_black_hole_wind_speed(props, constants, bp, Eddington_rate); + + /* This is always true in the ADAF mode; only heating happens */ + if (bp->state == BH_states_adaf) { + bp->v_kick = 0.f; + } + +#ifdef OBSIDIAN_DEBUG_CHECKS + tdyn_inv = (tdyn_inv > 0.f) ? tdyn_inv : FLT_MIN; + message("BH_ACC: z=%g bid=%lld ms=%g dms=%g sfr=%g mbh=%g dmbh=%g state=%d " + "torque=%g bondi=%g fEdd=%g facc=%g fsupp=%g mcold=%g mhot=%g mdisk=%g" + " tin=%g vkick=%g dmass=%g radeff=%g mres=%g tdyn=%g", + cosmo->z, + bp->id, + bp->group_data.stellar_mass * props->mass_to_solar_mass, + bp->group_data.ssfr * bp->group_data.stellar_mass * dt * + props->mass_to_solar_mass, + bp->group_data.ssfr * bp->group_data.stellar_mass * + props->mass_to_solar_mass / props->time_to_yr, + bp->subgrid_mass * props->mass_to_solar_mass, + delta_mass * props->mass_to_solar_mass, + bp->state, + torque_accr_rate * props->mass_to_solar_mass / props->time_to_yr, + bondi_accr_rate * props->mass_to_solar_mass / props->time_to_yr, + bp->eddington_fraction, + bp->f_accretion, + 1. - exp(-bp->subgrid_mass * props->mass_to_solar_mass / + fabs(props->bh_characteristic_suppression_mass) * cosmo->a), + bp->cold_gas_mass * props->mass_to_solar_mass, + bp->hot_gas_mass * props->mass_to_solar_mass, + corot_gas_mass * props->mass_to_solar_mass, + t_infall * props->time_to_Myr, + bp->v_kick / props->kms_to_internal, + delta_mass, + bp->radiative_efficiency, + bp->accretion_disk_mass, + (1.f / tdyn_inv) * props->time_to_Myr); + + message("BH_STATES: id=%lld, new_state=%d, predicted_mdot_medd=%g, " + "eps_r=%g, f_Edd=%g, f_acc=%g, " + "luminosity=%g, accr_rate=%g Msun/yr, coupling=%g, v_kick=%g km/s, " + "jet_mass_reservoir=%g Msun unresolved_reservoir=%g Msun", + bp->id, + bp->state, + predicted_mdot_medd, + bp->radiative_efficiency, + bp->eddington_fraction, + bp->f_accretion, + bp->radiative_luminosity * props->conv_factor_energy_rate_to_cgs, + bp->accretion_rate * props->mass_to_solar_mass / props->time_to_yr, + get_black_hole_coupling(props, cosmo, bp->state), + bp->v_kick / props->kms_to_internal, + bp->jet_mass_reservoir * props->mass_to_solar_mass, + bp->unresolved_mass_reservoir * props->mass_to_solar_mass); +#endif + + printf("BH_DETAILS " + "%2.12f %lld " + " %g %g %g %g %g %g %g " + " %g %g %g %g " + " %g %g %g %g %g " + " %2.10f %2.10f %2.10f " + " %2.7f %2.7f %2.7f " + " %g %g %g %g %g %g" + " %g %d %g %g" + " %g %g\n", + cosmo->a, + bp->id, + bp->mass * props->mass_to_solar_mass, + bp->subgrid_mass * props->mass_to_solar_mass, + total_mass * props->mass_to_solar_mass, + bp->accretion_rate * props->mass_to_solar_mass / props->time_to_yr, + Bondi_rate * props->mass_to_solar_mass / props->time_to_yr, + torque_accr_rate * props->mass_to_solar_mass / props->time_to_yr, + dt * props->time_to_Myr, + (bp->rho_gas * cosmo->a3_inv) * props->rho_to_n_cgs, + bp->hot_gas_internal_energy * cosmo->a_factor_internal_energy * + props->conv_factor_specific_energy_to_cgs, + bp->gas_SFR * props->mass_to_solar_mass / props->time_to_yr, + bp->ngb_mass * props->mass_to_solar_mass, + bp->hot_gas_mass * props->mass_to_solar_mass, + bp->stellar_mass * props->mass_to_solar_mass, + 0.f /* Mgas,bulge */, + bp->stellar_bulge_mass * props->mass_to_solar_mass, + 0.f, + bp->x[0] * cosmo->a * props->length_to_parsec / 1.0e3f, + bp->x[1] * cosmo->a * props->length_to_parsec / 1.0e3f, + bp->x[2] * cosmo->a * props->length_to_parsec / 1.0e3f, + bp->v[0] * cosmo->a_inv / props->kms_to_internal, + bp->v[1] * cosmo->a_inv / props->kms_to_internal, + bp->v[2] * cosmo->a_inv / props->kms_to_internal, + bp->angular_momentum_gas[0], + bp->angular_momentum_gas[1], + bp->angular_momentum_gas[2], + 0.f, /* specific angular momentum of the stars */ + 0.f, /* specific angular momentum of the stars */ + 0.f, /* specific angular momentum of the stars */ + bp->radiative_luminosity * props->conv_factor_energy_rate_to_cgs, + bp->state, + bp->f_accretion, + bp->radiative_efficiency, + bp->eddington_fraction, + bp->gravitational_ngb_mass * props->mass_to_solar_mass); +} + +/** + * @brief Computes the (maximal) repositioning speed for a black hole. + * + * Calculated as upsilon * (m_BH / m_ref) ^ beta_m * (n_H_BH / n_ref) ^ beta_n + * where m_BH = BH subgrid mass, n_H_BH = physical gas density around BH + * and upsilon, m_ref, beta_m, n_ref, and beta_n are parameters. + * + * @param bp The #bpart. + * @param props The properties of the black hole model. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static double +black_holes_get_repositioning_speed(const struct bpart* restrict bp, + const struct black_holes_props* props, + const struct cosmology* cosmo) { + + const double n_gas_phys = bp->rho_gas * cosmo->a3_inv * props->rho_to_n_cgs; + const double v_repos = + props->reposition_coefficient_upsilon * + pow(bp->subgrid_mass / props->reposition_reference_mass, + props->reposition_exponent_mass) * + pow(n_gas_phys / props->reposition_reference_n_H, + props->reposition_exponent_n_H); + + /* Make sure the repositioning is not back-firing... */ + if (v_repos < 0) + error( + "BH %lld wants to reposition at negative speed (%g U_V). Do you " + "think you are being funny? No-one is laughing.", + bp->id, v_repos); + + return v_repos; +} + +/** + * @brief Finish the calculation of the new BH position. + * + * Here, we check that the BH should indeed be moved in the next drift. + * + * @param bp The black hole particle. + * @param props The properties of the black hole scheme. + * @param constants The physical constants (in internal units). + * @param cosmo The cosmological model. + * @param dt The black hole particle's time step. + * @param ti_begin The time at the start of the temp + */ +__attribute__((always_inline)) INLINE static void black_holes_end_reposition( + struct bpart* restrict bp, const struct black_holes_props* props, + const struct phys_const* constants, const struct cosmology* cosmo, + const double dt, const integertime_t ti_begin) { + + /* First check: did we find any eligible neighbour particle to jump to? */ + if (bp->reposition.min_potential != FLT_MAX) { + + /* Record that we have a (possible) repositioning situation */ + bp->number_of_reposition_attempts++; + + /* Is the potential lower (i.e. the BH is at the bottom already) + * OR is the BH massive enough that we don't reposition? */ + const float potential = gravity_get_comoving_potential(bp->gpart); + if (potential < bp->reposition.min_potential || + bp->subgrid_mass > props->max_reposition_mass) { + + /* No need to reposition */ + bp->reposition.min_potential = FLT_MAX; + bp->reposition.delta_x[0] = -FLT_MAX; + bp->reposition.delta_x[1] = -FLT_MAX; + bp->reposition.delta_x[2] = -FLT_MAX; + + } else if (props->set_reposition_speed) { + + /* If we are re-positioning, move the BH a fraction of delta_x, so + * that we have a well-defined re-positioning velocity (repos_vel + * cannot be negative). */ + double repos_vel = black_holes_get_repositioning_speed(bp, props, cosmo); + + /* Convert target reposition velocity to a fractional reposition + * along reposition.delta_x */ + const double dx = bp->reposition.delta_x[0]; + const double dy = bp->reposition.delta_x[1]; + const double dz = bp->reposition.delta_x[2]; + const double d = sqrt(dx * dx + dy * dy + dz * dz); + + /* Exclude the pathological case of repositioning by zero distance */ + if (d > 0) { + double repos_frac = repos_vel * dt / d; + + /* We should never get negative repositioning fractions... */ + if (repos_frac < 0) + error("Wanting to reposition by negative fraction (%g)?", repos_frac); + + /* ... but fractions > 1 can occur if the target velocity is high. + * We do not want this, because it could lead to overshooting the + * actual potential minimum. */ + if (repos_frac > 1) { + repos_frac = 1.; + repos_vel = repos_frac * d / dt; + } + + bp->last_repos_vel = (float)repos_vel; + bp->reposition.delta_x[0] *= repos_frac; + bp->reposition.delta_x[1] *= repos_frac; + bp->reposition.delta_x[2] *= repos_frac; + } + + /* ends section for fractional repositioning */ + } else { + + /* We _should_ reposition, but not fractionally. Here, we will + * reposition exactly on top of another gas particle - which + * could cause issues, so we add on a small fractional offset + * of magnitude 0.001 h in the reposition delta. */ + + /* Generate three random numbers in the interval [-0.5, 0.5[; id, + * id**2, and id**3 are required to give unique random numbers (as + * random_unit_interval is completely reproducible). */ + const float offset_dx = + random_unit_interval(bp->id, ti_begin, random_number_BH_reposition) - + 0.5f; + const float offset_dy = + random_unit_interval(bp->id * bp->id, ti_begin, + random_number_BH_reposition) - + 0.5f; + const float offset_dz = + random_unit_interval(bp->id * bp->id * bp->id, ti_begin, + random_number_BH_reposition) - + 0.5f; + + const float length_inv = + 1.0f / sqrtf(offset_dx * offset_dx + offset_dy * offset_dy + + offset_dz * offset_dz); + + const float norm = 0.001f * bp->h * length_inv; + + bp->reposition.delta_x[0] += offset_dx * norm; + bp->reposition.delta_x[1] += offset_dy * norm; + bp->reposition.delta_x[2] += offset_dz * norm; + } + } /* ends section if we found eligible repositioning target(s) */ +} + +/** + * @brief Reset acceleration fields of a particle + * + * This is the equivalent of hydro_reset_acceleration. + * We do not compute the acceleration on black hole, therefore no need to use + * it. + * + * @param bp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void black_holes_reset_feedback( + struct bpart* restrict bp) { + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + bp->ids_ngbs_force[i] = -1; + bp->num_ngb_force = 0; +#endif +} + +/** + * @brief Store the gravitational potential of a black hole by copying it from + * its #gpart friend. + * + * @param bp The black hole particle. + * @param gp The black hole's #gpart. + */ +__attribute__((always_inline)) INLINE static void +black_holes_store_potential_in_bpart(struct bpart* bp, const struct gpart* gp) { + +#ifdef SWIFT_DEBUG_CHECKS + if (bp->gpart != gp) error("Copying potential to the wrong black hole!"); +#endif + + bp->reposition.potential = gp->potential; +} + +/** + * @brief Store the gravitational potential of a particle by copying it from + * its #gpart friend. + * + * @param p_data The black hole data of a gas particle. + * @param gp The black hole's #gpart. + */ +__attribute__((always_inline)) INLINE static void +black_holes_store_potential_in_part(struct black_holes_part_data* p_data, + const struct gpart* gp) { + p_data->potential = gp->potential; +} + +/** + * @brief Initialise a BH particle that has just been seeded. + * + * @param bp The #bpart to initialise. + * @param props The properties of the black hole scheme. + * @param constants The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param p The #part that became a black hole. + * @param xp The #xpart that became a black hole. + */ +INLINE static void black_holes_create_from_gas( + struct bpart* bp, const struct black_holes_props* props, + const struct phys_const* constants, const struct cosmology* cosmo, + const struct part* p, const struct xpart* xp, + const integertime_t ti_current) { + + /* All the non-basic properties of the black hole have been zeroed + * in the FOF code. We update them here. + * (i.e. position, velocity, mass, time-step have been set) */ + + /* Birth time and density */ + bp->formation_scale_factor = cosmo->a; + bp->formation_gas_density = hydro_get_physical_density(p, cosmo); + + /* Initial seed mass */ + bp->subgrid_mass = props->subgrid_seed_mass; + + /* We haven't accreted anything yet */ + bp->total_accreted_mass = 0.f; + bp->cumulative_number_seeds = 1; + bp->number_of_mergers = 0; + bp->number_of_gas_swallows = 0; + bp->number_of_direct_gas_swallows = 0; + bp->number_of_time_steps = 0; + + /* We haven't repositioned yet, nor attempted it */ + bp->number_of_repositions = 0; + bp->number_of_reposition_attempts = 0; + bp->last_repos_vel = 0.f; + + /* Copy over the splitting struct */ + bp->split_data = xp->split_data; + + /* Initial metal masses */ + const float gas_mass = hydro_get_mass(p); + struct chemistry_bpart_data* bp_chem = &bp->chemistry_data; + const struct chemistry_part_data* p_chem = &p->chemistry_data; + chemistry_bpart_from_part(bp_chem, p_chem, gas_mass); + + /* No swallowed angular momentum */ + bp->swallowed_angular_momentum[0] = 0.f; + bp->swallowed_angular_momentum[1] = 0.f; + bp->swallowed_angular_momentum[2] = 0.f; + + /* Last time of mergers */ + bp->last_minor_merger_time = -1.; + bp->last_major_merger_time = -1.; + + /* First initialisation */ + black_holes_init_bpart(bp); + + bp->state = BH_states_slim_disk; + + black_holes_mark_bpart_as_not_swallowed(&bp->merger_data); +} + +/** + * @brief Should this bh particle be doing any stars looping? + * + * @param bp The #bpart. + * @param e The #engine. + */ +__attribute__((always_inline)) INLINE static int bh_stars_loop_is_active( + const struct bpart* bp, const struct engine* e) { + /* Active bhs never do the stars loop for the Obsidian model */ + return 0; +} + +#endif /* SWIFT_OBSIDIAN_BLACK_HOLES_H */ diff --git a/src/black_holes/Obsidian/black_holes_debug.h b/src/black_holes/Obsidian/black_holes_debug.h new file mode 100644 index 0000000000..0fe0e6824e --- /dev/null +++ b/src/black_holes/Obsidian/black_holes_debug.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_BLACK_HOLES_OBSIDIAN_DEBUG_H +#define SWIFT_BLACK_HOLES_OBSIDIAN_DEBUG_H + +__attribute__((always_inline)) INLINE static void black_holes_debug_particle( + const struct part* p, const struct xpart* xp) { + + warning("[PID%lld] black_holes_part_data:", p->id); + warning("[PID%lld] swallow_id = %lld, potential = %.3e", p->id, + p->black_holes_data.swallow_id, p->black_holes_data.potential); +} + +#endif /* SWIFT_BLACK_HOLES_OBSIDIAN_DEBUG_H */ diff --git a/src/black_holes/Obsidian/black_holes_iact.h b/src/black_holes/Obsidian/black_holes_iact.h new file mode 100644 index 0000000000..d5ba074963 --- /dev/null +++ b/src/black_holes/Obsidian/black_holes_iact.h @@ -0,0 +1,1408 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2021 Edo Altamura (edoardo.altamura@manchester.ac.uk) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_OBSIDIAN_BH_IACT_H +#define SWIFT_OBSIDIAN_BH_IACT_H + +/* Local includes */ +#include "black_holes_parameters.h" +#include "black_holes_properties.h" +#include "entropy_floor.h" +#include "equation_of_state.h" +#include "gravity.h" +#include "gravity_iact.h" +#include "hydro.h" +#include "random.h" +#include "space.h" +#include "timestep_sync_part.h" +#include "tracers.h" + +//#define OBSIDIAN_DEBUG_CHECKS + +/** + * @brief Set direction of black hole feedback kick + * + * @param bi Black hole particle. + * @param pj Gas particle being kicked. + * @param ti_current Current integer time value (for random numbers). + * @param dir_flag Flag to choose direction: 0=rendom, 1=L_gas, 2=L_BH. + * @param dir Direction of kick (returned). + */ +__attribute__((always_inline)) INLINE static float +black_hole_set_kick_direction( + const struct bpart *bi, const struct part *pj, + const integertime_t ti_current, + const int dir_flag, float *dir) { + + float kick_dir = 1.f; + double random_number = 1.; + switch (dir_flag) { + /* Isotropic */ + case 0: + { + const double random_for_theta = + random_unit_interval(bi->id, ti_current, random_number_BH_feedback); + const double random_for_phi = + random_unit_interval(bi->id, ti_current, random_number_BH_feedback); + + const float theta = acosf(2.f * random_for_theta - 1.f); + const float phi = 2.f * M_PI * random_for_phi; + + dir[0] = sinf(theta) * cosf(phi); + dir[1] = sinf(theta) * sinf(phi); + dir[2] = cosf(theta); + break; + } + + /* Along the angular momentum vector of the gas */ + case 1: + dir[0] = bi->angular_momentum_gas[0]; + dir[1] = bi->angular_momentum_gas[1]; + dir[2] = bi->angular_momentum_gas[2]; + random_number = + random_unit_interval(bi->id, ti_current, random_number_BH_feedback); + kick_dir = (random_number > 0.5) ? 1.f : -1.f; + break; + + /* Along the accreted angular momentum of the gas */ + case 2: + dir[0] = + bi->accreted_angular_momentum[0] + bi->swallowed_angular_momentum[0]; + dir[1] = + bi->accreted_angular_momentum[1] + bi->swallowed_angular_momentum[1]; + dir[2] = + bi->accreted_angular_momentum[2] + bi->swallowed_angular_momentum[2]; + random_number = + random_unit_interval(bi->id, ti_current, random_number_BH_feedback); + kick_dir = (random_number > 0.5) ? 1.f : -1.f; + break; + + /* Outwards from BH*/ + case 3: + dir[0] = pj->x[0] - bi->x[0]; + dir[1] = pj->x[1] - bi->x[1]; + dir[2] = pj->x[2] - bi->x[2]; + break; + + default: + error("dir_flag=%d but must be 0, 1, or 2", dir_flag); + break; + } + + return kick_dir; +} + +/** + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas, not updated). + * @param xpj The extended data of the second particle (not updated). + * @param with_cosmology Are we doing a cosmological run? + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + * @param time Current physical time in the simulation. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_density( + const float r2, const float dx[3], const float hi, const float hj, + struct bpart *bi, const struct part *pj, const struct xpart *xpj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const struct entropy_floor_properties *floor_props, + const integertime_t ti_current, const double time) { + + /* Get the total gravitationally interacting mass within the kernel */ + const float mj = hydro_get_mass(pj); + + /* Compute total mass that contributes to the dynamical time */ + bi->gravitational_ngb_mass += mj; + bi->num_gravitational_ngbs += 1; + + float wi, wi_dx; + + /* Compute the kernel function; note that r cannot be optimised + * to r2 / sqrtf(r2) because of 1 / 0 behaviour. */ + const float r = sqrtf(r2); + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Contribution to the BH gas density */ + bi->rho_gas += mj * wi; + + /* Compute contribution to the number of neighbours */ + bi->density.wcount += wi; + bi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + + /* Contribution to the smoothed sound speed */ + const float cj = hydro_get_comoving_soundspeed(pj); + bi->sound_speed_gas += mj * wi * cj; + + /* Neighbour internal energy */ + const float uj = hydro_get_drifted_comoving_internal_energy(pj); + + /* Contribution to the smoothed internal energy */ + bi->internal_energy_gas += mj * uj * wi; + + /* weighting for feedback */ + bi->kernel_wt_sum += mj * wi; + + /* Contribution to the number of neighbours */ + bi->num_ngbs += 1; + + /* Contribution to the total neighbour mass */ + bi->ngb_mass += mj; + + /* Neighbour's (drifted) velocity in the frame of the black hole + * (we don't include a Hubble term since we are interested in the + * velocity contribution at the location of the black hole) */ + const float dv[3] = {pj->v[0] - bi->v[0], + pj->v[1] - bi->v[1], + pj->v[2] - bi->v[2]}; + + /* Account for hot and cold gas surrounding the SMBH */ + int is_hot_gas = 0; + const float Tj = + uj * cosmo->a_factor_internal_energy / bh_props->temp_to_u_factor; + /*const float rho_crit_0 = cosmo->critical_density_0; + const float rho_crit_baryon = cosmo->Omega_b * rho_crit_0; + const double rho_com = hydro_get_comoving_density(pj); + const double rho_phys = hydro_get_physical_density(pj, cosmo); + const float T_EoS = entropy_floor_temperature(pj, cosmo, floor_props); + if ((rho_com >= rho_crit_baryon * floor_props->Jeans_over_density_threshold) + && (rho_phys >= floor_props->Jeans_density_threshold)) { + if (Tj > T_EoS * bh_props->fixed_T_above_EoS_factor) { + is_hot_gas = 1; + } + } + else { + if (Tj > bh_props->environment_temperature_cut) { + is_hot_gas = 1; + } + }*/ + if (Tj > bh_props->environment_temperature_cut && pj->sf_data.SFR <= 0.f) { + is_hot_gas = 1; + } + + if (is_hot_gas) { + bi->hot_gas_mass += mj; + bi->hot_gas_internal_energy += mj * uj; /* Not kernel weighted */ + } + else { + bi->cold_gas_mass += mj; + bi->gas_SFR += max(pj->sf_data.SFR, 0.); + } + + const float L_x = mj * (dx[1] * dv[2] - dx[2] * dv[1]); + const float L_y = mj * (dx[2] * dv[0] - dx[0] * dv[2]); + const float L_z = mj * (dx[0] * dv[1] - dx[1] * dv[0]); + + /* Gas angular momentum in kernel */ + bi->angular_momentum_gas[0] -= L_x; + bi->angular_momentum_gas[1] -= L_y; + bi->angular_momentum_gas[2] -= L_z; + + /* Contribution to the velocity (gas w.r.t. black hole) */ + bi->velocity_gas[0] += mj * dv[0]; + bi->velocity_gas[1] += mj * dv[1]; + bi->velocity_gas[2] += mj * dv[2]; + + /* Contribution to the specific angular momentum of gas, which is later + * converted to the circular velocity */ + bi->circular_velocity_gas[0] -= L_x; + bi->circular_velocity_gas[1] -= L_y; + bi->circular_velocity_gas[2] -= L_z; + +#ifdef DEBUG_INTERACTIONS_BH + /* Update ngb counters */ + if (si->num_ngb_density < MAX_NUM_OF_NEIGHBOURS_BH) + bi->ids_ngbs_density[si->num_ngb_density] = pj->id; + + /* Update ngb counters */ + ++si->num_ngb_density; +#endif + +} + +/** + * @brief Repositioning interaction between two particles (non-symmetric). + * + * Function used to identify the gas particle that this BH may move towards. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas) + * @param xpj The extended data of the second particle. + * @param with_cosmology Are we doing a cosmological run? + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + * @param time Current physical time in the simulation. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_repos( + const float r2, const float dx[3], const float hi, const float hj, + struct bpart *bi, const struct part *pj, const struct xpart *xpj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const struct entropy_floor_properties *floor_props, + const integertime_t ti_current, const double time) { + + /* Ignore decoupled wind particles for repositioning BH */ + if (pj->decoupled) return; + + float wi; + + /* Compute the kernel function; note that r cannot be optimised + * to r2 / sqrtf(r2) because of 1 / 0 behaviour. */ + const float r = sqrtf(r2); + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + kernel_eval(ui, &wi); + + /* (Square of) Max repositioning distance allowed based on the softening */ + const float max_dist_repos2 = + kernel_gravity_softening_plummer_equivalent_inv * + kernel_gravity_softening_plummer_equivalent_inv * + bh_props->max_reposition_distance_ratio * + bh_props->max_reposition_distance_ratio * bi->h * bi->h; + + /* Is this gas neighbour close enough that we can consider its potential + for repositioning? */ + if (r2 < max_dist_repos2) { + + /* Flag to check whether neighbour is slow enough to be considered + * as repositioning target. Always true if velocity cut is switched off. */ + int neighbour_is_slow_enough = 1; + if (bh_props->with_reposition_velocity_threshold) { + + /* Compute relative peculiar velocity between the two BHs + * Recall that in SWIFT v is (v_pec * a) */ + const float delta_v[3] = {bi->v[0] - pj->v[0], bi->v[1] - pj->v[1], + bi->v[2] - pj->v[2]}; + const float v2 = delta_v[0] * delta_v[0] + delta_v[1] * delta_v[1] + + delta_v[2] * delta_v[2]; + const float v2_pec = v2 * cosmo->a2_inv; + + /* Compute the maximum allowed velocity */ + float v2_max = bh_props->max_reposition_velocity_ratio * + bh_props->max_reposition_velocity_ratio * + bi->sound_speed_gas * bi->sound_speed_gas * + cosmo->a_factor_sound_speed * cosmo->a_factor_sound_speed; + + /* If desired, limit the value of the threshold (v2_max) to be no + * smaller than a user-defined value */ + if (bh_props->min_reposition_velocity_threshold > 0) { + const float v2_min_thresh = + bh_props->min_reposition_velocity_threshold * + bh_props->min_reposition_velocity_threshold; + v2_max = max(v2_max, v2_min_thresh); + } + + /* Is the neighbour too fast to jump to? */ + if (v2_pec >= v2_max) neighbour_is_slow_enough = 0; + } + + if (neighbour_is_slow_enough) { + float potential = pj->black_holes_data.potential; + + if (bh_props->correct_bh_potential_for_repositioning) { + + /* Let's not include the contribution of the BH + * itself to the potential of the gas particle */ + + /* Note: This assumes the BH and gas have the same + * softening, which is currently true */ + const float eps = gravity_get_softening(bi->gpart, grav_props); + const float eps2 = eps * eps; + const float eps_inv = 1.f / eps; + const float eps_inv3 = eps_inv * eps_inv * eps_inv; + const float BH_mass = bi->mass; + + /* Compute the Newtonian or truncated potential the BH + * exherts onto the gas particle */ + float dummy, pot_ij; + runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, BH_mass, &dummy, + &pot_ij); + + /* Deduct the BH contribution */ + potential -= pot_ij * grav_props->G_Newton; + } + + /* Is the potential lower? */ + if (potential < bi->reposition.min_potential) { + + /* Store this as our new best */ + bi->reposition.min_potential = potential; + bi->reposition.delta_x[0] = -dx[0]; + bi->reposition.delta_x[1] = -dx[1]; + bi->reposition.delta_x[2] = -dx[2]; + } + } + } +} + +/** + * @brief Swallowing interaction between two particles (non-symmetric). + * + * Function used to flag the gas particles that will be swallowed + * by the black hole particle. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas) + * @param xpj The extended data of the second particle. + * @param with_cosmology Are we doing a cosmological run? + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + * @param time Current physical time in the simulation. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_swallow( + const float r2, const float dx[3], const float hi, const float hj, + struct bpart *bi, struct part *pj, struct xpart *xpj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const struct entropy_floor_properties *floor_props, + const integertime_t ti_current, const double time) { + + /* Collect information about galaxy that the particle belongs to */ + float galaxy_mstar = bi->galaxy_data.stellar_mass; + + /* A black hole should never accrete/feedback if it is not in a galaxy */ + if (galaxy_mstar <= 0.f) return; + + /* If there is no gas, skip */ + if (bi->ngb_mass <= 0.f) return; + + /* Gas particle must be bound to BH kernel mass to be swallowed */ + const float r = sqrtf(r2); + const float dv[3] = {bi->v[0] - pj->v[0], bi->v[1] - pj->v[1], + bi->v[2] - pj->v[2]}; + const float u_kinetic = 0.5 * (dv[0]*dv[0] + dv[1]*dv[1] + dv[2]*dv[2]); + const float u_potential = bh_props->const_newton_G * (bi->ngb_mass + bi->mass) / r; + + if (u_kinetic > u_potential) return; + + /* Compute the kernel function */ + float wi; + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + kernel_eval(ui, &wi); + + /* Sum up cold disk mass corotating relative to total angular momentum. */ + /* Neighbour internal energy */ + const float uj = hydro_get_drifted_comoving_internal_energy(pj); + const float mj = hydro_get_mass(pj); + + /* Identify gas surrounding the SMBH as hot or cold */ + const float Tj = + uj * cosmo->a_factor_internal_energy / bh_props->temp_to_u_factor; + int is_hot_gas = 0; + if (Tj > bh_props->environment_temperature_cut && pj->sf_data.SFR <= 0.) { + is_hot_gas = 1; + } + + /* Compute gas angular momentum around BH */ + const float Lx = mj * (dx[1] * dv[2] - dx[2] * dv[1]); + const float Ly = mj * (dx[2] * dv[0] - dx[0] * dv[2]); + const float Lz = mj * (dx[0] * dv[1] - dx[1] * dv[0]); + const float proj = Lx * bi->angular_momentum_gas[0] + + Ly * bi->angular_momentum_gas[1] + + Lz * bi->angular_momentum_gas[2]; + if ((proj > 0.f) && (is_hot_gas == 0)) { + bi->cold_disk_mass += mj; + } + + /* Probability to swallow this particle */ + float prob = -1.f; + float f_accretion = bi->f_accretion; + if (f_accretion <= 0.f) return; + + const float pj_mass_orig = mj; + const float nibbled_mass = f_accretion * pj_mass_orig; + + /* Normalize the weights */ + const float kernel_wt = + (bi->kernel_wt_sum > 0.f) ? wi / bi->kernel_wt_sum : 0.f; + + /* Radiation was already accounted for in bi->subgrid_mass + * so if is is bigger than bi->mass we can simply + * flag particles to eat and satisfy the mass constraint. + * + * If bi->subgrid_mass < bi->mass then there is a problem, + * but we use the full accretion rate to select particles + * and then don't actually take anything away from them. + * The bi->mass variable is decreased previously to account + * for the radiative losses. + */ + const float mass_deficit = bi->subgrid_mass - bi->mass_at_start_of_step; + if (mass_deficit > 0.f) { + /* Don't nibble from particles that are too small already */ + if (mj < bh_props->min_gas_mass_for_nibbling) return; + + /* Just enough to satisfy M_dot,inflow */ + prob = (mass_deficit / f_accretion) * kernel_wt; + } + else { + /* Do not grow the physical mass, only kick */ + f_accretion = 0.f; + + /* Check the accretion reservoir and if it has passed the limit */ + if (bi->unresolved_mass_reservoir > 0.f) { + prob = bi->unresolved_mass_reservoir * kernel_wt; + } + } + + /* Draw a random number (Note mixing both IDs) */ + const float rand = random_unit_interval(bi->id + pj->id, ti_current, + random_number_BH_swallow); + float new_gas_mass = pj_mass_orig; + /* Are we lucky? */ + if (rand < prob) { + + if (f_accretion > 0.f) { + const float bi_mass_orig = bi->mass; + new_gas_mass = pj_mass_orig - nibbled_mass; + /* Don't go below the minimum for stability */ + if (new_gas_mass < bh_props->min_gas_mass_for_nibbling) return; + + bi->mass += nibbled_mass; + hydro_set_mass(pj, new_gas_mass); + + /* Add the angular momentum of the accreted gas to the BH total. + * Note no change to gas here. The cosmological conversion factors for + * velocity (a^-1) and distance (a) cancel out, so the angular momentum + * is already in physical units. */ + bi->swallowed_angular_momentum[0] += + nibbled_mass * (dx[1] * dv[2] - dx[2] * dv[1]); + bi->swallowed_angular_momentum[1] += + nibbled_mass * (dx[2] * dv[0] - dx[0] * dv[2]); + bi->swallowed_angular_momentum[2] += + nibbled_mass * (dx[0] * dv[1] - dx[1] * dv[0]); + + /* Update the BH momentum and velocity. Again, no change to gas here. */ + const float bi_mom[3] = { + bi_mass_orig * bi->v[0] + nibbled_mass * pj->v[0], + bi_mass_orig * bi->v[1] + nibbled_mass * pj->v[1], + bi_mass_orig * bi->v[2] + nibbled_mass * pj->v[2]}; + + /* TODO: Spoke to Matthieu about this, it is a bug cannot assign here. */ + bi->v[0] = bi_mom[0] / bi->mass; + bi->v[1] = bi_mom[1] / bi->mass; + bi->v[2] = bi_mom[2] / bi->mass; + + /* Update the BH and also gas metal masses */ + struct chemistry_bpart_data *bi_chem = &bi->chemistry_data; + struct chemistry_part_data *pj_chem = &pj->chemistry_data; + chemistry_transfer_part_to_bpart(bi_chem, pj_chem, nibbled_mass, + nibbled_mass / pj_mass_orig); + } + + /* This particle is swallowed by the BH with the largest ID of all the + * candidates wanting to swallow it */ + if (pj->black_holes_data.swallow_id < bi->id) { + /* Handle the ADAF heating separately */ + if (bi->state != BH_states_adaf) { + pj->black_holes_data.swallow_id = bi->id; + } + + /* Keep track of unresolved mass kicks */ + if (mass_deficit <= 0.f) { + bi->unresolved_mass_kicked_this_step += new_gas_mass; + } + } + else { + message( + "BH %lld wants to swallow gas particle %lld BUT CANNOT (old " + "swallow id=%lld)", + bi->id, pj->id, pj->black_holes_data.swallow_id); + } + } + + /* When there is zero mass loading the ADAF mode heats the entire kernel + * so all of the weights are required in the sum. */ + if (bi->state == BH_states_adaf) { + /* Zero mass loading implies entire + kernel heating */ + if (bh_props->adaf_wind_mass_loading == 0.f) { + const float adaf_wt = new_gas_mass * wi; + bi->adaf_wt_sum += adaf_wt; + + /* Normalized later */ + bi->adaf_energy_used_this_step += bi->adaf_energy_to_dump * adaf_wt; + } + else { + /* --- The entire kernel is NOT heated here ---- */ + + /* Heating is based on specific energy and the mass loading */ + if (bi->adaf_energy_to_dump > 0.f && bh_props->adaf_wind_speed > 0.f) { + const float adaf_v2 = + bh_props->adaf_wind_speed * bh_props->adaf_wind_speed; + const float adaf_mass_to_heat = 2.f * bi->adaf_energy_to_dump / adaf_v2; + + /* Bernoulli trial P = M_adaf * wi / sum(mj * wj) */ + const float adaf_heat_prob = adaf_mass_to_heat * kernel_wt; + + /* Draw a random number (Note mixing both IDs) */ + const float adaf_rand = random_unit_interval(bi->id + pj->id, ti_current, + random_number_BH_swallow); + + /* Identify ADAF heating particles by their ID, and only heat those! */ + if (adaf_rand < adaf_heat_prob) { + if (pj->black_holes_data.adaf_id < bi->id) { + pj->black_holes_data.adaf_id = bi->id; + + /* New normalization for ADAF heating. Use new gas mass since that + * is what is in the feedback loop. */ + const float adaf_wt = new_gas_mass * wi; + bi->adaf_wt_sum += adaf_wt; + /* Will be normalized at the end when reducing the reservoir */ + bi->adaf_energy_used_this_step += bi->adaf_energy_to_dump * adaf_wt; + } + else { + message( + "BH %lld wants to heat particle %lld BUT CANNOT (old " + "adaf_id=%lld)", + bi->id, pj->id, pj->black_holes_data.adaf_id); + } + } + } + } + } + + /* Check jet reservoir regardless of the state, allows simultaneous + * ADAF heating and a kinetic jet. */ + if (bi->jet_mass_reservoir >= bh_props->jet_minimum_reservoir_mass) { + + /* Make sure there is enough gas to kick */ + if (bi->ngb_mass < bh_props->jet_minimum_reservoir_mass) return; + + float jet_prob = bi->jet_mass_reservoir * kernel_wt; + const float rand_jet = random_unit_interval(bi->id + pj->id, ti_current, + random_number_BH_kick); + /* Always kick if above luminosity threshold */ + if (bi->radiative_luminosity > bh_props->lum_thresh_always_jet + && bh_props->lum_thresh_always_jet > 0.f) { + jet_prob = 1.f; + } + + /* Here the particle is also identified to be kicked out as a jet */ + if (rand_jet < jet_prob) { + + /* If we also are accreting above, the mass loss is already taken + * into account */ + + if (pj->black_holes_data.jet_id < bi->id) { + bi->jet_mass_kicked_this_step += new_gas_mass; + pj->black_holes_data.jet_id = bi->id; + } + else { + message( + "BH %lld wants to kick jet particle %lld BUT CANNOT (old " + "jet_id=%lld)", + bi->id, pj->id, pj->black_holes_data.jet_id); + } + } + } +} + +/** + * @brief Swallowing interaction between two BH particles (non-symmetric). + * + * Function used to identify the BH particle that this BH may move towards. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param bj Second particle (black hole) + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_bh_repos(const float r2, const float dx[3], + const float hi, const float hj, struct bpart *bi, + const struct bpart *bj, + const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const integertime_t ti_current) { + + /* Compute relative peculiar velocity between the two BHs + * Recall that in SWIFT v is (v_pec * a) */ + const float delta_v[3] = {bi->v[0] - bj->v[0], bi->v[1] - bj->v[1], + bi->v[2] - bj->v[2]}; + const float v2 = delta_v[0] * delta_v[0] + delta_v[1] * delta_v[1] + + delta_v[2] * delta_v[2]; + + const float v2_pec = v2 * cosmo->a2_inv; + + /* (Square of) Max repositioning distance allowed based on the softening */ + const float max_dist_repos2 = + kernel_gravity_softening_plummer_equivalent_inv * + kernel_gravity_softening_plummer_equivalent_inv * + bh_props->max_reposition_distance_ratio * + bh_props->max_reposition_distance_ratio * bi->h * bi->h; + + /* Is this BH neighbour close enough that we can consider its potential + for repositioning? */ + if (r2 < max_dist_repos2) { + + /* Flag to check whether neighbour is slow enough to be considered + * as repositioning target. Always true if velocity cut switched off */ + int neighbour_is_slow_enough = 1; + if (bh_props->with_reposition_velocity_threshold) { + + /* Compute the maximum allowed velocity */ + float v2_max = bh_props->max_reposition_velocity_ratio * + bh_props->max_reposition_velocity_ratio * + bi->sound_speed_gas * bi->sound_speed_gas * + cosmo->a_factor_sound_speed * cosmo->a_factor_sound_speed; + + /* If desired, limit the value of the threshold (v2_max) to be no + * smaller than a user-defined value */ + if (bh_props->min_reposition_velocity_threshold > 0) { + const float v2_min_thresh = + bh_props->min_reposition_velocity_threshold * + bh_props->min_reposition_velocity_threshold; + v2_max = max(v2_max, v2_min_thresh); + } + + /* Is the neighbour too fast to jump to? */ + if (v2_pec >= v2_max) neighbour_is_slow_enough = 0; + } + + if (neighbour_is_slow_enough) { + float potential = bj->reposition.potential; + + if (bh_props->correct_bh_potential_for_repositioning) { + + /* Let's not include the contribution of the BH i + * to the potential of the BH j */ + const float eps = gravity_get_softening(bi->gpart, grav_props); + const float eps2 = eps * eps; + const float eps_inv = 1.f / eps; + const float eps_inv3 = eps_inv * eps_inv * eps_inv; + const float BH_mass = bi->mass; + + /* Compute the Newtonian or truncated potential the BH + * exherts onto the gas particle */ + float dummy, pot_ij; + runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, BH_mass, &dummy, + &pot_ij); + + /* Deduct the BH contribution */ + potential -= pot_ij * grav_props->G_Newton; + } + + /* Is the potential lower? */ + if (potential < bi->reposition.min_potential) { + + /* Store this as our new best */ + bi->reposition.min_potential = potential; + bi->reposition.delta_x[0] = -dx[0]; + bi->reposition.delta_x[1] = -dx[1]; + bi->reposition.delta_x[2] = -dx[2]; + } + } + } +} + +/** + * @brief Swallowing interaction between two BH particles (non-symmetric). + * + * Function used to flag the BH particles that will be swallowed + * by the black hole particle. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param bj Second particle (black hole) + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_bh_swallow(const float r2, const float dx[3], + const float hi, const float hj, + struct bpart *bi, struct bpart *bj, + const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const integertime_t ti_current) { + + /* Compute relative peculiar velocity between the two BHs + * Recall that in SWIFT v is (v_pec * a) */ + const float delta_v[3] = {bi->v[0] - bj->v[0], bi->v[1] - bj->v[1], + bi->v[2] - bj->v[2]}; + const float v2 = delta_v[0] * delta_v[0] + delta_v[1] * delta_v[1] + + delta_v[2] * delta_v[2]; + + const float v2_pec = v2 * cosmo->a2_inv; + + /* Find the most massive of the two BHs */ + float M = bi->subgrid_mass; + float h = hi; + if (bj->subgrid_mass > M) { + M = bj->subgrid_mass; + h = hj; + } + + /* (Square of) max swallowing distance allowed based on the softening */ + const float max_dist_merge2 = + kernel_gravity_softening_plummer_equivalent_inv * + kernel_gravity_softening_plummer_equivalent_inv * + bh_props->max_merging_distance_ratio * + bh_props->max_merging_distance_ratio * bi->h * bi->h; + + const float G_Newton = grav_props->G_Newton; + + /* The BH with the smaller mass will be merged onto the one with the + * larger mass. + * To avoid rounding issues, we additionally check for IDs if the BHs + * have the exact same mass. */ + if ((bj->subgrid_mass < bi->subgrid_mass) || + (bj->subgrid_mass == bi->subgrid_mass && bj->id < bi->id)) { + + /* Merge if gravitationally bound AND if within max distance + * Note that we use the kernel support here as the size and not just the + * smoothing length */ + + /* Maximum velocity difference between BHs allowed to merge */ + float v2_threshold; + + if (bh_props->merger_threshold_type == BH_mergers_circular_velocity) { + + /* 'Old-style' merger threshold using circular velocity at the + * edge of the more massive BH's kernel */ + v2_threshold = G_Newton * M / (kernel_gamma * h); + } else { + + /* Arguably better merger threshold using the escape velocity at + * the distance between the BHs */ + + if (bh_props->merger_threshold_type == BH_mergers_escape_velocity) { + + /* Standard formula (not softening BH interactions) */ + v2_threshold = 2.f * G_Newton * M / sqrt(r2); + } else if (bh_props->merger_threshold_type == + BH_mergers_dynamical_escape_velocity) { + + /* General two-body escape velocity based on dynamical masses */ + v2_threshold = 2.f * G_Newton * (bi->mass + bj->mass) / sqrt(r2); + } else { + /* Cannot happen! */ +#ifdef SWIFT_DEBUG_CHECKS + error("Invalid choice of BH merger threshold type"); +#endif + v2_threshold = 0.f; + } + } /* Ends sections for different merger thresholds */ + + if ((v2_pec < v2_threshold) && (r2 < max_dist_merge2)) { + + /* This particle is swallowed by the BH with the largest mass of all the + * candidates wanting to swallow it (we use IDs to break ties)*/ + if ((bj->merger_data.swallow_mass < bi->subgrid_mass) || + (bj->merger_data.swallow_mass == bi->subgrid_mass && + bj->merger_data.swallow_id < bi->id)) { + +#ifdef SWIFT_DEBUG_CHECKS + message("BH %lld wants to swallow BH particle %lld", bi->id, bj->id); +#endif + + bj->merger_data.swallow_id = bi->id; + bj->merger_data.swallow_mass = bi->subgrid_mass; + + } else { + + message( + "BH %lld wants to swallow bh particle %lld BUT CANNOT (old " + "swallow id=%lld)", + bi->id, bj->id, bj->merger_data.swallow_id); + } + } + } +} + +/** + * @brief Feedback interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param bi First particle (black hole). + * @param pj Second particle (gas) + * @param xpj The extended data of the second particle. + * @param with_cosmology Are we doing a cosmological run? + * @param cosmo The cosmological model. + * @param grav_props The properties of the gravity scheme (softening, G, ...). + * @param bh_props The properties of the BH scheme + * @param ti_current Current integer time value (for random numbers). + * @param time current physical time in the simulation + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_bh_gas_feedback( + const float r2, const float dx[3], const float hi, const float hj, + const struct bpart *bi, struct part *pj, struct xpart *xpj, + const int with_cosmology, const struct cosmology *cosmo, + const struct gravity_props *grav_props, + const struct black_holes_props *bh_props, + const struct entropy_floor_properties *floor_props, + const integertime_t ti_current, const double time) { + + /* Gas particle must be bound to BH kernel mass to have feedback + * (avoids fast-moving decoupled particles) */ + const float r = sqrtf(r2); + const float dv[3] = {bi->v[0] - pj->v[0], bi->v[1] - pj->v[1], + bi->v[2] - pj->v[2]}; + const float u_kinetic = 0.5 * (dv[0]*dv[0] + dv[1]*dv[1] + dv[2]*dv[2]); + const float u_potential = bh_props->const_newton_G * (bi->ngb_mass + bi->mass) / r; + + if (u_kinetic > u_potential) return; + + /* Collect information about galaxy that the particle belongs to */ + const float galaxy_mstar = bi->galaxy_data.stellar_mass; + + /* A black hole should never accrete/feedback if it is not in a galaxy */ + if (galaxy_mstar <= 0.f) return; + + /* A black hole should have gas surrounding it. */ + if (bi->ngb_mass <= 0.f) return; + + /* Need time-step for decoupling and ADAF heating */ + double dt; + if (with_cosmology) { + const integertime_t ti_step = get_integer_timestep(bi->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current - 1, bi->time_bin); + + dt = cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + } + else { + error("Kiara BH model can only be run with cosmology."); + } + + /* Save gas density and entropy before feedback */ + tracers_before_black_holes_feedback(pj, xpj, cosmo->a); + + float v_kick = bi->v_kick; /* PHYSICAL */ + + const float bh_mass_msun = + bi->subgrid_mass * bh_props->mass_to_solar_mass; + + /* by-eye fit from Fig 6 of Zhang+2023 (2305.06803) */ + double halo_mass = 1.e12 * pow(bh_mass_msun * 1.e-7, 0.75); + if (halo_mass < 6.3e11) halo_mass = 6.3e11; + + /* In internal temperature units */ + const double T_vir = + 9.52e7 * pow(halo_mass * 1.e-15, 0.6666) * + bh_props->T_K_to_int * (1. + cosmo->z); + + /* In the swallow loop the particle was marked as a kick particle */ + const int swallow_flag = (pj->black_holes_data.swallow_id == bi->id); + + /* Can get reset if there is a adaf_kick_factor > 0 and heating */ + int adaf_kick_flag = 0; + + /* Initialize heating */ + const double u_init = hydro_get_physical_internal_energy(pj, xpj, cosmo); + double E_heat = 0.; + double E_inject = 0.; + double u_new = u_init; + double T_new = u_new / bh_props->temp_to_u_factor; + + int adaf_energy_flag = + (bi->state == BH_states_adaf && bi->adaf_energy_to_dump > 0.f); + int adaf_heat_flag = + (bi->state == BH_states_adaf && pj->black_holes_data.adaf_id == bi->id); + + /* In the case of non-zero mass loading, require only certain particles + * to be heated to satisfy M_dot,ADAF = psi_ADAF * M_dot,acc */ + if (bh_props->adaf_wind_mass_loading > 0.f) { + adaf_heat_flag = (adaf_energy_flag && adaf_heat_flag); + } + else { + /* In this case, all of the kernel is heated with adaf_energy_to_dump */ + adaf_heat_flag = adaf_energy_flag; + } + + /* In the swallow loop the particle was marked as a jet particle */ + int jet_flag = (pj->black_holes_data.jet_id == bi->id); + + /* Compute ramp-up in energy above ADAF mass limit */ + float jet_ramp = black_hole_compute_jet_energy_ramp(bi, cosmo, bh_props); + + /* ADAF heating: Only heat this particle if it is NOT a jet particle */ + if (adaf_heat_flag && !jet_flag) { + + /* compute kernel weights */ + float wj; + kernel_eval(sqrtf(r2) / hi, &wj); + const float mj = hydro_get_mass(pj); + + /* Below is equivalent to + * E_inject_i = E_ADAF * (w_j * m_j) / Sum(w_i * mi) */ + E_inject = bi->adaf_energy_to_dump * mj * wj / bi->adaf_wt_sum; + + /* Initialise heat energy to injection energy */ + E_heat = E_inject; + + /* Heat and/or kick the particle */ + if (E_inject > 0.f) { + + const double n_H_cgs = + hydro_get_physical_density(pj, cosmo) * bh_props->rho_to_n_cgs; + const double T_gas_cgs = + u_init / (bh_props->temp_to_u_factor * bh_props->T_K_to_int); + const double T_EoS_cgs = + entropy_floor_temperature(pj, cosmo, floor_props) / + bh_props->T_K_to_int; + + /* Check whether we are close to the entropy floor or SF/ing. If we are, + * we classify the gas as cold regardless of temperature. */ + if ((n_H_cgs > bh_props->adaf_heating_n_H_threshold_cgs && + (T_gas_cgs < bh_props->adaf_heating_T_threshold_cgs || + T_gas_cgs < T_EoS_cgs * bh_props->fixed_T_above_EoS_factor)) || + pj->sf_data.SFR > 0.f) { + + /* Kick with some fraction of the energy, if desired */ + if (bh_props->adaf_kick_factor > 0.f) { + + /* Compute kick velocity */ + double E_kick = bh_props->adaf_kick_factor * E_inject; + v_kick = sqrt(2. * E_kick / mj); + + /* Apply ramp-up in kick velocity above ADAF mass limit */ + const float adaf_max_speed = + bh_props->adaf_wind_speed * sqrtf(jet_ramp); + + /* Limit kick energy if velocity exceeds max */ + if (v_kick > adaf_max_speed) { + v_kick = adaf_max_speed; + E_kick = 0.5 * mj * v_kick * v_kick; + } + + /* Reduce energy available to heat */ + E_heat = E_inject - E_kick; + + /* Later will apply velocity as if it was flagged to swallow */ + adaf_kick_flag = 1; + + } /* adaf_kick_factor > 0 */ + + } /* If in ISM */ + + /* Heat gas with remaining energy, if any */ + if (E_heat > 0.) { + + /* Compute new energy per unit mass of this particle */ + u_new = u_init + E_heat / mj; + + /* New temperature */ + T_new = u_new / bh_props->temp_to_u_factor; + + /* Limit heating. There can sometimes be VERY large amounts of + * energy to deposit */ + if (bh_props->adaf_maximum_temperature > 0.f) { + if (T_new > bh_props->adaf_maximum_temperature) { + u_new = + bh_props->adaf_maximum_temperature * bh_props->temp_to_u_factor; + } + } + else { + const float T_max = + fabs(bh_props->adaf_maximum_temperature) * T_vir; + if (T_new > T_max) { + u_new = T_max * bh_props->temp_to_u_factor; + T_new = T_max; + } + } + + /* Reset in case clipped at the upper limit */ + E_heat = (u_new - u_init) * mj; + + /* Heat particle: We are overwriting the internal energy of the + * particle */ + hydro_set_physical_internal_energy(pj, xpj, cosmo, u_new); + hydro_set_drifted_physical_internal_energy(pj, cosmo, NULL, u_new); + + /* Shut off cooling for some time, if desired */ + if (bh_props->adaf_cooling_shutoff_factor > 0.f) { + + /* u_init is physical so cs_physical is physical */ + const double u_com = u_new / cosmo->a_factor_internal_energy; + const double cs = gas_soundspeed_from_internal_energy(pj->rho, u_com); + + const float h_phys = kernel_gamma * pj->h * cosmo->a; + const float cs_physical = cs * cosmo->a_factor_sound_speed; + const float dt_sound_phys = h_phys / cs_physical; + + /* a_factor_sound_speed converts cs_physical to comoving units, + * twice the BH timestep as a lower limit */ + pj->feedback_data.cooling_shutoff_delay_time = + bh_props->adaf_cooling_shutoff_factor * min(dt_sound_phys, dt); + } + + } /* E_heat > 0 */ + + } /* E_inject > 0 */ + + } /* non-jet ADAF mode */ + + /* ----- If particle is marked as a jet, do jet feedback ----- */ + + /* Heat the particle and set kinetic kick information if jet particle */ + if (jet_flag) { + + /* Set jet velocity, accounting for energy ramp-up */ + v_kick = black_hole_compute_jet_velocity(bi, cosmo, bh_props); + v_kick *= sqrtf(jet_ramp); + + /* Heat jet particle */ + float new_Tj = bh_props->jet_temperature; + + /* Use the halo T_vir? */ + if (bh_props->jet_temperature < 0.f) { + new_Tj = fabs(bh_props->jet_temperature) * T_vir; + } + + /* Compute new energy per unit mass of this particle */ + u_new = new_Tj * bh_props->temp_to_u_factor; + + /* Only increase the gas temperature if it's below the target T */ + if (u_new > u_init) { + /* account for energy ramp-up */ + u_new = jet_ramp * (u_new - u_init) + u_init; + /* We are overwriting the internal energy of the particle */ + hydro_set_physical_internal_energy(pj, xpj, cosmo, u_new); + hydro_set_drifted_physical_internal_energy(pj, cosmo, NULL, u_new); + + const double delta_energy = (u_new - u_init) * hydro_get_mass(pj); + E_heat += delta_energy; + + tracers_after_black_holes_feedback(pj, xpj, with_cosmology, cosmo->a, + time, delta_energy); + } + + } /* jet_flag */ + +#ifdef OBSIDIAN_DEBUG_CHECKS + float pj_vel_norm = FLT_MAX; +#endif + + /* Flagged if it is a jet particle, marked to swallow (i.e. kick) or + * if there was an ADAF kick because of energy splitting. */ + int flagged_to_kick = + jet_flag || (swallow_flag && !adaf_heat_flag) || adaf_kick_flag; + + /* Kick the particle if is was tagged only */ + if (v_kick > 0.f && flagged_to_kick) { + + /* Set direction of launch: 0=random, 1=L_gas, 2=L_BH, 3=outwards */ + float dir[3] = {0.f, 0.f, 0.f}; + int dir_flag = 0; + if (jet_flag) { + dir_flag = bh_props->jet_launch_dir; + } + else if (adaf_heat_flag) { + dir_flag = bh_props->adaf_wind_dir; + } + else if (bi->state == BH_states_quasar) { + dir_flag = bh_props->quasar_wind_dir; + if (bi->radiative_luminosity > bh_props->quasar_luminosity_thresh && bh_props->quasar_luminosity_thresh > 0.f) { + dir_flag = 3; // outwards blowout above threshold luminosity + } + } + else if (bi->state == BH_states_slim_disk) { + dir_flag = bh_props->slim_disk_wind_dir; + } + else { + warning("Cannot determine wind direction (BH state=%d) for v_kick=%g, setting to random", bi->state, v_kick / bh_props->kms_to_internal); + } + + float dirsign = + black_hole_set_kick_direction(bi, pj, ti_current, dir_flag, dir); + + /* Do the kick */ + const float norm = + sqrtf(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]); + + if (norm > 0.f) { + const float prefactor = v_kick * cosmo->a * dirsign / norm; + +#ifdef OBSIDIAN_DEBUG_CHECKS + pj_vel_norm = sqrtf( + xpj->v_full[0] * xpj->v_full[0] + + xpj->v_full[1] * xpj->v_full[1] + + xpj->v_full[2] * xpj->v_full[2] + ); +#endif + + xpj->v_full[0] += prefactor * dir[0]; + xpj->v_full[1] += prefactor * dir[1]; + xpj->v_full[2] += prefactor * dir[2]; + +#ifdef OBSIDIAN_DEBUG_CHECKS + const float v_mag = sqrtf(xpj->v_full[0] * xpj->v_full[0] + + xpj->v_full[1] * xpj->v_full[1] + + xpj->v_full[2] * xpj->v_full[2]); + + if (prefactor * norm > 1.e3 * v_mag) { + warning("LARGE KICK! z=%g id=%lld dv=%g vkick=%g vadaf=%g vjet=%g v=%g " + "(%g,%g,%g) dir=%g,%g,%g", + cosmo->z, + pj->id, + prefactor, + v_kick, + bh_props->adaf_wind_speed, + bh_props->jet_velocity, + v_mag, + xpj->v_full[0], + xpj->v_full[1], + xpj->v_full[2], dir[0], dir[1], dir[2]); + } +#endif + + /* Update the signal velocity of the particle based + * on the PHYSICAL velocity kick. */ + hydro_set_v_sig_based_on_velocity_kick(pj, cosmo, v_kick); + pj->chemistry_data.diffusion_coefficient = 0.f; + + float f_decouple = 0.f; + switch (bi->state) { + case BH_states_adaf: + f_decouple = bh_props->adaf_decouple_time_factor; + break; + case BH_states_quasar: + f_decouple = bh_props->quasar_decouple_time_factor; + break; + case BH_states_slim_disk: + f_decouple = bh_props->slim_disk_decouple_time_factor; + break; + } + + /* Hubble time in internal units */ + const double t_H = + cosmology_get_time_since_big_bang(cosmo, cosmo->a); + + /* Set delay time to at least the time-step*/ + pj->feedback_data.decoupling_delay_time = dt + f_decouple * t_H; + pj->decoupled = 1; + + /* Count number of decouplings */ + if (jet_flag) { + if (bh_props->jet_decouple_time_factor > 0.f) { + pj->feedback_data.decoupling_delay_time = + dt + bh_props->jet_decouple_time_factor * t_H; + } + else { + pj->feedback_data.decoupling_delay_time = 0.f; + } + pj->feedback_data.number_of_times_decoupled += 100000; + } + else { + if ((bh_props->slim_disk_decouple_time_factor > 0.f && + bi->state == BH_states_slim_disk) || + (bh_props->quasar_decouple_time_factor > 0.f && + bi->state == BH_states_quasar)) { + pj->feedback_data.decoupling_delay_time = + dt + bh_props->slim_disk_decouple_time_factor * t_H; + } + else { + pj->feedback_data.decoupling_delay_time = 0.f; + } + pj->feedback_data.number_of_times_decoupled += 1000; + } + } + else { + v_kick = 0.f; + } + } + + /* This particle was touched by BH feedback, so reset some variables */ + if ((v_kick > 0.f && flagged_to_kick) || E_heat > 0.f) { + /* set SFR=0 for BH feedback particle */ + if (pj->sf_data.SFR > 0.f) { + /* Record the current time as an indicator of when this particle was last + star-forming. */ + if (with_cosmology) { + pj->sf_data.SFR = -cosmo->a; + } else { + pj->sf_data.SFR = -time; + } + } + + /* Destroy all H2 and put into HI */ + xpj->cooling_data.HI_frac += xpj->cooling_data.HM_frac + + xpj->cooling_data.H2I_frac + + xpj->cooling_data.H2II_frac; + xpj->cooling_data.HM_frac = 0.f; + xpj->cooling_data.H2I_frac = 0.f; + xpj->cooling_data.H2II_frac = 0.f; + + /* Only take it out of ISM mode if it was kicked */ + if (v_kick > 0.f && flagged_to_kick) { + /* Take particle out of subgrid ISM mode */ + pj->cooling_data.subgrid_temp = 0.f; + pj->cooling_data.subgrid_dens = hydro_get_physical_density(pj, cosmo); + pj->cooling_data.subgrid_fcold = 0.f; + } + + /* Destroy all dust in ADAF-"touched" gas and the jet */ + if (jet_flag || E_inject > 0.) { +#if COOLING_GRACKLE_MODE >= 2 + const float old_dust_mass = pj->cooling_data.dust_mass; + pj->cooling_data.dust_mass = 0.f; + float new_Z_total = 0.f; + pj->chemistry_data.metal_mass_fraction_total = 0.f; + for (int elem = chemistry_element_He; + elem < chemistry_element_count; ++elem) { + const float old_metal_mass_elem = + pj->chemistry_data.metal_mass_fraction[elem] * hydro_get_mass(pj); + const float old_dust_mass_elem = + pj->cooling_data.dust_mass_fraction[elem] * old_dust_mass; + + pj->chemistry_data.metal_mass_fraction[elem] = + (old_metal_mass_elem + old_dust_mass_elem) / hydro_get_mass(pj); + + if (elem != chemistry_element_H && elem != chemistry_element_He) { + new_Z_total += pj->chemistry_data.metal_mass_fraction[elem]; + } + + pj->cooling_data.dust_mass_fraction[elem] = 0.f; + } + + pj->chemistry_data.metal_mass_fraction_total = new_Z_total; +#endif + } + + /* Impose maximal viscosity */ + hydro_diffusive_feedback_reset(pj); + + /* Synchronize the particle on the timeline */ + timestep_sync_part(pj); + +#ifdef OBSIDIAN_DEBUG_CHECKS + if (E_heat > 0.f) { + message("BH_HEAT_ADAF: z=%g bid=%lld pid=%lld mbh=%g Msun u=%g T=%g K " + "Tvir=%g K", + cosmo->z, + bi->id, + pj->id, + bh_mass_msun, + pj->u, + T_new / bh_props->T_K_to_int, + T_vir / bh_props->T_K_to_int); + } + + switch (bi->state) { + case BH_states_quasar: + message("BH_KICK_QSO: z=%g bid=%lld mbh=%g Msun v_kick=%g km/s " + "v_kick/v_part=%g T=%g K", + cosmo->z, + bi->id, + bh_mass_msun, + v_kick / bh_props->kms_to_internal, + v_kick * cosmo->a / pj_vel_norm, + hydro_get_physical_internal_energy(pj, xpj, cosmo) / + (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); + break; + case BH_states_slim_disk: + message("BH_KICK_SLIM: z=%g bid=%lld mbh=%g Msun v_kick=%g km/s T=%g K", + cosmo->z, + bi->id, + bh_mass_msun, + v_kick / bh_props->kms_to_internal, + hydro_get_physical_internal_energy(pj, xpj, cosmo) / + (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); + break; + case BH_states_adaf: + if (jet_flag) { + message("BH_KICK_JET: z=%g bid=%lld mbh=%g Msun v_kick=%g km/s " + "v_kick/v_part=%g T=%g", + cosmo->z, + bi->id, + bh_mass_msun, + v_kick / bh_props->kms_to_internal, + v_kick * cosmo->a / pj_vel_norm, + hydro_get_physical_internal_energy(pj, xpj, cosmo) / + (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); + } + else { + message("BH_KICK_ADAF: z=%g bid=%lld pid=%lld mbh=%g Msun " + "v_kick=%g km/s " + "v_kick/v_part=%g u=%g T=%g", + cosmo->z, + bi->id, + pj->id, + bh_mass_msun, + v_kick / bh_props->kms_to_internal, + v_kick * cosmo->a / pj_vel_norm, + pj->u, + hydro_get_physical_internal_energy(pj, xpj, cosmo) / + (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); + } + break; + } +#endif + + } + + if (swallow_flag) { + /* IMPORTANT: The particle MUST NOT be swallowed. + * We are taking a f_accretion from each particle, and then + * kicking the rest. We used the swallow marker as a temporary + * passer in order to remember which particles have been "nibbled" + * so that we can kick them out. + */ + black_holes_mark_part_as_not_swallowed(&pj->black_holes_data); + } +} + +#endif /* SWIFT_OBSIDIAN_BH_IACT_H */ diff --git a/src/black_holes/Obsidian/black_holes_io.h b/src/black_holes/Obsidian/black_holes_io.h new file mode 100644 index 0000000000..a5b91d64e2 --- /dev/null +++ b/src/black_holes/Obsidian/black_holes_io.h @@ -0,0 +1,459 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_OBSIDIAN_BLACK_HOLES_IO_H +#define SWIFT_OBSIDIAN_BLACK_HOLES_IO_H + +#include "adiabatic_index.h" +#include "black_holes_part.h" +#include "black_holes_properties.h" +#include "io_properties.h" +#include "kick.h" + +/** + * @brief Specifies which b-particle fields to read from a dataset + * + * @param bparts The b-particle array. + * @param list The list of i/o properties to read. + * @param num_fields The number of i/o fields to read. + */ +INLINE static void black_holes_read_particles(struct bpart* bparts, + struct io_props* list, + int* num_fields) { + + int num = 0; + + /* List what we want to read */ + list[num] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, + UNIT_CONV_LENGTH, bparts, x); + num++; + + list[num] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, + UNIT_CONV_SPEED, bparts, v); + num++; + + list[num] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + bparts, mass); + num++; + + list[num] = io_make_input_field("ParticleIDs", LONGLONG, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, bparts, id); + num++; + + list[num] = io_make_input_field("SmoothingLengths", FLOAT, 1, OPTIONAL, + UNIT_CONV_LENGTH, bparts, h); + num++; + + list[num] = io_make_input_field("JetMassReservoirs", FLOAT, 1, OPTIONAL, + UNIT_CONV_MASS, bparts, jet_mass_reservoir); + num++; + + list[num] = io_make_input_field("SubgridMasses", FLOAT, 1, OPTIONAL, + UNIT_CONV_MASS, bparts, subgrid_mass); + num++; + + *num_fields = num; + +} + +INLINE static void convert_bpart_pos(const struct engine* e, + const struct bpart* bp, double* ret) { + + const struct space* s = e->s; + if (s->periodic) { + ret[0] = box_wrap(bp->x[0], 0.0, s->dim[0]); + ret[1] = box_wrap(bp->x[1], 0.0, s->dim[1]); + ret[2] = box_wrap(bp->x[2], 0.0, s->dim[2]); + } else { + ret[0] = bp->x[0]; + ret[1] = bp->x[1]; + ret[2] = bp->x[2]; + } + if (e->snapshot_use_delta_from_edge) { + ret[0] = min(ret[0], s->dim[0] - e->snapshot_delta_from_edge); + ret[1] = min(ret[1], s->dim[1] - e->snapshot_delta_from_edge); + ret[2] = min(ret[2], s->dim[2] - e->snapshot_delta_from_edge); + } +} + +INLINE static void convert_bpart_vel(const struct engine* e, + const struct bpart* bp, float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + const float dt_kick_grav_mesh = e->dt_kick_grav_mesh_for_io; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, bp->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, bp->time_bin); + + /* Get time-step since the last kick */ + const float dt_kick_grav = + kick_get_grav_kick_dt(ti_beg, ti_current, time_base, with_cosmology, + cosmo) - + kick_get_grav_kick_dt(ti_beg, (ti_beg + ti_end) / 2, time_base, + with_cosmology, cosmo); + + /* Extrapolate the velocites to the current time */ + const struct gpart* gp = bp->gpart; + ret[0] = gp->v_full[0] + gp->a_grav[0] * dt_kick_grav; + ret[1] = gp->v_full[1] + gp->a_grav[1] * dt_kick_grav; + ret[2] = gp->v_full[2] + gp->a_grav[2] * dt_kick_grav; + + /* Extrapolate the velocites to the current time (mesh forces) */ + ret[0] += gp->a_grav_mesh[0] * dt_kick_grav_mesh; + ret[1] += gp->a_grav_mesh[1] * dt_kick_grav_mesh; + ret[2] += gp->a_grav_mesh[2] * dt_kick_grav_mesh; + + /* Conversion from internal to physical units */ + ret[0] *= cosmo->a_inv; + ret[1] *= cosmo->a_inv; + ret[2] *= cosmo->a_inv; +} + +INLINE static void convert_bpart_potential(const struct engine* e, + const struct bpart* bp, float* ret) { + + if (bp->gpart != NULL) + ret[0] = gravity_get_comoving_potential(bp->gpart); + else + ret[0] = 0.f; +} + +INLINE static void convert_bpart_gas_vel(const struct engine* e, + const struct bpart* bp, float* ret) { + + const struct cosmology* cosmo = e->cosmology; + + /* Convert relative velocities to physical units */ + ret[0] = bp->velocity_gas[0] * cosmo->a_inv; + ret[1] = bp->velocity_gas[1] * cosmo->a_inv; + ret[2] = bp->velocity_gas[2] * cosmo->a_inv; +} + +INLINE static void convert_bpart_gas_circular_vel(const struct engine* e, + const struct bpart* bp, + float* ret) { + + const struct cosmology* cosmo = e->cosmology; + + /* Conversion from internal to physical units */ + ret[0] = bp->circular_velocity_gas[0] * cosmo->a_inv; + ret[1] = bp->circular_velocity_gas[1] * cosmo->a_inv; + ret[2] = bp->circular_velocity_gas[2] * cosmo->a_inv; +} + +INLINE static void convert_bpart_gas_temperatures(const struct engine* e, + const struct bpart* bp, + float* ret) { + + const struct black_holes_props* props = e->black_holes_properties; + const struct cosmology* cosmo = e->cosmology; + + /* Conversion from specific internal energy to temperature */ + ret[0] = bp->internal_energy_gas * cosmo->a_factor_internal_energy / + props->temp_to_u_factor; +} + +/** + * @brief Specifies which b-particle fields to write to a dataset + * + * @param bparts The b-particle array. + * @param list The list of i/o properties to write. + * @param num_fields The number of i/o fields to write. + * @param with_cosmology Are we running a cosmological simulation? + */ +INLINE static void black_holes_write_particles(const struct bpart* bparts, + struct io_props* list, + int* num_fields, + int with_cosmology) { + + int num = 0; + + /* List what we want to write */ + list[num] = io_make_output_field_convert_bpart( + "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, bparts, + convert_bpart_pos, "Co-moving position of the particles"); + num++; + + list[num] = io_make_output_field_convert_bpart( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts, convert_bpart_vel, + "Peculiar velocities of the particles. This is a * dx/dt where x is the " + "co-moving position of the particles."); + num++; + + list[num] = + io_make_output_field("DynamicalMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + bparts, mass, "Dynamical masses of the particles"); + num++; + + list[num] = + io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f, + bparts, id, "Unique ID of the particles"); + num++; + + list[num] = io_make_output_field( + "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, bparts, h, + "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); + num++; + + list[num] = io_make_output_field("SubgridMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, + bparts, subgrid_mass, + "Subgrid masses of the particles; this is the actual BH mass"); + num++; + + if (with_cosmology) { + list[num] = io_make_output_field( + "FormationScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + formation_scale_factor, "Scale-factors at which the BHs were formed"); + } else { + list[num] = io_make_output_field("FormationTimes", FLOAT, 1, UNIT_CONV_TIME, + 0.f, bparts, formation_time, + "Times at which the BHs were formed"); + } + num++; + + list[num] = io_make_output_field( + "GasDensities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, bparts, rho_gas, + "Co-moving densities of the gas around the particles"); + num++; + + list[num] = io_make_output_field( + "GasSoundSpeeds", FLOAT, 1, UNIT_CONV_SPEED, + -1.5f * hydro_gamma_minus_one, bparts, sound_speed_gas, + "Co-moving sound-speeds of the gas around the particles"); + num++; + + list[num] = io_make_output_field( + "JetMassReservoirs", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts, + jet_mass_reservoir, + "Physcial mass contained in the jet reservoir of the particles"); + num++; + + list[num] = io_make_output_field( + "UnresolvedMassReservoirs", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts, + unresolved_mass_reservoir, + "Physcial mass contained in the unresolved reservoir of the particles"); + num++; + + list[num] = io_make_output_field( + "AccretionRates", FLOAT, 1, UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, bparts, + accretion_rate, + "Physical instantaneous accretion rates of the particles"); + num++; + + list[num] = io_make_output_field( + "BondiAccretionRates", FLOAT, 1, UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, bparts, + bondi_accretion_rate, + "Physical instantaneous Bondi accretion rates of the particles"); + num++; + + list[num] = io_make_output_field( + "TotalAccretedMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts, + total_accreted_mass, + "Total mass accreted onto the particles since its birth"); + num++; + + list[num] = io_make_output_field( + "CumulativeNumberOfSeeds", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + cumulative_number_seeds, + "Total number of BH seeds that have merged into this black hole"); + num++; + + list[num] = + io_make_output_field("NumberOfMergers", INT, 1, UNIT_CONV_NO_UNITS, 0.f, + bparts, number_of_mergers, + "Number of mergers the black holes went through. " + "This does not include the number of mergers " + "accumulated by any merged black hole."); + num++; + + if (with_cosmology) { + list[num] = io_make_output_field( + "LastMinorMergerScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, + bparts, last_minor_merger_scale_factor, + "Scale-factors at which the black holes last had a minor merger."); + } else { + list[num] = io_make_output_field( + "LastMinorMergerTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, + last_minor_merger_time, + "Times at which the black holes last had a minor merger."); + } + num++; + + if (with_cosmology) { + list[num] = io_make_output_field( + "LastMajorMergerScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, + bparts, last_major_merger_scale_factor, + "Scale-factors at which the black holes last had a major merger."); + } else { + list[num] = io_make_output_field( + "LastMajorMergerTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, + last_major_merger_time, + "Times at which the black holes last had a major merger."); + } + num++; + + list[num] = io_make_output_field( + "SwallowedAngularMomenta", FLOAT, 3, UNIT_CONV_ANGULAR_MOMENTUM, 0.f, + bparts, swallowed_angular_momentum, + "Physical angular momenta that the black holes have accumulated by " + "swallowing gas particles."); + num++; + + list[num] = io_make_output_field_convert_bpart( + "GasRelativeVelocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts, + convert_bpart_gas_vel, + "Peculiar relative velocities of the gas particles around the black " + "holes. This is a * dx/dt where x is the co-moving position of the " + "particles."); + num++; + + list[num] = io_make_output_field_convert_bpart( + "GasCircularVelocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts, + convert_bpart_gas_circular_vel, + "Circular velocities of the gas around the black hole at the " + "smoothing radius. This is j / h_BH, where j is the smoothed, peculiar " + "specific angular momentum of gas around the black holes, and h_BH is " + "the smoothing length of each black hole."); + num++; + + list[num] = + io_make_output_field("TimeBins", CHAR, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + time_bin, "Time-bins of the particles"); + num++; + + list[num] = io_make_output_field( + "NumberOfSwallows", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + number_of_gas_swallows, + "Number of gas particles the black holes have swallowed. " + "This includes the particles swallowed by any of the black holes that " + "merged into this one."); + num++; + + list[num] = io_make_output_field( + "NumberOfDirectSwallows", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + number_of_direct_gas_swallows, + "Number of gas particles the black holes have swallowed. " + "This does not include any particles swallowed by any of the black holes " + "that merged into this one."); + num++; + + list[num] = io_make_output_field( + "NumberOfRepositions", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + number_of_repositions, + "Number of repositioning events the black holes went through. This does " + "not include the number of reposition events accumulated by any merged " + "black holes."); + num++; + + list[num] = io_make_output_field( + "NumberOfRepositionAttempts", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + number_of_reposition_attempts, + "Number of time steps in which the black holes had an eligible particle " + "to reposition to. They may or may not have ended up moving there, " + "depending on their subgrid mass and on whether these particles were at " + "a lower or higher potential than the black holes themselves. It does " + "not include attempted repositioning events accumulated by any merged " + "black holes."); + num++; + + list[num] = io_make_output_field( + "NumberOfTimeSteps", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + number_of_time_steps, + "Total number of time steps at which the black holes were active."); + num++; + + list[num] = io_make_output_field( + "SubgridSoundSpeeds", FLOAT, 1, UNIT_CONV_SPEED, 0.f, bparts, + sound_speed_subgrid_gas, + "Physical subgrid sound-speeds used in the subgrid-Bondi model."); + num++; + + list[num] = io_make_output_field( + "BirthGasDensities", FLOAT, 1, UNIT_CONV_DENSITY, 0.f, bparts, + formation_gas_density, + "Physical densities of the converted part at the time of birth. " + "We store the physical density at the birth redshift, no conversion is " + "needed."); + num++; + + list[num] = io_make_output_field( + "AccretedAngularMomenta", FLOAT, 3, UNIT_CONV_ANGULAR_MOMENTUM, 0.f, + bparts, accreted_angular_momentum, + "Physical angular momenta that the black holes have accumulated through " + "subgrid accretion."); + num++; + + list[num] = io_make_output_field( + "NumberOfGasNeighbours", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + num_ngbs, + "Integer number of gas neighbour particles within the black hole " + "kernels."); + num++; + + list[num] = io_make_output_field( + "LastRepositionVelocities", FLOAT, 1, UNIT_CONV_SPEED, 0.f, bparts, + last_repos_vel, + "Physical speeds at which the black holes repositioned most recently. " + "This is 0 for black holes that have never repositioned, or if the " + "simulation has been run without prescribed repositioning speed."); + num++; + + list[num] = io_make_output_field_convert_bpart( + "GasTemperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, bparts, + convert_bpart_gas_temperatures, + "Temperature of the gas surrounding the black holes."); + num++; + + list[num] = io_make_output_field( + "EddingtonFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, + eddington_fraction, + "Accretion rates of black holes in units of their Eddington rates. " + "This is based on the unlimited accretion rates, so these fractions " + "can be above the limiting fEdd."); + num++; + + list[num] = io_make_output_field_convert_bpart( + "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, bparts, + convert_bpart_potential, "Gravitational potentials of the particles"); + num++; + + *num_fields = num; + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + + list += *num_fields; + *num_fields += 4; + + list[0] = io_make_output_field("Num_ngb_density", INT, 1, UNIT_CONV_NO_UNITS, + bparts, num_ngb_density); + list[1] = io_make_output_field("Num_ngb_force", INT, 1, UNIT_CONV_NO_UNITS, + bparts, num_ngb_force); + list[2] = io_make_output_field("Ids_ngb_density", LONGLONG, + MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES, + UNIT_CONV_NO_UNITS, bparts, ids_ngbs_density); + list[3] = io_make_output_field("Ids_ngb_force", LONGLONG, + MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES, + UNIT_CONV_NO_UNITS, bparts, ids_ngbs_force); +#endif +} + +#endif /* SWIFT_OBSIDIAN_BLACK_HOLES_IO_H */ diff --git a/src/black_holes/Obsidian/black_holes_parameters.h b/src/black_holes/Obsidian/black_holes_parameters.h new file mode 100644 index 0000000000..d195c1df4c --- /dev/null +++ b/src/black_holes/Obsidian/black_holes_parameters.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_OBSIDIAN_BLACK_HOLES_PARAMETERS_H +#define SWIFT_OBSIDIAN_BLACK_HOLES_PARAMETERS_H + +/* Configuration file */ +#include "config.h" + +/** + * @file Obsidian/black_holes_parameters.h + * @brief Parameters of the Obsidian black holes + * model that need to be defined at compile time. + * + * @note In this branch, these properties are not used anywhere! + */ + +/*! Maximal distance for merging particles in units of the (spline not Plummer) + * softening length. */ +#define const_max_merging_distance_ratio 3.f + +/*! Maximal distance for repositioning particles in units of the (spline not + * Plummer) softening length. */ +#define const_max_repositioning_distance_ratio 3.f + +#endif /* SWIFT_OBSIDIAN_BLACK_HOLES_PARAMETERS_H */ diff --git a/src/black_holes/Obsidian/black_holes_part.h b/src/black_holes/Obsidian/black_holes_part.h new file mode 100644 index 0000000000..95ee3d89ad --- /dev/null +++ b/src/black_holes/Obsidian/black_holes_part.h @@ -0,0 +1,327 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_OBSIDIAN_BLACK_HOLE_PART_H +#define SWIFT_OBSIDIAN_BLACK_HOLE_PART_H + +#include "black_holes_struct.h" +#include "chemistry_struct.h" +#ifdef WITH_FOF_GALAXIES +#include "fof_struct.h" +#endif +#include "particle_splitting_struct.h" +#include "timeline.h" + +/** + * @brief Particle fields for the black hole particles. + * + * All quantities related to gravity are stored in the associate #gpart. + */ +struct bpart { + + /*! Particle ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + + /*! Particle position. */ + double x[3]; + + /* Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /*! Particle velocity. */ + float v[3]; + + /*! Black hole mass */ + float mass; + + /*! Black hole mass at the start of each step, prior to any nibbling */ + float mass_at_start_of_step; + + /* Particle cutoff radius. */ + float h; + + /*! Particle time bin */ + timebin_t time_bin; + + /*! Tree-depth at which size / 2 <= h * gamma < size */ + char depth_h; + + struct { + + /* Number of neighbours. */ + float wcount; + + /* Number of neighbours spatial derivative. */ + float wcount_dh; + + } density; + +#ifdef WITH_FOF_GALAXIES + /*! Struct for host galaxy information */ + struct fof_galaxy_data galaxy_data; +#endif + + /*! Union for the formation time and formation scale factor */ + union { + + /*! Formation time */ + float formation_time; + + /*! Formation scale factor */ + float formation_scale_factor; + }; + + /*! Physical density of the converted part (internal units) */ + float formation_gas_density; + + /*! Subgrid mass of the black hole */ + float subgrid_mass; + + /*! Total accreted mass of the black hole (not including mass merged in + * from other black holes) */ + float total_accreted_mass; + + /*! Instantaneous accretion rate */ + float accretion_rate; + + /*! Instantaneous Bondi component of accretion rate */ + float bondi_accretion_rate; + + /*! Density of the gas surrounding the black hole. */ + float rho_gas; + + /*! Internal energy of the gas surrounding the black hole. */ + float internal_energy_gas; + + /*! The mass of hot gas surrounding the black hole */ + float hot_gas_mass; + + /*! The mass of cold gas surrounding the black hole */ + float cold_gas_mass; + + /*! The total SFR of cold gas surrounding the black hole */ + float gas_SFR; + + /*! The current state of the black hole */ + int state; + + /*! The radiative efficiency associated with M_dot,bh */ + float radiative_efficiency; + + /*! The large scale accretion rate onto the black hole */ + float m_dot_inflow; + + /*! The amount of jet energy available */ + float jet_mass_reservoir; + + /*! The amount of jet mass kicked this time step */ + float jet_mass_kicked_this_step; + + /*! The mass loading in the jet for the variable velocity scheme */ + float jet_mass_loading; + + /*! The amount of unresolved mass available to kick */ + float unresolved_mass_reservoir; + + /*! The amount of unresolved mass kicked this step */ + float unresolved_mass_kicked_this_step; + + /*! Energy to dump this step via the ADAF hot-wind, kernel-weighted */ + float adaf_energy_to_dump; + + /*! Energy injected this step in the ADAF mode */ + float adaf_energy_used_this_step; + + /*! sum(mi * wi) weights for accretion/feedback */ + float kernel_wt_sum; + + /*! sum(mi * wi) weights for adaf heating */ + float adaf_wt_sum; + + /*! The mass of cold disk around the black hole */ + float cold_disk_mass; + + /*! Mass in accretion disk from which BH accretes */ + float accretion_disk_mass; + + /*! The mass-weighted internal energy surrounding the black hole (unsmoothed) */ + float hot_gas_internal_energy; + + /*! Smoothed sound speed of the gas surrounding the black hole. */ + float sound_speed_gas; + + /*! Total gravitational gas mass within the kernel */ + float gravitational_ngb_mass; + + /*! Subgrid physical sound speed of the gas (updated when using the subgrid + * Bondi model) */ + float sound_speed_subgrid_gas; + + /*! Smoothed velocity of the gas surrounding the black hole, + * in the frame of the black hole (internal units) */ + float velocity_gas[3]; + + /*! The real angular momentum of the gas in the kernel */ + float angular_momentum_gas[3]; + + /*! Circular velocity of the gas around the black hole at the smoothing + * radius (calculated as j_gas / h_BH, where j is specific ang. mom.) */ + float circular_velocity_gas[3]; + + /*! Total mass of the gas neighbours. */ + float ngb_mass; + + /*! Integer number of neighbours */ + int num_ngbs; + + /*! Integer number of gravitational neighbors */ + int num_gravitational_ngbs; + + /*! Number of seeds in this BH (i.e. itself + the merged ones) */ + int cumulative_number_seeds; + + /*! Total number of BH merger events (i.e. not including all progenies) */ + int number_of_mergers; + + /*! Total number of gas particles swallowed (including particles swallowed + * by merged-in black holes) */ + int number_of_gas_swallows; + + /*! Total number of gas particles swallowed (excluding particles swallowed + * by merged-in black holes) */ + int number_of_direct_gas_swallows; + + /*! Total number of times the black hole has been repositioned (excluding + * repositionings of merged-in black holes) */ + int number_of_repositions; + + /*! Total number of times a black hole attempted repositioning (including + * cases where it was aborted because the black hole was already at a + * lower potential than all eligible neighbours) */ + int number_of_reposition_attempts; + + /* Velocity of most recent reposition jump */ + float last_repos_vel; + + /*! Total number of time steps in which the black hole was active. */ + int number_of_time_steps; + + /*! Total (physical) angular momentum accumulated by swallowing particles */ + float swallowed_angular_momentum[3]; + + /*! Total (physical) angular momentum accumulated from subgrid accretion */ + float accreted_angular_momentum[3]; + + /*! Eddington fractions */ + float eddington_fraction; + + /*! Wind velocity kick */ + float v_kick; + + /*! Fraction of Mdot,inflow that should be accreted, the rest is a wind */ + float f_accretion; + + /*! Bulge mass of stars within the kernel (twice the counter-rotating mass) */ + float stellar_bulge_mass; + + /*! The mass of stars within the kernel */ + float stellar_mass; + + /*! The radiative luminosity of the black hole */ + float radiative_luminosity; + + /*! How much energy has been given away in this timestep? */ + float delta_energy_this_timestep; + + /*! Union for the last minor merger point in time */ + union { + + /*! Last time the BH had a a high Eddington fraction */ + float last_minor_merger_time; + + /*! Last scale factor the BH had a a high Eddington fraction */ + float last_minor_merger_scale_factor; + }; + + /*! Union for the last major merger point in time */ + union { + + /*! Last time the BH had a a high Eddington fraction */ + float last_major_merger_time; + + /*! Last scale factor the BH had a major merger */ + float last_major_merger_scale_factor; + }; + + struct { + + /*! Gravitational potential copied from the #gpart. */ + float potential; + + /*! Value of the minimum potential across all neighbours. */ + float min_potential; + + /*! Delta position to apply after the reposition procedure */ + double delta_x[3]; + + } reposition; + + /*! Splitting structure */ + struct particle_splitting_data split_data; + + /*! Chemistry information (e.g. metal content at birth, swallowed metal + * content, etc.) */ + struct chemistry_bpart_data chemistry_data; + + /*! Black holes merger information (e.g. merging ID) */ + struct black_holes_bpart_data merger_data; + + /*! Tracer structure */ + struct tracers_bpart_data tracers_data; + +#ifdef SWIFT_DEBUG_CHECKS + + /* Time of the last drift */ + integertime_t ti_drift; + + /* Time of the last kick */ + integertime_t ti_kick; + +#endif + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + /*! Number of interactions in the density SELF and PAIR */ + int num_ngb_density; + + /*! List of interacting particles in the density SELF and PAIR */ + long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES]; + + /*! Number of interactions in the force SELF and PAIR */ + int num_ngb_force; + + /*! List of interacting particles in the force SELF and PAIR */ + long long ids_ngbs_force[MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES]; +#endif + +} SWIFT_STRUCT_ALIGN; + +#endif /* SWIFT_OBSIDIAN_BLACK_HOLE_PART_H */ diff --git a/src/black_holes/Obsidian/black_holes_properties.h b/src/black_holes/Obsidian/black_holes_properties.h new file mode 100644 index 0000000000..4c7b85773b --- /dev/null +++ b/src/black_holes/Obsidian/black_holes_properties.h @@ -0,0 +1,1302 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_OBSIDIAN_BLACK_HOLES_PROPERTIES_H +#define SWIFT_OBSIDIAN_BLACK_HOLES_PROPERTIES_H + +/* Config parameters. */ +#include "../config.h" + +/* Local includes. */ +#include "chemistry.h" +#include "exp10.h" +#include "hydro_properties.h" + +/* Includes. */ +#include + + +enum BH_states { + BH_states_adaf = 0, /* < 0.03 Mdot,BH / Mdot,Edd */ + BH_states_quasar, /* 0.03 < Mdot,BH / Mdot,Edd < 0.3 */ + BH_states_slim_disk /* Mdot,BH/Mdot,Edd > 0.3 */ +}; + +enum BH_merger_thresholds { + BH_mergers_circular_velocity, /*< v_circ at separation, as in EAGLE */ + BH_mergers_escape_velocity, /*< v_esc at separation */ + BH_mergers_dynamical_escape_velocity /*< combined v_esc_dyn at separation */ +}; + +enum BH_loading_types { + BH_jet_momentum_loaded, /* Momentum loaded jet, with subgrid energy loading */ + BH_jet_energy_loaded, /* Energy loaded jet, no subgrid */ + BH_jet_mixed_loaded /* A mix between momentum and energy loading */ +}; + +/** + * @brief Properties of black holes and AGN feedback in the EAGEL model. + */ +struct black_holes_props { + + /* ----- Basic neighbour search properties ------ */ + + /*! Resolution parameter */ + float eta_neighbours; + + /*! Target weightd number of neighbours (for info only)*/ + float target_neighbours; + + /*! Smoothing length tolerance */ + float h_tolerance; + + /*! Maximum smoothing length */ + float h_max; + + /*! Minimum smoothing length */ + float h_min; + + /*! Tolerance on neighbour number (for info only)*/ + float delta_neighbours; + + /*! Maximal number of iterations to converge h */ + int max_smoothing_iterations; + + /*! Maximal change of h over one time-step */ + float log_max_h_change; + + /*! Gravitational constant */ + float const_newton_G; + + /* ----- Initialisation properties ------ */ + + /*! Mass of a BH seed at creation time */ + float subgrid_seed_mass; + + /*! Should we use the subgrid mass specified in ICs? */ + int use_subgrid_mass_from_ics; + + /*! Should we enforce positive subgrid masses initially? */ + int with_subgrid_mass_check; + + /* ----- Properties of the accretion model ------ */ + + /*! Radiative efficiency of the black holes. */ + float epsilon_r; + + /*! Maximal fraction of the Eddington rate allowed for total accretion. */ + float f_Edd_maximum; + + /*! Maximal fraction of the Eddington rate allowed for Bondi accretion alone. */ + float f_Edd_Bondi_maximum; + + /*! Minimum gas particle mass in nibbling mode */ + float min_gas_mass_for_nibbling; + + /*! Switch to calculate the sound speed with a fixed T near the EoS */ + int with_fixed_T_near_EoS; + + /*! Factor above EoS below which fixed T applies for sound speed */ + float fixed_T_above_EoS_factor; + + /*! Where do we distinguish between hot gas for Bondi? */ + float environment_temperature_cut; + + /*! Number of dynamical times over which gas is accreted from accretion disk */ + float dynamical_time_factor; + + /*! Max dynamical time over which gas is accreted from accretion disk */ + float dynamical_time_max; + + /*! Method to compute torque accretion rate: + * 0=Mgas/tdyn on all gas; 1=Mgas/tdyn on disk gas; 2=Simba-style(HQ11) */ + int torque_accretion_method; + + /*! Normalization of the torque accretion rate */ + float torque_accretion_norm; + + /*! Factor in front of M/(dM/dt) for timestepping */ + float dt_accretion_factor; + + /*! Factor for exponentially limiting black hole growth in early stages. */ + float bh_characteristic_suppression_mass; + + /*! SF efficiency in BH kernel for suppression by winds (<0 means compute on the fly). */ + float suppression_sf_eff; + + /*! Gaussian spread in infall times when using SF-based growth suppression. */ + int tdyn_sigma; + + /*! Method to suppress early growth of BH */ + int suppress_growth; + + /*! A from Lupi+17 */ + float A_sd; + + /*! B from Lupi+17 */ + float B_sd; + + /*! C from Lupi+17 */ + float C_sd; + + /*! The spin of EVERY black hole */ + float fixed_spin; + + /*! Method to compute the dynamical time within the kernel */ + int dynamical_time_calculation_method; + + /* ---- Properties of the feedback model ------- */ + + /*! The loading for the jet: momentum or energy */ + enum BH_loading_types jet_loading_type; + + /*! What is the physical max. velocity of the jet? (km/s) */ + float jet_velocity; + + /*! How long to decouple black hole winds? */ + float jet_decouple_time_factor; + + /*! The temperature of the jet. Set < 0.f for halo virial temperature */ + float jet_temperature; + + /*! The fraction of energy loading that should go into mixed loading */ + float jet_energy_frac; + + /*! What lower Mdot,BH/Mdot,Edd boundary does the jet activate? */ + float eddington_fraction_lower_boundary; + + /*! What upper Mdot,BH/Mdot,Edd boundary does the slim disk mode activate? */ + float eddington_fraction_upper_boundary; + + /*! How long to decouple black hole winds? */ + float quasar_decouple_time_factor; + + /*! Constrains momentum of outflowing wind to p = F * L / c */ + float quasar_wind_momentum_flux; + + /*! The mass loading of the quasar outflow */ + float quasar_wind_mass_loading; + + /*! The wind speed of the quasar outflow */ + float quasar_wind_speed; + + /*! Direction of quasar winds: 0=random, 1=L_gas, 2=L_BH, 3=outwards */ + int quasar_wind_dir; + + /*! f_acc for the quasar mode */ + float quasar_f_accretion; + + /*! eps_f for the quasar mode */ + float quasar_coupling; + + /*! luminosity in system units above which to boost quasar eps_f quasar mode */ + double quasar_luminosity_thresh; + + /*! The disk wind efficiency from Benson & Babul 2009 */ + float adaf_disk_efficiency; + + /*! The wind speed of the ADAF outflow */ + float adaf_wind_speed; + + /*! The mass loading of the ADAF wind */ + float adaf_wind_mass_loading; + + /*! eps_f for the ADAF mode */ + float adaf_coupling; + + /*! Power-law scaling of adaf_coupling with (1+z) */ + float adaf_z_scaling; + + /*! f_acc for the ADAF mode */ + float adaf_f_accretion; + + /*! The maximum temperature to heat a gas particle */ + float adaf_maximum_temperature; + + /*! Above this density we should shut off cooling for heated particles */ + double adaf_heating_n_H_threshold_cgs; + + /*! Below this temperature we should shut off cooling for particles */ + double adaf_heating_T_threshold_cgs; + + /*! Lower mass limit (internal units) for BH to enter ADAF mode */ + float adaf_mass_limit; + + /*! Sets upper mass range (internal units) for BH to enter ADAF mode */ + float adaf_mass_limit_spread; + + /*! Sets power-law of expansion factor dependence of ADAF mass limit */ + float adaf_mass_limit_a_scaling; + + /*! Sets maximum expansion factor for evolving ADAF mass limit */ + float adaf_mass_limit_a_min; + + /*! A multiplicative factor for delaying cooling on a particle */ + float adaf_cooling_shutoff_factor; + + /*! A multiplicative factor 0. < f < 1. to multiply E_inject in the ADAF mode */ + float adaf_kick_factor; + + /*! Direction of ADAF winds: 0=random, 1=L_gas, 2=L_BH, 3=outwards */ + int adaf_wind_dir; + + /*! How long to decouple black hole winds? */ + float adaf_decouple_time_factor; + + /*! Should we use nibbling */ + int use_nibbling; + + /*! Use all of the gas in the kernel to compute Bondi */ + int bondi_use_all_gas; + + /*! Multiplicative factor in front of Bondi rate */ + float bondi_alpha; + + /*! Minimum BH mass for unresolved feedback (internal units) */ + float minimum_black_hole_mass_unresolved; + + /*! Minimum BH mass for the v_kick formula (internal units) */ + float minimum_black_hole_mass_v_kick; + + /*! Minimum kick velocity for variable v_kick */ + float minimum_v_kick_km_s; + + /*! The phi term for the slim disk mode (Eq. 9 from Rennehan+24) */ + float slim_disk_phi; + + /*! eps_f for the slim disk mode */ + float slim_disk_coupling; + + /*! wind speed in the slim disk mode */ + float slim_disk_wind_speed; + + /*! How long to decouple black hole winds? */ + float slim_disk_decouple_time_factor; + + /*! Direction of slim disk winds: 0=random, 1=L_gas, 2=L_BH, 3=outwards */ + int slim_disk_wind_dir; + + /*! Is the slim disk jet model active? */ + int slim_disk_jet_active; + + /*! The efficiency of the jet */ + float jet_efficiency; + + /*! The fraction of energy loading when using a mixed jet */ + float jet_frac_energy; + + /*! The mass loading in the jet */ + float jet_mass_loading; + + /*! The subgrid jet speed to set the accretion fraction */ + float jet_subgrid_velocity; + + /*! power-law scaling of jet vel with (MBH/1.e8 Mo) */ + float jet_velocity_scaling_with_BH_mass; + + /*! power-law scaling of jet vel with (LBH/1.e45 erg/s) */ + float jet_velocity_scaling_with_BH_lum; + + /*! Direction of jet launch: 0=random, 1=L_gas, 2=L_BH, 3=outwards */ + int jet_launch_dir; + + /*! The minimum mass required before the jet will launch */ + float jet_minimum_reservoir_mass; + + /*! Above this luminosity in erg/s always launch a jet */ + double lum_thresh_always_jet; + + /* ---- Properties of the repositioning model --- */ + + /*! Maximal mass of BH to reposition */ + float max_reposition_mass; + + /*! Maximal distance to reposition, in units of softening length */ + float max_reposition_distance_ratio; + + /*! Switch to enable a relative velocity limit for particles to which the + * black holes can reposition */ + int with_reposition_velocity_threshold; + + /*! Maximal velocity offset of particles to which the black hole can + * reposition, in units of the ambient sound speed of the black hole */ + float max_reposition_velocity_ratio; + + /*! Minimum value of the velocity repositioning threshold */ + float min_reposition_velocity_threshold; + + /*! Switch to enable repositioning at fixed (maximum) speed */ + int set_reposition_speed; + + /*! Normalisation factor for repositioning velocity */ + float reposition_coefficient_upsilon; + + /*! Reference black hole mass for repositioning scaling */ + float reposition_reference_mass; + + /*! Repositioning velocity scaling with black hole mass */ + float reposition_exponent_mass; + + /*! Reference gas density for repositioning scaling */ + float reposition_reference_n_H; + + /*! Repositioning velocity scaling with gas density */ + float reposition_exponent_n_H; + + /*! Correct potential of BH? */ + int correct_bh_potential_for_repositioning; + + /* ---- Properties of the merger model ---------- */ + + /*! Mass ratio above which a merger is considered 'minor' */ + float minor_merger_threshold; + + /*! Mass ratio above which a merger is considered 'major' */ + float major_merger_threshold; + + /*! Type of merger threshold */ + enum BH_merger_thresholds merger_threshold_type; + + /*! Maximal distance over which BHs merge, in units of softening length */ + float max_merging_distance_ratio; + + /* ---- Black hole time-step properties ---------- */ + + /*! Minimum allowed time-step of BH (internal units) */ + float time_step_min; + + /* ---- Common conversion factors --------------- */ + + /*! Conversion factor from temperature to internal energy */ + double temp_to_u_factor; + + /*! Conversion factor from physical density to n_H [cgs] */ + double rho_to_n_cgs; + + /*! Conversion factor from internal mass to solar masses */ + double mass_to_solar_mass; + + /*! Conversion factor from km/s to internal velocity units (without a-factor) */ + double kms_to_internal; + + /*! Conversion factor from internal length to parsec */ + double length_to_parsec; + + /*! Conversion factor from internal time to yr */ + double time_to_yr; + + /*! Conversion factor from internal time to Myr */ + double time_to_Myr; + + /*! Conversion factor from density to cgs */ + double conv_factor_density_to_cgs; + + /*! Conversion factor from luminosity to cgs */ + double conv_factor_energy_rate_to_cgs; + + /*! Conversion factor from length to cgs */ + double conv_factor_length_to_cgs; + + /*! Conversion factor from mass to cgs */ + double conv_factor_mass_to_cgs; + + /*! Conversion factor from time to cgs */ + double conv_factor_time_to_cgs; + + /*! Conversion factor from specific energy to cgs */ + double conv_factor_specific_energy_to_cgs; + + /*! Proton mass */ + double proton_mass_cgs_inv; + + /*! Convert Kelvin to internal temperature */ + double T_K_to_int; + + /* ------------ Stellar feedback properties for eta computation --------------- */ + + /*! Normalization for the mass loading curve */ + float FIRE_eta_normalization; + + /*! The location (in internal mass units) where the break in the + * mass loading curve occurs */ + float FIRE_eta_break; + + /*! The power-law slope of eta below FIRE_eta_break */ + float FIRE_eta_lower_slope; + + /*! The power-law slope of eta above FIRE_eta_break */ + float FIRE_eta_upper_slope; + + /*! The power-law slope of eta below FIRE_eta_break at z>6 */ + float FIRE_eta_lower_slope_EOR; + + /*! The minimum galaxy stellar mass in internal units */ + float minimum_galaxy_stellar_mass; + + /*! The mass loading factor of stellar feedback suppressed above this z */ + float wind_eta_suppression_redshift; +}; + +/** + * @brief Initialise the black hole properties from the parameter file. + * + * For the basic black holes neighbour finding properties we use the + * defaults from the hydro scheme if the users did not provide specific + * values. + * + * @param bp The #black_holes_props. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param hydro_props The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. + */ +INLINE static void black_holes_props_init(struct black_holes_props *bp, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct hydro_props *hydro_props, + const struct cosmology *cosmo) { + + /* Calculate conversion factors (all internal units) */ + const double k_B = phys_const->const_boltzmann_k; + const double m_p = phys_const->const_proton_mass; + const double mu = hydro_props->mu_ionised; + bp->temp_to_u_factor = k_B / (mu * hydro_gamma_minus_one * m_p); + + const double Myr_in_cgs = 1e6 * 365.25 * 24. * 60. * 60.; + bp->time_to_Myr = units_cgs_conversion_factor(us, UNIT_CONV_TIME) / + Myr_in_cgs; + + /* Store constant of gravity */ + bp->const_newton_G = phys_const->const_newton_G; + + /* Read in the basic neighbour search properties or default to the hydro + ones if the user did not provide any different values */ + + /* Kernel properties */ + bp->eta_neighbours = parser_get_opt_param_float( + params, "BlackHoles:resolution_eta", hydro_props->eta_neighbours); + + /* Tolerance for the smoothing length Newton-Raphson scheme */ + bp->h_tolerance = parser_get_opt_param_float(params, "BlackHoles:h_tolerance", + hydro_props->h_tolerance); + + /* Get derived properties */ + bp->target_neighbours = pow_dimension(bp->eta_neighbours) * kernel_norm; + const float delta_eta = bp->eta_neighbours * (1.f + bp->h_tolerance); + bp->delta_neighbours = + (pow_dimension(delta_eta) - pow_dimension(bp->eta_neighbours)) * + kernel_norm; + + /* Number of iterations to converge h */ + bp->max_smoothing_iterations = + parser_get_opt_param_int(params, "BlackHoles:max_ghost_iterations", + hydro_props->max_smoothing_iterations); + + /* Maximum smoothing length */ + bp->h_max = parser_get_opt_param_float(params, "BlackHoles:h_max", + hydro_props->h_max); + + /* Minimum smoothing length */ + bp->h_min = parser_get_opt_param_float(params, "BlackHoles:h_min", + hydro_props->h_min); + + /* Time integration properties */ + const float max_volume_change = + parser_get_opt_param_float(params, "BlackHoles:max_volume_change", -1); + if (max_volume_change == -1) + bp->log_max_h_change = hydro_props->log_max_h_change; + else + bp->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv)); + + /* Initialisation properties ---------------------------- */ + + bp->subgrid_seed_mass = + parser_get_param_float(params, "ObsidianAGN:subgrid_seed_mass_Msun"); + + /* Convert to internal units */ + bp->subgrid_seed_mass *= phys_const->const_solar_mass; + + bp->use_subgrid_mass_from_ics = parser_get_opt_param_int(params, + "ObsidianAGN:use_subgrid_mass_from_ics", 1); + if (bp->use_subgrid_mass_from_ics) { + bp->with_subgrid_mass_check = parser_get_opt_param_int(params, + "ObsidianAGN:with_subgrid_mass_check", 1); + } + + /* Accretion parameters ---------------------------------- */ + + /* Conversion factor for internal mass to M_solar */ + bp->mass_to_solar_mass = 1.f / phys_const->const_solar_mass; + + bp->min_gas_mass_for_nibbling = + parser_get_param_float(params, + "ObsidianAGN:min_gas_mass_for_nibbling_Msun"); + bp->min_gas_mass_for_nibbling /= bp->mass_to_solar_mass; + + const double T_K_to_int = + 1. / units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + bp->environment_temperature_cut = + parser_get_opt_param_float(params, + "ObsidianAGN:environment_temperature_cut_K", + 1.0e5f); + bp->environment_temperature_cut *= T_K_to_int; + + bp->dynamical_time_factor = parser_get_opt_param_float( + params, "ObsidianAGN:dynamical_time_factor", 1.f); + + bp->dynamical_time_max = parser_get_opt_param_float( + params, "ObsidianAGN:dynamical_time_max_in_Myr", 0.f); + bp->dynamical_time_max /= bp->time_to_Myr; + + bp->torque_accretion_method = + parser_get_opt_param_int(params, "ObsidianAGN:torque_accretion_method", 0); + + bp->torque_accretion_norm = + parser_get_param_float(params, "ObsidianAGN:torque_accretion_norm"); + + bp->suppress_growth = + parser_get_opt_param_int(params, "ObsidianAGN:suppress_growth", 0); + + if (bp->suppress_growth == 5 && bp->torque_accretion_method == 2) { + error("SF-based suppression of BH mass will not work correctly with " + "Simba-style torque-limited BH growth -- use tdyn method"); + } + + bp->dt_accretion_factor = + parser_get_opt_param_float(params, "ObsidianAGN:dt_accretion_factor", 1.f); + if (bp->dt_accretion_factor > 1.f || bp->dt_accretion_factor < 0.f) { + error("ObsidianAGN:dt_accretion_factor must be between 0 and 1"); + } + + bp->bh_characteristic_suppression_mass = parser_get_opt_param_float( + params, "ObsidianAGN:bh_characteristic_suppression_mass", 0.f); + + bp->suppression_sf_eff = parser_get_opt_param_float( + params, "ObsidianAGN:suppression_sf_eff", 0.f); + + bp->tdyn_sigma = parser_get_opt_param_float( + params, "ObsidianAGN:tdyn_sigma", 0.f); + + if (bp->suppress_growth == 4 || + bp->suppress_growth == 5) { + bp->FIRE_eta_normalization = + parser_get_param_float(params, "KIARAFeedback:FIRE_eta_normalization"); + bp->FIRE_eta_break = + parser_get_param_float(params, "KIARAFeedback:FIRE_eta_break_Msun"); + bp->FIRE_eta_break /= bp->mass_to_solar_mass; + bp->FIRE_eta_lower_slope = + parser_get_param_float(params, "KIARAFeedback:FIRE_eta_lower_slope"); + bp->FIRE_eta_upper_slope = + parser_get_param_float(params, "KIARAFeedback:FIRE_eta_upper_slope"); + bp->FIRE_eta_lower_slope_EOR = + parser_get_param_float(params, "KIARAFeedback:FIRE_eta_lower_slope_EOR"); + bp->minimum_galaxy_stellar_mass = parser_get_param_float(params, + "KIARAFeedback:minimum_galaxy_stellar_mass_Msun"); + bp->minimum_galaxy_stellar_mass /= bp->mass_to_solar_mass; + bp->wind_eta_suppression_redshift = parser_get_opt_param_float(params, + "KIARAFeedback:wind_eta_suppression_redshift", 0.f); + } + + bp->f_Edd_maximum = + parser_get_param_float(params, "ObsidianAGN:max_eddington_fraction"); + + bp->f_Edd_Bondi_maximum = parser_get_opt_param_float(params, + "ObsidianAGN:max_bondi_eddington_fraction", 1.f); + + bp->fixed_T_above_EoS_factor = + exp10(parser_get_param_float(params, "ObsidianAGN:fixed_T_above_EoS_dex")); + + bp->dynamical_time_calculation_method = parser_get_opt_param_int(params, + "ObsidianAGN:dynamical_time_calculation_method", 1); + + /* Feedback parameters ---------------------------------- */ + + bp->kms_to_internal = 1.0e5f / + units_cgs_conversion_factor(us, UNIT_CONV_SPEED); + + char temp3[40]; + parser_get_param_string(params, "ObsidianAGN:jet_loading_type", temp3); + if (strcmp(temp3, "EnergyLoaded") == 0) { + bp->jet_loading_type = BH_jet_energy_loaded; + } + else if (strcmp(temp3, "MomentumLoaded") == 0) { + bp->jet_loading_type = BH_jet_momentum_loaded; + } + else if (strcmp(temp3, "MixedLoaded") == 0) { + bp->jet_loading_type = BH_jet_mixed_loaded; + } + else { + error( + "The BH jet loading must be either EnergyLoaded or " + "MomentumLoaded or MixedLoaded, not %s", + temp3); + } + + bp->jet_velocity = + parser_get_param_float(params, "ObsidianAGN:jet_velocity_km_s"); + bp->jet_velocity *= bp->kms_to_internal; + if (bp->jet_velocity == 0.f) { + error("jet_velocity must be >0.f (or <0.f if scaling with z)."); + } + + /* Use this in all of the physical calculations */ + const float jet_velocity = fabs(bp->jet_velocity); + + bp->jet_temperature = + parser_get_param_float(params, "ObsidianAGN:jet_temperature_K"); + bp->jet_temperature *= T_K_to_int; + + bp->eddington_fraction_lower_boundary = + parser_get_param_float(params, + "ObsidianAGN:eddington_fraction_lower_boundary"); + + bp->eddington_fraction_upper_boundary = + parser_get_param_float(params, + "ObsidianAGN:eddington_fraction_upper_boundary"); + + const double kpc_per_km = 3.24078e-17; + const double age_s = 13800. * Myr_in_cgs; /* Approximate age at z = 0 */ + const double jet_velocity_kpc_s = + (jet_velocity / bp->kms_to_internal) * kpc_per_km; + const double recouple_distance_kpc = 10.; + const double f_jet_recouple = + recouple_distance_kpc / (jet_velocity_kpc_s * age_s); + + bp->jet_decouple_time_factor = + parser_get_opt_param_float(params, + "ObsidianAGN:jet_decouple_time_factor", f_jet_recouple); + + bp->fixed_spin = + parser_get_param_float(params, "ObsidianAGN:fixed_spin"); + if (bp->fixed_spin >= 1.f || bp->fixed_spin <= 0.f) { + error("Black hole must have spin > 0.0 and < 1.0"); + } + + bp->A_sd = powf(0.9663f - 0.9292f * bp->fixed_spin, -0.5639f); + bp->B_sd = powf(4.627f - 4.445f * bp->fixed_spin, -0.5524f); + bp->C_sd = powf(827.3f - 718.1f * bp->fixed_spin, -0.7060f); + + const float phi_bh = -20.2f * powf(bp->fixed_spin, 3.f) + -14.9f * powf(bp->fixed_spin, 2.f) + + 34.f * bp->fixed_spin + + 52.6f; + const float big_J = bp->fixed_spin / + (2.f * (1.f + sqrtf(1.f - powf(bp->fixed_spin, 2.f)))); + const float f_j = powf(big_J, 2.f) + + 1.38f * powf(big_J, 4.f) - 9.2f * powf(big_J, 6.f); + + bp->jet_efficiency = (1.f / (24.f * M_PI * M_PI)) * powf(phi_bh, 2.f) * f_j; + + /* Default to zero contribution from energy loading */ + bp->jet_energy_frac = 0.f; + + /* Useful when computing the mass loadings below */ + const double c_over_v = phys_const->const_speed_light_c / jet_velocity; + + /* How are we loading the jet? Momentum, mixed, or energy? */ + if (bp->jet_loading_type == BH_jet_momentum_loaded) { + bp->jet_mass_loading = bp->jet_efficiency * c_over_v; + } + else if (bp->jet_loading_type == BH_jet_mixed_loaded) { + bp->jet_frac_energy = + parser_get_param_float(params, "ObsidianAGN:jet_frac_energy_loaded"); + if (bp->jet_frac_energy <= 0.f || bp->jet_frac_energy >= 1.f) { + error("jet_frac_energy_loaded must be >0 and <1."); + } + + const double energy_loading = 2. * bp->jet_efficiency * pow(c_over_v, 2.); + const double momentum_loading = bp->jet_efficiency * c_over_v; + const double energy_term = bp->jet_frac_energy * energy_loading; + const double momentum_term = (1. - bp->jet_frac_energy) * momentum_loading; + bp->jet_mass_loading = energy_term + momentum_term; + } + else { + bp->jet_mass_loading = 2. * bp->jet_efficiency * pow(c_over_v, 2.); + } + + bp->jet_subgrid_velocity = + parser_get_param_float(params, "ObsidianAGN:jet_subgrid_velocity_km_s"); + bp->jet_subgrid_velocity *= bp->kms_to_internal; + + const float R = 1.f / bp->eddington_fraction_upper_boundary; + const float eta_at_slim_disk_boundary = + (R / 16.f) * bp->A_sd * ((0.985f / (R + (5.f / 8.f) * bp->B_sd)) + + (0.015f / (R + (5.f / 8.f) * bp->C_sd))); + + /* If we scale BH v_jet, choose power law scaling with MBH/1.e8 or LBH/1.e45. + * The minimum v_jet is always given by jet_subgrid_velocity */ + bp->jet_velocity_scaling_with_BH_mass + = parser_get_opt_param_float(params, + "ObsidianAGN:jet_velocity_scaling_with_BH_mass", 0.f); + + bp->jet_velocity_scaling_with_BH_lum + = parser_get_opt_param_float(params, + "ObsidianAGN:jet_velocity_scaling_with_BH_lum", 0.f); + + bp->jet_launch_dir = + parser_get_param_int(params, "ObsidianAGN:jet_launch_dir"); + + bp->jet_minimum_reservoir_mass + = parser_get_param_float(params, + "ObsidianAGN:jet_minimum_reservoir_mass_Msun"); + bp->jet_minimum_reservoir_mass /= bp->mass_to_solar_mass; + + bp->lum_thresh_always_jet + = parser_get_opt_param_float(params, + "ObsidianAGN:lum_thresh_always_jet_1e45_erg_s", 0.f); + bp->lum_thresh_always_jet *= 1.e45 * units_cgs_conversion_factor(us, UNIT_CONV_TIME) / + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); + + /* We need to keep epsilon_r continuous over all M_dot,BH/M_dot,Edd */ + bp->epsilon_r = eta_at_slim_disk_boundary; + if (bp->epsilon_r > 1.f) error("Somehow epsilon_r is greater than 1.0."); + + bp->adaf_coupling = + parser_get_param_float(params, "ObsidianAGN:adaf_coupling"); + bp->adaf_z_scaling = + parser_get_opt_param_float(params, "ObsidianAGN:adaf_z_scaling", 0.f); + bp->quasar_coupling = + parser_get_param_float(params, "ObsidianAGN:quasar_coupling"); + bp->quasar_luminosity_thresh = + parser_get_opt_param_float(params, "ObsidianAGN:quasar_lum_thresh_1e45_erg_s", 0.f); + bp->quasar_luminosity_thresh *= units_cgs_conversion_factor(us, UNIT_CONV_TIME) / + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY) * 1.e45; + bp->slim_disk_coupling = parser_get_opt_param_float(params, + "ObsidianAGN:slim_disk_coupling", bp->quasar_coupling); + + /* These are for momentum constrained winds */ + bp->quasar_wind_momentum_flux = parser_get_opt_param_float(params, + "ObsidianAGN:quasar_wind_momentum_flux", 20.f); + bp->quasar_wind_speed = + parser_get_param_float(params, "ObsidianAGN:quasar_wind_speed_km_s"); + bp->quasar_wind_speed *= bp->kms_to_internal; + + const double recouple_distance_non_jet_kpc = 1.5; + const double quasar_velocity_kpc_s = + (fabs(bp->quasar_wind_speed) / bp->kms_to_internal) * kpc_per_km; + const double f_quasar_recouple = + recouple_distance_non_jet_kpc / (quasar_velocity_kpc_s * age_s); + + bp->quasar_decouple_time_factor = + parser_get_opt_param_float(params, + "ObsidianAGN:quasar_decouple_time_factor", f_quasar_recouple); + + bp->quasar_wind_dir = + parser_get_param_int(params, "ObsidianAGN:quasar_wind_dir"); + + bp->quasar_wind_mass_loading = bp->quasar_wind_momentum_flux * + fabs(bp->quasar_coupling) * bp->epsilon_r * + (phys_const->const_speed_light_c / fabs(bp->quasar_wind_speed)); + bp->quasar_f_accretion = 1.f / (1.f + bp->quasar_wind_mass_loading); + + const double slim_disk_wind_momentum_flux = parser_get_opt_param_float(params, + "ObsidianAGN:slim_disk_wind_momentum_flux", bp->quasar_wind_momentum_flux); + + bp->slim_disk_wind_speed = parser_get_opt_param_float(params, + "ObsidianAGN:slim_disk_wind_speed_km_s", + bp->quasar_wind_speed / bp->kms_to_internal); + bp->slim_disk_wind_speed *= bp->kms_to_internal; + + bp->slim_disk_wind_dir = + parser_get_param_int(params, "ObsidianAGN:slim_disk_wind_dir"); + + const double slim_disk_velocity_kpc_s = + (fabs(bp->slim_disk_wind_speed) / bp->kms_to_internal) * kpc_per_km; + const double f_slim_disk_recouple = + recouple_distance_non_jet_kpc / (slim_disk_velocity_kpc_s * age_s); + + bp->slim_disk_decouple_time_factor = + parser_get_opt_param_float(params, + "ObsidianAGN:slim_disk_decouple_time_factor", f_slim_disk_recouple); + + /* Set the slim disk mass loading to be continuous at the + * eta upper boundary. Compute the phi term to solve for the + * accretion fraction */ + bp->slim_disk_phi = + slim_disk_wind_momentum_flux * fabs(bp->slim_disk_coupling) * + (phys_const->const_speed_light_c / fabs(bp->slim_disk_wind_speed)); + const double slim_disk_wind_mass_loading = bp->slim_disk_phi * bp->epsilon_r; + + bp->slim_disk_jet_active = + parser_get_param_int(params, "ObsidianAGN:slim_disk_jet_active"); + + bp->adaf_disk_efficiency = + parser_get_param_float(params, "ObsidianAGN:adaf_disk_efficiency"); + + bp->adaf_kick_factor = + parser_get_opt_param_float(params, + "ObsidianAGN:adaf_kick_factor", + 0.5f); + if (bp->adaf_kick_factor < 0.f || bp->adaf_kick_factor > 1.f) { + error("adaf_kick_factor must be >= 0 and <= 1."); + } + + bp->adaf_wind_speed = parser_get_opt_param_float(params, + "ObsidianAGN:adaf_wind_speed_km_s", 0.f); + bp->adaf_wind_speed *= bp->kms_to_internal; + + const float f_psi = parser_get_opt_param_float(params, + "ObsidianAGN:adaf_f_quasar_psi", -1.f); + if (f_psi > 1.f) { + error("adaf_f_quasar_psi must be <= 1."); + } + + bp->adaf_wind_dir = + parser_get_param_int(params, "ObsidianAGN:adaf_wind_dir"); + + float jet_subgrid_mass_loading + = 2.f * bp->jet_efficiency * + (phys_const->const_speed_light_c / bp->jet_subgrid_velocity) * + (phys_const->const_speed_light_c / bp->jet_subgrid_velocity); + + /* f_acc = 1 / (1 + psi_jet,sub + psi_adaf) must still be true, but f_acc + * is fixed at quasar_f_accretion if f_psi > 0 + */ + if (f_psi > 0.f) { + + /* This is always true in the negative case */ + bp->adaf_f_accretion = bp->quasar_f_accretion; + + const double jet_eff_psi_quasar = + 2. * bp->jet_efficiency / bp->quasar_wind_mass_loading; + if (jet_eff_psi_quasar > 1.) { + error("The jet efficiency is too high or the quasar wind mass loading" + " is too low for your choice of how to distribute energy in" + " the ADAF mode."); + } + + const double psi_jet_subgrid = + bp->quasar_wind_mass_loading * (1. - f_psi); + const double c_frac = sqrt(2. * bp->jet_efficiency / psi_jet_subgrid); + + /* Do not exceed the speed-of-light */ + if (c_frac >= 1.) { + const double f_max = + 1. - jet_eff_psi_quasar; + error("Cannot request more than f = %g of the quasar wind mass " + "loading as the ADAF mass loading since it violates the " + "speed-of-light in the sub-grid jet velocity.", f_max); + } + + bp->jet_subgrid_velocity = c_frac * phys_const->const_speed_light_c; + + /* Reset the sub-grid mass loading with the new velocity */ + jet_subgrid_mass_loading = + 2.f * bp->jet_efficiency * + (phys_const->const_speed_light_c / bp->jet_subgrid_velocity) * + (phys_const->const_speed_light_c / bp->jet_subgrid_velocity); + + bp->adaf_wind_mass_loading = f_psi * bp->quasar_wind_mass_loading; + + const double adaf_eps = bp->adaf_coupling * bp->adaf_disk_efficiency; + bp->adaf_wind_speed = sqrt(2. * adaf_eps / bp->adaf_wind_mass_loading); + bp->adaf_wind_speed *= phys_const->const_speed_light_c; + if (bp->adaf_wind_speed > bp->jet_subgrid_velocity) { + error("The ADAF wind speed is above the sub-grid jet velocity. Are " + "you sure this is right?"); + } + } + else { + if (f_psi < 0.f) { + /* Heat everything in the kernel in this case */ + bp->adaf_wind_mass_loading = 0.f; + + /* This is always true in the negative case */ + bp->adaf_f_accretion = 1.f / (1.f + jet_subgrid_mass_loading); + } + else { + if (bp->adaf_wind_speed > 0.f) { + bp->adaf_wind_mass_loading = + 2.f * bp->adaf_coupling * bp->adaf_disk_efficiency; + bp->adaf_wind_mass_loading *= pow( + phys_const->const_speed_light_c / bp->adaf_wind_speed, + 2.f + ); + bp->adaf_f_accretion = 1.f / + (1.f + jet_subgrid_mass_loading + bp->adaf_wind_mass_loading); + } + else { + error("adaf_wind_speed_km_s must be non-zero in this case!"); + } + } + } + + /* Do not decouple the ADAF winds */ + bp->adaf_decouple_time_factor = 0.; + + bp->adaf_maximum_temperature = + parser_get_opt_param_float(params, + "ObsidianAGN:adaf_maximum_temperature_K", + 5.e7f); + bp->adaf_maximum_temperature *= T_K_to_int; + + bp->adaf_heating_n_H_threshold_cgs = parser_get_opt_param_float( + params, "ObsidianAGN:adaf_heating_n_H_threshold_cgs", 0.13f); + + bp->adaf_heating_T_threshold_cgs = parser_get_opt_param_float( + params, "ObsidianAGN:adaf_heating_T_threshold_cgs", 5.0e5f); + + bp->adaf_mass_limit = + parser_get_param_float(params, "ObsidianAGN:adaf_mass_limit_Msun"); + bp->adaf_mass_limit /= bp->mass_to_solar_mass; + + bp->adaf_mass_limit_spread = + parser_get_opt_param_float(params, "ObsidianAGN:adaf_mass_limit_spread_Msun", 0.f); + bp->adaf_mass_limit_spread /= bp->mass_to_solar_mass; + + bp->adaf_mass_limit_a_scaling = + parser_get_opt_param_float(params, "ObsidianAGN:adaf_mass_limit_a_scaling", 0.f); + + bp->adaf_mass_limit_a_min = + parser_get_opt_param_float(params, "ObsidianAGN:adaf_mass_limit_a_min", 0.f); + + bp->adaf_cooling_shutoff_factor = + parser_get_opt_param_float(params, + "ObsidianAGN:adaf_cooling_shutoff_factor", + -1.f); + + /* Always use nibbling in Obsidian */ + bp->use_nibbling = 1; + + bp->bondi_use_all_gas = + parser_get_opt_param_int(params, + "ObsidianAGN:bondi_use_all_gas", + 0); + + bp->bondi_alpha = + parser_get_opt_param_float(params, + "ObsidianAGN:bondi_alpha", + 1.f); + + bp->minimum_black_hole_mass_unresolved = + parser_get_param_float(params, + "ObsidianAGN:minimum_black_hole_mass_unresolved_Msun"); + bp->minimum_black_hole_mass_unresolved /= bp->mass_to_solar_mass; + + bp->minimum_black_hole_mass_v_kick = + parser_get_param_float(params, + "ObsidianAGN:minimum_black_hole_mass_v_kick_Msun"); + bp->minimum_black_hole_mass_v_kick /= bp->mass_to_solar_mass; + + bp->minimum_v_kick_km_s = + parser_get_opt_param_float(params, "ObsidianAGN:minimum_v_kick_km_s", + 10.f); + + /* Reposition parameters --------------------------------- */ + + bp->max_reposition_mass = + parser_get_param_float(params, "ObsidianAGN:max_reposition_mass") * + phys_const->const_solar_mass; + bp->max_reposition_distance_ratio = + parser_get_param_float(params, "ObsidianAGN:max_reposition_distance_ratio"); + + bp->with_reposition_velocity_threshold = parser_get_param_int( + params, "ObsidianAGN:with_reposition_velocity_threshold"); + + if (bp->with_reposition_velocity_threshold) { + bp->max_reposition_velocity_ratio = parser_get_param_float( + params, "ObsidianAGN:max_reposition_velocity_ratio"); + + /* Prevent nonsensical input */ + if (bp->max_reposition_velocity_ratio <= 0) + error("max_reposition_velocity_ratio must be positive, not %f.", + bp->max_reposition_velocity_ratio); + + bp->min_reposition_velocity_threshold = parser_get_param_float( + params, "ObsidianAGN:min_reposition_velocity_threshold"); + /* Convert from km/s to internal units */ + bp->min_reposition_velocity_threshold *= + (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs)); + } + + bp->set_reposition_speed = + parser_get_param_int(params, "ObsidianAGN:set_reposition_speed"); + + if (bp->set_reposition_speed) { + bp->reposition_coefficient_upsilon = parser_get_param_float( + params, "ObsidianAGN:reposition_coefficient_upsilon"); + + /* Prevent the user from making silly wishes */ + if (bp->reposition_coefficient_upsilon <= 0) + error( + "reposition_coefficient_upsilon must be positive, not %f " + "km/s/M_sun.", + bp->reposition_coefficient_upsilon); + + /* Convert from km/s to internal units */ + bp->reposition_coefficient_upsilon *= + (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs)); + + /* Scaling parameters with BH mass and gas density */ + bp->reposition_reference_mass = + parser_get_param_float(params, "ObsidianAGN:reposition_reference_mass") * + phys_const->const_solar_mass; + bp->reposition_exponent_mass = parser_get_opt_param_float( + params, "ObsidianAGN:reposition_exponent_mass", 2.0); + bp->reposition_reference_n_H = + parser_get_param_float(params, "ObsidianAGN:reposition_reference_n_H"); + bp->reposition_exponent_n_H = parser_get_opt_param_float( + params, "ObsidianAGN:reposition_exponent_n_H", 1.0); + } + + bp->correct_bh_potential_for_repositioning = + parser_get_param_int(params, "ObsidianAGN:with_potential_correction"); + + /* Merger parameters ------------------------------------- */ + + bp->minor_merger_threshold = + parser_get_param_float(params, "ObsidianAGN:threshold_minor_merger"); + + bp->major_merger_threshold = + parser_get_param_float(params, "ObsidianAGN:threshold_major_merger"); + + char temp2[40]; + parser_get_param_string(params, "ObsidianAGN:merger_threshold_type", temp2); + if (strcmp(temp2, "CircularVelocity") == 0) + bp->merger_threshold_type = BH_mergers_circular_velocity; + else if (strcmp(temp2, "EscapeVelocity") == 0) + bp->merger_threshold_type = BH_mergers_escape_velocity; + else if (strcmp(temp2, "DynamicalEscapeVelocity") == 0) + bp->merger_threshold_type = BH_mergers_dynamical_escape_velocity; + else + error( + "The BH merger model must be either CircularVelocity, EscapeVelocity, " + "or DynamicalEscapeVelocity, not %s", + temp2); + + bp->max_merging_distance_ratio = + parser_get_param_float(params, "ObsidianAGN:merger_max_distance_ratio"); + + /* ---- Black hole time-step properties ------------------ */ + + const double time_step_min_Myr = parser_get_opt_param_float( + params, "ObsidianAGN:minimum_timestep_Myr", FLT_MAX); + + bp->time_step_min = time_step_min_Myr * Myr_in_cgs / + units_cgs_conversion_factor(us, UNIT_CONV_TIME); + + /* Common conversion factors ----------------------------- */ + + /* Calculate conversion factor from rho to n_H. + * Note this assumes primoridal abundance */ + const double X_H = hydro_props->hydrogen_mass_fraction; + bp->rho_to_n_cgs = + (X_H / m_p) * units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); + + bp->length_to_parsec = 1.f / phys_const->const_parsec; + + bp->time_to_yr = 1.f / phys_const->const_year; + + /* Some useful conversion values */ + bp->conv_factor_density_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + bp->conv_factor_energy_rate_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY) / + units_cgs_conversion_factor(us, UNIT_CONV_TIME); + bp->conv_factor_length_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); + bp->conv_factor_mass_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_MASS); + bp->conv_factor_time_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_TIME); + bp->conv_factor_specific_energy_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); + + /* Useful constants */ + bp->proton_mass_cgs_inv = + 1. / (phys_const->const_proton_mass * + units_cgs_conversion_factor(us, UNIT_CONV_MASS)); + + bp->T_K_to_int = T_K_to_int; + + if (engine_rank == 0) { + message("Black holes kernel: %s with eta=%f (%.2f neighbours).", + kernel_name, bp->eta_neighbours, bp->target_neighbours); + + message("Black holes relative tolerance in h: %.5f (+/- %.4f neighbours).", + bp->h_tolerance, bp->delta_neighbours); + + message("Black hole model is Rennehan+24"); + message("Black hole jet velocity is %g km/s", + bp->jet_velocity / bp->kms_to_internal); + if (bp->jet_loading_type == BH_jet_momentum_loaded) { + message("Black hole jet loading (momentum) is %g", + bp->jet_mass_loading); + } + else if (bp->jet_loading_type == BH_jet_mixed_loaded) { + message("Black hole jet loading (mixed) is %g", + bp->jet_mass_loading); + } + else { + message("Black hole jet loading (energy) is %g", + bp->jet_mass_loading); + } + message("Black hole subgrid jet velocity is %g km/s", + bp->jet_subgrid_velocity / bp->kms_to_internal); + message("Black hole subgrid jet loading (energy) is %g", + jet_subgrid_mass_loading); + message("Black hole jet efficiency is %g", + bp->jet_efficiency); + message("Black hole jet recouple factor is %g", + f_jet_recouple); + message("Black hole quasar radiative efficiency is %g", + bp->epsilon_r); + if (bp->quasar_luminosity_thresh > 0.f) { + message("Black hole quasar coupling %g is boosted above Lbol>%g erg/s", + bp->quasar_coupling, bp->quasar_luminosity_thresh * + bp->conv_factor_energy_rate_to_cgs); + } + if (bp->lum_thresh_always_jet > 0.f) { + message("Black hole jet mode always on above Lbol>%g erg/s", + bp->lum_thresh_always_jet * + bp->conv_factor_energy_rate_to_cgs); + } + message("Black hole quasar wind speed is %g km/s", + bp->quasar_wind_speed / bp->kms_to_internal); + message("Black hole quasar mass loading (momentum) is %g", + bp->quasar_wind_mass_loading); + message("Black hole quasar f_accretion is %g", + bp->quasar_f_accretion); + message("Black hole quasar recouple factor is %g", + f_quasar_recouple); + message("Black hole slim disk wind speed is %g km/s", + bp->slim_disk_wind_speed / bp->kms_to_internal); + message("Black hole slim disk mass loading (momentum) is %g " + "(at the eta=%g boundary)", + slim_disk_wind_mass_loading, bp->epsilon_r); + message("Black hole slim disk recouple factor is %g", + f_slim_disk_recouple); + message("Black hole ADAF mass loading (energy) is %g", + bp->adaf_wind_mass_loading); + message("Black hole ADAF f_accretion is %g", + bp->adaf_f_accretion); + message("Black hole ADAF v_wind is %g km/s (thermally dumped)", + bp->adaf_wind_speed / bp->kms_to_internal); + } +} + +/** + * @brief Computes the mass limit above which ADAF mode is allowed. + * + * @param bp The black hole particle. + * @param props The properties of the black hole scheme. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static +double get_black_hole_adaf_mass_limit(const struct bpart* const bp, + const struct black_holes_props* props, const struct cosmology* cosmo) { + double mass_min = fabs(props->adaf_mass_limit); + if (props->adaf_mass_limit < 0.) mass_min *= + pow(fmax(cosmo->a, props->adaf_mass_limit_a_min), props->adaf_mass_limit_a_scaling); + mass_min += 0.01f * (float)(bp->id % 100) * props->adaf_mass_limit_spread; + return mass_min; +} + +/** + * @brief Compute velocity of jet. + * + * @param bi Black hole particle. + * @param cosmo The current cosmological model. + * @param props The properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static float +black_hole_compute_jet_velocity( + const struct bpart *bi, + const struct cosmology *cosmo, + const struct black_holes_props *props) { + + float jet_velocity = fabs(props->jet_velocity); + /* If the user supplied a variable jet velocity we must recalculate the + * mass loading based on the variable jet velocity */ + if (props->jet_velocity < 0.f || props->jet_velocity_scaling_with_BH_mass > 0.f + || props->jet_velocity_scaling_with_BH_lum > 0.f) { + if (props->jet_velocity < 0.f) { + jet_velocity *= powf(cosmo->H / cosmo->H0, 1.f / 3.f); + } + if (props->jet_velocity_scaling_with_BH_mass > 0.f) { + float BH_mass_scaled = bi->subgrid_mass * props->mass_to_solar_mass * 1.0e-8; + BH_mass_scaled = fmax(BH_mass_scaled, 1.f); + jet_velocity *= powf(BH_mass_scaled, props->jet_velocity_scaling_with_BH_mass); + } + if (props->jet_velocity_scaling_with_BH_lum > 0.f) { + float BH_lum_scaled = bi->radiative_luminosity * + props->conv_factor_energy_rate_to_cgs * 1.e-45; + BH_lum_scaled = fmax(BH_lum_scaled, 1.f); + jet_velocity *= powf(BH_lum_scaled, props->jet_velocity_scaling_with_BH_lum); + } + } + return jet_velocity; +} + +/** + * @brief Compute ramp-up of jet energy above mass limit + * + * @param bi Black hole particle. + * @param cosmo The current cosmological model. + * @param props The properties of the black hole scheme. + */ +__attribute__((always_inline)) INLINE static float +black_hole_compute_jet_energy_ramp( + const struct bpart *bi, + const struct cosmology *cosmo, + const struct black_holes_props *props) { + + /* Only do jet if above ADAF mass limit */ + const float my_adaf_mass_limit = get_black_hole_adaf_mass_limit(bi, props, cosmo); + + /* Compute ramp-up of jet feedback energy */ + float jet_ramp = 0.f; + /* Ramp-up above ADAF min mass */ + if (bi->subgrid_mass > my_adaf_mass_limit) { + jet_ramp = fmin(bi->subgrid_mass / my_adaf_mass_limit - 1.f, 1.f); + } + return jet_ramp; +} + + +/** + * @brief Write a black_holes_props struct to the given FILE as a stream of + * bytes. + * + * @param props the black hole properties struct + * @param stream the file stream + */ +INLINE static void black_holes_struct_dump( + const struct black_holes_props *props, FILE *stream) { + restart_write_blocks((void *)props, sizeof(struct black_holes_props), 1, + stream, "black_holes props", "black holes props"); +} + +/** + * @brief Restore a black_holes_props struct from the given FILE as a stream of + * bytes. + * + * @param props the black hole properties struct + * @param stream the file stream + */ +INLINE static void black_holes_struct_restore( + const struct black_holes_props *props, FILE *stream) { + restart_read_blocks((void *)props, sizeof(struct black_holes_props), 1, + stream, NULL, "black holes props"); +} + +#endif /* SWIFT_OBSIDIAN_BLACK_HOLES_PROPERTIES_H */ diff --git a/src/black_holes/Obsidian/black_holes_struct.h b/src/black_holes/Obsidian/black_holes_struct.h new file mode 100644 index 0000000000..56ca10f41c --- /dev/null +++ b/src/black_holes/Obsidian/black_holes_struct.h @@ -0,0 +1,143 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_BLACK_HOLES_STRUCT_OBSIDIAN_H +#define SWIFT_BLACK_HOLES_STRUCT_OBSIDIAN_H + +/* Standard headers */ +#include + +/* Local includes */ +#include "inline.h" + +/** + * @brief Black holes-related fields carried by each *gas* particle. + */ +struct black_holes_part_data { + + /*! ID of the black-hole that will swallow this #part. */ + long long swallow_id; + + /*! ID of the black-hole that will kick this particle as a jet particle */ + long long jet_id; + + /*! ID of the black-hole that will heat this particle in the ADAF mode */ + long long adaf_id; + + /*! Gravitational potential of the particle (for repositioning) */ + float potential; +}; + +/** + * @brief Black holes-related fields carried by each *BH* particle. + */ +struct black_holes_bpart_data { + + /*! ID of the black-hole that will swallow this #bpart. */ + long long swallow_id; + + /*! Mass of the black-hole that will swallow this #bpart. */ + float swallow_mass; +}; + +/** + * @brief Update a given #part's BH data field to mark the particle has + * not yet been swallowed. + * + * @param p_data The #part's #black_holes_part_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_part_as_not_swallowed(struct black_holes_part_data* p_data) { + + p_data->swallow_id = -1; + p_data->jet_id = -1; + p_data->adaf_id = -1; +} + +/** + * @brief Reset the particle-carried potential at the start of a time-step. + * + * @param p_data The #part's black hole data. + */ +__attribute__((always_inline)) INLINE static void black_holes_init_potential( + struct black_holes_part_data* p_data) { + p_data->potential = FLT_MAX; +} + +/** + * @brief Update a given #part's BH data field to mark the particle has + * having been been swallowed. + * + * @param p_data The #part's #black_holes_part_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_part_as_swallowed(struct black_holes_part_data* p_data) { + + p_data->swallow_id = -2; +} + +/** + * @brief Return the ID of the BH that should swallow this #part. + * + * @param p_data The #part's #black_holes_part_data structure. + */ +__attribute__((always_inline)) INLINE static long long +black_holes_get_part_swallow_id(struct black_holes_part_data* p_data) { + + return p_data->swallow_id; +} + +/** + * @brief Update a given #bpart's BH data field to mark the particle has + * not yet been swallowed. + * + * @param p_data The #bpart's #black_holes_bpart_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_bpart_as_not_swallowed(struct black_holes_bpart_data* p_data) { + + p_data->swallow_id = -1; + p_data->swallow_mass = 0.f; +} + +/** + * @brief Update a given #bpart's BH data field to mark the particle has + * having been been swallowed. + * + * @param p_data The #bpart's #black_holes_bpart_data structure. + */ +__attribute__((always_inline)) INLINE static void +black_holes_mark_bpart_as_merged(struct black_holes_bpart_data* p_data) { + + p_data->swallow_id = -2; + p_data->swallow_mass = -1.f; +} + +/** + * @brief Return the ID of the BH that should swallow this #bpart. + * + * @param p_data The #bpart's #black_holes_bpart_data structure. + */ +__attribute__((always_inline)) INLINE static long long +black_holes_get_bpart_swallow_id(struct black_holes_bpart_data* p_data) { + + return p_data->swallow_id; +} + +#endif /* SWIFT_BLACK_HOLES_STRUCT_OBSIDIAN_H */ diff --git a/src/black_holes_debug.h b/src/black_holes_debug.h index 1a00ff2c5d..a33d6f5b40 100644 --- a/src/black_holes_debug.h +++ b/src/black_holes_debug.h @@ -29,6 +29,8 @@ #include "./black_holes/EAGLE/black_holes_debug.h" #elif defined(BLACK_HOLES_SPIN_JET) #include "./black_holes/SPIN_JET/black_holes_debug.h" +#elif defined(BLACK_HOLES_OBSIDIAN) +#include "./black_holes/Obsidian/black_holes_debug.h" #else #error "Invalid choice of BH model" #endif diff --git a/src/black_holes_iact.h b/src/black_holes_iact.h index 01fcd73160..f31736de18 100644 --- a/src/black_holes_iact.h +++ b/src/black_holes_iact.h @@ -29,6 +29,8 @@ #include "./black_holes/EAGLE/black_holes_iact.h" #elif defined(BLACK_HOLES_SPIN_JET) #include "./black_holes/SPIN_JET/black_holes_iact.h" +#elif defined(BLACK_HOLES_OBSIDIAN) +#include "./black_holes/Obsidian/black_holes_iact.h" #else #error "Invalid choice of black hole model" #endif diff --git a/src/black_holes_io.h b/src/black_holes_io.h index 0507cef106..5f6737dff3 100644 --- a/src/black_holes_io.h +++ b/src/black_holes_io.h @@ -31,6 +31,8 @@ #include "./black_holes/EAGLE/black_holes_io.h" #elif defined(BLACK_HOLES_SPIN_JET) #include "./black_holes/SPIN_JET/black_holes_io.h" +#elif defined(BLACK_HOLES_OBSIDIAN) +#include "./black_holes/Obsidian/black_holes_io.h" #else #error "Invalid choice of BH model" #endif diff --git a/src/black_holes_properties.h b/src/black_holes_properties.h index c54db15a93..bb4488cdc9 100644 --- a/src/black_holes_properties.h +++ b/src/black_holes_properties.h @@ -29,6 +29,8 @@ #include "./black_holes/EAGLE/black_holes_properties.h" #elif defined(BLACK_HOLES_SPIN_JET) #include "./black_holes/SPIN_JET/black_holes_properties.h" +#elif defined(BLACK_HOLES_OBSIDIAN) +#include "./black_holes/Obsidian/black_holes_properties.h" #else #error "Invalid choice of black hole model" #endif diff --git a/src/black_holes_struct.h b/src/black_holes_struct.h index 09027e83f7..a52ec1f57b 100644 --- a/src/black_holes_struct.h +++ b/src/black_holes_struct.h @@ -37,6 +37,8 @@ #include "./black_holes/EAGLE/black_holes_struct.h" #elif defined(BLACK_HOLES_SPIN_JET) #include "./black_holes/SPIN_JET/black_holes_struct.h" +#elif defined(BLACK_HOLES_OBSIDIAN) +#include "./black_holes/Obsidian/black_holes_struct.h" #else #error "Invalid choice of black hole model." #endif diff --git a/src/cooling/KIARA/cooling.c b/src/cooling/KIARA/cooling.c index 8ad1b9a482..f2dd9af078 100644 --- a/src/cooling/KIARA/cooling.c +++ b/src/cooling/KIARA/cooling.c @@ -1267,88 +1267,14 @@ __attribute__((always_inline)) INLINE void firehose_cooling_and_dust( } /** - * @brief Apply the cooling function to a particle. + * @brief Set subgrid ISM properties and initialise chemistry * - * @param phys_const The physical constants in internal units. - * @param us The internal system of units. - * @param cosmo The current cosmological model. - * @param hydro_props The #hydro_props. - * @param floor_props Properties of the entropy floor. * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. - * @param xp Pointer to the particle' extended data. - * @param dt The time-step of this particle. - * @param dt_therm The time-step operator used for thermal quantities. - * @param time The current time (since the Big Bang or start of the run) in - * internal units. */ -void cooling_cool_part(const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct hydro_props* hydro_props, - const struct entropy_floor_properties* floor_props, - const struct pressure_floor_props *pressure_floor_props, +__attribute__((always_inline)) INLINE void cooling_init_chemistry( const struct cooling_function_data* restrict cooling, - const struct fof_props* fof_props, - struct part* restrict p, struct xpart* restrict xp, - const double dt, const double dt_therm, - const double time) { - - /* Compute cooling time and other quantities needed for firehose */ - firehose_cooling_and_dust(phys_const, us, cosmo, hydro_props, - cooling, p, xp, dt); - - /* No cooling if particle is decoupled */ - if (p->decoupled) { - /* Make sure these are always set for the wind particles */ - p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); - p->cooling_data.subgrid_temp = 0.; - p->cooling_data.subgrid_fcold = 0.f; - - return; - } - - /* Collect information about galaxy that the particle belongs to */ - size_t group_id = p->gpart->fof_data.group_id; - float galaxy_mstar = fof_props->group_stellar_mass[group_id]; - float galaxy_ssfr = fof_props->group_star_formation_rate[group_id] / galaxy_mstar; - - /* Never cool if there is a cooling shut off, let the hydro do its magic - if (p->feedback_data.cooling_shutoff_delay_time > 0.f) { - p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); - p->cooling_data.subgrid_temp = 0.; - p->cooling_data.subgrid_fcold = 0.f; - - return; - }*/ - - /* Update the subgrid properties */ - cooling_set_particle_subgrid_properties( phys_const, us, - cosmo, hydro_props, floor_props, cooling, p, xp); - - /* No cooling happens over zero time */ - if (dt == 0.f || dt_therm == 0.f) { - return; - } - - /* If less that thermal_time has passed since last cooling, don't cool - * KIARA can't use this because the dust needs to be formed/destroyed over dt_therm - if (time - xp->cooling_data.time_last_event < cooling->thermal_time) { - return; - }*/ - - /* Compute the ISRF */ - p->cooling_data.G0 = fmax(cooling_compute_G0(p, p->cooling_data.subgrid_dens, cooling, galaxy_mstar, galaxy_ssfr, dt), 0.); - - /* Current energy */ - const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo); - //const float T_old = cooling_get_temperature( phys_const, hydro_props, us, cosmo, cooling, p, xp); // for debugging only - - /* Compute the entropy floor */ - //const double T_warm = entropy_floor_temperature(p, cosmo, floor_props); - const double T_warm = warm_ISM_temperature(p, cooling, phys_const, cosmo); - const double u_warm = - cooling_convert_temp_to_u(T_warm, xp->cooling_data.e_frac, cooling, p); + struct part* restrict p) { /* If it's eligible for SF and metal-free, crudely self-enrich to very small level; needed to kick-start Grackle dust. Arbitrary @@ -1411,8 +1337,54 @@ void cooling_cool_part(const struct phys_const* restrict phys_const, " particle id=%lld", p->id); } } +} + +/** + * @brief Apply the cooling function to a particle. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param floor_props Properties of the entropy floor. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the particle' extended data. + * @param dt The time-step of this particle. + * @param dt_therm The time-step operator used for thermal quantities. + * @param time The current time (since the Big Bang or start of the run) in + * internal units. + */ +__attribute__((always_inline)) INLINE void cooling_do_grackle_cooling( + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, + const double dt, const double dt_therm, + const double time) { + + /* Self-enrich gas if very low metallicity */ + cooling_init_chemistry(cooling, p); + + /* Collect information about galaxy that the particle belongs to */ + const float galaxy_mstar = p->galaxy_data.stellar_mass; + const float galaxy_ssfr = p->galaxy_data.specific_sfr; + + /* Compute the ISRF */ + p->cooling_data.G0 = fmax(cooling_compute_G0(p, p->cooling_data.subgrid_dens, cooling, galaxy_mstar, galaxy_ssfr, dt), 0.); + + /* Compute the entropy floor */ + //const double T_warm = entropy_floor_temperature(p, cosmo, floor_props); + const double T_warm = warm_ISM_temperature(p, cooling, phys_const, cosmo); + const double u_warm = + cooling_convert_temp_to_u(T_warm, xp->cooling_data.e_frac, cooling, p); /* Do grackle cooling */ + const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo); + //const float T_old = cooling_get_temperature( phys_const, hydro_props, us, cosmo, cooling, p, xp); // for debugging only gr_float u_new = u_old; u_new = cooling_grackle_driver(phys_const, us, cosmo, hydro_props, cooling, p, xp, dt, T_warm, 0); @@ -1512,6 +1484,53 @@ void cooling_cool_part(const struct phys_const* restrict phys_const, xp->cooling_data.time_last_event = time; } +/** + * @brief Apply the cooling function to a particle. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param floor_props Properties of the entropy floor. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the particle' extended data. + * @param dt The time-step of this particle. + * @param dt_therm The time-step operator used for thermal quantities. + * @param time The current time (since the Big Bang or start of the run) in + * internal units. + */ +void cooling_cool_part(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, + const struct pressure_floor_props *pressure_floor_props, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, + const double dt, const double dt_therm, + const double time) { + + /* Compute cooling time and other quantities needed for firehose */ + firehose_cooling_and_dust(phys_const, us, cosmo, hydro_props, + cooling, p, xp, dt); + + /* Update the subgrid properties */ + cooling_set_particle_subgrid_properties( phys_const, us, + cosmo, hydro_props, floor_props, cooling, p, xp); + + /* No cooling if particle is decoupled */ + if (p->decoupled) return; + + /* No cooling happens over zero time */ + if (dt == 0.f || dt_therm == 0.f) return; + + /* Do the cooling and chemistry */ + cooling_do_grackle_cooling(phys_const, us, cosmo, hydro_props, + floor_props, cooling, + p, xp, dt, dt_therm, time); +} + /** * @brief Set the subgrid properties (rho, T) of the gas particle for use * in SF routine @@ -1532,6 +1551,16 @@ void cooling_set_particle_subgrid_properties( const struct cooling_function_data *cooling, struct part *p, struct xpart *xp) { + /* No subgrid ISM if particle is decoupled */ + if (p->decoupled) { + /* Make sure these are always set for the wind particles */ + p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); + p->cooling_data.subgrid_temp = 0.; + p->cooling_data.subgrid_fcold = 0.f; + + return; + } + /* Get temperature of overall particle */ const double u = hydro_get_physical_internal_energy(p, xp, cosmo); const float temperature = diff --git a/src/cooling/KIARA/cooling.h b/src/cooling/KIARA/cooling.h index 75bb4fcb29..4b37e2746e 100644 --- a/src/cooling/KIARA/cooling.h +++ b/src/cooling/KIARA/cooling.h @@ -165,7 +165,6 @@ void cooling_cool_part(const struct phys_const* restrict phys_const, const struct entropy_floor_properties* floor_props, const struct pressure_floor_props *pressure_floor_props, const struct cooling_function_data* restrict cooling, - const struct fof_props* fof_props, struct part* restrict p, struct xpart* restrict xp, const double dt, const double dt_therm, const double time); diff --git a/src/feedback.h b/src/feedback.h index 65a64704c1..f8c3ff48a7 100644 --- a/src/feedback.h +++ b/src/feedback.h @@ -34,6 +34,9 @@ #include "./feedback/GEAR/feedback.h" #elif defined(FEEDBACK_AGORA) #include "./feedback/AGORA/feedback.h" +#elif defined(FEEDBACK_KIARA) +#include "./feedback/KIARA/feedback.h" +#define EXTRA_STAR_LOOPS #else #error "Invalid choice of feedback model" #endif diff --git a/src/feedback/KIARA/feedback.c b/src/feedback/KIARA/feedback.c new file mode 100644 index 0000000000..c4a0da4683 --- /dev/null +++ b/src/feedback/KIARA/feedback.c @@ -0,0 +1,2266 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ + +/* This file's header */ +#include "feedback.h" + +/* Local includes. */ +#include "hydro_properties.h" +#include "inline.h" +#include "random.h" +#include "timers.h" +#include "timestep_sync_part.h" + + +#if COOLING_GRACKLE_MODE >= 2 + +/* This seems to be needed to get N_SNe and mass loss rates + * correct in chem5 Kroupa/Chabrier. Not sure why. */ +#define IMF_FUDGE_FACTOR 1.0f + +/** + * @brief Return log10 of the Habing band luminosity for a given star + * based on its age and metallicity, in erg/s + * + * @param sp The #spart outputting the radiation + * @param age The age of the star in internal units + */ +double feedback_get_lum_from_star_particle(const struct spart* sp, + double age, + const struct feedback_props* fb_props) { + + /* Get age, convert to Myr */ + age *= fb_props->time_to_Myr; + if (age < 1.) age = 1.; /* lum is roughly constant prior to 1 Myr */ + /* Stars past 1000 Myr have negligible Habing flux; return very small log10 */ + if (age > 1000.) return -20.f; + + age = log10(age); + + /* Get mass in units of 10^6 Mo, which is the units of the STARBURST99 models */ + double logmass6 = log10(sp->mass * fb_props->mass_to_solar_mass * 1.e-6); + + /* set up metallicity interpolation */ + double z = sp->chemistry_data.metal_mass_fraction_total; + double z_bins[5] = {0.04, 0.02, 0.008, 0.004, 0.001}; + double lum1, lum2, fhi = 0., flo = 1.; + + /* Interpolate luminosity in Habing band based on fits to STARBURST99 models + * (cf. G0_polyfit.py), for various metallicities + */ + if (age > 0.8) { /* Do older star case, well fit by power law */ + if (z > z_bins[0]) { + lum1 = 42.9568 - 1.66469 * age; + lum2 = lum1; + } + else if (z > z_bins[1]) { + lum1 = 42.9568 - 1.66469 * age; + lum2 = 42.9754 - 1.57329 * age; + fhi = LOG_INTERPOLATION(z, z_bins[0], z_bins[1]); + } + else if (z > z_bins[2]) { + lum1 = 42.9754 - 1.57329 * age; + lum2 = 43.003 - 1.49815 * age; + fhi = LOG_INTERPOLATION(z, z_bins[1], z_bins[2]); + } + else if (z > z_bins[3]) { + lum1 = 43.003 - 1.49815 * age; + lum2 = 43.0151 - 1.46258 * age; + fhi = LOG_INTERPOLATION(z, z_bins[2], z_bins[3]); + } + else if (z > z_bins[4]) { + lum1 = 43.0151 - 1.46258 * age; + lum2 = 43.0254 - 1.40997 * age; + fhi = LOG_INTERPOLATION(z, z_bins[3], z_bins[4]); + } + else { + lum1 = 43.0254 - 1.40997 * age; + lum2 = lum1; + } + } + else { /* Otherwise the star is very young and bright, so use more accurate 6th order polynomial fit */ + if (z > z_bins[0]) { + lum1 = 41.8537 + 6.40018 * pow(age, 1) - 46.6675 * pow(age, 2) + + 180.784 * pow(age, 3) - 373.188 * pow(age, 4) + + 374.251 * pow(age, 5) - 144.345 * pow(age, 6); + lum2 = lum1; + } + else if (z > z_bins[1]) { + lum1 = 41.8537 + 6.40018 * pow(age, 1) + - 46.6675 * pow(age, 2) + 180.784 * pow(age,3) + - 373.188 * pow(age, 4) + 374.251 * pow(age, 5) + - 144.345 * pow(age, 6); + lum2 = 41.3428 + 17.0277 * pow(age, 1) - 132.565 * pow(age, 2) + + 508.436 * pow(age, 3) - 998.223 * pow(age, 4) + + 954.621 * pow(age, 5) - 353.419 * pow(age, 6); + fhi = LOG_INTERPOLATION(z, z_bins[0], z_bins[1]); + } + else if (z > z_bins[2]) { + lum1 = 41.3428+17.0277*pow(age,1) -132.565*pow(age,2) + +508.436*pow(age,3) -998.223*pow(age,4) + +954.621*pow(age,5) -353.419*pow(age,6); + lum2 = 41.0623+22.0205*pow(age,1) -172.018*pow(age,2) + +655.587*pow(age,3) -1270.91*pow(age,4) + +1201.92*pow(age,5) -441.57*pow(age,6); + fhi = LOG_INTERPOLATION(z, z_bins[1], z_bins[2]); + } + else if (z > z_bins[3]) { + lum1 = 41.0623+22.0205*pow(age,1) -172.018*pow(age,2) + +655.587*pow(age,3) -1270.91*pow(age,4) + +1201.92*pow(age,5) -441.57*pow(age,6); + lum2 = 41.3442+16.0189*pow(age,1) -126.891*pow(age,2) + +488.303*pow(age,3) -945.774*pow(age,4) + +887.47*pow(age,5) -322.584*pow(age,6); + fhi = LOG_INTERPOLATION(z, z_bins[2], z_bins[3]); + } + else if (z > z_bins[4]) { + lum1 = 41.3442+16.0189*pow(age,1) -126.891*pow(age,2) + +488.303*pow(age,3) -945.774*pow(age,4) + +887.47*pow(age,5) -322.584*pow(age,6); + lum2 = 40.738+25.8218*pow(age,1) -185.778*pow(age,2) + +641.036*pow(age,3) -1113.61*pow(age,4) + +937.23*pow(age,5) -304.342*pow(age,6); + fhi = LOG_INTERPOLATION(z, z_bins[3], z_bins[4]); + } + else { + lum1 = 40.738 + 25.8218 * pow(age, 1) - 185.778 * pow(age, 2) + + 641.036 * pow(age, 3) - 1113.61 * pow(age, 4) + + 937.23 * pow(age, 5) - 304.342 * pow(age, 6); + lum2 = lum1; + } + } + + flo = 1. - fhi; + + /* return the interpolated Habing luminosity for this star in log10 erg/s */ + return lum1 * fhi + lum2 * flo + logmass6; +} + +void feedback_dust_production_condensation( + struct spart* sp, + double star_age, + const struct feedback_props* fb_props, + double delta_metal_mass[chemistry_element_count]) { + + const double *delta_table; + int k; + + /* Get age of star to separate AGB from SNII */ + star_age *= fb_props->time_to_Myr; + + /* initialize change in dust mass */ + for (k = 0; k < chemistry_element_count; k++) { + sp->feedback_data.delta_dust_mass[k] = 0.f; + } + + const double C_minus_O = + delta_metal_mass[chemistry_element_C] + - delta_metal_mass[chemistry_element_O]; + if (star_age > 100. && C_minus_O > 0.) { + /* Compute dust mass created in high-C/O AGB stars + * (atomic C forms graphite) + */ + sp->feedback_data.delta_dust_mass[chemistry_element_C] = + fb_props->delta_AGBCOG1[chemistry_element_C] * + (delta_metal_mass[chemistry_element_C] + - 0.75 * delta_metal_mass[chemistry_element_O]); + const double max_dust_C = + fb_props->max_dust_fraction * delta_metal_mass[chemistry_element_C]; + /* Cap the new dust mass formed to some fraction of total ejecta + * metals in that element + */ + if (sp->feedback_data.delta_dust_mass[chemistry_element_C] > max_dust_C) { + sp->feedback_data.delta_dust_mass[chemistry_element_C] = max_dust_C; + } + + /* Subtract this from ejecta metals */ + delta_metal_mass[chemistry_element_C] -= + sp->feedback_data.delta_dust_mass[chemistry_element_C]; + } + else { + /* Choose dust table: If age > 100 Myr, assume ejecta is from AGB, + * otherwise SNII + */ + if (star_age > 100.) { + delta_table = fb_props->delta_AGBCOL1; + } + else { + delta_table = fb_props->delta_SNII; + } + + /* Compute dust mass created in either SNII or low-C/O AGB stars + * (same type of dust, just different coefficients) + */ + for (k = chemistry_element_He; k < chemistry_element_count; k++) { + if (k == chemistry_element_O) {/* O in oxide of Mg, Si, S, Ca, (Ti), Fe */ + sp->feedback_data.delta_dust_mass[k] = + 16.0 * (delta_table[chemistry_element_Mg] * + delta_metal_mass[chemistry_element_Mg] / 24.305 + + delta_table[chemistry_element_Si] * + delta_metal_mass[chemistry_element_Si] / 28.0855 + + fb_props->delta_AGBCOL1[chemistry_element_S] * + delta_metal_mass[chemistry_element_S] / 32.065 + + fb_props->delta_AGBCOL1[chemistry_element_Ca] * + delta_metal_mass[chemistry_element_Ca] / 40.078 + + fb_props->delta_AGBCOL1[chemistry_element_Fe] * + delta_metal_mass[chemistry_element_Fe] / 55.845); + } + else { + sp->feedback_data.delta_dust_mass[k] = + delta_table[k] * delta_metal_mass[k]; + } + + const double max_dust = + fb_props->max_dust_fraction * delta_metal_mass[k]; + if (sp->feedback_data.delta_dust_mass[k] > max_dust) { + sp->feedback_data.delta_dust_mass[k] = max_dust; + } + + delta_metal_mass[k] -= sp->feedback_data.delta_dust_mass[k]; + } + } + +} +#endif + +/** + * @brief Run the Chem5 module that interpolates the yield tables and returns + * the ejected mass, metals, and unprocessed materials. + * + * @param sp The #spart to consider. + * @param age The stellar age in code units. + * @param fb_props The feedback properties. + * @param dt The current timestep. + * @param ejecta_energy The total ejected energy in code units. + * @param ejecta_mass The total ejected mass in code units. + * @param ejecta_unprocessed The unprocessed mass in code units. + * @param ejecta_metal_mass The metal masses for each element in chem5_element_count in code units. + */ +void feedback_get_ejecta_from_star_particle(const struct spart* sp, + double age, + const struct feedback_props* fb_props, + double dt, + double *N_SNe, + double *ejecta_energy, + double *ejecta_mass, + double *ejecta_unprocessed, + double ejecta_metal_mass[chem5_element_count]) { + int j, k, j1, j2, l, l1 = 0, l2 = 0, ll1 = 0, ll2 = 0, lll1 = 0, lll2 = 0; + double SW_R, SNII_R, SNII_U, SNII_E, SNII_Z[chem5_element_count]; + double SNII_ENE, SNIa_R, SNIa_E = 0., SNIa_Z[chem5_element_count]; + double SNn = 0., SWn = 0., ejecta_mass_Ia = 0.; + double SNIIa, SNIIb, z, lz; + + /* Convert to yr for code below */ + age *= fb_props->time_to_yr; + dt *= fb_props->time_to_yr; + + *ejecta_energy = 0.; + *ejecta_mass = 0.; + *ejecta_unprocessed = 0.; + for (k = 0; k < chem5_element_count; k++) ejecta_metal_mass[k] = 0.; + + /* @TODO What does "fb" mean? fb stage? */ + int fb = 0; + int fb_first = 0 + fb; + + if (sp->mass_init == sp->mass) fb_first = 1; + + z = sp->chemistry_data.metal_mass_fraction_total; + + /* [Fe/H] */ + double feh = -10.; + if (z < 1.e-10) { + lz = -10.; + } + else { + lz = log10(z); + feh = sp->chemistry_data.metal_mass_fraction[chemistry_element_Fe] / + sp->chemistry_data.metal_mass_fraction[chemistry_element_H]; + if (feh > 0.) feh = log10((feh / fb_props->Fe_mf) * fb_props->H_mf); + if (feh > fb_props->tables.SNLZ1R[NZSN1R - 1]) { + feh = fb_props->tables.SNLZ1R[NZSN1R - 1]; + } + } + + double tm1 = feedback_get_turnover_mass(fb_props, age, z); + + if (tm1 >= fb_props->M_u3) return; + + double ltm = log10(tm1 * fb_props->solar_mass_to_mass); + + for (j = 1; j < NM; j++) { + j1 = j - 1; + j2 = j; + if (fb_props->tables.SNLM[j] < ltm) break; + } + + /* This is only true if we do PopIII stars */ + if (z <= fb_props->zmax3) { + SNII_U = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(1, 0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(1, 0, j2)], + ltm + ); + SNII_E = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(2, 0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(2, 0, j2)], + ltm + ); + SNII_ENE = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(0, 0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(0, 0, j2)], + ltm + ); + + for (k = 0; k < chem5_element_count; k++) { + SNII_Z[k] = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j2)], + ltm + ); + } + + SNII_R = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2R[SN2R_idx(0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2R[SN2R_idx(0, j2)], + ltm + ); + SW_R = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SWR[SWR_idx(0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SWR[SWR_idx(0, j2)], + ltm + ); + SNIa_R = 0.0; + } + else { + for (l = 2; l < NZSN; l++) { + l1 = l - 1; + l2 = l; + if (fb_props->tables.SNLZ[l] > lz) break; + } + for (l = 1; l < NZSN1R; l++) { + ll1 = l - 1; + ll2 = l; + if (fb_props->tables.SNLZ1R[l] > feh) break; + } + for (l = 1; l < NZSN1Y; l++) { + lll1 = l - 1; + lll2 = l; + if (fb_props->tables.SNLZ1Y[l] > lz) break; + } + + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(1, l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(1, l1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(1, l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(1, l2, j2)], + ltm + ); + SNII_U = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + SNIIa, + fb_props->tables.SNLZ[l2], + SNIIb, + lz + ); + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(2, l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(2, l1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(2, l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(2, l2, j2)], + ltm + ); + SNII_E = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + SNIIa, + fb_props->tables.SNLZ[l2], + SNIIb, + lz + ); + + if (l2 == NZSN - 1) { + SNII_ENE = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], + ltm + ); + } + else { + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + fb_props->tables.SN2E[SN2E_idx(0, l1, j1)], + fb_props->tables.SNLZ[l2], + fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], + lz + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + fb_props->tables.SN2E[SN2E_idx(0, l1, j2)], + fb_props->tables.SNLZ[l2], + fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], + lz + ); + SNII_ENE = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + SNIIa, + fb_props->tables.SNLM[j2], + SNIIb, + ltm + ); + } + + for (k = 0; k < chem5_element_count; k++) { + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j2)], + ltm + ); + SNII_Z[k] = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + SNIIa, + fb_props->tables.SNLZ[l2], + SNIIb, + lz + ); + } + + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2R[SN2R_idx(l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2R[SN2R_idx(l1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2R[SN2R_idx(l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2R[SN2R_idx(l2, j2)], + ltm + ); + SNII_R = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + SNIIa, + fb_props->tables.SNLZ[l2], + SNIIb, + lz + ); + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SWR[SWR_idx(l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SWR[SWR_idx(l1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SWR[SWR_idx(l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SWR[SWR_idx(l2, j2)], + ltm + ); + SW_R = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + SNIIa, + fb_props->tables.SNLZ[l2], + SNIIb, + lz + ); + + if (feh < fb_props->tables.SNLZ1R[0]) { + SNIa_R = 0.; + } + else { + SNIa_E = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ1Y[lll1], + fb_props->tables.SN1E[SN1E_idx(2, lll1)], + fb_props->tables.SNLZ1Y[lll2], + fb_props->tables.SN1E[SN1E_idx(2, lll2)], + lz + ); + + for (k = 0; k < chem5_element_count; k++) { + SNIa_Z[k] = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ1Y[lll1], + fb_props->tables.SN1E[SN1E_idx((k + 3), lll1)], + fb_props->tables.SNLZ1Y[lll2], + fb_props->tables.SN1E[SN1E_idx((k + 3), lll2)], + lz + ); + } + + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN1R[SN1R_idx(ll1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN1R[SN1R_idx(ll1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN1R[SN1R_idx(ll2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN1R[SN1R_idx(ll2, j2)], + ltm + ); + SNIa_R = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ1R[ll1], + SNIIa, + fb_props->tables.SNLZ1R[ll2], + SNIIb, + feh + ); + } + } + + double tm2 = 1.e-10; + + if (age > dt) { + tm2 = feedback_get_turnover_mass(fb_props, age - dt, z); + + ltm = log10(tm2 * fb_props->solar_mass_to_mass); + for (j = 1 ; j < NM; j++) { + j1 = j - 1; + j2 = j; + if (fb_props->tables.SNLM[j] < ltm) break; + } + + if (z <= fb_props->zmax3) { + SNII_U -= LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(1, 0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(1, 0, j2)], + ltm + ); + SNII_E -= LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(2, 0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(2, 0, j2)], + ltm + ); + SNII_ENE -= LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(0, 0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(0, 0, j2)], + ltm + ); + + for (k = 0; k < chem5_element_count; k++) { + SNII_Z[k] -= LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j2)], + ltm + ); + } + + SNII_R -= LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2R[SN2R_idx(0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2R[SN2R_idx(0, j2)], + ltm + ); + SW_R -= LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SWR[SWR_idx(0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SWR[SWR_idx(0, j2)], + ltm + ); + } + else { + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(1, l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(1, l1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(1, l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(1, l2, j2)], + ltm + ); + SNII_U -= LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + SNIIa, + fb_props->tables.SNLZ[l2], + SNIIb, + lz + ); + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(2, l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(2, l1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(2, l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(2, l2, j2)], + ltm + ); + SNII_E -= LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + SNIIa, + fb_props->tables.SNLZ[l2], + SNIIb, + lz + ); + + if (l2 == NZSN - 1) { + SNII_ENE -= LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], + ltm + ); + } + else { + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + fb_props->tables.SN2E[SN2E_idx(0, l1, j1)], + fb_props->tables.SNLZ[l2], + fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], + lz + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + fb_props->tables.SN2E[SN2E_idx(0, l1, j2)], + fb_props->tables.SNLZ[l2], + fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], + lz + ); + SNII_ENE -= LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + SNIIa, + fb_props->tables.SNLM[j2], + SNIIb, + ltm + ); + } + + for (k = 0; k < chem5_element_count; k++) { + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j2)], + ltm + ); + SNII_Z[k] -= LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + SNIIa, + fb_props->tables.SNLZ[l2], + SNIIb, + lz + ); + } + + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2R[SN2R_idx(l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2R[SN2R_idx(l1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN2R[SN2R_idx(l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2R[SN2R_idx(l2, j2)], + ltm + ); + SNII_R -= LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + SNIIa, + fb_props->tables.SNLZ[l2], + SNIIb, + lz + ); + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SWR[SWR_idx(l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SWR[SWR_idx(l1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SWR[SWR_idx(l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SWR[SWR_idx(l2, j2)], + ltm + ); + SW_R -= LINEAR_INTERPOLATION( + fb_props->tables.SNLZ[l1], + SNIIa, + fb_props->tables.SNLZ[l2], + SNIIb, + lz + ); + + if (feh < fb_props->tables.SNLZ1R[0]) { + SNIa_R = 0.; + } + else { + SNIIa = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN1R[SN1R_idx(ll1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN1R[SN1R_idx(ll1, j2)], + ltm + ); + SNIIb = LINEAR_INTERPOLATION( + fb_props->tables.SNLM[j1], + fb_props->tables.SN1R[SN1R_idx(ll2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN1R[SN1R_idx(ll2, j2)], + ltm + ); + SNIa_R -= LINEAR_INTERPOLATION( + fb_props->tables.SNLZ1R[ll1], + SNIIa, + fb_props->tables.SNLZ1R[ll2], + SNIIb, + feh + ); + } + } + } + + *ejecta_unprocessed = max(0., sp->mass_init * SNII_U); + *ejecta_mass = max(0., sp->mass_init * SNII_E); + + /* For some reason at the first step this might happen */ + if (isnan(SNII_U) || isnan(SNII_E)) { + warning("SNII_U or SNII_E is NaN, j1=%d l1=%d z=%g mturn=%g %g age=%g", + j1, l1, z, tm1, tm2, age); + *ejecta_unprocessed = *ejecta_mass = 0.; + return; + } + + if (tm1 > fb_props->M_u2) { + fb = 3; + if (z == 0.) { + *ejecta_energy = 0.; + } + else { + SWn = sp->mass_init * SW_R; + if (fb_props->with_HN_energy_from_chem5) { + /* E_sw converts to code units */ + *ejecta_energy = + SWn * fb_props->E_sw * pow(z / fb_props->Z_mf, 0.8); + } + + /* Needed for dust model within Grackle; for now treat PopIII SNe + * same as PopI/II + */ + *N_SNe = SWn; + } + } + else { + if (tm2 > fb_props->M_l2 || fb_first == 1) { + fb = 2; + + SWn = sp->mass_init * SW_R; + SNn = sp->mass_init * SNII_R; + if (fb_props->with_SNII_energy_from_chem5) { + *ejecta_energy = SWn * fb_props->E_sw; + *ejecta_energy += sp->mass_init * SNII_ENE; + } + /* Needed for dust model within Grackle; for now + * treat PopIII SNe same as PopI/II + */ + *N_SNe = SNn + SWn; + } + + for (k = 0; k < chem5_element_count; k++) { + ejecta_metal_mass[k] = sp->mass_init * SNII_Z[k]; + } + + if (tm1 <= fb_props->M_l2) { + if (feh < fb_props->tables.SNLZ1R[0]) { + SNIa_R = 0.; + } + else { + fb = 1; + SNn = sp->mass_init * SNIa_R; + /* SNIa always contribute */ + if (fb_props->with_SNIa_energy_from_chem5 == 1) { + *ejecta_energy += SNn * fb_props->E_sn1; + } + /* SNIa contribute if age < with_SNIa_energy_from_chem5 */ + else if (fb_props->with_SNIa_energy_from_chem5 > 10.) { + const double age_in_Myr = + (age / fb_props->time_to_yr) * fb_props->time_to_Myr; + if (age_in_Myr < fb_props->with_SNIa_energy_from_chem5) { + *ejecta_energy += SNn * fb_props->E_sn1; + } + } + + ejecta_mass_Ia += SNn * SNIa_E; + *ejecta_mass += SNn * SNIa_E; + for (k = 0; k < chem5_element_count; k++) { + ejecta_metal_mass[k] += SNn * SNIa_Z[k]; + } + + // Add in the TypeIa's too + *N_SNe += SNn; + } + } + } + + if (*ejecta_energy < 0.) { + warning("Star %lld energy<0! m=%g (frac=%g), age=%g Myr, Z=%g " + "is ejecting %g Msun (fIa=%g, Zej=%g) and %g erg (%g in SNe) " + "in %g Myr.", + sp->id, + sp->mass * fb_props->mass_to_solar_mass, + sp->mass/sp->mass_init, + (age / fb_props->time_to_yr) * fb_props->time_to_Myr, + log10(z + 1.e-6), + *ejecta_mass * fb_props->mass_to_solar_mass, + ejecta_mass_Ia / *ejecta_mass, + log10(ejecta_metal_mass[0] / *ejecta_mass + 1.e-6), + *ejecta_energy * fb_props->energy_to_cgs, + *N_SNe * 1.e51, + (dt / fb_props->time_to_yr) * fb_props->time_to_Myr); + } +} + +double feedback_life_time(const struct feedback_props* fb_props, + const double m, + const double z) { + int j, j1, j2, l, l1, l2; + double lm, lz, ta, tb, t; + + lm = log10(m); + j1 = 0; + j2 = 1; + for (j = 1; j < NMLF; j++) { + j1 = j - 1; + j2 = j; + if (fb_props->tables.LFLM[j] > lm) break; + } + + if (z == 0.) { + lz = -990.; + } + else { + lz = log10(z); + } + + if (lz <= fb_props->tables.LFLZ[0]) { + t = LINEAR_INTERPOLATION( + fb_props->tables.LFLM[j1], + fb_props->tables.LFLT[LFLT_idx(0, j1)], + fb_props->tables.LFLM[j2], + fb_props->tables.LFLT[LFLT_idx(0, j2)], + lm + ); + } + else { + l1 = 0; + l2 = 1; + for (l = 1; l < NZLF; l++) { + l1 = l - 1; + l2 = l; + if (fb_props->tables.LFLZ[l] > lz) break; + } + ta = LINEAR_INTERPOLATION( + fb_props->tables.LFLZ[l1], + fb_props->tables.LFLT[LFLT_idx(l1, j1)], + fb_props->tables.LFLZ[l2], + fb_props->tables.LFLT[LFLT_idx(l2, j1)], + lz + ); + tb = LINEAR_INTERPOLATION( + fb_props->tables.LFLZ[l1], + fb_props->tables.LFLT[LFLT_idx(l1, j2)], + fb_props->tables.LFLZ[l2], + fb_props->tables.LFLT[LFLT_idx(l2, j2)], + lz + ); + t = LINEAR_INTERPOLATION( + fb_props->tables.LFLM[j1], + ta, + fb_props->tables.LFLM[j2], + tb, + lm + ); + } + + return pow(10., t); +} + +double feedback_imf(const struct feedback_props* fb_props, const double m) { + if (fb_props->imf == 0) { /* Kroupa */ + if (m >= 0.5) { + return pow(m, -fb_props->ximf) * 0.5; + } + else if (m >= 0.08) { + return pow(m, -0.3); + } + else { + return pow(m, 0.7) / 0.08; + } + } + else if (fb_props->imf == 1) { /* Chabrier */ + if (m <= 1.0 && m > 0.01) { + const double dm = log10(m) - log10(0.079); + return 0.7895218 * exp((-1. * pow(dm, 2.)) / (2. * pow(0.69, 2.))); + } + else { + return 0.2203457 * pow(m, -fb_props->ximf); + } + } + else { + return pow(m, -fb_props->ximf); + } +} + +void feedback_set_turnover_mass(const struct feedback_props* fb_props, + const double z, double *LFLT2) { + double lz; + int j, l, l1, l2; + + if (z == 0.) { + lz = -4.1; + } + else { + lz = log10f(z); + } + + if (lz < -4.1) lz = -4.1; + + l1 = 0; + l2 = 1; + for (l = 1; l < NZLF; l++) { + l1 = l - 1; + l2 = l; + if (fb_props->tables.LFLZ[l] > lz) break; + } + + for (j = 0; j < NMLF; j++) { + LFLT2[j] = LINEAR_INTERPOLATION( + fb_props->tables.LFLZ[l1], + fb_props->tables.LFLT[LFLT_idx(l1, j)], + fb_props->tables.LFLZ[l2], + fb_props->tables.LFLT[LFLT_idx(l2, j)], + lz + ); + } + return; +} + +double feedback_get_turnover_mass(const struct feedback_props* fb_props, + const double t, const double z) { + if (t == 0.) return fb_props->M_u3; + + double result, m, lt; + int j, j1, j2; + double LFLT2[NMLF]; + + feedback_set_turnover_mass(fb_props, z, LFLT2); + + lt = log10(t); + j1 = 0; + j2 = 1; + for (j = 1; j < NMLF; j++) { + j1 = j - 1; + j2 = j; + if (LFLT2[j] < lt) break; + } + + m = LINEAR_INTERPOLATION( + LFLT2[j1], + fb_props->tables.LFLM[j1], + LFLT2[j2], + fb_props->tables.LFLM[j2], + lt + ); + result = pow(10., m); + + if (result < fb_props->M_l) return fb_props->M_l; + if (result > fb_props->M_u3) return fb_props->M_u3; + + return result; +} + +void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props) { + int i, j, k, j1, j2, l; + double sni[NXSNall][NZSN1Y], sn[NXSNall][NZSN][NMSN - 2], hn[NXSNall][NZSN][4]; + double sniilm[NMSN], snii[chem5_NXSN][NZSN][NMSN]; + double SN1wd[NZSN1R][NM], SN1ms[NZSN1R][NM], SN1rg[NZSN1R][NM]; + double m[NM], imf[NZSN][NM]; + double snii2_hi,snii2_lo; + double dlm, norm, norm3; + double m_l; + FILE *fp; + char buf[1000],*dummy; + double a1, a2, a3, a4, a5, a6, a7; + double a8, a9, a10, a11, a12, a13, a14, a15, a16; + double a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27; + double a28, a29, a30; + double effSNz[NZSN], temp, tempz; + const double lfz[NZLF] = {.0001, .0002, .0005, .0010, .0020, .0040, .0080, + .0200, .0500}; + double temp_ms, temp_rg; + + /* Massloss (Kobayashi et al. 2000) + * sw[2][24]: progenitor mass, He core mass = NSorWD mass + */ + const double sw[2][NMSN] = { + {40., 30., 25., 20., 18., 15., 13., 10., 9., 8.5, 8., 7.5, 7., 6.5, 6.0, + 5.5, 5.0, 4.5, 4., 3.5, 3., 2.5, 2.25, 2., 1.9, 1.75, 1.5, 1.25, 1.0, + 0.9, 0.7, 0.05}, + {0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., + 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.473, 0.459, 0.05} + }; + const double sniie[9] = {30, 20, 10, 10, 1, 1, 1, 1, 1}; + const double sniiz[NZSN] = {0., 0., .001, .004, .008, .02, .05}; + const double sniz[NZSN1Y] = {0., .002, .01, .02, .04, .06, .10}; + const double effHNz[NZSN] = {0.5, 0.5, 0.5, 0.4, 0.232036142, 0.01, 0.01}; + + /* [Fe/H] */ + const double feh_ia[NZSN1R] = {-1.1, -1.0, -0.69896996, 0., 0.39794001}; + const double M_l1rg[NZSN1R] = {0.9, 0.9, 0.9, 0.9, 0.8}; + const double M_l1ms[NZSN1R] = {1.8, 1.8, 1.8, 1.8, 1.8}; + const double M_u1rg[NZSN1R] = {0.9, 1.5, 2.0, 3.0, 3.5}; + const double M_u1ms[NZSN1R] = {1.8, 2.6, 4.0, 5.5, 6.0}; + const double M_l1wd[NZSN1R] = {2.4, 2.5, 2.80103004, 3.5, 3.89794001}; + const double M_u1wd[NZSN1R] = {6.75, 6.75, 7.05, 7.95, 7.95}; + + for (l = 0; l < NZSN1R; l++) fb_props->tables.SNLZ1R[l] = feh_ia[l]; + + if (engine_rank == 0) { + message("set nucleosynthesis yields for %i elements...", + chem5_element_count); + message("Z-dependent HN efficiency !!! "); + message("effHN = %f %f", effHNz[0], effHNz[NZSN - 1]); + message("Z-dependent SNIa model !!! "); + message("b=(%.3f %.3f) [Fe/H] > %f", fb_props->b_rg, fb_props->b_ms, + fb_props->tables.SNLZ1R[0]); + message("Z-dependent SAGB!!!"); + } + + sprintf(buf, "%s/SN2SAGBYIELD.DAT", fb_props->tables_path); + if ((fp = fopen(buf, "r")) == NULL) { + fprintf(stderr, "Can not open File %s\n", buf); + exit(-1); + } + + for (j = 1; j < NZSN; j++) { + dummy = fgets(buf, 1000, fp); /* metallicity */ + dummy = fgets(buf, 1000, fp); /* mass */ + dummy = fgets(buf, 1000, fp); /* energy */ + for (k = 0; k < NXSNall; k++) { /* k=0: masscut */ + dummy = fgets(buf, 1000, fp); + sscanf(buf, + "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf" + "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf\n", + &a1, &a2, &a3, &a4, &a5, &a6, &a7, + &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, + &a17, &a18, &a19, &a20, &a21, &a22, &a23, &a24, &a25, &a26, &a27, + &a28, &a29, &a30); + sn[k][j][0] = a30; + sn[k][j][1] = a29; + sn[k][j][2] = a28; + sn[k][j][3] = a27; + sn[k][j][4] = a26; + sn[k][j][5] = a25; + sn[k][j][6] = a24; + sn[k][j][7] = a23; + sn[k][j][8] = a22; + sn[k][j][9] = a21; + sn[k][j][10] = a20; + sn[k][j][11] = a19; + sn[k][j][12] = a18; + sn[k][j][13] = a17; + sn[k][j][14] = a16; + sn[k][j][15] = a15; + sn[k][j][16] = a14; + sn[k][j][17] = a13; + sn[k][j][18] = a12; + sn[k][j][19] = a11; + sn[k][j][20] = a10; + sn[k][j][21] = a9; + sn[k][j][22] = a8; + sn[k][j][23] = a7; + sn[k][j][24] = a6; + sn[k][j][25] = a5; + sn[k][j][26] = a4; + sn[k][j][27] = a3; + sn[k][j][28] = a2; + sn[k][j][29] = a1; + } + } + + for (j = 1; j < NZSN; j++) { + dummy = fgets(buf, 200, fp); + dummy = fgets(buf, 200, fp); + dummy = fgets(buf, 200, fp); + for (k = 0; k < NXSNall; k++) { + dummy = fgets(buf, 200, fp); + sscanf(buf, "%lf%lf%lf%lf\n", &a1, &a2, &a3, &a4); + hn[k][j][0] = a4; + hn[k][j][1] = a3; + hn[k][j][2] = a2; + hn[k][j][3] = a1; + } + } + + fclose(fp); + + for (i = 0; i < NMSN - 2; i++) { + for (k = 0; k < NXSNall; k++) sn[k][0][i] = sn[k][1][i]; /* pop3 */ + } + + for (i = 0; i < 4; i++) { + for (k = 0; k < NXSNall; k++) hn[k][0][i] = hn[k][1][i]; /* pop3 */ + } + + for (j = 0; j < NZSN; j++) effSNz[j] = 1. - effHNz[j]; + + for (i = 0; i < 4; i++) { + for (j = 0; j < NZSN; j++) { + for (k = 0; k < NXSNall; k++) { + sn[k][j][i] = effSNz[j] * sn[k][j][i] + effHNz[j] * hn[k][j][i]; + } + } + } + + /* Set up SNII yield tables */ + for (i = 0; i < NMSN - 2; i++) { + sniilm[i] = log10(sw[0][i]); + for (j = 0; j < NZSN; j++) { + temp = tempz = 0.; + for (k = 0; k < NXSNall; k++) { + sn[k][j][i] /= sw[0][i]; + if (k > 0) temp += sn[k][j][i]; + if (k > 9) tempz += sn[k][j][i]; + } + + snii[2][j][i] = 1. - sn[0][j][i]; /* ejected mass */ + snii[1][j][i] = snii[2][j][i] - temp; /* unprocessed mass */ + if (snii[1][j][i] < 0.) { + snii[2][j][i] = temp; + snii[1][j][i] = 0.; + } + snii[3][j][i] = tempz; /* Z */ + snii[4][j][i] = sn[1][j][i] + sn[2][j][i]; /* H */ + snii[5][j][i] = sn[3][j][i] + sn[4][j][i]; /* He */ + snii[6][j][i] = sn[5][j][i] + sn[6][j][i]; /* Li */ + snii[7][j][i] = sn[7][j][i]; /* Be */ + snii[8][j][i] = sn[8][j][i] + sn[9][j][i]; /* B */ + snii[9][j][i] = sn[10][j][i] + sn[11][j][i]; /* C */ + snii[10][j][i] = sn[12][j][i] + sn[13][j][i]; /* N */ + snii[11][j][i] = sn[14][j][i] + sn[15][j][i] + + sn[16][j][i]; /* O */ + snii[12][j][i] = sn[17][j][i]; /* F */ + snii[13][j][i] = sn[18][j][i] + sn[19][j][i] + + sn[20][j][i]; /* Ne */ + snii[14][j][i] = sn[21][j][i]; /* Na */ + snii[15][j][i] = sn[22][j][i] + sn[23][j][i] + + sn[24][j][i]; /* Mg */ + snii[16][j][i] = sn[25][j][i]; /* Al */ + snii[17][j][i] = sn[26][j][i] + sn[27][j][i] + + sn[28][j][i]; /* Si */ + snii[18][j][i] = sn[29][j][i]; /* P */ + snii[19][j][i] = sn[30][j][i] + sn[31][j][i] + + sn[32][j][i] + sn[33][j][i]; /* S */ + snii[20][j][i] = sn[34][j][i] + sn[35][j][i]; /* Cl */ + snii[21][j][i] = sn[36][j][i] + sn[37][j][i] + + sn[38][j][i]; /* Ar */ + snii[22][j][i] = sn[39][j][i] + sn[40][j][i] + + sn[41][j][i]; /* K */ + snii[23][j][i] = sn[42][j][i] + sn[43][j][i] + + sn[44][j][i] + sn[45][j][i] + + sn[46][j][i] + sn[47][j][i]; /* Ca */ + snii[24][j][i] = sn[48][j][i]; /* Sc */ + snii[25][j][i] = sn[49][j][i] + sn[50][j][i] + + sn[51][j][i] + sn[52][j][i] + + sn[53][j][i]; /* Ti */ + snii[26][j][i] = sn[54][j][i] + sn[55][j][i]; /* V */ + snii[27][j][i] = sn[56][j][i] + sn[57][j][i] + + sn[58][j][i] + sn[59][j][i]; /* Cr */ + snii[28][j][i] = sn[60][j][i]; /* Mn */ + snii[29][j][i] = sn[61][j][i] + sn[62][j][i] + + sn[63][j][i] + sn[64][j][i]; /* Fe */ + snii[30][j][i] = sn[65][j][i]; /* Co */ + snii[31][j][i] = sn[66][j][i] + sn[67][j][i] + + sn[68][j][i] + sn[69][j][i] + + sn[70][j][i]; /* Ni */ + snii[32][j][i] = sn[71][j][i] + sn[72][j][i]; /* Cu */ + snii[33][j][i] = sn[73][j][i] + sn[74][j][i] + + sn[75][j][i] + sn[76][j][i] + + sn[77][j][i]; /* Zn */ + snii[34][j][i] = sn[78][j][i] + sn[79][j][i]; /* Ga */ + snii[35][j][i] = sn[80][j][i] + sn[81][j][i] + + sn[82][j][i] + + sn[83][j][i]; /* Ge */ + snii[36][j][i] = 0.; + } + } + + for (i = 9; i < NMSN - 2; i++) { + for (j = 0; j < NZSN; j++) { + for (k = 29; k < 36; k++) snii[k][j][i] = 0.; /* Fe in AGB 09.10.12 */ + } + } + + for (i = NMSN - 2; i < NMSN; i++) /* 1 < Msun */{ + sniilm[i] = log10(sw[0][i]); + for (j = 0; j < NZSN; j++) { + snii[1][j][i] = 1. - sw[1][i] / sw[0][i]; + snii[2][j][i] = snii[1][j][i]; + for (k = 3; k < chem5_NXSN; k++) snii[k][j][i] = 0.; + } + } + + for (j = 0; j < NZSN; j++) { + /* Energy, 10-40 Msun */ + for (i = 0; i < 9; i++) { + snii[0][j][i] = effSNz[j] * 1.0 + effHNz[j] * sniie[i]; + } + + for (i = 9; i < NMSN; i++) { + snii[0][j][i] = 0.; + } + } + + /* Multiply all metal yields by a constant factor if desired */ + for (i = 0; i < NMSN - 2; i++) { + for (j = 0; j < NZSN; j++) { + for (k = 6; k < 36; k++) snii[k][j][i] *= fb_props->metal_yield_multiplier; + } + } + + + fb_props->tables.SNLZ[0] = -999.; /* z=0 */ + fb_props->tables.SNLZ[1] = -10.; /* z=0 */ + for (j = 2; j < NZSN; j++) fb_props->tables.SNLZ[j] = log10(sniiz[j]); + + fb_props->tables.SNLZ1Y[0] = -999.; /* z=0 */ + for (j = 1; j < NZSN1Y; j++) fb_props->tables.SNLZ1Y[j] = log10(sniz[j]); + + /* SNIa yield (Kobayashi et al. 2020a, DDT) */ + sprintf(buf, "%s/SN1YIELD_Z.DAT", fb_props->tables_path); + if ((fp = fopen(buf, "r")) == NULL) { + fprintf(stderr, "Can not open File %s %s\n", buf, dummy); + exit(-1); + } + + dummy = fgets(buf, 1000, fp); /* metallicity */ + for (k = 0; k < NXSNall; k++) { /* k=0: ejected mass */ + dummy = fgets(buf, 1000, fp); + sscanf(buf, "%lf%lf%lf%lf%lf%lf%lf\n", &a1, &a2, &a3, &a4, &a5, &a6, &a7); + sni[k][0] = a1; + sni[k][1] = a2; + sni[k][2] = a3; + sni[k][3] = a4; + sni[k][4] = a5; + sni[k][5] = a6; + sni[k][6] = a7; + } + fclose(fp); + + /* Set up SNIa+AGB yield tables */ + for (j = 0; j < NZSN1Y; j++) { + temp = tempz = 0.; + for (k = 0; k < NXSNall; k++) { + if (k > 0) temp += sni[k][j]; + if (k > 9) tempz += sni[k][j]; + } + + fb_props->tables.SN1E[SN1E_idx(0, j)] = 1.3; /* energy */ + fb_props->tables.SN1E[SN1E_idx(1, j)] = 0.; /* unprocessed */ + fb_props->tables.SN1E[SN1E_idx(2, j)] = temp; /* ejected */ + fb_props->tables.SN1E[SN1E_idx(3, j)] = tempz; /* Z */ + fb_props->tables.SN1E[SN1E_idx(4, j)] = sni[1][j] + sni[2][j]; /* H */ + fb_props->tables.SN1E[SN1E_idx(5, j)] = sni[3][j] + sni[4][j]; /* He */ + fb_props->tables.SN1E[SN1E_idx(6, j)] = sni[5][j] + sni[6][j]; /* Li */ + fb_props->tables.SN1E[SN1E_idx(7, j)] = sni[7][j]; /* Be */ + fb_props->tables.SN1E[SN1E_idx(8, j)] = sni[8][j] + sni[9][j]; /* B */ + fb_props->tables.SN1E[SN1E_idx(9, j)] = sni[10][j] + sni[11][j]; /* C */ + fb_props->tables.SN1E[SN1E_idx(10, j)] = sni[12][j] + sni[13][j]; /* N */ + fb_props->tables.SN1E[SN1E_idx(11, j)] = sni[14][j] + sni[15][j] + + sni[16][j]; /* O */ + fb_props->tables.SN1E[SN1E_idx(12, j)] = sni[17][j]; /* F */ + fb_props->tables.SN1E[SN1E_idx(13, j)] = sni[18][j] + sni[19][j] + + sni[20][j]; /* Ne */ + fb_props->tables.SN1E[SN1E_idx(14, j)] = sni[21][j]; /* Na */ + fb_props->tables.SN1E[SN1E_idx(15, j)] = sni[22][j] + sni[23][j] + + sni[24][j]; /* Mg */ + fb_props->tables.SN1E[SN1E_idx(16, j)] = sni[25][j]; /* Al */ + fb_props->tables.SN1E[SN1E_idx(17, j)] = sni[26][j] + sni[27][j] + + sni[28][j]; /* Si */ + fb_props->tables.SN1E[SN1E_idx(18, j)] = sni[29][j]; /* P */ + fb_props->tables.SN1E[SN1E_idx(19, j)] = sni[30][j] + sni[31][j] + + sni[32][j] + sni[33][j]; /* S */ + fb_props->tables.SN1E[SN1E_idx(20, j)] = sni[34][j] + sni[35][j]; /* Cl */ + fb_props->tables.SN1E[SN1E_idx(21, j)] = sni[36][j] + sni[37][j] + + sni[38][j]; /* Ar */ + fb_props->tables.SN1E[SN1E_idx(22, j)] = sni[39][j] + sni[40][j] + + sni[41][j]; /* K */ + fb_props->tables.SN1E[SN1E_idx(23, j)] = sni[42][j] + sni[43][j] + + sni[44][j] + sni[45][j] + + sni[46][j] + sni[47][j]; /* Ca */ + fb_props->tables.SN1E[SN1E_idx(24, j)] = sni[48][j]; /* Sc */ + fb_props->tables.SN1E[SN1E_idx(25, j)] = sni[49][j] + sni[50][j] + + sni[51][j] + sni[52][j] + + sni[53][j]; /* Ti */ + fb_props->tables.SN1E[SN1E_idx(26, j)] = sni[54][j] + sni[55][j]; /* V */ + fb_props->tables.SN1E[SN1E_idx(27, j)] = sni[56][j] + sni[57][j] + + sni[58][j] + sni[59][j]; /* Cr */ + fb_props->tables.SN1E[SN1E_idx(28, j)] = sni[60][j]; /* Mn */ + fb_props->tables.SN1E[SN1E_idx(29, j)] = sni[61][j] + sni[62][j] + + sni[63][j] + sni[64][j]; /* Fe */ + fb_props->tables.SN1E[SN1E_idx(30, j)] = sni[65][j]; /* Co */ + fb_props->tables.SN1E[SN1E_idx(31, j)] = sni[66][j] + sni[67][j] + + sni[68][j] + sni[69][j] + + sni[70][j]; /* Ni */ + fb_props->tables.SN1E[SN1E_idx(32, j)] = sni[71][j] + sni[72][j]; /* Cu */ + fb_props->tables.SN1E[SN1E_idx(33, j)] = sni[73][j] + sni[74][j] + + sni[75][j] + sni[76][j] + + sni[77][j]; /* Zn */ + fb_props->tables.SN1E[SN1E_idx(34, j)] = sni[78][j] + sni[79][j]; /* Ga */ + fb_props->tables.SN1E[SN1E_idx(35, j)] = sni[80][j] + sni[81][j] + + sni[82][j] + sni[83][j]; /* Ge */ + fb_props->tables.SN1E[SN1E_idx(36, j)] = + fb_props->tables.SN1E[SN1E_idx(29, j)]; + for (k = 1; k < chem5_NXSN; k++) { + fb_props->tables.SN1E[SN1E_idx(k, j)] *= fb_props->solar_mass_to_mass; + } + } + + /* lifetime (Kobayashi et al. 2000) */ + sprintf(buf, "%s/LIFETIME.DAT", fb_props->tables_path); + if ((fp = fopen(buf, "r")) == NULL) { + fprintf(stderr, "Can not open File %s\n", buf); + exit(-1); + } + dummy = fgets(buf, 1000, fp); + + for (j = 0; j < NZLF; j++) { + fb_props->tables.LFLZ[j] = log10(lfz[j]); + dummy = fgets(buf, 1000, fp); + dummy = fgets(buf, 1000, fp); + dummy = fgets(buf, 1000, fp); + for (i = 0; i < NMLF; i++) { + dummy = fgets(buf, 1000, fp); + sscanf(buf, "%lf%lf\n", &a1, &a2); + fb_props->tables.LFLM[i] = log10(a1); /* sm */ + fb_props->tables.LFLT[LFLT_idx(j, i)] = log10(a2); /* yr */ + } + } + fclose(fp); + + if (engine_rank == 0) { + message( + "total: %.2f %.1f %.2e %.2e", + fb_props->M_l, + fb_props->M_u, + feedback_life_time(fb_props, fb_props->M_l, 0.02), + feedback_life_time(fb_props, fb_props->M_u, 0.02) + ); + message( + "SN2: %.2f %.1f %.2e %.2e x=%.2f", + fb_props->M_l2, + fb_props->M_u2, + feedback_life_time(fb_props, fb_props->M_l2, 0.02), + feedback_life_time(fb_props, fb_props->M_u2, 0.02), + fb_props->ximf + ); + if (fb_props->zmax3 >= 0.0f) { + message( + "Pop3: %.2f %.1f %.2e %.2e x=%.2f\n", + fb_props->M_l3, + fb_props->M_u3, + feedback_life_time(fb_props, fb_props->M_l3, 0.02), + feedback_life_time(fb_props, fb_props->M_u3, 0.02), + fb_props->ximf3 + ); + } + } + + /* Set up IMF, normalized to 1 solar mass */ + if (fb_props->imf == 0) { /* Kroupa */ + if (fb_props->ximf == 1.) { + norm = log10(fb_props->M_u / 0.5) * 0.5 + + (pow(0.5, 0.7) - pow(0.08, 0.7)) / 0.7 + + (pow(0.08, 1.7) - pow(fb_props->M_l, 1.7)) / 1.7 / 0.08; + } + else { + norm = (pow(fb_props->M_u, 1. - fb_props->ximf) + - pow(0.5, 1. - fb_props->ximf)) + / (1. - fb_props->ximf) * 0.5 + + (pow(0.5, 0.7) - pow(0.08, 0.7)) + / 0.7f + + (pow(0.08, 1.7) - pow(fb_props->M_l, 1.7)) / 1.7 / 0.08; + } + + norm = 1. / norm; + } + else { /* Chabrier, anything else */ + if (fb_props->ximf == 1.) { + norm = 1. / log(fb_props->M_u / fb_props->M_l); + } + else { + norm = (1. - fb_props->ximf) + / (pow(fb_props->M_u, (1. - fb_props->ximf)) + - pow(fb_props->M_l, (1. - fb_props->ximf))); + } + } + + if (fb_props->ximf3 == 1.) { + norm3 = 1. / log(fb_props->M_u3 / fb_props->M_l3); + } + else { + norm3 = (1. - fb_props->ximf3) / + (powf(fb_props->M_u3, (1. - fb_props->ximf3)) + - powf(fb_props->M_l3, (1. - fb_props->ximf3))); + } + + /* Set up IMF integration */ + dlm = (log10(fb_props->M_u3) - log10(fb_props->M_l)) / NM; + for (i = 0; i < NM; i++) { + fb_props->tables.SNLM[i] = log10(fb_props->M_u3) - dlm * i; + m[i] = pow(10., fb_props->tables.SNLM[i]); + + if (m[i] >= fb_props->M_l3) { + imf[0][i] = pow(m[i], -fb_props->ximf3) * norm3; + } + else { + imf[0][i] = 0.; + } + + if (fb_props->imf == 1) { /* Chabrier */ + if (m[i] <= fb_props->M_u) { + imf[1][i] = IMF_FUDGE_FACTOR * feedback_imf(fb_props, m[i]); + } + else { + imf[1][i] = 0.; + } + } + else { /* Kroupa/else */ + if (m[i] <= fb_props->M_u) { + imf[1][i] = IMF_FUDGE_FACTOR * feedback_imf(fb_props, m[i]) * norm; + } + else { + imf[1][i] = 0.; + } + } + + for (l = 2; l < NZSN; l++) { + imf[l][i] = imf[1][i]; + } + } + + /* Loop over all NM masses to set up IMF-integrated yield tables */ + for (i = 0; i < NM; i++) { + for (l = 0; l < NZSN1R; l++) { + SN1wd[l][i] = 0.; + SN1ms[l][i] = 0.; + SN1rg[l][i] = 0.; + fb_props->tables.SN1R[SN1R_idx(l, i)] = 0.; + } + + for (l = 0; l < NZSN; l++) { + fb_props->tables.SN2R[SN2R_idx(l, i)] = 0.; + fb_props->tables.SWR[SWR_idx(l, i)] = 0.; + for (k = 0; k < chem5_NXSN; k++) { + fb_props->tables.SN2E[SN2E_idx(k, l, i)] = 0.; + } + } + } + + for (i = 1; i < NM; i++) { + /* find indices for interp. from NMSN mass entries to NM mass entries */ + j1 = 0; + j2 = 1; + for (j = 1; j < NMSN; j++) { + j1 = j - 1; + j2 = j; + if (sniilm[j] < fb_props->tables.SNLM[i]) break; + } + + /* For this mass, loop over metallicity values */ + for (l = 0; l < NZSN; l++) { + if (l == 0) { + m_l = max(fb_props->M_l2, fb_props->M_l3); + } + else { + m_l = fb_props->M_l2; + } + + if (m[i] > m_l) { + fb_props->tables.SWR[SWR_idx(l, i)] = + fb_props->tables.SWR[SWR_idx(l, (i - 1))] + + sqrt(imf[l][i] * imf[l][i - 1]) * dlm * log(10.); + } + else { + fb_props->tables.SWR[SWR_idx(l, i)] = + fb_props->tables.SWR[SWR_idx(l, (i - 1))]; + } + + if (l == 0) { + m_l = fb_props->M_l3; + } + else { + m_l = 0.; + } + + /* This is where we integrate up the IMF */ + if (m[i] > m_l) { /* H/He change from stars that go SN */ + for (k = 1; k < 3; k++) { + snii2_hi = LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], + sniilm[j2], snii[k][l][j2], + fb_props->tables.SNLM[i]); + snii2_lo = LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], + sniilm[j2], snii[k][l][j2], + fb_props->tables.SNLM[i-1]); + if (snii2_hi < 0.) snii2_hi = 0.; + if (snii2_lo < 0.) snii2_lo = 0.; + fb_props->tables.SN2E[SN2E_idx(k, l, i)] = + fb_props->tables.SN2E[SN2E_idx(k, l, (i - 1))] + + (snii2_hi + snii2_lo) / 2. + * sqrt(m[i] * m[i - 1] * imf[l][i] * imf[l][i - 1]) + * dlm * log(10.); + } + } + else { /* low mass stars */ + for (k = 1; k < 3; k++) { + fb_props->tables.SN2E[SN2E_idx(k, l, i)] = + fb_props->tables.SN2E[SN2E_idx(k, l, (i - 1))]; + } + } + + /* Metals from things that don't direct collapse to BH */ + if (m[i] > m_l && m[i] < fb_props->M_u2) { + for (k = 3; k < chem5_NXSN; k++) { + snii2_hi = LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], + sniilm[j2], snii[k][l][j2], + fb_props->tables.SNLM[i]); + snii2_lo = LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], + sniilm[j2], snii[k][l][j2], + fb_props->tables.SNLM[i-1]); + if (snii2_hi < 0.) snii2_hi = 0.; + if (snii2_lo < 0.) snii2_lo = 0.; + fb_props->tables.SN2E[SN2E_idx(k, l, i)] = + fb_props->tables.SN2E[SN2E_idx(k, l, (i - 1))] + + (snii2_hi + snii2_lo) / 2. + * sqrt(m[i] * m[i - 1] * imf[l][i] * imf[l][i - 1]) + * dlm * log(10.); + } + } + else { // low-mass stars, no metals from Type II + for (k = 3; k < chem5_NXSN; k++) { + fb_props->tables.SN2E[SN2E_idx(k, l, i)] = + fb_props->tables.SN2E[SN2E_idx(k, l, (i - 1))]; + } + } + + if (l == 0) { + m_l = max(fb_props->M_l2, fb_props->M_l3); + } + else { + m_l = fb_props->M_l2; + } + + /* IMF integration for total metal mass yield */ + if (m[i] > m_l && m[i] < fb_props->M_u2) { + snii2_hi = LINEAR_INTERPOLATION(sniilm[j1], snii[0][l][j1], + sniilm[j2], snii[0][l][j2], + fb_props->tables.SNLM[i]); + snii2_lo = LINEAR_INTERPOLATION(sniilm[j1], snii[0][l][j1], + sniilm[j2], snii[0][l][j2], + fb_props->tables.SNLM[i-1]); + if (snii2_hi < 0.) snii2_hi = 0.; + if (snii2_lo < 0.) snii2_lo = 0.; + fb_props->tables.SN2R[SN2R_idx(l, i)] = + fb_props->tables.SN2R[SN2R_idx(l, (i - 1))] + + sqrt(imf[l][i] * imf[l][i - 1]) * dlm * log(10.); + fb_props->tables.SN2E[SN2E_idx(0, l, i)] = + fb_props->tables.SN2E[SN2E_idx(0, l, (i - 1))] + + (snii2_hi + snii2_lo) / 2. + * sqrt(imf[l][i] * imf[l][i - 1]) * dlm * log(10.); + } else { + fb_props->tables.SN2R[SN2R_idx(l, i)] = + fb_props->tables.SN2R[SN2R_idx(l, (i - 1))]; + fb_props->tables.SN2E[SN2E_idx(0, l, i)] = + fb_props->tables.SN2E[SN2E_idx(0, l, (i - 1))]; + } + } + for (l = 1; l < NZSN1R; l++) { + if (m[i] > M_l1wd[l] && m[i] < M_u1wd[l]) { + SN1wd[l][i] = + SN1wd[l][i - 1] + sqrt(imf[l][i] * imf[l][i - 1]) * dlm * log(10.); + } + else { + SN1wd[l][i] = SN1wd[l][i - 1]; + } + + if (m[i] > M_l1ms[l] && m[i] < M_u1ms[l]) { + SN1ms[l][i] = + SN1ms[l][i - 1] + pow(sqrt(m[i] * m[i - 1]), -0.35) * dlm * log(10.); + } + else { + SN1ms[l][i] = SN1ms[l][i - 1]; + } + + if (m[i] > M_l1rg[l] && m[i] < M_u1rg[l]) { + SN1rg[l][i] = + SN1rg[l][i - 1] + pow(sqrt(m[i] * m[i - 1]), -0.35) * dlm * log(10.); + } + else { + SN1rg[l][i] = SN1rg[l][i - 1]; + } + } + } + temp_ms = SN1ms[2][NM - 1]; /* normalized at Z=0.004 */ + temp_rg = SN1rg[2][NM - 1]; /* normalized at Z=0.004 */ + + /* Put everything the code units */ + for (i = 0; i < NM; i++) { + for (l = 1; l < NZSN1R; l++) { + SN1ms[l][i] *= fb_props->b_ms / temp_ms; + SN1rg[l][i] *= fb_props->b_rg / temp_rg; + fb_props->tables.SN1R[SN1R_idx(l, i)] = + SN1wd[l][i] * (SN1ms[l][i] + SN1rg[l][i]); + fb_props->tables.SN1R[SN1R_idx(l, i)] /= fb_props->solar_mass_to_mass; + } + + for (l = 1; l < NZSN1Y; l++) { + fb_props->tables.SN1E[SN1E_idx(0, l)] *= (1.e51 / fb_props->energy_to_cgs); + } + + /* convert solar mass to code */ + fb_props->tables.SNLM[i] += log10(fb_props->solar_mass_to_mass); + + for (l = 0; l < NZSN; l++) { + fb_props->tables.SN2R[SN2R_idx(l, i)] /= fb_props->solar_mass_to_mass; + fb_props->tables.SWR[SWR_idx(l, i)] /= fb_props->solar_mass_to_mass; + fb_props->tables.SN2E[SN2E_idx(0, l, i)] *= + (1.e51 / fb_props->energy_to_cgs / fb_props->solar_mass_to_mass); + } + } + + if (engine_rank == 0) { + message("Done Chem5 setup."); + } +} + +/** + * @brief allocates space for the yield tables + * + * @param feedback_props the #feedback_props data struct to store the tables in + */ +INLINE static void feedback_allocate_feedback_tables(struct feedback_props *feedback_props) { + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.LFLT, + SWIFT_STRUCT_ALIGNMENT, + NZLF * NMLF * sizeof(double)) != 0) { + error("Failed to allocate LFLT array"); + } + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.LFLM, + SWIFT_STRUCT_ALIGNMENT, + NMLF * sizeof(double)) != 0) { + error("Failed to allocate LFLM array"); + } + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.LFLZ, + SWIFT_STRUCT_ALIGNMENT, + NZLF * sizeof(double)) != 0) { + error("Failed to allocate LFLZ array"); + } + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SWR, + SWIFT_STRUCT_ALIGNMENT, + NZSN * NM * sizeof(double)) != 0) { + error("Failed to allocate SWR array"); + } + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SN2E, + SWIFT_STRUCT_ALIGNMENT, + chem5_NXSN * NZSN * NM * sizeof(double)) != 0) { + error("Failed to allocate SN2E array"); + } + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SN2R, + SWIFT_STRUCT_ALIGNMENT, + NZSN * NM * sizeof(double)) != 0) { + error("Failed to allocate SN2R array"); + } + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SN1R, + SWIFT_STRUCT_ALIGNMENT, + NZSN1R * NM * sizeof(double)) != 0) { + error("Failed to allocate SN1R array"); + } + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SNLM, + SWIFT_STRUCT_ALIGNMENT, + NM * sizeof(double)) != 0) { + error("Failed to allocate SNLM array"); + } + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SNLZ, + SWIFT_STRUCT_ALIGNMENT, + NZSN * sizeof(double)) != 0) { + error("Failed to allocate SNLZ array"); + } + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SNLZ1R, + SWIFT_STRUCT_ALIGNMENT, + NZSN1R * sizeof(double)) != 0) { + error("Failed to allocate SNLZ1R array"); + } + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SN1E, + SWIFT_STRUCT_ALIGNMENT, + chem5_NXSN * NZSN1Y * sizeof(double)) != 0) { + error("Failed to allocate SN1E array"); + } + + if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SNLZ1Y, + SWIFT_STRUCT_ALIGNMENT, + NZSN1Y * sizeof(double)) != 0) { + error("Failed to allocate SNLZ1Y array"); + } + +} + +/** + * @brief Initialize the global properties of the feedback scheme. + * + * @param fp The #feedback_props. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param hydro_props The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. + */ +void feedback_props_init(struct feedback_props* fp, + const struct phys_const* phys_const, + const struct unit_system* us, + struct swift_params* params, + const struct hydro_props* hydro_props, + const struct cosmology* cosmo) { + + /* Common conversions ------------------------------------------------- */ + + /* Calculate internal mass to solar mass conversion factor */ + const double Msun_cgs = phys_const->const_solar_mass * + units_cgs_conversion_factor(us, UNIT_CONV_MASS); + const double unit_mass_cgs = units_cgs_conversion_factor(us, UNIT_CONV_MASS); + fp->mass_to_solar_mass = unit_mass_cgs / Msun_cgs; + fp->solar_mass_in_g = Msun_cgs; + fp->solar_mass_to_mass = 1. / fp->mass_to_solar_mass; + + /* Calculate temperature to internal energy conversion factor (all internal + * units) */ + const double k_B = phys_const->const_boltzmann_k; + const double m_p = phys_const->const_proton_mass; + const double mu = hydro_props->mu_ionised; + fp->temp_to_u_factor = k_B / (mu * hydro_gamma_minus_one * m_p); + + /* Calculate conversion factor from rho to n_H + * Note this assumes primoridal abundance */ + const double X_H = hydro_props->hydrogen_mass_fraction; + fp->rho_to_n_cgs = + (X_H / m_p) * units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); + + fp->kms_to_internal = + 1.e5 / units_cgs_conversion_factor(us, UNIT_CONV_SPEED); + + fp->kms_to_cms = 1.e5; + + fp->time_to_Myr = units_cgs_conversion_factor(us, UNIT_CONV_TIME) / + (1.e6 * 365.25 * 24. * 60. * 60.); + + /* Convert to Myr first, then multiply by a factor of 1e6 yr / 1 Myr */ + fp->time_to_yr = fp->time_to_Myr * 1.e6; + + fp->length_to_kpc = + units_cgs_conversion_factor(us, UNIT_CONV_LENGTH) / 3.08567758e21f; + + fp->energy_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); + + fp->T_to_internal = + 1. / units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + /* Constant Chem5 parameters ---------------------------------------------- */ + + /* Solar values */ + fp->H_mf = 7.35224e-1; + fp->He_mf = 2.50274e-1; + fp->Z_mf = 0.0144404378; + fp->O_mf = 0.675327e-2 + 0.272594e-5 + 0.152311e-4; + fp->Fe_mf = 0.733849e-4 + 0.119465e-2 + 0.280824e-4 + 0.380282e-5; + + /* supernova energy in foe (D. Rennehan: What does foe mean??) */ + fp->E_sw = 0.2 * (1.e51 / fp->energy_to_cgs); + fp->E_sn1 = 1.3 * (1.e51 / fp->energy_to_cgs); + + fp->imf = parser_get_param_int(params, "KIARAFeedback:imf"); + + /* Kroupa IMF || Chabrier IMF */ + if (fp->imf == 0 || fp->imf == 1) { + fp->ximf = 1.3; + fp->M_u = 120.; + fp->M_l = 0.01; + } + else { + fp->ximf = 1.35; + fp->M_u = 120.; + fp->M_l = 0.07; + } + + fp->ximf3 = 1.35; + fp->M_u3 = 120.; /* >= M_u */ + fp->M_l3 = 20.; /* >= M_l */ + fp->zmax3 = -999.; + fp->M_u2 = 50.; + fp->M_l2 = 8.; + fp->b_rg = 0.02; /* binary parameter for SNIa */ + fp->b_ms = 0.04; /* binary parameter for SNIa */ + + /* Chem5 indices to Swift */ + fp->element_index_conversions[chemistry_element_H] = chem5_element_H; + fp->element_index_conversions[chemistry_element_He] = chem5_element_He; + fp->element_index_conversions[chemistry_element_C] = chem5_element_C; + fp->element_index_conversions[chemistry_element_N] = chem5_element_N; + fp->element_index_conversions[chemistry_element_O] = chem5_element_O; + fp->element_index_conversions[chemistry_element_Ne] = chem5_element_Ne; + fp->element_index_conversions[chemistry_element_Mg] = chem5_element_Mg; + fp->element_index_conversions[chemistry_element_Si] = chem5_element_Si; + fp->element_index_conversions[chemistry_element_S] = chem5_element_S; + fp->element_index_conversions[chemistry_element_Ca] = chem5_element_Ca; + fp->element_index_conversions[chemistry_element_Fe] = chem5_element_Fe; + +#if COOLING_GRACKLE_MODE >= 2 + /* Production tables: AGB for C/O>1, AGB for C/O<1, and SNII (ignore SNIa) */ + fp->delta_AGBCOG1[chemistry_element_H] = 0.0; + fp->delta_AGBCOG1[chemistry_element_He] = 0.0; /* He could be removed */ + fp->delta_AGBCOG1[chemistry_element_C] = 0.2; + fp->delta_AGBCOG1[chemistry_element_N] = 0.0; /* N could be removed */ + fp->delta_AGBCOG1[chemistry_element_O] = 0.0; + fp->delta_AGBCOG1[chemistry_element_Ne] = 0.0; /* Ne could be removed */ + fp->delta_AGBCOG1[chemistry_element_Mg] = 0.0; + fp->delta_AGBCOG1[chemistry_element_Si] = 0.0; + fp->delta_AGBCOG1[chemistry_element_S] = 0.0; + fp->delta_AGBCOG1[chemistry_element_Ca] = 0.0; + fp->delta_AGBCOG1[chemistry_element_Fe] = 0.0; + + fp->delta_AGBCOL1[chemistry_element_H] = 0.0; + fp->delta_AGBCOL1[chemistry_element_He] = 0.0; + fp->delta_AGBCOL1[chemistry_element_C] = 0.0; + fp->delta_AGBCOL1[chemistry_element_N] = 0.0; + fp->delta_AGBCOL1[chemistry_element_O] = 0.2; + fp->delta_AGBCOL1[chemistry_element_Ne] = 0.0; + fp->delta_AGBCOL1[chemistry_element_Mg] = 0.2; + fp->delta_AGBCOL1[chemistry_element_Si] = 0.2; + fp->delta_AGBCOL1[chemistry_element_S] = 0.2; + fp->delta_AGBCOL1[chemistry_element_Ca] = 0.2; + fp->delta_AGBCOL1[chemistry_element_Fe] = 0.2; + + /* From Poping+17, default=2 in Simba's dust model */ + const float dust_boost_factor = + parser_get_opt_param_float(params, + "KIARAFeedback:dust_boost_factor", 2.f); + + fp->delta_SNII[chemistry_element_H] = 0.00 * dust_boost_factor; + fp->delta_SNII[chemistry_element_He] = 0.00 * dust_boost_factor; + fp->delta_SNII[chemistry_element_C] = 0.15 * dust_boost_factor ; + fp->delta_SNII[chemistry_element_N] = 0.00 * dust_boost_factor; + fp->delta_SNII[chemistry_element_O] = 0.15 * dust_boost_factor ; + fp->delta_SNII[chemistry_element_Ne] = 0.00 * dust_boost_factor; + fp->delta_SNII[chemistry_element_Mg] = 0.15 * dust_boost_factor; + fp->delta_SNII[chemistry_element_Si] = 0.15 * dust_boost_factor; + fp->delta_SNII[chemistry_element_S] = 0.15 * dust_boost_factor ; + fp->delta_SNII[chemistry_element_Ca] = 0.15 * dust_boost_factor; + fp->delta_SNII[chemistry_element_Fe] = 0.15 * dust_boost_factor; +#endif + + /* chem5 operation modes ------------------------------------------------- */ + + fp->with_HN_energy_from_chem5 = + parser_get_param_int(params, "KIARAFeedback:use_HN_energy_from_chem5"); + + fp->with_SNII_energy_from_chem5 = + parser_get_param_int(params, "KIARAFeedback:use_SNII_energy_from_chem5"); + + fp->with_SNIa_energy_from_chem5 = + parser_get_param_int(params, "KIARAFeedback:use_SNIa_energy_from_chem5"); + + fp->stellar_enrichment_frequency = + parser_get_opt_param_float(params, + "KIARAFeedback:stellar_enrichment_frequency", 0.f); + + /* Properties of the enrichment down-sampling ----------------------------- */ + + fp->stellar_evolution_age_cut = + parser_get_param_double(params, + "KIARAFeedback:stellar_evolution_age_cut_Gyr") * + phys_const->const_year * 1e9; + + /* Stellar feedback cannot grow a particle bigger than this factor + * times the particles' current mass */ + fp->max_mass_increase_factor = + parser_get_opt_param_float(params, + "KIARAFeedback:max_mass_increase_factor", + 1.5f); + + /* Stellar feedback heating cannot increase a particle's internal + * energy more than this factor */ + fp->max_energy_increase_factor = + parser_get_opt_param_float(params, + "KIARAFeedback:max_energy_increase_factor", + 10.f); + + /* Momentum exchange lower limit from stellar feedback mass injection + fp->min_energy_decrease_factor = + parser_get_opt_param_float(params, + "KIARAFeedback:min_energy_decrease_factor", + 0.5f);*/ + + /* Option to use heat from SNIa to move gas off of the EoS */ + fp->SNIa_add_heat_to_ISM = + parser_get_opt_param_int(params, "KIARAFeedback:SNIa_add_heat_to_ISM", 0); + fp->SNIa_add_heat_to_ISM_tolerance = + parser_get_opt_param_float(params, + "KIARAFeedback:SNIa_add_heat_to_ISM_tolerance", 1.e-6f); + + fp->stellar_evolution_sampling_rate = parser_get_param_double( + params, "KIARAFeedback:stellar_evolution_sampling_rate"); + + if (fp->stellar_evolution_sampling_rate < 1 || + fp->stellar_evolution_sampling_rate >= (1 << (8 * sizeof(char) - 1))) + error("Stellar evolution sampling rate too large. Must be >0 and <%d", + (1 << (8 * sizeof(char) - 1))); + + fp->metal_yield_multiplier = + parser_get_opt_param_float(params, + "KIARAFeedback:metal_yield_multiplier", 1.f); + + /* Properties of Simba kinetic winds -------------------------------------- */ + + fp->FIRE_velocity_normalization = + parser_get_param_double(params, + "KIARAFeedback:FIRE_velocity_normalization"); + fp->FIRE_velocity_slope = + parser_get_param_double(params, "KIARAFeedback:FIRE_velocity_slope"); + fp->FIRE_eta_normalization = + parser_get_param_double(params, "KIARAFeedback:FIRE_eta_normalization"); + fp->FIRE_eta_break = + parser_get_param_double(params, "KIARAFeedback:FIRE_eta_break_Msun"); + fp->FIRE_eta_break *= fp->solar_mass_to_mass; + fp->FIRE_eta_lower_slope = + parser_get_param_double(params, "KIARAFeedback:FIRE_eta_lower_slope"); + fp->FIRE_eta_upper_slope = + parser_get_param_double(params, "KIARAFeedback:FIRE_eta_upper_slope"); + fp->FIRE_eta_lower_slope_EOR = + parser_get_opt_param_double(params, "KIARAFeedback:FIRE_eta_lower_slope_EOR", fp->FIRE_eta_lower_slope ); + + fp->wind_velocity_suppression_redshift = + parser_get_opt_param_float(params, + "KIARAFeedback:wind_velocity_suppression_redshift", 0.f); + + fp->wind_eta_suppression_redshift = + parser_get_opt_param_float(params, + "KIARAFeedback:wind_eta_suppression_redshift", 0.f); + + fp->SNII_energy_multiplier = + parser_get_opt_param_float(params, + "KIARAFeedback:SNII_energy_multiplier", 1.f); + + fp->kick_radius_over_h = + parser_get_opt_param_float(params, + "KIARAFeedback:kick_radius_over_h", 0.5f); + + fp->max_frac_of_kernel_to_launch = + parser_get_opt_param_float(params, + "KIARAFeedback:max_frac_of_kernel_to_launch", 1.0f); + + fp->use_sfr_weighted_launch = + parser_get_opt_param_int(params, + "KIARAFeedback:use_sfr_weighted_launch", 1); + + fp->metal_dependent_vwind = + parser_get_opt_param_int(params, + "KIARAFeedback:metal_dependent_vwind", 0); + + fp->minimum_galaxy_stellar_mass = + parser_get_param_double(params, + "KIARAFeedback:minimum_galaxy_stellar_mass_Msun"); + fp->minimum_galaxy_stellar_mass *= fp->solar_mass_to_mass; + + fp->galaxy_particle_resolution_count = + parser_get_opt_param_int(params, + "KIARAFeedback:galaxy_particle_resolution_count", 0); + + fp->eta_suppression_factor_floor = + parser_get_opt_param_float(params, + "KIARAFeedback:eta_suppression_factor_floor", 0.2f); + + fp->kick_direction_flag = + parser_get_opt_param_double(params, "KIARAFeedback:kick_direction_flag", 1); + + fp->kick_velocity_scatter = + parser_get_param_double(params, "KIARAFeedback:kick_velocity_scatter"); + + fp->wind_decouple_time_factor = parser_get_param_double( + params, "KIARAFeedback:wind_decouple_time_factor"); + fp->recouple_ism_density_nH_cgs = parser_get_opt_param_double( + params, "KIARAFeedback:recouple_ism_density_nH_cgs", 0.075); + fp->recouple_density_factor = parser_get_opt_param_double( + params, "KIARAFeedback:recouple_density_factor", 0.01); + + fp->cold_wind_internal_energy = parser_get_opt_param_double( + params, "KIARAFeedback:cold_wind_temperature_K", 1.e4); + if (fp->cold_wind_internal_energy <= 0.) { + error("KIARAFeedback:cold_wind_temperature_K must be strictly positive."); + } + fp->hot_wind_internal_energy = parser_get_opt_param_double( + params, "KIARAFeedback:hot_wind_temperature_K", 1.e4); + if (fp->hot_wind_internal_energy <= 0.) { + error("KIARAFeedback:hot_wind_temperature_K must be strictly positive."); + } + /* Check if we are using Firehose model, if so turn off hot winds */ + int firehose_on = parser_get_opt_param_int( + params, "KIARAChemistry:use_firehose_wind_model", 0); + if (firehose_on) { + if (engine_rank == 0) { + message("WARNING: Firehose model is on. Setting hot_wind_temperature_K to " + "cold_wind_temperature_K"); + } + + fp->use_firehose_model = 1; + fp->hot_wind_internal_energy = fp->cold_wind_internal_energy; + } + else { + fp->use_firehose_model = 0; + } + + /* Early stellar feedback model of Keller et al 2022. */ + fp->early_stellar_feedback_alpha = parser_get_opt_param_float( + params, "KIARAFeedback:early_stellar_feedback_alpha", 0.); + /* Cloud-scale SF efficiency for early stellar feedback; default from Leroy+25 */ + const float epssf = parser_get_opt_param_float( + params, "KIARAFeedback:early_stellar_feedback_epssf", 0.35); + fp->early_stellar_feedback_epsterm = (1. - epssf) / epssf; + /* Early stellar feedback timescale in Myr; store inverse for efficiency */ + fp->early_stellar_feedback_tfb= parser_get_opt_param_float( + params, "KIARAFeedback:early_stellar_feedback_tfb", 3.31); + fp->early_stellar_feedback_tfb /= fp->time_to_Myr; + fp->early_stellar_feedback_tfb_inv = 1.f / fp->early_stellar_feedback_tfb; + +#if COOLING_GRACKLE_MODE >= 2 + fp->max_dust_fraction = parser_get_opt_param_double( + params, "KIARAFeedback:max_dust_fraction", 0.9); + fp->SNe_smoothing_time_in_Myr = parser_get_opt_param_double( + params, "KIARAFeedback:SNe_smoothing_time_in_Myr", 0.); +#endif + + /* Convert Kelvin to internal energy and internal units */ + fp->cold_wind_internal_energy *= + fp->temp_to_u_factor / + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + fp->hot_wind_internal_energy = + fp->temp_to_u_factor / + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + /* Read yield table filepath */ + parser_get_param_string(params, "KIARAFeedback:tables_path", + fp->tables_path); + + /* Allocate the memory for all of the feedback tables --------------------- */ + feedback_allocate_feedback_tables(fp); + + /* Initialise the yield/mass tables --------------------------------------- */ + feedback_prepare_interpolation_tables(fp); + + /* Output some information to the people ---------------------------------- */ + + if (engine_rank == 0) { + message("Feedback model is KIARA"); + message("Feedback FIRE velocity normalization: %g", + fp->FIRE_velocity_normalization); + message("Feedback FIRE velocity slope: %g", fp->FIRE_velocity_slope); + message("Feedback velocity scatter: %g", fp->kick_velocity_scatter); + message("Feedback FIRE eta normalization: %g", fp->FIRE_eta_normalization); + message("Feedback FIRE eta break: %g", fp->FIRE_eta_break); + message("Feedback FIRE eta upper slope: %g", fp->FIRE_eta_upper_slope); + message("Feedback FIRE eta lower slope: %g", fp->FIRE_eta_lower_slope); + message("Feedback FIRE eta lower slope at z>6: %g", fp->FIRE_eta_lower_slope_EOR); + + if (fabs(fp->wind_velocity_suppression_redshift) != 0.f) { + message("Feedback wind speed early suppression enabled " + "above redshift: %g", + fp->wind_velocity_suppression_redshift); + } + + message("Feedback use Chem5 SNII energy: %d", + fp->with_SNII_energy_from_chem5); + message("Feedback use Chem5 SNIa energy: %d", + fp->with_SNIa_energy_from_chem5); + } +} + +/** + * @brief Zero pointers in feedback_table structs + * + * @param table feedback_tables struct in which pointers to tables + * set to NULL + */ +void feedback_zero_table_pointers(struct feedback_tables* table) { + + table->LFLT = NULL; + table->LFLM = NULL; + table->LFLZ = NULL; + table->SWR = NULL; + table->SN2E = NULL; + table->SN2R = NULL; + table->SN1R = NULL; + table->SNLM = NULL; + table->SNLZ = NULL; + table->SNLZ1R = NULL; + table->SN1E = NULL; + table->SNLZ1Y = NULL; +} + +/** + * @brief Restore feedback tables (if applicable) after + * restart + * + * @param fp the #feedback_props structure + */ +void feedback_restore_tables(struct feedback_props* fp) { + + /* Allocate the memory for all of the feedback tables --------------------- */ + feedback_allocate_feedback_tables(fp); + + /* Initialise the yield/mass tables --------------------------------------- */ + feedback_prepare_interpolation_tables(fp); +} + +/** + * @brief Clean-up the memory allocated for the feedback routines + * + * We simply free all the arrays. + * + * @param fp the feedback data structure. + */ +void feedback_clean(struct feedback_props* fp) { } + +/** + * @brief Write a feedback struct to the given FILE as a stream of bytes. + * + * @param feedback the struct + * @param stream the file stream + */ +void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream) { + /* To make sure everything is restored correctly, we zero all the pointers to + tables. If they are not restored correctly, we would crash after restart on + the first call to the feedback routines. Helps debugging. */ + struct feedback_props feedback_copy = *feedback; + + feedback_zero_table_pointers(&feedback_copy.tables); + + restart_write_blocks((void*)&feedback_copy, sizeof(struct feedback_props), 1, + stream, "feedback", "feedback function"); +} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * Read the structure from the stream and restore the feedback tables by + * re-reading them. + * + * @param feedback the struct + * @param stream the file stream + */ +void feedback_struct_restore(struct feedback_props* feedback, FILE* stream) { + restart_read_blocks((void*)feedback, sizeof(struct feedback_props), 1, stream, + NULL, "feedback function"); + + if (strlen(feedback->tables_path) != 0) + feedback_restore_tables(feedback); +} diff --git a/src/feedback/KIARA/feedback.h b/src/feedback/KIARA/feedback.h new file mode 100644 index 0000000000..d8ee509b1d --- /dev/null +++ b/src/feedback/KIARA/feedback.h @@ -0,0 +1,1000 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_KIARA_H +#define SWIFT_FEEDBACK_KIARA_H + +#include "cosmology.h" +#include "engine.h" +#include "error.h" +#include "feedback_properties.h" +#include "hydro_properties.h" +#include "part.h" +#include "units.h" +#include "timers.h" +#include "timestep_sync.h" +#include "timestep_sync_part.h" +#include "star_formation.h" + +#include + +double feedback_get_lum_from_star_particle(const struct spart *sp, + double age, + const struct feedback_props* fb_props); +void feedback_get_ejecta_from_star_particle(const struct spart* sp, + double age, + const struct feedback_props* fb_props, + double dt, + double *N_SNe, + double *ejecta_energy, + double *ejecta_mass, + double *ejecta_unprocessed, + double ejecta_metal_mass[chem5_element_count]); +void feedback_dust_production_condensation(struct spart* sp, + double star_age, + const struct feedback_props* fb_props, + double delta_metal_mass[chemistry_element_count]); +double feedback_life_time(const struct feedback_props* fb_props, + const double m, + const double z); +double feedback_imf(const struct feedback_props* fb_props, + const double m); +void feedback_set_turnover_mass(const struct feedback_props* fb_props, + const double z, double* LFLT2); +double feedback_get_turnover_mass(const struct feedback_props* fb_props, + const double t, const double z); +void feedback_prepare_interpolation_tables( + const struct feedback_props* fb_props); + +/** + * @brief Determine the probability of a gas particle being kicked + * due to stellar feedback in star forming gas. + * + * @param p The #part to consider. + * @param xp The #xpart to consider. + * @param e The #engine. + * @param fb_props The feedback properties. + * @param ti_current The current timestep. + * @param dt_part The time step of the particle. + * @param rand_for_sf_wind The random number for the wind generation. + * @param wind_mass The amount of mass in the wind (code units). + */ +__attribute__((always_inline)) INLINE static double feedback_wind_probability( + struct part* p, struct xpart* xp, const struct engine* e, + const struct cosmology* cosmo, + const struct feedback_props* fb_props, + const integertime_t ti_current, + const double dt_part, + double *rand_for_sf_wind, + double *wind_mass) { + + return 0.f; +} + + +/** + * @brief Kick a gas particle selected for stellar feedback. + * + * @param p The #part to consider. + * @param xp The #xpart to consider. + * @param e The #engine. + * @param fb_props The feedback properties. + * @param ti_current The current timestep. + * @param with_cosmology Is cosmological integration on? + * @param dt_part The time step of the particle. + * @param wind_mass The amount of mass in the wind (code units). + */ +__attribute__((always_inline)) INLINE static +void feedback_kick_and_decouple_part( + struct part* p, struct xpart* xp, + const struct engine* e, + const struct cosmology* cosmo, + const struct feedback_props* fb_props, + const integertime_t ti_current, + const int with_cosmology, + const double dt_part, + const double wind_mass) {}; + + +/** + * @brief Recouple wind particles. + * + * @param p The #part to consider. + * @param xp The #xpart to consider. + * @param e The #engine. + * @param with_cosmology Is this a cosmological simulation? + */ +__attribute__((always_inline)) INLINE static +void feedback_recouple_set_flags(struct part* p, + const struct cosmology* cosmo) { + + p->feedback_data.decoupling_delay_time = 0.f; + p->decoupled = 0; + p->chemistry_data.radius_stream = 0.f; + + /* Reset subgrid properties */ + p->cooling_data.subgrid_temp = 0.f; + p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); + p->cooling_data.subgrid_fcold = 0.f; + + /* Make sure to sync the newly coupled part on the timeline */ + warning("Recoupling part %lld", p->id); + timestep_sync_part(p); +} + +/** + * @brief Recouple wind particles. + * + * @param p The #part to consider. + * @param xp The #xpart to consider. + * @param e The #engine. + * @param with_cosmology Is this a cosmological simulation? + * @param cosmo The cosmology of the simulation. + * @param fb_props The #feedback_props feedback parameters. + */ +__attribute__((always_inline)) INLINE static void feedback_recouple_part( + struct part* p, struct xpart* xp, const struct engine* e, + const int with_cosmology, + const struct cosmology* cosmo, + const struct feedback_props* fb_props) { + + if (p->decoupled) { + const integertime_t ti_step = get_integer_timestep(p->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current - 1, p->time_bin); + + /* Get particle time-step */ + double dt_part; + if (with_cosmology) { + dt_part = + cosmology_get_delta_time(e->cosmology, ti_begin, ti_begin + ti_step); + } + else { + dt_part = get_timestep(p->time_bin, e->time_base); + } + + /* Decrement the counter */ + p->feedback_data.decoupling_delay_time -= dt_part; + + /** + * Recouple under 3 conditions: + * (1) Below the density threshold. + * (2) If the stream radius is negative. + * (3) If the timer has run out. + */ + const double rho_nH_cgs = + hydro_get_physical_density(p, cosmo) * fb_props->rho_to_n_cgs; + const double rho_recouple_cgs = + fb_props->recouple_density_factor * + fb_props->recouple_ism_density_nH_cgs; + + const int recouple = (p->feedback_data.decoupling_delay_time <= 0.f || + p->chemistry_data.radius_stream < 0.f || + rho_nH_cgs < rho_recouple_cgs); + + if (recouple) { + feedback_recouple_set_flags(p, cosmo); + } + else { + /* Reset subgrid properties if decoupled for safety */ + p->cooling_data.subgrid_temp = 0.f; + p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); + p->cooling_data.subgrid_fcold = 0.f; + } + } +} + +/** + * @brief Sets the wind direction vector for feedback kicks + * + * @param p The #part to consider. + * @param xp The #xpart to consider. + * @param e The #engine. + * @param with_cosmology Is this a cosmological simulation? + * @param cosmo The cosmology of the simulation. + * @param fb_props The #feedback_props feedback parameters. + */ +__attribute__((always_inline)) INLINE static void feedback_set_wind_direction( + struct part* p, struct xpart* xp, const struct engine* e, + const int with_cosmology, + const struct cosmology* cosmo, + const struct feedback_props* fb_props) { + + p->feedback_data.wind_direction[0] = + p->gpart->a_grav[1] * p->gpart->v_full[2] - + p->gpart->a_grav[2] * p->gpart->v_full[1]; + p->feedback_data.wind_direction[1] = + p->gpart->a_grav[2] * p->gpart->v_full[0] - + p->gpart->a_grav[0] * p->gpart->v_full[2]; + p->feedback_data.wind_direction[2] = + p->gpart->a_grav[0] * p->gpart->v_full[1] - + p->gpart->a_grav[1] * p->gpart->v_full[0]; +} + +/** + * @brief Determine if particles that ignore cooling should start cooling again. + * + * @param p The #part to consider. + * @param xp The #xpart to consider. + * @param e The #engine. + * @param cosmo The cosmological information of the simulation. + * @param with_cosmology Is this a cosmological simulation? + */ +__attribute__((always_inline)) INLINE static void feedback_ready_to_cool( + struct part* p, struct xpart* xp, const struct engine* e, + const struct cosmology* restrict cosmo, const int with_cosmology) { + + /* No reason to do this if the decoupling time is zero */ + if (p->feedback_data.cooling_shutoff_delay_time > 0.f) { + + /* Reset subgrid properties */ + p->cooling_data.subgrid_temp = 0.f; + p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); + p->cooling_data.subgrid_fcold = 0.f; + + const integertime_t ti_step = get_integer_timestep(p->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current - 1, p->time_bin); + + /* Get particle time-step */ + double dt_part; + if (with_cosmology) { + dt_part = + cosmology_get_delta_time(e->cosmology, ti_begin, ti_begin + ti_step); + } else { + dt_part = get_timestep(p->time_bin, e->time_base); + } + + p->feedback_data.cooling_shutoff_delay_time -= dt_part; + + if (p->feedback_data.cooling_shutoff_delay_time < 0.f) { + p->feedback_data.cooling_shutoff_delay_time = 0.f; + + /* Make sure to sync the newly coupled part on the timeline */ + warning("Recoupling part that is now cooling %lld", p->id); + timestep_sync_part(p); + } + } + else { + /* Because we are using floats, always make sure to set exactly zero */ + p->feedback_data.cooling_shutoff_delay_time = 0.f; + p->decoupled = 0; + } + +} + +/** + * @brief Update the properties of a particle due to feedback effects after + * the cooling was applied. + * + * Nothing to do here in the KIARA model. + * + * @param p The #part to consider. + * @param xp The #xpart to consider. + * @param e The #engine. + * @param with_cosmology Is this a cosmological simulation? + */ +__attribute__((always_inline)) INLINE static void feedback_update_part( + struct part* p, struct xpart* xp, const struct engine* e) {} + +/** + * @brief Reset the gas particle-carried fields related to feedback at the + * start of a step. + * + * @param p The particle. + * @param xp The extended data of the particle. + */ +__attribute__((always_inline)) INLINE static void feedback_reset_part( + struct part* p, struct xpart* xp) {} + +/** + * @brief Should this particle be doing any feedback-related operation? + * + * @param sp The #spart. + * @param e The #engine. + */ +__attribute__((always_inline)) INLINE static int feedback_is_active( + const struct spart* sp, const struct engine* e) { + + //message("FEEDBACK_IS_ACTIVE %d %g %d yes? %d", e->step, sp->birth_time, sp->count_since_last_enrichment, e->step <= 0 || + // ((sp->birth_time != -1.) && (sp->count_since_last_enrichment == 0))); + return e->step <= 0 || + ((sp->birth_time != -1.) && (sp->count_since_last_enrichment == 0)); +} + +/** + * @brief Should this particle be doing any DM looping? + * + * @param sp The #spart. + * @param e The #engine. + */ +__attribute__((always_inline)) INLINE static int stars_dm_loop_is_active( + const struct spart* sp, const struct engine* e) { + /* Active stars always do the DM loop for the KIARA model */ + return 0; +} + +/** + * @brief Prepares a s-particle for its feedback interactions + * + * @param sp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void feedback_init_spart( + struct spart* sp) { + + /* Default to not suppression the mass loading in the winds */ + sp->feedback_data.eta_suppression_factor = 1.f; + sp->feedback_data.kernel_wt_sum = 0.f; + sp->feedback_data.wind_wt_sum = 0.f; + sp->feedback_data.ngb_mass = 0.f; + sp->feedback_data.wind_ngb_mass = 0.f; + + /* Check reservoirs each time-step for out-of-bounds values */ + if (sp->feedback_data.mass_to_launch < 0.f) { + sp->feedback_data.mass_to_launch = 0.f; + } + + if (sp->feedback_data.physical_energy_reservoir < 0.) { + sp->feedback_data.physical_energy_reservoir = 0.; + } + +#ifdef SWIFT_STARS_DENSITY_CHECKS + sp->has_done_feedback = 0; +#endif +} + +/** + * @brief Returns the length of time since the particle last did + * enrichment/feedback. + * + * @param sp The #spart. + * @param with_cosmology Are we running with cosmological time integration on? + * @param cosmo The cosmological model. + * @param time The current time (since the Big Bang / start of the run) in + * internal units. + * @param dt_star the length of this particle's time-step in internal units. + * @return The length of the enrichment step in internal units. + */ +INLINE static double feedback_get_enrichment_timestep( + const struct spart* sp, const int with_cosmology, + const struct cosmology* cosmo, const double time, const double dt_star) { + + if (with_cosmology) { + return cosmology_get_delta_time_from_scale_factors( + cosmo, (double)sp->last_enrichment_time, cosmo->a); + } + else { + return time - (double)sp->last_enrichment_time; + } +} + +/** + * @brief Prepares a star's feedback field before computing what + * needs to be distributed. + */ +__attribute__((always_inline)) INLINE static void feedback_reset_feedback( + struct spart* sp, const struct feedback_props* feedback_props) { + + /* Zero the amount of mass that is distributed */ + sp->feedback_data.mass = 0.; + + /* Zero the metal enrichment quantities */ + for (int i = 0; i < chemistry_element_count; i++) { + sp->feedback_data.metal_mass[i] = 0.; +#if COOLING_GRACKLE_MODE >= 2 + sp->feedback_data.delta_dust_mass[i] = 0.; +#endif + } + sp->feedback_data.total_metal_mass = 0.; + + /* Zero the energy to inject */ + sp->feedback_data.energy = 0.; + +} + +/** + * @brief Initialises the s-particles feedback props for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon. + * @param feedback_props The properties of the feedback model. + */ +__attribute__((always_inline)) INLINE static void feedback_first_init_spart( + struct spart* sp, const struct feedback_props* feedback_props) { + + feedback_init_spart(sp); + sp->feedback_data.SNe_ThisTimeStep = 0.; + sp->feedback_data.SNe_Total = 0.; + sp->feedback_data.firehose_radius_stream = 0.f; + sp->feedback_data.mass_to_launch = 0.f; + sp->feedback_data.total_mass_kicked = 0.f; + sp->feedback_data.wind_velocity = 0.f; + sp->feedback_data.physical_energy_reservoir = 0.; + sp->feedback_data.N_launched = 0; + sp->feedback_data.eta_suppression_factor = 1.f; + +} + +/** + * @brief Initialises the particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions or assignments between the particle + * and extended particle fields. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + */ +__attribute__((always_inline)) INLINE static void feedback_first_init_part( + struct part *restrict p, struct xpart *restrict xp) { + + p->feedback_data.decoupling_delay_time = 0.f; + p->feedback_data.number_of_times_decoupled = 0; + p->feedback_data.cooling_shutoff_delay_time = 0.f; + p->feedback_data.kick_id = -1; + p->feedback_data.mass_limiter_count = 0; + p->feedback_data.heating_limiter_count = 0; + for (int i = 0; i < 3; i++) p->feedback_data.wind_direction[i] = 0.f; +} + +/** + * @brief Initialises the s-particles feedback props for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon. + * @param feedback_props The properties of the feedback model. + */ +__attribute__((always_inline)) INLINE static void feedback_prepare_spart( + struct spart* sp, const struct feedback_props* feedback_props) {} + +/** + * @brief Compute kick velocity for particle sp based on host galaxy properties, + * in code units + * + * @param sp The #spart to consider + * @param cosmo The cosmological model. + * @param fb_props Properties of the feedback scheme. + * @param ti_current Current integer time used value for seeding random number + */ +__attribute__((always_inline)) INLINE static double +feedback_compute_kick_velocity(const float galaxy_stellar_mass, + const size_t sp_id, const struct cosmology* cosmo, + const struct feedback_props *fb_props, const integertime_t ti_current) { + + /* Compute galaxy mass. This is done in the RUNNER files. */ + float galaxy_stellar_mass_Msun = galaxy_stellar_mass; // in code units for now + if (galaxy_stellar_mass_Msun < fb_props->minimum_galaxy_stellar_mass) { + galaxy_stellar_mass_Msun = fb_props->minimum_galaxy_stellar_mass; + } + galaxy_stellar_mass_Msun *= fb_props->mass_to_solar_mass; // convert to Msun + + /* Physical circular velocity km/s from z=0-2 DEEP2 + measurements by Dutton+11 */ + + /* Dutton+11 eq 6: log (M* / 1e10) = -0.61 + 4.51 log (vdisk / 100) */ + const float v_circ_km_s = + 100.f * powf(4.0738f * galaxy_stellar_mass_Msun * 1.e-10f, 0.221729f) * + pow(cosmo->H / cosmo->H0, 1.f / 3.f); + + const float rand_for_scatter = + random_unit_interval(sp_id, ti_current, + random_number_stellar_feedback_2); + + /* The wind velocity in internal units and COMOVING from FIRE scalings */ + float wind_velocity = + fb_props->FIRE_velocity_normalization * + powf(v_circ_km_s / 200.f, fb_props->FIRE_velocity_slope) * + ( 1.f - fb_props->kick_velocity_scatter + + 2.f * fb_props->kick_velocity_scatter * rand_for_scatter) * + v_circ_km_s * + fb_props->kms_to_internal * + /* Note that xpj->v_full = a^2 * dx/dt, with x the comoving coordinate. + * Thus a physical kick, dv, gets translated into a code velocity kick, + * a * dv */ + cosmo->a; + + const float a_suppress_inv = + (1.f + fabs(fb_props->wind_velocity_suppression_redshift)); + if (fb_props->wind_velocity_suppression_redshift > 0 && + cosmo->z > fb_props->wind_velocity_suppression_redshift) { + wind_velocity *= cosmo->a * cosmo->a * a_suppress_inv * a_suppress_inv; + } + else if (fb_props->wind_velocity_suppression_redshift < 0) { + wind_velocity *= expf(-powf(cosmo->a * a_suppress_inv, -3.f)); + } + + /* internal, COMOVING units */ + return wind_velocity; +} + +/** + * @brief Prepare a #spart for the feedback task. + * + * In KIARA, this function does the stellar evolution for a #spart. + * + * @param sp The particle to act upon + * @param feedback_props The #feedback_props structure. + * @param cosmo The current cosmological model. + * @param us The unit system. + * @param phys_const The physical constants in internal units. + * @param star_age_beg_step The age of the star at the star of the time-step in + * internal units. + * @param dt The time-step size of this star in internal units. + * @param time The physical time in internal units. + * @param ti_begin The integer time at the beginning of the step. + * @param with_cosmology Are we running with cosmology on? + */ +__attribute__((always_inline)) INLINE static void feedback_prepare_feedback( + struct spart* restrict sp, const struct feedback_props* feedback_props, + const struct cosmology* cosmo, const struct unit_system* us, + const struct phys_const* phys_const, + const double star_age_beg_step, + const double dt, const double time, const integertime_t ti_begin, + const int with_cosmology) { + + if (sp->feedback_data.ngb_mass <= 0.f) { + warning("Star %lld has zero neighbor gas density.", sp->id); + return; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (sp->birth_time == -1.) error("Evolving a star particle that should not!"); +#endif + + TIMER_TIC; + +#if COOLING_GRACKLE_MODE >= 2 + /* Compute Habing luminosity of star for use in ISRF + (only with Grackle subgrid ISM model) */ + /*sp->feedback_data.lum_habing = + feedback_get_lum_from_star_particle(sp, star_age_beg_step, feedback_props); + message("G0: age %g Lhabing %g\n", + star_age_beg_step * feedback_props->time_to_Myr, + sp->feedback_data.lum_habing); + */ +#endif + + /* Zero out mass and energy return for this step */ + sp->feedback_data.mass = 0.; + sp->feedback_data.energy = 0.; + + /* Do chem5 chemical evolution model */ + int elem; + double N_SNe = 0.; + double ejecta_energy = 0.; + double ejecta_mass = 0.; + double ejecta_unprocessed = 0.; + double ejecta_metal_mass[chem5_element_count]; + for (elem = 0; elem < chem5_element_count; elem++) { + ejecta_metal_mass[elem] = 0.; + } + + feedback_get_ejecta_from_star_particle(sp, + star_age_beg_step, + feedback_props, + dt, + &N_SNe, + &ejecta_energy, + &ejecta_mass, + &ejecta_unprocessed, + ejecta_metal_mass); + + ejecta_mass *= 0.5f; // fudge factor + + if (isnan(ejecta_mass)) { + for (elem = 0; elem < chem5_element_count; elem++) { + message("ejecta_metal_mass[%d]=%g", elem, ejecta_metal_mass[elem]); + } + + message("[Fe/H] = %g", + sp->chemistry_data.metal_mass_fraction[chemistry_element_Fe] / + sp->chemistry_data.metal_mass_fraction[chemistry_element_H]); + message("Z = %g", sp->chemistry_data.metal_mass_fraction_total); + + error("Star particle %lld with mass %g (init_mass %g) is trying to give " + "away NaN mass (Mejecta=%g, Energy=%g, Unprocessed=%g)!", + sp->id, sp->mass, sp->mass_init, ejecta_mass, ejecta_energy, + ejecta_unprocessed); + } + + if (ejecta_energy < 0.f) { + warning("Star particle %lld with mass %g (init_mass %g) is trying to give " + "away negative energy (Mejecta=%g, Energy=%g, Unprocessed=%g)!", + sp->id, sp->mass, sp->mass_init, ejecta_mass, ejecta_energy, + ejecta_unprocessed); + feedback_reset_feedback(sp, feedback_props); + return; + } + + if (sp->mass-ejecta_mass < 0.2 * sp->mass_init) { + warning("Star particle %lld with mass %g is trying to lower its mass " + "past 0.2 of initial (Mejecta=%g)!", + sp->id, sp->mass, ejecta_mass); + feedback_reset_feedback(sp, feedback_props); + return; + } + + /* Collect information about galaxy that the particle belongs to */ + const float M_star = sp->galaxy_data.stellar_mass; + const float M_star_min = feedback_props->minimum_galaxy_stellar_mass; + const float FIRE_eta_norm = feedback_props->FIRE_eta_normalization; + const float FIRE_eta_break = feedback_props->FIRE_eta_break; + const float FIRE_eta_lower_slope = feedback_props->FIRE_eta_lower_slope; + const float FIRE_eta_upper_slope = feedback_props->FIRE_eta_upper_slope; + const float FIRE_eta_lower_slope_EOR = feedback_props->FIRE_eta_lower_slope_EOR; + const float wind_velocity_suppression_redshift = feedback_props->wind_velocity_suppression_redshift; + + float eta = feedback_mass_loading_factor(cosmo, M_star, + M_star_min, + FIRE_eta_norm, + FIRE_eta_break, + FIRE_eta_lower_slope, + FIRE_eta_upper_slope, + FIRE_eta_lower_slope_EOR, + wind_velocity_suppression_redshift); + + /* velocity in internal units which is a^2*comoving, or a*physical */ + float v_internal = + feedback_compute_kick_velocity(M_star, sp->id, cosmo, feedback_props, ti_begin); + + /* Early (non-SN) stellar feedback energy from Keller+22 eq. 10 */ + const float alpha = feedback_props->early_stellar_feedback_alpha; + const float alpha_power = 4.f * alpha - 1.f; + const float tfb_inv = feedback_props->early_stellar_feedback_tfb_inv; + if (alpha_power > 0.f && + star_age_beg_step < feedback_props->early_stellar_feedback_tfb) { + + const float eps_term = feedback_props->early_stellar_feedback_epsterm; + const float h_phys = kernel_gamma * sp->h * cosmo->a; + const float v_phys = v_internal * cosmo->a_inv; + + /* p0 is momentum per unit mass in km/s from early feedback sources */ + const float p0 = h_phys * eps_term * M_PI * tfb_inv; + const float t_prev = fmax(star_age_beg_step - dt, 0.f); + const float term1 = pow(star_age_beg_step * tfb_inv, alpha_power); + const float term2 = pow(t_prev * tfb_inv, alpha_power); + const double delta_p = alpha * p0 * sp->mass * (term1 - term2); + sp->feedback_data.physical_energy_reservoir += 0.5 * delta_p * v_phys; + +#ifdef KIARA_DEBUG_CHECKS + message("ESF: id=%lld age=%g dt=%g Myr, Etot=%g E_ESF=%g f_inc=%g", + sp->id, t_prev * feedback_props->time_to_Myr, + dt * feedback_props->time_to_Myr, + sp->feedback_data.physical_energy_reservoir, + 0.5f * delta_p * v_phys, + 0.5f * delta_p * v_phys / + sp->feedback_data.physical_energy_reservoir); +#endif + } + + /** + * Compute the mass loading and energy reservoirs for the stellar feedback. + * Mass loading will be limited by the physical energy available from chem5 + * directly at each step. Later, when computing the probability to kick + * a particle, the mass_to_launch will be limited by eta_suppression_factor. + */ + const float wind_mass = eta * sp->mass_init; + const float total_mass_kicked = sp->feedback_data.total_mass_kicked; + + if (total_mass_kicked < wind_mass) { + + /* Boost wind speed based on metallicity which governs + * photon energy output */ + float Z_fac = 1.f; + const int vwind_boost_flag = feedback_props->metal_dependent_vwind; + if (vwind_boost_flag != kiara_metal_boosting_off) { + Z_fac = 2.61634f; + const float Z_met = sp->chemistry_data.metal_mass_fraction_total; + if (Z_met > 1.e-9f) { + Z_fac = powf(10.f, + -0.0029f * powf(log10f(Z_met) + 9.f, 2.5f) + 0.417694f); + } + + Z_fac = max(Z_fac, 1.f); + } + + switch (vwind_boost_flag) { + case kiara_metal_boosting_vwind: + v_internal *= sqrtf(Z_fac); + break; + case kiara_metal_boosting_eta: + eta *= Z_fac; + break; + case kiara_metal_boosting_both: + v_internal *= sqrtf(Z_fac); + eta *= Z_fac; + break; + } + + /* ------ SNII Energy and Wind Launch Setup ------ */ + + /* Total SNII energy this timestep (physical units) */ + const double E_SNII_phys = 1.e51 * N_SNe / feedback_props->energy_to_cgs; + + /* Apply energy multiplier and metallicity scaling */ + const float energy_boost = + feedback_props->SNII_energy_multiplier * Z_fac; + + /* Add to physical energy reservoir */ + sp->feedback_data.physical_energy_reservoir += E_SNII_phys * energy_boost; + + /* Store updated wind velocity */ + sp->feedback_data.wind_velocity = v_internal; + + /* ------ Compute allowable wind mass this step ------ */ + + /* Wind energy per unit mass in physical units */ + const double specific_energy_phys = + 0.5 * v_internal * v_internal * cosmo->a2_inv; + + /* Max wind mass supportable by current energy */ + const double wind_mass_max = + sp->feedback_data.physical_energy_reservoir / specific_energy_phys; + + /* Remaining mass left to launch */ + const double wind_mass_left = max(wind_mass - total_mass_kicked, 0.); + + /* Launch only what is both allowed by energy and remaining */ + const double mass_to_launch = min(wind_mass_max, wind_mass_left); + + sp->feedback_data.mass_to_launch = mass_to_launch; + +#ifdef KIARA_DEBUG_CHECKS + message("ETA: z=%g id=%lld age=%g Eres=%g dE=%g NSNe=%g NSNtot=%g eta=%g " + "max=%g tot=%g mlaunch=%g Ntot=%d", + cosmo->z, + sp->id, + star_age_beg_step * feedback_props->time_to_Myr, + sp->feedback_data.physical_energy_reservoir * + feedback_props->energy_to_cgs, + 1.e51 * N_SNe * scaling, + N_SNe, + sp->mass_init * feedback_props->mass_to_solar_mass / 80.f, + /* 1 SNII for ~80 Mo for Kroupa/Chabrier IMF */ + mass_to_launch / sp->mass_init, + eta_max_this_timestep, + eta, + sp->feedback_data.mass_to_launch, + sp->feedback_data.N_launched); +#endif + + /* Set stream radius for firehose particles kicked by this star */ + + /* This is the physical initial density */ + const double stream_init_density = 0.1; /* n_H units CGS */ + const double rho_volumefilling_phys = + stream_init_density / feedback_props->rho_to_n_cgs; + float galaxy_stellar_mass_Msun = M_star; + const float min_gal_mass = feedback_props->minimum_galaxy_stellar_mass; + if (galaxy_stellar_mass_Msun < min_gal_mass) { + galaxy_stellar_mass_Msun = min_gal_mass; + } + galaxy_stellar_mass_Msun *= feedback_props->mass_to_solar_mass; + + /* stream size = 2 * comoving effective size of disk galaxies + * (Ward+2024 CEERS) */ + const float redge_obs = 2.f * + 7.1f * pow(cosmo->a, 0.63f) * + pow(galaxy_stellar_mass_Msun / 5.e10, 0.16f); + + /* Convert to internal units */ + sp->feedback_data.firehose_radius_stream = + cosmo->a_inv * redge_obs / feedback_props->length_to_kpc; + + if (sp->galaxy_data.stellar_mass > 0.f && sp->galaxy_data.specific_sfr > 0.f && + eta > 0.f && sp->feedback_data.wind_velocity != 0.f) { + + const float v_phys = + fabs(sp->feedback_data.wind_velocity) * cosmo->a_inv; + const float m_dot_wind_sfr = + eta * sp->galaxy_data.specific_sfr * sp->galaxy_data.stellar_mass; + const float specific_m_dot_wind_vel = + M_PI * rho_volumefilling_phys * v_phys; + + /* Put into comoving units */ + const float redge_est = + sqrtf(m_dot_wind_sfr / specific_m_dot_wind_vel) * cosmo->a_inv; + + sp->feedback_data.firehose_radius_stream = fmin(redge_est, redge_obs); + } + + /* Stream cannot be smaller than the smoothing length */ + sp->feedback_data.firehose_radius_stream = + fmax(sp->feedback_data.firehose_radius_stream, kernel_gamma * sp->h); + } + + /* D. Rennehan: Do some magic that I still don't understand + */ + double dum = 0.; + int flag_negative = 0; + /* Here we can loop over Swift metals because metal_mass_fraction + * would be zero for the unique Chem5 metals anyway, and would + * not activate the condition. + */ + for (elem = 0; elem < chemistry_element_count; elem++) { + dum = ejecta_unprocessed * sp->chemistry_data.metal_mass_fraction[elem]; + const int elem_conv = feedback_props->element_index_conversions[elem]; + ejecta_metal_mass[elem_conv] += dum; + if (ejecta_metal_mass[elem_conv] < 0.) { + ejecta_metal_mass[elem_conv] = 0.; + flag_negative = 1; + /* Do not break here, we need the zeroed elements where negative */ + } + } + + /* Check for any remaining that have negative mass after adding unprocessed */ + for (elem = 0; elem < chem5_element_count; elem++) { + if (ejecta_metal_mass[elem] < 0.) { + ejecta_metal_mass[elem] = 0.; + flag_negative = 1; + } + } + + /* If ANY element ended up negative we recompute everything */ + if (flag_negative) { + ejecta_mass = 0.; + ejecta_metal_mass[chem5_element_Z] = 0.; + for (elem = chem5_element_H; elem < chem5_element_Zn; elem++) { + ejecta_mass += ejecta_metal_mass[elem]; + } + + for (elem = chem5_element_C; elem < chem5_element_Zn; elem++) { + ejecta_metal_mass[chem5_element_Z] += ejecta_metal_mass[elem]; + } + } + + /* Now we loop over the Swift metals and set the proper values using the + conversion map */ + sp->feedback_data.total_metal_mass = ejecta_metal_mass[chem5_element_Z]; + for (elem = 0; elem < chemistry_element_count; elem++) { + sp->feedback_data.metal_mass[elem] = + ejecta_metal_mass[feedback_props->element_index_conversions[elem]]; + } + +#if COOLING_GRACKLE_MODE >= 2 + /* Put some of the ejecta metals into dust. Must be done after + chem5->chemistry conversion map is applied */ + if (sp->feedback_data.total_metal_mass > 0.) { + feedback_dust_production_condensation(sp, star_age_beg_step, feedback_props, + sp->feedback_data.metal_mass); + } +#endif + + /* Compute the total mass to distribute */ + sp->feedback_data.mass = ejecta_mass; + sp->feedback_data.energy = ejecta_energy; + + /* Decrease star mass by amount of mass distributed to gas neighbours */ + sp->mass -= ejecta_mass; + + /* Mark this is the last time we did enrichment */ + sp->last_enrichment_time = (with_cosmology) ? cosmo->a : time; + +#if COOLING_GRACKLE_MODE >= 2 + /* Update the number of SNe that have gone off, used in Grackle dust model. + Actually stores SNe rate */ + sp->feedback_data.SNe_ThisTimeStep = N_SNe / dt; + sp->feedback_data.SNe_Total += N_SNe; +#endif + +#ifdef SWIFT_STARS_DENSITY_CHECKS + sp->has_done_feedback = 1; +#endif + + TIMER_TOC(timer_do_star_evol); +} + +/** + * @brief Will this star particle want to do feedback during the next time-step? + * + * This is called in the time step task and increases counters of time-steps + * that have been performed. + * + * @param sp The particle to act upon + * @param feedback_props The #feedback_props structure. + * @param cosmo The current cosmological model. + * @param us The unit system. + * @param phys_const The #phys_const. + * @param time The physical time in internal units. + * @param with_cosmology Are we running with cosmology on? + * @param ti_current The current time (in integer) + * @param time_base The time base. + */ +__attribute__((always_inline)) INLINE static void feedback_will_do_feedback( + struct spart* sp, const struct feedback_props* feedback_props, + const int with_cosmology, const struct cosmology* cosmo, const double time, + const struct unit_system* us, const struct phys_const* phys_const, + const integertime_t ti_current, const double time_base) { + + /* Special case for new-born stars */ + if (with_cosmology) { + if (sp->birth_scale_factor == (float)cosmo->a) { + + /* Set the counter to "let's do enrichment" */ + sp->count_since_last_enrichment = 0; + + /* Ok, we are done. */ + return; + } + } else { + if (sp->birth_time == (float)time) { + + /* Set the counter to "let's do enrichment" */ + sp->count_since_last_enrichment = 0; + + /* Ok, we are done. */ + return; + } + } + + /* Calculate age of the star at current time */ + double age_of_star; + if (with_cosmology) { + age_of_star = cosmology_get_delta_time_from_scale_factors( + cosmo, (double)sp->birth_scale_factor, cosmo->a); + } else { + age_of_star = time - (double)sp->birth_time; + } + + /* Is the star still young? */ + if (age_of_star < feedback_props->stellar_evolution_age_cut) { + + /* Set the counter to "let's do enrichment" */ + sp->count_since_last_enrichment = 0; + + } else { + + /* Increment counter */ + sp->count_since_last_enrichment++; + + if ((sp->count_since_last_enrichment % + feedback_props->stellar_evolution_sampling_rate) == 0) { + + /* Reset counter */ + sp->count_since_last_enrichment = 0; + } + } +} + +void feedback_clean(struct feedback_props* fp); + +void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream); + +void feedback_struct_restore(struct feedback_props* feedback, FILE* stream); + +#ifdef HAVE_HDF5 +/** + * @brief Writes the current model of feedback to the file + * + * @param feedback The properties of the feedback scheme. + * @param h_grp The HDF5 group in which to write. + */ +INLINE static void feedback_write_flavour(struct feedback_props* feedback, + hid_t h_grp) { + + io_write_attribute_s(h_grp, "Feedback Model", "KIARA " + "(decoupled kinetic + chem5 enrichment)"); +} +#endif // HAVE_HDF5 + +#endif /* SWIFT_FEEDBACK_KIARA_H */ diff --git a/src/feedback/KIARA/feedback_debug.h b/src/feedback/KIARA/feedback_debug.h new file mode 100644 index 0000000000..2a4fb2b7d0 --- /dev/null +++ b/src/feedback/KIARA/feedback_debug.h @@ -0,0 +1,26 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_KIARA_DEBUG_H +#define SWIFT_FEEDBACK_KIARA_DEBUG_H + +__attribute__((always_inline)) INLINE static void feedback_debug_particle( + const struct part* p, const struct xpart* xp) {} + +#endif /* SWIFT_FEEDBACK_KIARA_DEBUG_H */ diff --git a/src/feedback/KIARA/feedback_iact.h b/src/feedback/KIARA/feedback_iact.h new file mode 100644 index 0000000000..b1e36eba8a --- /dev/null +++ b/src/feedback/KIARA/feedback_iact.h @@ -0,0 +1,894 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_KIARA_FEEDBACK_IACT_H +#define SWIFT_KIARA_FEEDBACK_IACT_H + +/* Local includes */ +#include "random.h" +#include "timestep_sync_part.h" +#include "tools.h" +#include "tracers.h" +#include + +#define KIARA_WIND_LOG + +/** + * @brief Set direction of stellar feedback kick + * + * @param si Star particle. + * @param pj Gas particle being kicked. + * @param ti_current Current integer time value (for random numbers). + * @param dir_flag Flag to choose direction: 0=rendom, 1=L_gas, 2=L_BH. + * @param dir Direction of kick (returned). + */ +__attribute__((always_inline)) INLINE static float +feedback_set_kick_direction( + const struct spart *si, const struct part *pj, + const integertime_t ti_current, + const int dir_flag, float *dir) { + + float kick_dir = 1.f; + double random_number = 1.; + + switch (dir_flag) { + /* Isotropic */ + case 0: + { + const double random_for_theta = + random_unit_interval(si->id, ti_current, random_number_isotropic_SNII_feedback_ray_theta); + const double random_for_phi = + random_unit_interval(si->id, ti_current, random_number_isotropic_SNII_feedback_ray_phi); + + const float theta = acosf(2.f * random_for_theta - 1.f); + const float phi = 2.f * M_PI * random_for_phi; + + dir[0] = sinf(theta) * cosf(phi); + dir[1] = sinf(theta) * sinf(phi); + dir[2] = cosf(theta); + break; + } + + /* Along the v x a direction */ + case 1: + { + dir[0] = + pj->gpart->a_grav[1] * pj->gpart->v_full[2] - + pj->gpart->a_grav[2] * pj->gpart->v_full[1]; + dir[1] = + pj->gpart->a_grav[2] * pj->gpart->v_full[0] - + pj->gpart->a_grav[0] * pj->gpart->v_full[2]; + dir[2] = + pj->gpart->a_grav[0] * pj->gpart->v_full[1] - + pj->gpart->a_grav[1] * pj->gpart->v_full[0]; + + random_number = + random_unit_interval(si->id, ti_current, random_number_stellar_feedback_1); + kick_dir = (random_number > 0.5) ? 1.f : -1.f; + break; + } + /* Outwards from star */ + case 2: + dir[0] = pj->x[0] - si->x[0]; + dir[1] = pj->x[1] - si->x[1]; + dir[2] = pj->x[2] - si->x[2]; + break; + + default: + error("dir_flag=%d but must be 0, 1, or 2", dir_flag); + break; + } + + return kick_dir; +} + + +/** + * @brief Compute customized kernel weight for feedback + * + * @param pj gas particle. + * @param wi SPH kernel weight at location of pj + */ +__attribute__((always_inline)) INLINE static float +feedback_kernel_weight(const struct part *pj, const float wi, const float ui, + const struct feedback_props *fb_props) { + + /* If it's beyond the kick radius, then the weighting is zero */ + if (ui >= fb_props->kick_radius_over_h) return 0.f; + + /* Weight towards higher SFR particles. As SFR->0, SFR_wi->wi and + * then radial weighting returns to normal. */ + float weight = wi; + if (fb_props->use_sfr_weighted_launch == 1) { + weight = (pj->sf_data.SFR > 0.f) ? wi + pj->sf_data.SFR : wi; + } + weight *= hydro_get_mass(pj); + + return weight; +} + +/** + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First sparticle. + * @param pj Second particle (not updated). + * @param xpj Extra particle data (not updated). + * @param cosmo The cosmological model. + * @param fb_props Properties of the feedback scheme. + * @param ti_current Current integer time value + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_feedback_density(const float r2, const float dx[3], + const float hi, const float hj, + struct spart *si, const struct part *pj, + const struct xpart *xpj, + const struct cosmology *cosmo, + const struct feedback_props *fb_props, + const integertime_t ti_current) { + + /* Do not count winds in the density */ + //if (pj->decoupled) return; + + const float rho = hydro_get_comoving_density(pj); + if (rho <= 0.f) return; + + /* Get the gas mass. */ + const float mj = hydro_get_mass(pj); + + /* Get r. */ + const float r = sqrtf(r2); + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + + float wi; + kernel_eval(ui, &wi); + + /* Add mass of pj to neighbour mass of si */ + si->feedback_data.ngb_mass += mj; + + /* sum(mj * wj) */ + si->feedback_data.kernel_wt_sum += mj * wi; + + /* If pj is being kicked in this step, don't kick again */ + if (pj->feedback_data.kick_id > -1) return; + + /* Sum up the weights for normalizing the kernel later */ + const float wt = feedback_kernel_weight(pj, wi, ui, fb_props); + si->feedback_data.wind_wt_sum += wt; + if (wt > 0.f) si->feedback_data.wind_ngb_mass += mj; + +} + +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_feedback_prep1(const float r2, const float dx[3], + const float hi, const float hj, + const struct spart *si, struct part *pj, + const struct xpart *xpj, + const struct cosmology *cosmo, + const struct feedback_props *fb_props, + const integertime_t ti_current) { + + /* No need to even check anything else if there is no mass or energy to launch */ + if (si->feedback_data.mass_to_launch <= 0.f) return; + + if (si->feedback_data.physical_energy_reservoir <= 0.f) return; + + /* No (eligible) mass surrounding the star, no kick */ + if (si->feedback_data.wind_wt_sum <= 0.f) return; + + /* If pj is being kicked by another star particle, don't kick again */ + if (pj->feedback_data.kick_id > -1) return; + + /* If pj is already a wind particle, don't kick again */ + // if (pj->decoupled) return; + + /* Get r. */ + const float r = sqrtf(r2); + + /* No kicks far away from the star */ + if (r >= fb_props->kick_radius_over_h * hi) return; + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + + float wi; + kernel_eval(ui, &wi); + + /* Bias towards the center of the kernel and to high SFR. Note: contains mj */ + const float wt = feedback_kernel_weight(pj, wi, ui, fb_props); + + /* No kick if weight is zero */ + if (wt <= 0.f) return; + + /* Total wind ngb mass in kernel */ + const float ngb_mass = si->feedback_data.wind_ngb_mass; + + /* Total mass to launch for this star particle */ + float mass_to_launch = si->feedback_data.mass_to_launch; + + /* Suppress based on the input parameter file */ + mass_to_launch *= si->feedback_data.eta_suppression_factor; + + /* Estimated number of particles to kick out of the kernel */ + const float mass_frac_to_launch = mass_to_launch / ngb_mass; + + if (mass_frac_to_launch > fb_props->max_frac_of_kernel_to_launch) { + mass_to_launch = fb_props->max_frac_of_kernel_to_launch * ngb_mass; + } + + /* Correct the weight term for the proper Bernoulli trial */ + const float wj = wt / hydro_get_mass(pj); + + /* Probability to swallow this particle */ + const float prob = + mass_to_launch * wj / si->feedback_data.wind_wt_sum; + + /* Draw a random number (Note mixing both IDs), up to max probability */ + const float rand = random_unit_interval(si->id + pj->id, ti_current, + random_number_stellar_feedback_1); + + /* We kick! */ + if (rand < prob) { + pj->feedback_data.kick_id = si->id; + } + +//#ifdef KIARA_DEBUG_CHECKS + message("KICK_PROB: z=%g sid=%lld, gid=%lld, prob=%g, rand=%g, eta=%g, mlaunch=%g, " + "m*=%g, wj=%g, wt_sum=%g, kicked? %d", + cosmo->z, + si->id, + pj->id, + prob, + rand, + si->feedback_data.mass_to_launch / si->mass_init, + si->feedback_data.mass_to_launch, + si->mass_init, + wj, + si->feedback_data.wind_wt_sum, + pj->feedback_data.kick_id == si->id); +//#endif + +} + +/** + * @brief Compile gas particles to be kicked by stellar feedback in this step, + * + * + * @param r2 Distance squared from star i to gas j. + * @param dx[3] x,y,z distance from star i to gas j. + * @param hi Smoothing length of star. + * @param hj Smoothing length of gas. + * @param si First (star) particle. + * @param pj Second (gas) particle (not updated). + * @param xpj Extra particle data. + * @param cosmo The cosmological model. + * @param ti_current Current integer time. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_feedback_prep2(const float r2, const float dx[3], + const float hi, const float hj, + struct spart *si, const struct part *pj, + const struct xpart *xpj, + const struct cosmology *cosmo, + const integertime_t ti_current) { + + /* Remove mass from the mass_to_launch reservoir */ + if (pj->feedback_data.kick_id == si->id) { + + si->feedback_data.mass_to_launch -= hydro_get_mass(pj); + si->feedback_data.total_mass_kicked += hydro_get_mass(pj); + + /* Work done on the particle */ + const float v2 = + si->feedback_data.wind_velocity * si->feedback_data.wind_velocity; + const double energy_phys = 0.5 * hydro_get_mass(pj) * v2 * cosmo->a2_inv; + + /* Remove energy used to kick particle from the SNII energy reservoir */ + si->feedback_data.physical_energy_reservoir -= energy_phys; + if (si->feedback_data.physical_energy_reservoir < 0.f) { + si->feedback_data.physical_energy_reservoir = 0.f; + } + + /* Keep track of how many particles launched */ + si->feedback_data.N_launched += 1; + + } + +} + +/** + * @brief Kick and sometimes heat gas particle near a star, + * if star has enough mass and energy for an ejection event. + * + * @param si First (star) particle (not updated). + * @param pj Second (gas) particle. + * @param xpj Extra particle data + * @param cosmo The cosmological model. + * @param fb_props Properties of the feedback scheme. + * @param ti_current Current integer time used value for seeding random number + * generator + */ +__attribute__((always_inline)) INLINE static void +feedback_kick_gas_around_star( + const struct spart *si, struct part *pj, struct xpart *xpj, + const struct cosmology *cosmo, + const struct feedback_props *fb_props, + const integertime_t ti_current) { + + if (pj->feedback_data.kick_id == si->id) { + + /* Need time-step for decoupling */ + const integertime_t ti_step = get_integer_timestep(pj->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current - 1, pj->time_bin); + + /* TODO: Requires always having with_cosmology! */ + const double dt = + cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + + /* Compute velocity and KE of wind event. + * Note that xpj->v_full = a^2 * dx/dt, with x the comoving + * coordinate. Therefore, a physical kick, dv, gets translated into a + * code velocity kick, a * dv. + */ + const float wind_velocity = si->feedback_data.wind_velocity; + const float wind_velocity_phys = + fabs(wind_velocity * cosmo->a_inv); + float dir[3] = {0.f, 0.f, 0.f}; + const int dir_flag = fb_props->kick_direction_flag; + const float dirsign = + feedback_set_kick_direction(si, pj, ti_current, dir_flag, dir); + + float norm = sqrtf(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]); + + /* Zero normalization (should basically never happen); randomize direction */ + if (norm <= 0.f) { + warning("z=%g sid=%lld pid=%lld normalization of wind direction is zero!\n(x, y, z) " + "= (%g, %g, %g); vw=%g. Randomizing direction.", cosmo->z, si->id, pj->id, + dir[0], dir[1], dir[2], fabs(wind_velocity * cosmo->a_inv)); + dir[0] = random_unit_interval(pj->id, ti_current, + random_number_stellar_feedback_1); + dir[1] = random_unit_interval(pj->id, ti_current, + random_number_stellar_feedback_2); + dir[2] = random_unit_interval(pj->id, ti_current, + random_number_stellar_feedback_3); + norm = sqrtf(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]); + } + + const float prefactor = dirsign * wind_velocity / norm; + + /* Do the kicks by updating the particle velocity. */ + xpj->v_full[0] += dir[0] * prefactor; + xpj->v_full[1] += dir[1] * prefactor; + xpj->v_full[2] += dir[2] * prefactor; + + /* DO WIND HEATING */ + double u_new = fb_props->cold_wind_internal_energy; + if (!fb_props->use_firehose_model) { + float galaxy_stellar_mass = si->galaxy_data.stellar_mass; + + if (galaxy_stellar_mass < fb_props->minimum_galaxy_stellar_mass) { + galaxy_stellar_mass = fb_props->minimum_galaxy_stellar_mass; + } + const float galaxy_stellar_mass_Msun = galaxy_stellar_mass * + fb_props->mass_to_solar_mass; + + /* Based on Pandya et al 2022 FIRE results */ + float pandya_slope = 0.f; + if (galaxy_stellar_mass_Msun > 3.16e10) { + pandya_slope = -2.1f; + } + else { + pandya_slope = -0.1f; + } + + /* 0.2511886 = pow(10., -0.6) */ + const float f_warm = + 0.2511886f * pow(galaxy_stellar_mass_Msun / 3.16e10f, pandya_slope); + /* additional 10% removed for cold phase */ + const float hot_wind_fraction = max(0.f, 0.9f - f_warm); + const float rand_for_hot = + random_unit_interval(pj->id, ti_current, + random_number_stellar_feedback_2); + const float rand_for_spread = + random_unit_interval(pj->id, ti_current, + random_number_stellar_feedback_3); + + /* If selected, heat the particle */ + const double u_wind = 0.5 * wind_velocity_phys * wind_velocity_phys; + if (rand_for_hot < hot_wind_fraction && + fb_props->hot_wind_internal_energy > u_wind) { + u_new = (fb_props->hot_wind_internal_energy - u_wind) * + (0.5 + rand_for_spread); + u_new += hydro_get_physical_internal_energy(pj, xpj, cosmo); + } + } + + /* Set the wind particle internal energy */ + hydro_set_physical_internal_energy(pj, xpj, cosmo, u_new); + hydro_set_drifted_physical_internal_energy(pj, cosmo, NULL, u_new); + +#ifdef FIREHOSE_DEBUG_CHECKS + /* For firehose model, set initial radius of stream */ + if (si->feedback_data.firehose_radius_stream <= 0.f) { + error("Firehose error: firehose_radius_stream <= 0. sid=%lld " + "pid=%lld Rstream=%g", + si->id, + pj->id, + si->feedback_data.firehose_radius_stream); + } +#endif + + pj->chemistry_data.radius_stream = si->feedback_data.firehose_radius_stream; + pj->chemistry_data.exchanged_mass = 0.f; + + /* FINISH UP FEEDBACK */ + /* Turn off any star formation in wind particle. + * Record exp factor of when this particle was last ejected as -SFR. */ + pj->sf_data.SFR = -cosmo->a; + + /* Update the signal velocity of the particle based on the velocity kick, + wind_velocity must be PHYSICAL passed into this function */ + hydro_set_v_sig_based_on_velocity_kick(pj, cosmo, wind_velocity_phys); + + /* Impose maximal viscosity */ + hydro_diffusive_feedback_reset(pj); + + /* Synchronize the particle on the timeline */ + timestep_sync_part(pj); + + if (fb_props->wind_decouple_time_factor > 0.f) { + /* Decouple the particles from the hydrodynamics */ + pj->feedback_data.decoupling_delay_time = + dt + fb_props->wind_decouple_time_factor * + cosmology_get_time_since_big_bang(cosmo, cosmo->a); + pj->decoupled = 1; + } + else { + pj->feedback_data.decoupling_delay_time = 0.f; + pj->decoupled = 0; + } + + /* TODO: Move to chemistry module */ + pj->chemistry_data.diffusion_coefficient = 0.f; + + /* Take particle out of subgrid ISM mode */ + pj->cooling_data.subgrid_temp = 0.f; + pj->cooling_data.subgrid_dens = hydro_get_physical_density(pj, cosmo); + pj->cooling_data.subgrid_fcold = 0.f; + + pj->feedback_data.number_of_times_decoupled += 1; + + /* Kicked and handled */ + pj->feedback_data.kick_id = -1; + +#ifdef KIARA_WIND_LOG + /** Log the wind event. + * z starid gasid dt M* vkick vkx vky vkz h x y z vx vy vz T rho v_sig tdec + * Ndec Z + */ + const float length_convert = cosmo->a * fb_props->length_to_kpc; + const float velocity_convert = cosmo->a_inv / fb_props->kms_to_internal; + const float rho_convert = cosmo->a3_inv * fb_props->rho_to_n_cgs; + const float u_convert = + cosmo->a_factor_internal_energy / fb_props->temp_to_u_factor; +/* Collect information about galaxy that the particle belongs to */ + const float galaxy_mstar = si->galaxy_data.stellar_mass; + const float galaxy_ssfr = si->galaxy_data.specific_sfr; + + printf("WIND_LOG z=%.5f sid=%lld mlaunch=%g mkicked=%g Nkicked=%g zbirth=%g M*=%g sSFR=%g pid=%lld vw=%g vwx=%g vwy=%g vwz=%g h=%g x=%g " + "y=%g z=%g vx=%g vy=%g vz=%g " + "T=%g nH=%g tdel=%g Ndec=%d fZ=%g\n", + cosmo->z, + si->id, + si->feedback_data.mass_to_launch * + fb_props->mass_to_solar_mass, + si->feedback_data.total_mass_kicked * + fb_props->mass_to_solar_mass, + si->feedback_data.total_mass_kicked / + si->mass, + 1.f/si->birth_scale_factor - 1.f, + galaxy_mstar * fb_props->mass_to_solar_mass, + galaxy_ssfr / fb_props->time_to_yr, + pj->id, + fabs(wind_velocity) * velocity_convert, + prefactor * dir[0] * velocity_convert, + prefactor * dir[1] * velocity_convert, + prefactor * dir[2] * velocity_convert, + pj->h * length_convert, + pj->x[0] * length_convert, + pj->x[1] * length_convert, + pj->x[2] * length_convert, + xpj->v_full[0] * velocity_convert, + xpj->v_full[1] * velocity_convert, + xpj->v_full[2] * velocity_convert, + hydro_get_comoving_internal_energy(pj, xpj) * u_convert, + pj->rho * rho_convert, + pj->feedback_data.decoupling_delay_time * fb_props->time_to_Myr, + pj->feedback_data.number_of_times_decoupled, + pj->chemistry_data.metal_mass_fraction_total); +#endif + } +} + +/** + * @brief Feedback interaction between two particles (non-symmetric). + * Used for updating properties of gas particles neighbouring a star particle + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (si - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First (star) particle (not updated). + * @param pj Second (gas) particle. + * @param xpj Extra particle data + * @param cosmo The cosmological model. + * @param fb_props Properties of the feedback scheme. + * @param ti_current Current integer time used value for seeding random number + * generator + */ +__attribute__((always_inline)) INLINE static void +feedback_do_chemical_enrichment_of_gas_around_star( + const float r2, const float dx[3], const float hi, const float hj, + const struct spart *si, struct part *pj, struct xpart *xpj, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct feedback_props *fb_props, + const integertime_t ti_current) { + + /* Nothing to distribute */ + if (si->feedback_data.mass <= 0.f || + si->feedback_data.kernel_wt_sum <= 0.f) return; + + /* Gas particle density */ + const float rho_j = hydro_get_comoving_density(pj); + if (rho_j <= 0.f) return; + + /* Get r. */ + const float r = sqrtf(r2); + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + float wi; + kernel_eval(ui, &wi); + + const double current_mass = hydro_get_mass(pj); + /* Compute weighting for distributing feedback quantities. + * f = (mi * wi) / sum(mj * wj) */ + float Omega_frac = current_mass * wi / si->feedback_data.kernel_wt_sum; + + /* Never apply feedback if Omega_frac is bigger than or equal to unity */ + if (Omega_frac < 0.f || (Omega_frac > 1.f && ui < 1.f)) { + warning( + "Invalid fraction of material to distribute for star ID=%lld " + "Omega_frac=%e count since last enrich=%d kernel_wt_sum=%g " + "wi=%g rho_j=%g", + si->id, Omega_frac, si->count_since_last_enrichment, + si->feedback_data.kernel_wt_sum, wi , rho_j); + if (Omega_frac < 0.f || (Omega_frac > 1.01f && ui < 1.f)) { + error("Omega_frac negative or too large! aborting"); + } + + Omega_frac = fmin(Omega_frac, 1.f); + } + + /* ------ Handle mass from SN explosions ------ */ + + /* Update particle mass */ + double delta_mass = si->feedback_data.mass * Omega_frac; + double new_mass = current_mass + delta_mass; + const double max_new_mass = + current_mass * fb_props->max_mass_increase_factor; + + if (new_mass > max_new_mass) { + /* Count for logging in the snapshot. */ + pj->feedback_data.mass_limiter_count++; + + /* Limit the mass growth and any other quantity below */ + delta_mass = max_new_mass - current_mass; + Omega_frac = delta_mass / si->feedback_data.mass; + + new_mass = current_mass + delta_mass; + +#ifdef KIARA_DEBUG_CHECKS + warning("New mass %g exceeds maximum %g for particle id=%lld --- limiting!", + new_mass, max_new_mass, + pj->id); +#endif + } + + hydro_set_mass(pj, new_mass); + + /* Inverse of the new mass */ + const double new_mass_inv = 1. / new_mass; + + /* ------ Energy from SN explosions ------ */ + + if (si->feedback_data.energy > 0.f) { + /* Update particle energy */ + double injected_energy = si->feedback_data.energy * Omega_frac; + + /* Compute the current thermal energy */ + const double current_thermal_energy = + current_mass * hydro_get_physical_internal_energy(pj, xpj, cosmo); + + /* To check overheating */ + const double current_u_phys = current_thermal_energy / current_mass; + + /* Check if we are gonna blow up */ + double new_u_phys = current_u_phys + injected_energy * new_mass_inv; + + const double max_new_u_phys = + fb_props->max_energy_increase_factor * current_u_phys; + + /* PHYSICAL comparison */ + if (new_u_phys > max_new_u_phys) { + + /* Count for logging in the snapshot. */ + pj->feedback_data.heating_limiter_count++; + + injected_energy = max_new_u_phys * new_mass - current_u_phys * current_mass; + + /* Make sure the injected energy doesn't decrease */ + if (injected_energy < 0.) injected_energy = 0.; + +#ifdef KIARA_DEBUG_CHECKS + warning("Injected energy %g exceeds maximum %g for particle id=%lld" + " --- limiting!", + new_u_phys, max_new_u_phys, pj->id); +#endif + } + + const double new_thermal_energy = + current_thermal_energy + injected_energy; + + /* Update after momentum conservation and limiting */ + new_u_phys = new_thermal_energy * new_mass_inv; + + /* Do we want to move things off of the ISM if there is sufficient heating? */ + if (fb_props->SNIa_add_heat_to_ISM) { + + if (pj->cooling_data.subgrid_temp > 0.f && + pj->cooling_data.subgrid_fcold > 0.f) { + + /* 0.8125 is mu for a fully neutral gas with XH=0.75; + * approximate but good enough */ + const double u_cold_phys = + 0.8125 * pj->cooling_data.subgrid_temp * fb_props->temp_to_u_factor; + + const double delta_u_ISM_phys = current_u_phys - u_cold_phys; + double f_evap = 0.; + + const double du_phys = new_u_phys - current_u_phys; + + /* Use extra heat to move off of the ISM */ + if (du_phys > 0. && delta_u_ISM_phys >= 0.) { + const double u_phys_tol = + fb_props->SNIa_add_heat_to_ISM_tolerance * current_u_phys; + + if (delta_u_ISM_phys > u_phys_tol) { + f_evap = du_phys / delta_u_ISM_phys; + f_evap = min(f_evap, 1.0); + } + else { + f_evap = 1.0; + } + + /* Clip values in case of overflow */ + if (f_evap > 0.) { + pj->cooling_data.subgrid_fcold *= 1. - f_evap; + + const double u_remaining_phys = + du_phys - f_evap * delta_u_ISM_phys; + new_u_phys = current_u_phys + max(u_remaining_phys, 0.); + + /* Limit internal energy increase here as well */ + if (new_u_phys > max_new_u_phys) { + new_u_phys = max_new_u_phys; + pj->feedback_data.heating_limiter_count++; + } + + if (pj->cooling_data.subgrid_fcold <= 0.f) { + pj->cooling_data.subgrid_temp = 0.f; + pj->cooling_data.subgrid_dens = + hydro_get_physical_density(pj, cosmo); + pj->cooling_data.subgrid_fcold = 0.f; + } + } + } + } + } + + hydro_set_physical_internal_energy(pj, xpj, cosmo, new_u_phys); + hydro_set_drifted_physical_internal_energy(pj, cosmo, /*pfloor=*/NULL, + new_u_phys); + } /* si->feedback_data.energy > 0.f */ + + /* ------ Handle metal injection from SN explosions ------ */ + + /* Recompute Z since we do not track all of the metals from Chem5 */ + pj->chemistry_data.metal_mass_fraction_total = 0.f; + + /* Update mass fraction of each tracked element */ + for (int elem = 0; elem < chemistry_element_count; elem++) { + const double current_metal_mass = + pj->chemistry_data.metal_mass_fraction[elem] * current_mass; + const double delta_metal_mass = + si->feedback_data.metal_mass[elem] * Omega_frac; + const double new_metal_mass = current_metal_mass + delta_metal_mass; + + pj->chemistry_data.metal_mass_fraction[elem] = + new_metal_mass * new_mass_inv; + + if (elem != chemistry_element_H && elem != chemistry_element_He) { + pj->chemistry_data.metal_mass_fraction_total += + pj->chemistry_data.metal_mass_fraction[elem]; + } + } + + /* Make sure that X + Y + Z = 1 */ + const float Y_He = + pj->chemistry_data.metal_mass_fraction[chemistry_element_He]; + const float Z = pj->chemistry_data.metal_mass_fraction_total; + const float X_H = 1.f - Y_He - Z; + + if (X_H < 0.f || X_H > 1.f) { + for (int elem = 0; elem < chemistry_element_count; elem++) { + warning("\telem[%d] is %g", + elem, pj->chemistry_data.metal_mass_fraction[elem]); + } + + error("Hydrogen fraction exeeds unity or is negative for" + " particle id=%lld due to stellar feedback.", pj->id); + } + + pj->chemistry_data.metal_mass_fraction[chemistry_element_H] = X_H; + + /* Compute kernel-smoothed contribution to number of SNe going off + * this timestep */ + pj->feedback_data.SNe_ThisTimeStep += + si->feedback_data.SNe_ThisTimeStep * Omega_frac; + pj->feedback_data.SNe_ThisTimeStep = + fmax(pj->feedback_data.SNe_ThisTimeStep, 0.); + + /* Spread dust ejecta to gas */ + for (int elem = chemistry_element_He; + elem < chemistry_element_count; elem++) { + const double current_dust_mass = + pj->cooling_data.dust_mass_fraction[elem] * pj->cooling_data.dust_mass; + const double delta_dust_mass = + si->feedback_data.delta_dust_mass[elem] * Omega_frac; + + /* at the moment this stores the mass (not mass frac) in each elem */ + pj->cooling_data.dust_mass_fraction[elem] = + (current_dust_mass + delta_dust_mass); + } + + /* Sum up each element to get total dust mass */ + pj->cooling_data.dust_mass = 0.; + for (int elem = chemistry_element_He; + elem < chemistry_element_count; elem++) { + pj->cooling_data.dust_mass += pj->cooling_data.dust_mass_fraction[elem]; + } + + if (pj->cooling_data.dust_mass > 0.) { + const double dust_mass_inv = 1. / pj->cooling_data.dust_mass; + + /* Divide by new dust mass to get the fractions */ + for (int elem = chemistry_element_He; + elem < chemistry_element_count; elem++) { + pj->cooling_data.dust_mass_fraction[elem] *= dust_mass_inv; + } + + /* Check for inconsistency */ + if (pj->cooling_data.dust_mass > pj->mass) { + for (int elem = chemistry_element_He; + elem < chemistry_element_count; elem++) { + message("DUST EXCEEDS MASS elem=%d md=%g delta=%g \n", + elem, + pj->cooling_data.dust_mass_fraction[elem] * + pj->cooling_data.dust_mass, + si->feedback_data.delta_dust_mass[elem] * Omega_frac); + } + + error("DUST EXCEEDS MASS mgas=%g mdust=%g\n", + pj->mass, + pj->cooling_data.dust_mass); + } + } + else { + /* For some reason the dust mass is zero or negative */ + for (int elem = 0; elem < chemistry_element_count; elem++) { + pj->cooling_data.dust_mass_fraction[elem] = 0.f; + } + } + +} + +/** + * @brief Feedback interaction between two particles (non-symmetric). + * Used for updating properties of gas particles neighbouring a star particle + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (si - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First (star) particle (not updated). + * @param pj Second (gas) particle. + * @param xpj Extra particle data + * @param cosmo The cosmological model. + * @param fb_props Properties of the feedback scheme. + * @param ti_current Current integer time used value for seeding random number + * generator + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_feedback_apply( + const float r2, const float dx[3], const float hi, const float hj, + const struct spart *si, struct part *pj, struct xpart *xpj, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct feedback_props *fb_props, const integertime_t ti_current) { + + /* Ignore decoupled particles */ + //if (pj->decoupled) return; + + /* Do chemical enrichment of gas, metals and dust from star */ + feedback_do_chemical_enrichment_of_gas_around_star( + r2, dx, hi, hj, si, pj, xpj, cosmo, hydro_props, + fb_props, ti_current); + + /* Do kinetic wind feedback */ + feedback_kick_gas_around_star(si, pj, xpj, cosmo, fb_props, ti_current); + +#if COOLING_GRACKLE_MODE >= 2 + /* NOT USED: Compute G0 contribution from star to the gas particle in Habing units of + * 1.6e-3 erg/s/cm^2. Note that this value includes the 4*pi geometric factor + if (0) { + const float length_to_physical_cm = cosmo->a * fb_props->length_to_kpc * 3.08567758e21f; + // Compute a softened distance from star to gas particle + const double r2_in_cm = (r2 + 0.01*hi*hi) * length_to_physical_cm * length_to_physical_cm; + const double r_in_cm = sqrt(r2_in_cm); + + // Compute self-shielding from H2, from Schauer et al. 2015 eq 8,9 + // H attenuation factor + const double NH_cgs = hydro_get_physical_density(pj, cosmo) * fb_props->rho_to_n_cgs * r_in_cm; + const double xH = NH_cgs / 2.85e23; + const double fH_shield = pow(1.f+xH,-1.62) * exp(-0.149*xH); + // H2 attenuation factor + const double NH2_cgs = pj->sf_data.H2_fraction * NH_cgs; + const double DH2_cgs = 1.e-5 * sqrt(2.*1.38e-16*cooling_get_subgrid_temperature(pj, xpj) / 3.346e-24); + const double xH2 = NH2_cgs / 8.465e13; + const double fH2_shield = 0.9379/pow(1.f+xH2/DH2_cgs,1.879) + 0.03465/pow(1.f+xH2,0.473) * exp(-2.293e-4*sqrt(1+xH2)); + //message("G0 shield: r=%g xH2=%g xH=%g fH2=%g fH=%g\n",r_in_cm/3.086e21,xH2,xH,fH2_shield,fH_shield); + + if (si->feedback_data.lum_habing > -10.) { + pj->chemistry_data.G0 += fH2_shield * fH_shield * pow(10.,si->feedback_data.lum_habing) / (1.6e-3 * r2_in_cm); + } + }*/ +#endif + +} + +#endif /* SWIFT_KIARA_FEEDBACK_IACT_H */ diff --git a/src/feedback/KIARA/feedback_properties.h b/src/feedback/KIARA/feedback_properties.h new file mode 100644 index 0000000000..8f48c997c9 --- /dev/null +++ b/src/feedback/KIARA/feedback_properties.h @@ -0,0 +1,392 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_KIARA_FEEDBACK_PROPERTIES_H +#define SWIFT_KIARA_FEEDBACK_PROPERTIES_H + +/* Config parameters. */ +#include "../config.h" + +/* Local includes. */ +#include "chemistry.h" +#include "hydro_properties.h" + +#define NM 5000 +#define NZSN 7 +#define NMLF 41 +#define NZLF 9 +#define NZSN1R 5 /* secondary mass ranges */ +#define NZSN1Y 7 /* yields */ +#define NMSN 32 +#define NXSNall 84 + +#define SN1E_idx(A, B) ((A) * NZSN1Y + B) +#define LFLT_idx(A, B) ((A) * NMLF + B) +#define SN1R_idx(A, B) ((A) * NM + B) +#define SN2R_idx(A, B) ((A) * NM + B) +#define SWR_idx(A, B) ((A) * NM + B) +#define SN2E_idx(A, B, C) ((A) * NZSN * NM + (B) * NM + C) + +#define LINEAR_INTERPOLATION(x1, y1, x2, y2, x) (((y2 - y1)/(x2 - x1))*(x - x1) + y1) +#define LOG_INTERPOLATION(x, x2, x1) ((log10(x2) - log10(x))/(log10(x2) - log10(x1))) + +enum kiara_metal_boosting { + kiara_metal_boosting_off, + kiara_metal_boosting_vwind, + kiara_metal_boosting_eta, + kiara_metal_boosting_both +}; + +/* Chem5 tracks A LOT of elements but we will just map the standard 11 back */ +enum chem5_element { + chem5_element_Z = 0, + chem5_element_H, + chem5_element_He, + chem5_element_Li, + chem5_element_Be, + chem5_element_B, + chem5_element_C, + chem5_element_N, + chem5_element_O, + chem5_element_F, + chem5_element_Ne, + chem5_element_Na, + chem5_element_Mg, + chem5_element_Al, + chem5_element_Si, + chem5_element_P, + chem5_element_S, + chem5_element_Cl, + chem5_element_Ar, + chem5_element_K, + chem5_element_Ca, + chem5_element_Sc, + chem5_element_Ti, + chem5_element_V, + chem5_element_Cr, + chem5_element_Mn, + chem5_element_Fe, + chem5_element_Co, + chem5_element_Ni, + chem5_element_Cu, + chem5_element_Zn, + chem5_element_Ga, + chem5_element_Ge, + chem5_element_unknown, + chem5_element_count, + chem5_dummy1, + chem5_dummy2, + chem5_NXSN +}; + + +/** + * @brief Stores the yield tables + */ +struct feedback_tables { + double *LFLT; + double *LFLM; + double *LFLZ; + double *LFLT2; + double *SWR; + double *SN2E; + double *SN2R; + double *SN1R; + double *SNLM; + double *SNLZ; + double *SNLZ1R; + double *SN1E; + double *SNLZ1Y; +}; + +/** + * @brief Properties of the KIARA feedback model. + */ +struct feedback_props { + + /* ------------ Main operation modes ------------- */ + + /*! Are we depositing energy from HN directly from Chem5? */ + int with_HN_energy_from_chem5; + + /*! Are we depositing energy from SNII directly from Chem5? */ + int with_SNII_energy_from_chem5; + + /*! Are we depositing energy from SNIa directly from Chem5? */ + int with_SNIa_energy_from_chem5; + + /*! If time since last chemical enrichment is above this value times the current stellar age, recompute */ + float stellar_enrichment_frequency; + + /* ------------ Yield tables ----------------- */ + + struct feedback_tables tables; + + /* Conversion indices from Chem5 to Swift */ + /* 0-Z, 2-He, 6-C , 7-N, 8-O, 10-Ne, 12-Mg, 14-Si, 26-Fe */ + int element_index_conversions[chemistry_element_count]; + + /* Location of feedback tables */ + char tables_path[200]; + + /* ------------- Conversion factors --------------- */ + + /*! Conversion factor from internal mass unit to solar mass */ + double mass_to_solar_mass; + + /*! The mass of the sun in g */ + double solar_mass_in_g; + + /*! Conversion factor from internal mass unit to solar mass */ + double solar_mass_to_mass; + + /*! Conversion factor from density in internal units to Hydrogen number + * density in cgs */ + double rho_to_n_cgs; + + /*! Conversion factor from temperature to internal energy */ + double temp_to_u_factor; + + /*! Conversion factor from km/s to cm/s */ + double kms_to_cms; + + /*! Factor to convert km/s to internal units */ + double kms_to_internal; + + /*! Convert internal units to kpc */ + double length_to_kpc; + + /*! Convert internal time to Myr */ + double time_to_Myr; + + /*! Convert internal time to yr */ + double time_to_yr; + + /*! Convert code energy units to cgs */ + double energy_to_cgs; + + /*! Convert temperature in K to internal */ + double T_to_internal; + + /* ------------ Enrichment sampling properties ------------ */ + + /*! Star age above which the enrichment will be downsampled (in internal + * units) */ + double stellar_evolution_age_cut; + + /*! Number of time-steps in-between two enrichment events */ + int stellar_evolution_sampling_rate; + + /* ------------ Kinetic feedback properties --------------- */ + + /*! Velocity normalization */ + float FIRE_velocity_normalization; + + /*! FIRE velocity slope */ + float FIRE_velocity_slope; + + /*! Normalization for the mass loading curve */ + float FIRE_eta_normalization; + + /*! The location (in internal mass units) where the break in the + * mass loading curve occurs */ + float FIRE_eta_break; + + /*! The power-law slope of eta below FIRE_eta_break */ + float FIRE_eta_lower_slope; + + /*! The power-law slope of eta above FIRE_eta_break */ + float FIRE_eta_upper_slope; + + /*! The power-law slope of eta below FIRE_eta_break at z>6 */ + float FIRE_eta_lower_slope_EOR; + + /*! The wind speed of stellar feedback suppressed above this z */ + float wind_velocity_suppression_redshift; + + /*! The mass loading factor of stellar feedback suppressed above this z */ + float wind_eta_suppression_redshift; + + /*! Maxiumum multiple of SNII energy that is available to launch winds */ + float SNII_energy_multiplier; + + /*! For KIARA, the radius from within which to launch wind */ + float kick_radius_over_h; + + /*! For KIARA, the maximum fraction of gas mass within kernel to launch */ + float max_frac_of_kernel_to_launch; + + /*! For KIARA, weight the launch probability by the particles's SFR (0/1) */ + int use_sfr_weighted_launch; + + /*! Flag to set feedback boost at low Z: + * 0=Off, 1=vwind boost, 2=eta boost, 3=both boost */ + int metal_dependent_vwind; + + /*! The minimum galaxy stellar mass in internal units */ + float minimum_galaxy_stellar_mass; + + /*! The number of star particles when a galaxy is considered resolved */ + int galaxy_particle_resolution_count; + + /*! Floor for the eta suppression factor */ + float eta_suppression_factor_floor; + + /*! Direction to launch wind: 0=random, 1=v x a, 2=outwards */ + float kick_direction_flag; + + /*! Added scatter to the wind velocities */ + float kick_velocity_scatter; + + /*! max decoupling time is (this factor) * current Hubble time */ + float wind_decouple_time_factor; + + /*! Density (cgs) above which recoupling considers it within ISM */ + float recouple_ism_density_nH_cgs; + + /*! Factor (<1) below ISM density below which to recouple */ + float recouple_density_factor; + + /*! The internal energy corresponding to the unheated wind temperature */ + float cold_wind_internal_energy; + + /*! The internal energy corresponding to the heated wind temperature */ + float hot_wind_internal_energy; + + /*! Whether the firehose wind model is on */ + char use_firehose_model; + + /*! Early stellar feedback alpha value from Keller et al 2022 */ + float early_stellar_feedback_alpha; + + /*! Early stellar feedback term for SF efficiency Keller et al 2022 */ + float early_stellar_feedback_epsterm; + + /*! Early stellar feedback t_fb from Keller et al 2022 */ + float early_stellar_feedback_tfb; + + /*! Early stellar feedback t_fb inverse */ + float early_stellar_feedback_tfb_inv; + + /*! Maximum mass growth factor from stellar feedback for a particle */ + float max_mass_increase_factor; + + /*! Maximum internal energy growth factor from stellar feedback to particle */ + float max_energy_increase_factor; + + /*! Minimum internal energy loss factor from momentum exchange */ + //float min_energy_decrease_factor; + + /*! Flag to add heat to destroy cold gas in the ISM from SNIa gas */ + int SNIa_add_heat_to_ISM; + + /*! Avoid floating point errors when comparing internal energies in the ISM */ + float SNIa_add_heat_to_ISM_tolerance; + + /* ------------ Chem5 Default Parameters --------------- */ + + /*! Which IMF? Kroupa=0, Chabrier=1, Else=2 */ + int imf; + + /*! Solar H */ + double H_mf; + + /*! Solar He */ + double He_mf; + + /*! Solar Z */ + double Z_mf; + + /*! Solar O */ + double O_mf; + + /*! Solar Fe */ + double Fe_mf; + + /*! IMF parameter */ + double ximf; + + /*! Upper limit for IMF integration */ + double M_u; + + /*! Lower limit for IMF integration */ + double M_l; + + /*! IMF parameter */ + double ximf3; + + /*! >= M_u */ + double M_u3; + + /*! >= M_l */ + double M_l3; + + /*! If set greater than zero, activates Pop3 stars */ + double zmax3; + + /*! Upper limit on IMF integration */ + double M_u2; + + /*! Lower limit on IMF integration */ + double M_l2; + + /*! binary parameter for SNIa */ + double b_rg; + + /*! binary parameter for SNIa */ + double b_ms; + + /*! Energy in supernova (Ia) */ + double E_sn1; + + /*! Energy in supernova (II) */ + double E_sw; + +#if COOLING_GRACKLE_MODE >= 2 + /* ------------ Dust Efficiency Tables --------------- */ + + /*! dust condensation efficiency for C/O>1 */ + double delta_AGBCOG1[chemistry_element_count]; + + /*! dust condensation efficiency for C/O<1 */ + double delta_AGBCOL1[chemistry_element_count]; + + /*! dust condensation efficiency from SNII */ + double delta_SNII[chemistry_element_count]; + + /*! max fraction of metals locked into dust */ + float max_dust_fraction; + + /*! Rolling value for number of SNe is smoothed over this timescale + * in Myr (0 for instantaneous) */ + float SNe_smoothing_time_in_Myr; +#endif + + /*! chem5 metal yield multiplier */ + float metal_yield_multiplier; +}; + +void feedback_props_init(struct feedback_props *fp, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct hydro_props *hydro_props, + const struct cosmology *cosmo); + +#endif /* SWIFT_KIARA_FEEDBACK_PROPERTIES_H */ diff --git a/src/feedback/KIARA/feedback_struct.h b/src/feedback/KIARA/feedback_struct.h new file mode 100644 index 0000000000..85a05a121f --- /dev/null +++ b/src/feedback/KIARA/feedback_struct.h @@ -0,0 +1,127 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * 2022 Doug Rennehan (douglas.rennehan@gmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_STRUCT_KIARA_H +#define SWIFT_FEEDBACK_STRUCT_KIARA_H + +#include "chemistry_struct.h" + + +/** + * @brief Feedback fields carried by each hydro particles + */ +struct feedback_part_data { + /*! remaining time left for decoupling */ + float decoupling_delay_time; + + /*! Number of times decoupled */ + int number_of_times_decoupled; + + /*! The time to shut off cooling for this particle */ + float cooling_shutoff_delay_time; + + /*! The ID of the star particle that is kicking this particle */ + long long kick_id; + + /*! The direction vector for wind kicks */ + float wind_direction[3]; + + /*! The number of times the SF mass limiter was applied */ + int mass_limiter_count; + + /*! The number of times the SF heat limiter was applied */ + int heating_limiter_count; + +#if COOLING_GRACKLE_MODE >= 2 + /*! Number of SNe (of any type) going off in nearby stars */ + float SNe_ThisTimeStep; +#endif +}; + +/** + * @brief Extra feedback fields carried by each hydro particles + */ +struct feedback_xpart_data {}; + +/** + * @brief Feedback fields carried by each star particles + */ +struct feedback_spart_data { + + /*! Normalisation factor used for the enrichment */ + float kernel_wt_sum; + + /*! Normalisation factor used for the kicking */ + float wind_wt_sum; + + /*! Total mass (unweighted) of neighbouring gas particles */ + float ngb_mass; + + /*! Total mass (unweighted) of neighbouring gas particles eligible for wind */ + float wind_ngb_mass; + + /*! Mass released */ + double mass; + + /*! Total metal mass released */ + double total_metal_mass; + + /*! Total mass released by each element */ + double metal_mass[chemistry_element_count]; + + /*! Energy change due to thermal and kinetic energy of ejecta */ + double energy; + + /*! Cumulative SNII energy available to launch wind */ + double physical_energy_reservoir; + + /*! Number of particles launched over the stars' lifetime */ + int N_launched; + + /*! Total mass left to be ejected in winds by this star */ + float mass_to_launch; + + /*! Total mass kicked over the stars' lifetime */ + float total_mass_kicked; + + /*! Kick velocity for gas launched by this star COMOVING */ + float wind_velocity; + + /*! The factor to multiply the wind_mass to prevent galaxy destruction */ + float eta_suppression_factor; + +#if COOLING_GRACKLE_MODE >= 2 + /*! Luminosity emitted by star in Habing band (912-1112 A) */ + float lum_habing; + + /*! Number of SNe (of any type) going off within star during this step */ + double SNe_ThisTimeStep; + + /*! Cumulative number of SNe that have gone off in this star from chem5 (for debugging) */ + double SNe_Total; + + /*! Total dust mass change for each element */ + double delta_dust_mass[chemistry_element_count]; +#endif + + /*! Initial stream radius for firehose model */ + float firehose_radius_stream; +}; + +#endif /* SWIFT_FEEDBACK_STRUCT_KIARA_H */ diff --git a/src/feedback/KIARA/libswiftsim_la-feedback-335341eb.o.tmp b/src/feedback/KIARA/libswiftsim_la-feedback-335341eb.o.tmp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/feedback/KIARA/libswiftsim_la-feedback-f98e8954.o.tmp b/src/feedback/KIARA/libswiftsim_la-feedback-f98e8954.o.tmp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/feedback/KIARA/mpi-feedback-8dfc2d63.o.tmp b/src/feedback/KIARA/mpi-feedback-8dfc2d63.o.tmp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/feedback_debug.h b/src/feedback_debug.h index ba76463099..baa8ab3d85 100644 --- a/src/feedback_debug.h +++ b/src/feedback_debug.h @@ -33,6 +33,8 @@ #include "./feedback/GEAR/feedback_debug.h" #elif defined(FEEDBACK_AGORA) #include "./feedback/AGORA/feedback_debug.h" +#elif defined(FEEDBACK_KIARA) +#include "./feedback/KIARA/feedback_debug.h" #else #error "Invalid choice of feedback model" #endif diff --git a/src/feedback_iact.h b/src/feedback_iact.h index 91ec58619f..8ea0fe5c32 100644 --- a/src/feedback_iact.h +++ b/src/feedback_iact.h @@ -33,6 +33,8 @@ #include "./feedback/GEAR/feedback_iact.h" #elif defined(FEEDBACK_AGORA) #include "./feedback/AGORA/feedback_iact.h" +#elif defined(FEEDBACK_KIARA) +#include "./feedback/KIARA/feedback_iact.h" #else #error "Invalid choice of feedback model" #endif diff --git a/src/feedback_new_stars.h b/src/feedback_new_stars.h index da012c646a..fe559d0a6a 100644 --- a/src/feedback_new_stars.h +++ b/src/feedback_new_stars.h @@ -34,6 +34,8 @@ #define feedback_use_newborn_stars 1 #elif defined(FEEDBACK_AGORA) #define feedback_use_newborn_stars 1 +#elif defined(FEEDBACK_KIARA) +#define feedback_use_newborn_stars 0 #else #error "Invalid choice of feedback model" #endif diff --git a/src/feedback_properties.h b/src/feedback_properties.h index 93504ff6aa..cb346f9a38 100644 --- a/src/feedback_properties.h +++ b/src/feedback_properties.h @@ -33,6 +33,8 @@ #include "./feedback/GEAR/feedback_properties.h" #elif defined(FEEDBACK_AGORA) #include "./feedback/AGORA/feedback_properties.h" +#elif defined(FEEDBACK_KIARA) +#include "./feedback/KIARA/feedback_properties.h" #else #error "Invalid choice of feedback model" #endif diff --git a/src/feedback_struct.h b/src/feedback_struct.h index 063a657095..a776773911 100644 --- a/src/feedback_struct.h +++ b/src/feedback_struct.h @@ -38,6 +38,8 @@ #include "./feedback/GEAR/feedback_struct.h" #elif defined(FEEDBACK_AGORA) #include "./feedback/AGORA/feedback_struct.h" +#elif defined(FEEDBACK_KIARA) +#include "./feedback/KIARA/feedback_struct.h" #else #error "Invalid choice of feedback function." #endif diff --git a/src/fof.c b/src/fof.c index 0882140808..0cbd724322 100644 --- a/src/fof.c +++ b/src/fof.c @@ -2734,6 +2734,31 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, /* Calculate the radius*/ float r = sqrtf((x[0] * x[0]) + (x[1] * x[1]) + (x[2] * x[2])); radii[index] = fmax(radii[index], r); + +#ifdef WITH_FOF_GALAXIES + /* Get a handle on the gpart. */ + const struct gpart *restrict gp = &gparts[i]; + + /* Load FoF data into particles of various types */ + if (gp->type == swift_type_gas) { + struct part *restrict p = &(s->parts[-gp->id_or_neg_offset]); + p->galaxy_data.stellar_mass = stellar_mass[index]; + p->galaxy_data.gas_mass = gas_mass[index]; + p->galaxy_data.specific_sfr = star_formation_rate[index] / stellar_mass[index]; + } + else if (gp->type == swift_type_stars) { + struct spart *restrict sp = &(s->sparts[-gp->id_or_neg_offset]); + sp->galaxy_data.stellar_mass = stellar_mass[index]; + sp->galaxy_data.gas_mass = gas_mass[index]; + sp->galaxy_data.specific_sfr = star_formation_rate[index] / stellar_mass[index]; + } + else if (gp->type == swift_type_black_hole) { + struct bpart *restrict bp = &(s->bparts[-gp->id_or_neg_offset]); + bp->galaxy_data.stellar_mass = stellar_mass[index]; + bp->galaxy_data.gas_mass = gas_mass[index]; + bp->galaxy_data.specific_sfr = star_formation_rate[index] / stellar_mass[index]; + } +#endif } #ifdef WITH_MPI diff --git a/src/fof_struct.h b/src/fof_struct.h index ea788b5bf6..a1271d7c18 100644 --- a/src/fof_struct.h +++ b/src/fof_struct.h @@ -36,6 +36,24 @@ struct fof_gpart_data { size_t group_size; }; +#ifdef WITH_FOF_GALAXIES +/** + * @brief Particle-carried fields for the FoF galaxies scheme (e.g. KIARA). + */ +struct fof_galaxy_data { + + /*! Host galaxy stellar mass */ + float stellar_mass; + + /*! Host galaxy gas mass */ + float gas_mass; + + /*! Host galaxy specific star formation rate = SFR/M* */ + float specific_sfr; +}; + +#endif + #else /** diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index f93d8b35ce..88417c18f1 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -2294,6 +2294,13 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( #endif p->decoupled = 0; +#ifdef WITH_FOF_GALAXIES + /* Initialise FoF galaxy data */ + p->galaxy_data.stellar_mass = 0.f; + p->galaxy_data.gas_mass = 0.f; + p->galaxy_data.specific_sfr = 0.f; +#endif + hydro_reset_acceleration(p); hydro_init_part(p, NULL); } diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 0a44e75a42..1e33dbcbd8 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -144,6 +144,11 @@ struct part { /*! Conduction du/dt */ float u_dt_cond; +#ifdef WITH_FOF_GALAXIES + /*! Additional data used to record host galaxy information */ + struct fof_galaxy_data galaxy_data; +#endif + #ifdef MAGMA2_DEBUG_CHECKS struct { /*! Correction matrix at the last time it was ill-conditioned */ diff --git a/src/part.h b/src/part.h index 73be132823..f6385cb8c0 100644 --- a/src/part.h +++ b/src/part.h @@ -136,6 +136,8 @@ struct threadpool; #include "./black_holes/EAGLE/black_holes_part.h" #elif defined(BLACK_HOLES_SPIN_JET) #include "./black_holes/SPIN_JET/black_holes_part.h" +#elif defined(BLACK_HOLES_OBSIDIAN) +#include "./black_holes/Obsidian/black_holes_part.h" #else #error "Invalid choice of black hole particle" #endif diff --git a/src/runner_doiact_functions_stars.h b/src/runner_doiact_functions_stars.h index abdb0bfdca..5c3465e5fa 100644 --- a/src/runner_doiact_functions_stars.h +++ b/src/runner_doiact_functions_stars.h @@ -154,7 +154,7 @@ void DOSELF1_STARS(struct runner *r, const struct cell *c, e->feedback_props, ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP1) runner_iact_nonsym_feedback_prep1(r2, dx, hi, hj, si, pj, NULL, cosmo, - ti_current); + e->feedback_props, ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP2) runner_iact_nonsym_feedback_prep2(r2, dx, hi, hj, si, pj, NULL, cosmo, ti_current); @@ -319,7 +319,7 @@ void DO_NONSYM_PAIR1_STARS_NAIVE(struct runner *r, e->feedback_props, ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP1) runner_iact_nonsym_feedback_prep1(r2, dx, hi, hj, si, pj, NULL, cosmo, - ti_current); + e->feedback_props, ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP2) runner_iact_nonsym_feedback_prep2(r2, dx, hi, hj, si, pj, NULL, cosmo, ti_current); @@ -546,7 +546,7 @@ void DO_SYM_PAIR1_STARS(struct runner *r, const struct cell *restrict ci, ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP1) runner_iact_nonsym_feedback_prep1(r2, dx, hi, hj, spi, pj, NULL, - cosmo, ti_current); + cosmo, e->feedback_props, ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP2) runner_iact_nonsym_feedback_prep2(r2, dx, hi, hj, spi, pj, NULL, cosmo, ti_current); @@ -710,7 +710,7 @@ void DO_SYM_PAIR1_STARS(struct runner *r, const struct cell *restrict ci, ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP1) runner_iact_nonsym_feedback_prep1(r2, dx, hj, hi, spj, pi, NULL, - cosmo, ti_current); + cosmo, e->feedback_props, ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP2) runner_iact_nonsym_feedback_prep2(r2, dx, hj, hi, spj, pi, NULL, cosmo, ti_current); diff --git a/src/runner_others.c b/src/runner_others.c index 74bf78930e..c822ecc4af 100644 --- a/src/runner_others.c +++ b/src/runner_others.c @@ -132,7 +132,6 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { const struct hydro_props *hydro_props = e->hydro_properties; const struct entropy_floor_properties *entropy_floor_props = e->entropy_floor; const struct pressure_floor_props *pressure_floor = e->pressure_floor_props; - const struct fof_props *fof_props = e->fof_properties; const double time_base = e->time_base; const integertime_t ti_current = e->ti_current; struct part *restrict parts = c->hydro.parts; @@ -181,7 +180,7 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { /* Let's cool ! */ cooling_cool_part(constants, us, cosmo, hydro_props, - entropy_floor_props, pressure_floor, cooling_func, fof_props, p, + entropy_floor_props, pressure_floor, cooling_func, p, xp, dt_cool, dt_therm, time); } } @@ -332,7 +331,6 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { const int with_cosmology = (e->policy & engine_policy_cosmology); const int with_feedback = (e->policy & engine_policy_feedback); const struct hydro_props *restrict hydro_props = e->hydro_properties; - const struct fof_props *restrict fof_props = e->fof_properties; const struct unit_system *restrict us = e->internal_units; struct cooling_function_data *restrict cooling = e->cooling_func; const struct entropy_floor_properties *entropy_floor = e->entropy_floor; @@ -415,7 +413,7 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { /* Compute the SF rate of the particle */ star_formation_compute_SFR(p, xp, sf_props, phys_const, hydro_props, - fof_props, cosmo, dt_star); + cosmo, dt_star); /* Add the SFR and SFR*dt to the SFH struct of this cell */ star_formation_logger_log_active_part(p, xp, &c->stars.sfh, dt_star); diff --git a/src/runner_time_integration.c b/src/runner_time_integration.c index f3c2718ffb..11ab9550e0 100644 --- a/src/runner_time_integration.c +++ b/src/runner_time_integration.c @@ -1397,8 +1397,8 @@ void runner_do_limiter(struct runner *r, struct cell *c, int force, if (!part_is_active(p, e) && p->limiter_data.to_be_synchronized) { warning( "Not limiting particle with id %lld because it needs to be " - "synced.", - p->id); + "synced tdel=%g dec=%d.", + p->id, p->feedback_data.decoupling_delay_time, p->decoupled); continue; } diff --git a/src/space_first_init.c b/src/space_first_init.c index 3232e445f0..ad5831c92e 100644 --- a/src/space_first_init.c +++ b/src/space_first_init.c @@ -130,6 +130,9 @@ void space_first_init_parts_mapper(void *restrict map_data, int count, cooling_first_init_part(phys_const, us, hydro_props, cosmo, cool_func, &p[k], &xp[k]); + /* And the feedback */ + feedback_first_init_part(&p[k], &xp[k]); + /* And the tracers */ tracers_first_init_xpart(&p[k], &xp[k], us, phys_const, cosmo, hydro_props, cool_func); diff --git a/src/star_formation/KIARA/star_formation.h b/src/star_formation/KIARA/star_formation.h index 1942eb473a..4bc62f4f39 100644 --- a/src/star_formation/KIARA/star_formation.h +++ b/src/star_formation/KIARA/star_formation.h @@ -335,7 +335,7 @@ INLINE static void star_formation_compute_SFR_schmidt_law( INLINE static void star_formation_compute_SFR_wn07( struct part* p, struct xpart* xp, const struct star_formation* starform, const struct phys_const* phys_const, - const struct hydro_props* hydro_props, const struct fof_props* fof_props, + const struct hydro_props* hydro_props, const struct cosmology* cosmo, const double dt_star) { p->sf_data.SFR = 0.f; @@ -344,10 +344,9 @@ INLINE static void star_formation_compute_SFR_wn07( const double rho_0 = starform->lognormal.rho0; /* Collect information about galaxy that the particle belongs to */ - size_t group_id = p->gpart->fof_data.group_id; - const float galaxy_mstar = fof_props->group_stellar_mass[group_id]; - const float galaxy_sfr = fof_props->group_star_formation_rate[group_id]; - const float galaxy_ssfr = galaxy_sfr / galaxy_mstar; + const float galaxy_mstar = p->galaxy_data.stellar_mass; + const float galaxy_ssfr = p->galaxy_data.specific_sfr; + const float galaxy_sfr = galaxy_mstar * galaxy_ssfr; /* Density is too low, so no SF */ if (rho_V <= 1.001 * rho_0) return; @@ -525,7 +524,7 @@ INLINE static void star_formation_compute_SFR_lognormal( INLINE static void star_formation_compute_SFR( struct part* p, struct xpart* xp, const struct star_formation* starform, const struct phys_const* phys_const, - const struct hydro_props* hydro_props, const struct fof_props* fof_props, + const struct hydro_props* hydro_props, const struct cosmology* cosmo, const double dt_star) { /* Abort early if time-step size is 0 */ @@ -615,8 +614,7 @@ INLINE static void star_formation_compute_SFR( break; case kiara_star_formation_WadaNorman: star_formation_compute_SFR_wn07(p, xp, starform, phys_const, - hydro_props, fof_props, - cosmo, dt_star); + hydro_props, cosmo, dt_star); break; case kiara_star_formation_lognormal: star_formation_compute_SFR_lognormal(p, xp, starform, phys_const, @@ -795,6 +793,11 @@ INLINE static void star_formation_copy_properties( sp->feedback_data.eta_suppression_factor = 1.f;*/ sp->last_enrichment_time = sp->birth_time; sp->count_since_last_enrichment = 0; + + /* Copy FoF galaxy data from spawning particle */ + sp->galaxy_data.stellar_mass = p->galaxy_data.stellar_mass; + sp->galaxy_data.gas_mass = p->galaxy_data.gas_mass; + sp->galaxy_data.specific_sfr = p->galaxy_data.specific_sfr; } /** diff --git a/src/stars/KIARA/stars_io.h b/src/stars/KIARA/stars_io.h index 85288682d5..b8d68ab769 100644 --- a/src/stars/KIARA/stars_io.h +++ b/src/stars/KIARA/stars_io.h @@ -144,7 +144,7 @@ INLINE static void stars_write_particles(const struct spart *sparts, struct io_props *list, int *num_fields, const int with_cosmology) { /* Say how much we want to write */ - *num_fields = 12; + *num_fields = 11; /* List what we want to write */ list[0] = io_make_output_field_convert_spart( @@ -196,12 +196,7 @@ INLINE static void stars_write_particles(const struct spart *sparts, "Temperatures at the time of birth of the gas " "particles that turned into stars"); -/* list[9] = io_make_output_field( - "TotalMassEjected", FLOAT, 1, UNIT_CONV_MASS, 0.f, sparts, - feedback_data.total_mass_kicked, - "Total gas mass kicked by the star over its life-time. ");*/ - - list[10] = io_make_output_field_convert_spart( + list[9] = io_make_output_field_convert_spart( "Luminosities", FLOAT, luminosity_bands_count, UNIT_CONV_NO_UNITS, 0.f, sparts, convert_spart_luminosities, "Rest-frame dust-free AB-luminosities of the star particles in the GAMA " @@ -213,7 +208,7 @@ INLINE static void stars_write_particles(const struct spart *sparts, "absolute AB-magnitudes (rest-frame absolute maggies) directly by " "applying -2.5 log10(L) without additional corrections."); - list[11] = io_make_output_field_convert_spart( + list[10] = io_make_output_field_convert_spart( "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, sparts, convert_spart_potential, "Gravitational potentials of the particles"); } diff --git a/src/stars/KIARA/stars_part.h b/src/stars/KIARA/stars_part.h index 1d1f8dbd1b..76ba601927 100644 --- a/src/stars/KIARA/stars_part.h +++ b/src/stars/KIARA/stars_part.h @@ -100,6 +100,11 @@ struct spart { /*! The birth temperature */ float birth_temperature; +#ifdef WITH_FOF_GALAXIES + /*! Struct for host galaxy information */ + struct fof_galaxy_data galaxy_data; +#endif + /*! Star formation struct */ struct star_formation_spart_data sf_data; From d03df6f9ea5df634052f336f73e4f8ead969c791 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Tue, 25 Nov 2025 12:16:40 +0000 Subject: [PATCH 08/37] Add recoupling; refactor grackle for compatibility with RT --- src/chemistry/KIARA/chemistry.h | 4 +- src/chemistry/KIARA/chemistry_io.h | 14 ++++ src/cooling/KIARA/cooling.c | 36 ++++++--- src/cooling/KIARA/cooling.h | 19 ++++- src/feedback/KIARA/feedback.h | 113 +++-------------------------- src/hydro/MAGMA2/hydro.h | 2 +- src/runner_time_integration.c | 5 ++ 7 files changed, 72 insertions(+), 121 deletions(-) diff --git a/src/chemistry/KIARA/chemistry.h b/src/chemistry/KIARA/chemistry.h index f3085e43ad..cc2c30cbdd 100644 --- a/src/chemistry/KIARA/chemistry.h +++ b/src/chemistry/KIARA/chemistry.h @@ -1269,9 +1269,9 @@ __attribute__((always_inline)) INLINE static float chemistry_timestep( } } - /* Decoupled winds need the hydro time-step for firehose model. */ if (cd->use_firehose_wind_model) { - if (p->decoupled) { + /* About-to-recouple winds need the hydro time-step. */ + if (p->decoupled == 2) { const float CFL_condition = hydro_props->CFL_condition; const float h = kernel_gamma * cosmo->a * p->h; const float v_sig = 2.f * hydro_get_physical_soundspeed(p, cosmo); diff --git a/src/chemistry/KIARA/chemistry_io.h b/src/chemistry/KIARA/chemistry_io.h index d749e5573a..44a216d158 100644 --- a/src/chemistry/KIARA/chemistry_io.h +++ b/src/chemistry/KIARA/chemistry_io.h @@ -83,6 +83,20 @@ INLINE static int chemistry_write_particles(const struct part* parts, "The full diffusion coefficient"); num++; + list[num] = io_make_output_field( + "DecouplingDelayTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, + feedback_data.decoupling_delay_time, + "Maximum time left as a firehose wind particle"); + num++; + + list[num] = io_make_output_field( + "NumberOfTimesDecoupled", INT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + feedback_data.number_of_times_decoupled, + "Number of times decoupled. Units of 1 are from SF feedback," + "units of 1000 are from non-jet AGN feedback," + "units of 100000 are from jet AGN feedback"); + num++; + #ifdef KIARA_DEBUG_CHECKS list[num] = io_make_output_field( "ElementDiffusionRates", FLOAT, chemistry_element_count, diff --git a/src/cooling/KIARA/cooling.c b/src/cooling/KIARA/cooling.c index f2dd9af078..fc902bf298 100644 --- a/src/cooling/KIARA/cooling.c +++ b/src/cooling/KIARA/cooling.c @@ -792,6 +792,7 @@ void cooling_copy_to_grackle(grackle_field_data* data, const struct part* p, const struct xpart* xp, const double dt, const double T_warm, gr_float species_densities[N_SPECIES], + gr_float* iact_rates, int mode) { int i; @@ -877,11 +878,12 @@ void cooling_copy_to_grackle(grackle_field_data* data, cooling_copy_to_grackle3(data, p, xp, species_densities[12], species_densities); - data->RT_heating_rate = NULL; - data->RT_HI_ionization_rate = NULL; - data->RT_HeI_ionization_rate = NULL; - data->RT_HeII_ionization_rate = NULL; - data->RT_H2_dissociation_rate = NULL; + /* RT heating and ionisation rates */ + data->RT_heating_rate = &iact_rates[0]; + data->RT_HI_ionization_rate = &iact_rates[1]; + data->RT_HeI_ionization_rate = &iact_rates[2]; + data->RT_HeII_ionization_rate = &iact_rates[3]; + data->RT_H2_dissociation_rate = &iact_rates[4]; species_densities[19] = chemistry_get_total_metal_mass_fraction_for_cooling(p) * @@ -955,7 +957,8 @@ gr_float cooling_grackle_driver( const struct cosmology* restrict cosmo, const struct hydro_props* hydro_props, const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, double dt, + struct part* restrict p, struct xpart* restrict xp, + gr_float* iact_rates, double dt, double T_warm, int mode) { /* set current units for conversion to physical quantities */ @@ -969,7 +972,7 @@ gr_float cooling_grackle_driver( /* load particle information from particle to grackle data */ cooling_copy_to_grackle(&data, us, cosmo, cooling, p, xp, dt, T_warm, - species_densities, mode); + species_densities, iact_rates, mode); /* Run Grackle in desired mode */ gr_float return_value = 0.f; @@ -1065,8 +1068,10 @@ gr_float cooling_time(const struct phys_const* restrict phys_const, if (rhocool > 0.f) p_temp.rho = rhocool; if (ucool > 0.f) p_temp.u = ucool; + gr_float iact_rates[5] = {0., 0., 0., 0., 0.}; + gr_float cooling_time = cooling_grackle_driver( - phys_const, us, cosmo, hydro_properties, cooling, &p_temp, xp, 0., 0., 1); + phys_const, us, cosmo, hydro_properties, cooling, &p_temp, xp, iact_rates, 0., 0., 1); return cooling_time; } @@ -1094,9 +1099,11 @@ float cooling_get_temperature( struct part p_temp = *p; struct xpart xp_temp = *xp; + gr_float iact_rates[5] = {0., 0., 0., 0., 0.}; + const float temperature = cooling_grackle_driver(phys_const, us, cosmo, hydro_properties, - cooling, &p_temp, &xp_temp, 0., 0., 2); + cooling, &p_temp, &xp_temp, iact_rates, 0., 0., 2); return temperature; } @@ -1350,12 +1357,13 @@ __attribute__((always_inline)) INLINE void cooling_init_chemistry( * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param xp Pointer to the particle' extended data. + * @param iact_rates Interaction rates for radiative transfer (if used) * @param dt The time-step of this particle. * @param dt_therm The time-step operator used for thermal quantities. * @param time The current time (since the Big Bang or start of the run) in * internal units. */ -__attribute__((always_inline)) INLINE void cooling_do_grackle_cooling( +void cooling_do_grackle_cooling( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, const struct cosmology* restrict cosmo, @@ -1363,6 +1371,7 @@ __attribute__((always_inline)) INLINE void cooling_do_grackle_cooling( const struct entropy_floor_properties* floor_props, const struct cooling_function_data* restrict cooling, struct part* restrict p, struct xpart* restrict xp, + gr_float* iact_rates, const double dt, const double dt_therm, const double time) { @@ -1387,7 +1396,7 @@ __attribute__((always_inline)) INLINE void cooling_do_grackle_cooling( //const float T_old = cooling_get_temperature( phys_const, hydro_props, us, cosmo, cooling, p, xp); // for debugging only gr_float u_new = u_old; u_new = cooling_grackle_driver(phys_const, us, cosmo, hydro_props, cooling, - p, xp, dt, T_warm, 0); + p, xp, iact_rates, dt, T_warm, 0); /* Apply simulation-wide minimum temperature */ u_new = max(u_new, hydro_props->minimal_internal_energy); @@ -1525,10 +1534,13 @@ void cooling_cool_part(const struct phys_const* restrict phys_const, /* No cooling happens over zero time */ if (dt == 0.f || dt_therm == 0.f) return; + /* Interaction rates for RT; not used here */ + gr_float iact_rates[5] = {0., 0., 0., 0., 0.}; + /* Do the cooling and chemistry */ cooling_do_grackle_cooling(phys_const, us, cosmo, hydro_props, floor_props, cooling, - p, xp, dt, dt_therm, time); + p, xp, iact_rates, dt, dt_therm, time); } /** diff --git a/src/cooling/KIARA/cooling.h b/src/cooling/KIARA/cooling.h index 4b37e2746e..c882ad8810 100644 --- a/src/cooling/KIARA/cooling.h +++ b/src/cooling/KIARA/cooling.h @@ -114,10 +114,11 @@ void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, void cooling_copy_to_grackle(grackle_field_data* data, const struct unit_system* restrict us, const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, + const struct cooling_function_data* restrict cooling, const struct part* p, const struct xpart* xp, const double dt, const double T_floor, gr_float species_densities[N_SPECIES], + gr_float* iact_rates, int mode); void cooling_copy_from_grackle(grackle_field_data* data, struct part* p, struct xpart* xp, @@ -130,10 +131,20 @@ gr_float cooling_grackle_driver(const struct phys_const* restrict phys_const, const struct hydro_props* hydro_properties, const struct cooling_function_data* restrict cooling, struct part* restrict p, - struct xpart* restrict xp, double dt, - double T_floor, + struct xpart* restrict xp, gr_float* iact_rates, + double dt, double T_floor, int mode); - +void cooling_do_grackle_cooling( + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, + gr_float* iact_rates, + const double dt, const double dt_therm, + const double time); gr_float cooling_time(const struct phys_const* restrict phys_const, const struct unit_system* restrict us, const struct hydro_props* hydro_properties, diff --git a/src/feedback/KIARA/feedback.h b/src/feedback/KIARA/feedback.h index d8ee509b1d..7c5d417b08 100644 --- a/src/feedback/KIARA/feedback.h +++ b/src/feedback/KIARA/feedback.h @@ -62,55 +62,6 @@ double feedback_get_turnover_mass(const struct feedback_props* fb_props, void feedback_prepare_interpolation_tables( const struct feedback_props* fb_props); -/** - * @brief Determine the probability of a gas particle being kicked - * due to stellar feedback in star forming gas. - * - * @param p The #part to consider. - * @param xp The #xpart to consider. - * @param e The #engine. - * @param fb_props The feedback properties. - * @param ti_current The current timestep. - * @param dt_part The time step of the particle. - * @param rand_for_sf_wind The random number for the wind generation. - * @param wind_mass The amount of mass in the wind (code units). - */ -__attribute__((always_inline)) INLINE static double feedback_wind_probability( - struct part* p, struct xpart* xp, const struct engine* e, - const struct cosmology* cosmo, - const struct feedback_props* fb_props, - const integertime_t ti_current, - const double dt_part, - double *rand_for_sf_wind, - double *wind_mass) { - - return 0.f; -} - - -/** - * @brief Kick a gas particle selected for stellar feedback. - * - * @param p The #part to consider. - * @param xp The #xpart to consider. - * @param e The #engine. - * @param fb_props The feedback properties. - * @param ti_current The current timestep. - * @param with_cosmology Is cosmological integration on? - * @param dt_part The time step of the particle. - * @param wind_mass The amount of mass in the wind (code units). - */ -__attribute__((always_inline)) INLINE static -void feedback_kick_and_decouple_part( - struct part* p, struct xpart* xp, - const struct engine* e, - const struct cosmology* cosmo, - const struct feedback_props* fb_props, - const integertime_t ti_current, - const int with_cosmology, - const double dt_part, - const double wind_mass) {}; - /** * @brief Recouple wind particles. @@ -134,7 +85,6 @@ void feedback_recouple_set_flags(struct part* p, p->cooling_data.subgrid_fcold = 0.f; /* Make sure to sync the newly coupled part on the timeline */ - warning("Recoupling part %lld", p->id); timestep_sync_part(p); } @@ -172,6 +122,11 @@ __attribute__((always_inline)) INLINE static void feedback_recouple_part( /* Decrement the counter */ p->feedback_data.decoupling_delay_time -= dt_part; + /* Estimate if it will decouple in the next step, if so set flag=2 */ + if (p->feedback_data.decoupling_delay_time <= dt_part) { + p->decoupled = 2; + } + /** * Recouple under 3 conditions: * (1) Below the density threshold. @@ -188,7 +143,12 @@ __attribute__((always_inline)) INLINE static void feedback_recouple_part( p->chemistry_data.radius_stream < 0.f || rho_nH_cgs < rho_recouple_cgs); - if (recouple) { + if (recouple && p->decoupled == 1) { + /* If it is recoupling, do one more decoupled step to set timestep etc */ + p->decoupled = 2; + } + else if (recouple) { + /* extra decoupled step is done, now properly recouple */ feedback_recouple_set_flags(p, cosmo); } else { @@ -227,57 +187,6 @@ __attribute__((always_inline)) INLINE static void feedback_set_wind_direction( p->gpart->a_grav[1] * p->gpart->v_full[0]; } -/** - * @brief Determine if particles that ignore cooling should start cooling again. - * - * @param p The #part to consider. - * @param xp The #xpart to consider. - * @param e The #engine. - * @param cosmo The cosmological information of the simulation. - * @param with_cosmology Is this a cosmological simulation? - */ -__attribute__((always_inline)) INLINE static void feedback_ready_to_cool( - struct part* p, struct xpart* xp, const struct engine* e, - const struct cosmology* restrict cosmo, const int with_cosmology) { - - /* No reason to do this if the decoupling time is zero */ - if (p->feedback_data.cooling_shutoff_delay_time > 0.f) { - - /* Reset subgrid properties */ - p->cooling_data.subgrid_temp = 0.f; - p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); - p->cooling_data.subgrid_fcold = 0.f; - - const integertime_t ti_step = get_integer_timestep(p->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(e->ti_current - 1, p->time_bin); - - /* Get particle time-step */ - double dt_part; - if (with_cosmology) { - dt_part = - cosmology_get_delta_time(e->cosmology, ti_begin, ti_begin + ti_step); - } else { - dt_part = get_timestep(p->time_bin, e->time_base); - } - - p->feedback_data.cooling_shutoff_delay_time -= dt_part; - - if (p->feedback_data.cooling_shutoff_delay_time < 0.f) { - p->feedback_data.cooling_shutoff_delay_time = 0.f; - - /* Make sure to sync the newly coupled part on the timeline */ - warning("Recoupling part that is now cooling %lld", p->id); - timestep_sync_part(p); - } - } - else { - /* Because we are using floats, always make sure to set exactly zero */ - p->feedback_data.cooling_shutoff_delay_time = 0.f; - p->decoupled = 0; - } - -} /** * @brief Update the properties of a particle due to feedback effects after diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 88417c18f1..6b77ea5b7a 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -447,7 +447,7 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const struct hydro_props *restrict hydro_properties, const struct cosmology *restrict cosmo) { - if (p->dt_min == 0.f) return FLT_MAX; + if (p->dt_min == 0.f || p->decoupled == 1) return FLT_MAX; /* Use full kernel support and physical time */ const float conv = kernel_gamma * cosmo->a / cosmo->a_factor_sound_speed; diff --git a/src/runner_time_integration.c b/src/runner_time_integration.c index 11ab9550e0..58f165d726 100644 --- a/src/runner_time_integration.c +++ b/src/runner_time_integration.c @@ -90,6 +90,7 @@ void runner_do_kick1(struct runner *r, struct cell *c, const int timer) { const struct cosmology *cosmo = e->cosmology; const struct hydro_props *hydro_props = e->hydro_properties; const struct entropy_floor_properties *entropy_floor = e->entropy_floor; + const struct feedback_props *feedback_props = e->feedback_props; const int periodic = e->s->periodic; const int with_cosmology = (e->policy & engine_policy_cosmology); struct part *restrict parts = c->hydro.parts; @@ -166,6 +167,10 @@ void runner_do_kick1(struct runner *r, struct cell *c, const int timer) { ti_current); #endif + /* Rennehan: Here we recouple the wind particles */ + feedback_recouple_part(p, xp, e, with_cosmology, cosmo, + feedback_props); + /* Time intervals for this half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); From 008761e8812d0667f14cfa4af2c38107ca0c467d Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Wed, 26 Nov 2025 15:00:16 +0800 Subject: [PATCH 09/37] Fix compilation problems with static functions --- src/cooling/KIARA/cooling.c | 4 ++-- src/cooling/KIARA/cooling.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cooling/KIARA/cooling.c b/src/cooling/KIARA/cooling.c index fc902bf298..017e88d741 100644 --- a/src/cooling/KIARA/cooling.c +++ b/src/cooling/KIARA/cooling.c @@ -293,7 +293,7 @@ __attribute__((always_inline)) INLINE static float cooling_compute_self_shieldin * @param dt The cooling timestep. * */ -__attribute__((always_inline)) INLINE float cooling_compute_G0( +__attribute__((always_inline)) INLINE static float cooling_compute_G0( const struct part *restrict p, const float rho, const struct cooling_function_data *cooling, @@ -1279,7 +1279,7 @@ __attribute__((always_inline)) INLINE void firehose_cooling_and_dust( * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. */ -__attribute__((always_inline)) INLINE void cooling_init_chemistry( +void cooling_init_chemistry( const struct cooling_function_data* restrict cooling, struct part* restrict p) { diff --git a/src/cooling/KIARA/cooling.h b/src/cooling/KIARA/cooling.h index c882ad8810..aaa8ac365f 100644 --- a/src/cooling/KIARA/cooling.h +++ b/src/cooling/KIARA/cooling.h @@ -354,7 +354,7 @@ INLINE static double cooling_compute_subgrid_density( * @param phys_const The physical constant in internal units. * @param cosmo The current cosmological model. */ -__attribute__((always_inline)) INLINE float warm_ISM_temperature( +__attribute__((always_inline)) INLINE static float warm_ISM_temperature( const struct part *restrict p, const struct cooling_function_data *cooling, const struct phys_const* phys_const, const struct cosmology* cosmo) { From 9b1999be9c303023263e8d4cbe987b426878e546 Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Wed, 26 Nov 2025 15:07:36 +0800 Subject: [PATCH 10/37] Fix debugging check error in the Obsidian checks --- src/black_holes/Obsidian/black_holes.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/black_holes/Obsidian/black_holes.h b/src/black_holes/Obsidian/black_holes.h index 0083b5434d..c643efe3b6 100644 --- a/src/black_holes/Obsidian/black_holes.h +++ b/src/black_holes/Obsidian/black_holes.h @@ -1523,7 +1523,8 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( if (bp->state == BH_states_adaf) bp->v_kick = 0.f; #ifdef OBSIDIAN_DEBUG_CHECKS - float galaxy_sfr = fof_props->group_star_formation_rate[group_id]; + // MATTHIEU: TODO: FIX THIS! + float galaxy_sfr = 0.; //fof_props->group_star_formation_rate[group_id]; tdyn_inv = (tdyn_inv > 0.f) ? tdyn_inv : FLT_MIN; message("BH_ACC: z=%g bid=%lld ms=%g dms=%g sfr=%g mbh=%g dmbh=%g state=%d " "torque=%g bondi=%g fEdd=%g facc=%g fsupp=%g mcold=%g mhot=%g mdisk=%g" @@ -1545,7 +1546,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( bp->cold_gas_mass * props->mass_to_solar_mass, bp->hot_gas_mass * props->mass_to_solar_mass, corot_gas_mass * props->mass_to_solar_mass, - props->time_to_Myr / bp->tdyn_inv, + props->time_to_Myr / tdyn_inv, bp->v_kick / props->kms_to_internal, delta_mass, bp->radiative_efficiency, From 5e9e9d916ee5c8abac4636d63b4d85217e2b4292 Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Wed, 26 Nov 2025 15:14:16 +0800 Subject: [PATCH 11/37] Applied the code formatting script to help diffing between versions --- src/black_holes/Obsidian/black_holes.h | 705 ++++--- src/black_holes/Obsidian/black_holes_debug.h | 2 +- src/black_holes/Obsidian/black_holes_iact.h | 355 ++-- src/black_holes/Obsidian/black_holes_io.h | 85 +- src/black_holes/Obsidian/black_holes_part.h | 29 +- .../Obsidian/black_holes_properties.h | 552 +++--- src/black_holes/Obsidian/black_holes_struct.h | 14 +- src/chemistry/KIARA/chemistry.h | 690 +++---- src/chemistry/KIARA/chemistry_additions.h | 8 +- src/chemistry/KIARA/chemistry_debug.h | 2 +- src/chemistry/KIARA/chemistry_iact.h | 244 +-- src/chemistry/KIARA/chemistry_io.h | 73 +- src/chemistry/KIARA/chemistry_struct.h | 29 +- src/cooling/KIARA/cooling.c | 860 ++++----- src/cooling/KIARA/cooling.h | 576 +++--- src/cooling/KIARA/cooling_debug.h | 2 +- src/cooling/KIARA/cooling_io.h | 285 ++- src/cooling/KIARA/cooling_properties.h | 30 +- src/feedback/KIARA/feedback.c | 1676 +++++++---------- src/feedback/KIARA/feedback.h | 368 ++-- src/feedback/KIARA/feedback_debug.h | 4 +- src/feedback/KIARA/feedback_iact.h | 404 ++-- src/feedback/KIARA/feedback_properties.h | 28 +- src/feedback/KIARA/feedback_struct.h | 16 +- src/fof.c | 15 +- src/runner_doiact_functions_hydro.h | 26 +- src/runner_doiact_functions_stars.h | 6 +- src/runner_time_integration.c | 5 +- src/star_formation/KIARA/star_formation.h | 415 ++-- .../KIARA/star_formation_debug.h | 2 +- src/star_formation/KIARA/star_formation_io.h | 25 +- .../KIARA/star_formation_logger.h | 4 +- src/stars/KIARA/stars.h | 40 +- src/stars/KIARA/stars_debug.h | 2 +- src/stars/KIARA/stars_io.h | 28 +- src/stars/KIARA/stars_part.h | 8 +- src/timestep.h | 8 +- swift.c | 6 +- tests/test125cells.c | 8 +- 39 files changed, 3465 insertions(+), 4170 deletions(-) diff --git a/src/black_holes/Obsidian/black_holes.h b/src/black_holes/Obsidian/black_holes.h index c643efe3b6..403e8f168f 100644 --- a/src/black_holes/Obsidian/black_holes.h +++ b/src/black_holes/Obsidian/black_holes.h @@ -24,7 +24,6 @@ #include "black_holes_properties.h" #include "black_holes_struct.h" #include "cooling.h" -#include "star_formation.h" #include "cosmology.h" #include "dimension.h" #include "exp10.h" @@ -33,12 +32,12 @@ #include "minmax.h" #include "physical_constants.h" #include "random.h" +#include "star_formation.h" /* Standard includes */ #include -#include #include - +#include /** * @brief How much of the feedback actually couples to the medium? @@ -49,12 +48,11 @@ * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static double get_black_hole_coupling( - const struct bpart* const bp, const struct black_holes_props* props, - const struct cosmology* cosmo, const struct phys_const* phys_const) { + const struct bpart *const bp, const struct black_holes_props *props, + const struct cosmology *cosmo, const struct phys_const *phys_const) { const int BH_state = bp->state; switch (BH_state) { - case BH_states_adaf: - { + case BH_states_adaf: { float scaling = 1.f; if (props->adaf_coupling < 0.f) { min(pow(1. + cosmo->z, props->adaf_z_scaling), 1.); @@ -62,15 +60,21 @@ __attribute__((always_inline)) INLINE static double get_black_hole_coupling( return fabs(props->adaf_coupling) * scaling; break; } - case BH_states_quasar: - { + case BH_states_quasar: { float quasar_coupling = fabs(props->quasar_coupling); const double c = phys_const->const_speed_light_c; - const double luminosity = bp->radiative_efficiency * bp->accretion_rate * c * c; + const double luminosity = + bp->radiative_efficiency * bp->accretion_rate * c * c; const float mass_limit = get_black_hole_adaf_mass_limit(bp, props, cosmo); - if (luminosity > props->quasar_luminosity_thresh && props->quasar_luminosity_thresh > 0.f && bp->subgrid_mass * props->mass_to_solar_mass > mass_limit) { - quasar_coupling = fmin(quasar_coupling * luminosity / props->quasar_luminosity_thresh, 1.); - //message("BOOST: z=%g id=%lld MBH=%g LBH=%g boost=%g qcoupling=%g", cosmo->z, bp->id, bp->subgrid_mass * props->mass_to_solar_mass, luminosity * props->conv_factor_energy_rate_to_cgs, luminosity / props->quasar_luminosity_thresh, quasar_coupling); + if (luminosity > props->quasar_luminosity_thresh && + props->quasar_luminosity_thresh > 0.f && + bp->subgrid_mass * props->mass_to_solar_mass > mass_limit) { + quasar_coupling = fmin( + quasar_coupling * luminosity / props->quasar_luminosity_thresh, 1.); + // message("BOOST: z=%g id=%lld MBH=%g LBH=%g boost=%g qcoupling=%g", + // cosmo->z, bp->id, bp->subgrid_mass * props->mass_to_solar_mass, + // luminosity * props->conv_factor_energy_rate_to_cgs, luminosity / + // props->quasar_luminosity_thresh, quasar_coupling); } return quasar_coupling; break; @@ -91,16 +95,16 @@ __attribute__((always_inline)) INLINE static double get_black_hole_coupling( * @param props The properties of the black hole scheme. * @param f_Edd M_dot,BH / M_dot,Edd */ -__attribute__((always_inline)) INLINE static double -get_black_hole_slim_disk_efficiency(const struct black_holes_props* props, +__attribute__((always_inline)) INLINE static double +get_black_hole_slim_disk_efficiency(const struct black_holes_props *props, const double f_Edd) { if (f_Edd <= 0.) return 0.; const double R = 1. / f_Edd; - /* Efficiency from Lupi et al. (2014), + /* Efficiency from Lupi et al. (2014), * super eddington accretion and feedback */ - return (R / 16.) * props->A_sd * - (0.985 / (R + (5. / 8.) * props->B_sd) + 0.015 / - (R + (5. / 8.) * props->C_sd)); + return (R / 16.) * props->A_sd * + (0.985 / (R + (5. / 8.) * props->B_sd) + + 0.015 / (R + (5. / 8.) * props->C_sd)); } /** @@ -109,25 +113,24 @@ get_black_hole_slim_disk_efficiency(const struct black_holes_props* props, * @param props The properties of the black hole scheme. * @param f_Edd M_dot,BH / M_dot,Edd */ -__attribute__((always_inline)) INLINE static -double get_black_hole_adaf_efficiency( - const struct black_holes_props* props, const double f_Edd) { - return props->epsilon_r * f_Edd; /* scales with M_dot,BH */ +__attribute__((always_inline)) INLINE static double +get_black_hole_adaf_efficiency(const struct black_holes_props *props, + const double f_Edd) { + return props->epsilon_r * f_Edd; /* scales with M_dot,BH */ } /** - * @brief Chooses and calls the proper radiative efficiency function for the + * @brief Chooses and calls the proper radiative efficiency function for the * state. * * @param props The properties of the black hole scheme. * @param f_Edd The accretion rate over the Eddington rate. * @param BH_state The current state of the BH. */ -__attribute__((always_inline)) INLINE static -double get_black_hole_radiative_efficiency( - const struct black_holes_props* props, - const double f_Edd, const int BH_state) { - switch(BH_state) { +__attribute__((always_inline)) INLINE static double +get_black_hole_radiative_efficiency(const struct black_holes_props *props, + const double f_Edd, const int BH_state) { + switch (BH_state) { case BH_states_adaf: return get_black_hole_adaf_efficiency(props, f_Edd); case BH_states_quasar: @@ -149,22 +152,21 @@ double get_black_hole_radiative_efficiency( * @param phys_const The physical phys_const (in internal units). * @param bp The black hole particle. */ -__attribute__((always_inline)) INLINE static double -get_black_hole_wind_speed(const struct black_holes_props* props, - const struct phys_const* phys_const, - const struct bpart *bp) { +__attribute__((always_inline)) INLINE static double get_black_hole_wind_speed( + const struct black_holes_props *props, const struct phys_const *phys_const, + const struct bpart *bp) { if (bp->accretion_rate < 0.f || bp->m_dot_inflow < 0.f) return 0.f; float v_kick = 0.f; if (props->quasar_wind_speed < 0.f || props->slim_disk_wind_speed < 0.f) { - const float subgrid_mass_Msun = + const float subgrid_mass_Msun = bp->subgrid_mass * props->mass_to_solar_mass; if (bp->subgrid_mass > props->subgrid_seed_mass) { - const float min_BH_mass_Msun = + const float min_BH_mass_Msun = props->minimum_black_hole_mass_v_kick * props->mass_to_solar_mass; - const float dlog10_BH_mass = + const float dlog10_BH_mass = log10f(subgrid_mass_Msun) - log10f(min_BH_mass_Msun); v_kick = 500.f + (500.f / 3.f) * dlog10_BH_mass; @@ -177,23 +179,21 @@ get_black_hole_wind_speed(const struct black_holes_props* props, } } - switch (bp->state) { + switch (bp->state) { case BH_states_adaf: return fabs(props->adaf_wind_speed); break; case BH_states_quasar: if (props->quasar_wind_speed < 0.f && v_kick > 0.f) { return v_kick; - } - else { + } else { return fabs(props->quasar_wind_speed); } break; case BH_states_slim_disk: if (props->slim_disk_wind_speed < 0.f && v_kick > 0.f) { return v_kick; - } - else { + } else { return fabs(props->slim_disk_wind_speed); } break; @@ -213,11 +213,11 @@ get_black_hole_wind_speed(const struct black_holes_props* props, * @param bp The black hole particle. * @param m_dot_inflow_m_dot_edd M_dot,inflow scaled to M_dot,Edd for the BH. */ -__attribute__((always_inline)) INLINE static double -get_black_hole_upper_mdot_medd(const struct black_holes_props* props, - const struct phys_const* phys_const, - const struct cosmology* cosmo, - const struct bpart* const bp, +__attribute__((always_inline)) INLINE static double +get_black_hole_upper_mdot_medd(const struct black_holes_props *props, + const struct phys_const *phys_const, + const struct cosmology *cosmo, + const struct bpart *const bp, const double m_dot_inflow_m_dot_edd) { if (m_dot_inflow_m_dot_edd <= 0.) return 0.; @@ -231,16 +231,17 @@ get_black_hole_upper_mdot_medd(const struct black_holes_props* props, if (v_kick > 0.f) { /* Set the slim disk mass loading to be continuous at the - * eta upper boundary. Compute the phi term to solve for the + * eta upper boundary. Compute the phi term to solve for the * accretion fraction. * WARNING: Using the quasar_wind_momentum_flux because that is the - * default way of using the model. Any changes to that require a + * default way of using the model. Any changes to that require a * change here. */ const double c_over_v = phys_const->const_speed_light_c / v_kick; /* TODO: Compute once at the beginning of the simulation */ - double mom_flux_times_epsilon_sd = - props->quasar_wind_momentum_flux * get_black_hole_coupling(bp, props, cosmo, phys_const); + double mom_flux_times_epsilon_sd = + props->quasar_wind_momentum_flux * + get_black_hole_coupling(bp, props, cosmo, phys_const); phi = mom_flux_times_epsilon_sd * c_over_v; } } @@ -248,11 +249,13 @@ get_black_hole_upper_mdot_medd(const struct black_holes_props* props, int num_roots; a3 = ((5. * 5.) / (8. * 8.)) * props->B_sd * props->C_sd; - a2 = (5. / 8.) * ((props->B_sd + props->C_sd) + (phi / 16.) * - props->A_sd * (0.015 * props->B_sd + 0.985 * props->C_sd) - + a2 = + (5. / 8.) * + ((props->B_sd + props->C_sd) + + (phi / 16.) * props->A_sd * (0.015 * props->B_sd + 0.985 * props->C_sd) - (5. / 8.) * props->B_sd * props->C_sd * m_dot_inflow_m_dot_edd); - a1 = 1. + (phi / 16.) * props->A_sd - (5. / 8.) * - (props->B_sd + props->C_sd) * m_dot_inflow_m_dot_edd; + a1 = 1. + (phi / 16.) * props->A_sd - + (5. / 8.) * (props->B_sd + props->C_sd) * m_dot_inflow_m_dot_edd; a0 = -m_dot_inflow_m_dot_edd; a2 /= a3; @@ -263,21 +266,21 @@ get_black_hole_upper_mdot_medd(const struct black_holes_props* props, if (num_roots == 1) { if (x1 >= 0.) { return x1; - } - else { - warning("num_roots=1 m_dot_inflow_m_dot_edd=%g phi=%g a3=%g a2=%g " - "a1=%g a0=%g", - m_dot_inflow_m_dot_edd, phi, a3, a2, a1, a0); + } else { + warning( + "num_roots=1 m_dot_inflow_m_dot_edd=%g phi=%g a3=%g a2=%g " + "a1=%g a0=%g", + m_dot_inflow_m_dot_edd, phi, a3, a2, a1, a0); return 0.; } } if (x3 >= 0.) { return x3; - } - else { - warning("num_roots=0 m_dot_inflow_m_dot_edd=%g phi=%g a3=%g a2=%g a1=%g " - "a0=%g", - m_dot_inflow_m_dot_edd, phi, a3, a2, a1, a0); + } else { + warning( + "num_roots=0 m_dot_inflow_m_dot_edd=%g phi=%g a3=%g a2=%g a1=%g " + "a0=%g", + m_dot_inflow_m_dot_edd, phi, a3, a2, a1, a0); return 0.; } @@ -294,13 +297,13 @@ get_black_hole_upper_mdot_medd(const struct black_holes_props* props, * @param BH_state The current state of the BH. * @param Eddington_rate M_dot,Edd in internal units. */ -__attribute__((always_inline)) INLINE static -double get_black_hole_accretion_factor(const struct black_holes_props* props, - const struct phys_const* phys_const, - const struct cosmology* cosmo, - const struct bpart* const bp, - const double Eddington_rate) { - +__attribute__((always_inline)) INLINE static double +get_black_hole_accretion_factor(const struct black_holes_props *props, + const struct phys_const *phys_const, + const struct cosmology *cosmo, + const struct bpart *const bp, + const double Eddington_rate) { + const double m_dot_inflow = bp->m_dot_inflow; const double BH_mass = bp->subgrid_mass; const int BH_state = bp->state; @@ -311,37 +314,35 @@ double get_black_hole_accretion_factor(const struct black_holes_props* props, case BH_states_adaf: return props->adaf_f_accretion; break; - case BH_states_quasar: - { + case BH_states_quasar: { float v_kick = 0.f; float f_accretion = 0.f; if (props->quasar_wind_speed < 0.f) { /* Save computation by only computing when specified by the user */ v_kick = get_black_hole_wind_speed(props, phys_const, bp); if (v_kick > 0.) { - const double c = phys_const->const_speed_light_c; + const double c = phys_const->const_speed_light_c; const double c_over_v = c / v_kick; - double quasar_coupling = get_black_hole_coupling(bp, props, cosmo, phys_const); - double quasar_wind_mass_loading = - props->quasar_wind_momentum_flux * - quasar_coupling * props->epsilon_r * c_over_v; + double quasar_coupling = + get_black_hole_coupling(bp, props, cosmo, phys_const); + double quasar_wind_mass_loading = props->quasar_wind_momentum_flux * + quasar_coupling * props->epsilon_r * + c_over_v; f_accretion = 1.f / (1.f + quasar_wind_mass_loading); } } if (f_accretion > 0.f) { return f_accretion; - } - else { + } else { return props->quasar_f_accretion; } break; } - case BH_states_slim_disk: - { + case BH_states_slim_disk: { /* This is the FRACTION of the total so divide by M_dot,inflow */ const double f_edd = m_dot_inflow / Eddington_rate; - double mdot_medd = + double mdot_medd = get_black_hole_upper_mdot_medd(props, phys_const, cosmo, bp, f_edd); return mdot_medd * Eddington_rate / m_dot_inflow; break; @@ -361,8 +362,8 @@ double get_black_hole_accretion_factor(const struct black_holes_props* props, * @param phys_const The physical phys_const (in internal units). */ __attribute__((always_inline)) INLINE static float black_holes_compute_timestep( - const struct bpart* const bp, const struct black_holes_props* props, - const struct phys_const* phys_const, const struct cosmology* cosmo) { + const struct bpart *const bp, const struct black_holes_props *props, + const struct phys_const *phys_const, const struct cosmology *cosmo) { /* Allow for finer timestepping if necessary! */ float dt_accr = FLT_MAX; @@ -376,8 +377,7 @@ __attribute__((always_inline)) INLINE static float black_holes_compute_timestep( if (bp->state == BH_states_adaf && bp->jet_mass_loading > 0.f) { dt_kick = bp->ngb_mass / (bp->jet_mass_loading * bp->accretion_rate); - } - else { + } else { if (bp->f_accretion > 0.f) { /* Make sure that the wind mass does not exceed the kernel gas mass */ const float psi = (1.f - bp->f_accretion) / bp->f_accretion; @@ -409,8 +409,8 @@ __attribute__((always_inline)) INLINE static float black_holes_compute_timestep( * @param props The properties of the black holes model. */ __attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( - struct bpart* bp, const struct black_holes_props* props) { - + struct bpart *bp, const struct black_holes_props *props) { + bp->time_bin = 0; if (props->use_subgrid_mass_from_ics == 0) { bp->subgrid_mass = bp->mass; @@ -461,7 +461,6 @@ __attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( bp->jet_mass_kicked_this_step = 0.f; bp->adaf_energy_to_dump = 0.f; bp->adaf_energy_used_this_step = 0.f; - } /** @@ -470,7 +469,7 @@ __attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( * @param bp The particle to act upon */ __attribute__((always_inline)) INLINE static void black_holes_init_bpart( - struct bpart* bp) { + struct bpart *bp) { #ifdef DEBUG_INTERACTIONS_BLACK_HOLES for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) @@ -508,11 +507,11 @@ __attribute__((always_inline)) INLINE static void black_holes_init_bpart( bp->reposition.delta_x[2] = -FLT_MAX; bp->reposition.min_potential = FLT_MAX; bp->reposition.potential = FLT_MAX; - bp->accretion_rate = 0.f; /* Optionally accumulated ngb-by-ngb */ + bp->accretion_rate = 0.f; /* Optionally accumulated ngb-by-ngb */ bp->bondi_accretion_rate = 0.f; /* Optionally accumulated ngb-by-ngb */ bp->cold_disk_mass = 0.f; bp->mass_at_start_of_step = bp->mass; /* bp->mass may grow in nibbling mode */ - bp->m_dot_inflow = 0.f; /* reset accretion rate */ + bp->m_dot_inflow = 0.f; /* reset accretion rate */ bp->kernel_wt_sum = 0.f; /* update the reservoir */ @@ -525,7 +524,7 @@ __attribute__((always_inline)) INLINE static void black_holes_init_bpart( bp->unresolved_mass_reservoir -= bp->unresolved_mass_kicked_this_step; bp->unresolved_mass_kicked_this_step = 0.f; if (bp->unresolved_mass_reservoir < 0.f) { - bp->unresolved_mass_reservoir = 0.f; + bp->unresolved_mass_reservoir = 0.f; } /* update the adaf energy reservoir */ if (bp->adaf_wt_sum > 0.f) { @@ -537,8 +536,7 @@ __attribute__((always_inline)) INLINE static void black_holes_init_bpart( if (bp->adaf_energy_to_dump < 0.f) { bp->adaf_energy_to_dump = 0.f; } - } - else { + } else { bp->adaf_energy_used_this_step = 0.f; } } @@ -553,7 +551,7 @@ __attribute__((always_inline)) INLINE static void black_holes_init_bpart( * @param dt_drift The drift time-step for positions. */ __attribute__((always_inline)) INLINE static void black_holes_predict_extra( - struct bpart* restrict bp, float dt_drift) { + struct bpart *restrict bp, float dt_drift) { /* Are we doing some repositioning? */ if (bp->reposition.min_potential != FLT_MAX) { @@ -606,7 +604,7 @@ __attribute__((always_inline)) INLINE static void black_holes_predict_extra( * @param bp The particle. */ __attribute__((always_inline)) INLINE static void -black_holes_reset_predicted_values(struct bpart* bp) {} +black_holes_reset_predicted_values(struct bpart *bp) {} /** * @brief Kick the additional variables @@ -615,7 +613,7 @@ black_holes_reset_predicted_values(struct bpart* bp) {} * @param dt The time-step for this kick */ __attribute__((always_inline)) INLINE static void black_holes_kick_extra( - struct bpart* bp, float dt) {} + struct bpart *bp, float dt) {} /** * @brief Finishes the calculation of density on black holes @@ -624,7 +622,7 @@ __attribute__((always_inline)) INLINE static void black_holes_kick_extra( * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void black_holes_end_density( - struct bpart* bp, const struct cosmology* cosmo) { + struct bpart *bp, const struct cosmology *cosmo) { /* Some smoothing length multiples. */ const float h = bp->h; @@ -666,7 +664,6 @@ __attribute__((always_inline)) INLINE static void black_holes_end_density( bp->circular_velocity_gas[0] *= h_inv; bp->circular_velocity_gas[1] *= h_inv; bp->circular_velocity_gas[2] *= h_inv; - } /** @@ -676,14 +673,14 @@ __attribute__((always_inline)) INLINE static void black_holes_end_density( * @param bp The particle to act upon * @param cosmo The current cosmological model. */ -__attribute__((always_inline)) INLINE static void -black_holes_bpart_has_no_neighbours(struct bpart* bp, - const struct cosmology* cosmo) { +__attribute__((always_inline)) INLINE static void +black_holes_bpart_has_no_neighbours(struct bpart *bp, + const struct cosmology *cosmo) { - //warning( - // "BH particle with ID %lld treated as having no neighbours (h: %g, " - // "wcount: %g).", - // bp->id, bp->h, bp->density.wcount); + // warning( + // "BH particle with ID %lld treated as having no neighbours (h: %g, " + // "wcount: %g).", + // bp->id, bp->h, bp->density.wcount); /* Some smoothing length multiples. */ const float h = bp->h; @@ -708,7 +705,7 @@ black_holes_bpart_has_no_neighbours(struct bpart* bp, * @param bp the #bpart. */ __attribute__((always_inline)) INLINE static double -black_holes_get_accretion_rate(const struct bpart* bp) { +black_holes_get_accretion_rate(const struct bpart *bp) { return bp->accretion_rate; } @@ -718,7 +715,7 @@ black_holes_get_accretion_rate(const struct bpart* bp) { * @param bp the #bpart. */ __attribute__((always_inline)) INLINE static double -black_holes_get_accreted_mass(const struct bpart* bp) { +black_holes_get_accreted_mass(const struct bpart *bp) { return bp->total_accreted_mass; } @@ -728,7 +725,7 @@ black_holes_get_accreted_mass(const struct bpart* bp) { * @param bp the #bpart. */ __attribute__((always_inline)) INLINE static double -black_holes_get_subgrid_mass(const struct bpart* bp) { +black_holes_get_subgrid_mass(const struct bpart *bp) { return bp->subgrid_mass; } @@ -738,8 +735,8 @@ black_holes_get_subgrid_mass(const struct bpart* bp) { * @param bp the #bpart. */ __attribute__((always_inline)) INLINE static double -black_holes_get_bolometric_luminosity(const struct bpart* bp, - const struct phys_const* phys_const) { +black_holes_get_bolometric_luminosity(const struct bpart *bp, + const struct phys_const *phys_const) { const double c = phys_const->const_speed_light_c; return bp->accretion_rate * bp->radiative_efficiency * c * c; } @@ -750,7 +747,7 @@ black_holes_get_bolometric_luminosity(const struct bpart* bp, * @param bp the #bpart. */ __attribute__((always_inline)) INLINE static double black_holes_get_jet_power( - const struct bpart* bp, const struct phys_const* phys_const) { + const struct bpart *bp, const struct phys_const *phys_const) { const double c = phys_const->const_speed_light_c; /* accretion_rate is M_dot,acc from the paper */ return bp->radiative_efficiency * bp->accretion_rate * c * c; @@ -766,8 +763,8 @@ __attribute__((always_inline)) INLINE static double black_holes_get_jet_power( * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void black_holes_swallow_part( - struct bpart* bp, const struct part* p, const struct xpart* xp, - const struct cosmology* cosmo) { + struct bpart *bp, const struct part *p, const struct xpart *xp, + const struct cosmology *cosmo) { /* Get the current dynamical masses */ const float gas_mass = hydro_get_mass(p); @@ -817,8 +814,8 @@ __attribute__((always_inline)) INLINE static void black_holes_swallow_part( (dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]) / dr); /* Update the BH metal masses */ - struct chemistry_bpart_data* bp_chem = &bp->chemistry_data; - const struct chemistry_part_data* p_chem = &p->chemistry_data; + struct chemistry_bpart_data *bp_chem = &bp->chemistry_data; + const struct chemistry_part_data *p_chem = &p->chemistry_data; chemistry_add_part_to_bpart(bp_chem, p_chem, gas_mass); /* This BH swallowed a gas particle */ @@ -843,9 +840,10 @@ __attribute__((always_inline)) INLINE static void black_holes_swallow_part( * @param props The properties of the black hole scheme. */ __attribute__((always_inline)) INLINE static void black_holes_swallow_bpart( - struct bpart* bpi, const struct bpart* bpj, const struct cosmology* cosmo, + struct bpart *bpi, const struct bpart *bpj, const struct cosmology *cosmo, const double time, const int with_cosmology, - const struct black_holes_props* props, const struct phys_const* phys_const) { + const struct black_holes_props *props, + const struct phys_const *phys_const) { /* Get the current dynamical masses */ const float bpi_dyn_mass = bpi->mass; @@ -890,8 +888,8 @@ __attribute__((always_inline)) INLINE static void black_holes_swallow_bpart( bpi->gpart->v_full[2] = bpi->v[2]; /* Update the BH metal masses */ - struct chemistry_bpart_data* bpi_chem = &bpi->chemistry_data; - const struct chemistry_bpart_data* bpj_chem = &bpj->chemistry_data; + struct chemistry_bpart_data *bpi_chem = &bpi->chemistry_data; + const struct chemistry_bpart_data *bpj_chem = &bpj->chemistry_data; chemistry_add_bpart_to_bpart(bpi_chem, bpj_chem); /* Update the energy reservoir */ @@ -913,20 +911,21 @@ __attribute__((always_inline)) INLINE static void black_holes_swallow_bpart( } /** - * @brief Function to generate a random number from a Gaussian distribution. + * @brief Function to generate a random number from a Gaussian distribution. * @param mu Mean of Gaussian * @param sigma Standard deviation of Gaussian - * @param u1 Random number in (0,1) - * @param u2 Random number in (0,1) + * @param u1 Random number in (0,1) + * @param u2 Random number in (0,1) */ -__attribute__((always_inline)) INLINE static float gaussian_random_number(float mu, float sigma, double u1, double u2) { +__attribute__((always_inline)) INLINE static float gaussian_random_number( + float mu, float sigma, double u1, double u2) { double mag, z0, z1; /* Apply the Box-Muller transform */ mag = sigma * sqrt(-2.0 * log(u1)); - z0 = mag * cos(2.0 * M_PI * u2) + mu; - z1 = mag * sin(2.0 * M_PI * u2) + mu; - if (u1+u2 < 1.f) { + z0 = mag * cos(2.0 * M_PI * u2) + mu; + z1 = mag * sin(2.0 * M_PI * u2) + mu; + if (u1 + u2 < 1.f) { return z0; } return z1; @@ -948,10 +947,10 @@ __attribute__((always_inline)) INLINE static float gaussian_random_number(float * @param ti_begin The time at which the step begun (ti_current). */ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( - struct bpart* restrict bp, const struct black_holes_props* props, - const struct phys_const* phys_const, const struct cosmology* cosmo, - const struct cooling_function_data* cooling, - const struct entropy_floor_properties* floor_props, const double time, + struct bpart *restrict bp, const struct black_holes_props *props, + const struct phys_const *phys_const, const struct cosmology *cosmo, + const struct cooling_function_data *cooling, + const struct entropy_floor_properties *floor_props, const double time, const int with_cosmology, const double dt, const integertime_t ti_begin) { /* Record that the black hole has another active time step */ @@ -959,7 +958,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( if (dt == 0. || bp->rho_gas == 0. || bp->h == 0.) return; -/* Collect information about galaxy that the particle belongs to */ + /* Collect information about galaxy that the particle belongs to */ const float galaxy_mstar = bp->galaxy_data.stellar_mass; const float galaxy_mgas = bp->galaxy_data.gas_mass; @@ -990,19 +989,18 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( const double bh_h = kernel_gamma * bp->h; const double bh_h_inv = 1. / bh_h; - const double volume_bh_inv = + const double volume_bh_inv = (3. / (4. * M_PI)) * bh_h_inv * bh_h_inv * bh_h_inv; double gas_rho = 0.; if (props->bondi_use_all_gas) { gas_rho = bp->rho_gas; - } - else { + } else { gas_rho = bp->hot_gas_mass * volume_bh_inv; } const double gas_rho_phys = gas_rho * cosmo->a3_inv; - /* We can now compute the Bondi accretion rate (internal units) + /* We can now compute the Bondi accretion rate (internal units) * D. Rennehan: In Simba, we only consider the hot gas within * the kernel for the Bondi rate, and the cold gas using the * torque accretion estimator. @@ -1015,8 +1013,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( if (bp->internal_energy_gas > 0.) { gas_internal_energy = bp->internal_energy_gas; } - } - else { + } else { if (bp->hot_gas_internal_energy > 0.) { gas_internal_energy = bp->hot_gas_internal_energy; } @@ -1024,23 +1021,23 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( /* Check if there is hot/any gas in the kernel */ if (gas_internal_energy > 0.) { - double gas_c = + double gas_c = gas_soundspeed_from_internal_energy(gas_rho, gas_internal_energy); if (gas_c > 0.) { - const double gas_c_phys_inv = - 1. / (cosmo->a_factor_sound_speed * gas_c); + const double gas_c_phys_inv = 1. / (cosmo->a_factor_sound_speed * gas_c); Bondi_rate = 4. * M_PI * G * G * BH_mass * BH_mass * gas_rho_phys * - gas_c_phys_inv * gas_c_phys_inv * gas_c_phys_inv; + gas_c_phys_inv * gas_c_phys_inv * gas_c_phys_inv; /* In the case of standard Bondi, we limit it to the Eddington rate */ - Bondi_rate = fmin(Bondi_rate, props->f_Edd_Bondi_maximum * Eddington_rate); + Bondi_rate = + fmin(Bondi_rate, props->f_Edd_Bondi_maximum * Eddington_rate); } } - /* The accretion rate estimators give Mdot,inflow - * (Mdot,BH = f_acc * Mdot,inflow) */ + /* The accretion rate estimators give Mdot,inflow + * (Mdot,BH = f_acc * Mdot,inflow) */ const double bondi_accr_rate = props->bondi_alpha * Bondi_rate; /* Compute the torque-limited accretion rate */ @@ -1048,12 +1045,10 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( double f_corr_stellar = 10.; if (galaxy_mgas > 0.) { - f_corr_stellar = - min(galaxy_mstar / galaxy_mgas, - f_corr_stellar); + f_corr_stellar = min(galaxy_mstar / galaxy_mgas, f_corr_stellar); } - /* Torque accretion rate based on some fraction of gas near BH + /* Torque accretion rate based on some fraction of gas near BH * falling in on dynamical time. (This is the default.) * Here the accretion rate is only based on Mgas / tdyn. * We do not use the DM mass to compute tdyn since it probably @@ -1076,9 +1071,8 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( /* Includes dynamical mass of the BH */ switch (props->dynamical_time_calculation_method) { /* Assume gas fraction is the same in the kernel and outside */ - case 0: - { - /* Compute correction to total dynamical mass around + case 0: { + /* Compute correction to total dynamical mass around * BH contributed by stars */ const float m_star_gal = galaxy_mstar; const float m_gas_cold_gal = galaxy_mgas; @@ -1098,7 +1092,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( const float rho_est = (m_star_bh + m_gas_bh + m_bh) * volume_bh_inv; /* Inverse physical dynamical time */ - tdyn_inv = sqrt(32. * G * rho_est * cosmo->a3_inv / (3. * M_PI)); + tdyn_inv = sqrt(32. * G * rho_est * cosmo->a3_inv / (3. * M_PI)); break; } @@ -1110,8 +1104,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( break; /* Assume dynamical time from the kernel mass */ - case 2: - { + case 2: { /* do not have gravity_props here */ const float hsml = kernel_gamma * bh_h; const float volume = (4. * M_PI / 3.) * hsml * hsml * hsml; @@ -1121,7 +1114,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( } default: - error("Unknown dynamical time calculation method %d", + error("Unknown dynamical time calculation method %d", props->dynamical_time_calculation_method); break; } @@ -1131,40 +1124,37 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( /* Limit by max dynamical time, with z=0 value scaled by H0/H */ if (props->dynamical_time_max > 0.) { - const float t_inv_min = cosmo->H / - (props->dynamical_time_max * cosmo->H0); + const float t_inv_min = cosmo->H / (props->dynamical_time_max * cosmo->H0); tdyn_inv = fmax(tdyn_inv, t_inv_min); } - /* Create a spread in accretion times, with minimum at - * free-fall time=0.5*tdyn */ + /* Create a spread in accretion times, with minimum at + * free-fall time=0.5*tdyn */ const float tdyn_sigma = props->tdyn_sigma; if (tdyn_sigma > 0.f) { const double ran1 = - random_unit_interval(bp->id, ti_begin, - random_number_BH_swallow); + random_unit_interval(bp->id, ti_begin, random_number_BH_swallow); const double ran2 = - random_unit_interval(bp->id, ti_begin, - random_number_BH_swallow); - const float gaussian_random = - gaussian_random_number(0.f, tdyn_sigma, ran1, ran2); + random_unit_interval(bp->id, ti_begin, random_number_BH_swallow); + const float gaussian_random = + gaussian_random_number(0.f, tdyn_sigma, ran1, ran2); tdyn_inv /= 0.5 * (1.f + fabs(gaussian_random)); } - const float corot_gas_mass = - bp->cold_gas_mass - 2. * (bp->cold_gas_mass - bp->cold_disk_mass); + const float corot_gas_mass = + bp->cold_gas_mass - 2. * (bp->cold_gas_mass - bp->cold_disk_mass); if (props->torque_accretion_norm > 0.f) { switch (props->torque_accretion_method) { case 0: if (galaxy_mgas > 0.) { - torque_accr_rate = + torque_accr_rate = props->torque_accretion_norm * bp->cold_disk_mass * tdyn_inv; } break; case 1: if (corot_gas_mass > 0. && bp->cold_gas_mass > 0.) { - torque_accr_rate = + torque_accr_rate = props->torque_accretion_norm * corot_gas_mass * tdyn_inv; } break; @@ -1181,8 +1171,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( const float mass_to_1e8solar = props->mass_to_solar_mass * 1.0e-8; const float f0 = - 0.31 * f_disk * f_disk * - pow(m_disk * mass_to_1e9solar, -1. / 3.); + 0.31 * f_disk * f_disk * pow(m_disk * mass_to_1e9solar, -1. / 3.); const float f_gas = corot_gas_mass / m_disk; const float mass_in_1e8solar = BH_mass * mass_to_1e8solar; @@ -1191,32 +1180,29 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( powf(f_disk, 5. / 2.) * powf(mass_in_1e8solar, 1. / 6.) * powf(r0, -3. / 2.) / (1. + f0 / f_gas); - torque_accr_rate *= - (props->time_to_yr / props->mass_to_solar_mass); + torque_accr_rate *= (props->time_to_yr / props->mass_to_solar_mass); } break; - case 3: - if (galaxy_mgas > 0.) { - torque_accr_rate = + case 3: + if (galaxy_mgas > 0.) { + torque_accr_rate = props->torque_accretion_norm * bp->cold_gas_mass * tdyn_inv; - } - break; + } + break; - default: - error("Unknown torque_accretion_method=%d", - props->torque_accretion_method); - break; + default: + error("Unknown torque_accretion_method=%d", + props->torque_accretion_method); + break; } /* Do suppression of BH growth */ switch (props->suppress_growth) { - case 1: - { + case 1: { const double r0 = bh_h * cosmo->a * props->length_to_parsec; const double sigma_eff = f_corr_stellar * bp->ngb_mass * - props->mass_to_solar_mass / - (M_PI * r0 * r0); + props->mass_to_solar_mass / (M_PI * r0 * r0); torque_accr_rate *= sigma_eff / (sigma_eff + 3000.); break; @@ -1224,49 +1210,55 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( case 2: case 6: - case 7: - { + case 7: { double m_suppress = fabs(props->bh_characteristic_suppression_mass); if (props->bh_characteristic_suppression_mass < 0) { m_suppress *= cosmo->a; } - torque_accr_rate *= + torque_accr_rate *= 1. - exp(-BH_mass * props->mass_to_solar_mass / m_suppress); break; } case 4: - case 5: - { - /* compute mass loading factor from SF feedback, - * should be same as used in feedback_mass_loading_factor() + case 5: { + /* compute mass loading factor from SF feedback, + * should be same as used in feedback_mass_loading_factor() */ const float galaxy_stellar_mass = galaxy_mstar; - const float eta_norm = props->FIRE_eta_normalization; - const float eta_break = props->FIRE_eta_break; - const float eta_lower_slope = props->FIRE_eta_lower_slope; - const float eta_upper_slope = props->FIRE_eta_upper_slope; - const float eta_lower_slope_EOR = props->FIRE_eta_lower_slope_EOR; - const float eta_minmass = props->minimum_galaxy_stellar_mass; - const float eta_suppress = props->wind_eta_suppression_redshift; - - const double eta = feedback_mass_loading_factor(cosmo, galaxy_stellar_mass, eta_minmass, eta_norm, eta_break, eta_lower_slope, eta_upper_slope, eta_lower_slope_EOR, eta_suppress); + const float eta_norm = props->FIRE_eta_normalization; + const float eta_break = props->FIRE_eta_break; + const float eta_lower_slope = props->FIRE_eta_lower_slope; + const float eta_upper_slope = props->FIRE_eta_upper_slope; + const float eta_lower_slope_EOR = props->FIRE_eta_lower_slope_EOR; + const float eta_minmass = props->minimum_galaxy_stellar_mass; + const float eta_suppress = props->wind_eta_suppression_redshift; + + const double eta = feedback_mass_loading_factor( + cosmo, galaxy_stellar_mass, eta_minmass, eta_norm, eta_break, + eta_lower_slope, eta_upper_slope, eta_lower_slope_EOR, + eta_suppress); if (bp->cold_gas_mass * tdyn_inv > 0.f) { - /* star formation efficiency, frac of gas converted + /* star formation efficiency, frac of gas converted * to stars per tdyn */ float sf_eff = props->suppression_sf_eff; if (sf_eff < 0.f) { - /* SF efficiency within BH kernel. Cap at cloud-scale SFE from Leroy+25 */ - sf_eff = fmin(bp->gas_SFR / (tdyn_inv * bp->cold_gas_mass), fabs(sf_eff)); + /* SF efficiency within BH kernel. Cap at cloud-scale SFE from + * Leroy+25 */ + sf_eff = fmin(bp->gas_SFR / (tdyn_inv * bp->cold_gas_mass), + fabs(sf_eff)); } /* Suppresses accretion by factor accounting for mass - * lost in outflow over accretion time. ODE: + * lost in outflow over accretion time. ODE: * dM/dt = -eta * sf_eff * M / tdyn */ torque_accr_rate *= exp(-eta * sf_eff); - //message("BH_SUPPRESS: z=%g id=%lld M*=%g eta=%g eff=%g tfac=%g fsupp=%g", cosmo->z, bp->id, galaxy_stellar_mass * props->mass_to_solar_mass, eta, sf_eff, t_accrete * tdyn_inv, exp(-eta * sf_eff * t_accrete * tdyn_inv)); + // message("BH_SUPPRESS: z=%g id=%lld M*=%g eta=%g eff=%g tfac=%g + // fsupp=%g", cosmo->z, bp->id, galaxy_stellar_mass * + // props->mass_to_solar_mass, eta, sf_eff, t_accrete * tdyn_inv, + // exp(-eta * sf_eff * t_accrete * tdyn_inv)); } break; } @@ -1281,29 +1273,30 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( if (isnan(torque_accr_rate)) error("torque_accr_rate nan"); #endif - /* Right now this is M_dot,inflow. We will multiply by + /* Right now this is M_dot,inflow. We will multiply by * f_accretion later to make it M_dot,acc */ bp->accretion_rate = bondi_accr_rate + torque_accr_rate; - /* We will use eddington_fraction_lower_boundary and - * eddington_fraction_upper_boundary to divide up the accretion rate + /* We will use eddington_fraction_lower_boundary and + * eddington_fraction_upper_boundary to divide up the accretion rate * in three regimes. - * - * In order to switch out of a regime (i.e. a state), it is necessary - * for the true accretion rate (compared to Eddington rate) to switch - * over the boundary. Therefore, before we switch a state we must calculate - * what the previous state predicts the true accretion rate onto the SMBH is, + * + * In order to switch out of a regime (i.e. a state), it is necessary + * for the true accretion rate (compared to Eddington rate) to switch + * over the boundary. Therefore, before we switch a state we must calculate + * what the previous state predicts the true accretion rate onto the SMBH is, * and then update the state if it crosses a boundary. */ - /* We need to store the full M_dot,inflow rate to calculate the + /* We need to store the full M_dot,inflow rate to calculate the * fraction at high accretion rate */ bp->m_dot_inflow = bp->accretion_rate; - const double f_accretion = - get_black_hole_accretion_factor(props, phys_const, cosmo, bp, Eddington_rate); - double predicted_mdot_medd = + const double f_accretion = get_black_hole_accretion_factor( + props, phys_const, cosmo, bp, Eddington_rate); + double predicted_mdot_medd = bp->accretion_rate * f_accretion / Eddington_rate; - const float my_adaf_mass_limit = get_black_hole_adaf_mass_limit(bp, props, cosmo); + const float my_adaf_mass_limit = + get_black_hole_adaf_mass_limit(bp, props, cosmo); /* Switch between states depending on the */ switch (bp->state) { @@ -1327,7 +1320,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( if (predicted_mdot_medd > props->eddington_fraction_upper_boundary) { bp->state = BH_states_slim_disk; } - + break; /* end case quasar */ case BH_states_slim_disk: if (BH_mass > my_adaf_mass_limit && @@ -1347,8 +1340,8 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( } /* This depends on the new state */ - bp->f_accretion = - get_black_hole_accretion_factor(props, phys_const, cosmo, bp, Eddington_rate); + bp->f_accretion = get_black_hole_accretion_factor(props, phys_const, cosmo, + bp, Eddington_rate); #ifdef OBSIDIAN_DEBUG_CHECKS if (isnan(bp->f_accretion)) error("f_accretion nan"); #endif @@ -1362,7 +1355,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( if (!props->bondi_use_all_gas) { /* Now we can Eddington limit. */ - bp->accretion_rate = + bp->accretion_rate = min(bp->accretion_rate, f_Edd_maximum * Eddington_rate); } @@ -1370,19 +1363,18 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( bp->eddington_fraction = bp->accretion_rate / Eddington_rate; /* Get the new radiative efficiency based on the new state */ - bp->radiative_efficiency = - get_black_hole_radiative_efficiency(props, bp->eddington_fraction, - bp->state); + bp->radiative_efficiency = get_black_hole_radiative_efficiency( + props, bp->eddington_fraction, bp->state); #ifdef OBSIDIAN_DEBUG_CHECKS if (isnan(bp->radiative_efficiency)) error("radiative_efficiency nan"); #endif if (bp->radiative_efficiency < 1.e-10f) bp->radiative_efficiency = 0.f; double mass_rate = 0.; - const double luminosity = + const double luminosity = bp->radiative_efficiency * bp->accretion_rate * c * c; - /* Factor in the radiative efficiency, don't subtract + /* Factor in the radiative efficiency, don't subtract * jet BZ efficiency (spin is fixed) */ mass_rate = (1. - bp->radiative_efficiency) * bp->accretion_rate; @@ -1395,8 +1387,8 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( /* Integrate forward in time */ double delta_mass = mass_rate * dt; - /* If desired we put mass into accretion disk which feeds BH on some - * frac of tdyn + /* If desired we put mass into accretion disk which feeds BH on some + * frac of tdyn */ if (tdyn_inv > 0.f) { /* Add accreted mass into a reservoir representing BH accretion disk */ @@ -1408,8 +1400,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( /* This mass gets removed from the accretion disk */ if (bp->accretion_disk_mass > delta_mass) { bp->accretion_disk_mass -= delta_mass; - } - else { + } else { delta_mass = bp->accretion_disk_mass; bp->accretion_disk_mass = 0.; } @@ -1427,59 +1418,57 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( /* ergs to dump in a kernel-weighted fashion */ if (props->adaf_wind_mass_loading == 0.f) { if (bp->subgrid_mass < my_adaf_mass_limit) { - bp->adaf_energy_to_dump = 0.f; + bp->adaf_energy_to_dump = 0.f; } /*else if (bp->subgrid_mass < 1.5f * my_adaf_mass_limit) { - bp->adaf_energy_to_dump *= + bp->adaf_energy_to_dump *= 4.f * powf(bp->subgrid_mass / my_adaf_mass_limit - 1.f, 2.f); }*/ else { - bp->adaf_energy_to_dump = - get_black_hole_coupling(bp, props, cosmo, phys_const) * + bp->adaf_energy_to_dump = + get_black_hole_coupling(bp, props, cosmo, phys_const) * props->adaf_disk_efficiency * bp->accretion_rate * c * c * dt; } - } - else { + } else { const float adaf_v2 = props->adaf_wind_speed * props->adaf_wind_speed; - const float mass_this_step = + const float mass_this_step = props->adaf_wind_mass_loading * bp->accretion_rate * dt; bp->adaf_energy_to_dump += 0.5f * mass_this_step * adaf_v2; } } - if (bp->state == BH_states_adaf || - (props->slim_disk_jet_active && bp->state == BH_states_slim_disk)) { - + if (bp->state == BH_states_adaf || + (props->slim_disk_jet_active && bp->state == BH_states_slim_disk)) { + float jet_velocity = black_hole_compute_jet_velocity(bp, cosmo, props); - /* If there is a variable jet velocity we must recalculate the mass loading */ + /* If there is a variable jet velocity we must recalculate the mass loading + */ if (jet_velocity != props->jet_velocity) { const double c_over_v = phys_const->const_speed_light_c / jet_velocity; if (props->jet_loading_type == BH_jet_momentum_loaded) { bp->jet_mass_loading = props->jet_efficiency * c_over_v; - } - else if (props->jet_loading_type == BH_jet_mixed_loaded) { - const double energy_loading = + } else if (props->jet_loading_type == BH_jet_mixed_loaded) { + const double energy_loading = 2. * props->jet_efficiency * pow(c_over_v, 2.); const double momentum_loading = props->jet_efficiency * c_over_v; /* Divide the contribution between energy and momentum loading */ const double energy_term = props->jet_frac_energy * energy_loading; - const double momentum_term = + const double momentum_term = (1. - props->jet_frac_energy) * momentum_loading; bp->jet_mass_loading = energy_term + momentum_term; - } - else { + } else { bp->jet_mass_loading = 2. * props->jet_efficiency * pow(c_over_v, 2.); } /* Psi_jet*M_dot,acc*dt is the total mass expected in the jet this step */ bp->jet_mass_reservoir += bp->jet_mass_loading * bp->accretion_rate * dt; - } - else { - bp->jet_mass_reservoir += props->jet_mass_loading * bp->accretion_rate * dt; + } else { + bp->jet_mass_reservoir += + props->jet_mass_loading * bp->accretion_rate * dt; } } @@ -1523,106 +1512,89 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( if (bp->state == BH_states_adaf) bp->v_kick = 0.f; #ifdef OBSIDIAN_DEBUG_CHECKS - // MATTHIEU: TODO: FIX THIS! - float galaxy_sfr = 0.; //fof_props->group_star_formation_rate[group_id]; + // MATTHIEU: TODO: FIX THIS! + float galaxy_sfr = 0.; // fof_props->group_star_formation_rate[group_id]; tdyn_inv = (tdyn_inv > 0.f) ? tdyn_inv : FLT_MIN; - message("BH_ACC: z=%g bid=%lld ms=%g dms=%g sfr=%g mbh=%g dmbh=%g state=%d " - "torque=%g bondi=%g fEdd=%g facc=%g fsupp=%g mcold=%g mhot=%g mdisk=%g" - " tin=%g vkick=%g dmass=%g radeff=%g mres=%g tdyn=%g", - cosmo->z, - bp->id, - galaxy_mstar * props->mass_to_solar_mass, - galaxy_sfr * dt * props->mass_to_solar_mass, - galaxy_sfr * props->mass_to_solar_mass / props->time_to_yr, - bp->subgrid_mass * props->mass_to_solar_mass, - delta_mass * props->mass_to_solar_mass, - bp->state, - torque_accr_rate * props->mass_to_solar_mass / props->time_to_yr, - bondi_accr_rate * props->mass_to_solar_mass / props->time_to_yr, - bp->eddington_fraction, - bp->f_accretion, - 1. - exp(-bp->subgrid_mass * props->mass_to_solar_mass / - fabs(props->bh_characteristic_suppression_mass) * cosmo->a), - bp->cold_gas_mass * props->mass_to_solar_mass, - bp->hot_gas_mass * props->mass_to_solar_mass, - corot_gas_mass * props->mass_to_solar_mass, - props->time_to_Myr / tdyn_inv, - bp->v_kick / props->kms_to_internal, - delta_mass, - bp->radiative_efficiency, - bp->accretion_disk_mass, - (1.f / tdyn_inv) * props->time_to_Myr); - - message("BH_STATES: id=%lld, new_state=%d, predicted_mdot_medd=%g, " - "eps_r=%g, f_Edd=%g, f_acc=%g, " - "luminosity=%g, accr_rate=%g Msun/yr, coupling=%g, v_kick=%g km/s, " - "jet_mass_reservoir=%g Msun unresolved_reservoir=%g Msun " - "jet_mass_loading=%g", - bp->id, - bp->state, - predicted_mdot_medd, - bp->radiative_efficiency, - bp->eddington_fraction, - bp->f_accretion, - bp->radiative_luminosity * props->conv_factor_energy_rate_to_cgs, - bp->accretion_rate * props->mass_to_solar_mass / props->time_to_yr, - get_black_hole_coupling(bp, props, cosmo, phys_const), - bp->v_kick / props->kms_to_internal, - bp->jet_mass_reservoir * props->mass_to_solar_mass, - bp->unresolved_mass_reservoir * props->mass_to_solar_mass, - bp->jet_mass_loading); + message( + "BH_ACC: z=%g bid=%lld ms=%g dms=%g sfr=%g mbh=%g dmbh=%g state=%d " + "torque=%g bondi=%g fEdd=%g facc=%g fsupp=%g mcold=%g mhot=%g mdisk=%g" + " tin=%g vkick=%g dmass=%g radeff=%g mres=%g tdyn=%g", + cosmo->z, bp->id, galaxy_mstar * props->mass_to_solar_mass, + galaxy_sfr * dt * props->mass_to_solar_mass, + galaxy_sfr * props->mass_to_solar_mass / props->time_to_yr, + bp->subgrid_mass * props->mass_to_solar_mass, + delta_mass * props->mass_to_solar_mass, bp->state, + torque_accr_rate * props->mass_to_solar_mass / props->time_to_yr, + bondi_accr_rate * props->mass_to_solar_mass / props->time_to_yr, + bp->eddington_fraction, bp->f_accretion, + 1. - exp(-bp->subgrid_mass * props->mass_to_solar_mass / + fabs(props->bh_characteristic_suppression_mass) * cosmo->a), + bp->cold_gas_mass * props->mass_to_solar_mass, + bp->hot_gas_mass * props->mass_to_solar_mass, + corot_gas_mass * props->mass_to_solar_mass, props->time_to_Myr / tdyn_inv, + bp->v_kick / props->kms_to_internal, delta_mass, bp->radiative_efficiency, + bp->accretion_disk_mass, (1.f / tdyn_inv) * props->time_to_Myr); + + message( + "BH_STATES: id=%lld, new_state=%d, predicted_mdot_medd=%g, " + "eps_r=%g, f_Edd=%g, f_acc=%g, " + "luminosity=%g, accr_rate=%g Msun/yr, coupling=%g, v_kick=%g km/s, " + "jet_mass_reservoir=%g Msun unresolved_reservoir=%g Msun " + "jet_mass_loading=%g", + bp->id, bp->state, predicted_mdot_medd, bp->radiative_efficiency, + bp->eddington_fraction, bp->f_accretion, + bp->radiative_luminosity * props->conv_factor_energy_rate_to_cgs, + bp->accretion_rate * props->mass_to_solar_mass / props->time_to_yr, + get_black_hole_coupling(bp, props, cosmo, phys_const), + bp->v_kick / props->kms_to_internal, + bp->jet_mass_reservoir * props->mass_to_solar_mass, + bp->unresolved_mass_reservoir * props->mass_to_solar_mass, + bp->jet_mass_loading); #endif #define OBSIDIAN_BH_DETAILS #ifdef OBSIDIAN_BH_DETAILS - printf("BH_DETAILS " - "z=%2.12f bid=%lld " - " Mdyn=%g MBH=%g Mres=%g BHAR=%g Bondi=%g torque=%g dt=%g " - " nH=%g T=%g SFR=%g mgas=%g " - " mhot=%g m*=%g mgbulge=%g msbulge=%g N/A=%g " - " x=%2.10f y=%2.10f z=%2.10f " - " vx=%2.7f vy=%2.7f vz=%2.7f " - " Lx=%g Ly=%g Lz=%g Lx*=%g Ly*=%g Lz*=%g" - " Lrad=%g state=%d facc=%g eff=%g" - " fedd=%g madaf=%g mngb=%g\n", - cosmo->z, - bp->id, - bp->mass * props->mass_to_solar_mass, - bp->subgrid_mass * props->mass_to_solar_mass, - bp->jet_mass_reservoir * props->mass_to_solar_mass, - bp->accretion_rate * props->mass_to_solar_mass / props->time_to_yr, - Bondi_rate * props->mass_to_solar_mass / props->time_to_yr, - torque_accr_rate * props->mass_to_solar_mass / props->time_to_yr, - dt * props->time_to_Myr, - (bp->rho_gas * cosmo->a3_inv) * props->rho_to_n_cgs, - bp->hot_gas_internal_energy * cosmo->a_factor_internal_energy / - (props->T_K_to_int * props->temp_to_u_factor), - bp->gas_SFR * props->mass_to_solar_mass / props->time_to_yr, - bp->ngb_mass * props->mass_to_solar_mass, - bp->hot_gas_mass * props->mass_to_solar_mass, - bp->stellar_mass * props->mass_to_solar_mass, - 0.f /* Mgas,bulge */, - bp->stellar_bulge_mass * props->mass_to_solar_mass, - 0.f, - bp->x[0] * cosmo->a * props->length_to_parsec / 1.0e3f, - bp->x[1] * cosmo->a * props->length_to_parsec / 1.0e3f, - bp->x[2] * cosmo->a * props->length_to_parsec / 1.0e3f, - bp->v[0] * cosmo->a_inv / props->kms_to_internal, - bp->v[1] * cosmo->a_inv / props->kms_to_internal, - bp->v[2] * cosmo->a_inv / props->kms_to_internal, - bp->angular_momentum_gas[0], - bp->angular_momentum_gas[1], - bp->angular_momentum_gas[2], - 0.f, /* specific angular momentum of the stars */ - 0.f, /* specific angular momentum of the stars */ - 0.f, /* specific angular momentum of the stars */ - bp->radiative_luminosity * props->conv_factor_energy_rate_to_cgs, - bp->state, - bp->f_accretion, - bp->radiative_efficiency, - bp->eddington_fraction, - my_adaf_mass_limit * props->mass_to_solar_mass, - bp->gravitational_ngb_mass * props->mass_to_solar_mass); + printf( + "BH_DETAILS " + "z=%2.12f bid=%lld " + " Mdyn=%g MBH=%g Mres=%g BHAR=%g Bondi=%g torque=%g dt=%g " + " nH=%g T=%g SFR=%g mgas=%g " + " mhot=%g m*=%g mgbulge=%g msbulge=%g N/A=%g " + " x=%2.10f y=%2.10f z=%2.10f " + " vx=%2.7f vy=%2.7f vz=%2.7f " + " Lx=%g Ly=%g Lz=%g Lx*=%g Ly*=%g Lz*=%g" + " Lrad=%g state=%d facc=%g eff=%g" + " fedd=%g madaf=%g mngb=%g\n", + cosmo->z, bp->id, bp->mass * props->mass_to_solar_mass, + bp->subgrid_mass * props->mass_to_solar_mass, + bp->jet_mass_reservoir * props->mass_to_solar_mass, + bp->accretion_rate * props->mass_to_solar_mass / props->time_to_yr, + Bondi_rate * props->mass_to_solar_mass / props->time_to_yr, + torque_accr_rate * props->mass_to_solar_mass / props->time_to_yr, + dt * props->time_to_Myr, + (bp->rho_gas * cosmo->a3_inv) * props->rho_to_n_cgs, + bp->hot_gas_internal_energy * cosmo->a_factor_internal_energy / + (props->T_K_to_int * props->temp_to_u_factor), + bp->gas_SFR * props->mass_to_solar_mass / props->time_to_yr, + bp->ngb_mass * props->mass_to_solar_mass, + bp->hot_gas_mass * props->mass_to_solar_mass, + bp->stellar_mass * props->mass_to_solar_mass, 0.f /* Mgas,bulge */, + bp->stellar_bulge_mass * props->mass_to_solar_mass, 0.f, + bp->x[0] * cosmo->a * props->length_to_parsec / 1.0e3f, + bp->x[1] * cosmo->a * props->length_to_parsec / 1.0e3f, + bp->x[2] * cosmo->a * props->length_to_parsec / 1.0e3f, + bp->v[0] * cosmo->a_inv / props->kms_to_internal, + bp->v[1] * cosmo->a_inv / props->kms_to_internal, + bp->v[2] * cosmo->a_inv / props->kms_to_internal, + bp->angular_momentum_gas[0], bp->angular_momentum_gas[1], + bp->angular_momentum_gas[2], + 0.f, /* specific angular momentum of the stars */ + 0.f, /* specific angular momentum of the stars */ + 0.f, /* specific angular momentum of the stars */ + bp->radiative_luminosity * props->conv_factor_energy_rate_to_cgs, + bp->state, bp->f_accretion, bp->radiative_efficiency, + bp->eddington_fraction, my_adaf_mass_limit * props->mass_to_solar_mass, + bp->gravitational_ngb_mass * props->mass_to_solar_mass); #endif } @@ -1638,9 +1610,9 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static double -black_holes_get_repositioning_speed(const struct bpart* restrict bp, - const struct black_holes_props* props, - const struct cosmology* cosmo) { +black_holes_get_repositioning_speed(const struct bpart *restrict bp, + const struct black_holes_props *props, + const struct cosmology *cosmo) { const double n_gas_phys = bp->rho_gas * cosmo->a3_inv * props->rho_to_n_cgs; const double v_repos = @@ -1673,8 +1645,8 @@ black_holes_get_repositioning_speed(const struct bpart* restrict bp, * @param ti_begin The time at the start of the temp */ __attribute__((always_inline)) INLINE static void black_holes_end_reposition( - struct bpart* restrict bp, const struct black_holes_props* props, - const struct phys_const* phys_const, const struct cosmology* cosmo, + struct bpart *restrict bp, const struct black_holes_props *props, + const struct phys_const *phys_const, const struct cosmology *cosmo, const double dt, const integertime_t ti_begin) { /* First check: did we find any eligible neighbour particle to jump to? */ @@ -1777,7 +1749,7 @@ __attribute__((always_inline)) INLINE static void black_holes_end_reposition( * @param bp The particle to act upon */ __attribute__((always_inline)) INLINE static void black_holes_reset_feedback( - struct bpart* restrict bp) { + struct bpart *restrict bp) { #ifdef DEBUG_INTERACTIONS_BLACK_HOLES for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) @@ -1794,7 +1766,7 @@ __attribute__((always_inline)) INLINE static void black_holes_reset_feedback( * @param gp The black hole's #gpart. */ __attribute__((always_inline)) INLINE static void -black_holes_store_potential_in_bpart(struct bpart* bp, const struct gpart* gp) { +black_holes_store_potential_in_bpart(struct bpart *bp, const struct gpart *gp) { #ifdef SWIFT_DEBUG_CHECKS if (bp->gpart != gp) error("Copying potential to the wrong black hole!"); @@ -1811,8 +1783,8 @@ black_holes_store_potential_in_bpart(struct bpart* bp, const struct gpart* gp) { * @param gp The black hole's #gpart. */ __attribute__((always_inline)) INLINE static void -black_holes_store_potential_in_part(struct black_holes_part_data* p_data, - const struct gpart* gp) { +black_holes_store_potential_in_part(struct black_holes_part_data *p_data, + const struct gpart *gp) { p_data->potential = gp->potential; } @@ -1827,9 +1799,9 @@ black_holes_store_potential_in_part(struct black_holes_part_data* p_data, * @param xp The #xpart that became a black hole. */ INLINE static void black_holes_create_from_gas( - struct bpart* bp, const struct black_holes_props* props, - const struct phys_const* phys_const, const struct cosmology* cosmo, - const struct part* p, const struct xpart* xp, + struct bpart *bp, const struct black_holes_props *props, + const struct phys_const *phys_const, const struct cosmology *cosmo, + const struct part *p, const struct xpart *xp, const integertime_t ti_current) { /* All the non-basic properties of the black hole have been zeroed @@ -1866,8 +1838,8 @@ INLINE static void black_holes_create_from_gas( /* Initial metal masses */ const float gas_mass = hydro_get_mass(p); - struct chemistry_bpart_data* bp_chem = &bp->chemistry_data; - const struct chemistry_part_data* p_chem = &p->chemistry_data; + struct chemistry_bpart_data *bp_chem = &bp->chemistry_data; + const struct chemistry_part_data *p_chem = &p->chemistry_data; chemistry_bpart_from_part(bp_chem, p_chem, gas_mass); /* No swallowed angular momentum */ @@ -1883,9 +1855,8 @@ INLINE static void black_holes_create_from_gas( black_holes_init_bpart(bp); bp->state = BH_states_slim_disk; - - black_holes_mark_bpart_as_not_swallowed(&bp->merger_data); + black_holes_mark_bpart_as_not_swallowed(&bp->merger_data); } #endif /* SWIFT_OBSIDIAN_BLACK_HOLES_H */ diff --git a/src/black_holes/Obsidian/black_holes_debug.h b/src/black_holes/Obsidian/black_holes_debug.h index 0fe0e6824e..5b97fc5c33 100644 --- a/src/black_holes/Obsidian/black_holes_debug.h +++ b/src/black_holes/Obsidian/black_holes_debug.h @@ -21,7 +21,7 @@ #define SWIFT_BLACK_HOLES_OBSIDIAN_DEBUG_H __attribute__((always_inline)) INLINE static void black_holes_debug_particle( - const struct part* p, const struct xpart* xp) { + const struct part *p, const struct xpart *xp) { warning("[PID%lld] black_holes_part_data:", p->id); warning("[PID%lld] swallow_id = %lld, potential = %.3e", p->id, diff --git a/src/black_holes/Obsidian/black_holes_iact.h b/src/black_holes/Obsidian/black_holes_iact.h index d5ba074963..29f40ea374 100644 --- a/src/black_holes/Obsidian/black_holes_iact.h +++ b/src/black_holes/Obsidian/black_holes_iact.h @@ -34,7 +34,7 @@ #include "timestep_sync_part.h" #include "tracers.h" -//#define OBSIDIAN_DEBUG_CHECKS +// #define OBSIDIAN_DEBUG_CHECKS /** * @brief Set direction of black hole feedback kick @@ -46,20 +46,18 @@ * @param dir Direction of kick (returned). */ __attribute__((always_inline)) INLINE static float -black_hole_set_kick_direction( - const struct bpart *bi, const struct part *pj, - const integertime_t ti_current, - const int dir_flag, float *dir) { +black_hole_set_kick_direction(const struct bpart *bi, const struct part *pj, + const integertime_t ti_current, + const int dir_flag, float *dir) { float kick_dir = 1.f; double random_number = 1.; switch (dir_flag) { /* Isotropic */ - case 0: - { - const double random_for_theta = + case 0: { + const double random_for_theta = random_unit_interval(bi->id, ti_current, random_number_BH_feedback); - const double random_for_phi = + const double random_for_phi = random_unit_interval(bi->id, ti_current, random_number_BH_feedback); const float theta = acosf(2.f * random_for_theta - 1.f); @@ -76,21 +74,21 @@ black_hole_set_kick_direction( dir[0] = bi->angular_momentum_gas[0]; dir[1] = bi->angular_momentum_gas[1]; dir[2] = bi->angular_momentum_gas[2]; - random_number = - random_unit_interval(bi->id, ti_current, random_number_BH_feedback); + random_number = + random_unit_interval(bi->id, ti_current, random_number_BH_feedback); kick_dir = (random_number > 0.5) ? 1.f : -1.f; break; /* Along the accreted angular momentum of the gas */ case 2: - dir[0] = + dir[0] = bi->accreted_angular_momentum[0] + bi->swallowed_angular_momentum[0]; - dir[1] = + dir[1] = bi->accreted_angular_momentum[1] + bi->swallowed_angular_momentum[1]; - dir[2] = + dir[2] = bi->accreted_angular_momentum[2] + bi->swallowed_angular_momentum[2]; - random_number = - random_unit_interval(bi->id, ti_current, random_number_BH_feedback); + random_number = + random_unit_interval(bi->id, ti_current, random_number_BH_feedback); kick_dir = (random_number > 0.5) ? 1.f : -1.f; break; @@ -138,7 +136,7 @@ runner_iact_nonsym_bh_gas_density( /* Get the total gravitationally interacting mass within the kernel */ const float mj = hydro_get_mass(pj); - + /* Compute total mass that contributes to the dynamical time */ bi->gravitational_ngb_mass += mj; bi->num_gravitational_ngbs += 1; @@ -181,8 +179,7 @@ runner_iact_nonsym_bh_gas_density( /* Neighbour's (drifted) velocity in the frame of the black hole * (we don't include a Hubble term since we are interested in the * velocity contribution at the location of the black hole) */ - const float dv[3] = {pj->v[0] - bi->v[0], - pj->v[1] - bi->v[1], + const float dv[3] = {pj->v[0] - bi->v[0], pj->v[1] - bi->v[1], pj->v[2] - bi->v[2]}; /* Account for hot and cold gas surrounding the SMBH */ @@ -194,7 +191,7 @@ runner_iact_nonsym_bh_gas_density( const double rho_com = hydro_get_comoving_density(pj); const double rho_phys = hydro_get_physical_density(pj, cosmo); const float T_EoS = entropy_floor_temperature(pj, cosmo, floor_props); - if ((rho_com >= rho_crit_baryon * floor_props->Jeans_over_density_threshold) + if ((rho_com >= rho_crit_baryon * floor_props->Jeans_over_density_threshold) && (rho_phys >= floor_props->Jeans_density_threshold)) { if (Tj > T_EoS * bh_props->fixed_T_above_EoS_factor) { is_hot_gas = 1; @@ -212,8 +209,7 @@ runner_iact_nonsym_bh_gas_density( if (is_hot_gas) { bi->hot_gas_mass += mj; bi->hot_gas_internal_energy += mj * uj; /* Not kernel weighted */ - } - else { + } else { bi->cold_gas_mass += mj; bi->gas_SFR += max(pj->sf_data.SFR, 0.); } @@ -246,7 +242,6 @@ runner_iact_nonsym_bh_gas_density( /* Update ngb counters */ ++si->num_ngb_density; #endif - } /** @@ -415,8 +410,9 @@ runner_iact_nonsym_bh_gas_swallow( const float r = sqrtf(r2); const float dv[3] = {bi->v[0] - pj->v[0], bi->v[1] - pj->v[1], bi->v[2] - pj->v[2]}; - const float u_kinetic = 0.5 * (dv[0]*dv[0] + dv[1]*dv[1] + dv[2]*dv[2]); - const float u_potential = bh_props->const_newton_G * (bi->ngb_mass + bi->mass) / r; + const float u_kinetic = 0.5 * (dv[0] * dv[0] + dv[1] * dv[1] + dv[2] * dv[2]); + const float u_potential = + bh_props->const_newton_G * (bi->ngb_mass + bi->mass) / r; if (u_kinetic > u_potential) return; @@ -443,23 +439,23 @@ runner_iact_nonsym_bh_gas_swallow( const float Lx = mj * (dx[1] * dv[2] - dx[2] * dv[1]); const float Ly = mj * (dx[2] * dv[0] - dx[0] * dv[2]); const float Lz = mj * (dx[0] * dv[1] - dx[1] * dv[0]); - const float proj = Lx * bi->angular_momentum_gas[0] - + Ly * bi->angular_momentum_gas[1] - + Lz * bi->angular_momentum_gas[2]; + const float proj = Lx * bi->angular_momentum_gas[0] + + Ly * bi->angular_momentum_gas[1] + + Lz * bi->angular_momentum_gas[2]; if ((proj > 0.f) && (is_hot_gas == 0)) { bi->cold_disk_mass += mj; } - + /* Probability to swallow this particle */ float prob = -1.f; float f_accretion = bi->f_accretion; if (f_accretion <= 0.f) return; - + const float pj_mass_orig = mj; const float nibbled_mass = f_accretion * pj_mass_orig; /* Normalize the weights */ - const float kernel_wt = + const float kernel_wt = (bi->kernel_wt_sum > 0.f) ? wi / bi->kernel_wt_sum : 0.f; /* Radiation was already accounted for in bi->subgrid_mass @@ -479,8 +475,7 @@ runner_iact_nonsym_bh_gas_swallow( /* Just enough to satisfy M_dot,inflow */ prob = (mass_deficit / f_accretion) * kernel_wt; - } - else { + } else { /* Do not grow the physical mass, only kick */ f_accretion = 0.f; @@ -536,7 +531,7 @@ runner_iact_nonsym_bh_gas_swallow( } /* This particle is swallowed by the BH with the largest ID of all the - * candidates wanting to swallow it */ + * candidates wanting to swallow it */ if (pj->black_holes_data.swallow_id < bi->id) { /* Handle the ADAF heating separately */ if (bi->state != BH_states_adaf) { @@ -547,8 +542,7 @@ runner_iact_nonsym_bh_gas_swallow( if (mass_deficit <= 0.f) { bi->unresolved_mass_kicked_this_step += new_gas_mass; } - } - else { + } else { message( "BH %lld wants to swallow gas particle %lld BUT CANNOT (old " "swallow id=%lld)", @@ -559,7 +553,7 @@ runner_iact_nonsym_bh_gas_swallow( /* When there is zero mass loading the ADAF mode heats the entire kernel * so all of the weights are required in the sum. */ if (bi->state == BH_states_adaf) { - /* Zero mass loading implies entire + /* Zero mass loading implies entire kernel heating */ if (bh_props->adaf_wind_mass_loading == 0.f) { const float adaf_wt = new_gas_mass * wi; @@ -567,13 +561,12 @@ runner_iact_nonsym_bh_gas_swallow( /* Normalized later */ bi->adaf_energy_used_this_step += bi->adaf_energy_to_dump * adaf_wt; - } - else { + } else { /* --- The entire kernel is NOT heated here ---- */ /* Heating is based on specific energy and the mass loading */ if (bi->adaf_energy_to_dump > 0.f && bh_props->adaf_wind_speed > 0.f) { - const float adaf_v2 = + const float adaf_v2 = bh_props->adaf_wind_speed * bh_props->adaf_wind_speed; const float adaf_mass_to_heat = 2.f * bi->adaf_energy_to_dump / adaf_v2; @@ -581,8 +574,8 @@ runner_iact_nonsym_bh_gas_swallow( const float adaf_heat_prob = adaf_mass_to_heat * kernel_wt; /* Draw a random number (Note mixing both IDs) */ - const float adaf_rand = random_unit_interval(bi->id + pj->id, ti_current, - random_number_BH_swallow); + const float adaf_rand = random_unit_interval( + bi->id + pj->id, ti_current, random_number_BH_swallow); /* Identify ADAF heating particles by their ID, and only heat those! */ if (adaf_rand < adaf_heat_prob) { @@ -595,8 +588,7 @@ runner_iact_nonsym_bh_gas_swallow( bi->adaf_wt_sum += adaf_wt; /* Will be normalized at the end when reducing the reservoir */ bi->adaf_energy_used_this_step += bi->adaf_energy_to_dump * adaf_wt; - } - else { + } else { message( "BH %lld wants to heat particle %lld BUT CANNOT (old " "adaf_id=%lld)", @@ -618,8 +610,8 @@ runner_iact_nonsym_bh_gas_swallow( const float rand_jet = random_unit_interval(bi->id + pj->id, ti_current, random_number_BH_kick); /* Always kick if above luminosity threshold */ - if (bi->radiative_luminosity > bh_props->lum_thresh_always_jet - && bh_props->lum_thresh_always_jet > 0.f) { + if (bi->radiative_luminosity > bh_props->lum_thresh_always_jet && + bh_props->lum_thresh_always_jet > 0.f) { jet_prob = 1.f; } @@ -632,8 +624,7 @@ runner_iact_nonsym_bh_gas_swallow( if (pj->black_holes_data.jet_id < bi->id) { bi->jet_mass_kicked_this_step += new_gas_mass; pj->black_holes_data.jet_id = bi->id; - } - else { + } else { message( "BH %lld wants to kick jet particle %lld BUT CANNOT (old " "jet_id=%lld)", @@ -895,13 +886,14 @@ runner_iact_nonsym_bh_gas_feedback( const struct entropy_floor_properties *floor_props, const integertime_t ti_current, const double time) { - /* Gas particle must be bound to BH kernel mass to have feedback + /* Gas particle must be bound to BH kernel mass to have feedback * (avoids fast-moving decoupled particles) */ const float r = sqrtf(r2); const float dv[3] = {bi->v[0] - pj->v[0], bi->v[1] - pj->v[1], bi->v[2] - pj->v[2]}; - const float u_kinetic = 0.5 * (dv[0]*dv[0] + dv[1]*dv[1] + dv[2]*dv[2]); - const float u_potential = bh_props->const_newton_G * (bi->ngb_mass + bi->mass) / r; + const float u_kinetic = 0.5 * (dv[0] * dv[0] + dv[1] * dv[1] + dv[2] * dv[2]); + const float u_potential = + bh_props->const_newton_G * (bi->ngb_mass + bi->mass) / r; if (u_kinetic > u_potential) return; @@ -916,33 +908,30 @@ runner_iact_nonsym_bh_gas_feedback( /* Need time-step for decoupling and ADAF heating */ double dt; - if (with_cosmology) { + if (with_cosmology) { const integertime_t ti_step = get_integer_timestep(bi->time_bin); const integertime_t ti_begin = - get_integer_time_begin(ti_current - 1, bi->time_bin); + get_integer_time_begin(ti_current - 1, bi->time_bin); dt = cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); - } - else { + } else { error("Kiara BH model can only be run with cosmology."); } /* Save gas density and entropy before feedback */ tracers_before_black_holes_feedback(pj, xpj, cosmo->a); - float v_kick = bi->v_kick; /* PHYSICAL */ + float v_kick = bi->v_kick; /* PHYSICAL */ - const float bh_mass_msun = - bi->subgrid_mass * bh_props->mass_to_solar_mass; + const float bh_mass_msun = bi->subgrid_mass * bh_props->mass_to_solar_mass; /* by-eye fit from Fig 6 of Zhang+2023 (2305.06803) */ - double halo_mass = 1.e12 * pow(bh_mass_msun * 1.e-7, 0.75); + double halo_mass = 1.e12 * pow(bh_mass_msun * 1.e-7, 0.75); if (halo_mass < 6.3e11) halo_mass = 6.3e11; /* In internal temperature units */ - const double T_vir = - 9.52e7 * pow(halo_mass * 1.e-15, 0.6666) * - bh_props->T_K_to_int * (1. + cosmo->z); + const double T_vir = 9.52e7 * pow(halo_mass * 1.e-15, 0.6666) * + bh_props->T_K_to_int * (1. + cosmo->z); /* In the swallow loop the particle was marked as a kick particle */ const int swallow_flag = (pj->black_holes_data.swallow_id == bi->id); @@ -957,7 +946,7 @@ runner_iact_nonsym_bh_gas_feedback( double u_new = u_init; double T_new = u_new / bh_props->temp_to_u_factor; - int adaf_energy_flag = + int adaf_energy_flag = (bi->state == BH_states_adaf && bi->adaf_energy_to_dump > 0.f); int adaf_heat_flag = (bi->state == BH_states_adaf && pj->black_holes_data.adaf_id == bi->id); @@ -966,8 +955,7 @@ runner_iact_nonsym_bh_gas_feedback( * to be heated to satisfy M_dot,ADAF = psi_ADAF * M_dot,acc */ if (bh_props->adaf_wind_mass_loading > 0.f) { adaf_heat_flag = (adaf_energy_flag && adaf_heat_flag); - } - else { + } else { /* In this case, all of the kernel is heated with adaf_energy_to_dump */ adaf_heat_flag = adaf_energy_flag; } @@ -986,10 +974,10 @@ runner_iact_nonsym_bh_gas_feedback( kernel_eval(sqrtf(r2) / hi, &wj); const float mj = hydro_get_mass(pj); - /* Below is equivalent to + /* Below is equivalent to * E_inject_i = E_ADAF * (w_j * m_j) / Sum(w_i * mi) */ E_inject = bi->adaf_energy_to_dump * mj * wj / bi->adaf_wt_sum; - + /* Initialise heat energy to injection energy */ E_heat = E_inject; @@ -1000,16 +988,16 @@ runner_iact_nonsym_bh_gas_feedback( hydro_get_physical_density(pj, cosmo) * bh_props->rho_to_n_cgs; const double T_gas_cgs = u_init / (bh_props->temp_to_u_factor * bh_props->T_K_to_int); - const double T_EoS_cgs = - entropy_floor_temperature(pj, cosmo, floor_props) / - bh_props->T_K_to_int; + const double T_EoS_cgs = + entropy_floor_temperature(pj, cosmo, floor_props) / + bh_props->T_K_to_int; /* Check whether we are close to the entropy floor or SF/ing. If we are, * we classify the gas as cold regardless of temperature. */ if ((n_H_cgs > bh_props->adaf_heating_n_H_threshold_cgs && - (T_gas_cgs < bh_props->adaf_heating_T_threshold_cgs || - T_gas_cgs < T_EoS_cgs * bh_props->fixed_T_above_EoS_factor)) || - pj->sf_data.SFR > 0.f) { + (T_gas_cgs < bh_props->adaf_heating_T_threshold_cgs || + T_gas_cgs < T_EoS_cgs * bh_props->fixed_T_above_EoS_factor)) || + pj->sf_data.SFR > 0.f) { /* Kick with some fraction of the energy, if desired */ if (bh_props->adaf_kick_factor > 0.f) { @@ -1018,7 +1006,7 @@ runner_iact_nonsym_bh_gas_feedback( double E_kick = bh_props->adaf_kick_factor * E_inject; v_kick = sqrt(2. * E_kick / mj); - /* Apply ramp-up in kick velocity above ADAF mass limit */ + /* Apply ramp-up in kick velocity above ADAF mass limit */ const float adaf_max_speed = bh_props->adaf_wind_speed * sqrtf(jet_ramp); @@ -1047,17 +1035,15 @@ runner_iact_nonsym_bh_gas_feedback( /* New temperature */ T_new = u_new / bh_props->temp_to_u_factor; - /* Limit heating. There can sometimes be VERY large amounts of + /* Limit heating. There can sometimes be VERY large amounts of * energy to deposit */ if (bh_props->adaf_maximum_temperature > 0.f) { if (T_new > bh_props->adaf_maximum_temperature) { - u_new = + u_new = bh_props->adaf_maximum_temperature * bh_props->temp_to_u_factor; } - } - else { - const float T_max = - fabs(bh_props->adaf_maximum_temperature) * T_vir; + } else { + const float T_max = fabs(bh_props->adaf_maximum_temperature) * T_vir; if (T_new > T_max) { u_new = T_max * bh_props->temp_to_u_factor; T_new = T_max; @@ -1067,7 +1053,7 @@ runner_iact_nonsym_bh_gas_feedback( /* Reset in case clipped at the upper limit */ E_heat = (u_new - u_init) * mj; - /* Heat particle: We are overwriting the internal energy of the + /* Heat particle: We are overwriting the internal energy of the * particle */ hydro_set_physical_internal_energy(pj, xpj, cosmo, u_new); hydro_set_drifted_physical_internal_energy(pj, cosmo, NULL, u_new); @@ -1085,11 +1071,11 @@ runner_iact_nonsym_bh_gas_feedback( /* a_factor_sound_speed converts cs_physical to comoving units, * twice the BH timestep as a lower limit */ - pj->feedback_data.cooling_shutoff_delay_time = - bh_props->adaf_cooling_shutoff_factor * min(dt_sound_phys, dt); + pj->feedback_data.cooling_shutoff_delay_time = + bh_props->adaf_cooling_shutoff_factor * min(dt_sound_phys, dt); } - } /* E_heat > 0 */ + } /* E_heat > 0 */ } /* E_inject > 0 */ @@ -1138,7 +1124,7 @@ runner_iact_nonsym_bh_gas_feedback( /* Flagged if it is a jet particle, marked to swallow (i.e. kick) or * if there was an ADAF kick because of energy splitting. */ - int flagged_to_kick = + int flagged_to_kick = jet_flag || (swallow_flag && !adaf_heat_flag) || adaf_kick_flag; /* Kick the particle if is was tagged only */ @@ -1148,68 +1134,60 @@ runner_iact_nonsym_bh_gas_feedback( float dir[3] = {0.f, 0.f, 0.f}; int dir_flag = 0; if (jet_flag) { - dir_flag = bh_props->jet_launch_dir; - } - else if (adaf_heat_flag) { - dir_flag = bh_props->adaf_wind_dir; - } - else if (bi->state == BH_states_quasar) { - dir_flag = bh_props->quasar_wind_dir; - if (bi->radiative_luminosity > bh_props->quasar_luminosity_thresh && bh_props->quasar_luminosity_thresh > 0.f) { - dir_flag = 3; // outwards blowout above threshold luminosity + dir_flag = bh_props->jet_launch_dir; + } else if (adaf_heat_flag) { + dir_flag = bh_props->adaf_wind_dir; + } else if (bi->state == BH_states_quasar) { + dir_flag = bh_props->quasar_wind_dir; + if (bi->radiative_luminosity > bh_props->quasar_luminosity_thresh && + bh_props->quasar_luminosity_thresh > 0.f) { + dir_flag = 3; // outwards blowout above threshold luminosity } - } - else if (bi->state == BH_states_slim_disk) { - dir_flag = bh_props->slim_disk_wind_dir; - } - else { - warning("Cannot determine wind direction (BH state=%d) for v_kick=%g, setting to random", bi->state, v_kick / bh_props->kms_to_internal); + } else if (bi->state == BH_states_slim_disk) { + dir_flag = bh_props->slim_disk_wind_dir; + } else { + warning( + "Cannot determine wind direction (BH state=%d) for v_kick=%g, " + "setting to random", + bi->state, v_kick / bh_props->kms_to_internal); } - float dirsign = + float dirsign = black_hole_set_kick_direction(bi, pj, ti_current, dir_flag, dir); /* Do the kick */ - const float norm = + const float norm = sqrtf(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]); - + if (norm > 0.f) { const float prefactor = v_kick * cosmo->a * dirsign / norm; #ifdef OBSIDIAN_DEBUG_CHECKS - pj_vel_norm = sqrtf( - xpj->v_full[0] * xpj->v_full[0] + - xpj->v_full[1] * xpj->v_full[1] + - xpj->v_full[2] * xpj->v_full[2] - ); + pj_vel_norm = sqrtf(xpj->v_full[0] * xpj->v_full[0] + + xpj->v_full[1] * xpj->v_full[1] + + xpj->v_full[2] * xpj->v_full[2]); #endif - xpj->v_full[0] += prefactor * dir[0]; - xpj->v_full[1] += prefactor * dir[1]; - xpj->v_full[2] += prefactor * dir[2]; + xpj->v_full[0] += prefactor * dir[0]; + xpj->v_full[1] += prefactor * dir[1]; + xpj->v_full[2] += prefactor * dir[2]; #ifdef OBSIDIAN_DEBUG_CHECKS - const float v_mag = sqrtf(xpj->v_full[0] * xpj->v_full[0] + - xpj->v_full[1] * xpj->v_full[1] + + const float v_mag = sqrtf(xpj->v_full[0] * xpj->v_full[0] + + xpj->v_full[1] * xpj->v_full[1] + xpj->v_full[2] * xpj->v_full[2]); if (prefactor * norm > 1.e3 * v_mag) { - warning("LARGE KICK! z=%g id=%lld dv=%g vkick=%g vadaf=%g vjet=%g v=%g " - "(%g,%g,%g) dir=%g,%g,%g", - cosmo->z, - pj->id, - prefactor, - v_kick, - bh_props->adaf_wind_speed, - bh_props->jet_velocity, - v_mag, - xpj->v_full[0], - xpj->v_full[1], - xpj->v_full[2], dir[0], dir[1], dir[2]); + warning( + "LARGE KICK! z=%g id=%lld dv=%g vkick=%g vadaf=%g vjet=%g v=%g " + "(%g,%g,%g) dir=%g,%g,%g", + cosmo->z, pj->id, prefactor, v_kick, bh_props->adaf_wind_speed, + bh_props->jet_velocity, v_mag, xpj->v_full[0], xpj->v_full[1], + xpj->v_full[2], dir[0], dir[1], dir[2]); } #endif - /* Update the signal velocity of the particle based + /* Update the signal velocity of the particle based * on the PHYSICAL velocity kick. */ hydro_set_v_sig_based_on_velocity_kick(pj, cosmo, v_kick); pj->chemistry_data.diffusion_coefficient = 0.f; @@ -1228,8 +1206,7 @@ runner_iact_nonsym_bh_gas_feedback( } /* Hubble time in internal units */ - const double t_H = - cosmology_get_time_since_big_bang(cosmo, cosmo->a); + const double t_H = cosmology_get_time_since_big_bang(cosmo, cosmo->a); /* Set delay time to at least the time-step*/ pj->feedback_data.decoupling_delay_time = dt + f_decouple * t_H; @@ -1240,27 +1217,23 @@ runner_iact_nonsym_bh_gas_feedback( if (bh_props->jet_decouple_time_factor > 0.f) { pj->feedback_data.decoupling_delay_time = dt + bh_props->jet_decouple_time_factor * t_H; - } - else { + } else { pj->feedback_data.decoupling_delay_time = 0.f; } pj->feedback_data.number_of_times_decoupled += 100000; - } - else { - if ((bh_props->slim_disk_decouple_time_factor > 0.f && - bi->state == BH_states_slim_disk) || + } else { + if ((bh_props->slim_disk_decouple_time_factor > 0.f && + bi->state == BH_states_slim_disk) || (bh_props->quasar_decouple_time_factor > 0.f && - bi->state == BH_states_quasar)) { + bi->state == BH_states_quasar)) { pj->feedback_data.decoupling_delay_time = dt + bh_props->slim_disk_decouple_time_factor * t_H; - } - else { + } else { pj->feedback_data.decoupling_delay_time = 0.f; } pj->feedback_data.number_of_times_decoupled += 1000; } - } - else { + } else { v_kick = 0.f; } } @@ -1276,11 +1249,11 @@ runner_iact_nonsym_bh_gas_feedback( } else { pj->sf_data.SFR = -time; } - } + } /* Destroy all H2 and put into HI */ - xpj->cooling_data.HI_frac += xpj->cooling_data.HM_frac + - xpj->cooling_data.H2I_frac + + xpj->cooling_data.HI_frac += xpj->cooling_data.HM_frac + + xpj->cooling_data.H2I_frac + xpj->cooling_data.H2II_frac; xpj->cooling_data.HM_frac = 0.f; xpj->cooling_data.H2I_frac = 0.f; @@ -1301,14 +1274,14 @@ runner_iact_nonsym_bh_gas_feedback( pj->cooling_data.dust_mass = 0.f; float new_Z_total = 0.f; pj->chemistry_data.metal_mass_fraction_total = 0.f; - for (int elem = chemistry_element_He; - elem < chemistry_element_count; ++elem) { - const float old_metal_mass_elem = + for (int elem = chemistry_element_He; elem < chemistry_element_count; + ++elem) { + const float old_metal_mass_elem = pj->chemistry_data.metal_mass_fraction[elem] * hydro_get_mass(pj); const float old_dust_mass_elem = pj->cooling_data.dust_mass_fraction[elem] * old_dust_mass; - pj->chemistry_data.metal_mass_fraction[elem] = + pj->chemistry_data.metal_mass_fraction[elem] = (old_metal_mass_elem + old_dust_mass_elem) / hydro_get_mass(pj); if (elem != chemistry_element_H && elem != chemistry_element_He) { @@ -1324,83 +1297,69 @@ runner_iact_nonsym_bh_gas_feedback( /* Impose maximal viscosity */ hydro_diffusive_feedback_reset(pj); - + /* Synchronize the particle on the timeline */ timestep_sync_part(pj); #ifdef OBSIDIAN_DEBUG_CHECKS if (E_heat > 0.f) { - message("BH_HEAT_ADAF: z=%g bid=%lld pid=%lld mbh=%g Msun u=%g T=%g K " - "Tvir=%g K", - cosmo->z, - bi->id, - pj->id, - bh_mass_msun, - pj->u, - T_new / bh_props->T_K_to_int, - T_vir / bh_props->T_K_to_int); + message( + "BH_HEAT_ADAF: z=%g bid=%lld pid=%lld mbh=%g Msun u=%g T=%g K " + "Tvir=%g K", + cosmo->z, bi->id, pj->id, bh_mass_msun, pj->u, + T_new / bh_props->T_K_to_int, T_vir / bh_props->T_K_to_int); } switch (bi->state) { case BH_states_quasar: - message("BH_KICK_QSO: z=%g bid=%lld mbh=%g Msun v_kick=%g km/s " - "v_kick/v_part=%g T=%g K", - cosmo->z, - bi->id, - bh_mass_msun, - v_kick / bh_props->kms_to_internal, - v_kick * cosmo->a / pj_vel_norm, - hydro_get_physical_internal_energy(pj, xpj, cosmo) / - (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); + message( + "BH_KICK_QSO: z=%g bid=%lld mbh=%g Msun v_kick=%g km/s " + "v_kick/v_part=%g T=%g K", + cosmo->z, bi->id, bh_mass_msun, v_kick / bh_props->kms_to_internal, + v_kick * cosmo->a / pj_vel_norm, + hydro_get_physical_internal_energy(pj, xpj, cosmo) / + (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); break; case BH_states_slim_disk: message("BH_KICK_SLIM: z=%g bid=%lld mbh=%g Msun v_kick=%g km/s T=%g K", - cosmo->z, - bi->id, - bh_mass_msun, - v_kick / bh_props->kms_to_internal, - hydro_get_physical_internal_energy(pj, xpj, cosmo) / - (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); + cosmo->z, bi->id, bh_mass_msun, + v_kick / bh_props->kms_to_internal, + hydro_get_physical_internal_energy(pj, xpj, cosmo) / + (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); break; case BH_states_adaf: if (jet_flag) { - message("BH_KICK_JET: z=%g bid=%lld mbh=%g Msun v_kick=%g km/s " - "v_kick/v_part=%g T=%g", - cosmo->z, - bi->id, - bh_mass_msun, - v_kick / bh_props->kms_to_internal, - v_kick * cosmo->a / pj_vel_norm, - hydro_get_physical_internal_energy(pj, xpj, cosmo) / - (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); - } - else { - message("BH_KICK_ADAF: z=%g bid=%lld pid=%lld mbh=%g Msun " - "v_kick=%g km/s " - "v_kick/v_part=%g u=%g T=%g", - cosmo->z, - bi->id, - pj->id, - bh_mass_msun, - v_kick / bh_props->kms_to_internal, - v_kick * cosmo->a / pj_vel_norm, - pj->u, - hydro_get_physical_internal_energy(pj, xpj, cosmo) / - (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); + message( + "BH_KICK_JET: z=%g bid=%lld mbh=%g Msun v_kick=%g km/s " + "v_kick/v_part=%g T=%g", + cosmo->z, bi->id, bh_mass_msun, + v_kick / bh_props->kms_to_internal, + v_kick * cosmo->a / pj_vel_norm, + hydro_get_physical_internal_energy(pj, xpj, cosmo) / + (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); + } else { + message( + "BH_KICK_ADAF: z=%g bid=%lld pid=%lld mbh=%g Msun " + "v_kick=%g km/s " + "v_kick/v_part=%g u=%g T=%g", + cosmo->z, bi->id, pj->id, bh_mass_msun, + v_kick / bh_props->kms_to_internal, + v_kick * cosmo->a / pj_vel_norm, pj->u, + hydro_get_physical_internal_energy(pj, xpj, cosmo) / + (bh_props->T_K_to_int * bh_props->temp_to_u_factor)); } - break; + break; } #endif - } if (swallow_flag) { - /* IMPORTANT: The particle MUST NOT be swallowed. + /* IMPORTANT: The particle MUST NOT be swallowed. * We are taking a f_accretion from each particle, and then * kicking the rest. We used the swallow marker as a temporary * passer in order to remember which particles have been "nibbled" * so that we can kick them out. - */ + */ black_holes_mark_part_as_not_swallowed(&pj->black_holes_data); } } diff --git a/src/black_holes/Obsidian/black_holes_io.h b/src/black_holes/Obsidian/black_holes_io.h index a5b91d64e2..7368aea97a 100644 --- a/src/black_holes/Obsidian/black_holes_io.h +++ b/src/black_holes/Obsidian/black_holes_io.h @@ -33,49 +33,48 @@ * @param list The list of i/o properties to read. * @param num_fields The number of i/o fields to read. */ -INLINE static void black_holes_read_particles(struct bpart* bparts, - struct io_props* list, - int* num_fields) { +INLINE static void black_holes_read_particles(struct bpart *bparts, + struct io_props *list, + int *num_fields) { int num = 0; /* List what we want to read */ list[num] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, - UNIT_CONV_LENGTH, bparts, x); + UNIT_CONV_LENGTH, bparts, x); num++; list[num] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, - UNIT_CONV_SPEED, bparts, v); + UNIT_CONV_SPEED, bparts, v); num++; - list[num] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, - bparts, mass); + list[num] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, + UNIT_CONV_MASS, bparts, mass); num++; list[num] = io_make_input_field("ParticleIDs", LONGLONG, 1, COMPULSORY, - UNIT_CONV_NO_UNITS, bparts, id); + UNIT_CONV_NO_UNITS, bparts, id); num++; list[num] = io_make_input_field("SmoothingLengths", FLOAT, 1, OPTIONAL, - UNIT_CONV_LENGTH, bparts, h); + UNIT_CONV_LENGTH, bparts, h); num++; list[num] = io_make_input_field("JetMassReservoirs", FLOAT, 1, OPTIONAL, - UNIT_CONV_MASS, bparts, jet_mass_reservoir); + UNIT_CONV_MASS, bparts, jet_mass_reservoir); num++; list[num] = io_make_input_field("SubgridMasses", FLOAT, 1, OPTIONAL, - UNIT_CONV_MASS, bparts, subgrid_mass); + UNIT_CONV_MASS, bparts, subgrid_mass); num++; *num_fields = num; - } -INLINE static void convert_bpart_pos(const struct engine* e, - const struct bpart* bp, double* ret) { +INLINE static void convert_bpart_pos(const struct engine *e, + const struct bpart *bp, double *ret) { - const struct space* s = e->s; + const struct space *s = e->s; if (s->periodic) { ret[0] = box_wrap(bp->x[0], 0.0, s->dim[0]); ret[1] = box_wrap(bp->x[1], 0.0, s->dim[1]); @@ -92,11 +91,11 @@ INLINE static void convert_bpart_pos(const struct engine* e, } } -INLINE static void convert_bpart_vel(const struct engine* e, - const struct bpart* bp, float* ret) { +INLINE static void convert_bpart_vel(const struct engine *e, + const struct bpart *bp, float *ret) { const int with_cosmology = (e->policy & engine_policy_cosmology); - const struct cosmology* cosmo = e->cosmology; + const struct cosmology *cosmo = e->cosmology; const integertime_t ti_current = e->ti_current; const double time_base = e->time_base; const float dt_kick_grav_mesh = e->dt_kick_grav_mesh_for_io; @@ -112,7 +111,7 @@ INLINE static void convert_bpart_vel(const struct engine* e, with_cosmology, cosmo); /* Extrapolate the velocites to the current time */ - const struct gpart* gp = bp->gpart; + const struct gpart *gp = bp->gpart; ret[0] = gp->v_full[0] + gp->a_grav[0] * dt_kick_grav; ret[1] = gp->v_full[1] + gp->a_grav[1] * dt_kick_grav; ret[2] = gp->v_full[2] + gp->a_grav[2] * dt_kick_grav; @@ -128,8 +127,8 @@ INLINE static void convert_bpart_vel(const struct engine* e, ret[2] *= cosmo->a_inv; } -INLINE static void convert_bpart_potential(const struct engine* e, - const struct bpart* bp, float* ret) { +INLINE static void convert_bpart_potential(const struct engine *e, + const struct bpart *bp, float *ret) { if (bp->gpart != NULL) ret[0] = gravity_get_comoving_potential(bp->gpart); @@ -137,10 +136,10 @@ INLINE static void convert_bpart_potential(const struct engine* e, ret[0] = 0.f; } -INLINE static void convert_bpart_gas_vel(const struct engine* e, - const struct bpart* bp, float* ret) { +INLINE static void convert_bpart_gas_vel(const struct engine *e, + const struct bpart *bp, float *ret) { - const struct cosmology* cosmo = e->cosmology; + const struct cosmology *cosmo = e->cosmology; /* Convert relative velocities to physical units */ ret[0] = bp->velocity_gas[0] * cosmo->a_inv; @@ -148,11 +147,11 @@ INLINE static void convert_bpart_gas_vel(const struct engine* e, ret[2] = bp->velocity_gas[2] * cosmo->a_inv; } -INLINE static void convert_bpart_gas_circular_vel(const struct engine* e, - const struct bpart* bp, - float* ret) { +INLINE static void convert_bpart_gas_circular_vel(const struct engine *e, + const struct bpart *bp, + float *ret) { - const struct cosmology* cosmo = e->cosmology; + const struct cosmology *cosmo = e->cosmology; /* Conversion from internal to physical units */ ret[0] = bp->circular_velocity_gas[0] * cosmo->a_inv; @@ -160,12 +159,12 @@ INLINE static void convert_bpart_gas_circular_vel(const struct engine* e, ret[2] = bp->circular_velocity_gas[2] * cosmo->a_inv; } -INLINE static void convert_bpart_gas_temperatures(const struct engine* e, - const struct bpart* bp, - float* ret) { +INLINE static void convert_bpart_gas_temperatures(const struct engine *e, + const struct bpart *bp, + float *ret) { - const struct black_holes_props* props = e->black_holes_properties; - const struct cosmology* cosmo = e->cosmology; + const struct black_holes_props *props = e->black_holes_properties; + const struct cosmology *cosmo = e->cosmology; /* Conversion from specific internal energy to temperature */ ret[0] = bp->internal_energy_gas * cosmo->a_factor_internal_energy / @@ -180,9 +179,9 @@ INLINE static void convert_bpart_gas_temperatures(const struct engine* e, * @param num_fields The number of i/o fields to write. * @param with_cosmology Are we running a cosmological simulation? */ -INLINE static void black_holes_write_particles(const struct bpart* bparts, - struct io_props* list, - int* num_fields, +INLINE static void black_holes_write_particles(const struct bpart *bparts, + struct io_props *list, + int *num_fields, int with_cosmology) { int num = 0; @@ -214,9 +213,9 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, "Co-moving smoothing lengths (FWHM of the kernel) of the particles"); num++; - list[num] = io_make_output_field("SubgridMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, - bparts, subgrid_mass, - "Subgrid masses of the particles; this is the actual BH mass"); + list[num] = io_make_output_field( + "SubgridMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts, subgrid_mass, + "Subgrid masses of the particles; this is the actual BH mass"); num++; if (with_cosmology) { @@ -225,8 +224,8 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, formation_scale_factor, "Scale-factors at which the BHs were formed"); } else { list[num] = io_make_output_field("FormationTimes", FLOAT, 1, UNIT_CONV_TIME, - 0.f, bparts, formation_time, - "Times at which the BHs were formed"); + 0.f, bparts, formation_time, + "Times at which the BHs were formed"); } num++; @@ -260,8 +259,8 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts, num++; list[num] = io_make_output_field( - "BondiAccretionRates", FLOAT, 1, UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, bparts, - bondi_accretion_rate, + "BondiAccretionRates", FLOAT, 1, UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, + bparts, bondi_accretion_rate, "Physical instantaneous Bondi accretion rates of the particles"); num++; diff --git a/src/black_holes/Obsidian/black_holes_part.h b/src/black_holes/Obsidian/black_holes_part.h index 95ee3d89ad..e3d97342b4 100644 --- a/src/black_holes/Obsidian/black_holes_part.h +++ b/src/black_holes/Obsidian/black_holes_part.h @@ -39,7 +39,7 @@ struct bpart { long long id; /*! Pointer to corresponding gravity part. */ - struct gpart* gpart; + struct gpart *gpart; /*! Particle position. */ double x[3]; @@ -111,7 +111,7 @@ struct bpart { /*! Internal energy of the gas surrounding the black hole. */ float internal_energy_gas; - + /*! The mass of hot gas surrounding the black hole */ float hot_gas_mass; @@ -135,16 +135,16 @@ struct bpart { /*! The amount of jet mass kicked this time step */ float jet_mass_kicked_this_step; - + /*! The mass loading in the jet for the variable velocity scheme */ float jet_mass_loading; - + /*! The amount of unresolved mass available to kick */ float unresolved_mass_reservoir; /*! The amount of unresolved mass kicked this step */ float unresolved_mass_kicked_this_step; - + /*! Energy to dump this step via the ADAF hot-wind, kernel-weighted */ float adaf_energy_to_dump; @@ -153,17 +153,18 @@ struct bpart { /*! sum(mi * wi) weights for accretion/feedback */ float kernel_wt_sum; - + /*! sum(mi * wi) weights for adaf heating */ float adaf_wt_sum; - + /*! The mass of cold disk around the black hole */ float cold_disk_mass; - + /*! Mass in accretion disk from which BH accretes */ float accretion_disk_mass; - /*! The mass-weighted internal energy surrounding the black hole (unsmoothed) */ + /*! The mass-weighted internal energy surrounding the black hole (unsmoothed) + */ float hot_gas_internal_energy; /*! Smoothed sound speed of the gas surrounding the black hole. */ @@ -171,7 +172,7 @@ struct bpart { /*! Total gravitational gas mass within the kernel */ float gravitational_ngb_mass; - + /*! Subgrid physical sound speed of the gas (updated when using the subgrid * Bondi model) */ float sound_speed_subgrid_gas; @@ -182,7 +183,7 @@ struct bpart { /*! The real angular momentum of the gas in the kernel */ float angular_momentum_gas[3]; - + /*! Circular velocity of the gas around the black hole at the smoothing * radius (calculated as j_gas / h_BH, where j is specific ang. mom.) */ float circular_velocity_gas[3]; @@ -195,7 +196,7 @@ struct bpart { /*! Integer number of gravitational neighbors */ int num_gravitational_ngbs; - + /*! Number of seeds in this BH (i.e. itself + the merged ones) */ int cumulative_number_seeds; @@ -248,7 +249,7 @@ struct bpart { /*! The radiative luminosity of the black hole */ float radiative_luminosity; - + /*! How much energy has been given away in this timestep? */ float delta_energy_this_timestep; @@ -297,7 +298,7 @@ struct bpart { /*! Tracer structure */ struct tracers_bpart_data tracers_data; - + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/black_holes/Obsidian/black_holes_properties.h b/src/black_holes/Obsidian/black_holes_properties.h index 4c7b85773b..f336f1aa9b 100644 --- a/src/black_holes/Obsidian/black_holes_properties.h +++ b/src/black_holes/Obsidian/black_holes_properties.h @@ -31,7 +31,6 @@ /* Includes. */ #include - enum BH_states { BH_states_adaf = 0, /* < 0.03 Mdot,BH / Mdot,Edd */ BH_states_quasar, /* 0.03 < Mdot,BH / Mdot,Edd < 0.3 */ @@ -45,9 +44,9 @@ enum BH_merger_thresholds { }; enum BH_loading_types { - BH_jet_momentum_loaded, /* Momentum loaded jet, with subgrid energy loading */ - BH_jet_energy_loaded, /* Energy loaded jet, no subgrid */ - BH_jet_mixed_loaded /* A mix between momentum and energy loading */ + BH_jet_momentum_loaded, /* Momentum loaded jet, with subgrid energy loading */ + BH_jet_energy_loaded, /* Energy loaded jet, no subgrid */ + BH_jet_mixed_loaded /* A mix between momentum and energy loading */ }; /** @@ -88,7 +87,7 @@ struct black_holes_props { /*! Mass of a BH seed at creation time */ float subgrid_seed_mass; - + /*! Should we use the subgrid mass specified in ICs? */ int use_subgrid_mass_from_ics; @@ -103,7 +102,8 @@ struct black_holes_props { /*! Maximal fraction of the Eddington rate allowed for total accretion. */ float f_Edd_maximum; - /*! Maximal fraction of the Eddington rate allowed for Bondi accretion alone. */ + /*! Maximal fraction of the Eddington rate allowed for Bondi accretion alone. + */ float f_Edd_Bondi_maximum; /*! Minimum gas particle mass in nibbling mode */ @@ -118,13 +118,14 @@ struct black_holes_props { /*! Where do we distinguish between hot gas for Bondi? */ float environment_temperature_cut; - /*! Number of dynamical times over which gas is accreted from accretion disk */ + /*! Number of dynamical times over which gas is accreted from accretion disk + */ float dynamical_time_factor; /*! Max dynamical time over which gas is accreted from accretion disk */ float dynamical_time_max; - /*! Method to compute torque accretion rate: + /*! Method to compute torque accretion rate: * 0=Mgas/tdyn on all gas; 1=Mgas/tdyn on disk gas; 2=Simba-style(HQ11) */ int torque_accretion_method; @@ -137,7 +138,8 @@ struct black_holes_props { /*! Factor for exponentially limiting black hole growth in early stages. */ float bh_characteristic_suppression_mass; - /*! SF efficiency in BH kernel for suppression by winds (<0 means compute on the fly). */ + /*! SF efficiency in BH kernel for suppression by winds (<0 means compute on + * the fly). */ float suppression_sf_eff; /*! Gaussian spread in infall times when using SF-based growth suppression. */ @@ -171,13 +173,13 @@ struct black_holes_props { /*! How long to decouple black hole winds? */ float jet_decouple_time_factor; - + /*! The temperature of the jet. Set < 0.f for halo virial temperature */ float jet_temperature; /*! The fraction of energy loading that should go into mixed loading */ float jet_energy_frac; - + /*! What lower Mdot,BH/Mdot,Edd boundary does the jet activate? */ float eddington_fraction_lower_boundary; @@ -205,7 +207,8 @@ struct black_holes_props { /*! eps_f for the quasar mode */ float quasar_coupling; - /*! luminosity in system units above which to boost quasar eps_f quasar mode */ + /*! luminosity in system units above which to boost quasar eps_f quasar mode + */ double quasar_luminosity_thresh; /*! The disk wind efficiency from Benson & Babul 2009 */ @@ -250,7 +253,8 @@ struct black_holes_props { /*! A multiplicative factor for delaying cooling on a particle */ float adaf_cooling_shutoff_factor; - /*! A multiplicative factor 0. < f < 1. to multiply E_inject in the ADAF mode */ + /*! A multiplicative factor 0. < f < 1. to multiply E_inject in the ADAF mode + */ float adaf_kick_factor; /*! Direction of ADAF winds: 0=random, 1=L_gas, 2=L_BH, 3=outwards */ @@ -303,7 +307,7 @@ struct black_holes_props { /*! The mass loading in the jet */ float jet_mass_loading; - + /*! The subgrid jet speed to set the accretion fraction */ float jet_subgrid_velocity; @@ -392,7 +396,8 @@ struct black_holes_props { /*! Conversion factor from internal mass to solar masses */ double mass_to_solar_mass; - /*! Conversion factor from km/s to internal velocity units (without a-factor) */ + /*! Conversion factor from km/s to internal velocity units (without a-factor) + */ double kms_to_internal; /*! Conversion factor from internal length to parsec */ @@ -428,7 +433,8 @@ struct black_holes_props { /*! Convert Kelvin to internal temperature */ double T_K_to_int; - /* ------------ Stellar feedback properties for eta computation --------------- */ + /* ------------ Stellar feedback properties for eta computation + * --------------- */ /*! Normalization for the mass loading curve */ float FIRE_eta_normalization; @@ -481,8 +487,8 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->temp_to_u_factor = k_B / (mu * hydro_gamma_minus_one * m_p); const double Myr_in_cgs = 1e6 * 365.25 * 24. * 60. * 60.; - bp->time_to_Myr = units_cgs_conversion_factor(us, UNIT_CONV_TIME) / - Myr_in_cgs; + bp->time_to_Myr = + units_cgs_conversion_factor(us, UNIT_CONV_TIME) / Myr_in_cgs; /* Store constant of gravity */ bp->const_newton_G = phys_const->const_newton_G; @@ -530,45 +536,42 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->subgrid_seed_mass = parser_get_param_float(params, "ObsidianAGN:subgrid_seed_mass_Msun"); - + /* Convert to internal units */ bp->subgrid_seed_mass *= phys_const->const_solar_mass; - bp->use_subgrid_mass_from_ics = parser_get_opt_param_int(params, - "ObsidianAGN:use_subgrid_mass_from_ics", 1); + bp->use_subgrid_mass_from_ics = parser_get_opt_param_int( + params, "ObsidianAGN:use_subgrid_mass_from_ics", 1); if (bp->use_subgrid_mass_from_ics) { - bp->with_subgrid_mass_check = parser_get_opt_param_int(params, - "ObsidianAGN:with_subgrid_mass_check", 1); + bp->with_subgrid_mass_check = parser_get_opt_param_int( + params, "ObsidianAGN:with_subgrid_mass_check", 1); } /* Accretion parameters ---------------------------------- */ - /* Conversion factor for internal mass to M_solar */ + /* Conversion factor for internal mass to M_solar */ bp->mass_to_solar_mass = 1.f / phys_const->const_solar_mass; - bp->min_gas_mass_for_nibbling = - parser_get_param_float(params, - "ObsidianAGN:min_gas_mass_for_nibbling_Msun"); + bp->min_gas_mass_for_nibbling = parser_get_param_float( + params, "ObsidianAGN:min_gas_mass_for_nibbling_Msun"); bp->min_gas_mass_for_nibbling /= bp->mass_to_solar_mass; const double T_K_to_int = 1. / units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); - bp->environment_temperature_cut = - parser_get_opt_param_float(params, - "ObsidianAGN:environment_temperature_cut_K", - 1.0e5f); + bp->environment_temperature_cut = parser_get_opt_param_float( + params, "ObsidianAGN:environment_temperature_cut_K", 1.0e5f); bp->environment_temperature_cut *= T_K_to_int; bp->dynamical_time_factor = parser_get_opt_param_float( params, "ObsidianAGN:dynamical_time_factor", 1.f); - + bp->dynamical_time_max = parser_get_opt_param_float( params, "ObsidianAGN:dynamical_time_max_in_Myr", 0.f); bp->dynamical_time_max /= bp->time_to_Myr; - - bp->torque_accretion_method = - parser_get_opt_param_int(params, "ObsidianAGN:torque_accretion_method", 0); + + bp->torque_accretion_method = parser_get_opt_param_int( + params, "ObsidianAGN:torque_accretion_method", 0); bp->torque_accretion_norm = parser_get_param_float(params, "ObsidianAGN:torque_accretion_norm"); @@ -577,12 +580,13 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, parser_get_opt_param_int(params, "ObsidianAGN:suppress_growth", 0); if (bp->suppress_growth == 5 && bp->torque_accretion_method == 2) { - error("SF-based suppression of BH mass will not work correctly with " - "Simba-style torque-limited BH growth -- use tdyn method"); + error( + "SF-based suppression of BH mass will not work correctly with " + "Simba-style torque-limited BH growth -- use tdyn method"); } - bp->dt_accretion_factor = - parser_get_opt_param_float(params, "ObsidianAGN:dt_accretion_factor", 1.f); + bp->dt_accretion_factor = parser_get_opt_param_float( + params, "ObsidianAGN:dt_accretion_factor", 1.f); if (bp->dt_accretion_factor > 1.f || bp->dt_accretion_factor < 0.f) { error("ObsidianAGN:dt_accretion_factor must be between 0 and 1"); } @@ -590,14 +594,13 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->bh_characteristic_suppression_mass = parser_get_opt_param_float( params, "ObsidianAGN:bh_characteristic_suppression_mass", 0.f); - bp->suppression_sf_eff = parser_get_opt_param_float( - params, "ObsidianAGN:suppression_sf_eff", 0.f); + bp->suppression_sf_eff = + parser_get_opt_param_float(params, "ObsidianAGN:suppression_sf_eff", 0.f); - bp->tdyn_sigma = parser_get_opt_param_float( - params, "ObsidianAGN:tdyn_sigma", 0.f); + bp->tdyn_sigma = + parser_get_opt_param_float(params, "ObsidianAGN:tdyn_sigma", 0.f); - if (bp->suppress_growth == 4 || - bp->suppress_growth == 5) { + if (bp->suppress_growth == 4 || bp->suppress_growth == 5) { bp->FIRE_eta_normalization = parser_get_param_float(params, "KIARAFeedback:FIRE_eta_normalization"); bp->FIRE_eta_break = @@ -607,51 +610,48 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, parser_get_param_float(params, "KIARAFeedback:FIRE_eta_lower_slope"); bp->FIRE_eta_upper_slope = parser_get_param_float(params, "KIARAFeedback:FIRE_eta_upper_slope"); - bp->FIRE_eta_lower_slope_EOR = - parser_get_param_float(params, "KIARAFeedback:FIRE_eta_lower_slope_EOR"); - bp->minimum_galaxy_stellar_mass = parser_get_param_float(params, - "KIARAFeedback:minimum_galaxy_stellar_mass_Msun"); + bp->FIRE_eta_lower_slope_EOR = parser_get_param_float( + params, "KIARAFeedback:FIRE_eta_lower_slope_EOR"); + bp->minimum_galaxy_stellar_mass = parser_get_param_float( + params, "KIARAFeedback:minimum_galaxy_stellar_mass_Msun"); bp->minimum_galaxy_stellar_mass /= bp->mass_to_solar_mass; - bp->wind_eta_suppression_redshift = parser_get_opt_param_float(params, - "KIARAFeedback:wind_eta_suppression_redshift", 0.f); + bp->wind_eta_suppression_redshift = parser_get_opt_param_float( + params, "KIARAFeedback:wind_eta_suppression_redshift", 0.f); } - bp->f_Edd_maximum = - parser_get_param_float(params, "ObsidianAGN:max_eddington_fraction"); + bp->f_Edd_maximum = + parser_get_param_float(params, "ObsidianAGN:max_eddington_fraction"); - bp->f_Edd_Bondi_maximum = parser_get_opt_param_float(params, - "ObsidianAGN:max_bondi_eddington_fraction", 1.f); + bp->f_Edd_Bondi_maximum = parser_get_opt_param_float( + params, "ObsidianAGN:max_bondi_eddington_fraction", 1.f); - bp->fixed_T_above_EoS_factor = - exp10(parser_get_param_float(params, "ObsidianAGN:fixed_T_above_EoS_dex")); + bp->fixed_T_above_EoS_factor = exp10( + parser_get_param_float(params, "ObsidianAGN:fixed_T_above_EoS_dex")); - bp->dynamical_time_calculation_method = parser_get_opt_param_int(params, - "ObsidianAGN:dynamical_time_calculation_method", 1); + bp->dynamical_time_calculation_method = parser_get_opt_param_int( + params, "ObsidianAGN:dynamical_time_calculation_method", 1); /* Feedback parameters ---------------------------------- */ - bp->kms_to_internal = 1.0e5f / - units_cgs_conversion_factor(us, UNIT_CONV_SPEED); + bp->kms_to_internal = + 1.0e5f / units_cgs_conversion_factor(us, UNIT_CONV_SPEED); char temp3[40]; parser_get_param_string(params, "ObsidianAGN:jet_loading_type", temp3); if (strcmp(temp3, "EnergyLoaded") == 0) { bp->jet_loading_type = BH_jet_energy_loaded; - } - else if (strcmp(temp3, "MomentumLoaded") == 0) { + } else if (strcmp(temp3, "MomentumLoaded") == 0) { bp->jet_loading_type = BH_jet_momentum_loaded; - } - else if (strcmp(temp3, "MixedLoaded") == 0) { + } else if (strcmp(temp3, "MixedLoaded") == 0) { bp->jet_loading_type = BH_jet_mixed_loaded; - } - else { + } else { error( "The BH jet loading must be either EnergyLoaded or " "MomentumLoaded or MixedLoaded, not %s", temp3); } - bp->jet_velocity = + bp->jet_velocity = parser_get_param_float(params, "ObsidianAGN:jet_velocity_km_s"); bp->jet_velocity *= bp->kms_to_internal; if (bp->jet_velocity == 0.f) { @@ -661,32 +661,28 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, /* Use this in all of the physical calculations */ const float jet_velocity = fabs(bp->jet_velocity); - bp->jet_temperature = + bp->jet_temperature = parser_get_param_float(params, "ObsidianAGN:jet_temperature_K"); bp->jet_temperature *= T_K_to_int; - bp->eddington_fraction_lower_boundary = - parser_get_param_float(params, - "ObsidianAGN:eddington_fraction_lower_boundary"); + bp->eddington_fraction_lower_boundary = parser_get_param_float( + params, "ObsidianAGN:eddington_fraction_lower_boundary"); - bp->eddington_fraction_upper_boundary = - parser_get_param_float(params, - "ObsidianAGN:eddington_fraction_upper_boundary"); + bp->eddington_fraction_upper_boundary = parser_get_param_float( + params, "ObsidianAGN:eddington_fraction_upper_boundary"); const double kpc_per_km = 3.24078e-17; const double age_s = 13800. * Myr_in_cgs; /* Approximate age at z = 0 */ - const double jet_velocity_kpc_s = + const double jet_velocity_kpc_s = (jet_velocity / bp->kms_to_internal) * kpc_per_km; - const double recouple_distance_kpc = 10.; - const double f_jet_recouple = + const double recouple_distance_kpc = 10.; + const double f_jet_recouple = recouple_distance_kpc / (jet_velocity_kpc_s * age_s); - bp->jet_decouple_time_factor = - parser_get_opt_param_float(params, - "ObsidianAGN:jet_decouple_time_factor", f_jet_recouple); + bp->jet_decouple_time_factor = parser_get_opt_param_float( + params, "ObsidianAGN:jet_decouple_time_factor", f_jet_recouple); - bp->fixed_spin = - parser_get_param_float(params, "ObsidianAGN:fixed_spin"); + bp->fixed_spin = parser_get_param_float(params, "ObsidianAGN:fixed_spin"); if (bp->fixed_spin >= 1.f || bp->fixed_spin <= 0.f) { error("Black hole must have spin > 0.0 and < 1.0"); } @@ -695,14 +691,13 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->B_sd = powf(4.627f - 4.445f * bp->fixed_spin, -0.5524f); bp->C_sd = powf(827.3f - 718.1f * bp->fixed_spin, -0.7060f); - const float phi_bh = -20.2f * powf(bp->fixed_spin, 3.f) - -14.9f * powf(bp->fixed_spin, 2.f) - + 34.f * bp->fixed_spin - + 52.6f; - const float big_J = bp->fixed_spin / - (2.f * (1.f + sqrtf(1.f - powf(bp->fixed_spin, 2.f)))); - const float f_j = powf(big_J, 2.f) + - 1.38f * powf(big_J, 4.f) - 9.2f * powf(big_J, 6.f); + const float phi_bh = -20.2f * powf(bp->fixed_spin, 3.f) - + 14.9f * powf(bp->fixed_spin, 2.f) + + 34.f * bp->fixed_spin + 52.6f; + const float big_J = + bp->fixed_spin / (2.f * (1.f + sqrtf(1.f - powf(bp->fixed_spin, 2.f)))); + const float f_j = + powf(big_J, 2.f) + 1.38f * powf(big_J, 4.f) - 9.2f * powf(big_J, 6.f); bp->jet_efficiency = (1.f / (24.f * M_PI * M_PI)) * powf(phi_bh, 2.f) * f_j; @@ -715,8 +710,7 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, /* How are we loading the jet? Momentum, mixed, or energy? */ if (bp->jet_loading_type == BH_jet_momentum_loaded) { bp->jet_mass_loading = bp->jet_efficiency * c_over_v; - } - else if (bp->jet_loading_type == BH_jet_mixed_loaded) { + } else if (bp->jet_loading_type == BH_jet_mixed_loaded) { bp->jet_frac_energy = parser_get_param_float(params, "ObsidianAGN:jet_frac_energy_loaded"); if (bp->jet_frac_energy <= 0.f || bp->jet_frac_energy >= 1.f) { @@ -728,145 +722,141 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, const double energy_term = bp->jet_frac_energy * energy_loading; const double momentum_term = (1. - bp->jet_frac_energy) * momentum_loading; bp->jet_mass_loading = energy_term + momentum_term; - } - else { + } else { bp->jet_mass_loading = 2. * bp->jet_efficiency * pow(c_over_v, 2.); } - bp->jet_subgrid_velocity = + bp->jet_subgrid_velocity = parser_get_param_float(params, "ObsidianAGN:jet_subgrid_velocity_km_s"); bp->jet_subgrid_velocity *= bp->kms_to_internal; - const float R = 1.f / bp->eddington_fraction_upper_boundary; - const float eta_at_slim_disk_boundary = - (R / 16.f) * bp->A_sd * ((0.985f / (R + (5.f / 8.f) * bp->B_sd)) + - (0.015f / (R + (5.f / 8.f) * bp->C_sd))); + const float R = 1.f / bp->eddington_fraction_upper_boundary; + const float eta_at_slim_disk_boundary = + (R / 16.f) * bp->A_sd * + ((0.985f / (R + (5.f / 8.f) * bp->B_sd)) + + (0.015f / (R + (5.f / 8.f) * bp->C_sd))); /* If we scale BH v_jet, choose power law scaling with MBH/1.e8 or LBH/1.e45. * The minimum v_jet is always given by jet_subgrid_velocity */ - bp->jet_velocity_scaling_with_BH_mass - = parser_get_opt_param_float(params, - "ObsidianAGN:jet_velocity_scaling_with_BH_mass", 0.f); + bp->jet_velocity_scaling_with_BH_mass = parser_get_opt_param_float( + params, "ObsidianAGN:jet_velocity_scaling_with_BH_mass", 0.f); - bp->jet_velocity_scaling_with_BH_lum - = parser_get_opt_param_float(params, - "ObsidianAGN:jet_velocity_scaling_with_BH_lum", 0.f); + bp->jet_velocity_scaling_with_BH_lum = parser_get_opt_param_float( + params, "ObsidianAGN:jet_velocity_scaling_with_BH_lum", 0.f); - bp->jet_launch_dir = - parser_get_param_int(params, "ObsidianAGN:jet_launch_dir"); + bp->jet_launch_dir = + parser_get_param_int(params, "ObsidianAGN:jet_launch_dir"); - bp->jet_minimum_reservoir_mass - = parser_get_param_float(params, - "ObsidianAGN:jet_minimum_reservoir_mass_Msun"); + bp->jet_minimum_reservoir_mass = parser_get_param_float( + params, "ObsidianAGN:jet_minimum_reservoir_mass_Msun"); bp->jet_minimum_reservoir_mass /= bp->mass_to_solar_mass; - bp->lum_thresh_always_jet - = parser_get_opt_param_float(params, - "ObsidianAGN:lum_thresh_always_jet_1e45_erg_s", 0.f); - bp->lum_thresh_always_jet *= 1.e45 * units_cgs_conversion_factor(us, UNIT_CONV_TIME) / + bp->lum_thresh_always_jet = parser_get_opt_param_float( + params, "ObsidianAGN:lum_thresh_always_jet_1e45_erg_s", 0.f); + bp->lum_thresh_always_jet *= + 1.e45 * units_cgs_conversion_factor(us, UNIT_CONV_TIME) / units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); /* We need to keep epsilon_r continuous over all M_dot,BH/M_dot,Edd */ bp->epsilon_r = eta_at_slim_disk_boundary; if (bp->epsilon_r > 1.f) error("Somehow epsilon_r is greater than 1.0."); - bp->adaf_coupling = + bp->adaf_coupling = parser_get_param_float(params, "ObsidianAGN:adaf_coupling"); - bp->adaf_z_scaling = + bp->adaf_z_scaling = parser_get_opt_param_float(params, "ObsidianAGN:adaf_z_scaling", 0.f); - bp->quasar_coupling = + bp->quasar_coupling = parser_get_param_float(params, "ObsidianAGN:quasar_coupling"); - bp->quasar_luminosity_thresh = - parser_get_opt_param_float(params, "ObsidianAGN:quasar_lum_thresh_1e45_erg_s", 0.f); - bp->quasar_luminosity_thresh *= units_cgs_conversion_factor(us, UNIT_CONV_TIME) / + bp->quasar_luminosity_thresh = parser_get_opt_param_float( + params, "ObsidianAGN:quasar_lum_thresh_1e45_erg_s", 0.f); + bp->quasar_luminosity_thresh *= + units_cgs_conversion_factor(us, UNIT_CONV_TIME) / units_cgs_conversion_factor(us, UNIT_CONV_ENERGY) * 1.e45; - bp->slim_disk_coupling = parser_get_opt_param_float(params, - "ObsidianAGN:slim_disk_coupling", bp->quasar_coupling); + bp->slim_disk_coupling = parser_get_opt_param_float( + params, "ObsidianAGN:slim_disk_coupling", bp->quasar_coupling); /* These are for momentum constrained winds */ - bp->quasar_wind_momentum_flux = parser_get_opt_param_float(params, - "ObsidianAGN:quasar_wind_momentum_flux", 20.f); - bp->quasar_wind_speed = + bp->quasar_wind_momentum_flux = parser_get_opt_param_float( + params, "ObsidianAGN:quasar_wind_momentum_flux", 20.f); + bp->quasar_wind_speed = parser_get_param_float(params, "ObsidianAGN:quasar_wind_speed_km_s"); bp->quasar_wind_speed *= bp->kms_to_internal; - const double recouple_distance_non_jet_kpc = 1.5; - const double quasar_velocity_kpc_s = + const double recouple_distance_non_jet_kpc = 1.5; + const double quasar_velocity_kpc_s = (fabs(bp->quasar_wind_speed) / bp->kms_to_internal) * kpc_per_km; - const double f_quasar_recouple = + const double f_quasar_recouple = recouple_distance_non_jet_kpc / (quasar_velocity_kpc_s * age_s); - bp->quasar_decouple_time_factor = - parser_get_opt_param_float(params, - "ObsidianAGN:quasar_decouple_time_factor", f_quasar_recouple); + bp->quasar_decouple_time_factor = parser_get_opt_param_float( + params, "ObsidianAGN:quasar_decouple_time_factor", f_quasar_recouple); - bp->quasar_wind_dir = - parser_get_param_int(params, "ObsidianAGN:quasar_wind_dir"); + bp->quasar_wind_dir = + parser_get_param_int(params, "ObsidianAGN:quasar_wind_dir"); - bp->quasar_wind_mass_loading = bp->quasar_wind_momentum_flux * - fabs(bp->quasar_coupling) * bp->epsilon_r * - (phys_const->const_speed_light_c / fabs(bp->quasar_wind_speed)); + bp->quasar_wind_mass_loading = + bp->quasar_wind_momentum_flux * fabs(bp->quasar_coupling) * + bp->epsilon_r * + (phys_const->const_speed_light_c / fabs(bp->quasar_wind_speed)); bp->quasar_f_accretion = 1.f / (1.f + bp->quasar_wind_mass_loading); - const double slim_disk_wind_momentum_flux = parser_get_opt_param_float(params, - "ObsidianAGN:slim_disk_wind_momentum_flux", bp->quasar_wind_momentum_flux); + const double slim_disk_wind_momentum_flux = parser_get_opt_param_float( + params, "ObsidianAGN:slim_disk_wind_momentum_flux", + bp->quasar_wind_momentum_flux); - bp->slim_disk_wind_speed = parser_get_opt_param_float(params, - "ObsidianAGN:slim_disk_wind_speed_km_s", + bp->slim_disk_wind_speed = parser_get_opt_param_float( + params, "ObsidianAGN:slim_disk_wind_speed_km_s", bp->quasar_wind_speed / bp->kms_to_internal); bp->slim_disk_wind_speed *= bp->kms_to_internal; - bp->slim_disk_wind_dir = - parser_get_param_int(params, "ObsidianAGN:slim_disk_wind_dir"); + bp->slim_disk_wind_dir = + parser_get_param_int(params, "ObsidianAGN:slim_disk_wind_dir"); - const double slim_disk_velocity_kpc_s = + const double slim_disk_velocity_kpc_s = (fabs(bp->slim_disk_wind_speed) / bp->kms_to_internal) * kpc_per_km; - const double f_slim_disk_recouple = + const double f_slim_disk_recouple = recouple_distance_non_jet_kpc / (slim_disk_velocity_kpc_s * age_s); - bp->slim_disk_decouple_time_factor = - parser_get_opt_param_float(params, - "ObsidianAGN:slim_disk_decouple_time_factor", f_slim_disk_recouple); + bp->slim_disk_decouple_time_factor = parser_get_opt_param_float( + params, "ObsidianAGN:slim_disk_decouple_time_factor", + f_slim_disk_recouple); /* Set the slim disk mass loading to be continuous at the - * eta upper boundary. Compute the phi term to solve for the + * eta upper boundary. Compute the phi term to solve for the * accretion fraction */ - bp->slim_disk_phi = - slim_disk_wind_momentum_flux * fabs(bp->slim_disk_coupling) * - (phys_const->const_speed_light_c / fabs(bp->slim_disk_wind_speed)); + bp->slim_disk_phi = + slim_disk_wind_momentum_flux * fabs(bp->slim_disk_coupling) * + (phys_const->const_speed_light_c / fabs(bp->slim_disk_wind_speed)); const double slim_disk_wind_mass_loading = bp->slim_disk_phi * bp->epsilon_r; bp->slim_disk_jet_active = - parser_get_param_int(params, "ObsidianAGN:slim_disk_jet_active"); + parser_get_param_int(params, "ObsidianAGN:slim_disk_jet_active"); bp->adaf_disk_efficiency = - parser_get_param_float(params, "ObsidianAGN:adaf_disk_efficiency"); + parser_get_param_float(params, "ObsidianAGN:adaf_disk_efficiency"); bp->adaf_kick_factor = - parser_get_opt_param_float(params, - "ObsidianAGN:adaf_kick_factor", - 0.5f); + parser_get_opt_param_float(params, "ObsidianAGN:adaf_kick_factor", 0.5f); if (bp->adaf_kick_factor < 0.f || bp->adaf_kick_factor > 1.f) { error("adaf_kick_factor must be >= 0 and <= 1."); } - bp->adaf_wind_speed = parser_get_opt_param_float(params, - "ObsidianAGN:adaf_wind_speed_km_s", 0.f); + bp->adaf_wind_speed = parser_get_opt_param_float( + params, "ObsidianAGN:adaf_wind_speed_km_s", 0.f); bp->adaf_wind_speed *= bp->kms_to_internal; - const float f_psi = parser_get_opt_param_float(params, - "ObsidianAGN:adaf_f_quasar_psi", -1.f); + const float f_psi = + parser_get_opt_param_float(params, "ObsidianAGN:adaf_f_quasar_psi", -1.f); if (f_psi > 1.f) { error("adaf_f_quasar_psi must be <= 1."); } - bp->adaf_wind_dir = - parser_get_param_int(params, "ObsidianAGN:adaf_wind_dir"); + bp->adaf_wind_dir = parser_get_param_int(params, "ObsidianAGN:adaf_wind_dir"); - float jet_subgrid_mass_loading - = 2.f * bp->jet_efficiency * - (phys_const->const_speed_light_c / bp->jet_subgrid_velocity) * - (phys_const->const_speed_light_c / bp->jet_subgrid_velocity); + float jet_subgrid_mass_loading = + 2.f * bp->jet_efficiency * + (phys_const->const_speed_light_c / bp->jet_subgrid_velocity) * + (phys_const->const_speed_light_c / bp->jet_subgrid_velocity); /* f_acc = 1 / (1 + psi_jet,sub + psi_adaf) must still be true, but f_acc * is fixed at quasar_f_accretion if f_psi > 0 @@ -876,34 +866,35 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, /* This is always true in the negative case */ bp->adaf_f_accretion = bp->quasar_f_accretion; - const double jet_eff_psi_quasar = + const double jet_eff_psi_quasar = 2. * bp->jet_efficiency / bp->quasar_wind_mass_loading; if (jet_eff_psi_quasar > 1.) { - error("The jet efficiency is too high or the quasar wind mass loading" - " is too low for your choice of how to distribute energy in" - " the ADAF mode."); + error( + "The jet efficiency is too high or the quasar wind mass loading" + " is too low for your choice of how to distribute energy in" + " the ADAF mode."); } - const double psi_jet_subgrid = - bp->quasar_wind_mass_loading * (1. - f_psi); + const double psi_jet_subgrid = bp->quasar_wind_mass_loading * (1. - f_psi); const double c_frac = sqrt(2. * bp->jet_efficiency / psi_jet_subgrid); /* Do not exceed the speed-of-light */ if (c_frac >= 1.) { - const double f_max = - 1. - jet_eff_psi_quasar; - error("Cannot request more than f = %g of the quasar wind mass " - "loading as the ADAF mass loading since it violates the " - "speed-of-light in the sub-grid jet velocity.", f_max); + const double f_max = 1. - jet_eff_psi_quasar; + error( + "Cannot request more than f = %g of the quasar wind mass " + "loading as the ADAF mass loading since it violates the " + "speed-of-light in the sub-grid jet velocity.", + f_max); } bp->jet_subgrid_velocity = c_frac * phys_const->const_speed_light_c; /* Reset the sub-grid mass loading with the new velocity */ - jet_subgrid_mass_loading = + jet_subgrid_mass_loading = 2.f * bp->jet_efficiency * - (phys_const->const_speed_light_c / bp->jet_subgrid_velocity) * - (phys_const->const_speed_light_c / bp->jet_subgrid_velocity); + (phys_const->const_speed_light_c / bp->jet_subgrid_velocity) * + (phys_const->const_speed_light_c / bp->jet_subgrid_velocity); bp->adaf_wind_mass_loading = f_psi * bp->quasar_wind_mass_loading; @@ -911,30 +902,26 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->adaf_wind_speed = sqrt(2. * adaf_eps / bp->adaf_wind_mass_loading); bp->adaf_wind_speed *= phys_const->const_speed_light_c; if (bp->adaf_wind_speed > bp->jet_subgrid_velocity) { - error("The ADAF wind speed is above the sub-grid jet velocity. Are " - "you sure this is right?"); + error( + "The ADAF wind speed is above the sub-grid jet velocity. Are " + "you sure this is right?"); } - } - else { + } else { if (f_psi < 0.f) { /* Heat everything in the kernel in this case */ bp->adaf_wind_mass_loading = 0.f; /* This is always true in the negative case */ bp->adaf_f_accretion = 1.f / (1.f + jet_subgrid_mass_loading); - } - else { + } else { if (bp->adaf_wind_speed > 0.f) { - bp->adaf_wind_mass_loading = + bp->adaf_wind_mass_loading = 2.f * bp->adaf_coupling * bp->adaf_disk_efficiency; - bp->adaf_wind_mass_loading *= pow( - phys_const->const_speed_light_c / bp->adaf_wind_speed, - 2.f - ); - bp->adaf_f_accretion = 1.f / - (1.f + jet_subgrid_mass_loading + bp->adaf_wind_mass_loading); - } - else { + bp->adaf_wind_mass_loading *= + pow(phys_const->const_speed_light_c / bp->adaf_wind_speed, 2.f); + bp->adaf_f_accretion = + 1.f / (1.f + jet_subgrid_mass_loading + bp->adaf_wind_mass_loading); + } else { error("adaf_wind_speed_km_s must be non-zero in this case!"); } } @@ -943,10 +930,8 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, /* Do not decouple the ADAF winds */ bp->adaf_decouple_time_factor = 0.; - bp->adaf_maximum_temperature = - parser_get_opt_param_float(params, - "ObsidianAGN:adaf_maximum_temperature_K", - 5.e7f); + bp->adaf_maximum_temperature = parser_get_opt_param_float( + params, "ObsidianAGN:adaf_maximum_temperature_K", 5.e7f); bp->adaf_maximum_temperature *= T_K_to_int; bp->adaf_heating_n_H_threshold_cgs = parser_get_opt_param_float( @@ -955,59 +940,50 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->adaf_heating_T_threshold_cgs = parser_get_opt_param_float( params, "ObsidianAGN:adaf_heating_T_threshold_cgs", 5.0e5f); - bp->adaf_mass_limit = + bp->adaf_mass_limit = parser_get_param_float(params, "ObsidianAGN:adaf_mass_limit_Msun"); bp->adaf_mass_limit /= bp->mass_to_solar_mass; - bp->adaf_mass_limit_spread = - parser_get_opt_param_float(params, "ObsidianAGN:adaf_mass_limit_spread_Msun", 0.f); + bp->adaf_mass_limit_spread = parser_get_opt_param_float( + params, "ObsidianAGN:adaf_mass_limit_spread_Msun", 0.f); bp->adaf_mass_limit_spread /= bp->mass_to_solar_mass; - bp->adaf_mass_limit_a_scaling = - parser_get_opt_param_float(params, "ObsidianAGN:adaf_mass_limit_a_scaling", 0.f); + bp->adaf_mass_limit_a_scaling = parser_get_opt_param_float( + params, "ObsidianAGN:adaf_mass_limit_a_scaling", 0.f); - bp->adaf_mass_limit_a_min = - parser_get_opt_param_float(params, "ObsidianAGN:adaf_mass_limit_a_min", 0.f); + bp->adaf_mass_limit_a_min = parser_get_opt_param_float( + params, "ObsidianAGN:adaf_mass_limit_a_min", 0.f); - bp->adaf_cooling_shutoff_factor = - parser_get_opt_param_float(params, - "ObsidianAGN:adaf_cooling_shutoff_factor", - -1.f); + bp->adaf_cooling_shutoff_factor = parser_get_opt_param_float( + params, "ObsidianAGN:adaf_cooling_shutoff_factor", -1.f); /* Always use nibbling in Obsidian */ bp->use_nibbling = 1; bp->bondi_use_all_gas = - parser_get_opt_param_int(params, - "ObsidianAGN:bondi_use_all_gas", - 0); + parser_get_opt_param_int(params, "ObsidianAGN:bondi_use_all_gas", 0); bp->bondi_alpha = - parser_get_opt_param_float(params, - "ObsidianAGN:bondi_alpha", - 1.f); + parser_get_opt_param_float(params, "ObsidianAGN:bondi_alpha", 1.f); - bp->minimum_black_hole_mass_unresolved = - parser_get_param_float(params, - "ObsidianAGN:minimum_black_hole_mass_unresolved_Msun"); + bp->minimum_black_hole_mass_unresolved = parser_get_param_float( + params, "ObsidianAGN:minimum_black_hole_mass_unresolved_Msun"); bp->minimum_black_hole_mass_unresolved /= bp->mass_to_solar_mass; - bp->minimum_black_hole_mass_v_kick = - parser_get_param_float(params, - "ObsidianAGN:minimum_black_hole_mass_v_kick_Msun"); + bp->minimum_black_hole_mass_v_kick = parser_get_param_float( + params, "ObsidianAGN:minimum_black_hole_mass_v_kick_Msun"); bp->minimum_black_hole_mass_v_kick /= bp->mass_to_solar_mass; - bp->minimum_v_kick_km_s = - parser_get_opt_param_float(params, "ObsidianAGN:minimum_v_kick_km_s", - 10.f); + bp->minimum_v_kick_km_s = parser_get_opt_param_float( + params, "ObsidianAGN:minimum_v_kick_km_s", 10.f); /* Reposition parameters --------------------------------- */ bp->max_reposition_mass = parser_get_param_float(params, "ObsidianAGN:max_reposition_mass") * phys_const->const_solar_mass; - bp->max_reposition_distance_ratio = - parser_get_param_float(params, "ObsidianAGN:max_reposition_distance_ratio"); + bp->max_reposition_distance_ratio = parser_get_param_float( + params, "ObsidianAGN:max_reposition_distance_ratio"); bp->with_reposition_velocity_threshold = parser_get_param_int( params, "ObsidianAGN:with_reposition_velocity_threshold"); @@ -1048,7 +1024,8 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, /* Scaling parameters with BH mass and gas density */ bp->reposition_reference_mass = - parser_get_param_float(params, "ObsidianAGN:reposition_reference_mass") * + parser_get_param_float(params, + "ObsidianAGN:reposition_reference_mass") * phys_const->const_solar_mass; bp->reposition_exponent_mass = parser_get_opt_param_float( params, "ObsidianAGN:reposition_exponent_mass", 2.0); @@ -1112,12 +1089,10 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->conv_factor_energy_rate_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_ENERGY) / units_cgs_conversion_factor(us, UNIT_CONV_TIME); - bp->conv_factor_length_to_cgs = + bp->conv_factor_length_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); - bp->conv_factor_mass_to_cgs = - units_cgs_conversion_factor(us, UNIT_CONV_MASS); - bp->conv_factor_time_to_cgs = - units_cgs_conversion_factor(us, UNIT_CONV_TIME); + bp->conv_factor_mass_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_MASS); + bp->conv_factor_time_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_TIME); bp->conv_factor_specific_energy_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); @@ -1129,7 +1104,7 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->T_K_to_int = T_K_to_int; if (engine_rank == 0) { - message("Black holes kernel: %s with eta=%f (%.2f neighbours).", + message("Black holes kernel: %s with eta=%f (%.2f neighbours).", kernel_name, bp->eta_neighbours, bp->target_neighbours); message("Black holes relative tolerance in h: %.5f (+/- %.4f neighbours).", @@ -1139,56 +1114,45 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, message("Black hole jet velocity is %g km/s", bp->jet_velocity / bp->kms_to_internal); if (bp->jet_loading_type == BH_jet_momentum_loaded) { - message("Black hole jet loading (momentum) is %g", - bp->jet_mass_loading); - } - else if (bp->jet_loading_type == BH_jet_mixed_loaded) { - message("Black hole jet loading (mixed) is %g", - bp->jet_mass_loading); - } - else { - message("Black hole jet loading (energy) is %g", - bp->jet_mass_loading); + message("Black hole jet loading (momentum) is %g", bp->jet_mass_loading); + } else if (bp->jet_loading_type == BH_jet_mixed_loaded) { + message("Black hole jet loading (mixed) is %g", bp->jet_mass_loading); + } else { + message("Black hole jet loading (energy) is %g", bp->jet_mass_loading); } message("Black hole subgrid jet velocity is %g km/s", - bp->jet_subgrid_velocity / bp->kms_to_internal); - message("Black hole subgrid jet loading (energy) is %g", - jet_subgrid_mass_loading); - message("Black hole jet efficiency is %g", - bp->jet_efficiency); - message("Black hole jet recouple factor is %g", - f_jet_recouple); - message("Black hole quasar radiative efficiency is %g", - bp->epsilon_r); + bp->jet_subgrid_velocity / bp->kms_to_internal); + message("Black hole subgrid jet loading (energy) is %g", + jet_subgrid_mass_loading); + message("Black hole jet efficiency is %g", bp->jet_efficiency); + message("Black hole jet recouple factor is %g", f_jet_recouple); + message("Black hole quasar radiative efficiency is %g", bp->epsilon_r); if (bp->quasar_luminosity_thresh > 0.f) { - message("Black hole quasar coupling %g is boosted above Lbol>%g erg/s", - bp->quasar_coupling, bp->quasar_luminosity_thresh * - bp->conv_factor_energy_rate_to_cgs); + message( + "Black hole quasar coupling %g is boosted above Lbol>%g erg/s", + bp->quasar_coupling, + bp->quasar_luminosity_thresh * bp->conv_factor_energy_rate_to_cgs); } if (bp->lum_thresh_always_jet > 0.f) { message("Black hole jet mode always on above Lbol>%g erg/s", - bp->lum_thresh_always_jet * - bp->conv_factor_energy_rate_to_cgs); + bp->lum_thresh_always_jet * bp->conv_factor_energy_rate_to_cgs); } message("Black hole quasar wind speed is %g km/s", bp->quasar_wind_speed / bp->kms_to_internal); - message("Black hole quasar mass loading (momentum) is %g", + message("Black hole quasar mass loading (momentum) is %g", bp->quasar_wind_mass_loading); - message("Black hole quasar f_accretion is %g", - bp->quasar_f_accretion); - message("Black hole quasar recouple factor is %g", - f_quasar_recouple); + message("Black hole quasar f_accretion is %g", bp->quasar_f_accretion); + message("Black hole quasar recouple factor is %g", f_quasar_recouple); message("Black hole slim disk wind speed is %g km/s", bp->slim_disk_wind_speed / bp->kms_to_internal); - message("Black hole slim disk mass loading (momentum) is %g " - "(at the eta=%g boundary)", - slim_disk_wind_mass_loading, bp->epsilon_r); - message("Black hole slim disk recouple factor is %g", - f_slim_disk_recouple); + message( + "Black hole slim disk mass loading (momentum) is %g " + "(at the eta=%g boundary)", + slim_disk_wind_mass_loading, bp->epsilon_r); + message("Black hole slim disk recouple factor is %g", f_slim_disk_recouple); message("Black hole ADAF mass loading (energy) is %g", bp->adaf_wind_mass_loading); - message("Black hole ADAF f_accretion is %g", - bp->adaf_f_accretion); + message("Black hole ADAF f_accretion is %g", bp->adaf_f_accretion); message("Black hole ADAF v_wind is %g km/s (thermally dumped)", bp->adaf_wind_speed / bp->kms_to_internal); } @@ -1201,12 +1165,14 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, * @param props The properties of the black hole scheme. * @param cosmo The current cosmological model. */ -__attribute__((always_inline)) INLINE static -double get_black_hole_adaf_mass_limit(const struct bpart* const bp, - const struct black_holes_props* props, const struct cosmology* cosmo) { +__attribute__((always_inline)) INLINE static double +get_black_hole_adaf_mass_limit(const struct bpart *const bp, + const struct black_holes_props *props, + const struct cosmology *cosmo) { double mass_min = fabs(props->adaf_mass_limit); - if (props->adaf_mass_limit < 0.) mass_min *= - pow(fmax(cosmo->a, props->adaf_mass_limit_a_min), props->adaf_mass_limit_a_scaling); + if (props->adaf_mass_limit < 0.) + mass_min *= pow(fmax(cosmo->a, props->adaf_mass_limit_a_min), + props->adaf_mass_limit_a_scaling); mass_min += 0.01f * (float)(bp->id % 100) * props->adaf_mass_limit_spread; return mass_min; } @@ -1219,29 +1185,32 @@ double get_black_hole_adaf_mass_limit(const struct bpart* const bp, * @param props The properties of the black hole scheme. */ __attribute__((always_inline)) INLINE static float -black_hole_compute_jet_velocity( - const struct bpart *bi, - const struct cosmology *cosmo, - const struct black_holes_props *props) { +black_hole_compute_jet_velocity(const struct bpart *bi, + const struct cosmology *cosmo, + const struct black_holes_props *props) { float jet_velocity = fabs(props->jet_velocity); /* If the user supplied a variable jet velocity we must recalculate the * mass loading based on the variable jet velocity */ - if (props->jet_velocity < 0.f || props->jet_velocity_scaling_with_BH_mass > 0.f - || props->jet_velocity_scaling_with_BH_lum > 0.f) { + if (props->jet_velocity < 0.f || + props->jet_velocity_scaling_with_BH_mass > 0.f || + props->jet_velocity_scaling_with_BH_lum > 0.f) { if (props->jet_velocity < 0.f) { jet_velocity *= powf(cosmo->H / cosmo->H0, 1.f / 3.f); } if (props->jet_velocity_scaling_with_BH_mass > 0.f) { - float BH_mass_scaled = bi->subgrid_mass * props->mass_to_solar_mass * 1.0e-8; + float BH_mass_scaled = + bi->subgrid_mass * props->mass_to_solar_mass * 1.0e-8; BH_mass_scaled = fmax(BH_mass_scaled, 1.f); - jet_velocity *= powf(BH_mass_scaled, props->jet_velocity_scaling_with_BH_mass); + jet_velocity *= + powf(BH_mass_scaled, props->jet_velocity_scaling_with_BH_mass); } if (props->jet_velocity_scaling_with_BH_lum > 0.f) { float BH_lum_scaled = bi->radiative_luminosity * - props->conv_factor_energy_rate_to_cgs * 1.e-45; + props->conv_factor_energy_rate_to_cgs * 1.e-45; BH_lum_scaled = fmax(BH_lum_scaled, 1.f); - jet_velocity *= powf(BH_lum_scaled, props->jet_velocity_scaling_with_BH_lum); + jet_velocity *= + powf(BH_lum_scaled, props->jet_velocity_scaling_with_BH_lum); } } return jet_velocity; @@ -1255,13 +1224,13 @@ black_hole_compute_jet_velocity( * @param props The properties of the black hole scheme. */ __attribute__((always_inline)) INLINE static float -black_hole_compute_jet_energy_ramp( - const struct bpart *bi, - const struct cosmology *cosmo, - const struct black_holes_props *props) { +black_hole_compute_jet_energy_ramp(const struct bpart *bi, + const struct cosmology *cosmo, + const struct black_holes_props *props) { /* Only do jet if above ADAF mass limit */ - const float my_adaf_mass_limit = get_black_hole_adaf_mass_limit(bi, props, cosmo); + const float my_adaf_mass_limit = + get_black_hole_adaf_mass_limit(bi, props, cosmo); /* Compute ramp-up of jet feedback energy */ float jet_ramp = 0.f; @@ -1272,7 +1241,6 @@ black_hole_compute_jet_energy_ramp( return jet_ramp; } - /** * @brief Write a black_holes_props struct to the given FILE as a stream of * bytes. diff --git a/src/black_holes/Obsidian/black_holes_struct.h b/src/black_holes/Obsidian/black_holes_struct.h index 56ca10f41c..644077d9f2 100644 --- a/src/black_holes/Obsidian/black_holes_struct.h +++ b/src/black_holes/Obsidian/black_holes_struct.h @@ -63,7 +63,7 @@ struct black_holes_bpart_data { * @param p_data The #part's #black_holes_part_data structure. */ __attribute__((always_inline)) INLINE static void -black_holes_mark_part_as_not_swallowed(struct black_holes_part_data* p_data) { +black_holes_mark_part_as_not_swallowed(struct black_holes_part_data *p_data) { p_data->swallow_id = -1; p_data->jet_id = -1; @@ -76,7 +76,7 @@ black_holes_mark_part_as_not_swallowed(struct black_holes_part_data* p_data) { * @param p_data The #part's black hole data. */ __attribute__((always_inline)) INLINE static void black_holes_init_potential( - struct black_holes_part_data* p_data) { + struct black_holes_part_data *p_data) { p_data->potential = FLT_MAX; } @@ -87,7 +87,7 @@ __attribute__((always_inline)) INLINE static void black_holes_init_potential( * @param p_data The #part's #black_holes_part_data structure. */ __attribute__((always_inline)) INLINE static void -black_holes_mark_part_as_swallowed(struct black_holes_part_data* p_data) { +black_holes_mark_part_as_swallowed(struct black_holes_part_data *p_data) { p_data->swallow_id = -2; } @@ -98,7 +98,7 @@ black_holes_mark_part_as_swallowed(struct black_holes_part_data* p_data) { * @param p_data The #part's #black_holes_part_data structure. */ __attribute__((always_inline)) INLINE static long long -black_holes_get_part_swallow_id(struct black_holes_part_data* p_data) { +black_holes_get_part_swallow_id(struct black_holes_part_data *p_data) { return p_data->swallow_id; } @@ -110,7 +110,7 @@ black_holes_get_part_swallow_id(struct black_holes_part_data* p_data) { * @param p_data The #bpart's #black_holes_bpart_data structure. */ __attribute__((always_inline)) INLINE static void -black_holes_mark_bpart_as_not_swallowed(struct black_holes_bpart_data* p_data) { +black_holes_mark_bpart_as_not_swallowed(struct black_holes_bpart_data *p_data) { p_data->swallow_id = -1; p_data->swallow_mass = 0.f; @@ -123,7 +123,7 @@ black_holes_mark_bpart_as_not_swallowed(struct black_holes_bpart_data* p_data) { * @param p_data The #bpart's #black_holes_bpart_data structure. */ __attribute__((always_inline)) INLINE static void -black_holes_mark_bpart_as_merged(struct black_holes_bpart_data* p_data) { +black_holes_mark_bpart_as_merged(struct black_holes_bpart_data *p_data) { p_data->swallow_id = -2; p_data->swallow_mass = -1.f; @@ -135,7 +135,7 @@ black_holes_mark_bpart_as_merged(struct black_holes_bpart_data* p_data) { * @param p_data The #bpart's #black_holes_bpart_data structure. */ __attribute__((always_inline)) INLINE static long long -black_holes_get_bpart_swallow_id(struct black_holes_bpart_data* p_data) { +black_holes_get_bpart_swallow_id(struct black_holes_bpart_data *p_data) { return p_data->swallow_id; } diff --git a/src/chemistry/KIARA/chemistry.h b/src/chemistry/KIARA/chemistry.h index cc2c30cbdd..c769a3b287 100644 --- a/src/chemistry/KIARA/chemistry.h +++ b/src/chemistry/KIARA/chemistry.h @@ -36,8 +36,8 @@ #include "parser.h" #include "part.h" #include "physical_constants.h" -#include "units.h" #include "timestep_sync_part.h" +#include "units.h" /** * @brief Initializes summed particle quantities for the firehose wind model @@ -47,25 +47,24 @@ * @param p The particle to act upon * @param cd #chemistry_global_data containing chemistry informations. */ -__attribute__((always_inline)) INLINE static void -firehose_init_ambient_quantities(struct part* restrict p, - const struct chemistry_global_data* cd) { +__attribute__((always_inline)) INLINE static void +firehose_init_ambient_quantities(struct part *restrict p, + const struct chemistry_global_data *cd) { - struct chemistry_part_data* cpd = &p->chemistry_data; + struct chemistry_part_data *cpd = &p->chemistry_data; cpd->w_ambient = 0.f; cpd->rho_ambient = 0.f; cpd->u_ambient = 0.f; } -__attribute__((always_inline)) INLINE static void -logger_windprops_printprops( - struct part *pi, - const struct cosmology *cosmo, const struct chemistry_global_data* cd) { +__attribute__((always_inline)) INLINE static void logger_windprops_printprops( + struct part *pi, const struct cosmology *cosmo, + const struct chemistry_global_data *cd) { - /* Ignore COUPLED particles */ + /* Ignore COUPLED particles */ if (!pi->decoupled) return; - + #ifdef CHEMISTRY_OUTPUT_FIREHOSE_LOG /* Print wind properties */ const float length_convert = cosmo->a * cd->length_to_kpc; @@ -73,22 +72,22 @@ logger_windprops_printprops( const float u_convert = cosmo->a_factor_internal_energy / cd->temp_to_u_factor; - message("FIREHOSE: z=%.3f id=%lld Mgal=%g h=%g T=%g rho=%g Rs=%g Z=%g tdel=%g Ndec=%d rhoamb=%g Tamb=%g tcmix=%g\n", - cosmo->z, - pi->id, - (pi->galaxy_data.gas_mass + pi->galaxy_data.stellar_mass) * - cd->mass_to_solar_mass, - pi->h * cosmo->a * cd->length_to_kpc, - hydro_get_drifted_comoving_internal_energy(pi) * u_convert, - pi->rho * rho_convert, - pi->chemistry_data.radius_stream * length_convert, - pi->chemistry_data.metal_mass_fraction_total, - pi->chemistry_data.decoupling_delay_time * cd->time_to_Myr, - pi->chemistry_data.number_of_times_decoupled, - pi->chemistry_data.rho_ambient * cd->rho_to_n_cgs * cosmo->a3_inv, - pi->chemistry_data.u_ambient * - cosmo->a_factor_internal_energy / cd->temp_to_u_factor, - pi->cooling_data.mixing_layer_cool_time); + message( + "FIREHOSE: z=%.3f id=%lld Mgal=%g h=%g T=%g rho=%g Rs=%g Z=%g tdel=%g " + "Ndec=%d rhoamb=%g Tamb=%g tcmix=%g\n", + cosmo->z, pi->id, + (pi->galaxy_data.gas_mass + pi->galaxy_data.stellar_mass) * + cd->mass_to_solar_mass, + pi->h * cosmo->a * cd->length_to_kpc, + hydro_get_drifted_comoving_internal_energy(pi) * u_convert, + pi->rho * rho_convert, pi->chemistry_data.radius_stream * length_convert, + pi->chemistry_data.metal_mass_fraction_total, + pi->chemistry_data.decoupling_delay_time * cd->time_to_Myr, + pi->chemistry_data.number_of_times_decoupled, + pi->chemistry_data.rho_ambient * cd->rho_to_n_cgs * cosmo->a3_inv, + pi->chemistry_data.u_ambient * cosmo->a_factor_internal_energy / + cd->temp_to_u_factor, + pi->cooling_data.mixing_layer_cool_time); #endif return; @@ -102,14 +101,14 @@ logger_windprops_printprops( * @param p The particle to act upon * @param cd #chemistry_global_data containing chemistry informations. */ -__attribute__((always_inline)) INLINE static void -firehose_end_ambient_quantities(struct part* restrict p, - struct xpart* restrict xp, - const struct chemistry_global_data* cd, - const struct cosmology* cosmo) { +__attribute__((always_inline)) INLINE static void +firehose_end_ambient_quantities(struct part *restrict p, + struct xpart *restrict xp, + const struct chemistry_global_data *cd, + const struct cosmology *cosmo) { const float u_floor = cd->firehose_u_floor / cosmo->a_factor_internal_energy; - const float rho_max = + const float rho_max = cd->firehose_ambient_rho_max * cosmo->a * cosmo->a * cosmo->a; /* No ambient properties for non-wind particles */ @@ -117,19 +116,15 @@ firehose_end_ambient_quantities(struct part* restrict p, /* Some smoothing length multiples. */ const float h = p->h; - const float h_inv = 1.0f / h; /* 1/h */ - const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ #ifdef FIREHOSE_DEBUG_CHECKS - message("FIREHOSE_prelim: id=%lld rhoamb=%g wamb=%g uamb=%g" - "h=%g h_inv=%g h_inv_dim=%g", - p->id, - p->chemistry_data.rho_ambient, - p->chemistry_data.w_ambient, - p->chemistry_data.u_ambient, - h, - h_inv, - h_inv_dim); + message( + "FIREHOSE_prelim: id=%lld rhoamb=%g wamb=%g uamb=%g" + "h=%g h_inv=%g h_inv_dim=%g", + p->id, p->chemistry_data.rho_ambient, p->chemistry_data.w_ambient, + p->chemistry_data.u_ambient, h, h_inv, h_inv_dim); #endif /* h_inv_dim can sometimes lead to rho_ambient -> 0 after normalization */ @@ -137,22 +132,18 @@ firehose_end_ambient_quantities(struct part* restrict p, if (p->chemistry_data.rho_ambient > 0.f) { p->chemistry_data.u_ambient *= h_inv_dim / p->chemistry_data.rho_ambient; - } - else { + } else { p->chemistry_data.rho_ambient = hydro_get_comoving_density(p); p->chemistry_data.u_ambient = u_floor; } - + #ifdef FIREHOSE_DEBUG_CHECKS message("FIREHOSE_lim: id=%lld rhoamb=%g wamb=%g uamb=%g ufloor=%g\n", - p->id, - p->chemistry_data.rho_ambient, - p->chemistry_data.w_ambient, - p->chemistry_data.u_ambient, + p->id, p->chemistry_data.rho_ambient, p->chemistry_data.w_ambient, + p->chemistry_data.u_ambient, cd->firehose_u_floor / cd->temp_to_u_factor); #endif - } - else { + } else { /* Set them to reasonable values for non-wind, just in case */ p->chemistry_data.rho_ambient = hydro_get_comoving_density(p); p->chemistry_data.u_ambient = hydro_get_drifted_comoving_internal_energy(p); @@ -163,19 +154,18 @@ firehose_end_ambient_quantities(struct part* restrict p, p->chemistry_data.u_ambient = max(p->chemistry_data.u_ambient, u_floor); #ifdef FIREHOSE_DEBUG_CHECKS if (p->decoupled) { - message("FIREHOSE_AMB: z=%g id=%lld nH=%g nHamb=%g u=%g uamb=%g T=%g " - "Tamb=%g tcool=%g", - cosmo->z, - p->id, - p->rho * cd->rho_to_n_cgs * cosmo->a3_inv, - p->chemistry_data.rho_ambient * cd->rho_to_n_cgs * cosmo->a3_inv, - hydro_get_drifted_comoving_internal_energy(p), - p->chemistry_data.u_ambient, - hydro_get_drifted_comoving_internal_energy(p) * - cosmo->a_factor_internal_energy / cd->temp_to_u_factor, - p->chemistry_data.u_ambient * - cosmo->a_factor_internal_energy / cd->temp_to_u_factor, - p->cooling_data.mixing_layer_cool_time); + message( + "FIREHOSE_AMB: z=%g id=%lld nH=%g nHamb=%g u=%g uamb=%g T=%g " + "Tamb=%g tcool=%g", + cosmo->z, p->id, p->rho * cd->rho_to_n_cgs * cosmo->a3_inv, + p->chemistry_data.rho_ambient * cd->rho_to_n_cgs * cosmo->a3_inv, + hydro_get_drifted_comoving_internal_energy(p), + p->chemistry_data.u_ambient, + hydro_get_drifted_comoving_internal_energy(p) * + cosmo->a_factor_internal_energy / cd->temp_to_u_factor, + p->chemistry_data.u_ambient * cosmo->a_factor_internal_energy / + cd->temp_to_u_factor, + p->cooling_data.mixing_layer_cool_time); } #endif @@ -184,16 +174,15 @@ firehose_end_ambient_quantities(struct part* restrict p, #endif } - /** * @brief Return a string containing the name of a given #chemistry_element. */ -__attribute__((always_inline)) INLINE static const char* +__attribute__((always_inline)) INLINE static const char * chemistry_get_element_name(enum chemistry_element elem) { - static const char* chemistry_element_names[chemistry_element_count] = { - "Hydrogen", "Helium", "Carbon", "Nitrogen", "Oxygen", - "Neon", "Magnesium", "Silicon", "Sulfur", "Calcium", "Iron"}; + static const char *chemistry_element_names[chemistry_element_count] = { + "Hydrogen", "Helium", "Carbon", "Nitrogen", "Oxygen", "Neon", + "Magnesium", "Silicon", "Sulfur", "Calcium", "Iron"}; return chemistry_element_names[elem]; } @@ -208,9 +197,9 @@ chemistry_get_element_name(enum chemistry_element elem) { * @param cd #chemistry_global_data containing chemistry informations. */ __attribute__((always_inline)) INLINE static void chemistry_init_part( - struct part* restrict p, const struct chemistry_global_data* cd) { + struct part *restrict p, const struct chemistry_global_data *cd) { - struct chemistry_part_data* cpd = &p->chemistry_data; + struct chemistry_part_data *cpd = &p->chemistry_data; /* Reset the shear tensor */ for (int i = 0; i < 3; i++) { @@ -258,17 +247,15 @@ __attribute__((always_inline)) INLINE static void chemistry_init_part( * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void chemistry_end_density( - struct part* restrict p, - struct xpart* restrict xp, - const struct chemistry_global_data* cd, - const struct cosmology* cosmo) { + struct part *restrict p, struct xpart *restrict xp, + const struct chemistry_global_data *cd, const struct cosmology *cosmo) { /* Some smoothing length multiples. */ const float h = p->h; - const float h_inv = 1.0f / h; /* 1/h */ - const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ - struct chemistry_part_data* cpd = &p->chemistry_data; + struct chemistry_part_data *cpd = &p->chemistry_data; /* If diffusion is on, finish up shear tensor & particle's diffusion coeff */ if (cd->diffusion_flag == 1 && cd->C_Smagorinsky > 0.f) { @@ -326,13 +313,13 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density( velocity_gradient_norm = sqrtf(velocity_gradient_norm); /* Never set D for wind, or ISM particles */ - if (!(p->decoupled) && - !(p->cooling_data.subgrid_temp > 0.f)) { + if (!(p->decoupled) && !(p->cooling_data.subgrid_temp > 0.f)) { /* Rennehan: Limit to maximum resolvable velocity scale */ - const float v_phys = sqrtf(xp->v_full[0] * xp->v_full[0] + - xp->v_full[1] * xp->v_full[1] + - xp->v_full[2] * xp->v_full[2]) * cosmo->a_inv; + const float v_phys = + sqrtf(xp->v_full[0] * xp->v_full[0] + xp->v_full[1] * xp->v_full[1] + + xp->v_full[2] * xp->v_full[2]) * + cosmo->a_inv; const float h_phys = cosmo->a * p->h * kernel_gamma; const float vel_norm_phys_max = 0.5f * v_phys / h_phys; if (velocity_gradient_norm > vel_norm_phys_max) { @@ -345,7 +332,7 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density( */ const float rho_phys = hydro_get_physical_density(p, cosmo); const float smag_length_scale = cd->C_Smagorinsky * h_phys; - const float D_phys = rho_phys * smag_length_scale * smag_length_scale * + const float D_phys = rho_phys * smag_length_scale * smag_length_scale * velocity_gradient_norm; cpd->diffusion_coefficient = D_phys; @@ -360,7 +347,6 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density( if (cd->use_firehose_wind_model) { firehose_end_ambient_quantities(p, xp, cd, cosmo); } - } /** @@ -372,13 +358,13 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density( * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void -chemistry_part_has_no_neighbours(struct part* restrict p, - struct xpart* restrict xp, - const struct chemistry_global_data* cd, - const struct cosmology* cosmo) { +chemistry_part_has_no_neighbours(struct part *restrict p, + struct xpart *restrict xp, + const struct chemistry_global_data *cd, + const struct cosmology *cosmo) { /* Just make all the smoothed fields default to the un-smoothed values */ - struct chemistry_part_data* cpd = &p->chemistry_data; + struct chemistry_part_data *cpd = &p->chemistry_data; /* Reset the shear tensor */ for (int i = 0; i < 3; i++) { cpd->shear_tensor[i][0] = 0.f; @@ -413,11 +399,11 @@ chemistry_part_has_no_neighbours(struct part* restrict p, * @param xp Pointer to the extended particle data. */ __attribute__((always_inline)) INLINE static void chemistry_first_init_part( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct chemistry_global_data* data, struct part* restrict p, - struct xpart* restrict xp) { + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct chemistry_global_data *data, struct part *restrict p, + struct xpart *restrict xp) { /* Initialize mass fractions for total metals and each metal individually */ if (data->initial_metal_mass_fraction_total != -1) { @@ -444,7 +430,7 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part( * @param sp Pointer to the sparticle data. */ __attribute__((always_inline)) INLINE static void chemistry_first_init_spart( - const struct chemistry_global_data* data, struct spart* restrict sp) { + const struct chemistry_global_data *data, struct spart *restrict sp) { /* Initialize mass fractions for total metals and each metal individually */ if (data->initial_metal_mass_fraction_total != -1) { @@ -466,8 +452,7 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_spart( * Required by space_first_init.c */ __attribute__((always_inline)) INLINE static void chemistry_first_init_sink( - const struct chemistry_global_data* data, struct sink* restrict sink) {} - + const struct chemistry_global_data *data, struct sink *restrict sink) {} /** * @brief Initialises the chemistry properties. @@ -477,117 +462,93 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_sink( * @param phys_const The physical constants in internal units. * @param data The properties to initialise. */ -static INLINE void chemistry_init_backend(struct swift_params* parameter_file, - const struct unit_system* us, - const struct phys_const* phys_const, - struct chemistry_global_data* data) { +static INLINE void chemistry_init_backend(struct swift_params *parameter_file, + const struct unit_system *us, + const struct phys_const *phys_const, + struct chemistry_global_data *data) { /* Set some useful unit conversions */ const double Msun_cgs = phys_const->const_solar_mass * units_cgs_conversion_factor(us, UNIT_CONV_MASS); const double unit_mass_cgs = units_cgs_conversion_factor(us, UNIT_CONV_MASS); data->mass_to_solar_mass = unit_mass_cgs / Msun_cgs; - data->temp_to_u_factor = - phys_const->const_boltzmann_k / - (hydro_gamma_minus_one * phys_const->const_proton_mass * - units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE)); - data->T_to_internal = + data->temp_to_u_factor = + phys_const->const_boltzmann_k / + (hydro_gamma_minus_one * phys_const->const_proton_mass * + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE)); + data->T_to_internal = 1. / units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); const double X_H = 0.752; data->rho_to_n_cgs = - (X_H / phys_const->const_proton_mass) * - units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); - data->kms_to_internal = + (X_H / phys_const->const_proton_mass) * + units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); + data->kms_to_internal = 1.0e5 / units_cgs_conversion_factor(us, UNIT_CONV_SPEED); data->time_to_Myr = units_cgs_conversion_factor(us, UNIT_CONV_TIME) / - (1.e6 * 365.25 * 24. * 60. * 60.); + (1.e6 * 365.25 * 24. * 60. * 60.); data->length_to_kpc = units_cgs_conversion_factor(us, UNIT_CONV_LENGTH) / 3.08567758e21; /* Is metal diffusion turned on? */ - data->diffusion_flag = parser_get_param_int(parameter_file, - "KIARAChemistry:diffusion_on"); + data->diffusion_flag = + parser_get_param_int(parameter_file, "KIARAChemistry:diffusion_on"); /* Read the diffusion coefficient */ - data->C_Smagorinsky = - parser_get_opt_param_float(parameter_file, - "KIARAChemistry:diffusion_coefficient", - 0.23f); + data->C_Smagorinsky = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:diffusion_coefficient", 0.23f); - /* Time-step restriction to <= 0.15*rho*h^2 / D from + /* Time-step restriction to <= 0.15*rho*h^2 / D from Parshikov & Medin 2002 equation 41 */ - data->diffusion_beta = - parser_get_opt_param_float(parameter_file, - "KIARAChemistry:diffusion_beta", - 0.1f); + data->diffusion_beta = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:diffusion_beta", 0.1f); if (data->diffusion_beta < 0.f || data->diffusion_beta > 0.1f) { error("diffusion_beta must be >= 0 and <= 0.1"); } - data->time_step_min = - parser_get_opt_param_float(parameter_file, - "KIARAChemistry:minimum_timestep_Myr", - 0.1f); + data->time_step_min = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:minimum_timestep_Myr", 0.1f); data->time_step_min /= data->time_to_Myr; if (data->time_step_min < 0.f) { error("time_step_min must be > 0"); } - data->max_fractional_Z_transfer = - parser_get_opt_param_float(parameter_file, - "KIARAChemistry:max_fractional_Z_transfer", - 0.25f); - if (data->max_fractional_Z_transfer < 0.f || - data->max_fractional_Z_transfer > 1.f) { + data->max_fractional_Z_transfer = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:max_fractional_Z_transfer", 0.25f); + if (data->max_fractional_Z_transfer < 0.f || + data->max_fractional_Z_transfer > 1.f) { error("diffusion_beta must be >= 0 and <= 1"); } /* Are we using the firehose wind model? */ - data->use_firehose_wind_model = - parser_get_opt_param_int(parameter_file, - "KIARAChemistry:use_firehose_wind_model", - 0); + data->use_firehose_wind_model = parser_get_opt_param_int( + parameter_file, "KIARAChemistry:use_firehose_wind_model", 0); if (data->use_firehose_wind_model) { /* Firehose model parameters */ - data->firehose_ambient_rho_max = - parser_get_opt_param_float(parameter_file, - "KIARAChemistry:firehose_nh_ambient_max_cgs", - 0.1f); + data->firehose_ambient_rho_max = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:firehose_nh_ambient_max_cgs", 0.1f); data->firehose_ambient_rho_max /= data->rho_to_n_cgs; - data->firehose_u_floor = - parser_get_opt_param_float(parameter_file, - "KIARAChemistry:firehose_temp_floor", - 1.e4f); + data->firehose_u_floor = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:firehose_temp_floor", 1.e4f); data->firehose_u_floor *= data->temp_to_u_factor * data->T_to_internal; /* Firehose recoupling parameters */ - data->firehose_recoupling_mach = - parser_get_opt_param_float(parameter_file, - "KIARAChemistry:firehose_recoupling_mach", - 0.5f); - - data->firehose_recoupling_u_factor = - parser_get_opt_param_float(parameter_file, - "KIARAChemistry:firehose_recoupling_u_factor", - 0.5f); - - data->firehose_recoupling_fmix = - parser_get_opt_param_float(parameter_file, - "KIARAChemistry:firehose_recoupling_fmix", - 0.9f); - - data->firehose_max_velocity = - parser_get_opt_param_float(parameter_file, - "KIARAChemistry:firehose_max_velocity", - 2000.f); + data->firehose_recoupling_mach = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:firehose_recoupling_mach", 0.5f); + + data->firehose_recoupling_u_factor = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:firehose_recoupling_u_factor", 0.5f); + + data->firehose_recoupling_fmix = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:firehose_recoupling_fmix", 0.9f); + + data->firehose_max_velocity = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:firehose_max_velocity", 2000.f); data->firehose_max_velocity *= data->kms_to_internal; - data->firehose_max_fmix_per_step = - parser_get_opt_param_float(parameter_file, - "KIARAChemistry:firehose_max_fmix_per_step", - 0.1f); + data->firehose_max_fmix_per_step = parser_get_opt_param_float( + parameter_file, "KIARAChemistry:firehose_max_fmix_per_step", 0.1f); } /* Read the total metallicity */ @@ -631,23 +592,23 @@ static INLINE void chemistry_init_backend(struct swift_params* parameter_file, "(%f)", total_frac, data->initial_metal_mass_fraction_total); } - } - else { + } else { /* If diffusion is on, need a metallicity floor so reset Z total */ if (total_frac > 1.02 * data->initial_metal_mass_fraction_total) { warning("Resetting total Z to the sum of all available metals."); data->initial_metal_mass_fraction_total = total_frac; /* H + He + Z should be ~1 */ - float total_frac_check = + float total_frac_check = data->initial_metal_mass_fraction[chemistry_element_H] + data->initial_metal_mass_fraction[chemistry_element_He] + data->initial_metal_mass_fraction_total; if (total_frac_check < 0.98 || total_frac_check > 1.02) { - error("After resetting, the abundances provided seem odd! " - "H + He + Z = %f =/= 1.", - total_frac_check); + error( + "After resetting, the abundances provided seem odd! " + "H + He + Z = %f =/= 1.", + total_frac_check); } } } @@ -663,7 +624,6 @@ static INLINE void chemistry_init_backend(struct swift_params* parameter_file, total_frac); } } - } /** @@ -673,29 +633,29 @@ static INLINE void chemistry_init_backend(struct swift_params* parameter_file, * model. */ static INLINE void chemistry_print_backend( - const struct chemistry_global_data* data) { + const struct chemistry_global_data *data) { if (data->use_firehose_wind_model) { if (data->diffusion_flag) { - message("Chemistry model is 'KIARA' tracking %d elements with the " - "firehose wind model and metal diffusion.", - chemistry_element_count); - } - else { - message("Chemistry model is 'KIARA' tracking %d elements with " - "the firehose wind model.", - chemistry_element_count); + message( + "Chemistry model is 'KIARA' tracking %d elements with the " + "firehose wind model and metal diffusion.", + chemistry_element_count); + } else { + message( + "Chemistry model is 'KIARA' tracking %d elements with " + "the firehose wind model.", + chemistry_element_count); } - } - else { + } else { if (data->diffusion_flag) { - message("Chemistry model is 'KIARA' tracking %d elements with " - " metal diffusion on.", - chemistry_element_count); - } - else { + message( + "Chemistry model is 'KIARA' tracking %d elements with " + " metal diffusion on.", + chemistry_element_count); + } else { message("Chemistry model is 'KIARA' tracking %d elements.", - chemistry_element_count); + chemistry_element_count); } } } @@ -707,15 +667,14 @@ static INLINE void chemistry_print_backend( * * @param pi Wind particle (not updated). * @param Mach Stream Mach number vs ambient - * @param r_stream Current radius of stream + * @param r_stream Current radius of stream * @param cd #chemistry_global_data containing chemistry information. * */ -__attribute__((always_inline)) INLINE static float -firehose_recoupling_criterion(struct part *p, - const float Mach, - const float r_stream, - const struct chemistry_global_data* cd) { +__attribute__((always_inline)) INLINE static float +firehose_recoupling_criterion(struct part *p, const float Mach, + const float r_stream, + const struct chemistry_global_data *cd) { if (!cd->use_firehose_wind_model) return 0.f; @@ -723,10 +682,11 @@ firehose_recoupling_criterion(struct part *p, const double u = hydro_get_drifted_comoving_internal_energy(p); const double u_max = max(u, p->chemistry_data.u_ambient); const double u_diff = fabs(u - p->chemistry_data.u_ambient) / u_max; - if (Mach < cd->firehose_recoupling_mach && - u_diff < cd->firehose_recoupling_u_factor) rs = -1.f; + if (Mach < cd->firehose_recoupling_mach && + u_diff < cd->firehose_recoupling_u_factor) + rs = -1.f; - const float exchanged_mass_frac = + const float exchanged_mass_frac = p->chemistry_data.exchanged_mass / hydro_get_mass(p); if (exchanged_mass_frac > cd->firehose_recoupling_fmix) rs = -1.f; @@ -776,14 +736,13 @@ __attribute__((always_inline)) INLINE static void chemistry_prepare_force( * @param dt Time step (in physical units). */ __attribute__((always_inline)) INLINE static void chemistry_end_force( - struct part* restrict p, struct xpart *xp, - const struct cosmology* cosmo, - const int with_cosmology, const double time, const double dt, - const struct chemistry_global_data* cd) { + struct part *restrict p, struct xpart *xp, const struct cosmology *cosmo, + const int with_cosmology, const double time, const double dt, + const struct chemistry_global_data *cd) { if (dt == 0.) return; - struct chemistry_part_data* ch = &p->chemistry_data; + struct chemistry_part_data *ch = &p->chemistry_data; const float h_inv = 1.f / p->h; const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ @@ -791,14 +750,13 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( const float factor = h_inv_dim * h_inv; if (cd->use_firehose_wind_model && ch->dm > 0.f) { - struct cooling_part_data* co = &p->cooling_data; + struct cooling_part_data *co = &p->cooling_data; const float m = hydro_get_mass(p); - const float v = sqrtf(xp->v_full[0] * xp->v_full[0] + - xp->v_full[1] * xp->v_full[1] + - xp->v_full[2] * xp->v_full[2]); - const float dv = sqrtf(ch->dv[0] * ch->dv[0] + - ch->dv[1] * ch->dv[1] + + const float v = + sqrtf(xp->v_full[0] * xp->v_full[0] + xp->v_full[1] * xp->v_full[1] + + xp->v_full[2] * xp->v_full[2]); + const float dv = sqrtf(ch->dv[0] * ch->dv[0] + ch->dv[1] * ch->dv[1] + ch->dv[2] * ch->dv[2]); float dv_phys = dv * cosmo->a_inv; @@ -806,13 +764,10 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( float alpha = 1.f; if (dv >= FIREHOSE_EPSILON_TOLERANCE * v) { - const float v_new[3] = { - xp->v_full[0] + ch->dv[0], - xp->v_full[1] + ch->dv[1], - xp->v_full[2] + ch->dv[2] - }; - const float v_new_norm = sqrtf(v_new[0] * v_new[0] + - v_new[1] * v_new[1] + + const float v_new[3] = {xp->v_full[0] + ch->dv[0], + xp->v_full[1] + ch->dv[1], + xp->v_full[2] + ch->dv[2]}; + const float v_new_norm = sqrtf(v_new[0] * v_new[0] + v_new[1] * v_new[1] + v_new[2] * v_new[2]); /* Apply a kinetic energy limiter */ @@ -826,19 +781,19 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( if (KE_out_of_bounds && dv2 > 0.) { /* Solve the same scaling equation, just with a different target */ - const float target_KE_factor = + const float target_KE_factor = (KE_low_flag) ? FIREHOSE_COOLLIM : FIREHOSE_HEATLIM; const float v_dot_dv = xp->v_full[0] * ch->dv[0] + - xp->v_full[1] * ch->dv[1] + - xp->v_full[2] * ch->dv[2]; - + xp->v_full[1] * ch->dv[1] + + xp->v_full[2] * ch->dv[2]; + /* How to scale all components equally? Solve quadratic: - * v^2 + 2 * alpha * v * dv + alpha^2 * dv^2 = target_KE_factor * v^2 - * + * v^2 + 2 * alpha * v * dv + alpha^2 * dv^2 = target_KE_factor * v^2 + * * Or equivalently: * A * alpha^2 + B * alpha + C = 0 - * + * * where A = 1 * B = 2 * (v * dv) / (dv^2)) * C = (v / dv)^2 * (1 - target_KE_factor) @@ -848,7 +803,7 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( const float discriminant = B * B - 4.f * C; /* For logging */ const double u_drift = hydro_get_drifted_comoving_internal_energy(p); - + if (discriminant >= 0.) { const float alpha1 = (-B - sqrtf(discriminant)) / 2.f; const float alpha2 = (-B + sqrtf(discriminant)) / 2.f; @@ -864,38 +819,35 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( ch->dv[1] *= alpha; ch->dv[2] *= alpha; - message("FIREHOSE_KE_LIMIT p=%lld alpha=%.4g KE_ratio=%.4g v=%.4g " - "dv=%g m=%g dm=%g u=%g du=%g " - "dv[0]=%g dv[1]=%g dv[2]=%g " - "v[0]=%g v[1]=%g v[2]=%g", - p->id, alpha, KE_ratio, v, - dv, m, ch->dm, u_drift, ch->du, - ch->dv[0], ch->dv[1], ch->dv[2], - xp->v_full[0], xp->v_full[1], xp->v_full[2]); - } - else { + message( + "FIREHOSE_KE_LIMIT p=%lld alpha=%.4g KE_ratio=%.4g v=%.4g " + "dv=%g m=%g dm=%g u=%g du=%g " + "dv[0]=%g dv[1]=%g dv[2]=%g " + "v[0]=%g v[1]=%g v[2]=%g", + p->id, alpha, KE_ratio, v, dv, m, ch->dm, u_drift, ch->du, + ch->dv[0], ch->dv[1], ch->dv[2], xp->v_full[0], xp->v_full[1], + xp->v_full[2]); + } else { ch->dv[0] = 0.f; ch->dv[1] = 0.f; ch->dv[2] = 0.f; - message("FIREHOSE_KE_LIMIT p=%lld alpha=INVALID KE_ratio=%.4g v=%.4g " - "dv=%g m=%g dm=%g u=%g du=%g " - "dv[0]=%g dv[1]=%g dv[2]=%g " - "v[0]=%g v[1]=%g v[2]=%g", - p->id, KE_ratio, v, - dv, m, ch->dm, u_drift, ch->du, - ch->dv[0], ch->dv[1], ch->dv[2], - xp->v_full[0], xp->v_full[1], xp->v_full[2]); + message( + "FIREHOSE_KE_LIMIT p=%lld alpha=INVALID KE_ratio=%.4g v=%.4g " + "dv=%g m=%g dm=%g u=%g du=%g " + "dv[0]=%g dv[1]=%g dv[2]=%g " + "v[0]=%g v[1]=%g v[2]=%g", + p->id, KE_ratio, v, dv, m, ch->dm, u_drift, ch->du, ch->dv[0], + ch->dv[1], ch->dv[2], xp->v_full[0], xp->v_full[1], + xp->v_full[2]); } /* Recompute the new updated limited values to set v_sig */ - dv2 = ch->dv[0] * ch->dv[0] + - ch->dv[1] * ch->dv[1] + + dv2 = ch->dv[0] * ch->dv[0] + ch->dv[1] * ch->dv[1] + ch->dv[2] * ch->dv[2]; dv_phys = sqrtf(dv2) * cosmo->a_inv; } - } - else { + } else { /* Cancel everything if the kick is so small it doesn't matter */ dv_phys = 0.f; } @@ -917,7 +869,7 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( ch->du *= alpha * alpha; double u_new = u + ch->du; - const double u_floor = + const double u_floor = cd->firehose_u_floor / cosmo->a_factor_internal_energy; if (u_new < u_floor) { u_new = u_floor; @@ -928,37 +880,30 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( const double u_eps = fabs(ch->du) / u; if (u_eps > FIREHOSE_EPSILON_TOLERANCE) { - #ifdef FIREHOSE_DEBUG_CHECKS +#ifdef FIREHOSE_DEBUG_CHECKS if (!isfinite(u) || !isfinite(ch->du)) { - message("FIREHOSE_BAD p=%lld u=%g du=%g dv_phys=%g m=%g dm=%g", - p->id, - u, - ch->du, - dv_phys, - m, - ch->dm); + message("FIREHOSE_BAD p=%lld u=%g du=%g dv_phys=%g m=%g dm=%g", p->id, + u, ch->du, dv_phys, m, ch->dm); } - #endif +#endif const double energy_frac = (u > 0.) ? u_new / u : 1.; if (energy_frac > FIREHOSE_HEATLIM) u_new = FIREHOSE_HEATLIM * u; if (energy_frac < FIREHOSE_COOLLIM) u_new = FIREHOSE_COOLLIM * u; - /* If it's in subgrid ISM mode, use additional heat to - * lower ISM cold fraction */ - const int firehose_add_heat_to_ISM = - (p->cooling_data.subgrid_temp > 0.f && - p->cooling_data.subgrid_fcold > 0.f && - ch->du > 0.); + /* If it's in subgrid ISM mode, use additional heat to + * lower ISM cold fraction */ + const int firehose_add_heat_to_ISM = + (p->cooling_data.subgrid_temp > 0.f && + p->cooling_data.subgrid_fcold > 0.f && ch->du > 0.); if (firehose_add_heat_to_ISM) { - /* 0.8125 is mu for a fully neutral gas with XH=0.75; - * approximate but good enough */ - const double T_conv = - cd->temp_to_u_factor / cosmo->a_factor_internal_energy; - const double u_cold = - 0.8125 * p->cooling_data.subgrid_temp * T_conv; + /* 0.8125 is mu for a fully neutral gas with XH=0.75; + * approximate but good enough */ + const double T_conv = + cd->temp_to_u_factor / cosmo->a_factor_internal_energy; + const double u_cold = 0.8125 * p->cooling_data.subgrid_temp * T_conv; const double delta_u = u - u_cold; double f_evap = 0.; @@ -966,8 +911,7 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( if (delta_u > FIREHOSE_EPSILON_TOLERANCE * u) { f_evap = ch->du / delta_u; f_evap = min(f_evap, 1.0); - } - else { + } else { f_evap = 1.0; } @@ -982,7 +926,7 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( if (p->cooling_data.subgrid_fcold <= 0.f) { p->cooling_data.subgrid_temp = 0.f; - p->cooling_data.subgrid_dens = + p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); p->cooling_data.subgrid_fcold = 0.f; } @@ -993,8 +937,7 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( hydro_set_physical_internal_energy(p, xp, cosmo, u_phys); hydro_set_drifted_physical_internal_energy(p, cosmo, NULL, u_phys); - } - else { + } else { ch->du = 0.; } @@ -1016,8 +959,7 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( const float Z_eps = fabs(ch->dm_Z[elem]) / old_mass_Z; if (Z_eps >= FIREHOSE_EPSILON_TOLERANCE) { - ch->metal_mass_fraction[elem] = - (old_mass_Z + ch->dm_Z[elem]) / m; + ch->metal_mass_fraction[elem] = (old_mass_Z + ch->dm_Z[elem]) / m; } } @@ -1027,9 +969,9 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( } if (dust_eps >= FIREHOSE_EPSILON_TOLERANCE) { - const float old_dust_mass_Z = + const float old_dust_mass_Z = co->dust_mass_fraction[elem] * co->dust_mass; - co->dust_mass_fraction[elem] = + co->dust_mass_fraction[elem] = (old_dust_mass_Z + ch->dm_dust_Z[elem]) / new_dust_mass; } } @@ -1053,24 +995,24 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( if (ch->metal_mass_fraction[chemistry_element_H] > 1.f || ch->metal_mass_fraction[chemistry_element_H] < 0.f) { for (int i = chemistry_element_H; i < chemistry_element_count; i++) { - warning("\telem[%d] is %g", - i, ch->metal_mass_fraction[i]); + warning("\telem[%d] is %g", i, ch->metal_mass_fraction[i]); } - error("Hydrogen fraction exeeds unity or is negative for" - " particle id=%lld due to firehose exchange", p->id); + error( + "Hydrogen fraction exeeds unity or is negative for" + " particle id=%lld due to firehose exchange", + p->id); } /* Update stream radius for stream particle */ if (p->decoupled) { - const float stream_growth_factor = - 1.f + ch->dm / hydro_get_mass(p); + const float stream_growth_factor = 1.f + ch->dm / hydro_get_mass(p); ch->radius_stream *= sqrtf(stream_growth_factor); - const double c_s = + const double c_s = sqrt(ch->u_ambient * hydro_gamma * hydro_gamma_minus_one); const float Mach = dv_phys / (cosmo->a_factor_sound_speed * c_s); - ch->radius_stream = + ch->radius_stream = firehose_recoupling_criterion(p, Mach, ch->radius_stream, cd); } } @@ -1085,10 +1027,10 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( /* Add diffused metals to particle */ const float dZ_tot = ch->dZ_dt_total * dt * factor; - const float new_metal_mass_fraction_total - = ch->metal_mass_fraction_total + dZ_tot; + const float new_metal_mass_fraction_total = + ch->metal_mass_fraction_total + dZ_tot; if (ch->metal_mass_fraction_total > 0.f) { - const float abs_fractional_change = + const float abs_fractional_change = fabs(dZ_tot) / ch->metal_mass_fraction_total; /* Check if dZ is bigger than 1/4 of the Z */ if (abs_fractional_change > cd->max_fractional_Z_transfer) { @@ -1098,43 +1040,33 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( /* Handle edge case where diffusion leads to negative metallicity */ if (new_metal_mass_fraction_total < 0.f) { - warning("Metal diffusion led to negative metallicity!\n" - "\tpid=%lld\n\tdt=%g\n\tZ=%g\n\tdZ_dt=%g\n" - "\tdZtot=%g\n\tZnewtot=%g\n\tfactor=%g", - p->id, - dt, - ch->metal_mass_fraction_total, - ch->dZ_dt_total, - dZ_tot, - new_metal_mass_fraction_total, - factor); + warning( + "Metal diffusion led to negative metallicity!\n" + "\tpid=%lld\n\tdt=%g\n\tZ=%g\n\tdZ_dt=%g\n" + "\tdZtot=%g\n\tZnewtot=%g\n\tfactor=%g", + p->id, dt, ch->metal_mass_fraction_total, ch->dZ_dt_total, dZ_tot, + new_metal_mass_fraction_total, factor); reset_time_derivatives = true; } /* Handle edge case where diffusion leads to super-unity metallicity */ if (new_metal_mass_fraction_total > 1.f) { - warning("Metal diffusion led to metal fractions above unity!\n" - "pid=%lld\n\tdt=%g\n\tZ=%g\n\tdZ_dt=%g\n" - "\tdZtot=%g\n\tZnewtot=%g\n\tfactor=%g", - p->id, - dt, - ch->metal_mass_fraction_total, - ch->dZ_dt_total, - dZ_tot, - new_metal_mass_fraction_total, - factor); + warning( + "Metal diffusion led to metal fractions above unity!\n" + "pid=%lld\n\tdt=%g\n\tZ=%g\n\tdZ_dt=%g\n" + "\tdZtot=%g\n\tZnewtot=%g\n\tfactor=%g", + p->id, dt, ch->metal_mass_fraction_total, ch->dZ_dt_total, dZ_tot, + new_metal_mass_fraction_total, factor); reset_time_derivatives = true; } - /* Add individual element contributions from diffusion */ for (int elem = 0; elem < chemistry_element_count; elem++) { const float dZ = ch->dZ_dt[elem] * dt * factor; - const float new_metal_fraction_elem - = ch->metal_mass_fraction[elem] + dZ; + const float new_metal_fraction_elem = ch->metal_mass_fraction[elem] + dZ; if (ch->metal_mass_fraction[elem] > 0.f) { - const float abs_fractional_change = + const float abs_fractional_change = fabs(dZ) / ch->metal_mass_fraction[elem]; if (abs_fractional_change > cd->max_fractional_Z_transfer) { reset_time_derivatives = true; @@ -1143,31 +1075,21 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( /* Make sure that the metallicity is 0 <= x <= 1 */ if (new_metal_fraction_elem < 0.f) { - warning("Z[elem] < 0! pid=%lld, dt=%g, elem=%d, Z=%g, dZ_dt=%g, dZ=%g, " - "dZtot=%g Ztot=%g Zdust=%g.", - p->id, - dt, - elem, - ch->metal_mass_fraction[elem], - ch->dZ_dt[elem], - dZ, - dZ_tot, - ch->metal_mass_fraction_total, - p->cooling_data.dust_mass_fraction[elem]); + warning( + "Z[elem] < 0! pid=%lld, dt=%g, elem=%d, Z=%g, dZ_dt=%g, dZ=%g, " + "dZtot=%g Ztot=%g Zdust=%g.", + p->id, dt, elem, ch->metal_mass_fraction[elem], ch->dZ_dt[elem], dZ, + dZ_tot, ch->metal_mass_fraction_total, + p->cooling_data.dust_mass_fraction[elem]); reset_time_derivatives = true; } if (new_metal_fraction_elem > 1.f) { - warning("Z[elem] > 1! pid=%lld, dt=%g, elem=%d, Z=%g, dZ_dt=%g, " - "dZ=%g, dZtot=%g Ztot=%g.", - p->id, - dt, - elem, - ch->metal_mass_fraction[elem], - ch->dZ_dt[elem], - dZ, - dZ_tot, - ch->metal_mass_fraction_total); + warning( + "Z[elem] > 1! pid=%lld, dt=%g, elem=%d, Z=%g, dZ_dt=%g, " + "dZ=%g, dZtot=%g Ztot=%g.", + p->id, dt, elem, ch->metal_mass_fraction[elem], ch->dZ_dt[elem], dZ, + dZ_tot, ch->metal_mass_fraction_total); reset_time_derivatives = true; } } @@ -1179,8 +1101,7 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( ch->dZ_dt[elem] = 0.f; } return; - } - else { + } else { #if COOLING_GRACKLE_MODE >= 2 if (ch->metal_mass_fraction_total > 0.f) { /* Add diffused dust to particle, in proportion to added metals */ @@ -1194,16 +1115,15 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( /* Add individual element contributions from diffusion */ for (int elem = 0; elem < chemistry_element_count; elem++) { const float dZ = ch->dZ_dt[elem] * dt * factor; - const float new_metal_fraction_elem = - ch->metal_mass_fraction[elem] + dZ; - - #if COOLING_GRACKLE_MODE >= 2 - /* Add diffused dust to particle, in proportion to added metals */ + const float new_metal_fraction_elem = ch->metal_mass_fraction[elem] + dZ; + +#if COOLING_GRACKLE_MODE >= 2 + /* Add diffused dust to particle, in proportion to added metals */ if (ch->metal_mass_fraction[elem] > 0.f) { - p->cooling_data.dust_mass_fraction[elem] *= + p->cooling_data.dust_mass_fraction[elem] *= 1.f + dZ / ch->metal_mass_fraction[elem]; } - #endif +#endif /* Treating Z like a passive scalar */ ch->metal_mass_fraction[elem] = new_metal_fraction_elem; @@ -1219,12 +1139,13 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( if (ch->metal_mass_fraction[chemistry_element_H] > 1.f || ch->metal_mass_fraction[chemistry_element_H] < 0.f) { for (int i = chemistry_element_H; i < chemistry_element_count; i++) { - warning("\telem[%d] is %g", - i, ch->metal_mass_fraction[i]); + warning("\telem[%d] is %g", i, ch->metal_mass_fraction[i]); } - error("Hydrogen fraction exeeds unity or is negative for" - " particle id=%lld due to metal diffusion", p->id); + error( + "Hydrogen fraction exeeds unity or is negative for" + " particle id=%lld due to metal diffusion", + p->id); } } @@ -1241,11 +1162,11 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( * @param p Pointer to the particle data. */ __attribute__((always_inline)) INLINE static float chemistry_timestep( - const struct phys_const* restrict phys_const, - const struct cosmology* restrict cosmo, - const struct unit_system* restrict us, - const struct hydro_props* hydro_props, - const struct chemistry_global_data* cd, const struct part* restrict p) { + const struct phys_const *restrict phys_const, + const struct cosmology *restrict cosmo, + const struct unit_system *restrict us, + const struct hydro_props *hydro_props, + const struct chemistry_global_data *cd, const struct part *restrict p) { float dt_chem = FLT_MAX; if (cd->diffusion_flag) { @@ -1259,10 +1180,10 @@ __attribute__((always_inline)) INLINE static float chemistry_timestep( dt_chem = cd->diffusion_beta * rho_phys * h_phys * h_phys / D_phys; if (dt_chem < cd->time_step_min) { message( - "Warning! dZ_dt timestep low: id=%lld (%g Myr) is below " - "time_step_min (%g Myr).", - p->id, dt_chem * cd->time_to_Myr, - cd->time_step_min * cd->time_to_Myr); + "Warning! dZ_dt timestep low: id=%lld (%g Myr) is below " + "time_step_min (%g Myr).", + p->id, dt_chem * cd->time_to_Myr, + cd->time_step_min * cd->time_to_Myr); } dt_chem = max(dt_chem, cd->time_step_min); @@ -1296,8 +1217,8 @@ __attribute__((always_inline)) INLINE static float chemistry_timestep( * @param gas_mass The mass of the gas particle. */ __attribute__((always_inline)) INLINE static void chemistry_bpart_from_part( - struct chemistry_bpart_data* bp_data, - const struct chemistry_part_data* p_data, const double gas_mass) { + struct chemistry_bpart_data *bp_data, + const struct chemistry_part_data *p_data, const double gas_mass) { bp_data->metal_mass_total = p_data->metal_mass_fraction_total * gas_mass; for (int i = 0; i < chemistry_element_count; ++i) { @@ -1317,8 +1238,8 @@ __attribute__((always_inline)) INLINE static void chemistry_bpart_from_part( * @param gas_mass The mass of the gas particle. */ __attribute__((always_inline)) INLINE static void chemistry_add_part_to_bpart( - struct chemistry_bpart_data* bp_data, - const struct chemistry_part_data* p_data, const double gas_mass) { + struct chemistry_bpart_data *bp_data, + const struct chemistry_part_data *p_data, const double gas_mass) { bp_data->metal_mass_total += p_data->metal_mass_fraction_total * gas_mass; for (int i = 0; i < chemistry_element_count; ++i) { @@ -1344,15 +1265,14 @@ __attribute__((always_inline)) INLINE static void chemistry_add_part_to_bpart( * particle that is removed. */ __attribute__((always_inline)) INLINE static void -chemistry_transfer_part_to_bpart(struct chemistry_bpart_data* bp_data, - struct chemistry_part_data* p_data, +chemistry_transfer_part_to_bpart(struct chemistry_bpart_data *bp_data, + struct chemistry_part_data *p_data, const double nibble_mass, const double nibble_fraction) { bp_data->metal_mass_total += p_data->metal_mass_fraction_total * nibble_mass; for (int i = 0; i < chemistry_element_count; ++i) bp_data->metal_mass[i] += p_data->metal_mass_fraction[i] * nibble_mass; - } /** @@ -1362,8 +1282,8 @@ chemistry_transfer_part_to_bpart(struct chemistry_bpart_data* bp_data, * @param swallowed_data The black hole data to use. */ __attribute__((always_inline)) INLINE static void chemistry_add_bpart_to_bpart( - struct chemistry_bpart_data* bp_data, - const struct chemistry_bpart_data* swallowed_data) { + struct chemistry_bpart_data *bp_data, + const struct chemistry_bpart_data *swallowed_data) { bp_data->metal_mass_total += swallowed_data->metal_mass_total; for (int i = 0; i < chemistry_element_count; ++i) { @@ -1380,7 +1300,7 @@ __attribute__((always_inline)) INLINE static void chemistry_add_bpart_to_bpart( * @param n The number of pieces to split into. */ __attribute__((always_inline)) INLINE static void chemistry_split_part( - struct part* p, const double n) { } + struct part *p, const double n) {} /** * @brief Returns the total metallicity (metal mass fraction) of the @@ -1393,7 +1313,7 @@ __attribute__((always_inline)) INLINE static void chemistry_split_part( */ __attribute__((always_inline)) INLINE static float chemistry_get_total_metal_mass_fraction_for_feedback( - const struct part* restrict p) { + const struct part *restrict p) { return p->chemistry_data.metal_mass_fraction_total; } @@ -1407,8 +1327,8 @@ chemistry_get_total_metal_mass_fraction_for_feedback( * * @param p Pointer to the particle data. */ -__attribute__((always_inline)) INLINE static float const* -chemistry_get_metal_mass_fraction_for_feedback(const struct part* restrict p) { +__attribute__((always_inline)) INLINE static float const * +chemistry_get_metal_mass_fraction_for_feedback(const struct part *restrict p) { return p->chemistry_data.metal_mass_fraction; } @@ -1423,7 +1343,7 @@ chemistry_get_metal_mass_fraction_for_feedback(const struct part* restrict p) { */ __attribute__((always_inline)) INLINE static float chemistry_get_star_total_metal_mass_fraction_for_feedback( - const struct spart* restrict sp) { + const struct spart *restrict sp) { return sp->chemistry_data.metal_mass_fraction_total; } @@ -1436,9 +1356,9 @@ chemistry_get_star_total_metal_mass_fraction_for_feedback( * * @param sp Pointer to the particle data. */ -__attribute__((always_inline)) INLINE static float const* +__attribute__((always_inline)) INLINE static float const * chemistry_get_star_metal_mass_fraction_for_feedback( - const struct spart* restrict sp) { + const struct spart *restrict sp) { return sp->chemistry_data.metal_mass_fraction; } @@ -1453,7 +1373,7 @@ chemistry_get_star_metal_mass_fraction_for_feedback( */ __attribute__((always_inline)) INLINE static float chemistry_get_total_metal_mass_fraction_for_cooling( - const struct part* restrict p) { + const struct part *restrict p) { return p->chemistry_data.metal_mass_fraction_total; } @@ -1466,8 +1386,8 @@ chemistry_get_total_metal_mass_fraction_for_cooling( * * @param p Pointer to the particle data. */ -__attribute__((always_inline)) INLINE static float const* -chemistry_get_metal_mass_fraction_for_cooling(const struct part* restrict p) { +__attribute__((always_inline)) INLINE static float const * +chemistry_get_metal_mass_fraction_for_cooling(const struct part *restrict p) { return p->chemistry_data.metal_mass_fraction; } @@ -1482,7 +1402,7 @@ chemistry_get_metal_mass_fraction_for_cooling(const struct part* restrict p) { */ __attribute__((always_inline)) INLINE static float chemistry_get_total_metal_mass_fraction_for_star_formation( - const struct part* restrict p) { + const struct part *restrict p) { return p->chemistry_data.metal_mass_fraction_total; } @@ -1495,9 +1415,9 @@ chemistry_get_total_metal_mass_fraction_for_star_formation( * * @param p Pointer to the particle data. */ -__attribute__((always_inline)) INLINE static float const* +__attribute__((always_inline)) INLINE static float const * chemistry_get_metal_mass_fraction_for_star_formation( - const struct part* restrict p) { + const struct part *restrict p) { return p->chemistry_data.metal_mass_fraction; } @@ -1509,7 +1429,7 @@ chemistry_get_metal_mass_fraction_for_star_formation( * @param p Pointer to the particle data. */ __attribute__((always_inline)) INLINE static float -chemistry_get_total_metal_mass_for_stats(const struct part* restrict p) { +chemistry_get_total_metal_mass_for_stats(const struct part *restrict p) { return p->chemistry_data.metal_mass_fraction_total * hydro_get_mass(p); } @@ -1521,7 +1441,7 @@ chemistry_get_total_metal_mass_for_stats(const struct part* restrict p) { * @param sp Pointer to the star particle data. */ __attribute__((always_inline)) INLINE static float -chemistry_get_star_total_metal_mass_for_stats(const struct spart* restrict sp) { +chemistry_get_star_total_metal_mass_for_stats(const struct spart *restrict sp) { return sp->chemistry_data.metal_mass_fraction_total * sp->mass; } @@ -1533,7 +1453,7 @@ chemistry_get_star_total_metal_mass_for_stats(const struct spart* restrict sp) { * @param bp Pointer to the BH particle data. */ __attribute__((always_inline)) INLINE static float -chemistry_get_bh_total_metal_mass_for_stats(const struct bpart* restrict bp) { +chemistry_get_bh_total_metal_mass_for_stats(const struct bpart *restrict bp) { return bp->chemistry_data.metal_mass_total; } @@ -1546,7 +1466,7 @@ chemistry_get_bh_total_metal_mass_for_stats(const struct bpart* restrict bp) { */ __attribute__((always_inline)) INLINE static float chemistry_get_star_total_metal_mass_fraction_for_luminosity( - const struct spart* restrict sp) { + const struct spart *restrict sp) { return sp->chemistry_data.metal_mass_fraction_total; } diff --git a/src/chemistry/KIARA/chemistry_additions.h b/src/chemistry/KIARA/chemistry_additions.h index 20f3d05697..67831265a1 100644 --- a/src/chemistry/KIARA/chemistry_additions.h +++ b/src/chemistry/KIARA/chemistry_additions.h @@ -34,9 +34,9 @@ * @param hydro_props Additional hydro properties. */ __attribute__((always_inline)) INLINE static void chemistry_kick_extra( - struct part* p, float dt_therm, float dt_grav, float dt_hydro, - float dt_kick_corr, const struct cosmology* cosmo, - const struct hydro_props* hydro_props) {} + struct part *p, float dt_therm, float dt_grav, float dt_hydro, + float dt_kick_corr, const struct cosmology *cosmo, + const struct hydro_props *hydro_props) {} /** * @brief update metal mass fluxes between two interacting particles during @@ -50,7 +50,7 @@ __attribute__((always_inline)) INLINE static void chemistry_kick_extra( * interaction. **/ __attribute__((always_inline)) INLINE static void runner_iact_chemistry_fluxes( - struct part* restrict pi, struct part* restrict pj, float mass_flux, + struct part *restrict pi, struct part *restrict pj, float mass_flux, float flux_dt, int mode) {} #endif // SWIFT_CHEMISTRY_KIARA_ADDITIONS_H diff --git a/src/chemistry/KIARA/chemistry_debug.h b/src/chemistry/KIARA/chemistry_debug.h index b8720722ee..69e2526499 100644 --- a/src/chemistry/KIARA/chemistry_debug.h +++ b/src/chemistry/KIARA/chemistry_debug.h @@ -20,7 +20,7 @@ #define SWIFT_CHEMISTRY_KIARA_DEBUG_H __attribute__((always_inline)) INLINE static void chemistry_debug_particle( - const struct part* p, const struct xpart* xp) { + const struct part *p, const struct xpart *xp) { warning("[PID%lld chemistry_part_data:", p->id); for (int i = 0; i < chemistry_element_count; i++) { diff --git a/src/chemistry/KIARA/chemistry_iact.h b/src/chemistry/KIARA/chemistry_iact.h index b887ea71f2..c049ff67b7 100644 --- a/src/chemistry/KIARA/chemistry_iact.h +++ b/src/chemistry/KIARA/chemistry_iact.h @@ -25,12 +25,14 @@ */ #include "timestep_sync_part.h" + #include /** * @brief Sums ambient quantities for the firehose wind model * - * This is called from runner_iact_chemistry, which is called during the density loop + * This is called from runner_iact_chemistry, which is called during the density + * loop * * @param r2 Comoving square distance between the two particles. * @param dx Comoving vector separating both particles (pi - pj). @@ -55,7 +57,7 @@ __attribute__((always_inline)) INLINE static void firehose_compute_ambient_sym( /* Accumulate ambient neighbour quantities with an SPH gather operation */ if (decoupled_i && !decoupled_j) { - struct chemistry_part_data* chi = &pi->chemistry_data; + struct chemistry_part_data *chi = &pi->chemistry_data; const float hi_inv = 1. / hi; const float ui = r * hi_inv; const float mj = hydro_get_mass(pj); @@ -64,15 +66,10 @@ __attribute__((always_inline)) INLINE static void firehose_compute_ambient_sym( #ifdef FIREHOSE_DEBUG_CHECKS if (!isfinite(mj * eint_j * wi)) { - message("FIREHOSE_BAD pi=%lld ui=%g neighbour pj=%lld uj=%g mj=%g hi=%g" - "wi=%g\n", - pi->id, - eint_i, - pj->id, - eint_j, - mj, - hi, - wi); + message( + "FIREHOSE_BAD pi=%lld ui=%g neighbour pj=%lld uj=%g mj=%g hi=%g" + "wi=%g\n", + pi->id, eint_i, pj->id, eint_j, mj, hi, wi); } #endif @@ -82,7 +79,7 @@ __attribute__((always_inline)) INLINE static void firehose_compute_ambient_sym( } if (!decoupled_i && decoupled_j) { - struct chemistry_part_data* chj = &pj->chemistry_data; + struct chemistry_part_data *chj = &pj->chemistry_data; const float hj_inv = 1. / hj; const float uj = r * hj_inv; const float mi = hydro_get_mass(pi); @@ -90,16 +87,11 @@ __attribute__((always_inline)) INLINE static void firehose_compute_ambient_sym( kernel_eval(uj, &wj); #ifdef FIREHOSE_DEBUG_CHECKS - if (!isfinite(mi * eint_i* wj)) { - message("FIREHOSE_BAD pj=%lld uj=%g neighbour pi=%lld ui=%g mi=%g hj=%g" - "wj=%g\n", - pj->id, - eint_j, - pi->id, - eint_i, - mi, - hj, - wj); + if (!isfinite(mi * eint_i * wj)) { + message( + "FIREHOSE_BAD pj=%lld uj=%g neighbour pi=%lld ui=%g mi=%g hj=%g" + "wj=%g\n", + pj->id, eint_j, pi->id, eint_i, mi, hj, wj); } #endif @@ -112,7 +104,8 @@ __attribute__((always_inline)) INLINE static void firehose_compute_ambient_sym( /** * @brief Sums ambient quantities for the firehose wind model * - * This is called from runner_iact_chemistry, which is called during the density loop + * This is called from runner_iact_chemistry, which is called during the density + * loop * * @param r2 Comoving square distance between the two particles. * @param dx Comoving vector separating both particles (pi - pj). @@ -121,10 +114,11 @@ __attribute__((always_inline)) INLINE static void firehose_compute_ambient_sym( * @param pi First particle. * @param pj Second particle. */ -__attribute__((always_inline)) INLINE static void -firehose_compute_ambient_nonsym( - const float r2, const float dx[3], const float hi, const float hj, - struct part *restrict pi, const struct part *restrict pj) { +__attribute__((always_inline)) INLINE static void +firehose_compute_ambient_nonsym(const float r2, const float dx[3], + const float hi, const float hj, + struct part *restrict pi, + const struct part *restrict pj) { /* Only decoupled winds need the ambient quantities computed because * they are in the stream. */ @@ -134,7 +128,7 @@ firehose_compute_ambient_nonsym( /* A wind particle cannot be in the ambient medium */ if (!decoupled_i || decoupled_j) return; - struct chemistry_part_data* chi = &pi->chemistry_data; + struct chemistry_part_data *chi = &pi->chemistry_data; /* Do accumulation of ambient quantities */ const float eint_j = hydro_get_drifted_comoving_internal_energy(pj); @@ -150,15 +144,10 @@ firehose_compute_ambient_nonsym( #ifdef FIREHOSE_DEBUG_CHECKS const float eint_i = hydro_get_drifted_comoving_internal_energy(pi); if (!isfinite(mj * eint_j * wi)) { - message("FIREHOSE_BAD pi=%lld ui=%g neighbour pj=%lld uj=%g mj=%g hi=%g" - "wi=%g\n", - pi->id, - eint_i, - pj->id, - eint_j, - mj, - hi, - wi); + message( + "FIREHOSE_BAD pi=%lld ui=%g neighbour pj=%lld uj=%g mj=%g hi=%g" + "wi=%g\n", + pi->id, eint_i, pj->id, eint_j, mj, hi, wi); } #endif @@ -191,7 +180,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_chemistry( /* Do not need diffusion properties for wind particles */ if (pi->decoupled || pj->decoupled) return; - + struct chemistry_part_data *chi = &pi->chemistry_data; struct chemistry_part_data *chj = &pj->chemistry_data; @@ -221,11 +210,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_chemistry( const float mi_wj_dr = mi * wj_dr; /* dx[i] is from i -> j, so should dv_ij be from i -> j */ - const float dv_ij[3] = { - pi->v[0] - pj->v[0], - pi->v[1] - pj->v[1], - pi->v[2] - pj->v[2] - }; + const float dv_ij[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; /* Compute the shear tensor, i = spatial direction */ for (int i = 0; i < 3; i++) { @@ -241,7 +227,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_chemistry( chj->shear_tensor[1][i] += dv_ij[1] * dxi_mi_wj_dr; chj->shear_tensor[2][i] += dv_ij[2] * dxi_mi_wj_dr; } - + #if COOLING_GRACKLE_MODE >= 2 /* Sum up local SFR density for computing G0 */ chi->local_sfr_density += wi * max(0.f, pj->sf_data.SFR); @@ -291,11 +277,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_chemistry( const float wi_dr = wi_dx * r_inv; const float mj_wi_dr = mj * wi_dr; - const float dv_ij[3] = { - pi->v[0] - pj->v[0], - pi->v[1] - pj->v[1], - pi->v[2] - pj->v[2] - }; + const float dv_ij[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; /* Compute the shear tensor */ for (int i = 0; i < 3; i++) { @@ -312,8 +295,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_chemistry( } /** - * @brief Computes the mass exchanged between the firehose stream - * and the ambient medium. Note that either i or j could be the stream + * @brief Computes the mass exchanged between the firehose stream + * and the ambient medium. Note that either i or j could be the stream * particle, with j or i being ambient. * * @param r2 Comoving square distance between the two particles. @@ -325,26 +308,27 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_chemistry( * @param xpi The #xpart data of the particle #pi. * @param xpj The #xpart data of the particle #pj. * @param time_base The time base used to convert integer to float time. - * @param ti_current Current integer time for seeding random number generator + * @param ti_current Current integer time for seeding random number generator * @param phys_const Physical constants * @param cd #chemistry_global_data containing chemistry information. * @param v2 velocity difference squared between i and j. * */ -__attribute__((always_inline)) INLINE static float -firehose_compute_mass_exchange( - const float r2, const float dx[3], const float hi, const float hj, - const struct part *pi, const struct part *pj, - const struct xpart *xpi, const struct xpart *xpj, - const float time_base, const integertime_t ti_current, - const struct phys_const* phys_const, const struct chemistry_global_data* cd, - float *v2, - const struct cosmology *cosmo) { +__attribute__((always_inline)) INLINE static float +firehose_compute_mass_exchange(const float r2, const float dx[3], + const float hi, const float hj, + const struct part *pi, const struct part *pj, + const struct xpart *xpi, const struct xpart *xpj, + const float time_base, + const integertime_t ti_current, + const struct phys_const *phys_const, + const struct chemistry_global_data *cd, + float *v2, const struct cosmology *cosmo) { const int i_stream = pi->decoupled; const int j_stream = pj->decoupled; - /* Both particles cannot be in the stream. The one with >0 delay time is + /* Both particles cannot be in the stream. The one with >0 delay time is * the stream particle. At least one particle must be in the stream. */ if (i_stream && j_stream) return 0.f; if (!i_stream && !j_stream) return 0.f; @@ -358,11 +342,11 @@ firehose_compute_mass_exchange( /* For stream particle, make sure the stream radius > 0 */ if (i_stream && pi->chemistry_data.radius_stream <= 0.f) return 0.f; if (j_stream && pj->chemistry_data.radius_stream <= 0.f) return 0.f; - - /* Stream particle cannot be an AGN jet particle - if (i_stream && + + /* Stream particle cannot be an AGN jet particle + if (i_stream && pi->feedback_data.number_of_times_decoupled >= 10000) return 0.f; - if (j_stream && + if (j_stream && pj->feedback_data.number_of_times_decoupled >= 10000) return 0.f;*/ /* Compute the velocity of the stream relative to ambient gas. @@ -375,7 +359,7 @@ firehose_compute_mass_exchange( /* Don't apply above some velocity to avoid jets */ const float v2_phys = *v2 * cosmo->a2_inv; - const float max_v2_phys = + const float max_v2_phys = cd->firehose_max_velocity * cd->firehose_max_velocity; if (v2_phys > max_v2_phys) return 0.f; @@ -415,7 +399,7 @@ firehose_compute_mass_exchange( radius_stream = pi->chemistry_data.radius_stream; mixing_layer_time = mixing_layer_time_i; } - + if (j_stream) { /* j must be the stream here */ dt = dt_j; mi = hydro_get_mass(pj); @@ -441,7 +425,7 @@ firehose_compute_mass_exchange( double dm = 0.; double t_cool_mix = 1.e10 * dt; - /* Mass change is growth due to cooling minus loss due to shearing, + /* Mass change is growth due to cooling minus loss due to shearing, kernel-weighted. */ /* Must compare internal velocity and soundspeed physically */ @@ -453,15 +437,13 @@ firehose_compute_mass_exchange( const float Mach = a_factor_Mach * (v_stream / (c_stream + c_amb)); const float alpha = 0.21f * (0.8f * exp(-3.f * Mach * Mach) + 0.2f); - t_cool_mix = - (mixing_layer_time < 0.f) ? fabs(mixing_layer_time) : t_cool_mix; + t_cool_mix = (mixing_layer_time < 0.f) ? fabs(mixing_layer_time) : t_cool_mix; - const double t_shear = + const double t_shear = a_factor_L_over_V * (radius_stream / (alpha * v_stream)); - const double t_sound = - a_factor_L_over_Cs * (2.f * radius_stream / c_stream); + const double t_sound = a_factor_L_over_Cs * (2.f * radius_stream / c_stream); - const double delta_growth = + const double delta_growth = (4. / (chi * t_sound)) * pow(t_cool_mix / t_sound, -0.25) * dt; double delta_shear = 0.; @@ -474,39 +456,29 @@ firehose_compute_mass_exchange( #ifdef FIREHOSE_DEBUG_CHECKS if (dm < 0.f && i_stream && pj->cooling_data.subgrid_temp > 0.f) { - message("FIREHOSE: z=%g %lld %lld m=%g nHamb=%g rhoamb/rhoi=%g rhoamb/rhoj=%g" - " Tamb=%g Tj/Tamb=%g cstr/camb=%g M=%g r=%g grow=%g shear=%g tshear=%g" - " tcool=%g fexch=%g", - cosmo->z, - pi->id, - pj->id, - pi->mass, - pi->chemistry_data.rho_ambient * cosmo->a3_inv * cd->rho_to_n_cgs, - pi->chemistry_data.rho_ambient / pi->rho, - pi->chemistry_data.rho_ambient / pj->rho, - pi->chemistry_data.u_ambient * cosmo->a_factor_internal_energy / - cd->temp_to_u_factor, - eint_j / - pi->chemistry_data.u_ambient, - c_stream / c_amb, - Mach, - radius_stream * cd->length_to_kpc * cosmo->a, - delta_growth, - delta_shear, - t_shear, - pi->cooling_data.mixing_layer_cool_time, - dm/pi->mass); + message( + "FIREHOSE: z=%g %lld %lld m=%g nHamb=%g rhoamb/rhoi=%g rhoamb/rhoj=%g" + " Tamb=%g Tj/Tamb=%g cstr/camb=%g M=%g r=%g grow=%g shear=%g tshear=%g" + " tcool=%g fexch=%g", + cosmo->z, pi->id, pj->id, pi->mass, + pi->chemistry_data.rho_ambient * cosmo->a3_inv * cd->rho_to_n_cgs, + pi->chemistry_data.rho_ambient / pi->rho, + pi->chemistry_data.rho_ambient / pj->rho, + pi->chemistry_data.u_ambient * cosmo->a_factor_internal_energy / + cd->temp_to_u_factor, + eint_j / pi->chemistry_data.u_ambient, c_stream / c_amb, Mach, + radius_stream * cd->length_to_kpc * cosmo->a, delta_growth, delta_shear, + t_shear, pi->cooling_data.mixing_layer_cool_time, dm / pi->mass); } #endif return dm; } - /** * @brief Computes the particle interaction via the firehose stream model * - * This is called from runner_iact_diffusion, which is called during the force + * This is called from runner_iact_diffusion, which is called during the force * loop * * @param r2 Comoving square distance between the two particles. @@ -525,10 +497,9 @@ firehose_compute_mass_exchange( */ __attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( const float r2, const float dx[3], const float hi, const float hj, - struct part *pi, struct part *pj, - struct xpart *xpi, struct xpart *xpj, + struct part *pi, struct part *pj, struct xpart *xpi, struct xpart *xpj, const float time_base, const integertime_t ti_current, - const struct phys_const* phys_const, const struct chemistry_global_data* cd, + const struct phys_const *phys_const, const struct chemistry_global_data *cd, const struct cosmology *cosmo) { /* Both particles must be within each others smoothing lengths */ @@ -547,17 +518,16 @@ __attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( if (r2 <= 0.f) return; - struct chemistry_part_data* chi = &pi->chemistry_data; - struct chemistry_part_data* chj = &pj->chemistry_data; + struct chemistry_part_data *chi = &pi->chemistry_data; + struct chemistry_part_data *chj = &pj->chemistry_data; /* Compute the amount of mass mixed between stream particle and ambient gas */ float v2 = 0.f; - const float dm = firehose_compute_mass_exchange(r2, dx, hi, hj, pi, pj, xpi, xpj, - time_base, ti_current, - phys_const, cd, &v2, - cosmo); + const float dm = firehose_compute_mass_exchange(r2, dx, hi, hj, pi, pj, xpi, + xpj, time_base, ti_current, + phys_const, cd, &v2, cosmo); float delta_m = fabs(dm); - if (delta_m <= 0.f) return; + if (delta_m <= 0.f) return; /* Limit mass exchange to some fraction of particles' mass */ const float mi = hydro_get_mass(pi); @@ -648,16 +618,16 @@ __attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( float new_pi_u = (wt_ii * old_pi_u + wt_ij * old_pj_u) / mi; float new_pj_u = (wt_ji * old_pi_u + wt_jj * old_pj_u) / mj; - /* Accumulate the velocity exchange due to the mass, conserving the + /* Accumulate the velocity exchange due to the mass, conserving the * momentum */ float new_v2 = 0.f; for (int i = 0; i < 3; i++) { - const float new_pi_v_full_i = + const float new_pi_v_full_i = (wt_ii * xpi->v_full[i] + wt_ij * xpj->v_full[i]) / mi; /* Keep track of the final new velocity */ chi->dv[i] += new_pi_v_full_i - xpi->v_full[i]; - - const float new_pj_v_full_i = + + const float new_pj_v_full_i = (wt_ji * xpi->v_full[i] + wt_jj * xpj->v_full[i]) / mj; chj->dv[i] += new_pj_v_full_i - xpj->v_full[i]; @@ -671,7 +641,7 @@ __attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( /* Limit to minimum of the two particles */ if (delta_KE > min_KE) delta_KE = min_KE; - + const float dKE_split = 0.5f * delta_KE; /* Add in the delta KE difference */ @@ -683,21 +653,12 @@ __attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( chj->du += new_pj_u - old_pj_u; #ifdef FIREHOSE_DEBUG_CHECKS - message("FIREHOSE_EXCHANGE: pi=%lld pj=%lld si=%d sj=%d npi_u=%g npj_u=%g " - "opi_u=%g opj_u=%g dKE=%g nv2=%g ov2=%g", - pi->id, - pj->id, - i_stream, - j_stream, - new_pi_u, - new_pj_u, - old_pi_u, - old_pj_u, - delta_KE, - new_v2, - v2); + message( + "FIREHOSE_EXCHANGE: pi=%lld pj=%lld si=%d sj=%d npi_u=%g npj_u=%g " + "opi_u=%g opj_u=%g dKE=%g nv2=%g ov2=%g", + pi->id, pj->id, i_stream, j_stream, new_pi_u, new_pj_u, old_pi_u, + old_pj_u, delta_KE, new_v2, v2); #endif - } /** @@ -722,18 +683,18 @@ __attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( */ __attribute__((always_inline)) INLINE static void runner_iact_diffusion( const float r2, const float dx[3], const float hi, const float hj, - struct part *restrict pi, struct part *restrict pj, - struct xpart *restrict xpi, struct xpart *restrict xpj, - const float a, + struct part *restrict pi, struct part *restrict pj, + struct xpart *restrict xpi, struct xpart *restrict xpj, const float a, const float H, const float time_base, const integertime_t t_current, - const struct cosmology *cosmo, const int with_cosmology, - const struct phys_const* phys_const, const struct chemistry_global_data *cd) { + const struct cosmology *cosmo, const int with_cosmology, + const struct phys_const *phys_const, + const struct chemistry_global_data *cd) { if (pi->decoupled || pj->decoupled) { if (cd->use_firehose_wind_model) { /* If in wind mode, do firehose wind diffusion */ - firehose_evolve_particle_sym(r2, dx, hi, hj, pi, pj, xpi, xpj, time_base, - t_current, phys_const, cd, cosmo); + firehose_evolve_particle_sym(r2, dx, hi, hj, pi, pj, xpi, xpj, time_base, + t_current, phys_const, cd, cosmo); } return; @@ -794,13 +755,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_diffusion( const float coef_j = coef * mi_dw_r * rhoij_inv; /* Compute the time derivative of metals due to diffusion */ - const float dZ_ij_tot = + const float dZ_ij_tot = chi->metal_mass_fraction_total - chj->metal_mass_fraction_total; chi->dZ_dt_total += coef_i * dZ_ij_tot; chj->dZ_dt_total -= coef_j * dZ_ij_tot; for (int elem = 0; elem < chemistry_element_count; elem++) { - const float dZ_ij = + const float dZ_ij = chi->metal_mass_fraction[elem] - chj->metal_mass_fraction[elem]; chi->dZ_dt[elem] += coef_i * dZ_ij; chj->dZ_dt[elem] -= coef_j * dZ_ij; @@ -831,8 +792,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_diffusion( const float r2, const float dx[3], const float hi, const float hj, struct part *restrict pi, const struct part *restrict pj, const float a, const float H, const float time_base, const integertime_t t_current, - const struct cosmology *cosmo, const int with_cosmology, - const struct chemistry_global_data* cd) { + const struct cosmology *cosmo, const int with_cosmology, + const struct chemistry_global_data *cd) { /* In nonsym case, two cases: depending on whether i is stream or ambient */ if (pi->decoupled || pj->decoupled) return; @@ -878,12 +839,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_diffusion( const float coef_i = coef * mj_dw_r * rhoij_inv; /* Compute the time derivative */ - const float dZ_ij_tot = + const float dZ_ij_tot = chi->metal_mass_fraction_total - chj->metal_mass_fraction_total; chi->dZ_dt_total += coef_i * dZ_ij_tot; for (int elem = 0; elem < chemistry_element_count; elem++) { - const float dZ_ij = + const float dZ_ij = chi->metal_mass_fraction[elem] - chj->metal_mass_fraction[elem]; chi->dZ_dt[elem] += coef_i * dZ_ij; } @@ -930,5 +891,4 @@ runner_iact_nonsym_gradient_diffusion(const float r2, const float dx[3], struct part *restrict pj, const float a, const float H) {} - #endif /* SWIFT_KIARA_CHEMISTRY_IACT_H */ diff --git a/src/chemistry/KIARA/chemistry_io.h b/src/chemistry/KIARA/chemistry_io.h index 44a216d158..f506df45a1 100644 --- a/src/chemistry/KIARA/chemistry_io.h +++ b/src/chemistry/KIARA/chemistry_io.h @@ -30,8 +30,8 @@ * * @return Returns the number of fields to read. */ -INLINE static int chemistry_read_particles(struct part* parts, - struct io_props* list) { +INLINE static int chemistry_read_particles(struct part *parts, + struct io_props *list) { int num = 0; /* List what we want to read */ list[num] = io_make_input_field( @@ -39,9 +39,9 @@ INLINE static int chemistry_read_particles(struct part* parts, UNIT_CONV_NO_UNITS, parts, chemistry_data.metal_mass_fraction); num++; - list[num] = - io_make_input_field("MetalMassFractions", FLOAT, 1, OPTIONAL, UNIT_CONV_NO_UNITS, - parts, chemistry_data.metal_mass_fraction_total); + list[num] = io_make_input_field("MetalMassFractions", FLOAT, 1, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, + chemistry_data.metal_mass_fraction_total); num++; return num; @@ -57,9 +57,9 @@ INLINE static int chemistry_read_particles(struct part* parts, * * @return Returns the number of fields to write. */ -INLINE static int chemistry_write_particles(const struct part* parts, - const struct xpart* xparts, - struct io_props* list, +INLINE static int chemistry_write_particles(const struct part *parts, + const struct xpart *xparts, + struct io_props *list, const int with_cosmology) { int num = 0; @@ -68,7 +68,8 @@ INLINE static int chemistry_write_particles(const struct part* parts, list[num] = io_make_output_field( "ElementMassFractions", FLOAT, chemistry_element_count, UNIT_CONV_NO_UNITS, 0.f, parts, chemistry_data.metal_mass_fraction, - "Fractions of the particles' masses that are in the gas phase of a given element"); + "Fractions of the particles' masses that are in the gas phase of a given " + "element"); num++; list[num] = io_make_output_field( @@ -79,19 +80,18 @@ INLINE static int chemistry_write_particles(const struct part* parts, list[num] = io_make_output_field( "DiffusionCoefficients", FLOAT, 1, UNIT_CONV_DIFF_COEFF, 0.f, parts, - chemistry_data.diffusion_coefficient, - "The full diffusion coefficient"); + chemistry_data.diffusion_coefficient, "The full diffusion coefficient"); num++; - list[num] = io_make_output_field( - "DecouplingDelayTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, - feedback_data.decoupling_delay_time, - "Maximum time left as a firehose wind particle"); + list[num] = + io_make_output_field("DecouplingDelayTimes", FLOAT, 1, UNIT_CONV_TIME, + 0.f, parts, feedback_data.decoupling_delay_time, + "Maximum time left as a firehose wind particle"); num++; list[num] = io_make_output_field( "NumberOfTimesDecoupled", INT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, - feedback_data.number_of_times_decoupled, + feedback_data.number_of_times_decoupled, "Number of times decoupled. Units of 1 are from SF feedback," "units of 1000 are from non-jet AGN feedback," "units of 100000 are from jet AGN feedback"); @@ -99,18 +99,15 @@ INLINE static int chemistry_write_particles(const struct part* parts, #ifdef KIARA_DEBUG_CHECKS list[num] = io_make_output_field( - "ElementDiffusionRates", FLOAT, chemistry_element_count, - UNIT_CONV_DIFF_RATE, - 0.f, parts, - chemistry_data.dZ_dt, + "ElementDiffusionRates", FLOAT, chemistry_element_count, + UNIT_CONV_DIFF_RATE, 0.f, parts, chemistry_data.dZ_dt, "The rate of transfer of metal concentration for each element"); num++; - list[num] = io_make_output_field( - "MetalDiffusionRates", FLOAT, 1, UNIT_CONV_DIFF_RATE, - 0.f, parts, - chemistry_data.dZ_dt_total, - "The rate of transfer of total metal concentration"); + list[num] = + io_make_output_field("MetalDiffusionRates", FLOAT, 1, UNIT_CONV_DIFF_RATE, + 0.f, parts, chemistry_data.dZ_dt_total, + "The rate of transfer of total metal concentration"); num++; #endif @@ -125,8 +122,8 @@ INLINE static int chemistry_write_particles(const struct part* parts, * * @return Returns the number of fields to write. */ -INLINE static int chemistry_write_sparticles(const struct spart* sparts, - struct io_props* list) { +INLINE static int chemistry_write_sparticles(const struct spart *sparts, + struct io_props *list) { int num = 0; @@ -146,7 +143,6 @@ INLINE static int chemistry_write_sparticles(const struct spart* sparts, return num; } - /** * @brief Specifies which sink fields to write to a dataset * @@ -154,14 +150,13 @@ INLINE static int chemistry_write_sparticles(const struct spart* sparts, * @param list The list of i/o properties to write. * * @return Returns the number of fields to write. - * required by src/common_io.c + * required by src/common_io.c */ -INLINE static int chemistry_write_sinkparticles(const struct sink* sinks, - struct io_props* list) { +INLINE static int chemistry_write_sinkparticles(const struct sink *sinks, + struct io_props *list) { return 0; } - /** * @brief Specifies which black hole particle fields to write to a dataset * @@ -170,8 +165,8 @@ INLINE static int chemistry_write_sinkparticles(const struct sink* sinks, * * @return Returns the number of fields to write. */ -INLINE static int chemistry_write_bparticles(const struct bpart* bparts, - struct io_props* list) { +INLINE static int chemistry_write_bparticles(const struct bpart *bparts, + struct io_props *list) { int num = 0; @@ -182,10 +177,10 @@ INLINE static int chemistry_write_bparticles(const struct bpart* bparts, "Masses of the BH particles in a given element"); num++; - list[num] = io_make_output_field("MetalMasses", FLOAT, chemistry_element_count, - UNIT_CONV_MASS, 0.f, bparts, - chemistry_data.metal_mass_total, - "Masses of the BH particles in a metals"); + list[num] = io_make_output_field("MetalMasses", FLOAT, + chemistry_element_count, UNIT_CONV_MASS, 0.f, + bparts, chemistry_data.metal_mass_total, + "Masses of the BH particles in a metals"); num++; return num; @@ -200,7 +195,7 @@ INLINE static int chemistry_write_bparticles(const struct bpart* bparts, * @param e The #engine. */ INLINE static void chemistry_write_flavour(hid_t h_grp, hid_t h_grp_columns, - const struct engine* e) { + const struct engine *e) { /* Write the chemistry model */ io_write_attribute_s(h_grp, "Chemistry Model", "KIARA"); diff --git a/src/chemistry/KIARA/chemistry_struct.h b/src/chemistry/KIARA/chemistry_struct.h index e3a3e9bdae..06a309fd9e 100644 --- a/src/chemistry/KIARA/chemistry_struct.h +++ b/src/chemistry/KIARA/chemistry_struct.h @@ -19,9 +19,9 @@ #ifndef SWIFT_CHEMISTRY_STRUCT_KIARA_H #define SWIFT_CHEMISTRY_STRUCT_KIARA_H -#define FIREHOSE_COOLLIM 0.1f /* -u_new / u */ -#define FIREHOSE_HEATLIM 10.f /* +u_new / u */ -#define FIREHOSE_EPSILON_TOLERANCE 1.e-6 /* Minimum rel. difference to add */ +#define FIREHOSE_COOLLIM 0.1f /* -u_new / u */ +#define FIREHOSE_HEATLIM 10.f /* +u_new / u */ +#define FIREHOSE_EPSILON_TOLERANCE 1.e-6 /* Minimum rel. difference to add */ /** * @brief The individual elements traced in the KIARA model. @@ -73,13 +73,13 @@ struct chemistry_global_data { /*! The timestep beta value from Parshikov & Medin 2002 equation 41 */ float diffusion_beta; - + /*! The minimum time step size in internal units for diffusion */ float time_step_min; - + /*! A limiter for how much Z/Z_init can be transferred (~0.25) */ float max_fractional_Z_transfer; - + /*! The metal diffusion coefficient (Smag ~0.23) */ float C_Smagorinsky; @@ -101,14 +101,16 @@ struct chemistry_global_data { /*! Firehose wind model mixing mass threshold for recoupling */ float firehose_recoupling_fmix; - /*! Firehose threshold relative velocity (km/s) above which model is turned off */ + /*! Firehose threshold relative velocity (km/s) above which model is turned + * off */ float firehose_max_velocity; - /*! Firehose maximum fraction of particles' mass that can be mixed in a single step */ + /*! Firehose maximum fraction of particles' mass that can be mixed in a single + * step */ float firehose_max_fmix_per_step; /*! Dust sputtering constant */ - float dust_sputtering_const; + float dust_sputtering_const; /*! Conversion factor from internal mass unit to solar mass */ double mass_to_solar_mass; @@ -118,7 +120,7 @@ struct chemistry_global_data { double rho_to_n_cgs; /*! Converts temperature to internal energy */ - float temp_to_u_factor; + float temp_to_u_factor; /*! Converst temperature to internal units */ float T_to_internal; @@ -178,7 +180,8 @@ struct chemistry_part_data { float shear_tensor[3][3]; #if COOLING_GRACKLE_MODE >= 2 - /*! SFR density (physical) within smoothing kernel needed for G0 calculation */ + /*! SFR density (physical) within smoothing kernel needed for G0 calculation + */ float local_sfr_density; #endif @@ -202,7 +205,7 @@ struct chemistry_part_data { /*! Firehose exchanged metal fractions */ float dm_Z[chemistry_element_count]; - + /*! Firehose exchanged dust mass */ float dm_dust; @@ -249,7 +252,7 @@ struct chemistry_bpart_data { /*! Iron mass coming from SNIa */ float iron_mass_from_SNIa; - + /*! Metallicity of converted part. */ float formation_metallicity; }; diff --git a/src/cooling/KIARA/cooling.c b/src/cooling/KIARA/cooling.c index 017e88d741..1609120725 100644 --- a/src/cooling/KIARA/cooling.c +++ b/src/cooling/KIARA/cooling.c @@ -33,10 +33,10 @@ #include "cooling.h" /* Some standard headers. */ +#include #include #include #include -#include /* The grackle library itself */ #include @@ -46,14 +46,14 @@ extern chemistry_data *grackle_data; #include "chemistry.h" #include "cooling_io.h" #include "entropy_floor.h" -#include "star_formation.h" #include "error.h" +#include "fof.h" #include "hydro.h" #include "parser.h" #include "part.h" #include "physical_constants.h" +#include "star_formation.h" #include "units.h" -#include "fof.h" /** * @brief Common operations performed on the cooling function at a @@ -73,12 +73,11 @@ void cooling_update(const struct phys_const *phys_const, const struct pressure_floor_props *pressure_floor, struct cooling_function_data *cooling, struct space *s, const double time) { - + /* set current time */ if (cooling->redshift == -1) { cooling->units.a_value = cosmo->a; - } - else { + } else { cooling->units.a_value = 1. / (1. + cooling->redshift); } } @@ -88,7 +87,7 @@ void cooling_update(const struct phys_const *phys_const, * * @param xp The #xpart to print */ -void cooling_print_fractions(const struct xpart* restrict xp) { +void cooling_print_fractions(const struct xpart *restrict xp) { const struct cooling_xpart_data tmp = xp->cooling_data; #if COOLING_GRACKLE_MODE > 0 @@ -129,11 +128,12 @@ __attribute__((always_inline)) INLINE void cooling_grackle_init_part( gr_float zero = 1.e-20; zero = 0.f; - /* primordial chemistry >= 1: Start with everything neutral (as in dark ages) */ - xp->cooling_data.HI_frac = + /* primordial chemistry >= 1: Start with everything neutral (as in dark ages) + */ + xp->cooling_data.HI_frac = p->chemistry_data.metal_mass_fraction[chemistry_element_H]; xp->cooling_data.HII_frac = zero; - xp->cooling_data.HeI_frac = + xp->cooling_data.HeI_frac = p->chemistry_data.metal_mass_fraction[chemistry_element_He]; xp->cooling_data.HeII_frac = zero; xp->cooling_data.HeIII_frac = zero; @@ -170,13 +170,13 @@ __attribute__((always_inline)) INLINE void cooling_grackle_init_part( * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. */ -void cooling_first_init_part(const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct hydro_props* hydro_props, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* cooling, - struct part* restrict p, - struct xpart* restrict xp) { +void cooling_first_init_part(const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct hydro_props *hydro_props, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *cooling, + struct part *restrict p, + struct xpart *restrict xp) { xp->cooling_data.radiated_energy = 0.f; xp->cooling_data.time_last_event = -cooling->thermal_time; @@ -185,7 +185,7 @@ void cooling_first_init_part(const struct phys_const* restrict phys_const, cooling_grackle_init_part(cooling, p, xp); p->cooling_data.subgrid_fcold = 0.f; - + /* Initialize dust properties */ #if COOLING_GRACKLE_MODE >= 2 p->cooling_data.dust_mass = 0.f; @@ -219,13 +219,12 @@ __attribute__((always_inline)) INLINE void cooling_post_init_part( const struct cooling_function_data *cooling, const struct part *restrict p, struct xpart *restrict xp) {} - /** * @brief Returns the total radiated energy by this particle. * * @param xp The extended particle data */ -float cooling_get_radiated_energy(const struct xpart* restrict xp) { +float cooling_get_radiated_energy(const struct xpart *restrict xp) { return xp->cooling_data.radiated_energy; } @@ -237,9 +236,9 @@ float cooling_get_radiated_energy(const struct xpart* restrict xp) { * @param p The particle to act upon. * @param cooling The properties of the cooling function. */ -__attribute__((always_inline)) INLINE static float cooling_compute_self_shielding( - const struct part* restrict p, - const struct cooling_function_data *cooling) { +__attribute__((always_inline)) INLINE static float +cooling_compute_self_shielding(const struct part *restrict p, + const struct cooling_function_data *cooling) { float fH2_shield = 1.f; #if COOLING_GRACKLE_MODE >= 2 @@ -248,11 +247,11 @@ __attribute__((always_inline)) INLINE static float cooling_compute_self_shieldin /* Compute self-shielding from H */ const float a = cooling->units.a_value; const float a3_inv = 1.f / (a * a * a); - //const double r_in_cm = p->h * a * cooling->units.length_units; - const double rho_grad_norm2 = p->rho_gradient[0] * p->rho_gradient[0] + - p->rho_gradient[1] * p->rho_gradient[1] + + // const double r_in_cm = p->h * a * cooling->units.length_units; + const double rho_grad_norm2 = p->rho_gradient[0] * p->rho_gradient[0] + + p->rho_gradient[1] * p->rho_gradient[1] + p->rho_gradient[2] * p->rho_gradient[2]; - const double rho_grad_norm_inv = + const double rho_grad_norm_inv = (rho_grad_norm2 > 0.) ? 1. / sqrt(rho_grad_norm2) : 0.; const double rho_com = hydro_get_comoving_density(p); double L_eff_com = rho_com * rho_grad_norm_inv; @@ -261,21 +260,22 @@ __attribute__((always_inline)) INLINE static float cooling_compute_self_shieldin L_eff_com = fmin(L_eff_com, L_eff_com_max); L_eff_com = fmax(L_eff_com, L_eff_com_min); const double L_eff_in_cm = L_eff_com * a * cooling->units.length_units; - const double rho_to_n_cgs = + const double rho_to_n_cgs = cooling->units.density_units * 5.97729e23 * 0.75; const double rho_cgs_phys = rho_com * a3_inv * rho_to_n_cgs; const double NH_cgs = rho_cgs_phys * L_eff_in_cm; const double xH = NH_cgs * 3.50877e-24; const double fH_shield = pow(1.f + xH, -1.62) * exp(-0.149 * xH); - /* Extra self-shielding from H2 if present - DON'T DO THIS HERE SINCE IT IS IN CRACKLE - const float fH2 = p->sf_data.H2_fraction; - if (fH2 > 0.f) { - const double NH2_cgs = fH2 * NH_cgs; - const double DH2_cgs = 1.e-5 * sqrt(2.*1.38e-16 * T_ism * 2.98864e23); - const double xH2 = NH2_cgs * 1.18133e-14; - fH2_shield = 0.9379 * pow(1.f + xH2 / DH2_cgs, -1.879) + 0.03465 * pow(1.f + xH2, -0.473) * exp(-2.293e-4 * sqrt(1.f + xH2)); - message("G0 shield: r=%g xH2=%g NH2=%g fH2sh=%g term1=%g term2=%g term3=%g\n",r_in_cm/3.086e21, xH2, NH2_cgs, fH2_shield, pow(1.f + xH2 / DH2_cgs, -1.879), pow(1.f + xH2, -0.473), exp(-2.293e-4 * sqrt(1.f + xH2))); + /* Extra self-shielding from H2 if present - DON'T DO THIS HERE SINCE IT IS + IN CRACKLE const float fH2 = p->sf_data.H2_fraction; if (fH2 > 0.f) { const + double NH2_cgs = fH2 * NH_cgs; const double DH2_cgs = 1.e-5 * + sqrt(2.*1.38e-16 * T_ism * 2.98864e23); const double xH2 = NH2_cgs + * 1.18133e-14; fH2_shield = 0.9379 * pow(1.f + xH2 / DH2_cgs, -1.879) + + 0.03465 * pow(1.f + xH2, -0.473) * exp(-2.293e-4 * sqrt(1.f + xH2)); + message("G0 shield: r=%g xH2=%g NH2=%g fH2sh=%g term1=%g term2=%g + term3=%g\n",r_in_cm/3.086e21, xH2, NH2_cgs, fH2_shield, pow(1.f + xH2 / + DH2_cgs, -1.879), pow(1.f + xH2, -0.473), exp(-2.293e-4 * sqrt(1.f + xH2))); }*/ fH2_shield *= fH_shield; } @@ -291,65 +291,57 @@ __attribute__((always_inline)) INLINE static float cooling_compute_self_shieldin * @param rho Physical density in system units. * @param cooling The properties of the cooling function. * @param dt The cooling timestep. - * + * */ __attribute__((always_inline)) INLINE static float cooling_compute_G0( - const struct part *restrict p, - const float rho, - const struct cooling_function_data *cooling, - const float mstar, const float ssfr, - const double dt) { + const struct part *restrict p, const float rho, + const struct cooling_function_data *cooling, const float mstar, + const float ssfr, const double dt) { float G0 = 0.f; float fH2_shield = 1.f; /* Determine ISRF in Habing units based on chosen method */ if (cooling->G0_computation_method == 0) { G0 = 0.f; - } - else if (cooling->G0_computation_method == 1) { + } else if (cooling->G0_computation_method == 1) { fH2_shield = cooling_compute_self_shielding(p, cooling); - G0 = fH2_shield * p->chemistry_data.local_sfr_density * - cooling->G0_factor1; - } - else if (cooling->G0_computation_method == 2) { + G0 = fH2_shield * p->chemistry_data.local_sfr_density * cooling->G0_factor1; + } else if (cooling->G0_computation_method == 2) { G0 = ssfr * cooling->G0_factor2; - } - else if (cooling->G0_computation_method == 3) { + } else if (cooling->G0_computation_method == 3) { if (ssfr > 0.) { G0 = ssfr * cooling->G0_factor2; - } - else { + } else { fH2_shield = cooling_compute_self_shielding(p, cooling); - G0 = fH2_shield * p->chemistry_data.local_sfr_density * + G0 = fH2_shield * p->chemistry_data.local_sfr_density * cooling->G0_factor1; } } #if COOLING_GRACKLE_MODE >= 2 - else if (cooling->G0_computation_method==4) { + else if (cooling->G0_computation_method == 4) { /* Remember SNe_ThisTimeStep stores SN **rate** */ G0 = p->cooling_data.SNe_ThisTimeStep * cooling->G0_factorSNe * dt; - } - else if (cooling->G0_computation_method == 5) { + } else if (cooling->G0_computation_method == 5) { float pssfr = max(p->sf_data.SFR, 0.f); pssfr /= max(mstar, 8. * p->mass); - G0 = max(ssfr, pssfr) * cooling->G0_factor2 + + G0 = max(ssfr, pssfr) * cooling->G0_factor2 + p->cooling_data.SNe_ThisTimeStep * cooling->G0_factorSNe * dt; } #endif else { - error("G0_computation_method %d not recognized\n", + error("G0_computation_method %d not recognized\n", cooling->G0_computation_method); } /*if (p->galaxy_mstar * 1.e10 > 1.e9 && p->id % 10000 == 0) { message("G0: id=%lld M*=%g SFR=%g rho_sfr=%g Td=%g fshield=%g G0=%g", - p->id, - mstar * 1.e10, + p->id, + mstar * 1.e10, mstar * 1.e10 * - ssfr / (1.e6 * cooling->time_to_Myr), - p->chemistry_data.local_sfr_density * 0.002 / 1.6, - p->cooling_data.dust_temperature, - fH2_shield, + ssfr / (1.e6 * cooling->time_to_Myr), + p->chemistry_data.local_sfr_density * 0.002 / 1.6, + p->cooling_data.dust_temperature, + fH2_shield, G0); }*/ @@ -361,14 +353,13 @@ __attribute__((always_inline)) INLINE static float cooling_compute_G0( * * @param cooling The properties of the cooling function. */ -void cooling_print_backend(const struct cooling_function_data* cooling) { +void cooling_print_backend(const struct cooling_function_data *cooling) { if (cooling->chemistry.use_grackle == 1) { - message("Cooling function is 'Grackle', mode = %i", + message("Cooling function is 'Grackle', mode = %i", cooling->chemistry.primordial_chemistry); - } - else if (cooling->chemistry.use_grackle == 2) { - message("Cooling function is 'Crackle', mode = %i", + } else if (cooling->chemistry.use_grackle == 2) { + message("Cooling function is 'Crackle', mode = %i", cooling->chemistry.primordial_chemistry); } @@ -377,7 +368,7 @@ void cooling_print_backend(const struct cooling_function_data* cooling) { message("UV background flag = %d", cooling->with_uv_background); message("Metal cooling flag = %i", cooling->chemistry.metal_cooling); message("Self Shielding flag = %i", cooling->self_shielding_method); - message("Max subgrid density (internal units) = %g", + message("Max subgrid density (internal units) = %g", cooling->max_subgrid_density); message("Thermal time = %g", cooling->thermal_time); message("Specific Heating Rates flag = %i", @@ -402,8 +393,8 @@ void cooling_print_backend(const struct cooling_function_data* cooling) { * @param rho Particle density */ #if COOLING_GRACKLE_MODE >= 1 -void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, - const struct xpart* xp, gr_float rho, +void cooling_copy_to_grackle1(grackle_field_data *data, const struct part *p, + const struct xpart *xp, gr_float rho, gr_float species_densities[N_SPECIES]) { /* HI */ species_densities[0] = xp->cooling_data.HI_frac * rho; @@ -430,8 +421,8 @@ void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, data->e_density = &species_densities[5]; } #else -void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, - const struct xpart* xp, gr_float rho, +void cooling_copy_to_grackle1(grackle_field_data *data, const struct part *p, + const struct xpart *xp, gr_float rho, gr_float species_densities[N_SPECIES]) { data->HI_density = NULL; data->HII_density = NULL; @@ -451,11 +442,10 @@ void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, * @param rho Particle density */ #if COOLING_GRACKLE_MODE >= 2 -void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, - const struct xpart* xp, - const struct cooling_function_data* restrict cooling, - const double dt, gr_float rho, - gr_float species_densities[N_SPECIES]) { +void cooling_copy_to_grackle2( + grackle_field_data *data, const struct part *p, const struct xpart *xp, + const struct cooling_function_data *restrict cooling, const double dt, + gr_float rho, gr_float species_densities[N_SPECIES]) { /* HM */ species_densities[6] = xp->cooling_data.HM_frac * rho; data->HM_density = &species_densities[6]; @@ -471,32 +461,37 @@ void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, /* Dust model */ if (cooling->use_grackle_dust_evol == 1) { /* Load dust and metal info */ - species_densities[20] = + species_densities[20] = p->cooling_data.dust_mass / p->mass * species_densities[12]; data->dust_density = &species_densities[20]; - species_densities[21] = - p->cooling_data.SNe_ThisTimeStep * dt / p->mass * species_densities[12]; - /* need to pass the number of SNe per volume of particle; - * recall SNe_ThisTimeStep is the SNe rate */ - - //if (species_densities[21] > 1.e15) message("SNe_density: %g %g %g %g\n",p->mass, species_densities[12], p->cooling_data.SNe_ThisTimeStep, species_densities[21]); + species_densities[21] = + p->cooling_data.SNe_ThisTimeStep * dt / p->mass * species_densities[12]; + /* need to pass the number of SNe per volume of particle; + * recall SNe_ThisTimeStep is the SNe rate */ + + // if (species_densities[21] > 1.e15) message("SNe_density: %g %g %g + // %g\n",p->mass, species_densities[12], p->cooling_data.SNe_ThisTimeStep, + // species_densities[21]); data->SNe_ThisTimeStep = &species_densities[21]; - //if( chemistry_get_total_metal_mass_fraction_for_cooling(p)>0.f) message("Zsm= %g Zp= %g Z= %g Zd= %g",chemistry_get_total_metal_mass_fraction_for_cooling(p), p->chemistry_data.metal_mass_fraction_total, species_densities[19], species_densities[20]); + // if( chemistry_get_total_metal_mass_fraction_for_cooling(p)>0.f) + // message("Zsm= %g Zp= %g Z= %g Zd= + // %g",chemistry_get_total_metal_mass_fraction_for_cooling(p), + // p->chemistry_data.metal_mass_fraction_total, species_densities[19], + // species_densities[20]); /* Determine ISRF in Habing units based on chosen method, -1 == non-ISM */ if (p->cooling_data.subgrid_temp == 0.f) { species_densities[22] = -1.f; - } - else { + } else { species_densities[22] = p->cooling_data.G0; } data->isrf_habing = &species_densities[22]; - - const double rho_grad_norm2 = p->rho_gradient[0] * p->rho_gradient[0] + - p->rho_gradient[1] * p->rho_gradient[1] + + + const double rho_grad_norm2 = p->rho_gradient[0] * p->rho_gradient[0] + + p->rho_gradient[1] * p->rho_gradient[1] + p->rho_gradient[2] * p->rho_gradient[2]; - const double rho_grad_norm_inv = + const double rho_grad_norm_inv = (rho_grad_norm2 > 0.) ? 1. / sqrt(rho_grad_norm2) : 0.; const double rho_com = hydro_get_comoving_density(p); double L_eff_com = rho_com * rho_grad_norm_inv; @@ -510,12 +505,13 @@ void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, /* Load gas metallicities */ for (int i = 0; i < chemistry_element_count; i++) { - species_densities[24 + i] = - max(p->chemistry_data.metal_mass_fraction[i] * - species_densities[12], 0.); - species_densities[24 + chemistry_element_count+i] = + species_densities[24 + i] = max( + p->chemistry_data.metal_mass_fraction[i] * species_densities[12], 0.); + species_densities[24 + chemistry_element_count + i] = max(p->cooling_data.dust_mass_fraction[i] * species_densities[20], 0); - //if (i>0) printf("dust densities: %d %g %g %g\n",i,species_densities[23+chemistry_element_count+i],species_densities[23+chemistry_element_count+i]/data->dust_density[0],p->cooling_data.dust_mass_fraction[i]*p->mass / p->cooling_data.dust_mass); + // if (i>0) printf("dust densities: %d %g %g + // %g\n",i,species_densities[23+chemistry_element_count+i],species_densities[23+chemistry_element_count+i]/data->dust_density[0],p->cooling_data.dust_mass_fraction[i]*p->mass + // / p->cooling_data.dust_mass); } data->He_gas_metalDensity = &species_densities[25]; @@ -525,7 +521,7 @@ void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, data->Ne_gas_metalDensity = &species_densities[29]; data->Mg_gas_metalDensity = &species_densities[30]; data->Si_gas_metalDensity = &species_densities[31]; - data->S_gas_metalDensity = &species_densities[32]; + data->S_gas_metalDensity = &species_densities[32]; data->Ca_gas_metalDensity = &species_densities[32]; data->Fe_gas_metalDensity = &species_densities[34]; /* Load dust metallicities */ @@ -539,8 +535,7 @@ void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, data->S_dust_metalDensity = &species_densities[43]; data->Ca_dust_metalDensity = &species_densities[44]; data->Fe_dust_metalDensity = &species_densities[45]; - } - else { + } else { data->dust_density = NULL; data->SNe_ThisTimeStep = NULL; data->isrf_habing = NULL; @@ -567,11 +562,10 @@ void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, } } #else -void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, - const struct xpart* xp, - const struct cooling_function_data* restrict cooling, - const double dt, gr_float rho, - gr_float species_densities[N_SPECIES]) { +void cooling_copy_to_grackle2( + grackle_field_data *data, const struct part *p, const struct xpart *xp, + const struct cooling_function_data *restrict cooling, const double dt, + gr_float rho, gr_float species_densities[N_SPECIES]) { data->HM_density = NULL; data->H2I_density = NULL; data->H2II_density = NULL; @@ -587,8 +581,8 @@ void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, * @param rho Particle density */ #if COOLING_GRACKLE_MODE >= 3 -void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, - const struct xpart* xp, gr_float rho, +void cooling_copy_to_grackle3(grackle_field_data *data, const struct part *p, + const struct xpart *xp, gr_float rho, gr_float species_densities[N_SPECIES]) { /* DI */ species_densities[9] = xp->cooling_data.DI_frac * rho; @@ -603,8 +597,8 @@ void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, data->HDI_density = &species_densities[11]; } #else -void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, - const struct xpart* xp, gr_float rho, +void cooling_copy_to_grackle3(grackle_field_data *data, const struct part *p, + const struct xpart *xp, gr_float rho, gr_float species_densities[N_SPECIES]) { data->DI_density = NULL; data->DII_density = NULL; @@ -621,8 +615,8 @@ void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, * @param rho The particle density. */ #if COOLING_GRACKLE_MODE >= 1 -void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho) { +void cooling_copy_from_grackle1(grackle_field_data *data, const struct part *p, + struct xpart *xp, gr_float rho) { const double rhoinv = 1.f / rho; /* HI */ @@ -644,8 +638,8 @@ void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, xp->cooling_data.e_frac = *data->e_density * rhoinv; } #else -void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho) {} +void cooling_copy_from_grackle1(grackle_field_data *data, const struct part *p, + struct xpart *xp, gr_float rho) {} #endif /** @@ -657,10 +651,9 @@ void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, * @param rho The particle density. */ #if COOLING_GRACKLE_MODE >= 2 -void cooling_copy_from_grackle2(grackle_field_data* data, struct part* p, - struct xpart* xp, - const struct cooling_function_data* restrict cooling, - gr_float rho) { +void cooling_copy_from_grackle2( + grackle_field_data *data, struct part *p, struct xpart *xp, + const struct cooling_function_data *restrict cooling, gr_float rho) { double rhoinv = 1.f / rho; /* HM */ @@ -673,37 +666,35 @@ void cooling_copy_from_grackle2(grackle_field_data* data, struct part* p, /* Dust model */ if (cooling->use_grackle_dust_evol == 1) { /* Load gas metallicities */ - p->chemistry_data.metal_mass_fraction_total = - *data->metal_density * rhoinv; - p->chemistry_data.metal_mass_fraction[1] = + p->chemistry_data.metal_mass_fraction_total = *data->metal_density * rhoinv; + p->chemistry_data.metal_mass_fraction[1] = *data->He_gas_metalDensity * rhoinv; - p->chemistry_data.metal_mass_fraction[2] = + p->chemistry_data.metal_mass_fraction[2] = *data->C_gas_metalDensity * rhoinv; - p->chemistry_data.metal_mass_fraction[3] = + p->chemistry_data.metal_mass_fraction[3] = *data->N_gas_metalDensity * rhoinv; - p->chemistry_data.metal_mass_fraction[4] = + p->chemistry_data.metal_mass_fraction[4] = *data->O_gas_metalDensity * rhoinv; - p->chemistry_data.metal_mass_fraction[5] = + p->chemistry_data.metal_mass_fraction[5] = *data->Ne_gas_metalDensity * rhoinv; - p->chemistry_data.metal_mass_fraction[6] = + p->chemistry_data.metal_mass_fraction[6] = *data->Mg_gas_metalDensity * rhoinv; - p->chemistry_data.metal_mass_fraction[7] = + p->chemistry_data.metal_mass_fraction[7] = *data->Si_gas_metalDensity * rhoinv; - p->chemistry_data.metal_mass_fraction[8] = + p->chemistry_data.metal_mass_fraction[8] = *data->S_gas_metalDensity * rhoinv; - p->chemistry_data.metal_mass_fraction[9] = + p->chemistry_data.metal_mass_fraction[9] = *data->Ca_gas_metalDensity * rhoinv; - p->chemistry_data.metal_mass_fraction[10] = + p->chemistry_data.metal_mass_fraction[10] = *data->Fe_gas_metalDensity * rhoinv; /* Load dust metallicities */ p->cooling_data.dust_mass = *data->dust_density * p->mass * rhoinv; if (p->cooling_data.dust_mass > 0.5 * p->mass) { - warning("DUST > METALS Mg=%g Zg=%g mdust=%g mmet=%g\n", - p->mass, - chemistry_get_total_metal_mass_fraction_for_cooling(p), - p->cooling_data.dust_mass, + warning("DUST > METALS Mg=%g Zg=%g mdust=%g mmet=%g\n", p->mass, + chemistry_get_total_metal_mass_fraction_for_cooling(p), + p->cooling_data.dust_mass, p->mass * chemistry_get_total_metal_mass_fraction_for_cooling(p)); } @@ -712,28 +703,21 @@ void cooling_copy_from_grackle2(grackle_field_data* data, struct part* p, rhoinv = 1. / *data->dust_density; const double rhodust[chemistry_element_count - 1] = { - *data->He_dust_metalDensity, - *data->C_dust_metalDensity, - *data->N_dust_metalDensity, - *data->O_dust_metalDensity, - *data->Ne_dust_metalDensity, - *data->Mg_dust_metalDensity, - *data->Si_dust_metalDensity, - *data->S_dust_metalDensity, - *data->Ca_dust_metalDensity, - *data->Fe_dust_metalDensity - }; + *data->He_dust_metalDensity, *data->C_dust_metalDensity, + *data->N_dust_metalDensity, *data->O_dust_metalDensity, + *data->Ne_dust_metalDensity, *data->Mg_dust_metalDensity, + *data->Si_dust_metalDensity, *data->S_dust_metalDensity, + *data->Ca_dust_metalDensity, *data->Fe_dust_metalDensity}; /* no Helium metal density */ assert(rhodust[0] == 0.); for (int i = 1; i < chemistry_element_count; i++) { p->cooling_data.dust_mass_fraction[i] = rhodust[i - 1] * rhoinv; - p->cooling_data.dust_mass_fraction[0] += + p->cooling_data.dust_mass_fraction[0] += p->cooling_data.dust_mass_fraction[i]; } - } - else { + } else { for (int i = 1; i < chemistry_element_count; i++) { p->cooling_data.dust_mass_fraction[i] = 0.f; } @@ -741,10 +725,9 @@ void cooling_copy_from_grackle2(grackle_field_data* data, struct part* p, } } #else -void cooling_copy_from_grackle2(grackle_field_data* data, struct part* p, - struct xpart* xp, - const struct cooling_function_data* restrict cooling, - gr_float rho) {} +void cooling_copy_from_grackle2( + grackle_field_data *data, struct part *p, struct xpart *xp, + const struct cooling_function_data *restrict cooling, gr_float rho) {} #endif /** @@ -756,8 +739,8 @@ void cooling_copy_from_grackle2(grackle_field_data* data, struct part* p, * @param rho The particle density. */ #if COOLING_GRACKLE_MODE > 2 -void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho) { +void cooling_copy_from_grackle3(grackle_field_data *data, const struct part *p, + struct xpart *xp, gr_float rho) { const double rhoinv = 1.f / rho; /* DI */ @@ -770,8 +753,8 @@ void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, xp->cooling_data.HDI_frac = *data->HDI_density * rhoinv; } #else -void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho) {} +void cooling_copy_from_grackle3(grackle_field_data *data, const struct part *p, + struct xpart *xp, gr_float rho) {} #endif /** @@ -785,15 +768,12 @@ void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, * @param xp The #xpart. * @param rho The particle density. */ -void cooling_copy_to_grackle(grackle_field_data* data, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, - const struct part* p, const struct xpart* xp, - const double dt, const double T_warm, - gr_float species_densities[N_SPECIES], - gr_float* iact_rates, - int mode) { +void cooling_copy_to_grackle( + grackle_field_data *data, const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, const struct part *p, + const struct xpart *xp, const double dt, const double T_warm, + gr_float species_densities[N_SPECIES], gr_float *iact_rates, int mode) { int i; /* set values */ @@ -814,9 +794,9 @@ void cooling_copy_to_grackle(grackle_field_data* data, data->grid_dimension[0] = GRACKLE_NPART; data->grid_end[0] = GRACKLE_NPART - 1; - /* get particle density, internal energy in + /* get particle density, internal energy in physical coordinates (still code units) */ - double T_subgrid = cooling_get_subgrid_temperature(p, xp); + double T_subgrid = cooling_get_subgrid_temperature(p, xp); /* mode 1 is cooling time, here we want non-subgrid values in all cases */ if (mode == 1) { species_densities[12] = hydro_get_physical_density(p, cosmo); @@ -825,42 +805,40 @@ void cooling_copy_to_grackle(grackle_field_data* data, species_densities[15] = 0.f; data->grid_end[0] = -1; // this signals to crackle to turn off UVB } - /* non-subgrid case, here we set the floor temperature - * by the EoS (if applicable). Note the cold fraction has a small + /* non-subgrid case, here we set the floor temperature + * by the EoS (if applicable). Note the cold fraction has a small * limit otherwise one can get underflows in crackle. */ - else if (p->cooling_data.subgrid_temp == 0. || - p->cooling_data.subgrid_fcold <= 1.e-6) { + else if (p->cooling_data.subgrid_temp == 0. || + p->cooling_data.subgrid_fcold <= 1.e-6) { species_densities[12] = hydro_get_physical_density(p, cosmo); species_densities[13] = hydro_get_physical_internal_energy(p, xp, cosmo); species_densities[14] = T_warm; - /* specific_heating_rate has to be in cgs units; + /* specific_heating_rate has to be in cgs units; no unit conversion done within grackle */ - species_densities[15] = + species_densities[15] = hydro_get_physical_internal_energy_dt(p, cosmo) * cooling->dudt_units; } /* subgrid ISM case, use subgrid ISM values and set floor by T_CMB */ - else { + else { /* Physical sub-grid density*/ - species_densities[12] = + species_densities[12] = cooling_get_subgrid_density(p, xp) * p->cooling_data.subgrid_fcold; /* Physical internal energy */ - species_densities[13] = - cooling_convert_temp_to_u(T_subgrid, xp->cooling_data.e_frac, - cooling, p); + species_densities[13] = cooling_convert_temp_to_u( + T_subgrid, xp->cooling_data.e_frac, cooling, p); /* CMB temp is floor*/ species_densities[14] = cooling->T_CMB_0 * (1.f + cosmo->z); /* If tracking H2, turn off specific heating rate in ISM. */ species_densities[15] = 0.f; if (cooling->ism_adiabatic_heating_method == 1) { - species_densities[15] += - hydro_get_physical_internal_energy_dt(p, cosmo) * - cooling->dudt_units; + species_densities[15] += + hydro_get_physical_internal_energy_dt(p, cosmo) * cooling->dudt_units; } } /* load into grackle structure */ data->density = &species_densities[12]; data->internal_energy = &species_densities[13]; - data->temperature_floor = &species_densities[14]; + data->temperature_floor = &species_densities[14]; data->specific_heating_rate = &species_densities[15]; /* velocity (maybe not needed?) */ @@ -871,12 +849,12 @@ void cooling_copy_to_grackle(grackle_field_data* data, data->y_velocity = &species_densities[17]; data->z_velocity = &species_densities[18]; - cooling_copy_to_grackle1(data, p, xp, - species_densities[12], species_densities); - cooling_copy_to_grackle2(data, p, xp, cooling, dt, - species_densities[12], species_densities); - cooling_copy_to_grackle3(data, p, xp, - species_densities[12], species_densities); + cooling_copy_to_grackle1(data, p, xp, species_densities[12], + species_densities); + cooling_copy_to_grackle2(data, p, xp, cooling, dt, species_densities[12], + species_densities); + cooling_copy_to_grackle3(data, p, xp, species_densities[12], + species_densities); /* RT heating and ionisation rates */ data->RT_heating_rate = &iact_rates[0]; @@ -885,19 +863,18 @@ void cooling_copy_to_grackle(grackle_field_data* data, data->RT_HeII_ionization_rate = &iact_rates[3]; data->RT_H2_dissociation_rate = &iact_rates[4]; - species_densities[19] = - chemistry_get_total_metal_mass_fraction_for_cooling(p) * - species_densities[12]; + species_densities[19] = + chemistry_get_total_metal_mass_fraction_for_cooling(p) * + species_densities[12]; data->metal_density = &species_densities[19]; for (i = 0; i < N_SPECIES; i++) { - if (fpclassify(species_densities[i]) == FP_NAN || - fpclassify(species_densities[i]) == FP_INFINITE) { - error("Passing a non-finite value to grackle! " - "i=%d / %d, species_densities[i]=%g\n", - i, - N_SPECIES, - species_densities[i]); + if (fpclassify(species_densities[i]) == FP_NAN || + fpclassify(species_densities[i]) == FP_INFINITE) { + error( + "Passing a non-finite value to grackle! " + "i=%d / %d, species_densities[i]=%g\n", + i, N_SPECIES, species_densities[i]); } } } @@ -913,10 +890,9 @@ void cooling_copy_to_grackle(grackle_field_data* data, * @param xp The #xpart. * @param rho The particle density. */ -void cooling_copy_from_grackle(grackle_field_data* data, struct part* p, - struct xpart* xp, - const struct cooling_function_data* restrict cooling, - gr_float rho) { +void cooling_copy_from_grackle( + grackle_field_data *data, struct part *p, struct xpart *xp, + const struct cooling_function_data *restrict cooling, gr_float rho) { cooling_copy_from_grackle1(data, p, xp, rho); cooling_copy_from_grackle2(data, p, xp, cooling, rho); @@ -928,7 +904,7 @@ void cooling_copy_from_grackle(grackle_field_data* data, struct part* p, * * @param data The grackle_field_data structure from grackle. */ -void cooling_grackle_free_data(grackle_field_data* data) { +void cooling_grackle_free_data(grackle_field_data *data) { free(data->grid_dimension); free(data->grid_start); @@ -952,14 +928,13 @@ void cooling_grackle_free_data(grackle_field_data* data) { * @return desired quantity based on mode */ gr_float cooling_grackle_driver( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct hydro_props* hydro_props, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, - gr_float* iact_rates, double dt, - double T_warm, int mode) { + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct hydro_props *hydro_props, + const struct cooling_function_data *restrict cooling, + struct part *restrict p, struct xpart *restrict xp, gr_float *iact_rates, + double dt, double T_warm, int mode) { /* set current units for conversion to physical quantities */ code_units units = cooling->units; @@ -968,10 +943,10 @@ gr_float cooling_grackle_driver( gr_float *species_densities; species_densities = (gr_float *)calloc(N_SPECIES, sizeof(gr_float)); grackle_field_data data; - //cooling_grackle_malloc_fields(&data, 1, cooling->chemistry.use_dust_evol); + // cooling_grackle_malloc_fields(&data, 1, cooling->chemistry.use_dust_evol); /* load particle information from particle to grackle data */ - cooling_copy_to_grackle(&data, us, cosmo, cooling, p, xp, dt, T_warm, + cooling_copy_to_grackle(&data, us, cosmo, cooling, p, xp, dt, T_warm, species_densities, iact_rates, mode); /* Run Grackle in desired mode */ @@ -984,9 +959,9 @@ gr_float cooling_grackle_driver( if (solve_chemistry(&units, &data, dt) == 0) { error("Error in Grackle solve_chemistry."); } - //if (solve_chemistry(&units, &data, -dt) == 0) { - // error("Error in Crackle solve_chemistry."); - //} + // if (solve_chemistry(&units, &data, -dt) == 0) { + // error("Error in Crackle solve_chemistry."); + // } /* copy from grackle data to particle */ cooling_copy_from_grackle(&data, p, xp, cooling, species_densities[12]); return_value = data.internal_energy[0]; @@ -1053,14 +1028,13 @@ gr_float cooling_grackle_driver( * * @return cooling time */ -gr_float cooling_time(const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct hydro_props* hydro_properties, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, - const struct part* restrict p, - struct xpart* restrict xp, - const float rhocool, const float ucool) { +gr_float cooling_time(const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct hydro_props *hydro_properties, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + const struct part *restrict p, struct xpart *restrict xp, + const float rhocool, const float ucool) { /* Removes const in declaration*/ struct part p_temp = *p; @@ -1070,8 +1044,9 @@ gr_float cooling_time(const struct phys_const* restrict phys_const, gr_float iact_rates[5] = {0., 0., 0., 0., 0.}; - gr_float cooling_time = cooling_grackle_driver( - phys_const, us, cosmo, hydro_properties, cooling, &p_temp, xp, iact_rates, 0., 0., 1); + gr_float cooling_time = + cooling_grackle_driver(phys_const, us, cosmo, hydro_properties, cooling, + &p_temp, xp, iact_rates, 0., 0., 1); return cooling_time; } @@ -1088,22 +1063,22 @@ gr_float cooling_time(const struct phys_const* restrict phys_const, * @param xp Pointer to the #xpart data. */ float cooling_get_temperature( - const struct phys_const* restrict phys_const, - const struct hydro_props* restrict hydro_properties, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, - const struct part* restrict p, const struct xpart* restrict xp) { + const struct phys_const *restrict phys_const, + const struct hydro_props *restrict hydro_properties, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + const struct part *restrict p, const struct xpart *restrict xp) { /* Remove const in declaration*/ - struct part p_temp = *p; + struct part p_temp = *p; struct xpart xp_temp = *xp; gr_float iact_rates[5] = {0., 0., 0., 0., 0.}; - const float temperature = - cooling_grackle_driver(phys_const, us, cosmo, hydro_properties, - cooling, &p_temp, &xp_temp, iact_rates, 0., 0., 2); + const float temperature = + cooling_grackle_driver(phys_const, us, cosmo, hydro_properties, cooling, + &p_temp, &xp_temp, iact_rates, 0., 0., 2); return temperature; } @@ -1121,65 +1096,62 @@ float cooling_get_temperature( * @param dt The time-step of this particle. */ __attribute__((always_inline)) INLINE void cooling_sputter_dust( - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, const double dt) { + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + struct part *restrict p, struct xpart *restrict xp, const double dt) { - /* Do dust destruction in stream particle */ + /* Do dust destruction in stream particle */ if (cooling->use_grackle_dust_evol && p->cooling_data.dust_mass > 0.f) { const float u_phys = hydro_get_physical_internal_energy(p, xp, cosmo); - const float Tstream = + const float Tstream = cooling_convert_u_to_temp(u_phys, xp->cooling_data.e_frac, cooling, p); - const double Tstream_K = + const double Tstream_K = Tstream * units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); /* Sputtering negligible at low-T */ if (Tstream_K > 1.e4) { - const double rho_cgs = - hydro_get_physical_density(p, cosmo) * - units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + const double rho_cgs = hydro_get_physical_density(p, cosmo) * + units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); /* sputtering timescale, Tsai & Mathews (1995) */ - const double tsp = - 1.7e8 * 3.15569251e7 / units_cgs_conversion_factor(us, UNIT_CONV_TIME) - * (cooling->dust_grainsize / 0.1) * (1.e-27 / rho_cgs) - * (pow(2.e6 / Tstream, 2.5) + 1.0); + const double tsp = 1.7e8 * 3.15569251e7 / + units_cgs_conversion_factor(us, UNIT_CONV_TIME) * + (cooling->dust_grainsize / 0.1) * (1.e-27 / rho_cgs) * + (pow(2.e6 / Tstream, 2.5) + 1.0); const float dust_mass_old = p->cooling_data.dust_mass; /* Update dust mass but limit the destruction */ - p->cooling_data.dust_mass -= + p->cooling_data.dust_mass -= dust_mass_old * (1.f - exp(-3.f * min(dt / tsp, 5.f))); if (p->cooling_data.dust_mass < 0.5f * dust_mass_old) { p->cooling_data.dust_mass = 0.5f * dust_mass_old; } #ifdef FIREHOSE_DEBUG_CHECKS - message("FIREHOSE_SPUT: id=%lld mdust=%g mdustnew=%g T=%g rho=%g " - "tsp_dt_ratio=%g", - p->id, - dust_mass_old, - p->cooling_data.dust_mass, - Tstream, - rho_cgs / 1.673e-24, /* units of mp */ - tsp / dt); + message( + "FIREHOSE_SPUT: id=%lld mdust=%g mdustnew=%g T=%g rho=%g " + "tsp_dt_ratio=%g", + p->id, dust_mass_old, p->cooling_data.dust_mass, Tstream, + rho_cgs / 1.673e-24, /* units of mp */ + tsp / dt); #endif /* factor by which dust mass changed */ const float dust_mass_new = p->cooling_data.dust_mass; const float dust_mass_ratio = dust_mass_new / dust_mass_old; p->chemistry_data.metal_mass_fraction_total = 0.f; - for (int elem = chemistry_element_He; - elem < chemistry_element_count; ++elem) { + for (int elem = chemistry_element_He; elem < chemistry_element_count; + ++elem) { const float Z_dust_elem_old = p->cooling_data.dust_mass_fraction[elem]; const float Z_dust_elem_new = Z_dust_elem_old * dust_mass_ratio; const float Z_elem_old = p->chemistry_data.metal_mass_fraction[elem]; const float elem_mass_old = Z_elem_old * hydro_get_mass(p); - /* This is the positive amount of metal mass to add since we + /* This is the positive amount of metal mass to add since we * are losing dust mass when sputtering. */ - const float delta_metal_mass_elem = + const float delta_metal_mass_elem = (Z_dust_elem_old * dust_mass_old - Z_dust_elem_new * dust_mass_new); const float elem_mass_new = elem_mass_old + delta_metal_mass_elem; const float Z_elem_new = elem_mass_new / hydro_get_mass(p); @@ -1194,7 +1166,7 @@ __attribute__((always_inline)) INLINE void cooling_sputter_dust( } /* Make sure that X + Y + Z = 1 */ - const float Y_He = + const float Y_He = p->chemistry_data.metal_mass_fraction[chemistry_element_He]; p->chemistry_data.metal_mass_fraction[chemistry_element_H] = 1.f - Y_He - p->chemistry_data.metal_mass_fraction_total; @@ -1203,12 +1175,14 @@ __attribute__((always_inline)) INLINE void cooling_sputter_dust( if (p->chemistry_data.metal_mass_fraction[chemistry_element_H] > 1.f || p->chemistry_data.metal_mass_fraction[chemistry_element_H] < 0.f) { for (int i = chemistry_element_H; i < chemistry_element_count; i++) { - warning("\telem[%d] is %g", - i, p->chemistry_data.metal_mass_fraction[i]); + warning("\telem[%d] is %g", i, + p->chemistry_data.metal_mass_fraction[i]); } - error("Hydrogen fraction exeeds unity or is negative for" - " particle id=%lld due to dust sputtering", p->id); + error( + "Hydrogen fraction exeeds unity or is negative for" + " particle id=%lld due to dust sputtering", + p->id); } } } @@ -1227,12 +1201,12 @@ __attribute__((always_inline)) INLINE void cooling_sputter_dust( * @param dt The time-step of this particle. */ __attribute__((always_inline)) INLINE void firehose_cooling_and_dust( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct hydro_props* restrict hydro_props, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, const double dt) { + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct hydro_props *restrict hydro_props, + const struct cooling_function_data *restrict cooling, + struct part *restrict p, struct xpart *restrict xp, const double dt) { /* Initialize cooling time */ p->cooling_data.mixing_layer_cool_time = 0.f; @@ -1240,55 +1214,52 @@ __attribute__((always_inline)) INLINE void firehose_cooling_and_dust( const float u = hydro_get_comoving_internal_energy(p, xp); /* If it's not a firehose particle, just compute particle cooling time */ - if (p->chemistry_data.radius_stream <= 0.f || - p->chemistry_data.rho_ambient <= 0.f) { - p->cooling_data.mixing_layer_cool_time = - cooling_time(phys_const, us, hydro_props, cosmo, cooling, p, xp, - p->rho, u); + if (p->chemistry_data.radius_stream <= 0.f || + p->chemistry_data.rho_ambient <= 0.f) { + p->cooling_data.mixing_layer_cool_time = cooling_time( + phys_const, us, hydro_props, cosmo, cooling, p, xp, p->rho, u); return; } - /* It's a firehose particles, so compute the cooling rate + /* It's a firehose particles, so compute the cooling rate * in the mixing layer */ const float rhocool = 0.5f * (p->chemistry_data.rho_ambient + p->rho); const float ucool = 0.5f * (p->chemistry_data.u_ambient + u); /* +ive if heating -ive if cooling*/ - p->cooling_data.mixing_layer_cool_time = - cooling_time(phys_const, us, hydro_props, cosmo, cooling, p, xp, - rhocool, ucool); + p->cooling_data.mixing_layer_cool_time = cooling_time( + phys_const, us, hydro_props, cosmo, cooling, p, xp, rhocool, ucool); #ifdef FIREHOSE_DEBUG_CHECKS message("FIREHOSE_COOLING: id=%lld nH=%g T=%g rhoamb=%g Tamb=%g tcool=%g", - p->id, - hydro_get_physical_density(p, cosmo) * - cooling->units.density_units * 0.75 / 1.673e-24, + p->id, + hydro_get_physical_density(p, cosmo) * cooling->units.density_units * + 0.75 / 1.673e-24, u_old * cosmo->a_factor_internal_energy / cooling->temp_to_u_factor, - p->chemistry_data.rho_ambient, - p->chemistry_data.u_ambient * - cosmo->a_factor_internal_energy / cooling->temp_to_u_factor, + p->chemistry_data.rho_ambient, + p->chemistry_data.u_ambient * cosmo->a_factor_internal_energy / + cooling->temp_to_u_factor, p->cooling_data.mixing_layer_cool_time); #endif - - cooling_sputter_dust(us, cosmo, cooling, p, xp, dt); + cooling_sputter_dust(us, cosmo, cooling, p, xp, dt); } /** - * @brief Set subgrid ISM properties and initialise chemistry + * @brief Set subgrid ISM properties and initialise chemistry * * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. */ void cooling_init_chemistry( - const struct cooling_function_data* restrict cooling, - struct part* restrict p) { + const struct cooling_function_data *restrict cooling, + struct part *restrict p) { - /* If it's eligible for SF and metal-free, crudely self-enrich to + /* If it's eligible for SF and metal-free, crudely self-enrich to very small level; needed to kick-start Grackle dust. Arbitrary amount to kick-start the model */ const float init_dust_to_gas = 0.2f; const float total_Z = chemistry_get_total_metal_mass_fraction_for_cooling(p); - const float self_Z = + const float self_Z = (1.f - init_dust_to_gas) * cooling->self_enrichment_metallicity; if (p->cooling_data.subgrid_temp > 0.f && total_Z < self_Z) { float Z_sun = 0.f; @@ -1296,7 +1267,7 @@ void cooling_init_chemistry( Z_sun += cooling->chemistry.SolarAbundances[i]; } - /* Distribute the self-enrichment metallicity among elements + /* Distribute the self-enrichment metallicity among elements assuming solar abundance ratios*/ p->chemistry_data.metal_mass_fraction_total = 0.f; p->cooling_data.dust_mass = 0.f; @@ -1304,24 +1275,24 @@ void cooling_init_chemistry( int j = 1; for (int i = chemistry_element_C; i < chemistry_element_count; i++) { /* fraction of gas mass in each element */ - p->chemistry_data.metal_mass_fraction[i] = - (1.f - init_dust_to_gas) * cooling->chemistry.SolarAbundances[j] * - cooling->self_enrichment_metallicity; + p->chemistry_data.metal_mass_fraction[i] = + (1.f - init_dust_to_gas) * cooling->chemistry.SolarAbundances[j] * + cooling->self_enrichment_metallicity; p->chemistry_data.metal_mass_fraction[i] /= Z_sun; /* Update to the new metal mass fraction */ - p->chemistry_data.metal_mass_fraction_total += + p->chemistry_data.metal_mass_fraction_total += p->chemistry_data.metal_mass_fraction[i]; /* fraction of dust mass in each element */ - p->cooling_data.dust_mass_fraction[i] = + p->cooling_data.dust_mass_fraction[i] = init_dust_to_gas * cooling->chemistry.SolarAbundances[j]; p->cooling_data.dust_mass_fraction[i] /= Z_sun; /* Sum up all of the dust mass */ - p->cooling_data.dust_mass += - p->cooling_data.dust_mass_fraction[i] - * p->chemistry_data.metal_mass_fraction[i] * p->mass; + p->cooling_data.dust_mass += p->cooling_data.dust_mass_fraction[i] * + p->chemistry_data.metal_mass_fraction[i] * + p->mass; j++; } @@ -1330,18 +1301,20 @@ void cooling_init_chemistry( /* Since He is fixed at SolarAbundances[0], make sure the hydrogen fraction makes sense, i.e. X_H + Y_He + Z = 1. */ p->chemistry_data.metal_mass_fraction[chemistry_element_H] = - 1.f - p->chemistry_data.metal_mass_fraction[chemistry_element_He] - - p->chemistry_data.metal_mass_fraction_total; + 1.f - p->chemistry_data.metal_mass_fraction[chemistry_element_He] - + p->chemistry_data.metal_mass_fraction_total; if (p->chemistry_data.metal_mass_fraction[chemistry_element_H] > 1.f || - p->chemistry_data.metal_mass_fraction[chemistry_element_H] < 0.f) { + p->chemistry_data.metal_mass_fraction[chemistry_element_H] < 0.f) { for (int i = chemistry_element_H; i < chemistry_element_count; i++) { - warning("\telem[%d] is %g", - i, p->chemistry_data.metal_mass_fraction[i]); + warning("\telem[%d] is %g", i, + p->chemistry_data.metal_mass_fraction[i]); } - error("Hydrogen fraction exeeds unity or is negative for" - " particle id=%lld", p->id); + error( + "Hydrogen fraction exeeds unity or is negative for" + " particle id=%lld", + p->id); } } } @@ -1364,16 +1337,14 @@ void cooling_init_chemistry( * internal units. */ void cooling_do_grackle_cooling( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct hydro_props* hydro_props, - const struct entropy_floor_properties* floor_props, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, - gr_float* iact_rates, - const double dt, const double dt_therm, - const double time) { + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct hydro_props *hydro_props, + const struct entropy_floor_properties *floor_props, + const struct cooling_function_data *restrict cooling, + struct part *restrict p, struct xpart *restrict xp, gr_float *iact_rates, + const double dt, const double dt_therm, const double time) { /* Self-enrich gas if very low metallicity */ cooling_init_chemistry(cooling, p); @@ -1383,20 +1354,24 @@ void cooling_do_grackle_cooling( const float galaxy_ssfr = p->galaxy_data.specific_sfr; /* Compute the ISRF */ - p->cooling_data.G0 = fmax(cooling_compute_G0(p, p->cooling_data.subgrid_dens, cooling, galaxy_mstar, galaxy_ssfr, dt), 0.); + p->cooling_data.G0 = + fmax(cooling_compute_G0(p, p->cooling_data.subgrid_dens, cooling, + galaxy_mstar, galaxy_ssfr, dt), + 0.); /* Compute the entropy floor */ - //const double T_warm = entropy_floor_temperature(p, cosmo, floor_props); + // const double T_warm = entropy_floor_temperature(p, cosmo, floor_props); const double T_warm = warm_ISM_temperature(p, cooling, phys_const, cosmo); - const double u_warm = + const double u_warm = cooling_convert_temp_to_u(T_warm, xp->cooling_data.e_frac, cooling, p); /* Do grackle cooling */ const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo); - //const float T_old = cooling_get_temperature( phys_const, hydro_props, us, cosmo, cooling, p, xp); // for debugging only + // const float T_old = cooling_get_temperature( phys_const, hydro_props, us, + // cosmo, cooling, p, xp); // for debugging only gr_float u_new = u_old; - u_new = cooling_grackle_driver(phys_const, us, cosmo, hydro_props, cooling, - p, xp, iact_rates, dt, T_warm, 0); + u_new = cooling_grackle_driver(phys_const, us, cosmo, hydro_props, cooling, p, + xp, iact_rates, dt, T_warm, 0); /* Apply simulation-wide minimum temperature */ u_new = max(u_new, hydro_props->minimal_internal_energy); @@ -1405,62 +1380,60 @@ void cooling_do_grackle_cooling( /* Calculate the cooling rate */ float cool_du_dt = (u_new - u_old) / dt_therm; - if (p->cooling_data.subgrid_temp == 0.) { - /* Normal cooling; check that we are not going to go + if (p->cooling_data.subgrid_temp == 0.) { + /* Normal cooling; check that we are not going to go * below any of the limits */ if (u_new > GRACKLE_HEATLIM * u_old) u_new = GRACKLE_HEATLIM * u_old; if (u_new < GRACKLE_COOLLIM * u_old) u_new = GRACKLE_COOLLIM * u_old; - //u_new = max(u_new, u_warm); + // u_new = max(u_new, u_warm); /* Rennehan: Recompute the actual thermal evolution after setting min/max */ cool_du_dt = (u_new - u_old) / dt_therm; - /* Update the internal energy time derivative, - * which will be evolved later */ + /* Update the internal energy time derivative, + * which will be evolved later */ hydro_set_physical_internal_energy_dt(p, cosmo, cool_du_dt); - /* If there is any dust outside of the ISM, sputter it + /* If there is any dust outside of the ISM, sputter it * back into gas phase metals */ cooling_sputter_dust(us, cosmo, cooling, p, xp, dt); - } - else { + } else { /* Particle is in subgrid mode; result is stored in subgrid_temp */ - p->cooling_data.subgrid_temp = + p->cooling_data.subgrid_temp = cooling_convert_u_to_temp(u_new, xp->cooling_data.e_frac, cooling, p); /* Set the subgrid cold ISM fraction for particle */ /* Get H number density */ const double rho = hydro_get_physical_density(p, cosmo); - const float X_H = chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; + const float X_H = + chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; const double n_H = rho * X_H / phys_const->const_proton_mass; - const double fcold_max = - cooling_compute_cold_ISM_fraction(n_H, cooling); + const double fcold_max = cooling_compute_cold_ISM_fraction(n_H, cooling); /* Compute cooling time in warm ISM component */ float rhocool = p->rho * (1.f - p->cooling_data.subgrid_fcold); rhocool = fmax(rhocool, 0.001f * p->rho); const float u = hydro_get_comoving_internal_energy(p, xp); - float tcool = - cooling_time(phys_const, us, hydro_props, cosmo, cooling, p, xp, - rhocool, u); + float tcool = cooling_time(phys_const, us, hydro_props, cosmo, cooling, p, + xp, rhocool, u); - /* Evolve fcold upwards by cooling from warm ISM on + /* Evolve fcold upwards by cooling from warm ISM on * relevant cooling timescale */ if (tcool < 0.f) { - p->cooling_data.subgrid_fcold += + p->cooling_data.subgrid_fcold += (fcold_max - p->cooling_data.subgrid_fcold) * (1.f - exp(dt / tcool)); } - /* Compare the adiabatic heating to the heating required to heat the - * gas back up to the equation of state line, and assume this is the + /* Compare the adiabatic heating to the heating required to heat the + * gas back up to the equation of state line, and assume this is the * fraction of cold cloud mass destroyed. */ if (cooling->ism_adiabatic_heating_method == 2) { /* Use adiabatic du/dt to evaporate cold gas clouds, into warm phase */ - const double f_evap = - hydro_get_physical_internal_energy_dt(p, cosmo) * dt_therm / - (hydro_get_physical_internal_energy(p, xp, cosmo) - u_new); - + const double f_evap = + hydro_get_physical_internal_energy_dt(p, cosmo) * dt_therm / + (hydro_get_physical_internal_energy(p, xp, cosmo) - u_new); + /* If it's in the ISM of a galaxy, suppress cold fraction */ if (f_evap > 0.f && galaxy_mstar > 0.f) { p->cooling_data.subgrid_fcold *= max(1. - f_evap, 0.f); @@ -1473,16 +1446,16 @@ void cooling_do_grackle_cooling( /* No cooling in warm phase since it is fixed on the EoS */ cool_du_dt = 0.f; - /* Force the overall particle to lie on the equation of state + /* Force the overall particle to lie on the equation of state hydro_set_physical_internal_energy(p, xp, cosmo, u_warm);*/ /* set subgrid properties for use in SF routine */ - cooling_set_particle_subgrid_properties( - phys_const, us, cosmo, hydro_props, floor_props, cooling, p, xp); + cooling_set_particle_subgrid_properties(phys_const, us, cosmo, hydro_props, + floor_props, cooling, p, xp); /* Overall particle u is combination of EOS u and subgrid u */ - const float u_part = p->cooling_data.subgrid_fcold * u_new + - (1. - p->cooling_data.subgrid_fcold) * u_warm; + const float u_part = p->cooling_data.subgrid_fcold * u_new + + (1. - p->cooling_data.subgrid_fcold) * u_warm; hydro_set_physical_internal_energy(p, xp, cosmo, u_part); } /* subgrid mode */ @@ -1509,24 +1482,24 @@ void cooling_do_grackle_cooling( * @param time The current time (since the Big Bang or start of the run) in * internal units. */ -void cooling_cool_part(const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct hydro_props* hydro_props, - const struct entropy_floor_properties* floor_props, +void cooling_cool_part(const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct hydro_props *hydro_props, + const struct entropy_floor_properties *floor_props, const struct pressure_floor_props *pressure_floor_props, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, + const struct cooling_function_data *restrict cooling, + struct part *restrict p, struct xpart *restrict xp, const double dt, const double dt_therm, const double time) { /* Compute cooling time and other quantities needed for firehose */ - firehose_cooling_and_dust(phys_const, us, cosmo, hydro_props, - cooling, p, xp, dt); + firehose_cooling_and_dust(phys_const, us, cosmo, hydro_props, cooling, p, xp, + dt); /* Update the subgrid properties */ - cooling_set_particle_subgrid_properties( phys_const, us, - cosmo, hydro_props, floor_props, cooling, p, xp); + cooling_set_particle_subgrid_properties(phys_const, us, cosmo, hydro_props, + floor_props, cooling, p, xp); /* No cooling if particle is decoupled */ if (p->decoupled) return; @@ -1538,13 +1511,12 @@ void cooling_cool_part(const struct phys_const* restrict phys_const, gr_float iact_rates[5] = {0., 0., 0., 0., 0.}; /* Do the cooling and chemistry */ - cooling_do_grackle_cooling(phys_const, us, cosmo, hydro_props, - floor_props, cooling, - p, xp, iact_rates, dt, dt_therm, time); + cooling_do_grackle_cooling(phys_const, us, cosmo, hydro_props, floor_props, + cooling, p, xp, iact_rates, dt, dt_therm, time); } /** - * @brief Set the subgrid properties (rho, T) of the gas particle for use + * @brief Set the subgrid properties (rho, T) of the gas particle for use * in SF routine * * @param phys_const The physical constants in internal units. @@ -1560,8 +1532,8 @@ void cooling_set_particle_subgrid_properties( const struct phys_const *phys_const, const struct unit_system *us, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props, - const struct cooling_function_data *cooling, - struct part *p, struct xpart *xp) { + const struct cooling_function_data *cooling, struct part *p, + struct xpart *xp) { /* No subgrid ISM if particle is decoupled */ if (p->decoupled) { @@ -1575,7 +1547,7 @@ void cooling_set_particle_subgrid_properties( /* Get temperature of overall particle */ const double u = hydro_get_physical_internal_energy(p, xp, cosmo); - const float temperature = + const float temperature = cooling_convert_u_to_temp(u, xp->cooling_data.e_frac, cooling, p); /* Get density */ @@ -1583,14 +1555,14 @@ void cooling_set_particle_subgrid_properties( /* Subgrid model is on if particle is in the Jeans EOS regime */ const double T_warm = warm_ISM_temperature(p, cooling, phys_const, cosmo); - //entropy_floor_gas_temperature( rho, rho_com, cosmo, floor_props); - const double u_warm = + // entropy_floor_gas_temperature( rho, rho_com, cosmo, floor_props); + const double u_warm = cooling_convert_temp_to_u(T_warm, xp->cooling_data.e_frac, cooling, p); - /* Check if it is in subgrid mode: Must be in Jeans EoS regime + /* Check if it is in subgrid mode: Must be in Jeans EoS regime * and have nonzero cold gas */ if (T_warm > 0 && u < u_warm * cooling->entropy_floor_margin) { - /* YES: If first time in subgrid, set temperature to particle T, + /* YES: If first time in subgrid, set temperature to particle T, otherwise limit to particle T */ if (p->cooling_data.subgrid_temp == 0.) { p->cooling_data.subgrid_temp = temperature; @@ -1598,26 +1570,23 @@ void cooling_set_particle_subgrid_properties( cooling_grackle_init_part(cooling, p, xp); /* Initialize ISM cold fraction */ p->cooling_data.subgrid_fcold = cooling->cold_ISM_frac; - } - else { - /* Subgrid temperature should be no higher + } else { + /* Subgrid temperature should be no higher * than overall particle temperature */ - p->cooling_data.subgrid_temp = + p->cooling_data.subgrid_temp = min(p->cooling_data.subgrid_temp, temperature); } /* Compute subgrid density assuming pressure equilibrium */ - const float X_H = chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; + const float X_H = + chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; const double n_H = rho * X_H / phys_const->const_proton_mass; - p->cooling_data.subgrid_dens = - cooling_compute_subgrid_density(rho, n_H, temperature, - p->cooling_data.subgrid_temp, - cooling); - } - else { + p->cooling_data.subgrid_dens = cooling_compute_subgrid_density( + rho, n_H, temperature, p->cooling_data.subgrid_temp, cooling); + } else { /* NO: subgrid density is the actual particle's physical density */ p->cooling_data.subgrid_dens = rho; - + /* set subgrid temperature to 0 indicating it's not in subgrid mode */ p->cooling_data.subgrid_temp = 0.f; @@ -1664,12 +1633,12 @@ float cooling_get_subgrid_density(const struct part *p, * @param p #part data. * @param xp Pointer to the #xpart data. */ -double Cooling_get_ycompton(const struct phys_const* phys_const, - const struct hydro_props* hydro_props, - const struct unit_system* us, - const struct cosmology* cosmo, - const struct cooling_function_data* cooling, - const struct part* p, const struct xpart* xp) { +double Cooling_get_ycompton(const struct phys_const *phys_const, + const struct hydro_props *hydro_props, + const struct unit_system *us, + const struct cosmology *cosmo, + const struct cooling_function_data *cooling, + const struct part *p, const struct xpart *xp) { return 0.; } @@ -1688,13 +1657,13 @@ double Cooling_get_ycompton(const struct phys_const* phys_const, * @param p Pointer to the particle data. * @param xp Pointer to the particle extra data */ -float cooling_timestep(const struct cooling_function_data* restrict cooling, - const struct phys_const* restrict phys_const, - const struct cosmology* restrict cosmo, - const struct unit_system* restrict us, - const struct hydro_props* hydro_props, - const struct part* restrict p, - const struct xpart* restrict xp) { +float cooling_timestep(const struct cooling_function_data *restrict cooling, + const struct phys_const *restrict phys_const, + const struct cosmology *restrict cosmo, + const struct unit_system *restrict us, + const struct hydro_props *hydro_props, + const struct part *restrict p, + const struct xpart *restrict xp) { return FLT_MAX; } @@ -1706,7 +1675,7 @@ float cooling_timestep(const struct cooling_function_data* restrict cooling, * @param xp The #xpart. * @param n The number of pieces to split into. */ -void cooling_split_part(struct part* p, struct xpart* xp, double n) { +void cooling_split_part(struct part *p, struct xpart *xp, double n) { xp->cooling_data.radiated_energy /= n; } @@ -1718,9 +1687,9 @@ void cooling_split_part(struct part* p, struct xpart* xp, double n) { * @param phys_const The #phys_const. * @param cooling The cooling properties to initialize */ -void cooling_init_units(const struct unit_system* us, - const struct phys_const* phys_const, - struct cooling_function_data* cooling) { +void cooling_init_units(const struct unit_system *us, + const struct phys_const *phys_const, + struct cooling_function_data *cooling) { /* These are conversions from code units to cgs. */ @@ -1731,8 +1700,7 @@ void cooling_init_units(const struct unit_system* us, cooling->units.a_units = 1.0; if (cooling->redshift == -1) { cooling->units.a_value = 0.01; - } - else { + } else { cooling->units.a_value = 1.0; } @@ -1753,17 +1721,17 @@ void cooling_init_units(const struct unit_system* us, cooling->units.velocity_units = units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); - cooling->temp_to_u_factor = - phys_const->const_boltzmann_k / (hydro_gamma_minus_one * - phys_const->const_proton_mass * - units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE)); - cooling->dudt_units = - units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS) / - units_cgs_conversion_factor(us, UNIT_CONV_TIME); + cooling->temp_to_u_factor = + phys_const->const_boltzmann_k / + (hydro_gamma_minus_one * phys_const->const_proton_mass * + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE)); + cooling->dudt_units = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS) / + units_cgs_conversion_factor(us, UNIT_CONV_TIME); /* converts galaxy sSFR into G0 by scaling to MW values */ const double time_to_yr = units_cgs_conversion_factor(us, UNIT_CONV_TIME) / - (365.25f * 24.f * 60.f * 60.f); + (365.25f * 24.f * 60.f * 60.f); const double mass_to_solar_mass = 1.f / phys_const->const_solar_mass; const double length_to_pc = units_cgs_conversion_factor(us, UNIT_CONV_LENGTH) / 3.08567758e18f; @@ -1771,11 +1739,11 @@ void cooling_init_units(const struct unit_system* us, cooling->time_to_Myr = time_to_yr * 1.e-6; /* G0 for MW=1.6 (Parravano etal 2003). */ - /* Scaled to SFR density in solar neighborhood =0.002 Mo/Gyr/pc^3 + /* Scaled to SFR density in solar neighborhood =0.002 Mo/Gyr/pc^3 (J. Isern 2019) */ - cooling->G0_factor1 = 1.6f * mass_to_solar_mass / - (0.002f * time_to_yr * 1.e-9) / - (length_to_pc * length_to_pc * length_to_pc); + cooling->G0_factor1 = 1.6f * mass_to_solar_mass / + (0.002f * time_to_yr * 1.e-9) / + (length_to_pc * length_to_pc * length_to_pc); /* Calibrated to sSFR for MW=2.71e-11 (Licquia etal 2015) */ cooling->G0_factor2 = 1.6f / (2.71e-11f * time_to_yr); @@ -1789,14 +1757,14 @@ void cooling_init_units(const struct unit_system* us, * * @param cooling The cooling properties to initialize */ -void cooling_init_grackle(struct cooling_function_data* cooling) { +void cooling_init_grackle(struct cooling_function_data *cooling) { #ifdef SWIFT_DEBUG_CHECKS /* enable verbose for grackle */ grackle_verbose = 1; #endif - chemistry_data* chemistry = &cooling->chemistry; + chemistry_data *chemistry = &cooling->chemistry; /* Create a chemistry object for parameters and rate data. */ if (set_default_chemistry_parameters(chemistry) == 0) { @@ -1817,7 +1785,7 @@ void cooling_init_grackle(struct cooling_function_data* cooling) { * file) */ chemistry->primordial_chemistry = COOLING_GRACKLE_MODE; - /** Flag to enable H2 formation on dust grains, dust cooling, and dust-gas + /** Flag to enable H2 formation on dust grains, dust cooling, and dust-gas * heat transfer follow Omukai (2000). This assumes that the dust to gas ratio * scales with the metallicity. Default: 0. */ chemistry->h2_on_dust = 0; @@ -1856,8 +1824,7 @@ void cooling_init_grackle(struct cooling_function_data* cooling) { chemistry->h2_optical_depth_approximation = 0; /* Flag to enable a spatially uniform heating term approximating * photo-electric heating from dust from Tasker & Bryan (2008). Default: 0. */ - chemistry->photoelectric_heating = - 0; + chemistry->photoelectric_heating = 0; /* photo-electric on [but not adjusted to local background, beware!] */ chemistry->photoelectric_heating_rate = 8.5e-26; @@ -1877,15 +1844,15 @@ void cooling_init_grackle(struct cooling_function_data* cooling) { /* volumetric heating rates is being provided in the volumetric_heating_rate * field of grackle_field_data */ - chemistry->use_volumetric_heating_rate = + chemistry->use_volumetric_heating_rate = cooling->provide_volumetric_heating_rates; /* specific heating rates is being provided in the specific_heating_rate field * of grackle_field_data */ - chemistry->use_specific_heating_rate = + chemistry->use_specific_heating_rate = cooling->provide_specific_heating_rates; - /* Set parameters of temperature floor: 0=none, 1=provide scalar, + /* Set parameters of temperature floor: 0=none, 1=provide scalar, * 2=provide array */ chemistry->use_temperature_floor = 2; @@ -1917,10 +1884,9 @@ void cooling_init_grackle(struct cooling_function_data* cooling) { /* control behaviour of Grackle sub-step integration damping */ if (cooling->grackle_damping_interval > 0) { chemistry->use_subcycle_timestep_damping = 1; - chemistry->subcycle_timestep_damping_interval = + chemistry->subcycle_timestep_damping_interval = cooling->grackle_damping_interval; - } - else { + } else { chemistry->use_subcycle_timestep_damping = 0; chemistry->subcycle_timestep_damping_interval = 0; } @@ -1945,11 +1911,11 @@ void cooling_init_grackle(struct cooling_function_data* cooling) { chemistry->h2_on_dust = 1; chemistry->use_isrf_field = 1; chemistry->H2_self_shielding = 4; - /* 2 means we specify the H2 shielding + /* 2 means we specify the H2 shielding length ourselves (the gas smoothing length) */ chemistry->H2_custom_shielding = 2; /* Solar abundances to pass to Grackle: - He (10.93 in units where log[H]=12, so photospheric mass fraction + He (10.93 in units where log[H]=12, so photospheric mass fraction -> Y=0.2485 [Hydrogen X=0.7381]; Anders+Grevesse Y=0.2485, X=0.7314) C (8.43 -> 2.38e-3, AG=3.18e-3) N (7.83 -> 0.70e-3, AG=1.15e-3) @@ -1975,7 +1941,7 @@ void cooling_init_grackle(struct cooling_function_data* cooling) { chemistry->use_dust_evol = 0; } - cooling->use_grackle_h2_form = + cooling->use_grackle_h2_form = cooling->use_grackle_dust_evol && COOLING_GRACKLE_MODE >= 2; /* Initialize the chemistry object. */ @@ -1993,11 +1959,11 @@ void cooling_init_grackle(struct cooling_function_data* cooling) { * @param hydro_props The properties of the hydro scheme. * @param cooling The cooling properties to initialize */ -void cooling_init_backend(struct swift_params* parameter_file, - const struct unit_system* us, - const struct phys_const* phys_const, - const struct hydro_props* hydro_props, - struct cooling_function_data* cooling) { +void cooling_init_backend(struct swift_params *parameter_file, + const struct unit_system *us, + const struct phys_const *phys_const, + const struct hydro_props *hydro_props, + struct cooling_function_data *cooling) { if (GRACKLE_NPART != 1) error("Grackle with multiple particles not implemented"); @@ -2017,7 +1983,7 @@ void cooling_init_backend(struct swift_params* parameter_file, * * @param cooling the cooling data structure. */ -void cooling_clean(struct cooling_function_data* cooling) { +void cooling_clean(struct cooling_function_data *cooling) { //_free_chemistry_data(&cooling->chemistry, &grackle_rates); } @@ -2029,9 +1995,9 @@ void cooling_clean(struct cooling_function_data* cooling) { * @param cooling the struct * @param stream the file stream */ -void cooling_struct_dump(const struct cooling_function_data* cooling, - FILE* stream) { - restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, +void cooling_struct_dump(const struct cooling_function_data *cooling, + FILE *stream) { + restart_write_blocks((void *)cooling, sizeof(struct cooling_function_data), 1, stream, "cooling", "cooling function"); } @@ -2045,9 +2011,9 @@ void cooling_struct_dump(const struct cooling_function_data* cooling, * @param stream the file stream * @param cosmo #cosmology structure */ -void cooling_struct_restore(struct cooling_function_data* cooling, FILE* stream, - const struct cosmology* cosmo) { - restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, +void cooling_struct_restore(struct cooling_function_data *cooling, FILE *stream, + const struct cosmology *cosmo) { + restart_read_blocks((void *)cooling, sizeof(struct cooling_function_data), 1, stream, NULL, "cooling function"); /* Set up grackle */ diff --git a/src/cooling/KIARA/cooling.h b/src/cooling/KIARA/cooling.h index aaa8ac365f..10a3337895 100644 --- a/src/cooling/KIARA/cooling.h +++ b/src/cooling/KIARA/cooling.h @@ -38,20 +38,22 @@ #include "cooling_properties.h" #include "entropy_floor.h" #include "error.h" +#include "fof.h" #include "hydro.h" #include "parser.h" #include "part.h" #include "physical_constants.h" #include "units.h" -#include "fof.h" /* need to rework (and check) code if changed */ #define GRACKLE_NPART 1 #define GRACKLE_RANK 3 #if COOLING_GRACKLE_MODE >= 2 - #define N_SPECIES 46 /* This further includes properties for dust model */ +#define N_SPECIES 46 /* This further includes properties for dust model */ #else - #define N_SPECIES 21 /* This includes extra values at end to hold rho,u,dudt,vx,vy,vz,u_floor,mZ,dummyvar */ +#define N_SPECIES \ + 21 /* This includes extra values at end to hold \ + rho,u,dudt,vx,vy,vz,u_floor,mZ,dummyvar */ #endif /* define heating and cooling limits on thermal energy, per timestep */ @@ -59,7 +61,7 @@ #define GRACKLE_COOLLIM 0.01f #define MAX_COLD_ISM_FRACTION 0.9f /* Minimum particle column length as a fraction of p->h. - * Should be <= mean interparticle spacing. + * Should be <= mean interparticle spacing. * For 48 Ngb mean spacing ~ 0.887 * For 57 Ngb mean spacing ~ 0.837 * For 114 Ngb mean spacing ~ 0.664 @@ -75,108 +77,100 @@ void cooling_update(const struct phys_const *phys_const, struct cooling_function_data *cooling, struct space *s, const double time); -void cooling_print_fractions(const struct xpart* restrict xp); -void cooling_first_init_part(const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct hydro_props* hydro_properties, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* cooling, - struct part* restrict p, - struct xpart* restrict xp); -void cooling_post_init_part(const struct phys_const *restrict phys_const, - const struct unit_system *restrict us, - const struct hydro_props *hydro_props, - const struct cosmology *restrict cosmo, - const struct cooling_function_data *restrict cooling, - const struct part *restrict p, struct xpart *restrict xp); - -void cooling_print_backend(const struct cooling_function_data* cooling); - -void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, - const struct xpart* xp, gr_float rho, - gr_float species_densities[N_SPECIES]); -void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, - const struct xpart* xp, - const struct cooling_function_data* restrict cooling, - const double dt, gr_float rho, +void cooling_print_fractions(const struct xpart *restrict xp); +void cooling_first_init_part(const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct hydro_props *hydro_properties, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *cooling, + struct part *restrict p, + struct xpart *restrict xp); +void cooling_post_init_part( + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct hydro_props *hydro_props, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + const struct part *restrict p, struct xpart *restrict xp); + +void cooling_print_backend(const struct cooling_function_data *cooling); + +void cooling_copy_to_grackle1(grackle_field_data *data, const struct part *p, + const struct xpart *xp, gr_float rho, gr_float species_densities[N_SPECIES]); -void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, - const struct xpart* xp, gr_float rho, +void cooling_copy_to_grackle2( + grackle_field_data *data, const struct part *p, const struct xpart *xp, + const struct cooling_function_data *restrict cooling, const double dt, + gr_float rho, gr_float species_densities[N_SPECIES]); +void cooling_copy_to_grackle3(grackle_field_data *data, const struct part *p, + const struct xpart *xp, gr_float rho, gr_float species_densities[N_SPECIES]); -void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho); -void cooling_copy_from_grackle2(grackle_field_data* data, struct part* p, - struct xpart* xp, - const struct cooling_function_data* restrict cooling, - gr_float rho); -void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, - struct xpart* xp, gr_float rho); -void cooling_copy_to_grackle(grackle_field_data* data, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, - const struct part* p, const struct xpart* xp, - const double dt, const double T_floor, - gr_float species_densities[N_SPECIES], - gr_float* iact_rates, - int mode); -void cooling_copy_from_grackle(grackle_field_data* data, struct part* p, - struct xpart* xp, - const struct cooling_function_data* restrict cooling, - gr_float rho); -void cooling_grackle_free_data(grackle_field_data* data); -gr_float cooling_grackle_driver(const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct hydro_props* hydro_properties, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, - struct xpart* restrict xp, gr_float* iact_rates, - double dt, double T_floor, - int mode); +void cooling_copy_from_grackle1(grackle_field_data *data, const struct part *p, + struct xpart *xp, gr_float rho); +void cooling_copy_from_grackle2( + grackle_field_data *data, struct part *p, struct xpart *xp, + const struct cooling_function_data *restrict cooling, gr_float rho); +void cooling_copy_from_grackle3(grackle_field_data *data, const struct part *p, + struct xpart *xp, gr_float rho); +void cooling_copy_to_grackle( + grackle_field_data *data, const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, const struct part *p, + const struct xpart *xp, const double dt, const double T_floor, + gr_float species_densities[N_SPECIES], gr_float *iact_rates, int mode); +void cooling_copy_from_grackle( + grackle_field_data *data, struct part *p, struct xpart *xp, + const struct cooling_function_data *restrict cooling, gr_float rho); +void cooling_grackle_free_data(grackle_field_data *data); +gr_float cooling_grackle_driver( + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct hydro_props *hydro_properties, + const struct cooling_function_data *restrict cooling, + struct part *restrict p, struct xpart *restrict xp, gr_float *iact_rates, + double dt, double T_floor, int mode); void cooling_do_grackle_cooling( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct hydro_props* hydro_props, - const struct entropy_floor_properties* floor_props, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, - gr_float* iact_rates, - const double dt, const double dt_therm, - const double time); -gr_float cooling_time(const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct hydro_props* hydro_properties, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, - const struct part* restrict p, struct xpart* restrict xp, - const float rhocool, const float ucool); + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct hydro_props *hydro_props, + const struct entropy_floor_properties *floor_props, + const struct cooling_function_data *restrict cooling, + struct part *restrict p, struct xpart *restrict xp, gr_float *iact_rates, + const double dt, const double dt_therm, const double time); +gr_float cooling_time(const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct hydro_props *hydro_properties, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + const struct part *restrict p, struct xpart *restrict xp, + const float rhocool, const float ucool); float cooling_get_temperature( - const struct phys_const* restrict phys_const, - const struct hydro_props* hydro_properties, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, - const struct part* restrict p, const struct xpart* restrict xp); + const struct phys_const *restrict phys_const, + const struct hydro_props *hydro_properties, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + const struct part *restrict p, const struct xpart *restrict xp); void firehose_cooling_and_dust( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct hydro_props* restrict hydro_props, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, const double dt); - -void cooling_cool_part(const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct hydro_props* hydro_properties, - const struct entropy_floor_properties* floor_props, - const struct pressure_floor_props *pressure_floor_props, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct hydro_props *restrict hydro_props, + const struct cooling_function_data *restrict cooling, + struct part *restrict p, struct xpart *restrict xp, const double dt); + +void cooling_cool_part(const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct hydro_props *hydro_properties, + const struct entropy_floor_properties *floor_props, + const struct pressure_floor_props *pressure_floor_props, + const struct cooling_function_data *restrict cooling, + struct part *restrict p, struct xpart *restrict xp, const double dt, const double dt_therm, const double time); @@ -184,54 +178,54 @@ void cooling_set_particle_subgrid_properties( const struct phys_const *phys_const, const struct unit_system *us, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct entropy_floor_properties *floor_props, - const struct cooling_function_data *cooling, - struct part *p, struct xpart *xp); + const struct cooling_function_data *cooling, struct part *p, + struct xpart *xp); float cooling_get_subgrid_temperature(const struct part *p, const struct xpart *xp); float cooling_get_subgrid_density(const struct part *p, const struct xpart *xp); -float cooling_get_radiated_energy(const struct xpart* restrict xp); - -double cooling_get_ycompton(const struct phys_const* phys_const, - const struct hydro_props* hydro_props, - const struct unit_system* us, - const struct cosmology* cosmo, - const struct cooling_function_data* cooling, - const struct part* p, const struct xpart* xp); - -float cooling_timestep(const struct cooling_function_data* restrict cooling, - const struct phys_const* restrict phys_const, - const struct cosmology* restrict cosmo, - const struct unit_system* restrict us, - const struct hydro_props* hydro_properties, - const struct part* restrict p, - const struct xpart* restrict xp); - -void cooling_split_part(struct part* p, struct xpart* xp, double n); - -void cooling_init_units(const struct unit_system* us, - const struct phys_const* phys_const, - struct cooling_function_data* cooling); -void cooling_init_grackle(struct cooling_function_data* cooling); - -void cooling_init_backend(struct swift_params* parameter_file, - const struct unit_system* us, - const struct phys_const* phys_const, - const struct hydro_props* hydro_props, - struct cooling_function_data* cooling); - -void cooling_clean(struct cooling_function_data* cooling); -void cooling_struct_dump(const struct cooling_function_data* cooling, - FILE* stream); -void cooling_struct_restore(struct cooling_function_data* cooling, FILE* stream, - const struct cosmology* cosmo); -void cooling_sputter_dust( - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, const double dt); +float cooling_get_radiated_energy(const struct xpart *restrict xp); + +double cooling_get_ycompton(const struct phys_const *phys_const, + const struct hydro_props *hydro_props, + const struct unit_system *us, + const struct cosmology *cosmo, + const struct cooling_function_data *cooling, + const struct part *p, const struct xpart *xp); + +float cooling_timestep(const struct cooling_function_data *restrict cooling, + const struct phys_const *restrict phys_const, + const struct cosmology *restrict cosmo, + const struct unit_system *restrict us, + const struct hydro_props *hydro_properties, + const struct part *restrict p, + const struct xpart *restrict xp); + +void cooling_split_part(struct part *p, struct xpart *xp, double n); + +void cooling_init_units(const struct unit_system *us, + const struct phys_const *phys_const, + struct cooling_function_data *cooling); +void cooling_init_grackle(struct cooling_function_data *cooling); + +void cooling_init_backend(struct swift_params *parameter_file, + const struct unit_system *us, + const struct phys_const *phys_const, + const struct hydro_props *hydro_props, + struct cooling_function_data *cooling); + +void cooling_clean(struct cooling_function_data *cooling); +void cooling_struct_dump(const struct cooling_function_data *cooling, + FILE *stream); +void cooling_struct_restore(struct cooling_function_data *cooling, FILE *stream, + const struct cosmology *cosmo); +void cooling_sputter_dust(const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + struct part *restrict p, struct xpart *restrict xp, + const double dt); /** * @brief Compute the electron pressure of a #part based on the cooling @@ -248,15 +242,16 @@ void cooling_sputter_dust( * @param xp Pointer to the #xpart data. */ INLINE static double cooling_get_electron_pressure( - const struct phys_const* phys_const, const struct hydro_props* hydro_props, - const struct unit_system* us, const struct cosmology* cosmo, - const struct cooling_function_data* cooling, const struct part* p, - const struct xpart* xp) { + const struct phys_const *phys_const, const struct hydro_props *hydro_props, + const struct unit_system *us, const struct cosmology *cosmo, + const struct cooling_function_data *cooling, const struct part *p, + const struct xpart *xp) { return 0; } /** - * @brief Compute the specific thermal energy (physical) for a given temperature. + * @brief Compute the specific thermal energy (physical) for a given + * temperature. * * Converts T to u (internal physical units) for a given particle. * @@ -266,10 +261,11 @@ INLINE static double cooling_get_electron_pressure( * @param p #part data. */ INLINE static double cooling_convert_temp_to_u( - const double temperature, const double ne, const struct cooling_function_data* cooling, - const struct part* p) { + const double temperature, const double ne, + const struct cooling_function_data *cooling, const struct part *p) { - const float X_H = chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; + const float X_H = + chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; const float yhelium = (1. - X_H) / (4. * X_H); const float mu = (1. + yhelium) / (1. + ne + 4. * yhelium); @@ -287,10 +283,11 @@ INLINE static double cooling_convert_temp_to_u( * @param p #part data. */ INLINE static double cooling_convert_u_to_temp( - const double u, const double ne, const struct cooling_function_data* cooling, - const struct part* p) { + const double u, const double ne, + const struct cooling_function_data *cooling, const struct part *p) { - const float X_H = chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; + const float X_H = + chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; const float yhelium = (1. - X_H) / (4. * X_H); const float mu = (1. + yhelium) / (1. + ne + 4. * yhelium); @@ -298,31 +295,36 @@ INLINE static double cooling_convert_u_to_temp( } /** - * @brief Compute the cold ISM fraction at a given factor above subgrid threshold density + * @brief Compute the cold ISM fraction at a given factor above subgrid + * threshold density * - * Compute the cold ISM fraction at a given factor above subgrid threshold density. - * This uses a fit to the density vs. cold gas fraction relation from Springel+Hernquist 2003. + * Compute the cold ISM fraction at a given factor above subgrid threshold + * density. This uses a fit to the density vs. cold gas fraction relation from + * Springel+Hernquist 2003. * * @param dens_fac Density factor above threshold density * @param cooling #cooling_function_data struct. */ INLINE static double cooling_compute_cold_ISM_fraction( - const double n_H, const struct cooling_function_data* cooling) { + const double n_H, const struct cooling_function_data *cooling) { float fc = cooling->cold_ISM_frac; float dens_fac = n_H * cooling->subgrid_threshold_n_H_inv; if (dens_fac > 1.) { - fc = cooling->cold_ISM_frac + (1. - cooling->cold_ISM_frac) * (1. - exp(-log10(dens_fac))); + fc = cooling->cold_ISM_frac + + (1. - cooling->cold_ISM_frac) * (1. - exp(-log10(dens_fac))); fc = fmin(fc, MAX_COLD_ISM_FRACTION); } return fc; } /** - * @brief Compute the subgrid density based on pressure equilibrium in a 2-phase ISM model + * @brief Compute the subgrid density based on pressure equilibrium in a 2-phase + * ISM model * - * We set the subgrid density based on pressure equilibrium with overall particle. - * The pressure is set by 1-cold_ISM_frac of the mass in the warm phase. + * We set the subgrid density based on pressure equilibrium with overall + * particle. The pressure is set by 1-cold_ISM_frac of the mass in the warm + * phase. * * @param rho SPH (non-subgrid) physical particle density. * @param n_H SPH (non-subgrid) physical particle H number density. @@ -331,13 +333,12 @@ INLINE static double cooling_compute_cold_ISM_fraction( * @param cooling #cooling_function_data struct. */ INLINE static double cooling_compute_subgrid_density( - const double rho, - const double n_H, const double temp, const double subgrid_temp, - const struct cooling_function_data* cooling) { + const double rho, const double n_H, const double temp, + const double subgrid_temp, const struct cooling_function_data *cooling) { - const double ism_frac = - cooling_compute_cold_ISM_fraction(n_H * cooling->subgrid_threshold_n_H_inv, cooling); - double subgrid_dens = + const double ism_frac = cooling_compute_cold_ISM_fraction( + n_H * cooling->subgrid_threshold_n_H_inv, cooling); + double subgrid_dens = (1.f - ism_frac) * rho * temp / (ism_frac * subgrid_temp); /* Cap at max value which should be something vaguely like GMC densities */ @@ -346,7 +347,8 @@ INLINE static double cooling_compute_subgrid_density( } /** - * @brief Return warm ISM temperature if above SF threshold density, otherwise 0. + * @brief Return warm ISM temperature if above SF threshold density, otherwise + * 0. * * @param p Pointer to the particle data. * @param cooling The properties of the cooling function. @@ -356,11 +358,11 @@ INLINE static double cooling_compute_subgrid_density( */ __attribute__((always_inline)) INLINE static float warm_ISM_temperature( const struct part *restrict p, const struct cooling_function_data *cooling, - const struct phys_const* phys_const, const struct cosmology* cosmo) { + const struct phys_const *phys_const, const struct cosmology *cosmo) { float temperature = 0.f; -/* Mean baryon density in co-moving internal units for over-density condition + /* 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; @@ -370,29 +372,32 @@ __attribute__((always_inline)) INLINE static float warm_ISM_temperature( if (rho_com >= rho_crit_baryon * 100.f) { const double n_H = rho_phys * 0.75 / phys_const->const_proton_mass; - if (n_H * cooling->subgrid_threshold_n_H_inv > 1.f ) { + if (n_H * cooling->subgrid_threshold_n_H_inv > 1.f) { const float EOS_slope = cooling->subgrid_warm_ism_EOS; - temperature = cooling->subgrid_threshold_T * pow(n_H * cooling->subgrid_threshold_n_H_inv, EOS_slope); + temperature = cooling->subgrid_threshold_T * + pow(n_H * cooling->subgrid_threshold_n_H_inv, EOS_slope); } } return temperature; } /** - * @brief Copy all grackle fields into a new set. THIS IS ONLY USED FOR DEBUGGING. + * @brief Copy all grackle fields into a new set. THIS IS ONLY USED FOR + * DEBUGGING. * * @param my_fields The target (new) set of grackle particle properties. * @param old_fields The original (old) set of grackle particle properties. * @param field_size Number of particles to copy. */ INLINE static void cooling_copy_grackle_fields(grackle_field_data *my_fields, - grackle_field_data *old_fields, + grackle_field_data *old_fields, int field_size) { int i; for (i = 0; i < field_size; i++) { - printf("loop copy_grackle_fields %g %p\n",old_fields->density[0],my_fields->density); + printf("loop copy_grackle_fields %g %p\n", old_fields->density[0], + my_fields->density); my_fields->density[i] = old_fields->density[i]; my_fields->HI_density[i] = old_fields->HI_density[i]; @@ -417,15 +422,19 @@ INLINE static void cooling_copy_grackle_fields(grackle_field_data *my_fields, // initilize internal energy (here 1000 K for no reason) my_fields->internal_energy[i] = old_fields->internal_energy[i]; - my_fields->volumetric_heating_rate[i] = old_fields->volumetric_heating_rate[i]; + my_fields->volumetric_heating_rate[i] = + old_fields->volumetric_heating_rate[i]; my_fields->specific_heating_rate[i] = old_fields->specific_heating_rate[i]; my_fields->temperature_floor[i] = old_fields->temperature_floor[i]; my_fields->isrf_habing[i] = old_fields->isrf_habing[i]; my_fields->RT_HI_ionization_rate[i] = old_fields->RT_HI_ionization_rate[i]; - my_fields->RT_HeI_ionization_rate[i] = old_fields->RT_HeI_ionization_rate[i]; - my_fields->RT_HeII_ionization_rate[i] = old_fields->RT_HeII_ionization_rate[i]; - my_fields->RT_H2_dissociation_rate[i] = old_fields->RT_H2_dissociation_rate[i]; + my_fields->RT_HeI_ionization_rate[i] = + old_fields->RT_HeI_ionization_rate[i]; + my_fields->RT_HeII_ionization_rate[i] = + old_fields->RT_HeII_ionization_rate[i]; + my_fields->RT_H2_dissociation_rate[i] = + old_fields->RT_H2_dissociation_rate[i]; my_fields->RT_heating_rate[i] = old_fields->RT_heating_rate[i]; if (grackle_data->use_dust_evol) { @@ -459,36 +468,38 @@ INLINE static void cooling_copy_grackle_fields(grackle_field_data *my_fields, } /** - * @brief Allocate a new set of grackle fields in memory. THIS IS ONLY USED FOR DEBUGGING. + * @brief Allocate a new set of grackle fields in memory. THIS IS ONLY USED FOR + * DEBUGGING. * * @param my_fields The target (new) set of grackle particle properties. * @param field_size Number of particles to copy. * @param dust_flag Are we using grackle's dust model (Jones, Smith, Dave 2024)? */ -INLINE static void cooling_grackle_malloc_fields(grackle_field_data *my_fields, int field_size, int dust_flag) -{ - my_fields->density = malloc(field_size * sizeof(gr_float)); +INLINE static void cooling_grackle_malloc_fields(grackle_field_data *my_fields, + int field_size, + int dust_flag) { + my_fields->density = malloc(field_size * sizeof(gr_float)); my_fields->internal_energy = malloc(field_size * sizeof(gr_float)); - my_fields->x_velocity = malloc(field_size * sizeof(gr_float)); - my_fields->y_velocity = malloc(field_size * sizeof(gr_float)); - my_fields->z_velocity = malloc(field_size * sizeof(gr_float)); + my_fields->x_velocity = malloc(field_size * sizeof(gr_float)); + my_fields->y_velocity = malloc(field_size * sizeof(gr_float)); + my_fields->z_velocity = malloc(field_size * sizeof(gr_float)); // for primordial_chemistry >= 1 - my_fields->HI_density = malloc(field_size * sizeof(gr_float)); - my_fields->HII_density = malloc(field_size * sizeof(gr_float)); - my_fields->HeI_density = malloc(field_size * sizeof(gr_float)); - my_fields->HeII_density = malloc(field_size * sizeof(gr_float)); - my_fields->HeIII_density = malloc(field_size * sizeof(gr_float)); - my_fields->e_density = malloc(field_size * sizeof(gr_float)); + my_fields->HI_density = malloc(field_size * sizeof(gr_float)); + my_fields->HII_density = malloc(field_size * sizeof(gr_float)); + my_fields->HeI_density = malloc(field_size * sizeof(gr_float)); + my_fields->HeII_density = malloc(field_size * sizeof(gr_float)); + my_fields->HeIII_density = malloc(field_size * sizeof(gr_float)); + my_fields->e_density = malloc(field_size * sizeof(gr_float)); // for primordial_chemistry >= 2 - my_fields->HM_density = malloc(field_size * sizeof(gr_float)); - my_fields->H2I_density = malloc(field_size * sizeof(gr_float)); - my_fields->H2II_density = malloc(field_size * sizeof(gr_float)); + my_fields->HM_density = malloc(field_size * sizeof(gr_float)); + my_fields->H2I_density = malloc(field_size * sizeof(gr_float)); + my_fields->H2II_density = malloc(field_size * sizeof(gr_float)); // for primordial_chemistry >= 3 - my_fields->DI_density = malloc(field_size * sizeof(gr_float)); - my_fields->DII_density = malloc(field_size * sizeof(gr_float)); - my_fields->HDI_density = malloc(field_size * sizeof(gr_float)); + my_fields->DI_density = malloc(field_size * sizeof(gr_float)); + my_fields->DII_density = malloc(field_size * sizeof(gr_float)); + my_fields->HDI_density = malloc(field_size * sizeof(gr_float)); // for metal_cooling = 1 - my_fields->metal_density = malloc(field_size * sizeof(gr_float)); + my_fields->metal_density = malloc(field_size * sizeof(gr_float)); // volumetric heating rate (provide in units [erg s^-1 cm^-3]) my_fields->volumetric_heating_rate = malloc(field_size * sizeof(gr_float)); @@ -496,7 +507,8 @@ INLINE static void cooling_grackle_malloc_fields(grackle_field_data *my_fields, my_fields->specific_heating_rate = malloc(field_size * sizeof(gr_float)); my_fields->temperature_floor = malloc(field_size * sizeof(gr_float)); - // radiative transfer ionization / dissociation rate fields (provide in units [1/s]) + // radiative transfer ionization / dissociation rate fields (provide in units + // [1/s]) my_fields->RT_HI_ionization_rate = malloc(field_size * sizeof(gr_float)); my_fields->RT_HeI_ionization_rate = malloc(field_size * sizeof(gr_float)); my_fields->RT_HeII_ionization_rate = malloc(field_size * sizeof(gr_float)); @@ -510,109 +522,111 @@ INLINE static void cooling_grackle_malloc_fields(grackle_field_data *my_fields, my_fields->isrf_habing = malloc(field_size * sizeof(gr_float)); if (dust_flag) { - my_fields->dust_density = malloc(field_size * sizeof(gr_float)); - my_fields->He_gas_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->C_gas_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->N_gas_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->O_gas_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->Ne_gas_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->Mg_gas_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->Si_gas_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->S_gas_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->Ca_gas_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->Fe_gas_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->He_dust_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->C_dust_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->N_dust_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->O_dust_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->Ne_dust_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->Mg_dust_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->Si_dust_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->S_dust_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->Ca_dust_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->Fe_dust_metalDensity = malloc(field_size * sizeof(gr_float)); - my_fields->SNe_ThisTimeStep = malloc(field_size * sizeof(gr_float)); + my_fields->dust_density = malloc(field_size * sizeof(gr_float)); + my_fields->He_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->C_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->N_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->O_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Ne_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Mg_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Si_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->S_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Ca_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Fe_gas_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->He_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->C_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->N_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->O_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Ne_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Mg_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Si_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->S_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Ca_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->Fe_dust_metalDensity = malloc(field_size * sizeof(gr_float)); + my_fields->SNe_ThisTimeStep = malloc(field_size * sizeof(gr_float)); } return; } /** - * @brief Free a set of grackle fields from memory. THIS IS ONLY USED FOR DEBUGGING. + * @brief Free a set of grackle fields from memory. THIS IS ONLY USED FOR + * DEBUGGING. * * @param my_fields The target (new) set of grackle particle properties. * @param dust_flag Are we using grackle's dust model (Jones, Smith, Dave 2024)? */ -INLINE static void cooling_grackle_free_fields(grackle_field_data *my_fields, int dust_flag) -{ - free(my_fields->grid_dimension ); - free(my_fields->grid_start ); - free(my_fields->grid_end ); - - free(my_fields->density ); - free(my_fields->internal_energy ); - free(my_fields->x_velocity ); - free(my_fields->y_velocity ); - free(my_fields->z_velocity ); +INLINE static void cooling_grackle_free_fields(grackle_field_data *my_fields, + int dust_flag) { + free(my_fields->grid_dimension); + free(my_fields->grid_start); + free(my_fields->grid_end); + + free(my_fields->density); + free(my_fields->internal_energy); + free(my_fields->x_velocity); + free(my_fields->y_velocity); + free(my_fields->z_velocity); // for primordial_chemistry >= 1 - free(my_fields->HI_density ); - free(my_fields->HII_density ); - free(my_fields->HeI_density ); - free(my_fields->HeII_density ); - free(my_fields->HeIII_density ); - free(my_fields->e_density ); + free(my_fields->HI_density); + free(my_fields->HII_density); + free(my_fields->HeI_density); + free(my_fields->HeII_density); + free(my_fields->HeIII_density); + free(my_fields->e_density); // for primordial_chemistry >= 2 - free(my_fields->HM_density ); - free(my_fields->H2I_density ); - free(my_fields->H2II_density ); + free(my_fields->HM_density); + free(my_fields->H2I_density); + free(my_fields->H2II_density); // for primordial_chemistry >= 3 - free(my_fields->DI_density ); - free(my_fields->DII_density ); - free(my_fields->HDI_density ); + free(my_fields->DI_density); + free(my_fields->DII_density); + free(my_fields->HDI_density); // for metal_cooling = 1 - free(my_fields->metal_density ); + free(my_fields->metal_density); // volumetric heating rate (provide in units [erg s^-1 cm^-3]) - free(my_fields->volumetric_heating_rate ); + free(my_fields->volumetric_heating_rate); // specific heating rate (provide in units [egs s^-1 g^-1] - free(my_fields->specific_heating_rate ); - free(my_fields->temperature_floor ); - - // radiative transfer ionization / dissociation rate fields (provide in units [1/s]) - free(my_fields->RT_HI_ionization_rate ); - free(my_fields->RT_HeI_ionization_rate ); - free(my_fields->RT_HeII_ionization_rate ); - free(my_fields->RT_H2_dissociation_rate ); + free(my_fields->specific_heating_rate); + free(my_fields->temperature_floor); + + // radiative transfer ionization / dissociation rate fields (provide in units + // [1/s]) + free(my_fields->RT_HI_ionization_rate); + free(my_fields->RT_HeI_ionization_rate); + free(my_fields->RT_HeII_ionization_rate); + free(my_fields->RT_H2_dissociation_rate); // radiative transfer heating rate field (provide in units [erg s^-1 cm^-3]) - free(my_fields->RT_heating_rate ); + free(my_fields->RT_heating_rate); // H2 model - free(my_fields->H2_self_shielding_length ); - free(my_fields->H2_custom_shielding_factor ); - free(my_fields->isrf_habing ); + free(my_fields->H2_self_shielding_length); + free(my_fields->H2_custom_shielding_factor); + free(my_fields->isrf_habing); if (dust_flag) { - free(my_fields->dust_density ); - free(my_fields->He_gas_metalDensity ); - free(my_fields->C_gas_metalDensity ); - free(my_fields->N_gas_metalDensity ); - free(my_fields->O_gas_metalDensity ); - free(my_fields->Ne_gas_metalDensity ); - free(my_fields->Mg_gas_metalDensity ); - free(my_fields->Si_gas_metalDensity ); - free(my_fields->S_gas_metalDensity ); - free(my_fields->Ca_gas_metalDensity ); - free(my_fields->Fe_gas_metalDensity ); - free(my_fields->He_dust_metalDensity ); - free(my_fields->C_dust_metalDensity ); - free(my_fields->N_dust_metalDensity ); - free(my_fields->O_dust_metalDensity ); - free(my_fields->Ne_dust_metalDensity ); - free(my_fields->Mg_dust_metalDensity ); - free(my_fields->Si_dust_metalDensity ); - free(my_fields->S_dust_metalDensity ); - free(my_fields->Ca_dust_metalDensity ); - free(my_fields->Fe_dust_metalDensity ); - free(my_fields->SNe_ThisTimeStep ); + free(my_fields->dust_density); + free(my_fields->He_gas_metalDensity); + free(my_fields->C_gas_metalDensity); + free(my_fields->N_gas_metalDensity); + free(my_fields->O_gas_metalDensity); + free(my_fields->Ne_gas_metalDensity); + free(my_fields->Mg_gas_metalDensity); + free(my_fields->Si_gas_metalDensity); + free(my_fields->S_gas_metalDensity); + free(my_fields->Ca_gas_metalDensity); + free(my_fields->Fe_gas_metalDensity); + free(my_fields->He_dust_metalDensity); + free(my_fields->C_dust_metalDensity); + free(my_fields->N_dust_metalDensity); + free(my_fields->O_dust_metalDensity); + free(my_fields->Ne_dust_metalDensity); + free(my_fields->Mg_dust_metalDensity); + free(my_fields->Si_dust_metalDensity); + free(my_fields->S_dust_metalDensity); + free(my_fields->Ca_dust_metalDensity); + free(my_fields->Fe_dust_metalDensity); + free(my_fields->SNe_ThisTimeStep); } return; } diff --git a/src/cooling/KIARA/cooling_debug.h b/src/cooling/KIARA/cooling_debug.h index f9d549e129..8167f820bb 100644 --- a/src/cooling/KIARA/cooling_debug.h +++ b/src/cooling/KIARA/cooling_debug.h @@ -20,7 +20,7 @@ #define SWIFT_COOLING_KIARA_DEBUG_H __attribute__((always_inline)) INLINE static void cooling_debug_particle( - const struct part* p, const struct xpart* xp) { + const struct part *p, const struct xpart *xp) { if (xp != NULL) { warning("[PID%lld] cooling_xpart_data:", p->id); diff --git a/src/cooling/KIARA/cooling_io.h b/src/cooling/KIARA/cooling_io.h index 3d6a745b69..7ac094446f 100644 --- a/src/cooling/KIARA/cooling_io.h +++ b/src/cooling/KIARA/cooling_io.h @@ -37,7 +37,7 @@ */ __attribute__((always_inline)) INLINE static void cooling_write_flavour( hid_t h_grp, hid_t h_grp_columns, - const struct cooling_function_data* cooling) { + const struct cooling_function_data *cooling) { #if COOLING_GRACKLE_MODE == 0 io_write_attribute_s(h_grp, "Cooling Model", "Grackle0"); @@ -53,68 +53,69 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( } #endif -INLINE static void convert_part_HI_mass(const struct engine* e, - const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_part_HI_mass(const struct engine *e, + const struct part *p, + const struct xpart *xp, float *ret) { *ret = hydro_get_mass(p) * xp->cooling_data.HI_frac; } -INLINE static void convert_part_H2_mass(const struct engine* e, - const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_part_H2_mass(const struct engine *e, + const struct part *p, + const struct xpart *xp, float *ret) { float H2_frac = 0.; - //const float X_H = chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; + // const float X_H = + // chemistry_get_metal_mass_fraction_for_cooling(p)[chemistry_element_H]; #if COOLING_GRACKLE_MODE >= 2 H2_frac = xp->cooling_data.H2I_frac + xp->cooling_data.H2II_frac; *ret = hydro_get_mass(p) * p->cooling_data.subgrid_fcold * H2_frac; #else - if ( p->sf_data.SFR > 0 ) H2_frac = 1. - xp->cooling_data.HI_frac; + if (p->sf_data.SFR > 0) H2_frac = 1. - xp->cooling_data.HI_frac; *ret = hydro_get_mass(p) * H2_frac; #endif } -INLINE static void convert_part_HII_mass(const struct engine* e, - const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_part_HII_mass(const struct engine *e, + const struct part *p, + const struct xpart *xp, float *ret) { *ret = hydro_get_mass(p) * xp->cooling_data.HII_frac; } -INLINE static void convert_part_HeI_mass(const struct engine* e, - const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_part_HeI_mass(const struct engine *e, + const struct part *p, + const struct xpart *xp, float *ret) { *ret = hydro_get_mass(p) * xp->cooling_data.HeI_frac; } -INLINE static void convert_part_HeII_mass(const struct engine* e, - const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_part_HeII_mass(const struct engine *e, + const struct part *p, + const struct xpart *xp, float *ret) { *ret = hydro_get_mass(p) * xp->cooling_data.HeII_frac; } -INLINE static void convert_part_HeIII_mass(const struct engine* e, - const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_part_HeIII_mass(const struct engine *e, + const struct part *p, + const struct xpart *xp, float *ret) { *ret = hydro_get_mass(p) * xp->cooling_data.HeIII_frac; } -INLINE static void convert_part_e_density(const struct engine* e, - const struct part* p, - const struct xpart* xp, float* ret) { +INLINE static void convert_part_e_density(const struct engine *e, + const struct part *p, + const struct xpart *xp, float *ret) { *ret = (float)xp->cooling_data.e_frac; } #ifdef RT_NONE -INLINE static void convert_mass_fractions(const struct engine* engine, - const struct part* part, - const struct xpart* xpart, - float* ret) { +INLINE static void convert_mass_fractions(const struct engine *engine, + const struct part *part, + const struct xpart *xpart, + float *ret) { ret[0] = (float)xpart->cooling_data.HI_frac; ret[1] = (float)xpart->cooling_data.HII_frac; @@ -131,8 +132,8 @@ INLINE static void convert_mass_fractions(const struct engine* engine, * @return Returns the number of fields to write. */ __attribute__((always_inline)) INLINE static int cooling_write_particles( - const struct part* parts, const struct xpart* xparts, - struct io_props* list) { + const struct part *parts, const struct xpart *xparts, + struct io_props *list) { int num = 0; @@ -141,99 +142,88 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( list[num] = io_make_output_field_convert_part( "AtomicHydrogenMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, convert_part_HI_mass, "Atomic hydrogen (HI) masses."); - num ++; + num++; list[num] = io_make_output_field_convert_part( "IonizedHydrogenMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, convert_part_HII_mass, "Ionized hydrogen (HII) masses."); - num ++; + num++; - list[num] = - io_make_output_field_convert_part( + list[num] = io_make_output_field_convert_part( "MolecularHydrogenMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, convert_part_H2_mass, "Molecular hydrogen (H2) masses."); - num ++; + num++; - list[num] = - io_make_output_field_convert_part( + list[num] = io_make_output_field_convert_part( "HeIMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, convert_part_HeII_mass, "HeI masses."); - num ++; + num++; - list[num] = - io_make_output_field_convert_part( + list[num] = io_make_output_field_convert_part( "HeIIMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, convert_part_HeII_mass, "HeII masses."); - num ++; + num++; - list[num] = - io_make_output_field_convert_part( + list[num] = io_make_output_field_convert_part( "HeIIIMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, convert_part_HeIII_mass, "HeIII masses."); - num ++; + num++; list[num] = io_make_output_field_convert_part( "ElectronNumberDensities", FLOAT, 1, UNIT_CONV_NUMBER_DENSITY, -3.f, - parts, xparts, convert_part_e_density, - "Electron number densities"); - num ++; + parts, xparts, convert_part_e_density, "Electron number densities"); + num++; #if COOLING_GRACKLE_MODE >= 2 - list[num] = - io_make_output_field("SubgridTemperatures", - FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, - cooling_data.subgrid_temp, - "Temperature of subgrid ISM in K"); - num ++; + list[num] = io_make_output_field( + "SubgridTemperatures", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + cooling_data.subgrid_temp, "Temperature of subgrid ISM in K"); + num++; list[num] = - io_make_output_field("SubgridDensities", - FLOAT, 1, UNIT_CONV_DENSITY, -3.f, parts, - cooling_data.subgrid_dens, + io_make_output_field("SubgridDensities", FLOAT, 1, UNIT_CONV_DENSITY, + -3.f, parts, cooling_data.subgrid_dens, "Mass density in physical units of subgrid ISM"); - num ++; + num++; - list[num] = - io_make_output_field("SubgridColdISMFraction", - FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, - cooling_data.subgrid_fcold, - "Fraction of particle mass in cold subgrid ISM"); - num ++; + list[num] = io_make_output_field( + "SubgridColdISMFraction", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + cooling_data.subgrid_fcold, + "Fraction of particle mass in cold subgrid ISM"); + num++; list[num] = - io_make_output_field("DustMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + io_make_output_field("DustMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, cooling_data.dust_mass, "Total mass in dust"); - num ++; + num++; - list[num] = io_make_output_field( - "DustMassFractions", FLOAT, chemistry_element_count, - UNIT_CONV_NO_UNITS, 0.f, parts, cooling_data.dust_mass_fraction, - "Fractions of the particles' masses that are in dust for a given element"); + list[num] = io_make_output_field("DustMassFractions", FLOAT, + chemistry_element_count, UNIT_CONV_NO_UNITS, + 0.f, parts, cooling_data.dust_mass_fraction, + "Fractions of the particles' masses that " + "are in dust for a given element"); num++; list[num] = - io_make_output_field("DustTemperatures", - FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, - cooling_data.dust_temperature, + io_make_output_field("DustTemperatures", FLOAT, 1, UNIT_CONV_NO_UNITS, + 0.f, parts, cooling_data.dust_temperature, "Dust temperature in subgrid dust model, in K"); - num ++; + num++; - list[num] = - io_make_output_field("CoolingTime", - FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, - cooling_data.mixing_layer_cool_time, - "Cooling time for particle; if it's currently a firehose wind" - "particle (delay_time>0), this is the mixing layer cooling time"); - num ++; + list[num] = io_make_output_field( + "CoolingTime", FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, + cooling_data.mixing_layer_cool_time, + "Cooling time for particle; if it's currently a firehose wind" + "particle (delay_time>0), this is the mixing layer cooling time"); + num++; #endif #endif #ifdef RT_NONE list[num] = io_make_output_field_convert_part( - "IonMassFractions", FLOAT, 2, UNIT_CONV_NO_UNITS, 0, parts, - xparts, convert_mass_fractions, - "Mass fractions of all constituent species"); - num ++; + "IonMassFractions", FLOAT, 2, UNIT_CONV_NO_UNITS, 0, parts, xparts, + convert_mass_fractions, "Mass fractions of all constituent species"); + num++; #endif return num; } @@ -246,8 +236,8 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( * @param phys_const The #phys_const. */ __attribute__((always_inline)) INLINE static void cooling_read_parameters( - struct swift_params* parameter_file, struct cooling_function_data* cooling, - const struct phys_const* phys_const, const struct unit_system* us) { + struct swift_params *parameter_file, struct cooling_function_data *cooling, + const struct phys_const *phys_const, const struct unit_system *us) { parser_get_param_string(parameter_file, "KIARACooling:cloudy_table", cooling->cloudy_table); @@ -280,101 +270,82 @@ __attribute__((always_inline)) INLINE static void cooling_read_parameters( parameter_file, "KIARACooling:ism_adiabatic_heating_method", 1); /* Initial step convergence */ - cooling->max_step = - parser_get_opt_param_int(parameter_file, - "KIARACooling:grackle_max_steps", - 500); - - cooling->timestep_accuracy = - parser_get_opt_param_double(parameter_file, - "KIARACooling:timestep_accuracy", 0.2); - - cooling->grackle_damping_interval = - parser_get_opt_param_double(parameter_file, - "KIARACooling:grackle_damping_interval", 5); - - cooling->thermal_time = - parser_get_opt_param_double(parameter_file, - "KIARACooling:thermal_time_myr", 0.); + cooling->max_step = parser_get_opt_param_int( + parameter_file, "KIARACooling:grackle_max_steps", 500); + + cooling->timestep_accuracy = parser_get_opt_param_double( + parameter_file, "KIARACooling:timestep_accuracy", 0.2); + + cooling->grackle_damping_interval = parser_get_opt_param_double( + parameter_file, "KIARACooling:grackle_damping_interval", 5); + + cooling->thermal_time = parser_get_opt_param_double( + parameter_file, "KIARACooling:thermal_time_myr", 0.); cooling->thermal_time *= phys_const->const_year * 1e6; - /* flag to turn on dust evolution option, only works for GRACKLE_CHEMISTRY>=2 (KIARA) */ - cooling->use_grackle_dust_evol = - parser_get_opt_param_int(parameter_file, - "KIARACooling:use_grackle_dust_evol", 1); + /* flag to turn on dust evolution option, only works for GRACKLE_CHEMISTRY>=2 + * (KIARA) */ + cooling->use_grackle_dust_evol = parser_get_opt_param_int( + parameter_file, "KIARACooling:use_grackle_dust_evol", 1); #if COOLING_GRACKLE_MODE <= 1 message("WARNING: Dust evol not implemented in SIMBA; use KIARA instead."); cooling->use_grackle_dust_evol = 0; #endif - /* These are dust parameters for KIARA's dust model (MODE>=2); irrelevant otherwise */ - cooling->dust_destruction_eff = - parser_get_opt_param_double(parameter_file, - "KIARACooling:dust_destruction_eff", 0.3); + /* These are dust parameters for KIARA's dust model (MODE>=2); irrelevant + * otherwise */ + cooling->dust_destruction_eff = parser_get_opt_param_double( + parameter_file, "KIARACooling:dust_destruction_eff", 0.3); - cooling->dust_sne_coeff = - parser_get_opt_param_double(parameter_file, - "KIARACooling:dust_sne_coeff", 1.0); + cooling->dust_sne_coeff = parser_get_opt_param_double( + parameter_file, "KIARACooling:dust_sne_coeff", 1.0); - cooling->dust_sne_shockspeed = - parser_get_opt_param_double(parameter_file, - "KIARACooling:dust_sne_shockspeed", 100.0); + cooling->dust_sne_shockspeed = parser_get_opt_param_double( + parameter_file, "KIARACooling:dust_sne_shockspeed", 100.0); - cooling->dust_grainsize = - parser_get_opt_param_double(parameter_file, - "KIARACooling:dust_grainsize", 0.1); + cooling->dust_grainsize = parser_get_opt_param_double( + parameter_file, "KIARACooling:dust_grainsize", 0.1); - cooling->dust_growth_densref = - parser_get_opt_param_double(parameter_file, - "KIARACooling:dust_growth_densref", 2.3e-20); + cooling->dust_growth_densref = parser_get_opt_param_double( + parameter_file, "KIARACooling:dust_growth_densref", 2.3e-20); - cooling->dust_growth_tauref = - parser_get_opt_param_double(parameter_file, - "KIARACooling:dust_growth_tauref", 1.0); + cooling->dust_growth_tauref = parser_get_opt_param_double( + parameter_file, "KIARACooling:dust_growth_tauref", 1.0); - cooling->cold_ISM_frac = - parser_get_opt_param_double(parameter_file, - "KIARACooling:cold_ISM_frac", 1.0); + cooling->cold_ISM_frac = parser_get_opt_param_double( + parameter_file, "KIARACooling:cold_ISM_frac", 1.0); - cooling->G0_computation_method = - parser_get_opt_param_double(parameter_file, - "KIARACooling:G0_computation_method", 3); + cooling->G0_computation_method = parser_get_opt_param_double( + parameter_file, "KIARACooling:G0_computation_method", 3); - cooling->max_subgrid_density = - parser_get_opt_param_double(parameter_file, - "KIARACooling:max_subgrid_density_g_p_cm3", - FLT_MAX); + cooling->max_subgrid_density = parser_get_opt_param_double( + parameter_file, "KIARACooling:max_subgrid_density_g_p_cm3", FLT_MAX); /* convert to internal units */ - cooling->max_subgrid_density /= units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + cooling->max_subgrid_density /= + units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); - cooling->subgrid_threshold_n_H_inv = - parser_get_opt_param_double(parameter_file, - "KIARACooling:subgrid_threshold_n_H_cgs", 0.13); + cooling->subgrid_threshold_n_H_inv = parser_get_opt_param_double( + parameter_file, "KIARACooling:subgrid_threshold_n_H_cgs", 0.13); /* convert to internal units, take inverse to save compute time */ - cooling->subgrid_threshold_n_H_inv /= units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); - cooling->subgrid_threshold_n_H_inv = 1.f / cooling->subgrid_threshold_n_H_inv; + cooling->subgrid_threshold_n_H_inv /= + units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); + cooling->subgrid_threshold_n_H_inv = 1.f / cooling->subgrid_threshold_n_H_inv; - cooling->subgrid_threshold_T = - parser_get_opt_param_double(parameter_file, - "KIARACooling:subgrid_threshold_T_K", 1.e4); + cooling->subgrid_threshold_T = parser_get_opt_param_double( + parameter_file, "KIARACooling:subgrid_threshold_T_K", 1.e4); /* convert to internal units */ - cooling->subgrid_threshold_T /= units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + cooling->subgrid_threshold_T /= + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); - cooling->subgrid_warm_ism_EOS = - parser_get_opt_param_double(parameter_file, - "KIARACooling:subgrid_warm_ism_EOS", 0.f); + cooling->subgrid_warm_ism_EOS = parser_get_opt_param_double( + parameter_file, "KIARACooling:subgrid_warm_ism_EOS", 0.f); - cooling->entropy_floor_margin = - parser_get_opt_param_double(parameter_file, - "KIARACooling:entropy_floor_margin_dex", - 1.0); + cooling->entropy_floor_margin = parser_get_opt_param_double( + parameter_file, "KIARACooling:entropy_floor_margin_dex", 1.0); cooling->entropy_floor_margin = pow(10.f, cooling->entropy_floor_margin); - cooling->self_enrichment_metallicity = - parser_get_opt_param_double(parameter_file, - "KIARACooling:self_enrichment_metallicity", - 0.f); - + cooling->self_enrichment_metallicity = parser_get_opt_param_double( + parameter_file, "KIARACooling:self_enrichment_metallicity", 0.f); } #endif /* SWIFT_COOLING_KIARA_IO_H */ diff --git a/src/cooling/KIARA/cooling_properties.h b/src/cooling/KIARA/cooling_properties.h index 0e48fa1f0a..a4d940f680 100644 --- a/src/cooling/KIARA/cooling_properties.h +++ b/src/cooling/KIARA/cooling_properties.h @@ -19,7 +19,6 @@ #ifndef SWIFT_COOLING_PROPERTIES_KIARA_H #define SWIFT_COOLING_PROPERTIES_KIARA_H - /* include grackle */ #include @@ -73,7 +72,7 @@ struct cooling_function_data { * GEAR) */ int self_shielding_method; - /*! What to do with adiabatic du/dt when in ISM mode: 0=no adiabatic heating, + /*! What to do with adiabatic du/dt when in ISM mode: 0=no adiabatic heating, * 1=evolve in grackle, 2=use to evaporate cold ism */ int ism_adiabatic_heating_method; @@ -83,7 +82,8 @@ struct cooling_function_data { /*! max fractional change in quantities in single grackle substep */ double timestep_accuracy; - /*! parameter to control how fast grackle damps oscillatory behaviour (lower=more aggressive) */ + /*! parameter to control how fast grackle damps oscillatory behaviour + * (lower=more aggressive) */ int grackle_damping_interval; /*! Duration for switching off cooling after an event (e.g. supernovae) */ @@ -92,10 +92,12 @@ struct cooling_function_data { /*! track dust growth and destruction (only available in KIARA) */ int use_grackle_dust_evol; - /*! track H2 formation; this is set within the code based on selection options */ + /*! track H2 formation; this is set within the code based on selection options + */ int use_grackle_h2_form; - /*! G0 conversion factors, scales to MW value based on local/global galaxy props */ + /*! G0 conversion factors, scales to MW value based on local/global galaxy + * props */ double G0_factor1; double G0_factor2; double G0_factorSNe; @@ -108,28 +110,34 @@ struct cooling_function_data { double dust_growth_densref; double dust_growth_tauref; - /*! For dust model, need self-enrichment up to a small metallicity to kick-start dust */ + /*! For dust model, need self-enrichment up to a small metallicity to + * kick-start dust */ double self_enrichment_metallicity; /*! For subgrid model (eg KIARA) need a subgrid ISM fraction */ double cold_ISM_frac; - /*! For Grackle subgrid model, choose way to determine G0: 1=Local SFR density; 2=Global sSFR */ + /*! For Grackle subgrid model, choose way to determine G0: 1=Local SFR + * density; 2=Global sSFR */ int G0_computation_method; - /*! For Grackle subgrid model, set max density to avoid pointlessly over-iterating in Grackle */ + /*! For Grackle subgrid model, set max density to avoid pointlessly + * over-iterating in Grackle */ double max_subgrid_density; - /*! For Grackle subgrid model, inverse of threshold nH above which multi-phase ISM model kicks in */ + /*! For Grackle subgrid model, inverse of threshold nH above which multi-phase + * ISM model kicks in */ double subgrid_threshold_n_H_inv; /*! For Grackle subgrid model, temperature at threshold nH */ double subgrid_threshold_T; - /*! For Grackle subgrid model, Power-law eqn of state for warm ISM component above threshold n_H */ + /*! For Grackle subgrid model, Power-law eqn of state for warm ISM component + * above threshold n_H */ double subgrid_warm_ism_EOS; - /*! For Grackle subgrid model, factor above entropy floor allowed to be in subgrid mode */ + /*! For Grackle subgrid model, factor above entropy floor allowed to be in + * subgrid mode */ double entropy_floor_margin; /*! Option to use Cloudy lookup tables when outside ISM */ diff --git a/src/feedback/KIARA/feedback.c b/src/feedback/KIARA/feedback.c index c4a0da4683..71fea7380a 100644 --- a/src/feedback/KIARA/feedback.c +++ b/src/feedback/KIARA/feedback.c @@ -2,7 +2,7 @@ * This file is part of SWIFT. * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2022 Doug Rennehan (douglas.rennehan@gmail.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 @@ -28,121 +28,109 @@ #include "timers.h" #include "timestep_sync_part.h" - #if COOLING_GRACKLE_MODE >= 2 -/* This seems to be needed to get N_SNe and mass loss rates +/* This seems to be needed to get N_SNe and mass loss rates * correct in chem5 Kroupa/Chabrier. Not sure why. */ -#define IMF_FUDGE_FACTOR 1.0f +#define IMF_FUDGE_FACTOR 1.0f /** * @brief Return log10 of the Habing band luminosity for a given star - * based on its age and metallicity, in erg/s + * based on its age and metallicity, in erg/s * * @param sp The #spart outputting the radiation * @param age The age of the star in internal units */ -double feedback_get_lum_from_star_particle(const struct spart* sp, - double age, - const struct feedback_props* fb_props) { +double feedback_get_lum_from_star_particle( + const struct spart *sp, double age, const struct feedback_props *fb_props) { /* Get age, convert to Myr */ age *= fb_props->time_to_Myr; - if (age < 1.) age = 1.; /* lum is roughly constant prior to 1 Myr */ + if (age < 1.) age = 1.; /* lum is roughly constant prior to 1 Myr */ /* Stars past 1000 Myr have negligible Habing flux; return very small log10 */ - if (age > 1000.) return -20.f; + if (age > 1000.) return -20.f; age = log10(age); - /* Get mass in units of 10^6 Mo, which is the units of the STARBURST99 models */ - double logmass6 = log10(sp->mass * fb_props->mass_to_solar_mass * 1.e-6); + /* Get mass in units of 10^6 Mo, which is the units of the STARBURST99 models + */ + double logmass6 = log10(sp->mass * fb_props->mass_to_solar_mass * 1.e-6); /* set up metallicity interpolation */ double z = sp->chemistry_data.metal_mass_fraction_total; double z_bins[5] = {0.04, 0.02, 0.008, 0.004, 0.001}; double lum1, lum2, fhi = 0., flo = 1.; - /* Interpolate luminosity in Habing band based on fits to STARBURST99 models - * (cf. G0_polyfit.py), for various metallicities + /* Interpolate luminosity in Habing band based on fits to STARBURST99 models + * (cf. G0_polyfit.py), for various metallicities */ - if (age > 0.8) { /* Do older star case, well fit by power law */ + if (age > 0.8) { /* Do older star case, well fit by power law */ if (z > z_bins[0]) { lum1 = 42.9568 - 1.66469 * age; lum2 = lum1; - } - else if (z > z_bins[1]) { + } else if (z > z_bins[1]) { lum1 = 42.9568 - 1.66469 * age; lum2 = 42.9754 - 1.57329 * age; fhi = LOG_INTERPOLATION(z, z_bins[0], z_bins[1]); - } - else if (z > z_bins[2]) { + } else if (z > z_bins[2]) { lum1 = 42.9754 - 1.57329 * age; lum2 = 43.003 - 1.49815 * age; fhi = LOG_INTERPOLATION(z, z_bins[1], z_bins[2]); - } - else if (z > z_bins[3]) { + } else if (z > z_bins[3]) { lum1 = 43.003 - 1.49815 * age; lum2 = 43.0151 - 1.46258 * age; fhi = LOG_INTERPOLATION(z, z_bins[2], z_bins[3]); - } - else if (z > z_bins[4]) { + } else if (z > z_bins[4]) { lum1 = 43.0151 - 1.46258 * age; lum2 = 43.0254 - 1.40997 * age; fhi = LOG_INTERPOLATION(z, z_bins[3], z_bins[4]); - } - else { + } else { lum1 = 43.0254 - 1.40997 * age; lum2 = lum1; } - } - else { /* Otherwise the star is very young and bright, so use more accurate 6th order polynomial fit */ + } else { /* Otherwise the star is very young and bright, so use more accurate + 6th order polynomial fit */ if (z > z_bins[0]) { - lum1 = 41.8537 + 6.40018 * pow(age, 1) - 46.6675 * pow(age, 2) - + 180.784 * pow(age, 3) - 373.188 * pow(age, 4) - + 374.251 * pow(age, 5) - 144.345 * pow(age, 6); + lum1 = 41.8537 + 6.40018 * pow(age, 1) - 46.6675 * pow(age, 2) + + 180.784 * pow(age, 3) - 373.188 * pow(age, 4) + + 374.251 * pow(age, 5) - 144.345 * pow(age, 6); lum2 = lum1; - } - else if (z > z_bins[1]) { - lum1 = 41.8537 + 6.40018 * pow(age, 1) - - 46.6675 * pow(age, 2) + 180.784 * pow(age,3) - - 373.188 * pow(age, 4) + 374.251 * pow(age, 5) - - 144.345 * pow(age, 6); - lum2 = 41.3428 + 17.0277 * pow(age, 1) - 132.565 * pow(age, 2) - + 508.436 * pow(age, 3) - 998.223 * pow(age, 4) - + 954.621 * pow(age, 5) - 353.419 * pow(age, 6); + } else if (z > z_bins[1]) { + lum1 = 41.8537 + 6.40018 * pow(age, 1) - 46.6675 * pow(age, 2) + + 180.784 * pow(age, 3) - 373.188 * pow(age, 4) + + 374.251 * pow(age, 5) - 144.345 * pow(age, 6); + lum2 = 41.3428 + 17.0277 * pow(age, 1) - 132.565 * pow(age, 2) + + 508.436 * pow(age, 3) - 998.223 * pow(age, 4) + + 954.621 * pow(age, 5) - 353.419 * pow(age, 6); fhi = LOG_INTERPOLATION(z, z_bins[0], z_bins[1]); - } - else if (z > z_bins[2]) { - lum1 = 41.3428+17.0277*pow(age,1) -132.565*pow(age,2) - +508.436*pow(age,3) -998.223*pow(age,4) - +954.621*pow(age,5) -353.419*pow(age,6); - lum2 = 41.0623+22.0205*pow(age,1) -172.018*pow(age,2) - +655.587*pow(age,3) -1270.91*pow(age,4) - +1201.92*pow(age,5) -441.57*pow(age,6); + } else if (z > z_bins[2]) { + lum1 = 41.3428 + 17.0277 * pow(age, 1) - 132.565 * pow(age, 2) + + 508.436 * pow(age, 3) - 998.223 * pow(age, 4) + + 954.621 * pow(age, 5) - 353.419 * pow(age, 6); + lum2 = 41.0623 + 22.0205 * pow(age, 1) - 172.018 * pow(age, 2) + + 655.587 * pow(age, 3) - 1270.91 * pow(age, 4) + + 1201.92 * pow(age, 5) - 441.57 * pow(age, 6); fhi = LOG_INTERPOLATION(z, z_bins[1], z_bins[2]); - } - else if (z > z_bins[3]) { - lum1 = 41.0623+22.0205*pow(age,1) -172.018*pow(age,2) - +655.587*pow(age,3) -1270.91*pow(age,4) - +1201.92*pow(age,5) -441.57*pow(age,6); - lum2 = 41.3442+16.0189*pow(age,1) -126.891*pow(age,2) - +488.303*pow(age,3) -945.774*pow(age,4) - +887.47*pow(age,5) -322.584*pow(age,6); + } else if (z > z_bins[3]) { + lum1 = 41.0623 + 22.0205 * pow(age, 1) - 172.018 * pow(age, 2) + + 655.587 * pow(age, 3) - 1270.91 * pow(age, 4) + + 1201.92 * pow(age, 5) - 441.57 * pow(age, 6); + lum2 = 41.3442 + 16.0189 * pow(age, 1) - 126.891 * pow(age, 2) + + 488.303 * pow(age, 3) - 945.774 * pow(age, 4) + + 887.47 * pow(age, 5) - 322.584 * pow(age, 6); fhi = LOG_INTERPOLATION(z, z_bins[2], z_bins[3]); - } - else if (z > z_bins[4]) { - lum1 = 41.3442+16.0189*pow(age,1) -126.891*pow(age,2) - +488.303*pow(age,3) -945.774*pow(age,4) - +887.47*pow(age,5) -322.584*pow(age,6); - lum2 = 40.738+25.8218*pow(age,1) -185.778*pow(age,2) - +641.036*pow(age,3) -1113.61*pow(age,4) - +937.23*pow(age,5) -304.342*pow(age,6); + } else if (z > z_bins[4]) { + lum1 = 41.3442 + 16.0189 * pow(age, 1) - 126.891 * pow(age, 2) + + 488.303 * pow(age, 3) - 945.774 * pow(age, 4) + + 887.47 * pow(age, 5) - 322.584 * pow(age, 6); + lum2 = 40.738 + 25.8218 * pow(age, 1) - 185.778 * pow(age, 2) + + 641.036 * pow(age, 3) - 1113.61 * pow(age, 4) + + 937.23 * pow(age, 5) - 304.342 * pow(age, 6); fhi = LOG_INTERPOLATION(z, z_bins[3], z_bins[4]); - } - else { - lum1 = 40.738 + 25.8218 * pow(age, 1) - 185.778 * pow(age, 2) - + 641.036 * pow(age, 3) - 1113.61 * pow(age, 4) - + 937.23 * pow(age, 5) - 304.342 * pow(age, 6); + } else { + lum1 = 40.738 + 25.8218 * pow(age, 1) - 185.778 * pow(age, 2) + + 641.036 * pow(age, 3) - 1113.61 * pow(age, 4) + + 937.23 * pow(age, 5) - 304.342 * pow(age, 6); lum2 = lum1; } } @@ -154,10 +142,8 @@ double feedback_get_lum_from_star_particle(const struct spart* sp, } void feedback_dust_production_condensation( - struct spart* sp, - double star_age, - const struct feedback_props* fb_props, - double delta_metal_mass[chemistry_element_count]) { + struct spart *sp, double star_age, const struct feedback_props *fb_props, + double delta_metal_mass[chemistry_element_count]) { const double *delta_table; int k; @@ -170,65 +156,61 @@ void feedback_dust_production_condensation( sp->feedback_data.delta_dust_mass[k] = 0.f; } - const double C_minus_O = - delta_metal_mass[chemistry_element_C] - - delta_metal_mass[chemistry_element_O]; + const double C_minus_O = delta_metal_mass[chemistry_element_C] - + delta_metal_mass[chemistry_element_O]; if (star_age > 100. && C_minus_O > 0.) { - /* Compute dust mass created in high-C/O AGB stars - * (atomic C forms graphite) + /* Compute dust mass created in high-C/O AGB stars + * (atomic C forms graphite) */ - sp->feedback_data.delta_dust_mass[chemistry_element_C] = - fb_props->delta_AGBCOG1[chemistry_element_C] * - (delta_metal_mass[chemistry_element_C] - - 0.75 * delta_metal_mass[chemistry_element_O]); + sp->feedback_data.delta_dust_mass[chemistry_element_C] = + fb_props->delta_AGBCOG1[chemistry_element_C] * + (delta_metal_mass[chemistry_element_C] - + 0.75 * delta_metal_mass[chemistry_element_O]); const double max_dust_C = fb_props->max_dust_fraction * delta_metal_mass[chemistry_element_C]; - /* Cap the new dust mass formed to some fraction of total ejecta - * metals in that element + /* Cap the new dust mass formed to some fraction of total ejecta + * metals in that element */ if (sp->feedback_data.delta_dust_mass[chemistry_element_C] > max_dust_C) { sp->feedback_data.delta_dust_mass[chemistry_element_C] = max_dust_C; } /* Subtract this from ejecta metals */ - delta_metal_mass[chemistry_element_C] -= + delta_metal_mass[chemistry_element_C] -= sp->feedback_data.delta_dust_mass[chemistry_element_C]; - } - else { - /* Choose dust table: If age > 100 Myr, assume ejecta is from AGB, - * otherwise SNII + } else { + /* Choose dust table: If age > 100 Myr, assume ejecta is from AGB, + * otherwise SNII */ if (star_age > 100.) { delta_table = fb_props->delta_AGBCOL1; - } - else { + } else { delta_table = fb_props->delta_SNII; } - /* Compute dust mass created in either SNII or low-C/O AGB stars - * (same type of dust, just different coefficients) + /* Compute dust mass created in either SNII or low-C/O AGB stars + * (same type of dust, just different coefficients) */ for (k = chemistry_element_He; k < chemistry_element_count; k++) { - if (k == chemistry_element_O) {/* O in oxide of Mg, Si, S, Ca, (Ti), Fe */ - sp->feedback_data.delta_dust_mass[k] = - 16.0 * (delta_table[chemistry_element_Mg] * - delta_metal_mass[chemistry_element_Mg] / 24.305 - + delta_table[chemistry_element_Si] * - delta_metal_mass[chemistry_element_Si] / 28.0855 - + fb_props->delta_AGBCOL1[chemistry_element_S] * - delta_metal_mass[chemistry_element_S] / 32.065 - + fb_props->delta_AGBCOL1[chemistry_element_Ca] * - delta_metal_mass[chemistry_element_Ca] / 40.078 - + fb_props->delta_AGBCOL1[chemistry_element_Fe] * - delta_metal_mass[chemistry_element_Fe] / 55.845); - } - else { - sp->feedback_data.delta_dust_mass[k] = + if (k == + chemistry_element_O) { /* O in oxide of Mg, Si, S, Ca, (Ti), Fe */ + sp->feedback_data.delta_dust_mass[k] = + 16.0 * (delta_table[chemistry_element_Mg] * + delta_metal_mass[chemistry_element_Mg] / 24.305 + + delta_table[chemistry_element_Si] * + delta_metal_mass[chemistry_element_Si] / 28.0855 + + fb_props->delta_AGBCOL1[chemistry_element_S] * + delta_metal_mass[chemistry_element_S] / 32.065 + + fb_props->delta_AGBCOL1[chemistry_element_Ca] * + delta_metal_mass[chemistry_element_Ca] / 40.078 + + fb_props->delta_AGBCOL1[chemistry_element_Fe] * + delta_metal_mass[chemistry_element_Fe] / 55.845); + } else { + sp->feedback_data.delta_dust_mass[k] = delta_table[k] * delta_metal_mass[k]; } - const double max_dust = - fb_props->max_dust_fraction * delta_metal_mass[k]; + const double max_dust = fb_props->max_dust_fraction * delta_metal_mass[k]; if (sp->feedback_data.delta_dust_mass[k] > max_dust) { sp->feedback_data.delta_dust_mass[k] = max_dust; } @@ -236,7 +218,6 @@ void feedback_dust_production_condensation( delta_metal_mass[k] -= sp->feedback_data.delta_dust_mass[k]; } } - } #endif @@ -251,17 +232,13 @@ void feedback_dust_production_condensation( * @param ejecta_energy The total ejected energy in code units. * @param ejecta_mass The total ejected mass in code units. * @param ejecta_unprocessed The unprocessed mass in code units. - * @param ejecta_metal_mass The metal masses for each element in chem5_element_count in code units. + * @param ejecta_metal_mass The metal masses for each element in + * chem5_element_count in code units. */ -void feedback_get_ejecta_from_star_particle(const struct spart* sp, - double age, - const struct feedback_props* fb_props, - double dt, - double *N_SNe, - double *ejecta_energy, - double *ejecta_mass, - double *ejecta_unprocessed, - double ejecta_metal_mass[chem5_element_count]) { +void feedback_get_ejecta_from_star_particle( + const struct spart *sp, double age, const struct feedback_props *fb_props, + double dt, double *N_SNe, double *ejecta_energy, double *ejecta_mass, + double *ejecta_unprocessed, double ejecta_metal_mass[chem5_element_count]) { int j, k, j1, j2, l, l1 = 0, l2 = 0, ll1 = 0, ll2 = 0, lll1 = 0, lll2 = 0; double SW_R, SNII_R, SNII_U, SNII_E, SNII_Z[chem5_element_count]; double SNII_ENE, SNIa_R, SNIa_E = 0., SNIa_Z[chem5_element_count]; @@ -289,10 +266,9 @@ void feedback_get_ejecta_from_star_particle(const struct spart* sp, double feh = -10.; if (z < 1.e-10) { lz = -10.; - } - else { + } else { lz = log10(z); - feh = sp->chemistry_data.metal_mass_fraction[chemistry_element_Fe] / + feh = sp->chemistry_data.metal_mass_fraction[chemistry_element_Fe] / sp->chemistry_data.metal_mass_fraction[chemistry_element_H]; if (feh > 0.) feh = log10((feh / fb_props->Fe_mf) * fb_props->H_mf); if (feh > fb_props->tables.SNLZ1R[NZSN1R - 1]) { @@ -315,54 +291,34 @@ void feedback_get_ejecta_from_star_particle(const struct spart* sp, /* This is only true if we do PopIII stars */ if (z <= fb_props->zmax3) { SNII_U = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(1, 0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(1, 0, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(1, 0, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(1, 0, j2)], + ltm); SNII_E = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(2, 0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(2, 0, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(2, 0, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(2, 0, j2)], + ltm); SNII_ENE = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(0, 0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(0, 0, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(0, 0, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(0, 0, j2)], + ltm); for (k = 0; k < chem5_element_count; k++) { SNII_Z[k] = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j2)], - ltm - ); + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j2)], ltm); } SNII_R = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2R[SN2R_idx(0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2R[SN2R_idx(0, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2R[SN2R_idx(0, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2R[SN2R_idx(0, j2)], ltm); SW_R = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SWR[SWR_idx(0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SWR[SWR_idx(0, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SWR[SWR_idx(0, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SWR[SWR_idx(0, j2)], ltm); SNIa_R = 0.0; - } - else { + } else { for (l = 2; l < NZSN; l++) { l1 = l - 1; l2 = l; @@ -380,191 +336,105 @@ void feedback_get_ejecta_from_star_particle(const struct spart* sp, } SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(1, l1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(1, l1, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(1, l1, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(1, l1, j2)], + ltm); SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(1, l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(1, l2, j2)], - ltm - ); - SNII_U = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - SNIIa, - fb_props->tables.SNLZ[l2], - SNIIb, - lz - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(1, l2, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(1, l2, j2)], + ltm); + SNII_U = LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], SNIIa, + fb_props->tables.SNLZ[l2], SNIIb, lz); SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(2, l1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(2, l1, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(2, l1, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(2, l1, j2)], + ltm); SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(2, l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(2, l2, j2)], - ltm - ); - SNII_E = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - SNIIa, - fb_props->tables.SNLZ[l2], - SNIIb, - lz - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(2, l2, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(2, l2, j2)], + ltm); + SNII_E = LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], SNIIa, + fb_props->tables.SNLZ[l2], SNIIb, lz); if (l2 == NZSN - 1) { SNII_ENE = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], - ltm - ); - } - else { + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], + ltm); + } else { SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - fb_props->tables.SN2E[SN2E_idx(0, l1, j1)], - fb_props->tables.SNLZ[l2], - fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], - lz - ); + fb_props->tables.SNLZ[l1], fb_props->tables.SN2E[SN2E_idx(0, l1, j1)], + fb_props->tables.SNLZ[l2], fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], + lz); SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - fb_props->tables.SN2E[SN2E_idx(0, l1, j2)], - fb_props->tables.SNLZ[l2], - fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], - lz - ); - SNII_ENE = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - SNIIa, - fb_props->tables.SNLM[j2], - SNIIb, - ltm - ); + fb_props->tables.SNLZ[l1], fb_props->tables.SN2E[SN2E_idx(0, l1, j2)], + fb_props->tables.SNLZ[l2], fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], + lz); + SNII_ENE = LINEAR_INTERPOLATION(fb_props->tables.SNLM[j1], SNIIa, + fb_props->tables.SNLM[j2], SNIIb, ltm); } for (k = 0; k < chem5_element_count; k++) { SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j2)], - ltm - ); + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j2)], ltm); SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j2)], - ltm - ); - SNII_Z[k] = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - SNIIa, - fb_props->tables.SNLZ[l2], - SNIIb, - lz - ); - } - - SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2R[SN2R_idx(l1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2R[SN2R_idx(l1, j2)], - ltm - ); - SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2R[SN2R_idx(l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2R[SN2R_idx(l2, j2)], - ltm - ); - SNII_R = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - SNIIa, - fb_props->tables.SNLZ[l2], - SNIIb, - lz - ); + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j2)], ltm); + SNII_Z[k] = LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], SNIIa, + fb_props->tables.SNLZ[l2], SNIIb, lz); + } + + SNIIa = LINEAR_INTERPOLATION(fb_props->tables.SNLM[j1], + fb_props->tables.SN2R[SN2R_idx(l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2R[SN2R_idx(l1, j2)], ltm); + SNIIb = LINEAR_INTERPOLATION(fb_props->tables.SNLM[j1], + fb_props->tables.SN2R[SN2R_idx(l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2R[SN2R_idx(l2, j2)], ltm); + SNII_R = LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], SNIIa, + fb_props->tables.SNLZ[l2], SNIIb, lz); SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SWR[SWR_idx(l1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SWR[SWR_idx(l1, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SWR[SWR_idx(l1, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SWR[SWR_idx(l1, j2)], ltm); SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SWR[SWR_idx(l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SWR[SWR_idx(l2, j2)], - ltm - ); - SW_R = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - SNIIa, - fb_props->tables.SNLZ[l2], - SNIIb, - lz - ); + fb_props->tables.SNLM[j1], fb_props->tables.SWR[SWR_idx(l2, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SWR[SWR_idx(l2, j2)], ltm); + SW_R = LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], SNIIa, + fb_props->tables.SNLZ[l2], SNIIb, lz); if (feh < fb_props->tables.SNLZ1R[0]) { SNIa_R = 0.; - } - else { - SNIa_E = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ1Y[lll1], - fb_props->tables.SN1E[SN1E_idx(2, lll1)], - fb_props->tables.SNLZ1Y[lll2], - fb_props->tables.SN1E[SN1E_idx(2, lll2)], - lz - ); + } else { + SNIa_E = + LINEAR_INTERPOLATION(fb_props->tables.SNLZ1Y[lll1], + fb_props->tables.SN1E[SN1E_idx(2, lll1)], + fb_props->tables.SNLZ1Y[lll2], + fb_props->tables.SN1E[SN1E_idx(2, lll2)], lz); for (k = 0; k < chem5_element_count; k++) { SNIa_Z[k] = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ1Y[lll1], - fb_props->tables.SN1E[SN1E_idx((k + 3), lll1)], - fb_props->tables.SNLZ1Y[lll2], - fb_props->tables.SN1E[SN1E_idx((k + 3), lll2)], - lz - ); + fb_props->tables.SNLZ1Y[lll1], + fb_props->tables.SN1E[SN1E_idx((k + 3), lll1)], + fb_props->tables.SNLZ1Y[lll2], + fb_props->tables.SN1E[SN1E_idx((k + 3), lll2)], lz); } SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN1R[SN1R_idx(ll1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN1R[SN1R_idx(ll1, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN1R[SN1R_idx(ll1, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN1R[SN1R_idx(ll1, j2)], + ltm); SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN1R[SN1R_idx(ll2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN1R[SN1R_idx(ll2, j2)], - ltm - ); - SNIa_R = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ1R[ll1], - SNIIa, - fb_props->tables.SNLZ1R[ll2], - SNIIb, - feh - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN1R[SN1R_idx(ll2, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN1R[SN1R_idx(ll2, j2)], + ltm); + SNIa_R = LINEAR_INTERPOLATION(fb_props->tables.SNLZ1R[ll1], SNIIa, + fb_props->tables.SNLZ1R[ll2], SNIIb, feh); } } @@ -574,7 +444,7 @@ void feedback_get_ejecta_from_star_particle(const struct spart* sp, tm2 = feedback_get_turnover_mass(fb_props, age - dt, z); ltm = log10(tm2 * fb_props->solar_mass_to_mass); - for (j = 1 ; j < NM; j++) { + for (j = 1; j < NM; j++) { j1 = j - 1; j2 = j; if (fb_props->tables.SNLM[j] < ltm) break; @@ -582,221 +452,126 @@ void feedback_get_ejecta_from_star_particle(const struct spart* sp, if (z <= fb_props->zmax3) { SNII_U -= LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(1, 0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(1, 0, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(1, 0, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(1, 0, j2)], + ltm); SNII_E -= LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(2, 0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(2, 0, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(2, 0, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(2, 0, j2)], + ltm); SNII_ENE -= LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(0, 0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(0, 0, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(0, 0, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(0, 0, j2)], + ltm); for (k = 0; k < chem5_element_count; k++) { SNII_Z[k] -= LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j2)], - ltm - ); + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), 0, j2)], ltm); } SNII_R -= LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2R[SN2R_idx(0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2R[SN2R_idx(0, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2R[SN2R_idx(0, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2R[SN2R_idx(0, j2)], + ltm); SW_R -= LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SWR[SWR_idx(0, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SWR[SWR_idx(0, j2)], - ltm - ); - } - else { + fb_props->tables.SNLM[j1], fb_props->tables.SWR[SWR_idx(0, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SWR[SWR_idx(0, j2)], ltm); + } else { SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(1, l1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(1, l1, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(1, l1, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(1, l1, j2)], + ltm); SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(1, l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(1, l2, j2)], - ltm - ); - SNII_U -= LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - SNIIa, - fb_props->tables.SNLZ[l2], - SNIIb, - lz - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(1, l2, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(1, l2, j2)], + ltm); + SNII_U -= LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], SNIIa, + fb_props->tables.SNLZ[l2], SNIIb, lz); SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(2, l1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(2, l1, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(2, l1, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(2, l1, j2)], + ltm); SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(2, l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(2, l2, j2)], - ltm - ); - SNII_E -= LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - SNIIa, - fb_props->tables.SNLZ[l2], - SNIIb, - lz - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2E[SN2E_idx(2, l2, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2E[SN2E_idx(2, l2, j2)], + ltm); + SNII_E -= LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], SNIIa, + fb_props->tables.SNLZ[l2], SNIIb, lz); if (l2 == NZSN - 1) { SNII_ENE -= LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], - ltm - ); - } - else { - SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - fb_props->tables.SN2E[SN2E_idx(0, l1, j1)], - fb_props->tables.SNLZ[l2], - fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], - lz - ); - SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - fb_props->tables.SN2E[SN2E_idx(0, l1, j2)], - fb_props->tables.SNLZ[l2], - fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], - lz - ); - SNII_ENE -= LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - SNIIa, - fb_props->tables.SNLM[j2], - SNIIb, - ltm - ); + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], ltm); + } else { + SNIIa = LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], + fb_props->tables.SN2E[SN2E_idx(0, l1, j1)], + fb_props->tables.SNLZ[l2], + fb_props->tables.SN2E[SN2E_idx(0, l2, j1)], + lz); + SNIIb = LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], + fb_props->tables.SN2E[SN2E_idx(0, l1, j2)], + fb_props->tables.SNLZ[l2], + fb_props->tables.SN2E[SN2E_idx(0, l2, j2)], + lz); + SNII_ENE -= LINEAR_INTERPOLATION(fb_props->tables.SNLM[j1], SNIIa, + fb_props->tables.SNLM[j2], SNIIb, ltm); } - + for (k = 0; k < chem5_element_count; k++) { SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j2)], - ltm - ); + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), l1, j2)], ltm); SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j2)], - ltm - ); - SNII_Z[k] -= LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - SNIIa, - fb_props->tables.SNLZ[l2], - SNIIb, - lz - ); + fb_props->tables.SNLM[j1], + fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SN2E[SN2E_idx((k + 3), l2, j2)], ltm); + SNII_Z[k] -= LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], SNIIa, + fb_props->tables.SNLZ[l2], SNIIb, lz); } SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2R[SN2R_idx(l1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2R[SN2R_idx(l1, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2R[SN2R_idx(l1, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2R[SN2R_idx(l1, j2)], + ltm); SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN2R[SN2R_idx(l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN2R[SN2R_idx(l2, j2)], - ltm - ); - SNII_R -= LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - SNIIa, - fb_props->tables.SNLZ[l2], - SNIIb, - lz - ); - SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SWR[SWR_idx(l1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SWR[SWR_idx(l1, j2)], - ltm - ); - SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SWR[SWR_idx(l2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SWR[SWR_idx(l2, j2)], - ltm - ); - SW_R -= LINEAR_INTERPOLATION( - fb_props->tables.SNLZ[l1], - SNIIa, - fb_props->tables.SNLZ[l2], - SNIIb, - lz - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN2R[SN2R_idx(l2, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN2R[SN2R_idx(l2, j2)], + ltm); + SNII_R -= LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], SNIIa, + fb_props->tables.SNLZ[l2], SNIIb, lz); + SNIIa = LINEAR_INTERPOLATION(fb_props->tables.SNLM[j1], + fb_props->tables.SWR[SWR_idx(l1, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SWR[SWR_idx(l1, j2)], ltm); + SNIIb = LINEAR_INTERPOLATION(fb_props->tables.SNLM[j1], + fb_props->tables.SWR[SWR_idx(l2, j1)], + fb_props->tables.SNLM[j2], + fb_props->tables.SWR[SWR_idx(l2, j2)], ltm); + SW_R -= LINEAR_INTERPOLATION(fb_props->tables.SNLZ[l1], SNIIa, + fb_props->tables.SNLZ[l2], SNIIb, lz); if (feh < fb_props->tables.SNLZ1R[0]) { SNIa_R = 0.; - } - else { + } else { SNIIa = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN1R[SN1R_idx(ll1, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN1R[SN1R_idx(ll1, j2)], - ltm - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN1R[SN1R_idx(ll1, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN1R[SN1R_idx(ll1, j2)], + ltm); SNIIb = LINEAR_INTERPOLATION( - fb_props->tables.SNLM[j1], - fb_props->tables.SN1R[SN1R_idx(ll2, j1)], - fb_props->tables.SNLM[j2], - fb_props->tables.SN1R[SN1R_idx(ll2, j2)], - ltm - ); - SNIa_R -= LINEAR_INTERPOLATION( - fb_props->tables.SNLZ1R[ll1], - SNIIa, - fb_props->tables.SNLZ1R[ll2], - SNIIb, - feh - ); + fb_props->tables.SNLM[j1], fb_props->tables.SN1R[SN1R_idx(ll2, j1)], + fb_props->tables.SNLM[j2], fb_props->tables.SN1R[SN1R_idx(ll2, j2)], + ltm); + SNIa_R -= + LINEAR_INTERPOLATION(fb_props->tables.SNLZ1R[ll1], SNIIa, + fb_props->tables.SNLZ1R[ll2], SNIIb, feh); } } } @@ -806,8 +581,8 @@ void feedback_get_ejecta_from_star_particle(const struct spart* sp, /* For some reason at the first step this might happen */ if (isnan(SNII_U) || isnan(SNII_E)) { - warning("SNII_U or SNII_E is NaN, j1=%d l1=%d z=%g mturn=%g %g age=%g", - j1, l1, z, tm1, tm2, age); + warning("SNII_U or SNII_E is NaN, j1=%d l1=%d z=%g mturn=%g %g age=%g", j1, + l1, z, tm1, tm2, age); *ejecta_unprocessed = *ejecta_mass = 0.; return; } @@ -816,22 +591,19 @@ void feedback_get_ejecta_from_star_particle(const struct spart* sp, fb = 3; if (z == 0.) { *ejecta_energy = 0.; - } - else { + } else { SWn = sp->mass_init * SW_R; if (fb_props->with_HN_energy_from_chem5) { /* E_sw converts to code units */ - *ejecta_energy = - SWn * fb_props->E_sw * pow(z / fb_props->Z_mf, 0.8); + *ejecta_energy = SWn * fb_props->E_sw * pow(z / fb_props->Z_mf, 0.8); } - /* Needed for dust model within Grackle; for now treat PopIII SNe + /* Needed for dust model within Grackle; for now treat PopIII SNe * same as PopI/II */ - *N_SNe = SWn; + *N_SNe = SWn; } - } - else { + } else { if (tm2 > fb_props->M_l2 || fb_first == 1) { fb = 2; @@ -841,10 +613,10 @@ void feedback_get_ejecta_from_star_particle(const struct spart* sp, *ejecta_energy = SWn * fb_props->E_sw; *ejecta_energy += sp->mass_init * SNII_ENE; } - /* Needed for dust model within Grackle; for now - * treat PopIII SNe same as PopI/II + /* Needed for dust model within Grackle; for now + * treat PopIII SNe same as PopI/II */ - *N_SNe = SNn + SWn; + *N_SNe = SNn + SWn; } for (k = 0; k < chem5_element_count; k++) { @@ -854,8 +626,7 @@ void feedback_get_ejecta_from_star_particle(const struct spart* sp, if (tm1 <= fb_props->M_l2) { if (feh < fb_props->tables.SNLZ1R[0]) { SNIa_R = 0.; - } - else { + } else { fb = 1; SNn = sp->mass_init * SNIa_R; /* SNIa always contribute */ @@ -878,31 +649,28 @@ void feedback_get_ejecta_from_star_particle(const struct spart* sp, } // Add in the TypeIa's too - *N_SNe += SNn; + *N_SNe += SNn; } } } if (*ejecta_energy < 0.) { - warning("Star %lld energy<0! m=%g (frac=%g), age=%g Myr, Z=%g " - "is ejecting %g Msun (fIa=%g, Zej=%g) and %g erg (%g in SNe) " - "in %g Myr.", - sp->id, - sp->mass * fb_props->mass_to_solar_mass, - sp->mass/sp->mass_init, - (age / fb_props->time_to_yr) * fb_props->time_to_Myr, - log10(z + 1.e-6), - *ejecta_mass * fb_props->mass_to_solar_mass, - ejecta_mass_Ia / *ejecta_mass, - log10(ejecta_metal_mass[0] / *ejecta_mass + 1.e-6), - *ejecta_energy * fb_props->energy_to_cgs, - *N_SNe * 1.e51, - (dt / fb_props->time_to_yr) * fb_props->time_to_Myr); + warning( + "Star %lld energy<0! m=%g (frac=%g), age=%g Myr, Z=%g " + "is ejecting %g Msun (fIa=%g, Zej=%g) and %g erg (%g in SNe) " + "in %g Myr.", + sp->id, sp->mass * fb_props->mass_to_solar_mass, + sp->mass / sp->mass_init, + (age / fb_props->time_to_yr) * fb_props->time_to_Myr, log10(z + 1.e-6), + *ejecta_mass * fb_props->mass_to_solar_mass, + ejecta_mass_Ia / *ejecta_mass, + log10(ejecta_metal_mass[0] / *ejecta_mass + 1.e-6), + *ejecta_energy * fb_props->energy_to_cgs, *N_SNe * 1.e51, + (dt / fb_props->time_to_yr) * fb_props->time_to_Myr); } } -double feedback_life_time(const struct feedback_props* fb_props, - const double m, +double feedback_life_time(const struct feedback_props *fb_props, const double m, const double z) { int j, j1, j2, l, l1, l2; double lm, lz, ta, tb, t; @@ -918,21 +686,15 @@ double feedback_life_time(const struct feedback_props* fb_props, if (z == 0.) { lz = -990.; - } - else { + } else { lz = log10(z); } if (lz <= fb_props->tables.LFLZ[0]) { t = LINEAR_INTERPOLATION( - fb_props->tables.LFLM[j1], - fb_props->tables.LFLT[LFLT_idx(0, j1)], - fb_props->tables.LFLM[j2], - fb_props->tables.LFLT[LFLT_idx(0, j2)], - lm - ); - } - else { + fb_props->tables.LFLM[j1], fb_props->tables.LFLT[LFLT_idx(0, j1)], + fb_props->tables.LFLM[j2], fb_props->tables.LFLT[LFLT_idx(0, j2)], lm); + } else { l1 = 0; l2 = 1; for (l = 1; l < NZLF; l++) { @@ -941,66 +703,47 @@ double feedback_life_time(const struct feedback_props* fb_props, if (fb_props->tables.LFLZ[l] > lz) break; } ta = LINEAR_INTERPOLATION( - fb_props->tables.LFLZ[l1], - fb_props->tables.LFLT[LFLT_idx(l1, j1)], - fb_props->tables.LFLZ[l2], - fb_props->tables.LFLT[LFLT_idx(l2, j1)], - lz - ); + fb_props->tables.LFLZ[l1], fb_props->tables.LFLT[LFLT_idx(l1, j1)], + fb_props->tables.LFLZ[l2], fb_props->tables.LFLT[LFLT_idx(l2, j1)], lz); tb = LINEAR_INTERPOLATION( - fb_props->tables.LFLZ[l1], - fb_props->tables.LFLT[LFLT_idx(l1, j2)], - fb_props->tables.LFLZ[l2], - fb_props->tables.LFLT[LFLT_idx(l2, j2)], - lz - ); - t = LINEAR_INTERPOLATION( - fb_props->tables.LFLM[j1], - ta, - fb_props->tables.LFLM[j2], - tb, - lm - ); + fb_props->tables.LFLZ[l1], fb_props->tables.LFLT[LFLT_idx(l1, j2)], + fb_props->tables.LFLZ[l2], fb_props->tables.LFLT[LFLT_idx(l2, j2)], lz); + t = LINEAR_INTERPOLATION(fb_props->tables.LFLM[j1], ta, + fb_props->tables.LFLM[j2], tb, lm); } return pow(10., t); } -double feedback_imf(const struct feedback_props* fb_props, const double m) { +double feedback_imf(const struct feedback_props *fb_props, const double m) { if (fb_props->imf == 0) { /* Kroupa */ if (m >= 0.5) { return pow(m, -fb_props->ximf) * 0.5; - } - else if (m >= 0.08) { + } else if (m >= 0.08) { return pow(m, -0.3); - } - else { + } else { return pow(m, 0.7) / 0.08; } - } - else if (fb_props->imf == 1) { /* Chabrier */ + } else if (fb_props->imf == 1) { /* Chabrier */ if (m <= 1.0 && m > 0.01) { const double dm = log10(m) - log10(0.079); return 0.7895218 * exp((-1. * pow(dm, 2.)) / (2. * pow(0.69, 2.))); - } - else { + } else { return 0.2203457 * pow(m, -fb_props->ximf); } - } - else { + } else { return pow(m, -fb_props->ximf); } } -void feedback_set_turnover_mass(const struct feedback_props* fb_props, +void feedback_set_turnover_mass(const struct feedback_props *fb_props, const double z, double *LFLT2) { double lz; int j, l, l1, l2; if (z == 0.) { lz = -4.1; - } - else { + } else { lz = log10f(z); } @@ -1016,17 +759,13 @@ void feedback_set_turnover_mass(const struct feedback_props* fb_props, for (j = 0; j < NMLF; j++) { LFLT2[j] = LINEAR_INTERPOLATION( - fb_props->tables.LFLZ[l1], - fb_props->tables.LFLT[LFLT_idx(l1, j)], - fb_props->tables.LFLZ[l2], - fb_props->tables.LFLT[LFLT_idx(l2, j)], - lz - ); + fb_props->tables.LFLZ[l1], fb_props->tables.LFLT[LFLT_idx(l1, j)], + fb_props->tables.LFLZ[l2], fb_props->tables.LFLT[LFLT_idx(l2, j)], lz); } return; } -double feedback_get_turnover_mass(const struct feedback_props* fb_props, +double feedback_get_turnover_mass(const struct feedback_props *fb_props, const double t, const double z) { if (t == 0.) return fb_props->M_u3; @@ -1045,13 +784,8 @@ double feedback_get_turnover_mass(const struct feedback_props* fb_props, if (LFLT2[j] < lt) break; } - m = LINEAR_INTERPOLATION( - LFLT2[j1], - fb_props->tables.LFLM[j1], - LFLT2[j2], - fb_props->tables.LFLM[j2], - lt - ); + m = LINEAR_INTERPOLATION(LFLT2[j1], fb_props->tables.LFLM[j1], LFLT2[j2], + fb_props->tables.LFLM[j2], lt); result = pow(10., m); if (result < fb_props->M_l) return fb_props->M_l; @@ -1060,36 +794,37 @@ double feedback_get_turnover_mass(const struct feedback_props* fb_props, return result; } -void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props) { +void feedback_prepare_interpolation_tables( + const struct feedback_props *fb_props) { int i, j, k, j1, j2, l; - double sni[NXSNall][NZSN1Y], sn[NXSNall][NZSN][NMSN - 2], hn[NXSNall][NZSN][4]; - double sniilm[NMSN], snii[chem5_NXSN][NZSN][NMSN]; + double sni[NXSNall][NZSN1Y], sn[NXSNall][NZSN][NMSN - 2], + hn[NXSNall][NZSN][4]; + double sniilm[NMSN], snii[chem5_NXSN][NZSN][NMSN]; double SN1wd[NZSN1R][NM], SN1ms[NZSN1R][NM], SN1rg[NZSN1R][NM]; double m[NM], imf[NZSN][NM]; - double snii2_hi,snii2_lo; + double snii2_hi, snii2_lo; double dlm, norm, norm3; double m_l; FILE *fp; - char buf[1000],*dummy; + char buf[1000], *dummy; double a1, a2, a3, a4, a5, a6, a7; double a8, a9, a10, a11, a12, a13, a14, a15, a16; double a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27; double a28, a29, a30; double effSNz[NZSN], temp, tempz; - const double lfz[NZLF] = {.0001, .0002, .0005, .0010, .0020, .0040, .0080, - .0200, .0500}; + const double lfz[NZLF] = {.0001, .0002, .0005, .0010, .0020, + .0040, .0080, .0200, .0500}; double temp_ms, temp_rg; /* Massloss (Kobayashi et al. 2000) - * sw[2][24]: progenitor mass, He core mass = NSorWD mass + * sw[2][24]: progenitor mass, He core mass = NSorWD mass */ const double sw[2][NMSN] = { - {40., 30., 25., 20., 18., 15., 13., 10., 9., 8.5, 8., 7.5, 7., 6.5, 6.0, - 5.5, 5.0, 4.5, 4., 3.5, 3., 2.5, 2.25, 2., 1.9, 1.75, 1.5, 1.25, 1.0, - 0.9, 0.7, 0.05}, - {0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., - 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.473, 0.459, 0.05} - }; + {40., 30., 25., 20., 18., 15., 13., 10., 9., 8.5, 8., + 7.5, 7., 6.5, 6.0, 5.5, 5.0, 4.5, 4., 3.5, 3., 2.5, + 2.25, 2., 1.9, 1.75, 1.5, 1.25, 1.0, 0.9, 0.7, 0.05}, + {0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., + 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.473, 0.459, 0.05}}; const double sniie[9] = {30, 20, 10, 10, 1, 1, 1, 1, 1}; const double sniiz[NZSN] = {0., 0., .001, .004, .008, .02, .05}; const double sniz[NZSN1Y] = {0., .002, .01, .02, .04, .06, .10}; @@ -1107,12 +842,12 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props for (l = 0; l < NZSN1R; l++) fb_props->tables.SNLZ1R[l] = feh_ia[l]; if (engine_rank == 0) { - message("set nucleosynthesis yields for %i elements...", + message("set nucleosynthesis yields for %i elements...", chem5_element_count); message("Z-dependent HN efficiency !!! "); message("effHN = %f %f", effHNz[0], effHNz[NZSN - 1]); message("Z-dependent SNIa model !!! "); - message("b=(%.3f %.3f) [Fe/H] > %f", fb_props->b_rg, fb_props->b_ms, + message("b=(%.3f %.3f) [Fe/H] > %f", fb_props->b_rg, fb_props->b_ms, fb_props->tables.SNLZ1R[0]); message("Z-dependent SAGB!!!"); } @@ -1124,18 +859,17 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props } for (j = 1; j < NZSN; j++) { - dummy = fgets(buf, 1000, fp); /* metallicity */ - dummy = fgets(buf, 1000, fp); /* mass */ - dummy = fgets(buf, 1000, fp); /* energy */ + dummy = fgets(buf, 1000, fp); /* metallicity */ + dummy = fgets(buf, 1000, fp); /* mass */ + dummy = fgets(buf, 1000, fp); /* energy */ for (k = 0; k < NXSNall; k++) { /* k=0: masscut */ dummy = fgets(buf, 1000, fp); - sscanf(buf, + sscanf(buf, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf" "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf\n", - &a1, &a2, &a3, &a4, &a5, &a6, &a7, - &a8, &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, - &a17, &a18, &a19, &a20, &a21, &a22, &a23, &a24, &a25, &a26, &a27, - &a28, &a29, &a30); + &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11, &a12, + &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20, &a21, &a22, &a23, + &a24, &a25, &a26, &a27, &a28, &a29, &a30); sn[k][j][0] = a30; sn[k][j][1] = a29; sn[k][j][2] = a28; @@ -1174,15 +908,15 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props dummy = fgets(buf, 200, fp); dummy = fgets(buf, 200, fp); for (k = 0; k < NXSNall; k++) { - dummy = fgets(buf, 200, fp); - sscanf(buf, "%lf%lf%lf%lf\n", &a1, &a2, &a3, &a4); - hn[k][j][0] = a4; - hn[k][j][1] = a3; - hn[k][j][2] = a2; - hn[k][j][3] = a1; + dummy = fgets(buf, 200, fp); + sscanf(buf, "%lf%lf%lf%lf\n", &a1, &a2, &a3, &a4); + hn[k][j][0] = a4; + hn[k][j][1] = a3; + hn[k][j][2] = a2; + hn[k][j][3] = a1; } } - + fclose(fp); for (i = 0; i < NMSN - 2; i++) { @@ -1214,64 +948,53 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props if (k > 9) tempz += sn[k][j][i]; } - snii[2][j][i] = 1. - sn[0][j][i]; /* ejected mass */ + snii[2][j][i] = 1. - sn[0][j][i]; /* ejected mass */ snii[1][j][i] = snii[2][j][i] - temp; /* unprocessed mass */ if (snii[1][j][i] < 0.) { snii[2][j][i] = temp; snii[1][j][i] = 0.; } - snii[3][j][i] = tempz; /* Z */ - snii[4][j][i] = sn[1][j][i] + sn[2][j][i]; /* H */ - snii[5][j][i] = sn[3][j][i] + sn[4][j][i]; /* He */ - snii[6][j][i] = sn[5][j][i] + sn[6][j][i]; /* Li */ - snii[7][j][i] = sn[7][j][i]; /* Be */ - snii[8][j][i] = sn[8][j][i] + sn[9][j][i]; /* B */ - snii[9][j][i] = sn[10][j][i] + sn[11][j][i]; /* C */ - snii[10][j][i] = sn[12][j][i] + sn[13][j][i]; /* N */ - snii[11][j][i] = sn[14][j][i] + sn[15][j][i] - + sn[16][j][i]; /* O */ - snii[12][j][i] = sn[17][j][i]; /* F */ - snii[13][j][i] = sn[18][j][i] + sn[19][j][i] - + sn[20][j][i]; /* Ne */ - snii[14][j][i] = sn[21][j][i]; /* Na */ - snii[15][j][i] = sn[22][j][i] + sn[23][j][i] - + sn[24][j][i]; /* Mg */ - snii[16][j][i] = sn[25][j][i]; /* Al */ - snii[17][j][i] = sn[26][j][i] + sn[27][j][i] - + sn[28][j][i]; /* Si */ - snii[18][j][i] = sn[29][j][i]; /* P */ - snii[19][j][i] = sn[30][j][i] + sn[31][j][i] - + sn[32][j][i] + sn[33][j][i]; /* S */ - snii[20][j][i] = sn[34][j][i] + sn[35][j][i]; /* Cl */ - snii[21][j][i] = sn[36][j][i] + sn[37][j][i] - + sn[38][j][i]; /* Ar */ - snii[22][j][i] = sn[39][j][i] + sn[40][j][i] - + sn[41][j][i]; /* K */ - snii[23][j][i] = sn[42][j][i] + sn[43][j][i] - + sn[44][j][i] + sn[45][j][i] - + sn[46][j][i] + sn[47][j][i]; /* Ca */ - snii[24][j][i] = sn[48][j][i]; /* Sc */ - snii[25][j][i] = sn[49][j][i] + sn[50][j][i] - + sn[51][j][i] + sn[52][j][i] - + sn[53][j][i]; /* Ti */ + snii[3][j][i] = tempz; /* Z */ + snii[4][j][i] = sn[1][j][i] + sn[2][j][i]; /* H */ + snii[5][j][i] = sn[3][j][i] + sn[4][j][i]; /* He */ + snii[6][j][i] = sn[5][j][i] + sn[6][j][i]; /* Li */ + snii[7][j][i] = sn[7][j][i]; /* Be */ + snii[8][j][i] = sn[8][j][i] + sn[9][j][i]; /* B */ + snii[9][j][i] = sn[10][j][i] + sn[11][j][i]; /* C */ + snii[10][j][i] = sn[12][j][i] + sn[13][j][i]; /* N */ + snii[11][j][i] = sn[14][j][i] + sn[15][j][i] + sn[16][j][i]; /* O */ + snii[12][j][i] = sn[17][j][i]; /* F */ + snii[13][j][i] = sn[18][j][i] + sn[19][j][i] + sn[20][j][i]; /* Ne */ + snii[14][j][i] = sn[21][j][i]; /* Na */ + snii[15][j][i] = sn[22][j][i] + sn[23][j][i] + sn[24][j][i]; /* Mg */ + snii[16][j][i] = sn[25][j][i]; /* Al */ + snii[17][j][i] = sn[26][j][i] + sn[27][j][i] + sn[28][j][i]; /* Si */ + snii[18][j][i] = sn[29][j][i]; /* P */ + snii[19][j][i] = + sn[30][j][i] + sn[31][j][i] + sn[32][j][i] + sn[33][j][i]; /* S */ + snii[20][j][i] = sn[34][j][i] + sn[35][j][i]; /* Cl */ + snii[21][j][i] = sn[36][j][i] + sn[37][j][i] + sn[38][j][i]; /* Ar */ + snii[22][j][i] = sn[39][j][i] + sn[40][j][i] + sn[41][j][i]; /* K */ + snii[23][j][i] = sn[42][j][i] + sn[43][j][i] + sn[44][j][i] + + sn[45][j][i] + sn[46][j][i] + sn[47][j][i]; /* Ca */ + snii[24][j][i] = sn[48][j][i]; /* Sc */ + snii[25][j][i] = sn[49][j][i] + sn[50][j][i] + sn[51][j][i] + + sn[52][j][i] + sn[53][j][i]; /* Ti */ snii[26][j][i] = sn[54][j][i] + sn[55][j][i]; /* V */ - snii[27][j][i] = sn[56][j][i] + sn[57][j][i] - + sn[58][j][i] + sn[59][j][i]; /* Cr */ - snii[28][j][i] = sn[60][j][i]; /* Mn */ - snii[29][j][i] = sn[61][j][i] + sn[62][j][i] - + sn[63][j][i] + sn[64][j][i]; /* Fe */ - snii[30][j][i] = sn[65][j][i]; /* Co */ - snii[31][j][i] = sn[66][j][i] + sn[67][j][i] - + sn[68][j][i] + sn[69][j][i] - + sn[70][j][i]; /* Ni */ + snii[27][j][i] = + sn[56][j][i] + sn[57][j][i] + sn[58][j][i] + sn[59][j][i]; /* Cr */ + snii[28][j][i] = sn[60][j][i]; /* Mn */ + snii[29][j][i] = + sn[61][j][i] + sn[62][j][i] + sn[63][j][i] + sn[64][j][i]; /* Fe */ + snii[30][j][i] = sn[65][j][i]; /* Co */ + snii[31][j][i] = sn[66][j][i] + sn[67][j][i] + sn[68][j][i] + + sn[69][j][i] + sn[70][j][i]; /* Ni */ snii[32][j][i] = sn[71][j][i] + sn[72][j][i]; /* Cu */ - snii[33][j][i] = sn[73][j][i] + sn[74][j][i] - + sn[75][j][i] + sn[76][j][i] - + sn[77][j][i]; /* Zn */ + snii[33][j][i] = sn[73][j][i] + sn[74][j][i] + sn[75][j][i] + + sn[76][j][i] + sn[77][j][i]; /* Zn */ snii[34][j][i] = sn[78][j][i] + sn[79][j][i]; /* Ga */ - snii[35][j][i] = sn[80][j][i] + sn[81][j][i] - + sn[82][j][i] - + sn[83][j][i]; /* Ge */ + snii[35][j][i] = + sn[80][j][i] + sn[81][j][i] + sn[82][j][i] + sn[83][j][i]; /* Ge */ snii[36][j][i] = 0.; } } @@ -1282,7 +1005,7 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props } } - for (i = NMSN - 2; i < NMSN; i++) /* 1 < Msun */{ + for (i = NMSN - 2; i < NMSN; i++) /* 1 < Msun */ { sniilm[i] = log10(sw[0][i]); for (j = 0; j < NZSN; j++) { snii[1][j][i] = 1. - sw[1][i] / sw[0][i]; @@ -1305,13 +1028,13 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props /* Multiply all metal yields by a constant factor if desired */ for (i = 0; i < NMSN - 2; i++) { for (j = 0; j < NZSN; j++) { - for (k = 6; k < 36; k++) snii[k][j][i] *= fb_props->metal_yield_multiplier; + for (k = 6; k < 36; k++) + snii[k][j][i] *= fb_props->metal_yield_multiplier; } } - fb_props->tables.SNLZ[0] = -999.; /* z=0 */ - fb_props->tables.SNLZ[1] = -10.; /* z=0 */ + fb_props->tables.SNLZ[1] = -10.; /* z=0 */ for (j = 2; j < NZSN; j++) fb_props->tables.SNLZ[j] = log10(sniiz[j]); fb_props->tables.SNLZ1Y[0] = -999.; /* z=0 */ @@ -1324,7 +1047,7 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props exit(-1); } - dummy = fgets(buf, 1000, fp); /* metallicity */ + dummy = fgets(buf, 1000, fp); /* metallicity */ for (k = 0; k < NXSNall; k++) { /* k=0: ejected mass */ dummy = fgets(buf, 1000, fp); sscanf(buf, "%lf%lf%lf%lf%lf%lf%lf\n", &a1, &a2, &a3, &a4, &a5, &a6, &a7); @@ -1346,61 +1069,58 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props if (k > 9) tempz += sni[k][j]; } - fb_props->tables.SN1E[SN1E_idx(0, j)] = 1.3; /* energy */ - fb_props->tables.SN1E[SN1E_idx(1, j)] = 0.; /* unprocessed */ - fb_props->tables.SN1E[SN1E_idx(2, j)] = temp; /* ejected */ + fb_props->tables.SN1E[SN1E_idx(0, j)] = 1.3; /* energy */ + fb_props->tables.SN1E[SN1E_idx(1, j)] = 0.; /* unprocessed */ + fb_props->tables.SN1E[SN1E_idx(2, j)] = temp; /* ejected */ fb_props->tables.SN1E[SN1E_idx(3, j)] = tempz; /* Z */ - fb_props->tables.SN1E[SN1E_idx(4, j)] = sni[1][j] + sni[2][j]; /* H */ - fb_props->tables.SN1E[SN1E_idx(5, j)] = sni[3][j] + sni[4][j]; /* He */ - fb_props->tables.SN1E[SN1E_idx(6, j)] = sni[5][j] + sni[6][j]; /* Li */ - fb_props->tables.SN1E[SN1E_idx(7, j)] = sni[7][j]; /* Be */ - fb_props->tables.SN1E[SN1E_idx(8, j)] = sni[8][j] + sni[9][j]; /* B */ - fb_props->tables.SN1E[SN1E_idx(9, j)] = sni[10][j] + sni[11][j]; /* C */ + fb_props->tables.SN1E[SN1E_idx(4, j)] = sni[1][j] + sni[2][j]; /* H */ + fb_props->tables.SN1E[SN1E_idx(5, j)] = sni[3][j] + sni[4][j]; /* He */ + fb_props->tables.SN1E[SN1E_idx(6, j)] = sni[5][j] + sni[6][j]; /* Li */ + fb_props->tables.SN1E[SN1E_idx(7, j)] = sni[7][j]; /* Be */ + fb_props->tables.SN1E[SN1E_idx(8, j)] = sni[8][j] + sni[9][j]; /* B */ + fb_props->tables.SN1E[SN1E_idx(9, j)] = sni[10][j] + sni[11][j]; /* C */ fb_props->tables.SN1E[SN1E_idx(10, j)] = sni[12][j] + sni[13][j]; /* N */ - fb_props->tables.SN1E[SN1E_idx(11, j)] = sni[14][j] + sni[15][j] - + sni[16][j]; /* O */ - fb_props->tables.SN1E[SN1E_idx(12, j)] = sni[17][j]; /* F */ - fb_props->tables.SN1E[SN1E_idx(13, j)] = sni[18][j] + sni[19][j] - + sni[20][j]; /* Ne */ - fb_props->tables.SN1E[SN1E_idx(14, j)] = sni[21][j]; /* Na */ - fb_props->tables.SN1E[SN1E_idx(15, j)] = sni[22][j] + sni[23][j] - + sni[24][j]; /* Mg */ - fb_props->tables.SN1E[SN1E_idx(16, j)] = sni[25][j]; /* Al */ - fb_props->tables.SN1E[SN1E_idx(17, j)] = sni[26][j] + sni[27][j] - + sni[28][j]; /* Si */ - fb_props->tables.SN1E[SN1E_idx(18, j)] = sni[29][j]; /* P */ - fb_props->tables.SN1E[SN1E_idx(19, j)] = sni[30][j] + sni[31][j] - + sni[32][j] + sni[33][j]; /* S */ + fb_props->tables.SN1E[SN1E_idx(11, j)] = + sni[14][j] + sni[15][j] + sni[16][j]; /* O */ + fb_props->tables.SN1E[SN1E_idx(12, j)] = sni[17][j]; /* F */ + fb_props->tables.SN1E[SN1E_idx(13, j)] = + sni[18][j] + sni[19][j] + sni[20][j]; /* Ne */ + fb_props->tables.SN1E[SN1E_idx(14, j)] = sni[21][j]; /* Na */ + fb_props->tables.SN1E[SN1E_idx(15, j)] = + sni[22][j] + sni[23][j] + sni[24][j]; /* Mg */ + fb_props->tables.SN1E[SN1E_idx(16, j)] = sni[25][j]; /* Al */ + fb_props->tables.SN1E[SN1E_idx(17, j)] = + sni[26][j] + sni[27][j] + sni[28][j]; /* Si */ + fb_props->tables.SN1E[SN1E_idx(18, j)] = sni[29][j]; /* P */ + fb_props->tables.SN1E[SN1E_idx(19, j)] = + sni[30][j] + sni[31][j] + sni[32][j] + sni[33][j]; /* S */ fb_props->tables.SN1E[SN1E_idx(20, j)] = sni[34][j] + sni[35][j]; /* Cl */ - fb_props->tables.SN1E[SN1E_idx(21, j)] = sni[36][j] + sni[37][j] - + sni[38][j]; /* Ar */ - fb_props->tables.SN1E[SN1E_idx(22, j)] = sni[39][j] + sni[40][j] - + sni[41][j]; /* K */ - fb_props->tables.SN1E[SN1E_idx(23, j)] = sni[42][j] + sni[43][j] - + sni[44][j] + sni[45][j] - + sni[46][j] + sni[47][j]; /* Ca */ - fb_props->tables.SN1E[SN1E_idx(24, j)] = sni[48][j]; /* Sc */ - fb_props->tables.SN1E[SN1E_idx(25, j)] = sni[49][j] + sni[50][j] - + sni[51][j] + sni[52][j] - + sni[53][j]; /* Ti */ - fb_props->tables.SN1E[SN1E_idx(26, j)] = sni[54][j] + sni[55][j]; /* V */ - fb_props->tables.SN1E[SN1E_idx(27, j)] = sni[56][j] + sni[57][j] - + sni[58][j] + sni[59][j]; /* Cr */ - fb_props->tables.SN1E[SN1E_idx(28, j)] = sni[60][j]; /* Mn */ - fb_props->tables.SN1E[SN1E_idx(29, j)] = sni[61][j] + sni[62][j] - + sni[63][j] + sni[64][j]; /* Fe */ - fb_props->tables.SN1E[SN1E_idx(30, j)] = sni[65][j]; /* Co */ - fb_props->tables.SN1E[SN1E_idx(31, j)] = sni[66][j] + sni[67][j] - + sni[68][j] + sni[69][j] - + sni[70][j]; /* Ni */ - fb_props->tables.SN1E[SN1E_idx(32, j)] = sni[71][j] + sni[72][j]; /* Cu */ - fb_props->tables.SN1E[SN1E_idx(33, j)] = sni[73][j] + sni[74][j] - + sni[75][j] + sni[76][j] - + sni[77][j]; /* Zn */ - fb_props->tables.SN1E[SN1E_idx(34, j)] = sni[78][j] + sni[79][j]; /* Ga */ - fb_props->tables.SN1E[SN1E_idx(35, j)] = sni[80][j] + sni[81][j] - + sni[82][j] + sni[83][j]; /* Ge */ - fb_props->tables.SN1E[SN1E_idx(36, j)] = + fb_props->tables.SN1E[SN1E_idx(21, j)] = + sni[36][j] + sni[37][j] + sni[38][j]; /* Ar */ + fb_props->tables.SN1E[SN1E_idx(22, j)] = + sni[39][j] + sni[40][j] + sni[41][j]; /* K */ + fb_props->tables.SN1E[SN1E_idx(23, j)] = sni[42][j] + sni[43][j] + + sni[44][j] + sni[45][j] + + sni[46][j] + sni[47][j]; /* Ca */ + fb_props->tables.SN1E[SN1E_idx(24, j)] = sni[48][j]; /* Sc */ + fb_props->tables.SN1E[SN1E_idx(25, j)] = + sni[49][j] + sni[50][j] + sni[51][j] + sni[52][j] + sni[53][j]; /* Ti */ + fb_props->tables.SN1E[SN1E_idx(26, j)] = sni[54][j] + sni[55][j]; /* V */ + fb_props->tables.SN1E[SN1E_idx(27, j)] = + sni[56][j] + sni[57][j] + sni[58][j] + sni[59][j]; /* Cr */ + fb_props->tables.SN1E[SN1E_idx(28, j)] = sni[60][j]; /* Mn */ + fb_props->tables.SN1E[SN1E_idx(29, j)] = + sni[61][j] + sni[62][j] + sni[63][j] + sni[64][j]; /* Fe */ + fb_props->tables.SN1E[SN1E_idx(30, j)] = sni[65][j]; /* Co */ + fb_props->tables.SN1E[SN1E_idx(31, j)] = + sni[66][j] + sni[67][j] + sni[68][j] + sni[69][j] + sni[70][j]; /* Ni */ + fb_props->tables.SN1E[SN1E_idx(32, j)] = sni[71][j] + sni[72][j]; /* Cu */ + fb_props->tables.SN1E[SN1E_idx(33, j)] = + sni[73][j] + sni[74][j] + sni[75][j] + sni[76][j] + sni[77][j]; /* Zn */ + fb_props->tables.SN1E[SN1E_idx(34, j)] = sni[78][j] + sni[79][j]; /* Ga */ + fb_props->tables.SN1E[SN1E_idx(35, j)] = + sni[80][j] + sni[81][j] + sni[82][j] + sni[83][j]; /* Ge */ + fb_props->tables.SN1E[SN1E_idx(36, j)] = fb_props->tables.SN1E[SN1E_idx(29, j)]; for (k = 1; k < chem5_NXSN; k++) { fb_props->tables.SN1E[SN1E_idx(k, j)] *= fb_props->solar_mass_to_mass; @@ -1410,8 +1130,8 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props /* lifetime (Kobayashi et al. 2000) */ sprintf(buf, "%s/LIFETIME.DAT", fb_props->tables_path); if ((fp = fopen(buf, "r")) == NULL) { - fprintf(stderr, "Can not open File %s\n", buf); - exit(-1); + fprintf(stderr, "Can not open File %s\n", buf); + exit(-1); } dummy = fgets(buf, 1000, fp); @@ -1423,76 +1143,58 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props for (i = 0; i < NMLF; i++) { dummy = fgets(buf, 1000, fp); sscanf(buf, "%lf%lf\n", &a1, &a2); - fb_props->tables.LFLM[i] = log10(a1); /* sm */ + fb_props->tables.LFLM[i] = log10(a1); /* sm */ fb_props->tables.LFLT[LFLT_idx(j, i)] = log10(a2); /* yr */ } } fclose(fp); if (engine_rank == 0) { - message( - "total: %.2f %.1f %.2e %.2e", - fb_props->M_l, - fb_props->M_u, - feedback_life_time(fb_props, fb_props->M_l, 0.02), - feedback_life_time(fb_props, fb_props->M_u, 0.02) - ); - message( - "SN2: %.2f %.1f %.2e %.2e x=%.2f", - fb_props->M_l2, - fb_props->M_u2, - feedback_life_time(fb_props, fb_props->M_l2, 0.02), - feedback_life_time(fb_props, fb_props->M_u2, 0.02), - fb_props->ximf - ); + message("total: %.2f %.1f %.2e %.2e", fb_props->M_l, fb_props->M_u, + feedback_life_time(fb_props, fb_props->M_l, 0.02), + feedback_life_time(fb_props, fb_props->M_u, 0.02)); + message("SN2: %.2f %.1f %.2e %.2e x=%.2f", fb_props->M_l2, + fb_props->M_u2, feedback_life_time(fb_props, fb_props->M_l2, 0.02), + feedback_life_time(fb_props, fb_props->M_u2, 0.02), fb_props->ximf); if (fb_props->zmax3 >= 0.0f) { message( - "Pop3: %.2f %.1f %.2e %.2e x=%.2f\n", - fb_props->M_l3, - fb_props->M_u3, - feedback_life_time(fb_props, fb_props->M_l3, 0.02), - feedback_life_time(fb_props, fb_props->M_u3, 0.02), - fb_props->ximf3 - ); + "Pop3: %.2f %.1f %.2e %.2e x=%.2f\n", fb_props->M_l3, + fb_props->M_u3, feedback_life_time(fb_props, fb_props->M_l3, 0.02), + feedback_life_time(fb_props, fb_props->M_u3, 0.02), fb_props->ximf3); } } /* Set up IMF, normalized to 1 solar mass */ if (fb_props->imf == 0) { /* Kroupa */ if (fb_props->ximf == 1.) { - norm = log10(fb_props->M_u / 0.5) * 0.5 - + (pow(0.5, 0.7) - pow(0.08, 0.7)) / 0.7 - + (pow(0.08, 1.7) - pow(fb_props->M_l, 1.7)) / 1.7 / 0.08; - } - else { - norm = (pow(fb_props->M_u, 1. - fb_props->ximf) - - pow(0.5, 1. - fb_props->ximf)) - / (1. - fb_props->ximf) * 0.5 - + (pow(0.5, 0.7) - pow(0.08, 0.7)) - / 0.7f - + (pow(0.08, 1.7) - pow(fb_props->M_l, 1.7)) / 1.7 / 0.08; + norm = log10(fb_props->M_u / 0.5) * 0.5 + + (pow(0.5, 0.7) - pow(0.08, 0.7)) / 0.7 + + (pow(0.08, 1.7) - pow(fb_props->M_l, 1.7)) / 1.7 / 0.08; + } else { + norm = (pow(fb_props->M_u, 1. - fb_props->ximf) - + pow(0.5, 1. - fb_props->ximf)) / + (1. - fb_props->ximf) * 0.5 + + (pow(0.5, 0.7) - pow(0.08, 0.7)) / 0.7f + + (pow(0.08, 1.7) - pow(fb_props->M_l, 1.7)) / 1.7 / 0.08; } norm = 1. / norm; - } - else { /* Chabrier, anything else */ + } else { /* Chabrier, anything else */ if (fb_props->ximf == 1.) { norm = 1. / log(fb_props->M_u / fb_props->M_l); - } - else { - norm = (1. - fb_props->ximf) - / (pow(fb_props->M_u, (1. - fb_props->ximf)) - - pow(fb_props->M_l, (1. - fb_props->ximf))); + } else { + norm = + (1. - fb_props->ximf) / (pow(fb_props->M_u, (1. - fb_props->ximf)) - + pow(fb_props->M_l, (1. - fb_props->ximf))); } } if (fb_props->ximf3 == 1.) { norm3 = 1. / log(fb_props->M_u3 / fb_props->M_l3); - } - else { - norm3 = (1. - fb_props->ximf3) / - (powf(fb_props->M_u3, (1. - fb_props->ximf3)) - - powf(fb_props->M_l3, (1. - fb_props->ximf3))); + } else { + norm3 = + (1. - fb_props->ximf3) / (powf(fb_props->M_u3, (1. - fb_props->ximf3)) - + powf(fb_props->M_l3, (1. - fb_props->ximf3))); } /* Set up IMF integration */ @@ -1503,24 +1205,20 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props if (m[i] >= fb_props->M_l3) { imf[0][i] = pow(m[i], -fb_props->ximf3) * norm3; - } - else { + } else { imf[0][i] = 0.; } if (fb_props->imf == 1) { /* Chabrier */ if (m[i] <= fb_props->M_u) { imf[1][i] = IMF_FUDGE_FACTOR * feedback_imf(fb_props, m[i]); - } - else { + } else { imf[1][i] = 0.; } - } - else { /* Kroupa/else */ + } else { /* Kroupa/else */ if (m[i] <= fb_props->M_u) { imf[1][i] = IMF_FUDGE_FACTOR * feedback_imf(fb_props, m[i]) * norm; - } - else { + } else { imf[1][i] = 0.; } } @@ -1562,131 +1260,122 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props for (l = 0; l < NZSN; l++) { if (l == 0) { m_l = max(fb_props->M_l2, fb_props->M_l3); - } - else { + } else { m_l = fb_props->M_l2; } if (m[i] > m_l) { - fb_props->tables.SWR[SWR_idx(l, i)] = - fb_props->tables.SWR[SWR_idx(l, (i - 1))] - + sqrt(imf[l][i] * imf[l][i - 1]) * dlm * log(10.); - } - else { - fb_props->tables.SWR[SWR_idx(l, i)] = + fb_props->tables.SWR[SWR_idx(l, i)] = + fb_props->tables.SWR[SWR_idx(l, (i - 1))] + + sqrt(imf[l][i] * imf[l][i - 1]) * dlm * log(10.); + } else { + fb_props->tables.SWR[SWR_idx(l, i)] = fb_props->tables.SWR[SWR_idx(l, (i - 1))]; } if (l == 0) { m_l = fb_props->M_l3; - } - else { + } else { m_l = 0.; } /* This is where we integrate up the IMF */ if (m[i] > m_l) { /* H/He change from stars that go SN */ for (k = 1; k < 3; k++) { - snii2_hi = LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], + snii2_hi = + LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], sniilm[j2], + snii[k][l][j2], fb_props->tables.SNLM[i]); + snii2_lo = LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], sniilm[j2], snii[k][l][j2], - fb_props->tables.SNLM[i]); - snii2_lo = LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], - sniilm[j2], snii[k][l][j2], - fb_props->tables.SNLM[i-1]); + fb_props->tables.SNLM[i - 1]); if (snii2_hi < 0.) snii2_hi = 0.; if (snii2_lo < 0.) snii2_lo = 0.; - fb_props->tables.SN2E[SN2E_idx(k, l, i)] = - fb_props->tables.SN2E[SN2E_idx(k, l, (i - 1))] - + (snii2_hi + snii2_lo) / 2. - * sqrt(m[i] * m[i - 1] * imf[l][i] * imf[l][i - 1]) - * dlm * log(10.); + fb_props->tables.SN2E[SN2E_idx(k, l, i)] = + fb_props->tables.SN2E[SN2E_idx(k, l, (i - 1))] + + (snii2_hi + snii2_lo) / 2. * + sqrt(m[i] * m[i - 1] * imf[l][i] * imf[l][i - 1]) * dlm * + log(10.); } - } - else { /* low mass stars */ + } else { /* low mass stars */ for (k = 1; k < 3; k++) { - fb_props->tables.SN2E[SN2E_idx(k, l, i)] = + fb_props->tables.SN2E[SN2E_idx(k, l, i)] = fb_props->tables.SN2E[SN2E_idx(k, l, (i - 1))]; } } /* Metals from things that don't direct collapse to BH */ - if (m[i] > m_l && m[i] < fb_props->M_u2) { + if (m[i] > m_l && m[i] < fb_props->M_u2) { for (k = 3; k < chem5_NXSN; k++) { - snii2_hi = LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], - sniilm[j2], snii[k][l][j2], - fb_props->tables.SNLM[i]); - snii2_lo = LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], - sniilm[j2], snii[k][l][j2], - fb_props->tables.SNLM[i-1]); + snii2_hi = + LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], sniilm[j2], + snii[k][l][j2], fb_props->tables.SNLM[i]); + snii2_lo = LINEAR_INTERPOLATION(sniilm[j1], snii[k][l][j1], + sniilm[j2], snii[k][l][j2], + fb_props->tables.SNLM[i - 1]); if (snii2_hi < 0.) snii2_hi = 0.; if (snii2_lo < 0.) snii2_lo = 0.; - fb_props->tables.SN2E[SN2E_idx(k, l, i)] = - fb_props->tables.SN2E[SN2E_idx(k, l, (i - 1))] - + (snii2_hi + snii2_lo) / 2. - * sqrt(m[i] * m[i - 1] * imf[l][i] * imf[l][i - 1]) - * dlm * log(10.); + fb_props->tables.SN2E[SN2E_idx(k, l, i)] = + fb_props->tables.SN2E[SN2E_idx(k, l, (i - 1))] + + (snii2_hi + snii2_lo) / 2. * + sqrt(m[i] * m[i - 1] * imf[l][i] * imf[l][i - 1]) * dlm * + log(10.); } - } - else { // low-mass stars, no metals from Type II + } else { // low-mass stars, no metals from Type II for (k = 3; k < chem5_NXSN; k++) { - fb_props->tables.SN2E[SN2E_idx(k, l, i)] = + fb_props->tables.SN2E[SN2E_idx(k, l, i)] = fb_props->tables.SN2E[SN2E_idx(k, l, (i - 1))]; } } if (l == 0) { m_l = max(fb_props->M_l2, fb_props->M_l3); - } - else { + } else { m_l = fb_props->M_l2; } /* IMF integration for total metal mass yield */ if (m[i] > m_l && m[i] < fb_props->M_u2) { - snii2_hi = LINEAR_INTERPOLATION(sniilm[j1], snii[0][l][j1], - sniilm[j2], snii[0][l][j2], - fb_props->tables.SNLM[i]); - snii2_lo = LINEAR_INTERPOLATION(sniilm[j1], snii[0][l][j1], - sniilm[j2], snii[0][l][j2], - fb_props->tables.SNLM[i-1]); + snii2_hi = + LINEAR_INTERPOLATION(sniilm[j1], snii[0][l][j1], sniilm[j2], + snii[0][l][j2], fb_props->tables.SNLM[i]); + snii2_lo = + LINEAR_INTERPOLATION(sniilm[j1], snii[0][l][j1], sniilm[j2], + snii[0][l][j2], fb_props->tables.SNLM[i - 1]); if (snii2_hi < 0.) snii2_hi = 0.; if (snii2_lo < 0.) snii2_lo = 0.; - fb_props->tables.SN2R[SN2R_idx(l, i)] = - fb_props->tables.SN2R[SN2R_idx(l, (i - 1))] - + sqrt(imf[l][i] * imf[l][i - 1]) * dlm * log(10.); - fb_props->tables.SN2E[SN2E_idx(0, l, i)] = - fb_props->tables.SN2E[SN2E_idx(0, l, (i - 1))] - + (snii2_hi + snii2_lo) / 2. - * sqrt(imf[l][i] * imf[l][i - 1]) * dlm * log(10.); + fb_props->tables.SN2R[SN2R_idx(l, i)] = + fb_props->tables.SN2R[SN2R_idx(l, (i - 1))] + + sqrt(imf[l][i] * imf[l][i - 1]) * dlm * log(10.); + fb_props->tables.SN2E[SN2E_idx(0, l, i)] = + fb_props->tables.SN2E[SN2E_idx(0, l, (i - 1))] + + (snii2_hi + snii2_lo) / 2. * sqrt(imf[l][i] * imf[l][i - 1]) * dlm * + log(10.); } else { - fb_props->tables.SN2R[SN2R_idx(l, i)] = + fb_props->tables.SN2R[SN2R_idx(l, i)] = fb_props->tables.SN2R[SN2R_idx(l, (i - 1))]; - fb_props->tables.SN2E[SN2E_idx(0, l, i)] = + fb_props->tables.SN2E[SN2E_idx(0, l, i)] = fb_props->tables.SN2E[SN2E_idx(0, l, (i - 1))]; } } for (l = 1; l < NZSN1R; l++) { if (m[i] > M_l1wd[l] && m[i] < M_u1wd[l]) { - SN1wd[l][i] = + SN1wd[l][i] = SN1wd[l][i - 1] + sqrt(imf[l][i] * imf[l][i - 1]) * dlm * log(10.); - } - else { + } else { SN1wd[l][i] = SN1wd[l][i - 1]; } if (m[i] > M_l1ms[l] && m[i] < M_u1ms[l]) { - SN1ms[l][i] = - SN1ms[l][i - 1] + pow(sqrt(m[i] * m[i - 1]), -0.35) * dlm * log(10.); - } - else { + SN1ms[l][i] = SN1ms[l][i - 1] + + pow(sqrt(m[i] * m[i - 1]), -0.35) * dlm * log(10.); + } else { SN1ms[l][i] = SN1ms[l][i - 1]; } if (m[i] > M_l1rg[l] && m[i] < M_u1rg[l]) { - SN1rg[l][i] = - SN1rg[l][i - 1] + pow(sqrt(m[i] * m[i - 1]), -0.35) * dlm * log(10.); - } - else { + SN1rg[l][i] = SN1rg[l][i - 1] + + pow(sqrt(m[i] * m[i - 1]), -0.35) * dlm * log(10.); + } else { SN1rg[l][i] = SN1rg[l][i - 1]; } } @@ -1699,13 +1388,14 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props for (l = 1; l < NZSN1R; l++) { SN1ms[l][i] *= fb_props->b_ms / temp_ms; SN1rg[l][i] *= fb_props->b_rg / temp_rg; - fb_props->tables.SN1R[SN1R_idx(l, i)] = + fb_props->tables.SN1R[SN1R_idx(l, i)] = SN1wd[l][i] * (SN1ms[l][i] + SN1rg[l][i]); fb_props->tables.SN1R[SN1R_idx(l, i)] /= fb_props->solar_mass_to_mass; } for (l = 1; l < NZSN1Y; l++) { - fb_props->tables.SN1E[SN1E_idx(0, l)] *= (1.e51 / fb_props->energy_to_cgs); + fb_props->tables.SN1E[SN1E_idx(0, l)] *= + (1.e51 / fb_props->energy_to_cgs); } /* convert solar mass to code */ @@ -1714,7 +1404,7 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props for (l = 0; l < NZSN; l++) { fb_props->tables.SN2R[SN2R_idx(l, i)] /= fb_props->solar_mass_to_mass; fb_props->tables.SWR[SWR_idx(l, i)] /= fb_props->solar_mass_to_mass; - fb_props->tables.SN2E[SN2E_idx(0, l, i)] *= + fb_props->tables.SN2E[SN2E_idx(0, l, i)] *= (1.e51 / fb_props->energy_to_cgs / fb_props->solar_mass_to_mass); } } @@ -1729,7 +1419,8 @@ void feedback_prepare_interpolation_tables(const struct feedback_props* fb_props * * @param feedback_props the #feedback_props data struct to store the tables in */ -INLINE static void feedback_allocate_feedback_tables(struct feedback_props *feedback_props) { +INLINE static void feedback_allocate_feedback_tables( + struct feedback_props *feedback_props) { if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.LFLT, SWIFT_STRUCT_ALIGNMENT, @@ -1738,20 +1429,17 @@ INLINE static void feedback_allocate_feedback_tables(struct feedback_props *feed } if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.LFLM, - SWIFT_STRUCT_ALIGNMENT, - NMLF * sizeof(double)) != 0) { + SWIFT_STRUCT_ALIGNMENT, NMLF * sizeof(double)) != 0) { error("Failed to allocate LFLM array"); } if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.LFLZ, - SWIFT_STRUCT_ALIGNMENT, - NZLF * sizeof(double)) != 0) { + SWIFT_STRUCT_ALIGNMENT, NZLF * sizeof(double)) != 0) { error("Failed to allocate LFLZ array"); } if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SWR, - SWIFT_STRUCT_ALIGNMENT, - NZSN * NM * sizeof(double)) != 0) { + SWIFT_STRUCT_ALIGNMENT, NZSN * NM * sizeof(double)) != 0) { error("Failed to allocate SWR array"); } @@ -1762,8 +1450,7 @@ INLINE static void feedback_allocate_feedback_tables(struct feedback_props *feed } if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SN2R, - SWIFT_STRUCT_ALIGNMENT, - NZSN * NM * sizeof(double)) != 0) { + SWIFT_STRUCT_ALIGNMENT, NZSN * NM * sizeof(double)) != 0) { error("Failed to allocate SN2R array"); } @@ -1774,20 +1461,17 @@ INLINE static void feedback_allocate_feedback_tables(struct feedback_props *feed } if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SNLM, - SWIFT_STRUCT_ALIGNMENT, - NM * sizeof(double)) != 0) { + SWIFT_STRUCT_ALIGNMENT, NM * sizeof(double)) != 0) { error("Failed to allocate SNLM array"); } if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SNLZ, - SWIFT_STRUCT_ALIGNMENT, - NZSN * sizeof(double)) != 0) { + SWIFT_STRUCT_ALIGNMENT, NZSN * sizeof(double)) != 0) { error("Failed to allocate SNLZ array"); } if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SNLZ1R, - SWIFT_STRUCT_ALIGNMENT, - NZSN1R * sizeof(double)) != 0) { + SWIFT_STRUCT_ALIGNMENT, NZSN1R * sizeof(double)) != 0) { error("Failed to allocate SNLZ1R array"); } @@ -1798,11 +1482,9 @@ INLINE static void feedback_allocate_feedback_tables(struct feedback_props *feed } if (swift_memalign("feedback-tables", (void **)&feedback_props->tables.SNLZ1Y, - SWIFT_STRUCT_ALIGNMENT, - NZSN1Y * sizeof(double)) != 0) { + SWIFT_STRUCT_ALIGNMENT, NZSN1Y * sizeof(double)) != 0) { error("Failed to allocate SNLZ1Y array"); } - } /** @@ -1815,12 +1497,12 @@ INLINE static void feedback_allocate_feedback_tables(struct feedback_props *feed * @param hydro_props The already read-in properties of the hydro scheme. * @param cosmo The cosmological model. */ -void feedback_props_init(struct feedback_props* fp, - const struct phys_const* phys_const, - const struct unit_system* us, - struct swift_params* params, - const struct hydro_props* hydro_props, - const struct cosmology* cosmo) { +void feedback_props_init(struct feedback_props *fp, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct hydro_props *hydro_props, + const struct cosmology *cosmo) { /* Common conversions ------------------------------------------------- */ @@ -1845,24 +1527,22 @@ void feedback_props_init(struct feedback_props* fp, fp->rho_to_n_cgs = (X_H / m_p) * units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); - fp->kms_to_internal = - 1.e5 / units_cgs_conversion_factor(us, UNIT_CONV_SPEED); + fp->kms_to_internal = 1.e5 / units_cgs_conversion_factor(us, UNIT_CONV_SPEED); fp->kms_to_cms = 1.e5; fp->time_to_Myr = units_cgs_conversion_factor(us, UNIT_CONV_TIME) / - (1.e6 * 365.25 * 24. * 60. * 60.); + (1.e6 * 365.25 * 24. * 60. * 60.); /* Convert to Myr first, then multiply by a factor of 1e6 yr / 1 Myr */ fp->time_to_yr = fp->time_to_Myr * 1.e6; - fp->length_to_kpc = + fp->length_to_kpc = units_cgs_conversion_factor(us, UNIT_CONV_LENGTH) / 3.08567758e21f; - fp->energy_to_cgs = - units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); + fp->energy_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); - fp->T_to_internal = + fp->T_to_internal = 1. / units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); /* Constant Chem5 parameters ---------------------------------------------- */ @@ -1879,14 +1559,13 @@ void feedback_props_init(struct feedback_props* fp, fp->E_sn1 = 1.3 * (1.e51 / fp->energy_to_cgs); fp->imf = parser_get_param_int(params, "KIARAFeedback:imf"); - + /* Kroupa IMF || Chabrier IMF */ if (fp->imf == 0 || fp->imf == 1) { fp->ximf = 1.3; fp->M_u = 120.; fp->M_l = 0.01; - } - else { + } else { fp->ximf = 1.35; fp->M_u = 120.; fp->M_l = 0.07; @@ -1894,7 +1573,7 @@ void feedback_props_init(struct feedback_props* fp, fp->ximf3 = 1.35; fp->M_u3 = 120.; /* >= M_u */ - fp->M_l3 = 20.; /* >= M_l */ + fp->M_l3 = 20.; /* >= M_l */ fp->zmax3 = -999.; fp->M_u2 = 50.; fp->M_l2 = 8.; @@ -1918,42 +1597,41 @@ void feedback_props_init(struct feedback_props* fp, /* Production tables: AGB for C/O>1, AGB for C/O<1, and SNII (ignore SNIa) */ fp->delta_AGBCOG1[chemistry_element_H] = 0.0; fp->delta_AGBCOG1[chemistry_element_He] = 0.0; /* He could be removed */ - fp->delta_AGBCOG1[chemistry_element_C] = 0.2; + fp->delta_AGBCOG1[chemistry_element_C] = 0.2; fp->delta_AGBCOG1[chemistry_element_N] = 0.0; /* N could be removed */ - fp->delta_AGBCOG1[chemistry_element_O] = 0.0; + fp->delta_AGBCOG1[chemistry_element_O] = 0.0; fp->delta_AGBCOG1[chemistry_element_Ne] = 0.0; /* Ne could be removed */ fp->delta_AGBCOG1[chemistry_element_Mg] = 0.0; - fp->delta_AGBCOG1[chemistry_element_Si] = 0.0; - fp->delta_AGBCOG1[chemistry_element_S] = 0.0; - fp->delta_AGBCOG1[chemistry_element_Ca] = 0.0; + fp->delta_AGBCOG1[chemistry_element_Si] = 0.0; + fp->delta_AGBCOG1[chemistry_element_S] = 0.0; + fp->delta_AGBCOG1[chemistry_element_Ca] = 0.0; fp->delta_AGBCOG1[chemistry_element_Fe] = 0.0; fp->delta_AGBCOL1[chemistry_element_H] = 0.0; - fp->delta_AGBCOL1[chemistry_element_He] = 0.0; - fp->delta_AGBCOL1[chemistry_element_C] = 0.0; + fp->delta_AGBCOL1[chemistry_element_He] = 0.0; + fp->delta_AGBCOL1[chemistry_element_C] = 0.0; fp->delta_AGBCOL1[chemistry_element_N] = 0.0; - fp->delta_AGBCOL1[chemistry_element_O] = 0.2; - fp->delta_AGBCOL1[chemistry_element_Ne] = 0.0; - fp->delta_AGBCOL1[chemistry_element_Mg] = 0.2; - fp->delta_AGBCOL1[chemistry_element_Si] = 0.2; - fp->delta_AGBCOL1[chemistry_element_S] = 0.2; - fp->delta_AGBCOL1[chemistry_element_Ca] = 0.2; + fp->delta_AGBCOL1[chemistry_element_O] = 0.2; + fp->delta_AGBCOL1[chemistry_element_Ne] = 0.0; + fp->delta_AGBCOL1[chemistry_element_Mg] = 0.2; + fp->delta_AGBCOL1[chemistry_element_Si] = 0.2; + fp->delta_AGBCOL1[chemistry_element_S] = 0.2; + fp->delta_AGBCOL1[chemistry_element_Ca] = 0.2; fp->delta_AGBCOL1[chemistry_element_Fe] = 0.2; /* From Poping+17, default=2 in Simba's dust model */ - const float dust_boost_factor = - parser_get_opt_param_float(params, - "KIARAFeedback:dust_boost_factor", 2.f); + const float dust_boost_factor = parser_get_opt_param_float( + params, "KIARAFeedback:dust_boost_factor", 2.f); fp->delta_SNII[chemistry_element_H] = 0.00 * dust_boost_factor; - fp->delta_SNII[chemistry_element_He] = 0.00 * dust_boost_factor; - fp->delta_SNII[chemistry_element_C] = 0.15 * dust_boost_factor ; + fp->delta_SNII[chemistry_element_He] = 0.00 * dust_boost_factor; + fp->delta_SNII[chemistry_element_C] = 0.15 * dust_boost_factor; fp->delta_SNII[chemistry_element_N] = 0.00 * dust_boost_factor; - fp->delta_SNII[chemistry_element_O] = 0.15 * dust_boost_factor ; + fp->delta_SNII[chemistry_element_O] = 0.15 * dust_boost_factor; fp->delta_SNII[chemistry_element_Ne] = 0.00 * dust_boost_factor; fp->delta_SNII[chemistry_element_Mg] = 0.15 * dust_boost_factor; fp->delta_SNII[chemistry_element_Si] = 0.15 * dust_boost_factor; - fp->delta_SNII[chemistry_element_S] = 0.15 * dust_boost_factor ; + fp->delta_SNII[chemistry_element_S] = 0.15 * dust_boost_factor; fp->delta_SNII[chemistry_element_Ca] = 0.15 * dust_boost_factor; fp->delta_SNII[chemistry_element_Fe] = 0.15 * dust_boost_factor; #endif @@ -1969,9 +1647,8 @@ void feedback_props_init(struct feedback_props* fp, fp->with_SNIa_energy_from_chem5 = parser_get_param_int(params, "KIARAFeedback:use_SNIa_energy_from_chem5"); - fp->stellar_enrichment_frequency = - parser_get_opt_param_float(params, - "KIARAFeedback:stellar_enrichment_frequency", 0.f); + fp->stellar_enrichment_frequency = parser_get_opt_param_float( + params, "KIARAFeedback:stellar_enrichment_frequency", 0.f); /* Properties of the enrichment down-sampling ----------------------------- */ @@ -1982,30 +1659,25 @@ void feedback_props_init(struct feedback_props* fp, /* Stellar feedback cannot grow a particle bigger than this factor * times the particles' current mass */ - fp->max_mass_increase_factor = - parser_get_opt_param_float(params, - "KIARAFeedback:max_mass_increase_factor", - 1.5f); + fp->max_mass_increase_factor = parser_get_opt_param_float( + params, "KIARAFeedback:max_mass_increase_factor", 1.5f); /* Stellar feedback heating cannot increase a particle's internal * energy more than this factor */ - fp->max_energy_increase_factor = - parser_get_opt_param_float(params, - "KIARAFeedback:max_energy_increase_factor", - 10.f); + fp->max_energy_increase_factor = parser_get_opt_param_float( + params, "KIARAFeedback:max_energy_increase_factor", 10.f); - /* Momentum exchange lower limit from stellar feedback mass injection + /* Momentum exchange lower limit from stellar feedback mass injection fp->min_energy_decrease_factor = - parser_get_opt_param_float(params, - "KIARAFeedback:min_energy_decrease_factor", + parser_get_opt_param_float(params, + "KIARAFeedback:min_energy_decrease_factor", 0.5f);*/ /* Option to use heat from SNIa to move gas off of the EoS */ - fp->SNIa_add_heat_to_ISM = + fp->SNIa_add_heat_to_ISM = parser_get_opt_param_int(params, "KIARAFeedback:SNIa_add_heat_to_ISM", 0); - fp->SNIa_add_heat_to_ISM_tolerance = - parser_get_opt_param_float(params, - "KIARAFeedback:SNIa_add_heat_to_ISM_tolerance", 1.e-6f); + fp->SNIa_add_heat_to_ISM_tolerance = parser_get_opt_param_float( + params, "KIARAFeedback:SNIa_add_heat_to_ISM_tolerance", 1.e-6f); fp->stellar_evolution_sampling_rate = parser_get_param_double( params, "KIARAFeedback:stellar_evolution_sampling_rate"); @@ -2014,16 +1686,14 @@ void feedback_props_init(struct feedback_props* fp, fp->stellar_evolution_sampling_rate >= (1 << (8 * sizeof(char) - 1))) error("Stellar evolution sampling rate too large. Must be >0 and <%d", (1 << (8 * sizeof(char) - 1))); - - fp->metal_yield_multiplier = - parser_get_opt_param_float(params, - "KIARAFeedback:metal_yield_multiplier", 1.f); + + fp->metal_yield_multiplier = parser_get_opt_param_float( + params, "KIARAFeedback:metal_yield_multiplier", 1.f); /* Properties of Simba kinetic winds -------------------------------------- */ - fp->FIRE_velocity_normalization = - parser_get_param_double(params, - "KIARAFeedback:FIRE_velocity_normalization"); + fp->FIRE_velocity_normalization = parser_get_param_double( + params, "KIARAFeedback:FIRE_velocity_normalization"); fp->FIRE_velocity_slope = parser_get_param_double(params, "KIARAFeedback:FIRE_velocity_slope"); fp->FIRE_eta_normalization = @@ -2035,52 +1705,43 @@ void feedback_props_init(struct feedback_props* fp, parser_get_param_double(params, "KIARAFeedback:FIRE_eta_lower_slope"); fp->FIRE_eta_upper_slope = parser_get_param_double(params, "KIARAFeedback:FIRE_eta_upper_slope"); - fp->FIRE_eta_lower_slope_EOR = - parser_get_opt_param_double(params, "KIARAFeedback:FIRE_eta_lower_slope_EOR", fp->FIRE_eta_lower_slope ); + fp->FIRE_eta_lower_slope_EOR = parser_get_opt_param_double( + params, "KIARAFeedback:FIRE_eta_lower_slope_EOR", + fp->FIRE_eta_lower_slope); - fp->wind_velocity_suppression_redshift = - parser_get_opt_param_float(params, - "KIARAFeedback:wind_velocity_suppression_redshift", 0.f); + fp->wind_velocity_suppression_redshift = parser_get_opt_param_float( + params, "KIARAFeedback:wind_velocity_suppression_redshift", 0.f); - fp->wind_eta_suppression_redshift = - parser_get_opt_param_float(params, - "KIARAFeedback:wind_eta_suppression_redshift", 0.f); + fp->wind_eta_suppression_redshift = parser_get_opt_param_float( + params, "KIARAFeedback:wind_eta_suppression_redshift", 0.f); - fp->SNII_energy_multiplier = - parser_get_opt_param_float(params, - "KIARAFeedback:SNII_energy_multiplier", 1.f); + fp->SNII_energy_multiplier = parser_get_opt_param_float( + params, "KIARAFeedback:SNII_energy_multiplier", 1.f); - fp->kick_radius_over_h = - parser_get_opt_param_float(params, - "KIARAFeedback:kick_radius_over_h", 0.5f); + fp->kick_radius_over_h = parser_get_opt_param_float( + params, "KIARAFeedback:kick_radius_over_h", 0.5f); - fp->max_frac_of_kernel_to_launch = - parser_get_opt_param_float(params, - "KIARAFeedback:max_frac_of_kernel_to_launch", 1.0f); + fp->max_frac_of_kernel_to_launch = parser_get_opt_param_float( + params, "KIARAFeedback:max_frac_of_kernel_to_launch", 1.0f); - fp->use_sfr_weighted_launch = - parser_get_opt_param_int(params, - "KIARAFeedback:use_sfr_weighted_launch", 1); + fp->use_sfr_weighted_launch = parser_get_opt_param_int( + params, "KIARAFeedback:use_sfr_weighted_launch", 1); - fp->metal_dependent_vwind = - parser_get_opt_param_int(params, - "KIARAFeedback:metal_dependent_vwind", 0); + fp->metal_dependent_vwind = parser_get_opt_param_int( + params, "KIARAFeedback:metal_dependent_vwind", 0); - fp->minimum_galaxy_stellar_mass = - parser_get_param_double(params, - "KIARAFeedback:minimum_galaxy_stellar_mass_Msun"); + fp->minimum_galaxy_stellar_mass = parser_get_param_double( + params, "KIARAFeedback:minimum_galaxy_stellar_mass_Msun"); fp->minimum_galaxy_stellar_mass *= fp->solar_mass_to_mass; - fp->galaxy_particle_resolution_count = - parser_get_opt_param_int(params, - "KIARAFeedback:galaxy_particle_resolution_count", 0); + fp->galaxy_particle_resolution_count = parser_get_opt_param_int( + params, "KIARAFeedback:galaxy_particle_resolution_count", 0); - fp->eta_suppression_factor_floor = - parser_get_opt_param_float(params, - "KIARAFeedback:eta_suppression_factor_floor", 0.2f); + fp->eta_suppression_factor_floor = parser_get_opt_param_float( + params, "KIARAFeedback:eta_suppression_factor_floor", 0.2f); - fp->kick_direction_flag = - parser_get_opt_param_double(params, "KIARAFeedback:kick_direction_flag", 1); + fp->kick_direction_flag = parser_get_opt_param_double( + params, "KIARAFeedback:kick_direction_flag", 1); fp->kick_velocity_scatter = parser_get_param_double(params, "KIARAFeedback:kick_velocity_scatter"); @@ -2107,26 +1768,27 @@ void feedback_props_init(struct feedback_props* fp, params, "KIARAChemistry:use_firehose_wind_model", 0); if (firehose_on) { if (engine_rank == 0) { - message("WARNING: Firehose model is on. Setting hot_wind_temperature_K to " - "cold_wind_temperature_K"); + message( + "WARNING: Firehose model is on. Setting hot_wind_temperature_K to " + "cold_wind_temperature_K"); } fp->use_firehose_model = 1; fp->hot_wind_internal_energy = fp->cold_wind_internal_energy; - } - else { + } else { fp->use_firehose_model = 0; } /* Early stellar feedback model of Keller et al 2022. */ fp->early_stellar_feedback_alpha = parser_get_opt_param_float( params, "KIARAFeedback:early_stellar_feedback_alpha", 0.); - /* Cloud-scale SF efficiency for early stellar feedback; default from Leroy+25 */ + /* Cloud-scale SF efficiency for early stellar feedback; default from Leroy+25 + */ const float epssf = parser_get_opt_param_float( params, "KIARAFeedback:early_stellar_feedback_epssf", 0.35); fp->early_stellar_feedback_epsterm = (1. - epssf) / epssf; /* Early stellar feedback timescale in Myr; store inverse for efficiency */ - fp->early_stellar_feedback_tfb= parser_get_opt_param_float( + fp->early_stellar_feedback_tfb = parser_get_opt_param_float( params, "KIARAFeedback:early_stellar_feedback_tfb", 3.31); fp->early_stellar_feedback_tfb /= fp->time_to_Myr; fp->early_stellar_feedback_tfb_inv = 1.f / fp->early_stellar_feedback_tfb; @@ -2139,16 +1801,15 @@ void feedback_props_init(struct feedback_props* fp, #endif /* Convert Kelvin to internal energy and internal units */ - fp->cold_wind_internal_energy *= - fp->temp_to_u_factor / - units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); - fp->hot_wind_internal_energy = - fp->temp_to_u_factor / - units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + fp->cold_wind_internal_energy *= + fp->temp_to_u_factor / + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + fp->hot_wind_internal_energy = + fp->temp_to_u_factor / + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); /* Read yield table filepath */ - parser_get_param_string(params, "KIARAFeedback:tables_path", - fp->tables_path); + parser_get_param_string(params, "KIARAFeedback:tables_path", fp->tables_path); /* Allocate the memory for all of the feedback tables --------------------- */ feedback_allocate_feedback_tables(fp); @@ -2157,10 +1818,10 @@ void feedback_props_init(struct feedback_props* fp, feedback_prepare_interpolation_tables(fp); /* Output some information to the people ---------------------------------- */ - + if (engine_rank == 0) { message("Feedback model is KIARA"); - message("Feedback FIRE velocity normalization: %g", + message("Feedback FIRE velocity normalization: %g", fp->FIRE_velocity_normalization); message("Feedback FIRE velocity slope: %g", fp->FIRE_velocity_slope); message("Feedback velocity scatter: %g", fp->kick_velocity_scatter); @@ -2168,17 +1829,19 @@ void feedback_props_init(struct feedback_props* fp, message("Feedback FIRE eta break: %g", fp->FIRE_eta_break); message("Feedback FIRE eta upper slope: %g", fp->FIRE_eta_upper_slope); message("Feedback FIRE eta lower slope: %g", fp->FIRE_eta_lower_slope); - message("Feedback FIRE eta lower slope at z>6: %g", fp->FIRE_eta_lower_slope_EOR); - + message("Feedback FIRE eta lower slope at z>6: %g", + fp->FIRE_eta_lower_slope_EOR); + if (fabs(fp->wind_velocity_suppression_redshift) != 0.f) { - message("Feedback wind speed early suppression enabled " - "above redshift: %g", - fp->wind_velocity_suppression_redshift); + message( + "Feedback wind speed early suppression enabled " + "above redshift: %g", + fp->wind_velocity_suppression_redshift); } - message("Feedback use Chem5 SNII energy: %d", + message("Feedback use Chem5 SNII energy: %d", fp->with_SNII_energy_from_chem5); - message("Feedback use Chem5 SNIa energy: %d", + message("Feedback use Chem5 SNIa energy: %d", fp->with_SNIa_energy_from_chem5); } } @@ -2189,7 +1852,7 @@ void feedback_props_init(struct feedback_props* fp, * @param table feedback_tables struct in which pointers to tables * set to NULL */ -void feedback_zero_table_pointers(struct feedback_tables* table) { +void feedback_zero_table_pointers(struct feedback_tables *table) { table->LFLT = NULL; table->LFLM = NULL; @@ -2211,7 +1874,7 @@ void feedback_zero_table_pointers(struct feedback_tables* table) { * * @param fp the #feedback_props structure */ -void feedback_restore_tables(struct feedback_props* fp) { +void feedback_restore_tables(struct feedback_props *fp) { /* Allocate the memory for all of the feedback tables --------------------- */ feedback_allocate_feedback_tables(fp); @@ -2227,7 +1890,7 @@ void feedback_restore_tables(struct feedback_props* fp) { * * @param fp the feedback data structure. */ -void feedback_clean(struct feedback_props* fp) { } +void feedback_clean(struct feedback_props *fp) {} /** * @brief Write a feedback struct to the given FILE as a stream of bytes. @@ -2235,7 +1898,7 @@ void feedback_clean(struct feedback_props* fp) { } * @param feedback the struct * @param stream the file stream */ -void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream) { +void feedback_struct_dump(const struct feedback_props *feedback, FILE *stream) { /* To make sure everything is restored correctly, we zero all the pointers to tables. If they are not restored correctly, we would crash after restart on the first call to the feedback routines. Helps debugging. */ @@ -2243,7 +1906,7 @@ void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream) { feedback_zero_table_pointers(&feedback_copy.tables); - restart_write_blocks((void*)&feedback_copy, sizeof(struct feedback_props), 1, + restart_write_blocks((void *)&feedback_copy, sizeof(struct feedback_props), 1, stream, "feedback", "feedback function"); } @@ -2257,10 +1920,9 @@ void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream) { * @param feedback the struct * @param stream the file stream */ -void feedback_struct_restore(struct feedback_props* feedback, FILE* stream) { - restart_read_blocks((void*)feedback, sizeof(struct feedback_props), 1, stream, - NULL, "feedback function"); +void feedback_struct_restore(struct feedback_props *feedback, FILE *stream) { + restart_read_blocks((void *)feedback, sizeof(struct feedback_props), 1, + stream, NULL, "feedback function"); - if (strlen(feedback->tables_path) != 0) - feedback_restore_tables(feedback); + if (strlen(feedback->tables_path) != 0) feedback_restore_tables(feedback); } diff --git a/src/feedback/KIARA/feedback.h b/src/feedback/KIARA/feedback.h index 7c5d417b08..acd05ee447 100644 --- a/src/feedback/KIARA/feedback.h +++ b/src/feedback/KIARA/feedback.h @@ -2,7 +2,7 @@ * This file is part of SWIFT. * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2022 Doug Rennehan (douglas.rennehan@gmail.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 @@ -26,42 +26,32 @@ #include "feedback_properties.h" #include "hydro_properties.h" #include "part.h" -#include "units.h" +#include "star_formation.h" #include "timers.h" #include "timestep_sync.h" #include "timestep_sync_part.h" -#include "star_formation.h" +#include "units.h" #include -double feedback_get_lum_from_star_particle(const struct spart *sp, - double age, - const struct feedback_props* fb_props); -void feedback_get_ejecta_from_star_particle(const struct spart* sp, - double age, - const struct feedback_props* fb_props, - double dt, - double *N_SNe, - double *ejecta_energy, - double *ejecta_mass, - double *ejecta_unprocessed, - double ejecta_metal_mass[chem5_element_count]); -void feedback_dust_production_condensation(struct spart* sp, - double star_age, - const struct feedback_props* fb_props, - double delta_metal_mass[chemistry_element_count]); -double feedback_life_time(const struct feedback_props* fb_props, - const double m, +double feedback_get_lum_from_star_particle( + const struct spart *sp, double age, const struct feedback_props *fb_props); +void feedback_get_ejecta_from_star_particle( + const struct spart *sp, double age, const struct feedback_props *fb_props, + double dt, double *N_SNe, double *ejecta_energy, double *ejecta_mass, + double *ejecta_unprocessed, double ejecta_metal_mass[chem5_element_count]); +void feedback_dust_production_condensation( + struct spart *sp, double star_age, const struct feedback_props *fb_props, + double delta_metal_mass[chemistry_element_count]); +double feedback_life_time(const struct feedback_props *fb_props, const double m, const double z); -double feedback_imf(const struct feedback_props* fb_props, - const double m); -void feedback_set_turnover_mass(const struct feedback_props* fb_props, - const double z, double* LFLT2); -double feedback_get_turnover_mass(const struct feedback_props* fb_props, +double feedback_imf(const struct feedback_props *fb_props, const double m); +void feedback_set_turnover_mass(const struct feedback_props *fb_props, + const double z, double *LFLT2); +double feedback_get_turnover_mass(const struct feedback_props *fb_props, const double t, const double z); void feedback_prepare_interpolation_tables( - const struct feedback_props* fb_props); - + const struct feedback_props *fb_props); /** * @brief Recouple wind particles. @@ -71,9 +61,8 @@ void feedback_prepare_interpolation_tables( * @param e The #engine. * @param with_cosmology Is this a cosmological simulation? */ -__attribute__((always_inline)) INLINE static -void feedback_recouple_set_flags(struct part* p, - const struct cosmology* cosmo) { +__attribute__((always_inline)) INLINE static void feedback_recouple_set_flags( + struct part *p, const struct cosmology *cosmo) { p->feedback_data.decoupling_delay_time = 0.f; p->decoupled = 0; @@ -99,10 +88,9 @@ void feedback_recouple_set_flags(struct part* p, * @param fb_props The #feedback_props feedback parameters. */ __attribute__((always_inline)) INLINE static void feedback_recouple_part( - struct part* p, struct xpart* xp, const struct engine* e, - const int with_cosmology, - const struct cosmology* cosmo, - const struct feedback_props* fb_props) { + struct part *p, struct xpart *xp, const struct engine *e, + const int with_cosmology, const struct cosmology *cosmo, + const struct feedback_props *fb_props) { if (p->decoupled) { const integertime_t ti_step = get_integer_timestep(p->time_bin); @@ -114,8 +102,7 @@ __attribute__((always_inline)) INLINE static void feedback_recouple_part( if (with_cosmology) { dt_part = cosmology_get_delta_time(e->cosmology, ti_begin, ti_begin + ti_step); - } - else { + } else { dt_part = get_timestep(p->time_bin, e->time_base); } @@ -133,11 +120,10 @@ __attribute__((always_inline)) INLINE static void feedback_recouple_part( * (2) If the stream radius is negative. * (3) If the timer has run out. */ - const double rho_nH_cgs = + const double rho_nH_cgs = hydro_get_physical_density(p, cosmo) * fb_props->rho_to_n_cgs; - const double rho_recouple_cgs = - fb_props->recouple_density_factor * - fb_props->recouple_ism_density_nH_cgs; + const double rho_recouple_cgs = fb_props->recouple_density_factor * + fb_props->recouple_ism_density_nH_cgs; const int recouple = (p->feedback_data.decoupling_delay_time <= 0.f || p->chemistry_data.radius_stream < 0.f || @@ -146,12 +132,10 @@ __attribute__((always_inline)) INLINE static void feedback_recouple_part( if (recouple && p->decoupled == 1) { /* If it is recoupling, do one more decoupled step to set timestep etc */ p->decoupled = 2; - } - else if (recouple) { + } else if (recouple) { /* extra decoupled step is done, now properly recouple */ feedback_recouple_set_flags(p, cosmo); - } - else { + } else { /* Reset subgrid properties if decoupled for safety */ p->cooling_data.subgrid_temp = 0.f; p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); @@ -171,23 +155,21 @@ __attribute__((always_inline)) INLINE static void feedback_recouple_part( * @param fb_props The #feedback_props feedback parameters. */ __attribute__((always_inline)) INLINE static void feedback_set_wind_direction( - struct part* p, struct xpart* xp, const struct engine* e, - const int with_cosmology, - const struct cosmology* cosmo, - const struct feedback_props* fb_props) { + struct part *p, struct xpart *xp, const struct engine *e, + const int with_cosmology, const struct cosmology *cosmo, + const struct feedback_props *fb_props) { - p->feedback_data.wind_direction[0] = + p->feedback_data.wind_direction[0] = p->gpart->a_grav[1] * p->gpart->v_full[2] - p->gpart->a_grav[2] * p->gpart->v_full[1]; - p->feedback_data.wind_direction[1] = + p->feedback_data.wind_direction[1] = p->gpart->a_grav[2] * p->gpart->v_full[0] - p->gpart->a_grav[0] * p->gpart->v_full[2]; - p->feedback_data.wind_direction[2] = + p->feedback_data.wind_direction[2] = p->gpart->a_grav[0] * p->gpart->v_full[1] - p->gpart->a_grav[1] * p->gpart->v_full[0]; } - /** * @brief Update the properties of a particle due to feedback effects after * the cooling was applied. @@ -200,7 +182,7 @@ __attribute__((always_inline)) INLINE static void feedback_set_wind_direction( * @param with_cosmology Is this a cosmological simulation? */ __attribute__((always_inline)) INLINE static void feedback_update_part( - struct part* p, struct xpart* xp, const struct engine* e) {} + struct part *p, struct xpart *xp, const struct engine *e) {} /** * @brief Reset the gas particle-carried fields related to feedback at the @@ -210,7 +192,7 @@ __attribute__((always_inline)) INLINE static void feedback_update_part( * @param xp The extended data of the particle. */ __attribute__((always_inline)) INLINE static void feedback_reset_part( - struct part* p, struct xpart* xp) {} + struct part *p, struct xpart *xp) {} /** * @brief Should this particle be doing any feedback-related operation? @@ -219,10 +201,12 @@ __attribute__((always_inline)) INLINE static void feedback_reset_part( * @param e The #engine. */ __attribute__((always_inline)) INLINE static int feedback_is_active( - const struct spart* sp, const struct engine* e) { + const struct spart *sp, const struct engine *e) { - //message("FEEDBACK_IS_ACTIVE %d %g %d yes? %d", e->step, sp->birth_time, sp->count_since_last_enrichment, e->step <= 0 || - // ((sp->birth_time != -1.) && (sp->count_since_last_enrichment == 0))); + // message("FEEDBACK_IS_ACTIVE %d %g %d yes? %d", e->step, sp->birth_time, + // sp->count_since_last_enrichment, e->step <= 0 || + // ((sp->birth_time != -1.) && (sp->count_since_last_enrichment == + // 0))); return e->step <= 0 || ((sp->birth_time != -1.) && (sp->count_since_last_enrichment == 0)); } @@ -234,7 +218,7 @@ __attribute__((always_inline)) INLINE static int feedback_is_active( * @param e The #engine. */ __attribute__((always_inline)) INLINE static int stars_dm_loop_is_active( - const struct spart* sp, const struct engine* e) { + const struct spart *sp, const struct engine *e) { /* Active stars always do the DM loop for the KIARA model */ return 0; } @@ -245,7 +229,7 @@ __attribute__((always_inline)) INLINE static int stars_dm_loop_is_active( * @param sp The particle to act upon */ __attribute__((always_inline)) INLINE static void feedback_init_spart( - struct spart* sp) { + struct spart *sp) { /* Default to not suppression the mass loading in the winds */ sp->feedback_data.eta_suppression_factor = 1.f; @@ -281,14 +265,13 @@ __attribute__((always_inline)) INLINE static void feedback_init_spart( * @return The length of the enrichment step in internal units. */ INLINE static double feedback_get_enrichment_timestep( - const struct spart* sp, const int with_cosmology, - const struct cosmology* cosmo, const double time, const double dt_star) { - + const struct spart *sp, const int with_cosmology, + const struct cosmology *cosmo, const double time, const double dt_star) { + if (with_cosmology) { return cosmology_get_delta_time_from_scale_factors( cosmo, (double)sp->last_enrichment_time, cosmo->a); - } - else { + } else { return time - (double)sp->last_enrichment_time; } } @@ -298,7 +281,7 @@ INLINE static double feedback_get_enrichment_timestep( * needs to be distributed. */ __attribute__((always_inline)) INLINE static void feedback_reset_feedback( - struct spart* sp, const struct feedback_props* feedback_props) { + struct spart *sp, const struct feedback_props *feedback_props) { /* Zero the amount of mass that is distributed */ sp->feedback_data.mass = 0.; @@ -314,7 +297,6 @@ __attribute__((always_inline)) INLINE static void feedback_reset_feedback( /* Zero the energy to inject */ sp->feedback_data.energy = 0.; - } /** @@ -327,7 +309,7 @@ __attribute__((always_inline)) INLINE static void feedback_reset_feedback( * @param feedback_props The properties of the feedback model. */ __attribute__((always_inline)) INLINE static void feedback_first_init_spart( - struct spart* sp, const struct feedback_props* feedback_props) { + struct spart *sp, const struct feedback_props *feedback_props) { feedback_init_spart(sp); sp->feedback_data.SNe_ThisTimeStep = 0.; @@ -339,7 +321,6 @@ __attribute__((always_inline)) INLINE static void feedback_first_init_spart( sp->feedback_data.physical_energy_reservoir = 0.; sp->feedback_data.N_launched = 0; sp->feedback_data.eta_suppression_factor = 1.f; - } /** @@ -374,10 +355,10 @@ __attribute__((always_inline)) INLINE static void feedback_first_init_part( * @param feedback_props The properties of the feedback model. */ __attribute__((always_inline)) INLINE static void feedback_prepare_spart( - struct spart* sp, const struct feedback_props* feedback_props) {} + struct spart *sp, const struct feedback_props *feedback_props) {} /** - * @brief Compute kick velocity for particle sp based on host galaxy properties, + * @brief Compute kick velocity for particle sp based on host galaxy properties, * in code units * * @param sp The #spart to consider @@ -386,49 +367,49 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_spart( * @param ti_current Current integer time used value for seeding random number */ __attribute__((always_inline)) INLINE static double -feedback_compute_kick_velocity(const float galaxy_stellar_mass, - const size_t sp_id, const struct cosmology* cosmo, - const struct feedback_props *fb_props, const integertime_t ti_current) { +feedback_compute_kick_velocity(const float galaxy_stellar_mass, + const size_t sp_id, + const struct cosmology *cosmo, + const struct feedback_props *fb_props, + const integertime_t ti_current) { /* Compute galaxy mass. This is done in the RUNNER files. */ - float galaxy_stellar_mass_Msun = galaxy_stellar_mass; // in code units for now + float galaxy_stellar_mass_Msun = + galaxy_stellar_mass; // in code units for now if (galaxy_stellar_mass_Msun < fb_props->minimum_galaxy_stellar_mass) { galaxy_stellar_mass_Msun = fb_props->minimum_galaxy_stellar_mass; } - galaxy_stellar_mass_Msun *= fb_props->mass_to_solar_mass; // convert to Msun + galaxy_stellar_mass_Msun *= fb_props->mass_to_solar_mass; // convert to Msun - /* Physical circular velocity km/s from z=0-2 DEEP2 + /* Physical circular velocity km/s from z=0-2 DEEP2 measurements by Dutton+11 */ /* Dutton+11 eq 6: log (M* / 1e10) = -0.61 + 4.51 log (vdisk / 100) */ const float v_circ_km_s = 100.f * powf(4.0738f * galaxy_stellar_mass_Msun * 1.e-10f, 0.221729f) * - pow(cosmo->H / cosmo->H0, 1.f / 3.f); + pow(cosmo->H / cosmo->H0, 1.f / 3.f); - const float rand_for_scatter = - random_unit_interval(sp_id, ti_current, - random_number_stellar_feedback_2); + const float rand_for_scatter = + random_unit_interval(sp_id, ti_current, random_number_stellar_feedback_2); /* The wind velocity in internal units and COMOVING from FIRE scalings */ float wind_velocity = fb_props->FIRE_velocity_normalization * powf(v_circ_km_s / 200.f, fb_props->FIRE_velocity_slope) * - ( 1.f - fb_props->kick_velocity_scatter + - 2.f * fb_props->kick_velocity_scatter * rand_for_scatter) * - v_circ_km_s * - fb_props->kms_to_internal * - /* Note that xpj->v_full = a^2 * dx/dt, with x the comoving coordinate. - * Thus a physical kick, dv, gets translated into a code velocity kick, + (1.f - fb_props->kick_velocity_scatter + + 2.f * fb_props->kick_velocity_scatter * rand_for_scatter) * + v_circ_km_s * fb_props->kms_to_internal * + /* Note that xpj->v_full = a^2 * dx/dt, with x the comoving coordinate. + * Thus a physical kick, dv, gets translated into a code velocity kick, * a * dv */ cosmo->a; - - const float a_suppress_inv = - (1.f + fabs(fb_props->wind_velocity_suppression_redshift)); - if (fb_props->wind_velocity_suppression_redshift > 0 && - cosmo->z > fb_props->wind_velocity_suppression_redshift) { + + const float a_suppress_inv = + (1.f + fabs(fb_props->wind_velocity_suppression_redshift)); + if (fb_props->wind_velocity_suppression_redshift > 0 && + cosmo->z > fb_props->wind_velocity_suppression_redshift) { wind_velocity *= cosmo->a * cosmo->a * a_suppress_inv * a_suppress_inv; - } - else if (fb_props->wind_velocity_suppression_redshift < 0) { + } else if (fb_props->wind_velocity_suppression_redshift < 0) { wind_velocity *= expf(-powf(cosmo->a * a_suppress_inv, -3.f)); } @@ -454,10 +435,9 @@ feedback_compute_kick_velocity(const float galaxy_stellar_mass, * @param with_cosmology Are we running with cosmology on? */ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( - struct spart* restrict sp, const struct feedback_props* feedback_props, - const struct cosmology* cosmo, const struct unit_system* us, - const struct phys_const* phys_const, - const double star_age_beg_step, + struct spart *restrict sp, const struct feedback_props *feedback_props, + const struct cosmology *cosmo, const struct unit_system *us, + const struct phys_const *phys_const, const double star_age_beg_step, const double dt, const double time, const integertime_t ti_begin, const int with_cosmology) { @@ -473,13 +453,12 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( TIMER_TIC; #if COOLING_GRACKLE_MODE >= 2 - /* Compute Habing luminosity of star for use in ISRF + /* Compute Habing luminosity of star for use in ISRF (only with Grackle subgrid ISM model) */ - /*sp->feedback_data.lum_habing = - feedback_get_lum_from_star_particle(sp, star_age_beg_step, feedback_props); - message("G0: age %g Lhabing %g\n", - star_age_beg_step * feedback_props->time_to_Myr, - sp->feedback_data.lum_habing); + /*sp->feedback_data.lum_habing = + feedback_get_lum_from_star_particle(sp, star_age_beg_step, + feedback_props); message("G0: age %g Lhabing %g\n", star_age_beg_step * + feedback_props->time_to_Myr, sp->feedback_data.lum_habing); */ #endif @@ -498,15 +477,9 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( ejecta_metal_mass[elem] = 0.; } - feedback_get_ejecta_from_star_particle(sp, - star_age_beg_step, - feedback_props, - dt, - &N_SNe, - &ejecta_energy, - &ejecta_mass, - &ejecta_unprocessed, - ejecta_metal_mass); + feedback_get_ejecta_from_star_particle( + sp, star_age_beg_step, feedback_props, dt, &N_SNe, &ejecta_energy, + &ejecta_mass, &ejecta_unprocessed, ejecta_metal_mass); ejecta_mass *= 0.5f; // fudge factor @@ -515,30 +488,33 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( message("ejecta_metal_mass[%d]=%g", elem, ejecta_metal_mass[elem]); } - message("[Fe/H] = %g", - sp->chemistry_data.metal_mass_fraction[chemistry_element_Fe] / + message("[Fe/H] = %g", + sp->chemistry_data.metal_mass_fraction[chemistry_element_Fe] / sp->chemistry_data.metal_mass_fraction[chemistry_element_H]); message("Z = %g", sp->chemistry_data.metal_mass_fraction_total); - error("Star particle %lld with mass %g (init_mass %g) is trying to give " - "away NaN mass (Mejecta=%g, Energy=%g, Unprocessed=%g)!", - sp->id, sp->mass, sp->mass_init, ejecta_mass, ejecta_energy, - ejecta_unprocessed); + error( + "Star particle %lld with mass %g (init_mass %g) is trying to give " + "away NaN mass (Mejecta=%g, Energy=%g, Unprocessed=%g)!", + sp->id, sp->mass, sp->mass_init, ejecta_mass, ejecta_energy, + ejecta_unprocessed); } if (ejecta_energy < 0.f) { - warning("Star particle %lld with mass %g (init_mass %g) is trying to give " - "away negative energy (Mejecta=%g, Energy=%g, Unprocessed=%g)!", - sp->id, sp->mass, sp->mass_init, ejecta_mass, ejecta_energy, - ejecta_unprocessed); + warning( + "Star particle %lld with mass %g (init_mass %g) is trying to give " + "away negative energy (Mejecta=%g, Energy=%g, Unprocessed=%g)!", + sp->id, sp->mass, sp->mass_init, ejecta_mass, ejecta_energy, + ejecta_unprocessed); feedback_reset_feedback(sp, feedback_props); return; } - if (sp->mass-ejecta_mass < 0.2 * sp->mass_init) { - warning("Star particle %lld with mass %g is trying to lower its mass " - "past 0.2 of initial (Mejecta=%g)!", - sp->id, sp->mass, ejecta_mass); + if (sp->mass - ejecta_mass < 0.2 * sp->mass_init) { + warning( + "Star particle %lld with mass %g is trying to lower its mass " + "past 0.2 of initial (Mejecta=%g)!", + sp->id, sp->mass, ejecta_mass); feedback_reset_feedback(sp, feedback_props); return; } @@ -550,28 +526,26 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( const float FIRE_eta_break = feedback_props->FIRE_eta_break; const float FIRE_eta_lower_slope = feedback_props->FIRE_eta_lower_slope; const float FIRE_eta_upper_slope = feedback_props->FIRE_eta_upper_slope; - const float FIRE_eta_lower_slope_EOR = feedback_props->FIRE_eta_lower_slope_EOR; - const float wind_velocity_suppression_redshift = feedback_props->wind_velocity_suppression_redshift; - - float eta = feedback_mass_loading_factor(cosmo, M_star, - M_star_min, - FIRE_eta_norm, - FIRE_eta_break, - FIRE_eta_lower_slope, - FIRE_eta_upper_slope, - FIRE_eta_lower_slope_EOR, - wind_velocity_suppression_redshift); + const float FIRE_eta_lower_slope_EOR = + feedback_props->FIRE_eta_lower_slope_EOR; + const float wind_velocity_suppression_redshift = + feedback_props->wind_velocity_suppression_redshift; + + float eta = feedback_mass_loading_factor( + cosmo, M_star, M_star_min, FIRE_eta_norm, FIRE_eta_break, + FIRE_eta_lower_slope, FIRE_eta_upper_slope, FIRE_eta_lower_slope_EOR, + wind_velocity_suppression_redshift); /* velocity in internal units which is a^2*comoving, or a*physical */ - float v_internal = - feedback_compute_kick_velocity(M_star, sp->id, cosmo, feedback_props, ti_begin); + float v_internal = feedback_compute_kick_velocity(M_star, sp->id, cosmo, + feedback_props, ti_begin); /* Early (non-SN) stellar feedback energy from Keller+22 eq. 10 */ const float alpha = feedback_props->early_stellar_feedback_alpha; const float alpha_power = 4.f * alpha - 1.f; const float tfb_inv = feedback_props->early_stellar_feedback_tfb_inv; - if (alpha_power > 0.f && - star_age_beg_step < feedback_props->early_stellar_feedback_tfb) { + if (alpha_power > 0.f && + star_age_beg_step < feedback_props->early_stellar_feedback_tfb) { const float eps_term = feedback_props->early_stellar_feedback_epsterm; const float h_phys = kernel_gamma * sp->h * cosmo->a; @@ -586,20 +560,18 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( sp->feedback_data.physical_energy_reservoir += 0.5 * delta_p * v_phys; #ifdef KIARA_DEBUG_CHECKS - message("ESF: id=%lld age=%g dt=%g Myr, Etot=%g E_ESF=%g f_inc=%g", - sp->id, t_prev * feedback_props->time_to_Myr, - dt * feedback_props->time_to_Myr, - sp->feedback_data.physical_energy_reservoir, - 0.5f * delta_p * v_phys, - 0.5f * delta_p * v_phys / - sp->feedback_data.physical_energy_reservoir); + message( + "ESF: id=%lld age=%g dt=%g Myr, Etot=%g E_ESF=%g f_inc=%g", sp->id, + t_prev * feedback_props->time_to_Myr, dt * feedback_props->time_to_Myr, + sp->feedback_data.physical_energy_reservoir, 0.5f * delta_p * v_phys, + 0.5f * delta_p * v_phys / sp->feedback_data.physical_energy_reservoir); #endif } /** * Compute the mass loading and energy reservoirs for the stellar feedback. * Mass loading will be limited by the physical energy available from chem5 - * directly at each step. Later, when computing the probability to kick + * directly at each step. Later, when computing the probability to kick * a particle, the mass_to_launch will be limited by eta_suppression_factor. */ const float wind_mass = eta * sp->mass_init; @@ -607,18 +579,18 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( if (total_mass_kicked < wind_mass) { - /* Boost wind speed based on metallicity which governs - * photon energy output */ + /* Boost wind speed based on metallicity which governs + * photon energy output */ float Z_fac = 1.f; const int vwind_boost_flag = feedback_props->metal_dependent_vwind; if (vwind_boost_flag != kiara_metal_boosting_off) { Z_fac = 2.61634f; const float Z_met = sp->chemistry_data.metal_mass_fraction_total; if (Z_met > 1.e-9f) { - Z_fac = powf(10.f, - -0.0029f * powf(log10f(Z_met) + 9.f, 2.5f) + 0.417694f); + Z_fac = + powf(10.f, -0.0029f * powf(log10f(Z_met) + 9.f, 2.5f) + 0.417694f); } - + Z_fac = max(Z_fac, 1.f); } @@ -641,8 +613,7 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( const double E_SNII_phys = 1.e51 * N_SNe / feedback_props->energy_to_cgs; /* Apply energy multiplier and metallicity scaling */ - const float energy_boost = - feedback_props->SNII_energy_multiplier * Z_fac; + const float energy_boost = feedback_props->SNII_energy_multiplier * Z_fac; /* Add to physical energy reservoir */ sp->feedback_data.physical_energy_reservoir += E_SNII_phys * energy_boost; @@ -653,11 +624,11 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( /* ------ Compute allowable wind mass this step ------ */ /* Wind energy per unit mass in physical units */ - const double specific_energy_phys = + const double specific_energy_phys = 0.5 * v_internal * v_internal * cosmo->a2_inv; /* Max wind mass supportable by current energy */ - const double wind_mass_max = + const double wind_mass_max = sp->feedback_data.physical_energy_reservoir / specific_energy_phys; /* Remaining mass left to launch */ @@ -669,29 +640,24 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( sp->feedback_data.mass_to_launch = mass_to_launch; #ifdef KIARA_DEBUG_CHECKS - message("ETA: z=%g id=%lld age=%g Eres=%g dE=%g NSNe=%g NSNtot=%g eta=%g " - "max=%g tot=%g mlaunch=%g Ntot=%d", - cosmo->z, - sp->id, - star_age_beg_step * feedback_props->time_to_Myr, - sp->feedback_data.physical_energy_reservoir * - feedback_props->energy_to_cgs, - 1.e51 * N_SNe * scaling, - N_SNe, - sp->mass_init * feedback_props->mass_to_solar_mass / 80.f, - /* 1 SNII for ~80 Mo for Kroupa/Chabrier IMF */ - mass_to_launch / sp->mass_init, - eta_max_this_timestep, - eta, - sp->feedback_data.mass_to_launch, - sp->feedback_data.N_launched); + message( + "ETA: z=%g id=%lld age=%g Eres=%g dE=%g NSNe=%g NSNtot=%g eta=%g " + "max=%g tot=%g mlaunch=%g Ntot=%d", + cosmo->z, sp->id, star_age_beg_step * feedback_props->time_to_Myr, + sp->feedback_data.physical_energy_reservoir * + feedback_props->energy_to_cgs, + 1.e51 * N_SNe * scaling, N_SNe, + sp->mass_init * feedback_props->mass_to_solar_mass / 80.f, + /* 1 SNII for ~80 Mo for Kroupa/Chabrier IMF */ + mass_to_launch / sp->mass_init, eta_max_this_timestep, eta, + sp->feedback_data.mass_to_launch, sp->feedback_data.N_launched); #endif /* Set stream radius for firehose particles kicked by this star */ /* This is the physical initial density */ const double stream_init_density = 0.1; /* n_H units CGS */ - const double rho_volumefilling_phys = + const double rho_volumefilling_phys = stream_init_density / feedback_props->rho_to_n_cgs; float galaxy_stellar_mass_Msun = M_star; const float min_gal_mass = feedback_props->minimum_galaxy_stellar_mass; @@ -702,19 +668,18 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( /* stream size = 2 * comoving effective size of disk galaxies * (Ward+2024 CEERS) */ - const float redge_obs = 2.f * - 7.1f * pow(cosmo->a, 0.63f) * - pow(galaxy_stellar_mass_Msun / 5.e10, 0.16f); + const float redge_obs = 2.f * 7.1f * pow(cosmo->a, 0.63f) * + pow(galaxy_stellar_mass_Msun / 5.e10, 0.16f); /* Convert to internal units */ sp->feedback_data.firehose_radius_stream = cosmo->a_inv * redge_obs / feedback_props->length_to_kpc; - if (sp->galaxy_data.stellar_mass > 0.f && sp->galaxy_data.specific_sfr > 0.f && - eta > 0.f && sp->feedback_data.wind_velocity != 0.f) { + if (sp->galaxy_data.stellar_mass > 0.f && + sp->galaxy_data.specific_sfr > 0.f && eta > 0.f && + sp->feedback_data.wind_velocity != 0.f) { - const float v_phys = - fabs(sp->feedback_data.wind_velocity) * cosmo->a_inv; + const float v_phys = fabs(sp->feedback_data.wind_velocity) * cosmo->a_inv; const float m_dot_wind_sfr = eta * sp->galaxy_data.specific_sfr * sp->galaxy_data.stellar_mass; const float specific_m_dot_wind_vel = @@ -732,11 +697,11 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( fmax(sp->feedback_data.firehose_radius_stream, kernel_gamma * sp->h); } - /* D. Rennehan: Do some magic that I still don't understand + /* D. Rennehan: Do some magic that I still don't understand */ double dum = 0.; int flag_negative = 0; - /* Here we can loop over Swift metals because metal_mass_fraction + /* Here we can loop over Swift metals because metal_mass_fraction * would be zero for the unique Chem5 metals anyway, and would * not activate the condition. */ @@ -750,7 +715,7 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( /* Do not break here, we need the zeroed elements where negative */ } } - + /* Check for any remaining that have negative mass after adding unprocessed */ for (elem = 0; elem < chem5_element_count; elem++) { if (ejecta_metal_mass[elem] < 0.) { @@ -772,19 +737,19 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( } } - /* Now we loop over the Swift metals and set the proper values using the + /* Now we loop over the Swift metals and set the proper values using the conversion map */ sp->feedback_data.total_metal_mass = ejecta_metal_mass[chem5_element_Z]; for (elem = 0; elem < chemistry_element_count; elem++) { - sp->feedback_data.metal_mass[elem] = + sp->feedback_data.metal_mass[elem] = ejecta_metal_mass[feedback_props->element_index_conversions[elem]]; } #if COOLING_GRACKLE_MODE >= 2 - /* Put some of the ejecta metals into dust. Must be done after + /* Put some of the ejecta metals into dust. Must be done after chem5->chemistry conversion map is applied */ if (sp->feedback_data.total_metal_mass > 0.) { - feedback_dust_production_condensation(sp, star_age_beg_step, feedback_props, + feedback_dust_production_condensation(sp, star_age_beg_step, feedback_props, sp->feedback_data.metal_mass); } #endif @@ -800,9 +765,9 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( sp->last_enrichment_time = (with_cosmology) ? cosmo->a : time; #if COOLING_GRACKLE_MODE >= 2 - /* Update the number of SNe that have gone off, used in Grackle dust model. + /* Update the number of SNe that have gone off, used in Grackle dust model. Actually stores SNe rate */ - sp->feedback_data.SNe_ThisTimeStep = N_SNe / dt; + sp->feedback_data.SNe_ThisTimeStep = N_SNe / dt; sp->feedback_data.SNe_Total += N_SNe; #endif @@ -830,10 +795,10 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( * @param time_base The time base. */ __attribute__((always_inline)) INLINE static void feedback_will_do_feedback( - struct spart* sp, const struct feedback_props* feedback_props, - const int with_cosmology, const struct cosmology* cosmo, const double time, - const struct unit_system* us, const struct phys_const* phys_const, - const integertime_t ti_current, const double time_base) { + struct spart *sp, const struct feedback_props *feedback_props, + const int with_cosmology, const struct cosmology *cosmo, const double time, + const struct unit_system *us, const struct phys_const *phys_const, + const integertime_t ti_current, const double time_base) { /* Special case for new-born stars */ if (with_cosmology) { @@ -885,11 +850,11 @@ __attribute__((always_inline)) INLINE static void feedback_will_do_feedback( } } -void feedback_clean(struct feedback_props* fp); +void feedback_clean(struct feedback_props *fp); -void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream); +void feedback_struct_dump(const struct feedback_props *feedback, FILE *stream); -void feedback_struct_restore(struct feedback_props* feedback, FILE* stream); +void feedback_struct_restore(struct feedback_props *feedback, FILE *stream); #ifdef HAVE_HDF5 /** @@ -898,10 +863,11 @@ void feedback_struct_restore(struct feedback_props* feedback, FILE* stream); * @param feedback The properties of the feedback scheme. * @param h_grp The HDF5 group in which to write. */ -INLINE static void feedback_write_flavour(struct feedback_props* feedback, +INLINE static void feedback_write_flavour(struct feedback_props *feedback, hid_t h_grp) { - io_write_attribute_s(h_grp, "Feedback Model", "KIARA " + io_write_attribute_s(h_grp, "Feedback Model", + "KIARA " "(decoupled kinetic + chem5 enrichment)"); } #endif // HAVE_HDF5 diff --git a/src/feedback/KIARA/feedback_debug.h b/src/feedback/KIARA/feedback_debug.h index 2a4fb2b7d0..5d78a3fb88 100644 --- a/src/feedback/KIARA/feedback_debug.h +++ b/src/feedback/KIARA/feedback_debug.h @@ -2,7 +2,7 @@ * This file is part of SWIFT. * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * 2022 Doug Rennehan (douglas.rennehan@gmail.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 @@ -21,6 +21,6 @@ #define SWIFT_FEEDBACK_KIARA_DEBUG_H __attribute__((always_inline)) INLINE static void feedback_debug_particle( - const struct part* p, const struct xpart* xp) {} + const struct part *p, const struct xpart *xp) {} #endif /* SWIFT_FEEDBACK_KIARA_DEBUG_H */ diff --git a/src/feedback/KIARA/feedback_iact.h b/src/feedback/KIARA/feedback_iact.h index b1e36eba8a..efa815201d 100644 --- a/src/feedback/KIARA/feedback_iact.h +++ b/src/feedback/KIARA/feedback_iact.h @@ -2,7 +2,7 @@ * This file is part of SWIFT. * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2022 Doug Rennehan (douglas.rennehan@gmail.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 @@ -25,6 +25,7 @@ #include "timestep_sync_part.h" #include "tools.h" #include "tracers.h" + #include #define KIARA_WIND_LOG @@ -38,23 +39,20 @@ * @param dir_flag Flag to choose direction: 0=rendom, 1=L_gas, 2=L_BH. * @param dir Direction of kick (returned). */ -__attribute__((always_inline)) INLINE static float -feedback_set_kick_direction( +__attribute__((always_inline)) INLINE static float feedback_set_kick_direction( const struct spart *si, const struct part *pj, - const integertime_t ti_current, - const int dir_flag, float *dir) { + const integertime_t ti_current, const int dir_flag, float *dir) { float kick_dir = 1.f; double random_number = 1.; switch (dir_flag) { /* Isotropic */ - case 0: - { - const double random_for_theta = - random_unit_interval(si->id, ti_current, random_number_isotropic_SNII_feedback_ray_theta); - const double random_for_phi = - random_unit_interval(si->id, ti_current, random_number_isotropic_SNII_feedback_ray_phi); + case 0: { + const double random_for_theta = random_unit_interval( + si->id, ti_current, random_number_isotropic_SNII_feedback_ray_theta); + const double random_for_phi = random_unit_interval( + si->id, ti_current, random_number_isotropic_SNII_feedback_ray_phi); const float theta = acosf(2.f * random_for_theta - 1.f); const float phi = 2.f * M_PI * random_for_phi; @@ -66,20 +64,16 @@ feedback_set_kick_direction( } /* Along the v x a direction */ - case 1: - { - dir[0] = - pj->gpart->a_grav[1] * pj->gpart->v_full[2] - - pj->gpart->a_grav[2] * pj->gpart->v_full[1]; - dir[1] = - pj->gpart->a_grav[2] * pj->gpart->v_full[0] - - pj->gpart->a_grav[0] * pj->gpart->v_full[2]; - dir[2] = - pj->gpart->a_grav[0] * pj->gpart->v_full[1] - - pj->gpart->a_grav[1] * pj->gpart->v_full[0]; - - random_number = - random_unit_interval(si->id, ti_current, random_number_stellar_feedback_1); + case 1: { + dir[0] = pj->gpart->a_grav[1] * pj->gpart->v_full[2] - + pj->gpart->a_grav[2] * pj->gpart->v_full[1]; + dir[1] = pj->gpart->a_grav[2] * pj->gpart->v_full[0] - + pj->gpart->a_grav[0] * pj->gpart->v_full[2]; + dir[2] = pj->gpart->a_grav[0] * pj->gpart->v_full[1] - + pj->gpart->a_grav[1] * pj->gpart->v_full[0]; + + random_number = random_unit_interval(si->id, ti_current, + random_number_stellar_feedback_1); kick_dir = (random_number > 0.5) ? 1.f : -1.f; break; } @@ -98,22 +92,21 @@ feedback_set_kick_direction( return kick_dir; } - /** * @brief Compute customized kernel weight for feedback * * @param pj gas particle. * @param wi SPH kernel weight at location of pj */ -__attribute__((always_inline)) INLINE static float -feedback_kernel_weight(const struct part *pj, const float wi, const float ui, - const struct feedback_props *fb_props) { +__attribute__((always_inline)) INLINE static float feedback_kernel_weight( + const struct part *pj, const float wi, const float ui, + const struct feedback_props *fb_props) { /* If it's beyond the kick radius, then the weighting is zero */ if (ui >= fb_props->kick_radius_over_h) return 0.f; /* Weight towards higher SFR particles. As SFR->0, SFR_wi->wi and - * then radial weighting returns to normal. */ + * then radial weighting returns to normal. */ float weight = wi; if (fb_props->use_sfr_weighted_launch == 1) { weight = (pj->sf_data.SFR > 0.f) ? wi + pj->sf_data.SFR : wi; @@ -147,7 +140,7 @@ runner_iact_nonsym_feedback_density(const float r2, const float dx[3], const integertime_t ti_current) { /* Do not count winds in the density */ - //if (pj->decoupled) return; + // if (pj->decoupled) return; const float rho = hydro_get_comoving_density(pj); if (rho <= 0.f) return; @@ -178,7 +171,6 @@ runner_iact_nonsym_feedback_density(const float r2, const float dx[3], const float wt = feedback_kernel_weight(pj, wi, ui, fb_props); si->feedback_data.wind_wt_sum += wt; if (wt > 0.f) si->feedback_data.wind_ngb_mass += mj; - } __attribute__((always_inline)) INLINE static void @@ -190,7 +182,8 @@ runner_iact_nonsym_feedback_prep1(const float r2, const float dx[3], const struct feedback_props *fb_props, const integertime_t ti_current) { - /* No need to even check anything else if there is no mass or energy to launch */ + /* No need to even check anything else if there is no mass or energy to launch + */ if (si->feedback_data.mass_to_launch <= 0.f) return; if (si->feedback_data.physical_energy_reservoir <= 0.f) return; @@ -202,7 +195,7 @@ runner_iact_nonsym_feedback_prep1(const float r2, const float dx[3], if (pj->feedback_data.kick_id > -1) return; /* If pj is already a wind particle, don't kick again */ - // if (pj->decoupled) return; + // if (pj->decoupled) return; /* Get r. */ const float r = sqrtf(r2); @@ -223,7 +216,7 @@ runner_iact_nonsym_feedback_prep1(const float r2, const float dx[3], /* No kick if weight is zero */ if (wt <= 0.f) return; - /* Total wind ngb mass in kernel */ + /* Total wind ngb mass in kernel */ const float ngb_mass = si->feedback_data.wind_ngb_mass; /* Total mass to launch for this star particle */ @@ -243,8 +236,7 @@ runner_iact_nonsym_feedback_prep1(const float r2, const float dx[3], const float wj = wt / hydro_get_mass(pj); /* Probability to swallow this particle */ - const float prob = - mass_to_launch * wj / si->feedback_data.wind_wt_sum; + const float prob = mass_to_launch * wj / si->feedback_data.wind_wt_sum; /* Draw a random number (Note mixing both IDs), up to max probability */ const float rand = random_unit_interval(si->id + pj->id, ti_current, @@ -255,27 +247,21 @@ runner_iact_nonsym_feedback_prep1(const float r2, const float dx[3], pj->feedback_data.kick_id = si->id; } -//#ifdef KIARA_DEBUG_CHECKS - message("KICK_PROB: z=%g sid=%lld, gid=%lld, prob=%g, rand=%g, eta=%g, mlaunch=%g, " - "m*=%g, wj=%g, wt_sum=%g, kicked? %d", - cosmo->z, - si->id, - pj->id, - prob, - rand, - si->feedback_data.mass_to_launch / si->mass_init, - si->feedback_data.mass_to_launch, - si->mass_init, - wj, - si->feedback_data.wind_wt_sum, - pj->feedback_data.kick_id == si->id); -//#endif - + // #ifdef KIARA_DEBUG_CHECKS + message( + "KICK_PROB: z=%g sid=%lld, gid=%lld, prob=%g, rand=%g, eta=%g, " + "mlaunch=%g, " + "m*=%g, wj=%g, wt_sum=%g, kicked? %d", + cosmo->z, si->id, pj->id, prob, rand, + si->feedback_data.mass_to_launch / si->mass_init, + si->feedback_data.mass_to_launch, si->mass_init, wj, + si->feedback_data.wind_wt_sum, pj->feedback_data.kick_id == si->id); + // #endif } /** * @brief Compile gas particles to be kicked by stellar feedback in this step, - * + * * * @param r2 Distance squared from star i to gas j. * @param dx[3] x,y,z distance from star i to gas j. @@ -302,7 +288,7 @@ runner_iact_nonsym_feedback_prep2(const float r2, const float dx[3], si->feedback_data.total_mass_kicked += hydro_get_mass(pj); /* Work done on the particle */ - const float v2 = + const float v2 = si->feedback_data.wind_velocity * si->feedback_data.wind_velocity; const double energy_phys = 0.5 * hydro_get_mass(pj) * v2 * cosmo->a2_inv; @@ -314,13 +300,11 @@ runner_iact_nonsym_feedback_prep2(const float r2, const float dx[3], /* Keep track of how many particles launched */ si->feedback_data.N_launched += 1; - } - } /** - * @brief Kick and sometimes heat gas particle near a star, + * @brief Kick and sometimes heat gas particle near a star, * if star has enough mass and energy for an ejection event. * * @param si First (star) particle (not updated). @@ -331,11 +315,9 @@ runner_iact_nonsym_feedback_prep2(const float r2, const float dx[3], * @param ti_current Current integer time used value for seeding random number * generator */ -__attribute__((always_inline)) INLINE static void -feedback_kick_gas_around_star( +__attribute__((always_inline)) INLINE static void feedback_kick_gas_around_star( const struct spart *si, struct part *pj, struct xpart *xpj, - const struct cosmology *cosmo, - const struct feedback_props *fb_props, + const struct cosmology *cosmo, const struct feedback_props *fb_props, const integertime_t ti_current) { if (pj->feedback_data.kick_id == si->id) { @@ -343,38 +325,41 @@ feedback_kick_gas_around_star( /* Need time-step for decoupling */ const integertime_t ti_step = get_integer_timestep(pj->time_bin); const integertime_t ti_begin = - get_integer_time_begin(ti_current - 1, pj->time_bin); + get_integer_time_begin(ti_current - 1, pj->time_bin); /* TODO: Requires always having with_cosmology! */ - const double dt = + const double dt = cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); /* Compute velocity and KE of wind event. - * Note that xpj->v_full = a^2 * dx/dt, with x the comoving - * coordinate. Therefore, a physical kick, dv, gets translated into a - * code velocity kick, a * dv. - */ + * Note that xpj->v_full = a^2 * dx/dt, with x the comoving + * coordinate. Therefore, a physical kick, dv, gets translated into a + * code velocity kick, a * dv. + */ const float wind_velocity = si->feedback_data.wind_velocity; - const float wind_velocity_phys = - fabs(wind_velocity * cosmo->a_inv); + const float wind_velocity_phys = fabs(wind_velocity * cosmo->a_inv); float dir[3] = {0.f, 0.f, 0.f}; - const int dir_flag = fb_props->kick_direction_flag; + const int dir_flag = fb_props->kick_direction_flag; const float dirsign = feedback_set_kick_direction(si, pj, ti_current, dir_flag, dir); float norm = sqrtf(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]); - /* Zero normalization (should basically never happen); randomize direction */ + /* Zero normalization (should basically never happen); randomize direction + */ if (norm <= 0.f) { - warning("z=%g sid=%lld pid=%lld normalization of wind direction is zero!\n(x, y, z) " - "= (%g, %g, %g); vw=%g. Randomizing direction.", cosmo->z, si->id, pj->id, - dir[0], dir[1], dir[2], fabs(wind_velocity * cosmo->a_inv)); + warning( + "z=%g sid=%lld pid=%lld normalization of wind direction is " + "zero!\n(x, y, z) " + "= (%g, %g, %g); vw=%g. Randomizing direction.", + cosmo->z, si->id, pj->id, dir[0], dir[1], dir[2], + fabs(wind_velocity * cosmo->a_inv)); dir[0] = random_unit_interval(pj->id, ti_current, - random_number_stellar_feedback_1); + random_number_stellar_feedback_1); dir[1] = random_unit_interval(pj->id, ti_current, - random_number_stellar_feedback_2); + random_number_stellar_feedback_2); dir[2] = random_unit_interval(pj->id, ti_current, - random_number_stellar_feedback_3); + random_number_stellar_feedback_3); norm = sqrtf(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]); } @@ -389,41 +374,38 @@ feedback_kick_gas_around_star( double u_new = fb_props->cold_wind_internal_energy; if (!fb_props->use_firehose_model) { float galaxy_stellar_mass = si->galaxy_data.stellar_mass; - + if (galaxy_stellar_mass < fb_props->minimum_galaxy_stellar_mass) { galaxy_stellar_mass = fb_props->minimum_galaxy_stellar_mass; } - const float galaxy_stellar_mass_Msun = galaxy_stellar_mass * - fb_props->mass_to_solar_mass; + const float galaxy_stellar_mass_Msun = + galaxy_stellar_mass * fb_props->mass_to_solar_mass; /* Based on Pandya et al 2022 FIRE results */ float pandya_slope = 0.f; if (galaxy_stellar_mass_Msun > 3.16e10) { pandya_slope = -2.1f; - } - else { + } else { pandya_slope = -0.1f; } /* 0.2511886 = pow(10., -0.6) */ - const float f_warm = + const float f_warm = 0.2511886f * pow(galaxy_stellar_mass_Msun / 3.16e10f, pandya_slope); /* additional 10% removed for cold phase */ const float hot_wind_fraction = max(0.f, 0.9f - f_warm); - const float rand_for_hot = - random_unit_interval(pj->id, ti_current, - random_number_stellar_feedback_2); - const float rand_for_spread = - random_unit_interval(pj->id, ti_current, - random_number_stellar_feedback_3); + const float rand_for_hot = random_unit_interval( + pj->id, ti_current, random_number_stellar_feedback_2); + const float rand_for_spread = random_unit_interval( + pj->id, ti_current, random_number_stellar_feedback_3); /* If selected, heat the particle */ const double u_wind = 0.5 * wind_velocity_phys * wind_velocity_phys; - if (rand_for_hot < hot_wind_fraction && - fb_props->hot_wind_internal_energy > u_wind) { - u_new = (fb_props->hot_wind_internal_energy - u_wind) * - (0.5 + rand_for_spread); - u_new += hydro_get_physical_internal_energy(pj, xpj, cosmo); + if (rand_for_hot < hot_wind_fraction && + fb_props->hot_wind_internal_energy > u_wind) { + u_new = (fb_props->hot_wind_internal_energy - u_wind) * + (0.5 + rand_for_spread); + u_new += hydro_get_physical_internal_energy(pj, xpj, cosmo); } } @@ -434,11 +416,10 @@ feedback_kick_gas_around_star( #ifdef FIREHOSE_DEBUG_CHECKS /* For firehose model, set initial radius of stream */ if (si->feedback_data.firehose_radius_stream <= 0.f) { - error("Firehose error: firehose_radius_stream <= 0. sid=%lld " - "pid=%lld Rstream=%g", - si->id, - pj->id, - si->feedback_data.firehose_radius_stream); + error( + "Firehose error: firehose_radius_stream <= 0. sid=%lld " + "pid=%lld Rstream=%g", + si->id, pj->id, si->feedback_data.firehose_radius_stream); } #endif @@ -447,7 +428,7 @@ feedback_kick_gas_around_star( /* FINISH UP FEEDBACK */ /* Turn off any star formation in wind particle. - * Record exp factor of when this particle was last ejected as -SFR. */ + * Record exp factor of when this particle was last ejected as -SFR. */ pj->sf_data.SFR = -cosmo->a; /* Update the signal velocity of the particle based on the velocity kick, @@ -464,10 +445,9 @@ feedback_kick_gas_around_star( /* Decouple the particles from the hydrodynamics */ pj->feedback_data.decoupling_delay_time = dt + fb_props->wind_decouple_time_factor * - cosmology_get_time_since_big_bang(cosmo, cosmo->a); + cosmology_get_time_since_big_bang(cosmo, cosmo->a); pj->decoupled = 1; - } - else { + } else { pj->feedback_data.decoupling_delay_time = 0.f; pj->decoupled = 0; } @@ -487,7 +467,7 @@ feedback_kick_gas_around_star( #ifdef KIARA_WIND_LOG /** Log the wind event. - * z starid gasid dt M* vkick vkx vky vkz h x y z vx vy vz T rho v_sig tdec + * z starid gasid dt M* vkick vkx vky vkz h x y z vx vy vz T rho v_sig tdec * Ndec Z */ const float length_convert = cosmo->a * fb_props->length_to_kpc; @@ -495,41 +475,34 @@ feedback_kick_gas_around_star( const float rho_convert = cosmo->a3_inv * fb_props->rho_to_n_cgs; const float u_convert = cosmo->a_factor_internal_energy / fb_props->temp_to_u_factor; -/* Collect information about galaxy that the particle belongs to */ + /* Collect information about galaxy that the particle belongs to */ const float galaxy_mstar = si->galaxy_data.stellar_mass; - const float galaxy_ssfr = si->galaxy_data.specific_sfr; - - printf("WIND_LOG z=%.5f sid=%lld mlaunch=%g mkicked=%g Nkicked=%g zbirth=%g M*=%g sSFR=%g pid=%lld vw=%g vwx=%g vwy=%g vwz=%g h=%g x=%g " - "y=%g z=%g vx=%g vy=%g vz=%g " - "T=%g nH=%g tdel=%g Ndec=%d fZ=%g\n", - cosmo->z, - si->id, - si->feedback_data.mass_to_launch * - fb_props->mass_to_solar_mass, - si->feedback_data.total_mass_kicked * - fb_props->mass_to_solar_mass, - si->feedback_data.total_mass_kicked / - si->mass, - 1.f/si->birth_scale_factor - 1.f, - galaxy_mstar * fb_props->mass_to_solar_mass, - galaxy_ssfr / fb_props->time_to_yr, - pj->id, - fabs(wind_velocity) * velocity_convert, - prefactor * dir[0] * velocity_convert, - prefactor * dir[1] * velocity_convert, - prefactor * dir[2] * velocity_convert, - pj->h * length_convert, - pj->x[0] * length_convert, - pj->x[1] * length_convert, - pj->x[2] * length_convert, - xpj->v_full[0] * velocity_convert, - xpj->v_full[1] * velocity_convert, - xpj->v_full[2] * velocity_convert, - hydro_get_comoving_internal_energy(pj, xpj) * u_convert, - pj->rho * rho_convert, - pj->feedback_data.decoupling_delay_time * fb_props->time_to_Myr, - pj->feedback_data.number_of_times_decoupled, - pj->chemistry_data.metal_mass_fraction_total); + const float galaxy_ssfr = si->galaxy_data.specific_sfr; + + printf( + "WIND_LOG z=%.5f sid=%lld mlaunch=%g mkicked=%g Nkicked=%g zbirth=%g " + "M*=%g sSFR=%g pid=%lld vw=%g vwx=%g vwy=%g vwz=%g h=%g x=%g " + "y=%g z=%g vx=%g vy=%g vz=%g " + "T=%g nH=%g tdel=%g Ndec=%d fZ=%g\n", + cosmo->z, si->id, + si->feedback_data.mass_to_launch * fb_props->mass_to_solar_mass, + si->feedback_data.total_mass_kicked * fb_props->mass_to_solar_mass, + si->feedback_data.total_mass_kicked / si->mass, + 1.f / si->birth_scale_factor - 1.f, + galaxy_mstar * fb_props->mass_to_solar_mass, + galaxy_ssfr / fb_props->time_to_yr, pj->id, + fabs(wind_velocity) * velocity_convert, + prefactor * dir[0] * velocity_convert, + prefactor * dir[1] * velocity_convert, + prefactor * dir[2] * velocity_convert, pj->h * length_convert, + pj->x[0] * length_convert, pj->x[1] * length_convert, + pj->x[2] * length_convert, xpj->v_full[0] * velocity_convert, + xpj->v_full[1] * velocity_convert, xpj->v_full[2] * velocity_convert, + hydro_get_comoving_internal_energy(pj, xpj) * u_convert, + pj->rho * rho_convert, + pj->feedback_data.decoupling_delay_time * fb_props->time_to_Myr, + pj->feedback_data.number_of_times_decoupled, + pj->chemistry_data.metal_mass_fraction_total); #endif } } @@ -555,12 +528,11 @@ feedback_do_chemical_enrichment_of_gas_around_star( const float r2, const float dx[3], const float hi, const float hj, const struct spart *si, struct part *pj, struct xpart *xpj, const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const struct feedback_props *fb_props, - const integertime_t ti_current) { + const struct feedback_props *fb_props, const integertime_t ti_current) { /* Nothing to distribute */ - if (si->feedback_data.mass <= 0.f || - si->feedback_data.kernel_wt_sum <= 0.f) return; + if (si->feedback_data.mass <= 0.f || si->feedback_data.kernel_wt_sum <= 0.f) + return; /* Gas particle density */ const float rho_j = hydro_get_comoving_density(pj); @@ -587,11 +559,11 @@ feedback_do_chemical_enrichment_of_gas_around_star( "Omega_frac=%e count since last enrich=%d kernel_wt_sum=%g " "wi=%g rho_j=%g", si->id, Omega_frac, si->count_since_last_enrichment, - si->feedback_data.kernel_wt_sum, wi , rho_j); + si->feedback_data.kernel_wt_sum, wi, rho_j); if (Omega_frac < 0.f || (Omega_frac > 1.01f && ui < 1.f)) { error("Omega_frac negative or too large! aborting"); } - + Omega_frac = fmin(Omega_frac, 1.f); } @@ -600,9 +572,8 @@ feedback_do_chemical_enrichment_of_gas_around_star( /* Update particle mass */ double delta_mass = si->feedback_data.mass * Omega_frac; double new_mass = current_mass + delta_mass; - const double max_new_mass = - current_mass * fb_props->max_mass_increase_factor; - + const double max_new_mass = current_mass * fb_props->max_mass_increase_factor; + if (new_mass > max_new_mass) { /* Count for logging in the snapshot. */ pj->feedback_data.mass_limiter_count++; @@ -615,8 +586,7 @@ feedback_do_chemical_enrichment_of_gas_around_star( #ifdef KIARA_DEBUG_CHECKS warning("New mass %g exceeds maximum %g for particle id=%lld --- limiting!", - new_mass, max_new_mass, - pj->id); + new_mass, max_new_mass, pj->id); #endif } @@ -650,33 +620,35 @@ feedback_do_chemical_enrichment_of_gas_around_star( /* Count for logging in the snapshot. */ pj->feedback_data.heating_limiter_count++; - injected_energy = max_new_u_phys * new_mass - current_u_phys * current_mass; + injected_energy = + max_new_u_phys * new_mass - current_u_phys * current_mass; /* Make sure the injected energy doesn't decrease */ if (injected_energy < 0.) injected_energy = 0.; #ifdef KIARA_DEBUG_CHECKS - warning("Injected energy %g exceeds maximum %g for particle id=%lld" - " --- limiting!", - new_u_phys, max_new_u_phys, pj->id); + warning( + "Injected energy %g exceeds maximum %g for particle id=%lld" + " --- limiting!", + new_u_phys, max_new_u_phys, pj->id); #endif } - const double new_thermal_energy = - current_thermal_energy + injected_energy; + const double new_thermal_energy = current_thermal_energy + injected_energy; /* Update after momentum conservation and limiting */ new_u_phys = new_thermal_energy * new_mass_inv; - /* Do we want to move things off of the ISM if there is sufficient heating? */ + /* Do we want to move things off of the ISM if there is sufficient heating? + */ if (fb_props->SNIa_add_heat_to_ISM) { - if (pj->cooling_data.subgrid_temp > 0.f && + if (pj->cooling_data.subgrid_temp > 0.f && pj->cooling_data.subgrid_fcold > 0.f) { - /* 0.8125 is mu for a fully neutral gas with XH=0.75; - * approximate but good enough */ - const double u_cold_phys = + /* 0.8125 is mu for a fully neutral gas with XH=0.75; + * approximate but good enough */ + const double u_cold_phys = 0.8125 * pj->cooling_data.subgrid_temp * fb_props->temp_to_u_factor; const double delta_u_ISM_phys = current_u_phys - u_cold_phys; @@ -686,14 +658,13 @@ feedback_do_chemical_enrichment_of_gas_around_star( /* Use extra heat to move off of the ISM */ if (du_phys > 0. && delta_u_ISM_phys >= 0.) { - const double u_phys_tol = + const double u_phys_tol = fb_props->SNIa_add_heat_to_ISM_tolerance * current_u_phys; if (delta_u_ISM_phys > u_phys_tol) { f_evap = du_phys / delta_u_ISM_phys; f_evap = min(f_evap, 1.0); - } - else { + } else { f_evap = 1.0; } @@ -701,8 +672,7 @@ feedback_do_chemical_enrichment_of_gas_around_star( if (f_evap > 0.) { pj->cooling_data.subgrid_fcold *= 1. - f_evap; - const double u_remaining_phys = - du_phys - f_evap * delta_u_ISM_phys; + const double u_remaining_phys = du_phys - f_evap * delta_u_ISM_phys; new_u_phys = current_u_phys + max(u_remaining_phys, 0.); /* Limit internal energy increase here as well */ @@ -713,7 +683,7 @@ feedback_do_chemical_enrichment_of_gas_around_star( if (pj->cooling_data.subgrid_fcold <= 0.f) { pj->cooling_data.subgrid_temp = 0.f; - pj->cooling_data.subgrid_dens = + pj->cooling_data.subgrid_dens = hydro_get_physical_density(pj, cosmo); pj->cooling_data.subgrid_fcold = 0.f; } @@ -724,8 +694,8 @@ feedback_do_chemical_enrichment_of_gas_around_star( hydro_set_physical_internal_energy(pj, xpj, cosmo, new_u_phys); hydro_set_drifted_physical_internal_energy(pj, cosmo, /*pfloor=*/NULL, - new_u_phys); - } /* si->feedback_data.energy > 0.f */ + new_u_phys); + } /* si->feedback_data.energy > 0.f */ /* ------ Handle metal injection from SN explosions ------ */ @@ -750,33 +720,35 @@ feedback_do_chemical_enrichment_of_gas_around_star( } /* Make sure that X + Y + Z = 1 */ - const float Y_He = + const float Y_He = pj->chemistry_data.metal_mass_fraction[chemistry_element_He]; const float Z = pj->chemistry_data.metal_mass_fraction_total; const float X_H = 1.f - Y_He - Z; if (X_H < 0.f || X_H > 1.f) { for (int elem = 0; elem < chemistry_element_count; elem++) { - warning("\telem[%d] is %g", - elem, pj->chemistry_data.metal_mass_fraction[elem]); + warning("\telem[%d] is %g", elem, + pj->chemistry_data.metal_mass_fraction[elem]); } - error("Hydrogen fraction exeeds unity or is negative for" - " particle id=%lld due to stellar feedback.", pj->id); + error( + "Hydrogen fraction exeeds unity or is negative for" + " particle id=%lld due to stellar feedback.", + pj->id); } pj->chemistry_data.metal_mass_fraction[chemistry_element_H] = X_H; - /* Compute kernel-smoothed contribution to number of SNe going off + /* Compute kernel-smoothed contribution to number of SNe going off * this timestep */ - pj->feedback_data.SNe_ThisTimeStep += + pj->feedback_data.SNe_ThisTimeStep += si->feedback_data.SNe_ThisTimeStep * Omega_frac; - pj->feedback_data.SNe_ThisTimeStep = + pj->feedback_data.SNe_ThisTimeStep = fmax(pj->feedback_data.SNe_ThisTimeStep, 0.); /* Spread dust ejecta to gas */ - for (int elem = chemistry_element_He; - elem < chemistry_element_count; elem++) { + for (int elem = chemistry_element_He; elem < chemistry_element_count; + elem++) { const double current_dust_mass = pj->cooling_data.dust_mass_fraction[elem] * pj->cooling_data.dust_mass; const double delta_dust_mass = @@ -789,8 +761,8 @@ feedback_do_chemical_enrichment_of_gas_around_star( /* Sum up each element to get total dust mass */ pj->cooling_data.dust_mass = 0.; - for (int elem = chemistry_element_He; - elem < chemistry_element_count; elem++) { + for (int elem = chemistry_element_He; elem < chemistry_element_count; + elem++) { pj->cooling_data.dust_mass += pj->cooling_data.dust_mass_fraction[elem]; } @@ -798,34 +770,30 @@ feedback_do_chemical_enrichment_of_gas_around_star( const double dust_mass_inv = 1. / pj->cooling_data.dust_mass; /* Divide by new dust mass to get the fractions */ - for (int elem = chemistry_element_He; - elem < chemistry_element_count; elem++) { + for (int elem = chemistry_element_He; elem < chemistry_element_count; + elem++) { pj->cooling_data.dust_mass_fraction[elem] *= dust_mass_inv; } /* Check for inconsistency */ if (pj->cooling_data.dust_mass > pj->mass) { - for (int elem = chemistry_element_He; - elem < chemistry_element_count; elem++) { - message("DUST EXCEEDS MASS elem=%d md=%g delta=%g \n", - elem, - pj->cooling_data.dust_mass_fraction[elem] * + for (int elem = chemistry_element_He; elem < chemistry_element_count; + elem++) { + message("DUST EXCEEDS MASS elem=%d md=%g delta=%g \n", elem, + pj->cooling_data.dust_mass_fraction[elem] * pj->cooling_data.dust_mass, si->feedback_data.delta_dust_mass[elem] * Omega_frac); } - error("DUST EXCEEDS MASS mgas=%g mdust=%g\n", - pj->mass, + error("DUST EXCEEDS MASS mgas=%g mdust=%g\n", pj->mass, pj->cooling_data.dust_mass); } - } - else { + } else { /* For some reason the dust mass is zero or negative */ for (int elem = 0; elem < chemistry_element_count; elem++) { pj->cooling_data.dust_mass_fraction[elem] = 0.f; } } - } /** @@ -852,43 +820,47 @@ runner_iact_nonsym_feedback_apply( const struct feedback_props *fb_props, const integertime_t ti_current) { /* Ignore decoupled particles */ - //if (pj->decoupled) return; - + // if (pj->decoupled) return; + /* Do chemical enrichment of gas, metals and dust from star */ feedback_do_chemical_enrichment_of_gas_around_star( - r2, dx, hi, hj, si, pj, xpj, cosmo, hydro_props, - fb_props, ti_current); + r2, dx, hi, hj, si, pj, xpj, cosmo, hydro_props, fb_props, ti_current); /* Do kinetic wind feedback */ feedback_kick_gas_around_star(si, pj, xpj, cosmo, fb_props, ti_current); #if COOLING_GRACKLE_MODE >= 2 - /* NOT USED: Compute G0 contribution from star to the gas particle in Habing units of - * 1.6e-3 erg/s/cm^2. Note that this value includes the 4*pi geometric factor + /* NOT USED: Compute G0 contribution from star to the gas particle in Habing + units of + * 1.6e-3 erg/s/cm^2. Note that this value includes the 4*pi geometric factor if (0) { - const float length_to_physical_cm = cosmo->a * fb_props->length_to_kpc * 3.08567758e21f; - // Compute a softened distance from star to gas particle - const double r2_in_cm = (r2 + 0.01*hi*hi) * length_to_physical_cm * length_to_physical_cm; - const double r_in_cm = sqrt(r2_in_cm); - + const float length_to_physical_cm = cosmo->a * fb_props->length_to_kpc + * 3.08567758e21f; + // Compute a softened distance from star to gas particle + const double r2_in_cm = (r2 + 0.01*hi*hi) * length_to_physical_cm * + length_to_physical_cm; const double r_in_cm = sqrt(r2_in_cm); + // Compute self-shielding from H2, from Schauer et al. 2015 eq 8,9 - // H attenuation factor - const double NH_cgs = hydro_get_physical_density(pj, cosmo) * fb_props->rho_to_n_cgs * r_in_cm; - const double xH = NH_cgs / 2.85e23; - const double fH_shield = pow(1.f+xH,-1.62) * exp(-0.149*xH); + // H attenuation factor + const double NH_cgs = hydro_get_physical_density(pj, cosmo) * + fb_props->rho_to_n_cgs * r_in_cm; const double xH = NH_cgs / 2.85e23; const + double fH_shield = pow(1.f+xH,-1.62) * exp(-0.149*xH); // H2 attenuation factor const double NH2_cgs = pj->sf_data.H2_fraction * NH_cgs; - const double DH2_cgs = 1.e-5 * sqrt(2.*1.38e-16*cooling_get_subgrid_temperature(pj, xpj) / 3.346e-24); - const double xH2 = NH2_cgs / 8.465e13; - const double fH2_shield = 0.9379/pow(1.f+xH2/DH2_cgs,1.879) + 0.03465/pow(1.f+xH2,0.473) * exp(-2.293e-4*sqrt(1+xH2)); - //message("G0 shield: r=%g xH2=%g xH=%g fH2=%g fH=%g\n",r_in_cm/3.086e21,xH2,xH,fH2_shield,fH_shield); - - if (si->feedback_data.lum_habing > -10.) { - pj->chemistry_data.G0 += fH2_shield * fH_shield * pow(10.,si->feedback_data.lum_habing) / (1.6e-3 * r2_in_cm); + const double DH2_cgs = 1.e-5 * + sqrt(2.*1.38e-16*cooling_get_subgrid_temperature(pj, xpj) / 3.346e-24); const + double xH2 = NH2_cgs / 8.465e13; const double fH2_shield = + 0.9379/pow(1.f+xH2/DH2_cgs,1.879) + 0.03465/pow(1.f+xH2,0.473) * + exp(-2.293e-4*sqrt(1+xH2)); + //message("G0 shield: r=%g xH2=%g xH=%g fH2=%g + fH=%g\n",r_in_cm/3.086e21,xH2,xH,fH2_shield,fH_shield); + + if (si->feedback_data.lum_habing > -10.) { + pj->chemistry_data.G0 += fH2_shield * fH_shield * + pow(10.,si->feedback_data.lum_habing) / (1.6e-3 * r2_in_cm); } }*/ #endif - } #endif /* SWIFT_KIARA_FEEDBACK_IACT_H */ diff --git a/src/feedback/KIARA/feedback_properties.h b/src/feedback/KIARA/feedback_properties.h index 8f48c997c9..fa13be5407 100644 --- a/src/feedback/KIARA/feedback_properties.h +++ b/src/feedback/KIARA/feedback_properties.h @@ -2,7 +2,7 @@ * This file is part of SWIFT. * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2022 Doug Rennehan (douglas.rennehan@gmail.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 @@ -43,8 +43,10 @@ #define SWR_idx(A, B) ((A) * NM + B) #define SN2E_idx(A, B, C) ((A) * NZSN * NM + (B) * NM + C) -#define LINEAR_INTERPOLATION(x1, y1, x2, y2, x) (((y2 - y1)/(x2 - x1))*(x - x1) + y1) -#define LOG_INTERPOLATION(x, x2, x1) ((log10(x2) - log10(x))/(log10(x2) - log10(x1))) +#define LINEAR_INTERPOLATION(x1, y1, x2, y2, x) \ + (((y2 - y1) / (x2 - x1)) * (x - x1) + y1) +#define LOG_INTERPOLATION(x, x2, x1) \ + ((log10(x2) - log10(x)) / (log10(x2) - log10(x1))) enum kiara_metal_boosting { kiara_metal_boosting_off, @@ -95,7 +97,6 @@ enum chem5_element { chem5_NXSN }; - /** * @brief Stores the yield tables */ @@ -131,7 +132,8 @@ struct feedback_props { /*! Are we depositing energy from SNIa directly from Chem5? */ int with_SNIa_energy_from_chem5; - /*! If time since last chemical enrichment is above this value times the current stellar age, recompute */ + /*! If time since last chemical enrichment is above this value times the + * current stellar age, recompute */ float stellar_enrichment_frequency; /* ------------ Yield tables ----------------- */ @@ -204,7 +206,7 @@ struct feedback_props { /*! Normalization for the mass loading curve */ float FIRE_eta_normalization; - /*! The location (in internal mass units) where the break in the + /*! The location (in internal mass units) where the break in the * mass loading curve occurs */ float FIRE_eta_break; @@ -219,10 +221,10 @@ struct feedback_props { /*! The wind speed of stellar feedback suppressed above this z */ float wind_velocity_suppression_redshift; - + /*! The mass loading factor of stellar feedback suppressed above this z */ float wind_eta_suppression_redshift; - + /*! Maxiumum multiple of SNII energy that is available to launch winds */ float SNII_energy_multiplier; @@ -235,7 +237,7 @@ struct feedback_props { /*! For KIARA, weight the launch probability by the particles's SFR (0/1) */ int use_sfr_weighted_launch; - /*! Flag to set feedback boost at low Z: + /*! Flag to set feedback boost at low Z: * 0=Off, 1=vwind boost, 2=eta boost, 3=both boost */ int metal_dependent_vwind; @@ -247,7 +249,7 @@ struct feedback_props { /*! Floor for the eta suppression factor */ float eta_suppression_factor_floor; - + /*! Direction to launch wind: 0=random, 1=v x a, 2=outwards */ float kick_direction_flag; @@ -291,7 +293,7 @@ struct feedback_props { float max_energy_increase_factor; /*! Minimum internal energy loss factor from momentum exchange */ - //float min_energy_decrease_factor; + // float min_energy_decrease_factor; /*! Flag to add heat to destroy cold gas in the ISM from SNIa gas */ int SNIa_add_heat_to_ISM; @@ -360,7 +362,7 @@ struct feedback_props { #if COOLING_GRACKLE_MODE >= 2 /* ------------ Dust Efficiency Tables --------------- */ - + /*! dust condensation efficiency for C/O>1 */ double delta_AGBCOG1[chemistry_element_count]; @@ -373,7 +375,7 @@ struct feedback_props { /*! max fraction of metals locked into dust */ float max_dust_fraction; - /*! Rolling value for number of SNe is smoothed over this timescale + /*! Rolling value for number of SNe is smoothed over this timescale * in Myr (0 for instantaneous) */ float SNe_smoothing_time_in_Myr; #endif diff --git a/src/feedback/KIARA/feedback_struct.h b/src/feedback/KIARA/feedback_struct.h index 85a05a121f..168917009d 100644 --- a/src/feedback/KIARA/feedback_struct.h +++ b/src/feedback/KIARA/feedback_struct.h @@ -22,7 +22,6 @@ #include "chemistry_struct.h" - /** * @brief Feedback fields carried by each hydro particles */ @@ -38,13 +37,13 @@ struct feedback_part_data { /*! The ID of the star particle that is kicking this particle */ long long kick_id; - + /*! The direction vector for wind kicks */ float wind_direction[3]; - + /*! The number of times the SF mass limiter was applied */ int mass_limiter_count; - + /*! The number of times the SF heat limiter was applied */ int heating_limiter_count; @@ -75,7 +74,7 @@ struct feedback_spart_data { /*! Total mass (unweighted) of neighbouring gas particles eligible for wind */ float wind_ngb_mass; - + /*! Mass released */ double mass; @@ -93,13 +92,13 @@ struct feedback_spart_data { /*! Number of particles launched over the stars' lifetime */ int N_launched; - + /*! Total mass left to be ejected in winds by this star */ float mass_to_launch; /*! Total mass kicked over the stars' lifetime */ float total_mass_kicked; - + /*! Kick velocity for gas launched by this star COMOVING */ float wind_velocity; @@ -113,7 +112,8 @@ struct feedback_spart_data { /*! Number of SNe (of any type) going off within star during this step */ double SNe_ThisTimeStep; - /*! Cumulative number of SNe that have gone off in this star from chem5 (for debugging) */ + /*! Cumulative number of SNe that have gone off in this star from chem5 (for + * debugging) */ double SNe_Total; /*! Total dust mass change for each element */ diff --git a/src/fof.c b/src/fof.c index 0cbd724322..4d33abba10 100644 --- a/src/fof.c +++ b/src/fof.c @@ -2744,19 +2744,20 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, struct part *restrict p = &(s->parts[-gp->id_or_neg_offset]); p->galaxy_data.stellar_mass = stellar_mass[index]; p->galaxy_data.gas_mass = gas_mass[index]; - p->galaxy_data.specific_sfr = star_formation_rate[index] / stellar_mass[index]; - } - else if (gp->type == swift_type_stars) { + p->galaxy_data.specific_sfr = + star_formation_rate[index] / stellar_mass[index]; + } else if (gp->type == swift_type_stars) { struct spart *restrict sp = &(s->sparts[-gp->id_or_neg_offset]); sp->galaxy_data.stellar_mass = stellar_mass[index]; sp->galaxy_data.gas_mass = gas_mass[index]; - sp->galaxy_data.specific_sfr = star_formation_rate[index] / stellar_mass[index]; - } - else if (gp->type == swift_type_black_hole) { + sp->galaxy_data.specific_sfr = + star_formation_rate[index] / stellar_mass[index]; + } else if (gp->type == swift_type_black_hole) { struct bpart *restrict bp = &(s->bparts[-gp->id_or_neg_offset]); bp->galaxy_data.stellar_mass = stellar_mass[index]; bp->galaxy_data.gas_mass = gas_mass[index]; - bp->galaxy_data.specific_sfr = star_formation_rate[index] / stellar_mass[index]; + bp->galaxy_data.specific_sfr = + star_formation_rate[index] / stellar_mass[index]; } #endif } diff --git a/src/runner_doiact_functions_hydro.h b/src/runner_doiact_functions_hydro.h index 941c537011..e01dbb1965 100644 --- a/src/runner_doiact_functions_hydro.h +++ b/src/runner_doiact_functions_hydro.h @@ -1883,7 +1883,7 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) struct xpart *xpj = &xparts_j[sort_j[pjd].i]; #endif - const char depth_j = pj->depth_h; + const char depth_j = pj->depth_h; /* Skip inhibited particles. */ if (part_is_inhibited(pj, e)) continue; @@ -1968,8 +1968,8 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, time_base, - t_current, cosmo, with_cosmology, + runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, + time_base, t_current, cosmo, with_cosmology, e->physical_constants, e->chemistry); #endif } else { @@ -2226,8 +2226,8 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_diffusion(r2, dx, hj, hi, pj, pi, xpj, xpi, a, H, time_base, - t_current, cosmo, with_cosmology, + runner_iact_diffusion(r2, dx, hj, hi, pj, pi, xpj, xpi, a, H, + time_base, t_current, cosmo, with_cosmology, e->physical_constants, e->chemistry); #endif } else { @@ -2456,8 +2456,8 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_nonsym_timebin(r2, dx, hj, hi, pj, pi, a, H); runner_iact_nonsym_rt_timebin(r2, dx, hj, hi, pj, pi, a, H); - runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, time_base, - t_current, cosmo, with_cosmology, + runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, + time_base, t_current, cosmo, with_cosmology, e->physical_constants, chem_data); #endif } @@ -2482,7 +2482,7 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) struct xpart *restrict xpj = &xparts[pjd]; #endif - const char depth_j = pj->depth_h; + const char depth_j = pj->depth_h; /* Skip inhibited particles. */ if (part_is_inhibited(pj, e)) continue; @@ -2543,8 +2543,8 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, time_base, - t_current, cosmo, with_cosmology, + runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, + time_base, t_current, cosmo, with_cosmology, e->physical_constants, chem_data); #endif } else if (doi) { @@ -2726,7 +2726,7 @@ void DOSELF2(struct runner *r, const struct cell *c, const int limit_min_h, struct part *restrict pi = &parts[pid]; #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) struct xpart *restrict xpi = &xparts[pid]; -#endif +#endif const char depth_i = pi->depth_h; /* Skip inhibited particles. */ @@ -2878,8 +2878,8 @@ void DOSELF2(struct runner *r, const struct cell *c, const int limit_min_h, #if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_iact_timebin(r2, dx, hi, hj, pi, pj, a, H); runner_iact_rt_timebin(r2, dx, hi, hj, pi, pj, a, H); - runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, time_base, - t_current, cosmo, with_cosmology, + runner_iact_diffusion(r2, dx, hi, hj, pi, pj, xpi, xpj, a, H, + time_base, t_current, cosmo, with_cosmology, e->physical_constants, chem_data); #endif } else if (doi) { diff --git a/src/runner_doiact_functions_stars.h b/src/runner_doiact_functions_stars.h index 5c3465e5fa..10cb237dec 100644 --- a/src/runner_doiact_functions_stars.h +++ b/src/runner_doiact_functions_stars.h @@ -546,7 +546,8 @@ void DO_SYM_PAIR1_STARS(struct runner *r, const struct cell *restrict ci, ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP1) runner_iact_nonsym_feedback_prep1(r2, dx, hi, hj, spi, pj, NULL, - cosmo, e->feedback_props, ti_current); + cosmo, e->feedback_props, + ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP2) runner_iact_nonsym_feedback_prep2(r2, dx, hi, hj, spi, pj, NULL, cosmo, ti_current); @@ -710,7 +711,8 @@ void DO_SYM_PAIR1_STARS(struct runner *r, const struct cell *restrict ci, ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP1) runner_iact_nonsym_feedback_prep1(r2, dx, hj, hi, spj, pi, NULL, - cosmo, e->feedback_props, ti_current); + cosmo, e->feedback_props, + ti_current); #elif (FUNCTION_TASK_LOOP == TASK_LOOP_STARS_PREP2) runner_iact_nonsym_feedback_prep2(r2, dx, hj, hi, spj, pi, NULL, cosmo, ti_current); diff --git a/src/runner_time_integration.c b/src/runner_time_integration.c index 58f165d726..61d037109b 100644 --- a/src/runner_time_integration.c +++ b/src/runner_time_integration.c @@ -167,9 +167,8 @@ void runner_do_kick1(struct runner *r, struct cell *c, const int timer) { ti_current); #endif - /* Rennehan: Here we recouple the wind particles */ - feedback_recouple_part(p, xp, e, with_cosmology, cosmo, - feedback_props); + /* Rennehan: Here we recouple the wind particles */ + feedback_recouple_part(p, xp, e, with_cosmology, cosmo, feedback_props); /* Time intervals for this half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( diff --git a/src/star_formation/KIARA/star_formation.h b/src/star_formation/KIARA/star_formation.h index 4bc62f4f39..fcfc7b1809 100644 --- a/src/star_formation/KIARA/star_formation.h +++ b/src/star_formation/KIARA/star_formation.h @@ -29,6 +29,7 @@ #include "entropy_floor.h" #include "equation_of_state.h" #include "exp10.h" +#include "fof.h" #include "hydro.h" #include "parser.h" #include "part.h" @@ -36,7 +37,6 @@ #include "random.h" #include "stars.h" #include "units.h" -#include "fof.h" #define star_formation_need_update_dx_max 0 @@ -58,7 +58,8 @@ enum star_formation_H2_model { }; enum star_formation_SF_model { - /*z > 6) slope = FIRE_eta_lower_slope_EOR; @@ -181,16 +176,13 @@ __attribute__((always_inline)) INLINE static float feedback_mass_loading_factor( slope = FIRE_eta_upper_slope; } - float eta = - FIRE_eta_normalization * powf(m_star / FIRE_eta_break, slope); + float eta = FIRE_eta_normalization * powf(m_star / FIRE_eta_break, slope); - const float a_suppress_inv = - (1.f + fabs(wind_eta_suppression_redshift)); + const float a_suppress_inv = (1.f + fabs(wind_eta_suppression_redshift)); if (wind_eta_suppression_redshift > 0 && - cosmo->z > wind_eta_suppression_redshift) { + cosmo->z > wind_eta_suppression_redshift) { eta *= cosmo->a * cosmo->a * a_suppress_inv * a_suppress_inv; - } - else if (wind_eta_suppression_redshift < 0) { + } else if (wind_eta_suppression_redshift < 0) { eta *= expf(-powf(cosmo->a * a_suppress_inv, -3.f)); } @@ -211,11 +203,11 @@ __attribute__((always_inline)) INLINE static float feedback_mass_loading_factor( * @param entropy_floor_props The entropy floor assumed in this run. */ INLINE static int star_formation_is_star_forming_subgrid( - const struct part* p, const struct xpart* xp, - const struct star_formation* starform, const struct phys_const* phys_const, - const struct cosmology* cosmo, const struct hydro_props* hydro_props, - const struct unit_system* us, const struct cooling_function_data* cooling, - const struct entropy_floor_properties* entropy_floor_props) { + const struct part *p, const struct xpart *xp, + const struct star_formation *starform, const struct phys_const *phys_const, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct unit_system *us, const struct cooling_function_data *cooling, + const struct entropy_floor_properties *entropy_floor_props) { const double number_density_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); @@ -238,12 +230,12 @@ INLINE static int star_formation_is_star_forming_subgrid( * * Recall that particles above the EoS have T_sub = T and rho_sub = rho. */ - return ( (subgrid_T_cgs < starform->subgrid_thresh.T_threshold) && - (subgrid_n_H_cgs > starform->subgrid_thresh.nH_threshold) ); + return ((subgrid_T_cgs < starform->subgrid_thresh.T_threshold) && + (subgrid_n_H_cgs > starform->subgrid_thresh.nH_threshold)); } /** - * @brief Calculate if the gas particle satisfies the conditions for star + * @brief Calculate if the gas particle satisfies the conditions for star * formation. * * @param starform the star formation law properties to use. @@ -257,11 +249,11 @@ INLINE static int star_formation_is_star_forming_subgrid( * @param entropy_floor_props The entropy floor assumed in this run. */ INLINE static int star_formation_is_star_forming( - const struct part* p, const struct xpart* xp, - const struct star_formation* starform, const struct phys_const* phys_const, - const struct cosmology* cosmo, const struct hydro_props* hydro_props, - const struct unit_system* us, const struct cooling_function_data* cooling, - const struct entropy_floor_properties* entropy_floor_props) { + const struct part *p, const struct xpart *xp, + const struct star_formation *starform, const struct phys_const *phys_const, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct unit_system *us, const struct cooling_function_data *cooling, + const struct entropy_floor_properties *entropy_floor_props) { /* Decide whether we should form stars or not */ @@ -282,9 +274,9 @@ INLINE static int star_formation_is_star_forming( /* Check overdensity criterion */ if (physical_density < rho_mean_b_times_min_over_den) return 0; - return star_formation_is_star_forming_subgrid( - p, xp, starform, phys_const, cosmo, hydro_props, - us, cooling, entropy_floor_props); + return star_formation_is_star_forming_subgrid(p, xp, starform, phys_const, + cosmo, hydro_props, us, cooling, + entropy_floor_props); } /** @@ -302,13 +294,12 @@ INLINE static int star_formation_is_star_forming( * @param dt_star The time-step of this particle. */ INLINE static void star_formation_compute_SFR_schmidt_law( - struct part* p, struct xpart* xp, - const struct star_formation* starform, const struct phys_const* phys_const, - const struct hydro_props* hydro_props, const struct cosmology* cosmo, - const double dt_star) { + struct part *p, struct xpart *xp, const struct star_formation *starform, + const struct phys_const *phys_const, const struct hydro_props *hydro_props, + const struct cosmology *cosmo, const double dt_star) { /* Mass density of this particle */ - //const float physical_density = cooling_get_subgrid_density(p, xp); + // const float physical_density = cooling_get_subgrid_density(p, xp); const float physical_density = cooling_get_subgrid_density(p, xp); /* Calculate the SFR per gas mass */ @@ -321,7 +312,7 @@ INLINE static void star_formation_compute_SFR_schmidt_law( /** * @brief Compute the star-formation rate of a given particle and store - * it into the #part. The star formation is calculated based on + * it into the #part. The star formation is calculated based on * the model of Wada & Norman 2007, eq. 17 * * @param p #part. @@ -333,10 +324,9 @@ INLINE static void star_formation_compute_SFR_schmidt_law( * @param dt_star The time-step of this particle. */ INLINE static void star_formation_compute_SFR_wn07( - struct part* p, struct xpart* xp, - const struct star_formation* starform, const struct phys_const* phys_const, - const struct hydro_props* hydro_props, - const struct cosmology* cosmo, const double dt_star) { + struct part *p, struct xpart *xp, const struct star_formation *starform, + const struct phys_const *phys_const, const struct hydro_props *hydro_props, + const struct cosmology *cosmo, const double dt_star) { p->sf_data.SFR = 0.f; /* Mean density of the gas described by a lognormal PDF (i.e. dense gas). */ @@ -351,61 +341,54 @@ INLINE static void star_formation_compute_SFR_wn07( /* Density is too low, so no SF */ if (rho_V <= 1.001 * rho_0) return; - /* Calculate SFR efficiency, which WN07 suggests should - * scale from 0.1 for starbursts to 0.001 for normal galaxies + /* Calculate SFR efficiency, which WN07 suggests should + * scale from 0.1 for starbursts to 0.001 for normal galaxies */ double epsc = 0.01; /* if it's not in a galaxy, assume it's a small proto-galaxy so starburst */ if (galaxy_mstar == 0.f) { epsc = 0.1; - } - else if (starform->lognormal.wn07_epsc_method == 0) { /* constant value */ + } else if (starform->lognormal.wn07_epsc_method == 0) { /* constant value */ epsc = 0.01; } /* deviation from all-galaxies main sequence taken from Koprowski+24 */ - else if (starform->lognormal.wn07_epsc_method == 1 || - starform->lognormal.wn07_epsc_method == 2) { - const double mstar = - galaxy_mstar * starform->lognormal.to_solar_mass; + else if (starform->lognormal.wn07_epsc_method == 1 || + starform->lognormal.wn07_epsc_method == 2) { + const double mstar = galaxy_mstar * starform->lognormal.to_solar_mass; const double sfrmax_data = pow(10., 3.69 - 3.81 * exp(-0.47 * cosmo->z)); const double M0_data = pow(10., 11.91 - 2.48 * exp(-0.44 * cosmo->z)); const double sfr_data = sfrmax_data / (1. + (M0_data / mstar)); - const double sfr_msun_per_yr = galaxy_sfr * - starform->lognormal.to_msun_per_yr; + const double sfr_msun_per_yr = + galaxy_sfr * starform->lognormal.to_msun_per_yr; if (starform->lognormal.wn07_epsc_method == 1) { epsc = 0.01 * sfr_msun_per_yr / sfr_data; - } - else { + } else { epsc = 0.01 * sqrtf(sfr_msun_per_yr / sfr_data); } } /* deviation from SF-galaxies main sequence data taken from Koprowski+24 */ - else if (starform->lognormal.wn07_epsc_method == 3 || - starform->lognormal.wn07_epsc_method == 4) { - const double mstar = - galaxy_mstar * starform->lognormal.to_solar_mass; + else if (starform->lognormal.wn07_epsc_method == 3 || + starform->lognormal.wn07_epsc_method == 4) { + const double mstar = galaxy_mstar * starform->lognormal.to_solar_mass; const double sfrmax_data = pow(10., 3.47 - 3.13 * exp(-0.56 * cosmo->z)); const double M0_data = pow(10., 11.69 - 1.66 * exp(-0.53 * cosmo->z)); const double sfr_data = sfrmax_data / (1. + (M0_data / mstar)); - const double sfr_msun_per_yr = galaxy_sfr * - starform->lognormal.to_msun_per_yr; + const double sfr_msun_per_yr = + galaxy_sfr * starform->lognormal.to_msun_per_yr; epsc = 0.01 * sqrt(sfr_msun_per_yr / sfr_data); } /* based on direct scaling with sSFR */ else if (starform->lognormal.wn07_epsc_method == 5) { - epsc = min( - galaxy_ssfr * starform->lognormal.time_to_year_inverse * 1.e7, - 0.1 - ); + epsc = + min(galaxy_ssfr * starform->lognormal.time_to_year_inverse * 1.e7, 0.1); } /* Scale with galaxy SFR instead of SSFR */ else if (starform->lognormal.wn07_epsc_method == 6) { - const float sfr_msun_per_yr = galaxy_sfr * - starform->lognormal.to_msun_per_yr; + const float sfr_msun_per_yr = + galaxy_sfr * starform->lognormal.to_msun_per_yr; epsc = min(0.001 * cbrt(sfr_msun_per_yr * 300.), 0.1); epsc = max(epsc, 0.001); - } - else { + } else { error("wn07_epsc_method value of %d not recognised", starform->lognormal.wn07_epsc_method); } @@ -416,22 +399,20 @@ INLINE static void star_formation_compute_SFR_wn07( /* Calculate parameters in WN07 model */ const double sigma = sqrt(2. * log(rho_V / rho_0)); - const double z_num = - log(starform->lognormal.rhocrit / rho_0) - sigma * sigma; + const double z_num = log(starform->lognormal.rhocrit / rho_0) - sigma * sigma; const double z_den = sqrt(2.) * sigma; const double z = z_num / z_den; /* fraction of dense gas */ const double fc = 0.5 * erfc(z); - /* This is the SFR density from eq. 17, except use actual - * density rho_V not estimated density rho_c - */ - const double rhosfr = - epsc * sqrt(phys_const->const_newton_G * rho_V) * fc; + /* This is the SFR density from eq. 17, except use actual + * density rho_V not estimated density rho_c + */ + const double rhosfr = epsc * sqrt(phys_const->const_newton_G * rho_V) * fc; /* multiply by dense gas effective volume to get SFR */ - const double sfr = + const double sfr = rhosfr * (p->cooling_data.subgrid_fcold * hydro_get_mass(p)); /* Multiply by the H2 fraction */ @@ -440,7 +421,7 @@ INLINE static void star_formation_compute_SFR_wn07( /** * @brief Compute the star-formation rate of a given particle and store - * it into the #part. The star formation is calculated by assuming + * it into the #part. The star formation is calculated by assuming * a lognormal density distribution with a mean density given by the * subgrid density, above a critical density threshold that is an * input parameter. Lognormal params based on sims by Wada & Norman 2007. @@ -454,11 +435,9 @@ INLINE static void star_formation_compute_SFR_wn07( * @param dt_star The time-step of this particle. */ INLINE static void star_formation_compute_SFR_lognormal( - struct part* p, struct xpart* xp, - const struct star_formation* starform, const struct phys_const* phys_const, - const struct hydro_props* hydro_props, const struct cosmology* cosmo, - const double dt_star) { - + struct part *p, struct xpart *xp, const struct star_formation *starform, + const struct phys_const *phys_const, const struct hydro_props *hydro_props, + const struct cosmology *cosmo, const double dt_star) { /* Limit for Eq. 12 in Wada+Norman 2007 */ const double rho_limit = 1.001 * starform->lognormal.rho0; @@ -466,25 +445,25 @@ INLINE static void star_formation_compute_SFR_lognormal( /* H2 fraction in the particle */ const double f_H2 = p->sf_data.H2_fraction; - /* Mean density of the gas described by a lognormal PDF + /* Mean density of the gas described by a lognormal PDF * (i.e. subgrid ISM gas) PHYSICAL */ double rho_V = cooling_get_subgrid_density(p, xp); - /* SF cannot occur below characteristic - * density~1 cm^-3 (formulae below give nans) + /* SF cannot occur below characteristic + * density~1 cm^-3 (formulae below give nans) */ if (rho_V < rho_limit || f_H2 <= 0.) { - p->sf_data.SFR = 0.f; - return; + p->sf_data.SFR = 0.f; + return; } - /* Calculate the SFR based on a lognormal density distribution at rho0 with a - * threshold density for star formation rhocrit. sigma comes from WN07 model. + /* Calculate the SFR based on a lognormal density distribution at rho0 with a + * threshold density for star formation rhocrit. sigma comes from WN07 model. */ const double rho_0 = starform->lognormal.rho0; /* Mass-averaged density for cold phase */ const double sigma = sqrt(log(2. * rho_V / rho_0)); - const double z_num = + const double z_num = log(starform->lognormal.rhocrit / rho_0) - (sigma * sigma); const double z_den = sqrt(2.) * sigma; const double z = z_num / z_den; @@ -494,18 +473,15 @@ INLINE static void star_formation_compute_SFR_lognormal( const double rho_phys = hydro_get_physical_density(p, cosmo); - /* Calculate the SFR per gas mass, using lognormal mass fraction above - * rhocrit as efficiency + /* Calculate the SFR per gas mass, using lognormal mass fraction above + * rhocrit as efficiency */ - const double sSFR = - f_c * starform->lognormal.ff_const_inv * sqrt(rho_phys); + const double sSFR = f_c * starform->lognormal.ff_const_inv * sqrt(rho_phys); - const double mass = - f_H2 * p->cooling_data.subgrid_fcold * hydro_get_mass(p); + const double mass = f_H2 * p->cooling_data.subgrid_fcold * hydro_get_mass(p); /* Store the SFR */ p->sf_data.SFR = starform->lognormal.epsilon * sSFR * mass; - } /** @@ -522,10 +498,9 @@ INLINE static void star_formation_compute_SFR_lognormal( * @param dt_star The time-step of this particle. */ INLINE static void star_formation_compute_SFR( - struct part* p, struct xpart* xp, - const struct star_formation* starform, const struct phys_const* phys_const, - const struct hydro_props* hydro_props, - const struct cosmology* cosmo, const double dt_star) { + struct part *p, struct xpart *xp, const struct star_formation *starform, + const struct phys_const *phys_const, const struct hydro_props *hydro_props, + const struct cosmology *cosmo, const double dt_star) { /* Abort early if time-step size is 0 */ if (dt_star == 0.) { @@ -559,20 +534,18 @@ INLINE static void star_formation_compute_SFR( } if (physical_density > 0.f) { - gas_gradrho_mag = sqrtf( - p->rho_gradient[0] * p->rho_gradient[0] + - p->rho_gradient[1] * p->rho_gradient[1] + - p->rho_gradient[2] * p->rho_gradient[2] - ); + gas_gradrho_mag = sqrtf(p->rho_gradient[0] * p->rho_gradient[0] + + p->rho_gradient[1] * p->rho_gradient[1] + + p->rho_gradient[2] * p->rho_gradient[2]); if (gas_gradrho_mag > 0) { gas_sigma = (p->rho * p->rho) / gas_gradrho_mag; /* surface density must be in Msun/pc^2 */ - gas_sigma *= starform->surface_rho_to_Msun_per_parsec2 - * cosmo->a2_inv; + gas_sigma *= + starform->surface_rho_to_Msun_per_parsec2 * cosmo->a2_inv; - /* Lower clumping factor with higher resolution + /* Lower clumping factor with higher resolution (CF = 30 @ ~1 kpc resolution) */ clumping_factor *= starform->clumping_factor_scaling; if (clumping_factor < 1.f) { @@ -580,11 +553,10 @@ INLINE static void star_formation_compute_SFR( } /* chi ~ 1/R ~ 1/clump from KG11 eq. 3 */ - chi = 0.756f * (1.f + 3.1f * powf(gas_Z, 0.365f)) - * (30.f / clumping_factor); - s = logf(1.f + 0.6f * chi + 0.01f * chi * chi) - / (0.0396f * powf(clumping_factor, 2.f / 3.f) - * gas_Z * gas_sigma); + chi = 0.756f * (1.f + 3.1f * powf(gas_Z, 0.365f)) * + (30.f / clumping_factor); + s = logf(1.f + 0.6f * chi + 0.01f * chi * chi) / + (0.0396f * powf(clumping_factor, 2.f / 3.f) * gas_Z * gas_sigma); if (s > 0.f && s < 2.f) { p->sf_data.H2_fraction = 1.f - 0.75f * (s / (1.f + 0.25f * s)); @@ -594,11 +566,11 @@ INLINE static void star_formation_compute_SFR( break; case kiara_star_formation_grackle_model: #if COOLING_GRACKLE_MODE >= 2 - p->sf_data.H2_fraction = p->cooling_data.subgrid_fcold * + p->sf_data.H2_fraction = + p->cooling_data.subgrid_fcold * (xp->cooling_data.H2I_frac + xp->cooling_data.H2II_frac); #else - p->sf_data.H2_fraction = - 1. - xp->cooling_data.HI_frac; + p->sf_data.H2_fraction = 1. - xp->cooling_data.HI_frac; #endif break; default: @@ -610,15 +582,15 @@ INLINE static void star_formation_compute_SFR( switch (starform->SF_model) { case kiara_star_formation_SchmidtLaw: star_formation_compute_SFR_schmidt_law(p, xp, starform, phys_const, - hydro_props, cosmo, dt_star); + hydro_props, cosmo, dt_star); break; case kiara_star_formation_WadaNorman: - star_formation_compute_SFR_wn07(p, xp, starform, phys_const, - hydro_props, cosmo, dt_star); + star_formation_compute_SFR_wn07(p, xp, starform, phys_const, hydro_props, + cosmo, dt_star); break; case kiara_star_formation_lognormal: star_formation_compute_SFR_lognormal(p, xp, starform, phys_const, - hydro_props, cosmo, dt_star); + hydro_props, cosmo, dt_star); break; default: error("Invalid SF model in star formation!!!"); @@ -637,7 +609,7 @@ INLINE static void star_formation_compute_SFR( * (return 0 if the gas particle itself is to be converted) */ INLINE static int star_formation_number_spart_to_spawn( - struct part* p, struct xpart* xp, const struct star_formation* starform) { + struct part *p, struct xpart *xp, const struct star_formation *starform) { return 0; } @@ -653,8 +625,8 @@ INLINE static int star_formation_number_spart_to_spawn( * (This has to be 0 or 1) */ INLINE static int star_formation_number_spart_to_convert( - const struct part* p, const struct xpart* xp, - const struct star_formation* starform) { + const struct part *p, const struct xpart *xp, + const struct star_formation *starform) { return 1; } @@ -674,8 +646,8 @@ INLINE static int star_formation_number_spart_to_convert( * @return 1 if a conversion should be done, 0 otherwise. */ INLINE static int star_formation_should_convert_to_star( - const struct part* p, const struct xpart* xp, - const struct star_formation* starform, const struct engine* e, + const struct part *p, const struct xpart *xp, + const struct star_formation *starform, const struct engine *e, const double dt_star) { /* Calculate the propability of forming a star */ @@ -700,7 +672,7 @@ INLINE static int star_formation_should_convert_to_star( * @return 1 if a new spart needs to be created. */ INLINE static int star_formation_should_spawn_spart( - struct part* p, struct xpart* xp, const struct star_formation* starform) { + struct part *p, struct xpart *xp, const struct star_formation *starform) { return 0; } @@ -714,8 +686,8 @@ INLINE static int star_formation_should_spawn_spart( * @param with_cosmology Are we running with cosmology switched on? */ INLINE static void star_formation_update_part_not_SFR( - struct part* p, struct xpart* xp, const struct engine* e, - const struct star_formation* starform, const int with_cosmology) { + struct part *p, struct xpart *xp, const struct engine *e, + const struct star_formation *starform, const int with_cosmology) { /* Check if it is the first time steps after star formation */ if (p->sf_data.SFR > 0.f) { @@ -784,7 +756,7 @@ INLINE static void star_formation_copy_properties( /* Store the birth temperature in the star particle */ sp->birth_temperature = cooling_get_subgrid_temperature(p, xp); - /* Flag that this particle has not done feedback yet + /* Flag that this particle has not done feedback yet sp->feedback_data.physical_energy_reservoir = 0.; sp->feedback_data.N_launched = 0; sp->feedback_data.mass_to_launch = 0.f; @@ -813,11 +785,11 @@ INLINE static void star_formation_copy_properties( * @param starform the star formation law properties to initialize */ INLINE static void starformation_init_backend( - struct swift_params* parameter_file, const struct phys_const* phys_const, - const struct unit_system* us, const struct hydro_props* hydro_props, - const struct cosmology* cosmo, - const struct entropy_floor_properties* entropy_floor, - struct star_formation* starform) { + struct swift_params *parameter_file, const struct phys_const *phys_const, + const struct unit_system *us, const struct hydro_props *hydro_props, + const struct cosmology *cosmo, + const struct entropy_floor_properties *entropy_floor, + struct star_formation *starform) { /* Get the Gravitational constant */ const double G_newton = phys_const->const_newton_G; @@ -831,56 +803,48 @@ INLINE static void starformation_init_backend( (phys_const->const_parsec * phys_const->const_parsec); starform->surface_rho_to_Msun_per_parsec2 = 1. / Msun_per_pc2; - starform->conv_factor_surface_rho_to_cgs = + starform->conv_factor_surface_rho_to_cgs = rho_to_cgs * units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); /* Read the SF model we are using */ char SF_model[32]; - parser_get_param_string(parameter_file, - "KIARAStarFormation:SF_model", SF_model); + parser_get_param_string(parameter_file, "KIARAStarFormation:SF_model", + SF_model); if (strstr(SF_model, "SchmidtLaw") != NULL) { starform->SF_model = kiara_star_formation_SchmidtLaw; - } - else if (strstr(SF_model, "WadaNorman") != NULL) { + } else if (strstr(SF_model, "WadaNorman") != NULL) { starform->SF_model = kiara_star_formation_WadaNorman; - } - else if (strstr(SF_model, "lognormal") != NULL) { + } else if (strstr(SF_model, "lognormal") != NULL) { starform->SF_model = kiara_star_formation_lognormal; - } - else { + } else { error("Invalid SF model in SF params %s", SF_model); } /* Read the H2 model we are using */ char H2_model[32]; - parser_get_param_string(parameter_file, - "KIARAStarFormation:H2_model", H2_model); + parser_get_param_string(parameter_file, "KIARAStarFormation:H2_model", + H2_model); if (strstr(H2_model, "Thresh") != NULL) { starform->H2_model = kiara_star_formation_density_thresh; - } - else if (strstr(H2_model, "KMT") != NULL) { + } else if (strstr(H2_model, "KMT") != NULL) { starform->H2_model = kiara_star_formation_kmt_model; - } - else if (strstr(H2_model, "Grackle") != NULL) { + } else if (strstr(H2_model, "Grackle") != NULL) { starform->H2_model = kiara_star_formation_grackle_model; - } - else { + } else { error("Invalid H2 model in SF params %s", H2_model); } - /* Read the ISM subgrid clumping factor value at the resolved scale - * (KMT model only) + /* Read the ISM subgrid clumping factor value at the resolved scale + * (KMT model only) */ - starform->clumping_factor_scaling = - parser_get_opt_param_double(parameter_file, - "KIARAStarFormation:clumping_factor_scaling", 30.f); + starform->clumping_factor_scaling = parser_get_opt_param_double( + parameter_file, "KIARAStarFormation:clumping_factor_scaling", 30.f); /* Read the total metal mass fraction of the Sun */ - starform->Z_solar = - parser_get_opt_param_double(parameter_file, - "KIARAStarFormation:Z_solar", 0.0134f); + starform->Z_solar = parser_get_opt_param_double( + parameter_file, "KIARAStarFormation:Z_solar", 0.0134f); /* Read the critical density contrast from the parameter file*/ starform->min_over_den = parser_get_param_double( @@ -888,10 +852,10 @@ INLINE static void starformation_init_backend( /* Read threshold properties */ starform->subgrid_thresh.T_threshold = parser_get_param_double( - parameter_file, "KIARAStarFormation:threshold_temperature_K"); + parameter_file, "KIARAStarFormation:threshold_temperature_K"); starform->subgrid_thresh.nH_threshold = parser_get_param_double( - parameter_file, "KIARAStarFormation:threshold_number_density_H_p_cm3"); + parameter_file, "KIARAStarFormation:threshold_number_density_H_p_cm3"); /* Calculate the ff constant */ const double ff_const = sqrt(3. * M_PI / (32. * G_newton)); @@ -899,25 +863,24 @@ INLINE static void starformation_init_backend( if (starform->SF_model == kiara_star_formation_SchmidtLaw) { /* Get the star formation efficiency */ starform->schmidt_law.sfe = parser_get_param_double( - parameter_file, "KIARAStarFormation:star_formation_efficiency"); + parameter_file, "KIARAStarFormation:star_formation_efficiency"); /* Calculate the constant */ starform->schmidt_law.mdot_const = starform->schmidt_law.sfe / ff_const; - } - else if (starform->SF_model == kiara_star_formation_WadaNorman || - starform->SF_model == kiara_star_formation_lognormal) { + } else if (starform->SF_model == kiara_star_formation_WadaNorman || + starform->SF_model == kiara_star_formation_lognormal) { /* Critical density for SF (in physical cm^-3) for lognormal SF model */ starform->lognormal.ncrit = parser_get_opt_param_double( parameter_file, "KIARAStarFormation:lognormal_critical_density", 1.e3); /* code units */ - starform->lognormal.rhocrit = starform->lognormal.ncrit * 1.673e-24 / - rho_to_cgs; - - /* Set characeristic density of ISM lognormal (Table 1 of Wada+Norman07) - * in code units */ - starform->lognormal.rho0 = + starform->lognormal.rhocrit = + starform->lognormal.ncrit * 1.673e-24 / rho_to_cgs; + + /* Set characeristic density of ISM lognormal (Table 1 of Wada+Norman07) + * in code units */ + starform->lognormal.rho0 = pow(10., -1.5) * 1.98841e33 / (rho_to_cgs * pow(3.08567758149e18, 3.)); /* Like the star formation efficiency but to control for sub-grid factors */ @@ -925,18 +888,18 @@ INLINE static void starformation_init_backend( parameter_file, "KIARAStarFormation:lognormal_epsilon"); /* used to scale epsilon_c (efficiency) in lognormal model to sSFR */ - starform->lognormal.time_to_year_inverse = - (365.25 * 24. * 60. * 60.) / - units_cgs_conversion_factor(us, UNIT_CONV_TIME); + starform->lognormal.time_to_year_inverse = + (365.25 * 24. * 60. * 60.) / + units_cgs_conversion_factor(us, UNIT_CONV_TIME); /* used to scale epsilon_c (efficiency) in lognormal model to SFR */ - starform->lognormal.to_solar_mass = + starform->lognormal.to_solar_mass = units_cgs_conversion_factor(us, UNIT_CONV_MASS) / 1.98841e33; /* used to scale epsilon_c (efficiency) in lognormal model to SFR */ - starform->lognormal.to_msun_per_yr = - units_cgs_conversion_factor(us, UNIT_CONV_SFR) / 1.98841e33 - * (365.25 * 24. * 60. * 60.); + starform->lognormal.to_msun_per_yr = + units_cgs_conversion_factor(us, UNIT_CONV_SFR) / 1.98841e33 * + (365.25 * 24. * 60. * 60.); /* Calculate the constant needed for the free-fall time */ starform->lognormal.ff_const_inv = 1. / ff_const; @@ -946,7 +909,6 @@ INLINE static void starformation_init_backend( parameter_file, "KIARAStarFormation:wn07_epsc_method", 0); } } - } /** @@ -955,38 +917,35 @@ INLINE static void starformation_init_backend( * @param starform the star formation law properties. * */ INLINE static void starformation_print_backend( - const struct star_formation* starform) { + const struct star_formation *starform) { message("Star formation model is KIARA"); - message("Particles are eligible for star formation if their have " - "T < %e K, n_H > %e cm^-3, and overdensity > %e", - starform->subgrid_thresh.T_threshold, - starform->subgrid_thresh.nH_threshold, - starform->min_over_den); + message( + "Particles are eligible for star formation if their have " + "T < %e K, n_H > %e cm^-3, and overdensity > %e", + starform->subgrid_thresh.T_threshold, + starform->subgrid_thresh.nH_threshold, starform->min_over_den); if (starform->SF_model == kiara_star_formation_SchmidtLaw) { - message("Star formation law is a Schmidt law: Star formation " - "efficiency = %e", - starform->schmidt_law.sfe); - } - else if (starform->SF_model == kiara_star_formation_WadaNorman) { - message("Star formation based on Wada+Norman 2007: critical " - "density (code units) = %e", - starform->lognormal.rhocrit); - } - else if (starform->SF_model == kiara_star_formation_lognormal) { - message("Star formation based on lognormal density pdf: " - "critical density (code units) = %e" - "efficiency = %e", - starform->lognormal.rhocrit, - starform->lognormal.epsilon); - } - else { + message( + "Star formation law is a Schmidt law: Star formation " + "efficiency = %e", + starform->schmidt_law.sfe); + } else if (starform->SF_model == kiara_star_formation_WadaNorman) { + message( + "Star formation based on Wada+Norman 2007: critical " + "density (code units) = %e", + starform->lognormal.rhocrit); + } else if (starform->SF_model == kiara_star_formation_lognormal) { + message( + "Star formation based on lognormal density pdf: " + "critical density (code units) = %e" + "efficiency = %e", + starform->lognormal.rhocrit, starform->lognormal.epsilon); + } else { error("Invalid SF model in star formation!!!"); } - - } /** @@ -997,8 +956,8 @@ INLINE static void starformation_print_backend( * @param p The particle. * @param xp The extended data of the particle. */ -INLINE static float star_formation_get_SFR(const struct part* p, - const struct xpart* xp) { +INLINE static float star_formation_get_SFR(const struct part *p, + const struct xpart *xp) { if (p->sf_data.SFR <= 0.) return 0.f; else @@ -1017,8 +976,8 @@ INLINE static float star_formation_get_SFR(const struct part* p, * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void star_formation_end_density( - struct part* p, struct xpart* xp, const struct star_formation* cd, - const struct cosmology* cosmo) {} + struct part *p, struct xpart *xp, const struct star_formation *cd, + const struct cosmology *cosmo) {} /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. @@ -1032,10 +991,9 @@ __attribute__((always_inline)) INLINE static void star_formation_end_density( * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void -star_formation_part_has_no_neighbours(struct part* p, struct xpart* xp, - const struct star_formation* cd, - const struct cosmology* cosmo) { -} +star_formation_part_has_no_neighbours(struct part *p, struct xpart *xp, + const struct star_formation *cd, + const struct cosmology *cosmo) {} /** * @brief Sets the star_formation properties of the (x-)particles to a valid @@ -1048,8 +1006,7 @@ star_formation_part_has_no_neighbours(struct part* p, struct xpart* xp, * @param p Pointer to the particle data. */ __attribute__((always_inline)) INLINE static void star_formation_init_part( - struct part* p, const struct star_formation* data) { -} + struct part *p, const struct star_formation *data) {} /** * @brief Sets the star_formation properties of the (x-)particles to a valid @@ -1063,11 +1020,11 @@ __attribute__((always_inline)) INLINE static void star_formation_init_part( * @param xp Pointer to the extended particle data. */ __attribute__((always_inline)) INLINE static void -star_formation_first_init_part(const struct phys_const* phys_const, - const struct unit_system* us, - const struct cosmology* cosmo, - const struct star_formation* data, - struct part* p, struct xpart* xp) { +star_formation_first_init_part(const struct phys_const *phys_const, + const struct unit_system *us, + const struct cosmology *cosmo, + const struct star_formation *data, + struct part *p, struct xpart *xp) { /* This may need to be updated elsewhere */ p->sf_data.H2_fraction = 0.f; star_formation_init_part(p, data); @@ -1084,7 +1041,7 @@ star_formation_first_init_part(const struct phys_const* phys_const, * @param n The number of pieces to split into. */ __attribute__((always_inline)) INLINE static void star_formation_split_part( - struct part* p, struct xpart* xp, const double n) { + struct part *p, struct xpart *xp, const double n) { if (p->sf_data.SFR > 0.) p->sf_data.SFR /= n; } @@ -1097,8 +1054,8 @@ __attribute__((always_inline)) INLINE static void star_formation_split_part( * @param xp The #xpart. */ __attribute__((always_inline)) INLINE static void -star_formation_no_spart_available(const struct engine* e, const struct part* p, - const struct xpart* xp) { +star_formation_no_spart_available(const struct engine *e, const struct part *p, + const struct xpart *xp) { /* Nothing to do, we just skip it and deal with it next step */ } @@ -1114,7 +1071,7 @@ star_formation_no_spart_available(const struct engine* e, const struct part* p, * @param e The #engine. */ __attribute__((always_inline)) INLINE static void -star_formation_first_init_stats(struct star_formation* star_form, - const struct engine* e) {} +star_formation_first_init_stats(struct star_formation *star_form, + const struct engine *e) {} #endif /* SWIFT_KIARA_STAR_FORMATION_H */ diff --git a/src/star_formation/KIARA/star_formation_debug.h b/src/star_formation/KIARA/star_formation_debug.h index ecbe430bb6..64e8190724 100644 --- a/src/star_formation/KIARA/star_formation_debug.h +++ b/src/star_formation/KIARA/star_formation_debug.h @@ -20,7 +20,7 @@ #define SWIFT_STAR_FORMATION_KIARA_DEBUG_H __attribute__((always_inline)) INLINE static void star_formation_debug_particle( - const struct part* p, const struct xpart* xp) { + const struct part *p, const struct xpart *xp) { if (xp != NULL) { warning("[PID%lld] sf_data:", p->id); diff --git a/src/star_formation/KIARA/star_formation_io.h b/src/star_formation/KIARA/star_formation_io.h index 2d880dfd4c..de7c9990b4 100644 --- a/src/star_formation/KIARA/star_formation_io.h +++ b/src/star_formation/KIARA/star_formation_io.h @@ -33,8 +33,8 @@ * * @return num_fields The number of i/o fields to read. */ -INLINE static int star_formation_read_particles(struct spart* sparts, - struct io_props* list) { +INLINE static int star_formation_read_particles(struct spart *sparts, + struct io_props *list) { return 0; } @@ -48,29 +48,30 @@ INLINE static int star_formation_read_particles(struct spart* sparts, * @return Returns the number of fields to write. */ __attribute__((always_inline)) INLINE static int star_formation_write_particles( - const struct part* parts, const struct xpart* xparts, - struct io_props* list) { + const struct part *parts, const struct xpart *xparts, + struct io_props *list) { int num = 0; list[num] = io_make_output_field( - "StarFormationRates", FLOAT, 1, UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, parts, sf_data.SFR, + "StarFormationRates", FLOAT, 1, UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, parts, + sf_data.SFR, "If positive, star formation rates of the particles. If negative, stores " "the last time/scale-factor at which the gas particle was star-forming. " "If zero, the particle was never star-forming."); num++; #if COOLING_GRACKLE_MODE < 2 - /* If using Kiara/Grackle then this is output in cooling_io since it is tracked in Grackle */ + /* If using Kiara/Grackle then this is output in cooling_io since it is + * tracked in Grackle */ list[num] = io_make_output_field( - "MolecularHydrogenFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, - sf_data.H2_fraction, - "The H2 fraction of the gas particle. "); + "MolecularHydrogenFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + sf_data.H2_fraction, "The H2 fraction of the gas particle. "); num++; #endif list[num] = io_make_output_field( - "InterstellarRadiationField", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + "InterstellarRadiationField", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, sf_data.G0, "The interstellar radiation field strength in Habing units. "); num++; @@ -87,8 +88,8 @@ __attribute__((always_inline)) INLINE static int star_formation_write_particles( * @return Returns the number of fields to write. */ __attribute__((always_inline)) INLINE static int -star_formation_write_sparticles(const struct spart* sparts, - struct io_props* list) { +star_formation_write_sparticles(const struct spart *sparts, + struct io_props *list) { return 0; } #endif /* SWIFT_STAR_FORMATION_KIARA_IO_H */ diff --git a/src/star_formation/KIARA/star_formation_logger.h b/src/star_formation/KIARA/star_formation_logger.h index 310e0057ff..a92598fb0a 100644 --- a/src/star_formation/KIARA/star_formation_logger.h +++ b/src/star_formation/KIARA/star_formation_logger.h @@ -236,7 +236,7 @@ INLINE static void star_formation_logger_log_active_part( /* No SFR logging for wind particles. */ if (p->decoupled) return; - + /* Add the SFR to the logger file */ sf->SFR_active += max(p->sf_data.SFR, 0.f); @@ -258,7 +258,7 @@ INLINE static void star_formation_logger_log_inactive_part( /* No SFR logging for wind particles. */ if (p->decoupled) return; - + /* Add the SFR to the logger file */ sf->SFR_inactive += max(p->sf_data.SFR, 0.f); } diff --git a/src/stars/KIARA/stars.h b/src/stars/KIARA/stars.h index 2e5a2d42d2..1d9a0ab602 100644 --- a/src/stars/KIARA/stars.h +++ b/src/stars/KIARA/stars.h @@ -41,7 +41,7 @@ */ __attribute__((always_inline)) INLINE static float stars_compute_timestep( const struct spart *const sp, const struct stars_props *stars_properties, - //const struct feedback_props *feedback_props, + // const struct feedback_props *feedback_props, const struct phys_const *phys_const, const struct unit_system *us, const int with_cosmology, const struct cosmology *cosmo, const integertime_t ti_current, const double time, const double time_base) { @@ -72,13 +72,12 @@ __attribute__((always_inline)) INLINE static float stars_compute_timestep( if (star_age < stars_properties->age_threshold) { dt_star = min(star_age * stars_properties->time_step_factor_young, stars_properties->max_time_step_young); - } - else { + } else { dt_star = min(star_age * stars_properties->time_step_factor_old, stars_properties->max_time_step_old); } - return max(stars_properties->min_time_step, dt_star); + return max(stars_properties->min_time_step, dt_star); } /** @@ -90,7 +89,7 @@ __attribute__((always_inline)) INLINE static float stars_compute_timestep( * @param with_cosmology Are we running with cosmological integration? */ __attribute__((always_inline)) INLINE static double stars_compute_age( - const struct spart* sp, const struct cosmology* cosmo, double time, + const struct spart *sp, const struct cosmology *cosmo, double time, const int with_cosmology) { if (with_cosmology) { @@ -108,7 +107,7 @@ __attribute__((always_inline)) INLINE static double stars_compute_age( * @param sp The particle to act upon */ __attribute__((always_inline)) INLINE static void stars_init_spart( - struct spart* sp) { + struct spart *sp) { #ifdef DEBUG_INTERACTIONS_STARS for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) @@ -144,12 +143,12 @@ __attribute__((always_inline)) INLINE static void stars_init_spart( * @param time The current time (used if running without cosmology). */ __attribute__((always_inline)) INLINE static void stars_first_init_spart( - struct spart* sp, const struct stars_props* stars_properties, + struct spart *sp, const struct stars_props *stars_properties, const int with_cosmology, const double scale_factor, const double time) { sp->time_bin = 0; sp->count_since_last_enrichment = -1; - + if (stars_properties->overwrite_birth_time) sp->birth_time = stars_properties->spart_first_init_birth_time; if (stars_properties->overwrite_birth_density) @@ -164,7 +163,6 @@ __attribute__((always_inline)) INLINE static void stars_first_init_spart( sp->last_enrichment_time = time; stars_init_spart(sp); - } /** @@ -174,7 +172,7 @@ __attribute__((always_inline)) INLINE static void stars_first_init_spart( * @param dt_drift The drift time-step for positions. */ __attribute__((always_inline)) INLINE static void stars_predict_extra( - struct spart* restrict sp, float dt_drift) {} + struct spart *restrict sp, float dt_drift) {} /** * @brief Sets the values to be predicted in the drifts to their values at a @@ -183,7 +181,7 @@ __attribute__((always_inline)) INLINE static void stars_predict_extra( * @param sp The particle. */ __attribute__((always_inline)) INLINE static void stars_reset_predicted_values( - struct spart* restrict sp) {} + struct spart *restrict sp) {} /** * @brief Finishes the calculation of (non-gravity) forces acting on stars @@ -193,7 +191,7 @@ __attribute__((always_inline)) INLINE static void stars_reset_predicted_values( * @param sp The particle to act upon */ __attribute__((always_inline)) INLINE static void stars_end_feedback( - struct spart* sp) {} + struct spart *sp) {} /** * @brief Kick the additional variables @@ -202,7 +200,7 @@ __attribute__((always_inline)) INLINE static void stars_end_feedback( * @param dt The time-step for this kick */ __attribute__((always_inline)) INLINE static void stars_kick_extra( - struct spart* sp, float dt) {} + struct spart *sp, float dt) {} /** * @brief Finishes the calculation of density on stars @@ -211,7 +209,7 @@ __attribute__((always_inline)) INLINE static void stars_kick_extra( * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void stars_end_density( - struct spart* sp, const struct cosmology* cosmo) { + struct spart *sp, const struct cosmology *cosmo) { /* Some smoothing length multiples. */ const float h = sp->h; @@ -237,7 +235,7 @@ __attribute__((always_inline)) INLINE static void stars_end_density( * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours( - struct spart* restrict sp, const struct cosmology* cosmo) { + struct spart *restrict sp, const struct cosmology *cosmo) { warning( "Star particle with ID %lld treated as having no neighbours (h: %g, " @@ -258,7 +256,7 @@ __attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours( * @param p The particle to act upon */ __attribute__((always_inline)) INLINE static void stars_reset_acceleration( - struct spart* restrict p) { + struct spart *restrict p) { #ifdef DEBUG_INTERACTIONS_STARS p->num_ngb_feedback = 0; @@ -274,7 +272,7 @@ __attribute__((always_inline)) INLINE static void stars_reset_acceleration( * @param p The particle to act upon */ __attribute__((always_inline)) INLINE static void stars_reset_feedback( - struct spart* restrict p) { + struct spart *restrict p) { #ifdef DEBUG_INTERACTIONS_STARS for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) @@ -295,9 +293,9 @@ __attribute__((always_inline)) INLINE static void stars_reset_feedback( * @param luminosities (return) The luminosity in each band. */ INLINE static void stars_get_luminosities( - const struct spart* sp, const int with_cosmology, - const struct cosmology* cosmo, const double time, - const struct phys_const* phys_const, const struct stars_props* props, + const struct spart *sp, const int with_cosmology, + const struct cosmology *cosmo, const double time, + const struct phys_const *phys_const, const struct stars_props *props, float luminosities[luminosity_bands_count]) { const int count_Z = eagle_stars_lum_tables_N_Z; @@ -345,7 +343,7 @@ INLINE static void stars_get_luminosities( if (Z_index == 0) error("Invalid Z index!"); #endif - const float* array = props->lum_tables_luminosities[i]; + const float *array = props->lum_tables_luminosities[i]; /* 2D interpolation */ diff --git a/src/stars/KIARA/stars_debug.h b/src/stars/KIARA/stars_debug.h index ed3d15bc40..ea26f9f3d4 100644 --- a/src/stars/KIARA/stars_debug.h +++ b/src/stars/KIARA/stars_debug.h @@ -20,7 +20,7 @@ #define SWIFT_SIMBA_STARS_DEBUG_H __attribute__((always_inline)) INLINE static void stars_debug_particle( - const struct spart* p) { + const struct spart *p) { printf( "x=[%.3e,%.3e,%.3e], " "v_full=[%.3e,%.3e,%.3e] p->mass=%.3e \n t_begin=%d, t_end=%d\n", diff --git a/src/stars/KIARA/stars_io.h b/src/stars/KIARA/stars_io.h index b8d68ab769..a1f85d4db7 100644 --- a/src/stars/KIARA/stars_io.h +++ b/src/stars/KIARA/stars_io.h @@ -51,9 +51,9 @@ INLINE static void stars_read_particles(struct spart *sparts, list[5] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, sparts, mass_init); float def_value = -1.f; - list[6] = - io_make_input_field_default("StellarFormationTime", FLOAT, 1, OPTIONAL, - UNIT_CONV_NO_UNITS, sparts, birth_time, def_value); + list[6] = io_make_input_field_default("StellarFormationTime", FLOAT, 1, + OPTIONAL, UNIT_CONV_NO_UNITS, sparts, + birth_time, def_value); list[7] = io_make_input_field("BirthDensities", FLOAT, 1, OPTIONAL, UNIT_CONV_DENSITY, sparts, birth_density); list[8] = @@ -190,11 +190,11 @@ INLINE static void stars_write_particles(const struct spart *sparts, "we store the physical density at the birth redshift, no conversion is " "needed)"); - list[8] = io_make_output_field("BirthTemperatures", - FLOAT, 1, UNIT_CONV_TEMPERATURE, - 0.f, sparts, birth_temperature, - "Temperatures at the time of birth of the gas " - "particles that turned into stars"); + list[8] = + io_make_output_field("BirthTemperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, + 0.f, sparts, birth_temperature, + "Temperatures at the time of birth of the gas " + "particles that turned into stars"); list[9] = io_make_output_field_convert_spart( "Luminosities", FLOAT, luminosity_bands_count, UNIT_CONV_NO_UNITS, 0.f, @@ -288,12 +288,12 @@ INLINE static void stars_props_init(struct stars_props *sp, const double Myr = 1e6 * 365.25 * 24. * 60. * 60.; const double conv_fac = units_cgs_conversion_factor(us, UNIT_CONV_TIME); - sp->time_step_factor_young = parser_get_opt_param_float( - params, "Stars:time_step_factor_young", 1.f); - sp->time_step_factor_old = parser_get_opt_param_float( - params, "Stars:time_step_factor_old", 1.f); - const double min_time_step_Myr = parser_get_opt_param_float( - params, "Stars:min_time_step_Myr", 30.); + sp->time_step_factor_young = + parser_get_opt_param_float(params, "Stars:time_step_factor_young", 1.f); + sp->time_step_factor_old = + parser_get_opt_param_float(params, "Stars:time_step_factor_old", 1.f); + const double min_time_step_Myr = + parser_get_opt_param_float(params, "Stars:min_time_step_Myr", 30.); const double max_time_step_young_Myr = parser_get_opt_param_float( params, "Stars:max_timestep_young_Myr", FLT_MAX); const double max_time_step_old_Myr = diff --git a/src/stars/KIARA/stars_part.h b/src/stars/KIARA/stars_part.h index 76ba601927..064b377469 100644 --- a/src/stars/KIARA/stars_part.h +++ b/src/stars/KIARA/stars_part.h @@ -48,7 +48,7 @@ struct spart { long long id; /*! Pointer to corresponding gravity part. */ - struct gpart* gpart; + struct gpart *gpart; /*! Particle position. */ double x[3]; @@ -265,13 +265,13 @@ struct stars_props { double age_threshold_unlimited; /*! The metallicities (metal mass frac) for the luminosity interpolations */ - float* lum_tables_Z[luminosity_bands_count]; + float *lum_tables_Z[luminosity_bands_count]; /*! The age (in Gyr) for the luminosity interpolations */ - float* lum_tables_ages[luminosity_bands_count]; + float *lum_tables_ages[luminosity_bands_count]; /*! The luminosities */ - float* lum_tables_luminosities[luminosity_bands_count]; + float *lum_tables_luminosities[luminosity_bands_count]; /*! Conversion factor to luminosities */ double lum_tables_factor; diff --git a/src/timestep.h b/src/timestep.h index 684d0697e4..825e46edfe 100644 --- a/src/timestep.h +++ b/src/timestep.h @@ -297,10 +297,10 @@ __attribute__((always_inline)) INLINE static integertime_t get_spart_timestep( /* Stellar time-step */ float new_dt_stars = stars_compute_timestep( - sp, e->stars_properties, //e->feedback_props, - e->physical_constants, - e->internal_units, (e->policy & engine_policy_cosmology), e->cosmology, - e->ti_current, e->time, e->time_base); + sp, e->stars_properties, // e->feedback_props, + e->physical_constants, e->internal_units, + (e->policy & engine_policy_cosmology), e->cosmology, e->ti_current, + e->time, e->time_base); /* Gravity time-step */ float new_dt_self = FLT_MAX, new_dt_ext = FLT_MAX; diff --git a/swift.c b/swift.c index 7cb695f090..cf3b1fd2e2 100644 --- a/swift.c +++ b/swift.c @@ -428,7 +428,7 @@ int main(int argc, char *argv[]) { with_stars = 1; with_star_formation = 1; with_cooling = 1; - //with_hydro_decoupling = 1; + // with_hydro_decoupling = 1; with_feedback = 1; with_black_holes = 1; with_fof = 1; @@ -862,7 +862,7 @@ int main(int argc, char *argv[]) { error("Cannot reconstruct m-poles every step over MPI (yet)."); #endif - /* Temporary early aborts for modes not supported with hand-vec. */ + /* Temporary early aborts for modes not supported with hand-vec. */ #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) && \ !defined(CHEMISTRY_NONE) error( @@ -1200,7 +1200,7 @@ int main(int argc, char *argv[]) { } else bzero(&sink_properties, sizeof(struct sink_props)); - /* Initialise the cooling function properties */ + /* Initialise the cooling function properties */ #ifdef COOLING_NONE if (with_cooling) { error( diff --git a/tests/test125cells.c b/tests/test125cells.c index d2f420d791..e233850d8c 100644 --- a/tests/test125cells.c +++ b/tests/test125cells.c @@ -111,8 +111,8 @@ void set_energy_state(struct part *part, enum pressure_field press, float size, part->entropy = pressure / pow_gamma(density); #elif defined(PHANTOM_SPH) part->u = pressure / (hydro_gamma_minus_one * density); -#elif defined(MINIMAL_SPH) || defined(HOPKINS_PU_SPH) || \ - defined(HOPKINS_PU_SPH_MONAGHAN) || defined(ANARCHY_PU_SPH) || \ +#elif defined(MINIMAL_SPH) || defined(HOPKINS_PU_SPH) || \ + defined(HOPKINS_PU_SPH_MONAGHAN) || defined(ANARCHY_PU_SPH) || \ defined(SPHENIX_SPH) || defined(PHANTOM_SPH) || defined(GASOLINE_SPH) || \ defined(MAGMA2_SPH) part->u = pressure / (hydro_gamma_minus_one * density); @@ -692,7 +692,7 @@ int main(int argc, char *argv[]) { for (int j = 0; j < 125; ++j) runner_do_hydro_sort(&runner, cells[j], 0x1FFF, 0, 0, 0, 0); - /* Do the density calculation */ + /* Do the density calculation */ /* Initialise the particle cache. */ #ifdef WITH_VECTORIZATION @@ -802,7 +802,7 @@ int main(int argc, char *argv[]) { #endif /* EXTRA_HYDRO_LOOP */ - /* Do the force calculation */ + /* Do the force calculation */ #ifdef WITH_VECTORIZATION /* Initialise the cache. */ From e408342480adda83c6a3fad2e31d681835146fe2 Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Wed, 26 Nov 2025 20:09:59 +0800 Subject: [PATCH 12/37] Let the code compile with just hydro --- src/chemistry/none/chemistry.h | 6 +++--- src/chemistry/none/chemistry_iact.h | 4 +++- src/feedback/none/feedback.h | 18 ++++++++++++++++++ src/runner_time_integration.c | 9 ++++++++- .../none/star_formation_struct.h | 5 +++++ src/stars/KIARA/stars.h | 2 +- src/timestep.h | 7 +++---- 7 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/chemistry/none/chemistry.h b/src/chemistry/none/chemistry.h index 813d0ffb08..cb2998f9fb 100644 --- a/src/chemistry/none/chemistry.h +++ b/src/chemistry/none/chemistry.h @@ -95,8 +95,8 @@ static INLINE void chemistry_print_backend( * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void chemistry_end_density( - struct part *restrict p, const struct chemistry_global_data *cd, - const struct cosmology *cosmo) {} + struct part *restrict p, struct xpart *restrict xp, + const struct chemistry_global_data *cd, const struct cosmology *cosmo) {} /** * @brief Finishes the gradient calculation. @@ -139,7 +139,7 @@ __attribute__((always_inline)) INLINE static void chemistry_prepare_force( * @param chem_data The global properties of the chemistry scheme. */ __attribute__((always_inline)) INLINE static void chemistry_end_force( - struct part *restrict p, const struct cosmology *cosmo, + struct part *restrict p, struct xpart *xp, const struct cosmology *cosmo, const int with_cosmology, const double time, const double dt, const struct chemistry_global_data *cd) {} diff --git a/src/chemistry/none/chemistry_iact.h b/src/chemistry/none/chemistry_iact.h index 3bff6980dd..1f08d21250 100644 --- a/src/chemistry/none/chemistry_iact.h +++ b/src/chemistry/none/chemistry_iact.h @@ -122,9 +122,11 @@ runner_iact_nonsym_gradient_diffusion(const float r2, const float dx[3], */ __attribute__((always_inline)) INLINE static void runner_iact_diffusion( const float r2, const float dx[3], const float hi, const float hj, - struct part *restrict pi, struct part *restrict pj, const float a, + struct part *restrict pi, struct part *restrict pj, + struct xpart *restrict xpi, struct xpart *restrict xpj, const float a, const float H, const float time_base, const integertime_t t_current, const struct cosmology *cosmo, const int with_cosmology, + const struct phys_const *phys_const, const struct chemistry_global_data *chem_data) {} /** diff --git a/src/feedback/none/feedback.h b/src/feedback/none/feedback.h index 26ddb3f3b0..6be3d876d6 100644 --- a/src/feedback/none/feedback.h +++ b/src/feedback/none/feedback.h @@ -50,6 +50,11 @@ feedback_compute_spart_timestep( return FLT_MAX; } +__attribute__((always_inline)) INLINE static void feedback_recouple_part( + struct part *p, struct xpart *xp, const struct engine *e, + const int with_cosmology, const struct cosmology *cosmo, + const struct feedback_props *fb_props) {} + /** * @brief Update the properties of a particle fue to feedback effects after * the cooling was applied. @@ -155,6 +160,19 @@ __attribute__((always_inline)) INLINE static void feedback_reset_feedback( __attribute__((always_inline)) INLINE static void feedback_first_init_spart( struct spart *sp, const struct feedback_props *feedback_props) {} +/** + * @brief Initialises the particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions or assignments between the particle + * and extended particle fields. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + */ +__attribute__((always_inline)) INLINE static void feedback_first_init_part( + struct part *restrict p, struct xpart *restrict xp) {} + /** * @brief Initialises the s-particles feedback props for the first time * diff --git a/src/runner_time_integration.c b/src/runner_time_integration.c index 61d037109b..11094ea492 100644 --- a/src/runner_time_integration.c +++ b/src/runner_time_integration.c @@ -1402,7 +1402,14 @@ void runner_do_limiter(struct runner *r, struct cell *c, int force, warning( "Not limiting particle with id %lld because it needs to be " "synced tdel=%g dec=%d.", - p->id, p->feedback_data.decoupling_delay_time, p->decoupled); + p->id, + // MATTHIEU: TODO: Fix this +#ifdef FEEDBACK_KIARA + p->feedback_data.decoupling_delay_time, p->decoupled +#else + 0., 0 +#endif + ); continue; } diff --git a/src/star_formation/none/star_formation_struct.h b/src/star_formation/none/star_formation_struct.h index 909e593bf3..0445e75a7c 100644 --- a/src/star_formation/none/star_formation_struct.h +++ b/src/star_formation/none/star_formation_struct.h @@ -23,6 +23,11 @@ new particles, conversion gas->stars does not need unique IDs) */ #define star_formation_need_unique_id 0 +/** + * @brief Star-formation-related properties stored in the particle data. + */ +struct star_formation_part_data {}; + /** * @brief Star-formation-related properties stored in the extended particle * data. diff --git a/src/stars/KIARA/stars.h b/src/stars/KIARA/stars.h index 1d9a0ab602..3fa6ed4ed4 100644 --- a/src/stars/KIARA/stars.h +++ b/src/stars/KIARA/stars.h @@ -41,7 +41,7 @@ */ __attribute__((always_inline)) INLINE static float stars_compute_timestep( const struct spart *const sp, const struct stars_props *stars_properties, - // const struct feedback_props *feedback_props, + const struct feedback_props *feedback_props, const struct phys_const *phys_const, const struct unit_system *us, const int with_cosmology, const struct cosmology *cosmo, const integertime_t ti_current, const double time, const double time_base) { diff --git a/src/timestep.h b/src/timestep.h index 825e46edfe..8db2ea2100 100644 --- a/src/timestep.h +++ b/src/timestep.h @@ -297,10 +297,9 @@ __attribute__((always_inline)) INLINE static integertime_t get_spart_timestep( /* Stellar time-step */ float new_dt_stars = stars_compute_timestep( - sp, e->stars_properties, // e->feedback_props, - e->physical_constants, e->internal_units, - (e->policy & engine_policy_cosmology), e->cosmology, e->ti_current, - e->time, e->time_base); + sp, e->stars_properties, e->feedback_props, e->physical_constants, + e->internal_units, (e->policy & engine_policy_cosmology), e->cosmology, + e->ti_current, e->time, e->time_base); /* Gravity time-step */ float new_dt_self = FLT_MAX, new_dt_ext = FLT_MAX; From 0e8f3027ddacb91fa7f4c9e88205cfc980c9c851 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Wed, 26 Nov 2025 12:57:27 +0000 Subject: [PATCH 13/37] KIARA-RT first push. Compiles but does not work. --- configure.ac | 64 +- src/Makefile.am | 40 +- src/common_io.c | 2 +- src/const.h | 1 + src/cooling/KIARA/cooling.c | 10 +- src/cooling/KIARA/cooling.h | 2 +- src/fvpm_geometry.h | 2 +- src/fvpm_geometry/Gizmo/fvpm_geometry.h | 2 +- src/fvpm_geometry_struct.h | 2 +- src/hydro/MAGMA2/hydro_part.h | 4 + src/rt.h | 3 + src/rt/KIARA/rt.h | 788 +++ src/rt/KIARA/rt_additions.h | 93 + src/rt/KIARA/rt_blackbody.h | 88 + src/rt/KIARA/rt_debugging.h | 362 ++ src/rt/KIARA/rt_flux.h | 114 + src/rt/KIARA/rt_getters.h | 230 + src/rt/KIARA/rt_grackle_utils.h | 540 ++ src/rt/KIARA/rt_gradients.h | 474 ++ src/rt/KIARA/rt_iact.h | 481 ++ src/rt/KIARA/rt_interaction_cross_sections.c | 458 ++ src/rt/KIARA/rt_interaction_cross_sections.h | 153 + src/rt/KIARA/rt_interaction_rates.h | 180 + src/rt/KIARA/rt_io.h | 529 ++ src/rt/KIARA/rt_ionization_equilibrium.h | 294 + src/rt/KIARA/rt_parameters.h | 38 + src/rt/KIARA/rt_properties.h | 620 +++ src/rt/KIARA/rt_riemann_GLF.h | 80 + src/rt/KIARA/rt_riemann_HLL.h | 224 + src/rt/KIARA/rt_riemann_HLL_eigenvalues.h | 5246 ++++++++++++++++++ src/rt/KIARA/rt_slope_limiters_cell.h | 162 + src/rt/KIARA/rt_slope_limiters_face.h | 215 + src/rt/KIARA/rt_species.h | 47 + src/rt/KIARA/rt_stellar_emission_model.h | 388 ++ src/rt/KIARA/rt_struct.h | 172 + src/rt/KIARA/rt_thermochemistry.c | 513 ++ src/rt/KIARA/rt_thermochemistry.h | 120 + src/rt/KIARA/rt_thermochemistry_utils.h | 294 + src/rt/KIARA/rt_unphysical.h | 237 + src/rt_additions.h | 2 + src/rt_io.h | 2 + src/rt_parameters.c | 2 +- src/rt_parameters.h | 2 + src/rt_properties.h | 2 + src/rt_struct.h | 2 + src/runner_others.c | 8 +- src/space.c | 7 +- src/timestep.h | 2 +- 48 files changed, 13272 insertions(+), 29 deletions(-) create mode 100644 src/rt/KIARA/rt.h create mode 100644 src/rt/KIARA/rt_additions.h create mode 100644 src/rt/KIARA/rt_blackbody.h create mode 100644 src/rt/KIARA/rt_debugging.h create mode 100644 src/rt/KIARA/rt_flux.h create mode 100644 src/rt/KIARA/rt_getters.h create mode 100644 src/rt/KIARA/rt_grackle_utils.h create mode 100644 src/rt/KIARA/rt_gradients.h create mode 100644 src/rt/KIARA/rt_iact.h create mode 100644 src/rt/KIARA/rt_interaction_cross_sections.c create mode 100644 src/rt/KIARA/rt_interaction_cross_sections.h create mode 100644 src/rt/KIARA/rt_interaction_rates.h create mode 100644 src/rt/KIARA/rt_io.h create mode 100644 src/rt/KIARA/rt_ionization_equilibrium.h create mode 100644 src/rt/KIARA/rt_parameters.h create mode 100644 src/rt/KIARA/rt_properties.h create mode 100644 src/rt/KIARA/rt_riemann_GLF.h create mode 100644 src/rt/KIARA/rt_riemann_HLL.h create mode 100644 src/rt/KIARA/rt_riemann_HLL_eigenvalues.h create mode 100644 src/rt/KIARA/rt_slope_limiters_cell.h create mode 100644 src/rt/KIARA/rt_slope_limiters_face.h create mode 100644 src/rt/KIARA/rt_species.h create mode 100644 src/rt/KIARA/rt_stellar_emission_model.h create mode 100644 src/rt/KIARA/rt_struct.h create mode 100644 src/rt/KIARA/rt_thermochemistry.c create mode 100644 src/rt/KIARA/rt_thermochemistry.h create mode 100644 src/rt/KIARA/rt_thermochemistry_utils.h create mode 100644 src/rt/KIARA/rt_unphysical.h diff --git a/configure.ac b/configure.ac index ad7fa56e55..302877a065 100644 --- a/configure.ac +++ b/configure.ac @@ -2047,7 +2047,7 @@ AC_SUBST([SUNDIALS_INCS]) # As an example for this, see the call to AC_ARG_WITH for cooling. AC_ARG_WITH([subgrid], [AS_HELP_STRING([--with-subgrid=], - [Master switch for subgrid methods. Inexperienced user should start here. Options are: @<:@none, GEAR, GEAR-G3, AGORA, QLA, QLA-EAGLE, EAGLE, EAGLE-XL, SPIN_JET_EAGLE, SIMBA, KIARA default: none@:>@] + [Master switch for subgrid methods. Inexperienced user should start here. Options are: @<:@none, GEAR, GEAR-G3, AGORA, QLA, QLA-EAGLE, EAGLE, EAGLE-XL, SPIN_JET_EAGLE, SIMBA, KIARA, KIARART default: none@:>@] )], [with_subgrid="$withval"], [with_subgrid=none] @@ -2210,8 +2210,8 @@ case "$with_subgrid" in with_subgrid_cooling=KIARA with_subgrid_chemistry=KIARA with_subgrid_tracers=none - with_subgrid_entropy_floor=SIMBA - with_subgrid_stars=SIMBA + with_subgrid_entropy_floor=none + with_subgrid_stars=KIARA with_subgrid_star_formation=KIARA with_subgrid_feedback=KIARA with_subgrid_black_holes=Obsidian @@ -3283,18 +3283,18 @@ AC_DEFINE_UNQUOTED([SELF_GRAVITY_MULTIPOLE_ORDER], [$with_multipole_order], [Mul # Radiative transfer scheme AC_ARG_WITH([rt], [AS_HELP_STRING([--with-rt=], - [Radiative transfer scheme to use @<:@none, GEAR_*, SPHM1RT_*, debug default: none@:>@. - For GEAR and SPHM1RT, the number of photon groups (e.g. GEAR_4) needs to be provided.] + [Radiative transfer scheme to use @<:@none, GEAR_*, SPHM1RT_*, KIARA_* debug default: none@:>@. + For GEAR, SPHM1RT, and KIARA the number of photon groups (e.g. GEAR_4) needs to be provided.] )], [with_rt="$withval"], [with_rt="none"] ) -# For GEAR-RT scheme: Select a RT Riemann solver +# For GEAR-RT/KIARA-RT scheme: Select a RT Riemann solver AC_ARG_WITH([rt-riemann-solver], [AS_HELP_STRING([--with-rt-riemann-solver=], [Riemann solver for the moments of the ratiadiative transfer equation with the M1 closure to use @<:@none, HLL, GLF, default: none@:>@. - For the GEAR RT scheme, you need to select one Riemann solver.] + For the GEAR/KIARA RT scheme, you need to select one Riemann solver.] )], [with_rt_riemann_solver="$withval"], [with_rt_riemann_solver="none"] @@ -3339,11 +3339,11 @@ case "$with_rt" in fi case "$with_hydro" in - "gizmo-mfv" | "sphenix") + "gizmo-mfv" | "sphenix" | "magma2") # allowed. ;; *) - AC_MSG_ERROR([GEAR-RT: Cannot work without gizmo-mfv or sphenix hydro. Compile using --with-hydro=gizmo-mfv or --with-hydro=sphenix]) + AC_MSG_ERROR([GEAR-RT: Cannot work without gizmo-mfv or sphenix/magma2 hydro. Compile using --with-hydro=gizmo-mfv or --with-hydro=sphenix or --with-hydro=magma2]) ;; esac @@ -3359,6 +3359,47 @@ case "$with_rt" in AC_MSG_ERROR([GEAR-RT: You need to select a cooling module (--with-cooling=grackle_1)]) fi + ;; + KIARA_*) + AC_DEFINE([RT_KIARA], [1], [KIARA M1 closure scheme]) + number_group=${with_rt#*_} + AC_DEFINE_UNQUOTED([RT_NGROUPS], [$number_group], [Number of photon groups to follow]) + AC_DEFINE([MPI_SYMMETRIC_FORCE_INTERACTION_RT], [1], [Do symmetric MPI interactions]) + + if test "$number_group" = "0"; then + AC_MSG_ERROR([KIARA-RT: Cannot work with zero photon groups]) + fi + if ! test $number_group -eq $number_group; then + # abuse -eq to check whether $number_group is an integer. -eq + # only works with those. + AC_MSG_ERROR([KIARA-RT: Cannot work with non-integer photon groups]) + fi + + if test "$enable_debugging_checks" = "yes"; then + AC_DEFINE([SWIFT_RT_DEBUG_CHECKS], [1], [additional debugging checks for RT]) + fi + + case "$with_hydro" in + "sphenix" | "magma2") + # allowed. + ;; + *) + AC_MSG_ERROR([KIARA-RT: Needs sphenix/magma2 hydro. Compile using --with-hydro=sphenix or --with-hydro=magma2]) + ;; + esac + + if test "$with_rt_riemann_solver" = "none"; then + AC_MSG_ERROR([KIARA-RT: You need to select an RT Riemann solver (--with-rt-riemann-solver=...)]) + fi + + if test "$have_grackle" != "yes"; then + AC_MSG_ERROR([KIARA-RT: You need the grackle library for KIARA-RT. (--with-grackle=PATH)]) + fi + + if test "$with_cooling" = "none"; then + AC_MSG_ERROR([KIARA-RT: You need to select a cooling module (--with-cooling=grackle_1)]) + fi + ;; debug) AC_DEFINE([RT_DEBUG], [1], [debugging scheme]) @@ -3500,12 +3541,13 @@ AM_CONDITIONAL([HAVESPHM1RTRT], [test "${with_rt:0:7}" = "SPHM1RT"]) # Check if using GEAR-RT radiative transfer AM_CONDITIONAL([HAVEGEARRT], [test "${with_rt:0:4}" = "GEAR"]) +# Check if using KIARA-RT radiative transfer +AM_CONDITIONAL([HAVEKIARART], [test "${with_rt:0:5}" = "KIARA"]) + # Check if using Moving mesh AM_CONDITIONAL([HAVE_MOVING_MESH], [test "$enable_moving_mesh" = "yes"]) - - # Handle .in files. AC_CONFIG_FILES([Makefile src/Makefile examples/Makefile examples/Cooling/CoolingRates/Makefile doc/Makefile doc/Doxyfile tests/Makefile]) AC_CONFIG_FILES([argparse/Makefile tools/Makefile]) diff --git a/src/Makefile.am b/src/Makefile.am index 30fc8ba522..f57c85b421 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -163,6 +163,13 @@ if HAVEGEARRT GEAR_RT_SOURCES += rt/GEAR/rt_interaction_cross_sections.c endif +# source files for KIARA RT +KIARA_RT_SOURCES = +if HAVEKIARART +KIARA_RT_SOURCES += rt/KIARA/rt_interaction_cross_sections.c +KIARA_RT_SOURCES += rt/KIARA/rt_thermochemistry.c +endif + # source files for SPHM1RT cooling SPHM1RT_RT_SOURCES = if HAVESPHM1RTRT @@ -231,6 +238,7 @@ AM_SOURCES += $(AGORA_FEEDBACK_SOURCES) AM_SOURCES += $(PS2020_COOLING_SOURCES) AM_SOURCES += $(SPHM1RT_RT_SOURCES) AM_SOURCES += $(GEAR_RT_SOURCES) +AM_SOURCES += $(KIARA_RT_SOURCES) AM_SOURCES += $(KIARA_COOLING_SOURCES) $(KIARA_FEEDBACK_SOURCES) AM_SOURCES += swift_lustre_api.c @@ -366,6 +374,32 @@ nobase_noinst_HEADERS += rt/GEAR/rt_struct.h nobase_noinst_HEADERS += rt/GEAR/rt_thermochemistry.h nobase_noinst_HEADERS += rt/GEAR/rt_thermochemistry_utils.h nobase_noinst_HEADERS += rt/GEAR/rt_unphysical.h +nobase_noinst_HEADERS += rt/KIARA/rt_additions.h +nobase_noinst_HEADERS += rt/KIARA/rt_blackbody.h +nobase_noinst_HEADERS += rt/KIARA/rt_debugging.h +nobase_noinst_HEADERS += rt/KIARA/rt_flux.h +nobase_noinst_HEADERS += rt/KIARA/rt_getters.h +nobase_noinst_HEADERS += rt/KIARA/rt_grackle_utils.h +nobase_noinst_HEADERS += rt/KIARA/rt_gradients.h +nobase_noinst_HEADERS += rt/KIARA/rt.h +nobase_noinst_HEADERS += rt/KIARA/rt_iact.h +nobase_noinst_HEADERS += rt/KIARA/rt_interaction_cross_sections.h +nobase_noinst_HEADERS += rt/KIARA/rt_interaction_rates.h +nobase_noinst_HEADERS += rt/KIARA/rt_io.h +nobase_noinst_HEADERS += rt/KIARA/rt_ionization_equilibrium.h +nobase_noinst_HEADERS += rt/KIARA/rt_parameters.h +nobase_noinst_HEADERS += rt/KIARA/rt_properties.h +nobase_noinst_HEADERS += rt/KIARA/rt_riemann_GLF.h +nobase_noinst_HEADERS += rt/KIARA/rt_riemann_HLL_eigenvalues.h +nobase_noinst_HEADERS += rt/KIARA/rt_riemann_HLL.h +nobase_noinst_HEADERS += rt/KIARA/rt_slope_limiters_cell.h +nobase_noinst_HEADERS += rt/KIARA/rt_slope_limiters_face.h +nobase_noinst_HEADERS += rt/KIARA/rt_species.h +nobase_noinst_HEADERS += rt/KIARA/rt_stellar_emission_model.h +nobase_noinst_HEADERS += rt/KIARA/rt_struct.h +nobase_noinst_HEADERS += rt/KIARA/rt_thermochemistry.h +nobase_noinst_HEADERS += rt/KIARA/rt_thermochemistry_utils.h +nobase_noinst_HEADERS += rt/KIARA/rt_unphysical.h nobase_noinst_HEADERS += rt/SPHM1RT/rt.h nobase_noinst_HEADERS += rt/SPHM1RT/rt_getters.h nobase_noinst_HEADERS += rt/SPHM1RT/rt_setters.h @@ -501,9 +535,9 @@ nobase_noinst_HEADERS += feedback/GEAR/feedback_properties.h feedback/GEAR/feedb nobase_noinst_HEADERS += feedback/GEAR/initial_mass_function.h feedback/GEAR/supernovae_ia.h feedback/GEAR/supernovae_ii.h nobase_noinst_HEADERS += feedback/GEAR/lifetime.h feedback/GEAR/hdf5_functions.h feedback/GEAR/interpolation.h nobase_noinst_HEADERS += feedback/GEAR/feedback_debug.h -#nobase_noinst_HEADERS += feedback/KIARA/feedback.h feedback/KIARA/feedback_struct.h -#nobase_noinst_HEADERS += feedback/KIARA/feedback_properties.h feedback/KIARA/feedback_iact.h -#nobase_noinst_HEADERS += feedback/KIARA/feedback_debug.h +nobase_noinst_HEADERS += feedback/KIARA/feedback.h feedback/KIARA/feedback_struct.h +nobase_noinst_HEADERS += feedback/KIARA/feedback_properties.h feedback/KIARA/feedback_iact.h +nobase_noinst_HEADERS += feedback/KIARA/feedback_debug.h nobase_noinst_HEADERS += black_holes/Default/black_holes.h black_holes/Default/black_holes_io.h nobase_noinst_HEADERS += black_holes/Default/black_holes_part.h black_holes/Default/black_holes_iact.h nobase_noinst_HEADERS += black_holes/Default/black_holes_properties.h diff --git a/src/common_io.c b/src/common_io.c index 97acf3c1f1..fc55f1e8e8 100644 --- a/src/common_io.c +++ b/src/common_io.c @@ -1742,7 +1742,7 @@ void io_select_hydro_fields(const struct part *const parts, *num_fields += star_formation_write_particles(parts, xparts, list + *num_fields); if (with_rt) { - *num_fields += rt_write_particles(parts, list + *num_fields); + *num_fields += rt_write_particles(parts, xparts, list + *num_fields); } *num_fields += extra_io_write_particles(parts, xparts, list + *num_fields, with_cosmology); diff --git a/src/const.h b/src/const.h index 75be9963f5..fddaa843db 100644 --- a/src/const.h +++ b/src/const.h @@ -93,5 +93,6 @@ /* GRACKLE doesn't really like exact zeroes, so use something * comparatively small instead. */ #define RT_GEAR_TINY_MASS_FRACTION 1.e-20 +#define RT_KIARA_TINY_MASS_FRACTION 1.e-20 #endif /* SWIFT_CONST_H */ diff --git a/src/cooling/KIARA/cooling.c b/src/cooling/KIARA/cooling.c index 1609120725..d4c7416dd0 100644 --- a/src/cooling/KIARA/cooling.c +++ b/src/cooling/KIARA/cooling.c @@ -1344,7 +1344,7 @@ void cooling_do_grackle_cooling( const struct entropy_floor_properties *floor_props, const struct cooling_function_data *restrict cooling, struct part *restrict p, struct xpart *restrict xp, gr_float *iact_rates, - const double dt, const double dt_therm, const double time) { + const double dt, const double dt_therm) { /* Self-enrich gas if very low metallicity */ cooling_init_chemistry(cooling, p); @@ -1461,9 +1461,6 @@ void cooling_do_grackle_cooling( /* Store the radiated energy */ xp->cooling_data.radiated_energy -= hydro_get_mass(p) * cool_du_dt * dt_therm; - - /* Record this cooling event */ - xp->cooling_data.time_last_event = time; } /** @@ -1512,7 +1509,10 @@ void cooling_cool_part(const struct phys_const *restrict phys_const, /* Do the cooling and chemistry */ cooling_do_grackle_cooling(phys_const, us, cosmo, hydro_props, floor_props, - cooling, p, xp, iact_rates, dt, dt_therm, time); + cooling, p, xp, iact_rates, dt, dt_therm); + + /* Record this cooling event */ + xp->cooling_data.time_last_event = time; } /** diff --git a/src/cooling/KIARA/cooling.h b/src/cooling/KIARA/cooling.h index 10a3337895..4645835481 100644 --- a/src/cooling/KIARA/cooling.h +++ b/src/cooling/KIARA/cooling.h @@ -138,7 +138,7 @@ void cooling_do_grackle_cooling( const struct entropy_floor_properties *floor_props, const struct cooling_function_data *restrict cooling, struct part *restrict p, struct xpart *restrict xp, gr_float *iact_rates, - const double dt, const double dt_therm, const double time); + const double dt, const double dt_therm); gr_float cooling_time(const struct phys_const *restrict phys_const, const struct unit_system *restrict us, const struct hydro_props *hydro_properties, diff --git a/src/fvpm_geometry.h b/src/fvpm_geometry.h index 847e241501..e14bf1017d 100644 --- a/src/fvpm_geometry.h +++ b/src/fvpm_geometry.h @@ -23,7 +23,7 @@ #include /* Import the right FVPM geometry functions */ -#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(RT_GEAR) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(RT_GEAR) || defined(RT_KIARA) #include "./fvpm_geometry/Gizmo/fvpm_geometry.h" #else #include "./fvpm_geometry/None/fvpm_geometry.h" diff --git a/src/fvpm_geometry/Gizmo/fvpm_geometry.h b/src/fvpm_geometry/Gizmo/fvpm_geometry.h index 7ae5d47719..f7d0ffdf00 100644 --- a/src/fvpm_geometry/Gizmo/fvpm_geometry.h +++ b/src/fvpm_geometry/Gizmo/fvpm_geometry.h @@ -24,7 +24,7 @@ #error "Combining GIZMO MFM and GEAR-RT not implemented yet." #endif -#if defined(GIZMO_MFV_SPH) || defined(RT_GEAR) +#if defined(GIZMO_MFV_SPH) || defined(RT_GEAR) || defined(RT_KIARA) #include "./MFV/fvpm_geometry.h" #elif defined(GIZMO_MFM_SPH) #include "./MFM/fvpm_geometry.h" diff --git a/src/fvpm_geometry_struct.h b/src/fvpm_geometry_struct.h index 9cd32b7846..9de267f021 100644 --- a/src/fvpm_geometry_struct.h +++ b/src/fvpm_geometry_struct.h @@ -23,7 +23,7 @@ #include /* Import the right geometry struct definition */ -#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(RT_GEAR) +#if defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) || defined(RT_GEAR) || defined(RT_KIARA) #include "./fvpm_geometry/Gizmo/fvpm_geometry_struct.h" #else #include "./fvpm_geometry/None/fvpm_geometry_struct.h" diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 1e33dbcbd8..400b45b99f 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -36,6 +36,7 @@ #ifdef WITH_FOF_GALAXIES #include "fof_struct.h" #endif +#include "fvpm_geometry_struct.h" #include "hydro_parameters.h" #include "mhd_struct.h" #include "particle_splitting_struct.h" @@ -347,6 +348,9 @@ struct part { /*! Flag to indicate particle is decoupled */ int decoupled; + /*! Geometrical quantities used for Finite Volume Particle Method RT. */ + struct fvpm_geometry_struct geometry; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/rt.h b/src/rt.h index 43abd3d0b2..fdeb597ede 100644 --- a/src/rt.h +++ b/src/rt.h @@ -38,6 +38,9 @@ #elif defined(RT_GEAR) #include "./rt/GEAR/rt.h" #include "./rt/GEAR/rt_iact.h" +#elif defined(RT_KIARA) +#include "./rt/KIARA/rt.h" +#include "./rt/KIARA/rt_iact.h" #elif defined(RT_SPHM1RT) #include "./rt/SPHM1RT/rt.h" #include "./rt/SPHM1RT/rt_iact.h" diff --git a/src/rt/KIARA/rt.h b/src/rt/KIARA/rt.h new file mode 100644 index 0000000000..70da525f00 --- /dev/null +++ b/src/rt/KIARA/rt.h @@ -0,0 +1,788 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_KIARA_H +#define SWIFT_RT_KIARA_H + +#include "rt_debugging.h" +#include "rt_flux.h" +#include "rt_gradients.h" +#include "rt_properties.h" +/* #include "rt_slope_limiters_cell.h" [> skipped for now <] */ +#include "rt_stellar_emission_model.h" +#include "rt_thermochemistry.h" + +#include + +/** + * @file src/rt/KIARA/rt.h + * @brief Main header file for the KIARA M1 Closure radiative transfer scheme. + */ + +/** + * @brief Compute the photon emission rates for this stellar particle. + * This function is called every time the spart is being reset + * (during start-up and during stars ghost if spart is active) + * and assumes that the photon emission rate is an intrinsic + * stellar property, i.e. doesn't depend on the environment. + * + * @param sp star particle to work on + * @param time current system time + * @param star_age age of the star *at the end of the step* + * @param dt star time step + * @param rt_props RT properties struct + * @param phys_const physical constants struct + * @param internal_units struct holding internal units + */ +__attribute__((always_inline)) INLINE static void +rt_compute_stellar_emission_rate(struct spart* restrict sp, double time, + double star_age, double dt, + const struct rt_props* rt_props, + const struct phys_const* phys_const, + const struct unit_system* internal_units) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + sp->rt_data.debug_emission_rate_set += 1; +#endif + + /* Skip initial fake time-step */ + if (dt == 0.0l) return; + + if (time == 0.l) { + /* if function is called before the first actual step, time is still + * at zero unless specified otherwise in parameter file.*/ + /* We're going to need the star age later for more sophistiscated models, + * but for now the compiler won't let me get away with keeping this here, + * so keep it as a comment. */ + star_age = dt; + } + + /* TODO: this is for later, when we use more sophisticated models. */ + /* now get the emission rates */ + double star_age_begin_of_step = star_age - dt; + star_age_begin_of_step = max(0.l, star_age_begin_of_step); + + double emission_this_step[RT_NGROUPS]; + for (int g = 0; g < RT_NGROUPS; g++) emission_this_step[g] = 0.; + + if (rt_props->stellar_emission_model == rt_stellar_emission_model_const) { + rt_get_emission_this_step_const(emission_this_step, + rt_props->stellar_const_emission_rates, dt); + } else if (rt_props->stellar_emission_model == + rt_stellar_emission_model_IlievTest) { + rt_get_emission_this_step_IlievTest( + emission_this_step, sp->mass, dt, rt_props->photon_number_integral, + rt_props->average_photon_energy, phys_const, internal_units); + } else if (rt_props->stellar_emission_model == + rt_stellar_emission_model_BPASS) { + rt_get_emission_this_step_BPASS( + emission_this_step, sp->mass, sp->chemistry_data.metal_mass_fraction_total, + star_age_begin_of_step, star_age, rt_props->ionizing_tables, + rt_props->average_photon_energy, phys_const, internal_units, rt_props->f_esc); + /* Convert some quantities. */ + const double time_to_Myr = units_cgs_conversion_factor(internal_units, UNIT_CONV_TIME) / + (365.25f * 24.f * 60.f * 60.f * 1e6f); + const double energy_units = + units_cgs_conversion_factor(internal_units, UNIT_CONV_ENERGY); + if(star_age_begin_of_step * time_to_Myr < 5) message("RT_energy: id=%lld dE=%g (%g erg) age_start=%g age_now=%g Z=%g", sp->id, emission_this_step[0], emission_this_step[0] * energy_units, star_age_begin_of_step * time_to_Myr, star_age * time_to_Myr, sp->chemistry_data.metal_mass_fraction_total); + + } else { + error("Unknown stellar emission rate model %d", + rt_props->stellar_emission_model); + } + + for (int g = 0; g < RT_NGROUPS; g++) + sp->rt_data.emission_this_step[g] = emission_this_step[g]; +} + +/** + * @brief Initialisation of the RT density loop related particle data. + * Note: during initalisation (space_init), rt_reset_part and rt_init_part + * are both called individually. + * + * @param p Particle to work on + */ +__attribute__((always_inline)) INLINE static void rt_init_part( + struct part* restrict p) {} + +/** + * @brief Reset the RT hydro particle data not related to the hydro density. + * Note: during initalisation (space_init), rt_reset_part and rt_init_part + * are both called individually. To reset RT data needed in each RT sub-cycle, + * use rt_reset_part_each_subcycle(). + * + * @param p particle to work on + * @param cosmo Cosmology. + */ +__attribute__((always_inline)) INLINE static void rt_reset_part( + struct part* restrict p, const struct cosmology* cosmo) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* reset this here as well as in the rt_debugging_checks_end_of_step() + * routine to test task dependencies are done right */ + p->rt_data.debug_iact_stars_inject = 0; + p->rt_data.debug_nsubcycles = 0; + p->rt_data.debug_kicked = 0; +#endif +} + +/** + * @brief Reset RT particle data which needs to be reset each sub-cycle. + * + * @param p the particle to work on + * @param cosmo Cosmology. + * @param dt the current particle RT time step + */ +__attribute__((always_inline)) INLINE static void rt_reset_part_each_subcycle( + struct part* restrict p, const struct cosmology* cosmo, double dt) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_debugging_reset_each_subcycle(p); +#endif + + rt_gradients_init(p); + /* the Gizmo-style slope limiting doesn't help for RT as is, + * so we're skipping it for now. */ + /* rt_slope_limit_cell_init(p); */ + + p->rt_data.flux_dt = dt; +} + +/** + * @brief First initialisation of the RT hydro particle data. + * + * @param p particle to work on + * @param cosmo #cosmology data structure. + * @param rt_props RT properties struct + */ +__attribute__((always_inline)) INLINE static void rt_first_init_part( + struct part* restrict p, const struct cosmology* cosmo, + const struct rt_props* restrict rt_props) { + + /* Don't reset conserved quantities here! ICs will be overwritten */ + rt_init_part(p); + rt_reset_part(p, cosmo); + rt_part_reset_mass_fluxes(p); + rt_reset_part_each_subcycle(p, cosmo, 0.); + rt_part_reset_fluxes(p); + +#ifdef SWIFT_RT_DEBUG_CHECKS + p->rt_data.debug_radiation_absorbed_tot = 0ULL; +#endif +} + +/** + * @brief Initialisation of the RT density loop related star particle data. + * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart + * are both called individually. + * + * @param sp star particle to work on + */ +__attribute__((always_inline)) INLINE static void rt_init_spart( + struct spart* restrict sp) { + + for (int i = 0; i < 8; i++) { + sp->rt_data.octant_weights[i] = 0.f; + } + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* reset this here as well as in the rt_debugging_checks_end_of_step() + * routine to test task dependencies are done right */ + sp->rt_data.debug_iact_hydro_inject_prep = 0; + sp->rt_data.debug_iact_hydro_inject = 0; + sp->rt_data.debug_emission_rate_set = 0; + + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.debug_injected_energy[g] = 0.f; + } + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.emission_this_step[g] = 0.f; + } + sp->rt_data.debug_psi_sum = 0.f; +#endif +} + +/** + * @brief Reset of the RT star particle data not related to the density. + * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart + * are both called individually. + * + * @param sp star particle to work on + */ +__attribute__((always_inline)) INLINE static void rt_reset_spart( + struct spart* restrict sp) { + + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.emission_this_step[g] = 0.f; + } +} + +/** + * @brief First initialisation of the RT star particle data. + * + * @param sp star particle to work on + */ +__attribute__((always_inline)) INLINE static void rt_first_init_spart( + struct spart* restrict sp) { + + rt_init_spart(sp); + rt_reset_spart(sp); +#ifdef SWIFT_RT_DEBUG_CHECKS + sp->rt_data.debug_radiation_emitted_tot = 0ULL; + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.debug_injected_energy_tot[g] = 0.f; + } +#endif +} + +/** + * @brief Split the RT data of a particle into n pieces + * + * @param p The #part. + * @param n The number of pieces to split into. + */ +__attribute__((always_inline)) INLINE static void rt_split_part(struct part* p, + double n) { + error("RT can't run with split particles for now."); +} + +/** + * @brief Exception handle a hydro part not having any neighbours in ghost task + * + * @param p The #part. + */ +__attribute__((always_inline)) INLINE static void rt_part_has_no_neighbours( + struct part* p) { + message("WARNING: found particle without neighbours"); +} + +/** + * @brief Exception handle a star part not having any neighbours in ghost task + * + * @param sp The #spart. + */ +__attribute__((always_inline)) INLINE static void rt_spart_has_no_neighbours( + struct spart* sp) { + + /* Reset energy to be injected so that global statistics + * checks still work */ + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.emission_this_step[g] = 0.f; + } + message("WARNING: found star without neighbours"); +} + +/** + * @brief Do checks/conversions on particles on startup. + * + * @param p The particle to work on + * @param rt_props The RT properties struct + * @param hydro_props The hydro properties struct + * @param phys_const physical constants struct + * @param us unit_system struct + * @param cosmo cosmology struct + */ +__attribute__((always_inline)) INLINE static void rt_convert_quantities( + struct part* restrict p, struct xpart* restrict xp, const struct rt_props* rt_props, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cooling_function_data* cooling, + const struct cosmology* restrict cosmo) { + + /* If we're reducing the speed of light, then we may encounter + * photon fluxes which are way too high than the physically + * allowable limit. This can lead to catastrophic problems for + * the propagation of photons, as the pressure tensor assumes + * the upper limit to be respected. So check this and correct + * it if necessary. + * We only read in conserved quantities, so only check those. */ + + struct rt_part_data* rtd = &p->rt_data; + const float Vinv = 1.f / p->geometry.volume; + + /* If we read in radiation energy, we read in + * total energy and store it as energy density. + * Same for fluxes. + * Correct that now. */ + for (int g = 0; g < RT_NGROUPS; g++) { + rtd->radiation[g].energy_density *= Vinv; + rtd->radiation[g].flux[0] *= Vinv; + rtd->radiation[g].flux[1] *= Vinv; + rtd->radiation[g].flux[2] *= Vinv; + + /* Additional check with possible exit for ICs */ + rt_check_unphysical_state_ICs(p, g, &rtd->radiation[g].energy_density, + rtd->radiation[g].flux, + phys_const->const_speed_light_c); + /* Check for too high fluxes */ + rt_check_unphysical_state(&rtd->radiation[g].energy_density, + rtd->radiation[g].flux, /*e_old =*/0.f, + /*callloc=*/0); + } + + /* If we're setting up ionising equilibrium initial conditions, + * then the particles need to have their densities known first. + * So we can call the mass fractions initialization now. */ + rt_tchem_first_init_part(p, xp, rt_props, hydro_props, phys_const, us, cooling, cosmo); +} + +/** + * @brief Computes the next radiative transfer time step size + * of a given particle (during timestep tasks) + * + * @param p Particle to work on. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. + */ +__attribute__((always_inline)) INLINE static float rt_compute_timestep( + const struct part* restrict p, const struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct cooling_function_data* restrict cooling, + const struct unit_system* restrict us) { + + /* If no radiation, it's not doing RT, so don't limit timestep */ + float radiation_energy_density[RT_NGROUPS], total_energy_density = 0.f; + rt_part_get_physical_radiation_energy_density(p, radiation_energy_density, cosmo); + for (int i=0; ia * cosmo->a * + powf(p->geometry.volume / hydro_dimension_unit_sphere, + hydro_dimension_inv); + float dt = psize * rt_params.reduced_speed_of_light_inverse * + rt_props->CFL_condition; + + if (rt_props->skip_thermochemistry) return dt; + + float dt_cool = 1e20f; + //if (rt_props->f_limit_cooling_time > 0.f) + /* Note: cooling time may be negative if the gas is being heated */ + // dt_cool = rt_props->f_limit_cooling_time * + // rt_tchem_get_tchem_time(p, xp, rt_props, cosmo, hydro_props, + // phys_const, cooling, us); + + return min(dt, fabsf(dt_cool)); +} + +/** + * @brief Computes the next radiative transfer time step size + * of a given star particle (during timestep tasks). + * + * @param sp spart to work on + * @param rt_props the RT properties struct + * @param cosmo the cosmology + */ +__attribute__((always_inline)) INLINE static float rt_compute_spart_timestep( + const struct spart* restrict sp, const struct rt_props* restrict rt_props, + const struct cosmology* restrict cosmo) { + + /* For now, the only thing we care about is the upper threshold for stars. */ + return rt_props->stars_max_timestep; +} + +/** + * @brief Compute the time-step length for an RT step of a particle from given + * integer times ti_beg and ti_end. This time-step length is then used to + * compute the actual time integration of the transport/force step and the + * thermochemistry. This is not used to determine the time-step length during + * the time-step tasks. + * + * @param ti_beg Start of the time-step (on the integer time-line). + * @param ti_end End of the time-step (on the integer time-line). + * @param time_base Minimal time-step size on the time-line. + * @param with_cosmology Are we running with cosmology integration? + * @param cosmo The #cosmology object. + * + * @return The time-step size for the rt integration. (internal units). + */ +__attribute__((always_inline)) INLINE static double rt_part_dt( + const integertime_t ti_beg, const integertime_t ti_end, + const double time_base, const int with_cosmology, + const struct cosmology* cosmo) { + if (with_cosmology) { + return cosmology_get_delta_time(cosmo, ti_beg, ti_end); + } else { + return (ti_end - ti_beg) * time_base; + } +} + +/** + * @brief Compute the time-step length for an RT step of a particle from given + * integer times ti_beg and ti_end. This time-step length is then used to + * compute the actual time integration of the transport/force step and the + * thermochemistry. This is not used to determine the time-step length during + * the time-step tasks. + * + * @param ti_beg Start of the time-step (on the integer time-line). + * @param ti_end End of the time-step (on the integer time-line). + * @param time_base Minimal time-step size on the time-line. + * @param with_cosmology Are we running with cosmology integration? + * @param cosmo The #cosmology object. + * + * @return The time-step size for the rt integration. (internal units). + */ +__attribute__((always_inline)) INLINE static double rt_part_dt_therm( + const integertime_t ti_beg, const integertime_t ti_end, + const double time_base, const int with_cosmology, + const struct cosmology* cosmo) { + if (with_cosmology) { + return cosmology_get_therm_kick_factor(cosmo, ti_beg, ti_end); + } else { + return (ti_end - ti_beg) * time_base; + } +} + +/** + * @brief This function finalises the injection step. + * + * @param p particle to work on + * @param props struct #rt_props that contains global RT properties + */ +__attribute__((always_inline)) INLINE static void rt_finalise_injection( + struct part* restrict p, struct rt_props* props) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_debug_sequence_check(p, 1, "rt_ghost1/rt_finalise_injection"); + + p->rt_data.debug_injection_done += 1; +#endif + + for (int g = 0; g < RT_NGROUPS; g++) { + rt_check_unphysical_state(&p->rt_data.radiation[g].energy_density, + p->rt_data.radiation[g].flux, /*e_old=*/0.f, + /*callloc=*/3); + } +} + +/** + * @brief finishes up the gradient computation + * + * @param p particle to work on + * @param cosmo #cosmology data structure. + */ +__attribute__((always_inline)) INLINE static void rt_end_gradient( + struct part* restrict p, const struct cosmology* cosmo) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_debug_sequence_check(p, 2, __func__); + + if (p->rt_data.debug_calls_iact_gradient_interaction == 0) + message( + "WARNING: Called finalise gradient on particle %lld" + "with iact gradient count from rt_iact = %d", + p->id, p->rt_data.debug_calls_iact_gradient_interaction); + + p->rt_data.debug_gradients_done += 1; +#endif + + rt_finalise_gradient_part(p); +} + +/** + * @brief finishes up the transport step + * + * @param p particle to work on + * @param dt the current time step of the particle + * @param cosmo #cosmology data structure. + */ +__attribute__((always_inline)) INLINE static void rt_finalise_transport( + struct part* restrict p, struct rt_props* rtp, const double dt, + const struct cosmology* restrict cosmo) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_debug_sequence_check(p, 3, __func__); + + if (p->rt_data.debug_calls_iact_transport_interaction == 0) + message( + "WARNING: Called finalise transport on particle %lld" + "with iact transport count from rt_iact = %d", + p->id, p->rt_data.debug_calls_iact_transport_interaction); + + p->rt_data.debug_transport_done += 1; +#endif + + struct rt_part_data* restrict rtd = &p->rt_data; + const float Vinv = 1.f / p->geometry.volume; + + /* Do not redshift if we have a constant spectrum (type == 0) */ + const float redshift_factor = + (rtp->stellar_spectrum_type == 0) ? 0. : cosmo->H * dt; + + for (int g = 0; g < RT_NGROUPS; g++) { + const float e_old = rtd->radiation[g].energy_density; + + /* Note: in this scheme, we're updating d/dt (U * V) + sum F * A * dt = 0. + * So we'll need the division by the volume here. */ + + rtd->radiation[g].energy_density += rtd->flux[g].energy * Vinv; + rtd->radiation[g].energy_density -= + rtd->radiation[g].energy_density * + redshift_factor; // Energy lost due to redshift + + rtd->radiation[g].flux[0] += rtd->flux[g].flux[0] * Vinv; + rtd->radiation[g].flux[0] -= + rtd->radiation[g].flux[0] * + redshift_factor; // Energy lost due to redshift + + rtd->radiation[g].flux[1] += rtd->flux[g].flux[1] * Vinv; + rtd->radiation[g].flux[1] -= + rtd->radiation[g].flux[1] * + redshift_factor; // Energy lost due to redshift + + rtd->radiation[g].flux[2] += rtd->flux[g].flux[2] * Vinv; + rtd->radiation[g].flux[2] -= + rtd->radiation[g].flux[2] * + redshift_factor; // Energy lost due to redshift + + rt_check_unphysical_state(&rtd->radiation[g].energy_density, + rtd->radiation[g].flux, e_old, /*callloc=*/4); + } + + /* Reset the fluxes now that they have been applied. */ + rt_part_reset_fluxes(p); + /* Mark the particle as inactive for now. */ + rtd->flux_dt = -1.f; +} + +/** + * @brief Do the thermochemistry on a particle. + * + * This function wraps around rt_do_thermochemistry function. + * + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param floor_props Properties of the entropy floor. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. + * @param dt_therm The time-step operator used for thermal quantities. + * @param time The current time (since the Big Bang or start of the run) in + * internal units. + */ +__attribute__((always_inline)) INLINE static void rt_tchem( + struct part* restrict p, struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, + const struct phys_const* restrict phys_const, + const struct cooling_function_data* restrict cooling, + const struct unit_system* restrict us, const double dt, + const double dt_therm, const double time) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_debug_sequence_check(p, 4, __func__); + p->rt_data.debug_thermochem_done += 1; +#endif + + if (rt_props->rt_with_galaxy_subgrid) { + rt_do_thermochemistry_with_subgrid(p, xp, rt_props, cosmo, hydro_props, floor_props, + phys_const, cooling, us, dt, dt_therm, + 0); + + /* Record this cooling event */ + xp->cooling_data.time_last_event = time; + } else { + /* Note: Can't pass rt_props as const struct because of grackle + * accessinging its properties there */ + rt_do_thermochemistry(p, xp, rt_props, cosmo, hydro_props, phys_const, cooling, us, dt, dt_therm, + 0); + }//RT_WITH_COOLING_SUBGRID we couple rt with some subgrid physics properties. +} + +/** + * @brief Extra operations done during the kick. This needs to be + * done before the particle mass is updated in the hydro_kick_extra. + * + * @param p Particle to act upon. + * @param dt_therm Thermal energy time-step @f$\frac{dt}{a^2}@f$. + * @param dt_grav Gravity time-step @f$\frac{dt}{a}@f$. + * @param dt_hydro Hydro acceleration time-step + * @f$\frac{dt}{a^{3(\gamma{}-1)}}@f$. + * @param dt_kick_corr Gravity correction time-step @f$adt@f$. + * @param cosmo Cosmology. + * @param hydro_props Additional hydro properties. + */ +__attribute__((always_inline)) INLINE static void rt_kick_extra( + struct part* p, float dt_therm, float dt_grav, float dt_hydro, + float dt_kick_corr, const struct cosmology* cosmo, + const struct hydro_props* hydro_props) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* Don't account for timestep_sync backward kicks */ + if (dt_therm >= 0.f && dt_grav >= 0.f && dt_hydro >= 0.f && + dt_kick_corr >= 0.f) { + + rt_debug_sequence_check(p, 0, __func__); + p->rt_data.debug_kicked += 1; + } +#endif + +#ifdef GIZMO_MFV_SPH + + /* Note: We need to mimick here what Gizmo does for the mass fluxes. + * The relevant time scale is the hydro time step for the mass fluxes, + * not the RT times. We also need to prevent the kick to apply the mass + * fluxes twice, so exit if the particle time step < 0 */ + + if (p->flux.dt > 0.0f) { + /* Update the mass fraction changes due to interparticle fluxes */ + const float current_mass = p->conserved.mass; + + if ((current_mass <= 0.f) || (p->rho <= 0.f)) { + /* Deal with vacuum. Let hydro deal with actuall mass < 0, just do your + * mass fractions thing. */ + p->rt_data.tchem.mass_fraction_HI = 0.f; + p->rt_data.tchem.mass_fraction_HII = 0.f; + p->rt_data.tchem.mass_fraction_HeI = 0.f; + p->rt_data.tchem.mass_fraction_HeII = 0.f; + p->rt_data.tchem.mass_fraction_HeIII = 0.f; + rt_part_reset_mass_fluxes(p); + return; + } + + const float current_mass_HI = + current_mass * p->rt_data.tchem.mass_fraction_HI; + const float current_mass_HII = + current_mass * p->rt_data.tchem.mass_fraction_HII; + const float current_mass_HeI = + current_mass * p->rt_data.tchem.mass_fraction_HeI; + const float current_mass_HeII = + current_mass * p->rt_data.tchem.mass_fraction_HeII; + const float current_mass_HeIII = + current_mass * p->rt_data.tchem.mass_fraction_HeIII; + + /* At this point, we're exchanging (time integrated) mass fluxes, + * which in rare cases can lead to unphysical results, i.e. negative + * masses. Make sure we prevent unphysical solutions propagating by + * enforcing a minumum of zero. */ + const float new_mass_HI = + max(current_mass_HI + p->rt_data.mass_flux.HI, 0.f); + const float new_mass_HII = + max(current_mass_HII + p->rt_data.mass_flux.HII, 0.f); + const float new_mass_HeI = + max(current_mass_HeI + p->rt_data.mass_flux.HeI, 0.f); + const float new_mass_HeII = + max(current_mass_HeII + p->rt_data.mass_flux.HeII, 0.f); + const float new_mass_HeIII = + max(current_mass_HeIII + p->rt_data.mass_flux.HeIII, 0.f); + + const float new_mass_tot = new_mass_HI + new_mass_HII + new_mass_HeI + + new_mass_HeII + new_mass_HeIII; + + /* During the initial fake time step, if the mass fractions haven't been set + * up yet, we could encounter divisions by zero here, so skip that. If it + * isn't the initial time step, the error will be caught down the line by + * another call to rt_check_unphysical_mass_fractions() (not the one 10 + * lines below this) */ + if (new_mass_tot == 0.f) return; + + const float new_mass_tot_inv = 1.f / new_mass_tot; + + p->rt_data.tchem.mass_fraction_HI = new_mass_HI * new_mass_tot_inv; + p->rt_data.tchem.mass_fraction_HII = new_mass_HII * new_mass_tot_inv; + p->rt_data.tchem.mass_fraction_HeI = new_mass_HeI * new_mass_tot_inv; + p->rt_data.tchem.mass_fraction_HeII = new_mass_HeII * new_mass_tot_inv; + p->rt_data.tchem.mass_fraction_HeIII = new_mass_HeIII * new_mass_tot_inv; + + /* Reset fluxes after they have been applied, so they can be collected + * again even when particle is inactive. */ + rt_part_reset_mass_fluxes(p); + + /* Don't update actual particle mass, that'll be done in the + * hydro_kick_extra calls */ + } + +#endif + + //rt_check_unphysical_mass_fractions(p); +} + +/** + * @brief Prepare a particle for the !HYDRO! force calculation. + * E.g. for the meshless schemes, we need to take into account the + * mass fluxes of the constituent species between particles. + * NOTE: don't call this during rt_init_part or rt_reset_part, + * follow the hydro_prepare_force logic. + * + * @param p particle to work on + **/ +__attribute__((always_inline)) INLINE static void rt_prepare_force( + struct part* p) {} + +/** + * @brief Extra operations to be done during the drift + * + * @param p Particle to act upon. + * @param xp The extended particle data to act upon. + * @param dt_drift The drift time-step for positions. + */ +__attribute__((always_inline)) INLINE static void rt_predict_extra( + struct part* p, struct xpart* xp, float dt_drift) { + + float dx[3] = {xp->v_full[0] * dt_drift, xp->v_full[1] * dt_drift, + xp->v_full[2] * dt_drift}; + + for (int g = 0; g < RT_NGROUPS; g++) { + float Unew[4]; + rt_gradients_predict_drift(p, Unew, g, dx); + p->rt_data.radiation[g].energy_density = Unew[0]; + p->rt_data.radiation[g].flux[0] = Unew[1]; + p->rt_data.radiation[g].flux[1] = Unew[2]; + p->rt_data.radiation[g].flux[2] = Unew[3]; + } +} + +/** + * @brief Clean the allocated memory inside the RT properties struct. + * + * @param props the #rt_props. + * @param restart did we restart? + */ +__attribute__((always_inline)) INLINE static void rt_clean( + struct rt_props* props, int restart) { + + /* If we were restarting, free-ing manually will lead to + * segfaults since we didn't malloc the stuff */ + if (!restart) { + /* Clean up grackle data. This is a call to a grackle function */ + local_free_chemistry_data(&props->grackle_chemistry_data, + &props->grackle_chemistry_rates); + + for (int g = 0; g < RT_NGROUPS; g++) { + free(props->energy_weighted_cross_sections[g]); + free(props->number_weighted_cross_sections[g]); + } + free(props->energy_weighted_cross_sections); + free(props->number_weighted_cross_sections); + } +} + +#endif /* SWIFT_RT_KIARA_H */ diff --git a/src/rt/KIARA/rt_additions.h b/src/rt/KIARA/rt_additions.h new file mode 100644 index 0000000000..349896dfa5 --- /dev/null +++ b/src/rt/KIARA/rt_additions.h @@ -0,0 +1,93 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_KIARA_ADDITIONS_H +#define SWIFT_RT_KIARA_ADDITIONS_H + +/** + * @file src/rt/KIARA/rt_additions.h + * @brief additional functions required for files outside of the RT modules + * moved to separate file to avoid circular inclusions + * */ + +/** + * @brief update mass fluxes between two interacting particles during + * hydro_iact_(non)sym(...) calls. + * + * @param pi first interacting particle + * @param pj second interacting particle + * @param mass_flux the mass flux between these two particles + * @param mode 0: non-symmetric interaction, update i only. 1: symmetric + * interaction. + **/ +__attribute__((always_inline)) INLINE static void rt_part_update_mass_fluxes( + struct part* restrict pi, struct part* restrict pj, float mass_flux, + int mode) { + + /* If a particle is losing mass, then it loses mass according to + * its own mass fractions. If it's gaining mass, it's gaining mass + * according to the interacting particle's mass fractions. */ + + /* get the time step for the flux exchange. This is always the smallest time + step among the two particles */ + const float mindt = + (pj->flux.dt > 0.0f) ? fminf(pi->flux.dt, pj->flux.dt) : pi->flux.dt; + + const float mdt = mass_flux * mindt; + + /* Convention: negative flux for "left" particle pi */ + if (mass_flux < 0.f) { + /* "left" particle is gaining mass */ + pi->rt_data.mass_flux.HI -= pj->rt_data.tchem.mass_fraction_HI * mdt; + pi->rt_data.mass_flux.HII -= pj->rt_data.tchem.mass_fraction_HII * mdt; + pi->rt_data.mass_flux.HeI -= pj->rt_data.tchem.mass_fraction_HeI * mdt; + pi->rt_data.mass_flux.HeII -= pj->rt_data.tchem.mass_fraction_HeII * mdt; + pi->rt_data.mass_flux.HeIII -= pj->rt_data.tchem.mass_fraction_HeIII * mdt; + } else { + /* "left" particle is losing mass */ + pi->rt_data.mass_flux.HI -= pi->rt_data.tchem.mass_fraction_HI * mdt; + pi->rt_data.mass_flux.HII -= pi->rt_data.tchem.mass_fraction_HII * mdt; + pi->rt_data.mass_flux.HeI -= pi->rt_data.tchem.mass_fraction_HeI * mdt; + pi->rt_data.mass_flux.HeII -= pi->rt_data.tchem.mass_fraction_HeII * mdt; + pi->rt_data.mass_flux.HeIII -= pi->rt_data.tchem.mass_fraction_HeIII * mdt; + } + + if (mode == 1 || (pj->flux.dt < 0.f)) { + /* Update right particle as well, even if it's inactive */ + + if (mass_flux > 0.f) { + /* "right" particle is gaining mass */ + pj->rt_data.mass_flux.HI += pi->rt_data.tchem.mass_fraction_HI * mdt; + pj->rt_data.mass_flux.HII += pi->rt_data.tchem.mass_fraction_HII * mdt; + pj->rt_data.mass_flux.HeI += pi->rt_data.tchem.mass_fraction_HeI * mdt; + pj->rt_data.mass_flux.HeII += pi->rt_data.tchem.mass_fraction_HeII * mdt; + pj->rt_data.mass_flux.HeIII += + pi->rt_data.tchem.mass_fraction_HeIII * mdt; + } else { + /* "right" particle is losing mass */ + pj->rt_data.mass_flux.HI += pj->rt_data.tchem.mass_fraction_HI * mdt; + pj->rt_data.mass_flux.HII += pj->rt_data.tchem.mass_fraction_HII * mdt; + pj->rt_data.mass_flux.HeI += pj->rt_data.tchem.mass_fraction_HeI * mdt; + pj->rt_data.mass_flux.HeII += pj->rt_data.tchem.mass_fraction_HeII * mdt; + pj->rt_data.mass_flux.HeIII += + pj->rt_data.tchem.mass_fraction_HeIII * mdt; + } + } +} + +#endif /* SWIFT_RT_KIARA_ADDITIONS_H */ diff --git a/src/rt/KIARA/rt_blackbody.h b/src/rt/KIARA/rt_blackbody.h new file mode 100644 index 0000000000..d702b60e10 --- /dev/null +++ b/src/rt/KIARA/rt_blackbody.h @@ -0,0 +1,88 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_KIARA_BLACKBODY_H +#define SWIFT_RT_KIARA_BLACKBODY_H + +/** + * @file src/rt/KIARA/rt_blackbody.h + * @brief Functions related to the blackbody spectrum + */ + +#include "inline.h" + +#include + +/** + * @brief Return the specific intensity of the blackbody spectrum + * + * @param nu frequency at which to compute specific intensity + * @param T temperature characterizing the spectrum + * @param kB Boltzmann constant + * @param h_planck Planck's constant + * @param c speed of light + */ +__attribute__((always_inline)) INLINE double blackbody_spectrum_intensity( + const double nu, const double T, const double kB, const double h_planck, + const double c) { + + const double hnu = h_planck * nu; + const double kT = kB * T; + const double nu2 = nu * nu; + double temp; + if (hnu / kT < 1e-6) { + /* prevent division by zero, use Taylor approximation */ + temp = kT; + } else if (hnu / kT > 700.) { + /* prevent infs */ + temp = 0.; + } else { + temp = 1. / (exp(hnu / kT) - 1.); + } + return 2. * hnu * nu2 / (c * c) * temp; +} + +/** + * Return the blackbody spectrum energy density + * + * @param nu frequency at which to compute specific intensity + * @param T temperature characterizing the spectrum + * @param kB Boltzmann constant + * @param h_planck Planck's constant + * @param c speed of light + */ +__attribute__((always_inline)) INLINE double blackbody_spectrum_energy_density( + const double nu, const double T, const double kB, const double h_planck, + const double c) { + return 4. * M_PI / c * blackbody_spectrum_intensity(nu, T, kB, h_planck, c); +} + +/** + * Return the frequency at which the blackbody spectrum at a given tempterature + * peaks. + * + * @param T temperature characterizing the spectrum + * @param kB Boltzmann constant + * @param h_planck Planck's constant + */ +__attribute__((always_inline)) INLINE double blackbody_peak_frequency( + const double T, const double kB, const double h_planck) { + return 2.82144 * kB * T / h_planck; +} + +#endif diff --git a/src/rt/KIARA/rt_debugging.h b/src/rt/KIARA/rt_debugging.h new file mode 100644 index 0000000000..84e070dd6b --- /dev/null +++ b/src/rt/KIARA/rt_debugging.h @@ -0,0 +1,362 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_DEBUGGING_KIARA_H +#define SWIFT_RT_DEBUGGING_KIARA_H + +#ifdef SWIFT_RT_DEBUG_CHECKS + +#include "active.h" +#include "rt_properties.h" +#include "timeline.h" + +/** + * @file src/rt/KIARA/rt_debugging.h + * @brief Main header file for the KIARA radiative transfer scheme + * extra debugging functions. + */ + +/** + * @brief Check whether RT time step size is valid compared to the hydro one. + * + * @param p particle to work on + * @param dti_rt current RT integer time step + * @param dti_hydro current hydro integer time step + * @param max_nr_rt_subcycles max number of RT sub-cycles. + * @param time_base minimal time step in this run + */ +__attribute__((always_inline)) INLINE static void rt_debugging_check_timestep( + const struct part *restrict p, integertime_t *dti_rt, + const integertime_t *dti_hydro, int max_nr_rt_subcycles, double time_base) { + + if (*dti_hydro < *dti_rt) + error("part %lld has hydro time (%lld/%g) < RT time step (%lld/%g", p->id, + *dti_hydro, *dti_hydro * time_base, *dti_rt, *dti_rt * time_base); +} + +/** + * @brief This resets particle carried quantities after each subcycling + * step such that the internal checks are still consistent. + * @param p the particle to work on + */ +__attribute__((always_inline)) INLINE static void rt_debugging_count_subcycle( + struct part *restrict p) { + p->rt_data.debug_nsubcycles += 1; +} + +/** + * @brief Check that the particle completed the correct number of subcycles. + * This is checked in every rt_reset_part, before the subcycling count is reset. + * @param p the particle to work on + * @param rt_props RT properties struct + */ +__attribute__((always_inline)) INLINE static void +rt_debugging_check_nr_subcycles(struct part *restrict p, + const struct rt_props *rt_props) { + + /* TODO: this check may fail when running with limiter/sync. */ + + /* NOTE: we need to do this check somewhere in the hydro tasks. + * (1) it needs to be done when all tasks are active and before the + * particle hydro time step is changed. + * (2) If you do it during RT tasks, it won't properly check how + * many sub-cycles you did during a single hydro task. + * (3) You can't do it during the timestep task, since between + * the hydro and the timestep we already do an RT step. */ + + /* skip initialization */ + if (p->time_bin == 0) return; + if (p->rt_time_data.time_bin == 0) + error("Got part %lld with RT time bin 0", p->id); + + timebin_t bindiff = p->time_bin - p->rt_time_data.time_bin; + + if (rt_props->debug_max_nr_subcycles <= 1) { + /* Running without subcycling. */ + if (bindiff != 0) + error("Running without subcycling but got bindiff=%d for part=%lld", + bindiff, p->id); + if (p->rt_data.debug_nsubcycles != 1) + error("Running without subcycling but got part=%lld subcycle count=%d", + p->id, p->rt_data.debug_nsubcycles); + return; + } +} + +/** + * @brief This resets particle carried quantities after each subcycling + * step such that the internal checks are still consistent. + * @param p the particle to work on + */ +__attribute__((always_inline)) INLINE static void +rt_debugging_reset_each_subcycle(struct part *restrict p) { + + p->rt_data.debug_calls_iact_gradient_interaction = 0; + p->rt_data.debug_calls_iact_transport_interaction = 0; + + p->rt_data.debug_injection_done = 0; + p->rt_data.debug_gradients_done = 0; + p->rt_data.debug_transport_done = 0; + p->rt_data.debug_thermochem_done = 0; +} + +/** + * @brief Debugging checks loop over all star particles after each time step + */ +static void rt_debugging_end_of_step_stars_mapper(void *restrict map_data, + int scount, + void *restrict extra_data) { + + struct spart *restrict sparts = (struct spart *)map_data; + const struct engine *restrict e = (struct engine *)extra_data; + + unsigned long long emission_sum_this_step = 0ULL; + unsigned long long emission_sum_tot = 0ULL; + + for (int k = 0; k < scount; k++) { + + struct spart *restrict sp = &sparts[k]; + emission_sum_this_step += sp->rt_data.debug_iact_hydro_inject; + emission_sum_tot += sp->rt_data.debug_radiation_emitted_tot; + /* Reset all values here in case stars won't be active next step */ + sp->rt_data.debug_iact_hydro_inject = 0; + sp->rt_data.debug_iact_hydro_inject_prep = 0; + +#ifndef WITH_MPI + /* These checks will fail with MPI since we're not + * sending data back */ + for (int g = 0; g < RT_NGROUPS; g++) { + /* also check now that we actually injected the correct + * amount of energy + * sp->rt_data.emission_this_step: energy we should distribute + * this step + * sp->rt_data.debug_injected_energy: energy we actually did + * distribute this step */ + if (sp->rt_data.debug_injected_energy[g] != 0.f) { + float diff = 1.f - sp->rt_data.emission_this_step[g] / + sp->rt_data.debug_injected_energy[g]; + + if (fabsf(diff) > 1e-4) { + /* Dividing the total into several parts and summing them up again + * while hoping to obtain the same results may lead to diappointment + * due to roundoff errors. Check that the sum of the individual + * weights and the ones we collected for the injection are close + * enough. */ + float psi_sum_now = 0.f; + for (int i = 0; i < 8; i++) + psi_sum_now += sp->rt_data.octant_weights[i]; + float diff_weights = 1.f - sp->rt_data.debug_psi_sum / psi_sum_now; + if (fabsf(diff_weights) > 1e-4) + message( + "Incorrect injection ID %lld: " + "group %d expected %.3g got %.3g diff %.3g diff_weights %.3g", + sp->id, g, sp->rt_data.emission_this_step[g], + sp->rt_data.debug_injected_energy[g], diff, diff_weights); + } + } + } +#endif + + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.debug_injected_energy[g] = 0.f; + } + for (int g = 0; g < RT_NGROUPS; g++) { + sp->rt_data.emission_this_step[g] = 0.f; + } + } + + atomic_add(&e->rt_props->debug_radiation_emitted_this_step, + emission_sum_this_step); + atomic_add(&e->rt_props->debug_radiation_emitted_tot, emission_sum_tot); +} + +/** + * @brief Debugging checks loop over all hydro particles after each time step + */ +static void rt_debugging_end_of_step_hydro_mapper(void *restrict map_data, + int count, + void *restrict extra_data) { + + struct part *restrict parts = (struct part *)map_data; + const struct engine *restrict e = (struct engine *)extra_data; + + unsigned long long absorption_sum_this_step = 0ULL; + unsigned long long absorption_sum_tot = 0ULL; + float energy_sum[RT_NGROUPS]; + for (int g = 0; g < RT_NGROUPS; g++) energy_sum[g] = 0.f; + + for (int k = 0; k < count; k++) { + + struct part *restrict p = &parts[k]; + absorption_sum_this_step += p->rt_data.debug_iact_stars_inject; + absorption_sum_tot += p->rt_data.debug_radiation_absorbed_tot; + + /* Reset all values here in case particles won't be active next step */ + p->rt_data.debug_iact_stars_inject = 0; + + /* Sum up total energies for budget */ + for (int g = 0; g < RT_NGROUPS; g++) { + energy_sum[g] += + p->rt_data.radiation[g].energy_density * p->geometry.volume; + } + } + + atomic_add(&e->rt_props->debug_radiation_absorbed_this_step, + absorption_sum_this_step); + atomic_add(&e->rt_props->debug_radiation_absorbed_tot, absorption_sum_tot); +} + +/** + * @brief At the end of each time step, loop over both hydro and star + * particles and do whatever checks for this particular time step you + * want done. + * + * @param e The #engine. + * @param verbose Are we talkative? + */ +__attribute__((always_inline)) INLINE static void +rt_debugging_checks_end_of_step(struct engine *e, int verbose) { + + struct space *s = e->s; + if (!(e->policy & engine_policy_rt)) return; + + const ticks tic = getticks(); + + /* reset values before the particle loops. + * reset total counts as well. We track the totals since the beginning + * of time in particles individually. */ + e->rt_props->debug_radiation_emitted_this_step = 0ULL; + e->rt_props->debug_radiation_absorbed_this_step = 0ULL; + e->rt_props->debug_radiation_emitted_tot = 0ULL; + e->rt_props->debug_radiation_absorbed_tot = 0ULL; + + /* hydro particle loop */ + if (s->nr_parts > 0) + threadpool_map(&e->threadpool, rt_debugging_end_of_step_hydro_mapper, + s->parts, s->nr_parts, sizeof(struct part), + threadpool_auto_chunk_size, /*extra_data=*/e); + + /* star particle loop */ + if (s->nr_sparts > 0) + threadpool_map(&e->threadpool, rt_debugging_end_of_step_stars_mapper, + s->sparts, s->nr_sparts, sizeof(struct spart), + threadpool_auto_chunk_size, /*extra_data=*/e); + +#ifdef WITH_MPI + /* Since we aren't sending data back, none of these checks will + * pass a run over MPI. Make sure you run the threadpool functions + * first though so certain variables can get reset properly. */ + return; +#endif + + /* Have we accidentally invented or deleted some radiation somewhere? */ + + if ((e->rt_props->debug_radiation_emitted_this_step != + e->rt_props->debug_radiation_absorbed_this_step) || + (e->rt_props->debug_radiation_emitted_tot != + e->rt_props->debug_radiation_absorbed_tot)) + error( + "Emitted and absorbed radiation vary.\n" + " This step: star emission %12lld; gas absorption %12lld\n" + "Since start: star emission %12lld; gas absorption %12lld", + e->rt_props->debug_radiation_emitted_this_step, + e->rt_props->debug_radiation_absorbed_this_step, + e->rt_props->debug_radiation_emitted_tot, + e->rt_props->debug_radiation_absorbed_tot); + + if (verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + +/** + * @brief Perform a series of consistency and sanity checks. + * + * @param p particle to check + * @param loc location where this is called from. This determines which checks + * will be done: + * + * 0: during kicks/after drifts. + * 1: during rt_ghost1/finalise_injection / after kicks. + * 2: during gradients / after injection. + * 3: during transport / after gradients. + * 4: during thermochem / after transport. + * 5: after thermochem. + * + * @param function_name: Function name (or message) you want printed on error. + */ +__attribute__((always_inline)) INLINE static void rt_debug_sequence_check( + struct part *restrict p, int loc, const char *function_name) { + + /* Note: Checking whether a particle has been drifted at all is not + * compatible with subcycling. There is no reliable point where to + * reset the counters and have sensible results. */ + + if (loc > 0) { + /* Are kicks done? */ + if (p->rt_data.debug_nsubcycles == 0) { + if (p->rt_data.debug_kicked != 1) + error( + "called %s on particle %lld with wrong kick count=%d (expected " + "1) cycle=%d", + function_name, p->id, p->rt_data.debug_kicked, + p->rt_data.debug_nsubcycles); + } else if (p->rt_data.debug_nsubcycles > 0) { + /* This covers case 1, 2, 3, 4, 5 */ + if (p->rt_data.debug_kicked != 2) + error( + "called %s on particle %lld with wrong kick count=%d (expected 2) " + "cycle=%d", + function_name, p->id, p->rt_data.debug_kicked, + p->rt_data.debug_nsubcycles); + } else { + error("Got negative subcycle???"); + } + } + + if (loc > 1) { + /* is injection done? */ + if (p->rt_data.debug_injection_done != 1) + error("called %s on part %lld when finalise injection count is %d ID", + function_name, p->id, p->rt_data.debug_injection_done); + } + + if (loc > 2) { + /* are gradients done? */ + if (p->rt_data.debug_gradients_done != 1) + error("called %s on part %lld when gradients_done count is %d", + function_name, p->id, p->rt_data.debug_gradients_done); + } + + if (loc > 3) { + /* is transport done? */ + if (p->rt_data.debug_transport_done != 1) + error("called %s on part %lld when transport_done != 1: %d", + function_name, p->id, p->rt_data.debug_transport_done); + } + + if (loc > 4) { + /* is thermochemistry done? */ + if (p->rt_data.debug_thermochem_done != 1) + error("called %s on part %lld with thermochem_done count=%d", + function_name, p->id, p->rt_data.debug_thermochem_done); + } +} + +#endif /* SWIFT_RT_DEBUG_CHECKS */ +#endif /* SWIFT_RT_DEBUGGING_KIARA_H */ diff --git a/src/rt/KIARA/rt_flux.h b/src/rt/KIARA/rt_flux.h new file mode 100644 index 0000000000..8386f261c0 --- /dev/null +++ b/src/rt/KIARA/rt_flux.h @@ -0,0 +1,114 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ + +#ifndef SWIFT_KIARA_RT_FLUX_H +#define SWIFT_KIARA_RT_FLUX_H + +#if defined(RT_RIEMANN_SOLVER_GLF) +#include "rt_riemann_GLF.h" +#elif defined(RT_RIEMANN_SOLVER_HLL) +#include "rt_riemann_HLL.h" +#else +#error "No valid choice of RT Riemann solver has been selected" +#endif + +#include "rt_unphysical.h" + +/** + * @file src/rt/KIARA/rt_flux.h + * @brief Functions related to compute the interparticle flux term of the + * conservation law. This is its own file so we can switch between Riemann + * solvers more easily. + */ + +/** + * @brief Reset the fluxes for the given particle. + * + * @param p Particle. + */ +__attribute__((always_inline)) INLINE static void rt_part_reset_fluxes( + struct part* restrict p) { + + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.flux[g].energy = 0.f; + p->rt_data.flux[g].flux[0] = 0.f; + p->rt_data.flux[g].flux[1] = 0.f; + p->rt_data.flux[g].flux[2] = 0.f; + } +} + +/** + * @brief Compute the flux between a left state UL and a right + * state UR along the direction of the unit vector n_unit + * through a surface of size Anorm. + * + * @param UL left state (energy density, fluxes) + * @param UR right state (energy density, fluxes) + * @param n_unit unit vector of the direction of the surface + * @param Anorm size of the surface through which the flux goes + * @param fluxes (return) the resulting flux + */ +__attribute__((always_inline)) INLINE static void rt_compute_flux( + float UL[4], float UR[4], const float n_unit[3], const float Anorm, + float fluxes[4]) { + + /* Unphysical check not necessary here. + * It's done in gradients_predict as well. */ + + const float FLnorm = sqrtf(UL[1] * UL[1] + UL[2] * UL[2] + UL[3] * UL[3]); + const float FRnorm = sqrtf(UR[1] * UR[1] + UR[2] * UR[2] + UR[3] * UR[3]); + + /* Get the fluxes in the hyperbolic conservation law sense. */ + float hyperFluxL[4][3]; + rt_get_hyperbolic_flux(UL, FLnorm, hyperFluxL); + float hyperFluxR[4][3]; + rt_get_hyperbolic_flux(UR, FRnorm, hyperFluxR); + +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_check_unphysical_hyperbolic_flux(hyperFluxL); + rt_check_unphysical_hyperbolic_flux(hyperFluxR); +#endif + + rt_riemann_solve_for_flux(UL, UR, FLnorm, FRnorm, hyperFluxL, hyperFluxR, + n_unit, fluxes); + + /* get the actual flux */ + fluxes[0] *= Anorm; + fluxes[1] *= Anorm; + fluxes[2] *= Anorm; + fluxes[3] *= Anorm; +} + +/** + * @brief reset the mass fluxes of constituent species for a particle. + * + * @param p particle to work on. + **/ +__attribute__((always_inline)) INLINE static void rt_part_reset_mass_fluxes( + struct part* restrict p) { +#ifdef GIZMO_MFV_SPH + p->rt_data.mass_flux.HI = 0.f; + p->rt_data.mass_flux.HII = 0.f; + p->rt_data.mass_flux.HeI = 0.f; + p->rt_data.mass_flux.HeII = 0.f; + p->rt_data.mass_flux.HeIII = 0.f; +#endif +} + +#endif /* SWIFT_KIARA_RT_FLUX_H */ diff --git a/src/rt/KIARA/rt_getters.h b/src/rt/KIARA/rt_getters.h new file mode 100644 index 0000000000..4e53261adc --- /dev/null +++ b/src/rt/KIARA/rt_getters.h @@ -0,0 +1,230 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ + +#ifndef SWIFT_KIARA_RT_GETTERS_H +#define SWIFT_KIARA_RT_GETTERS_H + +#include "rt_parameters.h" + +/** + * @file src/rt/KIARA/rt_getters.h + * @brief Getter functions for KIARA RT scheme to keep code clean and lean + */ + +/** + * @brief Get the comoving radiation energy densities of a particle. + * + * @param p Particle. + * @param E (return) Pointer to the array in which the result needs to be stored + */ +__attribute__((always_inline)) INLINE static void +rt_part_get_comoving_radiation_energy_density(const struct part *restrict p, + float E[RT_NGROUPS]) { + + for (int g = 0; g < RT_NGROUPS; g++) { + E[g] = p->rt_data.radiation[g].energy_density; + } +} + +/** + * @brief Get the physical radiation energy densities of a particle + * + * @param p Particle. + * @param E (return) Pointer to the array in which the result needs to be stored + */ +__attribute__((always_inline)) INLINE static void +rt_part_get_physical_radiation_energy_density(const struct part *restrict p, + float E[RT_NGROUPS], + const struct cosmology *cosmo) { + for (int g = 0; g < RT_NGROUPS; g++) { + E[g] = cosmo->a3_inv * p->rt_data.radiation[g].energy_density; + } +} + +/** + * @brief Get a 4-element state vector U containing the radiation energy + * density and fluxes for a specific photon group. + * + * @param p Particle. + * @param group Index of photon group + * @param U Pointer to the array in which the result needs to be stored + */ +__attribute__((always_inline)) INLINE static void +rt_part_get_radiation_state_vector(const struct part *restrict p, int group, + float U[4]) { + + U[0] = p->rt_data.radiation[group].energy_density; + U[1] = p->rt_data.radiation[group].flux[0]; + U[2] = p->rt_data.radiation[group].flux[1]; + U[3] = p->rt_data.radiation[group].flux[2]; +} + +/** + * @brief Get the gradients of energy density and fluxes for a given photon + * group + * + * @param p Particle. + * @param group Index of photon group + * @param dE (return) Array to write energy density gradient into + * @param dFx (return) Array to write flux x component gradient into + * @param dFy (return) Array to write flux y component gradient into + * @param dFz (return) Array to write flux z component gradient into + */ +__attribute__((always_inline)) INLINE static void rt_part_get_gradients( + const struct part *restrict p, int group, float dE[3], float dFx[3], + float dFy[3], float dFz[3]) { + + dE[0] = p->rt_data.gradient[group].energy_density[0]; + dE[1] = p->rt_data.gradient[group].energy_density[1]; + dE[2] = p->rt_data.gradient[group].energy_density[2]; + + dFx[0] = p->rt_data.gradient[group].flux[0][0]; + dFx[1] = p->rt_data.gradient[group].flux[0][1]; + dFx[2] = p->rt_data.gradient[group].flux[0][2]; + + dFy[0] = p->rt_data.gradient[group].flux[1][0]; + dFy[1] = p->rt_data.gradient[group].flux[1][1]; + dFy[2] = p->rt_data.gradient[group].flux[1][2]; + + dFz[0] = p->rt_data.gradient[group].flux[2][0]; + dFz[1] = p->rt_data.gradient[group].flux[2][1]; + dFz[2] = p->rt_data.gradient[group].flux[2][2]; +} + +/** + * @brief compute the pressure tensor for a given radiation state U + * + * @param U the state (radiation energy density, radiation flux) to use + * @param Fnorm the norm of the radiation flux + * @param pressure_tensor (return) 3x3 array to write resulting Eddington + * pressure tensor into + */ +__attribute__((always_inline)) INLINE static void rt_get_pressure_tensor( + const float U[4], const float Fnorm, float pressure_tensor[3][3]) { + + /* We may encounter zero flux even with nonzero energy. + * Also even with nonzero flux, the norm may round down + * to exactly zero, so exit early if that is the case. */ + if (Fnorm == 0.f) { + const float diagonal_element = U[0] / 3.f; + pressure_tensor[0][0] = diagonal_element; + pressure_tensor[0][1] = 0.f; + pressure_tensor[0][2] = 0.f; + pressure_tensor[1][0] = 0.f; + pressure_tensor[1][1] = diagonal_element; + pressure_tensor[1][2] = 0.f; + pressure_tensor[2][0] = 0.f; + pressure_tensor[2][1] = 0.f; + pressure_tensor[2][2] = diagonal_element; + return; + } + + /* f mustn't be > 1. This may happen because of the reduced speed of light. + * Energy density U[0] is nonzero at this point, or this function wouldn't + * have been called. */ + const float f = + min(1.f, rt_params.reduced_speed_of_light_inverse * Fnorm / U[0]); + const float f2 = f * f; + const float rootterm = 4.f - 3.f * f2; + const float chi = (3.f + 4.f * f2) / (5.f + 2.f * sqrtf(rootterm)); + + /* get unit vector n */ + const float Fnorm_inv = 1.f / Fnorm; + const float n[3] = {U[1] * Fnorm_inv, U[2] * Fnorm_inv, U[3] * Fnorm_inv}; + + const float temp = 0.5f * (3.f * chi - 1.f); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + pressure_tensor[i][j] = temp * n[i] * n[j]; + } + } + + const float temp2 = 0.5f * (1.f - chi); + pressure_tensor[0][0] += temp2; + pressure_tensor[1][1] += temp2; + pressure_tensor[2][2] += temp2; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + pressure_tensor[i][j] *= U[0]; + } + } + +#ifdef SWIFT_RT_DEBUG_CHECKS + if (pressure_tensor[0][0] != pressure_tensor[0][0]) { + message( + "Found NaNs in pressure tensor: 1/c %.3e |" + " |F| %.3e f %.3e f^2 %.3e root %.3e chi %.3e |" + " n %.3e %.3e %.3e | U %.3e %.3e %.3e |" + " temp %.3e %.3e", + rt_params.reduced_speed_of_light_inverse, Fnorm, f, f2, rootterm, chi, + n[0], n[1], n[2], U[1], U[2], U[3], temp, temp2); + } +#endif +} + +/** + * @brief compute the flux of the hyperbolic conservation law for a given + * state U + * + * @param U the state (radiation energy density, radiation flux) to use + * @param Fnorm the norm of the radiation flux + * @param hypflux (return) resulting flux F(U) of the hyperbolic conservation + * law + */ +__attribute__((always_inline)) INLINE static void rt_get_hyperbolic_flux( + const float U[4], const float Fnorm, float hypflux[4][3]) { + + if (U[0] == 0.f) { + /* At this point, the state U has been corrected to not contain + * unphysical values. If we encounter this situation, it means + * that the fluxes are zero as well, meaning that when we compute + * 1/|F| we get infinities. So skip this. The pressure tensor is + * P_ij = D_ij * E_i anyway. */ + + for (int i = 0; i < 4; i++) { + hypflux[i][0] = 0.f; + hypflux[i][1] = 0.f; + hypflux[i][2] = 0.f; + } + return; + } + + hypflux[0][0] = U[1]; + hypflux[0][1] = U[2]; + hypflux[0][2] = U[3]; + + float pressure_tensor[3][3]; + rt_get_pressure_tensor(U, Fnorm, pressure_tensor); + + const float c_red = rt_params.reduced_speed_of_light; + const float c2 = c_red * c_red; + hypflux[1][0] = pressure_tensor[0][0] * c2; + hypflux[1][1] = pressure_tensor[0][1] * c2; + hypflux[1][2] = pressure_tensor[0][2] * c2; + hypflux[2][0] = pressure_tensor[1][0] * c2; + hypflux[2][1] = pressure_tensor[1][1] * c2; + hypflux[2][2] = pressure_tensor[1][2] * c2; + hypflux[3][0] = pressure_tensor[2][0] * c2; + hypflux[3][1] = pressure_tensor[2][1] * c2; + hypflux[3][2] = pressure_tensor[2][2] * c2; +} + +#endif diff --git a/src/rt/KIARA/rt_grackle_utils.h b/src/rt/KIARA/rt_grackle_utils.h new file mode 100644 index 0000000000..971f9b618c --- /dev/null +++ b/src/rt/KIARA/rt_grackle_utils.h @@ -0,0 +1,540 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_GRACKLE_UTILS_H +#define SWIFT_RT_GRACKLE_UTILS_H + +/* skip deprecation warnings. I cleaned old API calls. */ +//#define OMIT_LEGACY_INTERNAL_GRACKLE_FUNC + +/* need hydro gamma */ +#include "hydro.h" + +#include + +/* need to rework (and check) code if changed */ +#define FIELD_SIZE 1 +#define DUST_MODEL 1 + +/** + * @file src/rt/KIARA/rt_grackle_utils.h + * @brief Utility and helper functions related to using grackle. + */ + +/** + * @brief Update grackle units during run + * + * @param grackle_units grackle units struct + * @param cosmo cosmology struct + * + * NOTE: In the current implementation, this function does nothing. + * However, there might be use-cases in the future (e.g. switching + * UV background on or off depending on redshift) that might be + * needed in the future, which can be implemented into this function. + */ +__attribute__((always_inline)) INLINE void update_grackle_units_cosmo( + code_units *grackle_units, const struct unit_system *us, + const struct cosmology *restrict cosmo) { + /*TODO: add the if statement when it is not in cosmology mode. */ + grackle_units->a_value = cosmo->a; + +} + +/** + * @brief initialize grackle during rt_props_init + * + * @param grackle_units grackle units struct to fill up correctly. + * @param grackle_chemistry_dat grackle chemistry data struct to fill up + *correctly. + * @param hydrogen_mass_fraction global hydrogen mass fraction. + * @param grackle_verb run grackle in verbose mode? + * @param case_B_recombination use grackle with case B recombination? + * @param us #unit_system struct + **/ +__attribute__((always_inline)) INLINE static void rt_init_grackle( + code_units *grackle_units, chemistry_data *grackle_chemistry_data, + chemistry_data_storage *grackle_chemistry_rates, + float hydrogen_mass_fraction, const int grackle_verb, + const int case_B_recombination, const struct unit_system *us, + const struct cosmology *restrict cosmo) { + + grackle_verbose = grackle_verb; + + /* Initialize units */ + /* ---------------- */ + /* we assume all quantities to be physical, not comoving */ + grackle_units->a_units = 1.0; + grackle_units->a_value = 0.01; + grackle_units->comoving_coordinates = 0; + grackle_units->density_units = + units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + grackle_units->length_units = + units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); + grackle_units->time_units = units_cgs_conversion_factor(us, UNIT_CONV_TIME); + /* Set velocity units */ + //set_velocity_units(grackle_units); + grackle_units->velocity_units = + units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); + + /* Chemistry Parameters */ + /* -------------------- */ + /* More details on + * https://grackle.readthedocs.io/en/grackle-3.2.0/Integration.html#chemistry-data + */ + + if (local_initialize_chemistry_parameters(grackle_chemistry_data) == 0) { + error("Error in set_default_chemistry_parameters."); + } + + /* chemistry on */ + grackle_chemistry_data->use_grackle = 2; + /* cooling on */ + /* NOTE: without cooling on, it also won't heat... */ + grackle_chemistry_data->with_radiative_cooling = 1; + /* 6 species atomic H and He */ + grackle_chemistry_data->primordial_chemistry = COOLING_GRACKLE_MODE; + /* No dust processes */ + grackle_chemistry_data->dust_chemistry = 0; + /* No H2 formation on dust */ + grackle_chemistry_data->h2_on_dust = 0; + /* metal cooling (uses Cloudy) off (for now) */ + grackle_chemistry_data->metal_cooling = 1; + /* no cooling below CMB temperature */ + grackle_chemistry_data->cmb_temperature_floor = 1; + /* UV background off */ + grackle_chemistry_data->UVbackground = 0; + /* data file - currently not used */ + grackle_chemistry_data->grackle_data_file = "CloudyData_UVB=FG2011_shielded.h5"; + /* adiabatic index */ + grackle_chemistry_data->Gamma = hydro_gamma; + /* we'll provide grackle with ionization and heating rates from RT */ + grackle_chemistry_data->use_radiative_transfer = 1; + + //volumetric heating rates is being provided in the volumetric_heating_rate + // field of grackle_field_data + grackle_chemistry_data->use_volumetric_heating_rate = 0; + // specific heating rates is being provided in the specific_heating_rate field + // of grackle_field_data + grackle_chemistry_data->use_specific_heating_rate = 1; + // Set parameters of temperature floor: 0=none, 1=provide scalar, 2=provide array + grackle_chemistry_data->use_temperature_floor = 2; + // control behaviour of Grackle sub-step integrator + grackle_chemistry_data->max_iterations = 300; + grackle_chemistry_data->exit_after_iterations_exceeded = 0; + + grackle_chemistry_data->use_subcycle_timestep_damping = 0; + grackle_chemistry_data->subcycle_timestep_damping_interval = 0; + + // Use Rahmati+13 self-shielding; 0=none, 1=HI only, 2=HI+HeI, 3=HI+HeI but + // set HeII rates to 0 + grackle_chemistry_data->self_shielding_method = 0; + grackle_chemistry_data->accuracy = 0.2; + + // Turn on Li+ 2019 dust evolution model + grackle_chemistry_data->use_dust_evol = 1; + grackle_chemistry_data->use_dust_density_field = 1; + + if (DUST_MODEL) { + + grackle_chemistry_data->dust_destruction_eff = 0.3; + grackle_chemistry_data->sne_coeff = 1.0; + grackle_chemistry_data->sne_shockspeed = 100.0; + grackle_chemistry_data->dust_grainsize = 0.1; + grackle_chemistry_data->dust_growth_densref = 1.673e-24; + grackle_chemistry_data->dust_growth_tauref = 1.0; + // Enable dust temperature calculation using ISRF + grackle_chemistry_data->metal_cooling = 1; + grackle_chemistry_data->dust_chemistry = 1; + grackle_chemistry_data->h2_on_dust = 1; + grackle_chemistry_data->use_isrf_field = 1; + grackle_chemistry_data->H2_self_shielding = 4; + grackle_chemistry_data->H2_custom_shielding = 2; // 2 means we specify the H2 shielding length ourselves ( the gas smoothing length) + // Solar abundances to pass to Grackle + grackle_chemistry_data->SolarAbundances[0]=0.2485; // He (10.93 in units where log[H]=12, so photospheric mass fraction -> Y=0.2485 [Hydrogen X=0.7381]; Anders+Grevesse Y=0.2485, X=0.7314) + grackle_chemistry_data->SolarAbundances[1]=2.38e-3; // C (8.43 -> 2.38e-3, AG=3.18e-3) + grackle_chemistry_data->SolarAbundances[2]=0.70e-3; // N (7.83 -> 0.70e-3, AG=1.15e-3) + grackle_chemistry_data->SolarAbundances[3]=5.79e-3; // O (8.69 -> 5.79e-3, AG=9.97e-3) + grackle_chemistry_data->SolarAbundances[4]=1.26e-3; // Ne (7.93 -> 1.26e-3, AG=1.72e-3) + grackle_chemistry_data->SolarAbundances[5]=7.14e-4; // Mg (7.60 -> 7.14e-4, AG=6.75e-4) + grackle_chemistry_data->SolarAbundances[6]=6.71e-3; // Si (7.51 -> 6.71e-4, AG=7.30e-4) + grackle_chemistry_data->SolarAbundances[7]=3.12e-4; // S (7.12 -> 3.12e-4, AG=3.80e-4) + grackle_chemistry_data->SolarAbundances[8]=0.65e-4; // Ca (6.34 -> 0.65e-4, AG=0.67e-4) + grackle_chemistry_data->SolarAbundances[9]=1.31e-3; // Fe (7.50 -> 1.31e-3, AG=1.92e-3) + } else { + grackle_chemistry_data->use_dust_evol = 0; + } + + /* fraction by mass of Hydrogen in the metal-free portion of the gas */ + //grackle_chemistry_data->HydrogenFractionByMass = hydrogen_mass_fraction; + /* Use case B recombination? (On-the-spot approximation) */ + grackle_chemistry_data->CaseBRecombination = case_B_recombination; + + if (local_initialize_chemistry_data(grackle_chemistry_data, + grackle_chemistry_rates, + grackle_units) == 0) { + error("Error in initialize_chemistry_data"); + } +} + +/** + * @brief fill out a grackle field struct with the relevant (gas) data from a + *particle + * + * @param grackle_fields (return) grackle field to copy into + * @param density array of particle density + * @param internal_energy array of particle internal_energy + * @param species_densities array of species densities of particle (HI, HII, + *HeI, HeII, HeIII, e-) + * @param iact_rates array of interaction rates (heating, 3 ioniziation, H2 + *dissociation) + * + **/ +__attribute__((always_inline)) INLINE static void +rt_get_grackle_particle_fields(grackle_field_data *grackle_fields, + gr_float density, gr_float internal_energy, + gr_float species_densities[6], + gr_float iact_rates[5]) { + + int *dimension = malloc(3 * sizeof(int)); + int *start = malloc(3 * sizeof(int)); + int *end = malloc(3 * sizeof(int)); + + dimension[0] = FIELD_SIZE; + dimension[1] = 0; + dimension[2] = 0; + start[0] = 0; + start[1] = 0; + start[2] = 0; + end[0] = FIELD_SIZE - 1; + end[1] = 0; + end[2] = 0; + + grackle_fields->grid_dx = 0.; + grackle_fields->grid_rank = 3; + grackle_fields->grid_dimension = dimension; + grackle_fields->grid_start = start; + grackle_fields->grid_end = end; + + /* Set initial quantities */ + grackle_fields->density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->internal_energy = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->x_velocity = NULL; + grackle_fields->y_velocity = NULL; + grackle_fields->z_velocity = NULL; + /* for primordial_chemistry >= 1 */ + grackle_fields->HI_density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->HII_density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->HeI_density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->HeII_density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->HeIII_density = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->e_density = malloc(FIELD_SIZE * sizeof(gr_float)); + /* for primordial_chemistry >= 2 */ + grackle_fields->HM_density = NULL; + grackle_fields->H2I_density = NULL; + grackle_fields->H2II_density = NULL; + /* for primordial_chemistry >= 3 */ + grackle_fields->DI_density = NULL; + grackle_fields->DII_density = NULL; + grackle_fields->HDI_density = NULL; + /* for metal_cooling = 1 */ + grackle_fields->metal_density = NULL; + /* for use_dust_density_field = 1 */ + grackle_fields->dust_density = NULL; + + /* volumetric heating rate (provide in units [erg s^-1 cm^-3]) */ + grackle_fields->volumetric_heating_rate = NULL; + /* specific heating rate (provide in units [egs s^-1 g^-1] */ + grackle_fields->specific_heating_rate = NULL; + + /* radiative transfer ionization / dissociation rate fields (provide in units + * [1/s]) */ + grackle_fields->RT_HI_ionization_rate = malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->RT_HeI_ionization_rate = + malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->RT_HeII_ionization_rate = + malloc(FIELD_SIZE * sizeof(gr_float)); + grackle_fields->RT_H2_dissociation_rate = + malloc(FIELD_SIZE * sizeof(gr_float)); + /* radiative transfer heating rate field + * (provide in units [erg s^-1 cm^-3] / nHI in cgs) */ + grackle_fields->RT_heating_rate = malloc(FIELD_SIZE * sizeof(gr_float)); + + grackle_fields->H2_self_shielding_length = NULL; + grackle_fields->H2_custom_shielding_factor = NULL; + grackle_fields->isrf_habing = NULL; + + for (int i = 0; i < FIELD_SIZE; i++) { + + grackle_fields->density[i] = density; + grackle_fields->internal_energy[i] = internal_energy; + + grackle_fields->HI_density[i] = species_densities[0]; + grackle_fields->HII_density[i] = species_densities[1]; + grackle_fields->HeI_density[i] = species_densities[2]; + grackle_fields->HeII_density[i] = species_densities[3]; + grackle_fields->HeIII_density[i] = species_densities[4]; + /* e_density = electron density*mh/me = n_e * m_h */ + grackle_fields->e_density[i] = species_densities[5]; + + /* grackle_fields->HM_density[i] = species_densities[6]; */ + /* grackle_fields->H2I_density[i] = species_densities[7]; */ + /* grackle_fields->H2II_density[i] = species_densities[8]; */ + /* grackle_fields->DI_density[i] = species_densities[9]; */ + /* grackle_fields->DII_density[i] = species_densities[10]; */ + /* grackle_fields->HDI_density[i] = species_densities[11]; */ + + /* grackle_fields->metal_density[i] = 0.0; */ + /* solar metallicity */ + /* grackle_chemistry_data.SolarMetalFractionByMass * + * grackle_fields->density[i]; */ + + /* grackle_fields->x_velocity[i] = 0.0; */ + /* grackle_fields->y_velocity[i] = 0.0; */ + /* grackle_fields->z_velocity[i] = 0.0; */ + + /* grackle_fields->volumetric_heating_rate[i] = 0.0; */ + /* grackle_fields->specific_heating_rate[i] = 0.0; */ + + grackle_fields->RT_heating_rate[i] = iact_rates[0]; + grackle_fields->RT_HI_ionization_rate[i] = iact_rates[1]; + grackle_fields->RT_HeI_ionization_rate[i] = iact_rates[2]; + grackle_fields->RT_HeII_ionization_rate[i] = iact_rates[3]; + grackle_fields->RT_H2_dissociation_rate[i] = iact_rates[4]; + } +} + +/** + * @brief free arrays allocated in grackle_fields. + * + * @param grackle_fields grackle fields to clean up + * + **/ +__attribute__((always_inline)) INLINE static void rt_clean_grackle_fields( + grackle_field_data *grackle_fields) { + + free(grackle_fields->grid_dimension); + free(grackle_fields->grid_start); + free(grackle_fields->grid_end); + + /* initial quantities */ + free(grackle_fields->density); + free(grackle_fields->internal_energy); + /* free(grackle_fields->x_velocity); */ + /* free(grackle_fields->y_velocity); */ + /* free(grackle_fields->z_velocity); */ + + /* for primordial_chemistry >= 1 */ + free(grackle_fields->HI_density); + free(grackle_fields->HII_density); + free(grackle_fields->HeI_density); + free(grackle_fields->HeII_density); + free(grackle_fields->HeIII_density); + free(grackle_fields->e_density); + + /* for primordial_chemistry >= 2 */ + /* free(grackle_fields->HM_density); */ + /* free(grackle_fields->H2I_density); */ + /* free(grackle_fields->H2II_density); */ + + /* for primordial_chemistry >= 3 */ + /* free(grackle_fields->DI_density); */ + /* free(grackle_fields->DII_density); */ + /* free(grackle_fields->HDI_density); */ + + /* for metal_cooling = 1 */ + /* free(grackle_fields->metal_density); */ + + /* for use_dust_density_field = 1 */ + /* free(grackle_fields->dust_density); */ + + /* free(grackle_fields->volumetric_heating_rate); */ + /* free(grackle_fields->specific_heating_rate); */ + + free(grackle_fields->RT_HI_ionization_rate); + free(grackle_fields->RT_HeI_ionization_rate); + free(grackle_fields->RT_HeII_ionization_rate); + free(grackle_fields->RT_H2_dissociation_rate); + free(grackle_fields->RT_heating_rate); + + /* free(grackle_fields->H2_self_shielding_length); */ + /* free(grackle_fields->H2_custom_shielding_factor); */ + /* free(grackle_fields->isrf_habing); */ +} + +/** + * @brief Write out all available grackle field data for a given index + * and setup to a file. + * This function is intended for debugging. + * + * @param fp FILE pointer to write into. + * @param grackle_fields grackle field data + * @param grackle_chemistry_data grackle chemistry data. + * @param grackle_units units used by grackle + * @param field_index grackle field index to print out. + **/ +__attribute__((always_inline)) INLINE static void +rt_write_grackle_setup_and_field(FILE *fp, grackle_field_data grackle_fields, + chemistry_data *grackle_chemistry_data, + code_units *grackle_units, int field_index) { + + fprintf(fp, "Grackle chemistry parameters:\n"); + + fprintf(fp, "use_grackle = %d\n", + grackle_chemistry_data->use_grackle); + fprintf(fp, "with_radiative_cooling = %d\n", + grackle_chemistry_data->with_radiative_cooling); + fprintf(fp, "primordial_chemistry = %d\n", + grackle_chemistry_data->primordial_chemistry); + fprintf(fp, "dust_chemistry = %d\n", + grackle_chemistry_data->dust_chemistry); + fprintf(fp, "metal_cooling = %d\n", + grackle_chemistry_data->metal_cooling); + fprintf(fp, "UVbackground = %d\n", + grackle_chemistry_data->UVbackground); + fprintf(fp, "grackle_data_file = %s\n", + grackle_chemistry_data->grackle_data_file); + fprintf(fp, "cmb_temperature_floor = %d\n", + grackle_chemistry_data->cmb_temperature_floor); + fprintf(fp, "Gamma = %g\n", + grackle_chemistry_data->Gamma); + fprintf(fp, "h2_on_dust = %d\n", + grackle_chemistry_data->h2_on_dust); + fprintf(fp, "use_dust_density_field = %d\n", + grackle_chemistry_data->use_dust_density_field); + fprintf(fp, "dust_recombination_cooling = %d\n", + grackle_chemistry_data->dust_recombination_cooling); + fprintf(fp, "photoelectric_heating = %d\n", + grackle_chemistry_data->photoelectric_heating); + fprintf(fp, "photoelectric_heating_rate = %g\n", + grackle_chemistry_data->photoelectric_heating_rate); + fprintf(fp, "use_isrf_field = %d\n", + grackle_chemistry_data->use_isrf_field); + fprintf(fp, "interstellar_radiation_field = %g\n", + grackle_chemistry_data->interstellar_radiation_field); + fprintf(fp, "use_volumetric_heating_rate = %d\n", + grackle_chemistry_data->use_volumetric_heating_rate); + fprintf(fp, "use_specific_heating_rate = %d\n", + grackle_chemistry_data->use_specific_heating_rate); + fprintf(fp, "three_body_rate = %d\n", + grackle_chemistry_data->three_body_rate); + fprintf(fp, "cie_cooling = %d\n", + grackle_chemistry_data->cie_cooling); + fprintf(fp, "h2_optical_depth_approximation = %d\n", + grackle_chemistry_data->h2_optical_depth_approximation); + fprintf(fp, "ih2co = %d\n", + grackle_chemistry_data->ih2co); + fprintf(fp, "ipiht = %d\n", + grackle_chemistry_data->ipiht); + fprintf(fp, "HydrogenFractionByMass = %g\n", + grackle_chemistry_data->HydrogenFractionByMass); + fprintf(fp, "DeuteriumToHydrogenRatio = %g\n", + grackle_chemistry_data->DeuteriumToHydrogenRatio); + fprintf(fp, "SolarMetalFractionByMass = %g\n", + grackle_chemistry_data->SolarMetalFractionByMass); + fprintf(fp, "local_dust_to_gas_ratio = %g\n", + grackle_chemistry_data->local_dust_to_gas_ratio); + fprintf(fp, "NumberOfTemperatureBins = %d\n", + grackle_chemistry_data->NumberOfTemperatureBins); + fprintf(fp, "CaseBRecombination = %d\n", + grackle_chemistry_data->CaseBRecombination); + fprintf(fp, "TemperatureStart = %g\n", + grackle_chemistry_data->TemperatureStart); + fprintf(fp, "TemperatureEnd = %g\n", + grackle_chemistry_data->TemperatureEnd); + fprintf(fp, "NumberOfDustTemperatureBins = %d\n", + grackle_chemistry_data->NumberOfDustTemperatureBins); + fprintf(fp, "DustTemperatureStart = %g\n", + grackle_chemistry_data->DustTemperatureStart); + fprintf(fp, "DustTemperatureEnd = %g\n", + grackle_chemistry_data->DustTemperatureEnd); + fprintf(fp, "Compton_xray_heating = %d\n", + grackle_chemistry_data->Compton_xray_heating); + fprintf(fp, "LWbackground_sawtooth_suppression = %d\n", + grackle_chemistry_data->LWbackground_sawtooth_suppression); + fprintf(fp, "LWbackground_intensity = %g\n", + grackle_chemistry_data->LWbackground_intensity); + fprintf(fp, "UVbackground_redshift_on = %g\n", + grackle_chemistry_data->UVbackground_redshift_on); + fprintf(fp, "UVbackground_redshift_off = %g\n", + grackle_chemistry_data->UVbackground_redshift_off); + fprintf(fp, "UVbackground_redshift_fullon = %g\n", + grackle_chemistry_data->UVbackground_redshift_fullon); + fprintf(fp, "UVbackground_redshift_drop = %g\n", + grackle_chemistry_data->UVbackground_redshift_drop); + fprintf(fp, "cloudy_electron_fraction_factor = %g\n", + grackle_chemistry_data->cloudy_electron_fraction_factor); + fprintf(fp, "use_radiative_transfer = %d\n", + grackle_chemistry_data->use_radiative_transfer); + fprintf(fp, "radiative_transfer_coupled_rate_solver = %d\n", + grackle_chemistry_data->radiative_transfer_coupled_rate_solver); + fprintf(fp, "radiative_transfer_intermediate_step = %d\n", + grackle_chemistry_data->radiative_transfer_intermediate_step); + fprintf(fp, "radiative_transfer_hydrogen_only = %d\n", + grackle_chemistry_data->radiative_transfer_hydrogen_only); + fprintf(fp, "self_shielding_method = %d\n", + grackle_chemistry_data->self_shielding_method); + fprintf(fp, "H2_custom_shielding = %d\n", + grackle_chemistry_data->H2_custom_shielding); + fprintf(fp, "H2_self_shielding = %d\n", + grackle_chemistry_data->H2_self_shielding); + + fprintf(fp, "\nUnits:\n"); + fprintf(fp, "a_units = %g\n", grackle_units->a_units); + fprintf(fp, "a_value = %g\n", grackle_units->a_value); + fprintf(fp, "comoving_coordinates = %d\n", + grackle_units->comoving_coordinates); + fprintf(fp, "density_units = %g\n", grackle_units->density_units); + fprintf(fp, "length_units = %g\n", grackle_units->length_units); + fprintf(fp, "time_units = %g\n", grackle_units->time_units); + fprintf(fp, "velocity_units = %g\n", grackle_units->velocity_units); + +#define rt_print_grackle_field(v) \ + if (grackle_fields.v != NULL) \ + fprintf(fp, "grackle_fields." #v " = %g\n", grackle_fields.v[field_index]) + + fprintf(fp, "\nGrackle field data:\n"); + rt_print_grackle_field(density); + rt_print_grackle_field(internal_energy); + rt_print_grackle_field(HI_density); + rt_print_grackle_field(HII_density); + rt_print_grackle_field(HeI_density); + rt_print_grackle_field(HeII_density); + rt_print_grackle_field(HeIII_density); + rt_print_grackle_field(e_density); + rt_print_grackle_field(HM_density); + rt_print_grackle_field(H2I_density); + rt_print_grackle_field(H2II_density); + rt_print_grackle_field(DI_density); + rt_print_grackle_field(DII_density); + rt_print_grackle_field(HDI_density); + rt_print_grackle_field(metal_density); + rt_print_grackle_field(x_velocity); + rt_print_grackle_field(y_velocity); + rt_print_grackle_field(z_velocity); + rt_print_grackle_field(volumetric_heating_rate); + rt_print_grackle_field(specific_heating_rate); + rt_print_grackle_field(RT_HI_ionization_rate); + rt_print_grackle_field(RT_HeI_ionization_rate); + rt_print_grackle_field(RT_HeII_ionization_rate); + rt_print_grackle_field(RT_H2_dissociation_rate); + rt_print_grackle_field(RT_heating_rate); + +#undef rt_print_grackle_field +} + +#endif /* SWIFT_RT_GRACKLE_UTILS_H */ diff --git a/src/rt/KIARA/rt_gradients.h b/src/rt/KIARA/rt_gradients.h new file mode 100644 index 0000000000..60ca44b3bb --- /dev/null +++ b/src/rt/KIARA/rt_gradients.h @@ -0,0 +1,474 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_GRADIENTS_KIARA_H +#define SWIFT_RT_GRADIENTS_KIARA_H + +#include "fvpm_geometry.h" +#include "rt_getters.h" +/* #include "rt_slope_limiters_cell.h" [> skipped for now <] */ +#include "rt_slope_limiters_face.h" +#include "rt_unphysical.h" + +/** + * @file src/rt/KIARA/rt_gradients.h + * @brief Main header file for the KIARA M1 closure radiative transfer scheme + * gradients + */ + +/** + * @brief Initialisation of the RT gradient data. + * + * @param p particle to work on + */ +__attribute__((always_inline)) INLINE static void rt_gradients_init( + struct part *restrict p) { + + struct rt_part_data *rtd = &p->rt_data; + + for (int g = 0; g < RT_NGROUPS; g++) { + for (int i = 0; i < 3; i++) { + rtd->gradient[g].energy_density[i] = 0.f; + rtd->gradient[g].flux[i][0] = 0.f; + rtd->gradient[g].flux[i][1] = 0.f; + rtd->gradient[g].flux[i][2] = 0.f; + } + } +} + +/** + * @brief Update the gradients for the given particle with the + * given contributions. + * + * @param p Particle + * @param g photon group index to update (0 <= g < RT_NGROUPS) + * @param dE energy density gradient + * @param dFx gradient of the x direction flux component + * @param dFy gradient of the y direction flux component + * @param dFz gradient of the z direction flux component + */ +__attribute__((always_inline)) INLINE static void rt_gradients_update_part( + struct part *restrict p, int g, float dE[3], float dFx[3], float dFy[3], + float dFz[3]) { + + struct rt_part_data *rtd = &p->rt_data; + + rtd->gradient[g].energy_density[0] += dE[0]; + rtd->gradient[g].energy_density[1] += dE[1]; + rtd->gradient[g].energy_density[2] += dE[2]; + + rtd->gradient[g].flux[0][0] += dFx[0]; + rtd->gradient[g].flux[0][1] += dFx[1]; + rtd->gradient[g].flux[0][2] += dFx[2]; + + rtd->gradient[g].flux[1][0] += dFy[0]; + rtd->gradient[g].flux[1][1] += dFy[1]; + rtd->gradient[g].flux[1][2] += dFy[2]; + + rtd->gradient[g].flux[2][0] += dFz[0]; + rtd->gradient[g].flux[2][1] += dFz[1]; + rtd->gradient[g].flux[2][2] += dFz[2]; +} + +/** + * @brief Finalise the gradient computation after all + * particle-particle interactions are finished. + * + * @param p the Particle + **/ + +__attribute__((always_inline)) INLINE static void rt_finalise_gradient_part( + struct part *restrict p) { + + /* add kernel normalization to gradients */ + const float h = p->h; + const float h_inv = 1.0f / h; + + float norm; + if (fvpm_part_geometry_well_behaved(p)) { + const float hinvdim = pow_dimension(h_inv); + norm = hinvdim; + } else { + const float hinvdimp1 = pow_dimension_plus_one(h_inv); + const float volume = p->geometry.volume; + norm = hinvdimp1 * volume; + } + + struct rt_part_data *rtd = &p->rt_data; + for (int g = 0; g < RT_NGROUPS; g++) { + rtd->gradient[g].energy_density[0] *= norm; + rtd->gradient[g].energy_density[1] *= norm; + rtd->gradient[g].energy_density[2] *= norm; + for (int i = 0; i < 3; i++) { + rtd->gradient[g].flux[i][0] *= norm; + rtd->gradient[g].flux[i][1] *= norm; + rtd->gradient[g].flux[i][2] *= norm; + } + } + + /* the Gizmo-style slope limiting doesn't help for RT as is, + * so we're skipping it for now. */ + /* rt_slope_limit_cell(p); */ +} + +/** + * @brief symmetric gradient calculations done during the neighbour loop + * + * @param r2 Squared distance between the two particles. + * @param dx Distance vector (pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. + */ +__attribute__((always_inline)) INLINE static void rt_gradients_collect( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_debug_sequence_check(pi, 2, __func__); + rt_debug_sequence_check(pj, 2, __func__); + + pi->rt_data.debug_calls_iact_gradient_interaction += 1; + pj->rt_data.debug_calls_iact_gradient_interaction += 1; +#endif + + /* Get r and 1/r. */ + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + float Bi[3][3]; + float Bj[3][3]; + + for (int k = 0; k < 3; k++) { + for (int l = 0; l < 3; l++) { + Bi[k][l] = pi->geometry.matrix_E[k][l]; + Bj[k][l] = pj->geometry.matrix_E[k][l]; + } + } + + /* Compute kernel of pi. */ + float wi, wi_dx; + const float hi_inv = 1.0f / hi; + const float qi = r * hi_inv; + /* Note: factor 1/omega for psi is swallowed in matrix */ + kernel_deval(qi, &wi, &wi_dx); + + /* Compute kernel of pj */ + float wj, wj_dx; + const float hj_inv = 1.0f / hj; + const float qj = r * hj_inv; + kernel_deval(qj, &wj, &wj_dx); + + /* Compute psi tilde */ + float psii_tilde[3]; + if (fvpm_part_geometry_well_behaved(pi)) { + psii_tilde[0] = + wi * (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]); + psii_tilde[1] = + wi * (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]); + psii_tilde[2] = + wi * (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]); + } else { + const float norm = -wi_dx * r_inv; + psii_tilde[0] = norm * dx[0]; + psii_tilde[1] = norm * dx[1]; + psii_tilde[2] = norm * dx[2]; + } + + float psij_tilde[3]; + if (fvpm_part_geometry_well_behaved(pj)) { + psij_tilde[0] = + wi * (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); + psij_tilde[1] = + wi * (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); + psij_tilde[2] = + wi * (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]); + } else { + const float norm = -wj_dx * r_inv; + psij_tilde[0] = norm * dx[0]; + psij_tilde[1] = norm * dx[1]; + psij_tilde[2] = norm * dx[2]; + } + + for (int g = 0; g < RT_NGROUPS; g++) { + + float Ui[4], Uj[4]; + rt_part_get_radiation_state_vector(pi, g, Ui); + rt_part_get_radiation_state_vector(pj, g, Uj); + const float dU[4] = {Ui[0] - Uj[0], Ui[1] - Uj[1], Ui[2] - Uj[2], + Ui[3] - Uj[3]}; + + /* First to the gradients of pi */ + float dE_i[3], dFx_i[3], dFy_i[3], dFz_i[3]; + + /* Compute gradients for pi */ + /* there is a sign difference w.r.t. eqn. (6) because of the inverse + * definition of dx */ + dE_i[0] = dU[0] * psii_tilde[0]; + dE_i[1] = dU[0] * psii_tilde[1]; + dE_i[2] = dU[0] * psii_tilde[2]; + + dFx_i[0] = dU[1] * psii_tilde[0]; + dFx_i[1] = dU[1] * psii_tilde[1]; + dFx_i[2] = dU[1] * psii_tilde[2]; + dFy_i[0] = dU[2] * psii_tilde[0]; + dFy_i[1] = dU[2] * psii_tilde[1]; + dFy_i[2] = dU[2] * psii_tilde[2]; + dFz_i[0] = dU[3] * psii_tilde[0]; + dFz_i[1] = dU[3] * psii_tilde[1]; + dFz_i[2] = dU[3] * psii_tilde[2]; + + rt_gradients_update_part(pi, g, dE_i, dFx_i, dFy_i, dFz_i); + /* the Gizmo-style slope limiting doesn't help for RT as is, + * so we're skipping it for now. */ + /* rt_slope_limit_cell_collect(pi, pj, g); */ + + /* Now do the gradients of pj */ + float dE_j[3], dFx_j[3], dFy_j[3], dFz_j[3]; + + /* We don't need a sign change here: both the dx and the dU + * should switch their sign, resulting in no net change */ + dE_j[0] = dU[0] * psij_tilde[0]; + dE_j[1] = dU[0] * psij_tilde[1]; + dE_j[2] = dU[0] * psij_tilde[2]; + + dFx_j[0] = dU[1] * psij_tilde[0]; + dFx_j[1] = dU[1] * psij_tilde[1]; + dFx_j[2] = dU[1] * psij_tilde[2]; + dFy_j[0] = dU[2] * psij_tilde[0]; + dFy_j[1] = dU[2] * psij_tilde[1]; + dFy_j[2] = dU[2] * psij_tilde[2]; + dFz_j[0] = dU[3] * psij_tilde[0]; + dFz_j[1] = dU[3] * psij_tilde[1]; + dFz_j[2] = dU[3] * psij_tilde[2]; + + rt_gradients_update_part(pj, g, dE_j, dFx_j, dFy_j, dFz_j); + /* the Gizmo-style slope limiting doesn't help for RT as is, + * so we're skipping it for now. */ + /* rt_slope_limit_cell_collect(pj, pi, g); */ + } +} + +/** + * @brief Non-symmetric gradient calculations done during the neighbour loop + * + * @param r2 Squared distance between the two particles. + * @param dx Distance vector (pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. + */ +__attribute__((always_inline)) INLINE static void rt_gradients_nonsym_collect( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + rt_debug_sequence_check(pi, 2, __func__); + pi->rt_data.debug_calls_iact_gradient_interaction += 1; +#endif + + /* Get r and 1/r. */ + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + float Bi[3][3]; + + for (int k = 0; k < 3; k++) { + for (int l = 0; l < 3; l++) { + Bi[k][l] = pi->geometry.matrix_E[k][l]; + } + } + + /* Compute kernel of pi. */ + float wi, wi_dx; + const float hi_inv = 1.0f / hi; + const float qi = r * hi_inv; + /* factor 1/omega for psi is swallowed in matrix */ + kernel_deval(qi, &wi, &wi_dx); + + /* Compute psi tilde */ + float psii_tilde[3]; + if (fvpm_part_geometry_well_behaved(pi)) { + psii_tilde[0] = + wi * (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]); + psii_tilde[1] = + wi * (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]); + psii_tilde[2] = + wi * (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]); + } else { + const float norm = -wi_dx * r_inv; + psii_tilde[0] = norm * dx[0]; + psii_tilde[1] = norm * dx[1]; + psii_tilde[2] = norm * dx[2]; + } + + for (int g = 0; g < RT_NGROUPS; g++) { + + float Ui[4], Uj[4]; + rt_part_get_radiation_state_vector(pi, g, Ui); + rt_part_get_radiation_state_vector(pj, g, Uj); + const float dU[4] = {Ui[0] - Uj[0], Ui[1] - Uj[1], Ui[2] - Uj[2], + Ui[3] - Uj[3]}; + + float dE_i[3], dFx_i[3], dFy_i[3], dFz_i[3]; + + /* Compute gradients for pi */ + /* there is a sign difference w.r.t. eqn. (6) because of the inverse + * definition of dx */ + dE_i[0] = dU[0] * psii_tilde[0]; + dE_i[1] = dU[0] * psii_tilde[1]; + dE_i[2] = dU[0] * psii_tilde[2]; + + dFx_i[0] = dU[1] * psii_tilde[0]; + dFx_i[1] = dU[1] * psii_tilde[1]; + dFx_i[2] = dU[1] * psii_tilde[2]; + dFy_i[0] = dU[2] * psii_tilde[0]; + dFy_i[1] = dU[2] * psii_tilde[1]; + dFy_i[2] = dU[2] * psii_tilde[2]; + dFz_i[0] = dU[3] * psii_tilde[0]; + dFz_i[1] = dU[3] * psii_tilde[1]; + dFz_i[2] = dU[3] * psii_tilde[2]; + + rt_gradients_update_part(pi, g, dE_i, dFx_i, dFy_i, dFz_i); + /* the Gizmo-style slope limiting doesn't help for RT as is, + * so we're skipping it for now. */ + /* rt_slope_limit_cell_collect(pi, pj, g); */ + } +} + +/** + * @brief Extrapolate the given gradient over the given distance. + * + * @param dU Gradient of the quantity + * @param dx Distance vector + * @return Change in the quantity after a displacement along the given distance + * vector. + */ +__attribute__((always_inline)) INLINE static float rt_gradients_extrapolate( + const float dU[3], const float dx[3]) { + + return dU[0] * dx[0] + dU[1] * dx[1] + dU[2] * dx[2]; +} + +/** + * @brief Gradients reconstruction. Predict the value at point x_ij given + * current values at particle positions and gradients at particle positions. + * + * @param pi Particle i + * @param pj Particle j + * @param Ui (return) Resulting predicted and limited radiation state of + * particle i + * @param Uj (return) Resulting predicted and limited radiation state of + * particle j + * @param group which photon group to use + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param r Comoving distance between particle i and particle j. + * @param xij_i Position of the "interface" w.r.t. position of particle i + */ +__attribute__((always_inline)) INLINE static void rt_gradients_predict( + const struct part *restrict pi, const struct part *restrict pj, float Ui[4], + float Uj[4], int group, const float dx[3], const float r, + const float xij_i[3]) { + + rt_part_get_radiation_state_vector(pi, group, Ui); + rt_part_get_radiation_state_vector(pj, group, Uj); + /* No need to check unphysical state here: + * they haven't been touched since the call + * to rt_injection_update_photon_density */ + + float dE_i[3], dFx_i[3], dFy_i[3], dFz_i[3]; + float dE_j[3], dFx_j[3], dFy_j[3], dFz_j[3]; + rt_part_get_gradients(pi, group, dE_i, dFx_i, dFy_i, dFz_i); + rt_part_get_gradients(pj, group, dE_j, dFx_j, dFy_j, dFz_j); + + /* Compute interface position (relative to pj, since we don't need the actual + * position) eqn. (8) + * Do it this way in case dx contains periodicity corrections already */ + const float xij_j[3] = {xij_i[0] + dx[0], xij_i[1] + dx[1], xij_i[2] + dx[2]}; + + float dUi[4]; + dUi[0] = rt_gradients_extrapolate(dE_i, xij_i); + dUi[1] = rt_gradients_extrapolate(dFx_i, xij_i); + dUi[2] = rt_gradients_extrapolate(dFy_i, xij_i); + dUi[3] = rt_gradients_extrapolate(dFz_i, xij_i); + + float dUj[4]; + dUj[0] = rt_gradients_extrapolate(dE_j, xij_j); + dUj[1] = rt_gradients_extrapolate(dFx_j, xij_j); + dUj[2] = rt_gradients_extrapolate(dFy_j, xij_j); + dUj[3] = rt_gradients_extrapolate(dFz_j, xij_j); + + /* Apply the slope limiter at this interface */ + rt_slope_limit_face(Ui, Uj, dUi, dUj, dx, r, xij_i, xij_j); + + Ui[0] += dUi[0]; + Ui[1] += dUi[1]; + Ui[2] += dUi[2]; + Ui[3] += dUi[3]; + + Uj[0] += dUj[0]; + Uj[1] += dUj[1]; + Uj[2] += dUj[2]; + Uj[3] += dUj[3]; + + /* Check and correct unphysical extrapolated states */ + rt_check_unphysical_state(Ui, &Ui[1], /*e_old=*/0.f, /*callloc=*/1); + rt_check_unphysical_state(Uj, &Uj[1], /*e_old=*/0.f, /*callloc=*/1); +} + +/** + * @brief Gradients reconstruction. Predict the value at point x_ij given + * current values at particle positions and gradients at particle positions. + * + * @param p Particle to work on + * @param U (return) Resulting predicted and limited radiation state of + * particle + * @param group which photon group to use + * @param dx The drift distance + */ +__attribute__((always_inline)) INLINE static void rt_gradients_predict_drift( + const struct part *restrict p, float U[4], int group, const float dx[3]) { + + rt_part_get_radiation_state_vector(p, group, U); + /* No need to check unphysical state here: + * they haven't been touched since the call + * to rt_injection_update_photon_density */ + + float dE[3], dFx[3], dFy[3], dFz[3]; + rt_part_get_gradients(p, group, dE, dFx, dFy, dFz); + + float dU[4]; + dU[0] = rt_gradients_extrapolate(dE, dx); + dU[1] = rt_gradients_extrapolate(dFx, dx); + dU[2] = rt_gradients_extrapolate(dFy, dx); + dU[3] = rt_gradients_extrapolate(dFz, dx); + + U[0] += dU[0]; + U[1] += dU[1]; + U[2] += dU[2]; + U[3] += dU[3]; + + /* Check and correct unphysical extrapolated states */ + rt_check_unphysical_state(U, &U[1], /*e_old=*/0.f, /*callloc=*/1); +} + +#endif /* SWIFT_RT_GRADIENT_KIARA_H */ diff --git a/src/rt/KIARA/rt_iact.h b/src/rt/KIARA/rt_iact.h new file mode 100644 index 0000000000..0ea5e69a07 --- /dev/null +++ b/src/rt/KIARA/rt_iact.h @@ -0,0 +1,481 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_IACT_KIARA_H +#define SWIFT_RT_IACT_KIARA_H + +#include "fvpm_geometry.h" +#include "rt_debugging.h" +#include "rt_flux.h" +#include "rt_gradients.h" + +/** + * @file src/rt/KIARA/rt_iact.h + * @brief Main header file for the KIARA M1 closure radiative transfer scheme + * particle interactions. + */ + +/** + * @brief Preparation step for injection to gather necessary data. + * This function gets called during the feedback force loop. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (si - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First (star) particle. + * @param pj Second (gas) particle (not updated). + * @param cosmo The cosmological model. + * @param rt_props Properties of the RT scheme. + */ + +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_rt_injection_prep(const float r2, const float dx[3], + const float hi, const float hj, + struct spart *si, const struct part *pj, + const struct cosmology *cosmo, + const struct rt_props *rt_props) { + + /* If the star doesn't have any neighbours, we + * have nothing to do here. */ + if (si->density.wcount == 0.f) return; + +#ifdef SWIFT_RT_DEBUG_CHECKS + si->rt_data.debug_iact_hydro_inject_prep += 1; +#endif + + /* Compute the weight of the neighbouring particle */ + const float hi_inv = 1.f / hi; + const float r = sqrtf(r2); + const float xi = r * hi_inv; + float wi; + kernel_eval(xi, &wi); + const float hi_inv_dim = pow_dimension(hi_inv); + /* psi(x_star - x_gas, h_star) */ + /* Note: skip the devision by si->density.wcount here. It'll cancel out by the + * normalization anyway, and furthermore now that the injection prep is done + * during the star density loop, si->density.wcount won't be computed at this + * stage yet. */ + const float psi = wi * hi_inv_dim; + + /* Now add that weight to the appropriate octant */ + int octant_index = 0; + + if (dx[0] > 0.f) octant_index += 1; + if (dx[1] > 0.f) octant_index += 2; + if (dx[2] > 0.f) octant_index += 4; + + si->rt_data.octant_weights[octant_index] += psi; +} + +/** + * @brief Injection step interaction between star and hydro particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si Star particle. + * @param pj Hydro particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + * @param rt_props Properties of the RT scheme. + */ +__attribute__((always_inline)) INLINE static void runner_iact_rt_inject( + const float r2, const float dx[3], const float hi, const float hj, + struct spart *restrict si, struct part *restrict pj, const float a, + const float H, const struct rt_props *rt_props) { + + /* If the star doesn't have any neighbours, we + * have nothing to do here. */ + if (si->density.wcount == 0.f) return; + +#ifdef SWIFT_RT_DEBUG_CHECKS + + /* Do some checks and increase neighbour counts + * before other potential early exits */ + if (si->rt_data.debug_iact_hydro_inject_prep == 0) + error( + "Injecting energy from star that wasn't called during injection prep"); + + if (!si->rt_data.debug_emission_rate_set) + error("Injecting energy from star without setting emission rate"); + + si->rt_data.debug_iact_hydro_inject += 1; + si->rt_data.debug_radiation_emitted_tot += 1ULL; + + pj->rt_data.debug_iact_stars_inject += 1; + pj->rt_data.debug_radiation_absorbed_tot += 1ULL; + +#endif + + /* Compute the weight of the neighbouring particle */ + const float hi_inv = 1.f / hi; + const float r = sqrtf(r2); + const float xi = r * hi_inv; + float wi; + kernel_eval(xi, &wi); + const float hi_inv_dim = pow_dimension(hi_inv); + /* psi(x_star - x_gas, h_star) */ + /* Skip the division by si->density.wcount to remain consistent */ + const float psi = wi * hi_inv_dim; + +#if defined(HYDRO_DIMENSION_3D) + const int maxind = 8; +#elif defined(HYDRO_DIMENSION_2D) + const int maxind = 4; +#elif defined(HYDRO_DIMENSION_1D) + const int maxind = 2; +#endif + + /* Get weight for particle, including isotropy correction */ + float nonempty_octants = 0.f; + + for (int i = 0; i < maxind; i++) { + if (si->rt_data.octant_weights[i] > 0.f) nonempty_octants += 1.f; + } + + int octant_index = 0; + if (dx[0] > 0.f) octant_index += 1; + if (dx[1] > 0.f) octant_index += 2; + if (dx[2] > 0.f) octant_index += 4; + + const float octw = si->rt_data.octant_weights[octant_index]; + /* We might end up in this scenario due to roundoff errors */ + if (psi == 0.f || octw == 0.f) return; + + const float weight = psi / (nonempty_octants * octw); + const float Vinv = 1.f / pj->geometry.volume; + + /* Nurse, the patient is ready now */ + for (int g = 0; g < RT_NGROUPS; g++) { + /* Inject energy. */ + const float injected_energy_density = + si->rt_data.emission_this_step[g] * weight * Vinv; + pj->rt_data.radiation[g].energy_density += injected_energy_density; + if (pj->rt_data.radiation[g].energy_density > 0.f && g==0) { + message("RT_energy_inj: z=%g sid=%lld pid=%lld RTgrp=%d w=%g Vinv=%g Reff=%g h=%g Enew=%g Eold=%g frac=%g\n", 1./a-1., si->id, pj->id, g, weight, Vinv, pow(3./(4.*M_PI*Vinv), 1./3), pj->h, injected_energy_density, pj->rt_data.radiation[g].energy_density-injected_energy_density, injected_energy_density/pj->rt_data.radiation[g].energy_density); + } + /* Don't inject flux. */ + } + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* Take note how much energy we actually injected */ + for (int g = 0; g < RT_NGROUPS; g++) { + const float injected_energy = si->rt_data.emission_this_step[g] * weight; + if (isinf(injected_energy) || isnan(injected_energy)) + error( + "Injecting abnormal energy spart %lld part %lld group %d | %.6e %.6e " + "%.6e", + si->id, pj->id, g, injected_energy, weight, + + si->rt_data.emission_this_step[g]); + si->rt_data.debug_injected_energy[g] += injected_energy; + si->rt_data.debug_injected_energy_tot[g] += injected_energy; + } + si->rt_data.debug_psi_sum += psi; +#endif +} + +/** + * @brief Flux calculation between particle i and particle j + * + * This method calls runner_iact_rt_fluxes_common with mode 1. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + * @param mode 0 if non-symmetric interaction, 1 if symmetric + */ +__attribute__((always_inline)) INLINE static void runner_iact_rt_flux_common( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H, int mode) { + +#ifdef SWIFT_RT_DEBUG_CHECKS + const char *func_name = (mode == 1) ? "sym flux iact" : "nonsym flux iact"; + rt_debug_sequence_check(pi, 3, func_name); + pi->rt_data.debug_calls_iact_transport_interaction += 1; + + if (mode == 1) { + rt_debug_sequence_check(pj, 3, func_name); + pj->rt_data.debug_calls_iact_transport_interaction += 1; + } +#endif + + /* Get r and 1/r. */ + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* Initialize local variables */ + float Bi[3][3]; + float Bj[3][3]; + for (int k = 0; k < 3; k++) { + for (int l = 0; l < 3; l++) { + Bi[k][l] = pi->geometry.matrix_E[k][l]; + Bj[k][l] = pj->geometry.matrix_E[k][l]; + } + } + const float Vi = pi->geometry.volume; + const float Vj = pj->geometry.volume; + + /* Compute kernel of pi. */ + float wi, wi_dx; + const float hi_inv = 1.0f / hi; + const float hi_inv_dim = pow_dimension(hi_inv); + const float xi = r * hi_inv; + kernel_deval(xi, &wi, &wi_dx); + + /* Compute kernel of pj. */ + float wj, wj_dx; + const float hj_inv = 1.0f / hj; + const float hj_inv_dim = pow_dimension(hj_inv); + const float xj = r * hj_inv; + kernel_deval(xj, &wj, &wj_dx); + + /* Compute (square of) area */ + /* eqn. (7) */ + float Anorm2 = 0.0f; + float A[3]; + if (fvpm_part_geometry_well_behaved(pi) && + fvpm_part_geometry_well_behaved(pj)) { + /* in principle, we use Vi and Vj as weights for the left and right + * contributions to the generalized surface vector. + * However, if Vi and Vj are very different (because they have very + * different smoothing lengths), then the expressions below are more + * stable. */ + float Xi = Vi; + float Xj = Vj; +#ifdef GIZMO_VOLUME_CORRECTION + if (fabsf(Vi - Vj) / min(Vi, Vj) > 1.5f * hydro_dimension) { + Xi = (Vi * hj + Vj * hi) / (hi + hj); + Xj = Xi; + } +#endif + for (int k = 0; k < 3; k++) { + /* we add a minus sign since dx is pi->x - pj->x */ + A[k] = -Xi * (Bi[k][0] * dx[0] + Bi[k][1] * dx[1] + Bi[k][2] * dx[2]) * + wi * hi_inv_dim - + Xj * (Bj[k][0] * dx[0] + Bj[k][1] * dx[1] + Bj[k][2] * dx[2]) * + wj * hj_inv_dim; + Anorm2 += A[k] * A[k]; + } + } else { + /* ill condition gradient matrix: revert to SPH face area */ + const float hidp1 = pow_dimension_plus_one(hi_inv); + const float hjdp1 = pow_dimension_plus_one(hj_inv); + const float Anorm = + -(hidp1 * Vi * Vi * wi_dx + hjdp1 * Vj * Vj * wj_dx) * r_inv; + A[0] = -Anorm * dx[0]; + A[1] = -Anorm * dx[1]; + A[2] = -Anorm * dx[2]; + Anorm2 = Anorm * Anorm * r2; + } + + /* if the interface has no area, nothing happens and we return */ + /* continuing results in dividing by zero and NaN's... */ + if (Anorm2 == 0.0f) { + return; + } + + /* Compute the area */ + const float Anorm_inv = 1.0f / sqrtf(Anorm2); + const float Anorm = Anorm2 * Anorm_inv; + +#ifdef SWIFT_DEBUG_CHECKS + /* For stability reasons, we do require A and dx to have opposite + * directions (basically meaning that the surface normal for the surface + * always points from particle i to particle j, as it would in a real + * moving-mesh code). If not, our scheme is no longer upwind and hence can + * become unstable. */ + const float dA_dot_dx = A[0] * dx[0] + A[1] * dx[1] + A[2] * dx[2]; + /* In GIZMO, Phil Hopkins reverts to an SPH integration scheme if this + * happens. We curently just ignore this case and display a message. */ + const float rdim = pow_dimension(r); + if (dA_dot_dx > 1.e-6f * rdim) { + message("Ill conditioned gradient matrix (%g %g %g %g %g)!", dA_dot_dx, + Anorm, Vi, Vj, r); + } +#endif + + /* compute the normal vector of the interface */ + const float n_unit[3] = {A[0] * Anorm_inv, A[1] * Anorm_inv, + A[2] * Anorm_inv}; + + /* Compute interface position (relative to pi, since we don't need + * the actual position) eqn. (8) */ + const float xfac = -hi / (hi + hj); + const float xij_i[3] = {xfac * dx[0], xfac * dx[1], xfac * dx[2]}; + + struct rt_part_data *restrict rti = &pi->rt_data; + struct rt_part_data *restrict rtj = &pj->rt_data; + /* Get the time step for the flux exchange. This is always the smallest time + * step among the two particles. */ + const float mindt = + (rtj->flux_dt > 0.f) ? fminf(rti->flux_dt, rtj->flux_dt) : rti->flux_dt; + + for (int g = 0; g < RT_NGROUPS; g++) { + + /* radiation state to be used to compute the flux */ + float Ui[4], Uj[4]; + rt_gradients_predict(pi, pj, Ui, Uj, g, dx, r, xij_i); + + /* For first order method, skip the gradients */ + /* float Ui[4], Uj[4]; */ + /* rt_part_get_radiation_state_vector(pi, g, Ui); */ + /* rt_part_get_radiation_state_vector(pj, g, Uj); */ + /* No need to check for unphysical quantities, they + * haven't been touched since + * rt_injection_update_photon_densities */ + + float totflux[4]; + + rt_compute_flux(Ui, Uj, n_unit, Anorm, totflux); + + /* When solving the Riemann problem, we assume pi is left state, and + * pj is right state. The sign convention is that a positive total + * flux is subtracted from the left state, and added to the right + * state, based on how we chose the unit vector. By this convention, + * the time integration results in conserved quantity += flux * dt */ + /* Unlike in SPH schemes, we do need to update inactive neighbours, so that + * the fluxes are always exchanged symmetrically. Thanks to our sneaky use + * of flux_dt, we can detect inactive neighbours through their negative time + * step. */ + /* Make sure mindt larger than 0 to avoid mindt=-1 case. */ + if (mindt > 0.f) { + rti->flux[g].energy -= totflux[0] * mindt; + rti->flux[g].flux[0] -= totflux[1] * mindt; + rti->flux[g].flux[1] -= totflux[2] * mindt; + rti->flux[g].flux[2] -= totflux[3] * mindt; + if (mode == 1 || (rtj->flux_dt < 0.f)) { + rtj->flux[g].energy += totflux[0] * mindt; + rtj->flux[g].flux[0] += totflux[1] * mindt; + rtj->flux[g].flux[1] += totflux[2] * mindt; + rtj->flux[g].flux[2] += totflux[3] * mindt; + } + } + } +} + +/** + * @brief Flux calculation between particle i and particle j + * + * This method calls runner_iact_rt_fluxes_common with mode 1. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_rt_transport( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) { + + runner_iact_rt_flux_common(r2, dx, hi, hj, pi, pj, a, H, 1); +} + +/** + * @brief Flux calculation between particle i and particle j: non-symmetric + * version + * + * This method calls runner_iact_rt_fluxes_common with mode 0. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_rt_transport(const float r2, const float dx[3], + const float hi, const float hj, + struct part *restrict pi, + struct part *restrict pj, const float a, + const float H) { + + runner_iact_rt_flux_common(r2, dx, hi, hj, pi, pj, a, H, 0); +} + +/** + * @brief Calculate the gradient interaction between particle i and particle j + * + * This method wraps around rt_gradients_collect, which can be an empty + * method, in which case no gradients are used. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_rt_gradient( + const float r2, const float dx[3], const float hi, const float hj, + struct part *restrict pi, struct part *restrict pj, const float a, + const float H) { + + rt_gradients_collect(r2, dx, hi, hj, pi, pj); +} + +/** + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version + * + * This method wraps around rt_gradients_nonsym_collect, which can be an + * empty method, in which case no gradients are used. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_rt_gradient(const float r2, const float dx[3], + const float hi, const float hj, + struct part *restrict pi, + struct part *restrict pj, const float a, + const float H) { + + rt_gradients_nonsym_collect(r2, dx, hi, hj, pi, pj); +} + +#endif /* SWIFT_RT_IACT_KIARA_H */ diff --git a/src/rt/KIARA/rt_interaction_cross_sections.c b/src/rt/KIARA/rt_interaction_cross_sections.c new file mode 100644 index 0000000000..24189d94f2 --- /dev/null +++ b/src/rt/KIARA/rt_interaction_cross_sections.c @@ -0,0 +1,458 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ + +#include "rt_interaction_cross_sections.h" + +#include "error.h" +#include "rt_blackbody.h" +#include "rt_properties.h" +#include "rt_species.h" + +#include + +/** + * @brief compute the chosen spectrum for a given frequency nu. + * This function is intended to be used to integrate the spectra + * over a frequency range in order to obtain averaged ionization + * cross sections. + * + * @param nu frequency at which to evaluate spectrum + * @param params spectrum integration params. Needs to be of type + * void* for GSL integrators. + */ +__attribute__((always_inline)) INLINE static double +rt_interaction_rates_get_spectrum(const double nu, void *params) { + /* Keep this function in the .c file so you don't have to + * include the spectra .h files like rt_blackbody.h anywhere + * else */ + + struct rt_spectrum_integration_params *pars = + (struct rt_spectrum_integration_params *)params; + + if (pars->spectrum_type == 0) { + /* Constant spectrum */ + if (nu <= pars->const_stellar_spectrum_frequency_max) { + return 1.; + } else { + return 0.; + } + } else if (pars->spectrum_type == 1) { + /* Blackbody spectrum */ + const double T = pars->T; + const double kB = pars->kB; + const double h_planck = pars->h_planck; + const double c = pars->c; + return blackbody_spectrum_intensity(nu, T, kB, h_planck, c); + } else if (pars->spectrum_type == 2) { + /* TODO: currently set it to Constant spectrum */ + if (nu <= pars->const_stellar_spectrum_frequency_max) { + return 1.; + } else { + return 0.; + } + } else { + error("Unknown stellar spectrum type selected: %d", pars->spectrum_type); + return 0.; + } +} + +/** + * Spectrum function to be integrated. + * This function is called by the GSL integrator. + * + * @param nu frequency at which to evaluate spectrum + * @param params spectrum integration params. Needs to be of type + * void* for GSL integrators. + */ +static double spectrum_integrand(double nu, void *params) { + return rt_interaction_rates_get_spectrum(nu, params); +} + +/** + * Spectrum function divided by photon energy h*nu to be integrated. + * This function is called by the GSL integrator. + * + * @param nu frequency at which to evaluate spectrum + * @param params spectrum integration params. Needs to be of type + * void* for GSL integrators. + */ +static double spectrum_over_hnu_integrand(double nu, void *params) { + struct rt_spectrum_integration_params *p = + (struct rt_spectrum_integration_params *)params; + const double E = nu * p->h_planck; + const double J = rt_interaction_rates_get_spectrum(nu, params); + if (E > 0.) { + return J / E; + } else { + return 0.; + } +} + +/** + * Spectrum times cross section function to be integrated. + * This function is called by the GSL integrator. + * + * @param nu frequency at which to evaluate spectrum + * @param params spectrum integration params. Needs to be of type + * void* for GSL integrators. + */ +static double spectrum_times_sigma_integrand(double nu, void *params) { + struct rt_spectrum_integration_params *p = + (struct rt_spectrum_integration_params *)params; + const double E = nu * p->h_planck; + const double sigma = + photoionization_cross_section(E, p->species, p->cs_params); + const double J = rt_interaction_rates_get_spectrum(nu, params); + return J * sigma; +} + +/** + * Spectrum times cross section divided by h*nu function to be integrated. + * This function is called by the GSL integrator. + * + * @param nu frequency at which to evaluate spectrum + * @param params spectrum integration params. Needs to be of type + * void* for GSL integrators. + */ +static double spectrum_times_sigma_over_hnu_integrand(double nu, void *params) { + struct rt_spectrum_integration_params *p = + (struct rt_spectrum_integration_params *)params; + const double E = nu * p->h_planck; + const double sigma = + photoionization_cross_section(E, p->species, p->cs_params); + const double J = rt_interaction_rates_get_spectrum(nu, params); + return J * sigma / E; +} + +/** + * Integrate a function from nu_start to nu_stop with GSL routines + * + * @param function function to integrate + * @param nu_start lower boundary of the integral + * @param nu_stop upper boundary of the integral + * @param params spectrum integration params. + * */ +static double rt_cross_sections_integrate_gsl( + double (*function)(double, void *), double nu_start, double nu_stop, + int npoints, struct rt_spectrum_integration_params *params) { + + gsl_function F; + gsl_integration_workspace *w = gsl_integration_workspace_alloc(npoints); + double result, error; + + F.function = function; + F.params = (void *)params; + /* NOTE: there is an option to use the integrator with an upper limit + * of infinity, but this is accurate enough for now when setting a + * high enough maximal frequency. */ + gsl_integration_qags(&F, nu_start, nu_stop, /*espabs=*/0., /*epsrel=*/1e-7, + npoints, w, &result, &error); + + /* Clean up after yourself. */ + gsl_integration_workspace_free(w); + + return result; +} + +/** + * @brief allocate and pre-compute the averaged cross sections + * for each photon group and ionizing species. + * + * @param rt_props RT properties struct + * @param phys_const physical constants struct + * @param us internal units struct + **/ +void rt_cross_sections_init(struct rt_props *restrict rt_props, + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us) { + + /* Allocate the space to store the (cross section) integrals */ + /* --------------------------------------------------------- */ + + double **cse = malloc(RT_NGROUPS * sizeof(double *)); + double **csn = malloc(RT_NGROUPS * sizeof(double *)); + double *av_energy = rt_props->average_photon_energy; + double *photon_number_integral = rt_props->photon_number_integral; + for (int group = 0; group < RT_NGROUPS; group++) { + cse[group] = malloc(rt_ionizing_species_count * sizeof(double)); + csn[group] = malloc(rt_ionizing_species_count * sizeof(double)); + av_energy[group] = 0.; + photon_number_integral[group] = 0.; + } + + /* TODO: BPASS photon group properties are hard code in now, + * need to think a better way to read in. + * Current values are using the values in first year review. */ + if (rt_props->stellar_spectrum_type == 2){ + /* Energy weighted cross section. unit cm^2 */ + cse[0][0] = 3.21e-18, cse[0][1] = 0, cse[0][2] = 0; + cse[1][0] = 6.99e-19, cse[1][1] = 5.14e-18, cse[1][2] = 0; + cse[2][0] = 1.19e-19, cse[2][1] = 1.64e-18, cse[2][2] = 4.64e-19; + + /* Number weighted cross section. unit cm^2 */ + csn[0][0] = 3.46e-18, csn[0][1] = 0, csn[0][2] = 0; + csn[1][0] = 7.55e-19, csn[1][1] = 5.42e-18, csn[1][2] = 0; + csn[2][0] = 1.19e-19, csn[2][1] = 1.65e-18, csn[2][2] = 4.55e-19; + + /* Average photon energy. unit erg */ + av_energy[0] = 2.864693e-11; + av_energy[1] = 4.955534e-11; + av_energy[2] = 8.834406e-11; + + } else { + + double integral_E[RT_NGROUPS]; + double integral_N[RT_NGROUPS]; + double integral_sigma_E[RT_NGROUPS][rt_ionizing_species_count]; + double integral_sigma_E_over_hnu[RT_NGROUPS][rt_ionizing_species_count]; + + /* Grab constants and conversions in cgs */ + /* ------------------------------------- */ + const double T_cgs = units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + const float dimension_kB[5] = {1, 2, -2, 0, -1}; /* [g cm^2 s^-2 K^-1] */ + const double kB_to_cgs = + units_general_cgs_conversion_factor(us, dimension_kB); + const double kB_cgs = phys_const->const_boltzmann_k * kB_to_cgs; + + const float dimension_h[5] = {1, 2, -1, 0, 0}; /* [g cm^2 s^-1] */ + const double h_to_cgs = units_general_cgs_conversion_factor(us, dimension_h); + const double h_planck_cgs = phys_const->const_planck_h * h_to_cgs; + + const double c_cgs = phys_const->const_speed_light_c * + units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); + + /* Prepare parameter struct for integration functions */ + /* -------------------------------------------------- */ + const int spectype = rt_props->stellar_spectrum_type; + const double maxfreq_const_spectrum = + rt_props->const_stellar_spectrum_max_frequency; + const double T_bb = rt_props->stellar_spectrum_blackbody_T * T_cgs; + struct rt_photoion_cs_parameters cs_params = rt_init_photoion_cs_params_cgs(); + + struct rt_spectrum_integration_params integration_params = { + /*species=*/0, + /*spectrum_type=*/spectype, + /*freq_max for const spectrum=*/maxfreq_const_spectrum, + /*T=*/T_bb, + /*kB=*/kB_cgs, + /*h_planck=*/h_planck_cgs, + /*c=*/c_cgs, + /*cross section params=*/&cs_params}; + + /* Set up Integration Limits */ + /* ------------------------- */ + + /* Get start and end points of the integrals */ + double nu_start[RT_NGROUPS]; + double nu_stop[RT_NGROUPS]; + for (int group = 0; group < RT_NGROUPS; group++) + nu_start[group] = rt_props->photon_groups[group]; + for (int group = 0; group < RT_NGROUPS - 1; group++) + nu_stop[group] = rt_props->photon_groups[group + 1]; + + if (RT_NGROUPS == 1) { + /* If we only have one group, start integrating from the Hydrogen + * ionization frequency, not from zero. The reasoning here is that + * typically you define the *ionizing* radiation as stellar emission + * rates, not the *total* radiation. */ + nu_start[0] = cs_params.E_ion[rt_ionizing_species_HI] / h_planck_cgs; + if (engine_rank == 0) + message( + "WARNING: with only 1 photon group, I'll start integrating" + " the cross sections at the first ionizing frequency %.3g", + nu_start[0]); + } else { + /* don't start at exactly 0 to avoid unlucky divisions */ + if (nu_start[0] == 0.) nu_start[0] = min(1e-20, 1e-12 * nu_start[1]); + } + + /* Get frequency at which we stop integrating */ + double nu_stop_final; + if (rt_props->stellar_spectrum_type == 0) { + nu_stop_final = rt_props->const_stellar_spectrum_max_frequency; + } else if (rt_props->stellar_spectrum_type == 1) { + nu_stop_final = 10. * blackbody_peak_frequency(T_bb, kB_cgs, h_planck_cgs); + } else if (rt_props->stellar_spectrum_type == 2) { + nu_stop_final = rt_props->const_stellar_spectrum_max_frequency; + /* TODO: set it as the const spectrum now. Later we need to add the real value we use. */ + } else { + nu_stop_final = -1.; + error("Unknown stellar spectrum type %d", rt_props->stellar_spectrum_type); + } + nu_stop[RT_NGROUPS - 1] = nu_stop_final; + + /* Compute Integrals */ + /* ----------------- */ + for (int g = 0; g < RT_NGROUPS; g++) { + /* This is independent of species. */ + integral_E[g] = rt_cross_sections_integrate_gsl( + spectrum_integrand, nu_start[g], nu_stop[g], RT_INTEGRAL_NPOINTS, + &integration_params); + integral_N[g] = rt_cross_sections_integrate_gsl( + spectrum_over_hnu_integrand, nu_start[g], nu_stop[g], + RT_INTEGRAL_NPOINTS, &integration_params); + + for (int s = 0; s < rt_ionizing_species_count; s++) { + integration_params.species = s; + integral_sigma_E[g][s] = rt_cross_sections_integrate_gsl( + spectrum_times_sigma_integrand, nu_start[g], nu_stop[g], + RT_INTEGRAL_NPOINTS, &integration_params); + integral_sigma_E_over_hnu[g][s] = rt_cross_sections_integrate_gsl( + spectrum_times_sigma_over_hnu_integrand, nu_start[g], nu_stop[g], + RT_INTEGRAL_NPOINTS, &integration_params); + } + } + + /* Now compute the actual average cross sections */ + /* --------------------------------------------- */ + for (int g = 0; g < RT_NGROUPS; g++) { + photon_number_integral[g] = integral_N[g]; + if (integral_N[g] > 0.) { + av_energy[g] = integral_E[g] / integral_N[g]; + } else { + av_energy[g] = 0.; + } + for (int s = 0; s < rt_ionizing_species_count; s++) { + if (integral_E[g] > 0.) { + cse[g][s] = integral_sigma_E[g][s] / integral_E[g]; + csn[g][s] = integral_sigma_E_over_hnu[g][s] / integral_N[g]; + } else { + /* No radiation = no interaction */ + cse[g][s] = 0.; + csn[g][s] = 0.; + } + } + } + + }//end for the simple spectrum calculation. + + /* for (int g = 0; g < RT_NGROUPS; g++) { */ + /* printf("\nGroup %d\n", g); */ + /* printf("nu_start: %12.6g\n", nu_start[g]); */ + /* printf("nu_end: %12.6g\n", nu_stop[g]); */ + /* printf("spectrum energy integral: %12.6g\n", integral_E[g]); */ + /* printf("spectrum number integral: %12.6g\n", integral_N[g]); */ + /* printf("average photon energy: %12.6g\n", av_energy[g]); */ + /* */ + /* printf("species: "); */ + /* for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12d ", s); + */ + /* printf("\n"); */ + /* */ + /* printf("integral sigma * E: "); */ + /* for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ", */ + /* integral_sigma_E[g][s]); */ + /* printf("\n"); */ + /* */ + /* printf("integral sigma * E / h nu: "); */ + /* for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ", */ + /* integral_sigma_E_over_hnu[g][s]); */ + /* printf("\n"); */ + /* */ + /* printf("energy weighted c.section: "); */ + /* for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ", */ + /* cse[g][s]); */ + /* printf("\n"); */ + /* */ + /* printf("number weighted c.section: "); */ + /* for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ", */ + /* csn[g][s]); */ + /* printf("\n"); */ + /* } */ + + /* Store the results */ + /* ----------------- */ + + if (rt_props->energy_weighted_cross_sections != NULL) { + for (int g = 0; g < RT_NGROUPS; g++) { + if (rt_props->energy_weighted_cross_sections[g] != NULL) + free(rt_props->energy_weighted_cross_sections[g]); + } + free(rt_props->energy_weighted_cross_sections); + } + rt_props->energy_weighted_cross_sections = cse; + + if (rt_props->number_weighted_cross_sections != NULL) { + for (int g = 0; g < RT_NGROUPS; g++) { + if (rt_props->number_weighted_cross_sections[g] != NULL) + free(rt_props->number_weighted_cross_sections[g]); + } + free(rt_props->number_weighted_cross_sections); + } + rt_props->number_weighted_cross_sections = csn; +} + +/** + * @brief allocate and pre-compute photon number table from BPASS. + * + * @param file_name bpass hdf5 file name char + * @param dataset_name each photon group data set name within the hdf5 file + **/ +double **read_Bpass_from_hdf5(char *file_name, char *dataset_name){ + double** Table; + + // Open the HDF5 file + hid_t file_id = H5Fopen(file_name, H5F_ACC_RDONLY, H5P_DEFAULT); + if (file_id < 0) error("Error: Could not open file %s.\n", file_name); + + + // Open the dataset for HI group + hid_t dataset_id = H5Dopen2(file_id, dataset_name, H5P_DEFAULT); + if (dataset_id < 0) error("Error: Could not open dataset %s.\n", dataset_name); + + /* read element name array into temporary array */ + hid_t dataspace = H5Dget_space(dataset_id); + + // Get the dimensions of the dataset + hsize_t dims[2]; // Assuming a 2D dataset + H5Sget_simple_extent_dims(dataspace, dims, NULL); + + // Use a buffer to read HDF5 dataset + double *buffer = malloc(dims[0] * dims[1] * sizeof(double)); + herr_t status = H5Dread(dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + if (status < 0) error("Error: Could not read dataset.\n"); + + // Allocate memory for ionizing_HI_table + Table = malloc(dims[0] * sizeof(double *)); + for (hsize_t i = 0; i < dims[0]; i++) { + Table[i] = malloc(dims[1] * sizeof(double)); + } + + // Copy the element names into their final destination + for (hsize_t i = 0; i < dims[0]; i++){ + for (hsize_t j = 0; j < dims[1]; j++) { + Table[i][j] = buffer[i * dims[1] + j]; + if (fabs(Table[i][j])< 0) error("Negative photon number in the table! row:%lu, column:%lu\n", i, j); + } + } + + // Free allocated memory + free(buffer); + + // Close HDF5 dataset + status = H5Sclose(dataspace); + if (status < 0) error("error closing dataspace"); + status = H5Dclose(dataset_id); + if (status < 0) error("error closing dataset"); + status = H5Fclose(file_id); + if (status < 0) error("error closing file"); + + return Table; +} diff --git a/src/rt/KIARA/rt_interaction_cross_sections.h b/src/rt/KIARA/rt_interaction_cross_sections.h new file mode 100644 index 0000000000..4409d83848 --- /dev/null +++ b/src/rt/KIARA/rt_interaction_cross_sections.h @@ -0,0 +1,153 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_KIARA_INTERACION_CROSS_SECTIONS_H +#define SWIFT_RT_KIARA_INTERACION_CROSS_SECTIONS_H + +#include "inline.h" +#include "rt_species.h" + +#include +#include + +/** + * @file src/rt/KIARA/rt_interaction_cross_sections.h + * @brief header file concerning photoionization cross sections + **/ + +#define RT_INTEGRAL_NPOINTS 10000 + +/*! Struct containing the parametrized cross section parameters + * for each photoionizing species. + * Correct usage is to call rt_init_photoion_cs_params_cgs(), + * which returns a fully initialized struct. */ +struct rt_photoion_cs_parameters { + double E_ion[rt_ionizing_species_count]; /* erg */ + double E_zero[rt_ionizing_species_count]; /* erg */ + double sigma_zero[rt_ionizing_species_count]; /* cm^-2 */ + double P[rt_ionizing_species_count]; + double ya[rt_ionizing_species_count]; + double yw[rt_ionizing_species_count]; + double y0[rt_ionizing_species_count]; + double y1[rt_ionizing_species_count]; +}; + +/*! Parameters needed to compute the stellar spectra integrals - + * the data needs to be encapsulated in a single struct to be passed + * as an argument for GSL integrators. */ +struct rt_spectrum_integration_params { + /* Which species are we dealing with? */ + int species; + /* Which spectrum type are we dealing with? */ + int spectrum_type; + /* Max frequency for const spectrum */ + double const_stellar_spectrum_frequency_max; + /* Temperature of blackbody in correct units */ + double T; + /* Boltzmann constant in correct units */ + double kB; + /* Planck constant in correct units */ + double h_planck; + /* speed of light in correct units */ + double c; + /* Values for the cross section parametrization */ + struct rt_photoion_cs_parameters *cs_params; +}; + +/** + * Initialize the parameters for the cross section computation in cgs, + * and return a fully and correctly initialized struct. + * The data is taken from Verner et al. 1996 + * (ui.adsabs.harvard.edu/abs/1996ApJ...465..487V) via Rosdahl et al. 2013 + * (ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R) (Table E1) + */ +__attribute__((always_inline)) INLINE static struct rt_photoion_cs_parameters +rt_init_photoion_cs_params_cgs(void) { + + struct rt_photoion_cs_parameters photoion_cs_params_cgs; + double E_ion[rt_ionizing_species_count]; + rt_species_get_ionizing_energy(E_ion); + + photoion_cs_params_cgs.E_ion[rt_ionizing_species_HI] = + E_ion[rt_ionizing_species_HI]; + photoion_cs_params_cgs.E_zero[rt_ionizing_species_HI] = 6.886e-13; + photoion_cs_params_cgs.sigma_zero[rt_ionizing_species_HI] = 5.475e-14; + photoion_cs_params_cgs.P[rt_ionizing_species_HI] = 2.963; + photoion_cs_params_cgs.ya[rt_ionizing_species_HI] = 32.88; + photoion_cs_params_cgs.yw[rt_ionizing_species_HI] = 0.; + photoion_cs_params_cgs.y0[rt_ionizing_species_HI] = 0.; + photoion_cs_params_cgs.y1[rt_ionizing_species_HI] = 0.; + + photoion_cs_params_cgs.E_ion[rt_ionizing_species_HeI] = + E_ion[rt_ionizing_species_HeI]; + /* Note: The value of 0.1361 eV for E_0 of HeI given in table 5.1 + * in Rosdahl et al. is an error. The correct value is 13.61 eV */ + photoion_cs_params_cgs.E_zero[rt_ionizing_species_HeI] = 2.181e-11; + photoion_cs_params_cgs.sigma_zero[rt_ionizing_species_HeI] = 9.492e-16; + photoion_cs_params_cgs.P[rt_ionizing_species_HeI] = 3.188; + photoion_cs_params_cgs.ya[rt_ionizing_species_HeI] = 1.469; + photoion_cs_params_cgs.yw[rt_ionizing_species_HeI] = 2.039; + photoion_cs_params_cgs.y0[rt_ionizing_species_HeI] = 0.4434; + photoion_cs_params_cgs.y1[rt_ionizing_species_HeI] = 2.136; + + photoion_cs_params_cgs.E_ion[rt_ionizing_species_HeII] = + E_ion[rt_ionizing_species_HeII]; + photoion_cs_params_cgs.E_zero[rt_ionizing_species_HeII] = 2.756e-12; + photoion_cs_params_cgs.sigma_zero[rt_ionizing_species_HeII] = 1.369e-14; + photoion_cs_params_cgs.P[rt_ionizing_species_HeII] = 2.963; + photoion_cs_params_cgs.ya[rt_ionizing_species_HeII] = 32.88; + photoion_cs_params_cgs.yw[rt_ionizing_species_HeII] = 0.; + photoion_cs_params_cgs.y0[rt_ionizing_species_HeII] = 0.; + photoion_cs_params_cgs.y1[rt_ionizing_species_HeII] = 0.; + + return photoion_cs_params_cgs; +} + +/** + * Compute the parametrized cross section for a given energy and species. + * The parametrization is taken from Verner et al. 1996 + * (ui.adsabs.harvard.edu/abs/1996ApJ...465..487V) via Rosdahl et al. 2013 + * (ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R) + * + * @param E energy for which to compute the cross section, in erg + * @param species index of species, 0 < species < rt_ionizing_species_count + * @param params cross section parameters struct + */ +__attribute__((always_inline)) INLINE static double +photoionization_cross_section(const double E, const int species, + const struct rt_photoion_cs_parameters *params) { + + const double E0 = params->E_zero[species]; + const double E_ion = params->E_ion[species]; + const double y0 = params->y0[species]; + const double y1 = params->y1[species]; + const double yw = params->yw[species]; + const double ya = params->ya[species]; + const double P = params->P[species]; + const double sigma_0 = params->sigma_zero[species]; + + if (E < E_ion) return 0.; + + const double x = E / E0 - y0; + const double y = sqrt(x * x + y1 * y1); + const double temp1 = pow(y, 0.5 * P - 5.5); + const double temp2 = pow(1. + sqrt(y / ya), -P); + return sigma_0 * ((x - 1.) * (x - 1.) + yw * yw) * temp1 * temp2; +} + +#endif /* SWIFT_RT_KIARA_INTERACION_CROSS_SECTIONS_H */ diff --git a/src/rt/KIARA/rt_interaction_rates.h b/src/rt/KIARA/rt_interaction_rates.h new file mode 100644 index 0000000000..5d4fc12379 --- /dev/null +++ b/src/rt/KIARA/rt_interaction_rates.h @@ -0,0 +1,180 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_KIARA_INTERACTION_RATES_H +#define SWIFT_RT_KIARA_INTERACTION_RATES_H + +#include "rt_parameters.h" +#include "rt_properties.h" +#include "rt_species.h" +#include "rt_thermochemistry_utils.h" + +/** + * @file src/rt/KIARA/rt_interaction_rates.h + * @brief header file concerning photoionization and photoheating rates + **/ + +/** + * @brief compute the heating, ionization, and dissassociation rates + * for the particle radiation field as needed by grackle. + * + * @param rates (return) Interaction rates for grackle. [0]: heating rate. + * [1]: HI ionization. [2]: HeI ionization. [3]: HeII ionization. + * [4]: H2 dissociation. + * @param energy_density energy densities of each photon group [internal units] + * @param species_densities physical densities of all species [internal units] + * @param average_photon_energy mean photon energy in group, in erg + * @param cse energy weighted photon interaction cross sections, in cm^2 + * @param csn number weighted photon interaction cross sections, in cm^2 + * @param phys_const physical constants struct + * @param us internal units struct + **/ +__attribute__((always_inline)) INLINE static void +rt_get_interaction_rates_for_grackle( + gr_float rates[5], float energy_density[RT_NGROUPS], + gr_float species_densities[6], + const double average_photon_energy[RT_NGROUPS], double **cse, double **csn, + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us) { + + rates[0] = 0.; /* Needs to be in [erg / s / cm^3 / nHI] for grackle. */ + rates[1] = 0.; /* [1 / time_units] */ + rates[2] = 0.; /* [1 / time_units] */ + rates[3] = 0.; /* [1 / time_units] */ + rates[4] = 0.; /* [1 / time_units] */ + + double E_ion_cgs[rt_ionizing_species_count]; + rt_species_get_ionizing_energy(E_ion_cgs); + + /* Get some conversions and constants first. */ + const double c_cgs = rt_params.reduced_speed_of_light * + units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); + const double to_energy_density_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_DENSITY); + const double inv_time_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_INV_TIME); + + /* get species number densities in cgs */ + double ns_cgs[rt_ionizing_species_count]; /* in cm^-3 */ + rt_tchem_get_ionizing_species_number_densities(ns_cgs, species_densities, + phys_const, us); + + /* store photoionization rate for each species here */ + double ionization_rates[rt_ionizing_species_count]; + for (int s = 0; s < rt_ionizing_species_count; s++) { + ionization_rates[s] = 0.; + } + + for (int g = 0; g < RT_NGROUPS; g++) { + + /* Sum results for this group over all species */ + double heating_rate_group_cgs = 0.; + const double Eg = energy_density[g] * to_energy_density_cgs; + const double Emean_g = average_photon_energy[g]; + const double Ng = (Emean_g > 0.) ? Eg / Emean_g : 0.; + + for (int s = 0; s < rt_ionizing_species_count; s++) { + /* All quantities here are in cgs. */ + heating_rate_group_cgs += + (cse[g][s] * Emean_g - E_ion_cgs[s] * csn[g][s]) * ns_cgs[s]; + ionization_rates[s] += csn[g][s] * Ng * c_cgs; + } + rates[0] += heating_rate_group_cgs * Ng * c_cgs; + } + + /* Convert into correct units. */ + const double nHI = ns_cgs[rt_ionizing_species_HI]; + if (nHI > 0.) + rates[0] /= nHI; + else + rates[0] = 0.; + + for (int s = 0; s < rt_ionizing_species_count; s++) { + ionization_rates[s] /= inv_time_cgs; /* internal units T^-1 */ +#ifdef SWIFT_RT_DEBUG_CHECKS + if (ionization_rates[s] < 0.) + error("unphysical ion rate spec %d - %.4g", s, ionization_rates[s]); +#endif + } + + /* We're done. Write the results in correct place */ + rates[1] = ionization_rates[0]; + rates[2] = ionization_rates[1]; + rates[3] = ionization_rates[2]; + /* rates[4] = skipped for now */ + +#ifdef SWIFT_RT_DEBUG_CHECKS + for (int i = 0; i < 5; i++) { + if (rates[i] < 0.) error("unphysical rate %d %.4g", i, rates[i]); + else if (isnan(rates[i])) { + error("NaN detected in rate %d", i); + } + } +#endif +} + +/** + * @brief compute the rates at which the photons get absorbed/destroyed + * during interactions with gas. + * + * @param absorption_rates (return) the energy absorption rates in + * internal units for each photon group. + * @param species_densities the physical densities of all traced species + *[internal units] + * @param average_photon_energy mean photon energy in group, in erg + * @param csn number weighted photon interaction cross sections, in cm^2 + * @param phys_const physical constants struct + * @param us internal units struct + **/ +__attribute__((always_inline)) INLINE static void rt_get_absorption_rates( + double absorption_rates[RT_NGROUPS], gr_float species_densities[6], + const double average_photon_energy[RT_NGROUPS], double **csn, + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us) { + + for (int g = 0; g < RT_NGROUPS; g++) absorption_rates[g] = 0.; + + double E_ion_cgs[rt_ionizing_species_count]; + rt_species_get_ionizing_energy(E_ion_cgs); + + /* Get some conversions and constants first. */ + const double c_cgs = rt_params.reduced_speed_of_light * + units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); + const double inv_time_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_INV_TIME); + + double ns_cgs[rt_ionizing_species_count]; /* in cm^-3 */ + rt_tchem_get_ionizing_species_number_densities(ns_cgs, species_densities, + phys_const, us); + + for (int g = 0; g < RT_NGROUPS; g++) { + for (int s = 0; s < rt_ionizing_species_count; s++) { + absorption_rates[g] += c_cgs * csn[g][s] * ns_cgs[s]; + } + } + + for (int g = 0; g < RT_NGROUPS; g++) { + absorption_rates[g] /= inv_time_cgs; +#ifdef SWIFT_RT_DEBUG_CHECKS + if (absorption_rates[g] < 0.) + error("unphysical rate %d - %.4g", g, absorption_rates[g]); +#endif + } +} + +#endif /* SWIFT_RT_KIARA_INTERACION_RATES_H */ diff --git a/src/rt/KIARA/rt_io.h b/src/rt/KIARA/rt_io.h new file mode 100644 index 0000000000..bcbf530a8c --- /dev/null +++ b/src/rt/KIARA/rt_io.h @@ -0,0 +1,529 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_IO_KIARA_H +#define SWIFT_RT_IO_KIARA_H + +#define RT_LABELS_SIZE 10 +#include "cooling_io.h" + +/** + * @file src/rt/KIARA/rt_io.h + * @brief Main header file for KIARA M1 Closure radiative transfer + * scheme IO routines. + */ + +/** + * @brief Specifies which particle fields to read from a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to read. + * + * @return Returns the number of fields to read. + */ +INLINE static int rt_read_particles(const struct part* parts, + struct io_props* list) { + + /* List what we want to read */ + + char fieldname[30]; + int count = 0; + for (int phg = 0; phg < RT_NGROUPS; phg++) { + sprintf(fieldname, "PhotonEnergiesGroup%d", phg + 1); + list[count++] = + io_make_input_field(fieldname, FLOAT, 1, OPTIONAL, UNIT_CONV_ENERGY, + parts, rt_data.radiation[phg].energy_density); + sprintf(fieldname, "PhotonFluxesGroup%d", phg + 1); + list[count++] = io_make_input_field(fieldname, FLOAT, 3, OPTIONAL, + UNIT_CONV_ENERGY_VELOCITY, parts, + rt_data.radiation[phg].flux); + } + + list[count++] = io_make_input_field("MassFractionHI", FLOAT, 1, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, + rt_data.tchem.mass_fraction_HI); + list[count++] = io_make_input_field("MassFractionHII", FLOAT, 1, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, + rt_data.tchem.mass_fraction_HII); + list[count++] = io_make_input_field("MassFractionHeI", FLOAT, 1, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, + rt_data.tchem.mass_fraction_HeI); + list[count++] = io_make_input_field("MassFractionHeII", FLOAT, 1, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, + rt_data.tchem.mass_fraction_HeII); + list[count++] = io_make_input_field("MassFractionHeIII", FLOAT, 1, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, + rt_data.tchem.mass_fraction_HeIII); + + return count; +} + +/** + * @brief Specifies which particle fields to read from a dataset + * + * @param sparts The star particle array. + * @param list The list of i/o properties to read. + * + * @return Returns the number of fields to read. + */ +INLINE static int rt_read_stars(const struct spart* sparts, + struct io_props* list) { + return 0; +} + +/** + * @brief Extract radiation energies of radiation struct for all photon groups + * Note: "allocation" of `float* ret` happens in io_copy_temp_buffer() + * + * @param engine the engine + * @param part the particle to extract data from + * @param xpart the according xpart to extract data from + * @param ret (return) the extracted data + */ +INLINE static void rt_convert_radiation_energies(const struct engine* engine, + const struct part* part, + const struct xpart* xpart, + float* ret) { + + for (int g = 0; g < RT_NGROUPS; g++) { + ret[g] = part->rt_data.radiation[g].energy_density * part->geometry.volume; + } +} + +/** + * @brief Extract radiation fluxes of radiation struct for all photon groups + * Note: "allocation" of `float* ret` happens in io_copy_temp_buffer() + * + * @param engine the engine + * @param part the particle to extract data from + * @param xpart the according xpart to extract data from + * @param ret (return) the extracted data + */ +INLINE static void rt_convert_radiation_fluxes(const struct engine* engine, + const struct part* part, + const struct xpart* xpart, + float* ret) { + + int i = 0; + for (int g = 0; g < RT_NGROUPS; g++) { + ret[i++] = part->rt_data.radiation[g].flux[0]; + ret[i++] = part->rt_data.radiation[g].flux[1]; + ret[i++] = part->rt_data.radiation[g].flux[2]; + } +} + +/** + * @brief Extract mass fractions of constituent species from tchem struct. + * Note: "allocation" of `float* ret` happens in io_copy_temp_buffer() + * + * @param engine the engine + * @param part the particle to extract data from + * @param xpart the according xpart to extract data from + * @param ret (return) the extracted data + */ +INLINE static void rt_convert_mass_fractions(const struct engine* engine, + const struct part* part, + const struct xpart* xpart, + float* ret) { + + ret[0] = part->rt_data.tchem.mass_fraction_HI; + ret[1] = part->rt_data.tchem.mass_fraction_HII; + ret[2] = part->rt_data.tchem.mass_fraction_HeI; + ret[3] = part->rt_data.tchem.mass_fraction_HeII; + ret[4] = part->rt_data.tchem.mass_fraction_HeIII; +} + +/** + * @brief Creates additional output fields for the radiative + * transfer data of hydro particles. + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +INLINE static int rt_write_particles(const struct part* parts, + const struct xpart* xparts, struct io_props* list) { + + int num_elements = 3; + + list[0] = io_make_physical_output_field_convert_part( + "PhotonEnergies", FLOAT, RT_NGROUPS, UNIT_CONV_ENERGY, 0, parts, + /*xparts=*/NULL, + /*convertible to comoving=*/1, rt_convert_radiation_energies, + "Photon Energies (all groups)"); + + list[1] = io_make_physical_output_field_convert_part( + "PhotonFluxes", FLOAT, 3 * RT_NGROUPS, UNIT_CONV_RADIATION_FLUX, 0, parts, + /*xparts=*/NULL, + /*convertible to comoving=*/1, rt_convert_radiation_fluxes, + "Photon Fluxes (all groups; x, y, and z coordinates)"); + list[2] = io_make_output_field_convert_part( + "IonMassFractions", FLOAT, 5, UNIT_CONV_NO_UNITS, 0, parts, + /*xparts=*/NULL, rt_convert_mass_fractions, + "Mass fractions of all constituent species"); + + #if COOLING_GRACKLE_MODE >= 1 + /* List what we want to write */ + list[num_elements] = io_make_output_field_convert_part( + "AtomicHydrogenMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, + convert_part_HI_mass, "Atomic hydrogen masses."); + num_elements ++; + + list[num_elements] = + io_make_output_field_convert_part( + "MolecularHydrogenMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, + convert_part_H2_mass, "Molecular hydrogen masses."); + num_elements ++; + + list[num_elements] = + io_make_output_field_convert_part( + "HeIMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, + convert_part_HeII_mass, "HeI masses."); + num_elements ++; + + list[num_elements] = + io_make_output_field_convert_part( + "HeIIMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, + convert_part_HeII_mass, "HeII masses."); + num_elements ++; + + list[num_elements] = io_make_output_field_convert_part( + "ElectronNumberDensities", FLOAT, 1, UNIT_CONV_NUMBER_DENSITY, -3.f, + parts, xparts, convert_part_e_density, + "Electron number densities"); + num_elements ++; + +#if COOLING_GRACKLE_MODE >= 2 + list[num_elements] = + io_make_output_field("SubgridTemperatures", + FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + cooling_data.subgrid_temp, + "Temperature of subgrid ISM in K"); + num_elements ++; + + list[num_elements] = + io_make_output_field("SubgridDensities", + FLOAT, 1, UNIT_CONV_DENSITY, -3.f, parts, + cooling_data.subgrid_dens, + "Mass density in physical units of subgrid ISM"); + num_elements ++; + + list[num_elements] = + io_make_output_field("DustMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + cooling_data.dust_mass, "Total mass in dust"); + num_elements ++; + + list[num_elements] = + io_make_output_field("DustTemperatures", + FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + cooling_data.dust_temperature, + "Dust temperature in subgrid dust model, in K"); + num_elements ++; +#endif +#endif + +#ifdef SWIFT_RT_DEBUG_CHECKS + num_elements += 8; + list[3] = + io_make_output_field("RTDebugInjectionDone", INT, 1, UNIT_CONV_NO_UNITS, + 0, parts, rt_data.debug_injection_done, + "How many times rt_injection_update_photon_density " + "has been called"); + list[4] = io_make_output_field( + "RTDebugCallsIactGradientInteractions", INT, 1, UNIT_CONV_NO_UNITS, 0, + parts, rt_data.debug_calls_iact_gradient_interaction, + "number of calls to this particle during the gradient interaction loop " + "from the actual interaction function"); + list[5] = io_make_output_field("RTDebugCallsIactTransportInteractions", INT, + 1, UNIT_CONV_NO_UNITS, 0, parts, + rt_data.debug_calls_iact_transport_interaction, + "number of calls to this particle during the " + "transport interaction loop from the actual " + "interaction function"); + list[6] = + io_make_output_field("RTDebugGradientsDone", INT, 1, UNIT_CONV_NO_UNITS, + 0, parts, rt_data.debug_gradients_done, + "How many times finalise_gradients was called"); + list[7] = + io_make_output_field("RTDebugTransportDone", INT, 1, UNIT_CONV_NO_UNITS, + 0, parts, rt_data.debug_transport_done, + "How many times finalise_transport was called"); + list[8] = io_make_output_field( + "RTDebugThermochemistryDone", INT, 1, UNIT_CONV_NO_UNITS, 0, parts, + rt_data.debug_thermochem_done, "How many times rt_tchem was called"); + list[9] = io_make_output_field( + "RTDebugRadAbsorbedTot", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0, parts, + rt_data.debug_radiation_absorbed_tot, + "Radiation absorbed by this part during its lifetime"); + list[10] = io_make_output_field( + "RTDebugSubcycles", INT, 1, UNIT_CONV_NO_UNITS, 0, parts, + rt_data.debug_nsubcycles, "How many times this part was subcycled"); +#endif + + return num_elements; +} + +/** + * @brief Creates additional output fields for the radiative + * transfer data of star particles. + * + * @param sparts The star particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +INLINE static int rt_write_stars(const struct spart* sparts, + struct io_props* list) { + int num_elements = 0; + +#ifdef SWIFT_RT_DEBUG_CHECKS + num_elements += 4; + list[0] = io_make_output_field("RTDebugHydroIact", INT, 1, UNIT_CONV_NO_UNITS, + 0, sparts, rt_data.debug_iact_hydro_inject, + "number of interactions between this star " + "particle and any particle during injection"); + list[1] = io_make_output_field( + "RTDebugEmissionRateSet", INT, 1, UNIT_CONV_NO_UNITS, 0, sparts, + rt_data.debug_emission_rate_set, "Stellar photon emission rates set?"); + list[2] = io_make_output_field( + "RTDebugRadEmittedTot", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0, sparts, + rt_data.debug_radiation_emitted_tot, + "Total radiation emitted during the lifetime of this star"); + list[3] = io_make_output_field("RTDebugInjectedPhotonEnergy", FLOAT, + RT_NGROUPS, UNIT_CONV_ENERGY, 0, sparts, + rt_data.debug_injected_energy_tot, + "Total radiation actually injected into gas"); +#endif + + return num_elements; +} + +/** + * @brief Write the RT model properties to the snapshot. + * + * @param h_grp The HDF5 group in which to write + * @param h_grp_columns The HDF5 group containing named columns + * @param e The engine + * @param internal_units The internal unit system + * @param snapshot_units Units used for the snapshot + * @param rtp The #rt_props + */ +INLINE static void rt_write_flavour(hid_t h_grp, hid_t h_grp_columns, + const struct engine* e, + const struct unit_system* internal_units, + const struct unit_system* snapshot_units, + const struct rt_props* rtp) { + +#if defined(HAVE_HDF5) + + /* Write scheme name */ + /* ----------------- */ + io_write_attribute_s(h_grp, "RT Scheme", RT_IMPLEMENTATION); + io_write_attribute_s(h_grp, "RT Riemann Solver", RT_RIEMANN_SOLVER_NAME); + + /* Write photon group counts */ + /* ------------------------- */ + io_write_attribute_i(h_grp, "PhotonGroupNumber", RT_NGROUPS); + + /* Write photon group bin edges */ + /* ---------------------------- */ + + /* Note: photon frequency bin edges are kept in cgs. Convert them here to + * internal units so we're still compatible with swiftsimio. */ + const float Hz_internal = + units_cgs_conversion_factor(internal_units, UNIT_CONV_INV_TIME); + const float Hz_internal_inv = 1.f / Hz_internal; + float photon_groups_internal[RT_NGROUPS]; + for (int g = 0; g < RT_NGROUPS; g++) + photon_groups_internal[g] = rtp->photon_groups[g] * Hz_internal_inv; + + hid_t type_float = H5Tcopy(io_hdf5_type(FLOAT)); + + hsize_t dims[1] = {RT_NGROUPS}; + hid_t space = H5Screate_simple(1, dims, NULL); + hid_t dset = H5Dcreate(h_grp, "PhotonGroupEdges", type_float, space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset, type_float, H5S_ALL, H5S_ALL, H5P_DEFAULT, + photon_groups_internal); + + /* Write unit conversion factors for this data set */ + char buffer[FIELD_BUFFER_SIZE] = {0}; + units_cgs_conversion_string(buffer, snapshot_units, UNIT_CONV_INV_TIME, + /*scale_factor_exponent=*/0); + float baseUnitsExp[5]; + units_get_base_unit_exponents_array(baseUnitsExp, UNIT_CONV_INV_TIME); + io_write_attribute_f(dset, "U_M exponent", baseUnitsExp[UNIT_MASS]); + io_write_attribute_f(dset, "U_L exponent", baseUnitsExp[UNIT_LENGTH]); + io_write_attribute_f(dset, "U_t exponent", baseUnitsExp[UNIT_TIME]); + io_write_attribute_f(dset, "U_I exponent", baseUnitsExp[UNIT_CURRENT]); + io_write_attribute_f(dset, "U_T exponent", baseUnitsExp[UNIT_TEMPERATURE]); + io_write_attribute_f(dset, "h-scale exponent", 0.f); + io_write_attribute_f(dset, "a-scale exponent", 0.f); + io_write_attribute_s(dset, "Expression for physical CGS units", buffer); + io_write_attribute_b(dset, "Value stored as physical", 1); + io_write_attribute_b(dset, "Property can be converted to comoving", 0); + + /* Write the actual number this conversion factor corresponds to */ + const double factor = + units_cgs_conversion_factor(snapshot_units, UNIT_CONV_INV_TIME); + io_write_attribute_d( + dset, "Conversion factor to CGS (not including cosmological corrections)", + factor); + io_write_attribute_d( + dset, + "Conversion factor to physical CGS (including cosmological corrections)", + factor * pow(e->cosmology->a, 0.f)); + + H5Dclose(dset); + /* H5Tclose(type_float); [> close this later <] */ + + /* If without RT, we have nothing more to do. */ + const int with_rt = e->policy & engine_policy_rt; + if (!with_rt) return; + + /* Write photon group names */ + /* -------------------------*/ + + /* Generate Energy Group names */ + char names_energy[RT_NGROUPS * RT_LABELS_SIZE]; + for (int g = 0; g < RT_NGROUPS; g++) { + char newEname[RT_LABELS_SIZE]; + sprintf(newEname, "Group%d", g + 1); + strcpy(names_energy + g * RT_LABELS_SIZE, newEname); + } + + /* Now write them down */ + hid_t type_string_label = H5Tcopy(H5T_C_S1); + H5Tset_size(type_string_label, RT_LABELS_SIZE); + + hsize_t dimsE[1] = {RT_NGROUPS}; + hid_t spaceE = H5Screate_simple(1, dimsE, NULL); + hid_t dsetE = H5Dcreate(h_grp_columns, "PhotonEnergies", type_string_label, + spaceE, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dsetE, type_string_label, H5S_ALL, H5S_ALL, H5P_DEFAULT, + names_energy); + H5Dclose(dsetE); + + /* Generate Fluxes Group Names */ + char names_fluxes[3 * RT_NGROUPS * RT_LABELS_SIZE]; + int i = 0; + for (int g = 0; g < RT_NGROUPS; g++) { + char newFnameX[RT_LABELS_SIZE]; + sprintf(newFnameX, "Group%dX", g + 1); + strcpy(names_fluxes + i * RT_LABELS_SIZE, newFnameX); + i++; + char newFnameY[RT_LABELS_SIZE]; + sprintf(newFnameY, "Group%dY", g + 1); + strcpy(names_fluxes + i * RT_LABELS_SIZE, newFnameY); + i++; + char newFnameZ[RT_LABELS_SIZE]; + sprintf(newFnameZ, "Group%dZ", g + 1); + strcpy(names_fluxes + i * RT_LABELS_SIZE, newFnameZ); + i++; + } + + /* Now write them down */ + hsize_t dimsF[1] = {3 * RT_NGROUPS}; + hid_t spaceF = H5Screate_simple(1, dimsF, NULL); + hid_t dsetF = H5Dcreate(h_grp_columns, "PhotonFluxes", type_string_label, + spaceF, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dsetF, type_string_label, H5S_ALL, H5S_ALL, H5P_DEFAULT, + names_fluxes); + H5Dclose(dsetF); + + /* H5Tclose(type_string_label); [> close this later <] */ + + /* Write reduced speed of light */ + /* ---------------------------- */ + /* hid_t type2 = H5Tcopy(io_hdf5_type(FLOAT)); */ + + hsize_t dims_cred[1] = {1}; + hid_t space_cred = H5Screate_simple(1, dims_cred, NULL); + hid_t dset_cred = + H5Dcreate(h_grp, "ReducedLightspeed", type_float, space_cred, H5P_DEFAULT, + H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset_cred, type_float, H5S_ALL, H5S_ALL, H5P_DEFAULT, + &rt_params.reduced_speed_of_light); + + /* Write unit conversion factors for this data set */ + char buffer_cred[FIELD_BUFFER_SIZE] = {0}; + units_cgs_conversion_string(buffer_cred, snapshot_units, UNIT_CONV_VELOCITY, + /*scale_factor_exponent=*/0); + float baseUnitsExp_cred[5]; + units_get_base_unit_exponents_array(baseUnitsExp_cred, UNIT_CONV_VELOCITY); + io_write_attribute_f(dset_cred, "U_M exponent", baseUnitsExp_cred[UNIT_MASS]); + io_write_attribute_f(dset_cred, "U_L exponent", + baseUnitsExp_cred[UNIT_LENGTH]); + io_write_attribute_f(dset_cred, "U_t exponent", baseUnitsExp_cred[UNIT_TIME]); + io_write_attribute_f(dset_cred, "U_I exponent", + baseUnitsExp_cred[UNIT_CURRENT]); + io_write_attribute_f(dset_cred, "U_T exponent", + baseUnitsExp_cred[UNIT_TEMPERATURE]); + io_write_attribute_f(dset_cred, "h-scale exponent", 0.f); + io_write_attribute_f(dset_cred, "a-scale exponent", 0.f); + io_write_attribute_s(dset_cred, "Expression for physical CGS units", + buffer_cred); + io_write_attribute_b(dset_cred, "Value stored as physical", 1); + io_write_attribute_b(dset_cred, "Property can be converted to comoving", 0); + + /* Write the actual number this conversion factor corresponds to */ + /* TODO Mladen: check cosmology. reduced_speed_of_light is physical only for + * now. */ + const double factor_cred = + units_cgs_conversion_factor(snapshot_units, UNIT_CONV_VELOCITY); + io_write_attribute_d( + dset_cred, + "Conversion factor to CGS (not including cosmological corrections)", + factor_cred); + io_write_attribute_d( + dset_cred, + "Conversion factor to physical CGS (including cosmological corrections)", + factor_cred * pow(e->cosmology->a, 0.f)); + + H5Dclose(dset_cred); + + /* Write constituent species mass fractions */ + /* ---------------------------------------- */ + + char names_mf[5 * RT_LABELS_SIZE]; + strcpy(names_mf + 0 * RT_LABELS_SIZE, "HI\0"); + strcpy(names_mf + 1 * RT_LABELS_SIZE, "HII\0"); + strcpy(names_mf + 2 * RT_LABELS_SIZE, "HeI\0"); + strcpy(names_mf + 3 * RT_LABELS_SIZE, "HeII\0"); + strcpy(names_mf + 4 * RT_LABELS_SIZE, "HeIII\0"); + + hsize_t dims_mf[1] = {5}; + hid_t space_mf = H5Screate_simple(1, dims_mf, NULL); + hid_t dset_mf = + H5Dcreate(h_grp_columns, "IonMassFractions", type_string_label, space_mf, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset_mf, type_string_label, H5S_ALL, H5S_ALL, H5P_DEFAULT, names_mf); + H5Dclose(dset_mf); + + /* Clean up after yourself */ + /* ----------------------- */ + + /* Close up the types */ + H5Tclose(type_float); + H5Tclose(type_string_label); + +#endif /* HAVE_HDF5 */ +} + +#endif /* SWIFT_RT_IO_KIARA_H */ diff --git a/src/rt/KIARA/rt_ionization_equilibrium.h b/src/rt/KIARA/rt_ionization_equilibrium.h new file mode 100644 index 0000000000..64a504d5c1 --- /dev/null +++ b/src/rt/KIARA/rt_ionization_equilibrium.h @@ -0,0 +1,294 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_IONIZATION_EQUILIBRIUM_H +#define SWIFT_RT_IONIZATION_EQUILIBRIUM_H + +#include "rt_thermochemistry_utils.h" + +/* some local definitions */ +#define RT_ION_EQUIL_ITER_MAX 50 +#define RT_ION_EQUIL_EPSILON 1e-4 + +/** + * @file src/rt/KIARA/rt_ionization_equilibrium.h + * @brief Function to get the number densities for primordial species as a + * function of the temperature, assuming ionization equilibrium (see Katz et + * al. 1996; ui.adsabs.harvard.edu/abs/1996ApJS..105...19K) + */ + +/** + * @brief compute the ionization equilibrium mass fractions + * for a given temperature + * @param T temperature in K + * @param X total mass fraction of all hydrogen species + * @param Y total mass fraction of all helium species + * @param XHI (return) mass fraction of HI + * @param XHII (return) mass fraction of HII + * @param XHeI (return) mass fraction of HeI + * @param XHeII (return) mass fraction of HeII + * @param XHeIII (return) mass fraction of HeII + */ +__attribute__((always_inline)) INLINE static void +rt_ion_equil_mass_fractions_from_T(double T, float X, float Y, float* XHI, + float* XHII, float* XHeI, float* XHeII, + float* XHeIII) { + + if (fabsf(X + Y - 1.f) > 1e-4) + error("mass fractions don't add up to one: X=%.3e, Y=%.3e", X, Y); + + /* Total number densities for all H and He species, respectively. + * We don't really care about the actual number densities of the + * species, but only want the mass fractions. So all quantities + * will be per unit volume, specifically per rho_gas / m_proton. */ + double nH, nHe; + + /* TODO: this assumes no metals. */ + if (X <= RT_KIARA_TINY_MASS_FRACTION) { + nH = RT_KIARA_TINY_MASS_FRACTION; + nHe = 1.; + } else { + nH = X; + nHe = 0.25 * Y; + } + + /* Number densities of the actual species */ + float nHI, nHII, nHeI, nHeII, nHeIII; + + if (T < 5e3) { + /* Below 5000K, we just go with fully neutral gas. + * Not only is this the case, but some divisions + * would otherwise not be safe and lead to problems + * if we don't exception handle it here. */ + nHI = nH; + nHII = 0.f; + nHeI = nHe; + nHeII = 0.f; + nHeIII = 0.f; + + } else { + + const double T_inv = 1. / T; + const double sqrtT = sqrt(T); + const double temp_pow07 = 1. / (1. + pow(T * 1e-6, 0.7)); + const double temp_sqrtT5 = 1. / (1. + sqrt(T * 1e-5)); + const double temp_pow02 = pow(T * 1e-3, -0.2); + /* Recombination rate for H+ in units of cm^3 s^-1 */ + const double A_Hp = 8.40e-11 / sqrtT * temp_pow02 * temp_pow07; + /* Dielectronic recombination rate for He+ in units of cm^3 s^-1 */ + const double A_d = 1.9e-3 / pow(T, 1.5) * exp(-470000.0 * T_inv) * + (1.0 + 0.3 * exp(-94000.0 * T_inv)); + /* Recombination rate for He+ in units of cm^3 s^-1 */ + const double A_Hep = 1.5e-10 / pow(T, 0.6353); + /* Recombination rate for He++ in units of cm^3 s^-1 */ + const double A_Hepp = 3.36e-10 / sqrtT * temp_pow02 * temp_pow07; + /* collisional ionization rate for H0 in units of cm^3 s^-1 */ + const double G_H0 = 5.85e-11 * sqrtT * exp(-157809.1 * T_inv) * temp_sqrtT5; + /* collisional ionization rate for He0 in units of cm^3 s^-1 */ + const double G_He0 = + 2.38e-11 * sqrtT * exp(-285335.4 * T_inv) * temp_sqrtT5; + /* collisional ionization rate for He+ in units of cm^3 s^-1 */ + const double G_Hep = + 5.68e-12 * sqrtT * exp(-631515.0 * T_inv) * temp_sqrtT5; + + /* Katz et al. 1996 eq. 33 - 38 */ + /* Note: We assume all photoionization rates to be zero. */ + nHI = nH * A_Hp / (A_Hp + G_H0); + nHII = nH - nHI; + const double div1 = (A_Hep + A_d) / G_He0; + const double div2 = G_Hep / A_Hepp; + nHeII = nHe / (1. + div1 + div2); + nHeI = nHeII * div1; + nHeIII = nHeII * div2; + /* ne = nHII + nHeII + 2.f * nHeIII; */ + } + + /* With the number densities per unit volume given, + * let's compute the mass fractions now. */ + const double mHI = nHI; + const double mHII = nHII; + const double mHeI = 4.0f * nHeI; + const double mHeII = 4.0f * nHeII; + const double mHeIII = 4.0f * nHeIII; + /* we ignore the electron mass fraction. */ + + const double mtot_inv = 1. / (mHI + mHII + mHeI + mHeII + mHeIII); + + *XHI = (float)(mHI * mtot_inv); + *XHI = max(*XHI, RT_KIARA_TINY_MASS_FRACTION); + *XHII = (float)(mHII * mtot_inv); + *XHII = max(*XHII, RT_KIARA_TINY_MASS_FRACTION); + *XHeI = (float)(mHeI * mtot_inv); + *XHeI = max(*XHeI, RT_KIARA_TINY_MASS_FRACTION); + *XHeII = (float)(mHeII * mtot_inv); + *XHeII = max(*XHeII, RT_KIARA_TINY_MASS_FRACTION); + *XHeIII = (float)(mHeIII * mtot_inv); + *XHeIII = max(*XHeIII, RT_KIARA_TINY_MASS_FRACTION); +} + +/** + * @brief compute the ionization equilibrium mass fractions + * for a given particle. + * @param XHI (return) mass fraction of HI + * @param XHII (return) mass fraction of HII + * @param XHeI (return) mass fraction of HeI + * @param XHeII (return) mass fraction of HeII + * @param XHeIII (return) mass fraction of HeII + * @param p part to work with + * @param rt_props rt_properties struct + * @param hydro_props hydro properties struct + * @param phys_const physical constants struct + * @param us unit system struct + * @param cosmo cosmology struct + */ +__attribute__((always_inline)) INLINE static void +rt_ion_equil_get_mass_fractions(float* XHI, float* XHII, float* XHeI, + float* XHeII, float* XHeIII, + struct part* restrict p, + const struct rt_props* rt_props, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo) { + + /* get conversions and constants */ + const double internal_energy_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); + const float dimension_kB[5] = {1, 2, -2, 0, -1}; /* [g cm^2 s^-2 K^-1] */ + const double kB_to_cgs = + units_general_cgs_conversion_factor(us, dimension_kB); + const double kB_cgs = phys_const->const_boltzmann_k * kB_to_cgs; + const double mp_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_MASS); + const double mp_cgs = phys_const->const_proton_mass * mp_to_cgs; + + /* Get the specific internal energy of the gas */ + const float u_minimal = hydro_props->minimal_internal_energy; + /* Using 'drifted' version here because I'm lazy and don't want to pass + * the xpart down to use in this function. */ + const float u_part = hydro_get_drifted_physical_internal_energy(p, cosmo); + const double u_expect = + ((double)max(u_minimal, u_part)) * internal_energy_to_cgs; + double mu_guess, T_guess; + + /* Get a first estimate for gas temperature. */ + const float X = rt_props->hydrogen_mass_fraction; + const float Y = rt_props->helium_mass_fraction; + *XHI = X; + *XHII = 0.f; + *XHeI = Y; + *XHeII = 0.f; + *XHeIII = 0.f; + mu_guess = + rt_tchem_get_mean_molecular_weight(*XHI, *XHII, *XHeI, *XHeII, *XHeIII); + T_guess = rt_tchem_temperature_from_internal_energy(u_expect, mu_guess, + kB_cgs, mp_cgs); + + if (T_guess > 1e4) { + /* T_guess should be in K, so it's fine to hardcode + * the ionization limit of ~1e4 K for the first guess here + * If we're above the temperature threshold with this guess, + * assume we're fully ionized as first guess instead. */ + *XHI = 0.f; + *XHII = X; + *XHeI = 0.f; + *XHeII = 0.f; + *XHeIII = Y; + mu_guess = + rt_tchem_get_mean_molecular_weight(*XHI, *XHII, *XHeI, *XHeII, *XHeIII); + T_guess = rt_tchem_temperature_from_internal_energy(u_expect, mu_guess, + kB_cgs, mp_cgs); + } + + /* Now given the first temperature guess, update + * the mass fractions and mean molecular weight */ + rt_ion_equil_mass_fractions_from_T(T_guess, X, Y, XHI, XHII, XHeI, XHeII, + XHeIII); + mu_guess = + rt_tchem_get_mean_molecular_weight(*XHI, *XHII, *XHeI, *XHeII, *XHeIII); + + /* Get first guess for internal energy */ + double u_guess = + rt_tchem_internal_energy_from_T(T_guess, mu_guess, kB_cgs, mp_cgs); + + int iter = 0; + double du = u_expect - u_guess; + double du_old = du; /* initialize as same value */ + + while (fabs(du) >= RT_ION_EQUIL_EPSILON * u_expect) { + iter += 1; + if (iter > RT_ION_EQUIL_ITER_MAX) { + message( + "Warning: Ionization Equilibrium iteration didn't converge; " + "T=%.6g, 1 - u/u_correct = %.6g", + T_guess, 1. - u_guess / u_expect); + break; + } + + if (T_guess < 0.) { + message("Warning: Got negative temperature, resetting"); + T_guess = 0.1; /* Note: T_guess should be in K here */ + } + + /* find next temperature guess by solving linear equation + * m * T_next + n = u_expect - u_guess_new ~ 0 + * NOTE: we pretend that the function that we're looking the + * root of is f(T) = u_expect - u_guess(T), with u_expect = const. + * so df/dT = - du_guess/dT, therefore we add a minus sign here. */ + double m = -rt_tchem_internal_energy_dT(mu_guess, kB_cgs, mp_cgs); + double n = u_expect - u_guess - m * T_guess; + double T_next = -n / m; + + /* Given the new temperature guess, compute the + * expected mean molecular weight */ + rt_ion_equil_mass_fractions_from_T(T_next, X, Y, XHI, XHII, XHeI, XHeII, + XHeIII); + double mu_next = + rt_tchem_get_mean_molecular_weight(*XHI, *XHII, *XHeI, *XHeII, *XHeIII); + + /* now given the new temperature and mass fraction guess, + * update the expected gas internal energy */ + double u_next = + rt_tchem_internal_energy_from_T(T_next, mu_next, kB_cgs, mp_cgs); + + /* save the old internal energy, and get the new one */ + du_old = du; + du = u_expect - u_next; + + /* if we're oscillating between positive and negative + * values, try a bisection to help out */ + if (du_old * du < 0.0) { + T_next = 0.5 * (T_guess + T_next); + rt_ion_equil_mass_fractions_from_T(T_next, X, Y, XHI, XHII, XHeI, XHeII, + XHeIII); + mu_next = rt_tchem_get_mean_molecular_weight(*XHI, *XHII, *XHeI, *XHeII, + *XHeIII); + u_next = rt_tchem_internal_energy_from_T(T_next, mu_next, kB_cgs, mp_cgs); + } + + /* prepare for next iteration */ + T_guess = T_next; + mu_guess = mu_next; + u_guess = u_next; + du = u_expect - u_next; + } +} + +#undef RT_ION_EQUIL_ITER_MAX +#undef RT_ION_EQUIL_EPSILON + +#endif /* SWIFT_RT_IONIZATION_EQUILIBRIUM_H */ diff --git a/src/rt/KIARA/rt_parameters.h b/src/rt/KIARA/rt_parameters.h new file mode 100644 index 0000000000..8d9b422e64 --- /dev/null +++ b/src/rt/KIARA/rt_parameters.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ + +#ifndef SWIFT_KIARA_RT_PARAMETERS_H +#define SWIFT_KIARA_RT_PARAMETERS_H + +/** + * @file src/rt/KIARA/rt_parameters.h + * @brief Global RT parameters. + */ + +extern struct rt_parameters rt_params; + +/** + * Some global RT related parameters. + */ +struct rt_parameters { + float reduced_speed_of_light; + float reduced_speed_of_light_inverse; +}; + +#endif /* SWIFT_KIARA_RT_PARAMETERS_H */ diff --git a/src/rt/KIARA/rt_properties.h b/src/rt/KIARA/rt_properties.h new file mode 100644 index 0000000000..37bd958516 --- /dev/null +++ b/src/rt/KIARA/rt_properties.h @@ -0,0 +1,620 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_PROPERTIES_KIARA_H +#define SWIFT_RT_PROPERTIES_KIARA_H + +#include "rt_grackle_utils.h" +#include "rt_interaction_rates.h" +#include "rt_parameters.h" +#include "rt_stellar_emission_model.h" + +#include + +/** + * @file src/rt/KIARA/rt_properties.h + * @brief Main header file for the 'KIARA' radiative transfer scheme + * properties. + */ + +#define RT_IMPLEMENTATION "KIARA M1closure" + +#if defined(RT_RIEMANN_SOLVER_GLF) +#define RT_RIEMANN_SOLVER_NAME "GLF Riemann Solver" +#elif defined(RT_RIEMANN_SOLVER_HLL) +#define RT_RIEMANN_SOLVER_NAME "HLL Riemann Solver" +#else +#error "No valid choice of RT Riemann solver has been selected" +#endif + +/** + * @brief Properties of the 'KIARA' radiative transfer model + */ +struct rt_props { + + /* Which stellar emission model to use */ + enum rt_stellar_emission_models stellar_emission_model; + + /* (Lower) frequency bin edges for photon groups */ + float photon_groups[RT_NGROUPS]; + + /* Global constant stellar emission rates */ + double stellar_const_emission_rates[RT_NGROUPS]; + + /* CFL condition */ + float CFL_condition; + + /* Factor to limit cooling time by */ + float f_limit_cooling_time; + + /* do we set initial ionization mass fractions manually? */ + int set_initial_ionization_mass_fractions; + int set_equilibrium_initial_ionization_mass_fractions; + + /* initial mass fractions for ionization species */ + /* the following are required for manually setting exact values */ + float mass_fraction_HI_init; + float mass_fraction_HII_init; + float mass_fraction_HeI_init; + float mass_fraction_HeII_init; + float mass_fraction_HeIII_init; + /* float number_density_electrons_init; [> todo: do we need this? <] */ + + /* Hydrogen and Helium mass fractions of the non-metal portion of the gas */ + float hydrogen_mass_fraction; + float helium_mass_fraction; + + /* Skip thermochemistry? For testing/debugging only! */ + int skip_thermochemistry; + + /* Coupling with galaxy subgrid physics? */ + int rt_with_galaxy_subgrid; + + /* Re-do thermochemistry recursively if difference in internal energy is too + * big? */ + int max_tchem_recursion; + + /* Optionally restrict maximal timestep for stars */ + float stars_max_timestep; + + /* Which stellar spectrum type to use? */ + int stellar_spectrum_type; + /* If constant: get max frequency */ + double const_stellar_spectrum_max_frequency; + /* If blackbody: get temperature */ + double stellar_spectrum_blackbody_T; + + /* Storage for integrated photoionization cross sections */ + /* Note: they are always in cgs. */ + double** energy_weighted_cross_sections; + double** number_weighted_cross_sections; + /* Mean photon energy in frequency bin for user provided spectrum. In erg.*/ + double average_photon_energy[RT_NGROUPS]; + /* Integral over photon numbers of user provided spectrum. */ + double photon_number_integral[RT_NGROUPS]; + + /* Location of Stellar spectrum tables. */ + char stellar_table_path[200]; + + /* Storeage for the BPASS photon number table in a 3d array with three photon + * group 2d tables. + * ionizing_tables[0] = ionizing_HI_table + * ionizing_tables[1] = ionizing_HeI_table + * ionizing_tables[2] = ionizing_HeII_table*/ + double*** ionizing_tables; + + /* Photon escape fraction from stellar emission model. */ + double f_esc; + + /* Grackle Stuff */ + /* ------------- */ + + /*! grackle unit system */ + code_units grackle_units; + + /*! grackle chemistry data */ + chemistry_data grackle_chemistry_data; + + /*! grackle chemistry data storage + * (needed for local function calls) */ + chemistry_data_storage grackle_chemistry_rates; + + /*! use case B recombination? */ + int case_B_recombination; + + /*! make grackle talkative? */ + int grackle_verbose; + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* radiation emitted by stars this step. This is not really a property, + * but a placeholder to sum up a global variable. It's being reset + * every timestep. */ + unsigned long long debug_radiation_emitted_this_step; + + /* total radiation emitted by stars. This is not really a property, + * but a placeholder to sum up a global variable */ + unsigned long long debug_radiation_emitted_tot; + + /* radiation absorbed by gas this step. This is not really a property, + * but a placeholder to sum up a global variable */ + unsigned long long debug_radiation_absorbed_this_step; + + /* total radiation absorbed by gas. This is not really a property, + * but a placeholder to sum up a global variable */ + unsigned long long debug_radiation_absorbed_tot; + + /* Max number of subcycles per hydro step */ + int debug_max_nr_subcycles; +#endif +}; + +/* Some declarations to avoid cyclical inclusions. */ +/* ----------------------------------------------- */ +/* Keep the declarations for *after* the definition of rt_props struct */ + +/** + * @brief allocate and pre-compute the averaged cross sections + * for each photon group and ionizing species. + * Declare this here to avoid cyclical inclusions. + * + * @param rt_props RT properties struct + * @param phys_const physical constants struct + * @param us internal units struct + **/ +void rt_cross_sections_init(struct rt_props* restrict rt_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us); + +/* Function to read the table. */ +double **read_Bpass_from_hdf5(char *file_name, char *dataset_name); + +/* Now for the good stuff */ +/* ------------------------------------- */ + +/** + * @brief Print the RT model. + * + * @param rtp The #rt_props + */ +__attribute__((always_inline)) INLINE static void rt_props_print( + const struct rt_props* rtp) { + + /* Only the master print */ + if (engine_rank != 0) return; + + message("Radiative transfer scheme: '%s'", RT_IMPLEMENTATION); + message("RT Riemann Solver used: '%s'", RT_RIEMANN_SOLVER_NAME); + char messagestring[200] = "Using photon frequency bins: [ "; + char freqstring[20]; + for (int g = 0; g < RT_NGROUPS; g++) { + sprintf(freqstring, "%.3g ", rtp->photon_groups[g]); + strcat(messagestring, freqstring); + } + strcat(messagestring, "]"); + message("%s", messagestring); + + if (rtp->stellar_emission_model == rt_stellar_emission_model_const) { + strcpy(messagestring, "Using constant stellar emission rates: [ "); + for (int g = 0; g < RT_NGROUPS; g++) { + sprintf(freqstring, "%.3g ", rtp->stellar_const_emission_rates[g]); + strcat(messagestring, freqstring); + } + strcat(messagestring, "]"); + message("%s", messagestring); + } else if (rtp->stellar_emission_model == + rt_stellar_emission_model_IlievTest) { + message("Using Iliev+06 Test 4 stellar emission model."); + } else if (rtp->stellar_emission_model == + rt_stellar_emission_model_BPASS) { + message("Using BPASS stellar emission model."); + } else { + error("Unknown stellar emission model %d", rtp->stellar_emission_model); + } + + if (rtp->set_equilibrium_initial_ionization_mass_fractions) + message( + "Setting initial ionization mass fractions " + "assuming ionization equilibrium"); + if (rtp->set_initial_ionization_mass_fractions) + message( + "Using initial ionization mass fractions specified in parameter file"); + if (rtp->skip_thermochemistry) + message("WARNING: Thermochemistry will be skipped."); + if (rtp->rt_with_galaxy_subgrid) + message("RT is coupling with galaxy subgrid modules."); +} + +/** + * @brief Initialize the global properties of the RT scheme. + * + * @param rtp The #rt_props. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void rt_props_init( + struct rt_props* rtp, const struct phys_const* phys_const, + const struct unit_system* us, struct swift_params* params, + struct cosmology* cosmo) { + + /* Make sure we reset debugging counters correctly after + * zeroth step. */ + + /* Read in photon frequency group properties */ + /* ----------------------------------------- */ + if (RT_NGROUPS <= 0) { + error( + "You need to run KIARA-RT with at least 1 photon group, " + "you have %d", + RT_NGROUPS); + } else { + /* !! Keep the frequencies in Hz for now. !! */ + parser_get_param_float_array(params, "KIARART:photon_groups_Hz", RT_NGROUPS, + rtp->photon_groups); + } + + /* Sanity check: photon group edges must be in increasing order. */ + for (int g = 0; g < RT_NGROUPS - 1; g++) { + if (rtp->photon_groups[g + 1] <= rtp->photon_groups[g]) + error( + "Photon frequency bin edges need to be in increasing order. " + "Found index %d %.3g <= %.3g", + g + 1, rtp->photon_groups[g + 1], rtp->photon_groups[g]); + } + + /* Get stellar emission rate model related parameters */ + /* -------------------------------------------------- */ + + /* First initialize everything */ + for (int g = 0; g < RT_NGROUPS; g++) { + rtp->stellar_const_emission_rates[g] = 0.; + } + + rtp->stellar_emission_model = rt_stellar_emission_model_none; + + char stellar_model_str[80]; + parser_get_param_string(params, "KIARART:stellar_luminosity_model", + stellar_model_str); + + if (strcmp(stellar_model_str, "const") == 0) { + rtp->stellar_emission_model = rt_stellar_emission_model_const; + } else if (strcmp(stellar_model_str, "IlievTest4") == 0) { + rtp->stellar_emission_model = rt_stellar_emission_model_IlievTest; + } else if (strcmp(stellar_model_str, "BPASS") == 0) { + rtp->stellar_emission_model = rt_stellar_emission_model_BPASS; + } else { + error("Unknown stellar luminosity model '%s'", stellar_model_str); + } + + if (rtp->stellar_emission_model == rt_stellar_emission_model_const) { + /* Read the luminosities from the parameter file */ + double emission_rates[RT_NGROUPS]; + parser_get_param_double_array(params, + "KIARART:const_stellar_luminosities_LSol", + RT_NGROUPS, emission_rates); + const double unit_power = units_cgs_conversion_factor(us, UNIT_CONV_POWER); + const double unit_power_inv = 1. / unit_power; + for (int g = 0; g < RT_NGROUPS; g++) { + rtp->stellar_const_emission_rates[g] = emission_rates[g] * unit_power_inv; + } + } else if (rtp->stellar_emission_model == + rt_stellar_emission_model_IlievTest) { + /* Nothing to do here */ + } else if (rtp->stellar_emission_model == + rt_stellar_emission_model_BPASS) { + parser_get_param_string(params, "KIARART:stellar_table_path", + rtp->stellar_table_path); + } else { + error("Unknown stellar emission model %d", rtp->stellar_emission_model); + } + + /* get reduced speed of light factor */ + /* --------------------------------- */ + const float f_r = parser_get_param_float(params, "KIARART:f_reduce_c"); + if (f_r <= 0.f) + error("Invalid speed of light reduction factor: %.3e <= 0.", f_r); + rt_params.reduced_speed_of_light = phys_const->const_speed_light_c * f_r; + rt_params.reduced_speed_of_light_inverse = + 1.f / rt_params.reduced_speed_of_light; + + /* get CFL condition */ + /* ----------------- */ + const float CFL = parser_get_param_float(params, "KIARART:CFL_condition"); + if (CFL <= 0.f) error("Invalid CFL number: %.3e <= 0.", CFL); + rtp->CFL_condition = CFL; + + const float f_limit_cooling_time = parser_get_opt_param_float( + params, "KIARART:f_limit_cooling_time", /*default=*/0.6); + if (f_limit_cooling_time < 0.f) + error("Invalid cooling time reduction factor: %.3e < 0.", + f_limit_cooling_time); + else if (f_limit_cooling_time == 0.f) + message("Warning: Computation of cooling time will be skipped"); + rtp->f_limit_cooling_time = f_limit_cooling_time; + + /* Get thermochemistry set-up */ + /* -------------------------- */ + rtp->hydrogen_mass_fraction = + parser_get_param_float(params, "KIARART:hydrogen_mass_fraction"); + rtp->helium_mass_fraction = 1.f - rtp->hydrogen_mass_fraction; + if (rtp->hydrogen_mass_fraction <= 0.f || rtp->hydrogen_mass_fraction > 1.f) + error("Invalid hydrogen mass fraction: %g", rtp->hydrogen_mass_fraction); + + /* Are we manually overwriting initial mass fractions of H and He? */ + rtp->set_initial_ionization_mass_fractions = parser_get_opt_param_int( + params, "KIARART:set_initial_ionization_mass_fractions", + /* default = */ 0); + if (rtp->set_initial_ionization_mass_fractions) { + /* Read in mass fractions */ + rtp->mass_fraction_HI_init = + parser_get_param_float(params, "KIARART:mass_fraction_HI"); + rtp->mass_fraction_HII_init = + parser_get_param_float(params, "KIARART:mass_fraction_HII"); + rtp->mass_fraction_HeI_init = + parser_get_param_float(params, "KIARART:mass_fraction_HeI"); + rtp->mass_fraction_HeII_init = + parser_get_param_float(params, "KIARART:mass_fraction_HeII"); + rtp->mass_fraction_HeIII_init = + parser_get_param_float(params, "KIARART:mass_fraction_HeIII"); + + /* Temporary check neglecting metals. Make sure we sum up to 1. */ + const float h_sum = + rtp->mass_fraction_HI_init + rtp->mass_fraction_HII_init; + if (fabsf(h_sum - rtp->hydrogen_mass_fraction) > 1e-4) + error( + "Inconsistent H mass fractions: XH_tot %.6g != XHI %.6g + XHII %.6g", + rtp->hydrogen_mass_fraction, rtp->mass_fraction_HI_init, + rtp->mass_fraction_HII_init); + + const float he_sum = rtp->mass_fraction_HeI_init + + rtp->mass_fraction_HeII_init + + rtp->mass_fraction_HeIII_init; + if (fabsf(he_sum - rtp->helium_mass_fraction) > 1e-4) + error( + "Inconsistent He mass fractions: XHe_tot %.6g != XHeI %.6g + XHeII " + "%.6g + XHeIII %.6g", + rtp->helium_mass_fraction, rtp->mass_fraction_HeI_init, + rtp->mass_fraction_HeII_init, rtp->mass_fraction_HeIII_init); + + const float mass_fraction_sum = h_sum + he_sum; + if (fabsf(mass_fraction_sum - 1.f) > 1e-5) + error("Constituent species mass fraction sums up to %.6f, I expect 1.0", + mass_fraction_sum); + } else { + /* Initialize properties to deliberately bogus values */ + rtp->mass_fraction_HI_init = -1.f; + rtp->mass_fraction_HII_init = -1.f; + rtp->mass_fraction_HeI_init = -1.f; + rtp->mass_fraction_HeII_init = -1.f; + rtp->mass_fraction_HeIII_init = -1.f; + } + + /* Are we setting up initial mass fractions in equilibrium? */ + rtp->set_equilibrium_initial_ionization_mass_fractions = + parser_get_opt_param_int( + params, "KIARART:set_equilibrium_initial_ionization_mass_fractions", + /* default = */ 0); + + if (rtp->set_equilibrium_initial_ionization_mass_fractions && + rtp->set_initial_ionization_mass_fractions) + error( + "Can't use equilibrium initial ionization mass fractions " + "simultaneously with manually set mass fractions. Pick one."); + + /* Are we skipping thermochemistry? */ + rtp->skip_thermochemistry = parser_get_opt_param_int( + params, "KIARART:skip_thermochemistry", /* default = */ 0); + + /* Are we coupling with galaxy subgrid modules? */ + rtp->rt_with_galaxy_subgrid = parser_get_opt_param_int( + params, "KIARART:rt_with_galaxy_subgrid", /* default = */ 0); + + /* Are we re-doing thermochemistry? */ + rtp->max_tchem_recursion = parser_get_opt_param_int( + params, "KIARART:max_tchem_recursion", /* default = */ 0); + + /* Stellar Spectra */ + /* --------------- */ + + /* Initialize conditional parameters to bogus values */ + rtp->const_stellar_spectrum_max_frequency = -1.; + rtp->stellar_spectrum_blackbody_T = -1.; + rtp->ionizing_tables = NULL; + rtp->f_esc = -1.; + + rtp->stellar_spectrum_type = + parser_get_param_int(params, "KIARART:stellar_spectrum_type"); + + /* Check for BPASS setup. */ + if (rtp->stellar_emission_model == rt_stellar_emission_model_BPASS && rtp->stellar_spectrum_type != 2) { + error("Warning: stellar_luminosity_model is BPASS, but stellar_spectrum_type is not 2!\n"); + } else if (rtp->stellar_emission_model != rt_stellar_emission_model_BPASS && rtp->stellar_spectrum_type == 2) { + error( "Warning: stellar_spectrum_type is 2, but stellar_luminosity_model is not BPASS!\n"); + } + + if (rtp->stellar_spectrum_type == 0) { + /* Constant spectrum: Read additional parameter */ + /* TODO: also translate back to internal units at later. For now, keep it in + * Hz */ + rtp->const_stellar_spectrum_max_frequency = parser_get_param_float( + params, "KIARART:stellar_spectrum_const_max_frequency_Hz"); + } else if (rtp->stellar_spectrum_type == 1) { + /* Blackbody spectrum: Read additional parameter */ + rtp->stellar_spectrum_blackbody_T = parser_get_param_float( + params, "KIARART:stellar_spectrum_blackbody_temperature_K"); + rtp->stellar_spectrum_blackbody_T /= + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + } else if (rtp->stellar_spectrum_type == 2) { + /* TODO: currently we use the const stellar spectrum to calculate photon group properties. + * We need to change it to use BPASS model value. */ + rtp->const_stellar_spectrum_max_frequency = parser_get_param_float( + params, "KIARART:stellar_spectrum_const_max_frequency_Hz"); + + /* Read the photon escape fraction value. */ + rtp->f_esc = parser_get_param_float( + params, "KIARART:photon_escape_fraction"); + + /* Read the table from BPASS. */ + char *fname = malloc(256); + char *dataset_name_HI = "/Table_HI/block0_values"; + char *dataset_name_HeI = "/Table_HeI/block0_values"; + char *dataset_name_HeII = "/Table_HeII/block0_values"; + /* Read stellar table filename. */ + sprintf(fname, "%s", rtp->stellar_table_path); + + int num_tables = 3; + rtp->ionizing_tables = malloc(num_tables * sizeof(double**)); + + rtp->ionizing_tables[0] = read_Bpass_from_hdf5(fname, dataset_name_HI); + rtp->ionizing_tables[1] = read_Bpass_from_hdf5(fname, dataset_name_HeI); + rtp->ionizing_tables[2] = read_Bpass_from_hdf5(fname, dataset_name_HeII); + + /* print table check. */ + //for (hsize_t i = 0; i < 13; i++){ + // for (hsize_t j = 0; j < 22; j++) { + // printf("%.2f\n", rtp->ionizing_tables[0][i][j]); + // } + // } + + /* free the data name. */ + free(fname); + } else { + error("Selected unknown stellar spectrum type %d", + rtp->stellar_spectrum_type); + } + + /* Maximal Star Timestep? */ + /* ---------------------- */ + rtp->stars_max_timestep = parser_get_opt_param_float( + params, "KIARART:stars_max_timestep", /*default=*/FLT_MAX); + /* Turn off if negative value given */ + if (rtp->stars_max_timestep < 0.f) rtp->stars_max_timestep = FLT_MAX; + /* Better safe than sorry */ + if (rtp->stars_max_timestep == 0.f) + error("You are restricting star time step to 0. That's a no-no."); + +#ifdef SWIFT_RT_DEBUG_CHECKS + rtp->debug_radiation_emitted_tot = 0ULL; + rtp->debug_radiation_emitted_this_step = 0ULL; + + rtp->debug_radiation_absorbed_tot = 0ULL; + rtp->debug_radiation_absorbed_this_step = 0ULL; + + /* Don't make it an optional parameter here so we crash + * if I forgot to provide it */ + rtp->debug_max_nr_subcycles = + parser_get_param_int(params, "TimeIntegration:max_nr_rt_subcycles"); +#endif + + /* Grackle setup */ + /* ------------- */ + rtp->grackle_verbose = + parser_get_opt_param_int(params, "KIARART:grackle_verbose", /*default=*/0); + rtp->case_B_recombination = parser_get_opt_param_int( + params, "KIARART:case_B_recombination", /*default=*/1); + rt_init_grackle(&rtp->grackle_units, &rtp->grackle_chemistry_data, + &rtp->grackle_chemistry_rates, rtp->hydrogen_mass_fraction, + rtp->grackle_verbose, rtp->case_B_recombination, us, cosmo); + + /* Pre-compute interaction rates/cross sections */ + /* -------------------------------------------- */ + + rtp->energy_weighted_cross_sections = NULL; + rtp->number_weighted_cross_sections = NULL; + rt_cross_sections_init(rtp, phys_const, us); + + /* Finishers */ + /* --------- */ +} + +__attribute__((always_inline)) INLINE static void rt_props_update( + struct rt_props* rtp, const struct unit_system* us, + struct cosmology* cosmo) { + update_grackle_units_cosmo(&(rtp->grackle_units), us, cosmo); +} + +/** + * @brief Write an RT properties struct to the given FILE as a + * stream of bytes. + * + * @param props the struct + * @param stream the file stream + */ +__attribute__((always_inline)) INLINE static void rt_struct_dump( + const struct rt_props* props, FILE* stream) { + + restart_write_blocks((void*)props, sizeof(struct rt_props), 1, stream, + "RT props", "RT properties struct"); + /* The RT parameters, in particular the reduced speed of light, are + * not defined at compile time. So we need to read them in again. */ + restart_write_blocks(&rt_params, sizeof(struct rt_parameters), 1, stream, + "RT global parameters", "RT global parameters struct"); +} + +/** + * @brief Restore an RT properties struct from the given FILE as + * a stream of bytes. + * + * @param props the struct + * @param stream the file stream + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param cosmo the #cosmology + */ +__attribute__((always_inline)) INLINE static void rt_struct_restore( + struct rt_props* props, FILE* stream, const struct phys_const* phys_const, + const struct unit_system* us, const struct cosmology* restrict cosmo) { + + restart_read_blocks((void*)props, sizeof(struct rt_props), 1, stream, NULL, + "RT properties struct"); + /* Set up stuff that needs array allocation */ + rt_init_grackle(&props->grackle_units, &props->grackle_chemistry_data, + &props->grackle_chemistry_rates, + props->hydrogen_mass_fraction, props->grackle_verbose, + props->case_B_recombination, us, cosmo); + + props->energy_weighted_cross_sections = NULL; + props->number_weighted_cross_sections = NULL; + rt_cross_sections_init(props, phys_const, us); + + /* Read the table from BPASS. */ + char *fname = malloc(256); + char *dataset_name_HI = "/Table_HI/block0_values"; + char *dataset_name_HeI = "/Table_HeI/block0_values"; + char *dataset_name_HeII = "/Table_HeII/block0_values"; + /* Read stellar table filename. */ + sprintf(fname, "%s", props->stellar_table_path); + + int num_tables = 3; + props->ionizing_tables = malloc(num_tables * sizeof(double**)); + + props->ionizing_tables[0] = read_Bpass_from_hdf5(fname, dataset_name_HI); + props->ionizing_tables[1] = read_Bpass_from_hdf5(fname, dataset_name_HeI); + props->ionizing_tables[2] = read_Bpass_from_hdf5(fname, dataset_name_HeII); + + /* free the data name. */ + free(fname); + + /* The RT parameters, in particular the reduced speed of light, are + * not defined at compile time. So we need to write them down. */ + restart_read_blocks(&rt_params, sizeof(struct rt_parameters), 1, stream, NULL, + "RT global parameters struct"); +} + +#endif /* SWIFT_RT_PROPERTIES_KIARA_H */ diff --git a/src/rt/KIARA/rt_riemann_GLF.h b/src/rt/KIARA/rt_riemann_GLF.h new file mode 100644 index 0000000000..2ce9e10d24 --- /dev/null +++ b/src/rt/KIARA/rt_riemann_GLF.h @@ -0,0 +1,80 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ + +#ifndef SWIFT_KIARA_RT_RIEMANN_GLF_H +#define SWIFT_KIARA_RT_RIEMANN_GLF_H + +#include "rt_getters.h" +#include "rt_parameters.h" +#include "rt_unphysical.h" + +/** + * @file src/rt/KIARA/rt_riemann_GLF.h + * @brief The Global Lax-Friedrich (GLF) Riemann solver for the moments of the + * radiative transfer equation following Rosdahl et al 2013 + * */ + +/** + * @brief Solve the Riemann problem for the RT equations and return the + * flux at the interface. + * + * @param UL left state (radiation energy density, flux) + * @param UR right state (radiation energy density, flux) + * @param FLnorm the norm of the radiation flux of the left state + * @param FRnorm the norm of the radiation flux of the right state + * @param hyperFluxL the flux of the hyperbolic conservation law of the left + * state + * @param hyperFluxR the flux of the hyperbolic conservation law of the right + * state + * @param n_unit the unit vector perpendicular to the "intercell" surface. + * @param flux_half (return) the resulting flux at the interface + */ +__attribute__((always_inline)) INLINE static void rt_riemann_solve_for_flux( + const float UL[4], const float UR[4], const float FLnorm, + const float FRnorm, float hyperFluxL[4][3], float hyperFluxR[4][3], + const float n_unit[3], float flux_half[4]) { + float fluxL[4]; + fluxL[0] = hyperFluxL[0][0] * n_unit[0] + hyperFluxL[0][1] * n_unit[1] + + hyperFluxL[0][2] * n_unit[2]; + fluxL[1] = hyperFluxL[1][0] * n_unit[0] + hyperFluxL[1][1] * n_unit[1] + + hyperFluxL[1][2] * n_unit[2]; + fluxL[2] = hyperFluxL[2][0] * n_unit[0] + hyperFluxL[2][1] * n_unit[1] + + hyperFluxL[2][2] * n_unit[2]; + fluxL[3] = hyperFluxL[3][0] * n_unit[0] + hyperFluxL[3][1] * n_unit[1] + + hyperFluxL[3][2] * n_unit[2]; + + float fluxR[4]; + fluxR[0] = hyperFluxR[0][0] * n_unit[0] + hyperFluxR[0][1] * n_unit[1] + + hyperFluxR[0][2] * n_unit[2]; + fluxR[1] = hyperFluxR[1][0] * n_unit[0] + hyperFluxR[1][1] * n_unit[1] + + hyperFluxR[1][2] * n_unit[2]; + fluxR[2] = hyperFluxR[2][0] * n_unit[0] + hyperFluxR[2][1] * n_unit[1] + + hyperFluxR[2][2] * n_unit[2]; + fluxR[3] = hyperFluxR[3][0] * n_unit[0] + hyperFluxR[3][1] * n_unit[1] + + hyperFluxR[3][2] * n_unit[2]; + + const float c_red = rt_params.reduced_speed_of_light; + + flux_half[0] = 0.5f * (fluxL[0] + fluxR[0] - c_red * (UR[0] - UL[0])); + flux_half[1] = 0.5f * (fluxL[1] + fluxR[1] - c_red * (UR[1] - UL[1])); + flux_half[2] = 0.5f * (fluxL[2] + fluxR[2] - c_red * (UR[2] - UL[2])); + flux_half[3] = 0.5f * (fluxL[3] + fluxR[3] - c_red * (UR[3] - UL[3])); +} + +#endif /* SWIFT_KIARA_RT_RIEMANN_GLF_H */ diff --git a/src/rt/KIARA/rt_riemann_HLL.h b/src/rt/KIARA/rt_riemann_HLL.h new file mode 100644 index 0000000000..c20c160dec --- /dev/null +++ b/src/rt/KIARA/rt_riemann_HLL.h @@ -0,0 +1,224 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ + +#ifndef SWIFT_KIARA_RT_RIEMANN_HLL_H +#define SWIFT_KIARA_RT_RIEMANN_HLL_H + +#define RT_RIEMANN_HLL_NPOINTS 100 +#define RT_RIEMANN_HLL_DF (1.f / (float)(RT_RIEMANN_HLL_NPOINTS - 1)) +#define RT_RIEMANN_HLL_ONE_OVER_DF ((float)RT_RIEMANN_HLL_NPOINTS - 1) +#define RT_RIEMANN_HLL_DTHETA (M_PI / ((float)(RT_RIEMANN_HLL_NPOINTS - 1))) +#define RT_RIEMANN_HLL_ONE_OVER_DTHETA \ + (((float)(RT_RIEMANN_HLL_NPOINTS - 1)) / M_PI) + +#include "rt_getters.h" +#include "rt_parameters.h" +#include "rt_riemann_HLL_eigenvalues.h" +#include "rt_unphysical.h" + +/** + * @file src/rt/KIARA/rt_riemann_HLL.h + * @brief The Hartmann-Lax-van Leer Riemann solver for the moments of the + * radiative transfer equation following following Gonzalez et al 2007 + * (ui.adsabs.harvard.edu/abs/2007A%26A...464..429G). + * */ + +/** + * @brief Interpolate the minimal and maximal eigenvalues from + * the lookup table given the reduced flux f and angle w.r.t. + * the surface theta. + * + * @param f reduced flux |F|/(c E) + * @param theta angle between flux and surface + * @param lambda_min minimal eigenvalue + * @param lambda_max maximal eigenvalue + */ + +__attribute__((always_inline)) INLINE static void +rt_riemann_interpolate_eigenvals(float f, float theta, float *lambda_min, + float *lambda_max) { + + /* find lower table indices for f and theta */ + int f_ind = floor(RT_RIEMANN_HLL_ONE_OVER_DF * f); + if (f_ind >= RT_RIEMANN_HLL_NPOINTS - 1) f_ind = RT_RIEMANN_HLL_NPOINTS - 2; + int theta_ind = floor(RT_RIEMANN_HLL_ONE_OVER_DTHETA * theta); + if (theta_ind >= RT_RIEMANN_HLL_NPOINTS - 1) + theta_ind = RT_RIEMANN_HLL_NPOINTS - 2; + + /* Grab the data */ + const float Q11min = rt_riemann_HLL_eigenvals[f_ind][theta_ind][0]; + const float Q12min = rt_riemann_HLL_eigenvals[f_ind][theta_ind + 1][0]; + const float Q21min = rt_riemann_HLL_eigenvals[f_ind + 1][theta_ind][0]; + const float Q22min = rt_riemann_HLL_eigenvals[f_ind + 1][theta_ind + 1][0]; + const float Q11max = rt_riemann_HLL_eigenvals[f_ind][theta_ind][1]; + const float Q12max = rt_riemann_HLL_eigenvals[f_ind][theta_ind + 1][1]; + const float Q21max = rt_riemann_HLL_eigenvals[f_ind + 1][theta_ind][1]; + const float Q22max = rt_riemann_HLL_eigenvals[f_ind + 1][theta_ind + 1][1]; + + /* (f - f1)/(f2 - f1) = (f - f_ind * Delta f)/Delta f */ + const float df1 = f * RT_RIEMANN_HLL_ONE_OVER_DF - (float)f_ind; + /* (f2 - f)/(f2 - f1) = ((f_ind + 1) * Delta f - f)/Delta f */ + const float df2 = 1.f - df1; + + /* linear interpolation in f direction */ + const float fmid1_min = df2 * Q11min + df1 * Q21min; + const float fmid2_min = df2 * Q12min + df1 * Q22min; + const float fmid1_max = df2 * Q11max + df1 * Q21max; + const float fmid2_max = df2 * Q12max + df1 * Q22max; + + /* Now second interpolation in theta direction */ + /* (theta - theta1)/(theta2 - theta1) */ + const float dtheta1 = + theta * RT_RIEMANN_HLL_ONE_OVER_DTHETA - (float)theta_ind; + /* (theta2 - theta)/(theta2 - theta1) */ + const float dtheta2 = 1.f - dtheta1; + + /* Make sure -1 < eigenvalue < 1 */ + float lmin = dtheta2 * fmid1_min + dtheta1 * fmid2_min; + lmin = max(lmin, -1.f); + lmin = min(lmin, 1.f); + *lambda_min = lmin; + float lmax = dtheta2 * fmid1_max + dtheta1 * fmid2_max; + lmax = max(lmax, -1.f); + lmax = min(lmax, 1.f); + *lambda_max = lmax; +} + +/** + * @brief Solve the Riemann problem for the RT equations and return the + * flux at the interface. + * + * @param UL left state (radiation energy density, flux) + * @param UR right state (radiation energy density, flux) + * @param FLnorm the norm of the radiation flux of the left state + * @param FRnorm the norm of the radiation flux of the right state + * @param hyperFluxL the flux of the hyperbolic conservation law of the left + * state + * @param hyperFluxR the flux of the hyperbolic conservation law of the right + * state + * @param n_unit the unit vector perpendicular to the "intercell" surface. + * @param flux_half (return) the resulting flux at the interface + */ +__attribute__((always_inline)) INLINE static void rt_riemann_solve_for_flux( + const float UL[4], const float UR[4], const float FLnorm, + const float FRnorm, float hyperFluxL[4][3], float hyperFluxR[4][3], + const float n_unit[3], float flux_half[4]) { + + /* Compute reduced fluxes and angles between surface and flux. + * These are based on physical fluxes, not hyperbolic fluxes. */ + const float c_red = rt_params.reduced_speed_of_light; + const float c_red_inv = rt_params.reduced_speed_of_light_inverse; + + float fL = 0.f; + float thetaL = 0.f; + + if (UL[0] > 0.f) { + fL = FLnorm / UL[0] * c_red_inv; + fL = min(fL, 1.f); + } + + if (FLnorm > 0.f) { + /* cos theta = F * n / (|F| |n|) */ + const float FLdotn = + UL[1] * n_unit[0] + UL[2] * n_unit[1] + UL[3] * n_unit[2]; + float costhetaL = min(FLdotn / FLnorm, 1.f); + costhetaL = max(costhetaL, -1.f); + thetaL = acosf(costhetaL); + } + + float fR = 0.f; + float thetaR = 0.f; + + if (UR[0] > 0.f) { + fR = FRnorm / UR[0] * c_red_inv; + fR = min(fR, 1.f); + } + + if (FRnorm > 0.f) { + const float FRdotn = + UR[1] * n_unit[0] + UR[2] * n_unit[1] + UR[3] * n_unit[2]; + float costhetaR = min(FRdotn / FRnorm, 1.f); + costhetaR = max(costhetaR, -1.f); + thetaR = acosf(costhetaR); + } + + /* interpolate eigenvalues in lookup table */ + float lambdaLmin = 0; + float lambdaLmax = 0; + rt_riemann_interpolate_eigenvals(fL, thetaL, &lambdaLmin, &lambdaLmax); + float lambdaRmin = 0; + float lambdaRmax = 0; + rt_riemann_interpolate_eigenvals(fR, thetaR, &lambdaRmin, &lambdaRmax); + + const float lminus = min3(lambdaLmin, lambdaRmin, 0.f); + const float lplus = max3(lambdaLmax, lambdaRmax, 0.f); + + /* Sanity check: This should give the same results as GLF solver */ + /* const float lminus = -1.f; */ + /* const float lplus = +1.f; */ + + if (lminus == 0.f && lplus == 0.f) { + flux_half[0] = 0.f; + flux_half[1] = 0.f; + flux_half[2] = 0.f; + flux_half[3] = 0.f; + return; + } + + /* Project the (hyperbolic) flux along the surface, + * reduce the problem to 1D with 4 quantities*/ + float fluxL[4]; + fluxL[0] = hyperFluxL[0][0] * n_unit[0] + hyperFluxL[0][1] * n_unit[1] + + hyperFluxL[0][2] * n_unit[2]; + fluxL[1] = hyperFluxL[1][0] * n_unit[0] + hyperFluxL[1][1] * n_unit[1] + + hyperFluxL[1][2] * n_unit[2]; + fluxL[2] = hyperFluxL[2][0] * n_unit[0] + hyperFluxL[2][1] * n_unit[1] + + hyperFluxL[2][2] * n_unit[2]; + fluxL[3] = hyperFluxL[3][0] * n_unit[0] + hyperFluxL[3][1] * n_unit[1] + + hyperFluxL[3][2] * n_unit[2]; + + float fluxR[4]; + fluxR[0] = hyperFluxR[0][0] * n_unit[0] + hyperFluxR[0][1] * n_unit[1] + + hyperFluxR[0][2] * n_unit[2]; + fluxR[1] = hyperFluxR[1][0] * n_unit[0] + hyperFluxR[1][1] * n_unit[1] + + hyperFluxR[1][2] * n_unit[2]; + fluxR[2] = hyperFluxR[2][0] * n_unit[0] + hyperFluxR[2][1] * n_unit[1] + + hyperFluxR[2][2] * n_unit[2]; + fluxR[3] = hyperFluxR[3][0] * n_unit[0] + hyperFluxR[3][1] * n_unit[1] + + hyperFluxR[3][2] * n_unit[2]; + + const float one_over_dl = 1.f / (lplus - lminus); + /* Remember that the eigenvalues are in units of c */ + const float lprod = lplus * lminus * c_red; + + flux_half[0] = + (lplus * fluxL[0] - lminus * fluxR[0] + lprod * (UR[0] - UL[0])) * + one_over_dl; + flux_half[1] = + (lplus * fluxL[1] - lminus * fluxR[1] + lprod * (UR[1] - UL[1])) * + one_over_dl; + flux_half[2] = + (lplus * fluxL[2] - lminus * fluxR[2] + lprod * (UR[2] - UL[2])) * + one_over_dl; + flux_half[3] = + (lplus * fluxL[3] - lminus * fluxR[3] + lprod * (UR[3] - UL[3])) * + one_over_dl; +} + +#endif /* SWIFT_KIARA_RT_RIEMANN_HLL_H */ diff --git a/src/rt/KIARA/rt_riemann_HLL_eigenvalues.h b/src/rt/KIARA/rt_riemann_HLL_eigenvalues.h new file mode 100644 index 0000000000..ff0d489863 --- /dev/null +++ b/src/rt/KIARA/rt_riemann_HLL_eigenvalues.h @@ -0,0 +1,5246 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ + +#ifndef RT_RIEMANN_HLL_EIGENVALUES_H +#define RT_RIEMANN_HLL_EIGENVALUES_H + +/** + * @file src/rt/KIARA/rt_riemann_HLL_eigenvalues.h + * @brief Contains the tabulated eigenvalues for the + * HLL RT Riemann solver following Gonzalez et al 2007 + * (ui.adsabs.harvard.edu/abs/2007A%26A...464..429G). + * + * For more details on how these eigenvalues are computed, look + * into /theory/RadiativeTransfer/HLLRiemannSolverEigenvalues + * */ + +/* We store the minimal and maximal eigenvalues for + * RT_RIEMANN_HLL_NPOINTS different values of + * 0 < f < 1 and 0 < theta < pi. + * + * First index: f + * Second index: theta + * [f][theta][0]: min eigenvalue + * [f][theta][1]: max eigenvalue + * */ +static float rt_riemann_HLL_eigenvals + [RT_RIEMANN_HLL_NPOINTS][RT_RIEMANN_HLL_NPOINTS][2] = { + { + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + {-.577350E+00, 0.577350E+00}, {-.577350E+00, 0.577350E+00}, + }, + { + {-.572277E+00, 0.582379E+00}, {-.572280E+00, 0.582376E+00}, + {-.572288E+00, 0.582369E+00}, {-.572300E+00, 0.582356E+00}, + {-.572318E+00, 0.582338E+00}, {-.572341E+00, 0.582316E+00}, + {-.572369E+00, 0.582288E+00}, {-.572402E+00, 0.582255E+00}, + {-.572440E+00, 0.582218E+00}, {-.572483E+00, 0.582175E+00}, + {-.572531E+00, 0.582128E+00}, {-.572583E+00, 0.582076E+00}, + {-.572641E+00, 0.582019E+00}, {-.572703E+00, 0.581957E+00}, + {-.572770E+00, 0.581891E+00}, {-.572841E+00, 0.581820E+00}, + {-.572917E+00, 0.581744E+00}, {-.572998E+00, 0.581665E+00}, + {-.573082E+00, 0.581580E+00}, {-.573172E+00, 0.581492E+00}, + {-.573265E+00, 0.581399E+00}, {-.573362E+00, 0.581303E+00}, + {-.573464E+00, 0.581202E+00}, {-.573569E+00, 0.581097E+00}, + {-.573678E+00, 0.580989E+00}, {-.573791E+00, 0.580877E+00}, + {-.573907E+00, 0.580761E+00}, {-.574027E+00, 0.580642E+00}, + {-.574150E+00, 0.580520E+00}, {-.574276E+00, 0.580394E+00}, + {-.574406E+00, 0.580265E+00}, {-.574538E+00, 0.580134E+00}, + {-.574673E+00, 0.579999E+00}, {-.574811E+00, 0.579862E+00}, + {-.574951E+00, 0.579722E+00}, {-.575094E+00, 0.579580E+00}, + {-.575239E+00, 0.579435E+00}, {-.575386E+00, 0.579289E+00}, + {-.575535E+00, 0.579140E+00}, {-.575686E+00, 0.578990E+00}, + {-.575839E+00, 0.578838E+00}, {-.575993E+00, 0.578684E+00}, + {-.576148E+00, 0.578529E+00}, {-.576304E+00, 0.578373E+00}, + {-.576462E+00, 0.578216E+00}, {-.576620E+00, 0.578058E+00}, + {-.576779E+00, 0.577899E+00}, {-.576939E+00, 0.577739E+00}, + {-.577099E+00, 0.577580E+00}, {-.577259E+00, 0.577419E+00}, + {-.577419E+00, 0.577259E+00}, {-.577580E+00, 0.577099E+00}, + {-.577739E+00, 0.576939E+00}, {-.577899E+00, 0.576779E+00}, + {-.578058E+00, 0.576620E+00}, {-.578216E+00, 0.576462E+00}, + {-.578373E+00, 0.576304E+00}, {-.578529E+00, 0.576148E+00}, + {-.578684E+00, 0.575993E+00}, {-.578838E+00, 0.575839E+00}, + {-.578990E+00, 0.575686E+00}, {-.579140E+00, 0.575535E+00}, + {-.579289E+00, 0.575386E+00}, {-.579435E+00, 0.575239E+00}, + {-.579580E+00, 0.575094E+00}, {-.579722E+00, 0.574951E+00}, + {-.579862E+00, 0.574811E+00}, {-.579999E+00, 0.574673E+00}, + {-.580134E+00, 0.574538E+00}, {-.580265E+00, 0.574406E+00}, + {-.580394E+00, 0.574276E+00}, {-.580520E+00, 0.574150E+00}, + {-.580642E+00, 0.574027E+00}, {-.580761E+00, 0.573907E+00}, + {-.580877E+00, 0.573791E+00}, {-.580989E+00, 0.573678E+00}, + {-.581097E+00, 0.573569E+00}, {-.581202E+00, 0.573464E+00}, + {-.581303E+00, 0.573362E+00}, {-.581399E+00, 0.573265E+00}, + {-.581492E+00, 0.573172E+00}, {-.581580E+00, 0.573082E+00}, + {-.581665E+00, 0.572998E+00}, {-.581744E+00, 0.572917E+00}, + {-.581820E+00, 0.572841E+00}, {-.581891E+00, 0.572770E+00}, + {-.581957E+00, 0.572703E+00}, {-.582019E+00, 0.572641E+00}, + {-.582076E+00, 0.572583E+00}, {-.582128E+00, 0.572531E+00}, + {-.582175E+00, 0.572483E+00}, {-.582218E+00, 0.572440E+00}, + {-.582255E+00, 0.572402E+00}, {-.582288E+00, 0.572369E+00}, + {-.582316E+00, 0.572341E+00}, {-.582338E+00, 0.572318E+00}, + {-.582356E+00, 0.572300E+00}, {-.582369E+00, 0.572288E+00}, + {-.582376E+00, 0.572280E+00}, {-.582379E+00, 0.572277E+00}, + }, + { + {-.567159E+00, 0.587364E+00}, {-.567164E+00, 0.587359E+00}, + {-.567180E+00, 0.587344E+00}, {-.567205E+00, 0.587319E+00}, + {-.567241E+00, 0.587284E+00}, {-.567287E+00, 0.587239E+00}, + {-.567343E+00, 0.587183E+00}, {-.567410E+00, 0.587118E+00}, + {-.567486E+00, 0.587043E+00}, {-.567572E+00, 0.586959E+00}, + {-.567668E+00, 0.586864E+00}, {-.567774E+00, 0.586760E+00}, + {-.567889E+00, 0.586647E+00}, {-.568014E+00, 0.586524E+00}, + {-.568148E+00, 0.586392E+00}, {-.568292E+00, 0.586251E+00}, + {-.568444E+00, 0.586100E+00}, {-.568606E+00, 0.585941E+00}, + {-.568776E+00, 0.585774E+00}, {-.568955E+00, 0.585597E+00}, + {-.569142E+00, 0.585413E+00}, {-.569338E+00, 0.585220E+00}, + {-.569541E+00, 0.585019E+00}, {-.569752E+00, 0.584811E+00}, + {-.569971E+00, 0.584594E+00}, {-.570198E+00, 0.584371E+00}, + {-.570431E+00, 0.584140E+00}, {-.570671E+00, 0.583903E+00}, + {-.570918E+00, 0.583659E+00}, {-.571172E+00, 0.583408E+00}, + {-.571431E+00, 0.583151E+00}, {-.571697E+00, 0.582889E+00}, + {-.571967E+00, 0.582620E+00}, {-.572244E+00, 0.582346E+00}, + {-.572525E+00, 0.582067E+00}, {-.572811E+00, 0.581784E+00}, + {-.573102E+00, 0.581495E+00}, {-.573396E+00, 0.581203E+00}, + {-.573695E+00, 0.580906E+00}, {-.573997E+00, 0.580606E+00}, + {-.574303E+00, 0.580302E+00}, {-.574611E+00, 0.579995E+00}, + {-.574922E+00, 0.579685E+00}, {-.575235E+00, 0.579373E+00}, + {-.575550E+00, 0.579059E+00}, {-.575867E+00, 0.578743E+00}, + {-.576186E+00, 0.578425E+00}, {-.576505E+00, 0.578106E+00}, + {-.576825E+00, 0.577787E+00}, {-.577146E+00, 0.577466E+00}, + {-.577466E+00, 0.577146E+00}, {-.577787E+00, 0.576825E+00}, + {-.578106E+00, 0.576505E+00}, {-.578425E+00, 0.576186E+00}, + {-.578743E+00, 0.575867E+00}, {-.579059E+00, 0.575550E+00}, + {-.579373E+00, 0.575235E+00}, {-.579685E+00, 0.574922E+00}, + {-.579995E+00, 0.574611E+00}, {-.580302E+00, 0.574303E+00}, + {-.580606E+00, 0.573997E+00}, {-.580906E+00, 0.573695E+00}, + {-.581203E+00, 0.573396E+00}, {-.581495E+00, 0.573102E+00}, + {-.581784E+00, 0.572811E+00}, {-.582067E+00, 0.572525E+00}, + {-.582346E+00, 0.572244E+00}, {-.582620E+00, 0.571967E+00}, + {-.582889E+00, 0.571697E+00}, {-.583151E+00, 0.571431E+00}, + {-.583408E+00, 0.571172E+00}, {-.583659E+00, 0.570918E+00}, + {-.583903E+00, 0.570671E+00}, {-.584140E+00, 0.570431E+00}, + {-.584371E+00, 0.570198E+00}, {-.584594E+00, 0.569971E+00}, + {-.584811E+00, 0.569752E+00}, {-.585019E+00, 0.569541E+00}, + {-.585220E+00, 0.569338E+00}, {-.585413E+00, 0.569142E+00}, + {-.585597E+00, 0.568955E+00}, {-.585774E+00, 0.568776E+00}, + {-.585941E+00, 0.568606E+00}, {-.586100E+00, 0.568444E+00}, + {-.586251E+00, 0.568292E+00}, {-.586392E+00, 0.568148E+00}, + {-.586524E+00, 0.568014E+00}, {-.586647E+00, 0.567889E+00}, + {-.586760E+00, 0.567774E+00}, {-.586864E+00, 0.567668E+00}, + {-.586959E+00, 0.567572E+00}, {-.587043E+00, 0.567486E+00}, + {-.587118E+00, 0.567410E+00}, {-.587183E+00, 0.567343E+00}, + {-.587239E+00, 0.567287E+00}, {-.587284E+00, 0.567241E+00}, + {-.587319E+00, 0.567205E+00}, {-.587344E+00, 0.567180E+00}, + {-.587359E+00, 0.567164E+00}, {-.587364E+00, 0.567159E+00}, + }, + { + {-.561995E+00, 0.592308E+00}, {-.562002E+00, 0.592301E+00}, + {-.562026E+00, 0.592278E+00}, {-.562064E+00, 0.592240E+00}, + {-.562118E+00, 0.592188E+00}, {-.562187E+00, 0.592120E+00}, + {-.562272E+00, 0.592038E+00}, {-.562372E+00, 0.591941E+00}, + {-.562487E+00, 0.591829E+00}, {-.562616E+00, 0.591702E+00}, + {-.562761E+00, 0.591561E+00}, {-.562920E+00, 0.591406E+00}, + {-.563094E+00, 0.591236E+00}, {-.563282E+00, 0.591053E+00}, + {-.563484E+00, 0.590855E+00}, {-.563700E+00, 0.590644E+00}, + {-.563930E+00, 0.590420E+00}, {-.564173E+00, 0.590182E+00}, + {-.564430E+00, 0.589931E+00}, {-.564699E+00, 0.589668E+00}, + {-.564981E+00, 0.589392E+00}, {-.565275E+00, 0.589103E+00}, + {-.565582E+00, 0.588803E+00}, {-.565900E+00, 0.588491E+00}, + {-.566229E+00, 0.588168E+00}, {-.566570E+00, 0.587834E+00}, + {-.566921E+00, 0.587489E+00}, {-.567283E+00, 0.587134E+00}, + {-.567654E+00, 0.586768E+00}, {-.568035E+00, 0.586393E+00}, + {-.568426E+00, 0.586009E+00}, {-.568825E+00, 0.585616E+00}, + {-.569232E+00, 0.585214E+00}, {-.569648E+00, 0.584804E+00}, + {-.570071E+00, 0.584387E+00}, {-.570501E+00, 0.583962E+00}, + {-.570937E+00, 0.583530E+00}, {-.571380E+00, 0.583092E+00}, + {-.571829E+00, 0.582647E+00}, {-.572283E+00, 0.582197E+00}, + {-.572742E+00, 0.581742E+00}, {-.573205E+00, 0.581283E+00}, + {-.573672E+00, 0.580819E+00}, {-.574142E+00, 0.580351E+00}, + {-.574616E+00, 0.579880E+00}, {-.575092E+00, 0.579406E+00}, + {-.575570E+00, 0.578930E+00}, {-.576049E+00, 0.578451E+00}, + {-.576529E+00, 0.577972E+00}, {-.577010E+00, 0.577491E+00}, + {-.577491E+00, 0.577010E+00}, {-.577972E+00, 0.576529E+00}, + {-.578451E+00, 0.576049E+00}, {-.578930E+00, 0.575570E+00}, + {-.579406E+00, 0.575092E+00}, {-.579880E+00, 0.574616E+00}, + {-.580351E+00, 0.574142E+00}, {-.580819E+00, 0.573672E+00}, + {-.581283E+00, 0.573205E+00}, {-.581742E+00, 0.572742E+00}, + {-.582197E+00, 0.572283E+00}, {-.582647E+00, 0.571829E+00}, + {-.583092E+00, 0.571380E+00}, {-.583530E+00, 0.570937E+00}, + {-.583962E+00, 0.570501E+00}, {-.584387E+00, 0.570071E+00}, + {-.584804E+00, 0.569648E+00}, {-.585214E+00, 0.569232E+00}, + {-.585616E+00, 0.568825E+00}, {-.586009E+00, 0.568426E+00}, + {-.586393E+00, 0.568035E+00}, {-.586768E+00, 0.567654E+00}, + {-.587134E+00, 0.567283E+00}, {-.587489E+00, 0.566921E+00}, + {-.587834E+00, 0.566570E+00}, {-.588168E+00, 0.566229E+00}, + {-.588491E+00, 0.565900E+00}, {-.588803E+00, 0.565582E+00}, + {-.589103E+00, 0.565275E+00}, {-.589392E+00, 0.564981E+00}, + {-.589668E+00, 0.564699E+00}, {-.589931E+00, 0.564430E+00}, + {-.590182E+00, 0.564173E+00}, {-.590420E+00, 0.563930E+00}, + {-.590644E+00, 0.563700E+00}, {-.590855E+00, 0.563484E+00}, + {-.591053E+00, 0.563282E+00}, {-.591236E+00, 0.563094E+00}, + {-.591406E+00, 0.562920E+00}, {-.591561E+00, 0.562761E+00}, + {-.591702E+00, 0.562616E+00}, {-.591829E+00, 0.562487E+00}, + {-.591941E+00, 0.562372E+00}, {-.592038E+00, 0.562272E+00}, + {-.592120E+00, 0.562187E+00}, {-.592188E+00, 0.562118E+00}, + {-.592240E+00, 0.562064E+00}, {-.592278E+00, 0.562026E+00}, + {-.592301E+00, 0.562002E+00}, {-.592308E+00, 0.561995E+00}, + }, + { + {-.556782E+00, 0.597211E+00}, {-.556792E+00, 0.597201E+00}, + {-.556824E+00, 0.597171E+00}, {-.556875E+00, 0.597121E+00}, + {-.556948E+00, 0.597051E+00}, {-.557040E+00, 0.596961E+00}, + {-.557154E+00, 0.596852E+00}, {-.557287E+00, 0.596723E+00}, + {-.557441E+00, 0.596574E+00}, {-.557615E+00, 0.596406E+00}, + {-.557809E+00, 0.596219E+00}, {-.558022E+00, 0.596013E+00}, + {-.558255E+00, 0.595787E+00}, {-.558506E+00, 0.595544E+00}, + {-.558777E+00, 0.595281E+00}, {-.559066E+00, 0.595001E+00}, + {-.559374E+00, 0.594703E+00}, {-.559700E+00, 0.594387E+00}, + {-.560043E+00, 0.594054E+00}, {-.560403E+00, 0.593704E+00}, + {-.560781E+00, 0.593337E+00}, {-.561174E+00, 0.592954E+00}, + {-.561584E+00, 0.592555E+00}, {-.562010E+00, 0.592140E+00}, + {-.562451E+00, 0.591711E+00}, {-.562906E+00, 0.591266E+00}, + {-.563376E+00, 0.590808E+00}, {-.563860E+00, 0.590335E+00}, + {-.564357E+00, 0.589849E+00}, {-.564866E+00, 0.589351E+00}, + {-.565388E+00, 0.588839E+00}, {-.565922E+00, 0.588316E+00}, + {-.566467E+00, 0.587782E+00}, {-.567022E+00, 0.587236E+00}, + {-.567587E+00, 0.586681E+00}, {-.568162E+00, 0.586115E+00}, + {-.568745E+00, 0.585540E+00}, {-.569337E+00, 0.584957E+00}, + {-.569937E+00, 0.584365E+00}, {-.570543E+00, 0.583766E+00}, + {-.571156E+00, 0.583160E+00}, {-.571774E+00, 0.582547E+00}, + {-.572398E+00, 0.581929E+00}, {-.573026E+00, 0.581306E+00}, + {-.573658E+00, 0.580678E+00}, {-.574293E+00, 0.580047E+00}, + {-.574931E+00, 0.579412E+00}, {-.575570E+00, 0.578774E+00}, + {-.576211E+00, 0.578135E+00}, {-.576853E+00, 0.577494E+00}, + {-.577494E+00, 0.576853E+00}, {-.578135E+00, 0.576211E+00}, + {-.578774E+00, 0.575570E+00}, {-.579412E+00, 0.574931E+00}, + {-.580047E+00, 0.574293E+00}, {-.580678E+00, 0.573658E+00}, + {-.581306E+00, 0.573026E+00}, {-.581929E+00, 0.572398E+00}, + {-.582547E+00, 0.571774E+00}, {-.583160E+00, 0.571156E+00}, + {-.583766E+00, 0.570543E+00}, {-.584365E+00, 0.569937E+00}, + {-.584957E+00, 0.569337E+00}, {-.585540E+00, 0.568745E+00}, + {-.586115E+00, 0.568162E+00}, {-.586681E+00, 0.567587E+00}, + {-.587236E+00, 0.567022E+00}, {-.587782E+00, 0.566467E+00}, + {-.588316E+00, 0.565922E+00}, {-.588839E+00, 0.565388E+00}, + {-.589351E+00, 0.564866E+00}, {-.589849E+00, 0.564357E+00}, + {-.590335E+00, 0.563860E+00}, {-.590808E+00, 0.563376E+00}, + {-.591266E+00, 0.562906E+00}, {-.591711E+00, 0.562451E+00}, + {-.592140E+00, 0.562010E+00}, {-.592555E+00, 0.561584E+00}, + {-.592954E+00, 0.561174E+00}, {-.593337E+00, 0.560781E+00}, + {-.593704E+00, 0.560403E+00}, {-.594054E+00, 0.560043E+00}, + {-.594387E+00, 0.559700E+00}, {-.594703E+00, 0.559374E+00}, + {-.595001E+00, 0.559066E+00}, {-.595281E+00, 0.558777E+00}, + {-.595544E+00, 0.558506E+00}, {-.595787E+00, 0.558255E+00}, + {-.596013E+00, 0.558022E+00}, {-.596219E+00, 0.557809E+00}, + {-.596406E+00, 0.557615E+00}, {-.596574E+00, 0.557441E+00}, + {-.596723E+00, 0.557287E+00}, {-.596852E+00, 0.557154E+00}, + {-.596961E+00, 0.557040E+00}, {-.597051E+00, 0.556948E+00}, + {-.597121E+00, 0.556875E+00}, {-.597171E+00, 0.556824E+00}, + {-.597201E+00, 0.556792E+00}, {-.597211E+00, 0.556782E+00}, + }, + { + {-.551521E+00, 0.602074E+00}, {-.551534E+00, 0.602061E+00}, + {-.551573E+00, 0.602024E+00}, {-.551637E+00, 0.601962E+00}, + {-.551728E+00, 0.601875E+00}, {-.551845E+00, 0.601763E+00}, + {-.551987E+00, 0.601627E+00}, {-.552155E+00, 0.601466E+00}, + {-.552348E+00, 0.601281E+00}, {-.552566E+00, 0.601072E+00}, + {-.552809E+00, 0.600839E+00}, {-.553077E+00, 0.600582E+00}, + {-.553369E+00, 0.600302E+00}, {-.553685E+00, 0.599998E+00}, + {-.554025E+00, 0.599671E+00}, {-.554388E+00, 0.599322E+00}, + {-.554775E+00, 0.598951E+00}, {-.555183E+00, 0.598557E+00}, + {-.555614E+00, 0.598142E+00}, {-.556066E+00, 0.597706E+00}, + {-.556540E+00, 0.597249E+00}, {-.557034E+00, 0.596772E+00}, + {-.557548E+00, 0.596275E+00}, {-.558082E+00, 0.595758E+00}, + {-.558635E+00, 0.595223E+00}, {-.559207E+00, 0.594669E+00}, + {-.559796E+00, 0.594097E+00}, {-.560402E+00, 0.593508E+00}, + {-.561025E+00, 0.592902E+00}, {-.561664E+00, 0.592280E+00}, + {-.562319E+00, 0.591643E+00}, {-.562988E+00, 0.590990E+00}, + {-.563670E+00, 0.590323E+00}, {-.564366E+00, 0.589643E+00}, + {-.565075E+00, 0.588949E+00}, {-.565795E+00, 0.588244E+00}, + {-.566526E+00, 0.587526E+00}, {-.567267E+00, 0.586798E+00}, + {-.568018E+00, 0.586059E+00}, {-.568777E+00, 0.585311E+00}, + {-.569544E+00, 0.584554E+00}, {-.570318E+00, 0.583790E+00}, + {-.571099E+00, 0.583018E+00}, {-.571885E+00, 0.582239E+00}, + {-.572676E+00, 0.581455E+00}, {-.573471E+00, 0.580665E+00}, + {-.574269E+00, 0.579872E+00}, {-.575069E+00, 0.579075E+00}, + {-.575870E+00, 0.578276E+00}, {-.576673E+00, 0.577475E+00}, + {-.577475E+00, 0.576673E+00}, {-.578276E+00, 0.575870E+00}, + {-.579075E+00, 0.575069E+00}, {-.579872E+00, 0.574269E+00}, + {-.580665E+00, 0.573471E+00}, {-.581455E+00, 0.572676E+00}, + {-.582239E+00, 0.571885E+00}, {-.583018E+00, 0.571099E+00}, + {-.583790E+00, 0.570318E+00}, {-.584554E+00, 0.569544E+00}, + {-.585311E+00, 0.568777E+00}, {-.586059E+00, 0.568018E+00}, + {-.586798E+00, 0.567267E+00}, {-.587526E+00, 0.566526E+00}, + {-.588244E+00, 0.565795E+00}, {-.588949E+00, 0.565075E+00}, + {-.589643E+00, 0.564366E+00}, {-.590323E+00, 0.563670E+00}, + {-.590990E+00, 0.562988E+00}, {-.591643E+00, 0.562319E+00}, + {-.592280E+00, 0.561664E+00}, {-.592902E+00, 0.561025E+00}, + {-.593508E+00, 0.560402E+00}, {-.594097E+00, 0.559796E+00}, + {-.594669E+00, 0.559207E+00}, {-.595223E+00, 0.558635E+00}, + {-.595758E+00, 0.558082E+00}, {-.596275E+00, 0.557548E+00}, + {-.596772E+00, 0.557034E+00}, {-.597249E+00, 0.556540E+00}, + {-.597706E+00, 0.556066E+00}, {-.598142E+00, 0.555614E+00}, + {-.598557E+00, 0.555183E+00}, {-.598951E+00, 0.554775E+00}, + {-.599322E+00, 0.554388E+00}, {-.599671E+00, 0.554025E+00}, + {-.599998E+00, 0.553685E+00}, {-.600302E+00, 0.553369E+00}, + {-.600582E+00, 0.553077E+00}, {-.600839E+00, 0.552809E+00}, + {-.601072E+00, 0.552566E+00}, {-.601281E+00, 0.552348E+00}, + {-.601466E+00, 0.552155E+00}, {-.601627E+00, 0.551987E+00}, + {-.601763E+00, 0.551845E+00}, {-.601875E+00, 0.551728E+00}, + {-.601962E+00, 0.551637E+00}, {-.602024E+00, 0.551573E+00}, + {-.602061E+00, 0.551534E+00}, {-.602074E+00, 0.551521E+00}, + }, + { + {-.546209E+00, 0.606898E+00}, {-.546224E+00, 0.606883E+00}, + {-.546271E+00, 0.606839E+00}, {-.546350E+00, 0.606764E+00}, + {-.546459E+00, 0.606660E+00}, {-.546600E+00, 0.606527E+00}, + {-.546771E+00, 0.606364E+00}, {-.546973E+00, 0.606172E+00}, + {-.547206E+00, 0.605951E+00}, {-.547469E+00, 0.605701E+00}, + {-.547762E+00, 0.605422E+00}, {-.548085E+00, 0.605115E+00}, + {-.548437E+00, 0.604780E+00}, {-.548818E+00, 0.604417E+00}, + {-.549228E+00, 0.604026E+00}, {-.549665E+00, 0.603609E+00}, + {-.550131E+00, 0.603164E+00}, {-.550623E+00, 0.602694E+00}, + {-.551142E+00, 0.602198E+00}, {-.551687E+00, 0.601676E+00}, + {-.552258E+00, 0.601129E+00}, {-.552853E+00, 0.600558E+00}, + {-.553472E+00, 0.599963E+00}, {-.554116E+00, 0.599345E+00}, + {-.554781E+00, 0.598705E+00}, {-.555470E+00, 0.598042E+00}, + {-.556179E+00, 0.597358E+00}, {-.556909E+00, 0.596653E+00}, + {-.557659E+00, 0.595927E+00}, {-.558428E+00, 0.595183E+00}, + {-.559216E+00, 0.594419E+00}, {-.560021E+00, 0.593638E+00}, + {-.560842E+00, 0.592840E+00}, {-.561680E+00, 0.592025E+00}, + {-.562532E+00, 0.591194E+00}, {-.563398E+00, 0.590348E+00}, + {-.564277E+00, 0.589489E+00}, {-.565169E+00, 0.588616E+00}, + {-.566071E+00, 0.587731E+00}, {-.566984E+00, 0.586834E+00}, + {-.567907E+00, 0.585927E+00}, {-.568837E+00, 0.585010E+00}, + {-.569775E+00, 0.584084E+00}, {-.570720E+00, 0.583150E+00}, + {-.571670E+00, 0.582209E+00}, {-.572625E+00, 0.581262E+00}, + {-.573583E+00, 0.580310E+00}, {-.574544E+00, 0.579354E+00}, + {-.575507E+00, 0.578395E+00}, {-.576470E+00, 0.577433E+00}, + {-.577433E+00, 0.576470E+00}, {-.578395E+00, 0.575507E+00}, + {-.579354E+00, 0.574544E+00}, {-.580310E+00, 0.573583E+00}, + {-.581262E+00, 0.572625E+00}, {-.582209E+00, 0.571670E+00}, + {-.583150E+00, 0.570720E+00}, {-.584084E+00, 0.569775E+00}, + {-.585010E+00, 0.568837E+00}, {-.585927E+00, 0.567907E+00}, + {-.586834E+00, 0.566984E+00}, {-.587731E+00, 0.566071E+00}, + {-.588616E+00, 0.565169E+00}, {-.589489E+00, 0.564277E+00}, + {-.590348E+00, 0.563398E+00}, {-.591194E+00, 0.562532E+00}, + {-.592025E+00, 0.561680E+00}, {-.592840E+00, 0.560842E+00}, + {-.593638E+00, 0.560021E+00}, {-.594419E+00, 0.559216E+00}, + {-.595183E+00, 0.558428E+00}, {-.595927E+00, 0.557659E+00}, + {-.596653E+00, 0.556909E+00}, {-.597358E+00, 0.556179E+00}, + {-.598042E+00, 0.555470E+00}, {-.598705E+00, 0.554781E+00}, + {-.599345E+00, 0.554116E+00}, {-.599963E+00, 0.553472E+00}, + {-.600558E+00, 0.552853E+00}, {-.601129E+00, 0.552258E+00}, + {-.601676E+00, 0.551687E+00}, {-.602198E+00, 0.551142E+00}, + {-.602694E+00, 0.550623E+00}, {-.603164E+00, 0.550131E+00}, + {-.603609E+00, 0.549665E+00}, {-.604026E+00, 0.549228E+00}, + {-.604417E+00, 0.548818E+00}, {-.604780E+00, 0.548437E+00}, + {-.605115E+00, 0.548085E+00}, {-.605422E+00, 0.547762E+00}, + {-.605701E+00, 0.547469E+00}, {-.605951E+00, 0.547206E+00}, + {-.606172E+00, 0.546973E+00}, {-.606364E+00, 0.546771E+00}, + {-.606527E+00, 0.546600E+00}, {-.606660E+00, 0.546459E+00}, + {-.606764E+00, 0.546350E+00}, {-.606839E+00, 0.546271E+00}, + {-.606883E+00, 0.546224E+00}, {-.606898E+00, 0.546209E+00}, + }, + { + {-.540845E+00, 0.611685E+00}, {-.540863E+00, 0.611668E+00}, + {-.540918E+00, 0.611616E+00}, {-.541010E+00, 0.611529E+00}, + {-.541138E+00, 0.611409E+00}, {-.541303E+00, 0.611253E+00}, + {-.541504E+00, 0.611064E+00}, {-.541741E+00, 0.610841E+00}, + {-.542014E+00, 0.610584E+00}, {-.542323E+00, 0.610293E+00}, + {-.542666E+00, 0.609969E+00}, {-.543044E+00, 0.609612E+00}, + {-.543457E+00, 0.609223E+00}, {-.543903E+00, 0.608801E+00}, + {-.544383E+00, 0.608347E+00}, {-.544896E+00, 0.607861E+00}, + {-.545441E+00, 0.607345E+00}, {-.546018E+00, 0.606798E+00}, + {-.546626E+00, 0.606221E+00}, {-.547265E+00, 0.605614E+00}, + {-.547933E+00, 0.604978E+00}, {-.548630E+00, 0.604314E+00}, + {-.549356E+00, 0.603622E+00}, {-.550109E+00, 0.602903E+00}, + {-.550889E+00, 0.602158E+00}, {-.551694E+00, 0.601387E+00}, + {-.552525E+00, 0.600590E+00}, {-.553380E+00, 0.599770E+00}, + {-.554257E+00, 0.598926E+00}, {-.555158E+00, 0.598059E+00}, + {-.556079E+00, 0.597170E+00}, {-.557021E+00, 0.596261E+00}, + {-.557982E+00, 0.595331E+00}, {-.558962E+00, 0.594382E+00}, + {-.559958E+00, 0.593414E+00}, {-.560972E+00, 0.592429E+00}, + {-.562000E+00, 0.591428E+00}, {-.563042E+00, 0.590411E+00}, + {-.564097E+00, 0.589379E+00}, {-.565165E+00, 0.588334E+00}, + {-.566243E+00, 0.587276E+00}, {-.567330E+00, 0.586207E+00}, + {-.568427E+00, 0.585128E+00}, {-.569530E+00, 0.584039E+00}, + {-.570640E+00, 0.582942E+00}, {-.571756E+00, 0.581837E+00}, + {-.572875E+00, 0.580727E+00}, {-.573997E+00, 0.579611E+00}, + {-.575121E+00, 0.578491E+00}, {-.576245E+00, 0.577369E+00}, + {-.577369E+00, 0.576245E+00}, {-.578491E+00, 0.575121E+00}, + {-.579611E+00, 0.573997E+00}, {-.580727E+00, 0.572875E+00}, + {-.581837E+00, 0.571756E+00}, {-.582942E+00, 0.570640E+00}, + {-.584039E+00, 0.569530E+00}, {-.585128E+00, 0.568427E+00}, + {-.586207E+00, 0.567330E+00}, {-.587276E+00, 0.566243E+00}, + {-.588334E+00, 0.565165E+00}, {-.589379E+00, 0.564097E+00}, + {-.590411E+00, 0.563042E+00}, {-.591428E+00, 0.562000E+00}, + {-.592429E+00, 0.560972E+00}, {-.593414E+00, 0.559958E+00}, + {-.594382E+00, 0.558962E+00}, {-.595331E+00, 0.557982E+00}, + {-.596261E+00, 0.557021E+00}, {-.597170E+00, 0.556079E+00}, + {-.598059E+00, 0.555158E+00}, {-.598926E+00, 0.554257E+00}, + {-.599770E+00, 0.553380E+00}, {-.600590E+00, 0.552525E+00}, + {-.601387E+00, 0.551694E+00}, {-.602158E+00, 0.550889E+00}, + {-.602903E+00, 0.550109E+00}, {-.603622E+00, 0.549356E+00}, + {-.604314E+00, 0.548630E+00}, {-.604978E+00, 0.547933E+00}, + {-.605614E+00, 0.547265E+00}, {-.606221E+00, 0.546626E+00}, + {-.606798E+00, 0.546018E+00}, {-.607345E+00, 0.545441E+00}, + {-.607861E+00, 0.544896E+00}, {-.608347E+00, 0.544383E+00}, + {-.608801E+00, 0.543903E+00}, {-.609223E+00, 0.543457E+00}, + {-.609612E+00, 0.543044E+00}, {-.609969E+00, 0.542666E+00}, + {-.610293E+00, 0.542323E+00}, {-.610584E+00, 0.542014E+00}, + {-.610841E+00, 0.541741E+00}, {-.611064E+00, 0.541504E+00}, + {-.611253E+00, 0.541303E+00}, {-.611409E+00, 0.541138E+00}, + {-.611529E+00, 0.541010E+00}, {-.611616E+00, 0.540918E+00}, + {-.611668E+00, 0.540863E+00}, {-.611685E+00, 0.540845E+00}, + }, + { + {-.535428E+00, 0.616435E+00}, {-.535449E+00, 0.616415E+00}, + {-.535512E+00, 0.616356E+00}, {-.535618E+00, 0.616258E+00}, + {-.535765E+00, 0.616120E+00}, {-.535954E+00, 0.615944E+00}, + {-.536185E+00, 0.615728E+00}, {-.536458E+00, 0.615474E+00}, + {-.536771E+00, 0.615181E+00}, {-.537125E+00, 0.614850E+00}, + {-.537519E+00, 0.614481E+00}, {-.537954E+00, 0.614075E+00}, + {-.538427E+00, 0.613631E+00}, {-.538940E+00, 0.613151E+00}, + {-.539491E+00, 0.612634E+00}, {-.540079E+00, 0.612081E+00}, + {-.540705E+00, 0.611493E+00}, {-.541367E+00, 0.610870E+00}, + {-.542065E+00, 0.610212E+00}, {-.542798E+00, 0.609521E+00}, + {-.543565E+00, 0.608797E+00}, {-.544365E+00, 0.608040E+00}, + {-.545197E+00, 0.607252E+00}, {-.546061E+00, 0.606432E+00}, + {-.546955E+00, 0.605583E+00}, {-.547879E+00, 0.604704E+00}, + {-.548832E+00, 0.603796E+00}, {-.549812E+00, 0.602860E+00}, + {-.550819E+00, 0.601898E+00}, {-.551851E+00, 0.600910E+00}, + {-.552908E+00, 0.599896E+00}, {-.553987E+00, 0.598858E+00}, + {-.555089E+00, 0.597798E+00}, {-.556211E+00, 0.596715E+00}, + {-.557354E+00, 0.595611E+00}, {-.558514E+00, 0.594487E+00}, + {-.559692E+00, 0.593344E+00}, {-.560886E+00, 0.592183E+00}, + {-.562095E+00, 0.591005E+00}, {-.563317E+00, 0.589812E+00}, + {-.564552E+00, 0.588604E+00}, {-.565797E+00, 0.587383E+00}, + {-.567052E+00, 0.586150E+00}, {-.568315E+00, 0.584906E+00}, + {-.569586E+00, 0.583652E+00}, {-.570862E+00, 0.582390E+00}, + {-.572142E+00, 0.581121E+00}, {-.573426E+00, 0.579846E+00}, + {-.574712E+00, 0.578566E+00}, {-.575998E+00, 0.577283E+00}, + {-.577283E+00, 0.575998E+00}, {-.578566E+00, 0.574712E+00}, + {-.579846E+00, 0.573426E+00}, {-.581121E+00, 0.572142E+00}, + {-.582390E+00, 0.570862E+00}, {-.583652E+00, 0.569586E+00}, + {-.584906E+00, 0.568315E+00}, {-.586150E+00, 0.567052E+00}, + {-.587383E+00, 0.565797E+00}, {-.588604E+00, 0.564552E+00}, + {-.589812E+00, 0.563317E+00}, {-.591005E+00, 0.562095E+00}, + {-.592183E+00, 0.560886E+00}, {-.593344E+00, 0.559692E+00}, + {-.594487E+00, 0.558514E+00}, {-.595611E+00, 0.557354E+00}, + {-.596715E+00, 0.556211E+00}, {-.597798E+00, 0.555089E+00}, + {-.598858E+00, 0.553987E+00}, {-.599896E+00, 0.552908E+00}, + {-.600910E+00, 0.551851E+00}, {-.601898E+00, 0.550819E+00}, + {-.602860E+00, 0.549812E+00}, {-.603796E+00, 0.548832E+00}, + {-.604704E+00, 0.547879E+00}, {-.605583E+00, 0.546955E+00}, + {-.606432E+00, 0.546061E+00}, {-.607252E+00, 0.545197E+00}, + {-.608040E+00, 0.544365E+00}, {-.608797E+00, 0.543565E+00}, + {-.609521E+00, 0.542798E+00}, {-.610212E+00, 0.542065E+00}, + {-.610870E+00, 0.541367E+00}, {-.611493E+00, 0.540705E+00}, + {-.612081E+00, 0.540079E+00}, {-.612634E+00, 0.539491E+00}, + {-.613151E+00, 0.538940E+00}, {-.613631E+00, 0.538427E+00}, + {-.614075E+00, 0.537954E+00}, {-.614481E+00, 0.537519E+00}, + {-.614850E+00, 0.537125E+00}, {-.615181E+00, 0.536771E+00}, + {-.615474E+00, 0.536458E+00}, {-.615728E+00, 0.536185E+00}, + {-.615944E+00, 0.535954E+00}, {-.616120E+00, 0.535765E+00}, + {-.616258E+00, 0.535618E+00}, {-.616356E+00, 0.535512E+00}, + {-.616415E+00, 0.535449E+00}, {-.616435E+00, 0.535428E+00}, + }, + { + {-.529957E+00, 0.621149E+00}, {-.529980E+00, 0.621127E+00}, + {-.530052E+00, 0.621060E+00}, {-.530171E+00, 0.620950E+00}, + {-.530338E+00, 0.620796E+00}, {-.530552E+00, 0.620598E+00}, + {-.530813E+00, 0.620357E+00}, {-.531120E+00, 0.620072E+00}, + {-.531475E+00, 0.619744E+00}, {-.531875E+00, 0.619373E+00}, + {-.532321E+00, 0.618960E+00}, {-.532811E+00, 0.618504E+00}, + {-.533347E+00, 0.618007E+00}, {-.533926E+00, 0.617468E+00}, + {-.534549E+00, 0.616889E+00}, {-.535214E+00, 0.616269E+00}, + {-.535921E+00, 0.615609E+00}, {-.536669E+00, 0.614910E+00}, + {-.537457E+00, 0.614173E+00}, {-.538285E+00, 0.613398E+00}, + {-.539151E+00, 0.612586E+00}, {-.540055E+00, 0.611737E+00}, + {-.540995E+00, 0.610852E+00}, {-.541971E+00, 0.609933E+00}, + {-.542981E+00, 0.608980E+00}, {-.544024E+00, 0.607993E+00}, + {-.545100E+00, 0.606975E+00}, {-.546207E+00, 0.605925E+00}, + {-.547343E+00, 0.604845E+00}, {-.548508E+00, 0.603735E+00}, + {-.549700E+00, 0.602597E+00}, {-.550919E+00, 0.601432E+00}, + {-.552162E+00, 0.600241E+00}, {-.553428E+00, 0.599024E+00}, + {-.554717E+00, 0.597784E+00}, {-.556026E+00, 0.596521E+00}, + {-.557355E+00, 0.595237E+00}, {-.558701E+00, 0.593933E+00}, + {-.560064E+00, 0.592609E+00}, {-.561442E+00, 0.591268E+00}, + {-.562833E+00, 0.589910E+00}, {-.564237E+00, 0.588537E+00}, + {-.565651E+00, 0.587151E+00}, {-.567075E+00, 0.585751E+00}, + {-.568506E+00, 0.584341E+00}, {-.569944E+00, 0.582922E+00}, + {-.571386E+00, 0.581493E+00}, {-.572832E+00, 0.580059E+00}, + {-.574279E+00, 0.578618E+00}, {-.575727E+00, 0.577174E+00}, + {-.577174E+00, 0.575727E+00}, {-.578618E+00, 0.574279E+00}, + {-.580059E+00, 0.572832E+00}, {-.581493E+00, 0.571386E+00}, + {-.582922E+00, 0.569944E+00}, {-.584341E+00, 0.568506E+00}, + {-.585751E+00, 0.567075E+00}, {-.587151E+00, 0.565651E+00}, + {-.588537E+00, 0.564237E+00}, {-.589910E+00, 0.562833E+00}, + {-.591268E+00, 0.561442E+00}, {-.592609E+00, 0.560064E+00}, + {-.593933E+00, 0.558701E+00}, {-.595237E+00, 0.557355E+00}, + {-.596521E+00, 0.556026E+00}, {-.597784E+00, 0.554717E+00}, + {-.599024E+00, 0.553428E+00}, {-.600241E+00, 0.552162E+00}, + {-.601432E+00, 0.550919E+00}, {-.602597E+00, 0.549700E+00}, + {-.603735E+00, 0.548508E+00}, {-.604845E+00, 0.547343E+00}, + {-.605925E+00, 0.546207E+00}, {-.606975E+00, 0.545100E+00}, + {-.607993E+00, 0.544024E+00}, {-.608980E+00, 0.542981E+00}, + {-.609933E+00, 0.541971E+00}, {-.610852E+00, 0.540995E+00}, + {-.611737E+00, 0.540055E+00}, {-.612586E+00, 0.539151E+00}, + {-.613398E+00, 0.538285E+00}, {-.614173E+00, 0.537457E+00}, + {-.614910E+00, 0.536669E+00}, {-.615609E+00, 0.535921E+00}, + {-.616269E+00, 0.535214E+00}, {-.616889E+00, 0.534549E+00}, + {-.617468E+00, 0.533926E+00}, {-.618007E+00, 0.533347E+00}, + {-.618504E+00, 0.532811E+00}, {-.618960E+00, 0.532321E+00}, + {-.619373E+00, 0.531875E+00}, {-.619744E+00, 0.531475E+00}, + {-.620072E+00, 0.531120E+00}, {-.620357E+00, 0.530813E+00}, + {-.620598E+00, 0.530552E+00}, {-.620796E+00, 0.530338E+00}, + {-.620950E+00, 0.530171E+00}, {-.621060E+00, 0.530052E+00}, + {-.621127E+00, 0.529980E+00}, {-.621149E+00, 0.529957E+00}, + }, + { + {-.524429E+00, 0.625828E+00}, {-.524456E+00, 0.625803E+00}, + {-.524536E+00, 0.625730E+00}, {-.524669E+00, 0.625608E+00}, + {-.524855E+00, 0.625438E+00}, {-.525094E+00, 0.625219E+00}, + {-.525385E+00, 0.624951E+00}, {-.525729E+00, 0.624636E+00}, + {-.526124E+00, 0.624273E+00}, {-.526571E+00, 0.623862E+00}, + {-.527069E+00, 0.623405E+00}, {-.527617E+00, 0.622900E+00}, + {-.528214E+00, 0.622350E+00}, {-.528861E+00, 0.621753E+00}, + {-.529556E+00, 0.621111E+00}, {-.530298E+00, 0.620425E+00}, + {-.531087E+00, 0.619695E+00}, {-.531922E+00, 0.618921E+00}, + {-.532802E+00, 0.618104E+00}, {-.533726E+00, 0.617246E+00}, + {-.534692E+00, 0.616346E+00}, {-.535700E+00, 0.615405E+00}, + {-.536749E+00, 0.614425E+00}, {-.537838E+00, 0.613407E+00}, + {-.538964E+00, 0.612350E+00}, {-.540128E+00, 0.611257E+00}, + {-.541328E+00, 0.610128E+00}, {-.542562E+00, 0.608964E+00}, + {-.543829E+00, 0.607766E+00}, {-.545127E+00, 0.606536E+00}, + {-.546457E+00, 0.605274E+00}, {-.547815E+00, 0.603981E+00}, + {-.549200E+00, 0.602660E+00}, {-.550611E+00, 0.601311E+00}, + {-.552047E+00, 0.599935E+00}, {-.553506E+00, 0.598534E+00}, + {-.554986E+00, 0.597108E+00}, {-.556485E+00, 0.595660E+00}, + {-.558003E+00, 0.594191E+00}, {-.559538E+00, 0.592702E+00}, + {-.561087E+00, 0.591194E+00}, {-.562650E+00, 0.589670E+00}, + {-.564224E+00, 0.588130E+00}, {-.565808E+00, 0.586575E+00}, + {-.567401E+00, 0.585009E+00}, {-.569001E+00, 0.583431E+00}, + {-.570605E+00, 0.581844E+00}, {-.572213E+00, 0.580249E+00}, + {-.573823E+00, 0.578648E+00}, {-.575434E+00, 0.577042E+00}, + {-.577042E+00, 0.575434E+00}, {-.578648E+00, 0.573823E+00}, + {-.580249E+00, 0.572213E+00}, {-.581844E+00, 0.570605E+00}, + {-.583431E+00, 0.569001E+00}, {-.585009E+00, 0.567401E+00}, + {-.586575E+00, 0.565808E+00}, {-.588130E+00, 0.564224E+00}, + {-.589670E+00, 0.562650E+00}, {-.591194E+00, 0.561087E+00}, + {-.592702E+00, 0.559538E+00}, {-.594191E+00, 0.558003E+00}, + {-.595660E+00, 0.556485E+00}, {-.597108E+00, 0.554986E+00}, + {-.598534E+00, 0.553506E+00}, {-.599935E+00, 0.552047E+00}, + {-.601311E+00, 0.550611E+00}, {-.602660E+00, 0.549200E+00}, + {-.603981E+00, 0.547815E+00}, {-.605274E+00, 0.546457E+00}, + {-.606536E+00, 0.545127E+00}, {-.607766E+00, 0.543829E+00}, + {-.608964E+00, 0.542562E+00}, {-.610128E+00, 0.541328E+00}, + {-.611257E+00, 0.540128E+00}, {-.612350E+00, 0.538964E+00}, + {-.613407E+00, 0.537838E+00}, {-.614425E+00, 0.536749E+00}, + {-.615405E+00, 0.535700E+00}, {-.616346E+00, 0.534692E+00}, + {-.617246E+00, 0.533726E+00}, {-.618104E+00, 0.532802E+00}, + {-.618921E+00, 0.531922E+00}, {-.619695E+00, 0.531087E+00}, + {-.620425E+00, 0.530298E+00}, {-.621111E+00, 0.529556E+00}, + {-.621753E+00, 0.528861E+00}, {-.622350E+00, 0.528214E+00}, + {-.622900E+00, 0.527617E+00}, {-.623405E+00, 0.527069E+00}, + {-.623862E+00, 0.526571E+00}, {-.624273E+00, 0.526124E+00}, + {-.624636E+00, 0.525729E+00}, {-.624951E+00, 0.525385E+00}, + {-.625219E+00, 0.525094E+00}, {-.625438E+00, 0.524855E+00}, + {-.625608E+00, 0.524669E+00}, {-.625730E+00, 0.524536E+00}, + {-.625803E+00, 0.524456E+00}, {-.625828E+00, 0.524429E+00}, + }, + { + {-.518844E+00, 0.630473E+00}, {-.518874E+00, 0.630446E+00}, + {-.518962E+00, 0.630366E+00}, {-.519109E+00, 0.630233E+00}, + {-.519315E+00, 0.630046E+00}, {-.519579E+00, 0.629806E+00}, + {-.519901E+00, 0.629513E+00}, {-.520281E+00, 0.629167E+00}, + {-.520718E+00, 0.628769E+00}, {-.521212E+00, 0.628319E+00}, + {-.521762E+00, 0.627817E+00}, {-.522368E+00, 0.627265E+00}, + {-.523028E+00, 0.626661E+00}, {-.523743E+00, 0.626007E+00}, + {-.524511E+00, 0.625304E+00}, {-.525331E+00, 0.624551E+00}, + {-.526203E+00, 0.623750E+00}, {-.527126E+00, 0.622902E+00}, + {-.528098E+00, 0.622006E+00}, {-.529118E+00, 0.621064E+00}, + {-.530186E+00, 0.620077E+00}, {-.531299E+00, 0.619046E+00}, + {-.532458E+00, 0.617971E+00}, {-.533660E+00, 0.616853E+00}, + {-.534904E+00, 0.615694E+00}, {-.536189E+00, 0.614494E+00}, + {-.537514E+00, 0.613255E+00}, {-.538876E+00, 0.611978E+00}, + {-.540275E+00, 0.610663E+00}, {-.541708E+00, 0.609312E+00}, + {-.543175E+00, 0.607927E+00}, {-.544674E+00, 0.606508E+00}, + {-.546203E+00, 0.605056E+00}, {-.547760E+00, 0.603574E+00}, + {-.549344E+00, 0.602063E+00}, {-.550953E+00, 0.600523E+00}, + {-.552585E+00, 0.598957E+00}, {-.554239E+00, 0.597366E+00}, + {-.555912E+00, 0.595751E+00}, {-.557604E+00, 0.594114E+00}, + {-.559312E+00, 0.592457E+00}, {-.561034E+00, 0.590781E+00}, + {-.562769E+00, 0.589087E+00}, {-.564515E+00, 0.587378E+00}, + {-.566270E+00, 0.585654E+00}, {-.568032E+00, 0.583919E+00}, + {-.569800E+00, 0.582173E+00}, {-.571571E+00, 0.580418E+00}, + {-.573344E+00, 0.578656E+00}, {-.575117E+00, 0.576888E+00}, + {-.576888E+00, 0.575117E+00}, {-.578656E+00, 0.573344E+00}, + {-.580418E+00, 0.571571E+00}, {-.582173E+00, 0.569800E+00}, + {-.583919E+00, 0.568032E+00}, {-.585654E+00, 0.566270E+00}, + {-.587378E+00, 0.564515E+00}, {-.589087E+00, 0.562769E+00}, + {-.590781E+00, 0.561034E+00}, {-.592457E+00, 0.559312E+00}, + {-.594114E+00, 0.557604E+00}, {-.595751E+00, 0.555912E+00}, + {-.597366E+00, 0.554239E+00}, {-.598957E+00, 0.552585E+00}, + {-.600523E+00, 0.550953E+00}, {-.602063E+00, 0.549344E+00}, + {-.603574E+00, 0.547760E+00}, {-.605056E+00, 0.546203E+00}, + {-.606508E+00, 0.544674E+00}, {-.607927E+00, 0.543175E+00}, + {-.609312E+00, 0.541708E+00}, {-.610663E+00, 0.540275E+00}, + {-.611978E+00, 0.538876E+00}, {-.613255E+00, 0.537514E+00}, + {-.614494E+00, 0.536189E+00}, {-.615694E+00, 0.534904E+00}, + {-.616853E+00, 0.533660E+00}, {-.617971E+00, 0.532458E+00}, + {-.619046E+00, 0.531299E+00}, {-.620077E+00, 0.530186E+00}, + {-.621064E+00, 0.529118E+00}, {-.622006E+00, 0.528098E+00}, + {-.622902E+00, 0.527126E+00}, {-.623750E+00, 0.526203E+00}, + {-.624551E+00, 0.525331E+00}, {-.625304E+00, 0.524511E+00}, + {-.626007E+00, 0.523743E+00}, {-.626661E+00, 0.523028E+00}, + {-.627265E+00, 0.522368E+00}, {-.627817E+00, 0.521762E+00}, + {-.628319E+00, 0.521212E+00}, {-.628769E+00, 0.520718E+00}, + {-.629167E+00, 0.520281E+00}, {-.629513E+00, 0.519901E+00}, + {-.629806E+00, 0.519579E+00}, {-.630046E+00, 0.519315E+00}, + {-.630233E+00, 0.519109E+00}, {-.630366E+00, 0.518962E+00}, + {-.630446E+00, 0.518874E+00}, {-.630473E+00, 0.518844E+00}, + }, + { + {-.513200E+00, 0.635086E+00}, {-.513232E+00, 0.635056E+00}, + {-.513329E+00, 0.634969E+00}, {-.513490E+00, 0.634824E+00}, + {-.513716E+00, 0.634621E+00}, {-.514006E+00, 0.634360E+00}, + {-.514359E+00, 0.634042E+00}, {-.514775E+00, 0.633666E+00}, + {-.515254E+00, 0.633233E+00}, {-.515796E+00, 0.632744E+00}, + {-.516399E+00, 0.632199E+00}, {-.517063E+00, 0.631598E+00}, + {-.517787E+00, 0.630942E+00}, {-.518570E+00, 0.630231E+00}, + {-.519412E+00, 0.629466E+00}, {-.520311E+00, 0.628647E+00}, + {-.521267E+00, 0.627777E+00}, {-.522278E+00, 0.626854E+00}, + {-.523343E+00, 0.625880E+00}, {-.524461E+00, 0.624855E+00}, + {-.525631E+00, 0.623782E+00}, {-.526851E+00, 0.622659E+00}, + {-.528120E+00, 0.621490E+00}, {-.529437E+00, 0.620274E+00}, + {-.530800E+00, 0.619012E+00}, {-.532207E+00, 0.617707E+00}, + {-.533657E+00, 0.616358E+00}, {-.535149E+00, 0.614967E+00}, + {-.536680E+00, 0.613536E+00}, {-.538250E+00, 0.612065E+00}, + {-.539856E+00, 0.610556E+00}, {-.541496E+00, 0.609011E+00}, + {-.543169E+00, 0.607430E+00}, {-.544873E+00, 0.605816E+00}, + {-.546606E+00, 0.604169E+00}, {-.548366E+00, 0.602491E+00}, + {-.550152E+00, 0.600785E+00}, {-.551961E+00, 0.599050E+00}, + {-.553791E+00, 0.597290E+00}, {-.555641E+00, 0.595506E+00}, + {-.557508E+00, 0.593698E+00}, {-.559391E+00, 0.591870E+00}, + {-.561288E+00, 0.590023E+00}, {-.563196E+00, 0.588159E+00}, + {-.565113E+00, 0.586279E+00}, {-.567039E+00, 0.584385E+00}, + {-.568970E+00, 0.582479E+00}, {-.570905E+00, 0.580564E+00}, + {-.572841E+00, 0.578640E+00}, {-.574777E+00, 0.576711E+00}, + {-.576711E+00, 0.574777E+00}, {-.578640E+00, 0.572841E+00}, + {-.580564E+00, 0.570905E+00}, {-.582479E+00, 0.568970E+00}, + {-.584385E+00, 0.567039E+00}, {-.586279E+00, 0.565113E+00}, + {-.588159E+00, 0.563196E+00}, {-.590023E+00, 0.561288E+00}, + {-.591870E+00, 0.559391E+00}, {-.593698E+00, 0.557508E+00}, + {-.595506E+00, 0.555641E+00}, {-.597290E+00, 0.553791E+00}, + {-.599050E+00, 0.551961E+00}, {-.600785E+00, 0.550152E+00}, + {-.602491E+00, 0.548366E+00}, {-.604169E+00, 0.546606E+00}, + {-.605816E+00, 0.544873E+00}, {-.607430E+00, 0.543169E+00}, + {-.609011E+00, 0.541496E+00}, {-.610556E+00, 0.539856E+00}, + {-.612065E+00, 0.538250E+00}, {-.613536E+00, 0.536680E+00}, + {-.614967E+00, 0.535149E+00}, {-.616358E+00, 0.533657E+00}, + {-.617707E+00, 0.532207E+00}, {-.619012E+00, 0.530800E+00}, + {-.620274E+00, 0.529437E+00}, {-.621490E+00, 0.528120E+00}, + {-.622659E+00, 0.526851E+00}, {-.623782E+00, 0.525631E+00}, + {-.624855E+00, 0.524461E+00}, {-.625880E+00, 0.523343E+00}, + {-.626854E+00, 0.522278E+00}, {-.627777E+00, 0.521267E+00}, + {-.628647E+00, 0.520311E+00}, {-.629466E+00, 0.519412E+00}, + {-.630231E+00, 0.518570E+00}, {-.630942E+00, 0.517787E+00}, + {-.631598E+00, 0.517063E+00}, {-.632199E+00, 0.516399E+00}, + {-.632744E+00, 0.515796E+00}, {-.633233E+00, 0.515254E+00}, + {-.633666E+00, 0.514775E+00}, {-.634042E+00, 0.514359E+00}, + {-.634360E+00, 0.514006E+00}, {-.634621E+00, 0.513716E+00}, + {-.634824E+00, 0.513490E+00}, {-.634969E+00, 0.513329E+00}, + {-.635056E+00, 0.513232E+00}, {-.635086E+00, 0.513200E+00}, + }, + { + {-.507495E+00, 0.639666E+00}, {-.507530E+00, 0.639634E+00}, + {-.507636E+00, 0.639540E+00}, {-.507811E+00, 0.639383E+00}, + {-.508057E+00, 0.639164E+00}, {-.508372E+00, 0.638883E+00}, + {-.508757E+00, 0.638539E+00}, {-.509210E+00, 0.638133E+00}, + {-.509732E+00, 0.637666E+00}, {-.510322E+00, 0.637138E+00}, + {-.510978E+00, 0.636550E+00}, {-.511701E+00, 0.635901E+00}, + {-.512489E+00, 0.635192E+00}, {-.513342E+00, 0.634425E+00}, + {-.514258E+00, 0.633599E+00}, {-.515237E+00, 0.632715E+00}, + {-.516277E+00, 0.631774E+00}, {-.517377E+00, 0.630778E+00}, + {-.518537E+00, 0.629726E+00}, {-.519753E+00, 0.628619E+00}, + {-.521026E+00, 0.627459E+00}, {-.522354E+00, 0.626247E+00}, + {-.523734E+00, 0.624983E+00}, {-.525167E+00, 0.623669E+00}, + {-.526649E+00, 0.622305E+00}, {-.528180E+00, 0.620894E+00}, + {-.529757E+00, 0.619436E+00}, {-.531379E+00, 0.617933E+00}, + {-.533045E+00, 0.616385E+00}, {-.534751E+00, 0.614795E+00}, + {-.536497E+00, 0.613163E+00}, {-.538280E+00, 0.611491E+00}, + {-.540098E+00, 0.609782E+00}, {-.541950E+00, 0.608035E+00}, + {-.543833E+00, 0.606253E+00}, {-.545745E+00, 0.604438E+00}, + {-.547685E+00, 0.602591E+00}, {-.549650E+00, 0.600713E+00}, + {-.551638E+00, 0.598808E+00}, {-.553647E+00, 0.596876E+00}, + {-.555675E+00, 0.594919E+00}, {-.557719E+00, 0.592939E+00}, + {-.559778E+00, 0.590938E+00}, {-.561849E+00, 0.588918E+00}, + {-.563930E+00, 0.586881E+00}, {-.566019E+00, 0.584829E+00}, + {-.568114E+00, 0.582764E+00}, {-.570213E+00, 0.580688E+00}, + {-.572314E+00, 0.578602E+00}, {-.574413E+00, 0.576510E+00}, + {-.576510E+00, 0.574413E+00}, {-.578602E+00, 0.572314E+00}, + {-.580688E+00, 0.570213E+00}, {-.582764E+00, 0.568114E+00}, + {-.584829E+00, 0.566019E+00}, {-.586881E+00, 0.563930E+00}, + {-.588918E+00, 0.561849E+00}, {-.590938E+00, 0.559778E+00}, + {-.592939E+00, 0.557719E+00}, {-.594919E+00, 0.555675E+00}, + {-.596876E+00, 0.553647E+00}, {-.598808E+00, 0.551638E+00}, + {-.600713E+00, 0.549650E+00}, {-.602591E+00, 0.547685E+00}, + {-.604438E+00, 0.545745E+00}, {-.606253E+00, 0.543833E+00}, + {-.608035E+00, 0.541950E+00}, {-.609782E+00, 0.540098E+00}, + {-.611491E+00, 0.538280E+00}, {-.613163E+00, 0.536497E+00}, + {-.614795E+00, 0.534751E+00}, {-.616385E+00, 0.533045E+00}, + {-.617933E+00, 0.531379E+00}, {-.619436E+00, 0.529757E+00}, + {-.620894E+00, 0.528180E+00}, {-.622305E+00, 0.526649E+00}, + {-.623669E+00, 0.525167E+00}, {-.624983E+00, 0.523734E+00}, + {-.626247E+00, 0.522354E+00}, {-.627459E+00, 0.521026E+00}, + {-.628619E+00, 0.519753E+00}, {-.629726E+00, 0.518537E+00}, + {-.630778E+00, 0.517377E+00}, {-.631774E+00, 0.516277E+00}, + {-.632715E+00, 0.515237E+00}, {-.633599E+00, 0.514258E+00}, + {-.634425E+00, 0.513342E+00}, {-.635192E+00, 0.512489E+00}, + {-.635901E+00, 0.511701E+00}, {-.636550E+00, 0.510978E+00}, + {-.637138E+00, 0.510322E+00}, {-.637666E+00, 0.509732E+00}, + {-.638133E+00, 0.509210E+00}, {-.638539E+00, 0.508757E+00}, + {-.638883E+00, 0.508372E+00}, {-.639164E+00, 0.508057E+00}, + {-.639383E+00, 0.507811E+00}, {-.639540E+00, 0.507636E+00}, + {-.639634E+00, 0.507530E+00}, {-.639666E+00, 0.507495E+00}, + }, + { + {-.501728E+00, 0.644215E+00}, {-.501766E+00, 0.644181E+00}, + {-.501880E+00, 0.644080E+00}, {-.502070E+00, 0.643912E+00}, + {-.502336E+00, 0.643677E+00}, {-.502677E+00, 0.643374E+00}, + {-.503094E+00, 0.643006E+00}, {-.503585E+00, 0.642570E+00}, + {-.504149E+00, 0.642069E+00}, {-.504788E+00, 0.641503E+00}, + {-.505498E+00, 0.640871E+00}, {-.506280E+00, 0.640174E+00}, + {-.507134E+00, 0.639414E+00}, {-.508056E+00, 0.638590E+00}, + {-.509048E+00, 0.637703E+00}, {-.510107E+00, 0.636754E+00}, + {-.511233E+00, 0.635745E+00}, {-.512423E+00, 0.634674E+00}, + {-.513677E+00, 0.633545E+00}, {-.514993E+00, 0.632356E+00}, + {-.516370E+00, 0.631111E+00}, {-.517806E+00, 0.629808E+00}, + {-.519300E+00, 0.628451E+00}, {-.520849E+00, 0.627039E+00}, + {-.522452E+00, 0.625574E+00}, {-.524107E+00, 0.624058E+00}, + {-.525812E+00, 0.622491E+00}, {-.527566E+00, 0.620875E+00}, + {-.529366E+00, 0.619211E+00}, {-.531210E+00, 0.617502E+00}, + {-.533097E+00, 0.615747E+00}, {-.535024E+00, 0.613950E+00}, + {-.536989E+00, 0.612111E+00}, {-.538989E+00, 0.610233E+00}, + {-.541024E+00, 0.608316E+00}, {-.543090E+00, 0.606363E+00}, + {-.545185E+00, 0.604376E+00}, {-.547306E+00, 0.602355E+00}, + {-.549453E+00, 0.600304E+00}, {-.551622E+00, 0.598225E+00}, + {-.553811E+00, 0.596118E+00}, {-.556017E+00, 0.593986E+00}, + {-.558239E+00, 0.591832E+00}, {-.560474E+00, 0.589656E+00}, + {-.562720E+00, 0.587462E+00}, {-.564973E+00, 0.585251E+00}, + {-.567233E+00, 0.583026E+00}, {-.569497E+00, 0.580789E+00}, + {-.571762E+00, 0.578542E+00}, {-.574026E+00, 0.576286E+00}, + {-.576286E+00, 0.574026E+00}, {-.578542E+00, 0.571762E+00}, + {-.580789E+00, 0.569497E+00}, {-.583026E+00, 0.567233E+00}, + {-.585251E+00, 0.564973E+00}, {-.587462E+00, 0.562720E+00}, + {-.589656E+00, 0.560474E+00}, {-.591832E+00, 0.558239E+00}, + {-.593986E+00, 0.556017E+00}, {-.596118E+00, 0.553811E+00}, + {-.598225E+00, 0.551622E+00}, {-.600304E+00, 0.549453E+00}, + {-.602355E+00, 0.547306E+00}, {-.604376E+00, 0.545185E+00}, + {-.606363E+00, 0.543090E+00}, {-.608316E+00, 0.541024E+00}, + {-.610233E+00, 0.538989E+00}, {-.612111E+00, 0.536989E+00}, + {-.613950E+00, 0.535024E+00}, {-.615747E+00, 0.533097E+00}, + {-.617502E+00, 0.531210E+00}, {-.619211E+00, 0.529366E+00}, + {-.620875E+00, 0.527566E+00}, {-.622491E+00, 0.525812E+00}, + {-.624058E+00, 0.524107E+00}, {-.625574E+00, 0.522452E+00}, + {-.627039E+00, 0.520849E+00}, {-.628451E+00, 0.519300E+00}, + {-.629808E+00, 0.517806E+00}, {-.631111E+00, 0.516370E+00}, + {-.632356E+00, 0.514993E+00}, {-.633545E+00, 0.513677E+00}, + {-.634674E+00, 0.512423E+00}, {-.635745E+00, 0.511233E+00}, + {-.636754E+00, 0.510107E+00}, {-.637703E+00, 0.509048E+00}, + {-.638590E+00, 0.508056E+00}, {-.639414E+00, 0.507134E+00}, + {-.640174E+00, 0.506280E+00}, {-.640871E+00, 0.505498E+00}, + {-.641503E+00, 0.504788E+00}, {-.642069E+00, 0.504149E+00}, + {-.642570E+00, 0.503585E+00}, {-.643006E+00, 0.503094E+00}, + {-.643374E+00, 0.502677E+00}, {-.643677E+00, 0.502336E+00}, + {-.643912E+00, 0.502070E+00}, {-.644080E+00, 0.501880E+00}, + {-.644181E+00, 0.501766E+00}, {-.644215E+00, 0.501728E+00}, + }, + { + {-.495897E+00, 0.648733E+00}, {-.495938E+00, 0.648697E+00}, + {-.496061E+00, 0.648589E+00}, {-.496265E+00, 0.648410E+00}, + {-.496552E+00, 0.648159E+00}, {-.496919E+00, 0.647836E+00}, + {-.497368E+00, 0.647442E+00}, {-.497896E+00, 0.646978E+00}, + {-.498505E+00, 0.646443E+00}, {-.499192E+00, 0.645837E+00}, + {-.499957E+00, 0.645163E+00}, {-.500800E+00, 0.644419E+00}, + {-.501718E+00, 0.643607E+00}, {-.502712E+00, 0.642727E+00}, + {-.503780E+00, 0.641780E+00}, {-.504920E+00, 0.640767E+00}, + {-.506132E+00, 0.639688E+00}, {-.507413E+00, 0.638544E+00}, + {-.508763E+00, 0.637337E+00}, {-.510180E+00, 0.636068E+00}, + {-.511662E+00, 0.634737E+00}, {-.513207E+00, 0.633345E+00}, + {-.514814E+00, 0.631894E+00}, {-.516481E+00, 0.630385E+00}, + {-.518206E+00, 0.628819E+00}, {-.519987E+00, 0.627198E+00}, + {-.521821E+00, 0.625522E+00}, {-.523708E+00, 0.623794E+00}, + {-.525644E+00, 0.622015E+00}, {-.527627E+00, 0.620187E+00}, + {-.529656E+00, 0.618310E+00}, {-.531728E+00, 0.616387E+00}, + {-.533840E+00, 0.614420E+00}, {-.535991E+00, 0.612409E+00}, + {-.538178E+00, 0.610358E+00}, {-.540398E+00, 0.608267E+00}, + {-.542649E+00, 0.606140E+00}, {-.544929E+00, 0.603977E+00}, + {-.547235E+00, 0.601780E+00}, {-.549565E+00, 0.599553E+00}, + {-.551916E+00, 0.597296E+00}, {-.554286E+00, 0.595012E+00}, + {-.556671E+00, 0.592704E+00}, {-.559071E+00, 0.590373E+00}, + {-.561482E+00, 0.588021E+00}, {-.563901E+00, 0.585652E+00}, + {-.566326E+00, 0.583266E+00}, {-.568755E+00, 0.580867E+00}, + {-.571185E+00, 0.578457E+00}, {-.573614E+00, 0.576039E+00}, + {-.576039E+00, 0.573614E+00}, {-.578457E+00, 0.571185E+00}, + {-.580867E+00, 0.568755E+00}, {-.583266E+00, 0.566326E+00}, + {-.585652E+00, 0.563901E+00}, {-.588021E+00, 0.561482E+00}, + {-.590373E+00, 0.559071E+00}, {-.592704E+00, 0.556671E+00}, + {-.595012E+00, 0.554286E+00}, {-.597296E+00, 0.551916E+00}, + {-.599553E+00, 0.549565E+00}, {-.601780E+00, 0.547235E+00}, + {-.603977E+00, 0.544929E+00}, {-.606140E+00, 0.542649E+00}, + {-.608267E+00, 0.540398E+00}, {-.610358E+00, 0.538178E+00}, + {-.612409E+00, 0.535991E+00}, {-.614420E+00, 0.533840E+00}, + {-.616387E+00, 0.531728E+00}, {-.618310E+00, 0.529656E+00}, + {-.620187E+00, 0.527627E+00}, {-.622015E+00, 0.525644E+00}, + {-.623794E+00, 0.523708E+00}, {-.625522E+00, 0.521821E+00}, + {-.627198E+00, 0.519987E+00}, {-.628819E+00, 0.518206E+00}, + {-.630385E+00, 0.516481E+00}, {-.631894E+00, 0.514814E+00}, + {-.633345E+00, 0.513207E+00}, {-.634737E+00, 0.511662E+00}, + {-.636068E+00, 0.510180E+00}, {-.637337E+00, 0.508763E+00}, + {-.638544E+00, 0.507413E+00}, {-.639688E+00, 0.506132E+00}, + {-.640767E+00, 0.504920E+00}, {-.641780E+00, 0.503780E+00}, + {-.642727E+00, 0.502712E+00}, {-.643607E+00, 0.501718E+00}, + {-.644419E+00, 0.500800E+00}, {-.645163E+00, 0.499957E+00}, + {-.645837E+00, 0.499192E+00}, {-.646443E+00, 0.498505E+00}, + {-.646978E+00, 0.497896E+00}, {-.647442E+00, 0.497368E+00}, + {-.647836E+00, 0.496919E+00}, {-.648159E+00, 0.496552E+00}, + {-.648410E+00, 0.496265E+00}, {-.648589E+00, 0.496061E+00}, + {-.648697E+00, 0.495938E+00}, {-.648733E+00, 0.495897E+00}, + }, + { + {-.489999E+00, 0.653222E+00}, {-.490043E+00, 0.653184E+00}, + {-.490175E+00, 0.653069E+00}, {-.490395E+00, 0.652879E+00}, + {-.490702E+00, 0.652612E+00}, {-.491096E+00, 0.652269E+00}, + {-.491577E+00, 0.651850E+00}, {-.492144E+00, 0.651356E+00}, + {-.492796E+00, 0.650787E+00}, {-.493533E+00, 0.650144E+00}, + {-.494353E+00, 0.649427E+00}, {-.495257E+00, 0.648636E+00}, + {-.496242E+00, 0.647772E+00}, {-.497307E+00, 0.646837E+00}, + {-.498451E+00, 0.645830E+00}, {-.499674E+00, 0.644752E+00}, + {-.500973E+00, 0.643605E+00}, {-.502346E+00, 0.642389E+00}, + {-.503793E+00, 0.641105E+00}, {-.505311E+00, 0.639754E+00}, + {-.506899E+00, 0.638338E+00}, {-.508555E+00, 0.636857E+00}, + {-.510277E+00, 0.635313E+00}, {-.512063E+00, 0.633707E+00}, + {-.513911E+00, 0.632041E+00}, {-.515818E+00, 0.630315E+00}, + {-.517783E+00, 0.628531E+00}, {-.519803E+00, 0.626692E+00}, + {-.521877E+00, 0.624797E+00}, {-.524000E+00, 0.622850E+00}, + {-.526172E+00, 0.620851E+00}, {-.528390E+00, 0.618803E+00}, + {-.530651E+00, 0.616707E+00}, {-.532953E+00, 0.614565E+00}, + {-.535293E+00, 0.612379E+00}, {-.537669E+00, 0.610151E+00}, + {-.540078E+00, 0.607883E+00}, {-.542517E+00, 0.605577E+00}, + {-.544984E+00, 0.603235E+00}, {-.547475E+00, 0.600860E+00}, + {-.549989E+00, 0.598454E+00}, {-.552523E+00, 0.596018E+00}, + {-.555074E+00, 0.593555E+00}, {-.557639E+00, 0.591068E+00}, + {-.560216E+00, 0.588559E+00}, {-.562801E+00, 0.586030E+00}, + {-.565393E+00, 0.583484E+00}, {-.567988E+00, 0.580923E+00}, + {-.570584E+00, 0.578350E+00}, {-.573178E+00, 0.575767E+00}, + {-.575767E+00, 0.573178E+00}, {-.578350E+00, 0.570584E+00}, + {-.580923E+00, 0.567988E+00}, {-.583484E+00, 0.565393E+00}, + {-.586030E+00, 0.562801E+00}, {-.588559E+00, 0.560216E+00}, + {-.591068E+00, 0.557639E+00}, {-.593555E+00, 0.555074E+00}, + {-.596018E+00, 0.552523E+00}, {-.598454E+00, 0.549989E+00}, + {-.600860E+00, 0.547475E+00}, {-.603235E+00, 0.544984E+00}, + {-.605577E+00, 0.542517E+00}, {-.607883E+00, 0.540078E+00}, + {-.610151E+00, 0.537669E+00}, {-.612379E+00, 0.535293E+00}, + {-.614565E+00, 0.532953E+00}, {-.616707E+00, 0.530651E+00}, + {-.618803E+00, 0.528390E+00}, {-.620851E+00, 0.526172E+00}, + {-.622850E+00, 0.524000E+00}, {-.624797E+00, 0.521877E+00}, + {-.626692E+00, 0.519803E+00}, {-.628531E+00, 0.517783E+00}, + {-.630315E+00, 0.515818E+00}, {-.632041E+00, 0.513911E+00}, + {-.633707E+00, 0.512063E+00}, {-.635313E+00, 0.510277E+00}, + {-.636857E+00, 0.508555E+00}, {-.638338E+00, 0.506899E+00}, + {-.639754E+00, 0.505311E+00}, {-.641105E+00, 0.503793E+00}, + {-.642389E+00, 0.502346E+00}, {-.643605E+00, 0.500973E+00}, + {-.644752E+00, 0.499674E+00}, {-.645830E+00, 0.498451E+00}, + {-.646837E+00, 0.497307E+00}, {-.647772E+00, 0.496242E+00}, + {-.648636E+00, 0.495257E+00}, {-.649427E+00, 0.494353E+00}, + {-.650144E+00, 0.493533E+00}, {-.650787E+00, 0.492796E+00}, + {-.651356E+00, 0.492144E+00}, {-.651850E+00, 0.491577E+00}, + {-.652269E+00, 0.491096E+00}, {-.652612E+00, 0.490702E+00}, + {-.652879E+00, 0.490395E+00}, {-.653069E+00, 0.490175E+00}, + {-.653184E+00, 0.490043E+00}, {-.653222E+00, 0.489999E+00}, + }, + { + {-.484034E+00, 0.657682E+00}, {-.484081E+00, 0.657642E+00}, + {-.484222E+00, 0.657521E+00}, {-.484457E+00, 0.657319E+00}, + {-.484785E+00, 0.657036E+00}, {-.485206E+00, 0.656673E+00}, + {-.485720E+00, 0.656230E+00}, {-.486325E+00, 0.655707E+00}, + {-.487022E+00, 0.655104E+00}, {-.487809E+00, 0.654423E+00}, + {-.488685E+00, 0.653663E+00}, {-.489650E+00, 0.652826E+00}, + {-.490702E+00, 0.651911E+00}, {-.491840E+00, 0.650920E+00}, + {-.493062E+00, 0.649853E+00}, {-.494367E+00, 0.648712E+00}, + {-.495754E+00, 0.647496E+00}, {-.497221E+00, 0.646208E+00}, + {-.498765E+00, 0.644847E+00}, {-.500386E+00, 0.643416E+00}, + {-.502081E+00, 0.641915E+00}, {-.503849E+00, 0.640346E+00}, + {-.505687E+00, 0.638709E+00}, {-.507593E+00, 0.637006E+00}, + {-.509564E+00, 0.635239E+00}, {-.511600E+00, 0.633410E+00}, + {-.513696E+00, 0.631518E+00}, {-.515852E+00, 0.629567E+00}, + {-.518063E+00, 0.627557E+00}, {-.520329E+00, 0.625492E+00}, + {-.522645E+00, 0.623371E+00}, {-.525010E+00, 0.621197E+00}, + {-.527421E+00, 0.618973E+00}, {-.529876E+00, 0.616700E+00}, + {-.532370E+00, 0.614379E+00}, {-.534903E+00, 0.612014E+00}, + {-.537470E+00, 0.609606E+00}, {-.540069E+00, 0.607157E+00}, + {-.542698E+00, 0.604670E+00}, {-.545352E+00, 0.602147E+00}, + {-.548031E+00, 0.599590E+00}, {-.550730E+00, 0.597002E+00}, + {-.553446E+00, 0.594385E+00}, {-.556178E+00, 0.591742E+00}, + {-.558921E+00, 0.589075E+00}, {-.561673E+00, 0.586386E+00}, + {-.564432E+00, 0.583679E+00}, {-.567194E+00, 0.580956E+00}, + {-.569957E+00, 0.578219E+00}, {-.572717E+00, 0.575472E+00}, + {-.575472E+00, 0.572717E+00}, {-.578219E+00, 0.569957E+00}, + {-.580956E+00, 0.567194E+00}, {-.583679E+00, 0.564432E+00}, + {-.586386E+00, 0.561673E+00}, {-.589075E+00, 0.558921E+00}, + {-.591742E+00, 0.556178E+00}, {-.594385E+00, 0.553446E+00}, + {-.597002E+00, 0.550730E+00}, {-.599590E+00, 0.548031E+00}, + {-.602147E+00, 0.545352E+00}, {-.604670E+00, 0.542698E+00}, + {-.607157E+00, 0.540069E+00}, {-.609606E+00, 0.537470E+00}, + {-.612014E+00, 0.534903E+00}, {-.614379E+00, 0.532370E+00}, + {-.616700E+00, 0.529876E+00}, {-.618973E+00, 0.527421E+00}, + {-.621197E+00, 0.525010E+00}, {-.623371E+00, 0.522645E+00}, + {-.625492E+00, 0.520329E+00}, {-.627557E+00, 0.518063E+00}, + {-.629567E+00, 0.515852E+00}, {-.631518E+00, 0.513696E+00}, + {-.633410E+00, 0.511600E+00}, {-.635239E+00, 0.509564E+00}, + {-.637006E+00, 0.507593E+00}, {-.638709E+00, 0.505687E+00}, + {-.640346E+00, 0.503849E+00}, {-.641915E+00, 0.502081E+00}, + {-.643416E+00, 0.500386E+00}, {-.644847E+00, 0.498765E+00}, + {-.646208E+00, 0.497221E+00}, {-.647496E+00, 0.495754E+00}, + {-.648712E+00, 0.494367E+00}, {-.649853E+00, 0.493062E+00}, + {-.650920E+00, 0.491840E+00}, {-.651911E+00, 0.490702E+00}, + {-.652826E+00, 0.489650E+00}, {-.653663E+00, 0.488685E+00}, + {-.654423E+00, 0.487809E+00}, {-.655104E+00, 0.487022E+00}, + {-.655707E+00, 0.486325E+00}, {-.656230E+00, 0.485720E+00}, + {-.656673E+00, 0.485206E+00}, {-.657036E+00, 0.484785E+00}, + {-.657319E+00, 0.484457E+00}, {-.657521E+00, 0.484222E+00}, + {-.657642E+00, 0.484081E+00}, {-.657682E+00, 0.484034E+00}, + }, + { + {-.478000E+00, 0.662115E+00}, {-.478050E+00, 0.662072E+00}, + {-.478200E+00, 0.661944E+00}, {-.478449E+00, 0.661731E+00}, + {-.478799E+00, 0.661432E+00}, {-.479247E+00, 0.661049E+00}, + {-.479794E+00, 0.660582E+00}, {-.480439E+00, 0.660030E+00}, + {-.481180E+00, 0.659394E+00}, {-.482018E+00, 0.658675E+00}, + {-.482951E+00, 0.657873E+00}, {-.483978E+00, 0.656989E+00}, + {-.485097E+00, 0.656024E+00}, {-.486308E+00, 0.654978E+00}, + {-.487609E+00, 0.653851E+00}, {-.488998E+00, 0.652646E+00}, + {-.490474E+00, 0.651363E+00}, {-.492035E+00, 0.650002E+00}, + {-.493678E+00, 0.648566E+00}, {-.495403E+00, 0.647054E+00}, + {-.497206E+00, 0.645469E+00}, {-.499087E+00, 0.643811E+00}, + {-.501042E+00, 0.642082E+00}, {-.503069E+00, 0.640283E+00}, + {-.505166E+00, 0.638416E+00}, {-.507330E+00, 0.636482E+00}, + {-.509560E+00, 0.634483E+00}, {-.511851E+00, 0.632421E+00}, + {-.514203E+00, 0.630297E+00}, {-.516611E+00, 0.628113E+00}, + {-.519073E+00, 0.625870E+00}, {-.521587E+00, 0.623572E+00}, + {-.524149E+00, 0.621219E+00}, {-.526757E+00, 0.618814E+00}, + {-.529407E+00, 0.616359E+00}, {-.532097E+00, 0.613857E+00}, + {-.534824E+00, 0.611309E+00}, {-.537585E+00, 0.608717E+00}, + {-.540376E+00, 0.606084E+00}, {-.543195E+00, 0.603413E+00}, + {-.546039E+00, 0.600706E+00}, {-.548904E+00, 0.597966E+00}, + {-.551787E+00, 0.595194E+00}, {-.554686E+00, 0.592394E+00}, + {-.557597E+00, 0.589568E+00}, {-.560518E+00, 0.586720E+00}, + {-.563444E+00, 0.583851E+00}, {-.566374E+00, 0.580965E+00}, + {-.569304E+00, 0.578064E+00}, {-.572231E+00, 0.575152E+00}, + {-.575152E+00, 0.572231E+00}, {-.578064E+00, 0.569304E+00}, + {-.580965E+00, 0.566374E+00}, {-.583851E+00, 0.563444E+00}, + {-.586720E+00, 0.560518E+00}, {-.589568E+00, 0.557597E+00}, + {-.592394E+00, 0.554686E+00}, {-.595194E+00, 0.551787E+00}, + {-.597966E+00, 0.548904E+00}, {-.600706E+00, 0.546039E+00}, + {-.603413E+00, 0.543195E+00}, {-.606084E+00, 0.540376E+00}, + {-.608717E+00, 0.537585E+00}, {-.611309E+00, 0.534824E+00}, + {-.613857E+00, 0.532097E+00}, {-.616359E+00, 0.529407E+00}, + {-.618814E+00, 0.526757E+00}, {-.621219E+00, 0.524149E+00}, + {-.623572E+00, 0.521587E+00}, {-.625870E+00, 0.519073E+00}, + {-.628113E+00, 0.516611E+00}, {-.630297E+00, 0.514203E+00}, + {-.632421E+00, 0.511851E+00}, {-.634483E+00, 0.509560E+00}, + {-.636482E+00, 0.507330E+00}, {-.638416E+00, 0.505166E+00}, + {-.640283E+00, 0.503069E+00}, {-.642082E+00, 0.501042E+00}, + {-.643811E+00, 0.499087E+00}, {-.645469E+00, 0.497206E+00}, + {-.647054E+00, 0.495403E+00}, {-.648566E+00, 0.493678E+00}, + {-.650002E+00, 0.492035E+00}, {-.651363E+00, 0.490474E+00}, + {-.652646E+00, 0.488998E+00}, {-.653851E+00, 0.487609E+00}, + {-.654978E+00, 0.486308E+00}, {-.656024E+00, 0.485097E+00}, + {-.656989E+00, 0.483978E+00}, {-.657873E+00, 0.482951E+00}, + {-.658675E+00, 0.482018E+00}, {-.659394E+00, 0.481180E+00}, + {-.660030E+00, 0.480439E+00}, {-.660582E+00, 0.479794E+00}, + {-.661049E+00, 0.479247E+00}, {-.661432E+00, 0.478799E+00}, + {-.661731E+00, 0.478449E+00}, {-.661944E+00, 0.478200E+00}, + {-.662072E+00, 0.478050E+00}, {-.662115E+00, 0.478000E+00}, + }, + { + {-.471893E+00, 0.666520E+00}, {-.471947E+00, 0.666475E+00}, + {-.472106E+00, 0.666340E+00}, {-.472371E+00, 0.666116E+00}, + {-.472742E+00, 0.665802E+00}, {-.473217E+00, 0.665399E+00}, + {-.473798E+00, 0.664907E+00}, {-.474482E+00, 0.664326E+00}, + {-.475269E+00, 0.663657E+00}, {-.476158E+00, 0.662901E+00}, + {-.477148E+00, 0.662057E+00}, {-.478238E+00, 0.661127E+00}, + {-.479426E+00, 0.660111E+00}, {-.480711E+00, 0.659010E+00}, + {-.482092E+00, 0.657825E+00}, {-.483566E+00, 0.656556E+00}, + {-.485131E+00, 0.655205E+00}, {-.486787E+00, 0.653773E+00}, + {-.488530E+00, 0.652261E+00}, {-.490360E+00, 0.650669E+00}, + {-.492273E+00, 0.649000E+00}, {-.494267E+00, 0.647254E+00}, + {-.496340E+00, 0.645433E+00}, {-.498490E+00, 0.643538E+00}, + {-.500714E+00, 0.641571E+00}, {-.503008E+00, 0.639534E+00}, + {-.505372E+00, 0.637427E+00}, {-.507801E+00, 0.635254E+00}, + {-.510293E+00, 0.633015E+00}, {-.512845E+00, 0.630713E+00}, + {-.515455E+00, 0.628349E+00}, {-.518118E+00, 0.625926E+00}, + {-.520833E+00, 0.623445E+00}, {-.523596E+00, 0.620909E+00}, + {-.526403E+00, 0.618320E+00}, {-.529253E+00, 0.615680E+00}, + {-.532140E+00, 0.612991E+00}, {-.535064E+00, 0.610257E+00}, + {-.538019E+00, 0.607478E+00}, {-.541003E+00, 0.604659E+00}, + {-.544013E+00, 0.601802E+00}, {-.547045E+00, 0.598908E+00}, + {-.550097E+00, 0.595982E+00}, {-.553164E+00, 0.593025E+00}, + {-.556244E+00, 0.590040E+00}, {-.559333E+00, 0.587031E+00}, + {-.562428E+00, 0.584000E+00}, {-.565527E+00, 0.580951E+00}, + {-.568624E+00, 0.577885E+00}, {-.571719E+00, 0.574807E+00}, + {-.574807E+00, 0.571719E+00}, {-.577885E+00, 0.568624E+00}, + {-.580951E+00, 0.565527E+00}, {-.584000E+00, 0.562428E+00}, + {-.587031E+00, 0.559333E+00}, {-.590040E+00, 0.556244E+00}, + {-.593025E+00, 0.553164E+00}, {-.595982E+00, 0.550097E+00}, + {-.598908E+00, 0.547045E+00}, {-.601802E+00, 0.544013E+00}, + {-.604659E+00, 0.541003E+00}, {-.607478E+00, 0.538019E+00}, + {-.610257E+00, 0.535064E+00}, {-.612991E+00, 0.532140E+00}, + {-.615680E+00, 0.529253E+00}, {-.618320E+00, 0.526403E+00}, + {-.620909E+00, 0.523596E+00}, {-.623445E+00, 0.520833E+00}, + {-.625926E+00, 0.518118E+00}, {-.628349E+00, 0.515455E+00}, + {-.630713E+00, 0.512845E+00}, {-.633015E+00, 0.510293E+00}, + {-.635254E+00, 0.507801E+00}, {-.637427E+00, 0.505372E+00}, + {-.639534E+00, 0.503008E+00}, {-.641571E+00, 0.500714E+00}, + {-.643538E+00, 0.498490E+00}, {-.645433E+00, 0.496340E+00}, + {-.647254E+00, 0.494267E+00}, {-.649000E+00, 0.492273E+00}, + {-.650669E+00, 0.490360E+00}, {-.652261E+00, 0.488530E+00}, + {-.653773E+00, 0.486787E+00}, {-.655205E+00, 0.485131E+00}, + {-.656556E+00, 0.483566E+00}, {-.657825E+00, 0.482092E+00}, + {-.659010E+00, 0.480711E+00}, {-.660111E+00, 0.479426E+00}, + {-.661127E+00, 0.478238E+00}, {-.662057E+00, 0.477148E+00}, + {-.662901E+00, 0.476158E+00}, {-.663657E+00, 0.475269E+00}, + {-.664326E+00, 0.474482E+00}, {-.664907E+00, 0.473798E+00}, + {-.665399E+00, 0.473217E+00}, {-.665802E+00, 0.472742E+00}, + {-.666116E+00, 0.472371E+00}, {-.666340E+00, 0.472106E+00}, + {-.666475E+00, 0.471947E+00}, {-.666520E+00, 0.471893E+00}, + }, + { + {-.465714E+00, 0.670898E+00}, {-.465770E+00, 0.670851E+00}, + {-.465938E+00, 0.670710E+00}, {-.466219E+00, 0.670475E+00}, + {-.466611E+00, 0.670146E+00}, {-.467115E+00, 0.669723E+00}, + {-.467729E+00, 0.669206E+00}, {-.468454E+00, 0.668597E+00}, + {-.469287E+00, 0.667895E+00}, {-.470228E+00, 0.667102E+00}, + {-.471276E+00, 0.666216E+00}, {-.472429E+00, 0.665240E+00}, + {-.473687E+00, 0.664174E+00}, {-.475047E+00, 0.663018E+00}, + {-.476507E+00, 0.661774E+00}, {-.478067E+00, 0.660443E+00}, + {-.479724E+00, 0.659024E+00}, {-.481475E+00, 0.657521E+00}, + {-.483320E+00, 0.655933E+00}, {-.485255E+00, 0.654261E+00}, + {-.487279E+00, 0.652508E+00}, {-.489388E+00, 0.650674E+00}, + {-.491581E+00, 0.648762E+00}, {-.493854E+00, 0.646771E+00}, + {-.496206E+00, 0.644705E+00}, {-.498632E+00, 0.642564E+00}, + {-.501131E+00, 0.640351E+00}, {-.503699E+00, 0.638067E+00}, + {-.506334E+00, 0.635714E+00}, {-.509031E+00, 0.633293E+00}, + {-.511789E+00, 0.630808E+00}, {-.514604E+00, 0.628260E+00}, + {-.517472E+00, 0.625651E+00}, {-.520391E+00, 0.622984E+00}, + {-.523357E+00, 0.620260E+00}, {-.526367E+00, 0.617483E+00}, + {-.529417E+00, 0.614654E+00}, {-.532504E+00, 0.611776E+00}, + {-.535625E+00, 0.608852E+00}, {-.538775E+00, 0.605885E+00}, + {-.541953E+00, 0.602876E+00}, {-.545153E+00, 0.599830E+00}, + {-.548374E+00, 0.596748E+00}, {-.551610E+00, 0.593634E+00}, + {-.554860E+00, 0.590490E+00}, {-.558119E+00, 0.587320E+00}, + {-.561384E+00, 0.584126E+00}, {-.564652E+00, 0.580913E+00}, + {-.567919E+00, 0.577682E+00}, {-.571181E+00, 0.574437E+00}, + {-.574437E+00, 0.571181E+00}, {-.577682E+00, 0.567919E+00}, + {-.580913E+00, 0.564652E+00}, {-.584126E+00, 0.561384E+00}, + {-.587320E+00, 0.558119E+00}, {-.590490E+00, 0.554860E+00}, + {-.593634E+00, 0.551610E+00}, {-.596748E+00, 0.548374E+00}, + {-.599830E+00, 0.545153E+00}, {-.602876E+00, 0.541953E+00}, + {-.605885E+00, 0.538775E+00}, {-.608852E+00, 0.535625E+00}, + {-.611776E+00, 0.532504E+00}, {-.614654E+00, 0.529417E+00}, + {-.617483E+00, 0.526367E+00}, {-.620260E+00, 0.523357E+00}, + {-.622984E+00, 0.520391E+00}, {-.625651E+00, 0.517472E+00}, + {-.628260E+00, 0.514604E+00}, {-.630808E+00, 0.511789E+00}, + {-.633293E+00, 0.509031E+00}, {-.635714E+00, 0.506334E+00}, + {-.638067E+00, 0.503699E+00}, {-.640351E+00, 0.501131E+00}, + {-.642564E+00, 0.498632E+00}, {-.644705E+00, 0.496206E+00}, + {-.646771E+00, 0.493854E+00}, {-.648762E+00, 0.491581E+00}, + {-.650674E+00, 0.489388E+00}, {-.652508E+00, 0.487279E+00}, + {-.654261E+00, 0.485255E+00}, {-.655933E+00, 0.483320E+00}, + {-.657521E+00, 0.481475E+00}, {-.659024E+00, 0.479724E+00}, + {-.660443E+00, 0.478067E+00}, {-.661774E+00, 0.476507E+00}, + {-.663018E+00, 0.475047E+00}, {-.664174E+00, 0.473687E+00}, + {-.665240E+00, 0.472429E+00}, {-.666216E+00, 0.471276E+00}, + {-.667102E+00, 0.470228E+00}, {-.667895E+00, 0.469287E+00}, + {-.668597E+00, 0.468454E+00}, {-.669206E+00, 0.467729E+00}, + {-.669723E+00, 0.467115E+00}, {-.670146E+00, 0.466611E+00}, + {-.670475E+00, 0.466219E+00}, {-.670710E+00, 0.465938E+00}, + {-.670851E+00, 0.465770E+00}, {-.670898E+00, 0.465714E+00}, + }, + { + {-.459458E+00, 0.675252E+00}, {-.459517E+00, 0.675202E+00}, + {-.459695E+00, 0.675054E+00}, {-.459992E+00, 0.674808E+00}, + {-.460406E+00, 0.674464E+00}, {-.460938E+00, 0.674021E+00}, + {-.461587E+00, 0.673481E+00}, {-.462352E+00, 0.672843E+00}, + {-.463231E+00, 0.672108E+00}, {-.464225E+00, 0.671278E+00}, + {-.465332E+00, 0.670351E+00}, {-.466549E+00, 0.669329E+00}, + {-.467877E+00, 0.668213E+00}, {-.469312E+00, 0.667003E+00}, + {-.470854E+00, 0.665700E+00}, {-.472501E+00, 0.664306E+00}, + {-.474249E+00, 0.662821E+00}, {-.476098E+00, 0.661246E+00}, + {-.478045E+00, 0.659582E+00}, {-.480087E+00, 0.657832E+00}, + {-.482223E+00, 0.655995E+00}, {-.484449E+00, 0.654074E+00}, + {-.486762E+00, 0.652070E+00}, {-.489161E+00, 0.649984E+00}, + {-.491641E+00, 0.647818E+00}, {-.494201E+00, 0.645575E+00}, + {-.496836E+00, 0.643254E+00}, {-.499545E+00, 0.640860E+00}, + {-.502323E+00, 0.638392E+00}, {-.505167E+00, 0.635854E+00}, + {-.508075E+00, 0.633248E+00}, {-.511042E+00, 0.630575E+00}, + {-.514066E+00, 0.627838E+00}, {-.517142E+00, 0.625039E+00}, + {-.520268E+00, 0.622181E+00}, {-.523440E+00, 0.619266E+00}, + {-.526653E+00, 0.616297E+00}, {-.529905E+00, 0.613276E+00}, + {-.533192E+00, 0.610206E+00}, {-.536511E+00, 0.607090E+00}, + {-.539857E+00, 0.603931E+00}, {-.543227E+00, 0.600730E+00}, + {-.546618E+00, 0.597493E+00}, {-.550025E+00, 0.594221E+00}, + {-.553445E+00, 0.590917E+00}, {-.556875E+00, 0.587586E+00}, + {-.560311E+00, 0.584229E+00}, {-.563749E+00, 0.580850E+00}, + {-.567186E+00, 0.577453E+00}, {-.570618E+00, 0.574041E+00}, + {-.574041E+00, 0.570618E+00}, {-.577453E+00, 0.567186E+00}, + {-.580850E+00, 0.563749E+00}, {-.584229E+00, 0.560311E+00}, + {-.587586E+00, 0.556875E+00}, {-.590917E+00, 0.553445E+00}, + {-.594221E+00, 0.550025E+00}, {-.597493E+00, 0.546618E+00}, + {-.600730E+00, 0.543227E+00}, {-.603931E+00, 0.539857E+00}, + {-.607090E+00, 0.536511E+00}, {-.610206E+00, 0.533192E+00}, + {-.613276E+00, 0.529905E+00}, {-.616297E+00, 0.526653E+00}, + {-.619266E+00, 0.523440E+00}, {-.622181E+00, 0.520268E+00}, + {-.625039E+00, 0.517142E+00}, {-.627838E+00, 0.514066E+00}, + {-.630575E+00, 0.511042E+00}, {-.633248E+00, 0.508075E+00}, + {-.635854E+00, 0.505167E+00}, {-.638392E+00, 0.502323E+00}, + {-.640860E+00, 0.499545E+00}, {-.643254E+00, 0.496836E+00}, + {-.645575E+00, 0.494201E+00}, {-.647818E+00, 0.491641E+00}, + {-.649984E+00, 0.489161E+00}, {-.652070E+00, 0.486762E+00}, + {-.654074E+00, 0.484449E+00}, {-.655995E+00, 0.482223E+00}, + {-.657832E+00, 0.480087E+00}, {-.659582E+00, 0.478045E+00}, + {-.661246E+00, 0.476098E+00}, {-.662821E+00, 0.474249E+00}, + {-.664306E+00, 0.472501E+00}, {-.665700E+00, 0.470854E+00}, + {-.667003E+00, 0.469312E+00}, {-.668213E+00, 0.467877E+00}, + {-.669329E+00, 0.466549E+00}, {-.670351E+00, 0.465332E+00}, + {-.671278E+00, 0.464225E+00}, {-.672108E+00, 0.463231E+00}, + {-.672843E+00, 0.462352E+00}, {-.673481E+00, 0.461587E+00}, + {-.674021E+00, 0.460938E+00}, {-.674464E+00, 0.460406E+00}, + {-.674808E+00, 0.459992E+00}, {-.675054E+00, 0.459695E+00}, + {-.675202E+00, 0.459517E+00}, {-.675252E+00, 0.459458E+00}, + }, + { + {-.453124E+00, 0.679580E+00}, {-.453187E+00, 0.679528E+00}, + {-.453375E+00, 0.679374E+00}, {-.453687E+00, 0.679117E+00}, + {-.454123E+00, 0.678757E+00}, {-.454684E+00, 0.678295E+00}, + {-.455368E+00, 0.677731E+00}, {-.456173E+00, 0.677065E+00}, + {-.457100E+00, 0.676297E+00}, {-.458147E+00, 0.675430E+00}, + {-.459313E+00, 0.674462E+00}, {-.460596E+00, 0.673394E+00}, + {-.461994E+00, 0.672228E+00}, {-.463507E+00, 0.670964E+00}, + {-.465131E+00, 0.669603E+00}, {-.466865E+00, 0.668146E+00}, + {-.468707E+00, 0.666595E+00}, {-.470654E+00, 0.664949E+00}, + {-.472704E+00, 0.663210E+00}, {-.474855E+00, 0.661381E+00}, + {-.477103E+00, 0.659461E+00}, {-.479447E+00, 0.657453E+00}, + {-.481882E+00, 0.655357E+00}, {-.484407E+00, 0.653176E+00}, + {-.487018E+00, 0.650912E+00}, {-.489712E+00, 0.648565E+00}, + {-.492486E+00, 0.646138E+00}, {-.495336E+00, 0.643633E+00}, + {-.498259E+00, 0.641051E+00}, {-.501252E+00, 0.638396E+00}, + {-.504311E+00, 0.635668E+00}, {-.507432E+00, 0.632870E+00}, + {-.510612E+00, 0.630005E+00}, {-.513848E+00, 0.627075E+00}, + {-.517135E+00, 0.624083E+00}, {-.520469E+00, 0.621030E+00}, + {-.523848E+00, 0.617921E+00}, {-.527266E+00, 0.614756E+00}, + {-.530721E+00, 0.611540E+00}, {-.534209E+00, 0.608275E+00}, + {-.537725E+00, 0.604964E+00}, {-.541266E+00, 0.601610E+00}, + {-.544828E+00, 0.598216E+00}, {-.548406E+00, 0.594786E+00}, + {-.551999E+00, 0.591322E+00}, {-.555600E+00, 0.587828E+00}, + {-.559208E+00, 0.584308E+00}, {-.562817E+00, 0.580764E+00}, + {-.566425E+00, 0.577200E+00}, {-.570027E+00, 0.573620E+00}, + {-.573620E+00, 0.570027E+00}, {-.577200E+00, 0.566425E+00}, + {-.580764E+00, 0.562817E+00}, {-.584308E+00, 0.559208E+00}, + {-.587828E+00, 0.555600E+00}, {-.591322E+00, 0.551999E+00}, + {-.594786E+00, 0.548406E+00}, {-.598216E+00, 0.544828E+00}, + {-.601610E+00, 0.541266E+00}, {-.604964E+00, 0.537725E+00}, + {-.608275E+00, 0.534209E+00}, {-.611540E+00, 0.530721E+00}, + {-.614756E+00, 0.527266E+00}, {-.617921E+00, 0.523848E+00}, + {-.621030E+00, 0.520469E+00}, {-.624083E+00, 0.517135E+00}, + {-.627075E+00, 0.513848E+00}, {-.630005E+00, 0.510612E+00}, + {-.632870E+00, 0.507432E+00}, {-.635668E+00, 0.504311E+00}, + {-.638396E+00, 0.501252E+00}, {-.641051E+00, 0.498259E+00}, + {-.643633E+00, 0.495336E+00}, {-.646138E+00, 0.492486E+00}, + {-.648565E+00, 0.489712E+00}, {-.650912E+00, 0.487018E+00}, + {-.653176E+00, 0.484407E+00}, {-.655357E+00, 0.481882E+00}, + {-.657453E+00, 0.479447E+00}, {-.659461E+00, 0.477103E+00}, + {-.661381E+00, 0.474855E+00}, {-.663210E+00, 0.472704E+00}, + {-.664949E+00, 0.470654E+00}, {-.666595E+00, 0.468707E+00}, + {-.668146E+00, 0.466865E+00}, {-.669603E+00, 0.465131E+00}, + {-.670964E+00, 0.463507E+00}, {-.672228E+00, 0.461994E+00}, + {-.673394E+00, 0.460596E+00}, {-.674462E+00, 0.459313E+00}, + {-.675430E+00, 0.458147E+00}, {-.676297E+00, 0.457100E+00}, + {-.677065E+00, 0.456173E+00}, {-.677731E+00, 0.455368E+00}, + {-.678295E+00, 0.454684E+00}, {-.678757E+00, 0.454123E+00}, + {-.679117E+00, 0.453687E+00}, {-.679374E+00, 0.453375E+00}, + {-.679528E+00, 0.453187E+00}, {-.679580E+00, 0.453124E+00}, + }, + { + {-.446711E+00, 0.683884E+00}, {-.446776E+00, 0.683830E+00}, + {-.446974E+00, 0.683669E+00}, {-.447302E+00, 0.683401E+00}, + {-.447761E+00, 0.683027E+00}, {-.448351E+00, 0.682545E+00}, + {-.449070E+00, 0.681957E+00}, {-.449917E+00, 0.681263E+00}, + {-.450892E+00, 0.680463E+00}, {-.451992E+00, 0.679559E+00}, + {-.453218E+00, 0.678550E+00}, {-.454567E+00, 0.677437E+00}, + {-.456037E+00, 0.676221E+00}, {-.457627E+00, 0.674903E+00}, + {-.459334E+00, 0.673485E+00}, {-.461157E+00, 0.671965E+00}, + {-.463093E+00, 0.670347E+00}, {-.465140E+00, 0.668631E+00}, + {-.467295E+00, 0.666818E+00}, {-.469555E+00, 0.664909E+00}, + {-.471918E+00, 0.662906E+00}, {-.474380E+00, 0.660811E+00}, + {-.476939E+00, 0.658625E+00}, {-.479592E+00, 0.656349E+00}, + {-.482335E+00, 0.653985E+00}, {-.485165E+00, 0.651536E+00}, + {-.488078E+00, 0.649002E+00}, {-.491071E+00, 0.646387E+00}, + {-.494141E+00, 0.643691E+00}, {-.497284E+00, 0.640918E+00}, + {-.500495E+00, 0.638069E+00}, {-.503772E+00, 0.635147E+00}, + {-.507110E+00, 0.632154E+00}, {-.510506E+00, 0.629093E+00}, + {-.513955E+00, 0.625965E+00}, {-.517455E+00, 0.622775E+00}, + {-.520999E+00, 0.619525E+00}, {-.524586E+00, 0.616217E+00}, + {-.528210E+00, 0.612854E+00}, {-.531868E+00, 0.609440E+00}, + {-.535555E+00, 0.605977E+00}, {-.539268E+00, 0.602469E+00}, + {-.543003E+00, 0.598918E+00}, {-.546755E+00, 0.595329E+00}, + {-.550520E+00, 0.591705E+00}, {-.554295E+00, 0.588048E+00}, + {-.558075E+00, 0.584363E+00}, {-.561857E+00, 0.580653E+00}, + {-.565636E+00, 0.576921E+00}, {-.569409E+00, 0.573172E+00}, + {-.573172E+00, 0.569409E+00}, {-.576921E+00, 0.565636E+00}, + {-.580653E+00, 0.561857E+00}, {-.584363E+00, 0.558075E+00}, + {-.588048E+00, 0.554295E+00}, {-.591705E+00, 0.550520E+00}, + {-.595329E+00, 0.546755E+00}, {-.598918E+00, 0.543003E+00}, + {-.602469E+00, 0.539268E+00}, {-.605977E+00, 0.535555E+00}, + {-.609440E+00, 0.531868E+00}, {-.612854E+00, 0.528210E+00}, + {-.616217E+00, 0.524586E+00}, {-.619525E+00, 0.520999E+00}, + {-.622775E+00, 0.517455E+00}, {-.625965E+00, 0.513955E+00}, + {-.629093E+00, 0.510506E+00}, {-.632154E+00, 0.507110E+00}, + {-.635147E+00, 0.503772E+00}, {-.638069E+00, 0.500495E+00}, + {-.640918E+00, 0.497284E+00}, {-.643691E+00, 0.494141E+00}, + {-.646387E+00, 0.491071E+00}, {-.649002E+00, 0.488078E+00}, + {-.651536E+00, 0.485165E+00}, {-.653985E+00, 0.482335E+00}, + {-.656349E+00, 0.479592E+00}, {-.658625E+00, 0.476939E+00}, + {-.660811E+00, 0.474380E+00}, {-.662906E+00, 0.471918E+00}, + {-.664909E+00, 0.469555E+00}, {-.666818E+00, 0.467295E+00}, + {-.668631E+00, 0.465140E+00}, {-.670347E+00, 0.463093E+00}, + {-.671965E+00, 0.461157E+00}, {-.673485E+00, 0.459334E+00}, + {-.674903E+00, 0.457627E+00}, {-.676221E+00, 0.456037E+00}, + {-.677437E+00, 0.454567E+00}, {-.678550E+00, 0.453218E+00}, + {-.679559E+00, 0.451992E+00}, {-.680463E+00, 0.450892E+00}, + {-.681263E+00, 0.449917E+00}, {-.681957E+00, 0.449070E+00}, + {-.682545E+00, 0.448351E+00}, {-.683027E+00, 0.447761E+00}, + {-.683401E+00, 0.447302E+00}, {-.683669E+00, 0.446974E+00}, + {-.683830E+00, 0.446776E+00}, {-.683884E+00, 0.446711E+00}, + }, + { + {-.440214E+00, 0.688165E+00}, {-.440283E+00, 0.688109E+00}, + {-.440491E+00, 0.687942E+00}, {-.440835E+00, 0.687663E+00}, + {-.441317E+00, 0.687273E+00}, {-.441936E+00, 0.686772E+00}, + {-.442690E+00, 0.686160E+00}, {-.443580E+00, 0.685438E+00}, + {-.444603E+00, 0.684606E+00}, {-.445758E+00, 0.683665E+00}, + {-.447045E+00, 0.682615E+00}, {-.448460E+00, 0.681457E+00}, + {-.450003E+00, 0.680192E+00}, {-.451672E+00, 0.678821E+00}, + {-.453463E+00, 0.677344E+00}, {-.455376E+00, 0.675763E+00}, + {-.457407E+00, 0.674079E+00}, {-.459555E+00, 0.672292E+00}, + {-.461815E+00, 0.670404E+00}, {-.464186E+00, 0.668417E+00}, + {-.466665E+00, 0.666332E+00}, {-.469247E+00, 0.664150E+00}, + {-.471931E+00, 0.661872E+00}, {-.474713E+00, 0.659502E+00}, + {-.477590E+00, 0.657040E+00}, {-.480557E+00, 0.654488E+00}, + {-.483611E+00, 0.651848E+00}, {-.486749E+00, 0.649122E+00}, + {-.489967E+00, 0.646313E+00}, {-.493261E+00, 0.643422E+00}, + {-.496626E+00, 0.640452E+00}, {-.500060E+00, 0.637405E+00}, + {-.503558E+00, 0.634284E+00}, {-.507116E+00, 0.631091E+00}, + {-.510729E+00, 0.627829E+00}, {-.514395E+00, 0.624501E+00}, + {-.518107E+00, 0.621109E+00}, {-.521863E+00, 0.617657E+00}, + {-.525658E+00, 0.614148E+00}, {-.529487E+00, 0.610584E+00}, + {-.533347E+00, 0.606969E+00}, {-.537234E+00, 0.603306E+00}, + {-.541142E+00, 0.599599E+00}, {-.545068E+00, 0.595850E+00}, + {-.549008E+00, 0.592064E+00}, {-.552957E+00, 0.588244E+00}, + {-.556911E+00, 0.584393E+00}, {-.560866E+00, 0.580516E+00}, + {-.564818E+00, 0.576616E+00}, {-.568763E+00, 0.572697E+00}, + {-.572697E+00, 0.568763E+00}, {-.576616E+00, 0.564818E+00}, + {-.580516E+00, 0.560866E+00}, {-.584393E+00, 0.556911E+00}, + {-.588244E+00, 0.552957E+00}, {-.592064E+00, 0.549008E+00}, + {-.595850E+00, 0.545068E+00}, {-.599599E+00, 0.541142E+00}, + {-.603306E+00, 0.537234E+00}, {-.606969E+00, 0.533347E+00}, + {-.610584E+00, 0.529487E+00}, {-.614148E+00, 0.525658E+00}, + {-.617657E+00, 0.521863E+00}, {-.621109E+00, 0.518107E+00}, + {-.624501E+00, 0.514395E+00}, {-.627829E+00, 0.510729E+00}, + {-.631091E+00, 0.507116E+00}, {-.634284E+00, 0.503558E+00}, + {-.637405E+00, 0.500060E+00}, {-.640452E+00, 0.496626E+00}, + {-.643422E+00, 0.493261E+00}, {-.646313E+00, 0.489967E+00}, + {-.649122E+00, 0.486749E+00}, {-.651848E+00, 0.483611E+00}, + {-.654488E+00, 0.480557E+00}, {-.657040E+00, 0.477590E+00}, + {-.659502E+00, 0.474713E+00}, {-.661872E+00, 0.471931E+00}, + {-.664150E+00, 0.469247E+00}, {-.666332E+00, 0.466665E+00}, + {-.668417E+00, 0.464186E+00}, {-.670404E+00, 0.461815E+00}, + {-.672292E+00, 0.459555E+00}, {-.674079E+00, 0.457407E+00}, + {-.675763E+00, 0.455376E+00}, {-.677344E+00, 0.453463E+00}, + {-.678821E+00, 0.451672E+00}, {-.680192E+00, 0.450003E+00}, + {-.681457E+00, 0.448460E+00}, {-.682615E+00, 0.447045E+00}, + {-.683665E+00, 0.445758E+00}, {-.684606E+00, 0.444603E+00}, + {-.685438E+00, 0.443580E+00}, {-.686160E+00, 0.442690E+00}, + {-.686772E+00, 0.441936E+00}, {-.687273E+00, 0.441317E+00}, + {-.687663E+00, 0.440835E+00}, {-.687942E+00, 0.440491E+00}, + {-.688109E+00, 0.440283E+00}, {-.688165E+00, 0.440214E+00}, + }, + { + {-.433633E+00, 0.692423E+00}, {-.433706E+00, 0.692365E+00}, + {-.433923E+00, 0.692191E+00}, {-.434284E+00, 0.691902E+00}, + {-.434789E+00, 0.691497E+00}, {-.435437E+00, 0.690976E+00}, + {-.436228E+00, 0.690341E+00}, {-.437160E+00, 0.689591E+00}, + {-.438232E+00, 0.688727E+00}, {-.439443E+00, 0.687750E+00}, + {-.440791E+00, 0.686659E+00}, {-.442274E+00, 0.685456E+00}, + {-.443890E+00, 0.684142E+00}, {-.445638E+00, 0.682718E+00}, + {-.447515E+00, 0.681183E+00}, {-.449519E+00, 0.679540E+00}, + {-.451647E+00, 0.677790E+00}, {-.453896E+00, 0.675933E+00}, + {-.456263E+00, 0.673971E+00}, {-.458747E+00, 0.671906E+00}, + {-.461342E+00, 0.669738E+00}, {-.464047E+00, 0.667469E+00}, + {-.466857E+00, 0.665101E+00}, {-.469769E+00, 0.662636E+00}, + {-.472781E+00, 0.660075E+00}, {-.475887E+00, 0.657421E+00}, + {-.479084E+00, 0.654675E+00}, {-.482368E+00, 0.651839E+00}, + {-.485735E+00, 0.648916E+00}, {-.489182E+00, 0.645907E+00}, + {-.492703E+00, 0.642816E+00}, {-.496296E+00, 0.639645E+00}, + {-.499955E+00, 0.636395E+00}, {-.503676E+00, 0.633071E+00}, + {-.507455E+00, 0.629674E+00}, {-.511288E+00, 0.626208E+00}, + {-.515170E+00, 0.622675E+00}, {-.519097E+00, 0.619079E+00}, + {-.523064E+00, 0.615422E+00}, {-.527066E+00, 0.611708E+00}, + {-.531100E+00, 0.607940E+00}, {-.535161E+00, 0.604122E+00}, + {-.539245E+00, 0.600257E+00}, {-.543347E+00, 0.596348E+00}, + {-.547462E+00, 0.592400E+00}, {-.551586E+00, 0.588416E+00}, + {-.555715E+00, 0.584399E+00}, {-.559845E+00, 0.580354E+00}, + {-.563971E+00, 0.576285E+00}, {-.568089E+00, 0.572195E+00}, + {-.572195E+00, 0.568089E+00}, {-.576285E+00, 0.563971E+00}, + {-.580354E+00, 0.559845E+00}, {-.584399E+00, 0.555715E+00}, + {-.588416E+00, 0.551586E+00}, {-.592400E+00, 0.547462E+00}, + {-.596348E+00, 0.543347E+00}, {-.600257E+00, 0.539245E+00}, + {-.604122E+00, 0.535161E+00}, {-.607940E+00, 0.531100E+00}, + {-.611708E+00, 0.527066E+00}, {-.615422E+00, 0.523064E+00}, + {-.619079E+00, 0.519097E+00}, {-.622675E+00, 0.515170E+00}, + {-.626208E+00, 0.511288E+00}, {-.629674E+00, 0.507455E+00}, + {-.633071E+00, 0.503676E+00}, {-.636395E+00, 0.499955E+00}, + {-.639645E+00, 0.496296E+00}, {-.642816E+00, 0.492703E+00}, + {-.645907E+00, 0.489182E+00}, {-.648916E+00, 0.485735E+00}, + {-.651839E+00, 0.482368E+00}, {-.654675E+00, 0.479084E+00}, + {-.657421E+00, 0.475887E+00}, {-.660075E+00, 0.472781E+00}, + {-.662636E+00, 0.469769E+00}, {-.665101E+00, 0.466857E+00}, + {-.667469E+00, 0.464047E+00}, {-.669738E+00, 0.461342E+00}, + {-.671906E+00, 0.458747E+00}, {-.673971E+00, 0.456263E+00}, + {-.675933E+00, 0.453896E+00}, {-.677790E+00, 0.451647E+00}, + {-.679540E+00, 0.449519E+00}, {-.681183E+00, 0.447515E+00}, + {-.682718E+00, 0.445638E+00}, {-.684142E+00, 0.443890E+00}, + {-.685456E+00, 0.442274E+00}, {-.686659E+00, 0.440791E+00}, + {-.687750E+00, 0.439443E+00}, {-.688727E+00, 0.438232E+00}, + {-.689591E+00, 0.437160E+00}, {-.690341E+00, 0.436228E+00}, + {-.690976E+00, 0.435437E+00}, {-.691497E+00, 0.434789E+00}, + {-.691902E+00, 0.434284E+00}, {-.692191E+00, 0.433923E+00}, + {-.692365E+00, 0.433706E+00}, {-.692423E+00, 0.433633E+00}, + }, + { + {-.426965E+00, 0.696659E+00}, {-.427040E+00, 0.696599E+00}, + {-.427267E+00, 0.696419E+00}, {-.427645E+00, 0.696119E+00}, + {-.428174E+00, 0.695699E+00}, {-.428852E+00, 0.695159E+00}, + {-.429680E+00, 0.694500E+00}, {-.430655E+00, 0.693723E+00}, + {-.431776E+00, 0.692827E+00}, {-.433043E+00, 0.691813E+00}, + {-.434453E+00, 0.690682E+00}, {-.436005E+00, 0.689435E+00}, + {-.437696E+00, 0.688072E+00}, {-.439525E+00, 0.686594E+00}, + {-.441488E+00, 0.685002E+00}, {-.443584E+00, 0.683298E+00}, + {-.445809E+00, 0.681481E+00}, {-.448161E+00, 0.679555E+00}, + {-.450637E+00, 0.677519E+00}, {-.453234E+00, 0.675375E+00}, + {-.455948E+00, 0.673125E+00}, {-.458776E+00, 0.670770E+00}, + {-.461714E+00, 0.668312E+00}, {-.464758E+00, 0.665752E+00}, + {-.467906E+00, 0.663093E+00}, {-.471152E+00, 0.660336E+00}, + {-.474494E+00, 0.657484E+00}, {-.477926E+00, 0.654538E+00}, + {-.481444E+00, 0.651501E+00}, {-.485045E+00, 0.648375E+00}, + {-.488724E+00, 0.645163E+00}, {-.492477E+00, 0.641866E+00}, + {-.496299E+00, 0.638489E+00}, {-.500185E+00, 0.635032E+00}, + {-.504132E+00, 0.631501E+00}, {-.508134E+00, 0.627896E+00}, + {-.512186E+00, 0.624221E+00}, {-.516285E+00, 0.620480E+00}, + {-.520426E+00, 0.616676E+00}, {-.524603E+00, 0.612812E+00}, + {-.528813E+00, 0.608891E+00}, {-.533050E+00, 0.604917E+00}, + {-.537310E+00, 0.600893E+00}, {-.541589E+00, 0.596824E+00}, + {-.545881E+00, 0.592713E+00}, {-.550182E+00, 0.588564E+00}, + {-.554488E+00, 0.584380E+00}, {-.558793E+00, 0.580167E+00}, + {-.563094E+00, 0.575927E+00}, {-.567387E+00, 0.571666E+00}, + {-.571666E+00, 0.567387E+00}, {-.575927E+00, 0.563094E+00}, + {-.580167E+00, 0.558793E+00}, {-.584380E+00, 0.554488E+00}, + {-.588564E+00, 0.550182E+00}, {-.592713E+00, 0.545881E+00}, + {-.596824E+00, 0.541589E+00}, {-.600893E+00, 0.537310E+00}, + {-.604917E+00, 0.533050E+00}, {-.608891E+00, 0.528813E+00}, + {-.612812E+00, 0.524603E+00}, {-.616676E+00, 0.520426E+00}, + {-.620480E+00, 0.516285E+00}, {-.624221E+00, 0.512186E+00}, + {-.627896E+00, 0.508134E+00}, {-.631501E+00, 0.504132E+00}, + {-.635032E+00, 0.500185E+00}, {-.638489E+00, 0.496299E+00}, + {-.641866E+00, 0.492477E+00}, {-.645163E+00, 0.488724E+00}, + {-.648375E+00, 0.485045E+00}, {-.651501E+00, 0.481444E+00}, + {-.654538E+00, 0.477926E+00}, {-.657484E+00, 0.474494E+00}, + {-.660336E+00, 0.471152E+00}, {-.663093E+00, 0.467906E+00}, + {-.665752E+00, 0.464758E+00}, {-.668312E+00, 0.461714E+00}, + {-.670770E+00, 0.458776E+00}, {-.673125E+00, 0.455948E+00}, + {-.675375E+00, 0.453234E+00}, {-.677519E+00, 0.450637E+00}, + {-.679555E+00, 0.448161E+00}, {-.681481E+00, 0.445809E+00}, + {-.683298E+00, 0.443584E+00}, {-.685002E+00, 0.441488E+00}, + {-.686594E+00, 0.439525E+00}, {-.688072E+00, 0.437696E+00}, + {-.689435E+00, 0.436005E+00}, {-.690682E+00, 0.434453E+00}, + {-.691813E+00, 0.433043E+00}, {-.692827E+00, 0.431776E+00}, + {-.693723E+00, 0.430655E+00}, {-.694500E+00, 0.429680E+00}, + {-.695159E+00, 0.428852E+00}, {-.695699E+00, 0.428174E+00}, + {-.696119E+00, 0.427645E+00}, {-.696419E+00, 0.427267E+00}, + {-.696599E+00, 0.427040E+00}, {-.696659E+00, 0.426965E+00}, + }, + { + {-.420206E+00, 0.700874E+00}, {-.420285E+00, 0.700812E+00}, + {-.420522E+00, 0.700625E+00}, {-.420917E+00, 0.700314E+00}, + {-.421469E+00, 0.699880E+00}, {-.422178E+00, 0.699321E+00}, + {-.423043E+00, 0.698639E+00}, {-.424062E+00, 0.697834E+00}, + {-.425234E+00, 0.696906E+00}, {-.426557E+00, 0.695856E+00}, + {-.428030E+00, 0.694685E+00}, {-.429651E+00, 0.693393E+00}, + {-.431418E+00, 0.691981E+00}, {-.433328E+00, 0.690451E+00}, + {-.435379E+00, 0.688802E+00}, {-.437568E+00, 0.687036E+00}, + {-.439892E+00, 0.685154E+00}, {-.442349E+00, 0.683158E+00}, + {-.444935E+00, 0.681048E+00}, {-.447646E+00, 0.678826E+00}, + {-.450480E+00, 0.676494E+00}, {-.453432E+00, 0.674053E+00}, + {-.456500E+00, 0.671504E+00}, {-.459678E+00, 0.668850E+00}, + {-.462964E+00, 0.666093E+00}, {-.466352E+00, 0.663234E+00}, + {-.469839E+00, 0.660275E+00}, {-.473421E+00, 0.657219E+00}, + {-.477092E+00, 0.654069E+00}, {-.480850E+00, 0.650825E+00}, + {-.484688E+00, 0.647491E+00}, {-.488602E+00, 0.644070E+00}, + {-.492588E+00, 0.640564E+00}, {-.496642E+00, 0.636976E+00}, + {-.500757E+00, 0.633308E+00}, {-.504930E+00, 0.629565E+00}, + {-.509155E+00, 0.625749E+00}, {-.513428E+00, 0.621863E+00}, + {-.517744E+00, 0.617910E+00}, {-.522098E+00, 0.613895E+00}, + {-.526484E+00, 0.609820E+00}, {-.530899E+00, 0.605690E+00}, + {-.535337E+00, 0.601507E+00}, {-.539794E+00, 0.597277E+00}, + {-.544264E+00, 0.593002E+00}, {-.548744E+00, 0.588687E+00}, + {-.553227E+00, 0.584336E+00}, {-.557709E+00, 0.579952E+00}, + {-.562187E+00, 0.575542E+00}, {-.566654E+00, 0.571107E+00}, + {-.571107E+00, 0.566654E+00}, {-.575542E+00, 0.562187E+00}, + {-.579952E+00, 0.557709E+00}, {-.584336E+00, 0.553227E+00}, + {-.588687E+00, 0.548744E+00}, {-.593002E+00, 0.544264E+00}, + {-.597277E+00, 0.539794E+00}, {-.601507E+00, 0.535337E+00}, + {-.605690E+00, 0.530899E+00}, {-.609820E+00, 0.526484E+00}, + {-.613895E+00, 0.522098E+00}, {-.617910E+00, 0.517744E+00}, + {-.621863E+00, 0.513428E+00}, {-.625749E+00, 0.509155E+00}, + {-.629565E+00, 0.504930E+00}, {-.633308E+00, 0.500757E+00}, + {-.636976E+00, 0.496642E+00}, {-.640564E+00, 0.492588E+00}, + {-.644070E+00, 0.488602E+00}, {-.647491E+00, 0.484688E+00}, + {-.650825E+00, 0.480850E+00}, {-.654069E+00, 0.477092E+00}, + {-.657219E+00, 0.473421E+00}, {-.660275E+00, 0.469839E+00}, + {-.663234E+00, 0.466352E+00}, {-.666093E+00, 0.462964E+00}, + {-.668850E+00, 0.459678E+00}, {-.671504E+00, 0.456500E+00}, + {-.674053E+00, 0.453432E+00}, {-.676494E+00, 0.450480E+00}, + {-.678826E+00, 0.447646E+00}, {-.681048E+00, 0.444935E+00}, + {-.683158E+00, 0.442349E+00}, {-.685154E+00, 0.439892E+00}, + {-.687036E+00, 0.437568E+00}, {-.688802E+00, 0.435379E+00}, + {-.690451E+00, 0.433328E+00}, {-.691981E+00, 0.431418E+00}, + {-.693393E+00, 0.429651E+00}, {-.694685E+00, 0.428030E+00}, + {-.695856E+00, 0.426557E+00}, {-.696906E+00, 0.425234E+00}, + {-.697834E+00, 0.424062E+00}, {-.698639E+00, 0.423043E+00}, + {-.699321E+00, 0.422178E+00}, {-.699880E+00, 0.421469E+00}, + {-.700314E+00, 0.420917E+00}, {-.700625E+00, 0.420522E+00}, + {-.700812E+00, 0.420285E+00}, {-.700874E+00, 0.420206E+00}, + }, + { + {-.413354E+00, 0.705068E+00}, {-.413437E+00, 0.705004E+00}, + {-.413684E+00, 0.704811E+00}, {-.414096E+00, 0.704490E+00}, + {-.414673E+00, 0.704040E+00}, {-.415413E+00, 0.703462E+00}, + {-.416315E+00, 0.702757E+00}, {-.417378E+00, 0.701925E+00}, + {-.418601E+00, 0.700965E+00}, {-.419982E+00, 0.699879E+00}, + {-.421519E+00, 0.698668E+00}, {-.423210E+00, 0.697332E+00}, + {-.425054E+00, 0.695872E+00}, {-.427046E+00, 0.694288E+00}, + {-.429186E+00, 0.692582E+00}, {-.431469E+00, 0.690755E+00}, + {-.433894E+00, 0.688808E+00}, {-.436456E+00, 0.686742E+00}, + {-.439153E+00, 0.684559E+00}, {-.441981E+00, 0.682259E+00}, + {-.444936E+00, 0.679845E+00}, {-.448015E+00, 0.677318E+00}, + {-.451213E+00, 0.674679E+00}, {-.454527E+00, 0.671931E+00}, + {-.457952E+00, 0.669075E+00}, {-.461484E+00, 0.666114E+00}, + {-.465119E+00, 0.663049E+00}, {-.468851E+00, 0.659884E+00}, + {-.472678E+00, 0.656619E+00}, {-.476593E+00, 0.653258E+00}, + {-.480592E+00, 0.649802E+00}, {-.484670E+00, 0.646256E+00}, + {-.488822E+00, 0.642621E+00}, {-.493044E+00, 0.638901E+00}, + {-.497330E+00, 0.635098E+00}, {-.501675E+00, 0.631216E+00}, + {-.506075E+00, 0.627257E+00}, {-.510523E+00, 0.623226E+00}, + {-.515016E+00, 0.619125E+00}, {-.519548E+00, 0.614958E+00}, + {-.524113E+00, 0.610729E+00}, {-.528707E+00, 0.606441E+00}, + {-.533325E+00, 0.602099E+00}, {-.537961E+00, 0.597706E+00}, + {-.542611E+00, 0.593267E+00}, {-.547270E+00, 0.588785E+00}, + {-.551932E+00, 0.584265E+00}, {-.556593E+00, 0.579711E+00}, + {-.561248E+00, 0.575128E+00}, {-.565892E+00, 0.570520E+00}, + {-.570520E+00, 0.565892E+00}, {-.575128E+00, 0.561248E+00}, + {-.579711E+00, 0.556593E+00}, {-.584265E+00, 0.551932E+00}, + {-.588785E+00, 0.547270E+00}, {-.593267E+00, 0.542611E+00}, + {-.597706E+00, 0.537961E+00}, {-.602099E+00, 0.533325E+00}, + {-.606441E+00, 0.528707E+00}, {-.610729E+00, 0.524113E+00}, + {-.614958E+00, 0.519548E+00}, {-.619125E+00, 0.515016E+00}, + {-.623226E+00, 0.510523E+00}, {-.627257E+00, 0.506075E+00}, + {-.631216E+00, 0.501675E+00}, {-.635098E+00, 0.497330E+00}, + {-.638901E+00, 0.493044E+00}, {-.642621E+00, 0.488822E+00}, + {-.646256E+00, 0.484670E+00}, {-.649802E+00, 0.480592E+00}, + {-.653258E+00, 0.476593E+00}, {-.656619E+00, 0.472678E+00}, + {-.659884E+00, 0.468851E+00}, {-.663049E+00, 0.465119E+00}, + {-.666114E+00, 0.461484E+00}, {-.669075E+00, 0.457952E+00}, + {-.671931E+00, 0.454527E+00}, {-.674679E+00, 0.451213E+00}, + {-.677318E+00, 0.448015E+00}, {-.679845E+00, 0.444936E+00}, + {-.682259E+00, 0.441981E+00}, {-.684559E+00, 0.439153E+00}, + {-.686742E+00, 0.436456E+00}, {-.688808E+00, 0.433894E+00}, + {-.690755E+00, 0.431469E+00}, {-.692582E+00, 0.429186E+00}, + {-.694288E+00, 0.427046E+00}, {-.695872E+00, 0.425054E+00}, + {-.697332E+00, 0.423210E+00}, {-.698668E+00, 0.421519E+00}, + {-.699879E+00, 0.419982E+00}, {-.700965E+00, 0.418601E+00}, + {-.701925E+00, 0.417378E+00}, {-.702757E+00, 0.416315E+00}, + {-.703462E+00, 0.415413E+00}, {-.704040E+00, 0.414673E+00}, + {-.704490E+00, 0.414096E+00}, {-.704811E+00, 0.413684E+00}, + {-.705004E+00, 0.413437E+00}, {-.705068E+00, 0.413354E+00}, + }, + { + {-.406407E+00, 0.709243E+00}, {-.406493E+00, 0.709176E+00}, + {-.406751E+00, 0.708977E+00}, {-.407181E+00, 0.708645E+00}, + {-.407781E+00, 0.708181E+00}, {-.408552E+00, 0.707584E+00}, + {-.409493E+00, 0.706856E+00}, {-.410601E+00, 0.705996E+00}, + {-.411875E+00, 0.705005E+00}, {-.413315E+00, 0.703884E+00}, + {-.414917E+00, 0.702632E+00}, {-.416679E+00, 0.701252E+00}, + {-.418600E+00, 0.699743E+00}, {-.420677E+00, 0.698107E+00}, + {-.422906E+00, 0.696345E+00}, {-.425285E+00, 0.694457E+00}, + {-.427811E+00, 0.692444E+00}, {-.430481E+00, 0.690309E+00}, + {-.433290E+00, 0.688052E+00}, {-.436236E+00, 0.685675E+00}, + {-.439314E+00, 0.683179E+00}, {-.442520E+00, 0.680565E+00}, + {-.445851E+00, 0.677837E+00}, {-.449302E+00, 0.674995E+00}, + {-.452868E+00, 0.672041E+00}, {-.456546E+00, 0.668978E+00}, + {-.460330E+00, 0.665807E+00}, {-.464216E+00, 0.662531E+00}, + {-.468198E+00, 0.659152E+00}, {-.472273E+00, 0.655673E+00}, + {-.476434E+00, 0.652097E+00}, {-.480678E+00, 0.648425E+00}, + {-.484998E+00, 0.644661E+00}, {-.489390E+00, 0.640808E+00}, + {-.493849E+00, 0.636869E+00}, {-.498368E+00, 0.632848E+00}, + {-.502944E+00, 0.628746E+00}, {-.507570E+00, 0.624569E+00}, + {-.512241E+00, 0.620319E+00}, {-.516952E+00, 0.616000E+00}, + {-.521698E+00, 0.611616E+00}, {-.526473E+00, 0.607171E+00}, + {-.531272E+00, 0.602668E+00}, {-.536089E+00, 0.598112E+00}, + {-.540921E+00, 0.593508E+00}, {-.545760E+00, 0.588858E+00}, + {-.550603E+00, 0.584169E+00}, {-.555443E+00, 0.579443E+00}, + {-.560277E+00, 0.574687E+00}, {-.565099E+00, 0.569904E+00}, + {-.569904E+00, 0.565099E+00}, {-.574687E+00, 0.560277E+00}, + {-.579443E+00, 0.555443E+00}, {-.584169E+00, 0.550603E+00}, + {-.588858E+00, 0.545760E+00}, {-.593508E+00, 0.540921E+00}, + {-.598112E+00, 0.536089E+00}, {-.602668E+00, 0.531272E+00}, + {-.607171E+00, 0.526473E+00}, {-.611616E+00, 0.521698E+00}, + {-.616000E+00, 0.516952E+00}, {-.620319E+00, 0.512241E+00}, + {-.624569E+00, 0.507570E+00}, {-.628746E+00, 0.502944E+00}, + {-.632848E+00, 0.498368E+00}, {-.636869E+00, 0.493849E+00}, + {-.640808E+00, 0.489390E+00}, {-.644661E+00, 0.484998E+00}, + {-.648425E+00, 0.480678E+00}, {-.652097E+00, 0.476434E+00}, + {-.655673E+00, 0.472273E+00}, {-.659152E+00, 0.468198E+00}, + {-.662531E+00, 0.464216E+00}, {-.665807E+00, 0.460330E+00}, + {-.668978E+00, 0.456546E+00}, {-.672041E+00, 0.452868E+00}, + {-.674995E+00, 0.449302E+00}, {-.677837E+00, 0.445851E+00}, + {-.680565E+00, 0.442520E+00}, {-.683179E+00, 0.439314E+00}, + {-.685675E+00, 0.436236E+00}, {-.688052E+00, 0.433290E+00}, + {-.690309E+00, 0.430481E+00}, {-.692444E+00, 0.427811E+00}, + {-.694457E+00, 0.425285E+00}, {-.696345E+00, 0.422906E+00}, + {-.698107E+00, 0.420677E+00}, {-.699743E+00, 0.418600E+00}, + {-.701252E+00, 0.416679E+00}, {-.702632E+00, 0.414917E+00}, + {-.703884E+00, 0.413315E+00}, {-.705005E+00, 0.411875E+00}, + {-.705996E+00, 0.410601E+00}, {-.706856E+00, 0.409493E+00}, + {-.707584E+00, 0.408552E+00}, {-.708181E+00, 0.407781E+00}, + {-.708645E+00, 0.407181E+00}, {-.708977E+00, 0.406751E+00}, + {-.709176E+00, 0.406493E+00}, {-.709243E+00, 0.406407E+00}, + }, + { + {-.399361E+00, 0.713398E+00}, {-.399450E+00, 0.713329E+00}, + {-.399719E+00, 0.713124E+00}, {-.400167E+00, 0.712782E+00}, + {-.400792E+00, 0.712303E+00}, {-.401595E+00, 0.711687E+00}, + {-.402574E+00, 0.710936E+00}, {-.403728E+00, 0.710049E+00}, + {-.405054E+00, 0.709026E+00}, {-.406553E+00, 0.707869E+00}, + {-.408221E+00, 0.706578E+00}, {-.410055E+00, 0.705154E+00}, + {-.412055E+00, 0.703597E+00}, {-.414216E+00, 0.701908E+00}, + {-.416537E+00, 0.700089E+00}, {-.419013E+00, 0.698140E+00}, + {-.421642E+00, 0.696063E+00}, {-.424420E+00, 0.693859E+00}, + {-.427343E+00, 0.691528E+00}, {-.430408E+00, 0.689073E+00}, + {-.433611E+00, 0.686496E+00}, {-.436947E+00, 0.683797E+00}, + {-.440412E+00, 0.680978E+00}, {-.444001E+00, 0.678042E+00}, + {-.447711E+00, 0.674990E+00}, {-.451535E+00, 0.671825E+00}, + {-.455471E+00, 0.668548E+00}, {-.459511E+00, 0.665162E+00}, + {-.463652E+00, 0.661669E+00}, {-.467888E+00, 0.658072E+00}, + {-.472214E+00, 0.654374E+00}, {-.476625E+00, 0.650577E+00}, + {-.481115E+00, 0.646684E+00}, {-.485679E+00, 0.642698E+00}, + {-.490312E+00, 0.638623E+00}, {-.495008E+00, 0.634461E+00}, + {-.499761E+00, 0.630217E+00}, {-.504566E+00, 0.625893E+00}, + {-.509418E+00, 0.621493E+00}, {-.514310E+00, 0.617021E+00}, + {-.519238E+00, 0.612482E+00}, {-.524195E+00, 0.607878E+00}, + {-.529177E+00, 0.603214E+00}, {-.534177E+00, 0.598494E+00}, + {-.539191E+00, 0.593723E+00}, {-.544213E+00, 0.588905E+00}, + {-.549238E+00, 0.584045E+00}, {-.554259E+00, 0.579147E+00}, + {-.559273E+00, 0.574216E+00}, {-.564274E+00, 0.569257E+00}, + {-.569257E+00, 0.564274E+00}, {-.574216E+00, 0.559273E+00}, + {-.579147E+00, 0.554259E+00}, {-.584045E+00, 0.549238E+00}, + {-.588905E+00, 0.544213E+00}, {-.593723E+00, 0.539191E+00}, + {-.598494E+00, 0.534177E+00}, {-.603214E+00, 0.529177E+00}, + {-.607878E+00, 0.524195E+00}, {-.612482E+00, 0.519238E+00}, + {-.617021E+00, 0.514310E+00}, {-.621493E+00, 0.509418E+00}, + {-.625893E+00, 0.504566E+00}, {-.630217E+00, 0.499761E+00}, + {-.634461E+00, 0.495008E+00}, {-.638623E+00, 0.490312E+00}, + {-.642698E+00, 0.485679E+00}, {-.646684E+00, 0.481115E+00}, + {-.650577E+00, 0.476625E+00}, {-.654374E+00, 0.472214E+00}, + {-.658072E+00, 0.467888E+00}, {-.661669E+00, 0.463652E+00}, + {-.665162E+00, 0.459511E+00}, {-.668548E+00, 0.455471E+00}, + {-.671825E+00, 0.451535E+00}, {-.674990E+00, 0.447711E+00}, + {-.678042E+00, 0.444001E+00}, {-.680978E+00, 0.440412E+00}, + {-.683797E+00, 0.436947E+00}, {-.686496E+00, 0.433611E+00}, + {-.689073E+00, 0.430408E+00}, {-.691528E+00, 0.427343E+00}, + {-.693859E+00, 0.424420E+00}, {-.696063E+00, 0.421642E+00}, + {-.698140E+00, 0.419013E+00}, {-.700089E+00, 0.416537E+00}, + {-.701908E+00, 0.414216E+00}, {-.703597E+00, 0.412055E+00}, + {-.705154E+00, 0.410055E+00}, {-.706578E+00, 0.408221E+00}, + {-.707869E+00, 0.406553E+00}, {-.709026E+00, 0.405054E+00}, + {-.710049E+00, 0.403728E+00}, {-.710936E+00, 0.402574E+00}, + {-.711687E+00, 0.401595E+00}, {-.712303E+00, 0.400792E+00}, + {-.712782E+00, 0.400167E+00}, {-.713124E+00, 0.399719E+00}, + {-.713329E+00, 0.399450E+00}, {-.713398E+00, 0.399361E+00}, + }, + { + {-.392213E+00, 0.717535E+00}, {-.392306E+00, 0.717464E+00}, + {-.392586E+00, 0.717252E+00}, {-.393051E+00, 0.716900E+00}, + {-.393702E+00, 0.716406E+00}, {-.394537E+00, 0.715772E+00}, + {-.395555E+00, 0.714998E+00}, {-.396755E+00, 0.714083E+00}, + {-.398135E+00, 0.713030E+00}, {-.399693E+00, 0.711837E+00}, + {-.401428E+00, 0.710506E+00}, {-.403336E+00, 0.709038E+00}, + {-.405415E+00, 0.707433E+00}, {-.407663E+00, 0.705692E+00}, + {-.410075E+00, 0.703817E+00}, {-.412650E+00, 0.701807E+00}, + {-.415383E+00, 0.699665E+00}, {-.418271E+00, 0.697392E+00}, + {-.421310E+00, 0.694988E+00}, {-.424496E+00, 0.692456E+00}, + {-.427825E+00, 0.689797E+00}, {-.431292E+00, 0.687012E+00}, + {-.434893E+00, 0.684103E+00}, {-.438623E+00, 0.681073E+00}, + {-.442477E+00, 0.677923E+00}, {-.446451E+00, 0.674656E+00}, + {-.450539E+00, 0.671272E+00}, {-.454736E+00, 0.667776E+00}, + {-.459037E+00, 0.664169E+00}, {-.463436E+00, 0.660454E+00}, + {-.467929E+00, 0.656634E+00}, {-.472509E+00, 0.652711E+00}, + {-.477171E+00, 0.648689E+00}, {-.481909E+00, 0.644570E+00}, + {-.486718E+00, 0.640358E+00}, {-.491592E+00, 0.636056E+00}, + {-.496524E+00, 0.631668E+00}, {-.501511E+00, 0.627197E+00}, + {-.506544E+00, 0.622647E+00}, {-.511620E+00, 0.618022E+00}, + {-.516731E+00, 0.613326E+00}, {-.521873E+00, 0.608563E+00}, + {-.527039E+00, 0.603737E+00}, {-.532224E+00, 0.598852E+00}, + {-.537423E+00, 0.593914E+00}, {-.542628E+00, 0.588926E+00}, + {-.547836E+00, 0.583894E+00}, {-.553041E+00, 0.578822E+00}, + {-.558236E+00, 0.573715E+00}, {-.563417E+00, 0.568579E+00}, + {-.568579E+00, 0.563417E+00}, {-.573715E+00, 0.558236E+00}, + {-.578822E+00, 0.553041E+00}, {-.583894E+00, 0.547836E+00}, + {-.588926E+00, 0.542628E+00}, {-.593914E+00, 0.537423E+00}, + {-.598852E+00, 0.532224E+00}, {-.603737E+00, 0.527039E+00}, + {-.608563E+00, 0.521873E+00}, {-.613326E+00, 0.516731E+00}, + {-.618022E+00, 0.511620E+00}, {-.622647E+00, 0.506544E+00}, + {-.627197E+00, 0.501511E+00}, {-.631668E+00, 0.496524E+00}, + {-.636056E+00, 0.491592E+00}, {-.640358E+00, 0.486718E+00}, + {-.644570E+00, 0.481909E+00}, {-.648689E+00, 0.477171E+00}, + {-.652711E+00, 0.472509E+00}, {-.656634E+00, 0.467929E+00}, + {-.660454E+00, 0.463436E+00}, {-.664169E+00, 0.459037E+00}, + {-.667776E+00, 0.454736E+00}, {-.671272E+00, 0.450539E+00}, + {-.674656E+00, 0.446451E+00}, {-.677923E+00, 0.442477E+00}, + {-.681073E+00, 0.438623E+00}, {-.684103E+00, 0.434893E+00}, + {-.687012E+00, 0.431292E+00}, {-.689797E+00, 0.427825E+00}, + {-.692456E+00, 0.424496E+00}, {-.694988E+00, 0.421310E+00}, + {-.697392E+00, 0.418271E+00}, {-.699665E+00, 0.415383E+00}, + {-.701807E+00, 0.412650E+00}, {-.703817E+00, 0.410075E+00}, + {-.705692E+00, 0.407663E+00}, {-.707433E+00, 0.405415E+00}, + {-.709038E+00, 0.403336E+00}, {-.710506E+00, 0.401428E+00}, + {-.711837E+00, 0.399693E+00}, {-.713030E+00, 0.398135E+00}, + {-.714083E+00, 0.396755E+00}, {-.714998E+00, 0.395555E+00}, + {-.715772E+00, 0.394537E+00}, {-.716406E+00, 0.393702E+00}, + {-.716900E+00, 0.393051E+00}, {-.717252E+00, 0.392586E+00}, + {-.717464E+00, 0.392306E+00}, {-.717535E+00, 0.392213E+00}, + }, + { + {-.384961E+00, 0.721654E+00}, {-.385058E+00, 0.721581E+00}, + {-.385348E+00, 0.721363E+00}, {-.385832E+00, 0.721000E+00}, + {-.386508E+00, 0.720492E+00}, {-.387375E+00, 0.719839E+00}, + {-.388433E+00, 0.719042E+00}, {-.389680E+00, 0.718101E+00}, + {-.391114E+00, 0.717016E+00}, {-.392733E+00, 0.715788E+00}, + {-.394535E+00, 0.714418E+00}, {-.396518E+00, 0.712906E+00}, + {-.398678E+00, 0.711253E+00}, {-.401013E+00, 0.709460E+00}, + {-.403519E+00, 0.707528E+00}, {-.406193E+00, 0.705458E+00}, + {-.409032E+00, 0.703251E+00}, {-.412032E+00, 0.700909E+00}, + {-.415188E+00, 0.698432E+00}, {-.418496E+00, 0.695822E+00}, + {-.421953E+00, 0.693082E+00}, {-.425553E+00, 0.690211E+00}, + {-.429291E+00, 0.687213E+00}, {-.433164E+00, 0.684089E+00}, + {-.437165E+00, 0.680841E+00}, {-.441289E+00, 0.677471E+00}, + {-.445532E+00, 0.673981E+00}, {-.449888E+00, 0.670375E+00}, + {-.454351E+00, 0.666654E+00}, {-.458916E+00, 0.662820E+00}, + {-.463577E+00, 0.658878E+00}, {-.468328E+00, 0.654829E+00}, + {-.473164E+00, 0.650677E+00}, {-.478078E+00, 0.646424E+00}, + {-.483065E+00, 0.642075E+00}, {-.488118E+00, 0.637632E+00}, + {-.493233E+00, 0.633100E+00}, {-.498402E+00, 0.628481E+00}, + {-.503619E+00, 0.623781E+00}, {-.508880E+00, 0.619001E+00}, + {-.514177E+00, 0.614148E+00}, {-.519505E+00, 0.609225E+00}, + {-.524857E+00, 0.604236E+00}, {-.530229E+00, 0.599186E+00}, + {-.535613E+00, 0.594079E+00}, {-.541004E+00, 0.588921E+00}, + {-.546397E+00, 0.583716E+00}, {-.551786E+00, 0.578468E+00}, + {-.557164E+00, 0.573185E+00}, {-.562527E+00, 0.567869E+00}, + {-.567869E+00, 0.562527E+00}, {-.573185E+00, 0.557164E+00}, + {-.578468E+00, 0.551786E+00}, {-.583716E+00, 0.546397E+00}, + {-.588921E+00, 0.541004E+00}, {-.594079E+00, 0.535613E+00}, + {-.599186E+00, 0.530229E+00}, {-.604236E+00, 0.524857E+00}, + {-.609225E+00, 0.519505E+00}, {-.614148E+00, 0.514177E+00}, + {-.619001E+00, 0.508880E+00}, {-.623781E+00, 0.503619E+00}, + {-.628481E+00, 0.498402E+00}, {-.633100E+00, 0.493233E+00}, + {-.637632E+00, 0.488118E+00}, {-.642075E+00, 0.483065E+00}, + {-.646424E+00, 0.478078E+00}, {-.650677E+00, 0.473164E+00}, + {-.654829E+00, 0.468328E+00}, {-.658878E+00, 0.463577E+00}, + {-.662820E+00, 0.458916E+00}, {-.666654E+00, 0.454351E+00}, + {-.670375E+00, 0.449888E+00}, {-.673981E+00, 0.445532E+00}, + {-.677471E+00, 0.441289E+00}, {-.680841E+00, 0.437165E+00}, + {-.684089E+00, 0.433164E+00}, {-.687213E+00, 0.429291E+00}, + {-.690211E+00, 0.425553E+00}, {-.693082E+00, 0.421953E+00}, + {-.695822E+00, 0.418496E+00}, {-.698432E+00, 0.415188E+00}, + {-.700909E+00, 0.412032E+00}, {-.703251E+00, 0.409032E+00}, + {-.705458E+00, 0.406193E+00}, {-.707528E+00, 0.403519E+00}, + {-.709460E+00, 0.401013E+00}, {-.711253E+00, 0.398678E+00}, + {-.712906E+00, 0.396518E+00}, {-.714418E+00, 0.394535E+00}, + {-.715788E+00, 0.392733E+00}, {-.717016E+00, 0.391114E+00}, + {-.718101E+00, 0.389680E+00}, {-.719042E+00, 0.388433E+00}, + {-.719839E+00, 0.387375E+00}, {-.720492E+00, 0.386508E+00}, + {-.721000E+00, 0.385832E+00}, {-.721363E+00, 0.385348E+00}, + {-.721581E+00, 0.385058E+00}, {-.721654E+00, 0.384961E+00}, + }, + { + {-.377600E+00, 0.725756E+00}, {-.377701E+00, 0.725681E+00}, + {-.378002E+00, 0.725457E+00}, {-.378504E+00, 0.725083E+00}, + {-.379206E+00, 0.724561E+00}, {-.380107E+00, 0.723889E+00}, + {-.381206E+00, 0.723069E+00}, {-.382500E+00, 0.722101E+00}, + {-.383989E+00, 0.720985E+00}, {-.385669E+00, 0.719722E+00}, + {-.387540E+00, 0.718312E+00}, {-.389598E+00, 0.716757E+00}, + {-.391840E+00, 0.715056E+00}, {-.394263E+00, 0.713211E+00}, + {-.396865E+00, 0.711223E+00}, {-.399640E+00, 0.709093E+00}, + {-.402586E+00, 0.706822E+00}, {-.405699E+00, 0.704410E+00}, + {-.408974E+00, 0.701861E+00}, {-.412406E+00, 0.699174E+00}, + {-.415992E+00, 0.696352E+00}, {-.419727E+00, 0.693395E+00}, + {-.423605E+00, 0.690307E+00}, {-.427621E+00, 0.687089E+00}, + {-.431771E+00, 0.683743E+00}, {-.436049E+00, 0.680271E+00}, + {-.440448E+00, 0.676675E+00}, {-.444965E+00, 0.672958E+00}, + {-.449592E+00, 0.669122E+00}, {-.454324E+00, 0.665170E+00}, + {-.459155E+00, 0.661105E+00}, {-.464080E+00, 0.656930E+00}, + {-.469091E+00, 0.652648E+00}, {-.474184E+00, 0.648261E+00}, + {-.479351E+00, 0.643774E+00}, {-.484586E+00, 0.639190E+00}, + {-.489884E+00, 0.634513E+00}, {-.495238E+00, 0.629746E+00}, + {-.500642E+00, 0.624894E+00}, {-.506089E+00, 0.619959E+00}, + {-.511574E+00, 0.614948E+00}, {-.517089E+00, 0.609864E+00}, + {-.522630E+00, 0.604711E+00}, {-.528189E+00, 0.599494E+00}, + {-.533761E+00, 0.594218E+00}, {-.539340E+00, 0.588888E+00}, + {-.544919E+00, 0.583508E+00}, {-.550494E+00, 0.578085E+00}, + {-.556057E+00, 0.572623E+00}, {-.561603E+00, 0.567127E+00}, + {-.567127E+00, 0.561603E+00}, {-.572623E+00, 0.556057E+00}, + {-.578085E+00, 0.550494E+00}, {-.583508E+00, 0.544919E+00}, + {-.588888E+00, 0.539340E+00}, {-.594218E+00, 0.533761E+00}, + {-.599494E+00, 0.528189E+00}, {-.604711E+00, 0.522630E+00}, + {-.609864E+00, 0.517089E+00}, {-.614948E+00, 0.511574E+00}, + {-.619959E+00, 0.506089E+00}, {-.624894E+00, 0.500642E+00}, + {-.629746E+00, 0.495238E+00}, {-.634513E+00, 0.489884E+00}, + {-.639190E+00, 0.484586E+00}, {-.643774E+00, 0.479351E+00}, + {-.648261E+00, 0.474184E+00}, {-.652648E+00, 0.469091E+00}, + {-.656930E+00, 0.464080E+00}, {-.661105E+00, 0.459155E+00}, + {-.665170E+00, 0.454324E+00}, {-.669122E+00, 0.449592E+00}, + {-.672958E+00, 0.444965E+00}, {-.676675E+00, 0.440448E+00}, + {-.680271E+00, 0.436049E+00}, {-.683743E+00, 0.431771E+00}, + {-.687089E+00, 0.427621E+00}, {-.690307E+00, 0.423605E+00}, + {-.693395E+00, 0.419727E+00}, {-.696352E+00, 0.415992E+00}, + {-.699174E+00, 0.412406E+00}, {-.701861E+00, 0.408974E+00}, + {-.704410E+00, 0.405699E+00}, {-.706822E+00, 0.402586E+00}, + {-.709093E+00, 0.399640E+00}, {-.711223E+00, 0.396865E+00}, + {-.713211E+00, 0.394263E+00}, {-.715056E+00, 0.391840E+00}, + {-.716757E+00, 0.389598E+00}, {-.718312E+00, 0.387540E+00}, + {-.719722E+00, 0.385669E+00}, {-.720985E+00, 0.383989E+00}, + {-.722101E+00, 0.382500E+00}, {-.723069E+00, 0.381206E+00}, + {-.723889E+00, 0.380107E+00}, {-.724561E+00, 0.379206E+00}, + {-.725083E+00, 0.378504E+00}, {-.725457E+00, 0.378002E+00}, + {-.725681E+00, 0.377701E+00}, {-.725756E+00, 0.377600E+00}, + }, + { + {-.370128E+00, 0.729841E+00}, {-.370232E+00, 0.729764E+00}, + {-.370545E+00, 0.729534E+00}, {-.371066E+00, 0.729150E+00}, + {-.371794E+00, 0.728613E+00}, {-.372729E+00, 0.727923E+00}, + {-.373868E+00, 0.727080E+00}, {-.375211E+00, 0.726085E+00}, + {-.376755E+00, 0.724939E+00}, {-.378498E+00, 0.723640E+00}, + {-.380439E+00, 0.722191E+00}, {-.382573E+00, 0.720592E+00}, + {-.384898E+00, 0.718844E+00}, {-.387411E+00, 0.716947E+00}, + {-.390109E+00, 0.714903E+00}, {-.392987E+00, 0.712713E+00}, + {-.396042E+00, 0.710377E+00}, {-.399269E+00, 0.707897E+00}, + {-.402664E+00, 0.705274E+00}, {-.406223E+00, 0.702510E+00}, + {-.409941E+00, 0.699607E+00}, {-.413812E+00, 0.696565E+00}, + {-.417831E+00, 0.693387E+00}, {-.421994E+00, 0.690075E+00}, + {-.426294E+00, 0.686630E+00}, {-.430726E+00, 0.683056E+00}, + {-.435285E+00, 0.679353E+00}, {-.439964E+00, 0.675526E+00}, + {-.444757E+00, 0.671575E+00}, {-.449659E+00, 0.667504E+00}, + {-.454663E+00, 0.663317E+00}, {-.459763E+00, 0.659015E+00}, + {-.464952E+00, 0.654602E+00}, {-.470224E+00, 0.650081E+00}, + {-.475574E+00, 0.645456E+00}, {-.480993E+00, 0.640730E+00}, + {-.486477E+00, 0.635907E+00}, {-.492018E+00, 0.630991E+00}, + {-.497609E+00, 0.625986E+00}, {-.503246E+00, 0.620896E+00}, + {-.508920E+00, 0.615726E+00}, {-.514625E+00, 0.610479E+00}, + {-.520356E+00, 0.605161E+00}, {-.526105E+00, 0.599777E+00}, + {-.531867E+00, 0.594330E+00}, {-.537634E+00, 0.588827E+00}, + {-.543402E+00, 0.583272E+00}, {-.549164E+00, 0.577671E+00}, + {-.554913E+00, 0.572029E+00}, {-.560644E+00, 0.566351E+00}, + {-.566351E+00, 0.560644E+00}, {-.572029E+00, 0.554913E+00}, + {-.577671E+00, 0.549164E+00}, {-.583272E+00, 0.543402E+00}, + {-.588827E+00, 0.537634E+00}, {-.594330E+00, 0.531867E+00}, + {-.599777E+00, 0.526105E+00}, {-.605161E+00, 0.520356E+00}, + {-.610479E+00, 0.514625E+00}, {-.615726E+00, 0.508920E+00}, + {-.620896E+00, 0.503246E+00}, {-.625986E+00, 0.497609E+00}, + {-.630991E+00, 0.492018E+00}, {-.635907E+00, 0.486477E+00}, + {-.640730E+00, 0.480993E+00}, {-.645456E+00, 0.475574E+00}, + {-.650081E+00, 0.470224E+00}, {-.654602E+00, 0.464952E+00}, + {-.659015E+00, 0.459763E+00}, {-.663317E+00, 0.454663E+00}, + {-.667504E+00, 0.449659E+00}, {-.671575E+00, 0.444757E+00}, + {-.675526E+00, 0.439964E+00}, {-.679353E+00, 0.435285E+00}, + {-.683056E+00, 0.430726E+00}, {-.686630E+00, 0.426294E+00}, + {-.690075E+00, 0.421994E+00}, {-.693387E+00, 0.417831E+00}, + {-.696565E+00, 0.413812E+00}, {-.699607E+00, 0.409941E+00}, + {-.702510E+00, 0.406223E+00}, {-.705274E+00, 0.402664E+00}, + {-.707897E+00, 0.399269E+00}, {-.710377E+00, 0.396042E+00}, + {-.712713E+00, 0.392987E+00}, {-.714903E+00, 0.390109E+00}, + {-.716947E+00, 0.387411E+00}, {-.718844E+00, 0.384898E+00}, + {-.720592E+00, 0.382573E+00}, {-.722191E+00, 0.380439E+00}, + {-.723640E+00, 0.378498E+00}, {-.724939E+00, 0.376755E+00}, + {-.726085E+00, 0.375211E+00}, {-.727080E+00, 0.373868E+00}, + {-.727923E+00, 0.372729E+00}, {-.728613E+00, 0.371794E+00}, + {-.729150E+00, 0.371066E+00}, {-.729534E+00, 0.370545E+00}, + {-.729764E+00, 0.370232E+00}, {-.729841E+00, 0.370128E+00}, + }, + { + {-.362540E+00, 0.733910E+00}, {-.362649E+00, 0.733831E+00}, + {-.362973E+00, 0.733595E+00}, {-.363513E+00, 0.733201E+00}, + {-.364268E+00, 0.732650E+00}, {-.365236E+00, 0.731941E+00}, + {-.366417E+00, 0.731076E+00}, {-.367809E+00, 0.730054E+00}, + {-.369410E+00, 0.728877E+00}, {-.371217E+00, 0.727543E+00}, + {-.373228E+00, 0.726055E+00}, {-.375439E+00, 0.724413E+00}, + {-.377849E+00, 0.722617E+00}, {-.380454E+00, 0.720669E+00}, + {-.383249E+00, 0.718569E+00}, {-.386231E+00, 0.716318E+00}, + {-.389396E+00, 0.713918E+00}, {-.392740E+00, 0.711369E+00}, + {-.396257E+00, 0.708673E+00}, {-.399944E+00, 0.705832E+00}, + {-.403794E+00, 0.702847E+00}, {-.407804E+00, 0.699720E+00}, + {-.411967E+00, 0.696452E+00}, {-.416277E+00, 0.693046E+00}, + {-.420730E+00, 0.689503E+00}, {-.425320E+00, 0.685826E+00}, + {-.430039E+00, 0.682017E+00}, {-.434883E+00, 0.678078E+00}, + {-.439845E+00, 0.674013E+00}, {-.444918E+00, 0.669823E+00}, + {-.450097E+00, 0.665512E+00}, {-.455374E+00, 0.661083E+00}, + {-.460743E+00, 0.656539E+00}, {-.466198E+00, 0.651883E+00}, + {-.471732E+00, 0.647119E+00}, {-.477338E+00, 0.642251E+00}, + {-.483009E+00, 0.637281E+00}, {-.488739E+00, 0.632216E+00}, + {-.494521E+00, 0.627057E+00}, {-.500348E+00, 0.621811E+00}, + {-.506213E+00, 0.616481E+00}, {-.512111E+00, 0.611071E+00}, + {-.518033E+00, 0.605587E+00}, {-.523974E+00, 0.600033E+00}, + {-.529927E+00, 0.594415E+00}, {-.535886E+00, 0.588738E+00}, + {-.541844E+00, 0.583006E+00}, {-.547795E+00, 0.577226E+00}, + {-.553732E+00, 0.571402E+00}, {-.559650E+00, 0.565542E+00}, + {-.565542E+00, 0.559650E+00}, {-.571402E+00, 0.553732E+00}, + {-.577226E+00, 0.547795E+00}, {-.583006E+00, 0.541844E+00}, + {-.588738E+00, 0.535886E+00}, {-.594415E+00, 0.529927E+00}, + {-.600033E+00, 0.523974E+00}, {-.605587E+00, 0.518033E+00}, + {-.611071E+00, 0.512111E+00}, {-.616481E+00, 0.506213E+00}, + {-.621811E+00, 0.500348E+00}, {-.627057E+00, 0.494521E+00}, + {-.632216E+00, 0.488739E+00}, {-.637281E+00, 0.483009E+00}, + {-.642251E+00, 0.477338E+00}, {-.647119E+00, 0.471732E+00}, + {-.651883E+00, 0.466198E+00}, {-.656539E+00, 0.460743E+00}, + {-.661083E+00, 0.455374E+00}, {-.665512E+00, 0.450097E+00}, + {-.669823E+00, 0.444918E+00}, {-.674013E+00, 0.439845E+00}, + {-.678078E+00, 0.434883E+00}, {-.682017E+00, 0.430039E+00}, + {-.685826E+00, 0.425320E+00}, {-.689503E+00, 0.420730E+00}, + {-.693046E+00, 0.416277E+00}, {-.696452E+00, 0.411967E+00}, + {-.699720E+00, 0.407804E+00}, {-.702847E+00, 0.403794E+00}, + {-.705832E+00, 0.399944E+00}, {-.708673E+00, 0.396257E+00}, + {-.711369E+00, 0.392740E+00}, {-.713918E+00, 0.389396E+00}, + {-.716318E+00, 0.386231E+00}, {-.718569E+00, 0.383249E+00}, + {-.720669E+00, 0.380454E+00}, {-.722617E+00, 0.377849E+00}, + {-.724413E+00, 0.375439E+00}, {-.726055E+00, 0.373228E+00}, + {-.727543E+00, 0.371217E+00}, {-.728877E+00, 0.369410E+00}, + {-.730054E+00, 0.367809E+00}, {-.731076E+00, 0.366417E+00}, + {-.731941E+00, 0.365236E+00}, {-.732650E+00, 0.364268E+00}, + {-.733201E+00, 0.363513E+00}, {-.733595E+00, 0.362973E+00}, + {-.733831E+00, 0.362649E+00}, {-.733910E+00, 0.362540E+00}, + }, + { + {-.354834E+00, 0.737964E+00}, {-.354946E+00, 0.737884E+00}, + {-.355282E+00, 0.737641E+00}, {-.355841E+00, 0.737237E+00}, + {-.356623E+00, 0.736671E+00}, {-.357626E+00, 0.735944E+00}, + {-.358850E+00, 0.735057E+00}, {-.360291E+00, 0.734008E+00}, + {-.361949E+00, 0.732800E+00}, {-.363821E+00, 0.731432E+00}, + {-.365903E+00, 0.729904E+00}, {-.368194E+00, 0.728219E+00}, + {-.370690E+00, 0.726376E+00}, {-.373387E+00, 0.724376E+00}, + {-.376281E+00, 0.722220E+00}, {-.379369E+00, 0.719909E+00}, + {-.382646E+00, 0.717444E+00}, {-.386108E+00, 0.714827E+00}, + {-.389749E+00, 0.712059E+00}, {-.393565E+00, 0.709141E+00}, + {-.397551E+00, 0.706074E+00}, {-.401701E+00, 0.702861E+00}, + {-.406009E+00, 0.699504E+00}, {-.410470E+00, 0.696003E+00}, + {-.415077E+00, 0.692362E+00}, {-.419826E+00, 0.688582E+00}, + {-.424708E+00, 0.684666E+00}, {-.429719E+00, 0.680616E+00}, + {-.434851E+00, 0.676435E+00}, {-.440099E+00, 0.672126E+00}, + {-.445454E+00, 0.667692E+00}, {-.450911E+00, 0.663135E+00}, + {-.456463E+00, 0.658459E+00}, {-.462102E+00, 0.653668E+00}, + {-.467823E+00, 0.648764E+00}, {-.473617E+00, 0.643753E+00}, + {-.479478E+00, 0.638637E+00}, {-.485400E+00, 0.633420E+00}, + {-.491374E+00, 0.628108E+00}, {-.497394E+00, 0.622704E+00}, + {-.503453E+00, 0.617212E+00}, {-.509544E+00, 0.611639E+00}, + {-.515661E+00, 0.605987E+00}, {-.521796E+00, 0.600264E+00}, + {-.527942E+00, 0.594472E+00}, {-.534094E+00, 0.588619E+00}, + {-.540244E+00, 0.582709E+00}, {-.546385E+00, 0.576749E+00}, + {-.552512E+00, 0.570743E+00}, {-.558618E+00, 0.564697E+00}, + {-.564697E+00, 0.558618E+00}, {-.570743E+00, 0.552512E+00}, + {-.576749E+00, 0.546385E+00}, {-.582709E+00, 0.540244E+00}, + {-.588619E+00, 0.534094E+00}, {-.594472E+00, 0.527942E+00}, + {-.600264E+00, 0.521796E+00}, {-.605987E+00, 0.515661E+00}, + {-.611639E+00, 0.509544E+00}, {-.617212E+00, 0.503453E+00}, + {-.622704E+00, 0.497394E+00}, {-.628108E+00, 0.491374E+00}, + {-.633420E+00, 0.485400E+00}, {-.638637E+00, 0.479478E+00}, + {-.643753E+00, 0.473617E+00}, {-.648764E+00, 0.467823E+00}, + {-.653668E+00, 0.462102E+00}, {-.658459E+00, 0.456463E+00}, + {-.663135E+00, 0.450911E+00}, {-.667692E+00, 0.445454E+00}, + {-.672126E+00, 0.440099E+00}, {-.676435E+00, 0.434851E+00}, + {-.680616E+00, 0.429719E+00}, {-.684666E+00, 0.424708E+00}, + {-.688582E+00, 0.419826E+00}, {-.692362E+00, 0.415077E+00}, + {-.696003E+00, 0.410470E+00}, {-.699504E+00, 0.406009E+00}, + {-.702861E+00, 0.401701E+00}, {-.706074E+00, 0.397551E+00}, + {-.709141E+00, 0.393565E+00}, {-.712059E+00, 0.389749E+00}, + {-.714827E+00, 0.386108E+00}, {-.717444E+00, 0.382646E+00}, + {-.719909E+00, 0.379369E+00}, {-.722220E+00, 0.376281E+00}, + {-.724376E+00, 0.373387E+00}, {-.726376E+00, 0.370690E+00}, + {-.728219E+00, 0.368194E+00}, {-.729904E+00, 0.365903E+00}, + {-.731432E+00, 0.363821E+00}, {-.732800E+00, 0.361949E+00}, + {-.734008E+00, 0.360291E+00}, {-.735057E+00, 0.358850E+00}, + {-.735944E+00, 0.357626E+00}, {-.736671E+00, 0.356623E+00}, + {-.737237E+00, 0.355841E+00}, {-.737641E+00, 0.355282E+00}, + {-.737884E+00, 0.354946E+00}, {-.737964E+00, 0.354834E+00}, + }, + { + {-.347004E+00, 0.742004E+00}, {-.347120E+00, 0.741921E+00}, + {-.347468E+00, 0.741672E+00}, {-.348047E+00, 0.741258E+00}, + {-.348856E+00, 0.740678E+00}, {-.349895E+00, 0.739933E+00}, + {-.351161E+00, 0.739023E+00}, {-.352654E+00, 0.737948E+00}, + {-.354369E+00, 0.736709E+00}, {-.356307E+00, 0.735306E+00}, + {-.358462E+00, 0.733740E+00}, {-.360833E+00, 0.732011E+00}, + {-.363416E+00, 0.730121E+00}, {-.366207E+00, 0.728069E+00}, + {-.369202E+00, 0.725857E+00}, {-.372397E+00, 0.723487E+00}, + {-.375788E+00, 0.720958E+00}, {-.379369E+00, 0.718272E+00}, + {-.383136E+00, 0.715431E+00}, {-.387084E+00, 0.712436E+00}, + {-.391207E+00, 0.709288E+00}, {-.395499E+00, 0.705989E+00}, + {-.399954E+00, 0.702542E+00}, {-.404568E+00, 0.698947E+00}, + {-.409332E+00, 0.695207E+00}, {-.414242E+00, 0.691324E+00}, + {-.419290E+00, 0.687301E+00}, {-.424470E+00, 0.683140E+00}, + {-.429775E+00, 0.678843E+00}, {-.435199E+00, 0.674414E+00}, + {-.440733E+00, 0.669856E+00}, {-.446373E+00, 0.665171E+00}, + {-.452109E+00, 0.660363E+00}, {-.457935E+00, 0.655435E+00}, + {-.463845E+00, 0.650392E+00}, {-.469830E+00, 0.645236E+00}, + {-.475883E+00, 0.639972E+00}, {-.481998E+00, 0.634604E+00}, + {-.488167E+00, 0.629137E+00}, {-.494382E+00, 0.623574E+00}, + {-.500637E+00, 0.617921E+00}, {-.506925E+00, 0.612182E+00}, + {-.513237E+00, 0.606362E+00}, {-.519568E+00, 0.600467E+00}, + {-.525910E+00, 0.594501E+00}, {-.532257E+00, 0.588471E+00}, + {-.538600E+00, 0.582381E+00}, {-.544935E+00, 0.576238E+00}, + {-.551253E+00, 0.570048E+00}, {-.557549E+00, 0.563816E+00}, + {-.563816E+00, 0.557549E+00}, {-.570048E+00, 0.551253E+00}, + {-.576238E+00, 0.544935E+00}, {-.582381E+00, 0.538600E+00}, + {-.588471E+00, 0.532257E+00}, {-.594501E+00, 0.525910E+00}, + {-.600467E+00, 0.519568E+00}, {-.606362E+00, 0.513237E+00}, + {-.612182E+00, 0.506925E+00}, {-.617921E+00, 0.500637E+00}, + {-.623574E+00, 0.494382E+00}, {-.629137E+00, 0.488167E+00}, + {-.634604E+00, 0.481998E+00}, {-.639972E+00, 0.475883E+00}, + {-.645236E+00, 0.469830E+00}, {-.650392E+00, 0.463845E+00}, + {-.655435E+00, 0.457935E+00}, {-.660363E+00, 0.452109E+00}, + {-.665171E+00, 0.446373E+00}, {-.669856E+00, 0.440733E+00}, + {-.674414E+00, 0.435199E+00}, {-.678843E+00, 0.429775E+00}, + {-.683140E+00, 0.424470E+00}, {-.687301E+00, 0.419290E+00}, + {-.691324E+00, 0.414242E+00}, {-.695207E+00, 0.409332E+00}, + {-.698947E+00, 0.404568E+00}, {-.702542E+00, 0.399954E+00}, + {-.705989E+00, 0.395499E+00}, {-.709288E+00, 0.391207E+00}, + {-.712436E+00, 0.387084E+00}, {-.715431E+00, 0.383136E+00}, + {-.718272E+00, 0.379369E+00}, {-.720958E+00, 0.375788E+00}, + {-.723487E+00, 0.372397E+00}, {-.725857E+00, 0.369202E+00}, + {-.728069E+00, 0.366207E+00}, {-.730121E+00, 0.363416E+00}, + {-.732011E+00, 0.360833E+00}, {-.733740E+00, 0.358462E+00}, + {-.735306E+00, 0.356307E+00}, {-.736709E+00, 0.354369E+00}, + {-.737948E+00, 0.352654E+00}, {-.739023E+00, 0.351161E+00}, + {-.739933E+00, 0.349895E+00}, {-.740678E+00, 0.348856E+00}, + {-.741258E+00, 0.348047E+00}, {-.741672E+00, 0.347468E+00}, + {-.741921E+00, 0.347120E+00}, {-.742004E+00, 0.347004E+00}, + }, + { + {-.339048E+00, 0.746030E+00}, {-.339168E+00, 0.745945E+00}, + {-.339528E+00, 0.745690E+00}, {-.340127E+00, 0.745266E+00}, + {-.340964E+00, 0.744672E+00}, {-.342038E+00, 0.743908E+00}, + {-.343348E+00, 0.742976E+00}, {-.344892E+00, 0.741874E+00}, + {-.346667E+00, 0.740604E+00}, {-.348670E+00, 0.739167E+00}, + {-.350900E+00, 0.737562E+00}, {-.353352E+00, 0.735790E+00}, + {-.356023E+00, 0.733852E+00}, {-.358910E+00, 0.731749E+00}, + {-.362007E+00, 0.729482E+00}, {-.365311E+00, 0.727051E+00}, + {-.368817E+00, 0.724458E+00}, {-.372520E+00, 0.721704E+00}, + {-.376415E+00, 0.718790E+00}, {-.380496E+00, 0.715718E+00}, + {-.384758E+00, 0.712489E+00}, {-.389195E+00, 0.709104E+00}, + {-.393800E+00, 0.705567E+00}, {-.398568E+00, 0.701877E+00}, + {-.403492E+00, 0.698039E+00}, {-.408565E+00, 0.694053E+00}, + {-.413781E+00, 0.689922E+00}, {-.419133E+00, 0.685649E+00}, + {-.424613E+00, 0.681236E+00}, {-.430215E+00, 0.676687E+00}, + {-.435931E+00, 0.672004E+00}, {-.441755E+00, 0.667191E+00}, + {-.447678E+00, 0.662250E+00}, {-.453694E+00, 0.657185E+00}, + {-.459795E+00, 0.652001E+00}, {-.465973E+00, 0.646700E+00}, + {-.472221E+00, 0.641288E+00}, {-.478532E+00, 0.635768E+00}, + {-.484898E+00, 0.630144E+00}, {-.491311E+00, 0.624421E+00}, + {-.497764E+00, 0.618605E+00}, {-.504250E+00, 0.612700E+00}, + {-.510761E+00, 0.606710E+00}, {-.517289E+00, 0.600642E+00}, + {-.523829E+00, 0.594501E+00}, {-.530372E+00, 0.588292E+00}, + {-.536912E+00, 0.582021E+00}, {-.543441E+00, 0.575695E+00}, + {-.549953E+00, 0.569318E+00}, {-.556441E+00, 0.562898E+00}, + {-.562898E+00, 0.556441E+00}, {-.569318E+00, 0.549953E+00}, + {-.575695E+00, 0.543441E+00}, {-.582021E+00, 0.536912E+00}, + {-.588292E+00, 0.530372E+00}, {-.594501E+00, 0.523829E+00}, + {-.600642E+00, 0.517289E+00}, {-.606710E+00, 0.510761E+00}, + {-.612700E+00, 0.504250E+00}, {-.618605E+00, 0.497764E+00}, + {-.624421E+00, 0.491311E+00}, {-.630144E+00, 0.484898E+00}, + {-.635768E+00, 0.478532E+00}, {-.641288E+00, 0.472221E+00}, + {-.646700E+00, 0.465973E+00}, {-.652001E+00, 0.459795E+00}, + {-.657185E+00, 0.453694E+00}, {-.662250E+00, 0.447678E+00}, + {-.667191E+00, 0.441755E+00}, {-.672004E+00, 0.435931E+00}, + {-.676687E+00, 0.430215E+00}, {-.681236E+00, 0.424613E+00}, + {-.685649E+00, 0.419133E+00}, {-.689922E+00, 0.413781E+00}, + {-.694053E+00, 0.408565E+00}, {-.698039E+00, 0.403492E+00}, + {-.701877E+00, 0.398568E+00}, {-.705567E+00, 0.393800E+00}, + {-.709104E+00, 0.389195E+00}, {-.712489E+00, 0.384758E+00}, + {-.715718E+00, 0.380496E+00}, {-.718790E+00, 0.376415E+00}, + {-.721704E+00, 0.372520E+00}, {-.724458E+00, 0.368817E+00}, + {-.727051E+00, 0.365311E+00}, {-.729482E+00, 0.362007E+00}, + {-.731749E+00, 0.358910E+00}, {-.733852E+00, 0.356023E+00}, + {-.735790E+00, 0.353352E+00}, {-.737562E+00, 0.350900E+00}, + {-.739167E+00, 0.348670E+00}, {-.740604E+00, 0.346667E+00}, + {-.741874E+00, 0.344892E+00}, {-.742976E+00, 0.343348E+00}, + {-.743908E+00, 0.342038E+00}, {-.744672E+00, 0.340964E+00}, + {-.745266E+00, 0.340127E+00}, {-.745690E+00, 0.339528E+00}, + {-.745945E+00, 0.339168E+00}, {-.746030E+00, 0.339048E+00}, + }, + { + {-.330960E+00, 0.750042E+00}, {-.331084E+00, 0.749955E+00}, + {-.331456E+00, 0.749695E+00}, {-.332075E+00, 0.749260E+00}, + {-.332941E+00, 0.748652E+00}, {-.334052E+00, 0.747870E+00}, + {-.335406E+00, 0.746915E+00}, {-.337002E+00, 0.745787E+00}, + {-.338837E+00, 0.744487E+00}, {-.340908E+00, 0.743015E+00}, + {-.343213E+00, 0.741371E+00}, {-.345748E+00, 0.739556E+00}, + {-.348509E+00, 0.737572E+00}, {-.351492E+00, 0.735417E+00}, + {-.354694E+00, 0.733094E+00}, {-.358108E+00, 0.730604E+00}, + {-.361732E+00, 0.727947E+00}, {-.365558E+00, 0.725124E+00}, + {-.369583E+00, 0.722137E+00}, {-.373799E+00, 0.718988E+00}, + {-.378203E+00, 0.715677E+00}, {-.382786E+00, 0.712207E+00}, + {-.387543E+00, 0.708579E+00}, {-.392468E+00, 0.704795E+00}, + {-.397553E+00, 0.700857E+00}, {-.402792E+00, 0.696768E+00}, + {-.408178E+00, 0.692529E+00}, {-.413704E+00, 0.688144E+00}, + {-.419362E+00, 0.683615E+00}, {-.425145E+00, 0.678945E+00}, + {-.431045E+00, 0.674137E+00}, {-.437056E+00, 0.669194E+00}, + {-.443169E+00, 0.664120E+00}, {-.449377E+00, 0.658918E+00}, + {-.455671E+00, 0.653592E+00}, {-.462045E+00, 0.648146E+00}, + {-.468491E+00, 0.642584E+00}, {-.474999E+00, 0.636910E+00}, + {-.481564E+00, 0.631129E+00}, {-.488177E+00, 0.625246E+00}, + {-.494831E+00, 0.619265E+00}, {-.501517E+00, 0.613192E+00}, + {-.508229E+00, 0.607031E+00}, {-.514958E+00, 0.600789E+00}, + {-.521698E+00, 0.594471E+00}, {-.528440E+00, 0.588082E+00}, + {-.535178E+00, 0.581628E+00}, {-.541904E+00, 0.575116E+00}, + {-.548611E+00, 0.568552E+00}, {-.555293E+00, 0.561942E+00}, + {-.561942E+00, 0.555293E+00}, {-.568552E+00, 0.548611E+00}, + {-.575116E+00, 0.541904E+00}, {-.581628E+00, 0.535178E+00}, + {-.588082E+00, 0.528440E+00}, {-.594471E+00, 0.521698E+00}, + {-.600789E+00, 0.514958E+00}, {-.607031E+00, 0.508229E+00}, + {-.613192E+00, 0.501517E+00}, {-.619265E+00, 0.494831E+00}, + {-.625246E+00, 0.488177E+00}, {-.631129E+00, 0.481564E+00}, + {-.636910E+00, 0.474999E+00}, {-.642584E+00, 0.468491E+00}, + {-.648146E+00, 0.462045E+00}, {-.653592E+00, 0.455671E+00}, + {-.658918E+00, 0.449377E+00}, {-.664120E+00, 0.443169E+00}, + {-.669194E+00, 0.437056E+00}, {-.674137E+00, 0.431045E+00}, + {-.678945E+00, 0.425145E+00}, {-.683615E+00, 0.419362E+00}, + {-.688144E+00, 0.413704E+00}, {-.692529E+00, 0.408178E+00}, + {-.696768E+00, 0.402792E+00}, {-.700857E+00, 0.397553E+00}, + {-.704795E+00, 0.392468E+00}, {-.708579E+00, 0.387543E+00}, + {-.712207E+00, 0.382786E+00}, {-.715677E+00, 0.378203E+00}, + {-.718988E+00, 0.373799E+00}, {-.722137E+00, 0.369583E+00}, + {-.725124E+00, 0.365558E+00}, {-.727947E+00, 0.361732E+00}, + {-.730604E+00, 0.358108E+00}, {-.733094E+00, 0.354694E+00}, + {-.735417E+00, 0.351492E+00}, {-.737572E+00, 0.348509E+00}, + {-.739556E+00, 0.345748E+00}, {-.741371E+00, 0.343213E+00}, + {-.743015E+00, 0.340908E+00}, {-.744487E+00, 0.338837E+00}, + {-.745787E+00, 0.337002E+00}, {-.746915E+00, 0.335406E+00}, + {-.747870E+00, 0.334052E+00}, {-.748652E+00, 0.332941E+00}, + {-.749260E+00, 0.332075E+00}, {-.749695E+00, 0.331456E+00}, + {-.749955E+00, 0.331084E+00}, {-.750042E+00, 0.330960E+00}, + }, + { + {-.322736E+00, 0.754042E+00}, {-.322864E+00, 0.753953E+00}, + {-.323249E+00, 0.753686E+00}, {-.323889E+00, 0.753242E+00}, + {-.324783E+00, 0.752619E+00}, {-.325931E+00, 0.751820E+00}, + {-.327331E+00, 0.750842E+00}, {-.328980E+00, 0.749688E+00}, + {-.330876E+00, 0.748358E+00}, {-.333016E+00, 0.746851E+00}, + {-.335397E+00, 0.745168E+00}, {-.338016E+00, 0.743311E+00}, + {-.340868E+00, 0.741279E+00}, {-.343950E+00, 0.739073E+00}, + {-.347257E+00, 0.736695E+00}, {-.350784E+00, 0.734144E+00}, + {-.354526E+00, 0.731423E+00}, {-.358478E+00, 0.728532E+00}, + {-.362635E+00, 0.725472E+00}, {-.366989E+00, 0.722246E+00}, + {-.371536E+00, 0.718853E+00}, {-.376268E+00, 0.715297E+00}, + {-.381179E+00, 0.711579E+00}, {-.386263E+00, 0.707700E+00}, + {-.391512E+00, 0.703663E+00}, {-.396920E+00, 0.699470E+00}, + {-.402478E+00, 0.695123E+00}, {-.408180E+00, 0.690626E+00}, + {-.414019E+00, 0.685980E+00}, {-.419985E+00, 0.681188E+00}, + {-.426073E+00, 0.676255E+00}, {-.432273E+00, 0.671182E+00}, + {-.438578E+00, 0.665974E+00}, {-.444980E+00, 0.660633E+00}, + {-.451471E+00, 0.655165E+00}, {-.458043E+00, 0.649572E+00}, + {-.464688E+00, 0.643859E+00}, {-.471398E+00, 0.638031E+00}, + {-.478165E+00, 0.632092E+00}, {-.484980E+00, 0.626047E+00}, + {-.491837E+00, 0.619900E+00}, {-.498726E+00, 0.613658E+00}, + {-.505641E+00, 0.607325E+00}, {-.512573E+00, 0.600907E+00}, + {-.519514E+00, 0.594410E+00}, {-.526458E+00, 0.587839E+00}, + {-.533396E+00, 0.581201E+00}, {-.540321E+00, 0.574502E+00}, + {-.547226E+00, 0.567748E+00}, {-.554103E+00, 0.560946E+00}, + {-.560946E+00, 0.554103E+00}, {-.567748E+00, 0.547226E+00}, + {-.574502E+00, 0.540321E+00}, {-.581201E+00, 0.533396E+00}, + {-.587839E+00, 0.526458E+00}, {-.594410E+00, 0.519514E+00}, + {-.600907E+00, 0.512573E+00}, {-.607325E+00, 0.505641E+00}, + {-.613658E+00, 0.498726E+00}, {-.619900E+00, 0.491837E+00}, + {-.626047E+00, 0.484980E+00}, {-.632092E+00, 0.478165E+00}, + {-.638031E+00, 0.471398E+00}, {-.643859E+00, 0.464688E+00}, + {-.649572E+00, 0.458043E+00}, {-.655165E+00, 0.451471E+00}, + {-.660633E+00, 0.444980E+00}, {-.665974E+00, 0.438578E+00}, + {-.671182E+00, 0.432273E+00}, {-.676255E+00, 0.426073E+00}, + {-.681188E+00, 0.419985E+00}, {-.685980E+00, 0.414019E+00}, + {-.690626E+00, 0.408180E+00}, {-.695123E+00, 0.402478E+00}, + {-.699470E+00, 0.396920E+00}, {-.703663E+00, 0.391512E+00}, + {-.707700E+00, 0.386263E+00}, {-.711579E+00, 0.381179E+00}, + {-.715297E+00, 0.376268E+00}, {-.718853E+00, 0.371536E+00}, + {-.722246E+00, 0.366989E+00}, {-.725472E+00, 0.362635E+00}, + {-.728532E+00, 0.358478E+00}, {-.731423E+00, 0.354526E+00}, + {-.734144E+00, 0.350784E+00}, {-.736695E+00, 0.347257E+00}, + {-.739073E+00, 0.343950E+00}, {-.741279E+00, 0.340868E+00}, + {-.743311E+00, 0.338016E+00}, {-.745168E+00, 0.335397E+00}, + {-.746851E+00, 0.333016E+00}, {-.748358E+00, 0.330876E+00}, + {-.749688E+00, 0.328980E+00}, {-.750842E+00, 0.327331E+00}, + {-.751820E+00, 0.325931E+00}, {-.752619E+00, 0.324783E+00}, + {-.753242E+00, 0.323889E+00}, {-.753686E+00, 0.323249E+00}, + {-.753953E+00, 0.322864E+00}, {-.754042E+00, 0.322736E+00}, + }, + { + {-.314372E+00, 0.758030E+00}, {-.314504E+00, 0.757939E+00}, + {-.314901E+00, 0.757666E+00}, {-.315562E+00, 0.757212E+00}, + {-.316486E+00, 0.756575E+00}, {-.317672E+00, 0.755757E+00}, + {-.319117E+00, 0.754758E+00}, {-.320820E+00, 0.753578E+00}, + {-.322778E+00, 0.752217E+00}, {-.324988E+00, 0.750675E+00}, + {-.327447E+00, 0.748954E+00}, {-.330151E+00, 0.747054E+00}, + {-.333097E+00, 0.744975E+00}, {-.336279E+00, 0.742718E+00}, + {-.339693E+00, 0.740284E+00}, {-.343334E+00, 0.737674E+00}, + {-.347198E+00, 0.734888E+00}, {-.351277E+00, 0.731929E+00}, + {-.355567E+00, 0.728796E+00}, {-.360061E+00, 0.725492E+00}, + {-.364753E+00, 0.722018E+00}, {-.369637E+00, 0.718376E+00}, + {-.374705E+00, 0.714567E+00}, {-.379950E+00, 0.710593E+00}, + {-.385366E+00, 0.706456E+00}, {-.390944E+00, 0.702159E+00}, + {-.396678E+00, 0.697704E+00}, {-.402559E+00, 0.693094E+00}, + {-.408580E+00, 0.688330E+00}, {-.414733E+00, 0.683417E+00}, + {-.421010E+00, 0.678357E+00}, {-.427402E+00, 0.673154E+00}, + {-.433902E+00, 0.667810E+00}, {-.440502E+00, 0.662331E+00}, + {-.447192E+00, 0.656719E+00}, {-.453965E+00, 0.650979E+00}, + {-.460812E+00, 0.645114E+00}, {-.467725E+00, 0.639131E+00}, + {-.474696E+00, 0.633032E+00}, {-.481717E+00, 0.626824E+00}, + {-.488779E+00, 0.620510E+00}, {-.495874E+00, 0.614097E+00}, + {-.502994E+00, 0.607590E+00}, {-.510131E+00, 0.600995E+00}, + {-.517277E+00, 0.594317E+00}, {-.524424E+00, 0.587563E+00}, + {-.531565E+00, 0.580739E+00}, {-.538691E+00, 0.573851E+00}, + {-.545795E+00, 0.566906E+00}, {-.552871E+00, 0.559910E+00}, + {-.559910E+00, 0.552871E+00}, {-.566906E+00, 0.545795E+00}, + {-.573851E+00, 0.538691E+00}, {-.580739E+00, 0.531565E+00}, + {-.587563E+00, 0.524424E+00}, {-.594317E+00, 0.517277E+00}, + {-.600995E+00, 0.510131E+00}, {-.607590E+00, 0.502994E+00}, + {-.614097E+00, 0.495874E+00}, {-.620510E+00, 0.488779E+00}, + {-.626824E+00, 0.481717E+00}, {-.633032E+00, 0.474696E+00}, + {-.639131E+00, 0.467725E+00}, {-.645114E+00, 0.460812E+00}, + {-.650979E+00, 0.453965E+00}, {-.656719E+00, 0.447192E+00}, + {-.662331E+00, 0.440502E+00}, {-.667810E+00, 0.433902E+00}, + {-.673154E+00, 0.427402E+00}, {-.678357E+00, 0.421010E+00}, + {-.683417E+00, 0.414733E+00}, {-.688330E+00, 0.408580E+00}, + {-.693094E+00, 0.402559E+00}, {-.697704E+00, 0.396678E+00}, + {-.702159E+00, 0.390944E+00}, {-.706456E+00, 0.385366E+00}, + {-.710593E+00, 0.379950E+00}, {-.714567E+00, 0.374705E+00}, + {-.718376E+00, 0.369637E+00}, {-.722018E+00, 0.364753E+00}, + {-.725492E+00, 0.360061E+00}, {-.728796E+00, 0.355567E+00}, + {-.731929E+00, 0.351277E+00}, {-.734888E+00, 0.347198E+00}, + {-.737674E+00, 0.343334E+00}, {-.740284E+00, 0.339693E+00}, + {-.742718E+00, 0.336279E+00}, {-.744975E+00, 0.333097E+00}, + {-.747054E+00, 0.330151E+00}, {-.748954E+00, 0.327447E+00}, + {-.750675E+00, 0.324988E+00}, {-.752217E+00, 0.322778E+00}, + {-.753578E+00, 0.320820E+00}, {-.754758E+00, 0.319117E+00}, + {-.755757E+00, 0.317672E+00}, {-.756575E+00, 0.316486E+00}, + {-.757212E+00, 0.315562E+00}, {-.757666E+00, 0.314901E+00}, + {-.757939E+00, 0.314504E+00}, {-.758030E+00, 0.314372E+00}, + }, + { + {-.305862E+00, 0.762007E+00}, {-.305999E+00, 0.761914E+00}, + {-.306409E+00, 0.761635E+00}, {-.307091E+00, 0.761170E+00}, + {-.308045E+00, 0.760520E+00}, {-.309269E+00, 0.759684E+00}, + {-.310761E+00, 0.758662E+00}, {-.312519E+00, 0.757456E+00}, + {-.314540E+00, 0.756064E+00}, {-.316821E+00, 0.754489E+00}, + {-.319359E+00, 0.752729E+00}, {-.322150E+00, 0.750786E+00}, + {-.325190E+00, 0.748660E+00}, {-.328474E+00, 0.746352E+00}, + {-.331997E+00, 0.743862E+00}, {-.335755E+00, 0.741192E+00}, + {-.339741E+00, 0.738343E+00}, {-.343950E+00, 0.735314E+00}, + {-.348376E+00, 0.732109E+00}, {-.353012E+00, 0.728728E+00}, + {-.357852E+00, 0.725172E+00}, {-.362889E+00, 0.721443E+00}, + {-.368116E+00, 0.717543E+00}, {-.373526E+00, 0.713474E+00}, + {-.379110E+00, 0.709238E+00}, {-.384862E+00, 0.704836E+00}, + {-.390774E+00, 0.700272E+00}, {-.396837E+00, 0.695548E+00}, + {-.403044E+00, 0.690667E+00}, {-.409385E+00, 0.685631E+00}, + {-.415854E+00, 0.680444E+00}, {-.422442E+00, 0.675109E+00}, + {-.429139E+00, 0.669630E+00}, {-.435938E+00, 0.664011E+00}, + {-.442830E+00, 0.658254E+00}, {-.449807E+00, 0.652366E+00}, + {-.456859E+00, 0.646349E+00}, {-.463979E+00, 0.640208E+00}, + {-.471157E+00, 0.633949E+00}, {-.478385E+00, 0.627576E+00}, + {-.485655E+00, 0.621094E+00}, {-.492959E+00, 0.614509E+00}, + {-.500287E+00, 0.607827E+00}, {-.507631E+00, 0.601053E+00}, + {-.514984E+00, 0.594193E+00}, {-.522337E+00, 0.587253E+00}, + {-.529683E+00, 0.580241E+00}, {-.537012E+00, 0.573162E+00}, + {-.544319E+00, 0.566023E+00}, {-.551594E+00, 0.558832E+00}, + {-.558832E+00, 0.551594E+00}, {-.566023E+00, 0.544319E+00}, + {-.573162E+00, 0.537012E+00}, {-.580241E+00, 0.529683E+00}, + {-.587253E+00, 0.522337E+00}, {-.594193E+00, 0.514984E+00}, + {-.601053E+00, 0.507631E+00}, {-.607827E+00, 0.500287E+00}, + {-.614509E+00, 0.492959E+00}, {-.621094E+00, 0.485655E+00}, + {-.627576E+00, 0.478385E+00}, {-.633949E+00, 0.471157E+00}, + {-.640208E+00, 0.463979E+00}, {-.646349E+00, 0.456859E+00}, + {-.652366E+00, 0.449807E+00}, {-.658254E+00, 0.442830E+00}, + {-.664011E+00, 0.435938E+00}, {-.669630E+00, 0.429139E+00}, + {-.675109E+00, 0.422442E+00}, {-.680444E+00, 0.415854E+00}, + {-.685631E+00, 0.409385E+00}, {-.690667E+00, 0.403044E+00}, + {-.695548E+00, 0.396837E+00}, {-.700272E+00, 0.390774E+00}, + {-.704836E+00, 0.384862E+00}, {-.709238E+00, 0.379110E+00}, + {-.713474E+00, 0.373526E+00}, {-.717543E+00, 0.368116E+00}, + {-.721443E+00, 0.362889E+00}, {-.725172E+00, 0.357852E+00}, + {-.728728E+00, 0.353012E+00}, {-.732109E+00, 0.348376E+00}, + {-.735314E+00, 0.343950E+00}, {-.738343E+00, 0.339741E+00}, + {-.741192E+00, 0.335755E+00}, {-.743862E+00, 0.331997E+00}, + {-.746352E+00, 0.328474E+00}, {-.748660E+00, 0.325190E+00}, + {-.750786E+00, 0.322150E+00}, {-.752729E+00, 0.319359E+00}, + {-.754489E+00, 0.316821E+00}, {-.756064E+00, 0.314540E+00}, + {-.757456E+00, 0.312519E+00}, {-.758662E+00, 0.310761E+00}, + {-.759684E+00, 0.309269E+00}, {-.760520E+00, 0.308045E+00}, + {-.761170E+00, 0.307091E+00}, {-.761635E+00, 0.306409E+00}, + {-.761914E+00, 0.305999E+00}, {-.762007E+00, 0.305862E+00}, + }, + { + {-.297202E+00, 0.765973E+00}, {-.297343E+00, 0.765878E+00}, + {-.297766E+00, 0.765593E+00}, {-.298470E+00, 0.765118E+00}, + {-.299454E+00, 0.764454E+00}, {-.300717E+00, 0.763600E+00}, + {-.302257E+00, 0.762556E+00}, {-.304070E+00, 0.761324E+00}, + {-.306156E+00, 0.759902E+00}, {-.308509E+00, 0.758292E+00}, + {-.311128E+00, 0.756494E+00}, {-.314007E+00, 0.754508E+00}, + {-.317143E+00, 0.752335E+00}, {-.320530E+00, 0.749975E+00}, + {-.324165E+00, 0.747430E+00}, {-.328040E+00, 0.744701E+00}, + {-.332151E+00, 0.741787E+00}, {-.336492E+00, 0.738690E+00}, + {-.341056E+00, 0.735412E+00}, {-.345837E+00, 0.731953E+00}, + {-.350828E+00, 0.728315E+00}, {-.356021E+00, 0.724500E+00}, + {-.361409E+00, 0.720509E+00}, {-.366985E+00, 0.716344E+00}, + {-.372742E+00, 0.712007E+00}, {-.378670E+00, 0.707501E+00}, + {-.384762E+00, 0.702827E+00}, {-.391010E+00, 0.697989E+00}, + {-.397405E+00, 0.692989E+00}, {-.403938E+00, 0.687831E+00}, + {-.410602E+00, 0.682516E+00}, {-.417388E+00, 0.677049E+00}, + {-.424286E+00, 0.671434E+00}, {-.431287E+00, 0.665673E+00}, + {-.438384E+00, 0.659771E+00}, {-.445567E+00, 0.653733E+00}, + {-.452827E+00, 0.647562E+00}, {-.460156E+00, 0.641263E+00}, + {-.467544E+00, 0.634842E+00}, {-.474983E+00, 0.628303E+00}, + {-.482464E+00, 0.621651E+00}, {-.489978E+00, 0.614893E+00}, + {-.497516E+00, 0.608033E+00}, {-.505071E+00, 0.601079E+00}, + {-.512633E+00, 0.594035E+00}, {-.520195E+00, 0.586908E+00}, + {-.527748E+00, 0.579706E+00}, {-.535284E+00, 0.572434E+00}, + {-.542794E+00, 0.565099E+00}, {-.550272E+00, 0.557710E+00}, + {-.557710E+00, 0.550272E+00}, {-.565099E+00, 0.542794E+00}, + {-.572434E+00, 0.535284E+00}, {-.579706E+00, 0.527748E+00}, + {-.586908E+00, 0.520195E+00}, {-.594035E+00, 0.512633E+00}, + {-.601079E+00, 0.505071E+00}, {-.608033E+00, 0.497516E+00}, + {-.614893E+00, 0.489978E+00}, {-.621651E+00, 0.482464E+00}, + {-.628303E+00, 0.474983E+00}, {-.634842E+00, 0.467544E+00}, + {-.641263E+00, 0.460156E+00}, {-.647562E+00, 0.452827E+00}, + {-.653733E+00, 0.445567E+00}, {-.659771E+00, 0.438384E+00}, + {-.665673E+00, 0.431287E+00}, {-.671434E+00, 0.424286E+00}, + {-.677049E+00, 0.417388E+00}, {-.682516E+00, 0.410602E+00}, + {-.687831E+00, 0.403938E+00}, {-.692989E+00, 0.397405E+00}, + {-.697989E+00, 0.391010E+00}, {-.702827E+00, 0.384762E+00}, + {-.707501E+00, 0.378670E+00}, {-.712007E+00, 0.372742E+00}, + {-.716344E+00, 0.366985E+00}, {-.720509E+00, 0.361409E+00}, + {-.724500E+00, 0.356021E+00}, {-.728315E+00, 0.350828E+00}, + {-.731953E+00, 0.345837E+00}, {-.735412E+00, 0.341056E+00}, + {-.738690E+00, 0.336492E+00}, {-.741787E+00, 0.332151E+00}, + {-.744701E+00, 0.328040E+00}, {-.747430E+00, 0.324165E+00}, + {-.749975E+00, 0.320530E+00}, {-.752335E+00, 0.317143E+00}, + {-.754508E+00, 0.314007E+00}, {-.756494E+00, 0.311128E+00}, + {-.758292E+00, 0.308509E+00}, {-.759902E+00, 0.306156E+00}, + {-.761324E+00, 0.304070E+00}, {-.762556E+00, 0.302257E+00}, + {-.763600E+00, 0.300717E+00}, {-.764454E+00, 0.299454E+00}, + {-.765118E+00, 0.298470E+00}, {-.765593E+00, 0.297766E+00}, + {-.765878E+00, 0.297343E+00}, {-.765973E+00, 0.297202E+00}, + }, + { + {-.288386E+00, 0.769929E+00}, {-.288531E+00, 0.769832E+00}, + {-.288967E+00, 0.769541E+00}, {-.289694E+00, 0.769056E+00}, + {-.290709E+00, 0.768378E+00}, {-.292011E+00, 0.767506E+00}, + {-.293599E+00, 0.766440E+00}, {-.295470E+00, 0.765181E+00}, + {-.297620E+00, 0.763730E+00}, {-.300048E+00, 0.762085E+00}, + {-.302748E+00, 0.760249E+00}, {-.305717E+00, 0.758220E+00}, + {-.308951E+00, 0.756000E+00}, {-.312444E+00, 0.753590E+00}, + {-.316191E+00, 0.750989E+00}, {-.320186E+00, 0.748199E+00}, + {-.324425E+00, 0.745221E+00}, {-.328900E+00, 0.742056E+00}, + {-.333604E+00, 0.738704E+00}, {-.338532E+00, 0.735168E+00}, + {-.343675E+00, 0.731448E+00}, {-.349027E+00, 0.727546E+00}, + {-.354580E+00, 0.723463E+00}, {-.360325E+00, 0.719202E+00}, + {-.366256E+00, 0.714765E+00}, {-.372363E+00, 0.710154E+00}, + {-.378639E+00, 0.705370E+00}, {-.385074E+00, 0.700418E+00}, + {-.391660E+00, 0.695299E+00}, {-.398389E+00, 0.690016E+00}, + {-.405250E+00, 0.684573E+00}, {-.412236E+00, 0.678973E+00}, + {-.419338E+00, 0.673220E+00}, {-.426545E+00, 0.667317E+00}, + {-.433850E+00, 0.661269E+00}, {-.441242E+00, 0.655080E+00}, + {-.448713E+00, 0.648754E+00}, {-.456254E+00, 0.642296E+00}, + {-.463855E+00, 0.635711E+00}, {-.471507E+00, 0.629004E+00}, + {-.479201E+00, 0.622181E+00}, {-.486929E+00, 0.615248E+00}, + {-.494681E+00, 0.608209E+00}, {-.502449E+00, 0.601072E+00}, + {-.510223E+00, 0.593842E+00}, {-.517996E+00, 0.586527E+00}, + {-.525759E+00, 0.579132E+00}, {-.533503E+00, 0.571665E+00}, + {-.541220E+00, 0.564133E+00}, {-.548903E+00, 0.556543E+00}, + {-.556543E+00, 0.548903E+00}, {-.564133E+00, 0.541220E+00}, + {-.571665E+00, 0.533503E+00}, {-.579132E+00, 0.525759E+00}, + {-.586527E+00, 0.517996E+00}, {-.593842E+00, 0.510223E+00}, + {-.601072E+00, 0.502449E+00}, {-.608209E+00, 0.494681E+00}, + {-.615248E+00, 0.486929E+00}, {-.622181E+00, 0.479201E+00}, + {-.629004E+00, 0.471507E+00}, {-.635711E+00, 0.463855E+00}, + {-.642296E+00, 0.456254E+00}, {-.648754E+00, 0.448713E+00}, + {-.655080E+00, 0.441242E+00}, {-.661269E+00, 0.433850E+00}, + {-.667317E+00, 0.426545E+00}, {-.673220E+00, 0.419338E+00}, + {-.678973E+00, 0.412236E+00}, {-.684573E+00, 0.405250E+00}, + {-.690016E+00, 0.398389E+00}, {-.695299E+00, 0.391660E+00}, + {-.700418E+00, 0.385074E+00}, {-.705370E+00, 0.378639E+00}, + {-.710154E+00, 0.372363E+00}, {-.714765E+00, 0.366256E+00}, + {-.719202E+00, 0.360325E+00}, {-.723463E+00, 0.354580E+00}, + {-.727546E+00, 0.349027E+00}, {-.731448E+00, 0.343675E+00}, + {-.735168E+00, 0.338532E+00}, {-.738704E+00, 0.333604E+00}, + {-.742056E+00, 0.328900E+00}, {-.745221E+00, 0.324425E+00}, + {-.748199E+00, 0.320186E+00}, {-.750989E+00, 0.316191E+00}, + {-.753590E+00, 0.312444E+00}, {-.756000E+00, 0.308951E+00}, + {-.758220E+00, 0.305717E+00}, {-.760249E+00, 0.302748E+00}, + {-.762085E+00, 0.300048E+00}, {-.763730E+00, 0.297620E+00}, + {-.765181E+00, 0.295470E+00}, {-.766440E+00, 0.293599E+00}, + {-.767506E+00, 0.292011E+00}, {-.768378E+00, 0.290709E+00}, + {-.769056E+00, 0.289694E+00}, {-.769541E+00, 0.288967E+00}, + {-.769832E+00, 0.288531E+00}, {-.769929E+00, 0.288386E+00}, + }, + { + {-.279408E+00, 0.773876E+00}, {-.279557E+00, 0.773777E+00}, + {-.280007E+00, 0.773480E+00}, {-.280756E+00, 0.772985E+00}, + {-.281803E+00, 0.772293E+00}, {-.283146E+00, 0.771403E+00}, + {-.284783E+00, 0.770315E+00}, {-.286711E+00, 0.769030E+00}, + {-.288928E+00, 0.767548E+00}, {-.291431E+00, 0.765870E+00}, + {-.294214E+00, 0.763994E+00}, {-.297275E+00, 0.761923E+00}, + {-.300608E+00, 0.759657E+00}, {-.304208E+00, 0.757195E+00}, + {-.308070E+00, 0.754539E+00}, {-.312188E+00, 0.751689E+00}, + {-.316556E+00, 0.748647E+00}, {-.321167E+00, 0.745412E+00}, + {-.326015E+00, 0.741988E+00}, {-.331091E+00, 0.738373E+00}, + {-.336390E+00, 0.734571E+00}, {-.341903E+00, 0.730582E+00}, + {-.347623E+00, 0.726408E+00}, {-.353541E+00, 0.722050E+00}, + {-.359649E+00, 0.717512E+00}, {-.365938E+00, 0.712795E+00}, + {-.372400E+00, 0.707901E+00}, {-.379025E+00, 0.702833E+00}, + {-.385806E+00, 0.697594E+00}, {-.392732E+00, 0.692187E+00}, + {-.399795E+00, 0.686614E+00}, {-.406985E+00, 0.680881E+00}, + {-.414293E+00, 0.674989E+00}, {-.421709E+00, 0.668943E+00}, + {-.429224E+00, 0.662747E+00}, {-.436829E+00, 0.656406E+00}, + {-.444514E+00, 0.649923E+00}, {-.452269E+00, 0.643305E+00}, + {-.460086E+00, 0.636555E+00}, {-.467954E+00, 0.629679E+00}, + {-.475865E+00, 0.622683E+00}, {-.483810E+00, 0.615573E+00}, + {-.491778E+00, 0.608353E+00}, {-.499761E+00, 0.601032E+00}, + {-.507751E+00, 0.593614E+00}, {-.515738E+00, 0.586108E+00}, + {-.523713E+00, 0.578519E+00}, {-.531668E+00, 0.570855E+00}, + {-.539595E+00, 0.563122E+00}, {-.547485E+00, 0.555330E+00}, + {-.555330E+00, 0.547485E+00}, {-.563122E+00, 0.539595E+00}, + {-.570855E+00, 0.531668E+00}, {-.578519E+00, 0.523713E+00}, + {-.586108E+00, 0.515738E+00}, {-.593614E+00, 0.507751E+00}, + {-.601032E+00, 0.499761E+00}, {-.608353E+00, 0.491778E+00}, + {-.615573E+00, 0.483810E+00}, {-.622683E+00, 0.475865E+00}, + {-.629679E+00, 0.467954E+00}, {-.636555E+00, 0.460086E+00}, + {-.643305E+00, 0.452269E+00}, {-.649923E+00, 0.444514E+00}, + {-.656406E+00, 0.436829E+00}, {-.662747E+00, 0.429224E+00}, + {-.668943E+00, 0.421709E+00}, {-.674989E+00, 0.414293E+00}, + {-.680881E+00, 0.406985E+00}, {-.686614E+00, 0.399795E+00}, + {-.692187E+00, 0.392732E+00}, {-.697594E+00, 0.385806E+00}, + {-.702833E+00, 0.379025E+00}, {-.707901E+00, 0.372400E+00}, + {-.712795E+00, 0.365938E+00}, {-.717512E+00, 0.359649E+00}, + {-.722050E+00, 0.353541E+00}, {-.726408E+00, 0.347623E+00}, + {-.730582E+00, 0.341903E+00}, {-.734571E+00, 0.336390E+00}, + {-.738373E+00, 0.331091E+00}, {-.741988E+00, 0.326015E+00}, + {-.745412E+00, 0.321167E+00}, {-.748647E+00, 0.316556E+00}, + {-.751689E+00, 0.312188E+00}, {-.754539E+00, 0.308070E+00}, + {-.757195E+00, 0.304208E+00}, {-.759657E+00, 0.300608E+00}, + {-.761923E+00, 0.297275E+00}, {-.763994E+00, 0.294214E+00}, + {-.765870E+00, 0.291431E+00}, {-.767548E+00, 0.288928E+00}, + {-.769030E+00, 0.286711E+00}, {-.770315E+00, 0.284783E+00}, + {-.771403E+00, 0.283146E+00}, {-.772293E+00, 0.281803E+00}, + {-.772985E+00, 0.280756E+00}, {-.773480E+00, 0.280007E+00}, + {-.773777E+00, 0.279557E+00}, {-.773876E+00, 0.279408E+00}, + }, + { + {-.270262E+00, 0.777814E+00}, {-.270416E+00, 0.777713E+00}, + {-.270880E+00, 0.777410E+00}, {-.271652E+00, 0.776905E+00}, + {-.272730E+00, 0.776199E+00}, {-.274115E+00, 0.775291E+00}, + {-.275802E+00, 0.774181E+00}, {-.277789E+00, 0.772871E+00}, + {-.280074E+00, 0.771359E+00}, {-.282653E+00, 0.769646E+00}, + {-.285522E+00, 0.767732E+00}, {-.288675E+00, 0.765618E+00}, + {-.292110E+00, 0.763304E+00}, {-.295819E+00, 0.760792E+00}, + {-.299798E+00, 0.758080E+00}, {-.304040E+00, 0.755170E+00}, + {-.308540E+00, 0.752063E+00}, {-.313289E+00, 0.748760E+00}, + {-.318282E+00, 0.745262E+00}, {-.323511E+00, 0.741570E+00}, + {-.328968E+00, 0.737684E+00}, {-.334645E+00, 0.733608E+00}, + {-.340534E+00, 0.729342E+00}, {-.346627E+00, 0.724888E+00}, + {-.352915E+00, 0.720248E+00}, {-.359389E+00, 0.715424E+00}, + {-.366041E+00, 0.710419E+00}, {-.372860E+00, 0.705236E+00}, + {-.379838E+00, 0.699876E+00}, {-.386965E+00, 0.694343E+00}, + {-.394232E+00, 0.688641E+00}, {-.401629E+00, 0.682772E+00}, + {-.409146E+00, 0.676741E+00}, {-.416774E+00, 0.670550E+00}, + {-.424504E+00, 0.664206E+00}, {-.432324E+00, 0.657711E+00}, + {-.440226E+00, 0.651071E+00}, {-.448200E+00, 0.644290E+00}, + {-.456235E+00, 0.637373E+00}, {-.464323E+00, 0.630327E+00}, + {-.472454E+00, 0.623156E+00}, {-.480618E+00, 0.615867E+00}, + {-.488805E+00, 0.608465E+00}, {-.497007E+00, 0.600957E+00}, + {-.505214E+00, 0.593350E+00}, {-.513418E+00, 0.585650E+00}, + {-.521608E+00, 0.577864E+00}, {-.529777E+00, 0.570001E+00}, + {-.537916E+00, 0.562066E+00}, {-.546016E+00, 0.554068E+00}, + {-.554068E+00, 0.546016E+00}, {-.562066E+00, 0.537916E+00}, + {-.570001E+00, 0.529777E+00}, {-.577864E+00, 0.521608E+00}, + {-.585650E+00, 0.513418E+00}, {-.593350E+00, 0.505214E+00}, + {-.600957E+00, 0.497007E+00}, {-.608465E+00, 0.488805E+00}, + {-.615867E+00, 0.480618E+00}, {-.623156E+00, 0.472454E+00}, + {-.630327E+00, 0.464323E+00}, {-.637373E+00, 0.456235E+00}, + {-.644290E+00, 0.448200E+00}, {-.651071E+00, 0.440226E+00}, + {-.657711E+00, 0.432324E+00}, {-.664206E+00, 0.424504E+00}, + {-.670550E+00, 0.416774E+00}, {-.676741E+00, 0.409146E+00}, + {-.682772E+00, 0.401629E+00}, {-.688641E+00, 0.394232E+00}, + {-.694343E+00, 0.386965E+00}, {-.699876E+00, 0.379838E+00}, + {-.705236E+00, 0.372860E+00}, {-.710419E+00, 0.366041E+00}, + {-.715424E+00, 0.359389E+00}, {-.720248E+00, 0.352915E+00}, + {-.724888E+00, 0.346627E+00}, {-.729342E+00, 0.340534E+00}, + {-.733608E+00, 0.334645E+00}, {-.737684E+00, 0.328968E+00}, + {-.741570E+00, 0.323511E+00}, {-.745262E+00, 0.318282E+00}, + {-.748760E+00, 0.313289E+00}, {-.752063E+00, 0.308540E+00}, + {-.755170E+00, 0.304040E+00}, {-.758080E+00, 0.299798E+00}, + {-.760792E+00, 0.295819E+00}, {-.763304E+00, 0.292110E+00}, + {-.765618E+00, 0.288675E+00}, {-.767732E+00, 0.285522E+00}, + {-.769646E+00, 0.282653E+00}, {-.771359E+00, 0.280074E+00}, + {-.772871E+00, 0.277789E+00}, {-.774181E+00, 0.275802E+00}, + {-.775291E+00, 0.274115E+00}, {-.776199E+00, 0.272730E+00}, + {-.776905E+00, 0.271652E+00}, {-.777410E+00, 0.270880E+00}, + {-.777713E+00, 0.270416E+00}, {-.777814E+00, 0.270262E+00}, + }, + { + {-.260942E+00, 0.781744E+00}, {-.261101E+00, 0.781641E+00}, + {-.261579E+00, 0.781332E+00}, {-.262374E+00, 0.780818E+00}, + {-.263486E+00, 0.780097E+00}, {-.264912E+00, 0.779171E+00}, + {-.266650E+00, 0.778040E+00}, {-.268698E+00, 0.776703E+00}, + {-.271052E+00, 0.775161E+00}, {-.273708E+00, 0.773414E+00}, + {-.276663E+00, 0.771462E+00}, {-.279912E+00, 0.769305E+00}, + {-.283449E+00, 0.766945E+00}, {-.287270E+00, 0.764380E+00}, + {-.291368E+00, 0.761613E+00}, {-.295737E+00, 0.758644E+00}, + {-.300370E+00, 0.755472E+00}, {-.305261E+00, 0.752100E+00}, + {-.310402E+00, 0.748528E+00}, {-.315785E+00, 0.744757E+00}, + {-.321403E+00, 0.740789E+00}, {-.327247E+00, 0.736625E+00}, + {-.333309E+00, 0.732266E+00}, {-.339580E+00, 0.727715E+00}, + {-.346051E+00, 0.722973E+00}, {-.352713E+00, 0.718043E+00}, + {-.359557E+00, 0.712926E+00}, {-.366573E+00, 0.707626E+00}, + {-.373752E+00, 0.702144E+00}, {-.381083E+00, 0.696485E+00}, + {-.388557E+00, 0.690652E+00}, {-.396165E+00, 0.684647E+00}, + {-.403895E+00, 0.678475E+00}, {-.411738E+00, 0.672139E+00}, + {-.419685E+00, 0.665644E+00}, {-.427724E+00, 0.658995E+00}, + {-.435846E+00, 0.652195E+00}, {-.444041E+00, 0.645250E+00}, + {-.452299E+00, 0.638166E+00}, {-.460610E+00, 0.630947E+00}, + {-.468963E+00, 0.623600E+00}, {-.477350E+00, 0.616129E+00}, + {-.485759E+00, 0.608543E+00}, {-.494183E+00, 0.600847E+00}, + {-.502611E+00, 0.593047E+00}, {-.511034E+00, 0.585152E+00}, + {-.519443E+00, 0.577168E+00}, {-.527828E+00, 0.569102E+00}, + {-.536182E+00, 0.560962E+00}, {-.544494E+00, 0.552757E+00}, + {-.552757E+00, 0.544494E+00}, {-.560962E+00, 0.536182E+00}, + {-.569102E+00, 0.527828E+00}, {-.577168E+00, 0.519443E+00}, + {-.585152E+00, 0.511034E+00}, {-.593047E+00, 0.502611E+00}, + {-.600847E+00, 0.494183E+00}, {-.608543E+00, 0.485759E+00}, + {-.616129E+00, 0.477350E+00}, {-.623600E+00, 0.468963E+00}, + {-.630947E+00, 0.460610E+00}, {-.638166E+00, 0.452299E+00}, + {-.645250E+00, 0.444041E+00}, {-.652195E+00, 0.435846E+00}, + {-.658995E+00, 0.427724E+00}, {-.665644E+00, 0.419685E+00}, + {-.672139E+00, 0.411738E+00}, {-.678475E+00, 0.403895E+00}, + {-.684647E+00, 0.396165E+00}, {-.690652E+00, 0.388557E+00}, + {-.696485E+00, 0.381083E+00}, {-.702144E+00, 0.373752E+00}, + {-.707626E+00, 0.366573E+00}, {-.712926E+00, 0.359557E+00}, + {-.718043E+00, 0.352713E+00}, {-.722973E+00, 0.346051E+00}, + {-.727715E+00, 0.339580E+00}, {-.732266E+00, 0.333309E+00}, + {-.736625E+00, 0.327247E+00}, {-.740789E+00, 0.321403E+00}, + {-.744757E+00, 0.315785E+00}, {-.748528E+00, 0.310402E+00}, + {-.752100E+00, 0.305261E+00}, {-.755472E+00, 0.300370E+00}, + {-.758644E+00, 0.295737E+00}, {-.761613E+00, 0.291368E+00}, + {-.764380E+00, 0.287270E+00}, {-.766945E+00, 0.283449E+00}, + {-.769305E+00, 0.279912E+00}, {-.771462E+00, 0.276663E+00}, + {-.773414E+00, 0.273708E+00}, {-.775161E+00, 0.271052E+00}, + {-.776703E+00, 0.268698E+00}, {-.778040E+00, 0.266650E+00}, + {-.779171E+00, 0.264912E+00}, {-.780097E+00, 0.263486E+00}, + {-.780818E+00, 0.262374E+00}, {-.781332E+00, 0.261579E+00}, + {-.781641E+00, 0.261101E+00}, {-.781744E+00, 0.260942E+00}, + }, + { + {-.251442E+00, 0.785666E+00}, {-.251606E+00, 0.785561E+00}, + {-.252098E+00, 0.785247E+00}, {-.252917E+00, 0.784722E+00}, + {-.254062E+00, 0.783988E+00}, {-.255530E+00, 0.783045E+00}, + {-.257321E+00, 0.781891E+00}, {-.259430E+00, 0.780528E+00}, + {-.261854E+00, 0.778956E+00}, {-.264590E+00, 0.777175E+00}, + {-.267633E+00, 0.775184E+00}, {-.270978E+00, 0.772985E+00}, + {-.274621E+00, 0.770578E+00}, {-.278555E+00, 0.767962E+00}, + {-.282774E+00, 0.765139E+00}, {-.287272E+00, 0.762109E+00}, + {-.292042E+00, 0.758873E+00}, {-.297076E+00, 0.755432E+00}, + {-.302368E+00, 0.751786E+00}, {-.307909E+00, 0.747937E+00}, + {-.313690E+00, 0.743885E+00}, {-.319704E+00, 0.739633E+00}, + {-.325942E+00, 0.735182E+00}, {-.332394E+00, 0.730533E+00}, + {-.339051E+00, 0.725688E+00}, {-.345905E+00, 0.720650E+00}, + {-.352944E+00, 0.715421E+00}, {-.360160E+00, 0.710003E+00}, + {-.367543E+00, 0.704399E+00}, {-.375082E+00, 0.698613E+00}, + {-.382767E+00, 0.692647E+00}, {-.390588E+00, 0.686505E+00}, + {-.398535E+00, 0.680191E+00}, {-.406597E+00, 0.673709E+00}, + {-.414764E+00, 0.667063E+00}, {-.423025E+00, 0.660257E+00}, + {-.431371E+00, 0.653296E+00}, {-.439791E+00, 0.646186E+00}, + {-.448274E+00, 0.638932E+00}, {-.456811E+00, 0.631538E+00}, + {-.465390E+00, 0.624012E+00}, {-.474003E+00, 0.616359E+00}, + {-.482638E+00, 0.608586E+00}, {-.491287E+00, 0.600699E+00}, + {-.499939E+00, 0.592706E+00}, {-.508584E+00, 0.584612E+00}, + {-.517214E+00, 0.576427E+00}, {-.525819E+00, 0.568157E+00}, + {-.534390E+00, 0.559810E+00}, {-.542918E+00, 0.551394E+00}, + {-.551394E+00, 0.542918E+00}, {-.559810E+00, 0.534390E+00}, + {-.568157E+00, 0.525819E+00}, {-.576427E+00, 0.517214E+00}, + {-.584612E+00, 0.508584E+00}, {-.592706E+00, 0.499939E+00}, + {-.600699E+00, 0.491287E+00}, {-.608586E+00, 0.482638E+00}, + {-.616359E+00, 0.474003E+00}, {-.624012E+00, 0.465390E+00}, + {-.631538E+00, 0.456811E+00}, {-.638932E+00, 0.448274E+00}, + {-.646186E+00, 0.439791E+00}, {-.653296E+00, 0.431371E+00}, + {-.660257E+00, 0.423025E+00}, {-.667063E+00, 0.414764E+00}, + {-.673709E+00, 0.406597E+00}, {-.680191E+00, 0.398535E+00}, + {-.686505E+00, 0.390588E+00}, {-.692647E+00, 0.382767E+00}, + {-.698613E+00, 0.375082E+00}, {-.704399E+00, 0.367543E+00}, + {-.710003E+00, 0.360160E+00}, {-.715421E+00, 0.352944E+00}, + {-.720650E+00, 0.345905E+00}, {-.725688E+00, 0.339051E+00}, + {-.730533E+00, 0.332394E+00}, {-.735182E+00, 0.325942E+00}, + {-.739633E+00, 0.319704E+00}, {-.743885E+00, 0.313690E+00}, + {-.747937E+00, 0.307909E+00}, {-.751786E+00, 0.302368E+00}, + {-.755432E+00, 0.297076E+00}, {-.758873E+00, 0.292042E+00}, + {-.762109E+00, 0.287272E+00}, {-.765139E+00, 0.282774E+00}, + {-.767962E+00, 0.278555E+00}, {-.770578E+00, 0.274621E+00}, + {-.772985E+00, 0.270978E+00}, {-.775184E+00, 0.267633E+00}, + {-.777175E+00, 0.264590E+00}, {-.778956E+00, 0.261854E+00}, + {-.780528E+00, 0.259430E+00}, {-.781891E+00, 0.257321E+00}, + {-.783045E+00, 0.255530E+00}, {-.783988E+00, 0.254062E+00}, + {-.784722E+00, 0.252917E+00}, {-.785247E+00, 0.252098E+00}, + {-.785561E+00, 0.251606E+00}, {-.785666E+00, 0.251442E+00}, + }, + { + {-.241754E+00, 0.789582E+00}, {-.241923E+00, 0.789475E+00}, + {-.242430E+00, 0.789155E+00}, {-.243273E+00, 0.788621E+00}, + {-.244452E+00, 0.787873E+00}, {-.245964E+00, 0.786911E+00}, + {-.247808E+00, 0.785736E+00}, {-.249979E+00, 0.784347E+00}, + {-.252475E+00, 0.782745E+00}, {-.255292E+00, 0.780929E+00}, + {-.258425E+00, 0.778900E+00}, {-.261868E+00, 0.776658E+00}, + {-.265618E+00, 0.774204E+00}, {-.269667E+00, 0.771537E+00}, + {-.274010E+00, 0.768658E+00}, {-.278639E+00, 0.765568E+00}, + {-.283548E+00, 0.762267E+00}, {-.288729E+00, 0.758757E+00}, + {-.294174E+00, 0.755037E+00}, {-.299875E+00, 0.751108E+00}, + {-.305824E+00, 0.746973E+00}, {-.312011E+00, 0.742633E+00}, + {-.318427E+00, 0.738088E+00}, {-.325064E+00, 0.733340E+00}, + {-.331911E+00, 0.728393E+00}, {-.338959E+00, 0.723246E+00}, + {-.346197E+00, 0.717904E+00}, {-.353617E+00, 0.712368E+00}, + {-.361207E+00, 0.706641E+00}, {-.368956E+00, 0.700726E+00}, + {-.376856E+00, 0.694627E+00}, {-.384894E+00, 0.688347E+00}, + {-.393061E+00, 0.681890E+00}, {-.401345E+00, 0.675259E+00}, + {-.409737E+00, 0.668460E+00}, {-.418224E+00, 0.661496E+00}, + {-.426797E+00, 0.654373E+00}, {-.435445E+00, 0.647096E+00}, + {-.444157E+00, 0.639669E+00}, {-.452923E+00, 0.632100E+00}, + {-.461732E+00, 0.624393E+00}, {-.470574E+00, 0.616556E+00}, + {-.479438E+00, 0.608594E+00}, {-.488315E+00, 0.600514E+00}, + {-.497194E+00, 0.592323E+00}, {-.506066E+00, 0.584030E+00}, + {-.514920E+00, 0.575640E+00}, {-.523748E+00, 0.567163E+00}, + {-.532539E+00, 0.558606E+00}, {-.541285E+00, 0.549977E+00}, + {-.549977E+00, 0.541285E+00}, {-.558606E+00, 0.532539E+00}, + {-.567163E+00, 0.523748E+00}, {-.575640E+00, 0.514920E+00}, + {-.584030E+00, 0.506066E+00}, {-.592323E+00, 0.497194E+00}, + {-.600514E+00, 0.488315E+00}, {-.608594E+00, 0.479438E+00}, + {-.616556E+00, 0.470574E+00}, {-.624393E+00, 0.461732E+00}, + {-.632100E+00, 0.452923E+00}, {-.639669E+00, 0.444157E+00}, + {-.647096E+00, 0.435445E+00}, {-.654373E+00, 0.426797E+00}, + {-.661496E+00, 0.418224E+00}, {-.668460E+00, 0.409737E+00}, + {-.675259E+00, 0.401345E+00}, {-.681890E+00, 0.393061E+00}, + {-.688347E+00, 0.384894E+00}, {-.694627E+00, 0.376856E+00}, + {-.700726E+00, 0.368956E+00}, {-.706641E+00, 0.361207E+00}, + {-.712368E+00, 0.353617E+00}, {-.717904E+00, 0.346197E+00}, + {-.723246E+00, 0.338959E+00}, {-.728393E+00, 0.331911E+00}, + {-.733340E+00, 0.325064E+00}, {-.738088E+00, 0.318427E+00}, + {-.742633E+00, 0.312011E+00}, {-.746973E+00, 0.305824E+00}, + {-.751108E+00, 0.299875E+00}, {-.755037E+00, 0.294174E+00}, + {-.758757E+00, 0.288729E+00}, {-.762267E+00, 0.283548E+00}, + {-.765568E+00, 0.278639E+00}, {-.768658E+00, 0.274010E+00}, + {-.771537E+00, 0.269667E+00}, {-.774204E+00, 0.265618E+00}, + {-.776658E+00, 0.261868E+00}, {-.778900E+00, 0.258425E+00}, + {-.780929E+00, 0.255292E+00}, {-.782745E+00, 0.252475E+00}, + {-.784347E+00, 0.249979E+00}, {-.785736E+00, 0.247808E+00}, + {-.786911E+00, 0.245964E+00}, {-.787873E+00, 0.244452E+00}, + {-.788621E+00, 0.243273E+00}, {-.789155E+00, 0.242430E+00}, + {-.789475E+00, 0.241923E+00}, {-.789582E+00, 0.241754E+00}, + }, + { + {-.231872E+00, 0.793492E+00}, {-.232046E+00, 0.793383E+00}, + {-.232567E+00, 0.793057E+00}, {-.233435E+00, 0.792513E+00}, + {-.234649E+00, 0.791751E+00}, {-.236206E+00, 0.790772E+00}, + {-.238103E+00, 0.789574E+00}, {-.240338E+00, 0.788160E+00}, + {-.242908E+00, 0.786527E+00}, {-.245807E+00, 0.784678E+00}, + {-.249031E+00, 0.782610E+00}, {-.252575E+00, 0.780326E+00}, + {-.256434E+00, 0.777824E+00}, {-.260601E+00, 0.775106E+00}, + {-.265070E+00, 0.772172E+00}, {-.269833E+00, 0.769021E+00}, + {-.274883E+00, 0.765655E+00}, {-.280214E+00, 0.762075E+00}, + {-.285815E+00, 0.758280E+00}, {-.291679E+00, 0.754273E+00}, + {-.297797E+00, 0.750054E+00}, {-.304160E+00, 0.745624E+00}, + {-.310759E+00, 0.740985E+00}, {-.317583E+00, 0.736139E+00}, + {-.324623E+00, 0.731087E+00}, {-.331869E+00, 0.725832E+00}, + {-.339311E+00, 0.720376E+00}, {-.346937E+00, 0.714720E+00}, + {-.354738E+00, 0.708869E+00}, {-.362702E+00, 0.702825E+00}, + {-.370820E+00, 0.696592E+00}, {-.379079E+00, 0.690172E+00}, + {-.387469E+00, 0.683570E+00}, {-.395980E+00, 0.676790E+00}, + {-.404599E+00, 0.669836E+00}, {-.413316E+00, 0.662713E+00}, + {-.422120E+00, 0.655425E+00}, {-.430999E+00, 0.647979E+00}, + {-.439944E+00, 0.640379E+00}, {-.448943E+00, 0.632631E+00}, + {-.457985E+00, 0.624742E+00}, {-.467060E+00, 0.616717E+00}, + {-.476156E+00, 0.608563E+00}, {-.485265E+00, 0.600288E+00}, + {-.494374E+00, 0.591899E+00}, {-.503475E+00, 0.583402E+00}, + {-.512557E+00, 0.574806E+00}, {-.521611E+00, 0.566119E+00}, + {-.530626E+00, 0.557349E+00}, {-.539593E+00, 0.548504E+00}, + {-.548504E+00, 0.539593E+00}, {-.557349E+00, 0.530626E+00}, + {-.566119E+00, 0.521611E+00}, {-.574806E+00, 0.512557E+00}, + {-.583402E+00, 0.503475E+00}, {-.591899E+00, 0.494374E+00}, + {-.600288E+00, 0.485265E+00}, {-.608563E+00, 0.476156E+00}, + {-.616717E+00, 0.467060E+00}, {-.624742E+00, 0.457985E+00}, + {-.632631E+00, 0.448943E+00}, {-.640379E+00, 0.439944E+00}, + {-.647979E+00, 0.430999E+00}, {-.655425E+00, 0.422120E+00}, + {-.662713E+00, 0.413316E+00}, {-.669836E+00, 0.404599E+00}, + {-.676790E+00, 0.395980E+00}, {-.683570E+00, 0.387469E+00}, + {-.690172E+00, 0.379079E+00}, {-.696592E+00, 0.370820E+00}, + {-.702825E+00, 0.362702E+00}, {-.708869E+00, 0.354738E+00}, + {-.714720E+00, 0.346937E+00}, {-.720376E+00, 0.339311E+00}, + {-.725832E+00, 0.331869E+00}, {-.731087E+00, 0.324623E+00}, + {-.736139E+00, 0.317583E+00}, {-.740985E+00, 0.310759E+00}, + {-.745624E+00, 0.304160E+00}, {-.750054E+00, 0.297797E+00}, + {-.754273E+00, 0.291679E+00}, {-.758280E+00, 0.285815E+00}, + {-.762075E+00, 0.280214E+00}, {-.765655E+00, 0.274883E+00}, + {-.769021E+00, 0.269833E+00}, {-.772172E+00, 0.265070E+00}, + {-.775106E+00, 0.260601E+00}, {-.777824E+00, 0.256434E+00}, + {-.780326E+00, 0.252575E+00}, {-.782610E+00, 0.249031E+00}, + {-.784678E+00, 0.245807E+00}, {-.786527E+00, 0.242908E+00}, + {-.788160E+00, 0.240338E+00}, {-.789574E+00, 0.238103E+00}, + {-.790772E+00, 0.236206E+00}, {-.791751E+00, 0.234649E+00}, + {-.792513E+00, 0.233435E+00}, {-.793057E+00, 0.232567E+00}, + {-.793383E+00, 0.232046E+00}, {-.793492E+00, 0.231872E+00}, + }, + { + {-.221787E+00, 0.797397E+00}, {-.221966E+00, 0.797286E+00}, + {-.222503E+00, 0.796954E+00}, {-.223396E+00, 0.796400E+00}, + {-.224645E+00, 0.795624E+00}, {-.226247E+00, 0.794627E+00}, + {-.228200E+00, 0.793408E+00}, {-.230500E+00, 0.791967E+00}, + {-.233144E+00, 0.790305E+00}, {-.236127E+00, 0.788421E+00}, + {-.239445E+00, 0.786315E+00}, {-.243091E+00, 0.783988E+00}, + {-.247062E+00, 0.781439E+00}, {-.251348E+00, 0.778670E+00}, + {-.255945E+00, 0.775679E+00}, {-.260845E+00, 0.772468E+00}, + {-.266040E+00, 0.769037E+00}, {-.271522E+00, 0.765386E+00}, + {-.277283E+00, 0.761517E+00}, {-.283314E+00, 0.757430E+00}, + {-.289605E+00, 0.753127E+00}, {-.296147E+00, 0.748608E+00}, + {-.302931E+00, 0.743874E+00}, {-.309947E+00, 0.738929E+00}, + {-.317184E+00, 0.733772E+00}, {-.324631E+00, 0.728407E+00}, + {-.332279E+00, 0.722836E+00}, {-.340116E+00, 0.717060E+00}, + {-.348132E+00, 0.711084E+00}, {-.356314E+00, 0.704909E+00}, + {-.364654E+00, 0.698540E+00}, {-.373138E+00, 0.691980E+00}, + {-.381755E+00, 0.685231E+00}, {-.390495E+00, 0.678300E+00}, + {-.399346E+00, 0.671190E+00}, {-.408296E+00, 0.663905E+00}, + {-.417335E+00, 0.656452E+00}, {-.426450E+00, 0.648834E+00}, + {-.435631E+00, 0.641058E+00}, {-.444867E+00, 0.633130E+00}, + {-.454146E+00, 0.625056E+00}, {-.463457E+00, 0.616842E+00}, + {-.472789E+00, 0.608495E+00}, {-.482133E+00, 0.600022E+00}, + {-.491477E+00, 0.591430E+00}, {-.500810E+00, 0.582728E+00}, + {-.510123E+00, 0.573923E+00}, {-.519406E+00, 0.565023E+00}, + {-.528648E+00, 0.556036E+00}, {-.537840E+00, 0.546972E+00}, + {-.546972E+00, 0.537840E+00}, {-.556036E+00, 0.528648E+00}, + {-.565023E+00, 0.519406E+00}, {-.573923E+00, 0.510123E+00}, + {-.582728E+00, 0.500810E+00}, {-.591430E+00, 0.491477E+00}, + {-.600022E+00, 0.482133E+00}, {-.608495E+00, 0.472789E+00}, + {-.616842E+00, 0.463457E+00}, {-.625056E+00, 0.454146E+00}, + {-.633130E+00, 0.444867E+00}, {-.641058E+00, 0.435631E+00}, + {-.648834E+00, 0.426450E+00}, {-.656452E+00, 0.417335E+00}, + {-.663905E+00, 0.408296E+00}, {-.671190E+00, 0.399346E+00}, + {-.678300E+00, 0.390495E+00}, {-.685231E+00, 0.381755E+00}, + {-.691980E+00, 0.373138E+00}, {-.698540E+00, 0.364654E+00}, + {-.704909E+00, 0.356314E+00}, {-.711084E+00, 0.348132E+00}, + {-.717060E+00, 0.340116E+00}, {-.722836E+00, 0.332279E+00}, + {-.728407E+00, 0.324631E+00}, {-.733772E+00, 0.317184E+00}, + {-.738929E+00, 0.309947E+00}, {-.743874E+00, 0.302931E+00}, + {-.748608E+00, 0.296147E+00}, {-.753127E+00, 0.289605E+00}, + {-.757430E+00, 0.283314E+00}, {-.761517E+00, 0.277283E+00}, + {-.765386E+00, 0.271522E+00}, {-.769037E+00, 0.266040E+00}, + {-.772468E+00, 0.260845E+00}, {-.775679E+00, 0.255945E+00}, + {-.778670E+00, 0.251348E+00}, {-.781439E+00, 0.247062E+00}, + {-.783988E+00, 0.243091E+00}, {-.786315E+00, 0.239445E+00}, + {-.788421E+00, 0.236127E+00}, {-.790305E+00, 0.233144E+00}, + {-.791967E+00, 0.230500E+00}, {-.793408E+00, 0.228200E+00}, + {-.794627E+00, 0.226247E+00}, {-.795624E+00, 0.224645E+00}, + {-.796400E+00, 0.223396E+00}, {-.796954E+00, 0.222503E+00}, + {-.797286E+00, 0.221966E+00}, {-.797397E+00, 0.221787E+00}, + }, + { + {-.211491E+00, 0.801297E+00}, {-.211676E+00, 0.801184E+00}, + {-.212228E+00, 0.800846E+00}, {-.213147E+00, 0.800282E+00}, + {-.214432E+00, 0.799493E+00}, {-.216081E+00, 0.798478E+00}, + {-.218090E+00, 0.797237E+00}, {-.220456E+00, 0.795770E+00}, + {-.223176E+00, 0.794078E+00}, {-.226245E+00, 0.792160E+00}, + {-.229658E+00, 0.790016E+00}, {-.233409E+00, 0.787646E+00}, + {-.237493E+00, 0.785050E+00}, {-.241902E+00, 0.782228E+00}, + {-.246630E+00, 0.779182E+00}, {-.251669E+00, 0.775910E+00}, + {-.257012E+00, 0.772413E+00}, {-.262649E+00, 0.768692E+00}, + {-.268572E+00, 0.764748E+00}, {-.274772E+00, 0.760581E+00}, + {-.281240E+00, 0.756193E+00}, {-.287965E+00, 0.751584E+00}, + {-.294938E+00, 0.746755E+00}, {-.302148E+00, 0.741709E+00}, + {-.309585E+00, 0.736448E+00}, {-.317238E+00, 0.730972E+00}, + {-.325096E+00, 0.725285E+00}, {-.333147E+00, 0.719388E+00}, + {-.341382E+00, 0.713285E+00}, {-.349787E+00, 0.706979E+00}, + {-.358352E+00, 0.700472E+00}, {-.367064E+00, 0.693769E+00}, + {-.375913E+00, 0.686874E+00}, {-.384887E+00, 0.679790E+00}, + {-.393973E+00, 0.672521E+00}, {-.403161E+00, 0.665074E+00}, + {-.412438E+00, 0.657452E+00}, {-.421793E+00, 0.649661E+00}, + {-.431214E+00, 0.641708E+00}, {-.440690E+00, 0.633597E+00}, + {-.450210E+00, 0.625335E+00}, {-.459761E+00, 0.616929E+00}, + {-.469334E+00, 0.608386E+00}, {-.478916E+00, 0.599712E+00}, + {-.488497E+00, 0.590916E+00}, {-.498067E+00, 0.582005E+00}, + {-.507615E+00, 0.572988E+00}, {-.517130E+00, 0.563872E+00}, + {-.526602E+00, 0.554666E+00}, {-.536022E+00, 0.545380E+00}, + {-.545380E+00, 0.536022E+00}, {-.554666E+00, 0.526602E+00}, + {-.563872E+00, 0.517130E+00}, {-.572988E+00, 0.507615E+00}, + {-.582005E+00, 0.498067E+00}, {-.590916E+00, 0.488497E+00}, + {-.599712E+00, 0.478916E+00}, {-.608386E+00, 0.469334E+00}, + {-.616929E+00, 0.459761E+00}, {-.625335E+00, 0.450210E+00}, + {-.633597E+00, 0.440690E+00}, {-.641708E+00, 0.431214E+00}, + {-.649661E+00, 0.421793E+00}, {-.657452E+00, 0.412438E+00}, + {-.665074E+00, 0.403161E+00}, {-.672521E+00, 0.393973E+00}, + {-.679790E+00, 0.384887E+00}, {-.686874E+00, 0.375913E+00}, + {-.693769E+00, 0.367064E+00}, {-.700472E+00, 0.358352E+00}, + {-.706979E+00, 0.349787E+00}, {-.713285E+00, 0.341382E+00}, + {-.719388E+00, 0.333147E+00}, {-.725285E+00, 0.325096E+00}, + {-.730972E+00, 0.317238E+00}, {-.736448E+00, 0.309585E+00}, + {-.741709E+00, 0.302148E+00}, {-.746755E+00, 0.294938E+00}, + {-.751584E+00, 0.287965E+00}, {-.756193E+00, 0.281240E+00}, + {-.760581E+00, 0.274772E+00}, {-.764748E+00, 0.268572E+00}, + {-.768692E+00, 0.262649E+00}, {-.772413E+00, 0.257012E+00}, + {-.775910E+00, 0.251669E+00}, {-.779182E+00, 0.246630E+00}, + {-.782228E+00, 0.241902E+00}, {-.785050E+00, 0.237493E+00}, + {-.787646E+00, 0.233409E+00}, {-.790016E+00, 0.229658E+00}, + {-.792160E+00, 0.226245E+00}, {-.794078E+00, 0.223176E+00}, + {-.795770E+00, 0.220456E+00}, {-.797237E+00, 0.218090E+00}, + {-.798478E+00, 0.216081E+00}, {-.799493E+00, 0.214432E+00}, + {-.800282E+00, 0.213147E+00}, {-.800846E+00, 0.212228E+00}, + {-.801184E+00, 0.211676E+00}, {-.801297E+00, 0.211491E+00}, + }, + { + {-.200977E+00, 0.805193E+00}, {-.201166E+00, 0.805079E+00}, + {-.201734E+00, 0.804734E+00}, {-.202680E+00, 0.804161E+00}, + {-.204002E+00, 0.803358E+00}, {-.205698E+00, 0.802325E+00}, + {-.207764E+00, 0.801062E+00}, {-.210199E+00, 0.799570E+00}, + {-.212996E+00, 0.797847E+00}, {-.216153E+00, 0.795895E+00}, + {-.219663E+00, 0.793712E+00}, {-.223521E+00, 0.791299E+00}, + {-.227721E+00, 0.788656E+00}, {-.232255E+00, 0.785783E+00}, + {-.237117E+00, 0.782680E+00}, {-.242298E+00, 0.779347E+00}, + {-.247790E+00, 0.775785E+00}, {-.253585E+00, 0.771993E+00}, + {-.259674E+00, 0.767974E+00}, {-.266047E+00, 0.763726E+00}, + {-.272695E+00, 0.759252E+00}, {-.279606E+00, 0.754553E+00}, + {-.286772E+00, 0.749629E+00}, {-.294181E+00, 0.744482E+00}, + {-.301822E+00, 0.739114E+00}, {-.309684E+00, 0.733526E+00}, + {-.317755E+00, 0.727722E+00}, {-.326026E+00, 0.721703E+00}, + {-.334482E+00, 0.715473E+00}, {-.343114E+00, 0.709033E+00}, + {-.351908E+00, 0.702388E+00}, {-.360854E+00, 0.695541E+00}, + {-.369938E+00, 0.688497E+00}, {-.379149E+00, 0.681258E+00}, + {-.388476E+00, 0.673830E+00}, {-.397905E+00, 0.666217E+00}, + {-.407424E+00, 0.658425E+00}, {-.417023E+00, 0.650459E+00}, + {-.426689E+00, 0.642325E+00}, {-.436409E+00, 0.634029E+00}, + {-.446173E+00, 0.625577E+00}, {-.455969E+00, 0.616977E+00}, + {-.465785E+00, 0.608235E+00}, {-.475610E+00, 0.599358E+00}, + {-.485433E+00, 0.590354E+00}, {-.495243E+00, 0.581232E+00}, + {-.505029E+00, 0.571999E+00}, {-.514780E+00, 0.562664E+00}, + {-.524487E+00, 0.553236E+00}, {-.534138E+00, 0.543724E+00}, + {-.543724E+00, 0.534138E+00}, {-.553236E+00, 0.524487E+00}, + {-.562664E+00, 0.514780E+00}, {-.571999E+00, 0.505029E+00}, + {-.581232E+00, 0.495243E+00}, {-.590354E+00, 0.485433E+00}, + {-.599358E+00, 0.475610E+00}, {-.608235E+00, 0.465785E+00}, + {-.616977E+00, 0.455969E+00}, {-.625577E+00, 0.446173E+00}, + {-.634029E+00, 0.436409E+00}, {-.642325E+00, 0.426689E+00}, + {-.650459E+00, 0.417023E+00}, {-.658425E+00, 0.407424E+00}, + {-.666217E+00, 0.397905E+00}, {-.673830E+00, 0.388476E+00}, + {-.681258E+00, 0.379149E+00}, {-.688497E+00, 0.369938E+00}, + {-.695541E+00, 0.360854E+00}, {-.702388E+00, 0.351908E+00}, + {-.709033E+00, 0.343114E+00}, {-.715473E+00, 0.334482E+00}, + {-.721703E+00, 0.326026E+00}, {-.727722E+00, 0.317755E+00}, + {-.733526E+00, 0.309684E+00}, {-.739114E+00, 0.301822E+00}, + {-.744482E+00, 0.294181E+00}, {-.749629E+00, 0.286772E+00}, + {-.754553E+00, 0.279606E+00}, {-.759252E+00, 0.272695E+00}, + {-.763726E+00, 0.266047E+00}, {-.767974E+00, 0.259674E+00}, + {-.771993E+00, 0.253585E+00}, {-.775785E+00, 0.247790E+00}, + {-.779347E+00, 0.242298E+00}, {-.782680E+00, 0.237117E+00}, + {-.785783E+00, 0.232255E+00}, {-.788656E+00, 0.227721E+00}, + {-.791299E+00, 0.223521E+00}, {-.793712E+00, 0.219663E+00}, + {-.795895E+00, 0.216153E+00}, {-.797847E+00, 0.212996E+00}, + {-.799570E+00, 0.210199E+00}, {-.801062E+00, 0.207764E+00}, + {-.802325E+00, 0.205698E+00}, {-.803358E+00, 0.204002E+00}, + {-.804161E+00, 0.202680E+00}, {-.804734E+00, 0.201734E+00}, + {-.805079E+00, 0.201166E+00}, {-.805193E+00, 0.200977E+00}, + }, + { + {-.190234E+00, 0.809086E+00}, {-.190429E+00, 0.808970E+00}, + {-.191013E+00, 0.808620E+00}, {-.191986E+00, 0.808036E+00}, + {-.193345E+00, 0.807219E+00}, {-.195089E+00, 0.806169E+00}, + {-.197215E+00, 0.804884E+00}, {-.199718E+00, 0.803366E+00}, + {-.202595E+00, 0.801613E+00}, {-.205841E+00, 0.799626E+00}, + {-.209451E+00, 0.797405E+00}, {-.213418E+00, 0.794949E+00}, + {-.217736E+00, 0.792259E+00}, {-.222398E+00, 0.789334E+00}, + {-.227396E+00, 0.786174E+00}, {-.232722E+00, 0.782780E+00}, + {-.238368E+00, 0.779152E+00}, {-.244324E+00, 0.775290E+00}, + {-.250582E+00, 0.771194E+00}, {-.257131E+00, 0.766866E+00}, + {-.263962E+00, 0.762306E+00}, {-.271064E+00, 0.757515E+00}, + {-.278426E+00, 0.752494E+00}, {-.286037E+00, 0.747246E+00}, + {-.293886E+00, 0.741771E+00}, {-.301961E+00, 0.736071E+00}, + {-.310251E+00, 0.730148E+00}, {-.318744E+00, 0.724006E+00}, + {-.327427E+00, 0.717646E+00}, {-.336289E+00, 0.711072E+00}, + {-.345318E+00, 0.704287E+00}, {-.354500E+00, 0.697295E+00}, + {-.363824E+00, 0.690099E+00}, {-.373278E+00, 0.682704E+00}, + {-.382848E+00, 0.675114E+00}, {-.392522E+00, 0.667334E+00}, + {-.402289E+00, 0.659370E+00}, {-.412136E+00, 0.651226E+00}, + {-.422050E+00, 0.642910E+00}, {-.432019E+00, 0.634426E+00}, + {-.442032E+00, 0.625782E+00}, {-.452076E+00, 0.616984E+00}, + {-.462140E+00, 0.608040E+00}, {-.472212E+00, 0.598957E+00}, + {-.482280E+00, 0.589743E+00}, {-.492334E+00, 0.580406E+00}, + {-.502362E+00, 0.570954E+00}, {-.512353E+00, 0.561397E+00}, + {-.522297E+00, 0.551744E+00}, {-.532184E+00, 0.542003E+00}, + {-.542003E+00, 0.532184E+00}, {-.551744E+00, 0.522297E+00}, + {-.561397E+00, 0.512353E+00}, {-.570954E+00, 0.502362E+00}, + {-.580406E+00, 0.492334E+00}, {-.589743E+00, 0.482280E+00}, + {-.598957E+00, 0.472212E+00}, {-.608040E+00, 0.462140E+00}, + {-.616984E+00, 0.452076E+00}, {-.625782E+00, 0.442032E+00}, + {-.634426E+00, 0.432019E+00}, {-.642910E+00, 0.422050E+00}, + {-.651226E+00, 0.412136E+00}, {-.659370E+00, 0.402289E+00}, + {-.667334E+00, 0.392522E+00}, {-.675114E+00, 0.382848E+00}, + {-.682704E+00, 0.373278E+00}, {-.690099E+00, 0.363824E+00}, + {-.697295E+00, 0.354500E+00}, {-.704287E+00, 0.345318E+00}, + {-.711072E+00, 0.336289E+00}, {-.717646E+00, 0.327427E+00}, + {-.724006E+00, 0.318744E+00}, {-.730148E+00, 0.310251E+00}, + {-.736071E+00, 0.301961E+00}, {-.741771E+00, 0.293886E+00}, + {-.747246E+00, 0.286037E+00}, {-.752494E+00, 0.278426E+00}, + {-.757515E+00, 0.271064E+00}, {-.762306E+00, 0.263962E+00}, + {-.766866E+00, 0.257131E+00}, {-.771194E+00, 0.250582E+00}, + {-.775290E+00, 0.244324E+00}, {-.779152E+00, 0.238368E+00}, + {-.782780E+00, 0.232722E+00}, {-.786174E+00, 0.227396E+00}, + {-.789334E+00, 0.222398E+00}, {-.792259E+00, 0.217736E+00}, + {-.794949E+00, 0.213418E+00}, {-.797405E+00, 0.209451E+00}, + {-.799626E+00, 0.205841E+00}, {-.801613E+00, 0.202595E+00}, + {-.803366E+00, 0.199718E+00}, {-.804884E+00, 0.197215E+00}, + {-.806169E+00, 0.195089E+00}, {-.807219E+00, 0.193345E+00}, + {-.808036E+00, 0.191986E+00}, {-.808620E+00, 0.191013E+00}, + {-.808970E+00, 0.190429E+00}, {-.809086E+00, 0.190234E+00}, + }, + { + {-.179253E+00, 0.812977E+00}, {-.179453E+00, 0.812858E+00}, + {-.180054E+00, 0.812503E+00}, {-.181054E+00, 0.811909E+00}, + {-.182453E+00, 0.811078E+00}, {-.184246E+00, 0.810010E+00}, + {-.186432E+00, 0.808704E+00}, {-.189006E+00, 0.807159E+00}, + {-.191964E+00, 0.805377E+00}, {-.195302E+00, 0.803356E+00}, + {-.199012E+00, 0.801096E+00}, {-.203091E+00, 0.798597E+00}, + {-.207530E+00, 0.795859E+00}, {-.212322E+00, 0.792882E+00}, + {-.217459E+00, 0.789665E+00}, {-.222933E+00, 0.786210E+00}, + {-.228736E+00, 0.782515E+00}, {-.234857E+00, 0.778582E+00}, + {-.241287E+00, 0.774410E+00}, {-.248016E+00, 0.770000E+00}, + {-.255034E+00, 0.765354E+00}, {-.262330E+00, 0.760471E+00}, + {-.269892E+00, 0.755353E+00}, {-.277710E+00, 0.750002E+00}, + {-.285771E+00, 0.744418E+00}, {-.294063E+00, 0.738605E+00}, + {-.302575E+00, 0.732563E+00}, {-.311295E+00, 0.726296E+00}, + {-.320210E+00, 0.719806E+00}, {-.329307E+00, 0.713096E+00}, + {-.338573E+00, 0.706169E+00}, {-.347997E+00, 0.699030E+00}, + {-.357565E+00, 0.691681E+00}, {-.367265E+00, 0.684127E+00}, + {-.377084E+00, 0.676374E+00}, {-.387009E+00, 0.668424E+00}, + {-.397027E+00, 0.660285E+00}, {-.407126E+00, 0.651962E+00}, + {-.417292E+00, 0.643460E+00}, {-.427515E+00, 0.634786E+00}, + {-.437781E+00, 0.625947E+00}, {-.448078E+00, 0.616949E+00}, + {-.458394E+00, 0.607800E+00}, {-.468716E+00, 0.598507E+00}, + {-.479034E+00, 0.589080E+00}, {-.489336E+00, 0.579525E+00}, + {-.499610E+00, 0.569851E+00}, {-.509846E+00, 0.560068E+00}, + {-.520031E+00, 0.550185E+00}, {-.530157E+00, 0.540211E+00}, + {-.540211E+00, 0.530157E+00}, {-.550185E+00, 0.520031E+00}, + {-.560068E+00, 0.509846E+00}, {-.569851E+00, 0.499610E+00}, + {-.579525E+00, 0.489336E+00}, {-.589080E+00, 0.479034E+00}, + {-.598507E+00, 0.468716E+00}, {-.607800E+00, 0.458394E+00}, + {-.616949E+00, 0.448078E+00}, {-.625947E+00, 0.437781E+00}, + {-.634786E+00, 0.427515E+00}, {-.643460E+00, 0.417292E+00}, + {-.651962E+00, 0.407126E+00}, {-.660285E+00, 0.397027E+00}, + {-.668424E+00, 0.387009E+00}, {-.676374E+00, 0.377084E+00}, + {-.684127E+00, 0.367265E+00}, {-.691681E+00, 0.357565E+00}, + {-.699030E+00, 0.347997E+00}, {-.706169E+00, 0.338573E+00}, + {-.713096E+00, 0.329307E+00}, {-.719806E+00, 0.320210E+00}, + {-.726296E+00, 0.311295E+00}, {-.732563E+00, 0.302575E+00}, + {-.738605E+00, 0.294063E+00}, {-.744418E+00, 0.285771E+00}, + {-.750002E+00, 0.277710E+00}, {-.755353E+00, 0.269892E+00}, + {-.760471E+00, 0.262330E+00}, {-.765354E+00, 0.255034E+00}, + {-.770000E+00, 0.248016E+00}, {-.774410E+00, 0.241287E+00}, + {-.778582E+00, 0.234857E+00}, {-.782515E+00, 0.228736E+00}, + {-.786210E+00, 0.222933E+00}, {-.789665E+00, 0.217459E+00}, + {-.792882E+00, 0.212322E+00}, {-.795859E+00, 0.207530E+00}, + {-.798597E+00, 0.203091E+00}, {-.801096E+00, 0.199012E+00}, + {-.803356E+00, 0.195302E+00}, {-.805377E+00, 0.191964E+00}, + {-.807159E+00, 0.189006E+00}, {-.808704E+00, 0.186432E+00}, + {-.810010E+00, 0.184246E+00}, {-.811078E+00, 0.182453E+00}, + {-.811909E+00, 0.181054E+00}, {-.812503E+00, 0.180054E+00}, + {-.812858E+00, 0.179453E+00}, {-.812977E+00, 0.179253E+00}, + }, + { + {-.168024E+00, 0.816866E+00}, {-.168230E+00, 0.816745E+00}, + {-.168848E+00, 0.816384E+00}, {-.169876E+00, 0.815781E+00}, + {-.171314E+00, 0.814936E+00}, {-.173158E+00, 0.813850E+00}, + {-.175405E+00, 0.812522E+00}, {-.178051E+00, 0.810951E+00}, + {-.181093E+00, 0.809138E+00}, {-.184523E+00, 0.807083E+00}, + {-.188338E+00, 0.804784E+00}, {-.192530E+00, 0.802242E+00}, + {-.197093E+00, 0.799457E+00}, {-.202018E+00, 0.796427E+00}, + {-.207297E+00, 0.793154E+00}, {-.212923E+00, 0.789637E+00}, + {-.218885E+00, 0.785875E+00}, {-.225174E+00, 0.781870E+00}, + {-.231781E+00, 0.777621E+00}, {-.238694E+00, 0.773130E+00}, + {-.245903E+00, 0.768396E+00}, {-.253396E+00, 0.763420E+00}, + {-.261163E+00, 0.758205E+00}, {-.269191E+00, 0.752750E+00}, + {-.277468E+00, 0.747057E+00}, {-.285982E+00, 0.741129E+00}, + {-.294721E+00, 0.734967E+00}, {-.303672E+00, 0.728574E+00}, + {-.312822E+00, 0.721952E+00}, {-.322159E+00, 0.715104E+00}, + {-.331669E+00, 0.708034E+00}, {-.341338E+00, 0.700745E+00}, + {-.351155E+00, 0.693241E+00}, {-.361106E+00, 0.685527E+00}, + {-.371178E+00, 0.677607E+00}, {-.381357E+00, 0.669487E+00}, + {-.391632E+00, 0.661170E+00}, {-.401987E+00, 0.652664E+00}, + {-.412412E+00, 0.643974E+00}, {-.422892E+00, 0.635107E+00}, + {-.433415E+00, 0.626070E+00}, {-.443969E+00, 0.616869E+00}, + {-.454542E+00, 0.607512E+00}, {-.465120E+00, 0.598007E+00}, + {-.475692E+00, 0.588362E+00}, {-.486246E+00, 0.578586E+00}, + {-.496770E+00, 0.568687E+00}, {-.507254E+00, 0.558674E+00}, + {-.517685E+00, 0.548558E+00}, {-.528053E+00, 0.538348E+00}, + {-.538348E+00, 0.528053E+00}, {-.548558E+00, 0.517685E+00}, + {-.558674E+00, 0.507254E+00}, {-.568687E+00, 0.496770E+00}, + {-.578586E+00, 0.486246E+00}, {-.588362E+00, 0.475692E+00}, + {-.598007E+00, 0.465120E+00}, {-.607512E+00, 0.454542E+00}, + {-.616869E+00, 0.443969E+00}, {-.626070E+00, 0.433415E+00}, + {-.635107E+00, 0.422892E+00}, {-.643974E+00, 0.412412E+00}, + {-.652664E+00, 0.401987E+00}, {-.661170E+00, 0.391632E+00}, + {-.669487E+00, 0.381357E+00}, {-.677607E+00, 0.371178E+00}, + {-.685527E+00, 0.361106E+00}, {-.693241E+00, 0.351155E+00}, + {-.700745E+00, 0.341338E+00}, {-.708034E+00, 0.331669E+00}, + {-.715104E+00, 0.322159E+00}, {-.721952E+00, 0.312822E+00}, + {-.728574E+00, 0.303672E+00}, {-.734967E+00, 0.294721E+00}, + {-.741129E+00, 0.285982E+00}, {-.747057E+00, 0.277468E+00}, + {-.752750E+00, 0.269191E+00}, {-.758205E+00, 0.261163E+00}, + {-.763420E+00, 0.253396E+00}, {-.768396E+00, 0.245903E+00}, + {-.773130E+00, 0.238694E+00}, {-.777621E+00, 0.231781E+00}, + {-.781870E+00, 0.225174E+00}, {-.785875E+00, 0.218885E+00}, + {-.789637E+00, 0.212923E+00}, {-.793154E+00, 0.207297E+00}, + {-.796427E+00, 0.202018E+00}, {-.799457E+00, 0.197093E+00}, + {-.802242E+00, 0.192530E+00}, {-.804784E+00, 0.188338E+00}, + {-.807083E+00, 0.184523E+00}, {-.809138E+00, 0.181093E+00}, + {-.810951E+00, 0.178051E+00}, {-.812522E+00, 0.175405E+00}, + {-.813850E+00, 0.173158E+00}, {-.814936E+00, 0.171314E+00}, + {-.815781E+00, 0.169876E+00}, {-.816384E+00, 0.168848E+00}, + {-.816745E+00, 0.168230E+00}, {-.816866E+00, 0.168024E+00}, + }, + { + {-.156536E+00, 0.820754E+00}, {-.156748E+00, 0.820632E+00}, + {-.157383E+00, 0.820264E+00}, {-.158441E+00, 0.819651E+00}, + {-.159918E+00, 0.818793E+00}, {-.161814E+00, 0.817688E+00}, + {-.164124E+00, 0.816338E+00}, {-.166844E+00, 0.814742E+00}, + {-.169970E+00, 0.812899E+00}, {-.173497E+00, 0.810809E+00}, + {-.177418E+00, 0.808472E+00}, {-.181726E+00, 0.805887E+00}, + {-.186415E+00, 0.803053E+00}, {-.191476E+00, 0.799971E+00}, + {-.196901E+00, 0.796641E+00}, {-.202681E+00, 0.793061E+00}, + {-.208806E+00, 0.789233E+00}, {-.215267E+00, 0.785155E+00}, + {-.222054E+00, 0.780829E+00}, {-.229154E+00, 0.776255E+00}, + {-.236558E+00, 0.771433E+00}, {-.244254E+00, 0.766364E+00}, + {-.252229E+00, 0.761050E+00}, {-.260472E+00, 0.755490E+00}, + {-.268970E+00, 0.749687E+00}, {-.277711E+00, 0.743643E+00}, + {-.286681E+00, 0.737359E+00}, {-.295868E+00, 0.730838E+00}, + {-.305258E+00, 0.724082E+00}, {-.314839E+00, 0.717095E+00}, + {-.324596E+00, 0.709880E+00}, {-.334517E+00, 0.702440E+00}, + {-.344587E+00, 0.694780E+00}, {-.354794E+00, 0.686903E+00}, + {-.365124E+00, 0.678815E+00}, {-.375563E+00, 0.670520E+00}, + {-.386097E+00, 0.662024E+00}, {-.396715E+00, 0.653332E+00}, + {-.407401E+00, 0.644452E+00}, {-.418144E+00, 0.635388E+00}, + {-.428930E+00, 0.626150E+00}, {-.439746E+00, 0.616742E+00}, + {-.450579E+00, 0.607174E+00}, {-.461417E+00, 0.597453E+00}, + {-.472247E+00, 0.587587E+00}, {-.483058E+00, 0.577586E+00}, + {-.493837E+00, 0.567458E+00}, {-.504573E+00, 0.557212E+00}, + {-.515254E+00, 0.546859E+00}, {-.525870E+00, 0.536408E+00}, + {-.536408E+00, 0.525870E+00}, {-.546859E+00, 0.515254E+00}, + {-.557212E+00, 0.504573E+00}, {-.567458E+00, 0.493837E+00}, + {-.577586E+00, 0.483058E+00}, {-.587587E+00, 0.472247E+00}, + {-.597453E+00, 0.461417E+00}, {-.607174E+00, 0.450579E+00}, + {-.616742E+00, 0.439746E+00}, {-.626150E+00, 0.428930E+00}, + {-.635388E+00, 0.418144E+00}, {-.644452E+00, 0.407401E+00}, + {-.653332E+00, 0.396715E+00}, {-.662024E+00, 0.386097E+00}, + {-.670520E+00, 0.375563E+00}, {-.678815E+00, 0.365124E+00}, + {-.686903E+00, 0.354794E+00}, {-.694780E+00, 0.344587E+00}, + {-.702440E+00, 0.334517E+00}, {-.709880E+00, 0.324596E+00}, + {-.717095E+00, 0.314839E+00}, {-.724082E+00, 0.305258E+00}, + {-.730838E+00, 0.295868E+00}, {-.737359E+00, 0.286681E+00}, + {-.743643E+00, 0.277711E+00}, {-.749687E+00, 0.268970E+00}, + {-.755490E+00, 0.260472E+00}, {-.761050E+00, 0.252229E+00}, + {-.766364E+00, 0.244254E+00}, {-.771433E+00, 0.236558E+00}, + {-.776255E+00, 0.229154E+00}, {-.780829E+00, 0.222054E+00}, + {-.785155E+00, 0.215267E+00}, {-.789233E+00, 0.208806E+00}, + {-.793061E+00, 0.202681E+00}, {-.796641E+00, 0.196901E+00}, + {-.799971E+00, 0.191476E+00}, {-.803053E+00, 0.186415E+00}, + {-.805887E+00, 0.181726E+00}, {-.808472E+00, 0.177418E+00}, + {-.810809E+00, 0.173497E+00}, {-.812899E+00, 0.169970E+00}, + {-.814742E+00, 0.166844E+00}, {-.816338E+00, 0.164124E+00}, + {-.817688E+00, 0.161814E+00}, {-.818793E+00, 0.159918E+00}, + {-.819651E+00, 0.158441E+00}, {-.820264E+00, 0.157383E+00}, + {-.820632E+00, 0.156748E+00}, {-.820754E+00, 0.156536E+00}, + }, + { + {-.144778E+00, 0.824642E+00}, {-.144996E+00, 0.824518E+00}, + {-.145649E+00, 0.824144E+00}, {-.146736E+00, 0.823521E+00}, + {-.148255E+00, 0.822649E+00}, {-.150203E+00, 0.821527E+00}, + {-.152577E+00, 0.820155E+00}, {-.155373E+00, 0.818533E+00}, + {-.158586E+00, 0.816660E+00}, {-.162211E+00, 0.814535E+00}, + {-.166240E+00, 0.812159E+00}, {-.170667E+00, 0.809530E+00}, + {-.175485E+00, 0.806649E+00}, {-.180685E+00, 0.803514E+00}, + {-.186259E+00, 0.800126E+00}, {-.192197E+00, 0.796484E+00}, + {-.198489E+00, 0.792588E+00}, {-.205126E+00, 0.788438E+00}, + {-.212096E+00, 0.784034E+00}, {-.219389E+00, 0.779377E+00}, + {-.226992E+00, 0.774466E+00}, {-.234894E+00, 0.769303E+00}, + {-.243082E+00, 0.763888E+00}, {-.251544E+00, 0.758223E+00}, + {-.260268E+00, 0.752308E+00}, {-.269239E+00, 0.746147E+00}, + {-.278445E+00, 0.739739E+00}, {-.287873E+00, 0.733089E+00}, + {-.297509E+00, 0.726199E+00}, {-.307338E+00, 0.719070E+00}, + {-.317348E+00, 0.711708E+00}, {-.327525E+00, 0.704115E+00}, + {-.337854E+00, 0.696295E+00}, {-.348321E+00, 0.688253E+00}, + {-.358914E+00, 0.679994E+00}, {-.369617E+00, 0.671522E+00}, + {-.380418E+00, 0.662844E+00}, {-.391302E+00, 0.653964E+00}, + {-.402256E+00, 0.644890E+00}, {-.413266E+00, 0.635627E+00}, + {-.424318E+00, 0.626184E+00}, {-.435401E+00, 0.616567E+00}, + {-.446500E+00, 0.606784E+00}, {-.457603E+00, 0.596843E+00}, + {-.468696E+00, 0.586753E+00}, {-.479769E+00, 0.576523E+00}, + {-.490807E+00, 0.566162E+00}, {-.501800E+00, 0.555679E+00}, + {-.512735E+00, 0.545085E+00}, {-.523602E+00, 0.534389E+00}, + {-.534389E+00, 0.523602E+00}, {-.545085E+00, 0.512735E+00}, + {-.555679E+00, 0.501800E+00}, {-.566162E+00, 0.490807E+00}, + {-.576523E+00, 0.479769E+00}, {-.586753E+00, 0.468696E+00}, + {-.596843E+00, 0.457603E+00}, {-.606784E+00, 0.446500E+00}, + {-.616567E+00, 0.435401E+00}, {-.626184E+00, 0.424318E+00}, + {-.635627E+00, 0.413266E+00}, {-.644890E+00, 0.402256E+00}, + {-.653964E+00, 0.391302E+00}, {-.662844E+00, 0.380418E+00}, + {-.671522E+00, 0.369617E+00}, {-.679994E+00, 0.358914E+00}, + {-.688253E+00, 0.348321E+00}, {-.696295E+00, 0.337854E+00}, + {-.704115E+00, 0.327525E+00}, {-.711708E+00, 0.317348E+00}, + {-.719070E+00, 0.307338E+00}, {-.726199E+00, 0.297509E+00}, + {-.733089E+00, 0.287873E+00}, {-.739739E+00, 0.278445E+00}, + {-.746147E+00, 0.269239E+00}, {-.752308E+00, 0.260268E+00}, + {-.758223E+00, 0.251544E+00}, {-.763888E+00, 0.243082E+00}, + {-.769303E+00, 0.234894E+00}, {-.774466E+00, 0.226992E+00}, + {-.779377E+00, 0.219389E+00}, {-.784034E+00, 0.212096E+00}, + {-.788438E+00, 0.205126E+00}, {-.792588E+00, 0.198489E+00}, + {-.796484E+00, 0.192197E+00}, {-.800126E+00, 0.186259E+00}, + {-.803514E+00, 0.180685E+00}, {-.806649E+00, 0.175485E+00}, + {-.809530E+00, 0.170667E+00}, {-.812159E+00, 0.166240E+00}, + {-.814535E+00, 0.162211E+00}, {-.816660E+00, 0.158586E+00}, + {-.818533E+00, 0.155373E+00}, {-.820155E+00, 0.152577E+00}, + {-.821527E+00, 0.150203E+00}, {-.822649E+00, 0.148255E+00}, + {-.823521E+00, 0.146736E+00}, {-.824144E+00, 0.145649E+00}, + {-.824518E+00, 0.144996E+00}, {-.824642E+00, 0.144778E+00}, + }, + { + {-.132738E+00, 0.828531E+00}, {-.132962E+00, 0.828404E+00}, + {-.133633E+00, 0.828025E+00}, {-.134750E+00, 0.827392E+00}, + {-.136311E+00, 0.826506E+00}, {-.138314E+00, 0.825367E+00}, + {-.140754E+00, 0.823973E+00}, {-.143627E+00, 0.822324E+00}, + {-.146929E+00, 0.820421E+00}, {-.150653E+00, 0.818262E+00}, + {-.154793E+00, 0.815846E+00}, {-.159342E+00, 0.813174E+00}, + {-.164292E+00, 0.810244E+00}, {-.169635E+00, 0.807057E+00}, + {-.175361E+00, 0.803611E+00}, {-.181460E+00, 0.799906E+00}, + {-.187924E+00, 0.795942E+00}, {-.194740E+00, 0.791719E+00}, + {-.201898E+00, 0.787236E+00}, {-.209386E+00, 0.782495E+00}, + {-.217193E+00, 0.777494E+00}, {-.225306E+00, 0.772236E+00}, + {-.233712E+00, 0.766720E+00}, {-.242398E+00, 0.760948E+00}, + {-.251352E+00, 0.754921E+00}, {-.260559E+00, 0.748640E+00}, + {-.270006E+00, 0.742108E+00}, {-.279680E+00, 0.735327E+00}, + {-.289565E+00, 0.728299E+00}, {-.299649E+00, 0.721028E+00}, + {-.309917E+00, 0.713516E+00}, {-.320354E+00, 0.705768E+00}, + {-.330947E+00, 0.697787E+00}, {-.341681E+00, 0.689577E+00}, + {-.352541E+00, 0.681144E+00}, {-.363514E+00, 0.672493E+00}, + {-.374586E+00, 0.663629E+00}, {-.385742E+00, 0.654558E+00}, + {-.396968E+00, 0.645287E+00}, {-.408250E+00, 0.635822E+00}, + {-.419575E+00, 0.626171E+00}, {-.430930E+00, 0.616340E+00}, + {-.442299E+00, 0.606339E+00}, {-.453672E+00, 0.596175E+00}, + {-.465034E+00, 0.585857E+00}, {-.476372E+00, 0.575394E+00}, + {-.487675E+00, 0.564795E+00}, {-.498929E+00, 0.554071E+00}, + {-.510124E+00, 0.543231E+00}, {-.521246E+00, 0.532286E+00}, + {-.532286E+00, 0.521246E+00}, {-.543231E+00, 0.510124E+00}, + {-.554071E+00, 0.498929E+00}, {-.564795E+00, 0.487675E+00}, + {-.575394E+00, 0.476372E+00}, {-.585857E+00, 0.465034E+00}, + {-.596175E+00, 0.453672E+00}, {-.606339E+00, 0.442299E+00}, + {-.616340E+00, 0.430930E+00}, {-.626171E+00, 0.419575E+00}, + {-.635822E+00, 0.408250E+00}, {-.645287E+00, 0.396968E+00}, + {-.654558E+00, 0.385742E+00}, {-.663629E+00, 0.374586E+00}, + {-.672493E+00, 0.363514E+00}, {-.681144E+00, 0.352541E+00}, + {-.689577E+00, 0.341681E+00}, {-.697787E+00, 0.330947E+00}, + {-.705768E+00, 0.320354E+00}, {-.713516E+00, 0.309917E+00}, + {-.721028E+00, 0.299649E+00}, {-.728299E+00, 0.289565E+00}, + {-.735327E+00, 0.279680E+00}, {-.742108E+00, 0.270006E+00}, + {-.748640E+00, 0.260559E+00}, {-.754921E+00, 0.251352E+00}, + {-.760948E+00, 0.242398E+00}, {-.766720E+00, 0.233712E+00}, + {-.772236E+00, 0.225306E+00}, {-.777494E+00, 0.217193E+00}, + {-.782495E+00, 0.209386E+00}, {-.787236E+00, 0.201898E+00}, + {-.791719E+00, 0.194740E+00}, {-.795942E+00, 0.187924E+00}, + {-.799906E+00, 0.181460E+00}, {-.803611E+00, 0.175361E+00}, + {-.807057E+00, 0.169635E+00}, {-.810244E+00, 0.164292E+00}, + {-.813174E+00, 0.159342E+00}, {-.815846E+00, 0.154793E+00}, + {-.818262E+00, 0.150653E+00}, {-.820421E+00, 0.146929E+00}, + {-.822324E+00, 0.143627E+00}, {-.823973E+00, 0.140754E+00}, + {-.825367E+00, 0.138314E+00}, {-.826506E+00, 0.136311E+00}, + {-.827392E+00, 0.134750E+00}, {-.828025E+00, 0.133633E+00}, + {-.828404E+00, 0.132962E+00}, {-.828531E+00, 0.132738E+00}, + }, + { + {-.120402E+00, 0.832421E+00}, {-.120632E+00, 0.832292E+00}, + {-.121322E+00, 0.831907E+00}, {-.122470E+00, 0.831265E+00}, + {-.124074E+00, 0.830365E+00}, {-.126132E+00, 0.829207E+00}, + {-.128640E+00, 0.827792E+00}, {-.131593E+00, 0.826117E+00}, + {-.134985E+00, 0.824183E+00}, {-.138812E+00, 0.821989E+00}, + {-.143066E+00, 0.819535E+00}, {-.147740E+00, 0.816819E+00}, + {-.152825E+00, 0.813840E+00}, {-.158313E+00, 0.810600E+00}, + {-.164195E+00, 0.807095E+00}, {-.170459E+00, 0.803327E+00}, + {-.177097E+00, 0.799295E+00}, {-.184097E+00, 0.794998E+00}, + {-.191448E+00, 0.790436E+00}, {-.199136E+00, 0.785610E+00}, + {-.207151E+00, 0.780519E+00}, {-.215479E+00, 0.775164E+00}, + {-.224108E+00, 0.769546E+00}, {-.233023E+00, 0.763666E+00}, + {-.242212E+00, 0.757524E+00}, {-.251660E+00, 0.751123E+00}, + {-.261353E+00, 0.744465E+00}, {-.271278E+00, 0.737551E+00}, + {-.281419E+00, 0.730384E+00}, {-.291762E+00, 0.722968E+00}, + {-.302293E+00, 0.715304E+00}, {-.312997E+00, 0.707398E+00}, + {-.323859E+00, 0.699253E+00}, {-.334864E+00, 0.690873E+00}, + {-.345998E+00, 0.682264E+00}, {-.357246E+00, 0.673430E+00}, + {-.368594E+00, 0.664378E+00}, {-.380027E+00, 0.655112E+00}, + {-.391531E+00, 0.645641E+00}, {-.403091E+00, 0.635970E+00}, + {-.414694E+00, 0.626107E+00}, {-.426325E+00, 0.616060E+00}, + {-.437971E+00, 0.605836E+00}, {-.449618E+00, 0.595445E+00}, + {-.461253E+00, 0.584894E+00}, {-.472863E+00, 0.574194E+00}, + {-.484435E+00, 0.563354E+00}, {-.495956E+00, 0.552384E+00}, + {-.507415E+00, 0.541294E+00}, {-.518798E+00, 0.530095E+00}, + {-.530095E+00, 0.518798E+00}, {-.541294E+00, 0.507415E+00}, + {-.552384E+00, 0.495956E+00}, {-.563354E+00, 0.484435E+00}, + {-.574194E+00, 0.472863E+00}, {-.584894E+00, 0.461253E+00}, + {-.595445E+00, 0.449618E+00}, {-.605836E+00, 0.437971E+00}, + {-.616060E+00, 0.426325E+00}, {-.626107E+00, 0.414694E+00}, + {-.635970E+00, 0.403091E+00}, {-.645641E+00, 0.391531E+00}, + {-.655112E+00, 0.380027E+00}, {-.664378E+00, 0.368594E+00}, + {-.673430E+00, 0.357246E+00}, {-.682264E+00, 0.345998E+00}, + {-.690873E+00, 0.334864E+00}, {-.699253E+00, 0.323859E+00}, + {-.707398E+00, 0.312997E+00}, {-.715304E+00, 0.302293E+00}, + {-.722968E+00, 0.291762E+00}, {-.730384E+00, 0.281419E+00}, + {-.737551E+00, 0.271278E+00}, {-.744465E+00, 0.261353E+00}, + {-.751123E+00, 0.251660E+00}, {-.757524E+00, 0.242212E+00}, + {-.763666E+00, 0.233023E+00}, {-.769546E+00, 0.224108E+00}, + {-.775164E+00, 0.215479E+00}, {-.780519E+00, 0.207151E+00}, + {-.785610E+00, 0.199136E+00}, {-.790436E+00, 0.191448E+00}, + {-.794998E+00, 0.184097E+00}, {-.799295E+00, 0.177097E+00}, + {-.803327E+00, 0.170459E+00}, {-.807095E+00, 0.164195E+00}, + {-.810600E+00, 0.158313E+00}, {-.813840E+00, 0.152825E+00}, + {-.816819E+00, 0.147740E+00}, {-.819535E+00, 0.143066E+00}, + {-.821989E+00, 0.138812E+00}, {-.824183E+00, 0.134985E+00}, + {-.826117E+00, 0.131593E+00}, {-.827792E+00, 0.128640E+00}, + {-.829207E+00, 0.126132E+00}, {-.830365E+00, 0.124074E+00}, + {-.831265E+00, 0.122470E+00}, {-.831907E+00, 0.121322E+00}, + {-.832292E+00, 0.120632E+00}, {-.832421E+00, 0.120402E+00}, + }, + { + {-.107757E+00, 0.836313E+00}, {-.107994E+00, 0.836183E+00}, + {-.108702E+00, 0.835792E+00}, {-.109882E+00, 0.835139E+00}, + {-.111531E+00, 0.834226E+00}, {-.113646E+00, 0.833050E+00}, + {-.116222E+00, 0.831613E+00}, {-.119256E+00, 0.829912E+00}, + {-.122742E+00, 0.827948E+00}, {-.126674E+00, 0.825719E+00}, + {-.131045E+00, 0.823225E+00}, {-.135846E+00, 0.820465E+00}, + {-.141070E+00, 0.817438E+00}, {-.146707E+00, 0.814143E+00}, + {-.152748E+00, 0.810581E+00}, {-.159182E+00, 0.806749E+00}, + {-.165999E+00, 0.802647E+00}, {-.173186E+00, 0.798276E+00}, + {-.180733E+00, 0.793634E+00}, {-.188627E+00, 0.788722E+00}, + {-.196855E+00, 0.783539E+00}, {-.205403E+00, 0.778087E+00}, + {-.214259E+00, 0.772366E+00}, {-.223409E+00, 0.766376E+00}, + {-.232838E+00, 0.760119E+00}, {-.242532E+00, 0.753596E+00}, + {-.252477E+00, 0.746809E+00}, {-.262658E+00, 0.739761E+00}, + {-.273060E+00, 0.732453E+00}, {-.283668E+00, 0.724889E+00}, + {-.294468E+00, 0.717072E+00}, {-.305443E+00, 0.709005E+00}, + {-.316580E+00, 0.700693E+00}, {-.327862E+00, 0.692141E+00}, + {-.339276E+00, 0.683352E+00}, {-.350805E+00, 0.674332E+00}, + {-.362435E+00, 0.665088E+00}, {-.374151E+00, 0.655625E+00}, + {-.385938E+00, 0.645950E+00}, {-.397782E+00, 0.636069E+00}, + {-.409667E+00, 0.625991E+00}, {-.421581E+00, 0.615722E+00}, + {-.433509E+00, 0.605272E+00}, {-.445436E+00, 0.594649E+00}, + {-.457350E+00, 0.583862E+00}, {-.469236E+00, 0.572921E+00}, + {-.481083E+00, 0.561834E+00}, {-.492876E+00, 0.550614E+00}, + {-.504603E+00, 0.539269E+00}, {-.516252E+00, 0.527811E+00}, + {-.527811E+00, 0.516252E+00}, {-.539269E+00, 0.504603E+00}, + {-.550614E+00, 0.492876E+00}, {-.561834E+00, 0.481083E+00}, + {-.572921E+00, 0.469236E+00}, {-.583862E+00, 0.457350E+00}, + {-.594649E+00, 0.445436E+00}, {-.605272E+00, 0.433509E+00}, + {-.615722E+00, 0.421581E+00}, {-.625991E+00, 0.409667E+00}, + {-.636069E+00, 0.397782E+00}, {-.645950E+00, 0.385938E+00}, + {-.655625E+00, 0.374151E+00}, {-.665088E+00, 0.362435E+00}, + {-.674332E+00, 0.350805E+00}, {-.683352E+00, 0.339276E+00}, + {-.692141E+00, 0.327862E+00}, {-.700693E+00, 0.316580E+00}, + {-.709005E+00, 0.305443E+00}, {-.717072E+00, 0.294468E+00}, + {-.724889E+00, 0.283668E+00}, {-.732453E+00, 0.273060E+00}, + {-.739761E+00, 0.262658E+00}, {-.746809E+00, 0.252477E+00}, + {-.753596E+00, 0.242532E+00}, {-.760119E+00, 0.232838E+00}, + {-.766376E+00, 0.223409E+00}, {-.772366E+00, 0.214259E+00}, + {-.778087E+00, 0.205403E+00}, {-.783539E+00, 0.196855E+00}, + {-.788722E+00, 0.188627E+00}, {-.793634E+00, 0.180733E+00}, + {-.798276E+00, 0.173186E+00}, {-.802647E+00, 0.165999E+00}, + {-.806749E+00, 0.159182E+00}, {-.810581E+00, 0.152748E+00}, + {-.814143E+00, 0.146707E+00}, {-.817438E+00, 0.141070E+00}, + {-.820465E+00, 0.135846E+00}, {-.823225E+00, 0.131045E+00}, + {-.825719E+00, 0.126674E+00}, {-.827948E+00, 0.122742E+00}, + {-.829912E+00, 0.119256E+00}, {-.831613E+00, 0.116222E+00}, + {-.833050E+00, 0.113646E+00}, {-.834226E+00, 0.111531E+00}, + {-.835139E+00, 0.109882E+00}, {-.835792E+00, 0.108702E+00}, + {-.836183E+00, 0.107994E+00}, {-.836313E+00, 0.107757E+00}, + }, + { + {-.947884E-01, 0.840209E+00}, {-.950314E-01, 0.840077E+00}, + {-.957597E-01, 0.839680E+00}, {-.969721E-01, 0.839017E+00}, + {-.986664E-01, 0.838090E+00}, {-.100839E+00, 0.836897E+00}, + {-.103487E+00, 0.835437E+00}, {-.106604E+00, 0.833710E+00}, + {-.110186E+00, 0.831715E+00}, {-.114225E+00, 0.829451E+00}, + {-.118715E+00, 0.826918E+00}, {-.123647E+00, 0.824113E+00}, + {-.129013E+00, 0.821038E+00}, {-.134803E+00, 0.817689E+00}, + {-.141007E+00, 0.814067E+00}, {-.147615E+00, 0.810171E+00}, + {-.154614E+00, 0.806000E+00}, {-.161995E+00, 0.801553E+00}, + {-.169743E+00, 0.796830E+00}, {-.177846E+00, 0.791832E+00}, + {-.186292E+00, 0.786557E+00}, {-.195066E+00, 0.781006E+00}, + {-.204154E+00, 0.775180E+00}, {-.213544E+00, 0.769079E+00}, + {-.223218E+00, 0.762705E+00}, {-.233164E+00, 0.756058E+00}, + {-.243366E+00, 0.749141E+00}, {-.253809E+00, 0.741956E+00}, + {-.264478E+00, 0.734505E+00}, {-.275357E+00, 0.726791E+00}, + {-.286431E+00, 0.718817E+00}, {-.297684E+00, 0.710588E+00}, + {-.309102E+00, 0.702106E+00}, {-.320667E+00, 0.693378E+00}, + {-.332366E+00, 0.684406E+00}, {-.344181E+00, 0.675198E+00}, + {-.356099E+00, 0.665758E+00}, {-.368104E+00, 0.656094E+00}, + {-.380180E+00, 0.646211E+00}, {-.392313E+00, 0.636116E+00}, + {-.404488E+00, 0.625819E+00}, {-.416690E+00, 0.615325E+00}, + {-.428905E+00, 0.604644E+00}, {-.441118E+00, 0.593785E+00}, + {-.453316E+00, 0.582757E+00}, {-.465485E+00, 0.571569E+00}, + {-.477611E+00, 0.560232E+00}, {-.489681E+00, 0.548756E+00}, + {-.501683E+00, 0.537151E+00}, {-.513603E+00, 0.525430E+00}, + {-.525430E+00, 0.513603E+00}, {-.537151E+00, 0.501683E+00}, + {-.548756E+00, 0.489681E+00}, {-.560232E+00, 0.477611E+00}, + {-.571569E+00, 0.465485E+00}, {-.582757E+00, 0.453316E+00}, + {-.593785E+00, 0.441118E+00}, {-.604644E+00, 0.428905E+00}, + {-.615325E+00, 0.416690E+00}, {-.625819E+00, 0.404488E+00}, + {-.636116E+00, 0.392313E+00}, {-.646211E+00, 0.380180E+00}, + {-.656094E+00, 0.368104E+00}, {-.665758E+00, 0.356099E+00}, + {-.675198E+00, 0.344181E+00}, {-.684406E+00, 0.332366E+00}, + {-.693378E+00, 0.320667E+00}, {-.702106E+00, 0.309102E+00}, + {-.710588E+00, 0.297684E+00}, {-.718817E+00, 0.286431E+00}, + {-.726791E+00, 0.275357E+00}, {-.734505E+00, 0.264478E+00}, + {-.741956E+00, 0.253809E+00}, {-.749141E+00, 0.243366E+00}, + {-.756058E+00, 0.233164E+00}, {-.762705E+00, 0.223218E+00}, + {-.769079E+00, 0.213544E+00}, {-.775180E+00, 0.204154E+00}, + {-.781006E+00, 0.195066E+00}, {-.786557E+00, 0.186292E+00}, + {-.791832E+00, 0.177846E+00}, {-.796830E+00, 0.169743E+00}, + {-.801553E+00, 0.161995E+00}, {-.806000E+00, 0.154614E+00}, + {-.810171E+00, 0.147615E+00}, {-.814067E+00, 0.141007E+00}, + {-.817689E+00, 0.134803E+00}, {-.821038E+00, 0.129013E+00}, + {-.824113E+00, 0.123647E+00}, {-.826918E+00, 0.118715E+00}, + {-.829451E+00, 0.114225E+00}, {-.831715E+00, 0.110186E+00}, + {-.833710E+00, 0.106604E+00}, {-.835437E+00, 0.103487E+00}, + {-.836897E+00, 0.100839E+00}, {-.838090E+00, 0.986664E-01}, + {-.839017E+00, 0.969721E-01}, {-.839680E+00, 0.957597E-01}, + {-.840077E+00, 0.950314E-01}, {-.840209E+00, 0.947884E-01}, + }, + { + {-.814802E-01, 0.844109E+00}, {-.817299E-01, 0.843975E+00}, + {-.824783E-01, 0.843571E+00}, {-.837241E-01, 0.842899E+00}, + {-.854650E-01, 0.841958E+00}, {-.876977E-01, 0.840747E+00}, + {-.904180E-01, 0.839265E+00}, {-.936210E-01, 0.837512E+00}, + {-.973007E-01, 0.835486E+00}, {-.101450E+00, 0.833187E+00}, + {-.106062E+00, 0.830614E+00}, {-.111129E+00, 0.827765E+00}, + {-.116640E+00, 0.824640E+00}, {-.122587E+00, 0.821237E+00}, + {-.128958E+00, 0.817556E+00}, {-.135743E+00, 0.813595E+00}, + {-.142931E+00, 0.809353E+00}, {-.150508E+00, 0.804830E+00}, + {-.158462E+00, 0.800026E+00}, {-.166780E+00, 0.794940E+00}, + {-.175449E+00, 0.789571E+00}, {-.184454E+00, 0.783920E+00}, + {-.193781E+00, 0.777988E+00}, {-.203415E+00, 0.771775E+00}, + {-.213341E+00, 0.765281E+00}, {-.223544E+00, 0.758509E+00}, + {-.234009E+00, 0.751460E+00}, {-.244720E+00, 0.744136E+00}, + {-.255662E+00, 0.736539E+00}, {-.266818E+00, 0.728673E+00}, + {-.278172E+00, 0.720540E+00}, {-.289709E+00, 0.712145E+00}, + {-.301413E+00, 0.703491E+00}, {-.313268E+00, 0.694582E+00}, + {-.325258E+00, 0.685425E+00}, {-.337366E+00, 0.676024E+00}, + {-.349578E+00, 0.666386E+00}, {-.361878E+00, 0.656516E+00}, + {-.374249E+00, 0.646421E+00}, {-.386678E+00, 0.636109E+00}, + {-.399148E+00, 0.625588E+00}, {-.411644E+00, 0.614865E+00}, + {-.424152E+00, 0.603949E+00}, {-.436657E+00, 0.592849E+00}, + {-.449145E+00, 0.581575E+00}, {-.461602E+00, 0.570136E+00}, + {-.474014E+00, 0.558542E+00}, {-.486367E+00, 0.546806E+00}, + {-.498649E+00, 0.534936E+00}, {-.510846E+00, 0.522946E+00}, + {-.522946E+00, 0.510846E+00}, {-.534936E+00, 0.498649E+00}, + {-.546806E+00, 0.486367E+00}, {-.558542E+00, 0.474014E+00}, + {-.570136E+00, 0.461602E+00}, {-.581575E+00, 0.449145E+00}, + {-.592849E+00, 0.436657E+00}, {-.603949E+00, 0.424152E+00}, + {-.614865E+00, 0.411644E+00}, {-.625588E+00, 0.399148E+00}, + {-.636109E+00, 0.386678E+00}, {-.646421E+00, 0.374249E+00}, + {-.656516E+00, 0.361878E+00}, {-.666386E+00, 0.349578E+00}, + {-.676024E+00, 0.337366E+00}, {-.685425E+00, 0.325258E+00}, + {-.694582E+00, 0.313268E+00}, {-.703491E+00, 0.301413E+00}, + {-.712145E+00, 0.289709E+00}, {-.720540E+00, 0.278172E+00}, + {-.728673E+00, 0.266818E+00}, {-.736539E+00, 0.255662E+00}, + {-.744136E+00, 0.244720E+00}, {-.751460E+00, 0.234009E+00}, + {-.758509E+00, 0.223544E+00}, {-.765281E+00, 0.213341E+00}, + {-.771775E+00, 0.203415E+00}, {-.777988E+00, 0.193781E+00}, + {-.783920E+00, 0.184454E+00}, {-.789571E+00, 0.175449E+00}, + {-.794940E+00, 0.166780E+00}, {-.800026E+00, 0.158462E+00}, + {-.804830E+00, 0.150508E+00}, {-.809353E+00, 0.142931E+00}, + {-.813595E+00, 0.135743E+00}, {-.817556E+00, 0.128958E+00}, + {-.821237E+00, 0.122587E+00}, {-.824640E+00, 0.116640E+00}, + {-.827765E+00, 0.111129E+00}, {-.830614E+00, 0.106062E+00}, + {-.833187E+00, 0.101450E+00}, {-.835486E+00, 0.973007E-01}, + {-.837512E+00, 0.936210E-01}, {-.839265E+00, 0.904180E-01}, + {-.840747E+00, 0.876977E-01}, {-.841958E+00, 0.854650E-01}, + {-.842899E+00, 0.837241E-01}, {-.843571E+00, 0.824783E-01}, + {-.843975E+00, 0.817299E-01}, {-.844109E+00, 0.814802E-01}, + }, + { + {-.678158E-01, 0.848014E+00}, {-.680723E-01, 0.847877E+00}, + {-.688414E-01, 0.847468E+00}, {-.701215E-01, 0.846786E+00}, + {-.719103E-01, 0.845831E+00}, {-.742043E-01, 0.844602E+00}, + {-.769993E-01, 0.843098E+00}, {-.802900E-01, 0.841318E+00}, + {-.840704E-01, 0.839262E+00}, {-.883333E-01, 0.836928E+00}, + {-.930710E-01, 0.834314E+00}, {-.982748E-01, 0.831421E+00}, + {-.103935E+00, 0.828246E+00}, {-.110043E+00, 0.824788E+00}, + {-.116586E+00, 0.821047E+00}, {-.123553E+00, 0.817020E+00}, + {-.130932E+00, 0.812708E+00}, {-.138712E+00, 0.808108E+00}, + {-.146877E+00, 0.803221E+00}, {-.155415E+00, 0.798046E+00}, + {-.164313E+00, 0.792583E+00}, {-.173554E+00, 0.786831E+00}, + {-.183124E+00, 0.780791E+00}, {-.193009E+00, 0.774463E+00}, + {-.203193E+00, 0.767849E+00}, {-.213660E+00, 0.760949E+00}, + {-.224394E+00, 0.753765E+00}, {-.235379E+00, 0.746300E+00}, + {-.246599E+00, 0.738555E+00}, {-.258038E+00, 0.730533E+00}, + {-.269680E+00, 0.722239E+00}, {-.281507E+00, 0.713674E+00}, + {-.293504E+00, 0.704844E+00}, {-.305654E+00, 0.695753E+00}, + {-.317942E+00, 0.686407E+00}, {-.330350E+00, 0.676809E+00}, + {-.342862E+00, 0.666968E+00}, {-.355463E+00, 0.656888E+00}, + {-.368136E+00, 0.646578E+00}, {-.380866E+00, 0.636044E+00}, + {-.393638E+00, 0.625294E+00}, {-.406435E+00, 0.614337E+00}, + {-.419242E+00, 0.603181E+00}, {-.432046E+00, 0.591835E+00}, + {-.444830E+00, 0.580310E+00}, {-.457581E+00, 0.568615E+00}, + {-.470285E+00, 0.556760E+00}, {-.482927E+00, 0.544757E+00}, + {-.495494E+00, 0.532617E+00}, {-.507974E+00, 0.520352E+00}, + {-.520352E+00, 0.507974E+00}, {-.532617E+00, 0.495494E+00}, + {-.544757E+00, 0.482927E+00}, {-.556760E+00, 0.470285E+00}, + {-.568615E+00, 0.457581E+00}, {-.580310E+00, 0.444830E+00}, + {-.591835E+00, 0.432046E+00}, {-.603181E+00, 0.419242E+00}, + {-.614337E+00, 0.406435E+00}, {-.625294E+00, 0.393638E+00}, + {-.636044E+00, 0.380866E+00}, {-.646578E+00, 0.368136E+00}, + {-.656888E+00, 0.355463E+00}, {-.666968E+00, 0.342862E+00}, + {-.676809E+00, 0.330350E+00}, {-.686407E+00, 0.317942E+00}, + {-.695753E+00, 0.305654E+00}, {-.704844E+00, 0.293504E+00}, + {-.713674E+00, 0.281507E+00}, {-.722239E+00, 0.269680E+00}, + {-.730533E+00, 0.258038E+00}, {-.738555E+00, 0.246599E+00}, + {-.746300E+00, 0.235379E+00}, {-.753765E+00, 0.224394E+00}, + {-.760949E+00, 0.213660E+00}, {-.767849E+00, 0.203193E+00}, + {-.774463E+00, 0.193009E+00}, {-.780791E+00, 0.183124E+00}, + {-.786831E+00, 0.173554E+00}, {-.792583E+00, 0.164313E+00}, + {-.798046E+00, 0.155415E+00}, {-.803221E+00, 0.146877E+00}, + {-.808108E+00, 0.138712E+00}, {-.812708E+00, 0.130932E+00}, + {-.817020E+00, 0.123553E+00}, {-.821047E+00, 0.116586E+00}, + {-.824788E+00, 0.110043E+00}, {-.828246E+00, 0.103935E+00}, + {-.831421E+00, 0.982748E-01}, {-.834314E+00, 0.930710E-01}, + {-.836928E+00, 0.883333E-01}, {-.839262E+00, 0.840704E-01}, + {-.841318E+00, 0.802900E-01}, {-.843098E+00, 0.769993E-01}, + {-.844602E+00, 0.742043E-01}, {-.845831E+00, 0.719103E-01}, + {-.846786E+00, 0.701215E-01}, {-.847468E+00, 0.688414E-01}, + {-.847877E+00, 0.680723E-01}, {-.848014E+00, 0.678158E-01}, + }, + { + {-.537773E-01, 0.851924E+00}, {-.540409E-01, 0.851786E+00}, + {-.548311E-01, 0.851371E+00}, {-.561464E-01, 0.850679E+00}, + {-.579844E-01, 0.849710E+00}, {-.603415E-01, 0.848463E+00}, + {-.632131E-01, 0.846937E+00}, {-.665940E-01, 0.845130E+00}, + {-.704777E-01, 0.843043E+00}, {-.748569E-01, 0.840673E+00}, + {-.797235E-01, 0.838020E+00}, {-.850686E-01, 0.835081E+00}, + {-.908824E-01, 0.831857E+00}, {-.971544E-01, 0.828344E+00}, + {-.103873E+00, 0.824542E+00}, {-.111027E+00, 0.820449E+00}, + {-.118604E+00, 0.816064E+00}, {-.126590E+00, 0.811387E+00}, + {-.134972E+00, 0.806416E+00}, {-.143736E+00, 0.801151E+00}, + {-.152867E+00, 0.795591E+00}, {-.162351E+00, 0.789737E+00}, + {-.172171E+00, 0.783587E+00}, {-.182313E+00, 0.777144E+00}, + {-.192760E+00, 0.770406E+00}, {-.203497E+00, 0.763377E+00}, + {-.214506E+00, 0.756056E+00}, {-.225772E+00, 0.748447E+00}, + {-.237277E+00, 0.740551E+00}, {-.249006E+00, 0.732372E+00}, + {-.260941E+00, 0.723912E+00}, {-.273065E+00, 0.715175E+00}, + {-.285362E+00, 0.706166E+00}, {-.297815E+00, 0.696888E+00}, + {-.310406E+00, 0.687348E+00}, {-.323120E+00, 0.677551E+00}, + {-.335940E+00, 0.667502E+00}, {-.348849E+00, 0.657209E+00}, + {-.361831E+00, 0.646678E+00}, {-.374869E+00, 0.635917E+00}, + {-.387948E+00, 0.624935E+00}, {-.401053E+00, 0.613738E+00}, + {-.414167E+00, 0.602337E+00}, {-.427275E+00, 0.590740E+00}, + {-.440362E+00, 0.578959E+00}, {-.453413E+00, 0.567002E+00}, + {-.466415E+00, 0.554880E+00}, {-.479352E+00, 0.542605E+00}, + {-.492212E+00, 0.530189E+00}, {-.504980E+00, 0.517643E+00}, + {-.517643E+00, 0.504980E+00}, {-.530189E+00, 0.492212E+00}, + {-.542605E+00, 0.479352E+00}, {-.554880E+00, 0.466415E+00}, + {-.567002E+00, 0.453413E+00}, {-.578959E+00, 0.440362E+00}, + {-.590740E+00, 0.427275E+00}, {-.602337E+00, 0.414167E+00}, + {-.613738E+00, 0.401053E+00}, {-.624935E+00, 0.387948E+00}, + {-.635917E+00, 0.374869E+00}, {-.646678E+00, 0.361831E+00}, + {-.657209E+00, 0.348849E+00}, {-.667502E+00, 0.335940E+00}, + {-.677551E+00, 0.323120E+00}, {-.687348E+00, 0.310406E+00}, + {-.696888E+00, 0.297815E+00}, {-.706166E+00, 0.285362E+00}, + {-.715175E+00, 0.273065E+00}, {-.723912E+00, 0.260941E+00}, + {-.732372E+00, 0.249006E+00}, {-.740551E+00, 0.237277E+00}, + {-.748447E+00, 0.225772E+00}, {-.756056E+00, 0.214506E+00}, + {-.763377E+00, 0.203497E+00}, {-.770406E+00, 0.192760E+00}, + {-.777144E+00, 0.182313E+00}, {-.783587E+00, 0.172171E+00}, + {-.789737E+00, 0.162351E+00}, {-.795591E+00, 0.152867E+00}, + {-.801151E+00, 0.143736E+00}, {-.806416E+00, 0.134972E+00}, + {-.811387E+00, 0.126590E+00}, {-.816064E+00, 0.118604E+00}, + {-.820449E+00, 0.111027E+00}, {-.824542E+00, 0.103873E+00}, + {-.828344E+00, 0.971544E-01}, {-.831857E+00, 0.908824E-01}, + {-.835081E+00, 0.850686E-01}, {-.838020E+00, 0.797235E-01}, + {-.840673E+00, 0.748569E-01}, {-.843043E+00, 0.704777E-01}, + {-.845130E+00, 0.665940E-01}, {-.846937E+00, 0.632131E-01}, + {-.848463E+00, 0.603415E-01}, {-.849710E+00, 0.579844E-01}, + {-.850679E+00, 0.561464E-01}, {-.851371E+00, 0.548311E-01}, + {-.851786E+00, 0.540409E-01}, {-.851924E+00, 0.537773E-01}, + }, + { + {-.393454E-01, 0.855842E+00}, {-.396163E-01, 0.855702E+00}, + {-.404283E-01, 0.855281E+00}, {-.417799E-01, 0.854579E+00}, + {-.436684E-01, 0.853596E+00}, {-.460902E-01, 0.852331E+00}, + {-.490407E-01, 0.850782E+00}, {-.525142E-01, 0.848949E+00}, + {-.565040E-01, 0.846831E+00}, {-.610027E-01, 0.844425E+00}, + {-.660017E-01, 0.841732E+00}, {-.714919E-01, 0.838748E+00}, + {-.774630E-01, 0.835472E+00}, {-.839041E-01, 0.831904E+00}, + {-.908038E-01, 0.828040E+00}, {-.981495E-01, 0.823881E+00}, + {-.105929E+00, 0.819424E+00}, {-.114127E+00, 0.814668E+00}, + {-.122731E+00, 0.809612E+00}, {-.131726E+00, 0.804256E+00}, + {-.141098E+00, 0.798598E+00}, {-.150829E+00, 0.792639E+00}, + {-.160906E+00, 0.786378E+00}, {-.171311E+00, 0.779816E+00}, + {-.182028E+00, 0.772954E+00}, {-.193040E+00, 0.765792E+00}, + {-.204332E+00, 0.758332E+00}, {-.215885E+00, 0.750577E+00}, + {-.227683E+00, 0.742527E+00}, {-.239708E+00, 0.734186E+00}, + {-.251943E+00, 0.725558E+00}, {-.264371E+00, 0.716645E+00}, + {-.276975E+00, 0.707453E+00}, {-.289737E+00, 0.697985E+00}, + {-.302640E+00, 0.688248E+00}, {-.315667E+00, 0.678246E+00}, + {-.328801E+00, 0.667986E+00}, {-.342025E+00, 0.657474E+00}, + {-.355322E+00, 0.646718E+00}, {-.368676E+00, 0.635726E+00}, + {-.382070E+00, 0.624505E+00}, {-.395489E+00, 0.613064E+00}, + {-.408915E+00, 0.601412E+00}, {-.422335E+00, 0.589559E+00}, + {-.435732E+00, 0.577515E+00}, {-.449091E+00, 0.565290E+00}, + {-.462397E+00, 0.552896E+00}, {-.475636E+00, 0.540343E+00}, + {-.488794E+00, 0.527645E+00}, {-.501857E+00, 0.514812E+00}, + {-.514812E+00, 0.501857E+00}, {-.527645E+00, 0.488794E+00}, + {-.540343E+00, 0.475636E+00}, {-.552896E+00, 0.462397E+00}, + {-.565290E+00, 0.449091E+00}, {-.577515E+00, 0.435732E+00}, + {-.589559E+00, 0.422335E+00}, {-.601412E+00, 0.408915E+00}, + {-.613064E+00, 0.395489E+00}, {-.624505E+00, 0.382070E+00}, + {-.635726E+00, 0.368676E+00}, {-.646718E+00, 0.355322E+00}, + {-.657474E+00, 0.342025E+00}, {-.667986E+00, 0.328801E+00}, + {-.678246E+00, 0.315667E+00}, {-.688248E+00, 0.302640E+00}, + {-.697985E+00, 0.289737E+00}, {-.707453E+00, 0.276975E+00}, + {-.716645E+00, 0.264371E+00}, {-.725558E+00, 0.251943E+00}, + {-.734186E+00, 0.239708E+00}, {-.742527E+00, 0.227683E+00}, + {-.750577E+00, 0.215885E+00}, {-.758332E+00, 0.204332E+00}, + {-.765792E+00, 0.193040E+00}, {-.772954E+00, 0.182028E+00}, + {-.779816E+00, 0.171311E+00}, {-.786378E+00, 0.160906E+00}, + {-.792639E+00, 0.150829E+00}, {-.798598E+00, 0.141098E+00}, + {-.804256E+00, 0.131726E+00}, {-.809612E+00, 0.122731E+00}, + {-.814668E+00, 0.114127E+00}, {-.819424E+00, 0.105929E+00}, + {-.823881E+00, 0.981495E-01}, {-.828040E+00, 0.908038E-01}, + {-.831904E+00, 0.839041E-01}, {-.835472E+00, 0.774630E-01}, + {-.838748E+00, 0.714919E-01}, {-.841732E+00, 0.660017E-01}, + {-.844425E+00, 0.610027E-01}, {-.846831E+00, 0.565040E-01}, + {-.848949E+00, 0.525142E-01}, {-.850782E+00, 0.490407E-01}, + {-.852331E+00, 0.460902E-01}, {-.853596E+00, 0.436684E-01}, + {-.854579E+00, 0.417799E-01}, {-.855281E+00, 0.404283E-01}, + {-.855702E+00, 0.396163E-01}, {-.855842E+00, 0.393454E-01}, + }, + { + {-.244998E-01, 0.859768E+00}, {-.247781E-01, 0.859625E+00}, + {-.256125E-01, 0.859199E+00}, {-.270013E-01, 0.858487E+00}, + {-.289418E-01, 0.857490E+00}, {-.314303E-01, 0.856206E+00}, + {-.344618E-01, 0.854635E+00}, {-.380305E-01, 0.852776E+00}, + {-.421295E-01, 0.850626E+00}, {-.467510E-01, 0.848185E+00}, + {-.518861E-01, 0.845450E+00}, {-.575253E-01, 0.842420E+00}, + {-.636581E-01, 0.839094E+00}, {-.702730E-01, 0.835469E+00}, + {-.773582E-01, 0.831544E+00}, {-.849008E-01, 0.827317E+00}, + {-.928874E-01, 0.822786E+00}, {-.101304E+00, 0.817950E+00}, + {-.110136E+00, 0.812808E+00}, {-.119369E+00, 0.807359E+00}, + {-.128986E+00, 0.801602E+00}, {-.138972E+00, 0.795537E+00}, + {-.149311E+00, 0.789163E+00}, {-.159986E+00, 0.782481E+00}, + {-.170980E+00, 0.775491E+00}, {-.182275E+00, 0.768195E+00}, + {-.193856E+00, 0.760593E+00}, {-.205703E+00, 0.752687E+00}, + {-.217800E+00, 0.744481E+00}, {-.230129E+00, 0.735975E+00}, + {-.242672E+00, 0.727175E+00}, {-.255411E+00, 0.718083E+00}, + {-.268329E+00, 0.708704E+00}, {-.281408E+00, 0.699041E+00}, + {-.294629E+00, 0.689102E+00}, {-.307977E+00, 0.678891E+00}, + {-.321432E+00, 0.668415E+00}, {-.334978E+00, 0.657680E+00}, + {-.348598E+00, 0.646694E+00}, {-.362275E+00, 0.635464E+00}, + {-.375991E+00, 0.623999E+00}, {-.389731E+00, 0.612308E+00}, + {-.403478E+00, 0.600400E+00}, {-.417217E+00, 0.588285E+00}, + {-.430930E+00, 0.575973E+00}, {-.444603E+00, 0.563474E+00}, + {-.458221E+00, 0.550801E+00}, {-.471769E+00, 0.537964E+00}, + {-.485233E+00, 0.524976E+00}, {-.498598E+00, 0.511850E+00}, + {-.511850E+00, 0.498598E+00}, {-.524976E+00, 0.485233E+00}, + {-.537964E+00, 0.471769E+00}, {-.550801E+00, 0.458221E+00}, + {-.563474E+00, 0.444603E+00}, {-.575973E+00, 0.430930E+00}, + {-.588285E+00, 0.417217E+00}, {-.600400E+00, 0.403478E+00}, + {-.612308E+00, 0.389731E+00}, {-.623999E+00, 0.375991E+00}, + {-.635464E+00, 0.362275E+00}, {-.646694E+00, 0.348598E+00}, + {-.657680E+00, 0.334978E+00}, {-.668415E+00, 0.321432E+00}, + {-.678891E+00, 0.307977E+00}, {-.689102E+00, 0.294629E+00}, + {-.699041E+00, 0.281408E+00}, {-.708704E+00, 0.268329E+00}, + {-.718083E+00, 0.255411E+00}, {-.727175E+00, 0.242672E+00}, + {-.735975E+00, 0.230129E+00}, {-.744481E+00, 0.217800E+00}, + {-.752687E+00, 0.205703E+00}, {-.760593E+00, 0.193856E+00}, + {-.768195E+00, 0.182275E+00}, {-.775491E+00, 0.170980E+00}, + {-.782481E+00, 0.159986E+00}, {-.789163E+00, 0.149311E+00}, + {-.795537E+00, 0.138972E+00}, {-.801602E+00, 0.128986E+00}, + {-.807359E+00, 0.119369E+00}, {-.812808E+00, 0.110136E+00}, + {-.817950E+00, 0.101304E+00}, {-.822786E+00, 0.928874E-01}, + {-.827317E+00, 0.849008E-01}, {-.831544E+00, 0.773582E-01}, + {-.835469E+00, 0.702730E-01}, {-.839094E+00, 0.636581E-01}, + {-.842420E+00, 0.575253E-01}, {-.845450E+00, 0.518861E-01}, + {-.848185E+00, 0.467510E-01}, {-.850626E+00, 0.421295E-01}, + {-.852776E+00, 0.380305E-01}, {-.854635E+00, 0.344618E-01}, + {-.856206E+00, 0.314303E-01}, {-.857490E+00, 0.289418E-01}, + {-.858487E+00, 0.270013E-01}, {-.859199E+00, 0.256125E-01}, + {-.859625E+00, 0.247781E-01}, {-.859768E+00, 0.244998E-01}, + }, + { + {-.921795E-02, 0.863702E+00}, {-.950396E-02, 0.863558E+00}, + {-.103614E-01, 0.863125E+00}, {-.117886E-01, 0.862404E+00}, + {-.137827E-01, 0.861393E+00}, {-.163397E-01, 0.860091E+00}, + {-.194546E-01, 0.858497E+00}, {-.231213E-01, 0.856611E+00}, + {-.273326E-01, 0.854430E+00}, {-.320803E-01, 0.851952E+00}, + {-.373555E-01, 0.849176E+00}, {-.431480E-01, 0.846101E+00}, + {-.494469E-01, 0.842723E+00}, {-.562405E-01, 0.839041E+00}, + {-.635163E-01, 0.835053E+00}, {-.712611E-01, 0.830757E+00}, + {-.794610E-01, 0.826152E+00}, {-.881016E-01, 0.821235E+00}, + {-.971677E-01, 0.816006E+00}, {-.106644E+00, 0.810463E+00}, + {-.116514E+00, 0.804605E+00}, {-.126761E+00, 0.798431E+00}, + {-.137369E+00, 0.791942E+00}, {-.148321E+00, 0.785138E+00}, + {-.159598E+00, 0.778018E+00}, {-.171184E+00, 0.770584E+00}, + {-.183061E+00, 0.762837E+00}, {-.195210E+00, 0.754778E+00}, + {-.207614E+00, 0.746411E+00}, {-.220254E+00, 0.737738E+00}, + {-.233112E+00, 0.728762E+00}, {-.246170E+00, 0.719486E+00}, + {-.259409E+00, 0.709915E+00}, {-.272812E+00, 0.700054E+00}, + {-.286360E+00, 0.689908E+00}, {-.300035E+00, 0.679483E+00}, + {-.313820E+00, 0.668786E+00}, {-.327696E+00, 0.657822E+00}, + {-.341647E+00, 0.646601E+00}, {-.355654E+00, 0.635128E+00}, + {-.369700E+00, 0.623414E+00}, {-.383769E+00, 0.611467E+00}, + {-.397844E+00, 0.599297E+00}, {-.411909E+00, 0.586913E+00}, + {-.425946E+00, 0.574326E+00}, {-.439941E+00, 0.561547E+00}, + {-.453879E+00, 0.548588E+00}, {-.467743E+00, 0.535461E+00}, + {-.481519E+00, 0.522177E+00}, {-.495192E+00, 0.508750E+00}, + {-.508750E+00, 0.495192E+00}, {-.522177E+00, 0.481519E+00}, + {-.535461E+00, 0.467743E+00}, {-.548588E+00, 0.453879E+00}, + {-.561547E+00, 0.439941E+00}, {-.574326E+00, 0.425946E+00}, + {-.586913E+00, 0.411909E+00}, {-.599297E+00, 0.397844E+00}, + {-.611467E+00, 0.383769E+00}, {-.623414E+00, 0.369700E+00}, + {-.635128E+00, 0.355654E+00}, {-.646601E+00, 0.341647E+00}, + {-.657822E+00, 0.327696E+00}, {-.668786E+00, 0.313820E+00}, + {-.679483E+00, 0.300035E+00}, {-.689908E+00, 0.286360E+00}, + {-.700054E+00, 0.272812E+00}, {-.709915E+00, 0.259409E+00}, + {-.719486E+00, 0.246170E+00}, {-.728762E+00, 0.233112E+00}, + {-.737738E+00, 0.220254E+00}, {-.746411E+00, 0.207614E+00}, + {-.754778E+00, 0.195210E+00}, {-.762837E+00, 0.183061E+00}, + {-.770584E+00, 0.171184E+00}, {-.778018E+00, 0.159598E+00}, + {-.785138E+00, 0.148321E+00}, {-.791942E+00, 0.137369E+00}, + {-.798431E+00, 0.126761E+00}, {-.804605E+00, 0.116514E+00}, + {-.810463E+00, 0.106644E+00}, {-.816006E+00, 0.971677E-01}, + {-.821235E+00, 0.881016E-01}, {-.826152E+00, 0.794610E-01}, + {-.830757E+00, 0.712611E-01}, {-.835053E+00, 0.635163E-01}, + {-.839041E+00, 0.562405E-01}, {-.842723E+00, 0.494469E-01}, + {-.846101E+00, 0.431480E-01}, {-.849176E+00, 0.373555E-01}, + {-.851952E+00, 0.320803E-01}, {-.854430E+00, 0.273326E-01}, + {-.856611E+00, 0.231213E-01}, {-.858497E+00, 0.194546E-01}, + {-.860091E+00, 0.163397E-01}, {-.861393E+00, 0.137827E-01}, + {-.862404E+00, 0.117886E-01}, {-.863125E+00, 0.103614E-01}, + {-.863558E+00, 0.950396E-02}, {-.863702E+00, 0.921795E-02}, + }, + { + {0.652373E-02, 0.867647E+00}, {0.622980E-02, 0.867501E+00}, + {0.534860E-02, 0.867062E+00}, {0.388191E-02, 0.866331E+00}, + {0.183270E-02, 0.865305E+00}, {-.794918E-03, 0.863985E+00}, + {-.399569E-02, 0.862369E+00}, {-.776326E-02, 0.860456E+00}, + {-.120902E-01, 0.858243E+00}, {-.169680E-01, 0.855729E+00}, + {-.223871E-01, 0.852912E+00}, {-.283373E-01, 0.849789E+00}, + {-.348071E-01, 0.846359E+00}, {-.417845E-01, 0.842620E+00}, + {-.492563E-01, 0.838568E+00}, {-.572091E-01, 0.834203E+00}, + {-.656282E-01, 0.829522E+00}, {-.744988E-01, 0.824523E+00}, + {-.838053E-01, 0.819205E+00}, {-.935315E-01, 0.813566E+00}, + {-.103661E+00, 0.807605E+00}, {-.114176E+00, 0.801321E+00}, + {-.125061E+00, 0.794715E+00}, {-.136296E+00, 0.787785E+00}, + {-.147865E+00, 0.780532E+00}, {-.159749E+00, 0.772958E+00}, + {-.171929E+00, 0.765063E+00}, {-.184388E+00, 0.756848E+00}, + {-.197106E+00, 0.748317E+00}, {-.210065E+00, 0.739471E+00}, + {-.223246E+00, 0.730315E+00}, {-.236631E+00, 0.720851E+00}, + {-.250200E+00, 0.711085E+00}, {-.263935E+00, 0.701020E+00}, + {-.277817E+00, 0.690663E+00}, {-.291829E+00, 0.680019E+00}, + {-.305951E+00, 0.669094E+00}, {-.320165E+00, 0.657897E+00}, + {-.334454E+00, 0.646434E+00}, {-.348799E+00, 0.634713E+00}, + {-.363184E+00, 0.622743E+00}, {-.377590E+00, 0.610534E+00}, + {-.392001E+00, 0.598094E+00}, {-.406399E+00, 0.585435E+00}, + {-.420769E+00, 0.572567E+00}, {-.435094E+00, 0.559501E+00}, + {-.449358E+00, 0.546249E+00}, {-.463546E+00, 0.532824E+00}, + {-.477642E+00, 0.519237E+00}, {-.491632E+00, 0.505502E+00}, + {-.505502E+00, 0.491632E+00}, {-.519237E+00, 0.477642E+00}, + {-.532824E+00, 0.463546E+00}, {-.546249E+00, 0.449358E+00}, + {-.559501E+00, 0.435094E+00}, {-.572567E+00, 0.420769E+00}, + {-.585435E+00, 0.406399E+00}, {-.598094E+00, 0.392001E+00}, + {-.610534E+00, 0.377590E+00}, {-.622743E+00, 0.363184E+00}, + {-.634713E+00, 0.348799E+00}, {-.646434E+00, 0.334454E+00}, + {-.657897E+00, 0.320165E+00}, {-.669094E+00, 0.305951E+00}, + {-.680019E+00, 0.291829E+00}, {-.690663E+00, 0.277817E+00}, + {-.701020E+00, 0.263935E+00}, {-.711085E+00, 0.250200E+00}, + {-.720851E+00, 0.236631E+00}, {-.730315E+00, 0.223246E+00}, + {-.739471E+00, 0.210065E+00}, {-.748317E+00, 0.197106E+00}, + {-.756848E+00, 0.184388E+00}, {-.765063E+00, 0.171929E+00}, + {-.772958E+00, 0.159749E+00}, {-.780532E+00, 0.147865E+00}, + {-.787785E+00, 0.136296E+00}, {-.794715E+00, 0.125061E+00}, + {-.801321E+00, 0.114176E+00}, {-.807605E+00, 0.103661E+00}, + {-.813566E+00, 0.935315E-01}, {-.819205E+00, 0.838053E-01}, + {-.824523E+00, 0.744988E-01}, {-.829522E+00, 0.656282E-01}, + {-.834203E+00, 0.572091E-01}, {-.838568E+00, 0.492563E-01}, + {-.842620E+00, 0.417845E-01}, {-.846359E+00, 0.348071E-01}, + {-.849789E+00, 0.283373E-01}, {-.852912E+00, 0.223871E-01}, + {-.855729E+00, 0.169680E-01}, {-.858243E+00, 0.120902E-01}, + {-.860456E+00, 0.776326E-02}, {-.862369E+00, 0.399569E-02}, + {-.863985E+00, 0.794918E-03}, {-.865305E+00, -.183270E-02}, + {-.866331E+00, -.388191E-02}, {-.867062E+00, -.534860E-02}, + {-.867501E+00, -.622980E-02}, {-.867647E+00, -.652373E-02}, + }, + { + {0.227509E-01, 0.871603E+00}, {0.224488E-01, 0.871455E+00}, + {0.215432E-01, 0.871010E+00}, {0.200358E-01, 0.870269E+00}, + {0.179297E-01, 0.869229E+00}, {0.152293E-01, 0.867891E+00}, + {0.119401E-01, 0.866252E+00}, {0.806858E-02, 0.864311E+00}, + {0.362257E-02, 0.862066E+00}, {-.138913E-02, 0.859516E+00}, + {-.695665E-02, 0.856657E+00}, {-.130692E-01, 0.853487E+00}, + {-.197150E-01, 0.850005E+00}, {-.268814E-01, 0.846207E+00}, + {-.345549E-01, 0.842091E+00}, {-.427215E-01, 0.837655E+00}, + {-.513661E-01, 0.832897E+00}, {-.604732E-01, 0.827814E+00}, + {-.700268E-01, 0.822405E+00}, {-.800101E-01, 0.816669E+00}, + {-.904060E-01, 0.810603E+00}, {-.101197E+00, 0.804207E+00}, + {-.112365E+00, 0.797480E+00}, {-.123892E+00, 0.790423E+00}, + {-.135760E+00, 0.783035E+00}, {-.147949E+00, 0.775316E+00}, + {-.160442E+00, 0.767269E+00}, {-.173218E+00, 0.758895E+00}, + {-.186258E+00, 0.750196E+00}, {-.199544E+00, 0.741174E+00}, + {-.213057E+00, 0.731833E+00}, {-.226776E+00, 0.722177E+00}, + {-.240683E+00, 0.712210E+00}, {-.254759E+00, 0.701936E+00}, + {-.268984E+00, 0.691362E+00}, {-.283340E+00, 0.680493E+00}, + {-.297808E+00, 0.669336E+00}, {-.312369E+00, 0.657898E+00}, + {-.327005E+00, 0.646187E+00}, {-.341697E+00, 0.634212E+00}, + {-.356428E+00, 0.621980E+00}, {-.371179E+00, 0.609501E+00}, + {-.385934E+00, 0.596786E+00}, {-.400675E+00, 0.583845E+00}, + {-.415385E+00, 0.570688E+00}, {-.430048E+00, 0.557328E+00}, + {-.444647E+00, 0.543776E+00}, {-.459167E+00, 0.530045E+00}, + {-.473592E+00, 0.516147E+00}, {-.487906E+00, 0.502096E+00}, + {-.502096E+00, 0.487906E+00}, {-.516147E+00, 0.473592E+00}, + {-.530045E+00, 0.459167E+00}, {-.543776E+00, 0.444647E+00}, + {-.557328E+00, 0.430048E+00}, {-.570688E+00, 0.415385E+00}, + {-.583845E+00, 0.400675E+00}, {-.596786E+00, 0.385934E+00}, + {-.609501E+00, 0.371179E+00}, {-.621980E+00, 0.356428E+00}, + {-.634212E+00, 0.341697E+00}, {-.646187E+00, 0.327005E+00}, + {-.657898E+00, 0.312369E+00}, {-.669336E+00, 0.297808E+00}, + {-.680493E+00, 0.283340E+00}, {-.691362E+00, 0.268984E+00}, + {-.701936E+00, 0.254759E+00}, {-.712210E+00, 0.240683E+00}, + {-.722177E+00, 0.226776E+00}, {-.731833E+00, 0.213057E+00}, + {-.741174E+00, 0.199544E+00}, {-.750196E+00, 0.186258E+00}, + {-.758895E+00, 0.173218E+00}, {-.767269E+00, 0.160442E+00}, + {-.775316E+00, 0.147949E+00}, {-.783035E+00, 0.135760E+00}, + {-.790423E+00, 0.123892E+00}, {-.797480E+00, 0.112365E+00}, + {-.804207E+00, 0.101197E+00}, {-.810603E+00, 0.904060E-01}, + {-.816669E+00, 0.800101E-01}, {-.822405E+00, 0.700268E-01}, + {-.827814E+00, 0.604732E-01}, {-.832897E+00, 0.513661E-01}, + {-.837655E+00, 0.427215E-01}, {-.842091E+00, 0.345549E-01}, + {-.846207E+00, 0.268814E-01}, {-.850005E+00, 0.197150E-01}, + {-.853487E+00, 0.130692E-01}, {-.856657E+00, 0.695665E-02}, + {-.859516E+00, 0.138913E-02}, {-.862066E+00, -.362257E-02}, + {-.864311E+00, -.806858E-02}, {-.866252E+00, -.119401E-01}, + {-.867891E+00, -.152293E-01}, {-.869229E+00, -.179297E-01}, + {-.870269E+00, -.200358E-01}, {-.871010E+00, -.215432E-01}, + {-.871455E+00, -.224488E-01}, {-.871603E+00, -.227509E-01}, + }, + { + {0.394914E-01, 0.875572E+00}, {0.391809E-01, 0.875422E+00}, + {0.382500E-01, 0.874971E+00}, {0.367006E-01, 0.874219E+00}, + {0.345359E-01, 0.873165E+00}, {0.317605E-01, 0.871809E+00}, + {0.283800E-01, 0.870147E+00}, {0.244013E-01, 0.868179E+00}, + {0.198325E-01, 0.865902E+00}, {0.146828E-01, 0.863314E+00}, + {0.896243E-02, 0.860413E+00}, {0.268263E-02, 0.857196E+00}, + {-.414437E-02, 0.853660E+00}, {-.115055E-01, 0.849803E+00}, + {-.193867E-01, 0.845622E+00}, {-.277733E-01, 0.841114E+00}, + {-.366499E-01, 0.836277E+00}, {-.460004E-01, 0.831109E+00}, + {-.558081E-01, 0.825608E+00}, {-.660557E-01, 0.819772E+00}, + {-.767257E-01, 0.813599E+00}, {-.877998E-01, 0.807088E+00}, + {-.992596E-01, 0.800239E+00}, {-.111086E+00, 0.793050E+00}, + {-.123261E+00, 0.785523E+00}, {-.135764E+00, 0.777658E+00}, + {-.148576E+00, 0.769455E+00}, {-.161678E+00, 0.760917E+00}, + {-.175050E+00, 0.752045E+00}, {-.188672E+00, 0.742843E+00}, + {-.202524E+00, 0.733313E+00}, {-.216587E+00, 0.723459E+00}, + {-.230841E+00, 0.713285E+00}, {-.245266E+00, 0.702798E+00}, + {-.259843E+00, 0.692001E+00}, {-.274552E+00, 0.680901E+00}, + {-.289375E+00, 0.669506E+00}, {-.304291E+00, 0.657821E+00}, + {-.319283E+00, 0.645856E+00}, {-.334331E+00, 0.633618E+00}, + {-.349417E+00, 0.621118E+00}, {-.364523E+00, 0.608363E+00}, + {-.379630E+00, 0.595365E+00}, {-.394723E+00, 0.582134E+00}, + {-.409782E+00, 0.568681E+00}, {-.424791E+00, 0.555018E+00}, + {-.439734E+00, 0.541158E+00}, {-.454594E+00, 0.527113E+00}, + {-.469356E+00, 0.512896E+00}, {-.484003E+00, 0.498522E+00}, + {-.498522E+00, 0.484003E+00}, {-.512896E+00, 0.469356E+00}, + {-.527113E+00, 0.454594E+00}, {-.541158E+00, 0.439734E+00}, + {-.555018E+00, 0.424791E+00}, {-.568681E+00, 0.409782E+00}, + {-.582134E+00, 0.394723E+00}, {-.595365E+00, 0.379630E+00}, + {-.608363E+00, 0.364523E+00}, {-.621118E+00, 0.349417E+00}, + {-.633618E+00, 0.334331E+00}, {-.645856E+00, 0.319283E+00}, + {-.657821E+00, 0.304291E+00}, {-.669506E+00, 0.289375E+00}, + {-.680901E+00, 0.274552E+00}, {-.692001E+00, 0.259843E+00}, + {-.702798E+00, 0.245266E+00}, {-.713285E+00, 0.230841E+00}, + {-.723459E+00, 0.216587E+00}, {-.733313E+00, 0.202524E+00}, + {-.742843E+00, 0.188672E+00}, {-.752045E+00, 0.175050E+00}, + {-.760917E+00, 0.161678E+00}, {-.769455E+00, 0.148576E+00}, + {-.777658E+00, 0.135764E+00}, {-.785523E+00, 0.123261E+00}, + {-.793050E+00, 0.111086E+00}, {-.800239E+00, 0.992596E-01}, + {-.807088E+00, 0.877998E-01}, {-.813599E+00, 0.767257E-01}, + {-.819772E+00, 0.660557E-01}, {-.825608E+00, 0.558081E-01}, + {-.831109E+00, 0.460004E-01}, {-.836277E+00, 0.366499E-01}, + {-.841114E+00, 0.277733E-01}, {-.845622E+00, 0.193867E-01}, + {-.849803E+00, 0.115055E-01}, {-.853660E+00, 0.414437E-02}, + {-.857196E+00, -.268263E-02}, {-.860413E+00, -.896243E-02}, + {-.863314E+00, -.146828E-01}, {-.865902E+00, -.198325E-01}, + {-.868179E+00, -.244013E-01}, {-.870147E+00, -.283800E-01}, + {-.871809E+00, -.317605E-01}, {-.873165E+00, -.345359E-01}, + {-.874219E+00, -.367006E-01}, {-.874971E+00, -.382500E-01}, + {-.875422E+00, -.391809E-01}, {-.875572E+00, -.394914E-01}, + }, + { + {0.567752E-01, 0.879554E+00}, {0.564560E-01, 0.879402E+00}, + {0.554990E-01, 0.878945E+00}, {0.539062E-01, 0.878183E+00}, + {0.516811E-01, 0.877115E+00}, {0.488282E-01, 0.875740E+00}, + {0.453535E-01, 0.874055E+00}, {0.412642E-01, 0.872059E+00}, + {0.365688E-01, 0.869750E+00}, {0.312767E-01, 0.867125E+00}, + {0.253987E-01, 0.864181E+00}, {0.189465E-01, 0.860915E+00}, + {0.119327E-01, 0.857325E+00}, {0.437094E-02, 0.853408E+00}, + {-.372420E-02, 0.849161E+00}, {-.123375E-01, 0.844580E+00}, + {-.214530E-01, 0.839664E+00}, {-.310539E-01, 0.834409E+00}, + {-.411231E-01, 0.828813E+00}, {-.516427E-01, 0.822875E+00}, + {-.625945E-01, 0.816593E+00}, {-.739597E-01, 0.809964E+00}, + {-.857193E-01, 0.802989E+00}, {-.978539E-01, 0.795667E+00}, + {-.110344E+00, 0.787997E+00}, {-.123169E+00, 0.779981E+00}, + {-.136311E+00, 0.771619E+00}, {-.149747E+00, 0.762912E+00}, + {-.163459E+00, 0.753864E+00}, {-.177425E+00, 0.744475E+00}, + {-.191627E+00, 0.734751E+00}, {-.206042E+00, 0.724694E+00}, + {-.220652E+00, 0.714309E+00}, {-.235436E+00, 0.703600E+00}, + {-.250374E+00, 0.692575E+00}, {-.265445E+00, 0.681238E+00}, + {-.280632E+00, 0.669597E+00}, {-.295913E+00, 0.657659E+00}, + {-.311270E+00, 0.645433E+00}, {-.326683E+00, 0.632926E+00}, + {-.342133E+00, 0.620149E+00}, {-.357603E+00, 0.607110E+00}, + {-.373073E+00, 0.593821E+00}, {-.388526E+00, 0.580292E+00}, + {-.403943E+00, 0.566535E+00}, {-.419308E+00, 0.552562E+00}, + {-.434604E+00, 0.538385E+00}, {-.449814E+00, 0.524018E+00}, + {-.464921E+00, 0.509473E+00}, {-.479910E+00, 0.494766E+00}, + {-.494766E+00, 0.479910E+00}, {-.509473E+00, 0.464921E+00}, + {-.524018E+00, 0.449814E+00}, {-.538385E+00, 0.434604E+00}, + {-.552562E+00, 0.419308E+00}, {-.566535E+00, 0.403943E+00}, + {-.580292E+00, 0.388526E+00}, {-.593821E+00, 0.373073E+00}, + {-.607110E+00, 0.357603E+00}, {-.620149E+00, 0.342133E+00}, + {-.632926E+00, 0.326683E+00}, {-.645433E+00, 0.311270E+00}, + {-.657659E+00, 0.295913E+00}, {-.669597E+00, 0.280632E+00}, + {-.681238E+00, 0.265445E+00}, {-.692575E+00, 0.250374E+00}, + {-.703600E+00, 0.235436E+00}, {-.714309E+00, 0.220652E+00}, + {-.724694E+00, 0.206042E+00}, {-.734751E+00, 0.191627E+00}, + {-.744475E+00, 0.177425E+00}, {-.753864E+00, 0.163459E+00}, + {-.762912E+00, 0.149747E+00}, {-.771619E+00, 0.136311E+00}, + {-.779981E+00, 0.123169E+00}, {-.787997E+00, 0.110344E+00}, + {-.795667E+00, 0.978539E-01}, {-.802989E+00, 0.857193E-01}, + {-.809964E+00, 0.739597E-01}, {-.816593E+00, 0.625945E-01}, + {-.822875E+00, 0.516427E-01}, {-.828813E+00, 0.411231E-01}, + {-.834409E+00, 0.310539E-01}, {-.839664E+00, 0.214530E-01}, + {-.844580E+00, 0.123375E-01}, {-.849161E+00, 0.372420E-02}, + {-.853408E+00, -.437094E-02}, {-.857325E+00, -.119327E-01}, + {-.860915E+00, -.189465E-01}, {-.864181E+00, -.253987E-01}, + {-.867125E+00, -.312767E-01}, {-.869750E+00, -.365688E-01}, + {-.872059E+00, -.412642E-01}, {-.874055E+00, -.453535E-01}, + {-.875740E+00, -.488282E-01}, {-.877115E+00, -.516811E-01}, + {-.878183E+00, -.539062E-01}, {-.878945E+00, -.554990E-01}, + {-.879402E+00, -.564560E-01}, {-.879554E+00, -.567752E-01}, + }, + { + {0.746346E-01, 0.883551E+00}, {0.743064E-01, 0.883397E+00}, + {0.733225E-01, 0.882934E+00}, {0.716849E-01, 0.882162E+00}, + {0.693973E-01, 0.881080E+00}, {0.664643E-01, 0.879686E+00}, + {0.628924E-01, 0.877978E+00}, {0.586890E-01, 0.875954E+00}, + {0.538627E-01, 0.873612E+00}, {0.484237E-01, 0.870949E+00}, + {0.423831E-01, 0.867962E+00}, {0.357529E-01, 0.864647E+00}, + {0.285464E-01, 0.861003E+00}, {0.207778E-01, 0.857024E+00}, + {0.124622E-01, 0.852709E+00}, {0.361527E-02, 0.848054E+00}, + {-.574629E-02, 0.843056E+00}, {-.156052E-01, 0.837713E+00}, + {-.259437E-01, 0.832021E+00}, {-.367433E-01, 0.825979E+00}, + {-.479850E-01, 0.819584E+00}, {-.596497E-01, 0.812835E+00}, + {-.717177E-01, 0.805731E+00}, {-.841690E-01, 0.798271E+00}, + {-.969833E-01, 0.790455E+00}, {-.110140E+00, 0.782284E+00}, + {-.123619E+00, 0.773757E+00}, {-.137400E+00, 0.764878E+00}, + {-.151461E+00, 0.755647E+00}, {-.165781E+00, 0.746068E+00}, + {-.180341E+00, 0.736143E+00}, {-.195119E+00, 0.725877E+00}, + {-.210094E+00, 0.715274E+00}, {-.225246E+00, 0.704339E+00}, + {-.240555E+00, 0.693078E+00}, {-.255999E+00, 0.681497E+00}, + {-.271559E+00, 0.669604E+00}, {-.287215E+00, 0.657405E+00}, + {-.302946E+00, 0.644910E+00}, {-.318734E+00, 0.632126E+00}, + {-.334559E+00, 0.619064E+00}, {-.350402E+00, 0.605733E+00}, + {-.366244E+00, 0.592145E+00}, {-.382067E+00, 0.578310E+00}, + {-.397853E+00, 0.564240E+00}, {-.413583E+00, 0.549947E+00}, + {-.429241E+00, 0.535445E+00}, {-.444810E+00, 0.520746E+00}, + {-.460273E+00, 0.505865E+00}, {-.475614E+00, 0.490816E+00}, + {-.490816E+00, 0.475614E+00}, {-.505865E+00, 0.460273E+00}, + {-.520746E+00, 0.444810E+00}, {-.535445E+00, 0.429241E+00}, + {-.549947E+00, 0.413583E+00}, {-.564240E+00, 0.397853E+00}, + {-.578310E+00, 0.382067E+00}, {-.592145E+00, 0.366244E+00}, + {-.605733E+00, 0.350402E+00}, {-.619064E+00, 0.334559E+00}, + {-.632126E+00, 0.318734E+00}, {-.644910E+00, 0.302946E+00}, + {-.657405E+00, 0.287215E+00}, {-.669604E+00, 0.271559E+00}, + {-.681497E+00, 0.255999E+00}, {-.693078E+00, 0.240555E+00}, + {-.704339E+00, 0.225246E+00}, {-.715274E+00, 0.210094E+00}, + {-.725877E+00, 0.195119E+00}, {-.736143E+00, 0.180341E+00}, + {-.746068E+00, 0.165781E+00}, {-.755647E+00, 0.151461E+00}, + {-.764878E+00, 0.137400E+00}, {-.773757E+00, 0.123619E+00}, + {-.782284E+00, 0.110140E+00}, {-.790455E+00, 0.969833E-01}, + {-.798271E+00, 0.841690E-01}, {-.805731E+00, 0.717177E-01}, + {-.812835E+00, 0.596497E-01}, {-.819584E+00, 0.479850E-01}, + {-.825979E+00, 0.367433E-01}, {-.832021E+00, 0.259437E-01}, + {-.837713E+00, 0.156052E-01}, {-.843056E+00, 0.574629E-02}, + {-.848054E+00, -.361527E-02}, {-.852709E+00, -.124622E-01}, + {-.857024E+00, -.207778E-01}, {-.861003E+00, -.285464E-01}, + {-.864647E+00, -.357529E-01}, {-.867962E+00, -.423831E-01}, + {-.870949E+00, -.484237E-01}, {-.873612E+00, -.538627E-01}, + {-.875954E+00, -.586890E-01}, {-.877978E+00, -.628924E-01}, + {-.879686E+00, -.664643E-01}, {-.881080E+00, -.693973E-01}, + {-.882162E+00, -.716849E-01}, {-.882934E+00, -.733225E-01}, + {-.883397E+00, -.743064E-01}, {-.883551E+00, -.746346E-01}, + }, + { + {0.931052E-01, 0.887565E+00}, {0.927677E-01, 0.887409E+00}, + {0.917559E-01, 0.886940E+00}, {0.900720E-01, 0.886158E+00}, + {0.877197E-01, 0.885061E+00}, {0.847041E-01, 0.883648E+00}, + {0.810316E-01, 0.881917E+00}, {0.767102E-01, 0.879865E+00}, + {0.717488E-01, 0.877490E+00}, {0.661580E-01, 0.874788E+00}, + {0.599494E-01, 0.871757E+00}, {0.531355E-01, 0.868393E+00}, + {0.457302E-01, 0.864693E+00}, {0.377481E-01, 0.860652E+00}, + {0.292050E-01, 0.856268E+00}, {0.201171E-01, 0.851537E+00}, + {0.105019E-01, 0.846456E+00}, {0.377057E-03, 0.841022E+00}, + {-.102388E-01, 0.835231E+00}, {-.213268E-01, 0.829082E+00}, + {-.328672E-01, 0.822572E+00}, {-.448402E-01, 0.815699E+00}, + {-.572256E-01, 0.808463E+00}, {-.700026E-01, 0.800861E+00}, + {-.831506E-01, 0.792895E+00}, {-.966484E-01, 0.784564E+00}, + {-.110475E+00, 0.775869E+00}, {-.124609E+00, 0.766811E+00}, + {-.139029E+00, 0.757393E+00}, {-.153714E+00, 0.747617E+00}, + {-.168642E+00, 0.737486E+00}, {-.183792E+00, 0.727005E+00}, + {-.199143E+00, 0.716177E+00}, {-.214673E+00, 0.705008E+00}, + {-.230362E+00, 0.693504E+00}, {-.246189E+00, 0.681672E+00}, + {-.262133E+00, 0.669518E+00}, {-.278174E+00, 0.657051E+00}, + {-.294290E+00, 0.644278E+00}, {-.310463E+00, 0.631209E+00}, + {-.326673E+00, 0.617854E+00}, {-.342900E+00, 0.604223E+00}, + {-.359124E+00, 0.590326E+00}, {-.375328E+00, 0.576175E+00}, + {-.391492E+00, 0.561783E+00}, {-.407598E+00, 0.547162E+00}, + {-.423629E+00, 0.532325E+00}, {-.439567E+00, 0.517285E+00}, + {-.455395E+00, 0.502057E+00}, {-.471097E+00, 0.486656E+00}, + {-.486656E+00, 0.471097E+00}, {-.502057E+00, 0.455395E+00}, + {-.517285E+00, 0.439567E+00}, {-.532325E+00, 0.423629E+00}, + {-.547162E+00, 0.407598E+00}, {-.561783E+00, 0.391492E+00}, + {-.576175E+00, 0.375328E+00}, {-.590326E+00, 0.359124E+00}, + {-.604223E+00, 0.342900E+00}, {-.617854E+00, 0.326673E+00}, + {-.631209E+00, 0.310463E+00}, {-.644278E+00, 0.294290E+00}, + {-.657051E+00, 0.278174E+00}, {-.669518E+00, 0.262133E+00}, + {-.681672E+00, 0.246189E+00}, {-.693504E+00, 0.230362E+00}, + {-.705008E+00, 0.214673E+00}, {-.716177E+00, 0.199143E+00}, + {-.727005E+00, 0.183792E+00}, {-.737486E+00, 0.168642E+00}, + {-.747617E+00, 0.153714E+00}, {-.757393E+00, 0.139029E+00}, + {-.766811E+00, 0.124609E+00}, {-.775869E+00, 0.110475E+00}, + {-.784564E+00, 0.966484E-01}, {-.792895E+00, 0.831506E-01}, + {-.800861E+00, 0.700026E-01}, {-.808463E+00, 0.572256E-01}, + {-.815699E+00, 0.448402E-01}, {-.822572E+00, 0.328672E-01}, + {-.829082E+00, 0.213268E-01}, {-.835231E+00, 0.102388E-01}, + {-.841022E+00, -.377057E-03}, {-.846456E+00, -.105019E-01}, + {-.851537E+00, -.201171E-01}, {-.856268E+00, -.292050E-01}, + {-.860652E+00, -.377481E-01}, {-.864693E+00, -.457302E-01}, + {-.868393E+00, -.531355E-01}, {-.871757E+00, -.599494E-01}, + {-.874788E+00, -.661580E-01}, {-.877490E+00, -.717488E-01}, + {-.879865E+00, -.767102E-01}, {-.881917E+00, -.810316E-01}, + {-.883648E+00, -.847041E-01}, {-.885061E+00, -.877197E-01}, + {-.886158E+00, -.900720E-01}, {-.886940E+00, -.917559E-01}, + {-.887409E+00, -.927677E-01}, {-.887565E+00, -.931052E-01}, + }, + { + {0.112225E+00, 0.891596E+00}, {0.111878E+00, 0.891438E+00}, + {0.110838E+00, 0.890963E+00}, {0.109106E+00, 0.890171E+00}, + {0.106687E+00, 0.889059E+00}, {0.103585E+00, 0.887627E+00}, + {0.998088E-01, 0.885873E+00}, {0.953652E-01, 0.883792E+00}, + {0.902642E-01, 0.881384E+00}, {0.845165E-01, 0.878643E+00}, + {0.781341E-01, 0.875568E+00}, {0.711305E-01, 0.872153E+00}, + {0.635197E-01, 0.868396E+00}, {0.553172E-01, 0.864292E+00}, + {0.465393E-01, 0.859838E+00}, {0.372028E-01, 0.855030E+00}, + {0.273259E-01, 0.849863E+00}, {0.169268E-01, 0.844336E+00}, + {0.602490E-02, 0.838444E+00}, {-.536023E-02, 0.832185E+00}, + {-.172084E-01, 0.825556E+00}, {-.294990E-01, 0.818556E+00}, + {-.422112E-01, 0.811183E+00}, {-.553236E-01, 0.803436E+00}, + {-.688150E-01, 0.795315E+00}, {-.826635E-01, 0.786819E+00}, + {-.968476E-01, 0.777950E+00}, {-.111345E+00, 0.768709E+00}, + {-.126135E+00, 0.759097E+00}, {-.141194E+00, 0.749118E+00}, + {-.156501E+00, 0.738774E+00}, {-.172033E+00, 0.728070E+00}, + {-.187770E+00, 0.717011E+00}, {-.203690E+00, 0.705601E+00}, + {-.219771E+00, 0.693847E+00}, {-.235991E+00, 0.681755E+00}, + {-.252329E+00, 0.669332E+00}, {-.268766E+00, 0.656587E+00}, + {-.285278E+00, 0.643529E+00}, {-.301848E+00, 0.630166E+00}, + {-.318453E+00, 0.616508E+00}, {-.335074E+00, 0.602566E+00}, + {-.351692E+00, 0.588352E+00}, {-.368287E+00, 0.573876E+00}, + {-.384840E+00, 0.559152E+00}, {-.401333E+00, 0.544191E+00}, + {-.417747E+00, 0.529009E+00}, {-.434065E+00, 0.513618E+00}, + {-.450269E+00, 0.498033E+00}, {-.466343E+00, 0.482270E+00}, + {-.482270E+00, 0.466343E+00}, {-.498033E+00, 0.450269E+00}, + {-.513618E+00, 0.434065E+00}, {-.529009E+00, 0.417747E+00}, + {-.544191E+00, 0.401333E+00}, {-.559152E+00, 0.384840E+00}, + {-.573876E+00, 0.368287E+00}, {-.588352E+00, 0.351692E+00}, + {-.602566E+00, 0.335074E+00}, {-.616508E+00, 0.318453E+00}, + {-.630166E+00, 0.301848E+00}, {-.643529E+00, 0.285278E+00}, + {-.656587E+00, 0.268766E+00}, {-.669332E+00, 0.252329E+00}, + {-.681755E+00, 0.235991E+00}, {-.693847E+00, 0.219771E+00}, + {-.705601E+00, 0.203690E+00}, {-.717011E+00, 0.187770E+00}, + {-.728070E+00, 0.172033E+00}, {-.738774E+00, 0.156501E+00}, + {-.749118E+00, 0.141194E+00}, {-.759097E+00, 0.126135E+00}, + {-.768709E+00, 0.111345E+00}, {-.777950E+00, 0.968476E-01}, + {-.786819E+00, 0.826635E-01}, {-.795315E+00, 0.688150E-01}, + {-.803436E+00, 0.553236E-01}, {-.811183E+00, 0.422112E-01}, + {-.818556E+00, 0.294990E-01}, {-.825556E+00, 0.172084E-01}, + {-.832185E+00, 0.536023E-02}, {-.838444E+00, -.602490E-02}, + {-.844336E+00, -.169268E-01}, {-.849863E+00, -.273259E-01}, + {-.855030E+00, -.372028E-01}, {-.859838E+00, -.465393E-01}, + {-.864292E+00, -.553172E-01}, {-.868396E+00, -.635197E-01}, + {-.872153E+00, -.711305E-01}, {-.875568E+00, -.781341E-01}, + {-.878643E+00, -.845165E-01}, {-.881384E+00, -.902642E-01}, + {-.883792E+00, -.953652E-01}, {-.885873E+00, -.998088E-01}, + {-.887627E+00, -.103585E+00}, {-.889059E+00, -.106687E+00}, + {-.890171E+00, -.109106E+00}, {-.890963E+00, -.110838E+00}, + {-.891438E+00, -.111878E+00}, {-.891596E+00, -.112225E+00}, + }, + { + {0.132037E+00, 0.895647E+00}, {0.131680E+00, 0.895487E+00}, + {0.130609E+00, 0.895006E+00}, {0.128828E+00, 0.894203E+00}, + {0.126339E+00, 0.893077E+00}, {0.123149E+00, 0.891626E+00}, + {0.119265E+00, 0.889848E+00}, {0.114695E+00, 0.887739E+00}, + {0.109449E+00, 0.885296E+00}, {0.103539E+00, 0.882516E+00}, + {0.969773E-01, 0.879396E+00}, {0.897774E-01, 0.875930E+00}, + {0.819543E-01, 0.872115E+00}, {0.735240E-01, 0.867946E+00}, + {0.645035E-01, 0.863420E+00}, {0.549104E-01, 0.858532E+00}, + {0.447632E-01, 0.853278E+00}, {0.340812E-01, 0.847655E+00}, + {0.228842E-01, 0.841659E+00}, {0.111926E-01, 0.835287E+00}, + {-.972810E-03, 0.828536E+00}, {-.135907E-01, 0.821405E+00}, + {-.266396E-01, 0.813891E+00}, {-.400976E-01, 0.805994E+00}, + {-.539426E-01, 0.797713E+00}, {-.681523E-01, 0.789047E+00}, + {-.827044E-01, 0.779998E+00}, {-.975764E-01, 0.770566E+00}, + {-.112746E+00, 0.760755E+00}, {-.128190E+00, 0.750566E+00}, + {-.143887E+00, 0.740002E+00}, {-.159813E+00, 0.729068E+00}, + {-.175948E+00, 0.717769E+00}, {-.192268E+00, 0.706110E+00}, + {-.208751E+00, 0.694096E+00}, {-.225376E+00, 0.681736E+00}, + {-.242120E+00, 0.669036E+00}, {-.258963E+00, 0.656004E+00}, + {-.275884E+00, 0.642650E+00}, {-.292860E+00, 0.628983E+00}, + {-.309873E+00, 0.615013E+00}, {-.326900E+00, 0.600751E+00}, + {-.343922E+00, 0.586208E+00}, {-.360920E+00, 0.571397E+00}, + {-.377874E+00, 0.556330E+00}, {-.394765E+00, 0.541020E+00}, + {-.411574E+00, 0.525481E+00}, {-.428284E+00, 0.509728E+00}, + {-.444876E+00, 0.493775E+00}, {-.461333E+00, 0.477638E+00}, + {-.477638E+00, 0.461333E+00}, {-.493775E+00, 0.444876E+00}, + {-.509728E+00, 0.428284E+00}, {-.525481E+00, 0.411574E+00}, + {-.541020E+00, 0.394765E+00}, {-.556330E+00, 0.377874E+00}, + {-.571397E+00, 0.360920E+00}, {-.586208E+00, 0.343922E+00}, + {-.600751E+00, 0.326900E+00}, {-.615013E+00, 0.309873E+00}, + {-.628983E+00, 0.292860E+00}, {-.642650E+00, 0.275884E+00}, + {-.656004E+00, 0.258963E+00}, {-.669036E+00, 0.242120E+00}, + {-.681736E+00, 0.225376E+00}, {-.694096E+00, 0.208751E+00}, + {-.706110E+00, 0.192268E+00}, {-.717769E+00, 0.175948E+00}, + {-.729068E+00, 0.159813E+00}, {-.740002E+00, 0.143887E+00}, + {-.750566E+00, 0.128190E+00}, {-.760755E+00, 0.112746E+00}, + {-.770566E+00, 0.975764E-01}, {-.779998E+00, 0.827044E-01}, + {-.789047E+00, 0.681523E-01}, {-.797713E+00, 0.539426E-01}, + {-.805994E+00, 0.400976E-01}, {-.813891E+00, 0.266396E-01}, + {-.821405E+00, 0.135907E-01}, {-.828536E+00, 0.972810E-03}, + {-.835287E+00, -.111926E-01}, {-.841659E+00, -.228842E-01}, + {-.847655E+00, -.340812E-01}, {-.853278E+00, -.447632E-01}, + {-.858532E+00, -.549104E-01}, {-.863420E+00, -.645035E-01}, + {-.867946E+00, -.735240E-01}, {-.872115E+00, -.819543E-01}, + {-.875930E+00, -.897774E-01}, {-.879396E+00, -.969773E-01}, + {-.882516E+00, -.103539E+00}, {-.885296E+00, -.109449E+00}, + {-.887739E+00, -.114695E+00}, {-.889848E+00, -.119265E+00}, + {-.891626E+00, -.123149E+00}, {-.893077E+00, -.126339E+00}, + {-.894203E+00, -.128828E+00}, {-.895006E+00, -.130609E+00}, + {-.895487E+00, -.131680E+00}, {-.895647E+00, -.132037E+00}, + }, + { + {0.152585E+00, 0.899719E+00}, {0.152218E+00, 0.899557E+00}, + {0.151116E+00, 0.899070E+00}, {0.149283E+00, 0.898256E+00}, + {0.146723E+00, 0.897116E+00}, {0.143441E+00, 0.895645E+00}, + {0.139445E+00, 0.893843E+00}, {0.134744E+00, 0.891705E+00}, + {0.129349E+00, 0.889228E+00}, {0.123271E+00, 0.886408E+00}, + {0.116523E+00, 0.883241E+00}, {0.109120E+00, 0.879723E+00}, + {0.101077E+00, 0.875849E+00}, {0.924110E-01, 0.871614E+00}, + {0.831396E-01, 0.867014E+00}, {0.732812E-01, 0.862044E+00}, + {0.628550E-01, 0.856701E+00}, {0.518808E-01, 0.850979E+00}, + {0.403792E-01, 0.844876E+00}, {0.283714E-01, 0.838387E+00}, + {0.158787E-01, 0.831511E+00}, {0.292324E-02, 0.824244E+00}, + {-.104728E-01, 0.816585E+00}, {-.242869E-01, 0.808532E+00}, + {-.384963E-01, 0.800085E+00}, {-.530781E-01, 0.791243E+00}, + {-.680092E-01, 0.782008E+00}, {-.832667E-01, 0.772380E+00}, + {-.988275E-01, 0.762361E+00}, {-.114668E+00, 0.751954E+00}, + {-.130766E+00, 0.741163E+00}, {-.147098E+00, 0.729991E+00}, + {-.163641E+00, 0.718443E+00}, {-.180373E+00, 0.706526E+00}, + {-.197271E+00, 0.694244E+00}, {-.214312E+00, 0.681606E+00}, + {-.231475E+00, 0.668618E+00}, {-.248737E+00, 0.655290E+00}, + {-.266077E+00, 0.641630E+00}, {-.283473E+00, 0.627648E+00}, + {-.300904E+00, 0.613355E+00}, {-.318350E+00, 0.598762E+00}, + {-.335789E+00, 0.583880E+00}, {-.353202E+00, 0.568721E+00}, + {-.370569E+00, 0.553300E+00}, {-.387870E+00, 0.537629E+00}, + {-.405087E+00, 0.521722E+00}, {-.422200E+00, 0.505595E+00}, + {-.439192E+00, 0.489262E+00}, {-.456044E+00, 0.472740E+00}, + {-.472740E+00, 0.456044E+00}, {-.489262E+00, 0.439192E+00}, + {-.505595E+00, 0.422200E+00}, {-.521722E+00, 0.405087E+00}, + {-.537629E+00, 0.387870E+00}, {-.553300E+00, 0.370569E+00}, + {-.568721E+00, 0.353202E+00}, {-.583880E+00, 0.335789E+00}, + {-.598762E+00, 0.318350E+00}, {-.613355E+00, 0.300904E+00}, + {-.627648E+00, 0.283473E+00}, {-.641630E+00, 0.266077E+00}, + {-.655290E+00, 0.248737E+00}, {-.668618E+00, 0.231475E+00}, + {-.681606E+00, 0.214312E+00}, {-.694244E+00, 0.197271E+00}, + {-.706526E+00, 0.180373E+00}, {-.718443E+00, 0.163641E+00}, + {-.729991E+00, 0.147098E+00}, {-.741163E+00, 0.130766E+00}, + {-.751954E+00, 0.114668E+00}, {-.762361E+00, 0.988275E-01}, + {-.772380E+00, 0.832667E-01}, {-.782008E+00, 0.680092E-01}, + {-.791243E+00, 0.530781E-01}, {-.800085E+00, 0.384963E-01}, + {-.808532E+00, 0.242869E-01}, {-.816585E+00, 0.104728E-01}, + {-.824244E+00, -.292324E-02}, {-.831511E+00, -.158787E-01}, + {-.838387E+00, -.283714E-01}, {-.844876E+00, -.403792E-01}, + {-.850979E+00, -.518808E-01}, {-.856701E+00, -.628550E-01}, + {-.862044E+00, -.732812E-01}, {-.867014E+00, -.831396E-01}, + {-.871614E+00, -.924110E-01}, {-.875849E+00, -.101077E+00}, + {-.879723E+00, -.109120E+00}, {-.883241E+00, -.116523E+00}, + {-.886408E+00, -.123271E+00}, {-.889228E+00, -.129349E+00}, + {-.891705E+00, -.134744E+00}, {-.893843E+00, -.139445E+00}, + {-.895645E+00, -.143441E+00}, {-.897116E+00, -.146723E+00}, + {-.898256E+00, -.149283E+00}, {-.899070E+00, -.151116E+00}, + {-.899557E+00, -.152218E+00}, {-.899719E+00, -.152585E+00}, + }, + { + {0.173921E+00, 0.903814E+00}, {0.173543E+00, 0.903650E+00}, + {0.172409E+00, 0.903156E+00}, {0.170522E+00, 0.902332E+00}, + {0.167888E+00, 0.901177E+00}, {0.164510E+00, 0.899687E+00}, + {0.160399E+00, 0.897860E+00}, {0.155562E+00, 0.895693E+00}, + {0.150011E+00, 0.893181E+00}, {0.143758E+00, 0.890320E+00}, + {0.136818E+00, 0.887107E+00}, {0.129204E+00, 0.883535E+00}, + {0.120934E+00, 0.879600E+00}, {0.112025E+00, 0.875297E+00}, + {0.102494E+00, 0.870621E+00}, {0.923611E-01, 0.865568E+00}, + {0.816463E-01, 0.860131E+00}, {0.703703E-01, 0.854308E+00}, + {0.585541E-01, 0.848094E+00}, {0.462197E-01, 0.841485E+00}, + {0.333893E-01, 0.834478E+00}, {0.200856E-01, 0.827071E+00}, + {0.633144E-02, 0.819261E+00}, {-.784986E-02, 0.811047E+00}, + {-.224349E-01, 0.802428E+00}, {-.374002E-01, 0.793404E+00}, + {-.527220E-01, 0.783975E+00}, {-.683767E-01, 0.774143E+00}, + {-.843406E-01, 0.763909E+00}, {-.100590E+00, 0.753277E+00}, + {-.117101E+00, 0.742249E+00}, {-.133851E+00, 0.730830E+00}, + {-.150815E+00, 0.719024E+00}, {-.167971E+00, 0.706839E+00}, + {-.185296E+00, 0.694279E+00}, {-.202766E+00, 0.681352E+00}, + {-.220359E+00, 0.668066E+00}, {-.238053E+00, 0.654431E+00}, + {-.255825E+00, 0.640454E+00}, {-.273654E+00, 0.626146E+00}, + {-.291517E+00, 0.611518E+00}, {-.309393E+00, 0.596582E+00}, + {-.327263E+00, 0.581348E+00}, {-.345104E+00, 0.565831E+00}, + {-.362896E+00, 0.550043E+00}, {-.380621E+00, 0.533998E+00}, + {-.398257E+00, 0.517711E+00}, {-.415787E+00, 0.501197E+00}, + {-.433191E+00, 0.484472E+00}, {-.450452E+00, 0.467551E+00}, + {-.467551E+00, 0.450452E+00}, {-.484472E+00, 0.433191E+00}, + {-.501197E+00, 0.415787E+00}, {-.517711E+00, 0.398257E+00}, + {-.533998E+00, 0.380621E+00}, {-.550043E+00, 0.362896E+00}, + {-.565831E+00, 0.345104E+00}, {-.581348E+00, 0.327263E+00}, + {-.596582E+00, 0.309393E+00}, {-.611518E+00, 0.291517E+00}, + {-.626146E+00, 0.273654E+00}, {-.640454E+00, 0.255825E+00}, + {-.654431E+00, 0.238053E+00}, {-.668066E+00, 0.220359E+00}, + {-.681352E+00, 0.202766E+00}, {-.694279E+00, 0.185296E+00}, + {-.706839E+00, 0.167971E+00}, {-.719024E+00, 0.150815E+00}, + {-.730830E+00, 0.133851E+00}, {-.742249E+00, 0.117101E+00}, + {-.753277E+00, 0.100590E+00}, {-.763909E+00, 0.843406E-01}, + {-.774143E+00, 0.683767E-01}, {-.783975E+00, 0.527220E-01}, + {-.793404E+00, 0.374002E-01}, {-.802428E+00, 0.224349E-01}, + {-.811047E+00, 0.784986E-02}, {-.819261E+00, -.633144E-02}, + {-.827071E+00, -.200856E-01}, {-.834478E+00, -.333893E-01}, + {-.841485E+00, -.462197E-01}, {-.848094E+00, -.585541E-01}, + {-.854308E+00, -.703703E-01}, {-.860131E+00, -.816463E-01}, + {-.865568E+00, -.923611E-01}, {-.870621E+00, -.102494E+00}, + {-.875297E+00, -.112025E+00}, {-.879600E+00, -.120934E+00}, + {-.883535E+00, -.129204E+00}, {-.887107E+00, -.136818E+00}, + {-.890320E+00, -.143758E+00}, {-.893181E+00, -.150011E+00}, + {-.895693E+00, -.155562E+00}, {-.897860E+00, -.160399E+00}, + {-.899687E+00, -.164510E+00}, {-.901177E+00, -.167888E+00}, + {-.902332E+00, -.170522E+00}, {-.903156E+00, -.172409E+00}, + {-.903650E+00, -.173543E+00}, {-.903814E+00, -.173921E+00}, + }, + { + {0.196099E+00, 0.907933E+00}, {0.195709E+00, 0.907767E+00}, + {0.194542E+00, 0.907267E+00}, {0.192600E+00, 0.906432E+00}, + {0.189888E+00, 0.905262E+00}, {0.186411E+00, 0.903752E+00}, + {0.182179E+00, 0.901901E+00}, {0.177202E+00, 0.899704E+00}, + {0.171489E+00, 0.897156E+00}, {0.165056E+00, 0.894255E+00}, + {0.157916E+00, 0.890993E+00}, {0.150084E+00, 0.887366E+00}, + {0.141578E+00, 0.883369E+00}, {0.132416E+00, 0.878997E+00}, + {0.122617E+00, 0.874243E+00}, {0.112200E+00, 0.869102E+00}, + {0.101187E+00, 0.863570E+00}, {0.895986E-01, 0.857642E+00}, + {0.774573E-01, 0.851313E+00}, {0.647856E-01, 0.844579E+00}, + {0.516064E-01, 0.837437E+00}, {0.379431E-01, 0.829884E+00}, + {0.238194E-01, 0.821918E+00}, {0.925929E-02, 0.813536E+00}, + {-.571325E-02, 0.804738E+00}, {-.210740E-01, 0.795524E+00}, + {-.367986E-01, 0.785894E+00}, {-.528627E-01, 0.775850E+00}, + {-.692421E-01, 0.765392E+00}, {-.859125E-01, 0.754525E+00}, + {-.102850E+00, 0.743251E+00}, {-.120029E+00, 0.731575E+00}, + {-.137428E+00, 0.719501E+00}, {-.155021E+00, 0.707037E+00}, + {-.172786E+00, 0.694188E+00}, {-.190698E+00, 0.680962E+00}, + {-.208735E+00, 0.667366E+00}, {-.226874E+00, 0.653411E+00}, + {-.245091E+00, 0.639105E+00}, {-.263366E+00, 0.624459E+00}, + {-.281674E+00, 0.609484E+00}, {-.299996E+00, 0.594191E+00}, + {-.318308E+00, 0.578594E+00}, {-.336591E+00, 0.562704E+00}, + {-.354823E+00, 0.546537E+00}, {-.372985E+00, 0.530105E+00}, + {-.391055E+00, 0.513424E+00}, {-.409015E+00, 0.496510E+00}, + {-.426846E+00, 0.479378E+00}, {-.444529E+00, 0.462045E+00}, + {-.462045E+00, 0.444529E+00}, {-.479378E+00, 0.426846E+00}, + {-.496510E+00, 0.409015E+00}, {-.513424E+00, 0.391055E+00}, + {-.530105E+00, 0.372985E+00}, {-.546537E+00, 0.354823E+00}, + {-.562704E+00, 0.336591E+00}, {-.578594E+00, 0.318308E+00}, + {-.594191E+00, 0.299996E+00}, {-.609484E+00, 0.281674E+00}, + {-.624459E+00, 0.263366E+00}, {-.639105E+00, 0.245091E+00}, + {-.653411E+00, 0.226874E+00}, {-.667366E+00, 0.208735E+00}, + {-.680962E+00, 0.190698E+00}, {-.694188E+00, 0.172786E+00}, + {-.707037E+00, 0.155021E+00}, {-.719501E+00, 0.137428E+00}, + {-.731575E+00, 0.120029E+00}, {-.743251E+00, 0.102850E+00}, + {-.754525E+00, 0.859125E-01}, {-.765392E+00, 0.692421E-01}, + {-.775850E+00, 0.528627E-01}, {-.785894E+00, 0.367986E-01}, + {-.795524E+00, 0.210740E-01}, {-.804738E+00, 0.571325E-02}, + {-.813536E+00, -.925929E-02}, {-.821918E+00, -.238194E-01}, + {-.829884E+00, -.379431E-01}, {-.837437E+00, -.516064E-01}, + {-.844579E+00, -.647856E-01}, {-.851313E+00, -.774573E-01}, + {-.857642E+00, -.895986E-01}, {-.863570E+00, -.101187E+00}, + {-.869102E+00, -.112200E+00}, {-.874243E+00, -.122617E+00}, + {-.878997E+00, -.132416E+00}, {-.883369E+00, -.141578E+00}, + {-.887366E+00, -.150084E+00}, {-.890993E+00, -.157916E+00}, + {-.894255E+00, -.165056E+00}, {-.897156E+00, -.171489E+00}, + {-.899704E+00, -.177202E+00}, {-.901901E+00, -.182179E+00}, + {-.903752E+00, -.186411E+00}, {-.905262E+00, -.189888E+00}, + {-.906432E+00, -.192600E+00}, {-.907267E+00, -.194542E+00}, + {-.907767E+00, -.195709E+00}, {-.907933E+00, -.196099E+00}, + }, + { + {0.219179E+00, 0.912079E+00}, {0.218778E+00, 0.911910E+00}, + {0.217576E+00, 0.911404E+00}, {0.215576E+00, 0.910559E+00}, + {0.212783E+00, 0.909373E+00}, {0.209204E+00, 0.907844E+00}, + {0.204847E+00, 0.905968E+00}, {0.199722E+00, 0.903740E+00}, + {0.193843E+00, 0.901157E+00}, {0.187222E+00, 0.898212E+00}, + {0.179874E+00, 0.894902E+00}, {0.171816E+00, 0.891219E+00}, + {0.163065E+00, 0.887158E+00}, {0.153642E+00, 0.882713E+00}, + {0.143564E+00, 0.877879E+00}, {0.132853E+00, 0.872649E+00}, + {0.121531E+00, 0.867017E+00}, {0.109620E+00, 0.860980E+00}, + {0.971428E-01, 0.854532E+00}, {0.841224E-01, 0.847668E+00}, + {0.705828E-01, 0.840385E+00}, {0.565482E-01, 0.832680E+00}, + {0.420429E-01, 0.824551E+00}, {0.270917E-01, 0.815994E+00}, + {0.117193E-01, 0.807010E+00}, {-.404941E-02, 0.797598E+00}, + {-.201895E-01, 0.787758E+00}, {-.366759E-01, 0.777492E+00}, + {-.534838E-01, 0.766801E+00}, {-.705883E-01, 0.755689E+00}, + {-.879645E-01, 0.744158E+00}, {-.105588E+00, 0.732214E+00}, + {-.123434E+00, 0.719862E+00}, {-.141478E+00, 0.707107E+00}, + {-.159696E+00, 0.693956E+00}, {-.178064E+00, 0.680418E+00}, + {-.196559E+00, 0.666501E+00}, {-.215157E+00, 0.652213E+00}, + {-.233834E+00, 0.637565E+00}, {-.252569E+00, 0.622567E+00}, + {-.271337E+00, 0.607231E+00}, {-.290118E+00, 0.591568E+00}, + {-.308888E+00, 0.575592E+00}, {-.327627E+00, 0.559317E+00}, + {-.346314E+00, 0.542755E+00}, {-.364927E+00, 0.525922E+00}, + {-.383446E+00, 0.508832E+00}, {-.401851E+00, 0.491503E+00}, + {-.420123E+00, 0.473950E+00}, {-.438242E+00, 0.456191E+00}, + {-.456191E+00, 0.438242E+00}, {-.473950E+00, 0.420123E+00}, + {-.491503E+00, 0.401851E+00}, {-.508832E+00, 0.383446E+00}, + {-.525922E+00, 0.364927E+00}, {-.542755E+00, 0.346314E+00}, + {-.559317E+00, 0.327627E+00}, {-.575592E+00, 0.308888E+00}, + {-.591568E+00, 0.290118E+00}, {-.607231E+00, 0.271337E+00}, + {-.622567E+00, 0.252569E+00}, {-.637565E+00, 0.233834E+00}, + {-.652213E+00, 0.215157E+00}, {-.666501E+00, 0.196559E+00}, + {-.680418E+00, 0.178064E+00}, {-.693956E+00, 0.159696E+00}, + {-.707107E+00, 0.141478E+00}, {-.719862E+00, 0.123434E+00}, + {-.732214E+00, 0.105588E+00}, {-.744158E+00, 0.879645E-01}, + {-.755689E+00, 0.705883E-01}, {-.766801E+00, 0.534838E-01}, + {-.777492E+00, 0.366759E-01}, {-.787758E+00, 0.201895E-01}, + {-.797598E+00, 0.404941E-02}, {-.807010E+00, -.117193E-01}, + {-.815994E+00, -.270917E-01}, {-.824551E+00, -.420429E-01}, + {-.832680E+00, -.565482E-01}, {-.840385E+00, -.705828E-01}, + {-.847668E+00, -.841224E-01}, {-.854532E+00, -.971428E-01}, + {-.860980E+00, -.109620E+00}, {-.867017E+00, -.121531E+00}, + {-.872649E+00, -.132853E+00}, {-.877879E+00, -.143564E+00}, + {-.882713E+00, -.153642E+00}, {-.887158E+00, -.163065E+00}, + {-.891219E+00, -.171816E+00}, {-.894902E+00, -.179874E+00}, + {-.898212E+00, -.187222E+00}, {-.901157E+00, -.193843E+00}, + {-.903740E+00, -.199722E+00}, {-.905968E+00, -.204847E+00}, + {-.907844E+00, -.209204E+00}, {-.909373E+00, -.212783E+00}, + {-.910559E+00, -.215576E+00}, {-.911404E+00, -.217576E+00}, + {-.911910E+00, -.218778E+00}, {-.912079E+00, -.219179E+00}, + }, + { + {0.243229E+00, 0.916253E+00}, {0.242816E+00, 0.916082E+00}, + {0.241578E+00, 0.915570E+00}, {0.239518E+00, 0.914714E+00}, + {0.236641E+00, 0.913513E+00}, {0.232954E+00, 0.911963E+00}, + {0.228467E+00, 0.910062E+00}, {0.223190E+00, 0.907803E+00}, + {0.217136E+00, 0.905183E+00}, {0.210320E+00, 0.902195E+00}, + {0.202756E+00, 0.898834E+00}, {0.194463E+00, 0.895093E+00}, + {0.185459E+00, 0.890967E+00}, {0.175764E+00, 0.886447E+00}, + {0.165398E+00, 0.881529E+00}, {0.154383E+00, 0.876206E+00}, + {0.142741E+00, 0.870472E+00}, {0.130495E+00, 0.864321E+00}, + {0.117670E+00, 0.857748E+00}, {0.104289E+00, 0.850749E+00}, + {0.903768E-01, 0.843320E+00}, {0.759584E-01, 0.835456E+00}, + {0.610591E-01, 0.827156E+00}, {0.457040E-01, 0.818417E+00}, + {0.299188E-01, 0.809238E+00}, {0.137290E-01, 0.799618E+00}, + {-.283982E-02, 0.789559E+00}, {-.197620E-01, 0.779061E+00}, + {-.370119E-01, 0.768126E+00}, {-.545642E-01, 0.756758E+00}, + {-.723935E-01, 0.744959E+00}, {-.904744E-01, 0.732735E+00}, + {-.108782E+00, 0.720090E+00}, {-.127291E+00, 0.707032E+00}, + {-.145978E+00, 0.693567E+00}, {-.164816E+00, 0.679704E+00}, + {-.183783E+00, 0.665450E+00}, {-.202855E+00, 0.650815E+00}, + {-.222007E+00, 0.635810E+00}, {-.241216E+00, 0.620446E+00}, + {-.260460E+00, 0.604734E+00}, {-.279715E+00, 0.588687E+00}, + {-.298959E+00, 0.572317E+00}, {-.318170E+00, 0.555639E+00}, + {-.337326E+00, 0.538668E+00}, {-.356406E+00, 0.521418E+00}, + {-.375390E+00, 0.503904E+00}, {-.394256E+00, 0.486145E+00}, + {-.412985E+00, 0.468155E+00}, {-.431557E+00, 0.449953E+00}, + {-.449953E+00, 0.431557E+00}, {-.468155E+00, 0.412985E+00}, + {-.486145E+00, 0.394256E+00}, {-.503904E+00, 0.375390E+00}, + {-.521418E+00, 0.356406E+00}, {-.538668E+00, 0.337326E+00}, + {-.555639E+00, 0.318170E+00}, {-.572317E+00, 0.298959E+00}, + {-.588687E+00, 0.279715E+00}, {-.604734E+00, 0.260460E+00}, + {-.620446E+00, 0.241216E+00}, {-.635810E+00, 0.222007E+00}, + {-.650815E+00, 0.202855E+00}, {-.665450E+00, 0.183783E+00}, + {-.679704E+00, 0.164816E+00}, {-.693567E+00, 0.145978E+00}, + {-.707032E+00, 0.127291E+00}, {-.720090E+00, 0.108782E+00}, + {-.732735E+00, 0.904744E-01}, {-.744959E+00, 0.723935E-01}, + {-.756758E+00, 0.545642E-01}, {-.768126E+00, 0.370119E-01}, + {-.779061E+00, 0.197620E-01}, {-.789559E+00, 0.283982E-02}, + {-.799618E+00, -.137290E-01}, {-.809238E+00, -.299188E-01}, + {-.818417E+00, -.457040E-01}, {-.827156E+00, -.610591E-01}, + {-.835456E+00, -.759584E-01}, {-.843320E+00, -.903768E-01}, + {-.850749E+00, -.104289E+00}, {-.857748E+00, -.117670E+00}, + {-.864321E+00, -.130495E+00}, {-.870472E+00, -.142741E+00}, + {-.876206E+00, -.154383E+00}, {-.881529E+00, -.165398E+00}, + {-.886447E+00, -.175764E+00}, {-.890967E+00, -.185459E+00}, + {-.895093E+00, -.194463E+00}, {-.898834E+00, -.202756E+00}, + {-.902195E+00, -.210320E+00}, {-.905183E+00, -.217136E+00}, + {-.907803E+00, -.223190E+00}, {-.910062E+00, -.228467E+00}, + {-.911963E+00, -.232954E+00}, {-.913513E+00, -.236641E+00}, + {-.914714E+00, -.239518E+00}, {-.915570E+00, -.241578E+00}, + {-.916082E+00, -.242816E+00}, {-.916253E+00, -.243229E+00}, + }, + { + {0.268323E+00, 0.920458E+00}, {0.267897E+00, 0.920285E+00}, + {0.266621E+00, 0.919766E+00}, {0.264498E+00, 0.918899E+00}, + {0.261534E+00, 0.917683E+00}, {0.257735E+00, 0.916113E+00}, + {0.253112E+00, 0.914185E+00}, {0.247676E+00, 0.911895E+00}, + {0.241441E+00, 0.909237E+00}, {0.234421E+00, 0.906205E+00}, + {0.226634E+00, 0.902792E+00}, {0.218097E+00, 0.898992E+00}, + {0.208829E+00, 0.894797E+00}, {0.198852E+00, 0.890200E+00}, + {0.188186E+00, 0.885195E+00}, {0.176855E+00, 0.879775E+00}, + {0.164882E+00, 0.873933E+00}, {0.152290E+00, 0.867663E+00}, + {0.139105E+00, 0.860961E+00}, {0.125351E+00, 0.853820E+00}, + {0.111053E+00, 0.846236E+00}, {0.962384E-01, 0.838206E+00}, + {0.809318E-01, 0.829727E+00}, {0.651596E-01, 0.820796E+00}, + {0.489480E-01, 0.811413E+00}, {0.323234E-01, 0.801576E+00}, + {0.153121E-01, 0.791287E+00}, {-.205984E-02, 0.780545E+00}, + {-.197661E-01, 0.769355E+00}, {-.377805E-01, 0.757717E+00}, + {-.560772E-01, 0.745638E+00}, {-.746304E-01, 0.733120E+00}, + {-.934144E-01, 0.720170E+00}, {-.112404E+00, 0.706794E+00}, + {-.131573E+00, 0.693000E+00}, {-.150898E+00, 0.678796E+00}, + {-.170353E+00, 0.664190E+00}, {-.189914E+00, 0.649193E+00}, + {-.209556E+00, 0.633816E+00}, {-.229257E+00, 0.618069E+00}, + {-.248991E+00, 0.601965E+00}, {-.268737E+00, 0.585516E+00}, + {-.288471E+00, 0.568736E+00}, {-.308170E+00, 0.551640E+00}, + {-.327812E+00, 0.534242E+00}, {-.347377E+00, 0.516558E+00}, + {-.366841E+00, 0.498603E+00}, {-.386185E+00, 0.480396E+00}, + {-.405388E+00, 0.461952E+00}, {-.424429E+00, 0.443290E+00}, + {-.443290E+00, 0.424429E+00}, {-.461952E+00, 0.405388E+00}, + {-.480396E+00, 0.386185E+00}, {-.498603E+00, 0.366841E+00}, + {-.516558E+00, 0.347377E+00}, {-.534242E+00, 0.327812E+00}, + {-.551640E+00, 0.308170E+00}, {-.568736E+00, 0.288471E+00}, + {-.585516E+00, 0.268737E+00}, {-.601965E+00, 0.248991E+00}, + {-.618069E+00, 0.229257E+00}, {-.633816E+00, 0.209556E+00}, + {-.649193E+00, 0.189914E+00}, {-.664190E+00, 0.170353E+00}, + {-.678796E+00, 0.150898E+00}, {-.693000E+00, 0.131573E+00}, + {-.706794E+00, 0.112404E+00}, {-.720170E+00, 0.934144E-01}, + {-.733120E+00, 0.746304E-01}, {-.745638E+00, 0.560772E-01}, + {-.757717E+00, 0.377805E-01}, {-.769355E+00, 0.197661E-01}, + {-.780545E+00, 0.205984E-02}, {-.791287E+00, -.153121E-01}, + {-.801576E+00, -.323234E-01}, {-.811413E+00, -.489480E-01}, + {-.820796E+00, -.651596E-01}, {-.829727E+00, -.809318E-01}, + {-.838206E+00, -.962384E-01}, {-.846236E+00, -.111053E+00}, + {-.853820E+00, -.125351E+00}, {-.860961E+00, -.139105E+00}, + {-.867663E+00, -.152290E+00}, {-.873933E+00, -.164882E+00}, + {-.879775E+00, -.176855E+00}, {-.885195E+00, -.188186E+00}, + {-.890200E+00, -.198852E+00}, {-.894797E+00, -.208829E+00}, + {-.898992E+00, -.218097E+00}, {-.902792E+00, -.226634E+00}, + {-.906205E+00, -.234421E+00}, {-.909237E+00, -.241441E+00}, + {-.911895E+00, -.247676E+00}, {-.914185E+00, -.253112E+00}, + {-.916113E+00, -.257735E+00}, {-.917683E+00, -.261534E+00}, + {-.918899E+00, -.264498E+00}, {-.919766E+00, -.266621E+00}, + {-.920285E+00, -.267897E+00}, {-.920458E+00, -.268323E+00}, + }, + { + {0.294542E+00, 0.924695E+00}, {0.294103E+00, 0.924520E+00}, + {0.292788E+00, 0.923995E+00}, {0.290599E+00, 0.923118E+00}, + {0.287543E+00, 0.921885E+00}, {0.283628E+00, 0.920295E+00}, + {0.278863E+00, 0.918341E+00}, {0.273262E+00, 0.916019E+00}, + {0.266838E+00, 0.913322E+00}, {0.259606E+00, 0.910244E+00}, + {0.251585E+00, 0.906777E+00}, {0.242794E+00, 0.902915E+00}, + {0.233252E+00, 0.898649E+00}, {0.222982E+00, 0.893972E+00}, + {0.212006E+00, 0.888876E+00}, {0.200348E+00, 0.883354E+00}, + {0.188031E+00, 0.877400E+00}, {0.175080E+00, 0.871006E+00}, + {0.161522E+00, 0.864166E+00}, {0.147382E+00, 0.856876E+00}, + {0.132686E+00, 0.849130E+00}, {0.117460E+00, 0.840925E+00}, + {0.101733E+00, 0.832257E+00}, {0.855296E-01, 0.823125E+00}, + {0.688776E-01, 0.813526E+00}, {0.518040E-01, 0.803461E+00}, + {0.343356E-01, 0.792929E+00}, {0.164994E-01, 0.781931E+00}, + {-.167782E-02, 0.770471E+00}, {-.201694E-01, 0.758552E+00}, + {-.389487E-01, 0.746176E+00}, {-.579895E-01, 0.733350E+00}, + {-.772654E-01, 0.720079E+00}, {-.967506E-01, 0.706369E+00}, + {-.116419E+00, 0.692230E+00}, {-.136246E+00, 0.677668E+00}, + {-.156205E+00, 0.662694E+00}, {-.176272E+00, 0.647318E+00}, + {-.196421E+00, 0.631550E+00}, {-.216630E+00, 0.615403E+00}, + {-.236872E+00, 0.598889E+00}, {-.257126E+00, 0.582021E+00}, + {-.277366E+00, 0.564813E+00}, {-.297571E+00, 0.547279E+00}, + {-.317718E+00, 0.529437E+00}, {-.337784E+00, 0.511300E+00}, + {-.357748E+00, 0.492886E+00}, {-.377587E+00, 0.474212E+00}, + {-.397282E+00, 0.455296E+00}, {-.416812E+00, 0.436156E+00}, + {-.436156E+00, 0.416812E+00}, {-.455296E+00, 0.397282E+00}, + {-.474212E+00, 0.377587E+00}, {-.492886E+00, 0.357748E+00}, + {-.511300E+00, 0.337784E+00}, {-.529437E+00, 0.317718E+00}, + {-.547279E+00, 0.297571E+00}, {-.564813E+00, 0.277366E+00}, + {-.582021E+00, 0.257126E+00}, {-.598889E+00, 0.236872E+00}, + {-.615403E+00, 0.216630E+00}, {-.631550E+00, 0.196421E+00}, + {-.647318E+00, 0.176272E+00}, {-.662694E+00, 0.156205E+00}, + {-.677668E+00, 0.136246E+00}, {-.692230E+00, 0.116419E+00}, + {-.706369E+00, 0.967506E-01}, {-.720079E+00, 0.772654E-01}, + {-.733350E+00, 0.579895E-01}, {-.746176E+00, 0.389487E-01}, + {-.758552E+00, 0.201694E-01}, {-.770471E+00, 0.167782E-02}, + {-.781931E+00, -.164994E-01}, {-.792929E+00, -.343356E-01}, + {-.803461E+00, -.518040E-01}, {-.813526E+00, -.688776E-01}, + {-.823125E+00, -.855296E-01}, {-.832257E+00, -.101733E+00}, + {-.840925E+00, -.117460E+00}, {-.849130E+00, -.132686E+00}, + {-.856876E+00, -.147382E+00}, {-.864166E+00, -.161522E+00}, + {-.871006E+00, -.175080E+00}, {-.877400E+00, -.188031E+00}, + {-.883354E+00, -.200348E+00}, {-.888876E+00, -.212006E+00}, + {-.893972E+00, -.222982E+00}, {-.898649E+00, -.233252E+00}, + {-.902915E+00, -.242794E+00}, {-.906777E+00, -.251585E+00}, + {-.910244E+00, -.259606E+00}, {-.913322E+00, -.266838E+00}, + {-.916019E+00, -.273262E+00}, {-.918341E+00, -.278863E+00}, + {-.920295E+00, -.283628E+00}, {-.921885E+00, -.287543E+00}, + {-.923118E+00, -.290599E+00}, {-.923995E+00, -.292788E+00}, + {-.924520E+00, -.294103E+00}, {-.924695E+00, -.294542E+00}, + }, + { + {0.321979E+00, 0.928969E+00}, {0.321526E+00, 0.928792E+00}, + {0.320169E+00, 0.928260E+00}, {0.317912E+00, 0.927371E+00}, + {0.314760E+00, 0.926123E+00}, {0.310723E+00, 0.924512E+00}, + {0.305811E+00, 0.922531E+00}, {0.300036E+00, 0.920176E+00}, + {0.293414E+00, 0.917439E+00}, {0.285962E+00, 0.914313E+00}, + {0.277698E+00, 0.910791E+00}, {0.268643E+00, 0.906864E+00}, + {0.258816E+00, 0.902524E+00}, {0.248242E+00, 0.897762E+00}, + {0.236943E+00, 0.892571E+00}, {0.224944E+00, 0.886943E+00}, + {0.212270E+00, 0.880870E+00}, {0.198948E+00, 0.874344E+00}, + {0.185004E+00, 0.867361E+00}, {0.170464E+00, 0.859913E+00}, + {0.155355E+00, 0.851996E+00}, {0.139706E+00, 0.843605E+00}, + {0.123543E+00, 0.834739E+00}, {0.106894E+00, 0.825393E+00}, + {0.897874E-01, 0.815566E+00}, {0.722500E-01, 0.805259E+00}, + {0.543097E-01, 0.794470E+00}, {0.359940E-01, 0.783203E+00}, + {0.173304E-01, 0.771458E+00}, {-.165385E-02, 0.759240E+00}, + {-.209316E-01, 0.746553E+00}, {-.404759E-01, 0.733401E+00}, + {-.602599E-01, 0.719791E+00}, {-.802573E-01, 0.705731E+00}, + {-.100442E+00, 0.691228E+00}, {-.120787E+00, 0.676291E+00}, + {-.141267E+00, 0.660930E+00}, {-.161857E+00, 0.645155E+00}, + {-.182532E+00, 0.628978E+00}, {-.203266E+00, 0.612411E+00}, + {-.224035E+00, 0.595466E+00}, {-.244814E+00, 0.578159E+00}, + {-.265580E+00, 0.560503E+00}, {-.286310E+00, 0.542512E+00}, + {-.306980E+00, 0.524204E+00}, {-.327567E+00, 0.505595E+00}, + {-.348049E+00, 0.486701E+00}, {-.368404E+00, 0.467541E+00}, + {-.388610E+00, 0.448133E+00}, {-.408648E+00, 0.428495E+00}, + {-.428495E+00, 0.408648E+00}, {-.448133E+00, 0.388610E+00}, + {-.467541E+00, 0.368404E+00}, {-.486701E+00, 0.348049E+00}, + {-.505595E+00, 0.327567E+00}, {-.524204E+00, 0.306980E+00}, + {-.542512E+00, 0.286310E+00}, {-.560503E+00, 0.265580E+00}, + {-.578159E+00, 0.244814E+00}, {-.595466E+00, 0.224035E+00}, + {-.612411E+00, 0.203266E+00}, {-.628978E+00, 0.182532E+00}, + {-.645155E+00, 0.161857E+00}, {-.660930E+00, 0.141267E+00}, + {-.676291E+00, 0.120787E+00}, {-.691228E+00, 0.100442E+00}, + {-.705731E+00, 0.802573E-01}, {-.719791E+00, 0.602599E-01}, + {-.733401E+00, 0.404759E-01}, {-.746553E+00, 0.209316E-01}, + {-.759240E+00, 0.165385E-02}, {-.771458E+00, -.173304E-01}, + {-.783203E+00, -.359940E-01}, {-.794470E+00, -.543097E-01}, + {-.805259E+00, -.722500E-01}, {-.815566E+00, -.897874E-01}, + {-.825393E+00, -.106894E+00}, {-.834739E+00, -.123543E+00}, + {-.843605E+00, -.139706E+00}, {-.851996E+00, -.155355E+00}, + {-.859913E+00, -.170464E+00}, {-.867361E+00, -.185004E+00}, + {-.874344E+00, -.198948E+00}, {-.880870E+00, -.212270E+00}, + {-.886943E+00, -.224944E+00}, {-.892571E+00, -.236943E+00}, + {-.897762E+00, -.248242E+00}, {-.902524E+00, -.258816E+00}, + {-.906864E+00, -.268643E+00}, {-.910791E+00, -.277698E+00}, + {-.914313E+00, -.285962E+00}, {-.917439E+00, -.293414E+00}, + {-.920176E+00, -.300036E+00}, {-.922531E+00, -.305811E+00}, + {-.924512E+00, -.310723E+00}, {-.926123E+00, -.314760E+00}, + {-.927371E+00, -.317912E+00}, {-.928260E+00, -.320169E+00}, + {-.928792E+00, -.321526E+00}, {-.928969E+00, -.321979E+00}, + }, + { + {0.350735E+00, 0.933281E+00}, {0.350268E+00, 0.933101E+00}, + {0.348868E+00, 0.932563E+00}, {0.346538E+00, 0.931663E+00}, + {0.343287E+00, 0.930399E+00}, {0.339121E+00, 0.928766E+00}, + {0.334054E+00, 0.926758E+00}, {0.328099E+00, 0.924368E+00}, + {0.321271E+00, 0.921590E+00}, {0.313589E+00, 0.918415E+00}, + {0.305071E+00, 0.914835E+00}, {0.295740E+00, 0.910840E+00}, + {0.285616E+00, 0.906422E+00}, {0.274725E+00, 0.901572E+00}, + {0.263091E+00, 0.896281E+00}, {0.250739E+00, 0.890539E+00}, + {0.237696E+00, 0.884340E+00}, {0.223988E+00, 0.877676E+00}, + {0.209644E+00, 0.870539E+00}, {0.194690E+00, 0.862924E+00}, + {0.179155E+00, 0.854825E+00}, {0.163067E+00, 0.846238E+00}, + {0.146454E+00, 0.837159E+00}, {0.129345E+00, 0.827586E+00}, + {0.111768E+00, 0.817518E+00}, {0.937515E-01, 0.806953E+00}, + {0.753238E-01, 0.795893E+00}, {0.565130E-01, 0.784338E+00}, + {0.373472E-01, 0.772292E+00}, {0.178542E-01, 0.759758E+00}, + {-.193819E-02, 0.746740E+00}, {-.220026E-01, 0.733245E+00}, + {-.423116E-01, 0.719277E+00}, {-.628382E-01, 0.704846E+00}, + {-.835557E-01, 0.689959E+00}, {-.104437E+00, 0.674626E+00}, + {-.125457E+00, 0.658856E+00}, {-.146588E+00, 0.642662E+00}, + {-.167806E+00, 0.626054E+00}, {-.189085E+00, 0.609045E+00}, + {-.210399E+00, 0.591649E+00}, {-.231724E+00, 0.573880E+00}, + {-.253035E+00, 0.555754E+00}, {-.274310E+00, 0.537285E+00}, + {-.295522E+00, 0.518489E+00}, {-.316651E+00, 0.499385E+00}, + {-.337672E+00, 0.479990E+00}, {-.358563E+00, 0.460322E+00}, + {-.379303E+00, 0.440399E+00}, {-.399869E+00, 0.420242E+00}, + {-.420242E+00, 0.399869E+00}, {-.440399E+00, 0.379303E+00}, + {-.460322E+00, 0.358563E+00}, {-.479990E+00, 0.337672E+00}, + {-.499385E+00, 0.316651E+00}, {-.518489E+00, 0.295522E+00}, + {-.537285E+00, 0.274310E+00}, {-.555754E+00, 0.253035E+00}, + {-.573880E+00, 0.231724E+00}, {-.591649E+00, 0.210399E+00}, + {-.609045E+00, 0.189085E+00}, {-.626054E+00, 0.167806E+00}, + {-.642662E+00, 0.146588E+00}, {-.658856E+00, 0.125457E+00}, + {-.674626E+00, 0.104437E+00}, {-.689959E+00, 0.835557E-01}, + {-.704846E+00, 0.628382E-01}, {-.719277E+00, 0.423116E-01}, + {-.733245E+00, 0.220026E-01}, {-.746740E+00, 0.193819E-02}, + {-.759758E+00, -.178542E-01}, {-.772292E+00, -.373472E-01}, + {-.784338E+00, -.565130E-01}, {-.795893E+00, -.753238E-01}, + {-.806953E+00, -.937515E-01}, {-.817518E+00, -.111768E+00}, + {-.827586E+00, -.129345E+00}, {-.837159E+00, -.146454E+00}, + {-.846238E+00, -.163067E+00}, {-.854825E+00, -.179155E+00}, + {-.862924E+00, -.194690E+00}, {-.870539E+00, -.209644E+00}, + {-.877676E+00, -.223988E+00}, {-.884340E+00, -.237696E+00}, + {-.890539E+00, -.250739E+00}, {-.896281E+00, -.263091E+00}, + {-.901572E+00, -.274725E+00}, {-.906422E+00, -.285616E+00}, + {-.910840E+00, -.295740E+00}, {-.914835E+00, -.305071E+00}, + {-.918415E+00, -.313589E+00}, {-.921590E+00, -.321271E+00}, + {-.924368E+00, -.328099E+00}, {-.926758E+00, -.334054E+00}, + {-.928766E+00, -.339121E+00}, {-.930399E+00, -.343287E+00}, + {-.931663E+00, -.346538E+00}, {-.932563E+00, -.348868E+00}, + {-.933101E+00, -.350268E+00}, {-.933281E+00, -.350735E+00}, + }, + { + {0.380926E+00, 0.937634E+00}, {0.380443E+00, 0.937452E+00}, + {0.378998E+00, 0.936907E+00}, {0.376593E+00, 0.935996E+00}, + {0.373236E+00, 0.934715E+00}, {0.368937E+00, 0.933060E+00}, + {0.363708E+00, 0.931024E+00}, {0.357563E+00, 0.928599E+00}, + {0.350520E+00, 0.925778E+00}, {0.342597E+00, 0.922552E+00}, + {0.333815E+00, 0.918911E+00}, {0.324196E+00, 0.914845E+00}, + {0.313763E+00, 0.910345E+00}, {0.302543E+00, 0.905401E+00}, + {0.290560E+00, 0.900002E+00}, {0.277841E+00, 0.894141E+00}, + {0.264415E+00, 0.887808E+00}, {0.250307E+00, 0.880995E+00}, + {0.235548E+00, 0.873694E+00}, {0.220166E+00, 0.865900E+00}, + {0.204190E+00, 0.857607E+00}, {0.187648E+00, 0.848810E+00}, + {0.170570E+00, 0.839505E+00}, {0.152986E+00, 0.829690E+00}, + {0.134923E+00, 0.819363E+00}, {0.116412E+00, 0.808524E+00}, + {0.974814E-01, 0.797174E+00}, {0.781595E-01, 0.785313E+00}, + {0.584752E-01, 0.772946E+00}, {0.384569E-01, 0.760075E+00}, + {0.181330E-01, 0.746706E+00}, {-.246860E-02, 0.732845E+00}, + {-.233200E-01, 0.718498E+00}, {-.443937E-01, 0.703673E+00}, + {-.656624E-01, 0.688380E+00}, {-.870989E-01, 0.672627E+00}, + {-.108677E+00, 0.656426E+00}, {-.130369E+00, 0.639788E+00}, + {-.152149E+00, 0.622725E+00}, {-.173992E+00, 0.605251E+00}, + {-.195872E+00, 0.587379E+00}, {-.217763E+00, 0.569125E+00}, + {-.239641E+00, 0.550503E+00}, {-.261481E+00, 0.531531E+00}, + {-.283259E+00, 0.512224E+00}, {-.304951E+00, 0.492601E+00}, + {-.326533E+00, 0.472680E+00}, {-.347983E+00, 0.452479E+00}, + {-.369279E+00, 0.432019E+00}, {-.390398E+00, 0.411318E+00}, + {-.411318E+00, 0.390398E+00}, {-.432019E+00, 0.369279E+00}, + {-.452479E+00, 0.347983E+00}, {-.472680E+00, 0.326533E+00}, + {-.492601E+00, 0.304951E+00}, {-.512224E+00, 0.283259E+00}, + {-.531531E+00, 0.261481E+00}, {-.550503E+00, 0.239641E+00}, + {-.569125E+00, 0.217763E+00}, {-.587379E+00, 0.195872E+00}, + {-.605251E+00, 0.173992E+00}, {-.622725E+00, 0.152149E+00}, + {-.639788E+00, 0.130369E+00}, {-.656426E+00, 0.108677E+00}, + {-.672627E+00, 0.870989E-01}, {-.688380E+00, 0.656624E-01}, + {-.703673E+00, 0.443937E-01}, {-.718498E+00, 0.233200E-01}, + {-.732845E+00, 0.246860E-02}, {-.746706E+00, -.181330E-01}, + {-.760075E+00, -.384569E-01}, {-.772946E+00, -.584752E-01}, + {-.785313E+00, -.781595E-01}, {-.797174E+00, -.974814E-01}, + {-.808524E+00, -.116412E+00}, {-.819363E+00, -.134923E+00}, + {-.829690E+00, -.152986E+00}, {-.839505E+00, -.170570E+00}, + {-.848810E+00, -.187648E+00}, {-.857607E+00, -.204190E+00}, + {-.865900E+00, -.220166E+00}, {-.873694E+00, -.235548E+00}, + {-.880995E+00, -.250307E+00}, {-.887808E+00, -.264415E+00}, + {-.894141E+00, -.277841E+00}, {-.900002E+00, -.290560E+00}, + {-.905401E+00, -.302543E+00}, {-.910345E+00, -.313763E+00}, + {-.914845E+00, -.324196E+00}, {-.918911E+00, -.333815E+00}, + {-.922552E+00, -.342597E+00}, {-.925778E+00, -.350520E+00}, + {-.928599E+00, -.357563E+00}, {-.931024E+00, -.363708E+00}, + {-.933060E+00, -.368937E+00}, {-.934715E+00, -.373236E+00}, + {-.935996E+00, -.376593E+00}, {-.936907E+00, -.378998E+00}, + {-.937452E+00, -.380443E+00}, {-.937634E+00, -.380926E+00}, + }, + { + {0.412680E+00, 0.942031E+00}, {0.412181E+00, 0.941848E+00}, + {0.410688E+00, 0.941296E+00}, {0.408204E+00, 0.940373E+00}, + {0.404737E+00, 0.939076E+00}, {0.400297E+00, 0.937398E+00}, + {0.394898E+00, 0.935332E+00}, {0.388555E+00, 0.932871E+00}, + {0.381286E+00, 0.930005E+00}, {0.373111E+00, 0.926725E+00}, + {0.364053E+00, 0.923019E+00}, {0.354134E+00, 0.918878E+00}, + {0.343380E+00, 0.914291E+00}, {0.331817E+00, 0.909246E+00}, + {0.319471E+00, 0.903733E+00}, {0.306372E+00, 0.897743E+00}, + {0.292547E+00, 0.891266E+00}, {0.278026E+00, 0.884294E+00}, + {0.262838E+00, 0.876817E+00}, {0.247013E+00, 0.868831E+00}, + {0.230580E+00, 0.860329E+00}, {0.213570E+00, 0.851305E+00}, + {0.196012E+00, 0.841757E+00}, {0.177937E+00, 0.831682E+00}, + {0.159373E+00, 0.821077E+00}, {0.140352E+00, 0.809944E+00}, + {0.120902E+00, 0.798283E+00}, {0.101052E+00, 0.786095E+00}, + {0.808331E-01, 0.773384E+00}, {0.602727E-01, 0.760154E+00}, + {0.394001E-01, 0.746410E+00}, {0.182437E-01, 0.732158E+00}, + {-.316804E-02, 0.717406E+00}, {-.248071E-01, 0.702163E+00}, + {-.466457E-01, 0.686437E+00}, {-.686563E-01, 0.670238E+00}, + {-.908115E-01, 0.653579E+00}, {-.113084E+00, 0.636470E+00}, + {-.135448E+00, 0.618926E+00}, {-.157876E+00, 0.600959E+00}, + {-.180343E+00, 0.582584E+00}, {-.202822E+00, 0.563818E+00}, + {-.225289E+00, 0.544674E+00}, {-.247718E+00, 0.525171E+00}, + {-.270083E+00, 0.505327E+00}, {-.292362E+00, 0.485158E+00}, + {-.314531E+00, 0.464684E+00}, {-.336564E+00, 0.443925E+00}, + {-.358440E+00, 0.422900E+00}, {-.380136E+00, 0.401630E+00}, + {-.401630E+00, 0.380136E+00}, {-.422900E+00, 0.358440E+00}, + {-.443925E+00, 0.336564E+00}, {-.464684E+00, 0.314531E+00}, + {-.485158E+00, 0.292362E+00}, {-.505327E+00, 0.270083E+00}, + {-.525171E+00, 0.247718E+00}, {-.544674E+00, 0.225289E+00}, + {-.563818E+00, 0.202822E+00}, {-.582584E+00, 0.180343E+00}, + {-.600959E+00, 0.157876E+00}, {-.618926E+00, 0.135448E+00}, + {-.636470E+00, 0.113084E+00}, {-.653579E+00, 0.908115E-01}, + {-.670238E+00, 0.686563E-01}, {-.686437E+00, 0.466457E-01}, + {-.702163E+00, 0.248071E-01}, {-.717406E+00, 0.316804E-02}, + {-.732158E+00, -.182437E-01}, {-.746410E+00, -.394001E-01}, + {-.760154E+00, -.602727E-01}, {-.773384E+00, -.808331E-01}, + {-.786095E+00, -.101052E+00}, {-.798283E+00, -.120902E+00}, + {-.809944E+00, -.140352E+00}, {-.821077E+00, -.159373E+00}, + {-.831682E+00, -.177937E+00}, {-.841757E+00, -.196012E+00}, + {-.851305E+00, -.213570E+00}, {-.860329E+00, -.230580E+00}, + {-.868831E+00, -.247013E+00}, {-.876817E+00, -.262838E+00}, + {-.884294E+00, -.278026E+00}, {-.891266E+00, -.292547E+00}, + {-.897743E+00, -.306372E+00}, {-.903733E+00, -.319471E+00}, + {-.909246E+00, -.331817E+00}, {-.914291E+00, -.343380E+00}, + {-.918878E+00, -.354134E+00}, {-.923019E+00, -.364053E+00}, + {-.926725E+00, -.373111E+00}, {-.930005E+00, -.381286E+00}, + {-.932871E+00, -.388555E+00}, {-.935332E+00, -.394898E+00}, + {-.937398E+00, -.400297E+00}, {-.939076E+00, -.404737E+00}, + {-.940373E+00, -.408204E+00}, {-.941296E+00, -.410688E+00}, + {-.941848E+00, -.412181E+00}, {-.942031E+00, -.412680E+00}, + }, + { + {0.446144E+00, 0.946477E+00}, {0.445629E+00, 0.946291E+00}, + {0.444085E+00, 0.945732E+00}, {0.441517E+00, 0.944798E+00}, + {0.437934E+00, 0.943483E+00}, {0.433347E+00, 0.941782E+00}, + {0.427769E+00, 0.939686E+00}, {0.421218E+00, 0.937187E+00}, + {0.413712E+00, 0.934274E+00}, {0.405274E+00, 0.930936E+00}, + {0.395927E+00, 0.927162E+00}, {0.385696E+00, 0.922940E+00}, + {0.374606E+00, 0.918259E+00}, {0.362686E+00, 0.913106E+00}, + {0.349965E+00, 0.907470E+00}, {0.336471E+00, 0.901341E+00}, + {0.322234E+00, 0.894708E+00}, {0.307284E+00, 0.887562E+00}, + {0.291652E+00, 0.879895E+00}, {0.275369E+00, 0.871701E+00}, + {0.258465E+00, 0.862971E+00}, {0.240971E+00, 0.853703E+00}, + {0.222918E+00, 0.843891E+00}, {0.204337E+00, 0.833534E+00}, + {0.185257E+00, 0.822630E+00}, {0.165710E+00, 0.811179E+00}, + {0.145724E+00, 0.799182E+00}, {0.125331E+00, 0.786642E+00}, + {0.104561E+00, 0.773561E+00}, {0.834411E-01, 0.759944E+00}, + {0.620024E-01, 0.745797E+00}, {0.402735E-01, 0.731127E+00}, + {0.182832E-01, 0.715942E+00}, {-.393997E-02, 0.700251E+00}, + {-.263677E-01, 0.684062E+00}, {-.489721E-01, 0.667389E+00}, + {-.717252E-01, 0.650241E+00}, {-.945996E-01, 0.632632E+00}, + {-.117568E+00, 0.614576E+00}, {-.140604E+00, 0.596086E+00}, + {-.163680E+00, 0.577178E+00}, {-.186771E+00, 0.557868E+00}, + {-.209850E+00, 0.538172E+00}, {-.232891E+00, 0.518109E+00}, + {-.255870E+00, 0.497696E+00}, {-.278762E+00, 0.476952E+00}, + {-.301541E+00, 0.455897E+00}, {-.324185E+00, 0.434550E+00}, + {-.346669E+00, 0.412932E+00}, {-.368970E+00, 0.391065E+00}, + {-.391065E+00, 0.368970E+00}, {-.412932E+00, 0.346669E+00}, + {-.434550E+00, 0.324185E+00}, {-.455897E+00, 0.301541E+00}, + {-.476952E+00, 0.278762E+00}, {-.497696E+00, 0.255870E+00}, + {-.518109E+00, 0.232891E+00}, {-.538172E+00, 0.209850E+00}, + {-.557868E+00, 0.186771E+00}, {-.577178E+00, 0.163680E+00}, + {-.596086E+00, 0.140604E+00}, {-.614576E+00, 0.117568E+00}, + {-.632632E+00, 0.945996E-01}, {-.650241E+00, 0.717252E-01}, + {-.667389E+00, 0.489721E-01}, {-.684062E+00, 0.263677E-01}, + {-.700251E+00, 0.393997E-02}, {-.715942E+00, -.182832E-01}, + {-.731127E+00, -.402735E-01}, {-.745797E+00, -.620024E-01}, + {-.759944E+00, -.834411E-01}, {-.773561E+00, -.104561E+00}, + {-.786642E+00, -.125331E+00}, {-.799182E+00, -.145724E+00}, + {-.811179E+00, -.165710E+00}, {-.822630E+00, -.185257E+00}, + {-.833534E+00, -.204337E+00}, {-.843891E+00, -.222918E+00}, + {-.853703E+00, -.240971E+00}, {-.862971E+00, -.258465E+00}, + {-.871701E+00, -.275369E+00}, {-.879895E+00, -.291652E+00}, + {-.887562E+00, -.307284E+00}, {-.894708E+00, -.322234E+00}, + {-.901341E+00, -.336471E+00}, {-.907470E+00, -.349965E+00}, + {-.913106E+00, -.362686E+00}, {-.918259E+00, -.374606E+00}, + {-.922940E+00, -.385696E+00}, {-.927162E+00, -.395927E+00}, + {-.930936E+00, -.405274E+00}, {-.934274E+00, -.413712E+00}, + {-.937187E+00, -.421218E+00}, {-.939686E+00, -.427769E+00}, + {-.941782E+00, -.433347E+00}, {-.943483E+00, -.437934E+00}, + {-.944798E+00, -.441517E+00}, {-.945732E+00, -.444085E+00}, + {-.946291E+00, -.445629E+00}, {-.946477E+00, -.446144E+00}, + }, + { + {0.481485E+00, 0.950975E+00}, {0.480952E+00, 0.950786E+00}, + {0.479355E+00, 0.950221E+00}, {0.476699E+00, 0.949274E+00}, + {0.472993E+00, 0.947942E+00}, {0.468250E+00, 0.946216E+00}, + {0.462484E+00, 0.944089E+00}, {0.455715E+00, 0.941549E+00}, + {0.447962E+00, 0.938586E+00}, {0.439248E+00, 0.935187E+00}, + {0.429599E+00, 0.931339E+00}, {0.419041E+00, 0.927030E+00}, + {0.407603E+00, 0.922246E+00}, {0.395312E+00, 0.916976E+00}, + {0.382200E+00, 0.911205E+00}, {0.368297E+00, 0.904924E+00}, + {0.353633E+00, 0.898121E+00}, {0.338241E+00, 0.890786E+00}, + {0.322151E+00, 0.882910E+00}, {0.305396E+00, 0.874488E+00}, + {0.288006E+00, 0.865510E+00}, {0.270015E+00, 0.855974E+00}, + {0.251452E+00, 0.845875E+00}, {0.232350E+00, 0.835211E+00}, + {0.212739E+00, 0.823981E+00}, {0.192650E+00, 0.812184E+00}, + {0.172114E+00, 0.799823E+00}, {0.151162E+00, 0.786899E+00}, + {0.129823E+00, 0.773418E+00}, {0.108128E+00, 0.759383E+00}, + {0.861064E-01, 0.744802E+00}, {0.637873E-01, 0.729681E+00}, + {0.412002E-01, 0.714029E+00}, {0.183740E-01, 0.697856E+00}, + {-.466248E-02, 0.681172E+00}, {-.278808E-01, 0.663988E+00}, + {-.512527E-01, 0.646318E+00}, {-.747503E-01, 0.628174E+00}, + {-.983459E-01, 0.609571E+00}, {-.122012E+00, 0.590524E+00}, + {-.145722E+00, 0.571048E+00}, {-.169448E+00, 0.551161E+00}, + {-.193164E+00, 0.530879E+00}, {-.216845E+00, 0.510222E+00}, + {-.240464E+00, 0.489207E+00}, {-.263995E+00, 0.467855E+00}, + {-.287414E+00, 0.446185E+00}, {-.310696E+00, 0.424219E+00}, + {-.333817E+00, 0.401976E+00}, {-.356753E+00, 0.379480E+00}, + {-.379480E+00, 0.356753E+00}, {-.401976E+00, 0.333817E+00}, + {-.424219E+00, 0.310696E+00}, {-.446185E+00, 0.287414E+00}, + {-.467855E+00, 0.263995E+00}, {-.489207E+00, 0.240464E+00}, + {-.510222E+00, 0.216845E+00}, {-.530879E+00, 0.193164E+00}, + {-.551161E+00, 0.169448E+00}, {-.571048E+00, 0.145722E+00}, + {-.590524E+00, 0.122012E+00}, {-.609571E+00, 0.983459E-01}, + {-.628174E+00, 0.747503E-01}, {-.646318E+00, 0.512527E-01}, + {-.663988E+00, 0.278808E-01}, {-.681172E+00, 0.466248E-02}, + {-.697856E+00, -.183740E-01}, {-.714029E+00, -.412002E-01}, + {-.729681E+00, -.637873E-01}, {-.744802E+00, -.861064E-01}, + {-.759383E+00, -.108128E+00}, {-.773418E+00, -.129823E+00}, + {-.786899E+00, -.151162E+00}, {-.799823E+00, -.172114E+00}, + {-.812184E+00, -.192650E+00}, {-.823981E+00, -.212739E+00}, + {-.835211E+00, -.232350E+00}, {-.845875E+00, -.251452E+00}, + {-.855974E+00, -.270015E+00}, {-.865510E+00, -.288006E+00}, + {-.874488E+00, -.305396E+00}, {-.882910E+00, -.322151E+00}, + {-.890786E+00, -.338241E+00}, {-.898121E+00, -.353633E+00}, + {-.904924E+00, -.368297E+00}, {-.911205E+00, -.382200E+00}, + {-.916976E+00, -.395312E+00}, {-.922246E+00, -.407603E+00}, + {-.927030E+00, -.419041E+00}, {-.931339E+00, -.429599E+00}, + {-.935187E+00, -.439248E+00}, {-.938586E+00, -.447962E+00}, + {-.941549E+00, -.455715E+00}, {-.944089E+00, -.462484E+00}, + {-.946216E+00, -.468250E+00}, {-.947942E+00, -.472993E+00}, + {-.949274E+00, -.476699E+00}, {-.950221E+00, -.479355E+00}, + {-.950786E+00, -.480952E+00}, {-.950975E+00, -.481485E+00}, + }, + { + {0.518892E+00, 0.955528E+00}, {0.518340E+00, 0.955338E+00}, + {0.516686E+00, 0.954765E+00}, {0.513937E+00, 0.953806E+00}, + {0.510102E+00, 0.952455E+00}, {0.505195E+00, 0.950704E+00}, + {0.499232E+00, 0.948543E+00}, {0.492232E+00, 0.945961E+00}, + {0.484219E+00, 0.942943E+00}, {0.475217E+00, 0.939478E+00}, + {0.465254E+00, 0.935550E+00}, {0.454356E+00, 0.931145E+00}, + {0.442555E+00, 0.926249E+00}, {0.429880E+00, 0.920849E+00}, + {0.416363E+00, 0.914930E+00}, {0.402037E+00, 0.908480E+00}, + {0.386933E+00, 0.901488E+00}, {0.371084E+00, 0.893943E+00}, + {0.354523E+00, 0.885838E+00}, {0.337283E+00, 0.877162E+00}, + {0.319395E+00, 0.867912E+00}, {0.300892E+00, 0.858080E+00}, + {0.281806E+00, 0.847665E+00}, {0.262170E+00, 0.836663E+00}, + {0.242014E+00, 0.825074E+00}, {0.221370E+00, 0.812898E+00}, + {0.200270E+00, 0.800138E+00}, {0.178743E+00, 0.786796E+00}, + {0.156822E+00, 0.772878E+00}, {0.134535E+00, 0.758388E+00}, + {0.111914E+00, 0.743334E+00}, {0.889872E-01, 0.727723E+00}, + {0.657854E-01, 0.711566E+00}, {0.423377E-01, 0.694872E+00}, + {0.186731E-01, 0.677653E+00}, {-.517948E-02, 0.659920E+00}, + {-.291913E-01, 0.641687E+00}, {-.533341E-01, 0.622969E+00}, + {-.775796E-01, 0.603780E+00}, {-.101900E+00, 0.584136E+00}, + {-.126268E+00, 0.564053E+00}, {-.150656E+00, 0.543550E+00}, + {-.175036E+00, 0.522644E+00}, {-.199383E+00, 0.501354E+00}, + {-.223670E+00, 0.479701E+00}, {-.247871E+00, 0.457703E+00}, + {-.271960E+00, 0.435382E+00}, {-.295912E+00, 0.412760E+00}, + {-.319702E+00, 0.389858E+00}, {-.343305E+00, 0.366699E+00}, + {-.366699E+00, 0.343305E+00}, {-.389858E+00, 0.319702E+00}, + {-.412760E+00, 0.295912E+00}, {-.435382E+00, 0.271960E+00}, + {-.457703E+00, 0.247871E+00}, {-.479701E+00, 0.223670E+00}, + {-.501354E+00, 0.199383E+00}, {-.522644E+00, 0.175036E+00}, + {-.543550E+00, 0.150656E+00}, {-.564053E+00, 0.126268E+00}, + {-.584136E+00, 0.101900E+00}, {-.603780E+00, 0.775796E-01}, + {-.622969E+00, 0.533341E-01}, {-.641687E+00, 0.291913E-01}, + {-.659920E+00, 0.517948E-02}, {-.677653E+00, -.186731E-01}, + {-.694872E+00, -.423377E-01}, {-.711566E+00, -.657854E-01}, + {-.727723E+00, -.889872E-01}, {-.743334E+00, -.111914E+00}, + {-.758388E+00, -.134535E+00}, {-.772878E+00, -.156822E+00}, + {-.786796E+00, -.178743E+00}, {-.800138E+00, -.200270E+00}, + {-.812898E+00, -.221370E+00}, {-.825074E+00, -.242014E+00}, + {-.836663E+00, -.262170E+00}, {-.847665E+00, -.281806E+00}, + {-.858080E+00, -.300892E+00}, {-.867912E+00, -.319395E+00}, + {-.877162E+00, -.337283E+00}, {-.885838E+00, -.354523E+00}, + {-.893943E+00, -.371084E+00}, {-.901488E+00, -.386933E+00}, + {-.908480E+00, -.402037E+00}, {-.914930E+00, -.416363E+00}, + {-.920849E+00, -.429880E+00}, {-.926249E+00, -.442555E+00}, + {-.931145E+00, -.454356E+00}, {-.935550E+00, -.465254E+00}, + {-.939478E+00, -.475217E+00}, {-.942943E+00, -.484219E+00}, + {-.945961E+00, -.492232E+00}, {-.948543E+00, -.499232E+00}, + {-.950704E+00, -.505195E+00}, {-.952455E+00, -.510102E+00}, + {-.953806E+00, -.513937E+00}, {-.954765E+00, -.516686E+00}, + {-.955338E+00, -.518340E+00}, {-.955528E+00, -.518892E+00}, + }, + { + {0.558582E+00, 0.960142E+00}, {0.558011E+00, 0.959949E+00}, + {0.556297E+00, 0.959369E+00}, {0.553450E+00, 0.958398E+00}, + {0.549478E+00, 0.957028E+00}, {0.544397E+00, 0.955251E+00}, + {0.538226E+00, 0.953054E+00}, {0.530985E+00, 0.950424E+00}, + {0.522699E+00, 0.947348E+00}, {0.513396E+00, 0.943809E+00}, + {0.503104E+00, 0.939792E+00}, {0.491853E+00, 0.935281E+00}, + {0.479675E+00, 0.930260E+00}, {0.466603E+00, 0.924713E+00}, + {0.452669E+00, 0.918627E+00}, {0.437908E+00, 0.911988E+00}, + {0.422352E+00, 0.904785E+00}, {0.406035E+00, 0.897005E+00}, + {0.388992E+00, 0.888641E+00}, {0.371255E+00, 0.879683E+00}, + {0.352857E+00, 0.870126E+00}, {0.333833E+00, 0.859965E+00}, + {0.314213E+00, 0.849197E+00}, {0.294032E+00, 0.837820E+00}, + {0.273320E+00, 0.825833E+00}, {0.252109E+00, 0.813238E+00}, + {0.230432E+00, 0.800037E+00}, {0.208319E+00, 0.786234E+00}, + {0.185801E+00, 0.771835E+00}, {0.162909E+00, 0.756846E+00}, + {0.139673E+00, 0.741274E+00}, {0.116123E+00, 0.725129E+00}, + {0.922903E-01, 0.708420E+00}, {0.682033E-01, 0.691159E+00}, + {0.438919E-01, 0.673358E+00}, {0.193853E-01, 0.655030E+00}, + {-.528737E-02, 0.636188E+00}, {-.300974E-01, 0.616849E+00}, + {-.550161E-01, 0.597028E+00}, {-.800153E-01, 0.576741E+00}, + {-.105067E+00, 0.556007E+00}, {-.130143E+00, 0.534843E+00}, + {-.155216E+00, 0.513269E+00}, {-.180259E+00, 0.491304E+00}, + {-.205244E+00, 0.468968E+00}, {-.230146E+00, 0.446283E+00}, + {-.254938E+00, 0.423270E+00}, {-.279593E+00, 0.399952E+00}, + {-.304087E+00, 0.376351E+00}, {-.328395E+00, 0.352491E+00}, + {-.352491E+00, 0.328395E+00}, {-.376351E+00, 0.304087E+00}, + {-.399952E+00, 0.279593E+00}, {-.423270E+00, 0.254938E+00}, + {-.446283E+00, 0.230146E+00}, {-.468968E+00, 0.205244E+00}, + {-.491304E+00, 0.180259E+00}, {-.513269E+00, 0.155216E+00}, + {-.534843E+00, 0.130143E+00}, {-.556007E+00, 0.105067E+00}, + {-.576741E+00, 0.800153E-01}, {-.597028E+00, 0.550161E-01}, + {-.616849E+00, 0.300974E-01}, {-.636188E+00, 0.528737E-02}, + {-.655030E+00, -.193853E-01}, {-.673358E+00, -.438919E-01}, + {-.691159E+00, -.682033E-01}, {-.708420E+00, -.922903E-01}, + {-.725129E+00, -.116123E+00}, {-.741274E+00, -.139673E+00}, + {-.756846E+00, -.162909E+00}, {-.771835E+00, -.185801E+00}, + {-.786234E+00, -.208319E+00}, {-.800037E+00, -.230432E+00}, + {-.813238E+00, -.252109E+00}, {-.825833E+00, -.273320E+00}, + {-.837820E+00, -.294032E+00}, {-.849197E+00, -.314213E+00}, + {-.859965E+00, -.333833E+00}, {-.870126E+00, -.352857E+00}, + {-.879683E+00, -.371255E+00}, {-.888641E+00, -.388992E+00}, + {-.897005E+00, -.406035E+00}, {-.904785E+00, -.422352E+00}, + {-.911988E+00, -.437908E+00}, {-.918627E+00, -.452669E+00}, + {-.924713E+00, -.466603E+00}, {-.930260E+00, -.479675E+00}, + {-.935281E+00, -.491853E+00}, {-.939792E+00, -.503104E+00}, + {-.943809E+00, -.513396E+00}, {-.947348E+00, -.522699E+00}, + {-.950424E+00, -.530985E+00}, {-.953054E+00, -.538226E+00}, + {-.955251E+00, -.544397E+00}, {-.957028E+00, -.549478E+00}, + {-.958398E+00, -.553450E+00}, {-.959369E+00, -.556297E+00}, + {-.959949E+00, -.558011E+00}, {-.960142E+00, -.558582E+00}, + }, + { + {0.600809E+00, 0.964822E+00}, {0.600216E+00, 0.964627E+00}, + {0.598440E+00, 0.964040E+00}, {0.595487E+00, 0.963055E+00}, + {0.591371E+00, 0.961665E+00}, {0.586107E+00, 0.959859E+00}, + {0.579715E+00, 0.957623E+00}, {0.572221E+00, 0.954942E+00}, + {0.563651E+00, 0.951800E+00}, {0.554033E+00, 0.948179E+00}, + {0.543400E+00, 0.944061E+00}, {0.531784E+00, 0.939428E+00}, + {0.519218E+00, 0.934264E+00}, {0.505737E+00, 0.928551E+00}, + {0.491376E+00, 0.922274E+00}, {0.476170E+00, 0.915419E+00}, + {0.460153E+00, 0.907973E+00}, {0.443361E+00, 0.899925E+00}, + {0.425827E+00, 0.891266E+00}, {0.407587E+00, 0.881987E+00}, + {0.388673E+00, 0.872083E+00}, {0.369120E+00, 0.861550E+00}, + {0.348960E+00, 0.850384E+00}, {0.328226E+00, 0.838584E+00}, + {0.306951E+00, 0.826150E+00}, {0.285166E+00, 0.813085E+00}, + {0.262903E+00, 0.799392E+00}, {0.240194E+00, 0.785076E+00}, + {0.217070E+00, 0.770143E+00}, {0.193562E+00, 0.754600E+00}, + {0.169699E+00, 0.738456E+00}, {0.145514E+00, 0.721721E+00}, + {0.121035E+00, 0.704406E+00}, {0.962931E-01, 0.686523E+00}, + {0.713179E-01, 0.668085E+00}, {0.461388E-01, 0.649106E+00}, + {0.207854E-01, 0.629601E+00}, {-.471322E-02, 0.609587E+00}, + {-.303281E-01, 0.589080E+00}, {-.560305E-01, 0.568098E+00}, + {-.817918E-01, 0.546660E+00}, {-.107584E+00, 0.524783E+00}, + {-.133378E+00, 0.502490E+00}, {-.159148E+00, 0.479799E+00}, + {-.184864E+00, 0.456733E+00}, {-.210501E+00, 0.433313E+00}, + {-.236031E+00, 0.409562E+00}, {-.261427E+00, 0.385503E+00}, + {-.286663E+00, 0.361159E+00}, {-.311715E+00, 0.336555E+00}, + {-.336555E+00, 0.311715E+00}, {-.361159E+00, 0.286663E+00}, + {-.385503E+00, 0.261427E+00}, {-.409562E+00, 0.236031E+00}, + {-.433313E+00, 0.210501E+00}, {-.456733E+00, 0.184864E+00}, + {-.479799E+00, 0.159148E+00}, {-.502490E+00, 0.133378E+00}, + {-.524783E+00, 0.107584E+00}, {-.546660E+00, 0.817918E-01}, + {-.568098E+00, 0.560305E-01}, {-.589080E+00, 0.303281E-01}, + {-.609587E+00, 0.471322E-02}, {-.629601E+00, -.207854E-01}, + {-.649106E+00, -.461388E-01}, {-.668085E+00, -.713179E-01}, + {-.686523E+00, -.962931E-01}, {-.704406E+00, -.121035E+00}, + {-.721721E+00, -.145514E+00}, {-.738456E+00, -.169699E+00}, + {-.754600E+00, -.193562E+00}, {-.770143E+00, -.217070E+00}, + {-.785076E+00, -.240194E+00}, {-.799392E+00, -.262903E+00}, + {-.813085E+00, -.285166E+00}, {-.826150E+00, -.306951E+00}, + {-.838584E+00, -.328226E+00}, {-.850384E+00, -.348960E+00}, + {-.861550E+00, -.369120E+00}, {-.872083E+00, -.388673E+00}, + {-.881987E+00, -.407587E+00}, {-.891266E+00, -.425827E+00}, + {-.899925E+00, -.443361E+00}, {-.907973E+00, -.460153E+00}, + {-.915419E+00, -.476170E+00}, {-.922274E+00, -.491376E+00}, + {-.928551E+00, -.505737E+00}, {-.934264E+00, -.519218E+00}, + {-.939428E+00, -.531784E+00}, {-.944061E+00, -.543400E+00}, + {-.948179E+00, -.554033E+00}, {-.951800E+00, -.563651E+00}, + {-.954942E+00, -.572221E+00}, {-.957623E+00, -.579715E+00}, + {-.959859E+00, -.586107E+00}, {-.961665E+00, -.591371E+00}, + {-.963055E+00, -.595487E+00}, {-.964040E+00, -.598440E+00}, + {-.964627E+00, -.600216E+00}, {-.964822E+00, -.600809E+00}, + }, + { + {0.645865E+00, 0.969573E+00}, {0.645249E+00, 0.969376E+00}, + {0.643405E+00, 0.968781E+00}, {0.640341E+00, 0.967782E+00}, + {0.636071E+00, 0.966371E+00}, {0.630613E+00, 0.964533E+00}, + {0.623991E+00, 0.962254E+00}, {0.616231E+00, 0.959515E+00}, + {0.607364E+00, 0.956297E+00}, {0.597421E+00, 0.952581E+00}, + {0.586436E+00, 0.948345E+00}, {0.574444E+00, 0.943571E+00}, + {0.561482E+00, 0.938238E+00}, {0.547586E+00, 0.932330E+00}, + {0.532793E+00, 0.925829E+00}, {0.517137E+00, 0.918721E+00}, + {0.500657E+00, 0.910993E+00}, {0.483387E+00, 0.902632E+00}, + {0.465362E+00, 0.893631E+00}, {0.446618E+00, 0.883981E+00}, + {0.427188E+00, 0.873676E+00}, {0.407107E+00, 0.862713E+00}, + {0.386407E+00, 0.851091E+00}, {0.365121E+00, 0.838808E+00}, + {0.343282E+00, 0.825866E+00}, {0.320922E+00, 0.812268E+00}, + {0.298072E+00, 0.798018E+00}, {0.274764E+00, 0.783123E+00}, + {0.251030E+00, 0.767589E+00}, {0.226899E+00, 0.751426E+00}, + {0.202404E+00, 0.734642E+00}, {0.177574E+00, 0.717250E+00}, + {0.152439E+00, 0.699261E+00}, {0.127031E+00, 0.680688E+00}, + {0.101378E+00, 0.661547E+00}, {0.755109E-01, 0.641851E+00}, + {0.494592E-01, 0.621618E+00}, {0.232525E-01, 0.600864E+00}, + {-.308006E-02, 0.579608E+00}, {-.295092E-01, 0.557867E+00}, + {-.560059E-01, 0.535662E+00}, {-.825415E-01, 0.513013E+00}, + {-.109087E+00, 0.489941E+00}, {-.135615E+00, 0.466468E+00}, + {-.162097E+00, 0.442614E+00}, {-.188504E+00, 0.418405E+00}, + {-.214810E+00, 0.393862E+00}, {-.240987E+00, 0.369011E+00}, + {-.267009E+00, 0.343874E+00}, {-.292848E+00, 0.318478E+00}, + {-.318478E+00, 0.292848E+00}, {-.343874E+00, 0.267009E+00}, + {-.369011E+00, 0.240987E+00}, {-.393862E+00, 0.214810E+00}, + {-.418405E+00, 0.188504E+00}, {-.442614E+00, 0.162097E+00}, + {-.466468E+00, 0.135615E+00}, {-.489941E+00, 0.109087E+00}, + {-.513013E+00, 0.825415E-01}, {-.535662E+00, 0.560059E-01}, + {-.557867E+00, 0.295092E-01}, {-.579608E+00, 0.308006E-02}, + {-.600864E+00, -.232525E-01}, {-.621618E+00, -.494592E-01}, + {-.641851E+00, -.755109E-01}, {-.661547E+00, -.101378E+00}, + {-.680688E+00, -.127031E+00}, {-.699261E+00, -.152439E+00}, + {-.717250E+00, -.177574E+00}, {-.734642E+00, -.202404E+00}, + {-.751426E+00, -.226899E+00}, {-.767589E+00, -.251030E+00}, + {-.783123E+00, -.274764E+00}, {-.798018E+00, -.298072E+00}, + {-.812268E+00, -.320922E+00}, {-.825866E+00, -.343282E+00}, + {-.838808E+00, -.365121E+00}, {-.851091E+00, -.386407E+00}, + {-.862713E+00, -.407107E+00}, {-.873676E+00, -.427188E+00}, + {-.883981E+00, -.446618E+00}, {-.893631E+00, -.465362E+00}, + {-.902632E+00, -.483387E+00}, {-.910993E+00, -.500657E+00}, + {-.918721E+00, -.517137E+00}, {-.925829E+00, -.532793E+00}, + {-.932330E+00, -.547586E+00}, {-.938238E+00, -.561482E+00}, + {-.943571E+00, -.574444E+00}, {-.948345E+00, -.586436E+00}, + {-.952581E+00, -.597421E+00}, {-.956297E+00, -.607364E+00}, + {-.959515E+00, -.616231E+00}, {-.962254E+00, -.623991E+00}, + {-.964533E+00, -.630613E+00}, {-.966371E+00, -.636071E+00}, + {-.967782E+00, -.640341E+00}, {-.968781E+00, -.643405E+00}, + {-.969376E+00, -.645249E+00}, {-.969573E+00, -.645865E+00}, + }, + { + {0.694091E+00, 0.974403E+00}, {0.693451E+00, 0.974202E+00}, + {0.691534E+00, 0.973600E+00}, {0.688352E+00, 0.972586E+00}, + {0.683918E+00, 0.971151E+00}, {0.678257E+00, 0.969278E+00}, + {0.671393E+00, 0.966949E+00}, {0.663357E+00, 0.964141E+00}, + {0.654182E+00, 0.960833E+00}, {0.643906E+00, 0.957002E+00}, + {0.632563E+00, 0.952624E+00}, {0.620193E+00, 0.947678E+00}, + {0.606834E+00, 0.942142E+00}, {0.592524E+00, 0.935997E+00}, + {0.577301E+00, 0.929226E+00}, {0.561203E+00, 0.921813E+00}, + {0.544267E+00, 0.913745E+00}, {0.526529E+00, 0.905011E+00}, + {0.508024E+00, 0.895602E+00}, {0.488788E+00, 0.885511E+00}, + {0.468854E+00, 0.874733E+00}, {0.448257E+00, 0.863267E+00}, + {0.427029E+00, 0.851111E+00}, {0.405203E+00, 0.838265E+00}, + {0.382812E+00, 0.824733E+00}, {0.359887E+00, 0.810519E+00}, + {0.336459E+00, 0.795629E+00}, {0.312560E+00, 0.780071E+00}, + {0.288221E+00, 0.763852E+00}, {0.263473E+00, 0.746983E+00}, + {0.238346E+00, 0.729475E+00}, {0.212871E+00, 0.711341E+00}, + {0.187078E+00, 0.692594E+00}, {0.160998E+00, 0.673249E+00}, + {0.134660E+00, 0.653321E+00}, {0.108095E+00, 0.632827E+00}, + {0.813325E-01, 0.611785E+00}, {0.544023E-01, 0.590212E+00}, + {0.273340E-01, 0.568128E+00}, {0.157188E-03, 0.545553E+00}, + {-.270987E-01, 0.522508E+00}, {-.544045E-01, 0.499014E+00}, + {-.817310E-01, 0.475093E+00}, {-.109049E+00, 0.450768E+00}, + {-.136331E+00, 0.426062E+00}, {-.163547E+00, 0.400999E+00}, + {-.190670E+00, 0.375603E+00}, {-.217672E+00, 0.349900E+00}, + {-.244524E+00, 0.323914E+00}, {-.271200E+00, 0.297672E+00}, + {-.297672E+00, 0.271200E+00}, {-.323914E+00, 0.244524E+00}, + {-.349900E+00, 0.217672E+00}, {-.375603E+00, 0.190670E+00}, + {-.400999E+00, 0.163547E+00}, {-.426062E+00, 0.136331E+00}, + {-.450768E+00, 0.109049E+00}, {-.475093E+00, 0.817310E-01}, + {-.499014E+00, 0.544045E-01}, {-.522508E+00, 0.270987E-01}, + {-.545553E+00, -.157188E-03}, {-.568128E+00, -.273340E-01}, + {-.590212E+00, -.544023E-01}, {-.611785E+00, -.813325E-01}, + {-.632827E+00, -.108095E+00}, {-.653321E+00, -.134660E+00}, + {-.673249E+00, -.160998E+00}, {-.692594E+00, -.187078E+00}, + {-.711341E+00, -.212871E+00}, {-.729475E+00, -.238346E+00}, + {-.746983E+00, -.263473E+00}, {-.763852E+00, -.288221E+00}, + {-.780071E+00, -.312560E+00}, {-.795629E+00, -.336459E+00}, + {-.810519E+00, -.359887E+00}, {-.824733E+00, -.382812E+00}, + {-.838265E+00, -.405203E+00}, {-.851111E+00, -.427029E+00}, + {-.863267E+00, -.448257E+00}, {-.874733E+00, -.468854E+00}, + {-.885511E+00, -.488788E+00}, {-.895602E+00, -.508024E+00}, + {-.905011E+00, -.526529E+00}, {-.913745E+00, -.544267E+00}, + {-.921813E+00, -.561203E+00}, {-.929226E+00, -.577301E+00}, + {-.935997E+00, -.592524E+00}, {-.942142E+00, -.606834E+00}, + {-.947678E+00, -.620193E+00}, {-.952624E+00, -.632563E+00}, + {-.957002E+00, -.643906E+00}, {-.960833E+00, -.654182E+00}, + {-.964141E+00, -.663357E+00}, {-.966949E+00, -.671393E+00}, + {-.969278E+00, -.678257E+00}, {-.971151E+00, -.683918E+00}, + {-.972586E+00, -.688352E+00}, {-.973600E+00, -.691534E+00}, + {-.974202E+00, -.693451E+00}, {-.974403E+00, -.694091E+00}, + }, + { + {0.745890E+00, 0.979316E+00}, {0.745224E+00, 0.979114E+00}, + {0.743231E+00, 0.978503E+00}, {0.739921E+00, 0.977473E+00}, + {0.735316E+00, 0.976011E+00}, {0.729440E+00, 0.974096E+00}, + {0.722325E+00, 0.971705E+00}, {0.714006E+00, 0.968812E+00}, + {0.704522E+00, 0.965390E+00}, {0.693911E+00, 0.961413E+00}, + {0.682215E+00, 0.956854E+00}, {0.669476E+00, 0.951688E+00}, + {0.655733E+00, 0.945894E+00}, {0.641027E+00, 0.939450E+00}, + {0.625397E+00, 0.932338E+00}, {0.608882E+00, 0.924543E+00}, + {0.591518E+00, 0.916053E+00}, {0.573342E+00, 0.906856E+00}, + {0.554390E+00, 0.896947E+00}, {0.534695E+00, 0.886318E+00}, + {0.514291E+00, 0.874966E+00}, {0.493212E+00, 0.862892E+00}, + {0.471490E+00, 0.850095E+00}, {0.449157E+00, 0.836578E+00}, + {0.426245E+00, 0.822346E+00}, {0.402784E+00, 0.807405E+00}, + {0.378806E+00, 0.791763E+00}, {0.354341E+00, 0.775429E+00}, + {0.329421E+00, 0.758413E+00}, {0.304075E+00, 0.740727E+00}, + {0.278334E+00, 0.722384E+00}, {0.252228E+00, 0.703398E+00}, + {0.225788E+00, 0.683785E+00}, {0.199044E+00, 0.663560E+00}, + {0.172025E+00, 0.642740E+00}, {0.144762E+00, 0.621344E+00}, + {0.117285E+00, 0.599391E+00}, {0.896242E-01, 0.576901E+00}, + {0.618091E-01, 0.553893E+00}, {0.338696E-01, 0.530390E+00}, + {0.583553E-02, 0.506413E+00}, {-.222633E-01, 0.481986E+00}, + {-.503975E-01, 0.457130E+00}, {-.785374E-01, 0.431871E+00}, + {-.106654E+00, 0.406233E+00}, {-.134718E+00, 0.380240E+00}, + {-.162700E+00, 0.353919E+00}, {-.190573E+00, 0.327295E+00}, + {-.218306E+00, 0.300395E+00}, {-.245873E+00, 0.273245E+00}, + {-.273245E+00, 0.245873E+00}, {-.300395E+00, 0.218306E+00}, + {-.327295E+00, 0.190573E+00}, {-.353919E+00, 0.162700E+00}, + {-.380240E+00, 0.134718E+00}, {-.406233E+00, 0.106654E+00}, + {-.431871E+00, 0.785374E-01}, {-.457130E+00, 0.503975E-01}, + {-.481986E+00, 0.222633E-01}, {-.506413E+00, -.583553E-02}, + {-.530390E+00, -.338696E-01}, {-.553893E+00, -.618091E-01}, + {-.576901E+00, -.896242E-01}, {-.599391E+00, -.117285E+00}, + {-.621344E+00, -.144762E+00}, {-.642740E+00, -.172025E+00}, + {-.663560E+00, -.199044E+00}, {-.683785E+00, -.225788E+00}, + {-.703398E+00, -.252228E+00}, {-.722384E+00, -.278334E+00}, + {-.740727E+00, -.304075E+00}, {-.758413E+00, -.329421E+00}, + {-.775429E+00, -.354341E+00}, {-.791763E+00, -.378806E+00}, + {-.807405E+00, -.402784E+00}, {-.822346E+00, -.426245E+00}, + {-.836578E+00, -.449157E+00}, {-.850095E+00, -.471490E+00}, + {-.862892E+00, -.493212E+00}, {-.874966E+00, -.514291E+00}, + {-.886318E+00, -.534695E+00}, {-.896947E+00, -.554390E+00}, + {-.906856E+00, -.573342E+00}, {-.916053E+00, -.591518E+00}, + {-.924543E+00, -.608882E+00}, {-.932338E+00, -.625397E+00}, + {-.939450E+00, -.641027E+00}, {-.945894E+00, -.655733E+00}, + {-.951688E+00, -.669476E+00}, {-.956854E+00, -.682215E+00}, + {-.961413E+00, -.693911E+00}, {-.965390E+00, -.704522E+00}, + {-.968812E+00, -.714006E+00}, {-.971705E+00, -.722325E+00}, + {-.974096E+00, -.729440E+00}, {-.976011E+00, -.735316E+00}, + {-.977473E+00, -.739921E+00}, {-.978503E+00, -.743231E+00}, + {-.979114E+00, -.745224E+00}, {-.979316E+00, -.745890E+00}, + }, + { + {0.801743E+00, 0.984323E+00}, {0.801049E+00, 0.984118E+00}, + {0.798972E+00, 0.983498E+00}, {0.795529E+00, 0.982450E+00}, + {0.790743E+00, 0.980954E+00}, {0.784647E+00, 0.978984E+00}, + {0.777280E+00, 0.976510E+00}, {0.768682E+00, 0.973500E+00}, + {0.758898E+00, 0.969922E+00}, {0.747974E+00, 0.965744E+00}, + {0.735954E+00, 0.960936E+00}, {0.722882E+00, 0.955472E+00}, + {0.708800E+00, 0.949326E+00}, {0.693750E+00, 0.942480E+00}, + {0.677772E+00, 0.934915E+00}, {0.660903E+00, 0.926616E+00}, + {0.643179E+00, 0.917573E+00}, {0.624637E+00, 0.907778E+00}, + {0.605309E+00, 0.897225E+00}, {0.585230E+00, 0.885911E+00}, + {0.564431E+00, 0.873835E+00}, {0.542944E+00, 0.860998E+00}, + {0.520801E+00, 0.847405E+00}, {0.498031E+00, 0.833060E+00}, + {0.474666E+00, 0.817971E+00}, {0.450735E+00, 0.802145E+00}, + {0.426269E+00, 0.785593E+00}, {0.401298E+00, 0.768327E+00}, + {0.375850E+00, 0.750358E+00}, {0.349957E+00, 0.731702E+00}, + {0.323648E+00, 0.712372E+00}, {0.296953E+00, 0.692385E+00}, + {0.269901E+00, 0.671759E+00}, {0.242523E+00, 0.650510E+00}, + {0.214849E+00, 0.628659E+00}, {0.186908E+00, 0.606224E+00}, + {0.158732E+00, 0.583227E+00}, {0.130349E+00, 0.559689E+00}, + {0.101790E+00, 0.535632E+00}, {0.730852E-01, 0.511080E+00}, + {0.442649E-01, 0.486055E+00}, {0.153589E-01, 0.460581E+00}, + {-.136027E-01, 0.434684E+00}, {-.425898E-01, 0.408388E+00}, + {-.715726E-01, 0.381720E+00}, {-.100521E+00, 0.354705E+00}, + {-.129406E+00, 0.327371E+00}, {-.158198E+00, 0.299744E+00}, + {-.186868E+00, 0.271852E+00}, {-.215386E+00, 0.243723E+00}, + {-.243723E+00, 0.215386E+00}, {-.271852E+00, 0.186868E+00}, + {-.299744E+00, 0.158198E+00}, {-.327371E+00, 0.129406E+00}, + {-.354705E+00, 0.100521E+00}, {-.381720E+00, 0.715726E-01}, + {-.408388E+00, 0.425898E-01}, {-.434684E+00, 0.136027E-01}, + {-.460581E+00, -.153589E-01}, {-.486055E+00, -.442649E-01}, + {-.511080E+00, -.730852E-01}, {-.535632E+00, -.101790E+00}, + {-.559689E+00, -.130349E+00}, {-.583227E+00, -.158732E+00}, + {-.606224E+00, -.186908E+00}, {-.628659E+00, -.214849E+00}, + {-.650510E+00, -.242523E+00}, {-.671759E+00, -.269901E+00}, + {-.692385E+00, -.296953E+00}, {-.712372E+00, -.323648E+00}, + {-.731702E+00, -.349957E+00}, {-.750358E+00, -.375850E+00}, + {-.768327E+00, -.401298E+00}, {-.785593E+00, -.426269E+00}, + {-.802145E+00, -.450735E+00}, {-.817971E+00, -.474666E+00}, + {-.833060E+00, -.498031E+00}, {-.847405E+00, -.520801E+00}, + {-.860998E+00, -.542944E+00}, {-.873835E+00, -.564431E+00}, + {-.885911E+00, -.585230E+00}, {-.897225E+00, -.605309E+00}, + {-.907778E+00, -.624637E+00}, {-.917573E+00, -.643179E+00}, + {-.926616E+00, -.660903E+00}, {-.934915E+00, -.677772E+00}, + {-.942480E+00, -.693750E+00}, {-.949326E+00, -.708800E+00}, + {-.955472E+00, -.722882E+00}, {-.960936E+00, -.735954E+00}, + {-.965744E+00, -.747974E+00}, {-.969922E+00, -.758898E+00}, + {-.973500E+00, -.768682E+00}, {-.976510E+00, -.777280E+00}, + {-.978984E+00, -.784647E+00}, {-.980954E+00, -.790743E+00}, + {-.982450E+00, -.795529E+00}, {-.983498E+00, -.798972E+00}, + {-.984118E+00, -.801049E+00}, {-.984323E+00, -.801743E+00}, + }, + { + {0.862222E+00, 0.989432E+00}, {0.861498E+00, 0.989224E+00}, + {0.859333E+00, 0.988593E+00}, {0.855750E+00, 0.987520E+00}, + {0.850782E+00, 0.985975E+00}, {0.844474E+00, 0.983921E+00}, + {0.836874E+00, 0.981318E+00}, {0.828035E+00, 0.978124E+00}, + {0.818008E+00, 0.974299E+00}, {0.806843E+00, 0.969806E+00}, + {0.794589E+00, 0.964614E+00}, {0.781291E+00, 0.958695E+00}, + {0.766991E+00, 0.952025E+00}, {0.751730E+00, 0.944586E+00}, + {0.735546E+00, 0.936362E+00}, {0.718472E+00, 0.927344E+00}, + {0.700544E+00, 0.917523E+00}, {0.681793E+00, 0.906896E+00}, + {0.662250E+00, 0.895461E+00}, {0.641946E+00, 0.883218E+00}, + {0.620910E+00, 0.870172E+00}, {0.599172E+00, 0.856326E+00}, + {0.576760E+00, 0.841689E+00}, {0.553703E+00, 0.826269E+00}, + {0.530029E+00, 0.810076E+00}, {0.505767E+00, 0.793122E+00}, + {0.480945E+00, 0.775420E+00}, {0.455592E+00, 0.756984E+00}, + {0.429736E+00, 0.737829E+00}, {0.403407E+00, 0.717972E+00}, + {0.376633E+00, 0.697431E+00}, {0.349444E+00, 0.676224E+00}, + {0.321869E+00, 0.654371E+00}, {0.293936E+00, 0.631891E+00}, + {0.265677E+00, 0.608805E+00}, {0.237121E+00, 0.585137E+00}, + {0.208298E+00, 0.560907E+00}, {0.179237E+00, 0.536141E+00}, + {0.149970E+00, 0.510860E+00}, {0.120526E+00, 0.485091E+00}, + {0.909360E-01, 0.458858E+00}, {0.612305E-01, 0.432187E+00}, + {0.314400E-01, 0.405104E+00}, {0.159478E-02, 0.377636E+00}, + {-.282745E-01, 0.349811E+00}, {-.581375E-01, 0.321655E+00}, + {-.879638E-01, 0.293198E+00}, {-.117723E+00, 0.264467E+00}, + {-.147385E+00, 0.235491E+00}, {-.176920E+00, 0.206299E+00}, + {-.206299E+00, 0.176920E+00}, {-.235491E+00, 0.147385E+00}, + {-.264467E+00, 0.117723E+00}, {-.293198E+00, 0.879638E-01}, + {-.321655E+00, 0.581375E-01}, {-.349811E+00, 0.282745E-01}, + {-.377636E+00, -.159478E-02}, {-.405104E+00, -.314400E-01}, + {-.432187E+00, -.612305E-01}, {-.458858E+00, -.909360E-01}, + {-.485091E+00, -.120526E+00}, {-.510860E+00, -.149970E+00}, + {-.536141E+00, -.179237E+00}, {-.560907E+00, -.208298E+00}, + {-.585137E+00, -.237121E+00}, {-.608805E+00, -.265677E+00}, + {-.631891E+00, -.293936E+00}, {-.654371E+00, -.321869E+00}, + {-.676224E+00, -.349444E+00}, {-.697431E+00, -.376633E+00}, + {-.717972E+00, -.403407E+00}, {-.737829E+00, -.429736E+00}, + {-.756984E+00, -.455592E+00}, {-.775420E+00, -.480945E+00}, + {-.793122E+00, -.505767E+00}, {-.810076E+00, -.530029E+00}, + {-.826269E+00, -.553703E+00}, {-.841689E+00, -.576760E+00}, + {-.856326E+00, -.599172E+00}, {-.870172E+00, -.620910E+00}, + {-.883218E+00, -.641946E+00}, {-.895461E+00, -.662250E+00}, + {-.906896E+00, -.681793E+00}, {-.917523E+00, -.700544E+00}, + {-.927344E+00, -.718472E+00}, {-.936362E+00, -.735546E+00}, + {-.944586E+00, -.751730E+00}, {-.952025E+00, -.766991E+00}, + {-.958695E+00, -.781291E+00}, {-.964614E+00, -.794589E+00}, + {-.969806E+00, -.806843E+00}, {-.974299E+00, -.818008E+00}, + {-.978124E+00, -.828035E+00}, {-.981318E+00, -.836874E+00}, + {-.983921E+00, -.844474E+00}, {-.985975E+00, -.850782E+00}, + {-.987520E+00, -.855750E+00}, {-.988593E+00, -.859333E+00}, + {-.989224E+00, -.861498E+00}, {-.989432E+00, -.862222E+00}, + }, + { + {0.928024E+00, 0.994654E+00}, {0.927267E+00, 0.994442E+00}, + {0.925012E+00, 0.993795E+00}, {0.921298E+00, 0.992674E+00}, + {0.916184E+00, 0.991026E+00}, {0.909737E+00, 0.988790E+00}, + {0.902025E+00, 0.985907E+00}, {0.893114E+00, 0.982323E+00}, + {0.883060E+00, 0.977993E+00}, {0.871915E+00, 0.972880E+00}, + {0.859725E+00, 0.966955E+00}, {0.846530E+00, 0.960197E+00}, + {0.832364E+00, 0.952588E+00}, {0.817261E+00, 0.944120E+00}, + {0.801251E+00, 0.934786E+00}, {0.784361E+00, 0.924583E+00}, + {0.766620E+00, 0.913511E+00}, {0.748052E+00, 0.901574E+00}, + {0.728683E+00, 0.888777E+00}, {0.708538E+00, 0.875127E+00}, + {0.687643E+00, 0.860632E+00}, {0.666022E+00, 0.845305E+00}, + {0.643700E+00, 0.829156E+00}, {0.620704E+00, 0.812200E+00}, + {0.597057E+00, 0.794450E+00}, {0.572788E+00, 0.775922E+00}, + {0.547921E+00, 0.756634E+00}, {0.522483E+00, 0.736603E+00}, + {0.496502E+00, 0.715848E+00}, {0.470004E+00, 0.694388E+00}, + {0.443018E+00, 0.672244E+00}, {0.415572E+00, 0.649438E+00}, + {0.387694E+00, 0.625991E+00}, {0.359412E+00, 0.601926E+00}, + {0.330757E+00, 0.577268E+00}, {0.301757E+00, 0.552040E+00}, + {0.272443E+00, 0.526267E+00}, {0.242843E+00, 0.499974E+00}, + {0.212989E+00, 0.473188E+00}, {0.182911E+00, 0.445936E+00}, + {0.152639E+00, 0.418244E+00}, {0.122204E+00, 0.390139E+00}, + {0.916369E-01, 0.361652E+00}, {0.609690E-01, 0.332808E+00}, + {0.302312E-01, 0.303638E+00}, {-.545488E-03, 0.274171E+00}, + {-.313299E-01, 0.244436E+00}, {-.620910E-01, 0.214463E+00}, + {-.927977E-01, 0.184282E+00}, {-.123419E+00, 0.153924E+00}, + {-.153924E+00, 0.123419E+00}, {-.184282E+00, 0.927977E-01}, + {-.214463E+00, 0.620910E-01}, {-.244436E+00, 0.313299E-01}, + {-.274171E+00, 0.545488E-03}, {-.303638E+00, -.302312E-01}, + {-.332808E+00, -.609690E-01}, {-.361652E+00, -.916369E-01}, + {-.390139E+00, -.122204E+00}, {-.418244E+00, -.152639E+00}, + {-.445936E+00, -.182911E+00}, {-.473188E+00, -.212989E+00}, + {-.499974E+00, -.242843E+00}, {-.526267E+00, -.272443E+00}, + {-.552040E+00, -.301757E+00}, {-.577268E+00, -.330757E+00}, + {-.601926E+00, -.359412E+00}, {-.625991E+00, -.387694E+00}, + {-.649438E+00, -.415572E+00}, {-.672244E+00, -.443018E+00}, + {-.694388E+00, -.470004E+00}, {-.715848E+00, -.496502E+00}, + {-.736603E+00, -.522483E+00}, {-.756634E+00, -.547921E+00}, + {-.775922E+00, -.572788E+00}, {-.794450E+00, -.597057E+00}, + {-.812200E+00, -.620704E+00}, {-.829156E+00, -.643700E+00}, + {-.845305E+00, -.666022E+00}, {-.860632E+00, -.687643E+00}, + {-.875127E+00, -.708538E+00}, {-.888777E+00, -.728683E+00}, + {-.901574E+00, -.748052E+00}, {-.913511E+00, -.766620E+00}, + {-.924583E+00, -.784361E+00}, {-.934786E+00, -.801251E+00}, + {-.944120E+00, -.817261E+00}, {-.952588E+00, -.832364E+00}, + {-.960197E+00, -.846530E+00}, {-.966955E+00, -.859725E+00}, + {-.972880E+00, -.871915E+00}, {-.977993E+00, -.883060E+00}, + {-.982323E+00, -.893114E+00}, {-.985907E+00, -.902025E+00}, + {-.988790E+00, -.909737E+00}, {-.991026E+00, -.916184E+00}, + {-.992674E+00, -.921298E+00}, {-.993795E+00, -.925012E+00}, + {-.994442E+00, -.927267E+00}, {-.994654E+00, -.928024E+00}, + }, + { + {0.100000E+01, 0.100000E+01}, {0.999496E+00, 0.999497E+00}, + {0.997986E+00, 0.997988E+00}, {0.995471E+00, 0.995474E+00}, + {0.991954E+00, 0.991957E+00}, {0.987438E+00, 0.987441E+00}, + {0.981925E+00, 0.981930E+00}, {0.975428E+00, 0.975432E+00}, + {0.967946E+00, 0.967950E+00}, {0.959492E+00, 0.959496E+00}, + {0.950069E+00, 0.950072E+00}, {0.939691E+00, 0.939694E+00}, + {0.928363E+00, 0.928371E+00}, {0.916105E+00, 0.916110E+00}, + {0.902924E+00, 0.902933E+00}, {0.888833E+00, 0.888840E+00}, + {0.873844E+00, 0.873852E+00}, {0.857979E+00, 0.857985E+00}, + {0.841248E+00, 0.841256E+00}, {0.823674E+00, 0.823681E+00}, + {0.805268E+00, 0.805275E+00}, {0.786048E+00, 0.786056E+00}, + {0.766043E+00, 0.766047E+00}, {0.745258E+00, 0.745268E+00}, + {0.723730E+00, 0.723742E+00}, {0.701471E+00, 0.701482E+00}, + {0.678504E+00, 0.678512E+00}, {0.654854E+00, 0.654864E+00}, + {0.630550E+00, 0.630557E+00}, {0.605605E+00, 0.605612E+00}, + {0.580055E+00, 0.580058E+00}, {0.553917E+00, 0.553925E+00}, + {0.527218E+00, 0.527229E+00}, {0.499998E+00, 0.500005E+00}, + {0.472268E+00, 0.472277E+00}, {0.444064E+00, 0.444072E+00}, + {0.415409E+00, 0.415418E+00}, {0.386342E+00, 0.386347E+00}, + {0.356884E+00, 0.356891E+00}, {0.327066E+00, 0.327072E+00}, + {0.296919E+00, 0.296923E+00}, {0.266471E+00, 0.266479E+00}, + {0.235751E+00, 0.235763E+00}, {0.204804E+00, 0.204812E+00}, + {0.173646E+00, 0.173653E+00}, {0.142312E+00, 0.142316E+00}, + {0.110836E+00, 0.110839E+00}, {0.792484E-01, 0.792530E-01}, + {0.475798E-01, 0.475862E-01}, {0.158645E-01, 0.158688E-01}, + {-.158676E-01, -.158652E-01}, {-.475839E-01, -.475809E-01}, + {-.792525E-01, -.792449E-01}, {-.110840E+00, -.110834E+00}, + {-.142317E+00, -.142310E+00}, {-.173652E+00, -.173646E+00}, + {-.204809E+00, -.204805E+00}, {-.235763E+00, -.235757E+00}, + {-.266475E+00, -.266471E+00}, {-.296923E+00, -.296916E+00}, + {-.327070E+00, -.327065E+00}, {-.356889E+00, -.356881E+00}, + {-.386349E+00, -.386338E+00}, {-.415418E+00, -.415409E+00}, + {-.444070E+00, -.444060E+00}, {-.472275E+00, -.472263E+00}, + {-.500002E+00, -.499995E+00}, {-.527232E+00, -.527222E+00}, + {-.553923E+00, -.553919E+00}, {-.580061E+00, -.580055E+00}, + {-.605612E+00, -.605604E+00}, {-.630557E+00, -.630550E+00}, + {-.654866E+00, -.654858E+00}, {-.678513E+00, -.678508E+00}, + {-.701480E+00, -.701472E+00}, {-.723742E+00, -.723730E+00}, + {-.745270E+00, -.745262E+00}, {-.766048E+00, -.766042E+00}, + {-.786055E+00, -.786048E+00}, {-.805275E+00, -.805268E+00}, + {-.823681E+00, -.823674E+00}, {-.841256E+00, -.841248E+00}, + {-.857985E+00, -.857979E+00}, {-.873852E+00, -.873844E+00}, + {-.888838E+00, -.888831E+00}, {-.902929E+00, -.902921E+00}, + {-.916111E+00, -.916103E+00}, {-.928372E+00, -.928366E+00}, + {-.939697E+00, -.939690E+00}, {-.950074E+00, -.950070E+00}, + {-.959496E+00, -.959492E+00}, {-.967950E+00, -.967946E+00}, + {-.975432E+00, -.975428E+00}, {-.981931E+00, -.981927E+00}, + {-.987441E+00, -.987438E+00}, {-.991957E+00, -.991954E+00}, + {-.995474E+00, -.995471E+00}, {-.997987E+00, -.997985E+00}, + {-.999497E+00, -.999496E+00}, {-.100000E+01, -.100000E+01}, + }, +}; + +#endif /* #define RT_RIEMANN_HLL_EIGENVALUES_H */ diff --git a/src/rt/KIARA/rt_slope_limiters_cell.h b/src/rt/KIARA/rt_slope_limiters_cell.h new file mode 100644 index 0000000000..5c032b1284 --- /dev/null +++ b/src/rt/KIARA/rt_slope_limiters_cell.h @@ -0,0 +1,162 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_SLOPE_LIMITERS_CELL_KIARA_H +#define SWIFT_RT_SLOPE_LIMITERS_CELL_KIARA_H + +/** + * @file src/rt/KIARA/rt_slope_limiters_cell.h + * @brief File containing routines concerning the cell slope + * limiter for the KIARA RT scheme. (= fist slope limiting step + * that limits gradients such that they don't predict new extrema + * at neighbour praticle's positions ) + * + * The Gizmo-style slope limiter doesn't help for RT problems for + * now, so nothing in this file should actually be called. + * */ + +/** + * @brief Initialize variables for the cell wide slope limiter + * + * @param p Particle. + */ +__attribute__((always_inline)) INLINE static void rt_slope_limit_cell_init( + struct part* p) { + + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.limiter[g].energy_density[0] = FLT_MAX; + p->rt_data.limiter[g].energy_density[1] = -FLT_MAX; + p->rt_data.limiter[g].flux[0][0] = FLT_MAX; + p->rt_data.limiter[g].flux[0][1] = -FLT_MAX; + p->rt_data.limiter[g].flux[1][0] = FLT_MAX; + p->rt_data.limiter[g].flux[1][1] = -FLT_MAX; + p->rt_data.limiter[g].flux[2][0] = FLT_MAX; + p->rt_data.limiter[g].flux[2][1] = -FLT_MAX; + } + + /* just use the hydro one */ + /* p->limiter.maxr = -FLT_MAX; */ +} + +/** + * @brief Collect information for the cell wide slope limiter during the + * neighbour loop. We need the min and max value of densities of conserved + * quantities amongst all neighbours of particle pi. + * + * @param pi Particle i. + * @param pj Particle j. + * @param g index of photon group + */ +__attribute__((always_inline)) INLINE static void rt_slope_limit_cell_collect( + struct part* restrict pi, struct part* restrict pj, int g) { + + struct rt_part_data* rtdi = &pi->rt_data; + struct rt_part_data* rtdj = &pj->rt_data; + + /* basic slope limiter: collect the maximal and the minimal value for the + * primitive variables among the ngbs */ + rtdi->limiter[g].energy_density[0] = min(rtdj->radiation[g].energy_density, + rtdi->limiter[g].energy_density[0]); + rtdi->limiter[g].energy_density[1] = max(rtdj->radiation[g].energy_density, + rtdi->limiter[g].energy_density[1]); + + rtdi->limiter[g].flux[0][0] = + min(rtdj->radiation[g].flux[0], rtdi->limiter[g].flux[0][0]); + rtdi->limiter[g].flux[0][1] = + max(rtdj->radiation[g].flux[0], rtdi->limiter[g].flux[0][1]); + rtdi->limiter[g].flux[1][0] = + min(rtdj->radiation[g].flux[1], rtdi->limiter[g].flux[1][0]); + rtdi->limiter[g].flux[1][1] = + max(rtdj->radiation[g].flux[1], rtdi->limiter[g].flux[1][1]); + rtdi->limiter[g].flux[2][0] = + min(rtdj->radiation[g].flux[2], rtdi->limiter[g].flux[2][0]); + rtdi->limiter[g].flux[2][1] = + max(rtdj->radiation[g].flux[2], rtdi->limiter[g].flux[2][1]); + /* just use the hydro one */ + /* pi->limiter.maxr = max(r, pi->limiter.maxr); */ +} + +/** + * @brief Slope-limit the given quantity. Result will be written directly + * to float gradient[3]. + * + * @param gradient the gradient of the quantity + * @param maxr maximal distance to any neighbour of the particle + * @param value the current value of the quantity + * @param valmin the minimal value amongst all neighbours of the quantity + * @param valmax the maximal value amongst all neighbours of the quantity + */ +__attribute__((always_inline)) INLINE static void rt_slope_limit_quantity( + float gradient[3], const float maxr, const float value, const float valmin, + const float valmax) { + + float gradtrue = sqrtf(gradient[0] * gradient[0] + gradient[1] * gradient[1] + + gradient[2] * gradient[2]); + if (gradtrue != 0.0f) { + gradtrue *= maxr; + const float gradtrue_inv = 1.0f / gradtrue; + const float gradmax = valmax - value; + const float gradmin = valmin - value; + const float beta = 1.f; /* TODO: test for best value here. For now, take + stability over diffusivity. */ + const float min_temp = + min(gradmax * gradtrue_inv, gradmin * gradtrue_inv) * beta; + const float alpha = min(1.f, min_temp); + gradient[0] *= alpha; + gradient[1] *= alpha; + gradient[2] *= alpha; + } +} + +/** + * @brief Slope limit cell gradients. + * This is done in rt_gradients_finalise(). + * + * @param p Particle. + */ +__attribute__((always_inline)) INLINE static void rt_slope_limit_cell( + struct part* p) { + + const float maxr = p->limiter.maxr; + struct rt_part_data* rtd = &p->rt_data; + + for (int g = 0; g < RT_NGROUPS; g++) { + rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].energy_density, + /*maxr= */ maxr, + /*value= */ rtd->radiation[g].energy_density, + /*valmin= */ rtd->limiter[g].energy_density[0], + /*valmax= */ rtd->limiter[g].energy_density[1]); + rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].flux[0], + /*maxr= */ maxr, + /*value= */ rtd->radiation[g].flux[0], + /*valmin= */ rtd->limiter[g].flux[0][0], + /*valmax= */ rtd->limiter[g].flux[0][1]); + rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].flux[1], + /*maxr= */ maxr, + /*value= */ rtd->radiation[g].flux[1], + /*valmin= */ rtd->limiter[g].flux[1][0], + /*valmax= */ rtd->limiter[g].flux[1][1]); + rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].flux[2], + /*maxr= */ maxr, + /*value= */ rtd->radiation[g].flux[2], + /*valmin= */ rtd->limiter[g].flux[2][0], + /*valmax= */ rtd->limiter[g].flux[2][1]); + } +} +#endif /* SWIFT_RT_SLOPE_LIMITERS_CELL_KIARA_H */ diff --git a/src/rt/KIARA/rt_slope_limiters_face.h b/src/rt/KIARA/rt_slope_limiters_face.h new file mode 100644 index 0000000000..a8aa42cdff --- /dev/null +++ b/src/rt/KIARA/rt_slope_limiters_face.h @@ -0,0 +1,215 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_SLOPE_LIMITERS_FACE_KIARA_H +#define SWIFT_RT_SLOPE_LIMITERS_FACE_KIARA_H + +#include "sign.h" + +/** + * @file src/rt/KIARA/rt_slope_limiters_face.h + * @brief File containing routines concerning the face slope + * limiter for the KIARA RT scheme, i.e. limiting the actual + * interparticle flux. */ + +/** + * @brief Slope limit a single quantity at the interface. CURRENTLY NOT IN USE. + * + * @param phi_i Value of the quantity at the particle position. + * @param phi_j Value of the quantity at the neighbouring particle position. + * @param phi_mid0 Extrapolated value of the quantity at the interface position. + * @param xij_norm Distance between the particle position and the interface + * position. + * @param r_inv Inverse distance between the particle and its neighbour. + * @return The slope limited difference between the quantity at the particle + * position and the quantity at the interface position. + */ +__attribute__((always_inline)) INLINE static float rt_slope_limit_face_quantity( + float phi_i, float phi_j, float phi_mid0, float xij_norm, float r_inv) { + + const float dphi = phi_i - phi_j; + const float gamma1 = 0.5f; + const float gamma2 = 0.25f; + + const float delta1 = gamma1 * fabsf(dphi); + const float delta2 = gamma2 * fabsf(dphi); + + const float phimin = min(phi_i, phi_j); + const float phimax = max(phi_i, phi_j); + + const float phibar = phi_i + xij_norm * r_inv * dphi; + + float phiplus, phiminus, phi_mid; + + if (same_signf(phimax + delta1, phimax)) { + phiplus = phimax + delta1; + } else { + phiplus = phimax / (1.0f + delta1 / (fabsf(phimax) + FLT_MIN)); + } + + if (same_signf(phimin - delta1, phimin)) { + phiminus = phimin - delta1; + } else { + phiminus = phimin / (1.0f + delta1 / (fabsf(phimin) + FLT_MIN)); + } + + if (phi_i < phi_j) { + const float temp = min(phibar + delta2, phi_mid0); + phi_mid = max(phiminus, temp); + } else { + const float temp = max(phibar - delta2, phi_mid0); + phi_mid = min(phiplus, temp); + } + + return phi_mid - phi_i; +} + +/** + * the minmod slope limiter. + * + * @param dQi left slope + * @param dQj right slope + */ +__attribute__((always_inline)) INLINE static void rt_limiter_minmod( + float* dQi, float* dQj) { + + if (*dQi * *dQj > 0.f) { + if (fabsf(*dQi) < fabsf(*dQj)) { + *dQj = *dQi; + } else { + *dQi = *dQj; + } + } else { + *dQi = 0.f; + *dQj = 0.f; + } +} + +/** + * the monotonized cenetral limiter. + * + * @param dQi left slope + * @param dQj right slope + * @return factor to slope limit the slope dQi + */ +__attribute__((always_inline)) INLINE static float rt_limiter_mc( + const float dQi, const float dQj) { + + const float r = dQj == 0.f ? dQi * 1e6 : dQi / dQj; + const float minterm = min3(0.5 * (1. + r), 2.f, 2.f * r); + return max(0.f, minterm); +} + +/** + * the van Leer limiter. + * + * @param dQi left slope + * @param dQj right slope + * @return factor to slope limit the slope dQi + */ +__attribute__((always_inline)) INLINE static float rt_limiter_vanLeer( + const float dQi, const float dQj) { + const float r = dQj == 0.f ? dQi * 1e6 : dQi / dQj; + const float absr = fabsf(r); + return (r + absr) / (1.f + absr); +} + +/** + * the superbee limiter. + * + * @param dQi left slope + * @param dQj right slope + * @return factor to slope limit the slope dQi + */ +__attribute__((always_inline)) INLINE static float rt_limiter_superbee( + const float dQi, const float dQj) { + const float r = dQj == 0.f ? dQi * 1e6 : dQi / dQj; + const float minterm1 = min(1.f, 2.f * r); + const float minterm2 = min(2.f, r); + return max3(0.f, minterm1, minterm2); +} + +/** + * @brief Slope limit the slopes at the interface between two particles + * + * @param Qi RT quantities of particle i (energy density + fluxes in 3 dim) + * @param Qj RT quantities of particle j (energy density + fluxes in 3 dim) + * @param dQi Difference between the RT quantities of particle i at the + * position of particle i and at the interface position. + * @param dQj Difference between the RT quantities of particle j at the + * position of particle j and at the interface position. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param r Comoving distance between particle i and particle j. + * @param xij_i Position of the "interface" w.r.t. position of particle i + * @param xij_j Position of the "interface" w.r.t. position of particle j + */ +__attribute__((always_inline)) INLINE static void rt_slope_limit_face( + const float Qi[4], const float Qj[4], float dQi[4], float dQj[4], + const float* dx, const float r, const float xij_i[3], + const float xij_j[3]) { + + /* In 1D advection tests, any limiter works with the + * GLF solver. If you also activate (uncomment) the + * cell slope limiting, you should avoid the superbee + * and MC limiters. The Gizmo-style clever limiter + * that is used for hydro leads to a more diffusive + * behaviour. + * For the HLL solver, avoid superbee and MC limiters, + * as they lead to unstable results. + */ + + /* const float xij_i_norm = */ + /* sqrtf(xij_i[0] * xij_i[0] + xij_i[1] * xij_i[1] + xij_i[2] * xij_i[2]); + */ + /* */ + /* const float xij_j_norm = */ + /* sqrtf(xij_j[0] * xij_j[0] + xij_j[1] * xij_j[1] + xij_j[2] * xij_j[2]); + */ + /* */ + /* const float r_inv = 1.f / r; */ + + for (int i = 0; i < 4; i++) { + + rt_limiter_minmod(&dQi[i], &dQj[i]); + + /* const float alphai = rt_limiter_mc(dQi[i], dQj[i]); */ + /* const float alphaj = rt_limiter_mc(dQj[i], dQi[i]); */ + /* dQi[i] *= alphai; */ + /* dQj[i] *= alphaj; */ + + /* const float alphai = rt_limiter_superbee(dQi[i], dQj[i]); */ + /* const float alphaj = rt_limiter_superbee(dQj[i], dQi[i]); */ + /* dQi[i] *= alphai; */ + /* dQj[i] *= alphaj; */ + + /* const float alphai = rt_limiter_vanLeer(dQi[i], dQj[i]); */ + /* const float alphaj = rt_limiter_vanLeer(dQj[i], dQi[i]); */ + /* dQi[i] *= alphai; */ + /* dQj[i] *= alphaj; */ + + /* dQi[i] = rt_slope_limit_face_quantity(Qi[i], Qj[i], Qi[i] + dQi[i], */ + /* xij_i_norm, r_inv); */ + /* */ + /* dQj[i] = rt_slope_limit_face_quantity(Qj[i], Qi[i], Qj[i] + dQj[i], */ + /* xij_j_norm, r_inv); */ + } +} + +#endif /* SWIFT_RT_SLOPE_LIMITERS_FACE_KIARA_H */ diff --git a/src/rt/KIARA/rt_species.h b/src/rt/KIARA/rt_species.h new file mode 100644 index 0000000000..2594d90149 --- /dev/null +++ b/src/rt/KIARA/rt_species.h @@ -0,0 +1,47 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_KIARA_SPECIES_H +#define SWIFT_RT_KIARA_SPECIES_H + +/** + * @file src/rt/KIARA/rt_species.h + * @brief header file concerning (ionizing) species. + **/ + +enum rt_ionizing_species { + rt_ionizing_species_HI = 0, + rt_ionizing_species_HeI, + rt_ionizing_species_HeII, + rt_ionizing_species_count +}; + +/** + * Get the ionizing energy in erg for all ionizing species. + * + * @param E_ion (return) the ionizing energies. + **/ +__attribute__((always_inline)) INLINE static void +rt_species_get_ionizing_energy(double E_ion[rt_ionizing_species_count]) { + + E_ion[rt_ionizing_species_HI] = 2.179e-11; /* 13.60 eV in erg */ + E_ion[rt_ionizing_species_HeI] = 3.940e-11; /* 24.59 eV in erg */ + E_ion[rt_ionizing_species_HeII] = 8.719e-11; /* 54.42 eV in erg */ +} + +#endif /* SWIFT_RT_KIARA_SPECIES_H */ diff --git a/src/rt/KIARA/rt_stellar_emission_model.h b/src/rt/KIARA/rt_stellar_emission_model.h new file mode 100644 index 0000000000..bff80dcda2 --- /dev/null +++ b/src/rt/KIARA/rt_stellar_emission_model.h @@ -0,0 +1,388 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_STELLAR_EMISSION_MODEL_KIARA_H +#define SWIFT_RT_STELLAR_EMISSION_MODEL_KIARA_H + +/** + * @file src/rt/KIARA/rt_stellar_emission_model.h + * @brief Main header file for the KIARA M1 closure radiative transfer scheme + * stellar radiation emission models. + */ + +enum rt_stellar_emission_models { + rt_stellar_emission_model_none = 0, + rt_stellar_emission_model_const, + rt_stellar_emission_model_IlievTest, + rt_stellar_emission_model_BPASS, + rt_stellar_emission_model_count +}; + +/** + * @brief Compute the energy emitted from a star during the time step dt. + * This is for the constant emission rate model. + * + * @param emission_this_step (return) the emitted radiation energy of a star + * during the time interval dt + * @param const_stellar_emission_rates the constant emission rates used in this + * run + * @param dt time step size (in internal units) + */ +__attribute__((always_inline)) INLINE static void +rt_get_emission_this_step_const( + double emission_this_step[RT_NGROUPS], + const double const_stellar_emission_rates[RT_NGROUPS], double dt) { + + /* The read-in constant stellar emisison rates are in units of L_sol. + * But they have been read in assuming they are in cgs. Convert this + * only now to proper internal units to avoid float overflows. We only + * store the energy that is to be distributed from this spart to its + * neighbours in this step in internal units.*/ + const double solar_luminosity = 3.828e33; /* erg/s */ + for (int g = 0; g < RT_NGROUPS; g++) { + const double emission_rate_internal_units = + const_stellar_emission_rates[g] * solar_luminosity; + emission_this_step[g] = emission_rate_internal_units * dt; + } +} + +/** + * @brief Compute the energy emitted from a star during the time step dt. + * This is for the Iliev+2006 Test 4. + * + * @param emission_this_step (return) the emitted radiation energy of a star + * during the time interval dt + * @param M the star mass (in internal units) + * @param dt time step size (in internal units) + * @param photon_number_integral Integrated photon numbers over frequency + * interval + * @param average_photon_energy average photon energy in each frequency bin, in + * erg + * @param phys_const struct holding physical constants + * @param internal_units units struct containing internal units + */ +__attribute__((always_inline)) INLINE static void +rt_get_emission_this_step_IlievTest( + double emission_this_step[RT_NGROUPS], float M, const double dt, + const double photon_number_integral[RT_NGROUPS], + const double average_photon_energy[RT_NGROUPS], + const struct phys_const* phys_const, + const struct unit_system* internal_units) { + + /* Note that this model uses the halo mass to determine the luminosity + * of a source. I'm cheating the system here by storing the required halo + * mass as the star mass. This is only ok because the test is supposed to + * run with all dynamics and gravity turned off. */ + + const double Omega_b = 0.043; + const double Omega_0 = 0.27; + const double m_p = phys_const->const_proton_mass; + const double t_s = 3e6 * phys_const->const_year; + const double f_gamma = 250.; + const double Ndot_gamma = (f_gamma * M * Omega_b) / (Omega_0 * m_p * t_s); + + double Nsum = 0.; + for (int g = 0; g < RT_NGROUPS; g++) Nsum += photon_number_integral[g]; + + if (Nsum <= 0.) error("No photons in spectrum...???"); + + const double energy_units = + units_cgs_conversion_factor(internal_units, UNIT_CONV_ENERGY); + for (int g = 0; g < RT_NGROUPS; g++) { + const double fi = photon_number_integral[g] / Nsum; + const double Ndot_i = fi * Ndot_gamma; + /* average photon densities are in cgs! */ + const double Edot_i = average_photon_energy[g] * Ndot_i / energy_units; + emission_this_step[g] = Edot_i * dt; + } +} + +/* + * @brief compute the index of a lower bound bin for a non-uniform spaced 1D array_x. + * It takes an array_x that indicate the positions, its size and a value x for which + * we wish to find the lower bound. It returns only the index of the lower bound bin. + * + * + * @param array_x Values of gridpoints + * @param size Length of array_x + * @param x Value within range of array_x for which we want to find the lower bound + */ +static int interpolate_1D_non_uniform_LowerBound(const double* array_x, + const int size, + const double x) { + + // Handle below bounds explicitly + if (x <= array_x[0]) + return 0; + + // Handle above bounds explicitly + if (x >= array_x[size - 1]) + return size - 2; + + // Find the lower bin: array_x[i] <= x < array_x[i+1] + for (int i = 0; i < size - 1; ++i) { + if (x < array_x[i + 1]) + return i; + } + + // Should not reach here + return size - 2; +} + +/* + * @brief compute an interpolated value from a 2d table (array_y) whose values are + * given on a non-uniformly spaced grids. The first dimension uses the outer bounds + * of the grid when given values below/above their min/max. The second dimension does the + * same for its max but gives an error when below their min. + * + * @param array_x1 Values of gridpoints over first dimension + * @param array_x2 Values of gridpoints over second dimension + * @param array_y Values of a 2D table that we interpolate, lengths must match size1 and size2 + * @param size1 Length of array_x1 + * @param size2 Length of array_x2 + * @param x1 Value within range of array_x1 to interpolate to + * here is specifically metallicity + * @param x2 Value within range of array_x2 to interpolate to + * here is specifically stellar age + */ + +static double interpolate_2D_non_uniform(const double* array_x1,const double* array_x2, + double** array_y, + const int size1, const int size2, + double x1, double x2) { + +// First we check the bounds and if x1 and x2 are within it +// return 0 if Time is above star age threshold, give error when below lowest bin value (should always be 0 though if properly binned) +// check for star age +/* TODO: add debug macro for the error check. */ + +if (array_y == NULL) { + printf("ERROR: array_y is NULL!\n"); + return 0; // Handle error properly +} + +if (x2 > array_x2[size2-1]){ + //printf("\n"); + //printf("x2 Has exceeded the upper bound = %f", x2); + //printf("\n"); + //printf("Setting it to: %f",array_x2[size2-1]); + x2 = array_x2[size2-1]; + +} +if (x2 < array_x2[0]){ // MAKE THIS A REAL ERROR + //printf("\n"); + //printf("x2 = %f", x2); + //printf("\n"); + //printf("x2 IS TOO LOW!!!"); + error("Stellar age %e is below 0!", x2); + //return 0; // MAKE THIS A REAL ERROR +} + +//check for metallicity +if (x1 < array_x1[0]){ + //printf("\n"); + //printf("x1 = %f", x1); + + x1 = array_x1[0]; + //printf("\n"); + //printf("x1 = %f", x1); +} + +if (x1 > array_x1[size1-1]){ + x1 = array_x1[size1-1]; +} +// Now we finished bound checks, consider doing this seperate? + +// First we want to find the 4 indices for the corner values. +// We get the lower index for the Metals +const int Ind1 = interpolate_1D_non_uniform_LowerBound(array_x1,size1,x1); +// Now we get the lower index for the Times +const int Ind2 = interpolate_1D_non_uniform_LowerBound(array_x2,size2,x2); + +if (Ind1 >= size1 - 1 || Ind2 >= size2 - 1) { + printf("Index out of bounds: Ind1=%d, Ind2=%d (size1=%d, size2=%d)\n", Ind1, Ind2, size1, size2); + printf("x1 = %e, x2 = %e\n", x1, x2); + exit(1); // or return a safe default +} + +if (Ind1 < 0 || Ind2 < 0) { + error("Interpolation index out of bounds! Ind1=%d Ind2=%d", Ind1, Ind2); +} + +double dx1 = array_x1[Ind1+1] - array_x1[Ind1]; +double dx2 = array_x2[Ind2+1] - array_x2[Ind2]; + +// Handle very small differences (avoid division by zero) +if (fabs(dx1) < 1e-10 || fabs(dx2) < 1e-10) { + printf("Warning: Small step sizes detected: dx1 = %g, dx2 = %g\n", dx1, dx2); + exit(1); // Or return a default value if this is recoverable +} + +// We know the values we need are at [Ind1,Ind2], [Ind1 + 1,Ind2], [Ind1,Ind2 + 1] and [Ind1 + 1,Ind2 + 1] +// We calculate the offsets over the first dimension +const double offset1 = + (array_x1[Ind1+1] - x1) / dx1; +// And now the second dimension +const double offset2 = + (array_x2[Ind2+1] - x2) / dx2; + +// First Interpolation: +// Interpolate [Ind1,Ind2] --- [Ind1+1,Ind2] and interpolate [Ind1,Ind2+1] --- [Ind1+1,Ind2+1] with offset1 to make 2 new values. + +if (array_y[Ind1] == NULL || array_y[Ind1+1] == NULL) { + printf("ERROR: array_y[%d] or array_y[%d] is NULL!\n", Ind1, Ind1+1); + exit(1); +} + +const double Interpol1 = offset1 * array_y[Ind1][Ind2] + (1. - offset1) * array_y[Ind1+1][Ind2]; +const double Interpol2 = offset1 * array_y[Ind1][Ind2+1] + (1. - offset1) * array_y[Ind1+1][Ind2+1]; + +const double result = offset2 * Interpol1 + (1. - offset2) * Interpol2; + +if (isnan(result) || isinf(result)) { + printf("Interpolation result is NaN or Inf\n"); + printf("Metallicity (x1) = %g, age (x2) = %g, offsets: %g, %g\n", x1, x2, offset1, offset2); + printf("array_y values: %g %g %g %g\n", + array_y[Ind1][Ind2], array_y[Ind1+1][Ind2], + array_y[Ind1][Ind2+1], array_y[Ind1+1][Ind2+1]); + exit(1); +} + +return result; +} + +/** + * @brief Compute the energy emitted from a star during the time step dt. + * This is using BPASS model with three photon bins. + * + * @param emission_this_step (return) the emitted radiation energy of a star + * during the time interval dt + * @param M the star mass (in internal units) + * @param Metallicity the stellar metallicity (in internal units) + * @param star_age_begin_of_step the star age at the beginning of this timestep + * (in internal units) + * @param star_age the star age at the end of this timestep(in internal units) + * @param ionizing_tables BPASS table that stores the total emitted photon number + * at different stellar age + * @param average_photon_energy average photon energy in each frequency bin, in + * erg + * @param phys_const struct holding physical constants + * @param internal_units units struct containing internal units + */ +__attribute__((always_inline)) INLINE static void +rt_get_emission_this_step_BPASS( + double emission_this_step[RT_NGROUPS], float M,float Metallicity, + double star_age_begin_of_step, double star_age, + double ***ionizing_tables, + const double average_photon_energy[RT_NGROUPS], + const struct phys_const* phys_const, + const struct unit_system* internal_units, + const double f_esc) { + + /* Get the array for the table range. + * TODO: Hard code in now, need to replace it. */ + static const double Metallicities[] = {1e-5, 1e-4, 0.01, 0.02, 0.03, 0.04, 0.06, 0.08, 0.1, 0.14, 0.2, 0.3, 0.4}; + int size_Metals = sizeof(Metallicities) / sizeof(Metallicities[0]); + static const double age_100Myr[] = { + 0.0, // 0 + 1.0, // 10^0 + 1.258925, // 10^0.1 + 1.584893, // 10^0.2 + 1.995262, // 10^0.3 + 2.511886, // 10^0.4 + 3.162278, // 10^0.5 + 3.981072, // 10^0.6 + 5.011872, // 10^0.7 + 6.309573, // 10^0.8 + 7.943282, // 10^0.9 + 10.0, // 10^1.0 + 12.589254, // 10^1.1 + 15.848932, // 10^1.2 + 19.952623, // 10^1.3 + 25.118864, // 10^1.4 + 31.622777, // 10^1.5 + 39.810717, // 10^1.6 + 50.118723, // 10^1.7 + 63.095734, // 10^1.8 + 79.432823, // 10^1.9 + 100.0 // 10^2.0 + }; + int size_Times = sizeof(age_100Myr) / sizeof(age_100Myr[0]); + const double normalized_mass = 1; //units of solar mass + + /* Convert some quantities. */ + const double time_to_Myr = units_cgs_conversion_factor(internal_units, UNIT_CONV_TIME) / + (365.25f * 24.f * 60.f * 60.f * 1e6f); + const double energy_units = + units_cgs_conversion_factor(internal_units, UNIT_CONV_ENERGY); + + /* Calculate internal mass to solar mass conversion factor */ + const double Msun_cgs = phys_const->const_solar_mass * + units_cgs_conversion_factor(internal_units, UNIT_CONV_MASS); + const double unit_mass_cgs = units_cgs_conversion_factor(internal_units, UNIT_CONV_MASS); + const double mass_to_solar_mass = unit_mass_cgs / Msun_cgs; + + /* Get the converted quantities. */ + const double star_age_before_Myr = star_age_begin_of_step * time_to_Myr; + const double star_age_now_Myr = star_age * time_to_Myr; + const double star_mass_Msolar = M * mass_to_solar_mass; + const double M_star_fraction = star_mass_Msolar / normalized_mass; + + if (Metallicity < Metallicities[0]) + Metallicity = Metallicities[0]; + else if (Metallicity > Metallicities[size_Metals - 1]) + Metallicity = Metallicities[size_Metals - 1]; + + /* TODO: add debug check macro for error check. */ + if (star_age_before_Myr < 0 || isnan(star_age_before_Myr)) { + error("Invalid star_age_before_Myr = %e", star_age_before_Myr); + } + + for (int g = 0; g < RT_NGROUPS; g++) { + if (star_age_before_Myr > 100.) { + /* If the stellar age before this timestep is above 100Myr, we set the emission to zero*/ + emission_this_step[g] = 0.; + } else { + if (!ionizing_tables[g]) error("ionizing_tables[%d] is NULL", g); + for (int i = 0; i < size_Metals; i++) { + if (!ionizing_tables[g][i]) error("ionizing_tables[%d][%d] is NULL", g, i); + } + double N_total_before = interpolate_2D_non_uniform(Metallicities, age_100Myr, + ionizing_tables[g], + size_Metals, size_Times, + Metallicity, star_age_before_Myr); + double N_total_now = interpolate_2D_non_uniform(Metallicities, age_100Myr, + ionizing_tables[g], + size_Metals, size_Times, + Metallicity, star_age_now_Myr); + double N_emission_this_step = N_total_now - N_total_before; + + /* average photon densities are in cgs! */ + const double E_g = f_esc * average_photon_energy[g] * N_emission_this_step * M_star_fraction / energy_units; + emission_this_step[g] = E_g; + + if (E_g < 0) { + error("Negative Photons??, N_total_before: %e, N_total_now: %e,star_age_before_Myr: %e, star_age_now_Myr: %e",N_total_before, N_total_now, star_age_before_Myr, star_age_now_Myr); + } + + //message("energy this step: %e, N_total_before: %e, N_total_now: %e, average_photon_energy[g]: %e, star_age_before_Myr: %e, star_age_now_Myr: %e, M_star_fraction: %e, N_emission_this_step:%e, Metalicities: %e", E_g, N_total_before, N_total_now, average_photon_energy[g], star_age_before_Myr, star_age_now_Myr, M_star_fraction, N_emission_this_step, Metallicity); + } + } +} + +#endif /* SWIFT_RT_STELLAR_EMISSION_MODEL_KIARA_H */ diff --git a/src/rt/KIARA/rt_struct.h b/src/rt/KIARA/rt_struct.h new file mode 100644 index 0000000000..250c9a51b7 --- /dev/null +++ b/src/rt/KIARA/rt_struct.h @@ -0,0 +1,172 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_STRUCT_KIARA_H +#define SWIFT_RT_STRUCT_KIARA_H + +/** + * @file src/rt/KIARA/rt_struct.h + * @brief Main header file for the KIARA M1 Closure radiative transfer struct. + */ + +/* Additional RT data in hydro particle struct */ +struct rt_part_data { + + /* Radiation state vector. */ + struct { + float energy_density; + float flux[3]; + } radiation[RT_NGROUPS]; + + /* Fluxes in the conservation law sense */ + struct { + float energy; + float flux[3]; + } flux[RT_NGROUPS]; + + /* Particle RT time step. */ + float flux_dt; + + /* gradients of the radiation state. */ + /* for the flux[3][3] quantity: + * first index: x, y, z coordinate of the flux. + * Second index: gradient along x, y, z direction. */ + struct { + float energy_density[3]; + float flux[3][3]; + } gradient[RT_NGROUPS]; + + /* cell slope limiter quantities */ + /* array of length two: store min among all neighbours + * at first index, store max among all neighbours at + * second index */ + /* the Gizmo-style slope limiting doesn't help for RT as is, + * so we're skipping it for now. */ + /* struct { */ + /* float energy_density[2]; */ + /* float flux[3][2]; */ + /* [> float maxr; [> just use the hydro one <] <] */ + /* } limiter[RT_NGROUPS]; */ + + /* Data for thermochemistry */ + struct { + float mass_fraction_HI; /* mass fraction taken by HI */ + float mass_fraction_HII; /* mass fraction taken by HII */ + float mass_fraction_HeI; /* mass fraction taken by HeI */ + float mass_fraction_HeII; /* mass fraction taken by HeII */ + float mass_fraction_HeIII; /* mass fraction taken by HeIII */ + float number_density_electrons; /* number density of electrons */ + } tchem; + +#ifdef GIZMO_MFV_SPH + /* Keep track of the actual mass fluxes of the gas species */ + struct { + float HI; /* mass fraction taken by HI */ + float HII; /* mass fraction taken by HII */ + float HeI; /* mass fraction taken by HeI */ + float HeII; /* mass fraction taken by HeII */ + float HeIII; /* mass fraction taken by HeIII */ + } mass_flux; +#endif + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* debugging data to store during entire run */ + + /*! how much radiation this part received from stars during total lifetime */ + unsigned long long debug_radiation_absorbed_tot; + + /* data to store during one time step */ + + /*! how many stars this part interacted with during injection*/ + /* Note: It's useless to write this in outputs, as it gets reset + * at the end of every step. */ + int debug_iact_stars_inject; + + /*! calls from gradient interaction loop in actual function */ + int debug_calls_iact_gradient_interaction; + + /*! calls from transport interaction loop in actual function */ + int debug_calls_iact_transport_interaction; + + /* Task completion flags */ + + /*! part got kicked? */ + int debug_kicked; + + /*! calls from ghost1 tasks */ + int debug_injection_done; + + /*! finalised computing gradients? */ + int debug_gradients_done; + + /*! transport step done? */ + int debug_transport_done; + + /*! thermochemistry done? */ + int debug_thermochem_done; + + /* Subcycling flags */ + + /*! Current subcycle wrt (last) hydro step */ + int debug_nsubcycles; + +#endif +}; + +/* Additional RT data in star particle struct */ +struct rt_spart_data { + + /* Stellar energy emission that will be injected in to gas. + * Total energy, not density, not rate! */ + float emission_this_step[RT_NGROUPS]; + + /*! Neighbour weigths in each octant surrounding the star */ + float octant_weights[8]; + +#ifdef SWIFT_RT_DEBUG_CHECKS + /* data to store during entire run */ + + /*! how much radiation this star emitted during total lifetime */ + unsigned long long debug_radiation_emitted_tot; + + /* data to store during one time step */ + + /*! how many hydro particles this particle interacted with + * during injection */ + int debug_iact_hydro_inject; + + /*! how many hydro particles this particle interacted with + * during injection prep*/ + int debug_iact_hydro_inject_prep; + + /*! stellar photon emisison rate computed? */ + int debug_emission_rate_set; + + /*! how much energy this star particle actually has injected into the gas */ + float debug_injected_energy[RT_NGROUPS]; + + /*! how much energy this star particle actually has injected into the gas over + * the entire run*/ + float debug_injected_energy_tot[RT_NGROUPS]; + + /*! sum up total weights used during injection to compare consistency */ + float debug_psi_sum; +#endif +}; + +#endif /* SWIFT_RT_STRUCT_KIARA_H */ diff --git a/src/rt/KIARA/rt_thermochemistry.c b/src/rt/KIARA/rt_thermochemistry.c new file mode 100644 index 0000000000..cda008c2eb --- /dev/null +++ b/src/rt/KIARA/rt_thermochemistry.c @@ -0,0 +1,513 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2020 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#include "rt_thermochemistry.h" +#include "rt_grackle_utils.h" +#include "rt_interaction_cross_sections.h" +#include "rt_interaction_rates.h" +#include "rt_ionization_equilibrium.h" +#include "rt_unphysical.h" +#include "rt_getters.h" +#include "../../cooling.h" + +/** + * @file src/rt/KIARA/rt_thermochemistry.h + * @brief Main header file for the KIARA M1 closure radiative transfer scheme + * thermochemistry related functions. + */ + +/** + * @brief initialize particle quantities relevant for the thermochemistry. + * + * @param p part to work with + * @param rt_props rt_properties struct + * @param hydro_props hydro properties struct + * @param phys_const physical constants struct + * @param us unit system struct + * @param cosmo cosmology struct + */ +void rt_tchem_first_init_part( + struct part* restrict p, struct xpart* restrict xp, const struct rt_props* rt_props, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cooling_function_data* cooling, + const struct cosmology* restrict cosmo) { + + if (rt_props->set_equilibrium_initial_ionization_mass_fractions) { + float XHI, XHII, XHeI, XHeII, XHeIII; + rt_ion_equil_get_mass_fractions(&XHI, &XHII, &XHeI, &XHeII, &XHeIII, p, + rt_props, hydro_props, phys_const, us, + cosmo); + p->rt_data.tchem.mass_fraction_HI = XHI; + p->rt_data.tchem.mass_fraction_HII = XHII; + p->rt_data.tchem.mass_fraction_HeI = XHeI; + p->rt_data.tchem.mass_fraction_HeII = XHeII; + p->rt_data.tchem.mass_fraction_HeIII = XHeIII; + } else if (rt_props->set_initial_ionization_mass_fractions) { + p->rt_data.tchem.mass_fraction_HI = rt_props->mass_fraction_HI_init; + p->rt_data.tchem.mass_fraction_HII = rt_props->mass_fraction_HII_init; + p->rt_data.tchem.mass_fraction_HeI = rt_props->mass_fraction_HeI_init; + p->rt_data.tchem.mass_fraction_HeII = rt_props->mass_fraction_HeII_init; + p->rt_data.tchem.mass_fraction_HeIII = rt_props->mass_fraction_HeIII_init; + } + + /* Initialize the cooling initial particle quantities. */ + cooling_first_init_part(phys_const, us, hydro_props, cosmo, cooling, p, xp); + + /* Here for KIARART test. We need to reset the value for cooling data. + * TODO :And we need to restructure it when we do the cosmological run. */ + xp->cooling_data.HI_frac = p->rt_data.tchem.mass_fraction_HI; + xp->cooling_data.HII_frac = p->rt_data.tchem.mass_fraction_HII; + xp->cooling_data.HeI_frac = p->rt_data.tchem.mass_fraction_HeI; + xp->cooling_data.HeII_frac = p->rt_data.tchem.mass_fraction_HeII; + xp->cooling_data.HeIII_frac = p->rt_data.tchem.mass_fraction_HeIII; + xp->cooling_data.e_frac = xp->cooling_data.HII_frac + + 0.25 * xp->cooling_data.HeII_frac + + 0.5 * xp->cooling_data.HeIII_frac; +} + +/** + * @brief Main function for the thermochemistry step. + * + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. + * @param depth recursion depth + */ +INLINE void rt_do_thermochemistry( + struct part* restrict p, struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct cooling_function_data* restrict cooling, + const struct unit_system* restrict us, const double dt, + const double dt_therm, int depth) { + /* Note: Can't pass rt_props as const struct because of grackle + * accessinging its properties there */ + + /* Nothing to do here? */ + if (rt_props->skip_thermochemistry) return; + if (dt == 0.) return; + + /* This is where the fun begins */ + /* ---------------------------- */ + + /* initialize data so it'll be in scope */ + //grackle_field_data particle_grackle_data; + + gr_float density = hydro_get_physical_density(p, cosmo); + + /* In rare cases, unphysical solutions can arise with negative densities + * which won't be fixed in the hydro part until further down the dependency + * graph. Also, we can have vacuum, in which case we have nothing to do here. + * So exit early if that is the case. */ + if (density <= 0.) return; + + const float u_minimal = hydro_props->minimal_internal_energy; +#ifdef GIZMO_MFV_SPH + /* Physical internal energy */ + gr_float internal_energy_phys = + hydro_get_physical_internal_energy(p, xp, cosmo); + gr_float internal_energy = max(internal_energy_phys, u_minimal); + + const float u_old = internal_energy; +#else + /* Get physical internal energy */ + const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo); + + gr_float internal_energy_phys = hydro_get_physical_internal_energy(p, xp, cosmo); + + gr_float internal_energy = max(internal_energy_phys, u_minimal); + + const float u_old = internal_energy; +#endif + + /* initialize data to send to grackle */ + gr_float *species_densities; + species_densities = (gr_float *)calloc(N_SPECIES, sizeof(gr_float)); + grackle_field_data data; + + /* load particle information from particle to grackle data */ + float radiation_energy_density[RT_NGROUPS]; + rt_part_get_physical_radiation_energy_density(p, radiation_energy_density, cosmo); + + /* TODO: put the iact_rates to the cooling grackle data. */ + gr_float iact_rates[5]; + rt_get_interaction_rates_for_grackle( + iact_rates, radiation_energy_density, species_densities, + rt_props->average_photon_energy, rt_props->energy_weighted_cross_sections, + rt_props->number_weighted_cross_sections, phys_const, us); + + /* TODO: currently manually add iact_rates in grackle data field here. */ + data.RT_heating_rate = &iact_rates[0]; + data.RT_HI_ionization_rate = &iact_rates[1]; + data.RT_HeI_ionization_rate = &iact_rates[2]; + data.RT_HeII_ionization_rate = &iact_rates[3]; + data.RT_H2_dissociation_rate = &iact_rates[4]; + + cooling_copy_to_grackle(&data, us, cosmo, cooling, p, xp, dt, 0., species_densities, iact_rates, 0); + + /* solve chemistry */ + /* Note: `grackle_rates` is a global variable defined by grackle itself. + * Using a manually allocd and initialized variable here fails with MPI + * for some reason. */ + if (local_solve_chemistry( + &rt_props->grackle_chemistry_data, &rt_props->grackle_chemistry_rates, + &rt_props->grackle_units, &data, dt) == 0) + error("Error in solve_chemistry."); + + /* copy from grackle data to particle */ + cooling_copy_from_grackle(&data, p, xp, cooling, species_densities[12]); + + /* copy updated grackle data to particle */ + /* update particle internal energy. Grackle had access by reference + * to internal_energy */ + internal_energy_phys = data.internal_energy[0]; + const float u_new = max(internal_energy_phys, u_minimal); + + /* Re-do thermochemistry? */ + if ((rt_props->max_tchem_recursion > depth) && + (fabsf(u_old - u_new) > 0.1 * u_old)) { + /* Note that grackle already has internal "10% rules". But sometimes, they + * may not suffice. */ + //rt_clean_grackle_fields(&particle_grackle_data); + cooling_grackle_free_data(&data); + free(species_densities); + rt_do_thermochemistry(p, xp, rt_props, cosmo, hydro_props, phys_const, cooling, us, + 0.5 * dt, 0.5 * dt_therm, depth + 1); + rt_do_thermochemistry(p, xp, rt_props, cosmo, hydro_props, phys_const, cooling, us, + 0.5 * dt, 0.5 * dt_therm, depth + 1); + return; + } + + /* If we're good, update the particle data from grackle results */ +#ifdef GIZMO_MFV_SPH + hydro_set_physical_internal_energy(p, u_new); +#else + /* compute the heating/cooling due to the thermochemistry */ + float cool_du_dt = (u_new - u_old) / dt_therm; + + /* check whether the the thermochemistry heating/cooling is larger + * than du/dt of the particle. If it is, directly set the new internal energy + * of the particle, and set du/dt = 0.*/ + if (fabsf(cool_du_dt) > fabsf(hydro_du_dt)){ + hydro_set_physical_internal_energy(p, xp, cosmo, u_new); + + hydro_set_physical_internal_energy_dt(p, cosmo, 0.); + } else { + /* If it isn't, ignore the radiative cooling and apply only hydro du/dt. */ + hydro_set_physical_internal_energy_dt(p, cosmo, hydro_du_dt); + } +#endif + + /* Update mass fractions */ + const gr_float one_over_rho = 1. / density; + p->rt_data.tchem.mass_fraction_HI = + data.HI_density[0] * one_over_rho; + p->rt_data.tchem.mass_fraction_HII = + data.HII_density[0] * one_over_rho; + p->rt_data.tchem.mass_fraction_HeI = + data.HeI_density[0] * one_over_rho; + p->rt_data.tchem.mass_fraction_HeII = + data.HeII_density[0] * one_over_rho; + p->rt_data.tchem.mass_fraction_HeIII = + data.HeIII_density[0] * one_over_rho; + + /* Update radiation fields */ + /* First get absorption rates at the start and the end of the step */ + double absorption_rates[RT_NGROUPS]; + rt_get_absorption_rates( + absorption_rates, species_densities, rt_props->average_photon_energy, + rt_props->number_weighted_cross_sections, phys_const, us); + + gr_float species_densities_new[6]; + species_densities_new[0] = data.HI_density[0]; + species_densities_new[1] = data.HII_density[0]; + species_densities_new[2] = data.HeI_density[0]; + species_densities_new[3] = data.HeII_density[0]; + species_densities_new[4] = data.HeIII_density[0]; + species_densities_new[5] = data.e_density[0]; + double absorption_rates_new[RT_NGROUPS]; + rt_get_absorption_rates(absorption_rates_new, species_densities_new, + rt_props->average_photon_energy, + rt_props->number_weighted_cross_sections, phys_const, + us); + + /* Now remove absorbed radiation */ + for (int g = 0; g < RT_NGROUPS; g++) { + const float E_old = p->rt_data.radiation[g].energy_density; + double f = dt * 0.5 * (absorption_rates[g] + absorption_rates_new[g]); + f = min(1., f); + f = max(0., f); + p->rt_data.radiation[g].energy_density *= (1. - f); + for (int i = 0; i < 3; i++) { + p->rt_data.radiation[g].flux[i] *= (1. - f); + } + + rt_check_unphysical_state(&p->rt_data.radiation[g].energy_density, + p->rt_data.radiation[g].flux, E_old, + /*callloc=*/2); + } + + /* Clean up after yourself. */ + cooling_grackle_free_data(&data); + free(species_densities); +} + +/** + * @brief Computes an upper boundary for the thermochemistry/cooling + * time. + * + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + */ +float rt_tchem_get_tchem_time( + const struct part* restrict p, const struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct cooling_function_data* restrict cooling, + const struct unit_system* restrict us) { + /* Note: Can't pass rt_props as const struct because of grackle + * accessinging its properties there */ + + + /* initialize data to send to grackle */ + gr_float *species_densities; + species_densities = (gr_float *)calloc(N_SPECIES, sizeof(gr_float)); + grackle_field_data data; + + float radiation_energy_density[RT_NGROUPS]; + rt_part_get_physical_radiation_energy_density(p, radiation_energy_density, cosmo); + + gr_float iact_rates[5]; + rt_get_interaction_rates_for_grackle( + iact_rates, radiation_energy_density, species_densities, + rt_props->average_photon_energy, rt_props->energy_weighted_cross_sections, + rt_props->number_weighted_cross_sections, phys_const, us); + + /* load particle information from particle to grackle data */ + cooling_copy_to_grackle(&data, us, cosmo, cooling, p, xp, 0., 0., species_densities, iact_rates, 0); + + /* TODO: currently manually add iact_rates in grackle data field here. */ + data.RT_heating_rate = &iact_rates[0]; + data.RT_HI_ionization_rate = &iact_rates[1]; + data.RT_HeI_ionization_rate = &iact_rates[2]; + data.RT_HeII_ionization_rate = &iact_rates[3]; + data.RT_H2_dissociation_rate = &iact_rates[4]; + + /* Compute 'cooling' time */ + /* Note: grackle_rates is a global variable defined by grackle itself. + * Using a manually allocd and initialized variable here fails with MPI + * for some reason. */ + gr_float tchem_time; + if (local_calculate_cooling_time( + &rt_props->grackle_chemistry_data, &rt_props->grackle_chemistry_rates, + &rt_props->grackle_units, &data, &tchem_time) == 0) + error("Error in calculate_cooling_time."); + + /* Clean up after yourself. */ + cooling_grackle_free_data(&data); + free(species_densities); + + return (float)tchem_time; +} + +/** + * @brief Main function for the thermochemistry step when coupling with + * subgrid physics. + * + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. + * @param depth recursion depth + */ +INLINE void rt_do_thermochemistry_with_subgrid( + struct part* restrict p, struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, + const struct phys_const* restrict phys_const, + const struct cooling_function_data* restrict cooling, + const struct unit_system* restrict us, const double dt, + const double dt_therm, int depth) { + /* Note: Can't pass rt_props as const struct because of grackle + * accessinging its properties there */ + + /* Nothing to do here? */ + if (rt_props->skip_thermochemistry) return; + + /* Compute cooling time and other quantities needed for firehose */ + /* TODO: firehose is using without RT_rates */ + firehose_cooling_and_dust(phys_const, us, cosmo, hydro_props, + cooling, p, xp, dt); + + /* Update the subgrid properties */ + cooling_set_particle_subgrid_properties( phys_const, us, + cosmo, hydro_props, floor_props, cooling, p, xp); + + /* No cooling if particle is decoupled */ + if (p->decoupled) return; + + if (dt == 0.f || dt_therm == 0.f) return; + + float radiation_energy_density[RT_NGROUPS]; + rt_part_get_physical_radiation_energy_density(p, radiation_energy_density, cosmo); + + /* TODO: put the iact_rates to the cooling grackle data. */ + gr_float *iact_rates = (gr_float *)malloc(5 * sizeof(gr_float)); + if (iact_rates == NULL) { + fprintf(stderr, "Error: malloc failed for iact_rates\n"); + exit(EXIT_FAILURE); + } + + /* Load species info for computing interaction rates */ + gr_float rt_species[6]; + float rho = hydro_get_physical_density(p, cosmo); + if (p->cooling_data.subgrid_temp > 0. && + p->cooling_data.subgrid_fcold > 1.e-6) { + rho = cooling_get_subgrid_density(p, xp) * p->cooling_data.subgrid_fcold; + } + rt_tchem_get_species_densities(p, rho, rt_species); + + /* Compute interaction rates */ + rt_get_interaction_rates_for_grackle( + iact_rates, radiation_energy_density, rt_species, + rt_props->average_photon_energy, rt_props->energy_weighted_cross_sections, + rt_props->number_weighted_cross_sections, phys_const, us); + + //printf("=== RT computed interaction rates ===\n"); + //printf("RT RT_heating_rate = %e\n", iact_rates[0]); + //printf("RT RT_HI_ionization_rate = %e\n", iact_rates[1]); + //printf("RT RT_HeI_ionization_rate = %e\n", iact_rates[2]); + //printf("RT RT_HeII_ionization_rate = %e\n", iact_rates[3]); + //printf("RT RT_H2_dissociation_rate = %e\n", iact_rates[4]); + //printf("RT data RT_heating_rate = %e\n", *data.RT_heating_rate); + //printf("RT data RT_HI_ionization_rate = %e\n", *data.RT_HI_ionization_rate); + //printf("RT data RT_HeI_ionization_rate = %e\n", *data.RT_HeI_ionization_rate); + //printf("RT data RT_HeII_ionization_rate = %e\n", *data.RT_HeII_ionization_rate); + //printf("RT data RT_H2_dissociation_rate = %e\n", *data.RT_H2_dissociation_rate); + //printf("RT data data->HI_density = %e\n", *data.HI_density); + //printf("RT data data->HII_density = %e\n", *data.HII_density); + + /* Check for unphysical values (e.g., NaN or negative rates) */ + for (int i = 0; i < 5; i++) { + if (iact_rates[i] < 0.) { + error("Unphysical negative rate detected at index %d: %.4g", i, iact_rates[i]); + } else if (isnan(iact_rates[i]) || !isfinite(iact_rates[i])) { + error("NaN detected in rate at index %d", i); + } + //message("RT rate at index %d: %.4g", i, iact_rates[i]); + } + + /* solve chemistry, update thermal energy */ + cooling_do_grackle_cooling(phys_const, us, cosmo, hydro_props, + floor_props, cooling, + p, xp, iact_rates, dt, dt_therm); + + //int target_id = 2134785; + //int range = 50; + + //const float z = 1/cosmo->a - 1; + + //if (p->id >= target_id - range && p->id <= target_id + range) { + // Open file in append mode so new data is added without overwriting + // FILE *file = fopen("particle_track.txt", "a"); + // if (file == NULL) { + // printf("Error opening file!\n"); + // return; + // } + + // fprintf(file, "particle_track: p_id = %llu, density = %e, u_old = %e, u_new = %e, cool_du_dt = %e, hydro_du_dt = %e, p->cooling_data.subgrid_temp = %e, T_floor = %e, z=%e \n", p->id, p->rho, u_old, u_new, cool_du_dt, hydro_du_dt, p->cooling_data.subgrid_temp, T_floor, z); + + // Close the file + // fclose(file); + //} + + //message("particle_track: id = %llu, u_old = %e, u_new = %e, cool_du_dt = %e\n", p->id, u_old, u_new, cool_du_dt); + + /* Load mass fractions into rt_data */ + p->rt_data.tchem.mass_fraction_HI = xp->cooling_data.HI_frac; + p->rt_data.tchem.mass_fraction_HII = xp->cooling_data.HII_frac; + p->rt_data.tchem.mass_fraction_HeI = xp->cooling_data.HeI_frac; + p->rt_data.tchem.mass_fraction_HeII = xp->cooling_data.HeII_frac; + p->rt_data.tchem.mass_fraction_HeIII = xp->cooling_data.HeIII_frac; + + /* Update radiation fields */ + /* First get absorption rates at the start of the step */ + double absorption_rates[RT_NGROUPS]; + rt_get_absorption_rates( + absorption_rates, rt_species, rt_props->average_photon_energy, + rt_props->number_weighted_cross_sections, phys_const, us); + + /* Set up absorption rate calculation at end of step */ + gr_float rt_species_new[6]; + rt_tchem_get_species_densities(p, rho, rt_species_new); + + /* Constrain the value that are not physical */ + if (p->rt_data.tchem.mass_fraction_HI > 0.76f) { + rt_species_new[0] = 0.76f * rho; + } + + if (p->rt_data.tchem.mass_fraction_HeI > 0.24f) { + rt_species_new[2] = 0.24f * rho; + } + + /* Compute absorption rates at the end of the step */ + double absorption_rates_new[RT_NGROUPS]; + rt_get_absorption_rates(absorption_rates_new, rt_species_new, + rt_props->average_photon_energy, + rt_props->number_weighted_cross_sections, phys_const, + us); + + /* Now remove absorbed radiation, using average absorption */ + for (int g = 0; g < RT_NGROUPS; g++) { + const float E_old = p->rt_data.radiation[g].energy_density; + double f = dt * 0.5 * (absorption_rates[g] + absorption_rates_new[g]); + f = min(1., f); + f = max(0., f); + p->rt_data.radiation[g].energy_density *= (1. - f); + for (int i = 0; i < 3; i++) { + p->rt_data.radiation[g].flux[i] *= (1. - f); + } + + rt_check_unphysical_state(&p->rt_data.radiation[g].energy_density, + p->rt_data.radiation[g].flux, E_old, + /*callloc=*/2); + } + + /* Clean up after yourself. */ + free(iact_rates); +} + diff --git a/src/rt/KIARA/rt_thermochemistry.h b/src/rt/KIARA/rt_thermochemistry.h new file mode 100644 index 0000000000..debe743a81 --- /dev/null +++ b/src/rt/KIARA/rt_thermochemistry.h @@ -0,0 +1,120 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2024 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_KIARA_THERMOCHEMISTRY_H +#define SWIFT_RT_KIARA_THERMOCHEMISTRY_H + +#include "part.h" +#include "rt_properties.h" +#include "hydro_properties.h" +#include "physical_constants.h" +#include "units.h" +#include "cosmology.h" +#include "cooling.h" + +/** + * @file src/rt/KIARA/rt_thermochemistry.h + * @brief Main header file for the KIARA M1 closure radiative transfer scheme + * thermochemistry related functions. + */ + +/** + * @brief initialize particle quantities relevant for the thermochemistry. + * + * @param p part to work with + * @param rt_props rt_properties struct + * @param hydro_props hydro properties struct + * @param phys_const physical constants struct + * @param us unit system struct + * @param cosmo cosmology struct + */ +void rt_tchem_first_init_part( + struct part* restrict p, struct xpart* restrict xp, const struct rt_props* rt_props, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cooling_function_data* cooling, + const struct cosmology* restrict cosmo); + +/** + * @brief Main function for the thermochemistry step. + * + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. + * @param depth recursion depth + */ +void rt_do_thermochemistry( + struct part* restrict p, struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct cooling_function_data* restrict cooling, + const struct unit_system* restrict us, const double dt, + const double dt_therm, int depth); + +/** + * @brief Computes an upper boundary for the thermochemistry/cooling + * time. + * + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + */ +float rt_tchem_get_tchem_time( + const struct part* restrict p, const struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct cooling_function_data* restrict cooling, + const struct unit_system* restrict us); + +/** + * @brief Main function for the thermochemistry step when coupling with + * subgrid physics. + * + * @param p Particle to work on. + * @param xp Pointer to the particle' extended data. + * @param rt_props RT properties struct + * @param cosmo The current cosmological model. + * @param hydro_props The #hydro_props. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param dt The time-step of this particle. + * @param depth recursion depth + */ +void rt_do_thermochemistry_with_subgrid( + struct part* restrict p, struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, + const struct phys_const* restrict phys_const, + const struct cooling_function_data* restrict cooling, + const struct unit_system* restrict us, const double dt, + const double dt_therm, int depth); + +#endif /* SWIFT_RT_KIARA_THERMOCHEMISTRY_H */ diff --git a/src/rt/KIARA/rt_thermochemistry_utils.h b/src/rt/KIARA/rt_thermochemistry_utils.h new file mode 100644 index 0000000000..e3e6086fc7 --- /dev/null +++ b/src/rt/KIARA/rt_thermochemistry_utils.h @@ -0,0 +1,294 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_KIARA_THERMOCHEMISTRY_UTILS_H +#define SWIFT_RT_KIARA_THERMOCHEMISTRY_UTILS_H + +/** + * @file src/rt/KIARA/rt_thermochemistry_utils.h + * @brief thermochemistry utilities and misc functions. + * */ + +#include "rt_species.h" + +/** + * @brief compute the mean molecular weight mu for given + * hydrogen and helium mass fractions + * @param XHI mass fraction of HI + * @param XHII mass fraction of HII + * @param XHeI mass fraction of HeI + * @param XHeII mass fraction of HeII + * @param XHeIII mass fraction of HeII + */ +__attribute__((always_inline)) INLINE static double +rt_tchem_get_mean_molecular_weight(float XHI, float XHII, float XHeI, + float XHeII, float XHeIII) { + + /* 1/mu = sum_j X_j / A_j * (1 + E_j) + * A_H = 1, E_H = 0 + * A_Hp = 1, E_Hp = 1 + * A_He = 4, E_He = 0 + * A_Hep = 4, E_Hep = 1 + * A_Hepp = 4, E_Hepp = 2 */ + const double one_over_mu = + XHI + 2.0 * XHII + 0.25 * XHeI + 0.5 * XHeII + 0.75 * XHeIII; + + return (1.0 / one_over_mu); +} + +/** + * @brief compute the temperature of an ideal gas for a given + * specific internal energy and mean molecular weight + * + * @param u specific internal energy of the gas + * @param mu mean molecular weight of the gas + * @param kB Boltzmann constant + * @param mp proton mass + * */ +__attribute__((always_inline)) INLINE static float +rt_tchem_temperature_from_internal_energy(double u, double mu, const double kB, + const double mp) { + + return u * hydro_gamma_minus_one * mu * mp / kB; +} + +/** + * @brief compute the (physical) specific internal energy + * of an ideal gas for given temperature and mean molecular + * weight. + * + * @param u specific internal energy of the gas + * @param mu mean molecular weight of the gas + * @param kB Boltzmann constant + * @param mp proton mass + * */ +__attribute__((always_inline)) INLINE static float +rt_tchem_internal_energy_from_T(const double T, const double mu, + const double kB, const double mp) { + + return kB * T * hydro_one_over_gamma_minus_one / (mu * mp); +} + +/** + * @brief compute the derivative w.r.t. temperature of the + * (physical) specific internal energy of an ideal gas for + * given temperature and mean molecular weight + * + * @param u specific internal energy of the gas + * @param mu mean molecular weight of the gas + * @param kB Boltzmann constant + * @param mp proton mass + * */ +__attribute__((always_inline)) INLINE static float rt_tchem_internal_energy_dT( + double mu, const double kB, const double mp) { + + const double dudT = kB * hydro_one_over_gamma_minus_one / (mu * mp); + + return dudT; +} + +/** + * @brief get the densities of all species and electrons. + * + * @param p particle to use + * @param rho particle physical density + * @param species_densities array to write densities in + **/ +__attribute__((always_inline)) INLINE static void +rt_tchem_get_species_densities(const struct part* restrict p, gr_float rho, + gr_float species_densities[6]) { + + species_densities[0] = p->rt_data.tchem.mass_fraction_HI * rho; + species_densities[1] = p->rt_data.tchem.mass_fraction_HII * rho; + species_densities[2] = p->rt_data.tchem.mass_fraction_HeI * rho; + species_densities[3] = p->rt_data.tchem.mass_fraction_HeII * rho; + species_densities[4] = p->rt_data.tchem.mass_fraction_HeIII * rho; + + /* nHII = rho_HII / m_p + * nHeII = rho_HeII / 4 m_p + * nHeIII = rho_HeIII / 4 m_p + * ne = nHII + nHeII + 2 * nHeIII + * But: it is grackle convention to use rho_e = n_e * m_p */ + const gr_float rho_e = species_densities[1] + 0.25 * species_densities[3] + + 0.5 * species_densities[4]; + species_densities[5] = rho_e; +} + +/** + * @brief get the number densities of the ionizing species in cgs. + * + * @param ns_cgs (return) number densities in cgs of ionizing species + * @param species_densities densities of all species as returned by + * rt_tchem_get_species_densities() + * @param phys_const physical constants struct + * @param us internal units struct + * + **/ +__attribute__((always_inline)) INLINE static void +rt_tchem_get_ionizing_species_number_densities( + double ns_cgs[rt_ionizing_species_count], gr_float species_densities[6], + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us) { + + const double m_p = phys_const->const_proton_mass; + const double to_inv_volume_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_INV_VOLUME); + const double rho_to_n_cgs = to_inv_volume_cgs / m_p; + + ns_cgs[rt_ionizing_species_HI] = species_densities[0] * rho_to_n_cgs; + ns_cgs[rt_ionizing_species_HeI] = 0.25 * species_densities[2] * rho_to_n_cgs; + ns_cgs[rt_ionizing_species_HeII] = 0.25 * species_densities[3] * rho_to_n_cgs; +} + +/** + * @brief get the (physical) temperature of the gas + * + * @param p particle to use + * @param phys_const physical constants struct + * @param cosmo cosmology struct + **/ +__attribute__((always_inline)) INLINE static double +rt_tchem_get_gas_temperature(const struct part* restrict p, + const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo) { + + const double kB = phys_const->const_boltzmann_k; + const double mp = phys_const->const_proton_mass; + + const float XHI = p->rt_data.tchem.mass_fraction_HI; + const float XHII = p->rt_data.tchem.mass_fraction_HII; + const float XHeI = p->rt_data.tchem.mass_fraction_HeI; + const float XHeII = p->rt_data.tchem.mass_fraction_HeII; + const float XHeIII = p->rt_data.tchem.mass_fraction_HeIII; + + const double mu = + rt_tchem_get_mean_molecular_weight(XHI, XHII, XHeI, XHeII, XHeIII); + const double u = hydro_get_drifted_physical_internal_energy(p, cosmo); + + double T = rt_tchem_temperature_from_internal_energy(u, mu, kB, mp); + + return T; +} + +/** + * @brief Set a particle's density and internal energy. + * + * This function is only intended for use in very special case idealized + * tests, like the Iliev+06 tests, where we require fixed densities and + * temperatures. + **/ +__attribute__((always_inline)) INLINE static void +rt_tchem_set_particle_quantities_for_test(struct part* restrict p) { + +#ifdef GIZMO_MFV_SPH + + /* Set the values that you actually want. Needs to be in internal units.*/ + /* 1 hydrogen_atom_mass / cm^3 / (1.98848e18 g/IMU * 3.0857e15cm/ILU^3) */ + /* float density = 2.471e+04; */ + + /* Set the values that you actually want. Needs to be in internal units.*/ + /* 10^-3 hydrogen_atom_mass / cm^3 / (1.98848e18 g/IMU * 3.0857e15cm/ILU^3) */ + float density = 2.471e+01; + + /* 100 K */ + float internal_energy = 1.23816f; + + /* 10000 K with xHII = 1e-3 for Iliev Test 1 */ + /* float internal_energy = 124.8416491f; */ + + /* Be vocal, just in case somebody forgets you exist. */ + if (p->id == 1) message("Setting density from %.3e to %.3e", p->rho, density); + + float mass_corr = density / p->rho; + p->rho = density; + p->conserved.mass *= mass_corr; + /* This assumes zero velocity */ + p->conserved.energy = p->conserved.mass * internal_energy; + hydro_set_internal_energy(p, internal_energy); + +#else + + error("This isn't implemented for SPH yet."); + +#endif +} + +/** + * @brief Set a particle's radiation field given a photon flux. + * + * This function is only intended for use in very special case idealized + * tests, like the Iliev+06 tests, where we require fixed densities and + * temperatures. + * + * @param p particle to modify + * @param time current simulation time + * @param us unity system. + **/ +__attribute__((always_inline)) INLINE static void +rt_tchem_set_particle_radiation_field_for_test( + struct part* restrict p, const double time, + const struct unit_system* restrict us) { + + const double time_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_TIME); + const double t_Myr = time * time_to_cgs / (3600. * 24. * 365. * 1e6); + + /* NOTE: this assumes that the test is set up with 3 photon groups. */ + double fixed_fluxes[3]; + for (int g = 0; g < 3; g++) fixed_fluxes[g] = 0.; + + if (t_Myr < 0.5) { + /* Be vocal, just in case somebody forgets you exist. */ + if (p->id == 1) message("Setting fixed radiation field."); + /* Set fixed radiation fields, in cgs*/ + fixed_fluxes[0] = 1.350e01; + fixed_fluxes[1] = 2.779e01; + fixed_fluxes[2] = 6.152e00; + } + + const double flux_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_FLUX_PER_UNIT_SURFACE); + const double cf = rt_params.reduced_speed_of_light_inverse / flux_to_cgs; + + /* Note that we inject energy / time / surface, not identical to what */ + /* is in Iliev06 paper */ + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.radiation[g].energy_density = fixed_fluxes[g] * cf; + } +} + +/** + * @brief Modify a boundary particle. + * + * This function is only intended for use in very special case idealized + * tests, like the Iliev+06 tests, to deal with boundary conditions in + * a simple manner. + * */ +__attribute__((always_inline)) INLINE static void +rt_tchem_set_boundary_particles_for_test(struct part* restrict p) { + + if (p->id >= 1000000000) { + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.radiation[g].energy_density = 0.f; + p->rt_data.radiation[g].flux[0] = 0.f; + p->rt_data.radiation[g].flux[1] = 0.f; + p->rt_data.radiation[g].flux[2] = 0.f; + } + } +} + +#endif /* SWIFT_RT_KIARA_THERMOCHEMISTRY_UTILS_H */ diff --git a/src/rt/KIARA/rt_unphysical.h b/src/rt/KIARA/rt_unphysical.h new file mode 100644 index 0000000000..7e5b76692e --- /dev/null +++ b/src/rt/KIARA/rt_unphysical.h @@ -0,0 +1,237 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . + * + ******************************************************************************/ +#ifndef SWIFT_RT_UNPHYSICAL_KIARA_H +#define SWIFT_RT_UNPHYSICAL_KIARA_H + +/** + * @file src/rt/KIARA/rt_unphysical.h + * @brief Routines for checking for and correcting unphysical scenarios + */ + +/** + * @brief check for and correct if needed unphysical + * values for a radiation state. + * + * @param energy_density pointer to the radiation energy density + * @param flux pointer to radiation flux (3 dimensional) + * @param e_old energy density before change to check. Set = 0 if not available + * @param callloc integer indentifier where this function was called from + */ +__attribute__((always_inline)) INLINE static void rt_check_unphysical_state( + float* energy_density, float* flux, const float e_old, int callloc) { + + /* Check for negative energies */ + /* Note to self for printouts: Maximal allowable F = E * c. + * In some cases, e.g. while cooling, we don't modify the fluxes, + * so you can get an estimate of what the photon energy used to be + * by dividing the printed out fluxes by the speed of light in + * code units */ +#ifdef SWIFT_DEBUG_CHECKS + float ratio = -1.f; + char print = 0; + if (e_old == 0.) { + if (*energy_density < -1.e-4f) print = 1; + } else { + if (fabsf(*energy_density) > 1.e-30) { + if (*energy_density < -1.e-4f * fabsf(e_old)) print = 1; + ratio = fabsf(*energy_density / e_old); + } + } + /* callloc = 1 is gradient extrapolation. Don't print out those. */ + if (callloc == 1) print = 0; + if (print) + message("Fixing unphysical energy case %d | %.6e | %.6e %.6e %.6e | %.6e", + callloc, *energy_density, flux[0], flux[1], flux[2], ratio); +#endif + if (isinf(*energy_density) || isnan(*energy_density)) + error("Got inf/nan radiation energy case %d | %.6e | %.6e %.6e %.6e", + callloc, *energy_density, flux[0], flux[1], flux[2]); + + if (*energy_density <= 0.f) { + *energy_density = 0.f; + flux[0] = 0.f; + flux[1] = 0.f; + flux[2] = 0.f; + return; + } + + /* Check for too high fluxes */ + const double flux2 = + flux[0] * flux[0] + flux[1] * flux[1] + flux[2] * flux[2]; + const double flux_norm = sqrt(flux2); + const double flux_max = rt_params.reduced_speed_of_light * *energy_density; + if (flux_norm > flux_max) { + const double correct = flux_max / flux_norm; + message("RT flux=%g max=%g edens=%g correct=%g", sqrtf(flux2), flux_max, *energy_density, correct); + flux[0] *= correct; + flux[1] *= correct; + flux[2] *= correct; + } +} + +/** + * @brief Do additional checks after reading in initial conditions, and exit on + * error. + * + * @param p particle we're checking + * @param group current photon group we're checking + * @param energy_density pointer to the radiation energy density + * @param flux pointer to radiation flux (3 dimensional) + * @param c the speed of light (in internal units). NOT the reduced speed of + * light. + */ +__attribute__((always_inline)) INLINE static void rt_check_unphysical_state_ICs( + const struct part* restrict p, int group, float* energy_density, + float* flux, const double c) { + + /* Nothing to do here. The other unphysical check will catch other problems. + */ + if (*energy_density == 0.f) return; + + /* Check for negative energies */ + if (*energy_density < 0.f) + error( + "Found particle with negative energy density after reading in ICs: " + "pid= %lld group=%d E=%.6g", + p->id, group, *energy_density); + if (*energy_density > FLT_MAX || isnan(*energy_density)) + error("Got inf/nan energy_density: %g", *energy_density); + + /* Check for too high fluxes */ + const float flux2 = flux[0] * flux[0] + flux[1] * flux[1] + flux[2] * flux[2]; + const float flux_norm = sqrtf(flux2); + const float flux_max = c * *energy_density; + if (flux_max > FLT_MAX || isnan(flux_max)) + error("Got inf/nan flux_max: %g", flux_max); + if (flux_norm > FLT_MAX || isnan(flux_norm)) + error("Got inf/nan flux_norm: %g", flux_norm); + if (flux_norm > flux_max * 1.0001) { + error( + "Found too high radiation flux for a particle: pid=%lld, group=%d, " + "have=%.6g, max=%.6g", + p->id, group, flux_norm, flux_max); + } +} + +/** + * @brief check for and correct if needed unphysical + * values for a flux in the sense of hyperbolic conservation laws + * + * @param flux hyperbolic flux: 4 components (photon energy + + * photon flux) x 3 dimensions each + */ +__attribute__((always_inline)) INLINE static void +rt_check_unphysical_hyperbolic_flux(float flux[4][3]) { + +#ifdef SWIFT_DEBUG_CHECKS + int nans = 0; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 3; j++) { + if (isnan(flux[i][j])) { + nans += 1; + break; + } + } + } + + if (nans) { + message( + "Fixing unphysical hyperbolic flux:" + " %.3e %.3e %.3e | %.3e %.3e %.3e |" + " %.3e %.3e %.3e | %.3e %.3e %.3e", + flux[0][0], flux[0][1], flux[0][2], flux[1][0], flux[1][1], flux[1][2], + flux[2][0], flux[2][1], flux[2][2], flux[3][0], flux[3][1], flux[3][2]); + } +#endif + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 3; j++) { + if (isnan(flux[i][j])) { + flux[i][j] = 0.f; + } + } + } +} + +/** + * @brief check whether gas species mass fractions have physical + * values and correct small errors if necessary. + * + * @param p particle to work on + */ +__attribute__((always_inline)) INLINE static void +rt_check_unphysical_mass_fractions(struct part* restrict p) { + + /* For now, catch either mass or rho being zero. At the moment, they are not + * necessarily both zero. For example, an unphysical check may zero out both + * mass and density when it becomes negative in a hydro step. Once that + * happens, particles may gain mass through flux exchanges with other active + * particles, while they themselves remain inactive. The density of such + * inactive particles however remains zero until the particle is active + * again. See issue #833. */ + + if (hydro_get_mass(p) <= 0.f || p->rho <= 0.f) { + /* Deal with unphysical situations and vacuum. */ + p->rt_data.tchem.mass_fraction_HI = RT_KIARA_TINY_MASS_FRACTION; + p->rt_data.tchem.mass_fraction_HII = RT_KIARA_TINY_MASS_FRACTION; + p->rt_data.tchem.mass_fraction_HeI = RT_KIARA_TINY_MASS_FRACTION; + p->rt_data.tchem.mass_fraction_HeII = RT_KIARA_TINY_MASS_FRACTION; + p->rt_data.tchem.mass_fraction_HeIII = RT_KIARA_TINY_MASS_FRACTION; + return; + } + +#ifdef SWIFT_RT_DEBUG_CHECKS + if (p->rt_data.tchem.mass_fraction_HI < -1e4) + message("WARNING: Got negative HI mass fraction?"); + if (p->rt_data.tchem.mass_fraction_HII < -1e4) + message("WARNING: Got negative HII mass fraction?"); + if (p->rt_data.tchem.mass_fraction_HeI < -1e4) + message("WARNING: Got negative HeI mass fraction?"); + if (p->rt_data.tchem.mass_fraction_HeII < -1e4) + message("WARNING: Got negative HeII mass fraction?"); + if (p->rt_data.tchem.mass_fraction_HeIII < -1e4) + message("WARNING: Got negative HeIII mass fraction?"); +#endif + + /* TODO: this should be a for loop with mass fractions being enums. */ + p->rt_data.tchem.mass_fraction_HI = + max(p->rt_data.tchem.mass_fraction_HI, RT_KIARA_TINY_MASS_FRACTION); + p->rt_data.tchem.mass_fraction_HII = + max(p->rt_data.tchem.mass_fraction_HII, RT_KIARA_TINY_MASS_FRACTION); + p->rt_data.tchem.mass_fraction_HeI = + max(p->rt_data.tchem.mass_fraction_HeI, RT_KIARA_TINY_MASS_FRACTION); + p->rt_data.tchem.mass_fraction_HeII = + max(p->rt_data.tchem.mass_fraction_HeII, RT_KIARA_TINY_MASS_FRACTION); + p->rt_data.tchem.mass_fraction_HeIII = + max(p->rt_data.tchem.mass_fraction_HeIII, RT_KIARA_TINY_MASS_FRACTION); + + const float XHI = p->rt_data.tchem.mass_fraction_HI; + const float XHII = p->rt_data.tchem.mass_fraction_HII; + const float XHeI = p->rt_data.tchem.mass_fraction_HeI; + const float XHeII = p->rt_data.tchem.mass_fraction_HeII; + const float XHeIII = p->rt_data.tchem.mass_fraction_HeIII; + + const float Xtot = XHI + XHII + XHeI + XHeII + XHeIII; + + /* Make sure we sum up to 1. TODO: Assuming we have no metals. */ + if (fabsf(Xtot - 1.f) > 1e-3) + error("Got total mass fraction of gas = %.6g", Xtot); +} + +#endif /* SWIFT_RT_UNPHYSICAL_KIARA_H */ diff --git a/src/rt_additions.h b/src/rt_additions.h index cb7595c238..252069f334 100644 --- a/src/rt_additions.h +++ b/src/rt_additions.h @@ -35,6 +35,8 @@ #include "./rt/debug/rt_additions.h" #elif defined(RT_GEAR) #include "./rt/GEAR/rt_additions.h" +#elif defined(RT_KIARA) +#include "./rt/KIARA/rt_additions.h" #elif defined(RT_SPHM1RT) #include "./rt/SPHM1RT/rt_additions.h" #else diff --git a/src/rt_io.h b/src/rt_io.h index 655c13f3bd..a50b31116d 100644 --- a/src/rt_io.h +++ b/src/rt_io.h @@ -34,6 +34,8 @@ #include "./rt/debug/rt_io.h" #elif defined(RT_GEAR) #include "./rt/GEAR/rt_io.h" +#elif defined(RT_KIARA) +#include "./rt/KIARA/rt_io.h" #elif defined(RT_SPHM1RT) #include "./rt/SPHM1RT/rt_io.h" #else diff --git a/src/rt_parameters.c b/src/rt_parameters.c index a70ef113b4..76536f6a26 100644 --- a/src/rt_parameters.c +++ b/src/rt_parameters.c @@ -31,7 +31,7 @@ * The fake initialisation below forces the compiler to keep the * instance and pass it to the linker stage. */ -#if defined(RT_GEAR) +#if defined(RT_GEAR) || defined(RT_KIARA) struct rt_parameters rt_params = {.reduced_speed_of_light = 1.f, .reduced_speed_of_light_inverse = 1.f}; #else diff --git a/src/rt_parameters.h b/src/rt_parameters.h index fa3821005f..4107f5aedd 100644 --- a/src/rt_parameters.h +++ b/src/rt_parameters.h @@ -34,6 +34,8 @@ #include "./rt/debug/rt_parameters.h" #elif defined(RT_GEAR) #include "./rt/GEAR/rt_parameters.h" +#elif defined(RT_KIARA) +#include "./rt/KIARA/rt_parameters.h" #elif defined(RT_SPHM1RT) #include "./rt/SPHM1RT/rt_parameters.h" #elif defined(RT_NONE) diff --git a/src/rt_properties.h b/src/rt_properties.h index e0454afe79..dd6497c32d 100644 --- a/src/rt_properties.h +++ b/src/rt_properties.h @@ -36,6 +36,8 @@ #include "./rt/SPHM1RT/rt_properties.h" #elif defined(RT_GEAR) #include "./rt/GEAR/rt_properties.h" +#elif defined(RT_KIARA) +#include "./rt/KIARA/rt_properties.h" #else #error "Invalid choice of radiation scheme" #endif diff --git a/src/rt_struct.h b/src/rt_struct.h index 78986ef1af..90fd3cfc81 100644 --- a/src/rt_struct.h +++ b/src/rt_struct.h @@ -36,6 +36,8 @@ #include "./rt/debug/rt_struct.h" #elif defined(RT_GEAR) #include "./rt/GEAR/rt_struct.h" +#elif defined(RT_KIARA) +#include "./rt/KIARA/rt_struct.h" #elif defined(RT_SPHM1RT) #include "./rt/SPHM1RT/rt_struct.h" #else diff --git a/src/runner_others.c b/src/runner_others.c index c822ecc4af..8862643166 100644 --- a/src/runner_others.c +++ b/src/runner_others.c @@ -1141,9 +1141,12 @@ void runner_do_rt_tchem(struct runner *r, struct cell *c, int timer) { const int with_cosmology = (e->policy & engine_policy_cosmology); struct rt_props *rt_props = e->rt_props; const struct hydro_props *hydro_props = e->hydro_properties; + const struct entropy_floor_properties *entropy_floor_props = e->entropy_floor; const struct cosmology *cosmo = e->cosmology; const struct phys_const *phys_const = e->physical_constants; const struct unit_system *us = e->internal_units; + const struct cooling_function_data *cooling = e->cooling_func; + const double time = e->time; /* Anything to do here? */ if (count == 0) return; @@ -1183,6 +1186,8 @@ void runner_do_rt_tchem(struct runner *r, struct cell *c, int timer) { const double dt = rt_part_dt(ti_begin, ti_end, e->time_base, with_cosmology, cosmo); + const double dt_therm = + rt_part_dt_therm(ti_begin, ti_end, e->time_base, with_cosmology, cosmo); #ifdef SWIFT_DEBUG_CHECKS if (ti_begin != ti_current_subcycle) error( @@ -1197,7 +1202,8 @@ void runner_do_rt_tchem(struct runner *r, struct cell *c, int timer) { rt_finalise_transport(p, rt_props, dt, cosmo); /* And finally do thermochemistry */ - rt_tchem(p, xp, rt_props, cosmo, hydro_props, phys_const, us, dt); + rt_tchem(p, xp, rt_props, cosmo, hydro_props, entropy_floor_props, + phys_const, cooling, us, dt, dt_therm, time); } } diff --git a/src/space.c b/src/space.c index 46845e2b6e..ce6a7280f5 100644 --- a/src/space.c +++ b/src/space.c @@ -853,15 +853,18 @@ void space_convert_rt_quantities_mapper(void *restrict map_data, int count, const struct phys_const *restrict phys_const = e->physical_constants; const struct unit_system *restrict iu = e->internal_units; const struct cosmology *restrict cosmo = e->cosmology; + const struct cooling_function_data *cool_func = e->cooling_func; struct part *restrict parts = (struct part *)map_data; + const ptrdiff_t delta = parts - s->parts; + struct xpart *restrict xp = s->xparts + delta; /* Loop over all the particles ignoring the extra buffer ones for on-the-fly * creation */ for (int k = 0; k < count; k++) { if (parts[k].time_bin <= num_time_bins) - rt_convert_quantities(&parts[k], rt_props, hydro_props, phys_const, iu, - cosmo); + rt_convert_quantities(&parts[k], &xp[k], rt_props, hydro_props, phys_const, iu, + cool_func, cosmo); } } diff --git a/src/timestep.h b/src/timestep.h index 825e46edfe..43f07f4aac 100644 --- a/src/timestep.h +++ b/src/timestep.h @@ -256,7 +256,7 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_rt_timestep( float new_dt = rt_compute_timestep(p, xp, e->rt_props, e->cosmology, e->hydro_properties, - e->physical_constants, e->internal_units); + e->physical_constants, e->cooling_func, e->internal_units); if ((e->policy & engine_policy_cosmology)) /* Apply the maximal displacement constraint (FLT_MAX if non-cosmological)*/ From f4637aa604c354bdd543328dfeb69295c032f00d Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Wed, 26 Nov 2025 13:12:00 +0000 Subject: [PATCH 14/37] Make previous commit compatible when RT is off --- src/rt/KIARA/rt.h | 3 ++- src/rt/KIARA/rt_io.h | 1 + src/rt/none/rt.h | 61 +++++++++++++++++++++++++++++++++++--------- src/rt/none/rt_io.h | 5 ++-- 4 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/rt/KIARA/rt.h b/src/rt/KIARA/rt.h index 70da525f00..5bdb517406 100644 --- a/src/rt/KIARA/rt.h +++ b/src/rt/KIARA/rt.h @@ -347,7 +347,8 @@ __attribute__((always_inline)) INLINE static void rt_convert_quantities( * of a given particle (during timestep tasks) * * @param p Particle to work on. - * @param rt_props RT properties struct + * @param xp Particle extra data. + * @param rt_props RT properties struct. * @param cosmo The current cosmological model. * @param hydro_props The #hydro_props. * @param phys_const The physical constants in internal units. diff --git a/src/rt/KIARA/rt_io.h b/src/rt/KIARA/rt_io.h index bcbf530a8c..f08872f1aa 100644 --- a/src/rt/KIARA/rt_io.h +++ b/src/rt/KIARA/rt_io.h @@ -153,6 +153,7 @@ INLINE static void rt_convert_mass_fractions(const struct engine* engine, * transfer data of hydro particles. * * @param parts The particle array. + * @param xparts The particle extra information. * @param list The list of i/o properties to write. * * @return Returns the number of fields to write. diff --git a/src/rt/none/rt.h b/src/rt/none/rt.h index f014b1088b..c40d696499 100644 --- a/src/rt/none/rt.h +++ b/src/rt/none/rt.h @@ -157,10 +157,12 @@ __attribute__((always_inline)) INLINE static void rt_spart_has_no_neighbours( * @param cosmo cosmology struct */ __attribute__((always_inline)) INLINE static void rt_convert_quantities( - struct part *restrict p, const struct rt_props *rt_props, + struct part *restrict p, struct xpart *restrict xp, + const struct rt_props *rt_props, const struct hydro_props *hydro_props, const struct phys_const *restrict phys_const, const struct unit_system *restrict us, + const struct cooling_function_data *cool_func, const struct cosmology *restrict cosmo) {} /** @@ -168,7 +170,8 @@ __attribute__((always_inline)) INLINE static void rt_convert_quantities( * of a given particle (during timestep tasks) * * @param p Particle to work on. - * @param rt_props RT properties struct + * @param xp Particle extra data. + * @param rt_props RT properties struct. * @param cosmo The current cosmological model. * @param hydro_props The #hydro_props. * @param phys_const The physical constants in internal units. @@ -176,11 +179,12 @@ __attribute__((always_inline)) INLINE static void rt_convert_quantities( * @param dt The time-step of this particle. */ __attribute__((always_inline)) INLINE static float rt_compute_timestep( - const struct part *restrict p, const struct xpart *restrict xp, - struct rt_props *rt_props, const struct cosmology *restrict cosmo, - const struct hydro_props *hydro_props, - const struct phys_const *restrict phys_const, - const struct unit_system *restrict us) { + const struct part* restrict p, const struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct phys_const* restrict phys_const, + const struct cooling_function_data* restrict cooling, + const struct unit_system* restrict us) { return FLT_MAX; } @@ -250,24 +254,57 @@ __attribute__((always_inline)) INLINE static void rt_end_gradient( __attribute__((always_inline)) INLINE static void rt_finalise_transport( struct part *restrict p, struct rt_props *rtp, const double dt, const struct cosmology *restrict cosmo) {} + +/** + * @brief Compute the time-step length for an RT step of a particle from given + * integer times ti_beg and ti_end. This time-step length is then used to + * compute the actual time integration of the transport/force step and the + * thermochemistry. This is not used to determine the time-step length during + * the time-step tasks. + * + * @param ti_beg Start of the time-step (on the integer time-line). + * @param ti_end End of the time-step (on the integer time-line). + * @param time_base Minimal time-step size on the time-line. + * @param with_cosmology Are we running with cosmology integration? + * @param cosmo The #cosmology object. + * + * @return The time-step size for the rt integration. (internal units). + */ +__attribute__((always_inline)) INLINE static double rt_part_dt_therm( + const integertime_t ti_beg, const integertime_t ti_end, + const double time_base, const int with_cosmology, + const struct cosmology* cosmo) { + + return FLT_MAX; +} + /** * @brief Do the thermochemistry on a particle. * + * This function wraps around rt_do_thermochemistry function. + * * @param p Particle to work on. * @param xp Pointer to the particle' extended data. * @param rt_props RT properties struct * @param cosmo The current cosmological model. * @param hydro_props The #hydro_props. + * @param floor_props Properties of the entropy floor. * @param phys_const The physical constants in internal units. * @param us The internal system of units. * @param dt The time-step of this particle. + * @param dt_therm The time-step operator used for thermal quantities. + * @param time The current time (since the Big Bang or start of the run) in + * internal units. */ __attribute__((always_inline)) INLINE static void rt_tchem( - struct part *restrict p, struct xpart *restrict xp, - struct rt_props *rt_props, const struct cosmology *restrict cosmo, - const struct hydro_props *hydro_props, - const struct phys_const *restrict phys_const, - const struct unit_system *restrict us, const double dt) {} + struct part* restrict p, struct xpart* restrict xp, + struct rt_props* rt_props, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, + const struct phys_const* restrict phys_const, + const struct cooling_function_data* restrict cooling, + const struct unit_system* restrict us, const double dt, + const double dt_therm, const double time) {} /** * @brief Extra operations done during the kick. diff --git a/src/rt/none/rt_io.h b/src/rt/none/rt_io.h index d323363a11..a18e301f8d 100644 --- a/src/rt/none/rt_io.h +++ b/src/rt/none/rt_io.h @@ -57,12 +57,13 @@ INLINE static int rt_read_stars(const struct spart *sparts, * transfer data of hydro particles. * * @param parts The particle array. + * @param xparts The particle extra information. * @param list The list of i/o properties to write. * * @return Returns the number of fields to write. */ -INLINE static int rt_write_particles(const struct part *parts, - struct io_props *list) { +INLINE static int rt_write_particles(const struct part* parts, + const struct xpart* xparts, struct io_props* list) { return 0; } From 30f5869392e45a0898a7cc75b015cd2579c5f5f6 Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Thu, 27 Nov 2025 08:15:54 +0800 Subject: [PATCH 15/37] Remove files that should have been committed --- src/feedback/KIARA/libswiftsim_la-feedback-335341eb.o.tmp | 0 src/feedback/KIARA/libswiftsim_la-feedback-f98e8954.o.tmp | 0 src/feedback/KIARA/mpi-feedback-8dfc2d63.o.tmp | 0 tests/testSymmetry.c | 6 +++--- 4 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 src/feedback/KIARA/libswiftsim_la-feedback-335341eb.o.tmp delete mode 100644 src/feedback/KIARA/libswiftsim_la-feedback-f98e8954.o.tmp delete mode 100644 src/feedback/KIARA/mpi-feedback-8dfc2d63.o.tmp diff --git a/src/feedback/KIARA/libswiftsim_la-feedback-335341eb.o.tmp b/src/feedback/KIARA/libswiftsim_la-feedback-335341eb.o.tmp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/feedback/KIARA/libswiftsim_la-feedback-f98e8954.o.tmp b/src/feedback/KIARA/libswiftsim_la-feedback-f98e8954.o.tmp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/feedback/KIARA/mpi-feedback-8dfc2d63.o.tmp b/src/feedback/KIARA/mpi-feedback-8dfc2d63.o.tmp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c index 57d0c06e85..a023d6be80 100644 --- a/tests/testSymmetry.c +++ b/tests/testSymmetry.c @@ -222,7 +222,7 @@ void test(void) { runner_iact_force(r2, dx, pi.h, pj.h, &pi, &pj, a, H); runner_iact_mhd_force(r2, dx, pi.h, pj.h, &pi, &pj, mu_0, a, H); runner_iact_diffusion(r2, dx, pi.h, pj.h, &pi, &pj, a, H, time_base, - ti_current, /*cosmo=*/NULL, /*with_cosmology=*/0, + ti_current, /*cosmo=*/NULL, /*phys_const=*/NULL, /*with_cosmology=*/0, /*chem_data=*/NULL); runner_iact_timebin(r2, dx, pi.h, pj.h, &pi, &pj, a, H); @@ -230,7 +230,7 @@ void test(void) { runner_iact_nonsym_force(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); runner_iact_nonsym_mhd_force(r2, dx, pi2.h, pj2.h, &pi2, &pj2, mu_0, a, H); runner_iact_nonsym_diffusion(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H, - time_base, ti_current, /*cosmo=*/NULL, + time_base, ti_current, /*cosmo=*/NULL, /*phys_const=*/NULL, /*with_cosmology=*/0, /*chem_data=*/NULL); runner_iact_nonsym_timebin(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); dx[0] = -dx[0]; @@ -239,7 +239,7 @@ void test(void) { runner_iact_nonsym_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); runner_iact_nonsym_mhd_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2, mu_0, a, H); runner_iact_nonsym_diffusion(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H, - time_base, ti_current, /*cosmo=*/NULL, + time_base, ti_current, /*cosmo=*/NULL, /*phys_const=*/NULL, /*with_cosmology=*/0, /*chem_data=*/NULL); runner_iact_nonsym_timebin(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); From e5dabe62e3c6fac2915a942403d3848e8bf87de7 Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Thu, 27 Nov 2025 11:51:37 +0800 Subject: [PATCH 16/37] Fix compilation problems when configuring with subgrid=KIARA --- src/black_holes/Obsidian/black_holes_iact.h | 1 + src/stars/KIARA/stars.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/black_holes/Obsidian/black_holes_iact.h b/src/black_holes/Obsidian/black_holes_iact.h index 29f40ea374..2e3acb53d4 100644 --- a/src/black_holes/Obsidian/black_holes_iact.h +++ b/src/black_holes/Obsidian/black_holes_iact.h @@ -916,6 +916,7 @@ runner_iact_nonsym_bh_gas_feedback( dt = cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); } else { error("Kiara BH model can only be run with cosmology."); + dt = 0.; } /* Save gas density and entropy before feedback */ diff --git a/src/stars/KIARA/stars.h b/src/stars/KIARA/stars.h index 3fa6ed4ed4..13592202d1 100644 --- a/src/stars/KIARA/stars.h +++ b/src/stars/KIARA/stars.h @@ -20,6 +20,7 @@ #define SWIFT_SIMBA_STARS_H #include "exp10.h" +#include "feedback_properties.h" #include From aa004612e8a891c8683e2a8e94d7063b7dc61361 Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Thu, 27 Nov 2025 12:23:13 +0800 Subject: [PATCH 17/37] Add the proper includes in the unit tests --- tests/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index 7b10d52041..06e6dfe654 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -15,7 +15,7 @@ # along with this program. If not, see . # Add the source directory and the non-standard paths to the included library headers to CFLAGS -AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(CHEALPIX_CFLAGS) +AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(CHEALPIX_CFLAGS) $(GRACKLE_INCS) AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS) $(CHEALPIX_LIBS) From 86bc2ec4bd8aff41d47dc6aa94a7cf09c4733979 Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Thu, 27 Nov 2025 12:27:05 +0800 Subject: [PATCH 18/37] Make the symmetry and MPI-rules test work --- tests/testHydroMPIrules.c | 13 +++++++++++++ tests/testSymmetry.c | 17 ++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/tests/testHydroMPIrules.c b/tests/testHydroMPIrules.c index 799d86adee..1bf691ce63 100644 --- a/tests/testHydroMPIrules.c +++ b/tests/testHydroMPIrules.c @@ -20,6 +20,7 @@ /* Local includes. */ #include "swift.h" +#include "timestep_limiter_iact.h" /* System includes. */ #include @@ -42,6 +43,11 @@ void test(void) { const float a = (float)random_uniform(0.8, 1.); const float H = 1.f; const float mu_0 = 4. * M_PI; + const integertime_t ti_current = 1; + const double time_base = 1e-5; + const int with_cosmology = floor(random_uniform(0., 2.)); + struct cosmology cosmo; + cosmology_init_no_cosmo(&cosmo); /* Create two random particles (don't do this at home !) */ struct part pi, pj; @@ -89,6 +95,7 @@ void test(void) { runner_iact_nonsym_chemistry(r2, dx, pi.h, pj.h, &pi, &pj, a, H); runner_iact_nonsym_pressure_floor(r2, dx, pi.h, pj.h, &pi, &pj, a, H); runner_iact_nonsym_star_formation(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + runner_iact_nonsym_sink(r2, dx, pi.h, pj.h, &pi, &pj, a, H); /* Check whether pj has been modified */ j_not_ok = memcmp(&pj, &pj2, sizeof(struct part)); @@ -106,6 +113,7 @@ void test(void) { runner_iact_nonsym_gradient(r2, dx, pi.h, pj.h, &pi, &pj, a, H); runner_iact_nonsym_mhd_gradient(r2, dx, pi.h, pj.h, &pi, &pj, mu_0, a, H); + runner_iact_nonsym_gradient_diffusion(r2, dx, pi.h, pj.h, &pi, &pj, a, H); /* Check whether pj has been modified */ j_not_ok = memcmp((char *)&pj, (char *)&pj2, sizeof(struct part)); @@ -122,6 +130,11 @@ void test(void) { /* --- Test the force loop --- */ runner_iact_nonsym_force(r2, dx, pi.h, pj.h, &pi, &pj, a, H); runner_iact_nonsym_mhd_force(r2, dx, pi.h, pj.h, &pi, &pj, mu_0, a, H); + runner_iact_nonsym_timebin(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + runner_iact_nonsym_rt_timebin(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + runner_iact_nonsym_diffusion(r2, dx, pi.h, pj.h, &pi, &pj, a, H, time_base, + ti_current, &cosmo, with_cosmology, + /*chem_data=*/NULL); /* Check that the particles are the same */ j_not_ok = memcmp((char *)&pj, (char *)&pj2, sizeof(struct part)); diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c index a023d6be80..7582a10064 100644 --- a/tests/testSymmetry.c +++ b/tests/testSymmetry.c @@ -45,6 +45,8 @@ void test(void) { const float mu_0 = 4. * M_PI; const integertime_t ti_current = 1; const double time_base = 1e-5; + struct chemistry_global_data cd; + bzero(&cd, sizeof(struct chemistry_global_data)); /* Create two random particles (don't do this at home !) */ struct part pi, pj; @@ -221,17 +223,18 @@ void test(void) { /* Call the symmetric version */ runner_iact_force(r2, dx, pi.h, pj.h, &pi, &pj, a, H); runner_iact_mhd_force(r2, dx, pi.h, pj.h, &pi, &pj, mu_0, a, H); - runner_iact_diffusion(r2, dx, pi.h, pj.h, &pi, &pj, a, H, time_base, - ti_current, /*cosmo=*/NULL, /*phys_const=*/NULL, /*with_cosmology=*/0, - /*chem_data=*/NULL); + runner_iact_diffusion(r2, dx, pi.h, pj.h, &pi, &pj, &xpi, &xpj, a, H, + time_base, ti_current, /*cosmo=*/NULL, + /*with_cosmology=*/0, /*phys_const=*/NULL, + /*chem_data=*/&cd); runner_iact_timebin(r2, dx, pi.h, pj.h, &pi, &pj, a, H); /* Call the non-symmetric version */ runner_iact_nonsym_force(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); runner_iact_nonsym_mhd_force(r2, dx, pi2.h, pj2.h, &pi2, &pj2, mu_0, a, H); runner_iact_nonsym_diffusion(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H, - time_base, ti_current, /*cosmo=*/NULL, /*phys_const=*/NULL, - /*with_cosmology=*/0, /*chem_data=*/NULL); + time_base, ti_current, /*cosmo=*/NULL, + /*with_cosmology=*/0, /*chem_data=*/&cd); runner_iact_nonsym_timebin(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); dx[0] = -dx[0]; dx[1] = -dx[1]; @@ -239,8 +242,8 @@ void test(void) { runner_iact_nonsym_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); runner_iact_nonsym_mhd_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2, mu_0, a, H); runner_iact_nonsym_diffusion(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H, - time_base, ti_current, /*cosmo=*/NULL, /*phys_const=*/NULL, - /*with_cosmology=*/0, /*chem_data=*/NULL); + time_base, ti_current, /*cosmo=*/NULL, + /*with_cosmology=*/0, /*chem_data=*/&cd); runner_iact_nonsym_timebin(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); /* Check that the particles are the same */ From 8e9c9a2090e633ca3892f320a450390cfe1dbab2 Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Thu, 27 Nov 2025 15:22:15 +0800 Subject: [PATCH 19/37] Fix a bug in the MAGMA implementation where the du/dt_cond term was not computed the same way in the symmetrci and non-symmetric loops --- src/hydro/MAGMA2/hydro_iact.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index ce248c3ee8..bcc4e47128 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -665,6 +665,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_rad_ij[1] = dx[1]; G_rad_ij[2] = dx[2]; } + + G_rad_ij_norm = hydro_vec3_norm(G_rad_ij); } /* MAGMA2-style gradients (Matrix-Inversion-2 SPH) */ From bb617ca3ca0c15e0526d9ceebe7a4b4f9a60170a Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Thu, 27 Nov 2025 15:24:48 +0800 Subject: [PATCH 20/37] Expand the symmetry test to properly cover the KIARA chemistry model --- tests/testSymmetry.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c index 7582a10064..7b30472ed7 100644 --- a/tests/testSymmetry.c +++ b/tests/testSymmetry.c @@ -45,8 +45,14 @@ void test(void) { const float mu_0 = 4. * M_PI; const integertime_t ti_current = 1; const double time_base = 1e-5; + struct cosmology cosmo; + cosmology_init_no_cosmo(&cosmo); struct chemistry_global_data cd; bzero(&cd, sizeof(struct chemistry_global_data)); +#ifdef CHEMISTRY_KIARA + cd.use_firehose_wind_model = 1; + cd.firehose_max_velocity = 1000000.; +#endif /* CHEMISTRY_KIARA */ /* Create two random particles (don't do this at home !) */ struct part pi, pj; @@ -64,6 +70,10 @@ void test(void) { pj.id = 2ll; pi.time_bin = 1; pj.time_bin = 1; + pi.decoupled = floor(random_uniform(0, 2.)); + pj.decoupled = floor(random_uniform(0, 2.)); + + message("%d %d", pi.decoupled, pj.decoupled); #if defined(GIZMO_MFV_SPH) /* Give the primitive variables sensible values, since the Riemann solver does @@ -116,6 +126,11 @@ void test(void) { bzero(&xpi, sizeof(struct xpart)); bzero(&xpj, sizeof(struct xpart)); + for (size_t i = 0; i < sizeof(struct xpart) / sizeof(float); ++i) { + *(((float *)&xpi) + i) = (float)random_uniform(0., 2.); + *(((float *)&xpj) + i) = (float)random_uniform(0., 2.); + } + /* Make some copies */ struct part pi2, pj2; memcpy(&pi2, &pi, sizeof(struct part)); @@ -224,8 +239,8 @@ void test(void) { runner_iact_force(r2, dx, pi.h, pj.h, &pi, &pj, a, H); runner_iact_mhd_force(r2, dx, pi.h, pj.h, &pi, &pj, mu_0, a, H); runner_iact_diffusion(r2, dx, pi.h, pj.h, &pi, &pj, &xpi, &xpj, a, H, - time_base, ti_current, /*cosmo=*/NULL, - /*with_cosmology=*/0, /*phys_const=*/NULL, + time_base, ti_current, &cosmo, + /*with_cosmology=*/1, /*phys_const=*/NULL, /*chem_data=*/&cd); runner_iact_timebin(r2, dx, pi.h, pj.h, &pi, &pj, a, H); @@ -233,8 +248,8 @@ void test(void) { runner_iact_nonsym_force(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); runner_iact_nonsym_mhd_force(r2, dx, pi2.h, pj2.h, &pi2, &pj2, mu_0, a, H); runner_iact_nonsym_diffusion(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H, - time_base, ti_current, /*cosmo=*/NULL, - /*with_cosmology=*/0, /*chem_data=*/&cd); + time_base, ti_current, &cosmo, + /*with_cosmology=*/1, /*chem_data=*/&cd); runner_iact_nonsym_timebin(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); dx[0] = -dx[0]; dx[1] = -dx[1]; @@ -242,8 +257,8 @@ void test(void) { runner_iact_nonsym_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); runner_iact_nonsym_mhd_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2, mu_0, a, H); runner_iact_nonsym_diffusion(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H, - time_base, ti_current, /*cosmo=*/NULL, - /*with_cosmology=*/0, /*chem_data=*/&cd); + time_base, ti_current, &cosmo, + /*with_cosmology=*/1, /*chem_data=*/&cd); runner_iact_nonsym_timebin(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); /* Check that the particles are the same */ From c78aa5f22a1d4dc3f88b68ac912e390f56f21320 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Thu, 27 Nov 2025 10:30:38 +0000 Subject: [PATCH 21/37] Add option for capping BH mass in Bondi rate; remove FIRE_eta_lower_slope_EOR --- src/black_holes/Obsidian/black_holes.h | 8 ++++++-- .../Obsidian/black_holes_properties.h | 20 +++++++++++++------ src/feedback/KIARA/feedback.c | 5 ----- src/feedback/KIARA/feedback.h | 2 +- src/feedback/KIARA/feedback_properties.h | 3 --- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/black_holes/Obsidian/black_holes.h b/src/black_holes/Obsidian/black_holes.h index 403e8f168f..a3db3b5cb6 100644 --- a/src/black_holes/Obsidian/black_holes.h +++ b/src/black_holes/Obsidian/black_holes.h @@ -1027,7 +1027,11 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( if (gas_c > 0.) { const double gas_c_phys_inv = 1. / (cosmo->a_factor_sound_speed * gas_c); - Bondi_rate = 4. * M_PI * G * G * BH_mass * BH_mass * gas_rho_phys * + float BH_mass_bondi = BH_mass; + if (BH_mass_bondi > props->bondi_BH_mass_cap) + BH_mass_bondi = props->bondi_BH_mass_cap; + + Bondi_rate = 4. * M_PI * G * G * BH_mass_bondi * BH_mass_bondi * gas_rho_phys * gas_c_phys_inv * gas_c_phys_inv * gas_c_phys_inv; /* In the case of standard Bondi, we limit it to the Eddington rate */ @@ -1231,7 +1235,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( const float eta_break = props->FIRE_eta_break; const float eta_lower_slope = props->FIRE_eta_lower_slope; const float eta_upper_slope = props->FIRE_eta_upper_slope; - const float eta_lower_slope_EOR = props->FIRE_eta_lower_slope_EOR; + const float eta_lower_slope_EOR = props->FIRE_eta_lower_slope; const float eta_minmass = props->minimum_galaxy_stellar_mass; const float eta_suppress = props->wind_eta_suppression_redshift; diff --git a/src/black_holes/Obsidian/black_holes_properties.h b/src/black_holes/Obsidian/black_holes_properties.h index f336f1aa9b..940c635bc6 100644 --- a/src/black_holes/Obsidian/black_holes_properties.h +++ b/src/black_holes/Obsidian/black_holes_properties.h @@ -106,6 +106,9 @@ struct black_holes_props { */ float f_Edd_Bondi_maximum; + /*! Maximum BH mass to use in calculating Bondi rate */ + float bondi_BH_mass_cap; + /*! Minimum gas particle mass in nibbling mode */ float min_gas_mass_for_nibbling; @@ -449,9 +452,6 @@ struct black_holes_props { /*! The power-law slope of eta above FIRE_eta_break */ float FIRE_eta_upper_slope; - /*! The power-law slope of eta below FIRE_eta_break at z>6 */ - float FIRE_eta_lower_slope_EOR; - /*! The minimum galaxy stellar mass in internal units */ float minimum_galaxy_stellar_mass; @@ -610,8 +610,6 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, parser_get_param_float(params, "KIARAFeedback:FIRE_eta_lower_slope"); bp->FIRE_eta_upper_slope = parser_get_param_float(params, "KIARAFeedback:FIRE_eta_upper_slope"); - bp->FIRE_eta_lower_slope_EOR = parser_get_param_float( - params, "KIARAFeedback:FIRE_eta_lower_slope_EOR"); bp->minimum_galaxy_stellar_mass = parser_get_param_float( params, "KIARAFeedback:minimum_galaxy_stellar_mass_Msun"); bp->minimum_galaxy_stellar_mass /= bp->mass_to_solar_mass; @@ -625,6 +623,10 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->f_Edd_Bondi_maximum = parser_get_opt_param_float( params, "ObsidianAGN:max_bondi_eddington_fraction", 1.f); + bp->bondi_BH_mass_cap = parser_get_opt_param_float( + params, "ObsidianAGN:bondi_BH_mass_cap_Msun", FLT_MAX); + bp->bondi_BH_mass_cap /= bp->mass_to_solar_mass; + bp->fixed_T_above_EoS_factor = exp10( parser_get_param_float(params, "ObsidianAGN:fixed_T_above_EoS_dex")); @@ -1110,7 +1112,13 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, message("Black holes relative tolerance in h: %.5f (+/- %.4f neighbours).", bp->h_tolerance, bp->delta_neighbours); - message("Black hole model is Rennehan+24"); + message("Black hole model is Obsidian (Rennehan+24, with modifications for KIARA)"); + message("Black hole torque accretion efficiency is %g", + bp->torque_accretion_norm); + message("Black hole Bondi accretion alpha is %g", + bp->bondi_alpha); + message("Black hole Bondi accretion BH mass cap is %g Msun", + bp->bondi_BH_mass_cap * bp->mass_to_solar_mass); message("Black hole jet velocity is %g km/s", bp->jet_velocity / bp->kms_to_internal); if (bp->jet_loading_type == BH_jet_momentum_loaded) { diff --git a/src/feedback/KIARA/feedback.c b/src/feedback/KIARA/feedback.c index 71fea7380a..c5701d133b 100644 --- a/src/feedback/KIARA/feedback.c +++ b/src/feedback/KIARA/feedback.c @@ -1705,9 +1705,6 @@ void feedback_props_init(struct feedback_props *fp, parser_get_param_double(params, "KIARAFeedback:FIRE_eta_lower_slope"); fp->FIRE_eta_upper_slope = parser_get_param_double(params, "KIARAFeedback:FIRE_eta_upper_slope"); - fp->FIRE_eta_lower_slope_EOR = parser_get_opt_param_double( - params, "KIARAFeedback:FIRE_eta_lower_slope_EOR", - fp->FIRE_eta_lower_slope); fp->wind_velocity_suppression_redshift = parser_get_opt_param_float( params, "KIARAFeedback:wind_velocity_suppression_redshift", 0.f); @@ -1829,8 +1826,6 @@ void feedback_props_init(struct feedback_props *fp, message("Feedback FIRE eta break: %g", fp->FIRE_eta_break); message("Feedback FIRE eta upper slope: %g", fp->FIRE_eta_upper_slope); message("Feedback FIRE eta lower slope: %g", fp->FIRE_eta_lower_slope); - message("Feedback FIRE eta lower slope at z>6: %g", - fp->FIRE_eta_lower_slope_EOR); if (fabs(fp->wind_velocity_suppression_redshift) != 0.f) { message( diff --git a/src/feedback/KIARA/feedback.h b/src/feedback/KIARA/feedback.h index acd05ee447..f3c809d3d1 100644 --- a/src/feedback/KIARA/feedback.h +++ b/src/feedback/KIARA/feedback.h @@ -527,7 +527,7 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_feedback( const float FIRE_eta_lower_slope = feedback_props->FIRE_eta_lower_slope; const float FIRE_eta_upper_slope = feedback_props->FIRE_eta_upper_slope; const float FIRE_eta_lower_slope_EOR = - feedback_props->FIRE_eta_lower_slope_EOR; + feedback_props->FIRE_eta_lower_slope; const float wind_velocity_suppression_redshift = feedback_props->wind_velocity_suppression_redshift; diff --git a/src/feedback/KIARA/feedback_properties.h b/src/feedback/KIARA/feedback_properties.h index fa13be5407..46f6ef96a7 100644 --- a/src/feedback/KIARA/feedback_properties.h +++ b/src/feedback/KIARA/feedback_properties.h @@ -216,9 +216,6 @@ struct feedback_props { /*! The power-law slope of eta above FIRE_eta_break */ float FIRE_eta_upper_slope; - /*! The power-law slope of eta below FIRE_eta_break at z>6 */ - float FIRE_eta_lower_slope_EOR; - /*! The wind speed of stellar feedback suppressed above this z */ float wind_velocity_suppression_redshift; From 94d52f705eeb014d40edfeaeb832e9834f6190cc Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Mon, 1 Dec 2025 03:56:35 +0000 Subject: [PATCH 22/37] Do not place the recoupled particle back onto the timeline. They are already there --- src/feedback/KIARA/feedback.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/feedback/KIARA/feedback.h b/src/feedback/KIARA/feedback.h index f3c809d3d1..038e93fe7d 100644 --- a/src/feedback/KIARA/feedback.h +++ b/src/feedback/KIARA/feedback.h @@ -72,9 +72,6 @@ __attribute__((always_inline)) INLINE static void feedback_recouple_set_flags( p->cooling_data.subgrid_temp = 0.f; p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); p->cooling_data.subgrid_fcold = 0.f; - - /* Make sure to sync the newly coupled part on the timeline */ - timestep_sync_part(p); } /** From 09da22988e57a5f361ce4ab2ee2c2229003f4c93 Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Mon, 1 Dec 2025 03:57:37 +0000 Subject: [PATCH 23/37] Follow the convention of the rest of the code regarding i/o names so that our base examples can be run with Kiara/Obsidian --- src/black_holes/Obsidian/black_holes_io.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/black_holes/Obsidian/black_holes_io.h b/src/black_holes/Obsidian/black_holes_io.h index 7368aea97a..73e7aed459 100644 --- a/src/black_holes/Obsidian/black_holes_io.h +++ b/src/black_holes/Obsidian/black_holes_io.h @@ -56,7 +56,7 @@ INLINE static void black_holes_read_particles(struct bpart *bparts, UNIT_CONV_NO_UNITS, bparts, id); num++; - list[num] = io_make_input_field("SmoothingLengths", FLOAT, 1, OPTIONAL, + list[num] = io_make_input_field("SmoothingLength", FLOAT, 1, OPTIONAL, UNIT_CONV_LENGTH, bparts, h); num++; From af339d1b03542f45fa86e7101661767a63d6976b Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Mon, 1 Dec 2025 03:59:22 +0000 Subject: [PATCH 24/37] Indicate whether a particle is decoupled or not when calling magma's hydro_debug --- src/hydro/MAGMA2/hydro_debug.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hydro/MAGMA2/hydro_debug.h b/src/hydro/MAGMA2/hydro_debug.h index d540d30c2e..ec04ba95c3 100644 --- a/src/hydro/MAGMA2/hydro_debug.h +++ b/src/hydro/MAGMA2/hydro_debug.h @@ -31,6 +31,7 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part *p, const struct xpart *xp) { warning("[PID%lld] part:", p->id); + warning("[PID%lld] decoupled: %d", p->id, p->decoupled); warning("[PID%lld] x=[%.3e,%.3e,%.3e]", p->id, p->x[0], p->x[1], p->x[2]); warning("[PID%lld] v=[%.3e,%.3e,%.3e]", p->id, p->v[0], p->v[1], p->v[2]); warning("[PID%lld] a=[%.3e,%.3e,%.3e]", p->id, p->a_hydro[0], p->a_hydro[1], From 9416fa3b5420a910eee26643d002dfa0b62d4e2d Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Mon, 1 Dec 2025 04:00:13 +0000 Subject: [PATCH 25/37] Make git ignore the side tables used for Kiara --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 5213975f72..8738cad4d8 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,8 @@ examples/SubgridTests/CosmologicalStellarEvolution/StellarEvolutionSolution* examples/SmallCosmoVolume/SmallCosmoVolume_DM/power_spectra examples/SmallCosmoVolume/SmallCosmoVolume_cooling/snapshots/ examples/SmallCosmoVolume/SmallCosmoVolume_hydro/snapshots/ +examples/**/chem5 +examples/**/CloudyData_UVB=FG2011_shielded.h5 examples/**/CloudyData_UVB=HM2012.h5 examples/**/CloudyData_UVB=HM2012_shielded.h5 examples/**/CloudyData_UVB=HM2012_high_density.h5 From 7532928e18d030549a9a35434a5741a60f3a4991 Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Mon, 1 Dec 2025 08:09:22 +0000 Subject: [PATCH 26/37] Change CoolingTime to CoolingTimes in the snapshots to match the main code's CamelCase plural convention --- src/cooling/KIARA/cooling_io.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cooling/KIARA/cooling_io.h b/src/cooling/KIARA/cooling_io.h index 7ac094446f..1ba49c3514 100644 --- a/src/cooling/KIARA/cooling_io.h +++ b/src/cooling/KIARA/cooling_io.h @@ -211,7 +211,7 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( num++; list[num] = io_make_output_field( - "CoolingTime", FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, + "CoolingTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, cooling_data.mixing_layer_cool_time, "Cooling time for particle; if it's currently a firehose wind" "particle (delay_time>0), this is the mixing layer cooling time"); From 5affc77b6fc832b6c7f96a09ebab4b772bd3c565 Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Wed, 3 Dec 2025 11:07:18 +0800 Subject: [PATCH 27/37] Only do the expensive math operations in the ghost loop in prepare_gradient() rather than in end_density() --- src/hydro/MAGMA2/hydro.h | 52 ++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 6b77ea5b7a..e48978ee56 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -1425,7 +1425,35 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( const float a_inv2 = cosmo->a2_inv; p->density.div_v *= h_inv_dim_plus_one * rho_inv * a_inv2; p->density.div_v += cosmo->H * hydro_dimension; +} +/** + * @brief Prepare a particle for the gradient calculation. + * + * This function is called after the density loop and before the gradient loop. + * + * We use it to set the physical timestep for the particle and to copy the + * actual velocities, which we need to boost our interfaces during the flux + * calculation. We also initialize the variables used for the time step + * calculation. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + * @param hydro_props Hydrodynamic properties. + * @param pressure_floor The properties of the pressure floor. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct pressure_floor_props *pressure_floor) { + + /* Some smoothing length multiples. */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + /* Need this for correct dh/dt */ p->gradients.wcount = p->density.wcount; @@ -1559,29 +1587,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( } } } -} - -/** - * @brief Prepare a particle for the gradient calculation. - * - * This function is called after the density loop and before the gradient loop. - * - * We use it to set the physical timestep for the particle and to copy the - * actual velocities, which we need to boost our interfaces during the flux - * calculation. We also initialize the variables used for the time step - * calculation. - * - * @param p The particle to act upon. - * @param xp The extended particle data to act upon. - * @param cosmo The cosmological model. - * @param hydro_props Hydrodynamic properties. - * @param pressure_floor The properties of the pressure floor. - */ -__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( - struct part *restrict p, struct xpart *restrict xp, - const struct cosmology *cosmo, const struct hydro_props *hydro_props, - const struct pressure_floor_props *pressure_floor) { - + #ifdef hydro_props_use_adiabatic_correction /* Prepare the denominator for the adiabatic correction term */ p->gradients.adiabatic_f_denominator = 0.; From 781844de5e1a16acebde2097ff0119098cd9cfe7 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Thu, 4 Dec 2025 06:58:45 +0000 Subject: [PATCH 28/37] Fix handling of lum_thresh_always_jet in Obsidian; remove v_full from chemistry_iact.h --- src/black_holes/Obsidian/black_holes.h | 8 +++-- src/black_holes/Obsidian/black_holes_iact.h | 5 --- src/chemistry/KIARA/chemistry.h | 33 ++++++++---------- src/chemistry/KIARA/chemistry_iact.h | 38 +++++++-------------- 4 files changed, 33 insertions(+), 51 deletions(-) diff --git a/src/black_holes/Obsidian/black_holes.h b/src/black_holes/Obsidian/black_holes.h index a3db3b5cb6..60c8bbfa9e 100644 --- a/src/black_holes/Obsidian/black_holes.h +++ b/src/black_holes/Obsidian/black_holes.h @@ -1302,7 +1302,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( const float my_adaf_mass_limit = get_black_hole_adaf_mass_limit(bp, props, cosmo); - /* Switch between states depending on the */ + /* Switch between states depending on f_edd */ switch (bp->state) { case BH_states_adaf: if (predicted_mdot_medd > props->eddington_fraction_upper_boundary) { @@ -1382,7 +1382,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( * jet BZ efficiency (spin is fixed) */ mass_rate = (1. - bp->radiative_efficiency) * bp->accretion_rate; - /* This is used for X-ray feedback later */ + /* This is used for jet feedback later */ bp->radiative_luminosity = luminosity; #ifdef OBSIDIAN_DEBUG_CHECKS @@ -1442,7 +1442,9 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( } if (bp->state == BH_states_adaf || - (props->slim_disk_jet_active && bp->state == BH_states_slim_disk)) { + (props->slim_disk_jet_active && bp->state == BH_states_slim_disk) || + (bp->radiative_luminosity > props->lum_thresh_always_jet && + props->lum_thresh_always_jet > 0.f)) { float jet_velocity = black_hole_compute_jet_velocity(bp, cosmo, props); diff --git a/src/black_holes/Obsidian/black_holes_iact.h b/src/black_holes/Obsidian/black_holes_iact.h index 2e3acb53d4..bfc8b34c9a 100644 --- a/src/black_holes/Obsidian/black_holes_iact.h +++ b/src/black_holes/Obsidian/black_holes_iact.h @@ -609,11 +609,6 @@ runner_iact_nonsym_bh_gas_swallow( float jet_prob = bi->jet_mass_reservoir * kernel_wt; const float rand_jet = random_unit_interval(bi->id + pj->id, ti_current, random_number_BH_kick); - /* Always kick if above luminosity threshold */ - if (bi->radiative_luminosity > bh_props->lum_thresh_always_jet && - bh_props->lum_thresh_always_jet > 0.f) { - jet_prob = 1.f; - } /* Here the particle is also identified to be kicked out as a jet */ if (rand_jet < jet_prob) { diff --git a/src/chemistry/KIARA/chemistry.h b/src/chemistry/KIARA/chemistry.h index c769a3b287..61675c27d3 100644 --- a/src/chemistry/KIARA/chemistry.h +++ b/src/chemistry/KIARA/chemistry.h @@ -317,8 +317,7 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density( /* Rennehan: Limit to maximum resolvable velocity scale */ const float v_phys = - sqrtf(xp->v_full[0] * xp->v_full[0] + xp->v_full[1] * xp->v_full[1] + - xp->v_full[2] * xp->v_full[2]) * + sqrtf(p->v[0] * p->v[0] + p->v[1] * p->v[1] + p->v[2] * p->v[2]) * cosmo->a_inv; const float h_phys = cosmo->a * p->h * kernel_gamma; const float vel_norm_phys_max = 0.5f * v_phys / h_phys; @@ -754,8 +753,8 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( const float m = hydro_get_mass(p); const float v = - sqrtf(xp->v_full[0] * xp->v_full[0] + xp->v_full[1] * xp->v_full[1] + - xp->v_full[2] * xp->v_full[2]); + sqrtf(p->v[0] * p->v[0] + p->v[1] * p->v[1] + + p->v[2] * p->v[2]); const float dv = sqrtf(ch->dv[0] * ch->dv[0] + ch->dv[1] * ch->dv[1] + ch->dv[2] * ch->dv[2]); float dv_phys = dv * cosmo->a_inv; @@ -764,9 +763,9 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( float alpha = 1.f; if (dv >= FIREHOSE_EPSILON_TOLERANCE * v) { - const float v_new[3] = {xp->v_full[0] + ch->dv[0], - xp->v_full[1] + ch->dv[1], - xp->v_full[2] + ch->dv[2]}; + const float v_new[3] = {p->v[0] + ch->dv[0], + p->v[1] + ch->dv[1], + p->v[2] + ch->dv[2]}; const float v_new_norm = sqrtf(v_new[0] * v_new[0] + v_new[1] * v_new[1] + v_new[2] * v_new[2]); @@ -784,9 +783,9 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( const float target_KE_factor = (KE_low_flag) ? FIREHOSE_COOLLIM : FIREHOSE_HEATLIM; - const float v_dot_dv = xp->v_full[0] * ch->dv[0] + - xp->v_full[1] * ch->dv[1] + - xp->v_full[2] * ch->dv[2]; + const float v_dot_dv = p->v[0] * ch->dv[0] + + p->v[1] * ch->dv[1] + + p->v[2] * ch->dv[2]; /* How to scale all components equally? Solve quadratic: * v^2 + 2 * alpha * v * dv + alpha^2 * dv^2 = target_KE_factor * v^2 @@ -825,8 +824,7 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( "dv[0]=%g dv[1]=%g dv[2]=%g " "v[0]=%g v[1]=%g v[2]=%g", p->id, alpha, KE_ratio, v, dv, m, ch->dm, u_drift, ch->du, - ch->dv[0], ch->dv[1], ch->dv[2], xp->v_full[0], xp->v_full[1], - xp->v_full[2]); + ch->dv[0], ch->dv[1], ch->dv[2], p->v[0], p->v[1], p->v[2]); } else { ch->dv[0] = 0.f; ch->dv[1] = 0.f; @@ -837,9 +835,8 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( "dv=%g m=%g dm=%g u=%g du=%g " "dv[0]=%g dv[1]=%g dv[2]=%g " "v[0]=%g v[1]=%g v[2]=%g", - p->id, KE_ratio, v, dv, m, ch->dm, u_drift, ch->du, ch->dv[0], - ch->dv[1], ch->dv[2], xp->v_full[0], xp->v_full[1], - xp->v_full[2]); + p->id, KE_ratio, v, dv, m, ch->dm, u_drift, ch->du, + ch->dv[0], ch->dv[1], ch->dv[2], p->v[0], p->v[1], p->v[2]); } /* Recompute the new updated limited values to set v_sig */ @@ -858,9 +855,9 @@ __attribute__((always_inline)) INLINE static void chemistry_end_force( hydro_set_v_sig_based_on_velocity_kick(p, cosmo, dv_phys); - xp->v_full[0] += ch->dv[0]; - xp->v_full[1] += ch->dv[1]; - xp->v_full[2] += ch->dv[2]; + p->v[0] += ch->dv[0]; + p->v[1] += ch->dv[1]; + p->v[2] += ch->dv[2]; /* Grab the comoving internal energy at last kick */ const double u = hydro_get_drifted_comoving_internal_energy(p); diff --git a/src/chemistry/KIARA/chemistry_iact.h b/src/chemistry/KIARA/chemistry_iact.h index c049ff67b7..ad9def28e2 100644 --- a/src/chemistry/KIARA/chemistry_iact.h +++ b/src/chemistry/KIARA/chemistry_iact.h @@ -305,11 +305,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_chemistry( * @param hj Comoving smoothing-length of particle j. * @param pi Wind particle. * @param pj Ambient particle. - * @param xpi The #xpart data of the particle #pi. - * @param xpj The #xpart data of the particle #pj. * @param time_base The time base used to convert integer to float time. * @param ti_current Current integer time for seeding random number generator - * @param phys_const Physical constants * @param cd #chemistry_global_data containing chemistry information. * @param v2 velocity difference squared between i and j. * @@ -318,10 +315,8 @@ __attribute__((always_inline)) INLINE static float firehose_compute_mass_exchange(const float r2, const float dx[3], const float hi, const float hj, const struct part *pi, const struct part *pj, - const struct xpart *xpi, const struct xpart *xpj, const float time_base, const integertime_t ti_current, - const struct phys_const *phys_const, const struct chemistry_global_data *cd, float *v2, const struct cosmology *cosmo) { @@ -353,7 +348,7 @@ firehose_compute_mass_exchange(const float r2, const float dx[3], * Order does not matter here because it is symmetric. */ *v2 = 0.f; for (int i = 0; i < 3; i++) { - const float dv = xpi->v_full[i] - xpj->v_full[i]; + const float dv = pi->v[i] - pj->v[i]; *v2 += dv * dv; } @@ -487,19 +482,16 @@ firehose_compute_mass_exchange(const float r2, const float dx[3], * @param hj Comoving smoothing-length of particle j. * @param pi Wind particle. * @param pj Ambient particle. - * @param xpi The #xpart data of the particle #pi. - * @param xpj The #xpart data of the particle #pj. * @param time_base The time base used to convert integer to float time. * @param ti_current Current integer time for seeding random number generator. - * @param phys_const Physical constants * @param cd #chemistry_global_data containing chemistry information. * */ __attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( const float r2, const float dx[3], const float hi, const float hj, - struct part *pi, struct part *pj, struct xpart *xpi, struct xpart *xpj, + struct part *pi, struct part *pj, const float time_base, const integertime_t ti_current, - const struct phys_const *phys_const, const struct chemistry_global_data *cd, + const struct chemistry_global_data *cd, const struct cosmology *cosmo) { /* Both particles must be within each others smoothing lengths */ @@ -523,9 +515,9 @@ __attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( /* Compute the amount of mass mixed between stream particle and ambient gas */ float v2 = 0.f; - const float dm = firehose_compute_mass_exchange(r2, dx, hi, hj, pi, pj, xpi, - xpj, time_base, ti_current, - phys_const, cd, &v2, cosmo); + const float dm = firehose_compute_mass_exchange(r2, dx, hi, hj, pi, pj, + time_base, ti_current, + cd, &v2, cosmo); float delta_m = fabs(dm); if (delta_m <= 0.f) return; @@ -623,13 +615,13 @@ __attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( float new_v2 = 0.f; for (int i = 0; i < 3; i++) { const float new_pi_v_full_i = - (wt_ii * xpi->v_full[i] + wt_ij * xpj->v_full[i]) / mi; + (wt_ii * pi->v[i] + wt_ij * pj->v[i]) / mi; /* Keep track of the final new velocity */ - chi->dv[i] += new_pi_v_full_i - xpi->v_full[i]; + chi->dv[i] += new_pi_v_full_i - pi->v[i]; const float new_pj_v_full_i = - (wt_ji * xpi->v_full[i] + wt_jj * xpj->v_full[i]) / mj; - chj->dv[i] += new_pj_v_full_i - xpj->v_full[i]; + (wt_ji * pi->v[i] + wt_jj * pj->v[i]) / mj; + chj->dv[i] += new_pj_v_full_i - pj->v[i]; const float dv_i = new_pi_v_full_i - new_pj_v_full_i; new_v2 += dv_i * dv_i; @@ -671,8 +663,6 @@ __attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( * @param hj Comoving smoothing-length of particle j. * @param pi First particle. * @param pj Second particle. - * @param xpi The #xpart data of the particle #pi. - * @param xpj The #xpart data of the particle #pj. * @param a Current scale factor. * @param H Current Hubble parameter. * @param time_base The time base used to convert integer to float time. @@ -683,18 +673,16 @@ __attribute__((always_inline)) INLINE static void firehose_evolve_particle_sym( */ __attribute__((always_inline)) INLINE static void runner_iact_diffusion( const float r2, const float dx[3], const float hi, const float hj, - struct part *restrict pi, struct part *restrict pj, - struct xpart *restrict xpi, struct xpart *restrict xpj, const float a, + struct part *restrict pi, struct part *restrict pj, const float a, const float H, const float time_base, const integertime_t t_current, const struct cosmology *cosmo, const int with_cosmology, - const struct phys_const *phys_const, const struct chemistry_global_data *cd) { if (pi->decoupled || pj->decoupled) { if (cd->use_firehose_wind_model) { /* If in wind mode, do firehose wind diffusion */ - firehose_evolve_particle_sym(r2, dx, hi, hj, pi, pj, xpi, xpj, time_base, - t_current, phys_const, cd, cosmo); + firehose_evolve_particle_sym(r2, dx, hi, hj, pi, pj, time_base, + t_current, cd, cosmo); } return; From 2ffd4e01d1e3b18ffc4a140648f46fc736fd6175 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Thu, 4 Dec 2025 07:08:09 +0000 Subject: [PATCH 29/37] Fix previous commit, forgot to remove xparts declarations --- src/runner_doiact_functions_hydro.h | 52 ----------------------------- 1 file changed, 52 deletions(-) diff --git a/src/runner_doiact_functions_hydro.h b/src/runner_doiact_functions_hydro.h index cab76b5563..0d0b44571c 100644 --- a/src/runner_doiact_functions_hydro.h +++ b/src/runner_doiact_functions_hydro.h @@ -420,9 +420,6 @@ void DOSELF1_NAIVE(struct runner *r, const struct cell *c, /* Get a hold of the ith part in ci. */ struct part *restrict pi = &parts[pid]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *restrict xpi = &xparts[pid]; -#endif /* Skip inhibited particles. */ if (part_is_inhibited(pi, e)) continue; @@ -440,9 +437,6 @@ void DOSELF1_NAIVE(struct runner *r, const struct cell *c, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts[pjd]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *restrict xpj = &xparts[pjd]; -#endif /* Skip inhibited particles. */ if (part_is_inhibited(pj, e)) continue; @@ -591,9 +585,6 @@ void DOSELF2_NAIVE(struct runner *r, const struct cell *c, const int count = c->hydro.count; struct part *parts = c->hydro.parts; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *xparts = c->hydro.xparts; -#endif /* Get the depth limits (if any) */ const char min_depth = limit_max_h ? c->depth : 0; @@ -610,9 +601,6 @@ void DOSELF2_NAIVE(struct runner *r, const struct cell *c, /* Get a hold of the ith part in ci. */ struct part *restrict pi = &parts[pid]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *restrict xpi = &xparts[pid]; -#endif /* Skip inhibited particles. */ if (part_is_inhibited(pi, e)) continue; @@ -630,9 +618,6 @@ void DOSELF2_NAIVE(struct runner *r, const struct cell *c, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts[pjd]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *restrict xpj = &xparts[pjd]; -#endif /* Skip inhibited particles. */ if (part_is_inhibited(pj, e)) continue; @@ -1671,10 +1656,6 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, const int count_j = cj->hydro.count; struct part *restrict parts_i = ci->hydro.parts; struct part *restrict parts_j = cj->hydro.parts; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *restrict xparts_i = ci->hydro.xparts; - struct xpart *restrict xparts_j = cj->hydro.xparts; -#endif /* Cosmological terms and physical constants */ const float a = cosmo->a; @@ -1752,9 +1733,6 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, /* Get a hold of the ith part in ci. */ struct part *pi = &parts_i[sort_i[pid].i]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *xpi = &xparts_i[sort_i[pid].i]; -#endif const char depth_i = pi->depth_h; /* Skip inhibited particles. */ @@ -1879,9 +1857,6 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, /* Recover pj */ struct part *pj = &parts_j[sort_j[pjd].i]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *xpj = &xparts_j[sort_j[pjd].i]; -#endif const char depth_j = pj->depth_h; /* Skip inhibited particles. */ @@ -2010,9 +1985,6 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, /* Get a hold of the jth part in cj. */ struct part *pj = &parts_j[sort_j[pjd].i]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *xpj = &xparts_j[sort_j[pjd].i]; -#endif const char depth_j = pj->depth_h; /* Skip inhibited particles. */ @@ -2138,9 +2110,6 @@ void DOPAIR2(struct runner *r, const struct cell *restrict ci, /* Recover pi */ struct part *pi = &parts_i[sort_i[pid].i]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *xpi = &xparts_i[sort_i[pid].i]; -#endif const char depth_i = pi->depth_h; /* Skip inhibited particles. */ @@ -2343,9 +2312,6 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, TIMER_TIC; struct part *parts = c->hydro.parts; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *xparts = c->hydro.xparts; -#endif const int count = c->hydro.count; /* Get the depth limits (if any) */ @@ -2386,9 +2352,6 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, /* Get a pointer to the ith particle. */ struct part *restrict pi = &parts[pid]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *restrict xpi = &xparts[pid]; -#endif const char depth_i = pi->depth_h; /* Skip inhibited particles. */ @@ -2411,9 +2374,6 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, /* Get a pointer to the jth particle. (by construction pi != pj) */ struct part *restrict pj = &parts[indt[pjd]]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *restrict xpj = &xparts[indt[pjd]]; -#endif /* This particle's (square of) search radius. */ const float hj = pj->h; @@ -2478,9 +2438,6 @@ void DOSELF1(struct runner *r, const struct cell *c, const int limit_min_h, /* Get a pointer to the jth particle (by construction pi != pj). */ struct part *restrict pj = &parts[pjd]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *restrict xpj = &xparts[pjd]; -#endif const char depth_j = pj->depth_h; /* Skip inhibited particles. */ @@ -2680,9 +2637,6 @@ void DOSELF2(struct runner *r, const struct cell *c, const int limit_min_h, TIMER_TIC; struct part *parts = c->hydro.parts; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *xparts = c->hydro.xparts; -#endif const int count = c->hydro.count; /* Get the depth limits (if any) */ @@ -2723,9 +2677,6 @@ void DOSELF2(struct runner *r, const struct cell *c, const int limit_min_h, /* Get a pointer to the ith particle. */ struct part *restrict pi = &parts[pid]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *restrict xpi = &xparts[pid]; -#endif const char depth_i = pi->depth_h; /* Skip inhibited particles. */ @@ -2812,9 +2763,6 @@ void DOSELF2(struct runner *r, const struct cell *c, const int limit_min_h, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts[pjd]; -#if (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) - struct xpart *restrict xpj = &xparts[pjd]; -#endif const char depth_j = pj->depth_h; /* Skip inhibited particles. */ From 7504a32d476dfdcf6a0d700a4013f62d18c6e4c1 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Thu, 4 Dec 2025 07:36:45 +0000 Subject: [PATCH 30/37] Add in KIARART option; uses native Kiara/grackle cooling. Works without subsycling, gives sorting undrifted cell error with subcycling --- src/cooling/KIARA/cooling.c | 6 ++- src/cooling/KIARA/cooling_io.h | 3 ++ src/cooling/KIARA/cooling_properties.h | 4 ++ src/hydro/MAGMA2/hydro.h | 7 +++ src/hydro/MAGMA2/hydro_iact.h | 13 ++++++ src/rt/KIARA/rt_io.h | 60 -------------------------- src/rt/KIARA/rt_properties.h | 14 +++--- src/rt/KIARA/rt_thermochemistry.c | 21 +++++---- src/rt/KIARA/rt_unphysical.h | 1 - swift.c | 26 +++++++++-- 10 files changed, 75 insertions(+), 80 deletions(-) diff --git a/src/cooling/KIARA/cooling.c b/src/cooling/KIARA/cooling.c index d4c7416dd0..9622cea8ca 100644 --- a/src/cooling/KIARA/cooling.c +++ b/src/cooling/KIARA/cooling.c @@ -1501,6 +1501,8 @@ void cooling_cool_part(const struct phys_const *restrict phys_const, /* No cooling if particle is decoupled */ if (p->decoupled) return; + if (cooling->do_cooling_in_rt) return; + /* No cooling happens over zero time */ if (dt == 0.f || dt_therm == 0.f) return; @@ -1774,11 +1776,11 @@ void cooling_init_grackle(struct cooling_function_data *cooling) { // Set parameter values for chemistry & cooling // Flag to activate the grackle machinery: - chemistry->use_grackle = 2; // grackle on (duh) + chemistry->use_grackle = 2; // 1=original grackle, 2=crackle /* Flag to include radiative cooling and actually update the thermal energy * during the chemistry solver. If off, the chemistry species will still be * updated. The most common reason to set this to off is to iterate the - * chemistry network to an equilibrium state. Default: 1. */ + * chemistry network to an equilibrium state. */ chemistry->with_radiative_cooling = 1; /* Flag to control which primordial chemistry network is used (set by Config diff --git a/src/cooling/KIARA/cooling_io.h b/src/cooling/KIARA/cooling_io.h index 7ac094446f..d56291ee9e 100644 --- a/src/cooling/KIARA/cooling_io.h +++ b/src/cooling/KIARA/cooling_io.h @@ -346,6 +346,9 @@ __attribute__((always_inline)) INLINE static void cooling_read_parameters( cooling->self_enrichment_metallicity = parser_get_opt_param_double( parameter_file, "KIARACooling:self_enrichment_metallicity", 0.f); + + cooling->do_cooling_in_rt = parser_get_opt_param_int( + parameter_file, "KIARACooling:do_cooling_in_rt", 0); } #endif /* SWIFT_COOLING_KIARA_IO_H */ diff --git a/src/cooling/KIARA/cooling_properties.h b/src/cooling/KIARA/cooling_properties.h index a4d940f680..ccd9937fcc 100644 --- a/src/cooling/KIARA/cooling_properties.h +++ b/src/cooling/KIARA/cooling_properties.h @@ -142,6 +142,10 @@ struct cooling_function_data { /*! Option to use Cloudy lookup tables when outside ISM */ int use_tables_outside_ism; + + /*! When using radiative transfer, set this on if you want thermochemistry + * done within RT modules (eg in KIARART) */ + int do_cooling_in_rt; }; #endif /* SWIFT_COOLING_PROPERTIES_KIARA_H */ diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 6b77ea5b7a..04222cd4d0 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -34,6 +34,7 @@ #include "dimension.h" #include "entropy_floor.h" #include "equation_of_state.h" +#include "fvpm_geometry.h" #include "hydro_parameters.h" #include "hydro_properties.h" #include "hydro_space.h" @@ -578,6 +579,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->gradients.velocity_tensor_aux_norm[i][j] = 0.; } } + + /* Init geometry for FVPM Radiative Transfer */ + fvpm_geometry_init(p); } /** @@ -1559,6 +1563,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( } } } + + /* Finish matrix and volume computations for FVPM Radiative Transfer */ + fvpm_compute_volume_and_matrix(p, h_inv_dim); } /** diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index bcc4e47128..a5193858ba 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -32,6 +32,7 @@ #include "hydro_parameters.h" #include "minmax.h" #include "signal_velocity.h" +#include "fvpm_geometry.h" /** * @brief Density interaction between two particles. @@ -73,6 +74,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( adaptive_softening_add_correction_term(pi, xi, hi_inv, mj); + /* Collect data for FVPM matrix construction */ + fvpm_accumulate_geometry_and_matrix(pi, wi, dx); + fvpm_update_centroid_left(pi, dx, wi); + /* Compute density of pj. */ const hydro_real_t hj_inv = 1. / hj; const float xj = r * hj_inv; @@ -86,6 +91,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( adaptive_softening_add_correction_term(pj, xj, hj_inv, mi); + /* Collect data for FVPM matrix construction */ + fvpm_accumulate_geometry_and_matrix(pj, wj, dx); + fvpm_update_centroid_right(pj, dx, wj); + /* Now we need to compute the div terms */ const hydro_real_t r_inv = r ? 1.0 / r : 0.0; const hydro_real_t faci = mj * wi_dx * r_inv; @@ -202,6 +211,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( adaptive_softening_add_correction_term(pi, xi, h_inv, mj); + /* Collect data for FVPM matrix construction */ + fvpm_accumulate_geometry_and_matrix(pi, wi, dx); + fvpm_update_centroid_left(pi, dx, wi); + const hydro_real_t r_inv = r ? 1.0 / r : 0.0; const hydro_real_t faci = mj * wi_dx * r_inv; diff --git a/src/rt/KIARA/rt_io.h b/src/rt/KIARA/rt_io.h index f08872f1aa..573e6edcb0 100644 --- a/src/rt/KIARA/rt_io.h +++ b/src/rt/KIARA/rt_io.h @@ -179,66 +179,6 @@ INLINE static int rt_write_particles(const struct part* parts, /*xparts=*/NULL, rt_convert_mass_fractions, "Mass fractions of all constituent species"); - #if COOLING_GRACKLE_MODE >= 1 - /* List what we want to write */ - list[num_elements] = io_make_output_field_convert_part( - "AtomicHydrogenMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, - convert_part_HI_mass, "Atomic hydrogen masses."); - num_elements ++; - - list[num_elements] = - io_make_output_field_convert_part( - "MolecularHydrogenMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, - convert_part_H2_mass, "Molecular hydrogen masses."); - num_elements ++; - - list[num_elements] = - io_make_output_field_convert_part( - "HeIMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, - convert_part_HeII_mass, "HeI masses."); - num_elements ++; - - list[num_elements] = - io_make_output_field_convert_part( - "HeIIMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, xparts, - convert_part_HeII_mass, "HeII masses."); - num_elements ++; - - list[num_elements] = io_make_output_field_convert_part( - "ElectronNumberDensities", FLOAT, 1, UNIT_CONV_NUMBER_DENSITY, -3.f, - parts, xparts, convert_part_e_density, - "Electron number densities"); - num_elements ++; - -#if COOLING_GRACKLE_MODE >= 2 - list[num_elements] = - io_make_output_field("SubgridTemperatures", - FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, - cooling_data.subgrid_temp, - "Temperature of subgrid ISM in K"); - num_elements ++; - - list[num_elements] = - io_make_output_field("SubgridDensities", - FLOAT, 1, UNIT_CONV_DENSITY, -3.f, parts, - cooling_data.subgrid_dens, - "Mass density in physical units of subgrid ISM"); - num_elements ++; - - list[num_elements] = - io_make_output_field("DustMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, - cooling_data.dust_mass, "Total mass in dust"); - num_elements ++; - - list[num_elements] = - io_make_output_field("DustTemperatures", - FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, - cooling_data.dust_temperature, - "Dust temperature in subgrid dust model, in K"); - num_elements ++; -#endif -#endif - #ifdef SWIFT_RT_DEBUG_CHECKS num_elements += 8; list[3] = diff --git a/src/rt/KIARA/rt_properties.h b/src/rt/KIARA/rt_properties.h index 37bd958516..27187711de 100644 --- a/src/rt/KIARA/rt_properties.h +++ b/src/rt/KIARA/rt_properties.h @@ -140,6 +140,9 @@ struct rt_props { /*! make grackle talkative? */ int grackle_verbose; + /* Max number of subcycles per hydro step */ + int debug_max_nr_subcycles; + #ifdef SWIFT_RT_DEBUG_CHECKS /* radiation emitted by stars this step. This is not really a property, * but a placeholder to sum up a global variable. It's being reset @@ -157,9 +160,6 @@ struct rt_props { /* total radiation absorbed by gas. This is not really a property, * but a placeholder to sum up a global variable */ unsigned long long debug_radiation_absorbed_tot; - - /* Max number of subcycles per hydro step */ - int debug_max_nr_subcycles; #endif }; @@ -199,10 +199,10 @@ __attribute__((always_inline)) INLINE static void rt_props_print( message("Radiative transfer scheme: '%s'", RT_IMPLEMENTATION); message("RT Riemann Solver used: '%s'", RT_RIEMANN_SOLVER_NAME); - char messagestring[200] = "Using photon frequency bins: [ "; + char messagestring[200] = "Using photon frequency bins (Ryd): [ "; char freqstring[20]; for (int g = 0; g < RT_NGROUPS; g++) { - sprintf(freqstring, "%.3g ", rtp->photon_groups[g]); + sprintf(freqstring, "%.3g ", rtp->photon_groups[g] / 3.288e15); strcat(messagestring, freqstring); } strcat(messagestring, "]"); @@ -237,6 +237,7 @@ __attribute__((always_inline)) INLINE static void rt_props_print( message("WARNING: Thermochemistry will be skipped."); if (rtp->rt_with_galaxy_subgrid) message("RT is coupling with galaxy subgrid modules."); + message("RT max number of subcycles: %d", rtp->debug_max_nr_subcycles); } /** @@ -516,11 +517,12 @@ __attribute__((always_inline)) INLINE static void rt_props_init( rtp->debug_radiation_absorbed_tot = 0ULL; rtp->debug_radiation_absorbed_this_step = 0ULL; +#endif + /* Don't make it an optional parameter here so we crash * if I forgot to provide it */ rtp->debug_max_nr_subcycles = parser_get_param_int(params, "TimeIntegration:max_nr_rt_subcycles"); -#endif /* Grackle setup */ /* ------------- */ diff --git a/src/rt/KIARA/rt_thermochemistry.c b/src/rt/KIARA/rt_thermochemistry.c index cda008c2eb..7a69eec73b 100644 --- a/src/rt/KIARA/rt_thermochemistry.c +++ b/src/rt/KIARA/rt_thermochemistry.c @@ -25,6 +25,10 @@ #include "rt_getters.h" #include "../../cooling.h" +/* The grackle library itself */ +#include +extern chemistry_data *grackle_data; + /** * @file src/rt/KIARA/rt_thermochemistry.h * @brief Main header file for the KIARA M1 closure radiative transfer scheme @@ -108,7 +112,7 @@ INLINE void rt_do_thermochemistry( /* Nothing to do here? */ if (rt_props->skip_thermochemistry) return; - if (dt == 0.) return; + if (dt <= 0.) return; /* This is where the fun begins */ /* ---------------------------- */ @@ -368,17 +372,18 @@ INLINE void rt_do_thermochemistry_with_subgrid( /* Nothing to do here? */ if (rt_props->skip_thermochemistry) return; - /* Compute cooling time and other quantities needed for firehose */ - /* TODO: firehose is using without RT_rates */ - firehose_cooling_and_dust(phys_const, us, cosmo, hydro_props, + /* Compute cooling time and other quantities needed for firehose + if (dt > 0. && dt_therm > 0.) { + firehose_cooling_and_dust(phys_const, us, cosmo, hydro_props, cooling, p, xp, dt); + }*/ - /* Update the subgrid properties */ + /* Update the subgrid properties cooling_set_particle_subgrid_properties( phys_const, us, - cosmo, hydro_props, floor_props, cooling, p, xp); + cosmo, hydro_props, floor_props, cooling, p, xp);*/ - /* No cooling if particle is decoupled */ - if (p->decoupled) return; + /* No cooling if particle is decoupled + if (p->decoupled) return; */ if (dt == 0.f || dt_therm == 0.f) return; diff --git a/src/rt/KIARA/rt_unphysical.h b/src/rt/KIARA/rt_unphysical.h index 7e5b76692e..191624bad5 100644 --- a/src/rt/KIARA/rt_unphysical.h +++ b/src/rt/KIARA/rt_unphysical.h @@ -78,7 +78,6 @@ __attribute__((always_inline)) INLINE static void rt_check_unphysical_state( const double flux_max = rt_params.reduced_speed_of_light * *energy_density; if (flux_norm > flux_max) { const double correct = flux_max / flux_norm; - message("RT flux=%g max=%g edens=%g correct=%g", sqrtf(flux2), flux_max, *energy_density, correct); flux[0] *= correct; flux[1] *= correct; flux[2] *= correct; diff --git a/swift.c b/swift.c index c838fa3fde..e56fce52a6 100644 --- a/swift.c +++ b/swift.c @@ -204,6 +204,7 @@ int main(int argc, char *argv[]) { int with_rt = 0; int with_power = 0; int with_kiara = 0; + int with_kiarart = 0; int verbose = 0; int nr_threads = 1; int nr_pool_threads = -1; @@ -308,6 +309,12 @@ int main(int argc, char *argv[]) { "equivalent to --hydro --limiter --sync --self-gravity --stars " "--star-formation --cooling --feedback --black-holes --fof.", NULL, 0, 0), + OPT_BOOLEAN( + 0, "kiarart", &with_kiarart, + "Run with all the options needed for the Kiara-RT model. This is " + "equivalent to --hydro --limiter --sync --self-gravity --stars " + "--star-formation --feedback --black-holes --fof.", + NULL, 0, 0), OPT_GROUP(" Control options:\n"), OPT_BOOLEAN('a', "pin", &with_aff, @@ -436,6 +443,19 @@ int main(int argc, char *argv[]) { with_black_holes = 1; with_fof = 1; } + if (with_kiarart) { + with_hydro = 1; + with_timestep_limiter = 1; + with_timestep_sync = 1; + with_self_gravity = 1; + with_stars = 1; + with_star_formation = 1; + with_cooling = 1; + // with_hydro_decoupling = 1; + with_feedback = 1; + with_black_holes = 1; + with_fof = 1; + } #ifdef MOVING_MESH if (with_hydro) { with_grid = 1; @@ -705,9 +725,9 @@ int main(int argc, char *argv[]) { "Error: Cannot use radiative transfer without --feedback " "(even if configured --with-feedback=none)."); } - if (with_rt && with_cooling) { - error("Error: Cannot use radiative transfer and cooling simultaneously."); - } + //if (with_rt && with_cooling) { + // error("Error: Cannot use radiative transfer and cooling simultaneously."); + //} #endif /* idfef RT_NONE */ #ifdef SINK_NONE From 9726edee4120bebf323ed633432baab560f32d4c Mon Sep 17 00:00:00 2001 From: Matthieu Schaller Date: Fri, 5 Dec 2025 10:13:31 +0800 Subject: [PATCH 31/37] Fix compilation typo --- src/rt/KIARA/rt_interaction_cross_sections.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rt/KIARA/rt_interaction_cross_sections.c b/src/rt/KIARA/rt_interaction_cross_sections.c index 24189d94f2..bdb77bc632 100644 --- a/src/rt/KIARA/rt_interaction_cross_sections.c +++ b/src/rt/KIARA/rt_interaction_cross_sections.c @@ -439,7 +439,7 @@ double **read_Bpass_from_hdf5(char *file_name, char *dataset_name){ for (hsize_t i = 0; i < dims[0]; i++){ for (hsize_t j = 0; j < dims[1]; j++) { Table[i][j] = buffer[i * dims[1] + j]; - if (fabs(Table[i][j])< 0) error("Negative photon number in the table! row:%lu, column:%lu\n", i, j); + if (fabs(Table[i][j])< 0) error("Negative photon number in the table! row:%lu, column:%lu\n", (long int)i, (long int)j); } } From d7556e20507564f1aafb0830a31f75e035dc13ff Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Thu, 11 Dec 2025 04:23:53 +0000 Subject: [PATCH 32/37] Fix double definition of hydro_prepare_gradient; new cooling I/O stuff and some other fixes --- src/cooling/KIARA/cooling.h | 1 - src/cooling/KIARA/cooling_io.h | 48 ++++++++++++++++++++---------- src/feedback/KIARA/feedback_iact.h | 4 +-- src/hydro/MAGMA2/hydro.h | 4 +-- src/runner_ghost.c | 22 +++++++++++--- src/runner_time_integration.c | 6 ++-- 6 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/cooling/KIARA/cooling.h b/src/cooling/KIARA/cooling.h index 4645835481..390c8fae03 100644 --- a/src/cooling/KIARA/cooling.h +++ b/src/cooling/KIARA/cooling.h @@ -34,7 +34,6 @@ /* Local includes. */ #include "chemistry.h" -#include "cooling_io.h" #include "cooling_properties.h" #include "entropy_floor.h" #include "error.h" diff --git a/src/cooling/KIARA/cooling_io.h b/src/cooling/KIARA/cooling_io.h index b1db7bf8c7..78eb0c19ae 100644 --- a/src/cooling/KIARA/cooling_io.h +++ b/src/cooling/KIARA/cooling_io.h @@ -21,10 +21,10 @@ #define SWIFT_COOLING_KIARA_IO_H /* Local includes */ -#include "cooling_properties.h" -#include "cooling_struct.h" +#include "cooling.h" +#include "engine.h" #include "io_properties.h" -#include "physical_constants.h" +#include "cooling_properties.h" #ifdef HAVE_HDF5 @@ -111,6 +111,14 @@ INLINE static void convert_part_e_density(const struct engine *e, *ret = (float)xp->cooling_data.e_frac; } +INLINE static void convert_part_T(const struct engine *e, const struct part *p, + const struct xpart *xp, float *ret) { + + *ret = cooling_get_temperature(e->physical_constants, e->hydro_properties, + e->internal_units, e->cosmology, + e->cooling_func, p, xp); +} + #ifdef RT_NONE INLINE static void convert_mass_fractions(const struct engine *engine, const struct part *part, @@ -170,51 +178,59 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( num++; list[num] = io_make_output_field_convert_part( - "ElectronNumberDensities", FLOAT, 1, UNIT_CONV_NUMBER_DENSITY, -3.f, - parts, xparts, convert_part_e_density, "Electron number densities"); + "ElectronNumberDensities", FLOAT, 1, UNIT_CONV_NO_UNITS, -3.f, + parts, xparts, convert_part_e_density, "Electron number densities" + "in units of the hydrogen density."); + num++; + + list[num] = io_make_output_field_convert_part( + "Temperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, parts, xparts, + convert_part_T, "Temperatures of the overall gas particles."); num++; #if COOLING_GRACKLE_MODE >= 2 list[num] = io_make_output_field( - "SubgridTemperatures", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, - cooling_data.subgrid_temp, "Temperature of subgrid ISM in K"); + "SubgridTemperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, parts, + cooling_data.subgrid_temp, "Temperatures of the cold phase" + "of the subgrid ISM gas particles."); num++; list[num] = io_make_output_field("SubgridDensities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, parts, cooling_data.subgrid_dens, - "Mass density in physical units of subgrid ISM"); + "Mass densities in physical units of the " + "subgrid ISM gas particles."); num++; list[num] = io_make_output_field( "SubgridColdISMFraction", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, cooling_data.subgrid_fcold, - "Fraction of particle mass in cold subgrid ISM"); + "Fraction of gas particle masses in cold component of subgrid ISM."); num++; list[num] = io_make_output_field("DustMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, - cooling_data.dust_mass, "Total mass in dust"); + cooling_data.dust_mass, "Total masses in dust."); num++; list[num] = io_make_output_field("DustMassFractions", FLOAT, chemistry_element_count, UNIT_CONV_NO_UNITS, 0.f, parts, cooling_data.dust_mass_fraction, "Fractions of the particles' masses that " - "are in dust for a given element"); + "are in dust for a given element."); num++; list[num] = - io_make_output_field("DustTemperatures", FLOAT, 1, UNIT_CONV_NO_UNITS, + io_make_output_field("DustTemperatures", FLOAT, 1, UNIT_CONV_TEMPERATURE, 0.f, parts, cooling_data.dust_temperature, - "Dust temperature in subgrid dust model, in K"); + "Dust temperatures in subgrid ISM dust model."); num++; list[num] = io_make_output_field( "CoolingTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, parts, cooling_data.mixing_layer_cool_time, - "Cooling time for particle; if it's currently a firehose wind" - "particle (delay_time>0), this is the mixing layer cooling time"); + "Cooling times for the gas particle. If it's currently a firehose wind" + "particle (decoupling_delay_time>0), this is the mixing layer cooling time."); num++; #endif #endif @@ -222,7 +238,7 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( #ifdef RT_NONE list[num] = io_make_output_field_convert_part( "IonMassFractions", FLOAT, 2, UNIT_CONV_NO_UNITS, 0, parts, xparts, - convert_mass_fractions, "Mass fractions of all constituent species"); + convert_mass_fractions, "Mass fractions of all constituent species."); num++; #endif return num; diff --git a/src/feedback/KIARA/feedback_iact.h b/src/feedback/KIARA/feedback_iact.h index efa815201d..4bda827be1 100644 --- a/src/feedback/KIARA/feedback_iact.h +++ b/src/feedback/KIARA/feedback_iact.h @@ -247,7 +247,7 @@ runner_iact_nonsym_feedback_prep1(const float r2, const float dx[3], pj->feedback_data.kick_id = si->id; } - // #ifdef KIARA_DEBUG_CHECKS +#ifdef KIARA_DEBUG_CHECKS message( "KICK_PROB: z=%g sid=%lld, gid=%lld, prob=%g, rand=%g, eta=%g, " "mlaunch=%g, " @@ -256,7 +256,7 @@ runner_iact_nonsym_feedback_prep1(const float r2, const float dx[3], si->feedback_data.mass_to_launch / si->mass_init, si->feedback_data.mass_to_launch, si->mass_init, wj, si->feedback_data.wind_wt_sum, pj->feedback_data.kick_id == si->id); - // #endif +#endif } /** diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index d87977ef1c..e21309e4f2 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -1595,9 +1595,9 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( /* Finish matrix and volume computations for FVPM Radiative Transfer */ fvpm_compute_volume_and_matrix(p, h_inv_dim); +/** } -/** * @brief Prepare a particle for the gradient calculation. * * This function is called after the density loop and before the gradient loop. @@ -1612,11 +1612,11 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( * @param cosmo The cosmological model. * @param hydro_props Hydrodynamic properties. * @param pressure_floor The properties of the pressure floor. - */ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct pressure_floor_props *pressure_floor) { + */ #ifdef hydro_props_use_adiabatic_correction /* Prepare the denominator for the adiabatic correction term */ diff --git a/src/runner_ghost.c b/src/runner_ghost.c index 75ab0e6a98..f6cacc5081 100644 --- a/src/runner_ghost.c +++ b/src/runner_ghost.c @@ -112,6 +112,10 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { if (c->stars.count == 0) return; if (!cell_is_active_stars(c, e)) return; + /* Set up possible bisection if Newton-Raphson over-iterates */ + float h_high = stars_h_max; + float h_low = stars_h_min; + /* Recurse? */ if (c->split) { for (int k = 0; k < 8; k++) { @@ -317,11 +321,21 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { if (num_reruns > max_smoothing_iter - 10) { message( - "Smoothing length convergence problem: iter=%d p->id=%lld " + "Smoothing length convergence problem stars: iter=%d p->id=%lld " "h_init=%12.8e h_old=%12.8e h_new=%12.8e f=%f f_prime=%f " "n_sum=%12.8e n_target=%12.8e left=%12.8e right=%12.8e", num_reruns, sp->id, h_init, h_old, h_new, f, f_prime, n_sum, n_target, left[i], right[i]); + + /* Try bisection */ + if (n_sum < n_target) { + h_new = 0.5 * (h_old + h_high); + h_low = h_old; + } + if (n_sum > n_target) { + h_new = 0.5 * (h_old + h_low); + h_high = h_old; + } } /* Safety check: truncate to the range [ h_old/2 , 2h_old ]. */ @@ -735,7 +749,7 @@ void runner_do_black_holes_density_ghost(struct runner *r, struct cell *c, if (num_reruns > max_smoothing_iter - 10) { message( - "Smoothing length convergence problem: iter=%d p->id=%lld " + "Smoothing length convergence problem BH: iter=%d p->id=%lld " "h_init=%12.8e h_old=%12.8e h_new=%12.8e f=%f f_prime=%f " "n_sum=%12.8e n_target=%12.8e left=%12.8e right=%12.8e", num_reruns, bp->id, h_init, h_old, h_new, f, f_prime, n_sum, @@ -1337,7 +1351,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { if (num_reruns > max_smoothing_iter - 10) { message( - "Smoothing length convergence problem: iter=%d p->id=%lld " + "Smoothing length convergence problem gas: iter=%d p->id=%lld " "h_init=%12.8e h_old=%12.8e h_new=%12.8e f=%f f_prime=%f " "n_sum=%12.8e n_target=%12.8e left=%12.8e right=%12.8e", num_reruns, p->id, h_init, h_old, h_new, f, f_prime, n_sum, @@ -1891,7 +1905,7 @@ void runner_do_sinks_density_ghost(struct runner *r, struct cell *c, if (num_reruns > max_smoothing_iter - 10) { message( - "Smoothing length convergence problem: iter=%d p->id=%lld " + "Smoothing length convergence problem sink: iter=%d p->id=%lld " "h_init=%12.8e h_old=%12.8e h_new=%12.8e f=%f f_prime=%f " "n_sum=%12.8e n_target=%12.8e left=%12.8e right=%12.8e", num_reruns, sp->id, h_init, h_old, h_new, f, f_prime, n_sum, diff --git a/src/runner_time_integration.c b/src/runner_time_integration.c index 11094ea492..dc31950e65 100644 --- a/src/runner_time_integration.c +++ b/src/runner_time_integration.c @@ -1398,7 +1398,7 @@ void runner_do_limiter(struct runner *r, struct cell *c, int force, /* Bip, bip, bip... wake-up time */ if (p->limiter_data.wakeup != time_bin_not_awake) { - if (!part_is_active(p, e) && p->limiter_data.to_be_synchronized) { + /*if (!part_is_active(p, e) && p->limiter_data.to_be_synchronized) { warning( "Not limiting particle with id %lld because it needs to be " "synced tdel=%g dec=%d.", @@ -1411,9 +1411,7 @@ void runner_do_limiter(struct runner *r, struct cell *c, int force, #endif ); continue; - } - - // message("Limiting particle %lld in cell %lld", p->id, c->cellID); + }*/ /* Apply the limiter and get the new end of time-step */ const integertime_t ti_end_new = timestep_limit_part(p, xp, e); From c233d33b786285afcf31deb453d4dbc320a440b3 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Tue, 23 Dec 2025 07:14:58 +0000 Subject: [PATCH 33/37] Refactor BH suppression and cold inflow criterion; revert hydro_prepare_gradient to the way Doug had it arranged --- src/black_holes/Obsidian/black_holes.h | 74 ++++++++----- src/black_holes/Obsidian/black_holes_iact.h | 101 +++++++++++------- .../Obsidian/black_holes_properties.h | 8 ++ src/feedback/KIARA/feedback.h | 3 + src/hydro/MAGMA2/hydro.h | 15 ++- 5 files changed, 125 insertions(+), 76 deletions(-) diff --git a/src/black_holes/Obsidian/black_holes.h b/src/black_holes/Obsidian/black_holes.h index 60c8bbfa9e..972505dda9 100644 --- a/src/black_holes/Obsidian/black_holes.h +++ b/src/black_holes/Obsidian/black_holes.h @@ -267,6 +267,7 @@ get_black_hole_upper_mdot_medd(const struct black_holes_props *props, if (x1 >= 0.) { return x1; } else { + if (m_dot_inflow_m_dot_edd > 1.e-6) warning( "num_roots=1 m_dot_inflow_m_dot_edd=%g phi=%g a3=%g a2=%g " "a1=%g a0=%g", @@ -277,6 +278,7 @@ get_black_hole_upper_mdot_medd(const struct black_holes_props *props, if (x3 >= 0.) { return x3; } else { + if (m_dot_inflow_m_dot_edd > 1.e-6) warning( "num_roots=0 m_dot_inflow_m_dot_edd=%g phi=%g a3=%g a2=%g a1=%g " "a0=%g", @@ -428,7 +430,6 @@ __attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( } bp->total_accreted_mass = 0.f; bp->accretion_disk_mass = 0.f; - bp->gas_SFR = 0.f; bp->accretion_rate = 0.f; bp->bondi_accretion_rate = 0.f; bp->formation_time = -1.f; @@ -482,8 +483,10 @@ __attribute__((always_inline)) INLINE static void black_holes_init_bpart( bp->rho_gas = 0.f; bp->sound_speed_gas = 0.f; bp->internal_energy_gas = 0.f; + bp->gas_SFR = 0.f; bp->hot_gas_mass = 0.f; bp->cold_gas_mass = 0.f; + bp->cold_disk_mass = 0.f; bp->hot_gas_internal_energy = 0.f; bp->sound_speed_subgrid_gas = -1.f; bp->velocity_gas[0] = 0.f; @@ -509,7 +512,6 @@ __attribute__((always_inline)) INLINE static void black_holes_init_bpart( bp->reposition.potential = FLT_MAX; bp->accretion_rate = 0.f; /* Optionally accumulated ngb-by-ngb */ bp->bondi_accretion_rate = 0.f; /* Optionally accumulated ngb-by-ngb */ - bp->cold_disk_mass = 0.f; bp->mass_at_start_of_step = bp->mass; /* bp->mass may grow in nibbling mode */ bp->m_dot_inflow = 0.f; /* reset accretion rate */ bp->kernel_wt_sum = 0.f; @@ -1145,21 +1147,27 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( tdyn_inv /= 0.5 * (1.f + fabs(gaussian_random)); } + float f_suppress = 1.f; + const float corot_gas_mass = bp->cold_gas_mass - 2. * (bp->cold_gas_mass - bp->cold_disk_mass); - if (props->torque_accretion_norm > 0.f) { + float torque_norm = fabs(props->torque_accretion_norm); + if (props->torque_accretion_norm < 0.f && bp->subgrid_mass * props->mass_to_solar_mass > 1.e9) { + torque_norm *= exp(-bp->subgrid_mass * props->mass_to_solar_mass * 1.e-9) * 2.71828; // suppress torque accr + } + if (torque_norm > 0.f) { switch (props->torque_accretion_method) { case 0: if (galaxy_mgas > 0.) { torque_accr_rate = - props->torque_accretion_norm * bp->cold_disk_mass * tdyn_inv; + torque_norm * bp->cold_disk_mass * tdyn_inv; } break; case 1: if (corot_gas_mass > 0. && bp->cold_gas_mass > 0.) { torque_accr_rate = - props->torque_accretion_norm * corot_gas_mass * tdyn_inv; + torque_norm * corot_gas_mass * tdyn_inv; } break; @@ -1179,7 +1187,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( const float f_gas = corot_gas_mass / m_disk; const float mass_in_1e8solar = BH_mass * mass_to_1e8solar; - torque_accr_rate = props->torque_accretion_norm * alpha * + torque_accr_rate = torque_norm * alpha * corot_gas_mass * mass_to_1e9solar * powf(f_disk, 5. / 2.) * powf(mass_in_1e8solar, 1. / 6.) * @@ -1191,7 +1199,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( case 3: if (galaxy_mgas > 0.) { torque_accr_rate = - props->torque_accretion_norm * bp->cold_gas_mass * tdyn_inv; + torque_norm * bp->cold_gas_mass * tdyn_inv; } break; @@ -1207,8 +1215,8 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( const double r0 = bh_h * cosmo->a * props->length_to_parsec; const double sigma_eff = f_corr_stellar * bp->ngb_mass * props->mass_to_solar_mass / (M_PI * r0 * r0); - - torque_accr_rate *= sigma_eff / (sigma_eff + 3000.); + + f_suppress = sigma_eff / (sigma_eff + 3000.); break; } @@ -1220,7 +1228,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( m_suppress *= cosmo->a; } - torque_accr_rate *= + f_suppress = 1. - exp(-BH_mass * props->mass_to_solar_mass / m_suppress); break; } @@ -1258,7 +1266,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( /* Suppresses accretion by factor accounting for mass * lost in outflow over accretion time. ODE: * dM/dt = -eta * sf_eff * M / tdyn */ - torque_accr_rate *= exp(-eta * sf_eff); + f_suppress = exp(-eta * sf_eff); // message("BH_SUPPRESS: z=%g id=%lld M*=%g eta=%g eff=%g tfac=%g // fsupp=%g", cosmo->z, bp->id, galaxy_stellar_mass * // props->mass_to_solar_mass, eta, sf_eff, t_accrete * tdyn_inv, @@ -1272,6 +1280,9 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( } } + /* Apply suppression factor to torque accretion */ + torque_accr_rate *= f_suppress; + #ifdef OBSIDIAN_DEBUG_CHECKS if (isnan(bondi_accr_rate)) error("bondi_accr_rate nan"); if (isnan(torque_accr_rate)) error("torque_accr_rate nan"); @@ -1518,8 +1529,7 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( if (bp->state == BH_states_adaf) bp->v_kick = 0.f; #ifdef OBSIDIAN_DEBUG_CHECKS - // MATTHIEU: TODO: FIX THIS! - float galaxy_sfr = 0.; // fof_props->group_star_formation_rate[group_id]; + const float galaxy_sfr = bp->galaxy_data.stellar_mass * bp->galaxy_data.specific_sfr; tdyn_inv = (tdyn_inv > 0.f) ? tdyn_inv : FLT_MIN; message( "BH_ACC: z=%g bid=%lld ms=%g dms=%g sfr=%g mbh=%g dmbh=%g state=%d " @@ -1560,32 +1570,40 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( #define OBSIDIAN_BH_DETAILS #ifdef OBSIDIAN_BH_DETAILS + const float galaxy_sfr = bp->galaxy_data.stellar_mass * bp->galaxy_data.specific_sfr; printf( "BH_DETAILS " - "z=%2.12f bid=%lld " - " Mdyn=%g MBH=%g Mres=%g BHAR=%g Bondi=%g torque=%g dt=%g " - " nH=%g T=%g SFR=%g mgas=%g " - " mhot=%g m*=%g mgbulge=%g msbulge=%g N/A=%g " + "z=%2.12f bid=%lld galM*=%g galSFR=%g" + " Mdyn=%g MBH=%g h=%g Mres=%g BHAR=%g Bondi=%g torque=%g dt=%g dM=%g" + " nH=%g Thot=%g SFR=%g mngb=%g " + " mhot=%g mcold=%g m*=%g mdisk=%g mcorot=%g " " x=%2.10f y=%2.10f z=%2.10f " " vx=%2.7f vy=%2.7f vz=%2.7f " - " Lx=%g Ly=%g Lz=%g Lx*=%g Ly*=%g Lz*=%g" + " Lgasx=%g Lgasy=%g Lgasz=%g Lbhx=%g Lbhy=%g Lbhz=%g" " Lrad=%g state=%d facc=%g eff=%g" - " fedd=%g madaf=%g mngb=%g\n", - cosmo->z, bp->id, bp->mass * props->mass_to_solar_mass, + " fedd=%g madaf=%g mngb=%g tdyn=%g fsupp=%g\n", + cosmo->z, bp->id, + galaxy_mstar * props->mass_to_solar_mass, + galaxy_sfr * props->mass_to_solar_mass / props->time_to_yr, + bp->mass * props->mass_to_solar_mass, bp->subgrid_mass * props->mass_to_solar_mass, + bp->h * cosmo->a * props->length_to_parsec / 1.0e3f, bp->jet_mass_reservoir * props->mass_to_solar_mass, bp->accretion_rate * props->mass_to_solar_mass / props->time_to_yr, - Bondi_rate * props->mass_to_solar_mass / props->time_to_yr, - torque_accr_rate * props->mass_to_solar_mass / props->time_to_yr, + bp->bondi_accretion_rate * props->mass_to_solar_mass / props->time_to_yr, + (bp->accretion_rate - bp->bondi_accretion_rate) * props->mass_to_solar_mass / props->time_to_yr, dt * props->time_to_Myr, + bp->accretion_rate * dt * props->mass_to_solar_mass, (bp->rho_gas * cosmo->a3_inv) * props->rho_to_n_cgs, bp->hot_gas_internal_energy * cosmo->a_factor_internal_energy / (props->T_K_to_int * props->temp_to_u_factor), bp->gas_SFR * props->mass_to_solar_mass / props->time_to_yr, bp->ngb_mass * props->mass_to_solar_mass, bp->hot_gas_mass * props->mass_to_solar_mass, - bp->stellar_mass * props->mass_to_solar_mass, 0.f /* Mgas,bulge */, - bp->stellar_bulge_mass * props->mass_to_solar_mass, 0.f, + bp->cold_gas_mass * props->mass_to_solar_mass, + bp->stellar_mass * props->mass_to_solar_mass, + bp->cold_disk_mass * props->mass_to_solar_mass, + (bp->cold_gas_mass - 2. * (bp->cold_gas_mass - bp->cold_disk_mass)) * props->mass_to_solar_mass, bp->x[0] * cosmo->a * props->length_to_parsec / 1.0e3f, bp->x[1] * cosmo->a * props->length_to_parsec / 1.0e3f, bp->x[2] * cosmo->a * props->length_to_parsec / 1.0e3f, @@ -1594,13 +1612,13 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( bp->v[2] * cosmo->a_inv / props->kms_to_internal, bp->angular_momentum_gas[0], bp->angular_momentum_gas[1], bp->angular_momentum_gas[2], - 0.f, /* specific angular momentum of the stars */ - 0.f, /* specific angular momentum of the stars */ - 0.f, /* specific angular momentum of the stars */ + bp->swallowed_angular_momentum[0], bp->swallowed_angular_momentum[1], + bp->swallowed_angular_momentum[2], bp->radiative_luminosity * props->conv_factor_energy_rate_to_cgs, bp->state, bp->f_accretion, bp->radiative_efficiency, bp->eddington_fraction, my_adaf_mass_limit * props->mass_to_solar_mass, - bp->gravitational_ngb_mass * props->mass_to_solar_mass); + bp->gravitational_ngb_mass * props->mass_to_solar_mass, + 1.f / tdyn_inv * 1.e-6 * props->time_to_yr, f_suppress); #endif } diff --git a/src/black_holes/Obsidian/black_holes_iact.h b/src/black_holes/Obsidian/black_holes_iact.h index bfc8b34c9a..770705bb97 100644 --- a/src/black_holes/Obsidian/black_holes_iact.h +++ b/src/black_holes/Obsidian/black_holes_iact.h @@ -107,6 +107,36 @@ black_hole_set_kick_direction(const struct bpart *bi, const struct part *pj, return kick_dir; } +/** + * @brief Categorise gas temperature as hot or cold or neither for BH accretion + * + * @param bi First particle (black hole). + * @param pj Second particle (gas, not updated). + * @param cosmo The cosmological model. + * @param bh_props The properties of the BH scheme + */ +__attribute__((always_inline)) INLINE static int +black_hole_gas_hot_or_cold(const struct bpart *bi, const struct part *pj, + const struct cosmology *cosmo, const struct black_holes_props *bh_props) +{ + /* Neighbour internal energy */ + const float uj = hydro_get_drifted_comoving_internal_energy(pj); + + /* Determine if it is hot or cold gas surrounding the SMBH */ + int gas_state = 0; + const float Tj = + uj * cosmo->a_factor_internal_energy / bh_props->temp_to_u_factor; + if (Tj > bh_props->environment_temperature_cut && pj->sf_data.SFR <= 0.f) { + gas_state = 1; + } + /* Cold gas must be cold, SF'ing, and in same galaxy as BH */ + if (Tj < bh_props->cold_gas_temperature_cut && pj->sf_data.SFR > 0. && bi->galaxy_data.stellar_mass == pj->galaxy_data.stellar_mass) { + gas_state = -1; + } + + return gas_state; +} + /** * @brief Density interaction between two particles (non-symmetric). * @@ -182,36 +212,22 @@ runner_iact_nonsym_bh_gas_density( const float dv[3] = {pj->v[0] - bi->v[0], pj->v[1] - bi->v[1], pj->v[2] - bi->v[2]}; - /* Account for hot and cold gas surrounding the SMBH */ - int is_hot_gas = 0; - const float Tj = - uj * cosmo->a_factor_internal_energy / bh_props->temp_to_u_factor; - /*const float rho_crit_0 = cosmo->critical_density_0; - const float rho_crit_baryon = cosmo->Omega_b * rho_crit_0; - const double rho_com = hydro_get_comoving_density(pj); - const double rho_phys = hydro_get_physical_density(pj, cosmo); - const float T_EoS = entropy_floor_temperature(pj, cosmo, floor_props); - if ((rho_com >= rho_crit_baryon * floor_props->Jeans_over_density_threshold) - && (rho_phys >= floor_props->Jeans_density_threshold)) { - if (Tj > T_EoS * bh_props->fixed_T_above_EoS_factor) { - is_hot_gas = 1; - } - } - else { - if (Tj > bh_props->environment_temperature_cut) { - is_hot_gas = 1; - } - }*/ - if (Tj > bh_props->environment_temperature_cut && pj->sf_data.SFR <= 0.f) { - is_hot_gas = 1; - } + /* Classify gas as hot or cold for accretion */ + const int gas_temperature_state = black_hole_gas_hot_or_cold(bi, pj, cosmo, bh_props); - if (is_hot_gas) { + if (gas_temperature_state == 1) { bi->hot_gas_mass += mj; bi->hot_gas_internal_energy += mj * uj; /* Not kernel weighted */ - } else { + } + else if (gas_temperature_state == -1) { +#if COOLING_GRACKLE_MODE >= 2 + /* With subgrid ISM model, only allow cold component to be accreted */ + bi->cold_gas_mass += mj * pj->cooling_data.subgrid_fcold; +#else bi->cold_gas_mass += mj; +#endif bi->gas_SFR += max(pj->sf_data.SFR, 0.); + //if (bi->subgrid_mass * bh_props->mass_to_solar_mass > 1.e10) message("BH_SFR bid=%lld pid=%lld mj=%g psfr=%g nH=%g T=%g fH2=%g totSFR=%g", bi->id, pj->id, mj * bh_props->mass_to_solar_mass, max(pj->sf_data.SFR, 0.) * bh_props->mass_to_solar_mass / bh_props->time_to_yr, pj->rho * bh_props->conv_factor_density_to_cgs / 1.673e-24, pj->u * cosmo->a_factor_internal_energy / (bh_props->T_K_to_int * bh_props->temp_to_u_factor), pj->sf_data.H2_fraction, bi->gas_SFR * bh_props->mass_to_solar_mass / bh_props->time_to_yr); } const float L_x = mj * (dx[1] * dv[2] - dx[2] * dv[1]); @@ -422,33 +438,31 @@ runner_iact_nonsym_bh_gas_swallow( const float ui = r * hi_inv; kernel_eval(ui, &wi); - /* Sum up cold disk mass corotating relative to total angular momentum. */ - /* Neighbour internal energy */ - const float uj = hydro_get_drifted_comoving_internal_energy(pj); + /* Sum up cold disk mass corotating relative to total angular momentum. + * This can't be used to compute accretion since it is not summable + * prior to this swallow routine; we need an intermediate loop + * if we want to use this to set accretion ala Simba.*/ + const float mj = hydro_get_mass(pj); - /* Identify gas surrounding the SMBH as hot or cold */ - const float Tj = - uj * cosmo->a_factor_internal_energy / bh_props->temp_to_u_factor; - int is_hot_gas = 0; - if (Tj > bh_props->environment_temperature_cut && pj->sf_data.SFR <= 0.) { - is_hot_gas = 1; - } + /* Classify gas as hot or cold for accretion */ + const int gas_temperature_state = black_hole_gas_hot_or_cold(bi, pj, cosmo, bh_props); - /* Compute gas angular momentum around BH */ const float Lx = mj * (dx[1] * dv[2] - dx[2] * dv[1]); const float Ly = mj * (dx[2] * dv[0] - dx[0] * dv[2]); const float Lz = mj * (dx[0] * dv[1] - dx[1] * dv[0]); const float proj = Lx * bi->angular_momentum_gas[0] + Ly * bi->angular_momentum_gas[1] + Lz * bi->angular_momentum_gas[2]; - if ((proj > 0.f) && (is_hot_gas == 0)) { + if ((proj > 0.f) && gas_temperature_state == -1) { bi->cold_disk_mass += mj; } /* Probability to swallow this particle */ float prob = -1.f; float f_accretion = bi->f_accretion; + + /* No accretion? Nothing to do */ if (f_accretion <= 0.f) return; const float pj_mass_orig = mj; @@ -836,9 +850,9 @@ runner_iact_nonsym_bh_bh_swallow(const float r2, const float dx[3], (bj->merger_data.swallow_mass == bi->subgrid_mass && bj->merger_data.swallow_id < bi->id)) { -#ifdef SWIFT_DEBUG_CHECKS - message("BH %lld wants to swallow BH particle %lld", bi->id, bj->id); -#endif +//#ifdef SWIFT_DEBUG_CHECKS + if (bi->mass * bh_props->mass_to_solar_mass > 1.e9) message("BH_MERGE bid=%lld to swallow bid=%lld: MBHi=%g MBHj=%g Mgali=%g Mgalj=%g", bi->id, bj->id, bi->subgrid_mass * bh_props->mass_to_solar_mass, bj->subgrid_mass * bh_props->mass_to_solar_mass, bi->galaxy_data.stellar_mass * bh_props->mass_to_solar_mass, bj->galaxy_data.stellar_mass * bh_props->mass_to_solar_mass); +//#endif bj->merger_data.swallow_id = bi->id; bj->merger_data.swallow_mass = bi->subgrid_mass; @@ -1054,6 +1068,13 @@ runner_iact_nonsym_bh_gas_feedback( hydro_set_physical_internal_energy(pj, xpj, cosmo, u_new); hydro_set_drifted_physical_internal_energy(pj, cosmo, NULL, u_new); +#if COOLING_GRACKLE_MODE >= 2 + /* Take AGN-heated gas out of subgrid ISM mode */ + pj->cooling_data.subgrid_temp = 0.f; + pj->cooling_data.subgrid_dens = hydro_get_physical_density(pj, cosmo); + pj->cooling_data.subgrid_fcold = 0.f; +#endif + /* Shut off cooling for some time, if desired */ if (bh_props->adaf_cooling_shutoff_factor > 0.f) { diff --git a/src/black_holes/Obsidian/black_holes_properties.h b/src/black_holes/Obsidian/black_holes_properties.h index 940c635bc6..a8d59ac100 100644 --- a/src/black_holes/Obsidian/black_holes_properties.h +++ b/src/black_holes/Obsidian/black_holes_properties.h @@ -121,6 +121,9 @@ struct black_holes_props { /*! Where do we distinguish between hot gas for Bondi? */ float environment_temperature_cut; + /*! Where do we distinguish between cold gas for torque accretion? */ + float cold_gas_temperature_cut; + /*! Number of dynamical times over which gas is accreted from accretion disk */ float dynamical_time_factor; @@ -561,7 +564,12 @@ INLINE static void black_holes_props_init(struct black_holes_props *bp, bp->environment_temperature_cut = parser_get_opt_param_float( params, "ObsidianAGN:environment_temperature_cut_K", 1.0e5f); + + bp->cold_gas_temperature_cut = parser_get_opt_param_float( + params, "ObsidianAGN:cold_gas_temperature_cut_K", bp->environment_temperature_cut); + bp->environment_temperature_cut *= T_K_to_int; + bp->cold_gas_temperature_cut *= T_K_to_int; bp->dynamical_time_factor = parser_get_opt_param_float( params, "ObsidianAGN:dynamical_time_factor", 1.f); diff --git a/src/feedback/KIARA/feedback.h b/src/feedback/KIARA/feedback.h index 038e93fe7d..f3c809d3d1 100644 --- a/src/feedback/KIARA/feedback.h +++ b/src/feedback/KIARA/feedback.h @@ -72,6 +72,9 @@ __attribute__((always_inline)) INLINE static void feedback_recouple_set_flags( p->cooling_data.subgrid_temp = 0.f; p->cooling_data.subgrid_dens = hydro_get_physical_density(p, cosmo); p->cooling_data.subgrid_fcold = 0.f; + + /* Make sure to sync the newly coupled part on the timeline */ + timestep_sync_part(p); } /** diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index e21309e4f2..dbc93b162b 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -1429,9 +1429,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( const float a_inv2 = cosmo->a2_inv; p->density.div_v *= h_inv_dim_plus_one * rho_inv * a_inv2; p->density.div_v += cosmo->H * hydro_dimension; +/** } -/** * @brief Prepare a particle for the gradient calculation. * * This function is called after the density loop and before the gradient loop. @@ -1446,17 +1446,16 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( * @param cosmo The cosmological model. * @param hydro_props Hydrodynamic properties. * @param pressure_floor The properties of the pressure floor. - */ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct pressure_floor_props *pressure_floor) { - /* Some smoothing length multiples. */ const float h = p->h; - const float h_inv = 1.0f / h; /* 1/h */ - const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ - const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + const float h_inv = 1.0f / h; + const float h_inv_dim = pow_dimension(h_inv); + const float h_inv_dim_plus_one = h_inv_dim * h_inv; + */ /* Need this for correct dh/dt */ p->gradients.wcount = p->density.wcount; @@ -1595,9 +1594,9 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( /* Finish matrix and volume computations for FVPM Radiative Transfer */ fvpm_compute_volume_and_matrix(p, h_inv_dim); -/** } +/** * @brief Prepare a particle for the gradient calculation. * * This function is called after the density loop and before the gradient loop. @@ -1612,11 +1611,11 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( * @param cosmo The cosmological model. * @param hydro_props Hydrodynamic properties. * @param pressure_floor The properties of the pressure floor. + */ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( struct part *restrict p, struct xpart *restrict xp, const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct pressure_floor_props *pressure_floor) { - */ #ifdef hydro_props_use_adiabatic_correction /* Prepare the denominator for the adiabatic correction term */ From 76b4997c225ea9a5bebb3673c48e37ba52f26519 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Fri, 9 Jan 2026 04:26:46 +0000 Subject: [PATCH 34/37] Fix BH seeding in FOF_GALAXIES case where we use stellar (not halo) mass as the limit --- src/fof.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/fof.c b/src/fof.c index 4d33abba10..52b558c214 100644 --- a/src/fof.c +++ b/src/fof.c @@ -2658,7 +2658,12 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, centre_of_mass[index * 3 + 2] += gparts[i].mass * x[2]; /* Should we seed a BH in this group? */ - if (!has_black_hole[index] && group_mass[index] > seed_halo_mass) { + float group_mass_for_seeding = group_mass[index]; +#ifdef WITH_FOF_GALAXIES + /* In Kiara this is actually a limit on stellar mass not halo mass */ + group_mass_for_seeding = stellar_mass[index]; +#endif + if (!has_black_hole[index] && group_mass_for_seeding > seed_halo_mass) { /* Is this a gas particle? */ if (gparts[i].type == swift_type_gas) { @@ -2823,6 +2828,9 @@ void fof_seed_black_holes(const struct fof_props *props, /* Direct pointers to the arrays */ double *group_mass = props->group_mass; +#ifdef WITH_FOF_GALAXIES + float *stellar_mass = props->group_stellar_mass; +#endif char *has_black_hole = props->has_black_hole; float *max_part_density = props->max_part_density; @@ -2850,7 +2858,11 @@ void fof_seed_black_holes(const struct fof_props *props, const size_t index = gparts[i].fof_data.group_id - 1; /* Should we seed a BH in this group? */ - if (!has_black_hole[index] && group_mass[index] > seed_halo_mass) { + float group_mass_for_seeding = group_mass[index]; +#ifdef WITH_FOF_GALAXIES + group_mass_for_seeding = stellar_mass[index]; +#endif + if (!has_black_hole[index] && group_mass_for_seeding > seed_halo_mass) { /* Does it match the max density for this group? * (i.e. is it the particle we identified as the one to convert?) */ From 9f062f95831d095a59cd829b091613c85e6ec880 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Wed, 11 Mar 2026 11:33:09 +0000 Subject: [PATCH 35/37] Fix configure bug; add G0 options; turn off BH torque accr when far from centre --- configure.ac | 1 + src/black_holes/Obsidian/black_holes.h | 10 ++--- src/cooling/KIARA/cooling.c | 55 +++++++++++++++-------- src/cooling/KIARA/cooling_io.h | 5 ++- src/cooling/KIARA/cooling_properties.h | 7 ++- src/engine_unskip.c | 4 +- src/fof.c | 6 +-- src/star_formation/KIARA/star_formation.h | 7 +-- 8 files changed, 60 insertions(+), 35 deletions(-) diff --git a/configure.ac b/configure.ac index b7fc7414e4..8bf8e293bd 100644 --- a/configure.ac +++ b/configure.ac @@ -365,6 +365,7 @@ AC_ARG_ENABLE([obsidian-debugging-checks], ) if test "$enable_obsidian_debugging_checks" = "yes"; then AC_DEFINE([OBSIDIAN_DEBUG_CHECKS],1,[Enable Obsidian expensive debugging]) +fi AC_ARG_ENABLE([likwid], [AS_HELP_STRING([--enable-likwid], diff --git a/src/black_holes/Obsidian/black_holes.h b/src/black_holes/Obsidian/black_holes.h index 3fe2767aad..8e369713ae 100644 --- a/src/black_holes/Obsidian/black_holes.h +++ b/src/black_holes/Obsidian/black_holes.h @@ -1302,12 +1302,12 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( torque_accr_rate *= f_suppress; /* If not near galaxy center, torque accr is suppressed */ - //const float suppression_distance = kernel_gravity_softening_plummer_equivalent_inv * - // props->max_reposition_distance_ratio * bp->h; - //if (bp->galactocentric_radius > suppression_distance) { + const float suppression_distance = kernel_gravity_softening_plummer_equivalent_inv * + props->max_reposition_distance_ratio * bp->h; + if (bp->galactocentric_radius > suppression_distance) { //torque_accr_rate *= exp(-(bp->galactocentric_radius - suppression_distance) / suppression_distance); - //torque_accr_rate = 0.f; - //} + torque_accr_rate = 0.f; + } #ifdef OBSIDIAN_DEBUG_CHECKS if (isnan(bondi_accr_rate)) error("bondi_accr_rate nan"); diff --git a/src/cooling/KIARA/cooling.c b/src/cooling/KIARA/cooling.c index 9622cea8ca..66475f7c4f 100644 --- a/src/cooling/KIARA/cooling.c +++ b/src/cooling/KIARA/cooling.c @@ -247,7 +247,6 @@ cooling_compute_self_shielding(const struct part *restrict p, /* Compute self-shielding from H */ const float a = cooling->units.a_value; const float a3_inv = 1.f / (a * a * a); - // const double r_in_cm = p->h * a * cooling->units.length_units; const double rho_grad_norm2 = p->rho_gradient[0] * p->rho_gradient[0] + p->rho_gradient[1] * p->rho_gradient[1] + p->rho_gradient[2] * p->rho_gradient[2]; @@ -267,17 +266,16 @@ cooling_compute_self_shielding(const struct part *restrict p, const double xH = NH_cgs * 3.50877e-24; const double fH_shield = pow(1.f + xH, -1.62) * exp(-0.149 * xH); - /* Extra self-shielding from H2 if present - DON'T DO THIS HERE SINCE IT IS - IN CRACKLE const float fH2 = p->sf_data.H2_fraction; if (fH2 > 0.f) { const - double NH2_cgs = fH2 * NH_cgs; const double DH2_cgs = 1.e-5 * - sqrt(2.*1.38e-16 * T_ism * 2.98864e23); const double xH2 = NH2_cgs - * 1.18133e-14; fH2_shield = 0.9379 * pow(1.f + xH2 / DH2_cgs, -1.879) + - 0.03465 * pow(1.f + xH2, -0.473) * exp(-2.293e-4 * sqrt(1.f + xH2)); - message("G0 shield: r=%g xH2=%g NH2=%g fH2sh=%g term1=%g term2=%g - term3=%g\n",r_in_cm/3.086e21, xH2, NH2_cgs, fH2_shield, pow(1.f + xH2 / - DH2_cgs, -1.879), pow(1.f + xH2, -0.473), exp(-2.293e-4 * sqrt(1.f + xH2))); - }*/ fH2_shield *= fH_shield; + /* Extra self-shielding from H2 if present - DON'T DO THIS HERE SINCE IT IS IN CRACKLE + const float fH2 = p->sf_data.H2_fraction; + if (fH2 > 0.f) { + const double NH2_cgs = fH2 * NH_cgs; + const double DH2_cgs = 1.e-5 * sqrt(2.*1.38e-16 * T_ism * 2.98864e23); + const double xH2 = NH2_cgs * 1.18133e-14; + fH2_shield *= 0.9379 * pow(1.f + xH2 / DH2_cgs, -1.879) + + 0.03465 * pow(1.f + xH2, -0.473) * exp(-2.293e-4 * sqrt(1.f + xH2)); + } */ } #endif @@ -303,25 +301,40 @@ __attribute__((always_inline)) INLINE static float cooling_compute_G0( /* Determine ISRF in Habing units based on chosen method */ if (cooling->G0_computation_method == 0) { G0 = 0.f; - } else if (cooling->G0_computation_method == 1) { + } + else if (cooling->G0_computation_method == 1) { fH2_shield = cooling_compute_self_shielding(p, cooling); G0 = fH2_shield * p->chemistry_data.local_sfr_density * cooling->G0_factor1; - } else if (cooling->G0_computation_method == 2) { + } + else if (cooling->G0_computation_method == 2) { G0 = ssfr * cooling->G0_factor2; - } else if (cooling->G0_computation_method == 3) { + } + else if (cooling->G0_computation_method == 3) { if (ssfr > 0.) { G0 = ssfr * cooling->G0_factor2; - } else { + } + else { + fH2_shield = cooling_compute_self_shielding(p, cooling); + G0 = fH2_shield * p->chemistry_data.local_sfr_density * + cooling->G0_factor1; + } + } + else if (cooling->G0_computation_method == -3) { + if (p->chemistry_data.local_sfr_density > 0.) { fH2_shield = cooling_compute_self_shielding(p, cooling); G0 = fH2_shield * p->chemistry_data.local_sfr_density * cooling->G0_factor1; + } + else { + G0 = ssfr * cooling->G0_factor2; } } #if COOLING_GRACKLE_MODE >= 2 else if (cooling->G0_computation_method == 4) { /* Remember SNe_ThisTimeStep stores SN **rate** */ G0 = p->cooling_data.SNe_ThisTimeStep * cooling->G0_factorSNe * dt; - } else if (cooling->G0_computation_method == 5) { + } + else if (cooling->G0_computation_method == 5) { float pssfr = max(p->sf_data.SFR, 0.f); pssfr /= max(mstar, 8. * p->mass); G0 = max(ssfr, pssfr) * cooling->G0_factor2 + @@ -333,17 +346,21 @@ __attribute__((always_inline)) INLINE static float cooling_compute_G0( cooling->G0_computation_method); } - /*if (p->galaxy_mstar * 1.e10 > 1.e9 && p->id % 10000 == 0) { - message("G0: id=%lld M*=%g SFR=%g rho_sfr=%g Td=%g fshield=%g G0=%g", + /* Scale G0 by user-input value */ + G0 *= cooling->G0_multiplier; + + if (mstar * 1.e10 > 1.e9 && p->id % 100000 == 0 && p->chemistry_data.local_sfr_density > 0) { + message("G0: id=%lld M*=%g SFR=%g rho_sfr=%g SNe=%g Td=%g fshield=%g G0=%g", p->id, mstar * 1.e10, mstar * 1.e10 * ssfr / (1.e6 * cooling->time_to_Myr), p->chemistry_data.local_sfr_density * 0.002 / 1.6, + p->cooling_data.SNe_ThisTimeStep, p->cooling_data.dust_temperature, fH2_shield, G0); - }*/ + } return G0; } diff --git a/src/cooling/KIARA/cooling_io.h b/src/cooling/KIARA/cooling_io.h index 78eb0c19ae..fd390fb252 100644 --- a/src/cooling/KIARA/cooling_io.h +++ b/src/cooling/KIARA/cooling_io.h @@ -331,9 +331,12 @@ __attribute__((always_inline)) INLINE static void cooling_read_parameters( cooling->cold_ISM_frac = parser_get_opt_param_double( parameter_file, "KIARACooling:cold_ISM_frac", 1.0); - cooling->G0_computation_method = parser_get_opt_param_double( + cooling->G0_computation_method = parser_get_opt_param_int( parameter_file, "KIARACooling:G0_computation_method", 3); + cooling->G0_multiplier = parser_get_opt_param_double( + parameter_file, "KIARACooling:G0_multiplier", 1.0); + cooling->max_subgrid_density = parser_get_opt_param_double( parameter_file, "KIARACooling:max_subgrid_density_g_p_cm3", FLT_MAX); /* convert to internal units */ diff --git a/src/cooling/KIARA/cooling_properties.h b/src/cooling/KIARA/cooling_properties.h index ccd9937fcc..1a497547cf 100644 --- a/src/cooling/KIARA/cooling_properties.h +++ b/src/cooling/KIARA/cooling_properties.h @@ -117,10 +117,13 @@ struct cooling_function_data { /*! For subgrid model (eg KIARA) need a subgrid ISM fraction */ double cold_ISM_frac; - /*! For Grackle subgrid model, choose way to determine G0: 1=Local SFR - * density; 2=Global sSFR */ + /*! For Grackle subgrid model, choose way to determine G0: 1=Local SFR density; + * 2=Global sSFR; 3=2 if sSFR != 0, else 1; -3: vice versa */ int G0_computation_method; + /*! For Grackle subgrid model, arbitrary multiplier for G0 */ + double G0_multiplier; + /*! For Grackle subgrid model, set max density to avoid pointlessly * over-iterating in Grackle */ double max_subgrid_density; diff --git a/src/engine_unskip.c b/src/engine_unskip.c index f2e3feba67..37c5b927a7 100644 --- a/src/engine_unskip.c +++ b/src/engine_unskip.c @@ -225,10 +225,10 @@ static void engine_do_unskip_gravity(struct cell *c, struct engine *e) { if (!cell_is_active_gravity(c, e)) return; /* At the top level we need to recursively check particles haven't moved - * too far for the mesh gravity (if using the mesh). */ + * too far for the mesh gravity (if using the mesh). if (c->depth == 0 && e->s->periodic) { cell_check_grav_mesh_pairs(c, e); - } + }*/ /* Recurse */ if (c->split && ((c->maxdepth - c->depth) >= space_subdepth_diff_grav)) { diff --git a/src/fof.c b/src/fof.c index 6437b1afaf..60d605fade 100644 --- a/src/fof.c +++ b/src/fof.c @@ -2783,19 +2783,19 @@ void fof_calc_group_mass(struct fof_props *props, const struct space *s, struct part *restrict p = &(s->parts[-gp->id_or_neg_offset]); p->galaxy_data.stellar_mass = stellar_mass[index]; p->galaxy_data.gas_mass = gas_mass[index]; - p->galaxy_data.specific_sfr = + if (stellar_mass[index] > 0.f) p->galaxy_data.specific_sfr = star_formation_rate[index] / stellar_mass[index]; } else if (gp->type == swift_type_stars) { struct spart *restrict sp = &(s->sparts[-gp->id_or_neg_offset]); sp->galaxy_data.stellar_mass = stellar_mass[index]; sp->galaxy_data.gas_mass = gas_mass[index]; - sp->galaxy_data.specific_sfr = + if (stellar_mass[index] > 0.f) sp->galaxy_data.specific_sfr = star_formation_rate[index] / stellar_mass[index]; } else if (gp->type == swift_type_black_hole) { struct bpart *restrict bp = &(s->bparts[-gp->id_or_neg_offset]); bp->galaxy_data.stellar_mass = stellar_mass[index]; bp->galaxy_data.gas_mass = gas_mass[index]; - bp->galaxy_data.specific_sfr = + if (stellar_mass[index] > 0.f) bp->galaxy_data.specific_sfr = star_formation_rate[index] / stellar_mass[index]; bp->galactocentric_radius = r; } diff --git a/src/star_formation/KIARA/star_formation.h b/src/star_formation/KIARA/star_formation.h index aa0a6d3aff..78d9859711 100644 --- a/src/star_formation/KIARA/star_formation.h +++ b/src/star_formation/KIARA/star_formation.h @@ -407,11 +407,12 @@ INLINE static void star_formation_compute_SFR_wn07( const double fc = 0.5 * erfc(z); /* This is the SFR density from eq. 17, except use actual - * density rho_V not estimated density rho_c + * density rho_V not estimated density rho_c. 3pi/32=0.294524. */ - const double rhosfr = epsc * sqrt(phys_const->const_newton_G * rho_V) * fc; + const double rhosfr = epsc * sqrt(0.294524 * phys_const->const_newton_G * rho_V) * fc; - /* multiply by dense gas effective volume to get SFR */ + /* multiply by dense gas effective volume to get SFR (rho_V appears in both + * this eqn and previous one so it is cancelled out for efficiency) */ const double sfr = rhosfr * (p->cooling_data.subgrid_fcold * hydro_get_mass(p)); From eb856a5c1de23902bf59ef0af327e89a2470e330 Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Tue, 17 Mar 2026 14:26:16 +0000 Subject: [PATCH 36/37] Fix T output; add volume correction for RT --- src/cooling/KIARA/cooling_io.h | 6 +++--- src/rt/KIARA/rt.h | 16 +++++++++++++++- src/rt/KIARA/rt_iact.h | 2 +- src/rt/KIARA/rt_struct.h | 3 +++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/cooling/KIARA/cooling_io.h b/src/cooling/KIARA/cooling_io.h index fd390fb252..10bcdd020f 100644 --- a/src/cooling/KIARA/cooling_io.h +++ b/src/cooling/KIARA/cooling_io.h @@ -114,9 +114,9 @@ INLINE static void convert_part_e_density(const struct engine *e, INLINE static void convert_part_T(const struct engine *e, const struct part *p, const struct xpart *xp, float *ret) { - *ret = cooling_get_temperature(e->physical_constants, e->hydro_properties, - e->internal_units, e->cosmology, - e->cooling_func, p, xp); + const float u = hydro_get_physical_internal_energy(p, xp, e->cosmology); + const float ne = xp->cooling_data.e_frac; + *ret = cooling_convert_u_to_temp(u, ne, e->cooling_func, p); } #ifdef RT_NONE diff --git a/src/rt/KIARA/rt.h b/src/rt/KIARA/rt.h index 5bdb517406..5a051f8f45 100644 --- a/src/rt/KIARA/rt.h +++ b/src/rt/KIARA/rt.h @@ -118,7 +118,19 @@ rt_compute_stellar_emission_rate(struct spart* restrict sp, double time, * @param p Particle to work on */ __attribute__((always_inline)) INLINE static void rt_init_part( - struct part* restrict p) {} + struct part* restrict p) { + + /* Correct the energy densities and fluxes for change in volume */ + float h_ratio = p->rt_data.h_previous / p->h; + const float h_ratio2 = h_ratio * h_ratio; + for (int g = 0; g < RT_NGROUPS; g++) { + p->rt_data.radiation[g].energy_density *= h_ratio * h_ratio2; + p->rt_data.radiation[g].flux[0] *= h_ratio2; + p->rt_data.radiation[g].flux[1] *= h_ratio2; + p->rt_data.radiation[g].flux[2] *= h_ratio2; + } + p->rt_data.h_previous = p->h; +} /** * @brief Reset the RT hydro particle data not related to the hydro density. @@ -184,6 +196,8 @@ __attribute__((always_inline)) INLINE static void rt_first_init_part( #ifdef SWIFT_RT_DEBUG_CHECKS p->rt_data.debug_radiation_absorbed_tot = 0ULL; #endif + + p->rt_data.h_previous = p->h; } /** diff --git a/src/rt/KIARA/rt_iact.h b/src/rt/KIARA/rt_iact.h index 0ea5e69a07..840238320b 100644 --- a/src/rt/KIARA/rt_iact.h +++ b/src/rt/KIARA/rt_iact.h @@ -169,7 +169,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject( si->rt_data.emission_this_step[g] * weight * Vinv; pj->rt_data.radiation[g].energy_density += injected_energy_density; if (pj->rt_data.radiation[g].energy_density > 0.f && g==0) { - message("RT_energy_inj: z=%g sid=%lld pid=%lld RTgrp=%d w=%g Vinv=%g Reff=%g h=%g Enew=%g Eold=%g frac=%g\n", 1./a-1., si->id, pj->id, g, weight, Vinv, pow(3./(4.*M_PI*Vinv), 1./3), pj->h, injected_energy_density, pj->rt_data.radiation[g].energy_density-injected_energy_density, injected_energy_density/pj->rt_data.radiation[g].energy_density); + //message("RT_energy_inj: z=%g sid=%lld pid=%lld RTgrp=%d w=%g Vinv=%g Reff=%g h=%g Enew=%g Eold=%g frac=%g\n", 1./a-1., si->id, pj->id, g, weight, Vinv, pow(3./(4.*M_PI*Vinv), 1./3), pj->h, injected_energy_density, pj->rt_data.radiation[g].energy_density-injected_energy_density, injected_energy_density/pj->rt_data.radiation[g].energy_density); } /* Don't inject flux. */ } diff --git a/src/rt/KIARA/rt_struct.h b/src/rt/KIARA/rt_struct.h index 250c9a51b7..15abe248d2 100644 --- a/src/rt/KIARA/rt_struct.h +++ b/src/rt/KIARA/rt_struct.h @@ -42,6 +42,9 @@ struct rt_part_data { /* Particle RT time step. */ float flux_dt; + /* Smoothing length from previous timestep */ + float h_previous; + /* gradients of the radiation state. */ /* for the flux[3][3] quantity: * first index: x, y, z coordinate of the flux. From 95bca10b0aa7cb739e62f7864bbc38192f7f9e8e Mon Sep 17 00:00:00 2001 From: Romeel Dave Date: Tue, 17 Mar 2026 14:28:11 +0000 Subject: [PATCH 37/37] Fix options on v_kick for quasar/slim disk mode --- src/black_holes/Obsidian/black_holes.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/black_holes/Obsidian/black_holes.h b/src/black_holes/Obsidian/black_holes.h index 8e369713ae..ba62d49a31 100644 --- a/src/black_holes/Obsidian/black_holes.h +++ b/src/black_holes/Obsidian/black_holes.h @@ -164,7 +164,7 @@ __attribute__((always_inline)) INLINE static double get_black_hole_wind_speed( props->minimum_black_hole_mass_v_kick * props->mass_to_solar_mass; const float dlog10_BH_mass = log10f(subgrid_mass_Msun) - log10f(min_BH_mass_Msun); - v_kick = 500.f + (500.f / 3.f) * dlog10_BH_mass; + v_kick = fabs(props->quasar_wind_speed) + (fabs(props->slim_disk_wind_speed) / 3.f) * dlog10_BH_mass; /* Sometimes can get very small leading to huge mass loadings */ if (v_kick < props->minimum_v_kick_km_s) { @@ -1301,13 +1301,13 @@ __attribute__((always_inline)) INLINE static void black_holes_prepare_feedback( /* Apply suppression factor to torque accretion */ torque_accr_rate *= f_suppress; - /* If not near galaxy center, torque accr is suppressed */ + /* If not near galaxy center, torque accr is suppressed const float suppression_distance = kernel_gravity_softening_plummer_equivalent_inv * props->max_reposition_distance_ratio * bp->h; if (bp->galactocentric_radius > suppression_distance) { - //torque_accr_rate *= exp(-(bp->galactocentric_radius - suppression_distance) / suppression_distance); + torque_accr_rate *= exp(-(bp->galactocentric_radius - suppression_distance) / suppression_distance); torque_accr_rate = 0.f; - } + }*/ #ifdef OBSIDIAN_DEBUG_CHECKS if (isnan(bondi_accr_rate)) error("bondi_accr_rate nan");