From b8d89d9959c9ef385bb6433a285c572aa0cf557c Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Fri, 25 Jul 2025 17:09:13 -0400 Subject: [PATCH 01/39] First push for MAGMA2. Right now it is just a Gasoline2 clone. --- configure.ac | 5 +- src/debug.c | 2 + src/hydro.h | 4 + src/hydro/MAGMA2/hydro.h | 1104 ++++++++++++++++++++++ src/hydro/MAGMA2/hydro_debug.h | 70 ++ src/hydro/MAGMA2/hydro_iact.h | 576 +++++++++++ src/hydro/MAGMA2/hydro_io.h | 276 ++++++ src/hydro/MAGMA2/hydro_parameters.h | 261 +++++ src/hydro/MAGMA2/hydro_part.h | 275 ++++++ src/hydro_csds.h | 2 + src/hydro_io.h | 2 + src/hydro_parameters.h | 2 + src/part.h | 4 + src/sink/GEAR/sink_getters.h | 5 + src/star_formation/GEAR/star_formation.h | 5 + 15 files changed, 2592 insertions(+), 1 deletion(-) 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/configure.ac b/configure.ac index 82e96c5e18..9a2ef413a8 100644 --- a/configure.ac +++ b/configure.ac @@ -2223,7 +2223,7 @@ fi # Hydro scheme. AC_ARG_WITH([hydro], [AS_HELP_STRING([--with-hydro=], - [Hydro dynamics to use @<:@gadget2, minimal, pressure-entropy, pressure-energy, pressure-energy-monaghan, phantom, gizmo-mfv, gizmo-mfm, shadowswift, planetary, remix, sphenix, gasoline, anarchy-pu default: sphenix@:>@] + [Hydro dynamics to use @<:@gadget2, minimal, magma2, pressure-entropy, pressure-energy, pressure-energy-monaghan, phantom, gizmo-mfv, gizmo-mfm, shadowswift, planetary, remix, sphenix, gasoline, anarchy-pu default: sphenix@:>@] )], [with_hydro="$withval"], [with_hydro="sphenix"] @@ -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-du) AC_DEFINE([SPHENIX_SPH], [1], [SPHENIX SPH]) ;; 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/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..2aa780a98c --- /dev/null +++ b/src/hydro/MAGMA2/hydro.h @@ -0,0 +1,1104 @@ +/******************************************************************************* + * 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 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 + +/** + * @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; + + p->viscosity.v_sig = max(p->viscosity.v_sig, soundspeed); +} + +/** + * @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); + + /* Update the signal velocity */ + p->viscosity.v_sig = + max(soundspeed, p->viscosity.v_sig + const_viscosity_beta * dv); +} + +/** + * @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) { + p->viscosity.alpha = 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, + hydro_props_default_viscosity_alpha_feedback_reset); + + p->diffusion.rate = hydro_props_default_diffusion_coefficient_feedback_reset; +} + +/** + * @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) { + const float CFL_condition = hydro_properties->CFL_condition; + + /* CFL condition */ + const float dt_cfl = 2.f * kernel_gamma * CFL_condition * cosmo->a * p->h / + (cosmo->a_factor_sound_speed * p->viscosity.v_sig); + + 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; + + return 0.5f * (ci + cj) - mu_ij; +} + +/** + * @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->weighted_wcount = 0.f; + p->weighted_neighbour_wcount = 0.f; + p->density.rho_dh = 0.f; + + p->smooth_pressure_gradient[0] = 0.f; + p->smooth_pressure_gradient[1] = 0.f; + p->smooth_pressure_gradient[2] = 0.f; + + p->viscosity.shock_indicator = 0.f; + p->viscosity.shock_limiter = 0.f; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + p->viscosity.velocity_gradient[i][j] = 0.f; + } + } +} + +/** + * @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; + + /* Finish caclulation of the pressure gradients; note that the + * kernel derivative is zero at our particle's position. */ + p->smooth_pressure_gradient[0] *= hydro_gamma_minus_one * h_inv_dim_plus_one; + p->smooth_pressure_gradient[1] *= hydro_gamma_minus_one * h_inv_dim_plus_one; + p->smooth_pressure_gradient[2] *= hydro_gamma_minus_one * h_inv_dim_plus_one; + + /* Finish calculation of the velocity gradient tensor, and + * guard against FPEs here. */ + const float velocity_gradient_norm = + p->weighted_wcount == 0.f ? 0.f + : 3.f * cosmo->a2_inv / p->weighted_wcount; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + const float hubble_term = i == j ? hydro_dimension * cosmo->H : 0.f; + + p->viscosity.velocity_gradient[i][j] *= velocity_gradient_norm; + + p->viscosity.velocity_gradient[i][j] += hubble_term; + } + } +} + +/** + * @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) { + + /* 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; + + const float mod_pressure_gradient = + sqrtf(p->smooth_pressure_gradient[0] * p->smooth_pressure_gradient[0] + + p->smooth_pressure_gradient[1] * p->smooth_pressure_gradient[1] + + p->smooth_pressure_gradient[2] * p->smooth_pressure_gradient[2]); + + float unit_pressure_gradient[3]; + + /* As this is normalised, no cosmology factor is required */ + unit_pressure_gradient[0] = + p->smooth_pressure_gradient[0] / mod_pressure_gradient; + unit_pressure_gradient[1] = + p->smooth_pressure_gradient[1] / mod_pressure_gradient; + unit_pressure_gradient[2] = + p->smooth_pressure_gradient[2] / mod_pressure_gradient; + + float dv_dn = 0.f; + float shear_norm2 = 0.f; + float traceless_shear_norm2 = 0.f; + float div_v = 0.f; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + dv_dn += unit_pressure_gradient[i] * + p->viscosity.velocity_gradient[i][j] * unit_pressure_gradient[j]; + + const float shear_component = + 0.5f * (p->viscosity.velocity_gradient[i][j] + + p->viscosity.velocity_gradient[j][i]); + const float shear_component2 = shear_component * shear_component; + + shear_norm2 += shear_component2; + traceless_shear_norm2 += i == j ? 0.f : shear_component2; + div_v += i == j ? (1. / 3.) * p->viscosity.velocity_gradient[i][j] : 0.f; + } + } + + const float shock_indicator = + (3.f / 2.f) * (dv_dn + max(-(1.f / 3.f) * div_v, 0.f)); + + /* Now do the conduction coefficient; note that no limiter is used here. */ + /* These square roots are not included in the original documentation */ + const float h_physical = p->h * cosmo->a; + + const float diffusion_rate = hydro_props->diffusion.coefficient * + sqrtf(traceless_shear_norm2) * h_physical * + h_physical; + + p->diffusion.rate = diffusion_rate; + p->viscosity.tensor_norm = sqrtf(shear_norm2); + p->viscosity.shock_indicator = shock_indicator; +} + +/** + * @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->viscosity.v_sig = p->force.soundspeed; +} + +/** + * @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) { + /* The f_i is calculated explicitly in MAGMA2. */ + p->force.f = p->weighted_wcount / (p->weighted_neighbour_wcount * p->rho); + + /* 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 */ + + /* Apply correct normalisation */ + p->viscosity.shock_limiter *= h_inv_dim; +} + +/** + * @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 */ + + 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->viscosity.v_sig = 0.f; + p->density.wcount = kernel_root * h_inv_dim; + p->density.rho_dh = 0.f; + p->density.wcount_dh = 0.f; + /* Set to 1 as these are only used by taking the ratio */ + p->weighted_wcount = 1.f; + p->weighted_neighbour_wcount = 1.f; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + p->viscosity.velocity_gradient[i][j] = 0.f; + } + } +} + +/** + * @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) { + /* Here we need to update the artificial viscosity alpha */ + const float d_shock_indicator_dt = + dt_alpha == 0.f ? 0.f + : (p->viscosity.shock_indicator - + p->viscosity.shock_indicator_previous_step) / + dt_alpha; + + /* A_i in all of the equations */ + const float v_sig_physical = p->viscosity.v_sig * cosmo->a_factor_sound_speed; + const float soundspeed_physical = + gas_soundspeed_from_pressure(hydro_get_physical_density(p, cosmo), + hydro_get_physical_pressure(p, cosmo)); + const float h_physical = p->h * cosmo->a; + + /* Note that viscosity.shock_limiter still includes the cosmology dependence + * from the density, so what we do here is correct (i.e. include no explicit + * h-factors). */ + const float shock_limiter_core = + 0.5f * (1.f - p->viscosity.shock_limiter / p->rho); + const float shock_limiter_core_2 = shock_limiter_core * shock_limiter_core; + const float shock_limiter = shock_limiter_core_2 * shock_limiter_core_2; + + const float shock_detector = 2.f * h_physical * h_physical * kernel_gamma2 * + shock_limiter * + max(-1.f * d_shock_indicator_dt, 0.f); + + const float alpha_loc = + hydro_props->viscosity.alpha_max * + (shock_detector / (shock_detector + v_sig_physical * v_sig_physical)); + + /* TODO: Probably use physical _h_ throughout this function */ + const float d_alpha_dt = (alpha_loc - p->viscosity.alpha) * + hydro_props->viscosity.length * soundspeed_physical / + h_physical; + + float new_alpha = p->viscosity.alpha; + + if (new_alpha < alpha_loc) { + new_alpha = alpha_loc; + } else { + /* Very basic time integration here */ + new_alpha += d_alpha_dt * dt_alpha; + } + + new_alpha = max(new_alpha, hydro_props->viscosity.alpha_min); + new_alpha = min(new_alpha, hydro_props->viscosity.alpha_max); + + p->viscosity.shock_indicator_previous_step = p->viscosity.shock_indicator; + p->viscosity.alpha = new_alpha; +} + +/** + * @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->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; + + /* Update the signal velocity, if we need to. */ + p->viscosity.v_sig = max(p->viscosity.v_sig, soundspeed); +} + +/** + * @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) { + + /* 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; + + /* Update signal velocity if we need to */ + p->viscosity.v_sig = max(p->viscosity.v_sig, soundspeed); +} + +/** + * @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 *= p->h * hydro_dimension_inv; +} + +/** + * @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) { + /* 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 */ + + p->viscosity.alpha = hydro_props->viscosity.alpha; + + 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->viscosity.shock_indicator_previous_step = 0.f; + p->viscosity.tensor_norm = 0.f; + + hydro_reset_acceleration(p); + hydro_init_part(p, NULL); +} + +/** + * @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..20b07d8ee5 --- /dev/null +++ b/src/hydro/MAGMA2/hydro_debug.h @@ -0,0 +1,70 @@ +/******************************************************************************* + * 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 Gasoline/hydro_debug.h + * @brief Density-Energy conservative implementation of SPH, + * with added Gasoline 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 v_sig=%.3e, P=%.3e", p->id, p->u, + p->u_dt, p->viscosity.v_sig, 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] alpha=%.3e, time_bin=%d, rho=%.3e, velocity_gradient=[" + "[%.3e,%.3e,%.3e]," + "[%.3e,%.3e,%.3e]," + "[%.3e,%.3e,%.3e]]", + p->id, p->viscosity.alpha, p->time_bin, p->rho, + p->viscosity.velocity_gradient[0][0], + p->viscosity.velocity_gradient[0][1], + p->viscosity.velocity_gradient[0][2], + p->viscosity.velocity_gradient[1][0], + p->viscosity.velocity_gradient[1][1], + p->viscosity.velocity_gradient[1][2], + p->viscosity.velocity_gradient[2][0], + p->viscosity.velocity_gradient[2][1], + p->viscosity.velocity_gradient[2][2]); + warning("[PID%lld] smooth_pressure_gradient=[%.3e,%.3e,%.3e]", p->id, + p->smooth_pressure_gradient[0], p->smooth_pressure_gradient[1], + p->smooth_pressure_gradient[2]); + warning("[PID%lld] weighted_wcount=%.3e", p->id, p->weighted_wcount); + + 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..5b1f512b32 --- /dev/null +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -0,0 +1,576 @@ +/******************************************************************************* + * 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 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) { + float wi, wj, wi_dx, wj_dx; + + const float r = sqrtf(r2); + + /* Get the masses. */ + const float mi = pi->mass; + const float mj = pj->mass; + + /* Compute density of pi. */ + const float hi_inv = 1.f / hi; + const float ui = r * hi_inv; + + kernel_deval(ui, &wi, &wi_dx); + + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + + pi->density.wcount += wi; + pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + + adaptive_softening_add_correction_term(pi, ui, hi_inv, mj); + + /* Compute density of pj. */ + const float hj_inv = 1.f / hj; + const float uj = r * hj_inv; + kernel_deval(uj, &wj, &wj_dx); + + pj->rho += mi * wj; + pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); + + pj->density.wcount += wj; + pj->density.wcount_dh -= (hydro_dimension * wj + uj * wj_dx); + + adaptive_softening_add_correction_term(pj, uj, hj_inv, mi); + + /* Now we need to compute the div terms */ + const float r_inv = r ? 1.0f / r : 0.0f; + const float faci = mj * wi_dx * r_inv; + const float facj = mi * wj_dx * r_inv; + + /* Smooth pressure gradient */ + pi->smooth_pressure_gradient[0] += faci * pj->u * dx[0]; + pi->smooth_pressure_gradient[1] += faci * pj->u * dx[1]; + pi->smooth_pressure_gradient[2] += faci * pj->u * dx[2]; + + pj->smooth_pressure_gradient[0] -= facj * pi->u * dx[0]; + pj->smooth_pressure_gradient[1] -= facj * pi->u * dx[1]; + pj->smooth_pressure_gradient[2] -= facj * pi->u * dx[2]; + + /* Finally, the big boy; the velocity gradient tensor. Note that the + * loops here are over the coordinates, i=0 -> x, and so on. */ + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + const float dv_ij = pi->v[i] - pj->v[i]; + const float dx_ij = pi->x[j] - pj->x[j]; + + pi->viscosity.velocity_gradient[i][j] += + mj * dv_ij * dx_ij * wi_dx * r_inv; + pj->viscosity.velocity_gradient[i][j] += + mi * dv_ij * dx_ij * wj_dx * r_inv; + } + } + + /* Correction factors for kernel gradients, and norm for the velocity + * gradient. */ + + pi->weighted_wcount += mj * r2 * wi_dx * r_inv; + pj->weighted_wcount += mi * r2 * wj_dx * r_inv; +} + +/** + * @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) { + float wi, wi_dx; + + /* Get the masses. */ + const float mj = pj->mass; + + /* Get r and r inverse. */ + const float r = sqrtf(r2); + + const float h_inv = 1.f / hi; + const float ui = r * h_inv; + kernel_deval(ui, &wi, &wi_dx); + + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + + pi->density.wcount += wi; + pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + + adaptive_softening_add_correction_term(pi, ui, h_inv, mj); + + const float r_inv = r ? 1.0f / r : 0.0f; + const float faci = mj * wi_dx * r_inv; + + /* Compute pressure gradient */ + pi->smooth_pressure_gradient[0] += faci * pj->u * dx[0]; + pi->smooth_pressure_gradient[1] += faci * pj->u * dx[1]; + pi->smooth_pressure_gradient[2] += faci * pj->u * dx[2]; + + /* Finally, the big boy; the velocity gradient tensor. Note that the + * loops here are over the coordinates, i=0 -> x, and so on. */ + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + const float dv_ij = pi->v[i] - pj->v[i]; + const float dx_ij = pi->x[j] - pj->x[j]; + + pi->viscosity.velocity_gradient[i][j] += + mj * dv_ij * dx_ij * wi_dx * r_inv; + } + } + + /* Correction factors for kernel gradients, and norm for the velocity + * gradient. */ + pi->weighted_wcount += mj * r2 * wi_dx * r_inv; +} + +/** + * @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) { + /* We need to construct the maximal signal velocity between our particle + * and all of it's neighbours */ + + const float r = sqrtf(r2); + const float r_inv = r ? 1.0f / r : 0.0f; + + /* Cosmology terms for the signal velocity */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Add Hubble flow */ + + const float dvdr_Hubble = dvdr + a2_Hubble * r2; + /* Are the particles moving towards each others ? */ + const float omega_ij = min(dvdr_Hubble, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Signal velocity */ + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); + + /* Update if we need to */ + pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); + pj->viscosity.v_sig = max(pj->viscosity.v_sig, new_v_sig); + + /* Calculate Del^2 u for the thermal diffusion coefficient. */ + /* Need to get some kernel values F_ij = wi_dx */ + float wi, wi_dx, wj, wj_dx; + + const float ui = r / hi; + const float uj = r / hj; + + kernel_deval(ui, &wi, &wi_dx); + kernel_deval(uj, &wj, &wj_dx); + + /* Calculate the shock limiter component */ + const float shock_ratio_i = + pj->viscosity.tensor_norm > 0.f + ? pj->viscosity.shock_indicator / pj->viscosity.tensor_norm + : 0.f; + + const float shock_ratio_j = + pi->viscosity.tensor_norm > 0.f + ? pi->viscosity.shock_indicator / pi->viscosity.tensor_norm + : 0.f; + + pi->viscosity.shock_limiter += pj->mass * shock_ratio_i * wi; + pj->viscosity.shock_limiter += pi->mass * shock_ratio_j * wj; + + /* Correction factors for kernel gradients */ + const float rho_inv_i = 1.f / pi->rho; + const float rho_inv_j = 1.f / pj->rho; + + pi->weighted_neighbour_wcount += pj->mass * r2 * wi_dx * rho_inv_j * r_inv; + pj->weighted_neighbour_wcount += pi->mass * r2 * wj_dx * rho_inv_i * r_inv; + +} + +/** + * @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) { + /* We need to construct the maximal signal velocity between our particle + * and all of it's neighbours */ + + const float r = sqrtf(r2); + const float r_inv = r ? 1.0f / r : 0.0f; + + /* Cosmology terms for the signal velocity */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Add Hubble flow */ + + const float dvdr_Hubble = dvdr + a2_Hubble * r2; + /* Are the particles moving towards each others ? */ + const float omega_ij = min(dvdr_Hubble, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Signal velocity */ + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); + + /* Update if we need to */ + pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); + + /* Need to get some kernel values F_ij = wi_dx */ + float wi, wi_dx; + + const float ui = r / hi; + + kernel_deval(ui, &wi, &wi_dx); + + /* Calculate the shock limiter component */ + const float shock_ratio_i = + pj->viscosity.tensor_norm > 0.f + ? pj->viscosity.shock_indicator / pj->viscosity.tensor_norm + : 0.f; + + pi->viscosity.shock_limiter += pj->mass * shock_ratio_i * wi; + + /* Correction factors for kernel gradients */ + + const float rho_inv_j = 1.f / pj->rho; + + pi->weighted_neighbour_wcount += pj->mass * r2 * wi_dx * rho_inv_j * r_inv; +} + +/** + * @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) { + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + + const float r = sqrtf(r2); + const float r_inv = r ? 1.0f / r : 0.0f; + + /* Recover some data */ + const float mj = pj->mass; + const float mi = pi->mass; + + const float rhoi = pi->rho; + const float rhoj = pj->rho; + + const float pressurei = pi->force.pressure; + const float pressurej = pj->force.pressure; + + /* Get the kernel for hi. */ + const float hi_inv = 1.0f / hi; + const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float xi = r * hi_inv; + float wi, wi_dx; + kernel_deval(xi, &wi, &wi_dx); + const float wi_dr = hid_inv * wi_dx; + + /* Get the kernel for hj. */ + const float hj_inv = 1.0f / hj; + const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float xj = r * hj_inv; + float wj, wj_dx; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute dv dot r. */ + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Includes the hubble flow term; not used for du/dt */ + const float dvdr_Hubble = dvdr + a2_Hubble * r2; + + /* Are the particles moving towards each others ? */ + const float omega_ij = min(dvdr_Hubble, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Variable smoothing length term */ + const float kernel_gradient = + 0.5f * (wi_dr * pi->force.f + wj_dr * pj->force.f); + + /* Construct the full viscosity term */ + const float rho_ij = rhoi + rhoj; + const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; + const float visc = + omega_ij < 0.f + ? (-0.25f * alpha * (pi->force.soundspeed + pj->force.soundspeed) * + mu_ij + + const_viscosity_beta * mu_ij * mu_ij) / + (0.5f * rho_ij) + : 0.f; + + /* Convolve with the kernel */ + const float visc_acc_term = visc * kernel_gradient * r_inv; + + /* SPH acceleration term */ + const float sph_acc_term = + (pressurei + pressurej) * r_inv * kernel_gradient / (pi->rho * pj->rho); + + /* Adaptive softening acceleration term */ + const float adapt_soft_acc_term = adaptive_softening_get_acc_term( + pi, pj, wi_dr, wj_dr, pi->force.f, pj->force.f, r_inv); + + /* Assemble the acceleration */ + const float acc = sph_acc_term + visc_acc_term + adapt_soft_acc_term; + + /* Use the force Luke ! */ + pi->a_hydro[0] -= mj * acc * dx[0]; + pi->a_hydro[1] -= mj * acc * dx[1]; + pi->a_hydro[2] -= mj * acc * dx[2]; + + pj->a_hydro[0] += mi * acc * dx[0]; + pj->a_hydro[1] += mi * acc * dx[1]; + pj->a_hydro[2] += mi * acc * dx[2]; + + /* Get the time derivative for u. */ + const float sph_du_term_i = + pressurei * dvdr * r_inv * kernel_gradient / (pi->rho * pj->rho); + const float sph_du_term_j = + pressurej * dvdr * r_inv * kernel_gradient / (pi->rho * pj->rho); + + /* Viscosity term */ + const float visc_du_term = 0.5f * visc_acc_term * dvdr_Hubble; + + /* Diffusion term */ + const float diff_du_term = 2.f * (pi->diffusion.rate + pj->diffusion.rate) * + (pi->u - pj->u) * kernel_gradient / rho_ij; + + /* Assemble the energy equation term */ + const float du_dt_i = sph_du_term_i + visc_du_term + diff_du_term; + const float du_dt_j = sph_du_term_j + visc_du_term - diff_du_term; + + /* Internal energy time derivative */ + pi->u_dt += du_dt_i * mj; + pj->u_dt += du_dt_j * mi; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; +} + +/** + * @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) { + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + + const float r = sqrtf(r2); + const float r_inv = r ? 1.0f / r : 0.0f; + + /* Recover some data */ + const float mj = pj->mass; + + const float rhoi = pi->rho; + const float rhoj = pj->rho; + + const float pressurei = pi->force.pressure; + const float pressurej = pj->force.pressure; + + /* Get the kernel for hi. */ + const float hi_inv = 1.0f / hi; + const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float xi = r * hi_inv; + float wi, wi_dx; + kernel_deval(xi, &wi, &wi_dx); + const float wi_dr = hid_inv * wi_dx; + + /* Get the kernel for hj. */ + const float hj_inv = 1.0f / hj; + const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float xj = r * hj_inv; + float wj, wj_dx; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute dv dot r. */ + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Includes the hubble flow term; not used for du/dt */ + const float dvdr_Hubble = dvdr + a2_Hubble * r2; + + /* Are the particles moving towards each others ? */ + const float omega_ij = min(dvdr_Hubble, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Variable smoothing length term */ + const float kernel_gradient = + 0.5f * (wi_dr * pi->force.f + wj_dr * pj->force.f); + + /* Construct the full viscosity term */ + const float rho_ij = rhoi + rhoj; + const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; + const float visc = + omega_ij < 0.f + ? (-0.25f * alpha * (pi->force.soundspeed + pj->force.soundspeed) * + mu_ij + + const_viscosity_beta * mu_ij * mu_ij) / + (0.5f * rho_ij) + : 0.f; + + /* Convolve with the kernel */ + const float visc_acc_term = visc * kernel_gradient * r_inv; + + /* SPH acceleration term */ + const float sph_acc_term = + (pressurei + pressurej) * r_inv * kernel_gradient / (pi->rho * pj->rho); + + /* Adaptive softening acceleration term */ + const float adapt_soft_acc_term = adaptive_softening_get_acc_term( + pi, pj, wi_dr, wj_dr, pi->force.f, pj->force.f, r_inv); + + /* Assemble the acceleration */ + const float acc = sph_acc_term + visc_acc_term + adapt_soft_acc_term; + + /* Use the force Luke ! */ + pi->a_hydro[0] -= mj * acc * dx[0]; + pi->a_hydro[1] -= mj * acc * dx[1]; + pi->a_hydro[2] -= mj * acc * dx[2]; + + /* Get the time derivative for u. */ + const float sph_du_term_i = + pressurei * dvdr * r_inv * kernel_gradient / (pi->rho * pj->rho); + + /* Viscosity term */ + const float visc_du_term = 0.5f * visc_acc_term * dvdr_Hubble; + + /* Diffusion term */ + const float diff_du_term = 2.f * (pi->diffusion.rate + pj->diffusion.rate) * + (pi->u - pj->u) * kernel_gradient / rho_ij; + + /* Assemble the energy equation term */ + const float du_dt_i = sph_du_term_i + visc_du_term + diff_du_term; + + /* Internal energy time derivative */ + pi->u_dt += du_dt_i * mj; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; +} + +#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..974db07820 --- /dev/null +++ b/src/hydro/MAGMA2/hydro_io.h @@ -0,0 +1,276 @@ +/******************************************************************************* + * 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 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) { + /* The velocity divergence is the 1/3 of the trace of the velocity + * gradient tensor */ + ret[0] = (1. / 3.) * (p->viscosity.velocity_gradient[0][0] + + p->viscosity.velocity_gradient[1][1] + + p->viscosity.velocity_gradient[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_viscosity(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + ret[0] = p->viscosity.alpha; +} + +/** + * @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 = 14; + + /* List what we want to write */ + list[0] = 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"); + + list[1] = 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"); + + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, 0.f, parts, + mass, "Masses of the particles"); + + list[3] = 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"); + + list[4] = 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"); + + list[5] = 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"); + + list[6] = io_make_output_field("Densities", FLOAT, 1, UNIT_CONV_DENSITY, -3.f, + parts, rho, + "Co-moving mass densities of the particles"); + + list[7] = 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"); + + list[8] = 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"); + + list[9] = io_make_output_field_convert_part( + "ViscosityParameters", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, xparts, + convert_viscosity, + "Visosity coefficient (alpha_visc) of the particles, multiplied by the " + "balsara switch"); + + list[10] = 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)"); + + /* Units and cosmology TBD */ + list[11] = + io_make_output_field("ShockIndicators", FLOAT, 1, UNIT_CONV_FREQUENCY, + 0.f, parts, viscosity.shock_indicator, + "Physical shock indicators (D in the paper) created " + "from the velocity tensor."); + + /* Units and cosmology TBD */ + list[12] = io_make_output_field( + "DiffusionRates", FLOAT, 1, UNIT_CONV_THERMAL_DIFFUSIVITY, 0.f, parts, + diffusion.rate, + "Physical diffusion rates calculated from the shear tensor."); + + list[13] = 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"); +} + +/** + * @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", + "Simplified version of Cullen & Denhen (2011)"); +} + +/** + * @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..7fccf9e3de --- /dev/null +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -0,0 +1,261 @@ +/******************************************************************************* + * 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 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 parameters -- FIXED -- MUST BE DEFINED AT COMPILE-TIME */ + +/*! Cosmology default beta=3.0. + * Alpha can be set in the parameter file. + * Beta is defined as in e.g. Price (2010) Eqn (103) */ +#define const_viscosity_beta 3.0f + +/*! The viscosity that the particles are reset to after being hit by a + * feedback event. This should be set to the same value as the + * hydro_props_default_viscosity_alpha in fixed schemes, and likely + * to hydro_props_default_viscosity_alpha_max in variable schemes. */ +#define hydro_props_default_viscosity_alpha_feedback_reset 2.0f + +/* Viscosity paramaters -- Defaults; can be changed at run-time */ + +/*! The "initial" hydro viscosity, or the fixed value for non-variable + * schemes. This usually takes the value 0.8. */ +#define hydro_props_default_viscosity_alpha 0.1f + +/*! Minimal value for the viscosity alpha in variable schemes. */ +#define hydro_props_default_viscosity_alpha_min 0.0f + +/*! Maximal value for the viscosity alpha in variable schemes. */ +#define hydro_props_default_viscosity_alpha_max 2.0f + +/*! Decay length for the viscosity scheme. This is scheme dependent. */ +#define hydro_props_default_viscosity_length 0.2f + +/* Diffusion parameters -- FIXED -- MUST BE DEFINED AT COMPILE-TIME */ + +/*! The diffusion that the particles are reset to after being hit by a + * feedback event. This should be set to the same value as the + * hydro_props_default_diffusion_alpha in fixed schemes, and likely + * to hydro_props_default_diffusion_alpha_min in variable schemes. */ +#define hydro_props_default_diffusion_coefficient_feedback_reset 0.0f + +/* Diffusion parameters -- Defaults; can be changed at run-time */ + +/*! Rate limiting coefficient for the diffusion. */ +#define hydro_props_default_diffusion_coefficient 0.03f + +/* Structs that store the relevant variables */ + +/*! Artificial viscosity parameters */ +struct viscosity_global_data { + /*! For the fixed, simple case. Also used to set the initial AV + coefficient for variable schemes. */ + float alpha; + + /*! Artificial viscosity (max) for the variable case (e.g. M&M) */ + float alpha_max; + + /*! Artificial viscosity (min) for the variable case (e.g. M&M) */ + float alpha_min; + + /*! The decay length of the artificial viscosity (used in M&M, etc.) */ + float length; +}; + +/*! Thermal diffusion parameters */ +struct diffusion_global_data { + + /*! Rate limiting coefficcient */ + float coefficient; +}; + +/* Functions for reading from parameter file */ + +/* Forward declartions */ +struct swift_params; +struct phys_const; +struct unit_system; + +/* 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) { + + /* Read the artificial viscosity parameters from the file, if they exist, + * otherwise set them to the defaults defined above. */ + + viscosity->alpha = parser_get_opt_param_float( + params, "SPH:viscosity_alpha", hydro_props_default_viscosity_alpha); + + viscosity->alpha_max = + parser_get_opt_param_float(params, "SPH:viscosity_alpha_max", + hydro_props_default_viscosity_alpha_max); + + viscosity->alpha_min = + parser_get_opt_param_float(params, "SPH:viscosity_alpha_min", + hydro_props_default_viscosity_alpha_min); + + viscosity->length = parser_get_opt_param_float( + params, "SPH:viscosity_length", hydro_props_default_viscosity_length); +} + +/** + * @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) { + viscosity->alpha = hydro_props_default_viscosity_alpha; + viscosity->alpha_max = hydro_props_default_viscosity_alpha_max; + viscosity->alpha_min = hydro_props_default_viscosity_alpha_min; + viscosity->length = hydro_props_default_viscosity_length; +} + +/** + * @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 parameters set to alpha: %.3f, max: %.3f, " + "min: %.3f, length: %.3f.", + viscosity->alpha, viscosity->alpha_max, viscosity->alpha_min, + viscosity->length); +} + +#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", viscosity->alpha); + io_write_attribute_f(h_grpsph, "Alpha viscosity (max)", viscosity->alpha_max); + io_write_attribute_f(h_grpsph, "Alpha viscosity (min)", viscosity->alpha_min); + io_write_attribute_f(h_grpsph, "Viscosity decay length [internal units]", + viscosity->length); + 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) { + + diffusion->coefficient = + parser_get_opt_param_float(params, "SPH:diffusion_coefficient", + hydro_props_default_diffusion_coefficient); +} + +/** + * @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) { + diffusion->coefficient = hydro_props_default_diffusion_coefficient; +} + +/** + * @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 diffusion parameters set to coefficient: %.3f", + diffusion->coefficient); +} + +#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, "Diffusion coefficient", + diffusion->coefficient); +} +#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..dcca059c5e --- /dev/null +++ b/src/hydro/MAGMA2/hydro_part.h @@ -0,0 +1,275 @@ +/******************************************************************************* + * 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 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" +#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; + + /*! Smoothed pressure gradient */ + float smooth_pressure_gradient[3]; + + /*! Weightings for correction factor */ + float weighted_wcount; + + /*! Neighbour weightings */ + float weighted_neighbour_wcount; + + /*! Kernel derivatives */ + float G[3]; + + /*! Correction matrix (C^ki in Rosswog 2020) */ + float C[3][3]; + + /* Store viscosity information in a separate struct. */ + struct { + + /*! Velocity gradient tensor (physical) */ + float velocity_gradient[3][3]; + + /*! Velocity gradient tensor trace norm |T| */ + float tensor_norm; + + /*! Shock limiter (top portion of R) */ + float shock_limiter; + + /*! Shock indicator (D) */ + float shock_indicator; + + /*! Particle shock indicator from previous step */ + float shock_indicator_previous_step; + + /*! Artificial viscosity parameter */ + float alpha; + + /*! Signal velocity */ + float v_sig; + + } viscosity; + + /* Store thermal diffusion information in a separate struct. */ + struct { + + /*! Thermal diffusion rate */ + float rate; + + } diffusion; + + /* 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; + }; + + /*! 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; + + /*! Black holes information (e.g. swallowing ID) */ + struct black_holes_part_data black_holes_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/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 4612555b4a..a7fdd15172 100644 --- a/src/sink/GEAR/sink_getters.h +++ b/src/sink/GEAR/sink_getters.h @@ -106,6 +106,11 @@ 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 = p->viscosity.velocity_gradient[0][0] + + p->viscosity.velocity_gradient[1][1] + + p->viscosity.velocity_gradient[2][2]; #elif HOPKINS_PU_SPH div_v = p->density.div_v; #else diff --git a/src/star_formation/GEAR/star_formation.h b/src/star_formation/GEAR/star_formation.h index 40b95b2bd7..1a71ee287c 100644 --- a/src/star_formation/GEAR/star_formation.h +++ b/src/star_formation/GEAR/star_formation.h @@ -448,6 +448,11 @@ __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 = p->viscosity.velocity_gradient[0][0] + + p->viscosity.velocity_gradient[1][1] + + p->viscosity.velocity_gradient[2][2]; #elif HOPKINS_PU_SPH xp->sf_data.div_v = p->density.div_v; #else From a6b204d153b75c6b5245da5d3b7a93747c06747b Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Fri, 25 Jul 2025 18:03:34 -0400 Subject: [PATCH 02/39] Added and tested the calculation of the correction matrix. Added in MAGMA2_DEBUG_CHECKS to output the ill condition nature. --- configure.ac | 12 ++++ src/hydro/MAGMA2/hydro.h | 85 +++++++++++++++++++++++++++++ src/hydro/MAGMA2/hydro_debug.h | 4 +- src/hydro/MAGMA2/hydro_iact.h | 45 ++++++++++++--- src/hydro/MAGMA2/hydro_io.h | 15 +++++ src/hydro/MAGMA2/hydro_parameters.h | 3 + src/hydro/MAGMA2/hydro_part.h | 13 ++++- 7 files changed, 165 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index 9a2ef413a8..c642c47694 100644 --- a/configure.ac +++ b/configure.ac @@ -331,6 +331,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], diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 2aa780a98c..cd21a9399b 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -41,6 +41,9 @@ #include "minmax.h" #include "pressure_floor.h" +#include +#include +#include #include /** @@ -520,6 +523,7 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { + p->C[i][j] = 0.f; p->viscosity.velocity_gradient[i][j] = 0.f; } } @@ -679,6 +683,35 @@ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( p->viscosity.v_sig = p->force.soundspeed; } +/** + * @brief Computes the condition number of a matrix A + * + * + * @param A The matrix to compute the condition number of. + */ +__attribute__((always_inline)) INLINE static float +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.f) ? s_max / s_min : FLT_MAX; +} + /** * @brief Finishes the gradient calculation. * @@ -701,6 +734,53 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( /* Apply correct normalisation */ p->viscosity.shock_limiter *= h_inv_dim; + for (int k = 0; k < 3; k++) { + p->C[k][0] *= h_inv_dim; + p->C[k][1] *= h_inv_dim; + p->C[k][2] *= h_inv_dim; + } + + /* Invert the p->C[3][3] matrix */ + gsl_matrix_view C_view = gsl_matrix_view_array((double *)p->C, 3, 3); + gsl_matrix *C = &C_view.matrix; + + float 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++) { + for (int i = 0; i < 3; i++) { + p->C[k][i] = gsl_matrix_get(C_inv, k, i); + } + } + + gsl_matrix_free(C_inv); + gsl_permutation_free(p_perm); + } + else { +#ifdef MAGMA2_DEBUG_CHECKS + for (int k = 0; k < 3; k++) { + p->debug.C[k][0] = p->C[k][0]; + p->debug.C[k][1] = p->C[k][1]; + p->debug.C[k][2] = p->C[k][2]; + } + + p->debug.ill_conditioned_count++; +#endif + + /* Ill-condition matrix, revert back to normal SPH gradients */ + for (int k = 0; k < 3; k++) { + p->C[k][0] = 0.f; + p->C[k][1] = 0.f; + p->C[k][2] = 0.f; + } + } + } /** @@ -739,6 +819,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { + p->C[i][j] = 0.f; p->viscosity.velocity_gradient[i][j] = 0.f; } } @@ -1081,6 +1162,10 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( p->viscosity.shock_indicator_previous_step = 0.f; p->viscosity.tensor_norm = 0.f; +#ifdef MAGMA2_DEBUG_CHECKS + p->debug.ill_conditioned_count = 0; +#endif + hydro_reset_acceleration(p); hydro_init_part(p, NULL); } diff --git a/src/hydro/MAGMA2/hydro_debug.h b/src/hydro/MAGMA2/hydro_debug.h index 20b07d8ee5..958ed82a3e 100644 --- a/src/hydro/MAGMA2/hydro_debug.h +++ b/src/hydro/MAGMA2/hydro_debug.h @@ -23,9 +23,9 @@ #define SWIFT_MAGMA2_HYDRO_DEBUG_H /** - * @file Gasoline/hydro_debug.h + * @file MAGMA2/hydro_debug.h * @brief Density-Energy conservative implementation of SPH, - * with added Gasoline physics (Rosswog 2020) (Debugging routines) + * with added MAGMA2 physics (Rosswog 2020) (Debugging routines) */ __attribute__((always_inline)) INLINE static void hydro_debug_particle( diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 5b1f512b32..0069d15cdc 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -200,6 +200,14 @@ __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) { + + /* Get particle properties */ + const float mi = hydro_get_mass(pi); + const float mj = hydro_get_mass(pj); + + const float rhoi = hydro_get_comoving_density(pi); + const float rhoj = hydro_get_comoving_density(pj); + /* We need to construct the maximal signal velocity between our particle * and all of it's neighbours */ @@ -250,16 +258,23 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( ? pi->viscosity.shock_indicator / pi->viscosity.tensor_norm : 0.f; - pi->viscosity.shock_limiter += pj->mass * shock_ratio_i * wi; - pj->viscosity.shock_limiter += pi->mass * shock_ratio_j * wj; + pi->viscosity.shock_limiter += mj * shock_ratio_i * wi; + pj->viscosity.shock_limiter += mi * shock_ratio_j * wj; /* Correction factors for kernel gradients */ - const float rho_inv_i = 1.f / pi->rho; - const float rho_inv_j = 1.f / pj->rho; + const float rho_inv_i = 1.f / rhoi; + const float rho_inv_j = 1.f / rhoj; - pi->weighted_neighbour_wcount += pj->mass * r2 * wi_dx * rho_inv_j * r_inv; - pj->weighted_neighbour_wcount += pi->mass * r2 * wj_dx * rho_inv_i * r_inv; + pi->weighted_neighbour_wcount += mj * r2 * wi_dx * rho_inv_j * r_inv; + pj->weighted_neighbour_wcount += mi * r2 * wj_dx * rho_inv_i * r_inv; + for (int k = 0; k < 3; k++) { + for (int i = 0; i < 3; i++) { + /* dx is signed as (pi - pj), but it is symmetric so we add */ + pi->C[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; + pj->C[k][i] += mi * rho_inv_i * dx[k] * dx[i] * wj; + } + } } /** @@ -283,6 +298,11 @@ __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) { + + /* Get particle properties */ + const float mj = hydro_get_mass(pj); + const float rhoj = hydro_get_comoving_density(pj); + /* We need to construct the maximal signal velocity between our particle * and all of it's neighbours */ @@ -324,13 +344,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( ? pj->viscosity.shock_indicator / pj->viscosity.tensor_norm : 0.f; - pi->viscosity.shock_limiter += pj->mass * shock_ratio_i * wi; + pi->viscosity.shock_limiter += mj * shock_ratio_i * wi; /* Correction factors for kernel gradients */ - const float rho_inv_j = 1.f / pj->rho; + const float rho_inv_j = 1.f / rhoj; + + pi->weighted_neighbour_wcount += mj * r2 * wi_dx * rho_inv_j * r_inv; - pi->weighted_neighbour_wcount += pj->mass * r2 * wi_dx * rho_inv_j * r_inv; + for (int k = 0; k < 3; k++) { + for (int i = 0; i < 3; i++) { + /* dx is signed as (pi - pj), but it is symmetric so we add */ + pi->C[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; + } + } } /** diff --git a/src/hydro/MAGMA2/hydro_io.h b/src/hydro/MAGMA2/hydro_io.h index 974db07820..346763fb0b 100644 --- a/src/hydro/MAGMA2/hydro_io.h +++ b/src/hydro/MAGMA2/hydro_io.h @@ -251,6 +251,21 @@ INLINE static void hydro_write_particles(const struct part* parts, "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, parts, xparts, convert_part_potential, "Co-moving gravitational potential at position of the particles"); + +#ifdef MAGMA2_DEBUG_CHECKS + list[14] = io_make_output_field( + "CorrectionMatrices", FLOAT, 9, UNIT_CONV_LENGTH * UNIT_CONV_LENGTH, + 2.f, parts, debug.C, + "Co-moving correction matrices for the particles."); + + list[15] = io_make_output_field( + "IllConditionedCounts", INT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, + debug.ill_conditioned_count, + "Count for how many times this particle had an ill-conditioned C matrix"); + + *num_fields = 16; +#endif + } /** diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 7fccf9e3de..19657ddd3a 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -45,6 +45,9 @@ * as well as a number of compile-time parameters. */ +/*! Consider C matrix can be ill-conditioned above this limit */ +#define const_condition_number_upper_limit 1.e4f + /*! Viscosity parameters -- FIXED -- MUST BE DEFINED AT COMPILE-TIME */ /*! Cosmology default beta=3.0. diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index dcca059c5e..62c7427a00 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -142,7 +142,18 @@ struct part { /*! Correction matrix (C^ki in Rosswog 2020) */ float C[3][3]; - + +#ifdef MAGMA2_DEBUG_CHECKS + struct { + /*! C matrix at the last time it was ill-conditioned */ + float C[3][3]; + + /*! Number of times C was ill-conditioned */ + int ill_conditioned_count; + + } debug; +#endif + /* Store viscosity information in a separate struct. */ struct { From 89c0656254871202f5d35daef845de4df48f3c7f Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Sat, 26 Jul 2025 09:11:06 -0400 Subject: [PATCH 03/39] Now the correction matrix is used to compute the kernel gradients. The kernel gradients are used in the equations of motion symmetrically. The Sedov test runs for a while, but still eventually crashes. --- src/hydro/MAGMA2/hydro.h | 23 +-- src/hydro/MAGMA2/hydro_iact.h | 216 ++++++++++++++++++---------- src/hydro/MAGMA2/hydro_parameters.h | 2 +- src/hydro/MAGMA2/hydro_part.h | 8 +- 4 files changed, 157 insertions(+), 92 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index cd21a9399b..e86870440b 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -523,7 +523,7 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - p->C[i][j] = 0.f; + p->C[i][j] = 0.; p->viscosity.velocity_gradient[i][j] = 0.f; } } @@ -606,6 +606,9 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct pressure_floor_props *pressure_floor) { + /* Reset the SPH gradient flag */ + p->sph_gradients_flag = 0; + /* Compute the sound speed */ const float pressure = hydro_get_comoving_pressure(p); const float pressure_including_floor = @@ -689,7 +692,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( * * @param A The matrix to compute the condition number of. */ -__attribute__((always_inline)) INLINE static float +__attribute__((always_inline)) INLINE static double condition_number(gsl_matrix *A) { gsl_matrix *A_copy = gsl_matrix_alloc(3, 3); @@ -709,7 +712,7 @@ condition_number(gsl_matrix *A) { gsl_vector_free(S); gsl_vector_free(work); - return (s_min != 0.f) ? s_max / s_min : FLT_MAX; + return (s_min != 0.) ? s_max / s_min : const_condition_number_upper_limit; } /** @@ -744,7 +747,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( gsl_matrix_view C_view = gsl_matrix_view_array((double *)p->C, 3, 3); gsl_matrix *C = &C_view.matrix; - float cond = condition_number(C); + 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); @@ -774,10 +777,11 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( #endif /* Ill-condition matrix, revert back to normal SPH gradients */ + p->sph_gradients_flag = 1; for (int k = 0; k < 3; k++) { - p->C[k][0] = 0.f; - p->C[k][1] = 0.f; - p->C[k][2] = 0.f; + p->C[k][0] = 0.; + p->C[k][1] = 0.; + p->C[k][2] = 0.; } } @@ -817,9 +821,10 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( p->weighted_wcount = 1.f; p->weighted_neighbour_wcount = 1.f; + p->sph_gradients_flag = 1; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - p->C[i][j] = 0.f; + p->C[i][j] = 0.; p->viscosity.velocity_gradient[i][j] = 0.f; } } @@ -1044,7 +1049,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( */ __attribute__((always_inline)) INLINE static void hydro_end_force( struct part *restrict p, const struct cosmology *cosmo) { - p->force.h_dt *= p->h * hydro_dimension_inv; + p->force.h_dt *= p->h * hydro_dimension_inv; } /** diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 0069d15cdc..3b90caba4b 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -395,21 +395,48 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the kernel for hi. */ const float hi_inv = 1.0f / hi; - const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float hi_inv_dim = pow_dimension(hi_inv); + const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ const float xi = r * hi_inv; float wi, wi_dx; kernel_deval(xi, &wi, &wi_dx); - const float wi_dr = hid_inv * wi_dx; /* Get the kernel for hj. */ const float hj_inv = 1.0f / hj; - const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float hj_inv_dim = pow_dimension(hj_inv); + const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; /* 1/h^(d+1) */ const float xj = r * hj_inv; float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); - const float wj_dr = hjd_inv * wj_dx; - /* Compute dv dot r. */ + float wi_dr = hi_inv_dim_plus_one * wi_dx; + float wj_dr = hj_inv_dim_plus_one * wj_dx; + double G_ij[3] = {0., 0., 0.}; + + /* Always use SPH gradients between particles if one of them has an + * ill-conditioned C matrix */ + char sph_gradients_flag = 1; + if (!pi->sph_gradients_flag && !pj->sph_gradients_flag) { + sph_gradients_flag = 0; + double G_i[3] = {0., 0., 0.}; + double G_j[3] = {0., 0., 0.}; + for (int k = 0; k < 3; k++) { + for (int i = 0; i < 3; i++) { + /* Note: Negative because dx is (pj-pi) in Rosswog 2020 */ + G_i[k] -= pi->C[k][i] * dx[i] * wi * hi_inv_dim; + G_j[k] += pj->C[k][i] * dx[i] * wj * hj_inv_dim; + } + + G_ij[k] = 0.5 * (G_i[k] + G_j[k]); + } + } + + /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ + const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + + (pi->v[1] - pj->v[1]) * G_ij[1] + + (pi->v[2] - pj->v[2]) * G_ij[2]; + + /* Compute dv dot dr. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + (pi->v[2] - pj->v[2]) * dx[2]; @@ -421,10 +448,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float omega_ij = min(dvdr_Hubble, 0.f); const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ - /* Variable smoothing length term */ - const float kernel_gradient = - 0.5f * (wi_dr * pi->force.f + wj_dr * pj->force.f); - /* Construct the full viscosity term */ const float rho_ij = rhoi + rhoj; const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; @@ -436,53 +459,64 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( (0.5f * rho_ij) : 0.f; - /* Convolve with the kernel */ - const float visc_acc_term = visc * kernel_gradient * r_inv; - - /* SPH acceleration term */ - const float sph_acc_term = - (pressurei + pressurej) * r_inv * kernel_gradient / (pi->rho * pj->rho); - - /* Adaptive softening acceleration term */ - const float adapt_soft_acc_term = adaptive_softening_get_acc_term( - pi, pj, wi_dr, wj_dr, pi->force.f, pj->force.f, r_inv); + /* These are set whether or not we fall back onto SPH gradients */ + float visc_acc_term = visc; + float sph_acc_term = (pressurei + pressurej) / (pi->rho * pj->rho); + + float sph_du_term_i = pressurei / (pi->rho * pj->rho); + float sph_du_term_j = pressurej / (pi->rho * pj->rho); + float visc_du_term_i = 0.5f * visc_acc_term; + float visc_du_term_j = visc_du_term_i; + + if (sph_gradients_flag) { + /* Variable smoothing length term */ + const float kernel_gradient = + 0.5f * r_inv * (wi_dr * pi->force.f + wj_dr * pj->force.f); + + 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_i *= dvdr_Hubble * kernel_gradient; + visc_du_term_j = visc_du_term_i; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; + } + else { + sph_du_term_i *= dv_dot_G_ij; + sph_du_term_j *= dv_dot_G_ij; + visc_du_term_i *= dv_dot_G_ij + a2_Hubble + r2; + visc_du_term_j *= dv_dot_G_ij + a2_Hubble + r2; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dv_dot_G_ij / rhoj; + pj->force.h_dt -= mi * dv_dot_G_ij / rhoi; + } /* Assemble the acceleration */ - const float acc = sph_acc_term + visc_acc_term + adapt_soft_acc_term; + const float acc = sph_acc_term + visc_acc_term; /* Use the force Luke ! */ - pi->a_hydro[0] -= mj * acc * dx[0]; - pi->a_hydro[1] -= mj * acc * dx[1]; - pi->a_hydro[2] -= mj * acc * dx[2]; + pi->a_hydro[0] -= mj * acc * G_ij[0]; + pi->a_hydro[1] -= mj * acc * G_ij[1]; + pi->a_hydro[2] -= mj * acc * G_ij[2]; - pj->a_hydro[0] += mi * acc * dx[0]; - pj->a_hydro[1] += mi * acc * dx[1]; - pj->a_hydro[2] += mi * acc * dx[2]; + pj->a_hydro[0] += mi * acc * G_ij[0]; + pj->a_hydro[1] += mi * acc * G_ij[1]; + pj->a_hydro[2] += mi * acc * G_ij[2]; /* Get the time derivative for u. */ - const float sph_du_term_i = - pressurei * dvdr * r_inv * kernel_gradient / (pi->rho * pj->rho); - const float sph_du_term_j = - pressurej * dvdr * r_inv * kernel_gradient / (pi->rho * pj->rho); - - /* Viscosity term */ - const float visc_du_term = 0.5f * visc_acc_term * dvdr_Hubble; - - /* Diffusion term */ - const float diff_du_term = 2.f * (pi->diffusion.rate + pj->diffusion.rate) * - (pi->u - pj->u) * kernel_gradient / rho_ij; /* Assemble the energy equation term */ - const float du_dt_i = sph_du_term_i + visc_du_term + diff_du_term; - const float du_dt_j = sph_du_term_j + visc_du_term - diff_du_term; + const float du_dt_i = sph_du_term_i + visc_du_term_i; + const float du_dt_j = sph_du_term_j + visc_du_term_j; /* Internal energy time derivative */ pi->u_dt += du_dt_i * mj; pj->u_dt += du_dt_j * mi; - - /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; - pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; } /** @@ -519,21 +553,48 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get the kernel for hi. */ const float hi_inv = 1.0f / hi; - const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float hi_inv_dim = pow_dimension(hi_inv); + const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ const float xi = r * hi_inv; float wi, wi_dx; kernel_deval(xi, &wi, &wi_dx); - const float wi_dr = hid_inv * wi_dx; /* Get the kernel for hj. */ const float hj_inv = 1.0f / hj; - const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float hj_inv_dim = pow_dimension(hj_inv); + const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; /* 1/h^(d+1) */ const float xj = r * hj_inv; float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); - const float wj_dr = hjd_inv * wj_dx; - /* Compute dv dot r. */ + float wi_dr = hi_inv_dim_plus_one * wi_dx; + float wj_dr = hj_inv_dim_plus_one * wj_dx; + double G_ij[3] = {0., 0., 0.}; + + /* Always use SPH gradients between particles if one of them has an + * ill-conditioned C matrix */ + char sph_gradients_flag = 1; + if (!pi->sph_gradients_flag && !pj->sph_gradients_flag) { + sph_gradients_flag = 0; + double G_i[3] = {0., 0., 0.}; + double G_j[3] = {0., 0., 0.}; + for (int k = 0; k < 3; k++) { + for (int i = 0; i < 3; i++) { + /* Note: Negative because dx is (pj-pi) in Rosswog 2020 */ + G_i[k] -= pi->C[k][i] * dx[i] * wi * hi_inv_dim; + G_j[k] += pj->C[k][i] * dx[i] * wj * hj_inv_dim; + } + + G_ij[k] = 0.5 * (G_i[k] + G_j[k]); + } + } + + /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ + const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + + (pi->v[1] - pj->v[1]) * G_ij[1] + + (pi->v[2] - pj->v[2]) * G_ij[2]; + + /* Compute dv dot dr. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + (pi->v[2] - pj->v[2]) * dx[2]; @@ -545,10 +606,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float omega_ij = min(dvdr_Hubble, 0.f); const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ - /* Variable smoothing length term */ - const float kernel_gradient = - 0.5f * (wi_dr * pi->force.f + wj_dr * pj->force.f); - /* Construct the full viscosity term */ const float rho_ij = rhoi + rhoj; const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; @@ -560,44 +617,47 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( (0.5f * rho_ij) : 0.f; - /* Convolve with the kernel */ - const float visc_acc_term = visc * kernel_gradient * r_inv; - - /* SPH acceleration term */ - const float sph_acc_term = - (pressurei + pressurej) * r_inv * kernel_gradient / (pi->rho * pj->rho); + /* These are set whether or not we fall back onto SPH gradients */ + float visc_acc_term = visc; + float sph_acc_term = (pressurei + pressurej) / (pi->rho * pj->rho); + float sph_du_term_i = pressurei / (pi->rho * pj->rho); + float visc_du_term_i = visc; - /* Adaptive softening acceleration term */ - const float adapt_soft_acc_term = adaptive_softening_get_acc_term( - pi, pj, wi_dr, wj_dr, pi->force.f, pj->force.f, r_inv); + if (sph_gradients_flag) { + /* Variable smoothing length term */ + const float kernel_gradient = + 0.5f * r_inv * (wi_dr * pi->force.f + wj_dr * pj->force.f); - /* Assemble the acceleration */ - const float acc = sph_acc_term + visc_acc_term + adapt_soft_acc_term; + visc_acc_term *= kernel_gradient; + sph_acc_term *= kernel_gradient; + sph_du_term_i *= dvdr * kernel_gradient; + visc_du_term_i *= dvdr_Hubble * kernel_gradient; - /* Use the force Luke ! */ - pi->a_hydro[0] -= mj * acc * dx[0]; - pi->a_hydro[1] -= mj * acc * dx[1]; - pi->a_hydro[2] -= mj * acc * dx[2]; + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + } + else { + sph_du_term_i *= dv_dot_G_ij; + visc_du_term_i *= dv_dot_G_ij + a2_Hubble * r2; - /* Get the time derivative for u. */ - const float sph_du_term_i = - pressurei * dvdr * r_inv * kernel_gradient / (pi->rho * pj->rho); + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dv_dot_G_ij / rhoj; + } - /* Viscosity term */ - const float visc_du_term = 0.5f * visc_acc_term * dvdr_Hubble; + /* Assemble the acceleration */ + const float acc = sph_acc_term + visc_acc_term; - /* Diffusion term */ - const float diff_du_term = 2.f * (pi->diffusion.rate + pj->diffusion.rate) * - (pi->u - pj->u) * kernel_gradient / rho_ij; + /* Use the force Luke ! */ + pi->a_hydro[0] -= mj * acc * G_ij[0]; + pi->a_hydro[1] -= mj * acc * G_ij[1]; + pi->a_hydro[2] -= mj * acc * G_ij[2]; /* Assemble the energy equation term */ - const float du_dt_i = sph_du_term_i + visc_du_term + diff_du_term; + const float du_dt_i = sph_du_term_i + visc_du_term_i; /* Internal energy time derivative */ pi->u_dt += du_dt_i * mj; - /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; } #endif /* SWIFT_MAGMA2_HYDRO_IACT_H */ diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 19657ddd3a..425cac5904 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -46,7 +46,7 @@ */ /*! Consider C matrix can be ill-conditioned above this limit */ -#define const_condition_number_upper_limit 1.e4f +#define const_condition_number_upper_limit 99.0 /*! Viscosity parameters -- FIXED -- MUST BE DEFINED AT COMPILE-TIME */ diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 62c7427a00..4cacc978a1 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -137,12 +137,12 @@ struct part { /*! Neighbour weightings */ float weighted_neighbour_wcount; - /*! Kernel derivatives */ - float G[3]; - /*! Correction matrix (C^ki in Rosswog 2020) */ - float C[3][3]; + double C[3][3]; + /*! Flag as to whether the SPH gradients should be used */ + char sph_gradients_flag; + #ifdef MAGMA2_DEBUG_CHECKS struct { /*! C matrix at the last time it was ill-conditioned */ From c419a9948a773e0eb9564783e0491dac8fdeeee4 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Sat, 26 Jul 2025 09:29:34 -0400 Subject: [PATCH 04/39] Rosswog 2020 has Ga and Gb using the same dx vector. That is important for the antisymmetry of the problem. Also compute dh/dt using the density of particle pi rather than summing over pj contributions. --- src/hydro/MAGMA2/hydro.h | 4 +++- src/hydro/MAGMA2/hydro_iact.h | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index e86870440b..be9d370e01 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -1049,7 +1049,9 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( */ __attribute__((always_inline)) INLINE static void hydro_end_force( struct part *restrict p, const struct cosmology *cosmo) { - p->force.h_dt *= p->h * hydro_dimension_inv; + + const float rho_inv = hydro_get_comoving_density(p); + p->force.h_dt *= p->h * rho_inv * hydro_dimension_inv; } /** diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 3b90caba4b..df1d2b71f3 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -422,9 +422,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( double G_j[3] = {0., 0., 0.}; for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { - /* Note: Negative because dx is (pj-pi) in Rosswog 2020 */ + /* Note: Negative because dx is (pj-pi) in Rosswog 2020. + * It is (pj-pi) for both particles. */ G_i[k] -= pi->C[k][i] * dx[i] * wi * hi_inv_dim; - G_j[k] += pj->C[k][i] * dx[i] * wj * hj_inv_dim; + G_j[k] -= pj->C[k][i] * dx[i] * wj * hj_inv_dim; } G_ij[k] = 0.5 * (G_i[k] + G_j[k]); @@ -482,8 +483,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( visc_du_term_j = visc_du_term_i; /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; - pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; + pi->force.h_dt -= mj * dvdr * r_inv * wi_dr; + pj->force.h_dt -= mi * dvdr * r_inv * wj_dr; } else { sph_du_term_i *= dv_dot_G_ij; @@ -492,8 +493,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( visc_du_term_j *= dv_dot_G_ij + a2_Hubble + r2; /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dv_dot_G_ij / rhoj; - pj->force.h_dt -= mi * dv_dot_G_ij / rhoi; + pi->force.h_dt -= mj * dv_dot_G_ij; + pj->force.h_dt -= mi * dv_dot_G_ij; } /* Assemble the acceleration */ @@ -582,7 +583,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( for (int i = 0; i < 3; i++) { /* Note: Negative because dx is (pj-pi) in Rosswog 2020 */ G_i[k] -= pi->C[k][i] * dx[i] * wi * hi_inv_dim; - G_j[k] += pj->C[k][i] * dx[i] * wj * hj_inv_dim; + G_j[k] -= pj->C[k][i] * dx[i] * wj * hj_inv_dim; } G_ij[k] = 0.5 * (G_i[k] + G_j[k]); @@ -634,14 +635,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( visc_du_term_i *= dvdr_Hubble * kernel_gradient; /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + pi->force.h_dt -= mj * dvdr * r_inv * wi_dr; } else { sph_du_term_i *= dv_dot_G_ij; visc_du_term_i *= dv_dot_G_ij + a2_Hubble * r2; /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dv_dot_G_ij / rhoj; + pi->force.h_dt -= mj * dv_dot_G_ij; } /* Assemble the acceleration */ From 85d8344bbf48c15f78fcb68b3b5db896e5ab2fd3 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Sat, 26 Jul 2025 11:16:14 -0400 Subject: [PATCH 05/39] Equations 19 and 20 from Rosswog are now stored in the particle structure. I moved all of the gradient relevant tensors and quantites into a new gradients struct in the particle. --- src/hydro/MAGMA2/hydro.h | 222 +++++++++++++++-------- src/hydro/MAGMA2/hydro_debug.h | 24 +-- src/hydro/MAGMA2/hydro_iact.h | 144 ++++++++------- src/hydro/MAGMA2/hydro_io.h | 8 +- src/hydro/MAGMA2/hydro_part.h | 35 ++-- src/sink/GEAR/sink_getters.h | 6 +- src/star_formation/GEAR/star_formation.h | 6 +- 7 files changed, 269 insertions(+), 176 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index be9d370e01..3e552d8851 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -514,21 +514,52 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->weighted_neighbour_wcount = 0.f; p->density.rho_dh = 0.f; - p->smooth_pressure_gradient[0] = 0.f; - p->smooth_pressure_gradient[1] = 0.f; - p->smooth_pressure_gradient[2] = 0.f; + p->gradients.pressure[0] = 0.f; + p->gradients.pressure[1] = 0.f; + p->gradients.pressure[2] = 0.f; p->viscosity.shock_indicator = 0.f; p->viscosity.shock_limiter = 0.f; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - p->C[i][j] = 0.; - p->viscosity.velocity_gradient[i][j] = 0.f; + p->gradients.C[i][j] = 0.; + p->gradients.velocity_tensor[i][j] = 0.f; + p->gradients.velocity_tensor_aux[i][j] = 0.f; + p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; } } } +/** + * @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 Finishes the density calculation. * @@ -564,23 +595,89 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* Finish caclulation of the pressure gradients; note that the * kernel derivative is zero at our particle's position. */ - p->smooth_pressure_gradient[0] *= hydro_gamma_minus_one * h_inv_dim_plus_one; - p->smooth_pressure_gradient[1] *= hydro_gamma_minus_one * h_inv_dim_plus_one; - p->smooth_pressure_gradient[2] *= hydro_gamma_minus_one * h_inv_dim_plus_one; - - /* Finish calculation of the velocity gradient tensor, and - * guard against FPEs here. */ - const float velocity_gradient_norm = - p->weighted_wcount == 0.f ? 0.f - : 3.f * cosmo->a2_inv / p->weighted_wcount; + p->gradients.pressure[0] *= hydro_gamma_minus_one * h_inv_dim_plus_one; + p->gradients.pressure[1] *= hydro_gamma_minus_one * h_inv_dim_plus_one; + p->gradients.pressure[2] *= hydro_gamma_minus_one * h_inv_dim_plus_one; + double aux_norm[3][3]; 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 p->gradients.C[3][3] 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++) { + aux_norm[i][0] = gsl_matrix_get(A_inv, i, 0); + aux_norm[i][1] = gsl_matrix_get(A_inv, i, 1); + 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 */ + double aux_matrix[3][3]; for (int j = 0; j < 3; j++) { - const float hubble_term = i == j ? hydro_dimension * cosmo->H : 0.f; + for (int i = 0; i < 3; i++) { + aux_matrix[j][i] = 0.; + + /** + * 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] += aux_norm[j][k] * p->gradients.velocity_tensor_aux[i][k]; + } + } + } - p->viscosity.velocity_gradient[i][j] *= velocity_gradient_norm; + /* TODO: Change this from physical to comoving eventually */ + for (int a = 0; a < 3; a++) { + for (int b = 0; b < 3; b++) { + aux_matrix[a][b] *= cosmo->a2_inv; + if (a == b) aux_matrix[a][b] += cosmo->H; + + p->gradients.velocity_tensor_aux[a][b] = aux_matrix[a][b]; + + /* Co-moving */ + p->gradients.velocity_tensor_aux_norm[a][b] = aux_norm[a][b]; + } + } + } + else { - p->viscosity.velocity_gradient[i][j] += hubble_term; + /* Ensure no crazy gradients later */ + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + p->gradients.velocity_tensor_aux[i][j] = 0.f; + p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; + } } } } @@ -606,9 +703,6 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct pressure_floor_props *pressure_floor) { - /* Reset the SPH gradient flag */ - p->sph_gradients_flag = 0; - /* Compute the sound speed */ const float pressure = hydro_get_comoving_pressure(p); const float pressure_including_floor = @@ -621,19 +715,19 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( p->force.soundspeed = soundspeed; const float mod_pressure_gradient = - sqrtf(p->smooth_pressure_gradient[0] * p->smooth_pressure_gradient[0] + - p->smooth_pressure_gradient[1] * p->smooth_pressure_gradient[1] + - p->smooth_pressure_gradient[2] * p->smooth_pressure_gradient[2]); + sqrtf(p->gradients.pressure[0] * p->gradients.pressure[0] + + p->gradients.pressure[1] * p->gradients.pressure[1] + + p->gradients.pressure[2] * p->gradients.pressure[2]); float unit_pressure_gradient[3]; /* As this is normalised, no cosmology factor is required */ unit_pressure_gradient[0] = - p->smooth_pressure_gradient[0] / mod_pressure_gradient; + p->gradients.pressure[0] / mod_pressure_gradient; unit_pressure_gradient[1] = - p->smooth_pressure_gradient[1] / mod_pressure_gradient; + p->gradients.pressure[1] / mod_pressure_gradient; unit_pressure_gradient[2] = - p->smooth_pressure_gradient[2] / mod_pressure_gradient; + p->gradients.pressure[2] / mod_pressure_gradient; float dv_dn = 0.f; float shear_norm2 = 0.f; @@ -643,16 +737,16 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { dv_dn += unit_pressure_gradient[i] * - p->viscosity.velocity_gradient[i][j] * unit_pressure_gradient[j]; + p->gradients.velocity_tensor_aux[i][j] * unit_pressure_gradient[j]; const float shear_component = - 0.5f * (p->viscosity.velocity_gradient[i][j] + - p->viscosity.velocity_gradient[j][i]); + 0.5f * (p->gradients.velocity_tensor_aux[i][j] + + p->gradients.velocity_tensor_aux[j][i]); const float shear_component2 = shear_component * shear_component; shear_norm2 += shear_component2; traceless_shear_norm2 += i == j ? 0.f : shear_component2; - div_v += i == j ? (1. / 3.) * p->viscosity.velocity_gradient[i][j] : 0.f; + div_v += i == j ? p->gradients.velocity_tensor_aux[i][j] : 0.f; } } @@ -683,36 +777,9 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( */ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( struct part *restrict p) { - p->viscosity.v_sig = p->force.soundspeed; -} - -/** - * @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; + p->gradients.C_well_conditioned = 0; + p->viscosity.v_sig = p->force.soundspeed; } /** @@ -727,6 +794,7 @@ condition_number(gsl_matrix *A) { */ __attribute__((always_inline)) INLINE static void hydro_end_gradient( struct part *p) { + /* The f_i is calculated explicitly in MAGMA2. */ p->force.f = p->weighted_wcount / (p->weighted_neighbour_wcount * p->rho); @@ -738,13 +806,14 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( /* Apply correct normalisation */ p->viscosity.shock_limiter *= h_inv_dim; for (int k = 0; k < 3; k++) { - p->C[k][0] *= h_inv_dim; - p->C[k][1] *= h_inv_dim; - p->C[k][2] *= h_inv_dim; + p->gradients.C[k][0] *= h_inv_dim; + p->gradients.C[k][1] *= h_inv_dim; + p->gradients.C[k][2] *= h_inv_dim; } - /* Invert the p->C[3][3] matrix */ - gsl_matrix_view C_view = gsl_matrix_view_array((double *)p->C, 3, 3); + /* Invert the p->gradients.C[3][3] matrix */ + gsl_matrix_view C_view = + gsl_matrix_view_array((double *)p->gradients.C, 3, 3); gsl_matrix *C = &C_view.matrix; double cond = condition_number(C); @@ -758,7 +827,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { - p->C[k][i] = gsl_matrix_get(C_inv, k, i); + p->gradients.C[k][i] = gsl_matrix_get(C_inv, k, i); } } @@ -768,20 +837,20 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( else { #ifdef MAGMA2_DEBUG_CHECKS for (int k = 0; k < 3; k++) { - p->debug.C[k][0] = p->C[k][0]; - p->debug.C[k][1] = p->C[k][1]; - p->debug.C[k][2] = p->C[k][2]; + p->debug.C[k][0] = p->gradients.C[k][0]; + p->debug.C[k][1] = p->gradients.C[k][1]; + p->debug.C[k][2] = p->gradients.C[k][2]; } p->debug.ill_conditioned_count++; #endif /* Ill-condition matrix, revert back to normal SPH gradients */ - p->sph_gradients_flag = 1; + p->gradients.C_well_conditioned = 0; for (int k = 0; k < 3; k++) { - p->C[k][0] = 0.; - p->C[k][1] = 0.; - p->C[k][2] = 0.; + p->gradients.C[k][0] = 0.; + p->gradients.C[k][1] = 0.; + p->gradients.C[k][2] = 0.; } } @@ -821,11 +890,14 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( p->weighted_wcount = 1.f; p->weighted_neighbour_wcount = 1.f; - p->sph_gradients_flag = 1; + p->gradients.C_well_conditioned = 0; for (int i = 0; i < 3; i++) { + p->gradients.pressure[0] = 0.f; for (int j = 0; j < 3; j++) { - p->C[i][j] = 0.; - p->viscosity.velocity_gradient[i][j] = 0.f; + p->gradients.C[i][j] = 0.; + p->gradients.velocity_tensor[i][j] = 0.f; + p->gradients.velocity_tensor_aux[i][j] = 0.f; + p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; } } } diff --git a/src/hydro/MAGMA2/hydro_debug.h b/src/hydro/MAGMA2/hydro_debug.h index 958ed82a3e..a9005400a9 100644 --- a/src/hydro/MAGMA2/hydro_debug.h +++ b/src/hydro/MAGMA2/hydro_debug.h @@ -46,18 +46,18 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "[%.3e,%.3e,%.3e]," "[%.3e,%.3e,%.3e]]", p->id, p->viscosity.alpha, p->time_bin, p->rho, - p->viscosity.velocity_gradient[0][0], - p->viscosity.velocity_gradient[0][1], - p->viscosity.velocity_gradient[0][2], - p->viscosity.velocity_gradient[1][0], - p->viscosity.velocity_gradient[1][1], - p->viscosity.velocity_gradient[1][2], - p->viscosity.velocity_gradient[2][0], - p->viscosity.velocity_gradient[2][1], - p->viscosity.velocity_gradient[2][2]); - warning("[PID%lld] smooth_pressure_gradient=[%.3e,%.3e,%.3e]", p->id, - p->smooth_pressure_gradient[0], p->smooth_pressure_gradient[1], - p->smooth_pressure_gradient[2]); + 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]); + warning("[PID%lld] gradients.pressure=[%.3e,%.3e,%.3e]", p->id, + p->gradients.pressure[0], p->gradients.pressure[1], + p->gradients.pressure[2]); warning("[PID%lld] weighted_wcount=%.3e", p->id, p->weighted_wcount); if (xp != NULL) { diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index df1d2b71f3..881fd78438 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -90,25 +90,31 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( const float facj = mi * wj_dx * r_inv; /* Smooth pressure gradient */ - pi->smooth_pressure_gradient[0] += faci * pj->u * dx[0]; - pi->smooth_pressure_gradient[1] += faci * pj->u * dx[1]; - pi->smooth_pressure_gradient[2] += faci * pj->u * dx[2]; + pi->gradients.pressure[0] += faci * pj->u * dx[0]; + pi->gradients.pressure[1] += faci * pj->u * dx[1]; + pi->gradients.pressure[2] += faci * pj->u * dx[2]; - pj->smooth_pressure_gradient[0] -= facj * pi->u * dx[0]; - pj->smooth_pressure_gradient[1] -= facj * pi->u * dx[1]; - pj->smooth_pressure_gradient[2] -= facj * pi->u * dx[2]; + pj->gradients.pressure[0] -= facj * pi->u * dx[0]; + pj->gradients.pressure[1] -= facj * pi->u * dx[1]; + pj->gradients.pressure[2] -= facj * pi->u * dx[2]; - /* Finally, the big boy; the velocity gradient tensor. Note that the - * loops here are over the coordinates, i=0 -> x, and so on. */ + const float 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 (int j = 0; j < 3; j++) { - const float dv_ij = pi->v[i] - pj->v[i]; - const float dx_ij = pi->x[j] - pj->x[j]; - - pi->viscosity.velocity_gradient[i][j] += - mj * dv_ij * dx_ij * wi_dx * r_inv; - pj->viscosity.velocity_gradient[i][j] += - mi * dv_ij * dx_ij * wj_dx * r_inv; + for (int k = 0; k < 3; k++) { + pi->gradients.velocity_tensor_aux[i][k] += + mj * dv[i] * dx[k] * wi_dx * r_inv; + pj->gradients.velocity_tensor_aux[i][k] += + mi * dv[i] * dx[k] * wj_dx * r_inv; + + pi->gradients.velocity_tensor_aux_norm[i][k] += + mj * dx[i] * dx[k] * wi_dx * r_inv; + pj->gradients.velocity_tensor_aux_norm[i][k] += + mi * dx[i] * dx[k] * wj_dx * r_inv; } } @@ -159,19 +165,23 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( const float faci = mj * wi_dx * r_inv; /* Compute pressure gradient */ - pi->smooth_pressure_gradient[0] += faci * pj->u * dx[0]; - pi->smooth_pressure_gradient[1] += faci * pj->u * dx[1]; - pi->smooth_pressure_gradient[2] += faci * pj->u * dx[2]; + pi->gradients.pressure[0] += faci * pj->u * dx[0]; + pi->gradients.pressure[1] += faci * pj->u * dx[1]; + pi->gradients.pressure[2] += faci * pj->u * dx[2]; + + const float dv[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; - /* Finally, the big boy; the velocity gradient tensor. Note that the - * loops here are over the coordinates, i=0 -> x, and so on. */ + /* 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 (int j = 0; j < 3; j++) { - const float dv_ij = pi->v[i] - pj->v[i]; - const float dx_ij = pi->x[j] - pj->x[j]; + for (int k = 0; k < 3; k++) { + pi->gradients.velocity_tensor_aux[i][k] += + mj * dv[i] * dx[k] * wi_dx * r_inv; - pi->viscosity.velocity_gradient[i][j] += - mj * dv_ij * dx_ij * wi_dx * r_inv; + pi->gradients.velocity_tensor_aux_norm[i][k] += + mj * dx[i] * dx[k] * wi_dx * r_inv; } } @@ -271,8 +281,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { /* dx is signed as (pi - pj), but it is symmetric so we add */ - pi->C[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; - pj->C[k][i] += mi * rho_inv_i * dx[k] * dx[i] * wj; + pi->gradients.C[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; + pj->gradients.C[k][i] += mi * rho_inv_i * dx[k] * dx[i] * wj; } } } @@ -355,7 +365,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { /* dx is signed as (pi - pj), but it is symmetric so we add */ - pi->C[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; + pi->gradients.C[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; } } } @@ -396,7 +406,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the kernel for hi. */ const float hi_inv = 1.0f / hi; const float hi_inv_dim = pow_dimension(hi_inv); - const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ const float xi = r * hi_inv; float wi, wi_dx; kernel_deval(xi, &wi, &wi_dx); @@ -404,28 +413,25 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the kernel for hj. */ const float hj_inv = 1.0f / hj; const float hj_inv_dim = pow_dimension(hj_inv); - const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; /* 1/h^(d+1) */ const float xj = r * hj_inv; float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); - float wi_dr = hi_inv_dim_plus_one * wi_dx; - float wj_dr = hj_inv_dim_plus_one * wj_dx; double G_ij[3] = {0., 0., 0.}; /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ - char sph_gradients_flag = 1; - if (!pi->sph_gradients_flag && !pj->sph_gradients_flag) { - sph_gradients_flag = 0; + char C_well_conditioned = 0; + if (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned) { + C_well_conditioned = 1; double G_i[3] = {0., 0., 0.}; double G_j[3] = {0., 0., 0.}; for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { /* Note: Negative because dx is (pj-pi) in Rosswog 2020. * It is (pj-pi) for both particles. */ - G_i[k] -= pi->C[k][i] * dx[i] * wi * hi_inv_dim; - G_j[k] -= pj->C[k][i] * dx[i] * wj * hj_inv_dim; + G_i[k] -= pi->gradients.C[k][i] * dx[i] * wi * hi_inv_dim; + G_j[k] -= pj->gradients.C[k][i] * dx[i] * wj * hj_inv_dim; } G_ij[k] = 0.5 * (G_i[k] + G_j[k]); @@ -469,7 +475,22 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( float visc_du_term_i = 0.5f * visc_acc_term; float visc_du_term_j = visc_du_term_i; - if (sph_gradients_flag) { + if (C_well_conditioned) { + sph_du_term_i *= dv_dot_G_ij; + sph_du_term_j *= dv_dot_G_ij; + visc_du_term_i *= dv_dot_G_ij + a2_Hubble + r2; + visc_du_term_j *= dv_dot_G_ij + a2_Hubble + r2; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dv_dot_G_ij; + pj->force.h_dt -= mi * dv_dot_G_ij; + } + else { + const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ + const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; + const float wi_dr = hi_inv_dim_plus_one * wi_dx; + const float wj_dr = hj_inv_dim_plus_one * wj_dx; + /* Variable smoothing length term */ const float kernel_gradient = 0.5f * r_inv * (wi_dr * pi->force.f + wj_dr * pj->force.f); @@ -486,16 +507,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pi->force.h_dt -= mj * dvdr * r_inv * wi_dr; pj->force.h_dt -= mi * dvdr * r_inv * wj_dr; } - else { - sph_du_term_i *= dv_dot_G_ij; - sph_du_term_j *= dv_dot_G_ij; - visc_du_term_i *= dv_dot_G_ij + a2_Hubble + r2; - visc_du_term_j *= dv_dot_G_ij + a2_Hubble + r2; - - /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dv_dot_G_ij; - pj->force.h_dt -= mi * dv_dot_G_ij; - } /* Assemble the acceleration */ const float acc = sph_acc_term + visc_acc_term; @@ -555,7 +566,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get the kernel for hi. */ const float hi_inv = 1.0f / hi; const float hi_inv_dim = pow_dimension(hi_inv); - const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ const float xi = r * hi_inv; float wi, wi_dx; kernel_deval(xi, &wi, &wi_dx); @@ -563,27 +573,24 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get the kernel for hj. */ const float hj_inv = 1.0f / hj; const float hj_inv_dim = pow_dimension(hj_inv); - const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; /* 1/h^(d+1) */ const float xj = r * hj_inv; float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); - float wi_dr = hi_inv_dim_plus_one * wi_dx; - float wj_dr = hj_inv_dim_plus_one * wj_dx; double G_ij[3] = {0., 0., 0.}; /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ - char sph_gradients_flag = 1; - if (!pi->sph_gradients_flag && !pj->sph_gradients_flag) { - sph_gradients_flag = 0; + char C_well_conditioned = 0; + if (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned) { + C_well_conditioned = 1; double G_i[3] = {0., 0., 0.}; double G_j[3] = {0., 0., 0.}; for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { /* Note: Negative because dx is (pj-pi) in Rosswog 2020 */ - G_i[k] -= pi->C[k][i] * dx[i] * wi * hi_inv_dim; - G_j[k] -= pj->C[k][i] * dx[i] * wj * hj_inv_dim; + G_i[k] -= pi->gradients.C[k][i] * dx[i] * wi * hi_inv_dim; + G_j[k] -= pj->gradients.C[k][i] * dx[i] * wj * hj_inv_dim; } G_ij[k] = 0.5 * (G_i[k] + G_j[k]); @@ -624,7 +631,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( float sph_du_term_i = pressurei / (pi->rho * pj->rho); float visc_du_term_i = visc; - if (sph_gradients_flag) { + if (C_well_conditioned) { + sph_du_term_i *= dv_dot_G_ij; + visc_du_term_i *= dv_dot_G_ij + a2_Hubble * r2; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dv_dot_G_ij; + } + else { + const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ + const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; + const float wi_dr = hi_inv_dim_plus_one * wi_dx; + const float wj_dr = hj_inv_dim_plus_one * wj_dx; + /* Variable smoothing length term */ const float kernel_gradient = 0.5f * r_inv * (wi_dr * pi->force.f + wj_dr * pj->force.f); @@ -637,13 +656,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get the time derivative for h. */ pi->force.h_dt -= mj * dvdr * r_inv * wi_dr; } - else { - sph_du_term_i *= dv_dot_G_ij; - visc_du_term_i *= dv_dot_G_ij + a2_Hubble * r2; - - /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dv_dot_G_ij; - } /* Assemble the acceleration */ const float acc = sph_acc_term + visc_acc_term; diff --git a/src/hydro/MAGMA2/hydro_io.h b/src/hydro/MAGMA2/hydro_io.h index 346763fb0b..ec84ea274c 100644 --- a/src/hydro/MAGMA2/hydro_io.h +++ b/src/hydro/MAGMA2/hydro_io.h @@ -76,11 +76,9 @@ INLINE static void convert_P(const struct engine* e, const struct part* p, INLINE static void convert_div_v(const struct engine* e, const struct part* p, const struct xpart* xp, float* ret) { - /* The velocity divergence is the 1/3 of the trace of the velocity - * gradient tensor */ - ret[0] = (1. / 3.) * (p->viscosity.velocity_gradient[0][0] + - p->viscosity.velocity_gradient[1][1] + - p->viscosity.velocity_gradient[2][2]); + 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, diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 4cacc978a1..b52f95ccbe 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -128,21 +128,12 @@ struct part { /*! Particle density. */ float rho; - /*! Smoothed pressure gradient */ - float smooth_pressure_gradient[3]; - /*! Weightings for correction factor */ float weighted_wcount; /*! Neighbour weightings */ float weighted_neighbour_wcount; - /*! Correction matrix (C^ki in Rosswog 2020) */ - double C[3][3]; - - /*! Flag as to whether the SPH gradients should be used */ - char sph_gradients_flag; - #ifdef MAGMA2_DEBUG_CHECKS struct { /*! C matrix at the last time it was ill-conditioned */ @@ -154,11 +145,31 @@ struct part { } debug; #endif - /* Store viscosity information in a separate struct. */ + /* Store gradients in a separate struct */ struct { - /*! Velocity gradient tensor (physical) */ - float velocity_gradient[3][3]; + /*! Correction matrix (C^ki in Rosswog 2020) */ + double C[3][3]; + + /*! Flag for whether C is ill-conditioned */ + char C_well_conditioned; + + /*! Full velocity gradient tensor */ + float velocity_tensor[3][3]; + + /*! Auxiliary full velocity gradient tensor */ + float velocity_tensor_aux[3][3]; + + /*! Normalization for computing velocity_tensor_aux */ + float velocity_tensor_aux_norm[3][3]; + + /*! Smoothed pressure gradient */ + float pressure[3]; + + } gradients; + + /* Store viscosity information in a separate struct. */ + struct { /*! Velocity gradient tensor trace norm |T| */ float tensor_norm; diff --git a/src/sink/GEAR/sink_getters.h b/src/sink/GEAR/sink_getters.h index a7fdd15172..c25199cb1b 100644 --- a/src/sink/GEAR/sink_getters.h +++ b/src/sink/GEAR/sink_getters.h @@ -108,9 +108,9 @@ INLINE static float sink_get_physical_div_v_from_part( p->viscosity.velocity_gradient[2][2]); #elif MAGMA2_SPH /* Copy the velocity divergence */ - div_v = p->viscosity.velocity_gradient[0][0] + - p->viscosity.velocity_gradient[1][1] + - p->viscosity.velocity_gradient[2][2]; + div_v = p->gradients.velocity_tensor_aux[0][0] + + p->gradients.velocity_tensor_aux[1][1] + + p->gradients.velocity_tensor_aux[2][2]; #elif HOPKINS_PU_SPH div_v = p->density.div_v; #else diff --git a/src/star_formation/GEAR/star_formation.h b/src/star_formation/GEAR/star_formation.h index 1a71ee287c..73cc26c3fd 100644 --- a/src/star_formation/GEAR/star_formation.h +++ b/src/star_formation/GEAR/star_formation.h @@ -450,9 +450,9 @@ __attribute__((always_inline)) INLINE static void star_formation_end_density( p->viscosity.velocity_gradient[2][2]); #elif MAGMA2_SPH /* Copy the velocity divergence */ - xp->sf_data.div_v = p->viscosity.velocity_gradient[0][0] + - p->viscosity.velocity_gradient[1][1] + - p->viscosity.velocity_gradient[2][2]; + xp->sf_data.div_v = p->gradients.velocity_tensor_aux[0][0] + + p->gradients.velocity_tensor_aux[1][1] + + p->gradients.velocity_tensor_aux[2][2]; #elif HOPKINS_PU_SPH xp->sf_data.div_v = p->density.div_v; #else From ed11b98c55c862b0d16f08def104a0b6704e79dc Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Sat, 26 Jul 2025 12:58:43 -0400 Subject: [PATCH 06/39] Renamed some variables to be more clear about the matrices. Added more debugging for the auxiliary velocity gradient tensor. Fixed a couple of issues with the original Gasoline implementation in computing divergences. --- src/hydro/MAGMA2/hydro.h | 104 ++++++++++++++++++++++++---------- src/hydro/MAGMA2/hydro_iact.h | 14 ++--- src/hydro/MAGMA2/hydro_io.h | 23 ++++++-- src/hydro/MAGMA2/hydro_part.h | 18 +++++- 4 files changed, 114 insertions(+), 45 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 3e552d8851..19325f66c4 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -521,10 +521,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->viscosity.shock_indicator = 0.f; p->viscosity.shock_limiter = 0.f; + /* These must be zeroed before the density loop */ for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - p->gradients.C[i][j] = 0.; - p->gradients.velocity_tensor[i][j] = 0.f; p->gradients.velocity_tensor_aux[i][j] = 0.f; p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; } @@ -612,7 +611,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( aux_norm[i][2] = p->gradients.velocity_tensor_aux_norm[i][2]; } - /* Invert the p->gradients.C[3][3] matrix */ + /* 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; @@ -657,24 +656,31 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( } } - /* TODO: Change this from physical to comoving eventually */ + /* Copy over the matrices for use later */ for (int a = 0; a < 3; a++) { for (int b = 0; b < 3; b++) { - aux_matrix[a][b] *= cosmo->a2_inv; - if (a == b) aux_matrix[a][b] += cosmo->H; - p->gradients.velocity_tensor_aux[a][b] = aux_matrix[a][b]; - - /* Co-moving */ p->gradients.velocity_tensor_aux_norm[a][b] = aux_norm[a][b]; } } } else { +#ifdef MAGMA2_DEBUG_CHECKS + p->debug.D_well_conditioned = 0; + 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.f; p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; } @@ -734,19 +740,26 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( float traceless_shear_norm2 = 0.f; float div_v = 0.f; + /* Physical velocity gradient tensor with Hubble flow */ + float velocity_tensor_phys[3][3] = {0}; + for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { + /* Convert to physical for use in artificial viscosity */ + velocity_tensor_phys[i][j] = p->gradients.velocity_tensor_aux[i][j]; + velocity_tensor_phys[i][j] *= cosmo->a2_inv; + if (i == j) velocity_tensor_phys[i][j] += cosmo->H; + dv_dn += unit_pressure_gradient[i] * - p->gradients.velocity_tensor_aux[i][j] * unit_pressure_gradient[j]; + velocity_tensor_phys[i][j] * unit_pressure_gradient[j]; const float shear_component = - 0.5f * (p->gradients.velocity_tensor_aux[i][j] + - p->gradients.velocity_tensor_aux[j][i]); + 0.5f * (velocity_tensor_phys[i][j] + velocity_tensor_phys[j][i]); const float shear_component2 = shear_component * shear_component; shear_norm2 += shear_component2; traceless_shear_norm2 += i == j ? 0.f : shear_component2; - div_v += i == j ? p->gradients.velocity_tensor_aux[i][j] : 0.f; + div_v += i == j ? velocity_tensor_phys[i][j] : 0.f; } } @@ -778,8 +791,21 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( __attribute__((always_inline)) INLINE static void hydro_reset_gradient( struct part *restrict p) { - p->gradients.C_well_conditioned = 0; + p->gradients.C_well_conditioned = 1; +#ifdef MAGMA2_DEBUG_CHECKS + p->debug.D_well_conditioned = 1; +#endif p->viscosity.v_sig = p->force.soundspeed; + + for (int i = 0; i < 3; i++) { + 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.f; + p->gradients.velocity_tensor[i][1] = 0.f; + p->gradients.velocity_tensor[i][2] = 0.f; + } } /** @@ -806,14 +832,14 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( /* Apply correct normalisation */ p->viscosity.shock_limiter *= h_inv_dim; for (int k = 0; k < 3; k++) { - p->gradients.C[k][0] *= h_inv_dim; - p->gradients.C[k][1] *= h_inv_dim; - p->gradients.C[k][2] *= h_inv_dim; + 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; } - /* Invert the p->gradients.C[3][3] matrix */ + /* Invert the p->gradients.correction_matrix[3][3] matrix */ gsl_matrix_view C_view = - gsl_matrix_view_array((double *)p->gradients.C, 3, 3); + gsl_matrix_view_array((double *)p->gradients.correction_matrix, 3, 3); gsl_matrix *C = &C_view.matrix; double cond = condition_number(C); @@ -826,9 +852,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( gsl_linalg_LU_invert(C, p_perm, C_inv); for (int k = 0; k < 3; k++) { - for (int i = 0; i < 3; i++) { - p->gradients.C[k][i] = gsl_matrix_get(C_inv, k, i); - } + 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); @@ -837,20 +863,20 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( else { #ifdef MAGMA2_DEBUG_CHECKS for (int k = 0; k < 3; k++) { - p->debug.C[k][0] = p->gradients.C[k][0]; - p->debug.C[k][1] = p->gradients.C[k][1]; - p->debug.C[k][2] = p->gradients.C[k][2]; + 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.ill_conditioned_count++; + 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.C[k][0] = 0.; - p->gradients.C[k][1] = 0.; - p->gradients.C[k][2] = 0.; + p->gradients.correction_matrix[k][0] = 0.; + p->gradients.correction_matrix[k][1] = 0.; + p->gradients.correction_matrix[k][2] = 0.; } } @@ -894,7 +920,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( for (int i = 0; i < 3; i++) { p->gradients.pressure[0] = 0.f; for (int j = 0; j < 3; j++) { - p->gradients.C[i][j] = 0.; + p->gradients.correction_matrix[i][j] = 0.; p->gradients.velocity_tensor[i][j] = 0.f; p->gradients.velocity_tensor_aux[i][j] = 0.f; p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; @@ -1241,8 +1267,24 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( p->viscosity.shock_indicator_previous_step = 0.f; p->viscosity.tensor_norm = 0.f; + p->gradients.C_well_conditioned = 1; #ifdef MAGMA2_DEBUG_CHECKS - p->debug.ill_conditioned_count = 0; + p->debug.C_ill_conditioned_count = 0; + p->debug.D_well_conditioned = 1; + p->debug.D_ill_conditioned_count = 0; + for (int i = 0; i < 3; i++) { + p->debug.correction_matrix[i][0] = 0.f; + p->debug.correction_matrix[i][1] = 0.f; + p->debug.correction_matrix[i][2] = 0.f; + + p->debug.velocity_tensor_aux[i][0] = 0.f; + p->debug.velocity_tensor_aux[i][1] = 0.f; + p->debug.velocity_tensor_aux[i][2] = 0.f; + + p->debug.velocity_tensor_aux_norm[i][0] = 0.f; + p->debug.velocity_tensor_aux_norm[i][1] = 0.f; + p->debug.velocity_tensor_aux_norm[i][2] = 0.f; + } #endif hydro_reset_acceleration(p); diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 881fd78438..86209fe73c 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -281,8 +281,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { /* dx is signed as (pi - pj), but it is symmetric so we add */ - pi->gradients.C[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; - pj->gradients.C[k][i] += mi * rho_inv_i * dx[k] * dx[i] * wj; + pi->gradients.correction_matrix[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; + pj->gradients.correction_matrix[k][i] += mi * rho_inv_i * dx[k] * dx[i] * wj; } } } @@ -365,7 +365,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { /* dx is signed as (pi - pj), but it is symmetric so we add */ - pi->gradients.C[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; + pi->gradients.correction_matrix[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; } } } @@ -430,8 +430,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( for (int i = 0; i < 3; i++) { /* Note: Negative because dx is (pj-pi) in Rosswog 2020. * It is (pj-pi) for both particles. */ - G_i[k] -= pi->gradients.C[k][i] * dx[i] * wi * hi_inv_dim; - G_j[k] -= pj->gradients.C[k][i] * dx[i] * wj * hj_inv_dim; + G_i[k] -= pi->gradients.correction_matrix[k][i] * dx[i] * wi * hi_inv_dim; + G_j[k] -= pj->gradients.correction_matrix[k][i] * dx[i] * wj * hj_inv_dim; } G_ij[k] = 0.5 * (G_i[k] + G_j[k]); @@ -589,8 +589,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { /* Note: Negative because dx is (pj-pi) in Rosswog 2020 */ - G_i[k] -= pi->gradients.C[k][i] * dx[i] * wi * hi_inv_dim; - G_j[k] -= pj->gradients.C[k][i] * dx[i] * wj * hj_inv_dim; + G_i[k] -= pi->gradients.correction_matrix[k][i] * dx[i] * wi * hi_inv_dim; + G_j[k] -= pj->gradients.correction_matrix[k][i] * dx[i] * wj * hj_inv_dim; } G_ij[k] = 0.5 * (G_i[k] + G_j[k]); diff --git a/src/hydro/MAGMA2/hydro_io.h b/src/hydro/MAGMA2/hydro_io.h index ec84ea274c..20543f339a 100644 --- a/src/hydro/MAGMA2/hydro_io.h +++ b/src/hydro/MAGMA2/hydro_io.h @@ -253,15 +253,30 @@ INLINE static void hydro_write_particles(const struct part* parts, #ifdef MAGMA2_DEBUG_CHECKS list[14] = io_make_output_field( "CorrectionMatrices", FLOAT, 9, UNIT_CONV_LENGTH * UNIT_CONV_LENGTH, - 2.f, parts, debug.C, + 2.f, parts, debug.correction_matrix, "Co-moving correction matrices for the particles."); list[15] = io_make_output_field( - "IllConditionedCounts", INT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, - debug.ill_conditioned_count, + "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_fields = 16; + list[16] = io_make_output_field( + "NumeratorMatrices", 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."); + + list[17] = io_make_output_field( + "DenominatorMatrices", FLOAT, 9, UNIT_CONV_DENSITY, + -3.f, parts, debug.velocity_tensor_aux_norm, + "Co-moving denominator matrices for the particles."); + + list[18] = io_make_output_field( + "DenominatorIllConditionedCounts", 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_fields = 19; #endif } diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index b52f95ccbe..a7664f0cc7 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -137,10 +137,22 @@ struct part { #ifdef MAGMA2_DEBUG_CHECKS struct { /*! C matrix at the last time it was ill-conditioned */ - float C[3][3]; + float correction_matrix[3][3]; + + /*! Velocity tensor */ + float velocity_tensor_aux[3][3]; + + /*! Velocity tensor norm ill-conditioned */ + float velocity_tensor_aux_norm[3][3]; /*! Number of times C was ill-conditioned */ - int ill_conditioned_count; + int C_ill_conditioned_count; + + /*! Flag for whether aux_norm is ill-conditioned */ + char D_well_conditioned; + + /*! Number of times aux_norm was ill-conditioned */ + int D_ill_conditioned_count; } debug; #endif @@ -149,7 +161,7 @@ struct part { struct { /*! Correction matrix (C^ki in Rosswog 2020) */ - double C[3][3]; + double correction_matrix[3][3]; /*! Flag for whether C is ill-conditioned */ char C_well_conditioned; From 13be2dfaaaf34541bffeb7e7f524f9b159878fec Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Sat, 26 Jul 2025 13:59:46 -0400 Subject: [PATCH 07/39] The velocity Hessian is computed (a rank 3 tensor) for velocity reconstruction. --- src/hydro/MAGMA2/hydro.h | 48 ++++++++++++++++++++++++++++ src/hydro/MAGMA2/hydro_iact.h | 59 ++++++++++++++++++++++++++++++++++- src/hydro/MAGMA2/hydro_part.h | 3 ++ 3 files changed, 109 insertions(+), 1 deletion(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 19325f66c4..278328117c 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -805,6 +805,12 @@ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( p->gradients.velocity_tensor[i][0] = 0.f; p->gradients.velocity_tensor[i][1] = 0.f; p->gradients.velocity_tensor[i][2] = 0.f; + + for (int j = 0; j < 3; j++) { + p->gradients.velocity_hessian[i][j][0] = 0.f; + p->gradients.velocity_hessian[i][j][1] = 0.f; + p->gradients.velocity_hessian[i][j][2] = 0.f; + } } } @@ -835,6 +841,16 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( 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; + + 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 */ @@ -880,6 +896,34 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( } } + /* Contract the correction matrix with the velocity tensor */ + double velocity_tensor[3][3] = {0}; + double velocity_hessian[3][3][3] = {0}; + for (int j = 0; j < 3; j++) { + for (int i = 0; i < 3; i++) { + for (int k = 0; k < 3; 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]; + } + } + } + } + + /* Copy back over to the particle for later */ + for (int a = 0; a < 3; a++) { + p->gradients.velocity_tensor[a][0] = velocity_tensor[a][0]; + p->gradients.velocity_tensor[a][1] = velocity_tensor[a][1]; + p->gradients.velocity_tensor[a][2] = velocity_tensor[a][2]; + + for (int b = 0; b < 3; b++) { + p->gradients.velocity_hessian[a][b][0] = velocity_hessian[a][b][0]; + p->gradients.velocity_hessian[a][b][1] = velocity_hessian[a][b][1]; + p->gradients.velocity_hessian[a][b][2] = velocity_hessian[a][b][2]; + } + } } /** @@ -924,6 +968,10 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( p->gradients.velocity_tensor[i][j] = 0.f; p->gradients.velocity_tensor_aux[i][j] = 0.f; p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; + + for (int k = 0; k < 3; k++) { + p->gradients.velocity_hessian[i][j][k] = 0.f; + } } } } diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 86209fe73c..99f462df35 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -278,11 +278,40 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( pi->weighted_neighbour_wcount += mj * r2 * wi_dx * rho_inv_j * r_inv; pj->weighted_neighbour_wcount += mi * r2 * wj_dx * rho_inv_i * r_inv; + const float 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++) { for (int i = 0; i < 3; i++) { /* dx is signed as (pi - pj), but it is symmetric so we add */ pi->gradients.correction_matrix[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; pj->gradients.correction_matrix[k][i] += mi * rho_inv_i * dx[k] * dx[i] * wj; + + /* 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] += mj * rho_inv_j * dv[k] * dx[i] * wi; + pj->gradients.velocity_tensor[k][i] += mi * rho_inv_i * dv[k] * dx[i] * wj; + + const float 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] += + mj * rho_inv_j * dv_grad_ki * dx[j] * wi; + pj->gradients.velocity_hessian[k][i][j] += + mi * rho_inv_i * dv_grad_ki * dx[j] * wj; + } } } } @@ -362,10 +391,38 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( pi->weighted_neighbour_wcount += mj * r2 * wi_dx * rho_inv_j * r_inv; + const float 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++) { for (int i = 0; i < 3; i++) { /* dx is signed as (pi - pj), but it is symmetric so we add */ - pi->gradients.correction_matrix[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; + pi->gradients.correction_matrix[k][i] += + mj * rho_inv_j * dx[k] * dx[i] * wi; + + /* 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] += + mj * rho_inv_j * dv[k] * dx[i] * wi; + + const float 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] += + mj * rho_inv_j * dv_grad_ki * dx[j] * wi; + } } } } diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index a7664f0cc7..c06bd838fd 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -175,6 +175,9 @@ struct part { /*! Normalization for computing velocity_tensor_aux */ float velocity_tensor_aux_norm[3][3]; + /*! Hessian tensor */ + float velocity_hessian[3][3][3]; + /*! Smoothed pressure gradient */ float pressure[3]; From 609471be9b9289b16828bb63c242c697e63262f5 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Mon, 28 Jul 2025 09:39:27 -0400 Subject: [PATCH 08/39] Added in the Rosswog 2020 artificial viscosity using second order reconstruction of the velocity field. There are convenience functions in hydro.h that will compute the tensor and matrix contractions to clean up the code a bit and make it maintainable. Added in viscosity alpha as a constant 1 and changed beta to 2, and set the softening for viscosity to 10% of the softening length. Each particle gets its own artificial pressure rather than averaging as in other methods. The number of neighbors is stored in p->num_ngb now, although this should be constant for each run. Changed correction_matrix to a float since it seems very stable even in the Sedov Blastwave example. There is still a check for numerical conditioning and it will still revert to a Gasoline2-like SPH if the correction matrix is poorly conditioned. --- src/hydro/MAGMA2/hydro.h | 134 ++++++++- src/hydro/MAGMA2/hydro_iact.h | 436 +++++++++++++++++++++------- src/hydro/MAGMA2/hydro_parameters.h | 10 +- src/hydro/MAGMA2/hydro_part.h | 5 +- 4 files changed, 467 insertions(+), 118 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 278328117c..942042911b 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -513,6 +513,7 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->weighted_wcount = 0.f; p->weighted_neighbour_wcount = 0.f; p->density.rho_dh = 0.f; + p->num_ngb = 0; p->gradients.pressure[0] = 0.f; p->gradients.pressure[1] = 0.f; @@ -536,8 +537,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); @@ -559,8 +560,112 @@ condition_number(gsl_matrix *A) { return (s_min != 0.) ? s_max / s_min : const_condition_number_upper_limit; } +__attribute__((always_inline)) INLINE static void hydro_mat3x3_vec3_dot( + const float mat[3][3], const float vec[3], float result[3]) { + + for (int i = 0; i < 3; i++) { + result[i] = 0.f; + for (int j = 0; j < 3; j++) { + result[i] += mat[i][j] * vec[j]; + } + } +} + +__attribute__((always_inline)) INLINE static +void hydro_tensor3x3x3_matrix3x3_dot(const float tensor[3][3][3], + const float mat[3][3], + float result[3]) { + + for (int i = 0; i < 3; i++) { + result[i] = 0.f; + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) { + result[i] += tensor[i][j][k] * mat[j][k]; + } + } + } +} + +__attribute__((always_inline)) INLINE static +float hydro_van_leer_phi(const float grad_i[3][3], + const float grad_j[3][3], + const float dx[3], + const float eta_i, + const float eta_j, + const float num_ngb) { + + float num = 0.f; + float denom = 0.f; + for (int delta = 0; delta < 3; delta++) { + for (int gamma = 0; gamma < 3; gamma++) { + const float dxdx = dx[delta] * dx[gamma]; + num += grad_i[delta][gamma] * dxdx; + denom += grad_j[delta][gamma] * dxdx; + } + } + + /* Regularize denominator */ + if (fabsf(denom) < FLT_EPSILON) return 0.f; + + const float A_ij = num / denom; + float phi_raw = (4.f * A_ij) / ((1.f + A_ij) * (1.f + A_ij)); + phi_raw = fminf(1.f, fmaxf(0.f, phi_raw)); + + /* η_ab and η_crit damping */ + const float eta_ij = fminf(eta_i, eta_j); + const float eta_crit = powf((32.f * M_PI) / (3.f * num_ngb), 1.f / 3.f); + + float damping = 1.f; + if (eta_ij <= eta_crit) { + const float diff = (eta_ij - eta_crit) / 0.2f; + damping = expf(-diff * diff); + } + + return phi_raw * damping; +} + +__attribute__((always_inline)) INLINE static +void hydro_slope_limiter(const float dx[3], + const float eta_i, + const float eta_j, + const float num_ngb, + const float vi[3], + const float vj[3], + const float grad_i[3][3], + const float grad_j[3][3], + const float hess_i[3][3][3], + float vi_reconstruct[3]) { + + /* dx[i] carries a factor of 0.5 from Equation 17 Rosswog 2020 */ + const float dx_scaled[3] = {0.5f * dx[0], + 0.5f * dx[1], + 0.5f * dx[2]}; + float dxdx_scaled[3][3] = {0}; + for (int k = 0; k < 3; k++) { + dxdx_scaled[k][0] = dx_scaled[k] * dx_scaled[0]; + dxdx_scaled[k][1] = dx_scaled[k] * dx_scaled[1]; + dxdx_scaled[k][2] = dx_scaled[k] * dx_scaled[2]; + } + + float dvi_dx_dot_r[3] = {0}; + float dvi_dxj_dx_dot_r[3] = {0}; + + hydro_mat3x3_vec3_dot(grad_i, dx_scaled, dvi_dx_dot_r); + hydro_tensor3x3x3_matrix3x3_dot(hess_i, dxdx_scaled, dvi_dxj_dx_dot_r); + + /* Compute global Van Leer limiter (scalar, not component-wise) */ + const float phi_ij = + hydro_van_leer_phi(grad_i, grad_j, dx, eta_i, eta_j, num_ngb); + + /* Apply limited slope reconstruction */ + for (int k = 0; k < 3; k++) { + const float gradients_k = dvi_dx_dot_r[k] + 0.5f * dvi_dxj_dx_dot_r[k]; + vi_reconstruct[k] = vi[k] + phi_ij * gradients_k; + } +} + /** - * @brief Finishes the density calculation. + e @brief Finishes the density calculation. * * Multiplies the density and number of neighbours by the appropiate constants * and add the self-contribution term. @@ -798,9 +903,9 @@ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( p->viscosity.v_sig = p->force.soundspeed; for (int i = 0; i < 3; i++) { - p->gradients.correction_matrix[i][0] = 0.; - p->gradients.correction_matrix[i][1] = 0.; - p->gradients.correction_matrix[i][2] = 0.; + p->gradients.correction_matrix[i][0] = 0.f; + p->gradients.correction_matrix[i][1] = 0.f; + p->gradients.correction_matrix[i][2] = 0.f; p->gradients.velocity_tensor[i][0] = 0.f; p->gradients.velocity_tensor[i][1] = 0.f; @@ -835,6 +940,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( const float h_inv = 1.0f / h; /* 1/h */ const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + /* Temporary double for GSL */ + double correction_matrix[3][3] = {0}; + /* Apply correct normalisation */ p->viscosity.shock_limiter *= h_inv_dim; for (int k = 0; k < 3; k++) { @@ -842,6 +950,10 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( 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.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; @@ -855,7 +967,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_array((double *)p->gradients.correction_matrix, 3, 3); + gsl_matrix_view_array((double *)correction_matrix, 3, 3); gsl_matrix *C = &C_view.matrix; double cond = condition_number(C); @@ -890,9 +1002,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( /* 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.; + p->gradients.correction_matrix[k][0] = 0.f; + p->gradients.correction_matrix[k][1] = 0.f; + p->gradients.correction_matrix[k][2] = 0.f; } } @@ -964,7 +1076,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( for (int i = 0; i < 3; i++) { p->gradients.pressure[0] = 0.f; for (int j = 0; j < 3; j++) { - p->gradients.correction_matrix[i][j] = 0.; + p->gradients.correction_matrix[i][j] = 0.f; p->gradients.velocity_tensor[i][j] = 0.f; p->gradients.velocity_tensor_aux[i][j] = 0.f; p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 99f462df35..035cc36982 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -118,6 +118,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( } } + /* Number of neighbors */ + pi->num_ngb++; + pj->num_ngb++; + /* Correction factors for kernel gradients, and norm for the velocity * gradient. */ @@ -185,6 +189,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( } } + pi->num_ngb++; + /* Correction factors for kernel gradients, and norm for the velocity * gradient. */ pi->weighted_wcount += mj * r2 * wi_dx * r_inv; @@ -456,6 +462,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float rhoi = pi->rho; const float rhoj = pj->rho; + const float rhoi_inv = 1.f / rhoi; + const float rhoj_inv = 1.f / rhoj; + const float rhoij_inv = rhoi_inv * rhoj_inv; const float pressurei = pi->force.pressure; const float pressurej = pj->force.pressure; @@ -474,75 +483,187 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); - double G_ij[3] = {0., 0., 0.}; + /* The acceleration vector */ + float G_ij[3] = {0.f, 0.f, 0.f}; + + /* These are set whether or not we fall back onto SPH gradients */ + float visc_acc_term = 0.f; + float sph_acc_term = (pressurei + pressurej) * rhoij_inv; + + float sph_du_term_i = pressurei * rhoij_inv; + float sph_du_term_j = pressurej * rhoij_inv; + float visc_du_term_i = 0.f; + float visc_du_term_j = 0.f; /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ - char C_well_conditioned = 0; if (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned) { - C_well_conditioned = 1; - double G_i[3] = {0., 0., 0.}; - double G_j[3] = {0., 0., 0.}; - for (int k = 0; k < 3; k++) { - for (int i = 0; i < 3; i++) { - /* Note: Negative because dx is (pj-pi) in Rosswog 2020. - * It is (pj-pi) for both particles. */ - G_i[k] -= pi->gradients.correction_matrix[k][i] * dx[i] * wi * hi_inv_dim; - G_j[k] -= pj->gradients.correction_matrix[k][i] * dx[i] * wj * hj_inv_dim; - } - - G_ij[k] = 0.5 * (G_i[k] + G_j[k]); - } - } - - /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ - const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + - (pi->v[1] - pj->v[1]) * G_ij[1] + - (pi->v[2] - pj->v[2]) * G_ij[2]; - - /* Compute dv dot dr. */ - const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + - (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; - - /* Includes the hubble flow term; not used for du/dt */ - const float dvdr_Hubble = dvdr + a2_Hubble * r2; - - /* Are the particles moving towards each others ? */ - const float omega_ij = min(dvdr_Hubble, 0.f); - const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ - /* Construct the full viscosity term */ - const float rho_ij = rhoi + rhoj; - const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; - const float visc = - omega_ij < 0.f - ? (-0.25f * alpha * (pi->force.soundspeed + pj->force.soundspeed) * - mu_ij + - const_viscosity_beta * mu_ij * mu_ij) / - (0.5f * rho_ij) - : 0.f; + /* Corrected gradients */ + float G_i[3] = {0.f, 0.f, 0.f}; + float G_j[3] = {0.f, 0.f, 0.f}; + hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx, G_i); + hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx, G_j); + + /* Note: negative because dx is pj->x - pi->x in Rosswog 2020. + * It is pj->x - pi->x for BOTH particles, and then sign flip + * later */ + 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; + + /* Velocity tensor dot product with separation vector for pi */ + float dvi_dx_dot_r[3] = {0.f, 0.f, 0.f}; + hydro_mat3x3_vec3_dot(pi->gradients.velocity_tensor, dx, dvi_dx_dot_r); + + /* Equation 17 has r_b - r_a, here we have pi - pj so the + * sign is flipped (because of dx) */ + dvi_dx_dot_r[0] *= -0.5f; + dvi_dx_dot_r[1] *= -0.5f; + dvi_dx_dot_r[2] *= -0.5f; + + /* Velocity tensor dot product with separation vector for pi */ + float dvj_dx_dot_r[3] = {0.f, 0.f, 0.f}; + hydro_mat3x3_vec3_dot(pj->gradients.velocity_tensor, dx, dvj_dx_dot_r); + + /* Equation 17 has r_b - r_a, here we have pi - pj so the + * sign is NOT flipped on dvj_dx_dot_r */ + dvj_dx_dot_r[0] *= 0.5f; + dvj_dx_dot_r[1] *= 0.5f; + dvj_dx_dot_r[2] *= 0.5f; + + /* Compute second order reconstruction of velocity between pi & pj */ + float vi_reconstruct[3] = {0.f, 0.f, 0.f}; + float vj_reconstruct[3] = {0.f, 0.f, 0.f}; + + const float dx_ij[3] = {dx[0], dx[1], dx[2]}; + const float dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; + + /* TODO: make this constant */ + const float num_ngb_ij = + 0.5f * ((float)pi->num_ngb + (float)pj->num_ngb); + + hydro_slope_limiter(dx_ji, xi, xj, num_ngb_ij, pi->v, pj->v, + pi->gradients.velocity_tensor, + pj->gradients.velocity_tensor, + pi->gradients.velocity_hessian, + vi_reconstruct); + + hydro_slope_limiter(dx_ij, xj, xi, num_ngb_ij, pj->v, pi->v, + pj->gradients.velocity_tensor, + pi->gradients.velocity_tensor, + pj->gradients.velocity_hessian, + vj_reconstruct); + + /* Artificial viscosity */ + const float dv_ij[3] = {vi_reconstruct[0] - vj_reconstruct[0], + vi_reconstruct[1] - vj_reconstruct[1], + vi_reconstruct[2] - vj_reconstruct[2]}; + const float dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; + + const float eta_i[3] = {dx_ij[0] * hi_inv, + dx_ij[1] * hi_inv, + dx_ij[2] * hi_inv}; + const float eta_j[3] = {dx_ji[0] * hj_inv, + dx_ji[1] * hj_inv, + dx_ji[2] * hj_inv}; + const float eta_i2 = eta_i[0] * eta_i[0] + + eta_i[1] * eta_i[1] + + eta_i[2] * eta_i[2]; + const float eta_j2 = eta_j[0] * eta_j[0] + + eta_j[1] * eta_j[1] + + eta_j[2] * eta_j[2]; + const float dv_dot_eta_i = dv_ij[0] * eta_i[0] + + dv_ij[1] * eta_i[1] + + dv_ij[2] * eta_i[2]; + /* Scale Hubble flow by hi_inv so it is overall scaled */ + const float dv_dot_eta_i_phys = dv_dot_eta_i + a2_Hubble * r2 * hi_inv; + const float dv_dot_eta_j = dv_ji[0] * eta_j[0] + + dv_ji[1] * eta_j[1] + + dv_ji[2] * eta_j[2]; + /* Scale Hubble flow by hj_inv so it is overall scaled */ + const float dv_dot_eta_j_phys = dv_dot_eta_j + a2_Hubble * r2 * hj_inv; + + /* mu_i and mu_j are physical, not comoving */ + const float mu_i = + fminf(0.f, dv_dot_eta_i_phys / (eta_i2 + const_viscosity_epsilon2)); + const float mu_j = + fminf(0.f, dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2)); + + const float Q_i_alpha = + -const_viscosity_alpha * pi->force.soundspeed * mu_i; + const float Q_i_beta = const_viscosity_beta * mu_i * mu_i; + const float Q_i = rhoi * (Q_i_alpha + Q_i_beta); + + const float Q_j_alpha = + -const_viscosity_alpha * pj->force.soundspeed * mu_j; + const float Q_j_beta = const_viscosity_beta * mu_j * mu_j; + const float Q_j = rhoj * (Q_j_alpha + Q_j_beta); + + /* Add viscosity to the pressure */ + visc_acc_term = (Q_i + Q_j) * rhoij_inv; + visc_du_term_i = Q_i * rhoij_inv; + visc_du_term_j = Q_j * rhoij_inv; + + /* Averaged correction gradient. Note: antisymmetric, so only need + * a sign flip for pj */ + G_ij[0] = 0.5f * (G_i[0] + G_j[0]); + G_ij[1] = 0.5f * (G_i[1] + G_j[1]); + G_ij[2] = 0.5f * (G_i[2] + G_j[2]); + + /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ + const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + + (pi->v[1] - pj->v[1]) * G_ij[1] + + (pi->v[2] - pj->v[2]) * G_ij[2]; - /* These are set whether or not we fall back onto SPH gradients */ - float visc_acc_term = visc; - float sph_acc_term = (pressurei + pressurej) / (pi->rho * pj->rho); - - float sph_du_term_i = pressurei / (pi->rho * pj->rho); - float sph_du_term_j = pressurej / (pi->rho * pj->rho); - float visc_du_term_i = 0.5f * visc_acc_term; - float visc_du_term_j = visc_du_term_i; - - if (C_well_conditioned) { sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; - visc_du_term_i *= dv_dot_G_ij + a2_Hubble + r2; - visc_du_term_j *= dv_dot_G_ij + a2_Hubble + r2; + visc_du_term_i *= dv_dot_G_ij + a2_Hubble * r2; + visc_du_term_j *= dv_dot_G_ij + a2_Hubble * r2; /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; pj->force.h_dt -= mi * dv_dot_G_ij; } else { + + /* Dot product with the distance vector to align + * with dW/dr (dW/dr = r_ij / |r_ij| dot grad W */ + G_ij[0] = dx[0]; + G_ij[1] = dx[1]; + G_ij[2] = dx[2]; + + /* Compute dv dot dr. */ + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Includes the hubble flow term; not used for du/dt */ + const float dvdr_Hubble = dvdr + a2_Hubble * r2; + + /* Are the particles moving towards each others ? */ + const float omega_ij = min(dvdr_Hubble, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Construct the full viscosity term */ + const float rho_ij = rhoi + rhoj; + const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; + const float visc = + omega_ij < 0.f + ? (-0.25f * alpha * (pi->force.soundspeed + pj->force.soundspeed) * + mu_ij + + const_viscosity_beta * mu_ij * mu_ij) / + (0.5f * rho_ij) + : 0.f; + + visc_acc_term = visc; + visc_du_term_i = 0.5f * visc_acc_term; + visc_du_term_j = visc_du_term_i; + const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; const float wi_dr = hi_inv_dim_plus_one * wi_dx; @@ -616,6 +737,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float rhoi = pi->rho; const float rhoj = pj->rho; + const float rhoi_inv = 1.f / rhoi; + const float rhoj_inv = 1.f / rhoj; + const float rhoij_inv = rhoi_inv * rhoj_inv; const float pressurei = pi->force.pressure; const float pressurej = pj->force.pressure; @@ -634,61 +758,138 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); - double G_ij[3] = {0., 0., 0.}; + float G_ij[3] = {0.f, 0.f, 0.f}; + + /* These are set whether or not we fall back onto SPH gradients */ + float visc_acc_term = 0.f; + float sph_acc_term = (pressurei + pressurej) * rhoij_inv; + float sph_du_term_i = pressurei * rhoij_inv; + float visc_du_term_i = 0.f; /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ - char C_well_conditioned = 0; if (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned) { - C_well_conditioned = 1; - double G_i[3] = {0., 0., 0.}; - double G_j[3] = {0., 0., 0.}; - for (int k = 0; k < 3; k++) { - for (int i = 0; i < 3; i++) { - /* Note: Negative because dx is (pj-pi) in Rosswog 2020 */ - G_i[k] -= pi->gradients.correction_matrix[k][i] * dx[i] * wi * hi_inv_dim; - G_j[k] -= pj->gradients.correction_matrix[k][i] * dx[i] * wj * hj_inv_dim; - } - - G_ij[k] = 0.5 * (G_i[k] + G_j[k]); - } - } - - /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ - const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + - (pi->v[1] - pj->v[1]) * G_ij[1] + - (pi->v[2] - pj->v[2]) * G_ij[2]; - - /* Compute dv dot dr. */ - const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + - (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; - - /* Includes the hubble flow term; not used for du/dt */ - const float dvdr_Hubble = dvdr + a2_Hubble * r2; - - /* Are the particles moving towards each others ? */ - const float omega_ij = min(dvdr_Hubble, 0.f); - const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ - /* Construct the full viscosity term */ - const float rho_ij = rhoi + rhoj; - const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; - const float visc = - omega_ij < 0.f - ? (-0.25f * alpha * (pi->force.soundspeed + pj->force.soundspeed) * - mu_ij + - const_viscosity_beta * mu_ij * mu_ij) / - (0.5f * rho_ij) - : 0.f; - - /* These are set whether or not we fall back onto SPH gradients */ - float visc_acc_term = visc; - float sph_acc_term = (pressurei + pressurej) / (pi->rho * pj->rho); - float sph_du_term_i = pressurei / (pi->rho * pj->rho); - float visc_du_term_i = visc; + /* Corrected gradients */ + float G_i[3] = {0.f, 0.f, 0.f}; + float G_j[3] = {0.f, 0.f, 0.f}; + hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx, G_i); + hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx, G_j); + + /* Note: negative because dx is pj->x - pi->x in Rosswog 2020. + * It is pj->x - pi->x for BOTH particles, and then sign flip + * later */ + 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; + + /* Velocity tensor dot product with separation vector for pi */ + float dvi_dx_dot_r[3] = {0.f, 0.f, 0.f}; + hydro_mat3x3_vec3_dot(pi->gradients.velocity_tensor, dx, dvi_dx_dot_r); + + /* Equation 17 has r_b - r_a, here we have pi - pj so the + * sign is flipped (because of dx) */ + dvi_dx_dot_r[0] *= -0.5f; + dvi_dx_dot_r[1] *= -0.5f; + dvi_dx_dot_r[2] *= -0.5f; + + /* Velocity tensor dot product with separation vector for pi */ + float dvj_dx_dot_r[3] = {0.f, 0.f, 0.f}; + hydro_mat3x3_vec3_dot(pj->gradients.velocity_tensor, dx, dvj_dx_dot_r); + + /* Equation 17 has r_b - r_a, here we have pi - pj so the + * sign is NOT flipped on dvj_dx_dot_r */ + dvj_dx_dot_r[0] *= 0.5f; + dvj_dx_dot_r[1] *= 0.5f; + dvj_dx_dot_r[2] *= 0.5f; + + /* Compute second order reconstruction of velocity between pi & pj */ + float vi_reconstruct[3] = {0.f, 0.f, 0.f}; + float vj_reconstruct[3] = {0.f, 0.f, 0.f}; + + const float dx_ij[3] = {dx[0], dx[1], dx[2]}; + const float dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; + + /* TODO: make this constant */ + const float num_ngb_ij = + 0.5f * ((float)pi->num_ngb + (float)pj->num_ngb); + + hydro_slope_limiter(dx_ji, xi, xj, num_ngb_ij, pi->v, pj->v, + pi->gradients.velocity_tensor, + pj->gradients.velocity_tensor, + pi->gradients.velocity_hessian, + vi_reconstruct); + + hydro_slope_limiter(dx_ij, xj, xi, num_ngb_ij, pj->v, pi->v, + pj->gradients.velocity_tensor, + pi->gradients.velocity_tensor, + pj->gradients.velocity_hessian, + vj_reconstruct); + + /* Artificial viscosity */ + const float dv_ij[3] = {vi_reconstruct[0] - vj_reconstruct[0], + vi_reconstruct[1] - vj_reconstruct[1], + vi_reconstruct[2] - vj_reconstruct[2]}; + const float dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; + + const float eta_i[3] = {dx_ij[0] * hi_inv, + dx_ij[1] * hi_inv, + dx_ij[2] * hi_inv}; + const float eta_j[3] = {dx_ji[0] * hj_inv, + dx_ji[1] * hj_inv, + dx_ji[2] * hj_inv}; + const float eta_i2 = eta_i[0] * eta_i[0] + + eta_i[1] * eta_i[1] + + eta_i[2] * eta_i[2]; + const float eta_j2 = eta_j[0] * eta_j[0] + + eta_j[1] * eta_j[1] + + eta_j[2] * eta_j[2]; + const float dv_dot_eta_i = dv_ij[0] * eta_i[0] + + dv_ij[1] * eta_i[1] + + dv_ij[2] * eta_i[2]; + /* Scale Hubble flow by hi_inv so it is overall scaled */ + const float dv_dot_eta_i_phys = dv_dot_eta_i + a2_Hubble * r2 * hi_inv; + const float dv_dot_eta_j = dv_ji[0] * eta_j[0] + + dv_ji[1] * eta_j[1] + + dv_ji[2] * eta_j[2]; + /* Scale Hubble flow by hi_inv so it is overall scaled */ + const float dv_dot_eta_j_phys = dv_dot_eta_j + a2_Hubble * r2 * hj_inv; + + /* mu_i and mu_j are physical, not comoving */ + const float mu_i = + fminf(0.f, dv_dot_eta_i_phys / (eta_i2 + const_viscosity_epsilon2)); + const float mu_j = + fminf(0.f, dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2)); + + const float Q_i_alpha = + -const_viscosity_alpha * pi->force.soundspeed * mu_i; + const float Q_i_beta = const_viscosity_beta * mu_i * mu_i; + const float Q_i = rhoi * (Q_i_alpha + Q_i_beta); + + const float Q_j_alpha = + -const_viscosity_alpha * pj->force.soundspeed * mu_j; + const float Q_j_beta = const_viscosity_beta * mu_j * mu_j; + const float Q_j = rhoj * (Q_j_alpha + Q_j_beta); + + /* Add viscosity to the pressure */ + visc_acc_term = (Q_i + Q_j) * rhoij_inv; + visc_du_term_i = Q_i * rhoij_inv; + + /* Averaged correction gradient. Note: antisymmetric, so only need + * a sign flip for pj */ + G_ij[0] = 0.5f * (G_i[0] + G_j[0]); + G_ij[1] = 0.5f * (G_i[1] + G_j[1]); + G_ij[2] = 0.5f * (G_i[2] + G_j[2]); + + /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ + const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + + (pi->v[1] - pj->v[1]) * G_ij[1] + + (pi->v[2] - pj->v[2]) * G_ij[2]; - if (C_well_conditioned) { sph_du_term_i *= dv_dot_G_ij; visc_du_term_i *= dv_dot_G_ij + a2_Hubble * r2; @@ -696,6 +897,33 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->force.h_dt -= mj * dv_dot_G_ij; } else { + + /* Compute dv dot dr. */ + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Includes the hubble flow term; not used for du/dt */ + const float dvdr_Hubble = dvdr + a2_Hubble * r2; + + /* Are the particles moving towards each others ? */ + const float omega_ij = min(dvdr_Hubble, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Construct the full viscosity term */ + const float rho_ij = rhoi + rhoj; + const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; + const float visc = + omega_ij < 0.f + ? (-0.25f * alpha * (pi->force.soundspeed + pj->force.soundspeed) * + mu_ij + + const_viscosity_beta * mu_ij * mu_ij) / + (0.5f * rho_ij) + : 0.f; + + visc_acc_term = visc; + visc_du_term_i = 0.5f * visc_acc_term; + const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; const float wi_dr = hi_inv_dim_plus_one * wi_dx; diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 425cac5904..aa36192af8 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -46,14 +46,20 @@ */ /*! Consider C matrix can be ill-conditioned above this limit */ -#define const_condition_number_upper_limit 99.0 +#define const_condition_number_upper_limit 999.f /*! Viscosity parameters -- FIXED -- MUST BE DEFINED AT COMPILE-TIME */ /*! Cosmology default beta=3.0. * Alpha can be set in the parameter file. * Beta is defined as in e.g. Price (2010) Eqn (103) */ -#define const_viscosity_beta 3.0f +#define const_viscosity_beta 2.0f + +/*! Cosmology default alpha=1.0 */ +#define const_viscosity_alpha 1.0f + +/*! Softening squared (epsilon^2) in Eq. 15 Rosswog 2020 */ +#define const_viscosity_epsilon2 0.01f /*! The viscosity that the particles are reset to after being hit by a * feedback event. This should be set to the same value as the diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index c06bd838fd..9ad4ee3c54 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -134,6 +134,9 @@ struct part { /*! Neighbour weightings */ float weighted_neighbour_wcount; + /*! Number of neighbors in the kernel */ + int num_ngb; + #ifdef MAGMA2_DEBUG_CHECKS struct { /*! C matrix at the last time it was ill-conditioned */ @@ -161,7 +164,7 @@ struct part { struct { /*! Correction matrix (C^ki in Rosswog 2020) */ - double correction_matrix[3][3]; + float correction_matrix[3][3]; /*! Flag for whether C is ill-conditioned */ char C_well_conditioned; From 7c0316e8d9f0ce8d7bfa76c11c86599a8cb43a18 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Mon, 28 Jul 2025 11:38:08 -0400 Subject: [PATCH 09/39] There are now more tensor, matrix, and vector operation functions in the hydro.h file that clean up the code a lot and should improve efficiency. Also rewrote the van Leer slope limiter part of the code so that we can now do both scalar and vector fields easily. --- src/hydro/MAGMA2/hydro.h | 375 ++++++++++++++++++++++++++++------ src/hydro/MAGMA2/hydro_iact.h | 85 +++++--- 2 files changed, 360 insertions(+), 100 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 942042911b..13fd3e51b9 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -560,54 +560,154 @@ double condition_number(gsl_matrix *A) { 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 +void hydro_vec3_vec3_dot(const float *restrict vec_a, + const float *restrict vec_b, + float *result) { + + *result = vec_a[0] * vec_b[0] + vec_a[1] * vec_b[1] + vec_a[2] * vec_b[2]; +} + +/** + * @brief The scalar triple product of vector vec and matrix mat. + * That is vec^T * mat * vec. + * + * + * @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_mat3x3_dot(const float (*restrict mat_a)[3], + const float (*restrict mat_b)[3], + float *result) { + + *result = 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 float mat[3][3], const float vec[3], float result[3]) { + const float (*restrict mat)[3], + const float *restrict vec, + float *restrict result) { - for (int i = 0; i < 3; i++) { - result[i] = 0.f; - for (int j = 0; j < 3; j++) { - result[i] += mat[i][j] * vec[j]; - } - } + 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 float tensor[3][3][3], - const float mat[3][3], - float result[3]) { +void hydro_tensor3x3x3_matrix3x3_dot( + const float (*restrict tensor)[3][3], + const float (*restrict mat)[3], + float *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[3] = 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]; +} - for (int i = 0; i < 3; i++) { - result[i] = 0.f; - for (int j = 0; j < 3; j++) { - for (int k = 0; k < 3; k++) { - result[i] += tensor[i][j][k] * mat[j][k]; - } - } - } +/** + * @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 float *restrict vec_a, + const float *restrict vec_b, + float (*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 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. + * @param num_ngb The number of neighbours in the scheme. + */ __attribute__((always_inline)) INLINE static -float hydro_van_leer_phi(const float grad_i[3][3], - const float grad_j[3][3], - const float dx[3], +float hydro_van_leer_phi(const float A_ij, const float eta_i, const float eta_j, const float num_ngb) { - float num = 0.f; - float denom = 0.f; - for (int delta = 0; delta < 3; delta++) { - for (int gamma = 0; gamma < 3; gamma++) { - const float dxdx = dx[delta] * dx[gamma]; - num += grad_i[delta][gamma] * dxdx; - denom += grad_j[delta][gamma] * dxdx; - } - } - - /* Regularize denominator */ - if (fabsf(denom) < FLT_EPSILON) return 0.f; - - const float A_ij = num / denom; float phi_raw = (4.f * A_ij) / ((1.f + A_ij) * (1.f + A_ij)); phi_raw = fminf(1.f, fmaxf(0.f, phi_raw)); @@ -624,44 +724,185 @@ float hydro_van_leer_phi(const float grad_i[3][3], return phi_raw * damping; } +/** + * @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 -void hydro_slope_limiter(const float dx[3], - const float eta_i, - const float eta_j, - const float num_ngb, - const float vi[3], - const float vj[3], - const float grad_i[3][3], - const float grad_j[3][3], - const float hess_i[3][3][3], - float vi_reconstruct[3]) { - - /* dx[i] carries a factor of 0.5 from Equation 17 Rosswog 2020 */ - const float dx_scaled[3] = {0.5f * dx[0], - 0.5f * dx[1], - 0.5f * dx[2]}; - float dxdx_scaled[3][3] = {0}; - for (int k = 0; k < 3; k++) { - dxdx_scaled[k][0] = dx_scaled[k] * dx_scaled[0]; - dxdx_scaled[k][1] = dx_scaled[k] * dx_scaled[1]; - dxdx_scaled[k][2] = dx_scaled[k] * dx_scaled[2]; - } +float hydro_scalar_van_leer_A(const float *restrict grad_i, + const float *restrict grad_j, + const float *restrict dx) { + + float grad_dot_x_i = 0.f; + float grad_dot_x_j = 0.f; + hydro_vec3_vec3_dot(grad_i, dx, &grad_dot_x_i); + hydro_vec3_vec3_dot(grad_j, dx, &grad_dot_x_j); + + /* Regularize denominator */ + if (fabsf(grad_dot_x_j) < FLT_EPSILON) return 0.f; + + const float 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 +float hydro_vector_van_leer_A(const float (*restrict grad_i)[3], + const float (*restrict grad_j)[3], + const float *restrict dx) { - float dvi_dx_dot_r[3] = {0}; - float dvi_dxj_dx_dot_r[3] = {0}; + float delta_ij[3][3] = {0}; + hydro_vec3_vec3_outer(dx, dx, delta_ij); - hydro_mat3x3_vec3_dot(grad_i, dx_scaled, dvi_dx_dot_r); - hydro_tensor3x3x3_matrix3x3_dot(hess_i, dxdx_scaled, dvi_dxj_dx_dot_r); + float grad_dot_x_x_i = 0.f; + float grad_dot_x_x_j = 0.f; + hydro_mat3x3_mat3x3_dot(grad_i, delta_ij, &grad_dot_x_x_i); + hydro_mat3x3_mat3x3_dot(grad_j, delta_ij, &grad_dot_x_x_j); - /* Compute global Van Leer limiter (scalar, not component-wise) */ - const float phi_ij = - hydro_van_leer_phi(grad_i, grad_j, dx, eta_i, eta_j, num_ngb); + /* Regularize denominator */ + if (fabsf(grad_dot_x_x_j) < FLT_EPSILON) return 0.f; + + const float 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. + * @param num_ngb The number of neighbours in the scheme. + */ +__attribute__((always_inline)) INLINE static +float hydro_scalar_van_leer_phi(const float *restrict grad_i, + const float *restrict grad_j, + const float *restrict dx, + const float eta_i, + const float eta_j, + const float num_ngb) { + + const float A_ij = hydro_scalar_van_leer_A(grad_i, grad_j, dx); + + return hydro_van_leer_phi(A_ij, eta_i, eta_j, num_ngb); +} + +/** + * @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. + * @param num_ngb The number of neighbours in the scheme. + */ +__attribute__((always_inline)) INLINE static +float hydro_vector_van_leer_phi(const float (*restrict grad_i)[3], + const float (*restrict grad_j)[3], + const float *restrict dx, + const float eta_i, + const float eta_j, + const float num_ngb) { + + const float A_ij = hydro_vector_van_leer_A(grad_i, grad_j, dx); + + return hydro_van_leer_phi(A_ij, eta_i, eta_j, num_ngb); +} + +/** + * @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 float phi, + const float *restrict dx, + const float f, + const float *restrict grad, + const float (*restrict hess)[3], + float *f_reconstructed) { + + /* Midpoint from Equation 17 Rosswog 2020 */ + const float midpoint[3] = {0.5f * dx[0], + 0.5f * dx[1], + 0.5f * dx[2]}; + float delta_ij[3][3] = {0}; + hydro_vec3_vec3_outer(midpoint, midpoint, delta_ij); + + float df_dx_dot_r = 0.f; + float df_dx_dx_dot_r2 = 0.f; + + hydro_vec3_vec3_dot(grad, midpoint, &df_dx_dot_r); + hydro_mat3x3_mat3x3_dot(hess, delta_ij, &df_dx_dx_dot_r2); /* Apply limited slope reconstruction */ - for (int k = 0; k < 3; k++) { - const float gradients_k = dvi_dx_dot_r[k] + 0.5f * dvi_dxj_dx_dot_r[k]; - vi_reconstruct[k] = vi[k] + phi_ij * gradients_k; - } + *f_reconstructed = f + phi * (df_dx_dot_r + 0.5f * 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 float phi, + const float *restrict dx, + const float *restrict f, + const float (*restrict grad)[3], + const float (*restrict hess)[3][3], + float *restrict f_reconstructed) { + + /* Midpoint from Equation 17 Rosswog 2020 */ + const float midpoint[3] = {0.5f * dx[0], + 0.5f * dx[1], + 0.5f * dx[2]}; + float delta_ij[3][3] = {0}; + hydro_vec3_vec3_outer(midpoint, midpoint, delta_ij); + + float df_dx_dot_r[3] = {0}; + float 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.5f * df_dx_dx_dot_r2[0]); + f_reconstructed[1] = f[1] + phi * (df_dx_dot_r[1] + 0.5f * df_dx_dx_dot_r2[1]); + f_reconstructed[2] = f[2] + phi * (df_dx_dot_r[2] + 0.5f * df_dx_dx_dot_r2[2]); } /** diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 035cc36982..0fe4437036 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -537,8 +537,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( dvj_dx_dot_r[2] *= 0.5f; /* Compute second order reconstruction of velocity between pi & pj */ - float vi_reconstruct[3] = {0.f, 0.f, 0.f}; - float vj_reconstruct[3] = {0.f, 0.f, 0.f}; + float vi_reconstructed[3] = {0.f, 0.f, 0.f}; + float vj_reconstructed[3] = {0.f, 0.f, 0.f}; const float dx_ij[3] = {dx[0], dx[1], dx[2]}; const float dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; @@ -547,22 +547,29 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float num_ngb_ij = 0.5f * ((float)pi->num_ngb + (float)pj->num_ngb); - hydro_slope_limiter(dx_ji, xi, xj, num_ngb_ij, pi->v, pj->v, - pi->gradients.velocity_tensor, - pj->gradients.velocity_tensor, - pi->gradients.velocity_hessian, - vi_reconstruct); - - hydro_slope_limiter(dx_ij, xj, xi, num_ngb_ij, pj->v, pi->v, - pj->gradients.velocity_tensor, - pi->gradients.velocity_tensor, - pj->gradients.velocity_hessian, - vj_reconstruct); + /* Compute global Van Leer limiter (scalar, not component-wise) */ + const float phi_ij = hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, + pj->gradients.velocity_tensor, + dx_ij, xi, xj, num_ngb_ij); + const float phi_ji = hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, + pi->gradients.velocity_tensor, + dx_ji, xj, xi, num_ngb_ij); + + /* dx_ji for particle i and dx_ij for particle j */ + hydro_vector_second_order_reconstruction(phi_ij, dx_ji, pi->v, + pi->gradients.velocity_tensor, + pi->gradients.velocity_hessian, + vi_reconstructed); + + hydro_vector_second_order_reconstruction(phi_ji, dx_ij, pj->v, + pj->gradients.velocity_tensor, + pj->gradients.velocity_hessian, + vj_reconstructed); /* Artificial viscosity */ - const float dv_ij[3] = {vi_reconstruct[0] - vj_reconstruct[0], - vi_reconstruct[1] - vj_reconstruct[1], - vi_reconstruct[2] - vj_reconstruct[2]}; + const float dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], + vi_reconstructed[1] - vj_reconstructed[1], + vi_reconstructed[2] - vj_reconstructed[2]}; const float dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; const float eta_i[3] = {dx_ij[0] * hi_inv, @@ -808,8 +815,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( dvj_dx_dot_r[2] *= 0.5f; /* Compute second order reconstruction of velocity between pi & pj */ - float vi_reconstruct[3] = {0.f, 0.f, 0.f}; - float vj_reconstruct[3] = {0.f, 0.f, 0.f}; + float vi_reconstructed[3] = {0.f, 0.f, 0.f}; + float vj_reconstructed[3] = {0.f, 0.f, 0.f}; const float dx_ij[3] = {dx[0], dx[1], dx[2]}; const float dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; @@ -818,22 +825,29 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float num_ngb_ij = 0.5f * ((float)pi->num_ngb + (float)pj->num_ngb); - hydro_slope_limiter(dx_ji, xi, xj, num_ngb_ij, pi->v, pj->v, - pi->gradients.velocity_tensor, - pj->gradients.velocity_tensor, - pi->gradients.velocity_hessian, - vi_reconstruct); - - hydro_slope_limiter(dx_ij, xj, xi, num_ngb_ij, pj->v, pi->v, - pj->gradients.velocity_tensor, - pi->gradients.velocity_tensor, - pj->gradients.velocity_hessian, - vj_reconstruct); + /* Compute global Van Leer limiter (scalar, not component-wise) */ + const float phi_ij = hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, + pj->gradients.velocity_tensor, + dx_ij, xi, xj, num_ngb_ij); + const float phi_ji = hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, + pi->gradients.velocity_tensor, + dx_ji, xj, xi, num_ngb_ij); + + /* dx_ji for particle i and dx_ij for particle j */ + hydro_vector_second_order_reconstruction(phi_ij, dx_ji, pi->v, + pi->gradients.velocity_tensor, + pi->gradients.velocity_hessian, + vi_reconstructed); + + hydro_vector_second_order_reconstruction(phi_ji, dx_ij, pj->v, + pj->gradients.velocity_tensor, + pj->gradients.velocity_hessian, + vj_reconstructed); /* Artificial viscosity */ - const float dv_ij[3] = {vi_reconstruct[0] - vj_reconstruct[0], - vi_reconstruct[1] - vj_reconstruct[1], - vi_reconstruct[2] - vj_reconstruct[2]}; + const float dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], + vi_reconstructed[1] - vj_reconstructed[1], + vi_reconstructed[2] - vj_reconstructed[2]}; const float dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; const float eta_i[3] = {dx_ij[0] * hi_inv, @@ -898,6 +912,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( } else { + /* Fallback SPH points in the direction between the particles. */ + G_ij[0] = dx[0]; + G_ij[1] = dx[1]; + G_ij[2] = dx[2]; + /* Compute dv dot dr. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + @@ -923,7 +942,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( visc_acc_term = visc; visc_du_term_i = 0.5f * visc_acc_term; - + const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; const float wi_dr = hi_inv_dim_plus_one * wi_dx; From c7d27af12cf90079f54646a8f337ce39e97da645 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Mon, 28 Jul 2025 11:59:08 -0400 Subject: [PATCH 10/39] Symmetric heating from artificial viscosity is necessary or else there is extreme noise in front of the shock in the Sedov Blastwave test. --- src/hydro/MAGMA2/hydro_iact.h | 38 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 0fe4437036..9c5493208b 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -483,7 +483,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); - /* The acceleration vector */ + /* 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). */ float G_ij[3] = {0.f, 0.f, 0.f}; /* These are set whether or not we fall back onto SPH gradients */ @@ -492,8 +494,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( float sph_du_term_i = pressurei * rhoij_inv; float sph_du_term_j = pressurej * rhoij_inv; - float visc_du_term_i = 0.f; - float visc_du_term_j = 0.f; + float visc_du_term = 0.f; /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ @@ -613,8 +614,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; - visc_du_term_i = Q_i * rhoij_inv; - visc_du_term_j = Q_j * rhoij_inv; + visc_du_term = 0.5f * visc_acc_term; /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ @@ -629,8 +629,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; - visc_du_term_i *= dv_dot_G_ij + a2_Hubble * r2; - visc_du_term_j *= dv_dot_G_ij + a2_Hubble * r2; + visc_du_term *= dv_dot_G_ij + a2_Hubble * r2; /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; @@ -668,8 +667,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( : 0.f; visc_acc_term = visc; - visc_du_term_i = 0.5f * visc_acc_term; - visc_du_term_j = visc_du_term_i; + visc_du_term = 0.5f * visc_acc_term; const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; @@ -685,8 +683,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( sph_du_term_i *= dvdr * kernel_gradient; sph_du_term_j *= dvdr * kernel_gradient; - visc_du_term_i *= dvdr_Hubble * kernel_gradient; - visc_du_term_j = visc_du_term_i; + visc_du_term *= dvdr_Hubble * kernel_gradient; /* Get the time derivative for h. */ pi->force.h_dt -= mj * dvdr * r_inv * wi_dr; @@ -708,8 +705,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the time derivative for u. */ /* Assemble the energy equation term */ - const float du_dt_i = sph_du_term_i + visc_du_term_i; - const float du_dt_j = sph_du_term_j + visc_du_term_j; + const float du_dt_i = sph_du_term_i + visc_du_term; + const float du_dt_j = sph_du_term_j + visc_du_term; /* Internal energy time derivative */ pi->u_dt += du_dt_i * mj; @@ -765,13 +762,16 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); + /* 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). */ float G_ij[3] = {0.f, 0.f, 0.f}; /* These are set whether or not we fall back onto SPH gradients */ float visc_acc_term = 0.f; float sph_acc_term = (pressurei + pressurej) * rhoij_inv; float sph_du_term_i = pressurei * rhoij_inv; - float visc_du_term_i = 0.f; + float visc_du_term = 0.f; /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ @@ -891,7 +891,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; - visc_du_term_i = Q_i * rhoij_inv; + visc_du_term = 0.5f * visc_acc_term; /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ @@ -905,7 +905,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( (pi->v[2] - pj->v[2]) * G_ij[2]; sph_du_term_i *= dv_dot_G_ij; - visc_du_term_i *= dv_dot_G_ij + a2_Hubble * r2; + visc_du_term *= dv_dot_G_ij + a2_Hubble * r2; /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; @@ -941,7 +941,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( : 0.f; visc_acc_term = visc; - visc_du_term_i = 0.5f * visc_acc_term; + visc_du_term = 0.5f * visc_acc_term; const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; @@ -955,7 +955,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; sph_du_term_i *= dvdr * kernel_gradient; - visc_du_term_i *= dvdr_Hubble * kernel_gradient; + visc_du_term *= dvdr_Hubble * kernel_gradient; /* Get the time derivative for h. */ pi->force.h_dt -= mj * dvdr * r_inv * wi_dr; @@ -970,7 +970,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->a_hydro[2] -= mj * acc * G_ij[2]; /* Assemble the energy equation term */ - const float du_dt_i = sph_du_term_i + visc_du_term_i; + const float du_dt_i = sph_du_term_i + visc_du_term; /* Internal energy time derivative */ pi->u_dt += du_dt_i * mj; From 28ed7edfeee38b9a5e4cbfe9583d91f9ec93cc23 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Mon, 28 Jul 2025 13:05:39 -0400 Subject: [PATCH 11/39] Added in artificial conductivity following Rosswog 2020. Also fixed the vsig calculation as it was also wrong in the Gasoline2 code that I am using as a template. --- src/hydro/MAGMA2/hydro.h | 70 ++++++++-- src/hydro/MAGMA2/hydro_iact.h | 208 ++++++++++++++++++++++++---- src/hydro/MAGMA2/hydro_parameters.h | 3 + src/hydro/MAGMA2/hydro_part.h | 12 ++ 4 files changed, 257 insertions(+), 36 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 13fd3e51b9..38821d135e 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -377,7 +377,7 @@ hydro_set_drifted_physical_internal_energy( p->force.soundspeed = soundspeed; p->force.pressure = pressure_including_floor; - p->viscosity.v_sig = max(p->viscosity.v_sig, soundspeed); + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** @@ -401,7 +401,7 @@ hydro_set_v_sig_based_on_velocity_kick(struct part *p, /* Update the signal velocity */ p->viscosity.v_sig = - max(soundspeed, p->viscosity.v_sig + const_viscosity_beta * dv); + max(2.f * soundspeed, p->viscosity.v_sig + const_viscosity_beta * dv); } /** @@ -471,7 +471,7 @@ __attribute__((always_inline)) INLINE static float hydro_signal_velocity( const float ci = pi->force.soundspeed; const float cj = pj->force.soundspeed; - return 0.5f * (ci + cj) - mu_ij; + return ci + cj - beta * mu_ij; } /** @@ -524,6 +524,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( /* These must be zeroed before the density loop */ for (int i = 0; i < 3; i++) { + p->gradients.u_aux[i] = 0.f; + p->gradients.u_aux_norm[i] = 0.f; + for (int j = 0; j < 3; j++) { p->gradients.velocity_tensor_aux[i][j] = 0.f; p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; @@ -944,6 +947,18 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->gradients.pressure[1] *= hydro_gamma_minus_one * h_inv_dim_plus_one; p->gradients.pressure[2] *= hydro_gamma_minus_one * h_inv_dim_plus_one; + 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; + + 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]; + double aux_norm[3][3]; for (int i = 0; i < 3; i++) { p->gradients.velocity_tensor_aux[i][0] *= h_inv_dim_plus_one; @@ -1141,9 +1156,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( #ifdef MAGMA2_DEBUG_CHECKS p->debug.D_well_conditioned = 1; #endif - p->viscosity.v_sig = p->force.soundspeed; + p->viscosity.v_sig = 2.f * p->force.soundspeed; for (int i = 0; i < 3; i++) { + p->gradients.u[i] = 0.f; + p->gradients.u_hessian[i][0] = 0.f; + p->gradients.u_hessian[i][1] = 0.f; + p->gradients.u_hessian[i][2] = 0.f; + p->gradients.correction_matrix[i][0] = 0.f; p->gradients.correction_matrix[i][1] = 0.f; p->gradients.correction_matrix[i][2] = 0.f; @@ -1181,6 +1201,11 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( const float h_inv = 1.0f / h; /* 1/h */ const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + /* Normalize correctly with the smoothing length */ + 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}; @@ -1195,6 +1220,10 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( 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; @@ -1249,14 +1278,23 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( } } - /* Contract the correction matrix with the velocity tensor */ + /* Contract the correction matrix with the internal energy gradient and with + * the velocity tensor */ + double u_gradient[3] = {0}; + double u_hessian[3][3] = {0}; double velocity_tensor[3][3] = {0}; double 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]; @@ -1266,7 +1304,15 @@ __attribute__((always_inline)) INLINE static void hydro_end_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 a = 0; a < 3; a++) { + p->gradients.u_hessian[a][0] = u_hessian[a][0]; + p->gradients.u_hessian[a][1] = u_hessian[a][1]; + p->gradients.u_hessian[a][2] = u_hessian[a][2]; + p->gradients.velocity_tensor[a][0] = velocity_tensor[a][0]; p->gradients.velocity_tensor[a][1] = velocity_tensor[a][1]; p->gradients.velocity_tensor[a][2] = velocity_tensor[a][2]; @@ -1313,14 +1359,20 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( p->weighted_wcount = 1.f; p->weighted_neighbour_wcount = 1.f; + p->num_ngb = 0; p->gradients.C_well_conditioned = 0; for (int i = 0; i < 3; i++) { - p->gradients.pressure[0] = 0.f; + p->gradients.pressure[i] = 0.f; + p->gradients.u[i] = 0.f; + p->gradients.u_aux[i] = 0.f; + p->gradients.u_aux_norm[i] = 0.f; + for (int j = 0; j < 3; j++) { p->gradients.correction_matrix[i][j] = 0.f; p->gradients.velocity_tensor[i][j] = 0.f; p->gradients.velocity_tensor_aux[i][j] = 0.f; p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; + p->gradients.u_hessian[i][j] = 0.f; for (int k = 0; k < 3; k++) { p->gradients.velocity_hessian[i][j][k] = 0.f; @@ -1453,10 +1505,10 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( gas_soundspeed_from_pressure(p->rho, pressure_including_floor); p->force.pressure = pressure_including_floor; - p->force.soundspeed = soundspeed; + p->force.soundspeed = 2.f * soundspeed; /* Update the signal velocity, if we need to. */ - p->viscosity.v_sig = max(p->viscosity.v_sig, soundspeed); + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** @@ -1531,7 +1583,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( p->force.soundspeed = soundspeed; /* Update signal velocity if we need to */ - p->viscosity.v_sig = max(p->viscosity.v_sig, soundspeed); + p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); } /** diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 9c5493208b..e4e11349c3 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -98,6 +98,25 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( pj->gradients.pressure[1] -= facj * pi->u * dx[1]; pj->gradients.pressure[2] -= facj * pi->u * dx[2]; + /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary + * vector and norm for the gradient */ + const float du = pi->u - pj->u; + pi->gradients.u_aux[0] += mj * du * dx[0] * wi_dx * r_inv; + pi->gradients.u_aux[1] += mj * du * dx[1] * wi_dx * r_inv; + pi->gradients.u_aux[2] += mj * du * dx[2] * wi_dx * r_inv; + + pj->gradients.u_aux[0] += mi * du * dx[0] * wj_dx * r_inv; + pj->gradients.u_aux[1] += mi * du * dx[1] * wj_dx * r_inv; + pj->gradients.u_aux[2] += mi * du * dx[2] * wj_dx * r_inv; + + pi->gradients.u_aux_norm[0] += mj * dx[0] * dx[0] * wi_dx * r_inv; + pi->gradients.u_aux_norm[1] += mj * dx[1] * dx[1] * wi_dx * r_inv; + pi->gradients.u_aux_norm[2] += mj * dx[2] * dx[2] * wi_dx * r_inv; + + pj->gradients.u_aux_norm[0] += mi * dx[0] * dx[0] * wj_dx * r_inv; + pj->gradients.u_aux_norm[1] += mi * dx[1] * dx[1] * wj_dx * r_inv; + pj->gradients.u_aux_norm[2] += mi * dx[2] * dx[2] * wj_dx * r_inv; + const float dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; @@ -173,6 +192,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( pi->gradients.pressure[1] += faci * pj->u * dx[1]; pi->gradients.pressure[2] += faci * pj->u * dx[2]; + /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary + * vector and norm for the gradient */ + + const float du = pi->u - pj->u; + pi->gradients.u_aux[0] += mj * du * dx[0] * wi_dx * r_inv; + pi->gradients.u_aux[1] += mj * du * dx[1] * wi_dx * r_inv; + pi->gradients.u_aux[2] += mj * du * dx[2] * wi_dx * r_inv; + + pi->gradients.u_aux_norm[0] += mj * dx[0] * dx[0] * wi_dx * r_inv; + pi->gradients.u_aux_norm[1] += mj * dx[1] * dx[1] * wi_dx * r_inv; + pi->gradients.u_aux_norm[2] += mj * dx[2] * dx[2] * wi_dx * r_inv; + const float dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; @@ -284,12 +315,27 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( pi->weighted_neighbour_wcount += mj * r2 * wi_dx * rho_inv_j * r_inv; pj->weighted_neighbour_wcount += mi * r2 * wj_dx * rho_inv_i * r_inv; + /* Rosswog 2020 Equation 18 gradients. In the paper he uses (vj - vi) and + * (rj - ri), however this is symmetric so no sign problems. */ + const float du = pi->u - pj->u; + pi->gradients.u[0] += mj * rho_inv_j * du * dx[0] * wi; + pi->gradients.u[1] += mj * rho_inv_j * du * dx[1] * wi; + pi->gradients.u[2] += mj * rho_inv_j * du * dx[2] * wi; + + pj->gradients.u[0] += mi * rho_inv_i * du * dx[0] * wj; + pj->gradients.u[1] += mi * rho_inv_i * du * dx[1] * wj; + pj->gradients.u[2] += mi * rho_inv_i * du * dx[2] * wj; + const float 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++) { for (int i = 0; i < 3; i++) { + const float du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[k]; + pi->gradients.u_hessian[k][i] += mj * rho_inv_j * du_k * dx[i] * wi; + pj->gradients.u_hessian[k][i] += mi * rho_inv_i * du_k * dx[i] * wj; + /* dx is signed as (pi - pj), but it is symmetric so we add */ pi->gradients.correction_matrix[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; pj->gradients.correction_matrix[k][i] += mi * rho_inv_i * dx[k] * dx[i] * wj; @@ -397,12 +443,22 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( pi->weighted_neighbour_wcount += mj * r2 * wi_dx * rho_inv_j * r_inv; + /* Rosswog 2020 Equation 18 gradients. In the paper he uses (vj - vi) and + * (rj - ri), however this is symmetric so no sign problems. */ + const float du = pi->u - pj->u; + pi->gradients.u[0] += mj * rho_inv_j * du * dx[0] * wi; + pi->gradients.u[1] += mj * rho_inv_j * du * dx[1] * wi; + pi->gradients.u[2] += mj * rho_inv_j * du * dx[2] * wi; + const float 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++) { for (int i = 0; i < 3; i++) { + const float du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[i]; + pi->gradients.u_hessian[k][i] += mj * rho_inv_j * du_k * dx[i] * wi; + /* dx is signed as (pi - pj), but it is symmetric so we add */ pi->gradients.correction_matrix[k][i] += mj * rho_inv_j * dx[k] * dx[i] * wi; @@ -495,6 +551,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( float sph_du_term_i = pressurei * rhoij_inv; float sph_du_term_j = pressurej * rhoij_inv; float visc_du_term = 0.f; + float cond_du_term = 0.f; /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ @@ -549,20 +606,22 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 0.5f * ((float)pi->num_ngb + (float)pj->num_ngb); /* Compute global Van Leer limiter (scalar, not component-wise) */ - const float phi_ij = hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, - pj->gradients.velocity_tensor, - dx_ij, xi, xj, num_ngb_ij); - const float phi_ji = hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, - pi->gradients.velocity_tensor, - dx_ji, xj, xi, num_ngb_ij); + const float phi_ij_vec = + hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, + pj->gradients.velocity_tensor, + dx_ij, xi, xj, num_ngb_ij); + const float phi_ji_vec = + hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, + pi->gradients.velocity_tensor, + dx_ji, xj, xi, num_ngb_ij); /* dx_ji for particle i and dx_ij for particle j */ - hydro_vector_second_order_reconstruction(phi_ij, dx_ji, pi->v, + hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ji, pi->v, pi->gradients.velocity_tensor, pi->gradients.velocity_hessian, vi_reconstructed); - hydro_vector_second_order_reconstruction(phi_ji, dx_ij, pj->v, + hydro_vector_second_order_reconstruction(phi_ji_vec, dx_ij, pj->v, pj->gradients.velocity_tensor, pj->gradients.velocity_hessian, vj_reconstructed); @@ -603,25 +662,70 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( fminf(0.f, dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2)); const float Q_i_alpha = - -const_viscosity_alpha * pi->force.soundspeed * mu_i; - const float Q_i_beta = const_viscosity_beta * mu_i * mu_i; + -const_viscosity_alpha * pi->force.soundspeed * mu_i * fac_mu; + const float Q_i_beta = const_viscosity_beta * mu_i * mu_i * fac_mu * fac_mu; const float Q_i = rhoi * (Q_i_alpha + Q_i_beta); const float Q_j_alpha = - -const_viscosity_alpha * pj->force.soundspeed * mu_j; - const float Q_j_beta = const_viscosity_beta * mu_j * mu_j; + -const_viscosity_alpha * pj->force.soundspeed * mu_j * fac_mu; + const float Q_j_beta = const_viscosity_beta * mu_j * mu_j * fac_mu * fac_mu; const float Q_j = rhoj * (Q_j_alpha + Q_j_beta); /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; visc_du_term = 0.5f * visc_acc_term; + + /* Artificial conductivity */ + + + float ui_reconstructed = 0.f; + float uj_reconstructed = 0.f; + + /* Compute global Van Leer limiter (scalar, not component-wise) */ + const float phi_ij_scalar = + hydro_scalar_van_leer_phi(pi->gradients.u, + pj->gradients.u, + dx_ij, xi, xj, num_ngb_ij); + const float phi_ji_scalar = + hydro_scalar_van_leer_phi(pj->gradients.u, + pi->gradients.u, + dx_ji, xj, xi, num_ngb_ij); + + /* dx_ji for particle i and dx_ij for particle j */ + hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, pi->u, + pi->gradients.u, + pi->gradients.u_hessian, + &ui_reconstructed); + + hydro_scalar_second_order_reconstruction(phi_ji_scalar, dx_ij, pj->u, + pj->gradients.u, + pj->gradients.u_hessian, + &uj_reconstructed); + + const float vsig_u = sqrtf(dv_ij[0] * dv_ij[0] + + dv_ij[1] * dv_ij[1] + + dv_ij[2] * dv_ij[2]); + const float du_ij = ui_reconstructed - uj_reconstructed; + const float rho_ij = 0.5f * (rhoi + rhoj); + + /* Add conductivity to the specific energy */ + cond_du_term = -const_conductivity_alpha * vsig_u * du_ij / rho_ij; + + + /* Finalize everything with the correct normalizations. */ + + /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ G_ij[0] = 0.5f * (G_i[0] + G_j[0]); G_ij[1] = 0.5f * (G_i[1] + G_j[1]); G_ij[2] = 0.5f * (G_i[2] + G_j[2]); + const float G_ij_norm = sqrtf(G_ij[0] * G_ij[0] + + G_ij[1] * G_ij[1] + + G_ij[2] * G_ij[2]); + /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + (pi->v[1] - pj->v[1]) * G_ij[1] + @@ -630,6 +734,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; visc_du_term *= dv_dot_G_ij + a2_Hubble * r2; + cond_du_term *= G_ij_norm; /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; @@ -705,8 +810,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the time derivative for u. */ /* Assemble the energy equation term */ - const float du_dt_i = sph_du_term_i + visc_du_term; - const float du_dt_j = sph_du_term_j + visc_du_term; + const float du_dt_i = sph_du_term_i + visc_du_term + cond_du_term; + const float du_dt_j = sph_du_term_j + visc_du_term - cond_du_term; /* Internal energy time derivative */ pi->u_dt += du_dt_i * mj; @@ -772,6 +877,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( float sph_acc_term = (pressurei + pressurej) * rhoij_inv; float sph_du_term_i = pressurei * rhoij_inv; float visc_du_term = 0.f; + float cond_du_term = 0.f; /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ @@ -826,20 +932,22 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( 0.5f * ((float)pi->num_ngb + (float)pj->num_ngb); /* Compute global Van Leer limiter (scalar, not component-wise) */ - const float phi_ij = hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, - pj->gradients.velocity_tensor, - dx_ij, xi, xj, num_ngb_ij); - const float phi_ji = hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, - pi->gradients.velocity_tensor, - dx_ji, xj, xi, num_ngb_ij); + const float phi_ij_vec = + hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, + pj->gradients.velocity_tensor, + dx_ij, xi, xj, num_ngb_ij); + const float phi_ji_vec = + hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, + pi->gradients.velocity_tensor, + dx_ji, xj, xi, num_ngb_ij); /* dx_ji for particle i and dx_ij for particle j */ - hydro_vector_second_order_reconstruction(phi_ij, dx_ji, pi->v, + hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ji, pi->v, pi->gradients.velocity_tensor, pi->gradients.velocity_hessian, vi_reconstructed); - hydro_vector_second_order_reconstruction(phi_ji, dx_ij, pj->v, + hydro_vector_second_order_reconstruction(phi_ji_vec, dx_ij, pj->v, pj->gradients.velocity_tensor, pj->gradients.velocity_hessian, vj_reconstructed); @@ -880,25 +988,70 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( fminf(0.f, dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2)); const float Q_i_alpha = - -const_viscosity_alpha * pi->force.soundspeed * mu_i; - const float Q_i_beta = const_viscosity_beta * mu_i * mu_i; + -const_viscosity_alpha * pi->force.soundspeed * mu_i * fac_mu; + const float Q_i_beta = const_viscosity_beta * mu_i * mu_i * fac_mu * fac_mu; const float Q_i = rhoi * (Q_i_alpha + Q_i_beta); const float Q_j_alpha = - -const_viscosity_alpha * pj->force.soundspeed * mu_j; - const float Q_j_beta = const_viscosity_beta * mu_j * mu_j; + -const_viscosity_alpha * pj->force.soundspeed * mu_j * fac_mu; + const float Q_j_beta = const_viscosity_beta * mu_j * mu_j * fac_mu * fac_mu; const float Q_j = rhoj * (Q_j_alpha + Q_j_beta); /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; visc_du_term = 0.5f * visc_acc_term; + + /* Artificial conductivity */ + + + float ui_reconstructed = 0.f; + float uj_reconstructed = 0.f; + + /* Compute global Van Leer limiter (scalar, not component-wise) */ + const float phi_ij_scalar = + hydro_scalar_van_leer_phi(pi->gradients.u, + pj->gradients.u, + dx_ij, xi, xj, num_ngb_ij); + const float phi_ji_scalar = + hydro_scalar_van_leer_phi(pj->gradients.u, + pi->gradients.u, + dx_ji, xj, xi, num_ngb_ij); + + /* dx_ji for particle i and dx_ij for particle j */ + hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, pi->u, + pi->gradients.u, + pi->gradients.u_hessian, + &ui_reconstructed); + + hydro_scalar_second_order_reconstruction(phi_ji_scalar, dx_ij, pj->u, + pj->gradients.u, + pj->gradients.u_hessian, + &uj_reconstructed); + + const float vsig_u = sqrtf(dv_ij[0] * dv_ij[0] + + dv_ij[1] * dv_ij[1] + + dv_ij[2] * dv_ij[2]); + const float du_ij = ui_reconstructed - uj_reconstructed; + const float rho_ij = 0.5f * (rhoi + rhoj); + + /* Add conductivity to the specific energy */ + cond_du_term = -const_conductivity_alpha * vsig_u * du_ij / rho_ij; + + + /* Finalize the viscosity and conductivity with correct normalizations. */ + + /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ G_ij[0] = 0.5f * (G_i[0] + G_j[0]); G_ij[1] = 0.5f * (G_i[1] + G_j[1]); G_ij[2] = 0.5f * (G_i[2] + G_j[2]); + const float G_ij_norm = sqrtf(G_ij[0] * G_ij[0] + + G_ij[1] * G_ij[1] + + G_ij[2] * G_ij[2]); + /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + (pi->v[1] - pj->v[1]) * G_ij[1] + @@ -906,6 +1059,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( sph_du_term_i *= dv_dot_G_ij; visc_du_term *= dv_dot_G_ij + a2_Hubble * r2; + cond_du_term *= G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; @@ -970,7 +1124,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->a_hydro[2] -= mj * acc * G_ij[2]; /* Assemble the energy equation term */ - const float du_dt_i = sph_du_term_i + visc_du_term; + const float du_dt_i = sph_du_term_i + visc_du_term + cond_du_term; /* Internal energy time derivative */ pi->u_dt += du_dt_i * mj; diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index aa36192af8..1d14617f7d 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -61,6 +61,9 @@ /*! Softening squared (epsilon^2) in Eq. 15 Rosswog 2020 */ #define const_viscosity_epsilon2 0.01f +/*! Artificial conductivity alpha */ +#define const_conductivity_alpha 0.05f + /*! The viscosity that the particles are reset to after being hit by a * feedback event. This should be set to the same value as the * hydro_props_default_viscosity_alpha in fixed schemes, and likely diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 9ad4ee3c54..b648f599ea 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -184,6 +184,18 @@ struct part { /*! Smoothed pressure gradient */ float pressure[3]; + /*! Internal energy gradient */ + float u[3]; + + /*! Auxiliary internal energy gradient */ + float u_aux[3]; + + /*! Normalization for computing u_aux */ + float u_aux_norm[3]; + + /*! Internal energy Hessian */ + float u_hessian[3][3]; + } gradients; /* Store viscosity information in a separate struct. */ From 76cac7c494585f33620f0afa615c6f613780a62f Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Mon, 28 Jul 2025 17:05:45 -0400 Subject: [PATCH 12/39] A lot of code clean-up in the hydro interactions. Redefined the maximum signal velocity to a variable p->v_sig_max that is used to compute the timestep. Now use p->h_min (minimum smoothing length in the kernel) to compute the time-step. Removed old Gasoline2 alpha viscosity estimators and decay terms, as well as the diffusion rate estimators. Small changes to hydro tests to get them to run. --- .../KelvinHelmholtzGrowthRate_3D/run.sh | 2 +- examples/HydroTests/Rayleigh-Taylor_2D/run.sh | 0 .../roberts_flow_acceleration/forcing.h | 6 + src/hydro/MAGMA2/hydro.h | 254 +++----- src/hydro/MAGMA2/hydro_debug.h | 10 +- src/hydro/MAGMA2/hydro_iact.h | 548 +++++++++--------- src/hydro/MAGMA2/hydro_io.h | 91 ++- src/hydro/MAGMA2/hydro_parameters.h | 120 +--- src/hydro/MAGMA2/hydro_part.h | 43 +- 9 files changed, 430 insertions(+), 644 deletions(-) mode change 100644 => 100755 examples/HydroTests/Rayleigh-Taylor_2D/run.sh diff --git a/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh index 73f81e3da1..574f55b9ea 100755 --- a/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh +++ b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh @@ -14,7 +14,7 @@ then fi # Run SWIFT -../../../swift --hydro --threads=1 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log +../../../swift --hydro --threads=16 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log # Plot the solution python3 plotSolution.py 100 diff --git a/examples/HydroTests/Rayleigh-Taylor_2D/run.sh b/examples/HydroTests/Rayleigh-Taylor_2D/run.sh old mode 100644 new mode 100755 diff --git a/src/forcing/roberts_flow_acceleration/forcing.h b/src/forcing/roberts_flow_acceleration/forcing.h index d739e7e3a9..b07cdc666b 100644 --- a/src/forcing/roberts_flow_acceleration/forcing.h +++ b/src/forcing/roberts_flow_acceleration/forcing.h @@ -87,8 +87,14 @@ __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 + /* TODO: MAGMA2 does not track alpha per particle */ + const float nu = terms->nu * const_viscosity_alpha * c_s * p->h / + (2.f * (hydro_dimension + 1.f)); +#endif const float Vz_factor = terms->Vz_factor; const double k0 = (2. * M_PI / L) * terms->kv; diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 38821d135e..f458440c98 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -377,7 +377,7 @@ hydro_set_drifted_physical_internal_energy( p->force.soundspeed = soundspeed; p->force.pressure = pressure_including_floor; - p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); + p->v_sig_max = max(p->v_sig_max, soundspeed); } /** @@ -400,8 +400,8 @@ hydro_set_v_sig_based_on_velocity_kick(struct part *p, const float soundspeed = hydro_get_comoving_soundspeed(p); /* Update the signal velocity */ - p->viscosity.v_sig = - max(2.f * soundspeed, p->viscosity.v_sig + const_viscosity_beta * dv); + p->v_sig_max = + max(soundspeed, p->v_sig_max + const_viscosity_beta * dv); } /** @@ -411,9 +411,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) { - p->viscosity.alpha = alpha; -} + struct part *restrict p, float alpha) { } /** * @brief Update the value of the diffusive coefficients to the @@ -424,10 +422,7 @@ __attribute__((always_inline)) INLINE static void hydro_set_viscosity_alpha( __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, - hydro_props_default_viscosity_alpha_feedback_reset); - - p->diffusion.rate = hydro_props_default_diffusion_coefficient_feedback_reset; + hydro_set_viscosity_alpha(p, const_viscosity_alpha); } /** @@ -448,8 +443,9 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const float CFL_condition = hydro_properties->CFL_condition; /* CFL condition */ - const float dt_cfl = 2.f * kernel_gamma * CFL_condition * cosmo->a * p->h / - (cosmo->a_factor_sound_speed * p->viscosity.v_sig); + const float h_min = kernel_gamma * p->h_min * cosmo->a; + const float v_sig_max = p->v_sig_max * cosmo->a_factor_sound_speed; + const float dt_cfl = CFL_condition * h_min / v_sig_max; return dt_cfl; } @@ -471,7 +467,7 @@ __attribute__((always_inline)) INLINE static float hydro_signal_velocity( const float ci = pi->force.soundspeed; const float cj = pj->force.soundspeed; - return ci + cj - beta * mu_ij; + return 0.5f * (ci + cj - beta * mu_ij); } /** @@ -510,18 +506,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->density.wcount = 0.f; p->density.wcount_dh = 0.f; p->rho = 0.f; - p->weighted_wcount = 0.f; - p->weighted_neighbour_wcount = 0.f; p->density.rho_dh = 0.f; p->num_ngb = 0; - p->gradients.pressure[0] = 0.f; - p->gradients.pressure[1] = 0.f; - p->gradients.pressure[2] = 0.f; - - p->viscosity.shock_indicator = 0.f; - p->viscosity.shock_limiter = 0.f; - /* These must be zeroed before the density loop */ for (int i = 0; i < 3; i++) { p->gradients.u_aux[i] = 0.f; @@ -941,12 +928,6 @@ __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 caclulation of the pressure gradients; note that the - * kernel derivative is zero at our particle's position. */ - p->gradients.pressure[0] *= hydro_gamma_minus_one * h_inv_dim_plus_one; - p->gradients.pressure[1] *= hydro_gamma_minus_one * h_inv_dim_plus_one; - p->gradients.pressure[2] *= hydro_gamma_minus_one * h_inv_dim_plus_one; - 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; @@ -955,9 +936,22 @@ __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; - 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]; + if (fabsf(p->gradients.u_aux_norm[0]) > FLT_EPSILON && + fabsf(p->gradients.u_aux_norm[1]) > FLT_EPSILON && + fabsf(p->gradients.u_aux_norm[2]) > FLT_EPSILON) { + 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]; + } + else { + p->gradients.u_aux[0] = 0.f; + p->gradients.u_aux[1] = 0.f; + p->gradients.u_aux[2] = 0.f; + + p->gradients.u_aux_norm[0] = 0.f; + p->gradients.u_aux_norm[1] = 0.f; + p->gradients.u_aux_norm[2] = 0.f; + } double aux_norm[3][3]; for (int i = 0; i < 3; i++) { @@ -986,20 +980,18 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( gsl_linalg_LU_invert(A, p_perm, A_inv); for (int i = 0; i < 3; i++) { - aux_norm[i][0] = gsl_matrix_get(A_inv, i, 0); - aux_norm[i][1] = gsl_matrix_get(A_inv, i, 1); - aux_norm[i][2] = gsl_matrix_get(A_inv, i, 2); + 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 */ - double aux_matrix[3][3]; + float aux_matrix[3][3] = {0}; for (int j = 0; j < 3; j++) { for (int i = 0; i < 3; i++) { - aux_matrix[j][i] = 0.; - /** * The indices of aux_norm and velocity_gradient_aux are important. * aux_norm j: dx vector direction. @@ -1012,17 +1004,20 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( * i: velocity direction */ for (int k = 0; k < 3; k++) { - aux_matrix[j][i] += aux_norm[j][k] * p->gradients.velocity_tensor_aux[i][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 */ - for (int a = 0; a < 3; a++) { - for (int b = 0; b < 3; b++) { - p->gradients.velocity_tensor_aux[a][b] = aux_matrix[a][b]; - p->gradients.velocity_tensor_aux_norm[a][b] = aux_norm[a][b]; - } + + /* 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++) { + 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 { @@ -1080,64 +1075,6 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( /* Update variables. */ p->force.pressure = pressure_including_floor; p->force.soundspeed = soundspeed; - - const float mod_pressure_gradient = - sqrtf(p->gradients.pressure[0] * p->gradients.pressure[0] + - p->gradients.pressure[1] * p->gradients.pressure[1] + - p->gradients.pressure[2] * p->gradients.pressure[2]); - - float unit_pressure_gradient[3]; - - /* As this is normalised, no cosmology factor is required */ - unit_pressure_gradient[0] = - p->gradients.pressure[0] / mod_pressure_gradient; - unit_pressure_gradient[1] = - p->gradients.pressure[1] / mod_pressure_gradient; - unit_pressure_gradient[2] = - p->gradients.pressure[2] / mod_pressure_gradient; - - float dv_dn = 0.f; - float shear_norm2 = 0.f; - float traceless_shear_norm2 = 0.f; - float div_v = 0.f; - - /* Physical velocity gradient tensor with Hubble flow */ - float velocity_tensor_phys[3][3] = {0}; - - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - /* Convert to physical for use in artificial viscosity */ - velocity_tensor_phys[i][j] = p->gradients.velocity_tensor_aux[i][j]; - velocity_tensor_phys[i][j] *= cosmo->a2_inv; - if (i == j) velocity_tensor_phys[i][j] += cosmo->H; - - dv_dn += unit_pressure_gradient[i] * - velocity_tensor_phys[i][j] * unit_pressure_gradient[j]; - - const float shear_component = - 0.5f * (velocity_tensor_phys[i][j] + velocity_tensor_phys[j][i]); - const float shear_component2 = shear_component * shear_component; - - shear_norm2 += shear_component2; - traceless_shear_norm2 += i == j ? 0.f : shear_component2; - div_v += i == j ? velocity_tensor_phys[i][j] : 0.f; - } - } - - const float shock_indicator = - (3.f / 2.f) * (dv_dn + max(-(1.f / 3.f) * div_v, 0.f)); - - /* Now do the conduction coefficient; note that no limiter is used here. */ - /* These square roots are not included in the original documentation */ - const float h_physical = p->h * cosmo->a; - - const float diffusion_rate = hydro_props->diffusion.coefficient * - sqrtf(traceless_shear_norm2) * h_physical * - h_physical; - - p->diffusion.rate = diffusion_rate; - p->viscosity.tensor_norm = sqrtf(shear_norm2); - p->viscosity.shock_indicator = shock_indicator; } /** @@ -1152,11 +1089,11 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( __attribute__((always_inline)) INLINE static void hydro_reset_gradient( struct part *restrict p) { + p->h_min = p->h; p->gradients.C_well_conditioned = 1; #ifdef MAGMA2_DEBUG_CHECKS p->debug.D_well_conditioned = 1; #endif - p->viscosity.v_sig = 2.f * p->force.soundspeed; for (int i = 0; i < 3; i++) { p->gradients.u[i] = 0.f; @@ -1193,9 +1130,6 @@ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( __attribute__((always_inline)) INLINE static void hydro_end_gradient( struct part *p) { - /* The f_i is calculated explicitly in MAGMA2. */ - p->force.f = p->weighted_wcount / (p->weighted_neighbour_wcount * p->rho); - /* Calculate smoothing length powers */ const float h = p->h; const float h_inv = 1.0f / h; /* 1/h */ @@ -1210,7 +1144,6 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( double correction_matrix[3][3] = {0}; /* Apply correct normalisation */ - p->viscosity.shock_limiter *= h_inv_dim; 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; @@ -1280,10 +1213,10 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( /* Contract the correction matrix with the internal energy gradient and with * the velocity tensor */ - double u_gradient[3] = {0}; - double u_hessian[3][3] = {0}; - double velocity_tensor[3][3] = {0}; - double velocity_hessian[3][3][3] = {0}; + float u_gradient[3] = {0}; + float u_hessian[3][3] = {0}; + float velocity_tensor[3][3] = {0}; + float 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] * @@ -1308,20 +1241,26 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( p->gradients.u[1] = u_gradient[1]; p->gradients.u[2] = u_gradient[2]; - for (int a = 0; a < 3; a++) { - p->gradients.u_hessian[a][0] = u_hessian[a][0]; - p->gradients.u_hessian[a][1] = u_hessian[a][1]; - p->gradients.u_hessian[a][2] = u_hessian[a][2]; - - p->gradients.velocity_tensor[a][0] = velocity_tensor[a][0]; - p->gradients.velocity_tensor[a][1] = velocity_tensor[a][1]; - p->gradients.velocity_tensor[a][2] = velocity_tensor[a][2]; - - for (int b = 0; b < 3; b++) { - p->gradients.velocity_hessian[a][b][0] = velocity_hessian[a][b][0]; - p->gradients.velocity_hessian[a][b][1] = velocity_hessian[a][b][1]; - p->gradients.velocity_hessian[a][b][2] = velocity_hessian[a][b][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]; + + 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]; } } @@ -1351,18 +1290,15 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( /* Re-set problematic values */ p->rho = p->mass * kernel_root * h_inv_dim; - p->viscosity.v_sig = 0.f; + p->h_min = 0.f; + p->v_sig_max = 0.f; p->density.wcount = kernel_root * h_inv_dim; p->density.rho_dh = 0.f; p->density.wcount_dh = 0.f; - /* Set to 1 as these are only used by taking the ratio */ - p->weighted_wcount = 1.f; - p->weighted_neighbour_wcount = 1.f; p->num_ngb = 0; p->gradients.C_well_conditioned = 0; for (int i = 0; i < 3; i++) { - p->gradients.pressure[i] = 0.f; p->gradients.u[i] = 0.f; p->gradients.u_aux[i] = 0.f; p->gradients.u_aux_norm[i] = 0.f; @@ -1405,55 +1341,8 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( 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) { - /* Here we need to update the artificial viscosity alpha */ - const float d_shock_indicator_dt = - dt_alpha == 0.f ? 0.f - : (p->viscosity.shock_indicator - - p->viscosity.shock_indicator_previous_step) / - dt_alpha; - - /* A_i in all of the equations */ - const float v_sig_physical = p->viscosity.v_sig * cosmo->a_factor_sound_speed; - const float soundspeed_physical = - gas_soundspeed_from_pressure(hydro_get_physical_density(p, cosmo), - hydro_get_physical_pressure(p, cosmo)); - const float h_physical = p->h * cosmo->a; - - /* Note that viscosity.shock_limiter still includes the cosmology dependence - * from the density, so what we do here is correct (i.e. include no explicit - * h-factors). */ - const float shock_limiter_core = - 0.5f * (1.f - p->viscosity.shock_limiter / p->rho); - const float shock_limiter_core_2 = shock_limiter_core * shock_limiter_core; - const float shock_limiter = shock_limiter_core_2 * shock_limiter_core_2; - - const float shock_detector = 2.f * h_physical * h_physical * kernel_gamma2 * - shock_limiter * - max(-1.f * d_shock_indicator_dt, 0.f); - - const float alpha_loc = - hydro_props->viscosity.alpha_max * - (shock_detector / (shock_detector + v_sig_physical * v_sig_physical)); - - /* TODO: Probably use physical _h_ throughout this function */ - const float d_alpha_dt = (alpha_loc - p->viscosity.alpha) * - hydro_props->viscosity.length * soundspeed_physical / - h_physical; - - float new_alpha = p->viscosity.alpha; - - if (new_alpha < alpha_loc) { - new_alpha = alpha_loc; - } else { - /* Very basic time integration here */ - new_alpha += d_alpha_dt * dt_alpha; - } - - new_alpha = max(new_alpha, hydro_props->viscosity.alpha_min); - new_alpha = min(new_alpha, hydro_props->viscosity.alpha_max); - p->viscosity.shock_indicator_previous_step = p->viscosity.shock_indicator; - p->viscosity.alpha = new_alpha; + p->v_sig_max = p->force.soundspeed; } /** @@ -1505,10 +1394,10 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( gas_soundspeed_from_pressure(p->rho, pressure_including_floor); p->force.pressure = pressure_including_floor; - p->force.soundspeed = 2.f * soundspeed; + p->force.soundspeed = soundspeed; /* Update the signal velocity, if we need to. */ - p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); + p->v_sig_max = max(p->v_sig_max, soundspeed); } /** @@ -1583,7 +1472,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( p->force.soundspeed = soundspeed; /* Update signal velocity if we need to */ - p->viscosity.v_sig = max(p->viscosity.v_sig, 2.f * soundspeed); + p->v_sig_max = max(p->v_sig_max, soundspeed); } /** @@ -1686,8 +1575,6 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( /* Set the initial value of the artificial viscosity based on the non-variable schemes for safety */ - p->viscosity.alpha = hydro_props->viscosity.alpha; - 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); @@ -1717,9 +1604,6 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( xp->v_full[2] = p->v[2]; xp->u_full = p->u; - p->viscosity.shock_indicator_previous_step = 0.f; - p->viscosity.tensor_norm = 0.f; - p->gradients.C_well_conditioned = 1; #ifdef MAGMA2_DEBUG_CHECKS p->debug.C_ill_conditioned_count = 0; diff --git a/src/hydro/MAGMA2/hydro_debug.h b/src/hydro/MAGMA2/hydro_debug.h index a9005400a9..c79c9da7c4 100644 --- a/src/hydro/MAGMA2/hydro_debug.h +++ b/src/hydro/MAGMA2/hydro_debug.h @@ -36,16 +36,16 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( 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 v_sig=%.3e, P=%.3e", p->id, p->u, - p->u_dt, p->viscosity.v_sig, hydro_get_comoving_pressure(p)); + p->u_dt, p->v_sig_max, 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] alpha=%.3e, time_bin=%d, rho=%.3e, velocity_gradient=[" + "[PID%lld] time_bin=%d, rho=%.3e, velocity_gradient=[" "[%.3e,%.3e,%.3e]," "[%.3e,%.3e,%.3e]," "[%.3e,%.3e,%.3e]]", - p->id, p->viscosity.alpha, p->time_bin, p->rho, + 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], @@ -55,10 +55,6 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( p->gradients.velocity_tensor[2][0], p->gradients.velocity_tensor[2][1], p->gradients.velocity_tensor[2][2]); - warning("[PID%lld] gradients.pressure=[%.3e,%.3e,%.3e]", p->id, - p->gradients.pressure[0], p->gradients.pressure[1], - p->gradients.pressure[2]); - warning("[PID%lld] weighted_wcount=%.3e", p->id, p->weighted_wcount); 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 e4e11349c3..ec353bdcb9 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -84,38 +84,29 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( adaptive_softening_add_correction_term(pj, uj, hj_inv, mi); - /* Now we need to compute the div terms */ + /* Now we need to compute the derivative terms */ const float r_inv = r ? 1.0f / r : 0.0f; const float faci = mj * wi_dx * r_inv; const float facj = mi * wj_dx * r_inv; - /* Smooth pressure gradient */ - pi->gradients.pressure[0] += faci * pj->u * dx[0]; - pi->gradients.pressure[1] += faci * pj->u * dx[1]; - pi->gradients.pressure[2] += faci * pj->u * dx[2]; - - pj->gradients.pressure[0] -= facj * pi->u * dx[0]; - pj->gradients.pressure[1] -= facj * pi->u * dx[1]; - pj->gradients.pressure[2] -= facj * pi->u * dx[2]; - /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary * vector and norm for the gradient */ const float du = pi->u - pj->u; - pi->gradients.u_aux[0] += mj * du * dx[0] * wi_dx * r_inv; - pi->gradients.u_aux[1] += mj * du * dx[1] * wi_dx * r_inv; - pi->gradients.u_aux[2] += mj * du * dx[2] * wi_dx * r_inv; + 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] += mi * du * dx[0] * wj_dx * r_inv; - pj->gradients.u_aux[1] += mi * du * dx[1] * wj_dx * r_inv; - pj->gradients.u_aux[2] += mi * du * dx[2] * wj_dx * r_inv; + 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] += mj * dx[0] * dx[0] * wi_dx * r_inv; - pi->gradients.u_aux_norm[1] += mj * dx[1] * dx[1] * wi_dx * r_inv; - pi->gradients.u_aux_norm[2] += mj * dx[2] * dx[2] * wi_dx * r_inv; + 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] += mi * dx[0] * dx[0] * wj_dx * r_inv; - pj->gradients.u_aux_norm[1] += mi * dx[1] * dx[1] * wj_dx * r_inv; - pj->gradients.u_aux_norm[2] += mi * dx[2] * dx[2] * wj_dx * r_inv; + 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 float dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], @@ -125,15 +116,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( * dv * dx always results in a positive sign. */ for (int i = 0; i < 3; i++) { for (int k = 0; k < 3; k++) { - pi->gradients.velocity_tensor_aux[i][k] += - mj * dv[i] * dx[k] * wi_dx * r_inv; - pj->gradients.velocity_tensor_aux[i][k] += - mi * dv[i] * dx[k] * wj_dx * r_inv; - - pi->gradients.velocity_tensor_aux_norm[i][k] += - mj * dx[i] * dx[k] * wi_dx * r_inv; - pj->gradients.velocity_tensor_aux_norm[i][k] += - mi * dx[i] * dx[k] * wj_dx * r_inv; + 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; } } @@ -141,11 +128,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( pi->num_ngb++; pj->num_ngb++; - /* Correction factors for kernel gradients, and norm for the velocity - * gradient. */ - - pi->weighted_wcount += mj * r2 * wi_dx * r_inv; - pj->weighted_wcount += mi * r2 * wj_dx * r_inv; } /** @@ -187,22 +169,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( const float r_inv = r ? 1.0f / r : 0.0f; const float faci = mj * wi_dx * r_inv; - /* Compute pressure gradient */ - pi->gradients.pressure[0] += faci * pj->u * dx[0]; - pi->gradients.pressure[1] += faci * pj->u * dx[1]; - pi->gradients.pressure[2] += faci * pj->u * dx[2]; - /* Equations 19 & 20 in Rosswog 2020. Compute the internal energy auxiliary * vector and norm for the gradient */ const float du = pi->u - pj->u; - pi->gradients.u_aux[0] += mj * du * dx[0] * wi_dx * r_inv; - pi->gradients.u_aux[1] += mj * du * dx[1] * wi_dx * r_inv; - pi->gradients.u_aux[2] += mj * du * dx[2] * wi_dx * r_inv; + 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] += mj * dx[0] * dx[0] * wi_dx * r_inv; - pi->gradients.u_aux_norm[1] += mj * dx[1] * dx[1] * wi_dx * r_inv; - pi->gradients.u_aux_norm[2] += mj * dx[2] * dx[2] * wi_dx * r_inv; + 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 float dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], @@ -212,19 +189,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( * dv * dx always results in a positive sign. */ for (int i = 0; i < 3; i++) { for (int k = 0; k < 3; k++) { - pi->gradients.velocity_tensor_aux[i][k] += - mj * dv[i] * dx[k] * wi_dx * r_inv; - - pi->gradients.velocity_tensor_aux_norm[i][k] += - mj * dx[i] * dx[k] * wi_dx * r_inv; + 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; } } + /* Neighbour number */ pi->num_ngb++; - - /* Correction factors for kernel gradients, and norm for the velocity - * gradient. */ - pi->weighted_wcount += mj * r2 * wi_dx * r_inv; } /** @@ -255,37 +226,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float rhoi = hydro_get_comoving_density(pi); const float rhoj = hydro_get_comoving_density(pj); - /* We need to construct the maximal signal velocity between our particle - * and all of it's neighbours */ + const float rhoi_inv = 1.f / rhoi; + const float rhoj_inv = 1.f / rhoj; const float r = sqrtf(r2); - const float r_inv = r ? 1.0f / r : 0.0f; - - /* Cosmology terms for the signal velocity */ - const float fac_mu = pow_three_gamma_minus_five_over_two(a); - const float a2_Hubble = a * a * H; - - const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + - (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; - - /* Add Hubble flow */ - - const float dvdr_Hubble = dvdr + a2_Hubble * r2; - /* Are the particles moving towards each others ? */ - const float omega_ij = min(dvdr_Hubble, 0.f); - const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ - - /* Signal velocity */ - const float new_v_sig = - signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); - /* Update if we need to */ - pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); - pj->viscosity.v_sig = max(pj->viscosity.v_sig, new_v_sig); - - /* Calculate Del^2 u for the thermal diffusion coefficient. */ - /* Need to get some kernel values F_ij = wi_dx */ float wi, wi_dx, wj, wj_dx; const float ui = r / hi; @@ -294,38 +239,31 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( kernel_deval(ui, &wi, &wi_dx); kernel_deval(uj, &wj, &wj_dx); - /* Calculate the shock limiter component */ - const float shock_ratio_i = - pj->viscosity.tensor_norm > 0.f - ? pj->viscosity.shock_indicator / pj->viscosity.tensor_norm - : 0.f; - - const float shock_ratio_j = - pi->viscosity.tensor_norm > 0.f - ? pi->viscosity.shock_indicator / pi->viscosity.tensor_norm - : 0.f; - - pi->viscosity.shock_limiter += mj * shock_ratio_i * wi; - pj->viscosity.shock_limiter += mi * shock_ratio_j * wj; - - /* Correction factors for kernel gradients */ - const float rho_inv_i = 1.f / rhoi; - const float rho_inv_j = 1.f / rhoj; + const float faci = mj * rhoj_inv * wi; + const float facj = mi * rhoi_inv * wj; - pi->weighted_neighbour_wcount += mj * r2 * wi_dx * rho_inv_j * r_inv; - pj->weighted_neighbour_wcount += mi * r2 * wj_dx * rho_inv_i * r_inv; + /* Minimum smoothing length in the kernel */ + const float h_min = min(hi, hj); + pi->h_min = min(pi->h_min, h_min); + pj->h_min = min(pj->h_min, h_min); + + /* 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 float du = pi->u - pj->u; - pi->gradients.u[0] += mj * rho_inv_j * du * dx[0] * wi; - pi->gradients.u[1] += mj * rho_inv_j * du * dx[1] * wi; - pi->gradients.u[2] += mj * rho_inv_j * du * dx[2] * wi; - pj->gradients.u[0] += mi * rho_inv_i * du * dx[0] * wj; - pj->gradients.u[1] += mi * rho_inv_i * du * dx[1] * wj; - pj->gradients.u[2] += mi * rho_inv_i * du * dx[2] * wj; + 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 float dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; @@ -333,18 +271,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { const float du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[k]; - pi->gradients.u_hessian[k][i] += mj * rho_inv_j * du_k * dx[i] * wi; - pj->gradients.u_hessian[k][i] += mi * rho_inv_i * du_k * dx[i] * wj; + 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] += mj * rho_inv_j * dx[k] * dx[i] * wi; - pj->gradients.correction_matrix[k][i] += mi * rho_inv_i * dx[k] * dx[i] * wj; + 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] += mj * rho_inv_j * dv[k] * dx[i] * wi; - pj->gradients.velocity_tensor[k][i] += mi * rho_inv_i * dv[k] * dx[i] * wj; + pi->gradients.velocity_tensor[k][i] += dv[k] * dx[i] * faci; + pj->gradients.velocity_tensor[k][i] += dv[k] * dx[i] * facj; const float dv_grad_ki = pi->gradients.velocity_tensor_aux[k][i] - pj->gradients.velocity_tensor_aux[k][i]; @@ -359,10 +297,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( * Index j: second derivative gradient direction */ for (int j = 0; j < 3; j++) { - pi->gradients.velocity_hessian[k][i][j] += - mj * rho_inv_j * dv_grad_ki * dx[j] * wi; - pj->gradients.velocity_hessian[k][i][j] += - mi * rho_inv_i * dv_grad_ki * dx[j] * wj; + 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; } } } @@ -393,63 +329,30 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( /* Get particle properties */ const float mj = hydro_get_mass(pj); const float rhoj = hydro_get_comoving_density(pj); - - /* We need to construct the maximal signal velocity between our particle - * and all of it's neighbours */ - + const float rhoj_inv = 1.f / rhoj; const float r = sqrtf(r2); - const float r_inv = r ? 1.0f / r : 0.0f; - - /* Cosmology terms for the signal velocity */ - const float fac_mu = pow_three_gamma_minus_five_over_two(a); - const float a2_Hubble = a * a * H; - - const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + - (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; - - /* Add Hubble flow */ - - const float dvdr_Hubble = dvdr + a2_Hubble * r2; - /* Are the particles moving towards each others ? */ - const float omega_ij = min(dvdr_Hubble, 0.f); - const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ - - /* Signal velocity */ - const float new_v_sig = - signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); - - /* Update if we need to */ - pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); - - /* Need to get some kernel values F_ij = wi_dx */ float wi, wi_dx; - const float ui = r / hi; - kernel_deval(ui, &wi, &wi_dx); + const float faci = mj * rhoj_inv * wi; - /* Calculate the shock limiter component */ - const float shock_ratio_i = - pj->viscosity.tensor_norm > 0.f - ? pj->viscosity.shock_indicator / pj->viscosity.tensor_norm - : 0.f; - - pi->viscosity.shock_limiter += mj * shock_ratio_i * wi; - - /* Correction factors for kernel gradients */ - - const float rho_inv_j = 1.f / rhoj; + /* Minimum smoothing length in the kernel */ + const float h_min = min(hi, hj); + pi->h_min = min(pi->h_min, h_min); - pi->weighted_neighbour_wcount += mj * r2 * wi_dx * rho_inv_j * r_inv; + /* 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 float du = pi->u - pj->u; - pi->gradients.u[0] += mj * rho_inv_j * du * dx[0] * wi; - pi->gradients.u[1] += mj * rho_inv_j * du * dx[1] * wi; - pi->gradients.u[2] += mj * rho_inv_j * du * dx[2] * wi; + 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 float dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; @@ -457,17 +360,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { const float du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[i]; - pi->gradients.u_hessian[k][i] += mj * rho_inv_j * du_k * dx[i] * wi; + 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] += - mj * rho_inv_j * dx[k] * dx[i] * wi; + 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] += - mj * rho_inv_j * dv[k] * dx[i] * wi; + pi->gradients.velocity_tensor[k][i] += dv[k] * dx[i] * faci; const float dv_grad_ki = pi->gradients.velocity_tensor_aux[k][i] - pj->gradients.velocity_tensor_aux[k][i]; @@ -482,8 +383,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( * Index j: second derivative gradient direction */ for (int j = 0; j < 3; j++) { - pi->gradients.velocity_hessian[k][i][j] += - mj * rho_inv_j * dv_grad_ki * dx[j] * wi; + pi->gradients.velocity_hessian[k][i][j] += dv_grad_ki * dx[j] * faci; } } } @@ -605,15 +505,21 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float num_ngb_ij = 0.5f * ((float)pi->num_ngb + (float)pj->num_ngb); + /* Rosswog 2020 h_i and h_j are actually kernel_gamma * h */ + const float hi_inv_kernel = hi_inv * kernel_gamma_inv; + const float hj_inv_kernel = hj_inv * kernel_gamma_inv; + const float eta_i_ij = r * hi_inv_kernel; + const float eta_j_ij = r * hj_inv_kernel; + /* Compute global Van Leer limiter (scalar, not component-wise) */ const float phi_ij_vec = hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, pj->gradients.velocity_tensor, - dx_ij, xi, xj, num_ngb_ij); + dx_ij, eta_i_ij, eta_j_ij, num_ngb_ij); const float phi_ji_vec = hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, pi->gradients.velocity_tensor, - dx_ji, xj, xi, num_ngb_ij); + dx_ji, eta_j_ij, eta_i_ij, num_ngb_ij); /* dx_ji for particle i and dx_ij for particle j */ hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ji, pi->v, @@ -632,43 +538,77 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( vi_reconstructed[2] - vj_reconstructed[2]}; const float dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; - const float eta_i[3] = {dx_ij[0] * hi_inv, - dx_ij[1] * hi_inv, - dx_ij[2] * hi_inv}; - const float eta_j[3] = {dx_ji[0] * hj_inv, - dx_ji[1] * hj_inv, - dx_ji[2] * hj_inv}; - const float eta_i2 = eta_i[0] * eta_i[0] + - eta_i[1] * eta_i[1] + - eta_i[2] * eta_i[2]; - const float eta_j2 = eta_j[0] * eta_j[0] + - eta_j[1] * eta_j[1] + - eta_j[2] * eta_j[2]; - const float dv_dot_eta_i = dv_ij[0] * eta_i[0] + - dv_ij[1] * eta_i[1] + - dv_ij[2] * eta_i[2]; + const float eta_i[3] = {dx_ij[0] * hi_inv_kernel, + dx_ij[1] * hi_inv_kernel, + dx_ij[2] * hi_inv_kernel}; + const float eta_j[3] = {dx_ji[0] * hj_inv_kernel, + dx_ji[1] * hj_inv_kernel, + dx_ji[2] * hj_inv_kernel}; + float eta_i2 = 0.f; + hydro_vec3_vec3_dot(eta_i, eta_i, &eta_i2); + + float eta_j2 = 0.f; + hydro_vec3_vec3_dot(eta_j, eta_j, &eta_j2); + + float dv_dot_eta_i = 0.f; + hydro_vec3_vec3_dot(dv_ij, eta_i, &dv_dot_eta_i); /* Scale Hubble flow by hi_inv so it is overall scaled */ - const float dv_dot_eta_i_phys = dv_dot_eta_i + a2_Hubble * r2 * hi_inv; - const float dv_dot_eta_j = dv_ji[0] * eta_j[0] + - dv_ji[1] * eta_j[1] + - dv_ji[2] * eta_j[2]; + const float dv_dot_eta_i_phys = + dv_dot_eta_i + a2_Hubble * r2 * hi_inv_kernel; + + float dv_dot_eta_j = 0.f; + hydro_vec3_vec3_dot(dv_ji, eta_j, &dv_dot_eta_j); /* Scale Hubble flow by hj_inv so it is overall scaled */ - const float dv_dot_eta_j_phys = dv_dot_eta_j + a2_Hubble * r2 * hj_inv; + const float dv_dot_eta_j_phys = + dv_dot_eta_j + a2_Hubble * r2 * hj_inv_kernel; + + /* Is the flow converging? If so, multiply mu by fac_mu. If not, zero. */ + const float conv_i = (dv_dot_eta_i_phys < 0.f) ? fac_mu : 0.f; + const float conv_j = (dv_dot_eta_j_phys < 0.f) ? fac_mu : 0.f; + + /* Both must be approaching */ + const float conv = (conv_i != 0.f && conv_j != 0.f) ? fac_mu : 0.f; + +#ifdef MAGMA2_DEBUG_CHECKS + if (conv_i != conv_j) { + warning("Convergence factor mismatch for particles pi=%lld and pj=%lld.\n" + "conv_i = %g, conv_j = %g\n" + "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" + "vi_reconstructed = (%g, %g, %g), vj_reconstructed = (%g, %g, %g)\n" + "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" + "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" + "eta_i2 = %g, eta_j2 = %g\n" + "hi_inv_kernel = %g, hj_inv_kernel = %g\n" + "a2_Hubble = %g, r2 = %g\n", + pi->id, pj->id, + conv_i, conv_j, + dv_dot_eta_i_phys, dv_dot_eta_j_phys, + vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], + vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], + dv_ij[0], dv_ij[1], dv_ij[2], + dv_ji[0], dv_ji[1], dv_ji[2], + eta_i[0], eta_i[1], eta_i[2], + eta_j[0], eta_j[1], eta_j[2], + eta_i2, eta_j2, + hi_inv_kernel, hj_inv_kernel, + a2_Hubble, r2); + } +#endif - /* mu_i and mu_j are physical, not comoving */ + /* mu_i and mu_j include the Hubble flow */ const float mu_i = - fminf(0.f, dv_dot_eta_i_phys / (eta_i2 + const_viscosity_epsilon2)); + conv * dv_dot_eta_i_phys / (eta_i2 + const_viscosity_epsilon2); const float mu_j = - fminf(0.f, dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2)); + conv * dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2); const float Q_i_alpha = - -const_viscosity_alpha * pi->force.soundspeed * mu_i * fac_mu; - const float Q_i_beta = const_viscosity_beta * mu_i * mu_i * fac_mu * fac_mu; + -const_viscosity_alpha * pi->force.soundspeed * mu_i; + const float Q_i_beta = const_viscosity_beta * mu_i * mu_i; const float Q_i = rhoi * (Q_i_alpha + Q_i_beta); const float Q_j_alpha = - -const_viscosity_alpha * pj->force.soundspeed * mu_j * fac_mu; - const float Q_j_beta = const_viscosity_beta * mu_j * mu_j * fac_mu * fac_mu; + -const_viscosity_alpha * pj->force.soundspeed * mu_j; + const float Q_j_beta = const_viscosity_beta * mu_j * mu_j; const float Q_j = rhoj * (Q_j_alpha + Q_j_beta); /* Add viscosity to the pressure */ @@ -686,11 +626,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float phi_ij_scalar = hydro_scalar_van_leer_phi(pi->gradients.u, pj->gradients.u, - dx_ij, xi, xj, num_ngb_ij); + dx_ij, eta_i_ij, eta_j_ij, num_ngb_ij); const float phi_ji_scalar = hydro_scalar_van_leer_phi(pj->gradients.u, pi->gradients.u, - dx_ji, xj, xi, num_ngb_ij); + dx_ji, eta_j_ij, eta_i_ij, num_ngb_ij); /* dx_ji for particle i and dx_ij for particle j */ hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, pi->u, @@ -703,14 +643,16 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->gradients.u_hessian, &uj_reconstructed); - const float vsig_u = sqrtf(dv_ij[0] * dv_ij[0] + - dv_ij[1] * dv_ij[1] + - dv_ij[2] * dv_ij[2]); + float dv_ij_dot_dv_ij = 0.f; + hydro_vec3_vec3_dot(dv_ij, dv_ij, &dv_ij_dot_dv_ij); + /* Signal velocity is the norm of the velocity difference vector */ + const float v_sig_cond = sqrtf(dv_ij_dot_dv_ij); + const float du_ij = ui_reconstructed - uj_reconstructed; const float rho_ij = 0.5f * (rhoi + rhoj); /* Add conductivity to the specific energy */ - cond_du_term = -const_conductivity_alpha * vsig_u * du_ij / rho_ij; + cond_du_term = -const_conductivity_alpha * v_sig_cond * du_ij / rho_ij; /* Finalize everything with the correct normalizations. */ @@ -722,11 +664,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_ij[1] = 0.5f * (G_i[1] + G_j[1]); G_ij[2] = 0.5f * (G_i[2] + G_j[2]); - const float G_ij_norm = sqrtf(G_ij[0] * G_ij[0] + - G_ij[1] * G_ij[1] + - G_ij[2] * G_ij[2]); + float G_ij_dot_G_ij = 0.f; + hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_dot_G_ij); + const float G_ij_norm = sqrtf(G_ij_dot_G_ij); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ + /* TODO: Use second-order reconstructions here? */ const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + (pi->v[1] - pj->v[1]) * G_ij[1] + (pi->v[2] - pj->v[2]) * G_ij[2]; @@ -739,6 +682,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; pj->force.h_dt -= mi * dv_dot_G_ij; + + /* New signal velocity */ + const float v_sig_visc_i = + signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); + const float v_sig_visc_j = + signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); + const float v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); + const float v_sig = max(v_sig_visc, v_sig_cond); + + /* Update if we need to */ + pi->v_sig_max = max(pi->v_sig_max, v_sig); + pj->v_sig_max = max(pj->v_sig_max, v_sig); } else { @@ -749,9 +704,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_ij[2] = dx[2]; /* Compute dv dot dr. */ - const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + - (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + const float dv[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; + float dvdr = 0.f; + hydro_vec3_vec3_dot(dv, G_ij, &dvdr); /* Includes the hubble flow term; not used for du/dt */ const float dvdr_Hubble = dvdr + a2_Hubble * r2; @@ -762,7 +719,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Construct the full viscosity term */ const float rho_ij = rhoi + rhoj; - const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; + const float alpha = const_viscosity_alpha; const float visc = omega_ij < 0.f ? (-0.25f * alpha * (pi->force.soundspeed + pj->force.soundspeed) * @@ -771,6 +728,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( (0.5f * rho_ij) : 0.f; + /* New signal velocity */ + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); + + /* Update if we need to */ + pi->v_sig_max = max(pi->v_sig_max, new_v_sig); + pj->v_sig_max = max(pj->v_sig_max, new_v_sig); + visc_acc_term = visc; visc_du_term = 0.5f * visc_acc_term; @@ -780,8 +745,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float wj_dr = hj_inv_dim_plus_one * wj_dx; /* Variable smoothing length term */ - const float kernel_gradient = - 0.5f * r_inv * (wi_dr * pi->force.f + wj_dr * pj->force.f); + const float kernel_gradient = 0.5f * (wi_dr + wj_dr) * r_inv; visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; @@ -931,15 +895,21 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float num_ngb_ij = 0.5f * ((float)pi->num_ngb + (float)pj->num_ngb); + /* Rosswog 2020 h_i and h_j are actually kernel_gamma * h */ + const float hi_inv_kernel = hi_inv * kernel_gamma_inv; + const float hj_inv_kernel = hj_inv * kernel_gamma_inv; + const float eta_i_ij = r * hi_inv_kernel; + const float eta_j_ij = r * hj_inv_kernel; + /* Compute global Van Leer limiter (scalar, not component-wise) */ const float phi_ij_vec = hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, pj->gradients.velocity_tensor, - dx_ij, xi, xj, num_ngb_ij); + dx_ij, eta_i_ij, eta_j_ij, num_ngb_ij); const float phi_ji_vec = hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, pi->gradients.velocity_tensor, - dx_ji, xj, xi, num_ngb_ij); + dx_ji, eta_j_ij, eta_i_ij, num_ngb_ij); /* dx_ji for particle i and dx_ij for particle j */ hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ji, pi->v, @@ -958,43 +928,78 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( vi_reconstructed[2] - vj_reconstructed[2]}; const float dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; - const float eta_i[3] = {dx_ij[0] * hi_inv, - dx_ij[1] * hi_inv, - dx_ij[2] * hi_inv}; - const float eta_j[3] = {dx_ji[0] * hj_inv, - dx_ji[1] * hj_inv, - dx_ji[2] * hj_inv}; - const float eta_i2 = eta_i[0] * eta_i[0] + - eta_i[1] * eta_i[1] + - eta_i[2] * eta_i[2]; - const float eta_j2 = eta_j[0] * eta_j[0] + - eta_j[1] * eta_j[1] + - eta_j[2] * eta_j[2]; - const float dv_dot_eta_i = dv_ij[0] * eta_i[0] + - dv_ij[1] * eta_i[1] + - dv_ij[2] * eta_i[2]; - /* Scale Hubble flow by hi_inv so it is overall scaled */ - const float dv_dot_eta_i_phys = dv_dot_eta_i + a2_Hubble * r2 * hi_inv; - const float dv_dot_eta_j = dv_ji[0] * eta_j[0] + - dv_ji[1] * eta_j[1] + - dv_ji[2] * eta_j[2]; + const float eta_i[3] = {dx_ij[0] * hi_inv_kernel, + dx_ij[1] * hi_inv_kernel, + dx_ij[2] * hi_inv_kernel}; + const float eta_j[3] = {dx_ji[0] * hj_inv_kernel, + dx_ji[1] * hj_inv_kernel, + dx_ji[2] * hj_inv_kernel}; + + float eta_i2 = 0.f; + hydro_vec3_vec3_dot(eta_i, eta_i, &eta_i2); + + float eta_j2 = 0.f; + hydro_vec3_vec3_dot(eta_j, eta_j, &eta_j2); + + float dv_dot_eta_i = 0.f; + hydro_vec3_vec3_dot(dv_ij, eta_i, &dv_dot_eta_i); /* Scale Hubble flow by hi_inv so it is overall scaled */ - const float dv_dot_eta_j_phys = dv_dot_eta_j + a2_Hubble * r2 * hj_inv; + const float dv_dot_eta_i_phys = + dv_dot_eta_i + a2_Hubble * r2 * hi_inv_kernel; - /* mu_i and mu_j are physical, not comoving */ + float dv_dot_eta_j = 0.f; + hydro_vec3_vec3_dot(dv_ji, eta_j, &dv_dot_eta_j); + /* Scale Hubble flow by hj_inv so it is overall scaled */ + const float dv_dot_eta_j_phys = + dv_dot_eta_j + a2_Hubble * r2 * hj_inv_kernel; + + /* Is the flow converging? */ + const float conv_i = (dv_dot_eta_i_phys < 0.f) ? fac_mu : 0.f; + const float conv_j = (dv_dot_eta_j_phys < 0.f) ? fac_mu : 0.f; + + /* They both must be approaching. */ + const float conv = (conv_i != 0.f && conv_j != 0.f) ? fac_mu : 0.f; + +#ifdef MAGMA2_DEBUG_CHECKS + if (conv_i != conv_j) { + warning("Convergence factor mismatch for particles pi=%lld and pj=%lld.\n" + "conv_i = %g, conv_j = %g\n" + "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" + "vi_reconstructed = (%g, %g, %g), vj_reconstructed = (%g, %g, %g)\n" + "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" + "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" + "eta_i2 = %g, eta_j2 = %g\n" + "hi_inv_kernel = %g, hj_inv_kernel = %g\n" + "a2_Hubble = %g, r2 = %g\n", + pi->id, pj->id, + conv_i, conv_j, + dv_dot_eta_i_phys, dv_dot_eta_j_phys, + vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], + vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], + dv_ij[0], dv_ij[1], dv_ij[2], + dv_ji[0], dv_ji[1], dv_ji[2], + eta_i[0], eta_i[1], eta_i[2], + eta_j[0], eta_j[1], eta_j[2], + eta_i2, eta_j2, + hi_inv_kernel, hj_inv_kernel, + a2_Hubble, r2); + } +#endif + + /* mu_i and mu_j include the Hubble flow */ const float mu_i = - fminf(0.f, dv_dot_eta_i_phys / (eta_i2 + const_viscosity_epsilon2)); + conv * dv_dot_eta_i_phys / (eta_i2 + const_viscosity_epsilon2); const float mu_j = - fminf(0.f, dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2)); + conv * dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2); const float Q_i_alpha = - -const_viscosity_alpha * pi->force.soundspeed * mu_i * fac_mu; - const float Q_i_beta = const_viscosity_beta * mu_i * mu_i * fac_mu * fac_mu; + -const_viscosity_alpha * pi->force.soundspeed * mu_i; + const float Q_i_beta = const_viscosity_beta * mu_i * mu_i; const float Q_i = rhoi * (Q_i_alpha + Q_i_beta); const float Q_j_alpha = - -const_viscosity_alpha * pj->force.soundspeed * mu_j * fac_mu; - const float Q_j_beta = const_viscosity_beta * mu_j * mu_j * fac_mu * fac_mu; + -const_viscosity_alpha * pj->force.soundspeed * mu_j; + const float Q_j_beta = const_viscosity_beta * mu_j * mu_j; const float Q_j = rhoj * (Q_j_alpha + Q_j_beta); /* Add viscosity to the pressure */ @@ -1012,11 +1017,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float phi_ij_scalar = hydro_scalar_van_leer_phi(pi->gradients.u, pj->gradients.u, - dx_ij, xi, xj, num_ngb_ij); + dx_ij, eta_i_ij, eta_j_ij, num_ngb_ij); const float phi_ji_scalar = hydro_scalar_van_leer_phi(pj->gradients.u, pi->gradients.u, - dx_ji, xj, xi, num_ngb_ij); + dx_ji, eta_j_ij, eta_i_ij, num_ngb_ij); /* dx_ji for particle i and dx_ij for particle j */ hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, pi->u, @@ -1029,14 +1034,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pj->gradients.u_hessian, &uj_reconstructed); - const float vsig_u = sqrtf(dv_ij[0] * dv_ij[0] + - dv_ij[1] * dv_ij[1] + - dv_ij[2] * dv_ij[2]); + float dv_ij_dot_dv_ij = 0.f; + hydro_vec3_vec3_dot(dv_ij, dv_ij, &dv_ij_dot_dv_ij); + const float v_sig_cond = sqrtf(dv_ij_dot_dv_ij); + const float du_ij = ui_reconstructed - uj_reconstructed; const float rho_ij = 0.5f * (rhoi + rhoj); /* Add conductivity to the specific energy */ - cond_du_term = -const_conductivity_alpha * vsig_u * du_ij / rho_ij; + cond_du_term = -const_conductivity_alpha * v_sig_cond * du_ij / rho_ij; /* Finalize the viscosity and conductivity with correct normalizations. */ @@ -1048,11 +1054,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( G_ij[1] = 0.5f * (G_i[1] + G_j[1]); G_ij[2] = 0.5f * (G_i[2] + G_j[2]); - const float G_ij_norm = sqrtf(G_ij[0] * G_ij[0] + - G_ij[1] * G_ij[1] + - G_ij[2] * G_ij[2]); + float G_ij_dot_G_ij = 0.f; + hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_dot_G_ij); + const float G_ij_norm = sqrtf(G_ij_dot_G_ij); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ + /* TODO: Use reconstructed velocities here? */ const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + (pi->v[1] - pj->v[1]) * G_ij[1] + (pi->v[2] - pj->v[2]) * G_ij[2]; @@ -1063,6 +1070,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; + + /* New signal velocity */ + const float v_sig_visc_i = + signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); + const float v_sig_visc_j = + signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); + const float v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); + const float v_sig = max(v_sig_visc, v_sig_cond); + + /* Update if we need to */ + pi->v_sig_max = max(pi->v_sig_max, v_sig); } else { @@ -1072,9 +1090,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( G_ij[2] = dx[2]; /* Compute dv dot dr. */ - const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + - (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + const float dv[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[2]}; + float dvdr = 0.f; + hydro_vec3_vec3_dot(dv, G_ij, &dvdr); /* Includes the hubble flow term; not used for du/dt */ const float dvdr_Hubble = dvdr + a2_Hubble * r2; @@ -1085,7 +1105,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Construct the full viscosity term */ const float rho_ij = rhoi + rhoj; - const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; + const float alpha = const_viscosity_alpha; const float visc = omega_ij < 0.f ? (-0.25f * alpha * (pi->force.soundspeed + pj->force.soundspeed) * @@ -1094,6 +1114,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( (0.5f * rho_ij) : 0.f; + /* New signal velocity */ + const float new_v_sig = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); + + /* Update if we need to */ + pi->v_sig_max = max(pi->v_sig_max, new_v_sig); + visc_acc_term = visc; visc_du_term = 0.5f * visc_acc_term; @@ -1103,8 +1130,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float wj_dr = hj_inv_dim_plus_one * wj_dx; /* Variable smoothing length term */ - const float kernel_gradient = - 0.5f * r_inv * (wi_dr * pi->force.f + wj_dr * pj->force.f); + const float kernel_gradient = 0.5f * (wi_dr + wj_dr) * r_inv; visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; diff --git a/src/hydro/MAGMA2/hydro_io.h b/src/hydro/MAGMA2/hydro_io.h index 20543f339a..66fe0d5dae 100644 --- a/src/hydro/MAGMA2/hydro_io.h +++ b/src/hydro/MAGMA2/hydro_io.h @@ -161,12 +161,6 @@ INLINE static void convert_part_potential(const struct engine* e, ret[0] = 0.f; } -INLINE static void convert_viscosity(const struct engine* e, - const struct part* p, - const struct xpart* xp, float* ret) { - ret[0] = p->viscosity.alpha; -} - /** * @brief Specifies which particle fields to write to a dataset * @@ -178,107 +172,106 @@ INLINE static void hydro_write_particles(const struct part* parts, const struct xpart* xparts, struct io_props* list, int* num_fields) { - *num_fields = 14; + *num_fields = 0; + int num = 0; /* List what we want to write */ - list[0] = io_make_output_field_convert_part( + 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[1] = io_make_output_field_convert_part( + 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[2] = 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[3] = io_make_output_field( + 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[4] = io_make_output_field( + 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[5] = io_make_physical_output_field( + 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[6] = 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[7] = io_make_output_field_convert_part( + 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[8] = io_make_output_field_convert_part( + 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[9] = io_make_output_field_convert_part( - "ViscosityParameters", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, xparts, - convert_viscosity, - "Visosity coefficient (alpha_visc) of the particles, multiplied by the " - "balsara switch"); - - list[10] = io_make_output_field_convert_part( + 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++; - /* Units and cosmology TBD */ - list[11] = - io_make_output_field("ShockIndicators", FLOAT, 1, UNIT_CONV_FREQUENCY, - 0.f, parts, viscosity.shock_indicator, - "Physical shock indicators (D in the paper) created " - "from the velocity tensor."); - - /* Units and cosmology TBD */ - list[12] = io_make_output_field( - "DiffusionRates", FLOAT, 1, UNIT_CONV_THERMAL_DIFFUSIVITY, 0.f, parts, - diffusion.rate, - "Physical diffusion rates calculated from the shear tensor."); - - list[13] = io_make_output_field_convert_part( + 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++; #ifdef MAGMA2_DEBUG_CHECKS - list[14] = io_make_output_field( + 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[15] = io_make_output_field( + 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[16] = io_make_output_field( - "NumeratorMatrices", FLOAT, 9, UNIT_CONV_DENSITY * UNIT_CONV_SPEED / UNIT_CONV_LENGTH, + list[num] = io_make_output_field( + "NumeratorMatrices", 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[17] = io_make_output_field( + list[num] = io_make_output_field( "DenominatorMatrices", FLOAT, 9, UNIT_CONV_DENSITY, -3.f, parts, debug.velocity_tensor_aux_norm, "Co-moving denominator matrices for the particles."); + num++; - list[18] = io_make_output_field( + list[num] = io_make_output_field( "DenominatorIllConditionedCounts", 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_fields = 19; + num++; #endif + *num_fields = num; } /** @@ -291,7 +284,7 @@ INLINE static void hydro_write_flavour(hid_t h_grpsph) { io_write_attribute_s(h_grpsph, "Thermal Conductivity Model", "Simple treatment as in Price (2008)"); io_write_attribute_s(h_grpsph, "Viscosity Model", - "Simplified version of Cullen & Denhen (2011)"); + "Gingold & Monaghan (1983)"); } /** diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 1d14617f7d..4f0f3fe235 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -48,9 +48,9 @@ /*! Consider C matrix can be ill-conditioned above this limit */ #define const_condition_number_upper_limit 999.f -/*! Viscosity parameters -- FIXED -- MUST BE DEFINED AT COMPILE-TIME */ +/*! Viscosity & Conductivitiy parameters -- MUST BE DEFINED AT COMPILE-TIME */ -/*! Cosmology default beta=3.0. +/*! Cosmology default beta=2.0. * Alpha can be set in the parameter file. * Beta is defined as in e.g. Price (2010) Eqn (103) */ #define const_viscosity_beta 2.0f @@ -64,64 +64,13 @@ /*! Artificial conductivity alpha */ #define const_conductivity_alpha 0.05f -/*! The viscosity that the particles are reset to after being hit by a - * feedback event. This should be set to the same value as the - * hydro_props_default_viscosity_alpha in fixed schemes, and likely - * to hydro_props_default_viscosity_alpha_max in variable schemes. */ -#define hydro_props_default_viscosity_alpha_feedback_reset 2.0f - -/* Viscosity paramaters -- Defaults; can be changed at run-time */ - -/*! The "initial" hydro viscosity, or the fixed value for non-variable - * schemes. This usually takes the value 0.8. */ -#define hydro_props_default_viscosity_alpha 0.1f - -/*! Minimal value for the viscosity alpha in variable schemes. */ -#define hydro_props_default_viscosity_alpha_min 0.0f - -/*! Maximal value for the viscosity alpha in variable schemes. */ -#define hydro_props_default_viscosity_alpha_max 2.0f - -/*! Decay length for the viscosity scheme. This is scheme dependent. */ -#define hydro_props_default_viscosity_length 0.2f - -/* Diffusion parameters -- FIXED -- MUST BE DEFINED AT COMPILE-TIME */ - -/*! The diffusion that the particles are reset to after being hit by a - * feedback event. This should be set to the same value as the - * hydro_props_default_diffusion_alpha in fixed schemes, and likely - * to hydro_props_default_diffusion_alpha_min in variable schemes. */ -#define hydro_props_default_diffusion_coefficient_feedback_reset 0.0f - -/* Diffusion parameters -- Defaults; can be changed at run-time */ - -/*! Rate limiting coefficient for the diffusion. */ -#define hydro_props_default_diffusion_coefficient 0.03f - -/* Structs that store the relevant variables */ +/* Structures for below */ /*! Artificial viscosity parameters */ -struct viscosity_global_data { - /*! For the fixed, simple case. Also used to set the initial AV - coefficient for variable schemes. */ - float alpha; - - /*! Artificial viscosity (max) for the variable case (e.g. M&M) */ - float alpha_max; - - /*! Artificial viscosity (min) for the variable case (e.g. M&M) */ - float alpha_min; - - /*! The decay length of the artificial viscosity (used in M&M, etc.) */ - float length; -}; +struct viscosity_global_data { }; /*! Thermal diffusion parameters */ -struct diffusion_global_data { - - /*! Rate limiting coefficcient */ - float coefficient; -}; +struct diffusion_global_data { }; /* Functions for reading from parameter file */ @@ -144,25 +93,7 @@ struct unit_system; 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) { - - /* Read the artificial viscosity parameters from the file, if they exist, - * otherwise set them to the defaults defined above. */ - - viscosity->alpha = parser_get_opt_param_float( - params, "SPH:viscosity_alpha", hydro_props_default_viscosity_alpha); - - viscosity->alpha_max = - parser_get_opt_param_float(params, "SPH:viscosity_alpha_max", - hydro_props_default_viscosity_alpha_max); - - viscosity->alpha_min = - parser_get_opt_param_float(params, "SPH:viscosity_alpha_min", - hydro_props_default_viscosity_alpha_min); - - viscosity->length = parser_get_opt_param_float( - params, "SPH:viscosity_length", hydro_props_default_viscosity_length); -} + struct viscosity_global_data* viscosity) { } /** * @brief Initialises a viscosity struct to sensible numbers for mocking @@ -171,12 +102,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) { - viscosity->alpha = hydro_props_default_viscosity_alpha; - viscosity->alpha_max = hydro_props_default_viscosity_alpha_max; - viscosity->alpha_min = hydro_props_default_viscosity_alpha_min; - viscosity->length = hydro_props_default_viscosity_length; -} + struct viscosity_global_data* viscosity) { } /** * @brief Prints out the viscosity parameters at the start of a run. @@ -186,11 +112,8 @@ static INLINE void viscosity_init_no_hydro( **/ static INLINE void viscosity_print( const struct viscosity_global_data* viscosity) { - message( - "Artificial viscosity parameters set to alpha: %.3f, max: %.3f, " - "min: %.3f, length: %.3f.", - viscosity->alpha, viscosity->alpha_max, viscosity->alpha_min, - viscosity->length); + message("Artificial viscosity alpha set to %.3f", const_viscosity_alpha); + message("Artificial viscosity beta set to %.3f", const_viscosity_beta); } #if defined(HAVE_HDF5) @@ -203,11 +126,7 @@ static INLINE void viscosity_print( static INLINE void viscosity_print_snapshot( hid_t h_grpsph, const struct viscosity_global_data* viscosity) { - io_write_attribute_f(h_grpsph, "Alpha viscosity", viscosity->alpha); - io_write_attribute_f(h_grpsph, "Alpha viscosity (max)", viscosity->alpha_max); - io_write_attribute_f(h_grpsph, "Alpha viscosity (min)", viscosity->alpha_min); - io_write_attribute_f(h_grpsph, "Viscosity decay length [internal units]", - viscosity->length); + io_write_attribute_f(h_grpsph, "Alpha viscosity", const_viscosity_alpha); io_write_attribute_f(h_grpsph, "Beta viscosity", const_viscosity_beta); } #endif @@ -226,12 +145,7 @@ static INLINE void viscosity_print_snapshot( 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) { - - diffusion->coefficient = - parser_get_opt_param_float(params, "SPH:diffusion_coefficient", - hydro_props_default_diffusion_coefficient); -} + struct diffusion_global_data* diffusion) { } /** * @brief Initialises a diffusion struct to sensible numbers for mocking @@ -240,9 +154,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) { - diffusion->coefficient = hydro_props_default_diffusion_coefficient; -} + struct diffusion_global_data* diffusion) { } /** * @brief Prints out the diffusion parameters at the start of a run. @@ -252,8 +164,8 @@ static INLINE void diffusion_init_no_hydro( **/ static INLINE void diffusion_print( const struct diffusion_global_data* diffusion) { - message("Artificial diffusion parameters set to coefficient: %.3f", - diffusion->coefficient); + message("Artificial conductivity alpha set to %.3f", + const_conductivity_alpha); } #ifdef HAVE_HDF5 @@ -265,8 +177,8 @@ static INLINE void diffusion_print( **/ static INLINE void diffusion_print_snapshot( hid_t h_grpsph, const struct diffusion_global_data* diffusion) { - io_write_attribute_f(h_grpsph, "Diffusion coefficient", - diffusion->coefficient); + io_write_attribute_f(h_grpsph, "Conductivity alpha", + const_conductivity_alpha); } #endif diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index b648f599ea..0074d005c2 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -128,15 +128,15 @@ struct part { /*! Particle density. */ float rho; - /*! Weightings for correction factor */ - float weighted_wcount; - - /*! Neighbour weightings */ - float weighted_neighbour_wcount; - /*! Number of neighbors in the kernel */ int num_ngb; + /*! Minimum smoothing length in the kernel */ + float h_min; + + /*! Maximum signal velocity in the kernel */ + float v_sig_max; + #ifdef MAGMA2_DEBUG_CHECKS struct { /*! C matrix at the last time it was ill-conditioned */ @@ -198,37 +198,6 @@ struct part { } gradients; - /* Store viscosity information in a separate struct. */ - struct { - - /*! Velocity gradient tensor trace norm |T| */ - float tensor_norm; - - /*! Shock limiter (top portion of R) */ - float shock_limiter; - - /*! Shock indicator (D) */ - float shock_indicator; - - /*! Particle shock indicator from previous step */ - float shock_indicator_previous_step; - - /*! Artificial viscosity parameter */ - float alpha; - - /*! Signal velocity */ - float v_sig; - - } viscosity; - - /* Store thermal diffusion information in a separate struct. */ - struct { - - /*! Thermal diffusion rate */ - float rate; - - } diffusion; - /* Store density/force specific stuff. */ union { /** From 13e25aa6798cf11b0ab085b081998e92dfad3199 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Mon, 28 Jul 2025 17:34:20 -0400 Subject: [PATCH 13/39] It is important to avoid floating point errors in the dot products. It seems like they are building up rapidly in the 2nd order reconstruction. It might be that all of these calculations must be done in double precision, or at least zeroed out whenever the dot products are not above FLT_EPSILON. --- src/hydro/MAGMA2/hydro_iact.h | 122 +++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 55 deletions(-) diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index ec353bdcb9..412b79243b 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -563,37 +563,43 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( dv_dot_eta_j + a2_Hubble * r2 * hj_inv_kernel; /* Is the flow converging? If so, multiply mu by fac_mu. If not, zero. */ - const float conv_i = (dv_dot_eta_i_phys < 0.f) ? fac_mu : 0.f; - const float conv_j = (dv_dot_eta_j_phys < 0.f) ? fac_mu : 0.f; + float conv = 0.f; + if (fabsf(dv_dot_eta_i_phys) > FLT_EPSILON && + fabsf(dv_dot_eta_j_phys) > FLT_EPSILON) { + /* Is the flow converging? */ + const char conv_i = (dv_dot_eta_i_phys < 0.f) ? 1 : 0; + const char conv_j = (dv_dot_eta_j_phys < 0.f) ? 1 : 0; - /* Both must be approaching */ - const float conv = (conv_i != 0.f && conv_j != 0.f) ? fac_mu : 0.f; + /* They both must be approaching. */ + conv = (conv_i && conv_j) ? fac_mu : 0.f; #ifdef MAGMA2_DEBUG_CHECKS - if (conv_i != conv_j) { - warning("Convergence factor mismatch for particles pi=%lld and pj=%lld.\n" - "conv_i = %g, conv_j = %g\n" - "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" - "vi_reconstructed = (%g, %g, %g), vj_reconstructed = (%g, %g, %g)\n" - "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" - "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" - "eta_i2 = %g, eta_j2 = %g\n" - "hi_inv_kernel = %g, hj_inv_kernel = %g\n" - "a2_Hubble = %g, r2 = %g\n", - pi->id, pj->id, - conv_i, conv_j, - dv_dot_eta_i_phys, dv_dot_eta_j_phys, - vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], - vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], - dv_ij[0], dv_ij[1], dv_ij[2], - dv_ji[0], dv_ji[1], dv_ji[2], - eta_i[0], eta_i[1], eta_i[2], - eta_j[0], eta_j[1], eta_j[2], - eta_i2, eta_j2, - hi_inv_kernel, hj_inv_kernel, - a2_Hubble, r2); - } + if (conv_i != conv_j) { + warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" + "conv_i = %d, conv_j = %d\n" + "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" + "vi_reconstructed = (%g, %g, %g), " + "vj_reconstructed = (%g, %g, %g)\n" + "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" + "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" + "eta_i2 = %g, eta_j2 = %g\n" + "hi_inv_kernel = %g, hj_inv_kernel = %g\n" + "a2_Hubble = %g, r2 = %g\n", + pi->id, pj->id, + conv_i, conv_j, + dv_dot_eta_i_phys, dv_dot_eta_j_phys, + vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], + vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], + dv_ij[0], dv_ij[1], dv_ij[2], + dv_ji[0], dv_ji[1], dv_ji[2], + eta_i[0], eta_i[1], eta_i[2], + eta_j[0], eta_j[1], eta_j[2], + eta_i2, eta_j2, + hi_inv_kernel, hj_inv_kernel, + a2_Hubble, r2); + } #endif + } /* mu_i and mu_j include the Hubble flow */ const float mu_i = @@ -953,38 +959,44 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float dv_dot_eta_j_phys = dv_dot_eta_j + a2_Hubble * r2 * hj_inv_kernel; - /* Is the flow converging? */ - const float conv_i = (dv_dot_eta_i_phys < 0.f) ? fac_mu : 0.f; - const float conv_j = (dv_dot_eta_j_phys < 0.f) ? fac_mu : 0.f; + /* Is the flow converging? If yes, multiply by fac_mu. If no, zero. */ + float conv = 0.f; + if (fabsf(dv_dot_eta_i_phys) > FLT_EPSILON && + fabsf(dv_dot_eta_j_phys) > FLT_EPSILON) { + /* Is the flow converging? */ + const char conv_i = (dv_dot_eta_i_phys < 0.f) ? 1 : 0; + const char conv_j = (dv_dot_eta_j_phys < 0.f) ? 1 : 0; - /* They both must be approaching. */ - const float conv = (conv_i != 0.f && conv_j != 0.f) ? fac_mu : 0.f; + /* They both must be approaching. */ + conv = (conv_i && conv_j) ? fac_mu : 0.f; #ifdef MAGMA2_DEBUG_CHECKS - if (conv_i != conv_j) { - warning("Convergence factor mismatch for particles pi=%lld and pj=%lld.\n" - "conv_i = %g, conv_j = %g\n" - "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" - "vi_reconstructed = (%g, %g, %g), vj_reconstructed = (%g, %g, %g)\n" - "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" - "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" - "eta_i2 = %g, eta_j2 = %g\n" - "hi_inv_kernel = %g, hj_inv_kernel = %g\n" - "a2_Hubble = %g, r2 = %g\n", - pi->id, pj->id, - conv_i, conv_j, - dv_dot_eta_i_phys, dv_dot_eta_j_phys, - vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], - vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], - dv_ij[0], dv_ij[1], dv_ij[2], - dv_ji[0], dv_ji[1], dv_ji[2], - eta_i[0], eta_i[1], eta_i[2], - eta_j[0], eta_j[1], eta_j[2], - eta_i2, eta_j2, - hi_inv_kernel, hj_inv_kernel, - a2_Hubble, r2); - } + if (conv_i != conv_j) { + warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" + "conv_i = %d, conv_j = %d\n" + "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" + "vi_reconstructed = (%g, %g, %g), " + "vj_reconstructed = (%g, %g, %g)\n" + "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" + "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" + "eta_i2 = %g, eta_j2 = %g\n" + "hi_inv_kernel = %g, hj_inv_kernel = %g\n" + "a2_Hubble = %g, r2 = %g\n", + pi->id, pj->id, + conv_i, conv_j, + dv_dot_eta_i_phys, dv_dot_eta_j_phys, + vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], + vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], + dv_ij[0], dv_ij[1], dv_ij[2], + dv_ji[0], dv_ji[1], dv_ji[2], + eta_i[0], eta_i[1], eta_i[2], + eta_j[0], eta_j[1], eta_j[2], + eta_i2, eta_j2, + hi_inv_kernel, hj_inv_kernel, + a2_Hubble, r2); + } #endif + } /* mu_i and mu_j include the Hubble flow */ const float mu_i = From 66f25a8311e4ef90353b4bd43ca15eaffa2f69a5 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Mon, 28 Jul 2025 17:44:33 -0400 Subject: [PATCH 14/39] Add error message with MAGMA2_DEBUG_CHECKS for time-step crash. --- src/timestep.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/timestep.h b/src/timestep.h index a600241808..75e1ae759d 100644 --- a/src/timestep.h +++ b/src/timestep.h @@ -204,6 +204,27 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep( /* Limit timestep within the allowed range */ new_dt = min(new_dt, e->dt_max); +#ifdef MAGMA2_DEBUG_CHECKS + if (new_dt < e->dt_min) { + error("part (id=%lld) wants a time-step (%e) below dt_min (%e)" + "dt_h_change = %e, " + "new_dt_hydro = %e, new_dt_mhd = %e, " + "new_dt_cooling = %e, new_dt_grav = %e, " + "new_dt_forcing = %e, new_dt_chemistry = %e" + ", dt_max_RMS_displacement = %e", + p->id, + new_dt, e->dt_min, + dt_h_change * e->cosmology->time_step_factor, + new_dt_hydro * e->cosmology->time_step_factor, + new_dt_mhd * e->cosmology->time_step_factor, + new_dt_cooling * e->cosmology->time_step_factor, + new_dt_grav * e->cosmology->time_step_factor, + new_dt_forcing * e->cosmology->time_step_factor, + new_dt_chemistry * e->cosmology->time_step_factor, + e->dt_max_RMS_displacement * e->cosmology->time_step_factor); + } +#endif + if (new_dt < e->dt_min) error("part (id=%lld) wants a time-step (%e) below dt_min (%e)", p->id, new_dt, e->dt_min); From bcc5ebbfdef655f1fa53de6395e5bd61f9768005 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Mon, 28 Jul 2025 17:55:35 -0400 Subject: [PATCH 15/39] Add MAGMA2_DEBUG_CHECKS for force.h_dt. Seems there was a unit mistake in the Cooling/CoolingSedovBlastwave_3D test case. I fixed the units in the file so that they are in reasonable units. --- examples/Cooling/CoolingSedovBlast_3D/makeIC.py | 4 ++-- examples/Cooling/CoolingSedovBlast_3D/sedov.yml | 2 +- src/hydro/MAGMA2/hydro.h | 11 +++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/examples/Cooling/CoolingSedovBlast_3D/makeIC.py b/examples/Cooling/CoolingSedovBlast_3D/makeIC.py index 3449d73253..8b9d86b7ce 100644 --- a/examples/Cooling/CoolingSedovBlast_3D/makeIC.py +++ b/examples/Cooling/CoolingSedovBlast_3D/makeIC.py @@ -38,8 +38,8 @@ print(rho0, "cm s^-3") uL = 1.0e3 * pc -uM = 1.99e30 -uv = 1.0e10 +uM = 1.99e43 +uv = 1.0e5 ut = uL / uv uU = uv ** 2 print("ut:", ut / 3.154e7, "yr") diff --git a/examples/Cooling/CoolingSedovBlast_3D/sedov.yml b/examples/Cooling/CoolingSedovBlast_3D/sedov.yml index 352686068f..30ecc151f6 100644 --- a/examples/Cooling/CoolingSedovBlast_3D/sedov.yml +++ b/examples/Cooling/CoolingSedovBlast_3D/sedov.yml @@ -1,6 +1,6 @@ # Define the system of units to use internally. InternalUnitSystem: - UnitMass_in_cgs: 1.99e33 # g + UnitMass_in_cgs: 1.99e43 # g UnitLength_in_cgs: 3.086e21 # cm UnitVelocity_in_cgs: 1.e5 # Centimeters per second UnitCurrent_in_cgs: 1 # Amperes diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index f458440c98..838f0df25e 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -1492,6 +1492,17 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( const float rho_inv = hydro_get_comoving_density(p); p->force.h_dt *= p->h * rho_inv * hydro_dimension_inv; + +#ifdef MAGMA2_DEBUG_CHECKS + if (p->force.h_dt > 1.e10f) { + warning( + "Particle %lld has a very large h_dt value (%g). This may be due to " + "a very low density or a very high smoothing length." + "rho_inv = %g, h = %g, hydro_dimension_inv = %g", + p->id, p->force.h_dt, + rho_inv, p->h, hydro_dimension_inv); + } +#endif } /** From 5970ebf518f6d715172a31231513f52311edf8b4 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Tue, 29 Jul 2025 14:20:47 -0400 Subject: [PATCH 16/39] The timestep is now the minimum over all neighbours of h_ij (mean smoothing length) and v_sig_max_ij (maximum signal velocity). Defined new constants for eta_crit and eta_fold in the slope limiting procedure. It seems that slope limiting has a huge effect on the Sedov blastwave noise. I have symmetrized as many operations as possible to avoid floating point rounding errors that seem to be causing a lot of problems. Possible to use the second order velocities directly in the v_ij dot G_ij terms in the hydro equations, using hydro_props_use_second_order_velocities_in_divergence. Must compile with the desired number of neighbours for the slope limiting procedure using const_desired_number_of_neighbours or an error will occur. Must be within 5% of the computed target neighbours for a given SPH: eta in the parameter file. Should be computing the conductivity correctly with the Hubble flow added in. --- .gitignore | 1 + src/hydro/MAGMA2/hydro.h | 35 ++- src/hydro/MAGMA2/hydro_iact.h | 382 +++++++++++++++++----------- src/hydro/MAGMA2/hydro_parameters.h | 27 +- src/hydro/MAGMA2/hydro_part.h | 3 + src/hydro_properties.c | 17 ++ 6 files changed, 302 insertions(+), 163 deletions(-) diff --git a/.gitignore b/.gitignore index 5213975f72..94e155a565 100644 --- a/.gitignore +++ b/.gitignore @@ -72,6 +72,7 @@ examples/*/*/restart examples/Cooling/CoolingRates/cooling_rates examples/Cooling/CoolingRates/cooling_element_*.dat examples/Cooling/CoolingRates/cooling_output.dat +examples/Cooling/CoolingSedovBlast_3D/run/* examples/SubgridTests/StellarEvolution/StellarEvolutionSolution* examples/SubgridTests/CosmologicalStellarEvolution/StellarEvolutionSolution* examples/SmallCosmoVolume/SmallCosmoVolume_DM/power_spectra diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 838f0df25e..5e5c29db32 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -440,14 +440,9 @@ __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) { - const float CFL_condition = hydro_properties->CFL_condition; /* CFL condition */ - const float h_min = kernel_gamma * p->h_min * cosmo->a; - const float v_sig_max = p->v_sig_max * cosmo->a_factor_sound_speed; - const float dt_cfl = CFL_condition * h_min / v_sig_max; - - return dt_cfl; + return hydro_properties->CFL_condition * p->dt_min; } /** @@ -695,19 +690,18 @@ __attribute__((always_inline)) INLINE static void hydro_vec3_vec3_outer( __attribute__((always_inline)) INLINE static float hydro_van_leer_phi(const float A_ij, const float eta_i, - const float eta_j, - const float num_ngb) { + const float eta_j) { float phi_raw = (4.f * A_ij) / ((1.f + A_ij) * (1.f + A_ij)); phi_raw = fminf(1.f, fmaxf(0.f, phi_raw)); /* η_ab and η_crit damping */ const float eta_ij = fminf(eta_i, eta_j); - const float eta_crit = powf((32.f * M_PI) / (3.f * num_ngb), 1.f / 3.f); float damping = 1.f; - if (eta_ij <= eta_crit) { - const float diff = (eta_ij - eta_crit) / 0.2f; + if (eta_ij <= const_slope_limiter_eta_crit) { + const float diff = + (eta_ij - const_slope_limiter_eta_crit) / const_slope_limiter_eta_fold; damping = expf(-diff * diff); } @@ -786,12 +780,11 @@ float hydro_scalar_van_leer_phi(const float *restrict grad_i, const float *restrict grad_j, const float *restrict dx, const float eta_i, - const float eta_j, - const float num_ngb) { + const float eta_j) { const float A_ij = hydro_scalar_van_leer_A(grad_i, grad_j, dx); - return hydro_van_leer_phi(A_ij, eta_i, eta_j, num_ngb); + return hydro_van_leer_phi(A_ij, eta_i, eta_j); } /** @@ -811,12 +804,11 @@ float hydro_vector_van_leer_phi(const float (*restrict grad_i)[3], const float (*restrict grad_j)[3], const float *restrict dx, const float eta_i, - const float eta_j, - const float num_ngb) { + const float eta_j) { const float A_ij = hydro_vector_van_leer_A(grad_i, grad_j, dx); - return hydro_van_leer_phi(A_ij, eta_i, eta_j, num_ngb); + return hydro_van_leer_phi(A_ij, eta_i, eta_j); } /** @@ -1089,7 +1081,6 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( __attribute__((always_inline)) INLINE static void hydro_reset_gradient( struct part *restrict p) { - p->h_min = p->h; p->gradients.C_well_conditioned = 1; #ifdef MAGMA2_DEBUG_CHECKS p->debug.D_well_conditioned = 1; @@ -1291,6 +1282,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( /* Re-set problematic values */ p->rho = p->mass * kernel_root * h_inv_dim; p->h_min = 0.f; + p->dt_min = 0.f; p->v_sig_max = 0.f; p->density.wcount = kernel_root * h_inv_dim; p->density.rho_dh = 0.f; @@ -1342,7 +1334,11 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( 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 = p->h; p->v_sig_max = p->force.soundspeed; + p->dt_min = p->h_min / p->v_sig_max; } /** @@ -1493,6 +1489,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( const float rho_inv = hydro_get_comoving_density(p); p->force.h_dt *= p->h * rho_inv * hydro_dimension_inv; + /* dt_min is in physical units, and requires the kernel_gamma factor for h */ + p->dt_min *= kernel_gamma * cosmo->a / cosmo->a_factor_sound_speed; + #ifdef MAGMA2_DEBUG_CHECKS if (p->force.h_dt > 1.e10f) { warning( diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 412b79243b..d08b1c87df 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -241,11 +241,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float faci = mj * rhoj_inv * wi; const float facj = mi * rhoi_inv * wj; - - /* Minimum smoothing length in the kernel */ - const float h_min = min(hi, hj); - pi->h_min = min(pi->h_min, h_min); - pj->h_min = min(pj->h_min, h_min); /* Compute all of the first-order gradients, and second-order gradients */ @@ -336,10 +331,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( kernel_deval(ui, &wi, &wi_dx); const float faci = mj * rhoj_inv * wi; - /* Minimum smoothing length in the kernel */ - const float h_min = min(hi, hj); - pi->h_min = min(pi->h_min, h_min); - /* Compute all of the first-order gradients, and second-order gradients */ /* Rosswog 2020 Equation 18 gradients. In the paper he uses (vj - vi) and @@ -407,7 +398,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float H) { /* Cosmological factors entering the EoMs */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); - const float a2_Hubble = a * a * H; + const float a_Hubble = a * H; + const float a2_Hubble = a * a_Hubble; + const float a_inv = 1.f / a; const float r = sqrtf(r2); const float r_inv = r ? 1.0f / r : 0.0f; @@ -439,6 +432,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); + /* Peculiar velocity difference vector */ + const float dv[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[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). */ @@ -474,26 +472,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_j[1] *= -wj * hj_inv_dim; G_j[2] *= -wj * hj_inv_dim; - /* Velocity tensor dot product with separation vector for pi */ - float dvi_dx_dot_r[3] = {0.f, 0.f, 0.f}; - hydro_mat3x3_vec3_dot(pi->gradients.velocity_tensor, dx, dvi_dx_dot_r); - - /* Equation 17 has r_b - r_a, here we have pi - pj so the - * sign is flipped (because of dx) */ - dvi_dx_dot_r[0] *= -0.5f; - dvi_dx_dot_r[1] *= -0.5f; - dvi_dx_dot_r[2] *= -0.5f; - - /* Velocity tensor dot product with separation vector for pi */ - float dvj_dx_dot_r[3] = {0.f, 0.f, 0.f}; - hydro_mat3x3_vec3_dot(pj->gradients.velocity_tensor, dx, dvj_dx_dot_r); - - /* Equation 17 has r_b - r_a, here we have pi - pj so the - * sign is NOT flipped on dvj_dx_dot_r */ - dvj_dx_dot_r[0] *= 0.5f; - dvj_dx_dot_r[1] *= 0.5f; - dvj_dx_dot_r[2] *= 0.5f; - /* Compute second order reconstruction of velocity between pi & pj */ float vi_reconstructed[3] = {0.f, 0.f, 0.f}; float vj_reconstructed[3] = {0.f, 0.f, 0.f}; @@ -501,25 +479,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float dx_ij[3] = {dx[0], dx[1], dx[2]}; const float dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; - /* TODO: make this constant */ - const float num_ngb_ij = - 0.5f * ((float)pi->num_ngb + (float)pj->num_ngb); - - /* Rosswog 2020 h_i and h_j are actually kernel_gamma * h */ - const float hi_inv_kernel = hi_inv * kernel_gamma_inv; - const float hj_inv_kernel = hj_inv * kernel_gamma_inv; - const float eta_i_ij = r * hi_inv_kernel; - const float eta_j_ij = r * hj_inv_kernel; + /* 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 float phi_ij_vec = hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, pj->gradients.velocity_tensor, - dx_ij, eta_i_ij, eta_j_ij, num_ngb_ij); - const float phi_ji_vec = - hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, - pi->gradients.velocity_tensor, - dx_ji, eta_j_ij, eta_i_ij, num_ngb_ij); + dx_ij, xi, xj); /* dx_ji for particle i and dx_ij for particle j */ hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ji, pi->v, @@ -527,7 +494,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pi->gradients.velocity_hessian, vi_reconstructed); - hydro_vector_second_order_reconstruction(phi_ji_vec, dx_ij, pj->v, + hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ij, pj->v, pj->gradients.velocity_tensor, pj->gradients.velocity_hessian, vj_reconstructed); @@ -536,14 +503,16 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], vi_reconstructed[1] - vj_reconstructed[1], vi_reconstructed[2] - vj_reconstructed[2]}; + +#ifdef hydro_props_use_asymmetric_viscosity const float dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; - const float eta_i[3] = {dx_ij[0] * hi_inv_kernel, - dx_ij[1] * hi_inv_kernel, - dx_ij[2] * hi_inv_kernel}; - const float eta_j[3] = {dx_ji[0] * hj_inv_kernel, - dx_ji[1] * hj_inv_kernel, - dx_ji[2] * hj_inv_kernel}; + const float eta_i[3] = {dx_ij[0] * hi_inv, + dx_ij[1] * hi_inv, + dx_ij[2] * hi_inv}; + const float eta_j[3] = {dx_ji[0] * hj_inv, + dx_ji[1] * hj_inv, + dx_ji[2] * hj_inv}; float eta_i2 = 0.f; hydro_vec3_vec3_dot(eta_i, eta_i, &eta_i2); @@ -554,13 +523,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( hydro_vec3_vec3_dot(dv_ij, eta_i, &dv_dot_eta_i); /* Scale Hubble flow by hi_inv so it is overall scaled */ const float dv_dot_eta_i_phys = - dv_dot_eta_i + a2_Hubble * r2 * hi_inv_kernel; + dv_dot_eta_i + a2_Hubble * r2 * hi_inv; float dv_dot_eta_j = 0.f; hydro_vec3_vec3_dot(dv_ji, eta_j, &dv_dot_eta_j); /* Scale Hubble flow by hj_inv so it is overall scaled */ const float dv_dot_eta_j_phys = - dv_dot_eta_j + a2_Hubble * r2 * hj_inv_kernel; + dv_dot_eta_j + a2_Hubble * r2 * hj_inv; /* Is the flow converging? If so, multiply mu by fac_mu. If not, zero. */ float conv = 0.f; @@ -583,7 +552,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" "eta_i2 = %g, eta_j2 = %g\n" - "hi_inv_kernel = %g, hj_inv_kernel = %g\n" + "hi_inv = %g, hj_inv = %g\n" "a2_Hubble = %g, r2 = %g\n", pi->id, pj->id, conv_i, conv_j, @@ -595,7 +564,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( eta_i[0], eta_i[1], eta_i[2], eta_j[0], eta_j[1], eta_j[2], eta_i2, eta_j2, - hi_inv_kernel, hj_inv_kernel, + hi_inv, hj_inv, a2_Hubble, r2); } #endif @@ -619,6 +588,26 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; +#else + float dv_dot_dr_ij = 0.f; + hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_ij); + dv_dot_dr_ij += a2_Hubble * r2; + + const float conv = (dv_dot_dr_ij < 0.f) ? fac_mu : 0.f; + const float mu_ij = + conv * dv_dot_dr_ij * r_inv / (1.f + const_viscosity_epsilon2); + const float c_ij = 0.5f * (pi->force.soundspeed + pj->force.soundspeed); + + const float Q_ij_alpha = -const_viscosity_alpha * c_ij * mu_ij; + const float Q_ij_beta = const_viscosity_beta * mu_ij * mu_ij; + const float Q_ij_over_rho_ij = + 2.f * (Q_ij_alpha + Q_ij_beta) / (rhoi + rhoj); + + /* Add viscosity to the pressure */ + visc_acc_term = Q_ij_over_rho_ij; +#endif + + /* Split heating between the two particles */ visc_du_term = 0.5f * visc_acc_term; @@ -632,11 +621,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float phi_ij_scalar = hydro_scalar_van_leer_phi(pi->gradients.u, pj->gradients.u, - dx_ij, eta_i_ij, eta_j_ij, num_ngb_ij); - const float phi_ji_scalar = - hydro_scalar_van_leer_phi(pj->gradients.u, - pi->gradients.u, - dx_ji, eta_j_ij, eta_i_ij, num_ngb_ij); + dx_ij, xi, xj); /* dx_ji for particle i and dx_ij for particle j */ hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, pi->u, @@ -644,15 +629,27 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pi->gradients.u_hessian, &ui_reconstructed); - hydro_scalar_second_order_reconstruction(phi_ji_scalar, dx_ij, pj->u, + hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ij, pj->u, pj->gradients.u, pj->gradients.u_hessian, &uj_reconstructed); - float dv_ij_dot_dv_ij = 0.f; - hydro_vec3_vec3_dot(dv_ij, dv_ij, &dv_ij_dot_dv_ij); + /* It is important to get the Hubble flow correction right for the + * conductivity. The same Hubble flow correction for the viscosity is not + * correct, since Rosswog 2020 uses |dv_ij||G_ij| for the heat flux. + * + * The Hubble flow correction is only applied to the velocity, but then + * |G_ij| must be computed in physical coordinates (it is a distance). + */ + const float dv_ij_phys[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], + dv_ij[1] * a_inv + a_Hubble * dx[1], + dv_ij[2] * a_inv + a_Hubble * dx[2]}; + + float dv_ij_phys_norm = 0.f; + hydro_vec3_vec3_dot(dv_ij_phys, dv_ij_phys, &dv_ij_phys_norm); /* Signal velocity is the norm of the velocity difference vector */ - const float v_sig_cond = sqrtf(dv_ij_dot_dv_ij); + dv_ij_phys_norm = sqrtf(dv_ij_phys_norm); + const float v_sig_cond = fac_mu * dv_ij_phys_norm; const float du_ij = ui_reconstructed - uj_reconstructed; const float rho_ij = 0.5f * (rhoi + rhoj); @@ -670,36 +667,73 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_ij[1] = 0.5f * (G_i[1] + G_j[1]); G_ij[2] = 0.5f * (G_i[2] + G_j[2]); - float G_ij_dot_G_ij = 0.f; - hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_dot_G_ij); - const float G_ij_norm = sqrtf(G_ij_dot_G_ij); + const float G_ij_phys[3] = {G_ij[0] * a, + G_ij[1] * a, + G_ij[2] * a}; + + float G_ij_phys_norm = 0.f; + hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_phys_norm); + G_ij_phys_norm = sqrtf(G_ij_phys_norm); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ - /* TODO: Use second-order reconstructions here? */ - const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + - (pi->v[1] - pj->v[1]) * G_ij[1] + - (pi->v[2] - pj->v[2]) * G_ij[2]; +#ifdef hydro_props_use_second_order_velocities_in_divergence + float dv_dot_G_ij = 0.f; + hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); + + float dv_dot_G_ij_phys = 0.f; + hydro_vec3_vec3_dot(dv_ij_phys, G_ij_phys, &dv_dot_G_ij_phys); +#else + float dv_dot_G_ij = 0.f; + hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); + + const float dv_phys[3] = {dv[0] * a_inv + a_Hubble * dx[0], + dv[1] * a_inv + a_Hubble * dx[1], + dv[2] * a_inv + a_Hubble * dx[2]}; + float dv_dot_G_ij_phys = 0.f; + hydro_vec3_vec3_dot(dv_phys, G_ij_phys, &dv_dot_G_ij_phys); +#endif sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; - visc_du_term *= dv_dot_G_ij + a2_Hubble * r2; - cond_du_term *= G_ij_norm; + /* TODO: Should these be physical? */ + visc_du_term *= dv_dot_G_ij_phys; + cond_du_term *= G_ij_phys_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; pj->force.h_dt -= mi * dv_dot_G_ij; + + /* Timestepping */ + + /* New signal velocity */ +#ifdef hydro_props_use_asymmetric_viscosity const float v_sig_visc_i = signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); const float v_sig_visc_j = signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); const float v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); - const float v_sig = max(v_sig_visc, v_sig_cond); +#else + const float v_sig_visc = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); +#endif + const float v_sig_max = max(v_sig_visc, v_sig_cond); /* Update if we need to */ - pi->v_sig_max = max(pi->v_sig_max, v_sig); - pj->v_sig_max = max(pj->v_sig_max, v_sig); + pi->v_sig_max = max(pi->v_sig_max, v_sig_max); + pj->v_sig_max = max(pj->v_sig_max, v_sig_max); + + /* Average softening in kernel */ + const float h_ij = 0.5f * (hi + hj); + pi->h_min = min(pi->h_min, h_ij); + pj->h_min = min(pj->h_min, h_ij); + + /* New timestep estimate */ + const float dt_min_i = pi->h_min / pi->v_sig_max; + const float dt_min_j = pj->h_min / pj->v_sig_max; + pi->dt_min = min(pi->dt_min, dt_min_i); + pj->dt_min = min(pj->dt_min, dt_min_j); } else { @@ -710,9 +744,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_ij[2] = dx[2]; /* Compute dv dot dr. */ - const float dv[3] = {pi->v[0] - pj->v[0], - pi->v[1] - pj->v[1], - pi->v[2] - pj->v[2]}; float dvdr = 0.f; hydro_vec3_vec3_dot(dv, G_ij, &dvdr); @@ -742,6 +773,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pi->v_sig_max = max(pi->v_sig_max, new_v_sig); pj->v_sig_max = max(pj->v_sig_max, new_v_sig); + /* Minimum softening in kernel */ + const float h_ij = 0.5f * (hi + hj); + pi->h_min = min(pi->h_min, h_ij); + pj->h_min = min(pj->h_min, h_ij); + + /* New timestep estimate */ + const float dt_min_i = pi->h_min / pi->v_sig_max; + const float dt_min_j = pj->h_min / pj->v_sig_max; + pi->dt_min = min(pi->dt_min, dt_min_i); + pj->dt_min = min(pj->dt_min, dt_min_j); + visc_acc_term = visc; visc_du_term = 0.5f * visc_acc_term; @@ -806,7 +848,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float H) { /* Cosmological factors entering the EoMs */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); - const float a2_Hubble = a * a * H; + const float a_Hubble = a * H; + const float a2_Hubble = a * a_Hubble; + const float a_inv = 1.f / a; const float r = sqrtf(r2); const float r_inv = r ? 1.0f / r : 0.0f; @@ -837,6 +881,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); + /* Peculiar velocity difference vector */ + const float dv[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[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). */ @@ -870,26 +919,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( G_j[1] *= -wj * hj_inv_dim; G_j[2] *= -wj * hj_inv_dim; - /* Velocity tensor dot product with separation vector for pi */ - float dvi_dx_dot_r[3] = {0.f, 0.f, 0.f}; - hydro_mat3x3_vec3_dot(pi->gradients.velocity_tensor, dx, dvi_dx_dot_r); - - /* Equation 17 has r_b - r_a, here we have pi - pj so the - * sign is flipped (because of dx) */ - dvi_dx_dot_r[0] *= -0.5f; - dvi_dx_dot_r[1] *= -0.5f; - dvi_dx_dot_r[2] *= -0.5f; - - /* Velocity tensor dot product with separation vector for pi */ - float dvj_dx_dot_r[3] = {0.f, 0.f, 0.f}; - hydro_mat3x3_vec3_dot(pj->gradients.velocity_tensor, dx, dvj_dx_dot_r); - - /* Equation 17 has r_b - r_a, here we have pi - pj so the - * sign is NOT flipped on dvj_dx_dot_r */ - dvj_dx_dot_r[0] *= 0.5f; - dvj_dx_dot_r[1] *= 0.5f; - dvj_dx_dot_r[2] *= 0.5f; - /* Compute second order reconstruction of velocity between pi & pj */ float vi_reconstructed[3] = {0.f, 0.f, 0.f}; float vj_reconstructed[3] = {0.f, 0.f, 0.f}; @@ -897,25 +926,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float dx_ij[3] = {dx[0], dx[1], dx[2]}; const float dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; - /* TODO: make this constant */ - const float num_ngb_ij = - 0.5f * ((float)pi->num_ngb + (float)pj->num_ngb); - - /* Rosswog 2020 h_i and h_j are actually kernel_gamma * h */ - const float hi_inv_kernel = hi_inv * kernel_gamma_inv; - const float hj_inv_kernel = hj_inv * kernel_gamma_inv; - const float eta_i_ij = r * hi_inv_kernel; - const float eta_j_ij = r * hj_inv_kernel; + /* 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 float phi_ij_vec = hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, pj->gradients.velocity_tensor, - dx_ij, eta_i_ij, eta_j_ij, num_ngb_ij); + dx_ij, xi, xj); const float phi_ji_vec = hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, pi->gradients.velocity_tensor, - dx_ji, eta_j_ij, eta_i_ij, num_ngb_ij); + dx_ji, xj, xi); /* dx_ji for particle i and dx_ij for particle j */ hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ji, pi->v, @@ -932,14 +954,16 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], vi_reconstructed[1] - vj_reconstructed[1], vi_reconstructed[2] - vj_reconstructed[2]}; + +#ifdef hydro_props_use_asymmetric_viscosity const float dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; - const float eta_i[3] = {dx_ij[0] * hi_inv_kernel, - dx_ij[1] * hi_inv_kernel, - dx_ij[2] * hi_inv_kernel}; - const float eta_j[3] = {dx_ji[0] * hj_inv_kernel, - dx_ji[1] * hj_inv_kernel, - dx_ji[2] * hj_inv_kernel}; + const float eta_i[3] = {dx_ij[0] * hi_inv, + dx_ij[1] * hi_inv, + dx_ij[2] * hi_inv}; + const float eta_j[3] = {dx_ji[0] * hj_inv, + dx_ji[1] * hj_inv, + dx_ji[2] * hj_inv}; float eta_i2 = 0.f; hydro_vec3_vec3_dot(eta_i, eta_i, &eta_i2); @@ -951,13 +975,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( hydro_vec3_vec3_dot(dv_ij, eta_i, &dv_dot_eta_i); /* Scale Hubble flow by hi_inv so it is overall scaled */ const float dv_dot_eta_i_phys = - dv_dot_eta_i + a2_Hubble * r2 * hi_inv_kernel; + dv_dot_eta_i + a2_Hubble * r2 * hi_inv; float dv_dot_eta_j = 0.f; hydro_vec3_vec3_dot(dv_ji, eta_j, &dv_dot_eta_j); /* Scale Hubble flow by hj_inv so it is overall scaled */ const float dv_dot_eta_j_phys = - dv_dot_eta_j + a2_Hubble * r2 * hj_inv_kernel; + dv_dot_eta_j + a2_Hubble * r2 * hj_inv; /* Is the flow converging? If yes, multiply by fac_mu. If no, zero. */ float conv = 0.f; @@ -980,7 +1004,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" "eta_i2 = %g, eta_j2 = %g\n" - "hi_inv_kernel = %g, hj_inv_kernel = %g\n" + "hi_inv = %g, hj_inv = %g\n" "a2_Hubble = %g, r2 = %g\n", pi->id, pj->id, conv_i, conv_j, @@ -992,7 +1016,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( eta_i[0], eta_i[1], eta_i[2], eta_j[0], eta_j[1], eta_j[2], eta_i2, eta_j2, - hi_inv_kernel, hj_inv_kernel, + hi_inv, hj_inv, a2_Hubble, r2); } #endif @@ -1016,6 +1040,25 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; +#else + float dv_dot_dr_ij = 0.f; + hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_ij); + dv_dot_dr_ij += a2_Hubble * r2; + + const float conv = (dv_dot_dr_ij < 0.f) ? fac_mu : 0.f; + const float mu_ij = + conv * dv_dot_dr_ij * r_inv / (1.f + const_viscosity_epsilon2); + const float c_ij = 0.5f * (pi->force.soundspeed + pj->force.soundspeed); + + const float Q_ij_alpha = -const_viscosity_alpha * c_ij * mu_ij; + const float Q_ij_beta = const_viscosity_beta * mu_ij * mu_ij; + const float Q_ij_over_rho_ij = + 2.f * (Q_ij_alpha + Q_ij_beta) / (rhoi + rhoj); + + /* Add viscosity to the pressure */ + visc_acc_term = Q_ij_over_rho_ij; +#endif + visc_du_term = 0.5f * visc_acc_term; @@ -1029,11 +1072,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float phi_ij_scalar = hydro_scalar_van_leer_phi(pi->gradients.u, pj->gradients.u, - dx_ij, eta_i_ij, eta_j_ij, num_ngb_ij); - const float phi_ji_scalar = - hydro_scalar_van_leer_phi(pj->gradients.u, - pi->gradients.u, - dx_ji, eta_j_ij, eta_i_ij, num_ngb_ij); + dx_ij, xi, xj); /* dx_ji for particle i and dx_ij for particle j */ hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, pi->u, @@ -1041,14 +1080,30 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->gradients.u_hessian, &ui_reconstructed); - hydro_scalar_second_order_reconstruction(phi_ji_scalar, dx_ij, pj->u, + hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ij, pj->u, pj->gradients.u, pj->gradients.u_hessian, &uj_reconstructed); - float dv_ij_dot_dv_ij = 0.f; - hydro_vec3_vec3_dot(dv_ij, dv_ij, &dv_ij_dot_dv_ij); - const float v_sig_cond = sqrtf(dv_ij_dot_dv_ij); + /* It is important to get the Hubble flow correction right for the + * conductivity. The same Hubble flow correction for the viscosity is not + * correct, since Rosswog 2020 uses |dv_ij||G_ij| for the heat flux. + * + * The Hubble flow correction is only applied to the velocity, but then + * |G_ij| must be computed in physical coordinates (it is a distance). + * + * The scale factors in velocity and distance cancel, so we end up with + * something like a code velocity * code distance + Hubble flow correction. + */ + const float dv_ij_phys[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], + dv_ij[1] * a_inv + a_Hubble * dx[1], + dv_ij[2] * a_inv + a_Hubble * dx[2]}; + + float dv_ij_phys_norm = 0.f; + hydro_vec3_vec3_dot(dv_ij_phys, dv_ij_phys, &dv_ij_phys_norm); + /* Signal velocity is the norm of the velocity difference vector */ + dv_ij_phys_norm = sqrtf(dv_ij_phys_norm); + const float v_sig_cond = fac_mu * dv_ij_phys_norm; const float du_ij = ui_reconstructed - uj_reconstructed; const float rho_ij = 0.5f * (rhoi + rhoj); @@ -1066,33 +1121,66 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( G_ij[1] = 0.5f * (G_i[1] + G_j[1]); G_ij[2] = 0.5f * (G_i[2] + G_j[2]); - float G_ij_dot_G_ij = 0.f; - hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_dot_G_ij); - const float G_ij_norm = sqrtf(G_ij_dot_G_ij); + const float G_ij_phys[3] = {G_ij[0] * a, + G_ij[1] * a, + G_ij[2] * a}; + + float G_ij_phys_norm = 0.f; + hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_phys_norm); + G_ij_phys_norm = sqrtf(G_ij_phys_norm); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ - /* TODO: Use reconstructed velocities here? */ - const double dv_dot_G_ij = (pi->v[0] - pj->v[0]) * G_ij[0] + - (pi->v[1] - pj->v[1]) * G_ij[1] + - (pi->v[2] - pj->v[2]) * G_ij[2]; +#ifdef hydro_props_use_second_order_velocities_in_divergence + float dv_dot_G_ij = 0.f; + hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); + + float dv_dot_G_ij_phys = 0.f; + hydro_vec3_vec3_dot(dv_ij_phys, G_ij_phys, &dv_dot_G_ij_phys); +#else + float dv_dot_G_ij = 0.f; + hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); + + const float dv_phys[3] = {dv[0] * a_inv + a_Hubble * dx[0], + dv[1] * a_inv + a_Hubble * dx[1], + dv[2] * a_inv + a_Hubble * dx[2]}; + float dv_dot_G_ij_phys = 0.f; + hydro_vec3_vec3_dot(dv_phys, G_ij_phys, &dv_dot_G_ij_phys); +#endif sph_du_term_i *= dv_dot_G_ij; - visc_du_term *= dv_dot_G_ij + a2_Hubble * r2; - cond_du_term *= G_ij_norm; /* Eq. 24 Rosswog 2020 */ + /* TODO: Should these be physical? */ + visc_du_term *= dv_dot_G_ij_phys; + cond_du_term *= G_ij_phys_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; + + /* Timestepping */ + + /* New signal velocity */ +#ifdef hydro_props_use_asymmetric_viscosity const float v_sig_visc_i = signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); const float v_sig_visc_j = signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); const float v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); - const float v_sig = max(v_sig_visc, v_sig_cond); +#else + const float v_sig_visc = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); +#endif + const float v_sig_max = max(v_sig_visc, v_sig_cond); /* Update if we need to */ - pi->v_sig_max = max(pi->v_sig_max, v_sig); + pi->v_sig_max = max(pi->v_sig_max, v_sig_max); + + /* Compute new timestep */ + const float h_ij = 0.5f * (hi + hj); + pi->h_min = min(pi->h_min, h_ij); + + const float dt_min_i = pi->h_min / pi->v_sig_max; + pi->dt_min = min(pi->dt_min, dt_min_i); } else { @@ -1102,9 +1190,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( G_ij[2] = dx[2]; /* Compute dv dot dr. */ - const float dv[3] = {pi->v[0] - pj->v[0], - pi->v[1] - pj->v[1], - pi->v[2] - pj->v[2]}; float dvdr = 0.f; hydro_vec3_vec3_dot(dv, G_ij, &dvdr); @@ -1133,6 +1218,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Update if we need to */ pi->v_sig_max = max(pi->v_sig_max, new_v_sig); + /* Minimum softening in kernel */ + const float h_ij = 0.5f * (hi + hj); + pi->h_min = min(pi->h_min, h_ij); + + /* New time-step estimate */ + const float dt_min_i = pi->h_min / pi->v_sig_max; + pi->dt_min = min(pi->dt_min, dt_min_i); + + visc_acc_term = visc; visc_du_term = 0.5f * visc_acc_term; diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 4f0f3fe235..332bb410be 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -36,7 +36,7 @@ /** * @file MAGMA2/hydro_parameters.h - * @brief Density-Energy conservative implementation of SPH, + * @brief Density-Energy non-conservative implementation of SPH, * with added MAGMA2 physics (Rosswog 2020) (default compile-time * parameters). * @@ -64,6 +64,24 @@ /*! Artificial conductivity alpha */ #define const_conductivity_alpha 0.05f +/*! Desired number of neighbours -- CRITICAL that this matches hydro props */ +#define const_desired_number_of_neighbours 57.f + +/*! eta_crit from Rosswog 2020 equation 23. Should be of order the mean + * interparticle spacing. Example values for N = 64 assuming uniformly + * distributed particles: kernel_gamma * 0.402997988504f. */ +#define const_slope_limiter_eta_crit (kernel_gamma*(4.f * M_PI / (3.f * \ + powf(const_desired_number_of_neighbours, 1.f / 3.f)))) + +/*! eta_fold from Frontiere+'17 Equation 51 */ +#define const_slope_limiter_eta_fold 0.2f + +/*! Activate to use the second-order velocities in v_ij * G_ij */ +//#define hydro_props_use_second_order_velocities_in_divergence + +/*! Activate to use double precision for all matrix/vector operations */ +//#define hydro_props_use_double_precision + /* Structures for below */ /*! Artificial viscosity parameters */ @@ -79,6 +97,13 @@ 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 */ /** diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 0074d005c2..4202d5bdbb 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -137,6 +137,9 @@ struct part { /*! Maximum signal velocity in the kernel */ float v_sig_max; + /*! Minimum time-step amongst neighbours */ + float dt_min; + #ifdef MAGMA2_DEBUG_CHECKS struct { /*! C matrix at the last time it was ill-conditioned */ diff --git a/src/hydro_properties.c b/src/hydro_properties.c index f8b547bc56..b314e8b5fe 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -76,6 +76,23 @@ void hydro_props_init(struct hydro_props *p, (pow_dimension(delta_eta) - pow_dimension(p->eta_neighbours)) * kernel_norm; +#ifdef MAGMA2_SPH +#ifndef const_desired_number_of_neighbours + error("When using MAGMA2 SPH, the constant " + "const_desired_number_of_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(const_desired_number_of_neighbours - p->target_neighbours) > + 0.05f * p->target_neighbours) { + error("When using MAGMA2 SPH, the compiled constant " + "const_desired_number_of_neighbours (%g) must be within 5 percent of " + "the desired number of neighbours (%g) in the parameter file.", + const_desired_number_of_neighbours, p->target_neighbours); + } +#endif +#endif + #ifdef SHADOWFAX_SPH /* change the meaning of target_neighbours and delta_neighbours */ p->target_neighbours = 1.0f; From eac6a2c9b968729c4adf0bafe1baae827ae62924 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Tue, 29 Jul 2025 14:34:42 -0400 Subject: [PATCH 17/39] Renamed some constants for clarity and to allow const_slope_limiter_eta_crit to be a scaled version of the mean interparticle spacing which is fixed for a kernel/neighbour combination. --- src/hydro/MAGMA2/hydro_parameters.h | 13 +++++++------ src/hydro_properties.c | 10 +++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 332bb410be..a10615faec 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -65,13 +65,14 @@ #define const_conductivity_alpha 0.05f /*! Desired number of neighbours -- CRITICAL that this matches hydro props */ -#define const_desired_number_of_neighbours 57.f +#define const_kernel_target_neighbours 57.f -/*! eta_crit from Rosswog 2020 equation 23. Should be of order the mean - * interparticle spacing. Example values for N = 64 assuming uniformly - * distributed particles: kernel_gamma * 0.402997988504f. */ -#define const_slope_limiter_eta_crit (kernel_gamma*(4.f * M_PI / (3.f * \ - powf(const_desired_number_of_neighbours, 1.f / 3.f)))) +/*! Mean interparticle spacing for this kernel and neighbour number */ +#define const_kernel_mean_spacing (kernel_gamma*(4.f * M_PI / (3.f * \ + powf(const_kernel_target_neighbours, 1.f / 3.f)))) + +/*! eta_crit Rosswog 2020 Eq 23. Of order the mean interparticle spacing. */ +#define const_slope_limiter_eta_crit (2.f * const_kernel_mean_spacing) /*! eta_fold from Frontiere+'17 Equation 51 */ #define const_slope_limiter_eta_fold 0.2f diff --git a/src/hydro_properties.c b/src/hydro_properties.c index b314e8b5fe..531b04ac13 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -77,18 +77,18 @@ void hydro_props_init(struct hydro_props *p, kernel_norm; #ifdef MAGMA2_SPH -#ifndef const_desired_number_of_neighbours +#ifndef const_kernel_target_neighbours error("When using MAGMA2 SPH, the constant " - "const_desired_number_of_neighbours must be defined in the header file " + "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(const_desired_number_of_neighbours - p->target_neighbours) > + if (fabsf(const_kernel_target_neighbours - p->target_neighbours) > 0.05f * p->target_neighbours) { error("When using MAGMA2 SPH, the compiled constant " - "const_desired_number_of_neighbours (%g) must be within 5 percent of " + "const_kernel_target_neighbours (%g) must be within 5 percent of " "the desired number of neighbours (%g) in the parameter file.", - const_desired_number_of_neighbours, p->target_neighbours); + const_kernel_target_neighbours, p->target_neighbours); } #endif #endif From 3d90b7c020b42568d0250000ed91e634157679e9 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Tue, 29 Jul 2025 16:08:23 -0400 Subject: [PATCH 18/39] Can now switch between float and double precision by compiling with hydro_props_use_double_precision (sets double precision). --- src/hydro/MAGMA2/hydro.h | 300 +++++----- src/hydro/MAGMA2/hydro_iact.h | 868 ++++++++++++++-------------- src/hydro/MAGMA2/hydro_io.h | 27 +- src/hydro/MAGMA2/hydro_parameters.h | 25 +- src/hydro/MAGMA2/hydro_part.h | 54 +- src/hydro_properties.c | 2 +- 6 files changed, 679 insertions(+), 597 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 5e5c29db32..a696391d7d 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -504,14 +504,16 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->density.rho_dh = 0.f; p->num_ngb = 0; + p->gradients.u_well_conditioned = 1; + p->gradients.D_well_conditioned = 1; /* These must be zeroed before the density loop */ for (int i = 0; i < 3; i++) { - p->gradients.u_aux[i] = 0.f; - p->gradients.u_aux_norm[i] = 0.f; + p->gradients.u_aux[i] = 0; + p->gradients.u_aux_norm[i] = 0; for (int j = 0; j < 3; j++) { - p->gradients.velocity_tensor_aux[i][j] = 0.f; - p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; + p->gradients.velocity_tensor_aux[i][j] = 0; + p->gradients.velocity_tensor_aux_norm[i][j] = 0; } } } @@ -554,9 +556,9 @@ double condition_number(gsl_matrix *A) { * @param result The result of the dot product. */ __attribute__((always_inline)) INLINE static -void hydro_vec3_vec3_dot(const float *restrict vec_a, - const float *restrict vec_b, - float *result) { +void hydro_vec3_vec3_dot(const hydro_real_t *restrict vec_a, + const hydro_real_t *restrict vec_b, + hydro_real_t *result) { *result = vec_a[0] * vec_b[0] + vec_a[1] * vec_b[1] + vec_a[2] * vec_b[2]; } @@ -571,9 +573,9 @@ void hydro_vec3_vec3_dot(const float *restrict vec_a, * @param result The result of the contraction. */ __attribute__((always_inline)) INLINE static -void hydro_mat3x3_mat3x3_dot(const float (*restrict mat_a)[3], - const float (*restrict mat_b)[3], - float *result) { +void hydro_mat3x3_mat3x3_dot(const hydro_real_t (*restrict mat_a)[3], + const hydro_real_t (*restrict mat_b)[3], + hydro_real_t *result) { *result = mat_a[0][0] * mat_b[0][0] + mat_a[0][1] * mat_b[0][1] + @@ -596,9 +598,9 @@ void hydro_mat3x3_mat3x3_dot(const float (*restrict mat_a)[3], * @param result The result of the contraction. */ __attribute__((always_inline)) INLINE static void hydro_mat3x3_vec3_dot( - const float (*restrict mat)[3], - const float *restrict vec, - float *restrict result) { + 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]; @@ -616,9 +618,9 @@ __attribute__((always_inline)) INLINE static void hydro_mat3x3_vec3_dot( */ __attribute__((always_inline)) INLINE static void hydro_tensor3x3x3_matrix3x3_dot( - const float (*restrict tensor)[3][3], - const float (*restrict mat)[3], - float *restrict result) { + 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] + @@ -660,9 +662,9 @@ void hydro_tensor3x3x3_matrix3x3_dot( * @param result The result of the outer product. */ __attribute__((always_inline)) INLINE static void hydro_vec3_vec3_outer( - const float *restrict vec_a, - const float *restrict vec_b, - float (*restrict result)[3]) { + 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]; @@ -688,19 +690,20 @@ __attribute__((always_inline)) INLINE static void hydro_vec3_vec3_outer( * @param num_ngb The number of neighbours in the scheme. */ __attribute__((always_inline)) INLINE static -float hydro_van_leer_phi(const float A_ij, - const float eta_i, - const float eta_j) { +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) { - float phi_raw = (4.f * A_ij) / ((1.f + A_ij) * (1.f + A_ij)); - phi_raw = fminf(1.f, fmaxf(0.f, phi_raw)); + hydro_real_t phi_raw = (4. * A_ij) / ((1. + A_ij) * (1. + A_ij)); + phi_raw = min(1., phi_raw); + phi_raw = max(0., phi_raw); /* η_ab and η_crit damping */ - const float eta_ij = fminf(eta_i, eta_j); + const hydro_real_t eta_ij = min(eta_i, eta_j); - float damping = 1.f; + hydro_real_t damping = 1.f; if (eta_ij <= const_slope_limiter_eta_crit) { - const float diff = + const hydro_real_t diff = (eta_ij - const_slope_limiter_eta_crit) / const_slope_limiter_eta_fold; damping = expf(-diff * diff); } @@ -717,19 +720,19 @@ float hydro_van_leer_phi(const float A_ij, * @param dx The distance vector between the two particles ( ri - rj ). */ __attribute__((always_inline)) INLINE static -float hydro_scalar_van_leer_A(const float *restrict grad_i, - const float *restrict grad_j, - const float *restrict dx) { +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) { - float grad_dot_x_i = 0.f; - float grad_dot_x_j = 0.f; + hydro_real_t grad_dot_x_i = 0.; + hydro_real_t grad_dot_x_j = 0.; hydro_vec3_vec3_dot(grad_i, dx, &grad_dot_x_i); hydro_vec3_vec3_dot(grad_j, dx, &grad_dot_x_j); /* Regularize denominator */ - if (fabsf(grad_dot_x_j) < FLT_EPSILON) return 0.f; + if (grad_dot_x_j == 0.) return 0.; - const float A_ij = grad_dot_x_i / grad_dot_x_j; + const hydro_real_t A_ij = grad_dot_x_i / grad_dot_x_j; return A_ij; } @@ -743,22 +746,22 @@ float hydro_scalar_van_leer_A(const float *restrict grad_i, * @param dx The distance vector between the two particles ( ri - rj ). */ __attribute__((always_inline)) INLINE static -float hydro_vector_van_leer_A(const float (*restrict grad_i)[3], - const float (*restrict grad_j)[3], - const float *restrict dx) { +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) { - float delta_ij[3][3] = {0}; + hydro_real_t delta_ij[3][3] = {0}; hydro_vec3_vec3_outer(dx, dx, delta_ij); - float grad_dot_x_x_i = 0.f; - float grad_dot_x_x_j = 0.f; + hydro_real_t grad_dot_x_x_i = 0.; + hydro_real_t grad_dot_x_x_j = 0.; hydro_mat3x3_mat3x3_dot(grad_i, delta_ij, &grad_dot_x_x_i); hydro_mat3x3_mat3x3_dot(grad_j, delta_ij, &grad_dot_x_x_j); /* Regularize denominator */ - if (fabsf(grad_dot_x_x_j) < FLT_EPSILON) return 0.f; + if (grad_dot_x_x_j == 0.) return 0.; - const float A_ij = grad_dot_x_x_i / grad_dot_x_x_j; + const hydro_real_t A_ij = grad_dot_x_x_i / grad_dot_x_x_j; return A_ij; } @@ -776,13 +779,13 @@ float hydro_vector_van_leer_A(const float (*restrict grad_i)[3], * @param num_ngb The number of neighbours in the scheme. */ __attribute__((always_inline)) INLINE static -float hydro_scalar_van_leer_phi(const float *restrict grad_i, - const float *restrict grad_j, - const float *restrict dx, - const float eta_i, - const float eta_j) { +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 float A_ij = hydro_scalar_van_leer_A(grad_i, grad_j, dx); + 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); } @@ -800,13 +803,13 @@ float hydro_scalar_van_leer_phi(const float *restrict grad_i, * @param num_ngb The number of neighbours in the scheme. */ __attribute__((always_inline)) INLINE static -float hydro_vector_van_leer_phi(const float (*restrict grad_i)[3], - const float (*restrict grad_j)[3], - const float *restrict dx, - const float eta_i, - const float eta_j) { +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 float A_ij = hydro_vector_van_leer_A(grad_i, grad_j, dx); + 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); } @@ -823,29 +826,29 @@ float hydro_vector_van_leer_phi(const float (*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 hydro_scalar_second_order_reconstruction(const float phi, - const float *restrict dx, - const float f, - const float *restrict grad, - const float (*restrict hess)[3], - float *f_reconstructed) { +__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 float midpoint[3] = {0.5f * dx[0], - 0.5f * dx[1], - 0.5f * dx[2]}; - float delta_ij[3][3] = {0}; + 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); - float df_dx_dot_r = 0.f; - float df_dx_dx_dot_r2 = 0.f; + hydro_real_t df_dx_dot_r = 0.; + hydro_real_t df_dx_dx_dot_r2 = 0.; hydro_vec3_vec3_dot(grad, midpoint, &df_dx_dot_r); hydro_mat3x3_mat3x3_dot(hess, delta_ij, &df_dx_dx_dot_r2); /* Apply limited slope reconstruction */ - *f_reconstructed = f + phi * (df_dx_dot_r + 0.5f * df_dx_dx_dot_r2); + *f_reconstructed = f + phi * (df_dx_dot_r + 0.5 * df_dx_dx_dot_r2); } /** @@ -860,31 +863,31 @@ void hydro_scalar_second_order_reconstruction(const float phi, * @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 float phi, - const float *restrict dx, - const float *restrict f, - const float (*restrict grad)[3], - const float (*restrict hess)[3][3], - float *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 float midpoint[3] = {0.5f * dx[0], - 0.5f * dx[1], - 0.5f * dx[2]}; - float delta_ij[3][3] = {0}; + 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); - float df_dx_dot_r[3] = {0}; - float df_dx_dx_dot_r2[3] = {0}; + 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.5f * df_dx_dx_dot_r2[0]); - f_reconstructed[1] = f[1] + phi * (df_dx_dot_r[1] + 0.5f * df_dx_dx_dot_r2[1]); - f_reconstructed[2] = f[2] + phi * (df_dx_dot_r[2] + 0.5f * df_dx_dx_dot_r2[2]); + 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]); } /** @@ -928,24 +931,37 @@ __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 (fabsf(p->gradients.u_aux_norm[0]) > FLT_EPSILON && - fabsf(p->gradients.u_aux_norm[1]) > FLT_EPSILON && - fabsf(p->gradients.u_aux_norm[2]) > FLT_EPSILON) { + 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]; } else { - p->gradients.u_aux[0] = 0.f; - p->gradients.u_aux[1] = 0.f; - p->gradients.u_aux[2] = 0.f; + 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.f; - p->gradients.u_aux_norm[1] = 0.f; - p->gradients.u_aux_norm[2] = 0.f; + 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]; + 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; @@ -981,7 +997,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( gsl_permutation_free(p_perm); /* aux_norm is equation 20 in Rosswog 2020, finalize the gradient in 19 */ - float aux_matrix[3][3] = {0}; + hydro_real_t aux_matrix[3][3] = {0}; for (int j = 0; j < 3; j++) { for (int i = 0; i < 3; i++) { /** @@ -1014,8 +1030,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( } else { + p->gradients.D_well_conditioned = 0; #ifdef MAGMA2_DEBUG_CHECKS - p->debug.D_well_conditioned = 0; p->debug.D_ill_conditioned_count++; #endif @@ -1029,8 +1045,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->gradients.velocity_tensor_aux_norm[i][j]; #endif - p->gradients.velocity_tensor_aux[i][j] = 0.f; - p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; + p->gradients.velocity_tensor_aux[i][j] = 0.; + p->gradients.velocity_tensor_aux_norm[i][j] = 0.; } } } @@ -1082,28 +1098,25 @@ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( struct part *restrict p) { p->gradients.C_well_conditioned = 1; -#ifdef MAGMA2_DEBUG_CHECKS - p->debug.D_well_conditioned = 1; -#endif for (int i = 0; i < 3; i++) { - p->gradients.u[i] = 0.f; - p->gradients.u_hessian[i][0] = 0.f; - p->gradients.u_hessian[i][1] = 0.f; - p->gradients.u_hessian[i][2] = 0.f; + 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.f; - p->gradients.correction_matrix[i][1] = 0.f; - p->gradients.correction_matrix[i][2] = 0.f; + 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.f; - p->gradients.velocity_tensor[i][1] = 0.f; - p->gradients.velocity_tensor[i][2] = 0.f; + 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.f; - p->gradients.velocity_hessian[i][j][1] = 0.f; - p->gradients.velocity_hessian[i][j][2] = 0.f; + p->gradients.velocity_hessian[i][j][0] = 0.; + p->gradients.velocity_hessian[i][j][1] = 0.; + p->gradients.velocity_hessian[i][j][2] = 0.; } } } @@ -1164,7 +1177,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( gsl_matrix_view_array((double *)correction_matrix, 3, 3); gsl_matrix *C = &C_view.matrix; - double cond = condition_number(C); + 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); @@ -1196,18 +1209,18 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( /* 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.f; - p->gradients.correction_matrix[k][1] = 0.f; - p->gradients.correction_matrix[k][2] = 0.f; + 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 */ - float u_gradient[3] = {0}; - float u_hessian[3][3] = {0}; - float velocity_tensor[3][3] = {0}; - float velocity_hessian[3][3][3] = {0}; + 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] * @@ -1290,20 +1303,23 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( p->num_ngb = 0; p->gradients.C_well_conditioned = 0; + p->gradients.D_well_conditioned = 0; + p->gradients.u_well_conditioned = 0; + for (int i = 0; i < 3; i++) { - p->gradients.u[i] = 0.f; - p->gradients.u_aux[i] = 0.f; - p->gradients.u_aux_norm[i] = 0.f; + p->gradients.u[i] = 0.; + p->gradients.u_aux[i] = 0.; + p->gradients.u_aux_norm[i] = 0.; for (int j = 0; j < 3; j++) { - p->gradients.correction_matrix[i][j] = 0.f; - p->gradients.velocity_tensor[i][j] = 0.f; - p->gradients.velocity_tensor_aux[i][j] = 0.f; - p->gradients.velocity_tensor_aux_norm[i][j] = 0.f; - p->gradients.u_hessian[i][j] = 0.f; + 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.f; + p->gradients.velocity_hessian[i][j][k] = 0.; } } } @@ -1615,22 +1631,24 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( 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.C_ill_conditioned_count = 0; - p->debug.D_well_conditioned = 1; 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.f; - p->debug.correction_matrix[i][1] = 0.f; - p->debug.correction_matrix[i][2] = 0.f; + 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.f; - p->debug.velocity_tensor_aux[i][1] = 0.f; - p->debug.velocity_tensor_aux[i][2] = 0.f; + 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.f; - p->debug.velocity_tensor_aux_norm[i][1] = 0.f; - p->debug.velocity_tensor_aux_norm[i][2] = 0.f; + 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 diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index d08b1c87df..c38c78d323 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -49,16 +49,18 @@ __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) { + + /* Kernel weights to be filled */ float wi, wj, wi_dx, wj_dx; - const float r = sqrtf(r2); + const hydro_real_t r = sqrt(r2); /* Get the masses. */ - const float mi = pi->mass; - const float mj = pj->mass; + const hydro_real_t mi = pi->mass; + const hydro_real_t mj = pj->mass; /* Compute density of pi. */ - const float hi_inv = 1.f / hi; + const hydro_real_t hi_inv = 1. / hi; const float ui = r * hi_inv; kernel_deval(ui, &wi, &wi_dx); @@ -72,7 +74,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( adaptive_softening_add_correction_term(pi, ui, hi_inv, mj); /* Compute density of pj. */ - const float hj_inv = 1.f / hj; + const hydro_real_t hj_inv = 1. / hj; const float uj = r * hj_inv; kernel_deval(uj, &wj, &wj_dx); @@ -85,13 +87,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( adaptive_softening_add_correction_term(pj, uj, hj_inv, mi); /* Now we need to compute the derivative terms */ - const float r_inv = r ? 1.0f / r : 0.0f; - const float faci = mj * wi_dx * r_inv; - const float facj = mi * wj_dx * r_inv; + 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 float du = pi->u - pj->u; + const hydro_real_t du = pi->u - pj->u; 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; @@ -108,9 +110,9 @@ __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 float 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[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. */ @@ -146,15 +148,17 @@ __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) { + + /* Kernel weights to be filled */ float wi, wi_dx; /* Get the masses. */ - const float mj = pj->mass; + const hydro_real_t mj = pj->mass; /* Get r and r inverse. */ - const float r = sqrtf(r2); + const hydro_real_t r = sqrt(r2); - const float h_inv = 1.f / hi; + const hydro_real_t h_inv = 1. / hi; const float ui = r * h_inv; kernel_deval(ui, &wi, &wi_dx); @@ -166,13 +170,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( adaptive_softening_add_correction_term(pi, ui, h_inv, mj); - const float r_inv = r ? 1.0f / r : 0.0f; - const float faci = mj * wi_dx * r_inv; + 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 float du = pi->u - pj->u; + const hydro_real_t du = pi->u - pj->u; 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; @@ -181,9 +185,9 @@ __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 float 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[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. */ @@ -220,16 +224,16 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float H) { /* Get particle properties */ - const float mi = hydro_get_mass(pi); - const float mj = hydro_get_mass(pj); + const hydro_real_t mi = hydro_get_mass(pi); + const hydro_real_t mj = hydro_get_mass(pj); - const float rhoi = hydro_get_comoving_density(pi); - const float rhoj = hydro_get_comoving_density(pj); + const hydro_real_t rhoi = hydro_get_comoving_density(pi); + const hydro_real_t rhoj = hydro_get_comoving_density(pj); - const float rhoi_inv = 1.f / rhoi; - const float rhoj_inv = 1.f / rhoj; + const hydro_real_t rhoi_inv = 1. / rhoi; + const hydro_real_t rhoj_inv = 1. / rhoj; - const float r = sqrtf(r2); + const hydro_real_t r = sqrt(r2); float wi, wi_dx, wj, wj_dx; @@ -239,8 +243,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( kernel_deval(ui, &wi, &wi_dx); kernel_deval(uj, &wj, &wj_dx); - const float faci = mj * rhoj_inv * wi; - const float facj = mi * rhoi_inv * wj; + 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 */ @@ -248,7 +252,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( * (rj - ri), however this is symmetric so no sign problems. */ /* Internal energy gradient */ - const float du = pi->u - pj->u; + 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; @@ -259,13 +263,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( pj->gradients.u[2] += du * dx[2] * facj; /* Velocity gradients */ - const float 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[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++) { for (int i = 0; i < 3; i++) { - const float du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[k]; + const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[k]; pi->gradients.u_hessian[k][i] += du_k * dx[i] * faci; pj->gradients.u_hessian[k][i] += du_k * dx[i] * facj; @@ -279,8 +283,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( pi->gradients.velocity_tensor[k][i] += dv[k] * dx[i] * faci; pj->gradients.velocity_tensor[k][i] += dv[k] * dx[i] * facj; - const float dv_grad_ki = pi->gradients.velocity_tensor_aux[k][i] - - pj->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: * Index i: velocity direction @@ -322,14 +326,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float H) { /* Get particle properties */ - const float mj = hydro_get_mass(pj); - const float rhoj = hydro_get_comoving_density(pj); - const float rhoj_inv = 1.f / rhoj; - const float r = sqrtf(r2); + const hydro_real_t mj = hydro_get_mass(pj); + 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); float wi, wi_dx; const float ui = r / hi; kernel_deval(ui, &wi, &wi_dx); - const float faci = mj * rhoj_inv * wi; + const hydro_real_t faci = mj * rhoj_inv * wi; /* Compute all of the first-order gradients, and second-order gradients */ @@ -337,20 +341,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( * (rj - ri), however this is symmetric so no sign problems. */ /* Internal energy gradient */ - const float du = pi->u - pj->u; + 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 float 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[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++) { for (int i = 0; i < 3; i++) { - const float du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[i]; + const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[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 */ @@ -361,8 +365,8 @@ __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 float dv_grad_ki = pi->gradients.velocity_tensor_aux[k][i] - - pj->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: * Index i: velocity direction @@ -396,70 +400,86 @@ __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) { + /* Cosmological factors entering the EoMs */ - const float fac_mu = pow_three_gamma_minus_five_over_two(a); - const float a_Hubble = a * H; - const float a2_Hubble = a * a_Hubble; - const float a_inv = 1.f / a; + const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); + const hydro_real_t a_Hubble = a * H; + const hydro_real_t a2_Hubble = a * a_Hubble; + const hydro_real_t a_inv = 1. / a; - const float r = sqrtf(r2); - const float r_inv = r ? 1.0f / r : 0.0f; + const hydro_real_t r = sqrt(r2); + const hydro_real_t r_inv = r ? 1.0 / r : 0.0; /* Recover some data */ - const float mj = pj->mass; - const float mi = pi->mass; + const hydro_real_t mj = pj->mass; + const hydro_real_t mi = pi->mass; - const float rhoi = pi->rho; - const float rhoj = pj->rho; - const float rhoi_inv = 1.f / rhoi; - const float rhoj_inv = 1.f / rhoj; - const float rhoij_inv = rhoi_inv * rhoj_inv; + 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 float pressurei = pi->force.pressure; - const float pressurej = pj->force.pressure; + const hydro_real_t pressurei = pi->force.pressure; + const hydro_real_t pressurej = pj->force.pressure; /* Get the kernel for hi. */ - const float hi_inv = 1.0f / hi; - const float hi_inv_dim = pow_dimension(hi_inv); + 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 float hj_inv = 1.0f / hj; - const float hj_inv_dim = pow_dimension(hj_inv); + 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); /* Peculiar velocity difference vector */ - const float 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[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[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). */ - float G_ij[3] = {0.f, 0.f, 0.f}; + hydro_real_t G_ij[3] = {0., 0., 0.}; /* These are set whether or not we fall back onto SPH gradients */ - float visc_acc_term = 0.f; - float sph_acc_term = (pressurei + pressurej) * rhoij_inv; - - float sph_du_term_i = pressurei * rhoij_inv; - float sph_du_term_j = pressurej * rhoij_inv; - float visc_du_term = 0.f; - float cond_du_term = 0.f; + 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 char C_well_conditioned = + (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned); + /* First order density-independent velocity gradients */ + const char D_well_conditioned = + (pi->gradients.D_well_conditioned && pj->gradients.D_well_conditioned); + /* First order internal energy gradients */ + const char u_well_conditioned = + (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned); /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ - if (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned) { + if (C_well_conditioned && D_well_conditioned) { + + /* 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]}; /* Corrected gradients */ - float G_i[3] = {0.f, 0.f, 0.f}; - float G_j[3] = {0.f, 0.f, 0.f}; - hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx, G_i); - hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx, G_j); + hydro_real_t G_i[3] = {0., 0., 0.}; + hydro_real_t G_j[3] = {0., 0., 0.}; + hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx_ij, G_i); + /* Important: Use same separation vector */ + hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx_ij, G_j); /* Note: negative because dx is pj->x - pi->x in Rosswog 2020. * It is pj->x - pi->x for BOTH particles, and then sign flip @@ -473,166 +493,165 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_j[2] *= -wj * hj_inv_dim; /* Compute second order reconstruction of velocity between pi & pj */ - float vi_reconstructed[3] = {0.f, 0.f, 0.f}; - float vj_reconstructed[3] = {0.f, 0.f, 0.f}; - - const float dx_ij[3] = {dx[0], dx[1], dx[2]}; - const float dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; + 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 float phi_ij_vec = + const hydro_real_t phi_ij_vec = hydro_vector_van_leer_phi(pi->gradients.velocity_tensor, pj->gradients.velocity_tensor, dx_ij, xi, xj); + /* 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_ij_vec, dx_ji, pi->v, + hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ji, v_i, pi->gradients.velocity_tensor, pi->gradients.velocity_hessian, vi_reconstructed); - hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ij, pj->v, + hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ij, v_j, pj->gradients.velocity_tensor, pj->gradients.velocity_hessian, vj_reconstructed); /* Artificial viscosity */ - const float dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], - vi_reconstructed[1] - vj_reconstructed[1], - vi_reconstructed[2] - vj_reconstructed[2]}; + const hydro_real_t dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], + vi_reconstructed[1] - vj_reconstructed[1], + vi_reconstructed[2] - vj_reconstructed[2]}; #ifdef hydro_props_use_asymmetric_viscosity - const float dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; - - const float eta_i[3] = {dx_ij[0] * hi_inv, - dx_ij[1] * hi_inv, - dx_ij[2] * hi_inv}; - const float eta_j[3] = {dx_ji[0] * hj_inv, - dx_ji[1] * hj_inv, - dx_ji[2] * hj_inv}; - float eta_i2 = 0.f; + const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; + + const hydro_real_t eta_i[3] = {dx_ij[0] * hi_inv, + dx_ij[1] * hi_inv, + dx_ij[2] * hi_inv}; + const hydro_real_t eta_j[3] = {dx_ji[0] * hj_inv, + dx_ji[1] * hj_inv, + dx_ji[2] * hj_inv}; + hydro_real_t eta_i2 = 0.; hydro_vec3_vec3_dot(eta_i, eta_i, &eta_i2); - float eta_j2 = 0.f; + hydro_real_t eta_j2 = 0.; hydro_vec3_vec3_dot(eta_j, eta_j, &eta_j2); - float dv_dot_eta_i = 0.f; + hydro_real_t dv_dot_eta_i = 0.; hydro_vec3_vec3_dot(dv_ij, eta_i, &dv_dot_eta_i); /* Scale Hubble flow by hi_inv so it is overall scaled */ - const float dv_dot_eta_i_phys = + const hydro_real_t dv_dot_eta_i_phys = dv_dot_eta_i + a2_Hubble * r2 * hi_inv; - float dv_dot_eta_j = 0.f; + hydro_real_t dv_dot_eta_j = 0.; hydro_vec3_vec3_dot(dv_ji, eta_j, &dv_dot_eta_j); /* Scale Hubble flow by hj_inv so it is overall scaled */ - const float dv_dot_eta_j_phys = + const hydro_real_t dv_dot_eta_j_phys = dv_dot_eta_j + a2_Hubble * r2 * hj_inv; - /* Is the flow converging? If so, multiply mu by fac_mu. If not, zero. */ - float conv = 0.f; - if (fabsf(dv_dot_eta_i_phys) > FLT_EPSILON && - fabsf(dv_dot_eta_j_phys) > FLT_EPSILON) { - /* Is the flow converging? */ - const char conv_i = (dv_dot_eta_i_phys < 0.f) ? 1 : 0; - const char conv_j = (dv_dot_eta_j_phys < 0.f) ? 1 : 0; + /* Is the flow converging? */ + const char conv_i = (dv_dot_eta_i_phys < 0.) ? 1 : 0; + const char conv_j = (dv_dot_eta_j_phys < 0.) ? 1 : 0; - /* They both must be approaching. */ - conv = (conv_i && conv_j) ? fac_mu : 0.f; + /* Is the flow converging? If so, multiply mu by fac_mu. If not, zero. */ + const hydro_real_t conv = (conv_i && conv_j) ? fac_mu : 0.; #ifdef MAGMA2_DEBUG_CHECKS - if (conv_i != conv_j) { - warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" - "conv_i = %d, conv_j = %d\n" - "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" - "vi_reconstructed = (%g, %g, %g), " - "vj_reconstructed = (%g, %g, %g)\n" - "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" - "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" - "eta_i2 = %g, eta_j2 = %g\n" - "hi_inv = %g, hj_inv = %g\n" - "a2_Hubble = %g, r2 = %g\n", - pi->id, pj->id, - conv_i, conv_j, - dv_dot_eta_i_phys, dv_dot_eta_j_phys, - vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], - vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], - dv_ij[0], dv_ij[1], dv_ij[2], - dv_ji[0], dv_ji[1], dv_ji[2], - eta_i[0], eta_i[1], eta_i[2], - eta_j[0], eta_j[1], eta_j[2], - eta_i2, eta_j2, - hi_inv, hj_inv, - a2_Hubble, r2); - } -#endif + if (conv_i != conv_j) { + warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" + "conv_i = %d, conv_j = %d\n" + "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" + "vi_reconstructed = (%g, %g, %g), " + "vj_reconstructed = (%g, %g, %g)\n" + "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" + "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" + "eta_i2 = %g, eta_j2 = %g\n" + "hi_inv = %g, hj_inv = %g\n" + "a2_Hubble = %g, r2 = %g\n", + pi->id, pj->id, + conv_i, conv_j, + dv_dot_eta_i_phys, dv_dot_eta_j_phys, + vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], + vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], + dv_ij[0], dv_ij[1], dv_ij[2], + dv_ji[0], dv_ji[1], dv_ji[2], + eta_i[0], eta_i[1], eta_i[2], + eta_j[0], eta_j[1], eta_j[2], + eta_i2, eta_j2, + hi_inv, hj_inv, + a2_Hubble, r2); } +#endif /* mu_i and mu_j include the Hubble flow */ - const float mu_i = + const hydro_real_t mu_i = conv * dv_dot_eta_i_phys / (eta_i2 + const_viscosity_epsilon2); - const float mu_j = + const hydro_real_t mu_j = conv * dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2); - const float Q_i_alpha = + const hydro_real_t Q_i_alpha = -const_viscosity_alpha * pi->force.soundspeed * mu_i; - const float Q_i_beta = const_viscosity_beta * mu_i * mu_i; - const float Q_i = rhoi * (Q_i_alpha + Q_i_beta); + const hydro_real_t Q_i_beta = const_viscosity_beta * mu_i * mu_i; + const hydro_real_t Q_i = rhoi * (Q_i_alpha + Q_i_beta); - const float Q_j_alpha = + const hydro_real_t Q_j_alpha = -const_viscosity_alpha * pj->force.soundspeed * mu_j; - const float Q_j_beta = const_viscosity_beta * mu_j * mu_j; - const float Q_j = rhoj * (Q_j_alpha + Q_j_beta); + const hydro_real_t Q_j_beta = const_viscosity_beta * mu_j * mu_j; + const hydro_real_t Q_j = rhoj * (Q_j_alpha + Q_j_beta); /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; #else - float dv_dot_dr_ij = 0.f; + hydro_real_t dv_dot_dr_ij = 0.; hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_ij); dv_dot_dr_ij += a2_Hubble * r2; - const float conv = (dv_dot_dr_ij < 0.f) ? fac_mu : 0.f; - const float mu_ij = - conv * dv_dot_dr_ij * r_inv / (1.f + const_viscosity_epsilon2); - const float c_ij = 0.5f * (pi->force.soundspeed + pj->force.soundspeed); + const hydro_real_t conv = (dv_dot_dr_ij < 0.) ? fac_mu : 0.; + const hydro_real_t mu_ij = conv * dv_dot_dr_ij * r_inv; + const hydro_real_t c_ij = + 0.5 * (pi->force.soundspeed + pj->force.soundspeed); - const float Q_ij_alpha = -const_viscosity_alpha * c_ij * mu_ij; - const float Q_ij_beta = const_viscosity_beta * mu_ij * mu_ij; - const float Q_ij_over_rho_ij = - 2.f * (Q_ij_alpha + Q_ij_beta) / (rhoi + rhoj); + 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); /* Add viscosity to the pressure */ - visc_acc_term = Q_ij_over_rho_ij; + visc_acc_term = Q_ij * rhoij_inv; #endif /* Split heating between the two particles */ - visc_du_term = 0.5f * visc_acc_term; + visc_du_term = 0.5 * visc_acc_term; /* Artificial conductivity */ - float ui_reconstructed = 0.f; - float uj_reconstructed = 0.f; - - /* Compute global Van Leer limiter (scalar, not component-wise) */ - const float phi_ij_scalar = - hydro_scalar_van_leer_phi(pi->gradients.u, - pj->gradients.u, - dx_ij, xi, xj); - - /* dx_ji for particle i and dx_ij for particle j */ - hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, pi->u, - pi->gradients.u, - pi->gradients.u_hessian, - &ui_reconstructed); - - hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ij, pj->u, - pj->gradients.u, - pj->gradients.u_hessian, - &uj_reconstructed); + hydro_real_t ui_reconstructed = 0.; + hydro_real_t uj_reconstructed = 0.; + + if (u_well_conditioned) { + /* 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); + + /* dx_ji for particle i and dx_ij for particle j */ + hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, + (hydro_real_t)pi->u, + pi->gradients.u, + pi->gradients.u_hessian, + &ui_reconstructed); + + hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ij, + (hydro_real_t)pj->u, + pj->gradients.u, + pj->gradients.u_hessian, + &uj_reconstructed); + } /* It is important to get the Hubble flow correction right for the * conductivity. The same Hubble flow correction for the viscosity is not @@ -641,18 +660,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( * The Hubble flow correction is only applied to the velocity, but then * |G_ij| must be computed in physical coordinates (it is a distance). */ - const float dv_ij_phys[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], - dv_ij[1] * a_inv + a_Hubble * dx[1], - dv_ij[2] * a_inv + a_Hubble * dx[2]}; + const hydro_real_t dv_ij_phys[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], + dv_ij[1] * a_inv + a_Hubble * dx[1], + dv_ij[2] * a_inv + a_Hubble * dx[2]}; - float dv_ij_phys_norm = 0.f; + hydro_real_t dv_ij_phys_norm = 0.; hydro_vec3_vec3_dot(dv_ij_phys, dv_ij_phys, &dv_ij_phys_norm); /* Signal velocity is the norm of the velocity difference vector */ - dv_ij_phys_norm = sqrtf(dv_ij_phys_norm); - const float v_sig_cond = fac_mu * dv_ij_phys_norm; + dv_ij_phys_norm = sqrt(dv_ij_phys_norm); + const hydro_real_t v_sig_cond = fac_mu * dv_ij_phys_norm; - const float du_ij = ui_reconstructed - uj_reconstructed; - const float rho_ij = 0.5f * (rhoi + rhoj); + const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; + const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); /* Add conductivity to the specific energy */ cond_du_term = -const_conductivity_alpha * v_sig_cond * du_ij / rho_ij; @@ -663,33 +682,33 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ - G_ij[0] = 0.5f * (G_i[0] + G_j[0]); - G_ij[1] = 0.5f * (G_i[1] + G_j[1]); - G_ij[2] = 0.5f * (G_i[2] + G_j[2]); + 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]); - const float G_ij_phys[3] = {G_ij[0] * a, - G_ij[1] * a, - G_ij[2] * a}; + const hydro_real_t G_ij_phys[3] = {G_ij[0] * a, + G_ij[1] * a, + G_ij[2] * a}; - float G_ij_phys_norm = 0.f; + hydro_real_t G_ij_phys_norm = 0.; hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_phys_norm); G_ij_phys_norm = sqrtf(G_ij_phys_norm); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ #ifdef hydro_props_use_second_order_velocities_in_divergence - float dv_dot_G_ij = 0.f; + hydro_real_t dv_dot_G_ij = 0.; hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); - float dv_dot_G_ij_phys = 0.f; + hydro_real_t dv_dot_G_ij_phys = 0.; hydro_vec3_vec3_dot(dv_ij_phys, G_ij_phys, &dv_dot_G_ij_phys); #else - float dv_dot_G_ij = 0.f; + hydro_real_t dv_dot_G_ij = 0.; hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); - const float dv_phys[3] = {dv[0] * a_inv + a_Hubble * dx[0], - dv[1] * a_inv + a_Hubble * dx[1], - dv[2] * a_inv + a_Hubble * dx[2]}; - float dv_dot_G_ij_phys = 0.f; + const hydro_real_t dv_phys[3] = {dv[0] * a_inv + a_Hubble * dx[0], + dv[1] * a_inv + a_Hubble * dx[1], + dv[2] * a_inv + a_Hubble * dx[2]}; + hydro_real_t dv_dot_G_ij_phys = 0.; hydro_vec3_vec3_dot(dv_phys, G_ij_phys, &dv_dot_G_ij_phys); #endif @@ -709,29 +728,29 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* New signal velocity */ #ifdef hydro_props_use_asymmetric_viscosity - const float v_sig_visc_i = + const hydro_real_t v_sig_visc_i = signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); - const float v_sig_visc_j = + const hydro_real_t v_sig_visc_j = signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); - const float v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); + const hydro_real_t v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); #else - const float v_sig_visc = + const hydro_real_t v_sig_visc = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); #endif - const float v_sig_max = max(v_sig_visc, v_sig_cond); + const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); /* Update if we need to */ pi->v_sig_max = max(pi->v_sig_max, v_sig_max); pj->v_sig_max = max(pj->v_sig_max, v_sig_max); /* Average softening in kernel */ - const float h_ij = 0.5f * (hi + hj); + const hydro_real_t h_ij = 0.5 * (hi + hj); pi->h_min = min(pi->h_min, h_ij); pj->h_min = min(pj->h_min, h_ij); /* New timestep estimate */ - const float dt_min_i = pi->h_min / pi->v_sig_max; - const float dt_min_j = pj->h_min / pj->v_sig_max; + const hydro_real_t dt_min_i = pi->h_min / pi->v_sig_max; + const hydro_real_t dt_min_j = pj->h_min / pj->v_sig_max; pi->dt_min = min(pi->dt_min, dt_min_i); pj->dt_min = min(pj->dt_min, dt_min_j); } @@ -744,29 +763,30 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_ij[2] = dx[2]; /* Compute dv dot dr. */ - float dvdr = 0.f; + hydro_real_t dvdr = 0.; hydro_vec3_vec3_dot(dv, G_ij, &dvdr); /* Includes the hubble flow term; not used for du/dt */ - const float dvdr_Hubble = dvdr + a2_Hubble * r2; + const hydro_real_t dvdr_Hubble = dvdr + a2_Hubble * r2; /* Are the particles moving towards each others ? */ - const float omega_ij = min(dvdr_Hubble, 0.f); - const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + const hydro_real_t omega_ij = min(dvdr_Hubble, 0.); + /* This is 0 or negative */ + const hydro_real_t mu_ij = fac_mu * r_inv * omega_ij; /* Construct the full viscosity term */ - const float rho_ij = rhoi + rhoj; - const float alpha = const_viscosity_alpha; - const float visc = - omega_ij < 0.f - ? (-0.25f * alpha * (pi->force.soundspeed + pj->force.soundspeed) * + const hydro_real_t rho_ij = rhoi + rhoj; + const hydro_real_t alpha = const_viscosity_alpha; + const hydro_real_t visc = + omega_ij < 0. + ? (-0.25 * alpha * (pi->force.soundspeed + pj->force.soundspeed) * mu_ij + const_viscosity_beta * mu_ij * mu_ij) / - (0.5f * rho_ij) - : 0.f; + (0.5 * rho_ij) + : 0.; /* New signal velocity */ - const float new_v_sig = + const hydro_real_t new_v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ @@ -774,26 +794,26 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->v_sig_max = max(pj->v_sig_max, new_v_sig); /* Minimum softening in kernel */ - const float h_ij = 0.5f * (hi + hj); + const hydro_real_t h_ij = 0.5 * (hi + hj); pi->h_min = min(pi->h_min, h_ij); pj->h_min = min(pj->h_min, h_ij); /* New timestep estimate */ - const float dt_min_i = pi->h_min / pi->v_sig_max; - const float dt_min_j = pj->h_min / pj->v_sig_max; + const hydro_real_t dt_min_i = pi->h_min / pi->v_sig_max; + const hydro_real_t dt_min_j = pj->h_min / pj->v_sig_max; pi->dt_min = min(pi->dt_min, dt_min_i); pj->dt_min = min(pj->dt_min, dt_min_j); visc_acc_term = visc; - visc_du_term = 0.5f * visc_acc_term; + visc_du_term = 0.5 * visc_acc_term; - const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ - const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; - const float wi_dr = hi_inv_dim_plus_one * wi_dx; - const float wj_dr = hj_inv_dim_plus_one * wj_dx; + 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; /* Variable smoothing length term */ - const float kernel_gradient = 0.5f * (wi_dr + wj_dr) * r_inv; + const hydro_real_t kernel_gradient = 0.5 * (wi_dr + wj_dr) * r_inv; visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; @@ -808,7 +828,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( } /* Assemble the acceleration */ - const float acc = sph_acc_term + visc_acc_term; + const hydro_real_t acc = sph_acc_term + visc_acc_term; /* Use the force Luke ! */ pi->a_hydro[0] -= mj * acc * G_ij[0]; @@ -822,8 +842,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the time derivative for u. */ /* Assemble the energy equation term */ - const float du_dt_i = sph_du_term_i + visc_du_term + cond_du_term; - const float du_dt_j = sph_du_term_j + visc_du_term - cond_du_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; @@ -846,67 +866,83 @@ __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) { + /* Cosmological factors entering the EoMs */ - const float fac_mu = pow_three_gamma_minus_five_over_two(a); - const float a_Hubble = a * H; - const float a2_Hubble = a * a_Hubble; - const float a_inv = 1.f / a; + const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); + const hydro_real_t a_Hubble = a * H; + const hydro_real_t a2_Hubble = a * a_Hubble; + const hydro_real_t a_inv = 1. / a; - const float r = sqrtf(r2); - const float r_inv = r ? 1.0f / r : 0.0f; + const hydro_real_t r = sqrtf(r2); + const hydro_real_t r_inv = r ? 1.0 / r : 0.0; /* Recover some data */ - const float mj = pj->mass; + const hydro_real_t mj = pj->mass; - const float rhoi = pi->rho; - const float rhoj = pj->rho; - const float rhoi_inv = 1.f / rhoi; - const float rhoj_inv = 1.f / rhoj; - const float rhoij_inv = rhoi_inv * rhoj_inv; + 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 float pressurei = pi->force.pressure; - const float pressurej = pj->force.pressure; + const hydro_real_t pressurei = pi->force.pressure; + const hydro_real_t pressurej = pj->force.pressure; /* Get the kernel for hi. */ - const float hi_inv = 1.0f / hi; - const float hi_inv_dim = pow_dimension(hi_inv); + 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 float hj_inv = 1.0f / hj; - const float hj_inv_dim = pow_dimension(hj_inv); + 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); /* Peculiar velocity difference vector */ - const float 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[3] = {pi->v[0] - pj->v[0], + pi->v[1] - pj->v[1], + pi->v[2] - pj->v[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). */ - float G_ij[3] = {0.f, 0.f, 0.f}; + hydro_real_t G_ij[3] = {0., 0., 0.}; /* These are set whether or not we fall back onto SPH gradients */ - float visc_acc_term = 0.f; - float sph_acc_term = (pressurei + pressurej) * rhoij_inv; - float sph_du_term_i = pressurei * rhoij_inv; - float visc_du_term = 0.f; - float cond_du_term = 0.f; + 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 char C_well_conditioned = + (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned); + /* First order density-independent velocity gradients */ + const char D_well_conditioned = + (pi->gradients.D_well_conditioned && pj->gradients.D_well_conditioned); + /* First order internal energy gradients */ + const char u_well_conditioned = + (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned); /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ - if (pi->gradients.C_well_conditioned && pj->gradients.C_well_conditioned) { + if (C_well_conditioned && D_well_conditioned) { + + /* 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]}; /* Corrected gradients */ - float G_i[3] = {0.f, 0.f, 0.f}; - float G_j[3] = {0.f, 0.f, 0.f}; - hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx, G_i); - hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx, G_j); + hydro_real_t G_i[3] = {0., 0., 0.}; + hydro_real_t G_j[3] = {0., 0., 0.}; + hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx_ij, G_i); + /* Important: Use the same separation vector. */ + hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx_ij, G_j); /* Note: negative because dx is pj->x - pi->x in Rosswog 2020. * It is pj->x - pi->x for BOTH particles, and then sign flip @@ -920,170 +956,165 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( G_j[2] *= -wj * hj_inv_dim; /* Compute second order reconstruction of velocity between pi & pj */ - float vi_reconstructed[3] = {0.f, 0.f, 0.f}; - float vj_reconstructed[3] = {0.f, 0.f, 0.f}; - - const float dx_ij[3] = {dx[0], dx[1], dx[2]}; - const float dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; + 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 float phi_ij_vec = + 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 float phi_ji_vec = - hydro_vector_van_leer_phi(pj->gradients.velocity_tensor, - pi->gradients.velocity_tensor, - dx_ji, xj, xi); + + /* 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_ij_vec, dx_ji, pi->v, + hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ji, v_i, pi->gradients.velocity_tensor, pi->gradients.velocity_hessian, vi_reconstructed); - hydro_vector_second_order_reconstruction(phi_ji_vec, dx_ij, pj->v, + hydro_vector_second_order_reconstruction(phi_ij_vec, dx_ij, v_j, pj->gradients.velocity_tensor, pj->gradients.velocity_hessian, vj_reconstructed); /* Artificial viscosity */ - const float dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], - vi_reconstructed[1] - vj_reconstructed[1], - vi_reconstructed[2] - vj_reconstructed[2]}; + const hydro_real_t dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], + vi_reconstructed[1] - vj_reconstructed[1], + vi_reconstructed[2] - vj_reconstructed[2]}; #ifdef hydro_props_use_asymmetric_viscosity - const float dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; + const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; - const float eta_i[3] = {dx_ij[0] * hi_inv, - dx_ij[1] * hi_inv, - dx_ij[2] * hi_inv}; - const float eta_j[3] = {dx_ji[0] * hj_inv, - dx_ji[1] * hj_inv, - dx_ji[2] * hj_inv}; + const hydro_real_t eta_i[3] = {dx_ij[0] * hi_inv, + dx_ij[1] * hi_inv, + dx_ij[2] * hi_inv}; + const hydro_real_t eta_j[3] = {dx_ji[0] * hj_inv, + dx_ji[1] * hj_inv, + dx_ji[2] * hj_inv}; - float eta_i2 = 0.f; + hydro_real_t eta_i2 = 0.; hydro_vec3_vec3_dot(eta_i, eta_i, &eta_i2); - float eta_j2 = 0.f; + hydro_real_t eta_j2 = 0.; hydro_vec3_vec3_dot(eta_j, eta_j, &eta_j2); - float dv_dot_eta_i = 0.f; + hydro_real_t dv_dot_eta_i = 0.; hydro_vec3_vec3_dot(dv_ij, eta_i, &dv_dot_eta_i); /* Scale Hubble flow by hi_inv so it is overall scaled */ - const float dv_dot_eta_i_phys = + const hydro_real_t dv_dot_eta_i_phys = dv_dot_eta_i + a2_Hubble * r2 * hi_inv; - float dv_dot_eta_j = 0.f; + hydro_real_t dv_dot_eta_j = 0.; hydro_vec3_vec3_dot(dv_ji, eta_j, &dv_dot_eta_j); /* Scale Hubble flow by hj_inv so it is overall scaled */ - const float dv_dot_eta_j_phys = + const hydro_real_t dv_dot_eta_j_phys = dv_dot_eta_j + a2_Hubble * r2 * hj_inv; - /* Is the flow converging? If yes, multiply by fac_mu. If no, zero. */ - float conv = 0.f; - if (fabsf(dv_dot_eta_i_phys) > FLT_EPSILON && - fabsf(dv_dot_eta_j_phys) > FLT_EPSILON) { - /* Is the flow converging? */ - const char conv_i = (dv_dot_eta_i_phys < 0.f) ? 1 : 0; - const char conv_j = (dv_dot_eta_j_phys < 0.f) ? 1 : 0; + /* Is the flow converging? */ + const char conv_i = (dv_dot_eta_i_phys < 0.) ? 1 : 0; + const char conv_j = (dv_dot_eta_j_phys < 0.) ? 1 : 0; - /* They both must be approaching. */ - conv = (conv_i && conv_j) ? fac_mu : 0.f; + /* Is the flow converging? If yes, multiply by fac_mu. If no, zero. */ + const hydro_real_t conv = (conv_i && conv_j) ? fac_mu : 0.; #ifdef MAGMA2_DEBUG_CHECKS - if (conv_i != conv_j) { - warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" - "conv_i = %d, conv_j = %d\n" - "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" - "vi_reconstructed = (%g, %g, %g), " - "vj_reconstructed = (%g, %g, %g)\n" - "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" - "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" - "eta_i2 = %g, eta_j2 = %g\n" - "hi_inv = %g, hj_inv = %g\n" - "a2_Hubble = %g, r2 = %g\n", - pi->id, pj->id, - conv_i, conv_j, - dv_dot_eta_i_phys, dv_dot_eta_j_phys, - vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], - vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], - dv_ij[0], dv_ij[1], dv_ij[2], - dv_ji[0], dv_ji[1], dv_ji[2], - eta_i[0], eta_i[1], eta_i[2], - eta_j[0], eta_j[1], eta_j[2], - eta_i2, eta_j2, - hi_inv, hj_inv, - a2_Hubble, r2); - } -#endif + if (conv_i != conv_j) { + warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" + "conv_i = %d, conv_j = %d\n" + "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" + "vi_reconstructed = (%g, %g, %g), " + "vj_reconstructed = (%g, %g, %g)\n" + "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" + "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" + "eta_i2 = %g, eta_j2 = %g\n" + "hi_inv = %g, hj_inv = %g\n" + "a2_Hubble = %g, r2 = %g\n", + pi->id, pj->id, + conv_i, conv_j, + dv_dot_eta_i_phys, dv_dot_eta_j_phys, + vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], + vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], + dv_ij[0], dv_ij[1], dv_ij[2], + dv_ji[0], dv_ji[1], dv_ji[2], + eta_i[0], eta_i[1], eta_i[2], + eta_j[0], eta_j[1], eta_j[2], + eta_i2, eta_j2, + hi_inv, hj_inv, + a2_Hubble, r2); } +#endif /* mu_i and mu_j include the Hubble flow */ - const float mu_i = + const hydro_real_t mu_i = conv * dv_dot_eta_i_phys / (eta_i2 + const_viscosity_epsilon2); - const float mu_j = + const hydro_real_t mu_j = conv * dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2); - const float Q_i_alpha = + const hydro_real_t Q_i_alpha = -const_viscosity_alpha * pi->force.soundspeed * mu_i; - const float Q_i_beta = const_viscosity_beta * mu_i * mu_i; - const float Q_i = rhoi * (Q_i_alpha + Q_i_beta); + const hydro_real_t Q_i_beta = const_viscosity_beta * mu_i * mu_i; + const hydro_real_t Q_i = rhoi * (Q_i_alpha + Q_i_beta); - const float Q_j_alpha = + const hydro_real_t Q_j_alpha = -const_viscosity_alpha * pj->force.soundspeed * mu_j; - const float Q_j_beta = const_viscosity_beta * mu_j * mu_j; - const float Q_j = rhoj * (Q_j_alpha + Q_j_beta); + const hydro_real_t Q_j_beta = const_viscosity_beta * mu_j * mu_j; + const hydro_real_t Q_j = rhoj * (Q_j_alpha + Q_j_beta); /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; #else - float dv_dot_dr_ij = 0.f; + hydro_real_t dv_dot_dr_ij = 0.; hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_ij); dv_dot_dr_ij += a2_Hubble * r2; - const float conv = (dv_dot_dr_ij < 0.f) ? fac_mu : 0.f; - const float mu_ij = - conv * dv_dot_dr_ij * r_inv / (1.f + const_viscosity_epsilon2); - const float c_ij = 0.5f * (pi->force.soundspeed + pj->force.soundspeed); + const hydro_real_t conv = (dv_dot_dr_ij < 0.) ? fac_mu : 0.; + const hydro_real_t mu_ij = conv * dv_dot_dr_ij * r_inv; + const hydro_real_t c_ij = + 0.5 * (pi->force.soundspeed + pj->force.soundspeed); - const float Q_ij_alpha = -const_viscosity_alpha * c_ij * mu_ij; - const float Q_ij_beta = const_viscosity_beta * mu_ij * mu_ij; - const float Q_ij_over_rho_ij = - 2.f * (Q_ij_alpha + Q_ij_beta) / (rhoi + rhoj); + 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); /* Add viscosity to the pressure */ - visc_acc_term = Q_ij_over_rho_ij; + visc_acc_term = Q_ij * rhoij_inv; #endif - visc_du_term = 0.5f * visc_acc_term; + visc_du_term = 0.5 * visc_acc_term; /* Artificial conductivity */ - float ui_reconstructed = 0.f; - float uj_reconstructed = 0.f; - - /* Compute global Van Leer limiter (scalar, not component-wise) */ - const float phi_ij_scalar = - hydro_scalar_van_leer_phi(pi->gradients.u, - pj->gradients.u, - dx_ij, xi, xj); - - /* dx_ji for particle i and dx_ij for particle j */ - hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, pi->u, - pi->gradients.u, - pi->gradients.u_hessian, - &ui_reconstructed); - - hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ij, pj->u, - pj->gradients.u, - pj->gradients.u_hessian, - &uj_reconstructed); + hydro_real_t ui_reconstructed = 0.; + hydro_real_t uj_reconstructed = 0.; + + if (u_well_conditioned) { + /* 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); + + /* dx_ji for particle i and dx_ij for particle j */ + hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, + (hydro_real_t)pi->u, + pi->gradients.u, + pi->gradients.u_hessian, + &ui_reconstructed); + + hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ij, + (hydro_real_t)pj->u, + pj->gradients.u, + pj->gradients.u_hessian, + &uj_reconstructed); + } /* It is important to get the Hubble flow correction right for the * conductivity. The same Hubble flow correction for the viscosity is not @@ -1095,18 +1126,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( * The scale factors in velocity and distance cancel, so we end up with * something like a code velocity * code distance + Hubble flow correction. */ - const float dv_ij_phys[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], - dv_ij[1] * a_inv + a_Hubble * dx[1], - dv_ij[2] * a_inv + a_Hubble * dx[2]}; + const hydro_real_t dv_ij_phys[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], + dv_ij[1] * a_inv + a_Hubble * dx[1], + dv_ij[2] * a_inv + a_Hubble * dx[2]}; - float dv_ij_phys_norm = 0.f; + hydro_real_t dv_ij_phys_norm = 0.; hydro_vec3_vec3_dot(dv_ij_phys, dv_ij_phys, &dv_ij_phys_norm); /* Signal velocity is the norm of the velocity difference vector */ - dv_ij_phys_norm = sqrtf(dv_ij_phys_norm); - const float v_sig_cond = fac_mu * dv_ij_phys_norm; + dv_ij_phys_norm = sqrt(dv_ij_phys_norm); + const hydro_real_t v_sig_cond = fac_mu * dv_ij_phys_norm; - const float du_ij = ui_reconstructed - uj_reconstructed; - const float rho_ij = 0.5f * (rhoi + rhoj); + const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; + const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); /* Add conductivity to the specific energy */ cond_du_term = -const_conductivity_alpha * v_sig_cond * du_ij / rho_ij; @@ -1117,33 +1148,33 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ - G_ij[0] = 0.5f * (G_i[0] + G_j[0]); - G_ij[1] = 0.5f * (G_i[1] + G_j[1]); - G_ij[2] = 0.5f * (G_i[2] + G_j[2]); + 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]); - const float G_ij_phys[3] = {G_ij[0] * a, - G_ij[1] * a, - G_ij[2] * a}; + const hydro_real_t G_ij_phys[3] = {G_ij[0] * a, + G_ij[1] * a, + G_ij[2] * a}; - float G_ij_phys_norm = 0.f; + hydro_real_t G_ij_phys_norm = 0.; hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_phys_norm); G_ij_phys_norm = sqrtf(G_ij_phys_norm); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ #ifdef hydro_props_use_second_order_velocities_in_divergence - float dv_dot_G_ij = 0.f; + hydro_real_t dv_dot_G_ij = 0.; hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); - float dv_dot_G_ij_phys = 0.f; + hydro_real_t dv_dot_G_ij_phys = 0.; hydro_vec3_vec3_dot(dv_ij_phys, G_ij_phys, &dv_dot_G_ij_phys); #else - float dv_dot_G_ij = 0.f; + hydro_real_t dv_dot_G_ij = 0.; hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); - const float dv_phys[3] = {dv[0] * a_inv + a_Hubble * dx[0], - dv[1] * a_inv + a_Hubble * dx[1], - dv[2] * a_inv + a_Hubble * dx[2]}; - float dv_dot_G_ij_phys = 0.f; + const hydro_real_t dv_phys[3] = {dv[0] * a_inv + a_Hubble * dx[0], + dv[1] * a_inv + a_Hubble * dx[1], + dv[2] * a_inv + a_Hubble * dx[2]}; + hydro_real_t dv_dot_G_ij_phys = 0.; hydro_vec3_vec3_dot(dv_phys, G_ij_phys, &dv_dot_G_ij_phys); #endif @@ -1161,25 +1192,25 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* New signal velocity */ #ifdef hydro_props_use_asymmetric_viscosity - const float v_sig_visc_i = + const hydro_real_t v_sig_visc_i = signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); - const float v_sig_visc_j = + const hydro_real_t v_sig_visc_j = signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); - const float v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); + const hydro_real_t v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); #else - const float v_sig_visc = + const hydro_real_t v_sig_visc = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); #endif - const float v_sig_max = max(v_sig_visc, v_sig_cond); + const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); /* Update if we need to */ pi->v_sig_max = max(pi->v_sig_max, v_sig_max); /* Compute new timestep */ - const float h_ij = 0.5f * (hi + hj); + const hydro_real_t h_ij = 0.5 * (hi + hj); pi->h_min = min(pi->h_min, h_ij); - const float dt_min_i = pi->h_min / pi->v_sig_max; + const hydro_real_t dt_min_i = pi->h_min / pi->v_sig_max; pi->dt_min = min(pi->dt_min, dt_min_i); } else { @@ -1190,53 +1221,54 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( G_ij[2] = dx[2]; /* Compute dv dot dr. */ - float dvdr = 0.f; + hydro_real_t dvdr = 0.; hydro_vec3_vec3_dot(dv, G_ij, &dvdr); /* Includes the hubble flow term; not used for du/dt */ - const float dvdr_Hubble = dvdr + a2_Hubble * r2; + const hydro_real_t dvdr_Hubble = dvdr + a2_Hubble * r2; /* Are the particles moving towards each others ? */ - const float omega_ij = min(dvdr_Hubble, 0.f); - const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + const hydro_real_t omega_ij = min(dvdr_Hubble, 0.); + /* This is 0 or negative */ + const hydro_real_t mu_ij = fac_mu * r_inv * omega_ij; /* Construct the full viscosity term */ - const float rho_ij = rhoi + rhoj; - const float alpha = const_viscosity_alpha; - const float visc = - omega_ij < 0.f - ? (-0.25f * alpha * (pi->force.soundspeed + pj->force.soundspeed) * + const hydro_real_t rho_ij = rhoi + rhoj; + const hydro_real_t alpha = const_viscosity_alpha; + const hydro_real_t visc = + omega_ij < 0. + ? (-0.25 * alpha * (pi->force.soundspeed + pj->force.soundspeed) * mu_ij + const_viscosity_beta * mu_ij * mu_ij) / - (0.5f * rho_ij) - : 0.f; + (0.5 * rho_ij) + : 0.; /* New signal velocity */ - const float new_v_sig = + const hydro_real_t new_v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ pi->v_sig_max = max(pi->v_sig_max, new_v_sig); /* Minimum softening in kernel */ - const float h_ij = 0.5f * (hi + hj); + const hydro_real_t h_ij = 0.5 * (hi + hj); pi->h_min = min(pi->h_min, h_ij); /* New time-step estimate */ - const float dt_min_i = pi->h_min / pi->v_sig_max; + const hydro_real_t dt_min_i = pi->h_min / pi->v_sig_max; pi->dt_min = min(pi->dt_min, dt_min_i); visc_acc_term = visc; - visc_du_term = 0.5f * visc_acc_term; + visc_du_term = 0.5 * visc_acc_term; - const float hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ - const float hj_inv_dim_plus_one = hj_inv * hj_inv_dim; - const float wi_dr = hi_inv_dim_plus_one * wi_dx; - const float wj_dr = hj_inv_dim_plus_one * wj_dx; + 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; /* Variable smoothing length term */ - const float kernel_gradient = 0.5f * (wi_dr + wj_dr) * r_inv; + const hydro_real_t kernel_gradient = 0.5 * (wi_dr + wj_dr) * r_inv; visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; @@ -1248,7 +1280,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( } /* Assemble the acceleration */ - const float acc = sph_acc_term + visc_acc_term; + const hydro_real_t acc = sph_acc_term + visc_acc_term; /* Use the force Luke ! */ pi->a_hydro[0] -= mj * acc * G_ij[0]; @@ -1256,7 +1288,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->a_hydro[2] -= mj * acc * G_ij[2]; /* Assemble the energy equation term */ - const float du_dt_i = sph_du_term_i + visc_du_term + cond_du_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; diff --git a/src/hydro/MAGMA2/hydro_io.h b/src/hydro/MAGMA2/hydro_io.h index 66fe0d5dae..de27cc6611 100644 --- a/src/hydro/MAGMA2/hydro_io.h +++ b/src/hydro/MAGMA2/hydro_io.h @@ -252,23 +252,42 @@ INLINE static void hydro_write_particles(const struct part* parts, num++; list[num] = io_make_output_field( - "NumeratorMatrices", FLOAT, 9, + "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( - "DenominatorMatrices", FLOAT, 9, UNIT_CONV_DENSITY, + "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( - "DenominatorIllConditionedCounts", 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, + 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; diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index a10615faec..c1d307bcd3 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -53,35 +53,38 @@ /*! Cosmology default beta=2.0. * Alpha can be set in the parameter file. * Beta is defined as in e.g. Price (2010) Eqn (103) */ -#define const_viscosity_beta 2.0f +#define const_viscosity_beta 2.0 /*! Cosmology default alpha=1.0 */ -#define const_viscosity_alpha 1.0f +#define const_viscosity_alpha 1.0 /*! Softening squared (epsilon^2) in Eq. 15 Rosswog 2020 */ -#define const_viscosity_epsilon2 0.01f +#define const_viscosity_epsilon2 0.01 /*! Artificial conductivity alpha */ -#define const_conductivity_alpha 0.05f +#define const_conductivity_alpha 0.05 /*! Desired number of neighbours -- CRITICAL that this matches hydro props */ -#define const_kernel_target_neighbours 57.f +#define const_kernel_target_neighbours 57.0 /*! Mean interparticle spacing for this kernel and neighbour number */ -#define const_kernel_mean_spacing (kernel_gamma*(4.f * M_PI / (3.f * \ - powf(const_kernel_target_neighbours, 1.f / 3.f)))) +#define const_kernel_mean_spacing (kernel_gamma*(4. * M_PI / (3. * \ + powf(const_kernel_target_neighbours, 1. / 3.)))) /*! eta_crit Rosswog 2020 Eq 23. Of order the mean interparticle spacing. */ -#define const_slope_limiter_eta_crit (2.f * const_kernel_mean_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.2f +#define const_slope_limiter_eta_fold 0.2 + +/*! Activate to use the alternative viscosity weighting */ +//#define hydro_props_use_asymmetric_viscosity /*! Activate to use the second-order velocities in v_ij * G_ij */ -//#define hydro_props_use_second_order_velocities_in_divergence +#define hydro_props_use_second_order_velocities_in_divergence /*! Activate to use double precision for all matrix/vector operations */ -//#define hydro_props_use_double_precision +#define hydro_props_use_double_precision /* Structures for below */ diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 4202d5bdbb..0753042703 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -33,6 +33,7 @@ #include "cooling_struct.h" #include "csds.h" #include "feedback_struct.h" +#include "hydro_parameters.h" #include "mhd_struct.h" #include "particle_splitting_struct.h" #include "pressure_floor_struct.h" @@ -142,24 +143,30 @@ struct part { #ifdef MAGMA2_DEBUG_CHECKS struct { - /*! C matrix at the last time it was ill-conditioned */ - float correction_matrix[3][3]; + /*! Correction matrix at the last time it was ill-conditioned */ + hydro_real_t correction_matrix[3][3]; - /*! Velocity tensor */ - float velocity_tensor_aux[3][3]; + /*! Velocity tensor at ill-condition time */ + hydro_real_t velocity_tensor_aux[3][3]; /*! Velocity tensor norm ill-conditioned */ - float velocity_tensor_aux_norm[3][3]; + hydro_real_t velocity_tensor_aux_norm[3][3]; - /*! Number of times C was ill-conditioned */ - int C_ill_conditioned_count; + /*! u_aux tensor at ill-condition time */ + hydro_real_t u_aux[3]; - /*! Flag for whether aux_norm is ill-conditioned */ - char D_well_conditioned; + /*! u_aux_norm tensor ill-conditioned */ + hydro_real_t u_aux_norm[3]; - /*! Number of times aux_norm was ill-conditioned */ + /*! 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; + } debug; #endif @@ -167,37 +174,40 @@ struct part { struct { /*! Correction matrix (C^ki in Rosswog 2020) */ - float correction_matrix[3][3]; + hydro_real_t correction_matrix[3][3]; /*! Flag for whether C is ill-conditioned */ char C_well_conditioned; /*! Full velocity gradient tensor */ - float velocity_tensor[3][3]; + hydro_real_t velocity_tensor[3][3]; /*! Auxiliary full velocity gradient tensor */ - float velocity_tensor_aux[3][3]; + 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 */ - float velocity_tensor_aux_norm[3][3]; + hydro_real_t velocity_tensor_aux_norm[3][3]; /*! Hessian tensor */ - float velocity_hessian[3][3][3]; - - /*! Smoothed pressure gradient */ - float pressure[3]; + hydro_real_t velocity_hessian[3][3][3]; /*! Internal energy gradient */ - float u[3]; + hydro_real_t u[3]; /*! Auxiliary internal energy gradient */ - float u_aux[3]; + hydro_real_t u_aux[3]; /*! Normalization for computing u_aux */ - float u_aux_norm[3]; + hydro_real_t u_aux_norm[3]; + /*! Flag for whether u_aux_norm is ill-conditioned */ + char u_well_conditioned; + /*! Internal energy Hessian */ - float u_hessian[3][3]; + hydro_real_t u_hessian[3][3]; } gradients; diff --git a/src/hydro_properties.c b/src/hydro_properties.c index 531b04ac13..b587892edb 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -83,7 +83,7 @@ void hydro_props_init(struct hydro_props *p, "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(const_kernel_target_neighbours - p->target_neighbours) > + 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 " From db28d98b8d4bfdcad3e8794522a87a10b226e4dd Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Wed, 30 Jul 2025 10:36:55 -0400 Subject: [PATCH 19/39] Fixed a mistake when computing dh/dt in both the MAGMA2 SPH way and the alternative way. Reorganized the compile-time constants to separate those that can change and those that should be fixed. Changed the SPH divergence estimate to be consistent with the Swift comoving unit system. Conductivity v_sig_cond should be correct now. Allow target_neighbours to be set in the parameter file rather than resolution_eta. Fixed a mistake in makeMovie.py for the Rayleigh-Taylor test. --- .../Rayleigh-Taylor_2D/makeMovie.py | 2 +- src/hydro/MAGMA2/hydro.h | 21 +-- src/hydro/MAGMA2/hydro_iact.h | 153 ++++++------------ src/hydro/MAGMA2/hydro_parameters.h | 59 ++++--- src/hydro_properties.c | 24 ++- 5 files changed, 115 insertions(+), 144 deletions(-) diff --git a/examples/HydroTests/Rayleigh-Taylor_2D/makeMovie.py b/examples/HydroTests/Rayleigh-Taylor_2D/makeMovie.py index c6ca37e0c6..161a76ee95 100644 --- a/examples/HydroTests/Rayleigh-Taylor_2D/makeMovie.py +++ b/examples/HydroTests/Rayleigh-Taylor_2D/makeMovie.py @@ -71,7 +71,7 @@ def project(data, m_res, property, ylim): x = x[mask] y = y[mask] - np.float64(ylim[0]) - h = data.gas.smoothing_length[mask] + h = data.gas.smoothing_lengths[mask] if property == "density": property = "masses" diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index a696391d7d..39fd2de795 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -1502,22 +1502,17 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( __attribute__((always_inline)) INLINE static void hydro_end_force( struct part *restrict p, const struct cosmology *cosmo) { - const float rho_inv = hydro_get_comoving_density(p); - p->force.h_dt *= p->h * rho_inv * hydro_dimension_inv; + /* Evolve using G_ab if well-conditioned, otherwise regular SPH */ + if (p->gradients.C_well_conditioned && p->gradients.D_well_conditioned) { + p->force.h_dt *= p->h * hydro_dimension_inv; + } + else { + const float rho_inv = 1.f / hydro_get_comoving_density(p); + p->force.h_dt *= p->h * rho_inv * hydro_dimension_inv; + } /* dt_min is in physical units, and requires the kernel_gamma factor for h */ p->dt_min *= kernel_gamma * cosmo->a / cosmo->a_factor_sound_speed; - -#ifdef MAGMA2_DEBUG_CHECKS - if (p->force.h_dt > 1.e10f) { - warning( - "Particle %lld has a very large h_dt value (%g). This may be due to " - "a very low density or a very high smoothing length." - "rho_inv = %g, h = %g, hydro_dimension_inv = %g", - p->id, p->force.h_dt, - rho_inv, p->h, hydro_dimension_inv); - } -#endif } /** diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index c38c78d323..00aa118674 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -403,9 +403,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Cosmological factors entering the EoMs */ const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); - const hydro_real_t a_Hubble = a * H; - const hydro_real_t a2_Hubble = a * a_Hubble; - const hydro_real_t a_inv = 1. / 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; @@ -520,12 +518,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->gradients.velocity_hessian, vj_reconstructed); + /* Artificial viscosity */ + + const hydro_real_t dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], vi_reconstructed[1] - vj_reconstructed[1], vi_reconstructed[2] - vj_reconstructed[2]}; -#ifdef hydro_props_use_asymmetric_viscosity + /* Need this for viscosity and conductivity */ + hydro_real_t dv_dot_dr_ij = 0.; + hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_ij); + dv_dot_dr_ij += a2_Hubble * r2; + +#ifdef hydro_props_use_asymmetric_viscosity_mu const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; const hydro_real_t eta_i[3] = {dx_ij[0] * hi_inv, @@ -605,10 +611,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; #else - hydro_real_t dv_dot_dr_ij = 0.; - hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_ij); - dv_dot_dr_ij += a2_Hubble * r2; - const hydro_real_t conv = (dv_dot_dr_ij < 0.) ? fac_mu : 0.; const hydro_real_t mu_ij = conv * dv_dot_dr_ij * r_inv; const hydro_real_t c_ij = @@ -652,23 +654,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->gradients.u_hessian, &uj_reconstructed); } - - /* It is important to get the Hubble flow correction right for the - * conductivity. The same Hubble flow correction for the viscosity is not - * correct, since Rosswog 2020 uses |dv_ij||G_ij| for the heat flux. - * - * The Hubble flow correction is only applied to the velocity, but then - * |G_ij| must be computed in physical coordinates (it is a distance). - */ - const hydro_real_t dv_ij_phys[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], - dv_ij[1] * a_inv + a_Hubble * dx[1], - dv_ij[2] * a_inv + a_Hubble * dx[2]}; - - hydro_real_t dv_ij_phys_norm = 0.; - hydro_vec3_vec3_dot(dv_ij_phys, dv_ij_phys, &dv_ij_phys_norm); - /* Signal velocity is the norm of the velocity difference vector */ - dv_ij_phys_norm = sqrt(dv_ij_phys_norm); - const hydro_real_t v_sig_cond = fac_mu * dv_ij_phys_norm; + + /* Signal velocity is the velocity difference projected with Hubble flow */ + const hydro_real_t v_sig_cond = fac_mu * (dv_dot_dr_ij * r_inv); const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); @@ -686,58 +674,42 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_ij[1] = 0.5 * (G_i[1] + G_j[1]); G_ij[2] = 0.5 * (G_i[2] + G_j[2]); - const hydro_real_t G_ij_phys[3] = {G_ij[0] * a, - G_ij[1] * a, - G_ij[2] * a}; - - hydro_real_t G_ij_phys_norm = 0.; - hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_phys_norm); - G_ij_phys_norm = sqrtf(G_ij_phys_norm); + hydro_real_t G_ij_norm = 0.; + hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_norm); + G_ij_norm = sqrt(G_ij_norm); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ -#ifdef hydro_props_use_second_order_velocities_in_divergence hydro_real_t dv_dot_G_ij = 0.; +#ifdef hydro_props_use_second_order_velocities_in_divergence hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); - - hydro_real_t dv_dot_G_ij_phys = 0.; - hydro_vec3_vec3_dot(dv_ij_phys, G_ij_phys, &dv_dot_G_ij_phys); #else - hydro_real_t dv_dot_G_ij = 0.; hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); - - const hydro_real_t dv_phys[3] = {dv[0] * a_inv + a_Hubble * dx[0], - dv[1] * a_inv + a_Hubble * dx[1], - dv[2] * a_inv + a_Hubble * dx[2]}; - hydro_real_t dv_dot_G_ij_phys = 0.; - hydro_vec3_vec3_dot(dv_phys, G_ij_phys, &dv_dot_G_ij_phys); #endif sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; - /* TODO: Should these be physical? */ - visc_du_term *= dv_dot_G_ij_phys; - cond_du_term *= G_ij_phys_norm; /* Eq. 24 Rosswog 2020 */ + visc_du_term *= dv_dot_G_ij; + cond_du_term *= G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dv_dot_G_ij; - pj->force.h_dt -= mi * dv_dot_G_ij; + pi->force.h_dt += mj * dv_dot_G_ij; + pj->force.h_dt += mi * dv_dot_G_ij; /* Timestepping */ /* New signal velocity */ -#ifdef hydro_props_use_asymmetric_viscosity +#ifdef hydro_props_use_asymmetric_viscosity_mu const hydro_real_t v_sig_visc_i = signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); const hydro_real_t v_sig_visc_j = signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); - const hydro_real_t v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); + const hydro_real_t v_sig_max = max(v_sig_visc_i, v_sig_visc_j); #else - const hydro_real_t v_sig_visc = + const hydro_real_t v_sig_max = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); #endif - const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); /* Update if we need to */ pi->v_sig_max = max(pi->v_sig_max, v_sig_max); @@ -869,9 +841,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Cosmological factors entering the EoMs */ const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); - const hydro_real_t a_Hubble = a * H; - const hydro_real_t a2_Hubble = a * a_Hubble; - const hydro_real_t a_inv = 1. / a; + const hydro_real_t a2_Hubble = a * a * H; const hydro_real_t r = sqrtf(r2); const hydro_real_t r_inv = r ? 1.0 / r : 0.0; @@ -983,12 +953,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pj->gradients.velocity_hessian, vj_reconstructed); + /* Artificial viscosity */ + + const hydro_real_t dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], vi_reconstructed[1] - vj_reconstructed[1], vi_reconstructed[2] - vj_reconstructed[2]}; -#ifdef hydro_props_use_asymmetric_viscosity + /* Need this for viscosity and conductivity */ + hydro_real_t dv_dot_dr_ij = 0.; + hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_ij); + dv_dot_dr_ij += a2_Hubble * r2; + +#ifdef hydro_props_use_asymmetric_viscosity_mu const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; const hydro_real_t eta_i[3] = {dx_ij[0] * hi_inv, @@ -1069,10 +1047,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; #else - hydro_real_t dv_dot_dr_ij = 0.; - hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_ij); - dv_dot_dr_ij += a2_Hubble * r2; - const hydro_real_t conv = (dv_dot_dr_ij < 0.) ? fac_mu : 0.; const hydro_real_t mu_ij = conv * dv_dot_dr_ij * r_inv; const hydro_real_t c_ij = @@ -1115,26 +1089,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pj->gradients.u_hessian, &uj_reconstructed); } - - /* It is important to get the Hubble flow correction right for the - * conductivity. The same Hubble flow correction for the viscosity is not - * correct, since Rosswog 2020 uses |dv_ij||G_ij| for the heat flux. - * - * The Hubble flow correction is only applied to the velocity, but then - * |G_ij| must be computed in physical coordinates (it is a distance). - * - * The scale factors in velocity and distance cancel, so we end up with - * something like a code velocity * code distance + Hubble flow correction. - */ - const hydro_real_t dv_ij_phys[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], - dv_ij[1] * a_inv + a_Hubble * dx[1], - dv_ij[2] * a_inv + a_Hubble * dx[2]}; - - hydro_real_t dv_ij_phys_norm = 0.; - hydro_vec3_vec3_dot(dv_ij_phys, dv_ij_phys, &dv_ij_phys_norm); - /* Signal velocity is the norm of the velocity difference vector */ - dv_ij_phys_norm = sqrt(dv_ij_phys_norm); - const hydro_real_t v_sig_cond = fac_mu * dv_ij_phys_norm; + + /* Signal velocity is the velocity difference along r_ij */ + const hydro_real_t v_sig_cond = fac_mu * (dv_dot_dr_ij * r_inv); const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); @@ -1152,56 +1109,40 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( G_ij[1] = 0.5 * (G_i[1] + G_j[1]); G_ij[2] = 0.5 * (G_i[2] + G_j[2]); - const hydro_real_t G_ij_phys[3] = {G_ij[0] * a, - G_ij[1] * a, - G_ij[2] * a}; - - hydro_real_t G_ij_phys_norm = 0.; - hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_phys_norm); - G_ij_phys_norm = sqrtf(G_ij_phys_norm); + hydro_real_t G_ij_norm = 0.; + hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_norm); + G_ij_norm = sqrt(G_ij_norm); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ -#ifdef hydro_props_use_second_order_velocities_in_divergence hydro_real_t dv_dot_G_ij = 0.; +#ifdef hydro_props_use_second_order_velocities_in_divergence hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); - - hydro_real_t dv_dot_G_ij_phys = 0.; - hydro_vec3_vec3_dot(dv_ij_phys, G_ij_phys, &dv_dot_G_ij_phys); #else - hydro_real_t dv_dot_G_ij = 0.; hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); - - const hydro_real_t dv_phys[3] = {dv[0] * a_inv + a_Hubble * dx[0], - dv[1] * a_inv + a_Hubble * dx[1], - dv[2] * a_inv + a_Hubble * dx[2]}; - hydro_real_t dv_dot_G_ij_phys = 0.; - hydro_vec3_vec3_dot(dv_phys, G_ij_phys, &dv_dot_G_ij_phys); #endif sph_du_term_i *= dv_dot_G_ij; - /* TODO: Should these be physical? */ - visc_du_term *= dv_dot_G_ij_phys; - cond_du_term *= G_ij_phys_norm; /* Eq. 24 Rosswog 2020 */ + visc_du_term *= dv_dot_G_ij; + cond_du_term *= G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dv_dot_G_ij; + pi->force.h_dt += mj * dv_dot_G_ij; /* Timestepping */ /* New signal velocity */ -#ifdef hydro_props_use_asymmetric_viscosity +#ifdef hydro_props_use_asymmetric_viscosity_mu const hydro_real_t v_sig_visc_i = signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); const hydro_real_t v_sig_visc_j = signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); - const hydro_real_t v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); + const hydro_real_t v_sig_max = max(v_sig_visc_i, v_sig_visc_j); #else - const hydro_real_t v_sig_visc = + const hydro_real_t v_sig_max = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); #endif - const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); /* Update if we need to */ pi->v_sig_max = max(pi->v_sig_max, v_sig_max); diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index c1d307bcd3..9fcca5ce01 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -45,48 +45,65 @@ * as well as a number of compile-time parameters. */ -/*! Consider C matrix can be ill-conditioned above this limit */ -#define const_condition_number_upper_limit 999.f -/*! Viscosity & Conductivitiy parameters -- MUST BE DEFINED AT COMPILE-TIME */ +/* ---------- Viscosity & Conductivitiy parameters ---------- */ -/*! Cosmology default beta=2.0. - * Alpha can be set in the parameter file. - * Beta is defined as in e.g. Price (2010) Eqn (103) */ -#define const_viscosity_beta 2.0 -/*! Cosmology default alpha=1.0 */ +/*! Alpha viscosity, usually ~2. For lower neighbor number, should be higher */ #define const_viscosity_alpha 1.0 -/*! Softening squared (epsilon^2) in Eq. 15 Rosswog 2020 */ -#define const_viscosity_epsilon2 0.01 - /*! Artificial conductivity alpha */ #define const_conductivity_alpha 0.05 /*! Desired number of neighbours -- CRITICAL that this matches hydro props */ -#define const_kernel_target_neighbours 57.0 +#if defined(HYDRO_DIMENSION_1D) +#define const_kernel_target_neighbours 4.0 +#elif defined(HYDRO_DIMENSION_2D) +#define const_kernel_target_neighbours 17.0 +#else +#define const_kernel_target_neighbours 128.//57.0 +#endif + +/*! Use the alternative viscosity weighting (each particle has mu_i and mu_j) */ +#define hydro_props_use_asymmetric_viscosity_mu + +/*! Use the second-order velocities in v_ij * G_ij */ +#define hydro_props_use_second_order_velocities_in_divergence + +/*! Use double precision for all matrix/vector operations */ +#define hydro_props_use_double_precision + + +/* ---------- These parameters should not be changed ---------- */ + + +/*! Consider matrix inversion to be ill-conditioned above this limit */ +#ifdef hydro_props_use_double_precision +#define const_condition_number_upper_limit 99999. +#else +#define const_condition_number_upper_limit 999. +#endif /*! Mean interparticle spacing for this kernel and neighbour number */ #define const_kernel_mean_spacing (kernel_gamma*(4. * M_PI / (3. * \ - powf(const_kernel_target_neighbours, 1. / 3.)))) + powf((float)const_kernel_target_neighbours, 1. / 3.)))) /*! eta_crit Rosswog 2020 Eq 23. Of order the mean interparticle spacing. */ -#define const_slope_limiter_eta_crit (const_kernel_mean_spacing) +#define const_slope_limiter_eta_crit (4.f * const_kernel_mean_spacing) /*! eta_fold from Frontiere+'17 Equation 51 */ #define const_slope_limiter_eta_fold 0.2 -/*! Activate to use the alternative viscosity weighting */ -//#define hydro_props_use_asymmetric_viscosity +/*! Softening squared (epsilon^2) in Eq. 15 Rosswog 2020 */ +#define const_viscosity_epsilon2 0.01 -/*! Activate to use the second-order velocities in v_ij * G_ij */ -#define hydro_props_use_second_order_velocities_in_divergence +/*! 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) -/*! Activate to use double precision for all matrix/vector operations */ -#define hydro_props_use_double_precision -/* Structures for below */ +/* ---------- Structures for below ---------- */ + /*! Artificial viscosity parameters */ struct viscosity_global_data { }; diff --git a/src/hydro_properties.c b/src/hydro_properties.c index b587892edb..3107ed188a 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -63,14 +63,32 @@ 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)) * @@ -88,7 +106,7 @@ void hydro_props_init(struct hydro_props *p, 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.", - const_kernel_target_neighbours, p->target_neighbours); + (float)const_kernel_target_neighbours, p->target_neighbours); } #endif #endif From 7ce9ec8d35bb71c5dd915dcbb5d26a98d403a4af Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Wed, 30 Jul 2025 11:38:33 -0400 Subject: [PATCH 20/39] Fixed a bug in the tensor matrix contraction, now using the proper index. Added in a small debugging note if particles penetrate one another. Fixed a bug in the estimation of the mean interparticle spacing. Must not use second order velocities in the SPH divergence sums, that causes severe asymmetries. --- src/hydro/MAGMA2/hydro.h | 18 +++++++---- src/hydro/MAGMA2/hydro_iact.h | 46 +++++++++++++---------------- src/hydro/MAGMA2/hydro_parameters.h | 12 ++++---- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 39fd2de795..e2313d1b2d 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -564,8 +564,7 @@ void hydro_vec3_vec3_dot(const hydro_real_t *restrict vec_a, } /** - * @brief The scalar triple product of vector vec and matrix mat. - * That is vec^T * mat * vec. + * @brief The Frobenius inner product of two matrices. * * * @param mat The matrix to contract with the vector. @@ -642,7 +641,7 @@ void hydro_tensor3x3x3_matrix3x3_dot( tensor[1][2][1] * mat[2][1] + tensor[1][2][2] * mat[2][2]; - result[3] = tensor[2][0][0] * mat[0][0] + + 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] + @@ -701,11 +700,20 @@ hydro_real_t hydro_van_leer_phi(const hydro_real_t A_ij, /* η_ab and η_crit damping */ const hydro_real_t eta_ij = min(eta_i, eta_j); - hydro_real_t damping = 1.f; + 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 = expf(-diff * diff); + damping = exp(-diff * diff); + +#ifdef MAGMA2_DEBUG_CHECKS + if (eta_ij < 0.1 * const_slope_limiter_eta_crit) { + warning("Particles extremely close! <0.1 mean interparticle spacing. " + "phi_raw = %g, damping = %g, eta_ij = %g, " + "eta_crit = %g, eta_fold = %g", phi_raw, damping, eta_ij, + const_slope_limiter_eta_crit, const_slope_limiter_eta_fold); + } +#endif } return phi_raw * damping; diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 00aa118674..f16264b5ea 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -475,20 +475,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Corrected gradients */ hydro_real_t G_i[3] = {0., 0., 0.}; hydro_real_t G_j[3] = {0., 0., 0.}; - hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx_ij, G_i); - /* Important: Use same separation vector */ - hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx_ij, G_j); + /* 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); - /* Note: negative because dx is pj->x - pi->x in Rosswog 2020. - * It is pj->x - pi->x for BOTH particles, and then sign flip - * later */ - G_i[0] *= -wi * hi_inv_dim; - G_i[1] *= -wi * hi_inv_dim; - G_i[2] *= -wi * hi_inv_dim; + 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; + G_j[0] *= wj * hj_inv_dim; + G_j[1] *= wj * hj_inv_dim; + G_j[2] *= wj * hj_inv_dim; /* Compute second order reconstruction of velocity between pi & pj */ hydro_real_t vi_reconstructed[3] = {0., 0., 0.}; @@ -910,20 +907,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Corrected gradients */ hydro_real_t G_i[3] = {0., 0., 0.}; hydro_real_t G_j[3] = {0., 0., 0.}; - hydro_mat3x3_vec3_dot(pi->gradients.correction_matrix, dx_ij, G_i); - /* Important: Use the same separation vector. */ - hydro_mat3x3_vec3_dot(pj->gradients.correction_matrix, dx_ij, G_j); - - /* Note: negative because dx is pj->x - pi->x in Rosswog 2020. - * It is pj->x - pi->x for BOTH particles, and then sign flip - * later */ - 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; + /* 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; /* Compute second order reconstruction of velocity between pi & pj */ hydro_real_t vi_reconstructed[3] = {0., 0., 0.}; diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 9fcca5ce01..7ae1d4d43d 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -61,14 +61,14 @@ #elif defined(HYDRO_DIMENSION_2D) #define const_kernel_target_neighbours 17.0 #else -#define const_kernel_target_neighbours 128.//57.0 +#define const_kernel_target_neighbours 57.0 #endif /*! Use the alternative viscosity weighting (each particle has mu_i and mu_j) */ -#define hydro_props_use_asymmetric_viscosity_mu +// #define hydro_props_use_asymmetric_viscosity_mu /*! Use the second-order velocities in v_ij * G_ij */ -#define hydro_props_use_second_order_velocities_in_divergence +//#define hydro_props_use_second_order_velocities_in_divergence /*! Use double precision for all matrix/vector operations */ #define hydro_props_use_double_precision @@ -85,11 +85,11 @@ #endif /*! Mean interparticle spacing for this kernel and neighbour number */ -#define const_kernel_mean_spacing (kernel_gamma*(4. * M_PI / (3. * \ - powf((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.)) /*! eta_crit Rosswog 2020 Eq 23. Of order the mean interparticle spacing. */ -#define const_slope_limiter_eta_crit (4.f * const_kernel_mean_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 From 876867e1a02c2c48822840546e86b7bf15e1f170 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Wed, 30 Jul 2025 12:51:16 -0400 Subject: [PATCH 21/39] Added in the correct Hubble flow term for the artificial viscosity. --- src/hydro/MAGMA2/hydro_iact.h | 24 ++++++++++++++++++++++-- src/hydro/MAGMA2/hydro_parameters.h | 4 ++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index f16264b5ea..55ac0a81ea 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -677,15 +677,25 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ hydro_real_t dv_dot_G_ij = 0.; + hydro_real_t dv_dot_G_ij_Hubble = 0.; #ifdef hydro_props_use_second_order_velocities_in_divergence hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); + + const hydro_real_t dv_ij_Hubble[3] = {dv_ij[0] + a2_Hubble * dx[0], + dv_ij[1] + a2_Hubble * dx[1], + dv_ij[2] + a2_Hubble * dx[2]}; + hydro_vec3_vec3_dot(dv_ij_Hubble, G_ij, &dv_dot_G_ij_Hubble); #else hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); + 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]}; + hydro_vec3_vec3_dot(dv_Hubble, G_ij, &dv_dot_G_ij_Hubble); #endif sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; - visc_du_term *= dv_dot_G_ij; + visc_du_term *= dv_dot_G_ij_Hubble; cond_du_term *= G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ @@ -1109,14 +1119,24 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ hydro_real_t dv_dot_G_ij = 0.; + hydro_real_t dv_dot_G_ij_Hubble = 0.; #ifdef hydro_props_use_second_order_velocities_in_divergence hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); + + const hydro_real_t dv_ij_Hubble[3] = {dv_ij[0] + a2_Hubble * dx[0], + dv_ij[1] + a2_Hubble * dx[1], + dv_ij[2] + a2_Hubble * dx[2]}; + hydro_vec3_vec3_dot(dv_ij_Hubble, G_ij, &dv_dot_G_ij_Hubble); #else hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); + 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]}; + hydro_vec3_vec3_dot(dv_Hubble, G_ij, &dv_dot_G_ij_Hubble); #endif sph_du_term_i *= dv_dot_G_ij; - visc_du_term *= dv_dot_G_ij; + visc_du_term *= dv_dot_G_ij_Hubble; cond_du_term *= G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 7ae1d4d43d..7a15e76d68 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -49,7 +49,7 @@ /* ---------- Viscosity & Conductivitiy parameters ---------- */ -/*! Alpha viscosity, usually ~2. For lower neighbor number, should be higher */ +/*! Alpha viscosity, usually =1.0. For lower neighbor number, should be higher */ #define const_viscosity_alpha 1.0 /*! Artificial conductivity alpha */ @@ -71,7 +71,7 @@ //#define hydro_props_use_second_order_velocities_in_divergence /*! Use double precision for all matrix/vector operations */ -#define hydro_props_use_double_precision +//#define hydro_props_use_double_precision /* ---------- These parameters should not be changed ---------- */ From 26e3283a765bc140ae0ac007a35febc44e7ada57 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Wed, 30 Jul 2025 13:48:48 -0400 Subject: [PATCH 22/39] Use the pressure difference in the conductivity signal speed. I think that Rosswog 2020 has a sign error in the conductivity equation, so I fixed that here. --- src/hydro/MAGMA2/hydro.h | 10 ++------- src/hydro/MAGMA2/hydro_iact.h | 42 +++++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index e2313d1b2d..290bc196cd 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -1510,14 +1510,8 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( __attribute__((always_inline)) INLINE static void hydro_end_force( struct part *restrict p, const struct cosmology *cosmo) { - /* Evolve using G_ab if well-conditioned, otherwise regular SPH */ - if (p->gradients.C_well_conditioned && p->gradients.D_well_conditioned) { - p->force.h_dt *= p->h * hydro_dimension_inv; - } - else { - const float rho_inv = 1.f / hydro_get_comoving_density(p); - p->force.h_dt *= p->h * rho_inv * hydro_dimension_inv; - } + const float rho_inv = 1.f / hydro_get_comoving_density(p); + p->force.h_dt *= p->h * rho_inv * hydro_dimension_inv; /* dt_min is in physical units, and requires the kernel_gamma factor for h */ p->dt_min *= kernel_gamma * cosmo->a / cosmo->a_factor_sound_speed; diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 55ac0a81ea..5d65c190a9 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -652,14 +652,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( &uj_reconstructed); } + const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); + const hydro_real_t rho_ij_inv = 1. / rho_ij; + /* Signal velocity is the velocity difference projected with Hubble flow */ - const hydro_real_t v_sig_cond = fac_mu * (dv_dot_dr_ij * r_inv); + const hydro_real_t v_sig_speed = fac_mu * fabs(dv_dot_dr_ij) * r_inv; + const hydro_real_t v_sig_pressure = + sqrt(fabs(pi->force.pressure - pj->force.pressure) * rho_ij_inv); + const hydro_real_t v_sig_cond = v_sig_speed + v_sig_pressure; const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; - const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); /* Add conductivity to the specific energy */ - cond_du_term = -const_conductivity_alpha * v_sig_cond * du_ij / rho_ij; + cond_du_term = + const_conductivity_alpha * fabs(v_sig_cond) * du_ij * rho_ij_inv; /* Finalize everything with the correct normalizations. */ @@ -699,8 +705,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( cond_du_term *= G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ - pi->force.h_dt += mj * dv_dot_G_ij; - pj->force.h_dt += mi * dv_dot_G_ij; + pi->force.h_dt -= mj * dv_dot_G_ij; + pj->force.h_dt -= mi * dv_dot_G_ij; /* Timestepping */ @@ -712,11 +718,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); const hydro_real_t v_sig_visc_j = signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); - const hydro_real_t v_sig_max = max(v_sig_visc_i, v_sig_visc_j); + const hydro_real_t v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); #else - const hydro_real_t v_sig_max = + const hydro_real_t v_sig_visc = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); #endif + const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); /* Update if we need to */ pi->v_sig_max = max(pi->v_sig_max, v_sig_max); @@ -1094,14 +1101,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( &uj_reconstructed); } - /* Signal velocity is the velocity difference along r_ij */ - const hydro_real_t v_sig_cond = fac_mu * (dv_dot_dr_ij * r_inv); + const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); + const hydro_real_t rho_ij_inv = 1. / rho_ij; + + /* Signal velocity is the velocity difference projected with Hubble flow */ + const hydro_real_t v_sig_speed = fac_mu * fabs(dv_dot_dr_ij) * r_inv; + const hydro_real_t v_sig_pressure = + sqrt(fabs(pi->force.pressure - pj->force.pressure) * rho_ij_inv); + const hydro_real_t v_sig_cond = v_sig_speed + v_sig_pressure; const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; - const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); /* Add conductivity to the specific energy */ - cond_du_term = -const_conductivity_alpha * v_sig_cond * du_ij / rho_ij; + cond_du_term = + const_conductivity_alpha * fabs(v_sig_cond) * du_ij * rho_ij_inv; /* Finalize the viscosity and conductivity with correct normalizations. */ @@ -1140,7 +1153,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( cond_du_term *= G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ - pi->force.h_dt += mj * dv_dot_G_ij; + pi->force.h_dt -= mj * dv_dot_G_ij; /* Timestepping */ @@ -1152,11 +1165,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); const hydro_real_t v_sig_visc_j = signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); - const hydro_real_t v_sig_max = max(v_sig_visc_i, v_sig_visc_j); + const hydro_real_t v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); #else - const hydro_real_t v_sig_max = + const hydro_real_t v_sig_visc = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); #endif + const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); /* Update if we need to */ pi->v_sig_max = max(pi->v_sig_max, v_sig_max); From 5f2cbb5c978d37906fe4b627229b5def2410737b Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Wed, 30 Jul 2025 15:33:03 -0400 Subject: [PATCH 23/39] Got the sign correct for the conductivity (heat flows from high to low temperatures). Got the Hubble flow additions correct for the viscosity. --- src/hydro/MAGMA2/hydro_iact.h | 38 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 5d65c190a9..ecbc5888bd 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -403,7 +403,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* 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 a_inv = 1. / a; + const hydro_real_t a_Hubble = a * H; + const hydro_real_t a2_Hubble = a * a_Hubble; const hydro_real_t r = sqrt(r2); const hydro_real_t r_inv = r ? 1.0 / r : 0.0; @@ -687,22 +689,23 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( #ifdef hydro_props_use_second_order_velocities_in_divergence hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); - const hydro_real_t dv_ij_Hubble[3] = {dv_ij[0] + a2_Hubble * dx[0], - dv_ij[1] + a2_Hubble * dx[1], - dv_ij[2] + a2_Hubble * dx[2]}; + const hydro_real_t dv_ij_Hubble[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], + dv_ij[1] * a_inv + a_Hubble * dx[1], + dv_ij[2] * a_inv + a_Hubble * dx[2]}; hydro_vec3_vec3_dot(dv_ij_Hubble, G_ij, &dv_dot_G_ij_Hubble); #else hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); - 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_Hubble[3] = {dv[0] * a_inv + a_Hubble * dx[0], + dv[1] * a_inv + a_Hubble * dx[1], + dv[2] * a_inv + a_Hubble * dx[2]}; hydro_vec3_vec3_dot(dv_Hubble, G_ij, &dv_dot_G_ij_Hubble); #endif sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; visc_du_term *= dv_dot_G_ij_Hubble; - cond_du_term *= G_ij_norm; /* Eq. 24 Rosswog 2020 */ + cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; @@ -855,7 +858,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* 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 a_inv = 1. / a; + const hydro_real_t a_Hubble = a * H; + const hydro_real_t a2_Hubble = a * a_Hubble; const hydro_real_t r = sqrtf(r2); const hydro_real_t r_inv = r ? 1.0 / r : 0.0; @@ -1136,21 +1141,22 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( #ifdef hydro_props_use_second_order_velocities_in_divergence hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); - const hydro_real_t dv_ij_Hubble[3] = {dv_ij[0] + a2_Hubble * dx[0], - dv_ij[1] + a2_Hubble * dx[1], - dv_ij[2] + a2_Hubble * dx[2]}; + const hydro_real_t dv_ij_Hubble[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], + dv_ij[1] * a_inv + a_Hubble * dx[1], + dv_ij[2] * a_inv + a_Hubble * dx[2]}; hydro_vec3_vec3_dot(dv_ij_Hubble, G_ij, &dv_dot_G_ij_Hubble); #else hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); - 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_Hubble[3] = {dv[0] * a_inv + a_Hubble * dx[0], + dv[1] * a_inv + a_Hubble * dx[1], + dv[2] * a_inv + a_Hubble * dx[2]}; hydro_vec3_vec3_dot(dv_Hubble, G_ij, &dv_dot_G_ij_Hubble); #endif sph_du_term_i *= dv_dot_G_ij; visc_du_term *= dv_dot_G_ij_Hubble; - cond_du_term *= G_ij_norm; /* Eq. 24 Rosswog 2020 */ + cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; From 292c7ed2707cbb9cd406e8e0a99e53e11b1ed365 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Wed, 30 Jul 2025 16:19:28 -0400 Subject: [PATCH 24/39] Cleaning up some of the variable names in the force interaction. Also relabelled conservative to non-conservative. --- src/hydro/MAGMA2/hydro.h | 2 +- src/hydro/MAGMA2/hydro_debug.h | 2 +- src/hydro/MAGMA2/hydro_iact.h | 62 +++++++++++++++++----------------- src/hydro/MAGMA2/hydro_io.h | 2 +- src/hydro/MAGMA2/hydro_part.h | 2 +- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 290bc196cd..8ad2eb00dc 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -23,7 +23,7 @@ /** * @file MAGMA2/hydro.h - * @brief Density-Energy conservative implementation of SPH, + * @brief Density-Energy non-conservative implementation of SPH, * with added MAGMA2 physics (Rosswog 2020) (Non-neighbour loop * equations) */ diff --git a/src/hydro/MAGMA2/hydro_debug.h b/src/hydro/MAGMA2/hydro_debug.h index c79c9da7c4..0e4bcf656e 100644 --- a/src/hydro/MAGMA2/hydro_debug.h +++ b/src/hydro/MAGMA2/hydro_debug.h @@ -24,7 +24,7 @@ /** * @file MAGMA2/hydro_debug.h - * @brief Density-Energy conservative implementation of SPH, + * @brief Density-Energy non-conservative implementation of SPH, * with added MAGMA2 physics (Rosswog 2020) (Debugging routines) */ diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index ecbc5888bd..728539b34b 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -23,7 +23,7 @@ /** * @file MAGMA2/hydro_iact.h - * @brief Density-Energy conservative implementation of SPH, + * @brief Density-Energy non-conservative implementation of SPH, * with added MAGMA2 physics (Rosswog 2020) (interaction routines) */ @@ -526,9 +526,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( vi_reconstructed[2] - vj_reconstructed[2]}; /* Need this for viscosity and conductivity */ - hydro_real_t dv_dot_dr_ij = 0.; - hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_ij); - dv_dot_dr_ij += a2_Hubble * r2; + hydro_real_t dv_dot_dr_Hubble = 0.; + hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_Hubble); + dv_dot_dr_Hubble += a2_Hubble * r2; #ifdef hydro_props_use_asymmetric_viscosity_mu const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; @@ -548,18 +548,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( hydro_real_t dv_dot_eta_i = 0.; hydro_vec3_vec3_dot(dv_ij, eta_i, &dv_dot_eta_i); /* Scale Hubble flow by hi_inv so it is overall scaled */ - const hydro_real_t dv_dot_eta_i_phys = + const hydro_real_t dv_dot_eta_i_Hubble = dv_dot_eta_i + a2_Hubble * r2 * hi_inv; hydro_real_t dv_dot_eta_j = 0.; hydro_vec3_vec3_dot(dv_ji, eta_j, &dv_dot_eta_j); /* Scale Hubble flow by hj_inv so it is overall scaled */ - const hydro_real_t dv_dot_eta_j_phys = + const hydro_real_t dv_dot_eta_j_Hubble = dv_dot_eta_j + a2_Hubble * r2 * hj_inv; /* Is the flow converging? */ - const char conv_i = (dv_dot_eta_i_phys < 0.) ? 1 : 0; - const char conv_j = (dv_dot_eta_j_phys < 0.) ? 1 : 0; + const char conv_i = (dv_dot_eta_i_Hubble < 0.) ? 1 : 0; + const char conv_j = (dv_dot_eta_j_Hubble < 0.) ? 1 : 0; /* Is the flow converging? If so, multiply mu by fac_mu. If not, zero. */ const hydro_real_t conv = (conv_i && conv_j) ? fac_mu : 0.; @@ -568,7 +568,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( if (conv_i != conv_j) { warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" "conv_i = %d, conv_j = %d\n" - "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" + "dv_dot_eta_i_Hubble = %g, dv_dot_eta_j_Hubble = %g\n" "vi_reconstructed = (%g, %g, %g), " "vj_reconstructed = (%g, %g, %g)\n" "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" @@ -578,7 +578,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( "a2_Hubble = %g, r2 = %g\n", pi->id, pj->id, conv_i, conv_j, - dv_dot_eta_i_phys, dv_dot_eta_j_phys, + dv_dot_eta_i_Hubble, dv_dot_eta_j_Hubble, vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], dv_ij[0], dv_ij[1], dv_ij[2], @@ -593,9 +593,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* mu_i and mu_j include the Hubble flow */ const hydro_real_t mu_i = - conv * dv_dot_eta_i_phys / (eta_i2 + const_viscosity_epsilon2); + conv * dv_dot_eta_i_Hubble / (eta_i2 + const_viscosity_epsilon2); const hydro_real_t mu_j = - conv * dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2); + conv * dv_dot_eta_j_Hubble / (eta_j2 + const_viscosity_epsilon2); const hydro_real_t Q_i_alpha = -const_viscosity_alpha * pi->force.soundspeed * mu_i; @@ -610,8 +610,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; #else - const hydro_real_t conv = (dv_dot_dr_ij < 0.) ? fac_mu : 0.; - const hydro_real_t mu_ij = conv * dv_dot_dr_ij * r_inv; + const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; + const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; const hydro_real_t c_ij = 0.5 * (pi->force.soundspeed + pj->force.soundspeed); @@ -658,9 +658,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t rho_ij_inv = 1. / rho_ij; /* Signal velocity is the velocity difference projected with Hubble flow */ - const hydro_real_t v_sig_speed = fac_mu * fabs(dv_dot_dr_ij) * r_inv; + const hydro_real_t v_sig_speed = fac_mu * fabs(dv_dot_dr_Hubble) * r_inv; const hydro_real_t v_sig_pressure = - sqrt(fabs(pi->force.pressure - pj->force.pressure) * rho_ij_inv); + sqrt(fabs(pressurei - pressurej) * rho_ij_inv); const hydro_real_t v_sig_cond = v_sig_speed + v_sig_pressure; const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; @@ -978,9 +978,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( vi_reconstructed[2] - vj_reconstructed[2]}; /* Need this for viscosity and conductivity */ - hydro_real_t dv_dot_dr_ij = 0.; - hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_ij); - dv_dot_dr_ij += a2_Hubble * r2; + hydro_real_t dv_dot_dr_Hubble = 0.; + hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_Hubble); + dv_dot_dr_Hubble += a2_Hubble * r2; #ifdef hydro_props_use_asymmetric_viscosity_mu const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; @@ -1001,18 +1001,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( hydro_real_t dv_dot_eta_i = 0.; hydro_vec3_vec3_dot(dv_ij, eta_i, &dv_dot_eta_i); /* Scale Hubble flow by hi_inv so it is overall scaled */ - const hydro_real_t dv_dot_eta_i_phys = + const hydro_real_t dv_dot_eta_i_Hubble = dv_dot_eta_i + a2_Hubble * r2 * hi_inv; hydro_real_t dv_dot_eta_j = 0.; hydro_vec3_vec3_dot(dv_ji, eta_j, &dv_dot_eta_j); /* Scale Hubble flow by hj_inv so it is overall scaled */ - const hydro_real_t dv_dot_eta_j_phys = + const hydro_real_t dv_dot_eta_j_Hubble = dv_dot_eta_j + a2_Hubble * r2 * hj_inv; /* Is the flow converging? */ - const char conv_i = (dv_dot_eta_i_phys < 0.) ? 1 : 0; - const char conv_j = (dv_dot_eta_j_phys < 0.) ? 1 : 0; + const char conv_i = (dv_dot_eta_i_Hubble < 0.) ? 1 : 0; + const char conv_j = (dv_dot_eta_j_Hubble < 0.) ? 1 : 0; /* Is the flow converging? If yes, multiply by fac_mu. If no, zero. */ const hydro_real_t conv = (conv_i && conv_j) ? fac_mu : 0.; @@ -1021,7 +1021,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( if (conv_i != conv_j) { warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" "conv_i = %d, conv_j = %d\n" - "dv_dot_eta_i_phys = %g, dv_dot_eta_j_phys = %g\n" + "dv_dot_eta_i_Hubble = %g, dv_dot_eta_j_Hubble = %g\n" "vi_reconstructed = (%g, %g, %g), " "vj_reconstructed = (%g, %g, %g)\n" "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" @@ -1031,7 +1031,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( "a2_Hubble = %g, r2 = %g\n", pi->id, pj->id, conv_i, conv_j, - dv_dot_eta_i_phys, dv_dot_eta_j_phys, + dv_dot_eta_i_Hubble, dv_dot_eta_j_Hubble, vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], dv_ij[0], dv_ij[1], dv_ij[2], @@ -1046,9 +1046,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* mu_i and mu_j include the Hubble flow */ const hydro_real_t mu_i = - conv * dv_dot_eta_i_phys / (eta_i2 + const_viscosity_epsilon2); + conv * dv_dot_eta_i_Hubble / (eta_i2 + const_viscosity_epsilon2); const hydro_real_t mu_j = - conv * dv_dot_eta_j_phys / (eta_j2 + const_viscosity_epsilon2); + conv * dv_dot_eta_j_Hubble / (eta_j2 + const_viscosity_epsilon2); const hydro_real_t Q_i_alpha = -const_viscosity_alpha * pi->force.soundspeed * mu_i; @@ -1063,8 +1063,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; #else - const hydro_real_t conv = (dv_dot_dr_ij < 0.) ? fac_mu : 0.; - const hydro_real_t mu_ij = conv * dv_dot_dr_ij * r_inv; + const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; + const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; const hydro_real_t c_ij = 0.5 * (pi->force.soundspeed + pj->force.soundspeed); @@ -1110,9 +1110,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t rho_ij_inv = 1. / rho_ij; /* Signal velocity is the velocity difference projected with Hubble flow */ - const hydro_real_t v_sig_speed = fac_mu * fabs(dv_dot_dr_ij) * r_inv; + const hydro_real_t v_sig_speed = fac_mu * fabs(dv_dot_dr_Hubble) * r_inv; const hydro_real_t v_sig_pressure = - sqrt(fabs(pi->force.pressure - pj->force.pressure) * rho_ij_inv); + sqrt(fabs(pressurei - pressurej) * rho_ij_inv); const hydro_real_t v_sig_cond = v_sig_speed + v_sig_pressure; const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; diff --git a/src/hydro/MAGMA2/hydro_io.h b/src/hydro/MAGMA2/hydro_io.h index de27cc6611..97efa6917d 100644 --- a/src/hydro/MAGMA2/hydro_io.h +++ b/src/hydro/MAGMA2/hydro_io.h @@ -23,7 +23,7 @@ /** * @file MAGMA2/hydro_io.h - * @brief Density-Energy conservative implementation of SPH, + * @brief Density-Energy non-conservative implementation of SPH, * with added MAGMA2 physics (Rosswog 2020) (i/o routines) */ diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 0753042703..639337fc4c 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -23,7 +23,7 @@ /** * @file MAGMA2/hydro_part.h - * @brief Density-Energy conservative implementation of SPH, + * @brief Density-Energy non-conservative implementation of SPH, * with added MAGMA2 physics (Rosswog 2020) (particle definition) */ From f81e34e9b5b448b4bac7b1ff35a2129c429d2fe1 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Wed, 30 Jul 2025 17:11:11 -0400 Subject: [PATCH 25/39] Really now the proper dv_dot_G_ij term is included in the viscosity. --- .../Cosmology/ZeldovichPancake_3D/makeIC.py | 2 +- src/hydro/MAGMA2/hydro_iact.h | 30 +++---------------- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/examples/Cosmology/ZeldovichPancake_3D/makeIC.py b/examples/Cosmology/ZeldovichPancake_3D/makeIC.py index bd1366b53c..c5854ce75c 100644 --- a/examples/Cosmology/ZeldovichPancake_3D/makeIC.py +++ b/examples/Cosmology/ZeldovichPancake_3D/makeIC.py @@ -43,7 +43,7 @@ # Some useful variables in h-full units H_0 = 1.0 / Mpc_in_m * 10 ** 5 # h s^-1 -rho_0 = 3.0 * H_0 ** 2 / (8 * math.pi * G_in_SI) # h^2 kg m^-3 +rho_0 = 3.0 * H_0 ** 2 / (8 * pi * G_in_SI) # h^2 kg m^-3 lambda_i = 64.0 / H_0 * 10 ** 5 # h^-1 m (= 64 h^-1 Mpc) x_min = -0.5 * lambda_i x_max = 0.5 * lambda_i diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 728539b34b..0e036d8e3c 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -685,26 +685,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ hydro_real_t dv_dot_G_ij = 0.; - hydro_real_t dv_dot_G_ij_Hubble = 0.; #ifdef hydro_props_use_second_order_velocities_in_divergence hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); - - const hydro_real_t dv_ij_Hubble[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], - dv_ij[1] * a_inv + a_Hubble * dx[1], - dv_ij[2] * a_inv + a_Hubble * dx[2]}; - hydro_vec3_vec3_dot(dv_ij_Hubble, G_ij, &dv_dot_G_ij_Hubble); #else - hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); - - const hydro_real_t dv_Hubble[3] = {dv[0] * a_inv + a_Hubble * dx[0], - dv[1] * a_inv + a_Hubble * dx[1], - dv[2] * a_inv + a_Hubble * dx[2]}; - hydro_vec3_vec3_dot(dv_Hubble, G_ij, &dv_dot_G_ij_Hubble); + hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); #endif sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; - visc_du_term *= dv_dot_G_ij_Hubble; + visc_du_term *= dv_dot_G_ij; cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ @@ -1137,25 +1126,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ hydro_real_t dv_dot_G_ij = 0.; - hydro_real_t dv_dot_G_ij_Hubble = 0.; #ifdef hydro_props_use_second_order_velocities_in_divergence hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); - - const hydro_real_t dv_ij_Hubble[3] = {dv_ij[0] * a_inv + a_Hubble * dx[0], - dv_ij[1] * a_inv + a_Hubble * dx[1], - dv_ij[2] * a_inv + a_Hubble * dx[2]}; - hydro_vec3_vec3_dot(dv_ij_Hubble, G_ij, &dv_dot_G_ij_Hubble); #else - hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); - - const hydro_real_t dv_Hubble[3] = {dv[0] * a_inv + a_Hubble * dx[0], - dv[1] * a_inv + a_Hubble * dx[1], - dv[2] * a_inv + a_Hubble * dx[2]}; - hydro_vec3_vec3_dot(dv_Hubble, G_ij, &dv_dot_G_ij_Hubble); + hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); #endif sph_du_term_i *= dv_dot_G_ij; - visc_du_term *= dv_dot_G_ij_Hubble; + visc_du_term *= dv_dot_G_ij; cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ From 8a5aa7baf708241e7f119b77904b7485f519e0f9 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Wed, 30 Jul 2025 17:13:05 -0400 Subject: [PATCH 26/39] Forgot to remove a_inv, no longer needed. --- src/hydro/MAGMA2/hydro_iact.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 0e036d8e3c..e74ffddbf0 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -403,7 +403,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Cosmological factors entering the EoMs */ const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); - const hydro_real_t a_inv = 1. / a; const hydro_real_t a_Hubble = a * H; const hydro_real_t a2_Hubble = a * a_Hubble; @@ -847,7 +846,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Cosmological factors entering the EoMs */ const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); - const hydro_real_t a_inv = 1. / a; const hydro_real_t a_Hubble = a * H; const hydro_real_t a2_Hubble = a * a_Hubble; From 0d7be2944c2bfb857ab8338caf9a887cbd01c109 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Wed, 30 Jul 2025 17:53:02 -0400 Subject: [PATCH 27/39] Reorganized the viscosity weighting schemes and made new defaults. --- src/hydro/MAGMA2/hydro_iact.h | 91 ++++++++++++++++++++--------- src/hydro/MAGMA2/hydro_parameters.h | 38 ++++++++---- 2 files changed, 91 insertions(+), 38 deletions(-) diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index e74ffddbf0..9107c8419a 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -529,7 +529,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_Hubble); dv_dot_dr_Hubble += a2_Hubble * r2; -#ifdef hydro_props_use_asymmetric_viscosity_mu +#ifdef hydro_props_use_viscosity_weighting +#if (hydro_props_viscosity_weighting_type == 0) const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; const hydro_real_t eta_i[3] = {dx_ij[0] * hi_inv, @@ -596,30 +597,47 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t mu_j = conv * dv_dot_eta_j_Hubble / (eta_j2 + const_viscosity_epsilon2); - const hydro_real_t Q_i_alpha = + const hydro_real_t q_i_alpha = -const_viscosity_alpha * pi->force.soundspeed * mu_i; - const hydro_real_t Q_i_beta = const_viscosity_beta * mu_i * mu_i; - const hydro_real_t Q_i = rhoi * (Q_i_alpha + Q_i_beta); + const hydro_real_t q_i_beta = const_viscosity_beta * mu_i * mu_i; + 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_j; - const hydro_real_t Q_j_beta = const_viscosity_beta * mu_j * mu_j; - const hydro_real_t Q_j = rhoj * (Q_j_alpha + Q_j_beta); + const hydro_real_t q_j_beta = const_viscosity_beta * mu_j * mu_j; + const hydro_real_t Q_j = rhoj * (q_j_alpha + q_j_beta); /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; -#else +#elif (hydro_props_viscosity_weighting_type == 1) const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; 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); + 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); /* Add viscosity to the pressure */ visc_acc_term = Q_ij * rhoij_inv; +#elif (hydro_props_viscosity_weighting_type == 2) + const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; + const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; + 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); + + /* Add viscosity to the pressure */ + visc_acc_term = q_ij; +#else + error("Unknown compiled hydro_props_use_viscosity_weighting value: %d\n" + "Valid values are 0, 1, or 2.\n", + (int)hydro_props_use_viscosity_weighting); +#endif #endif /* Split heating between the two particles */ @@ -704,7 +722,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* New signal velocity */ -#ifdef hydro_props_use_asymmetric_viscosity_mu +#ifdef hydro_props_use_viscosity_weighting +#if (hydro_props_viscosity_weighting_type == 0) const hydro_real_t v_sig_visc_i = signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); const hydro_real_t v_sig_visc_j = @@ -713,6 +732,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( #else const hydro_real_t v_sig_visc = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); +#endif #endif const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); @@ -969,7 +989,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_Hubble); dv_dot_dr_Hubble += a2_Hubble * r2; -#ifdef hydro_props_use_asymmetric_viscosity_mu +#ifdef hydro_props_use_viscosity_weighting +#if (hydro_props_viscosity_weighting_type == 0) const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; const hydro_real_t eta_i[3] = {dx_ij[0] * hi_inv, @@ -978,7 +999,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t eta_j[3] = {dx_ji[0] * hj_inv, dx_ji[1] * hj_inv, dx_ji[2] * hj_inv}; - hydro_real_t eta_i2 = 0.; hydro_vec3_vec3_dot(eta_i, eta_i, &eta_i2); @@ -1001,7 +1021,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const char conv_i = (dv_dot_eta_i_Hubble < 0.) ? 1 : 0; const char conv_j = (dv_dot_eta_j_Hubble < 0.) ? 1 : 0; - /* Is the flow converging? If yes, multiply by fac_mu. If no, zero. */ + /* Is the flow converging? If so, multiply mu by fac_mu. If not, zero. */ const hydro_real_t conv = (conv_i && conv_j) ? fac_mu : 0.; #ifdef MAGMA2_DEBUG_CHECKS @@ -1037,30 +1057,47 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t mu_j = conv * dv_dot_eta_j_Hubble / (eta_j2 + const_viscosity_epsilon2); - const hydro_real_t Q_i_alpha = + const hydro_real_t q_i_alpha = -const_viscosity_alpha * pi->force.soundspeed * mu_i; - const hydro_real_t Q_i_beta = const_viscosity_beta * mu_i * mu_i; - const hydro_real_t Q_i = rhoi * (Q_i_alpha + Q_i_beta); + const hydro_real_t q_i_beta = const_viscosity_beta * mu_i * mu_i; + 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_j; - const hydro_real_t Q_j_beta = const_viscosity_beta * mu_j * mu_j; - const hydro_real_t Q_j = rhoj * (Q_j_alpha + Q_j_beta); + const hydro_real_t q_j_beta = const_viscosity_beta * mu_j * mu_j; + const hydro_real_t Q_j = rhoj * (q_j_alpha + q_j_beta); /* Add viscosity to the pressure */ visc_acc_term = (Q_i + Q_j) * rhoij_inv; -#else +#elif (hydro_props_viscosity_weighting_type == 1) const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; 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); + 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); /* Add viscosity to the pressure */ visc_acc_term = Q_ij * rhoij_inv; +#elif (hydro_props_viscosity_weighting_type == 2) + const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; + const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; + 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); + + /* Add viscosity to the pressure */ + visc_acc_term = q_ij; +#else + error("Unknown compiled hydro_props_use_viscosity_weighting value: %d\n" + "Valid values are 0, 1, or 2.\n", + (int)hydro_props_use_viscosity_weighting); +#endif #endif visc_du_term = 0.5 * visc_acc_term; @@ -1142,7 +1179,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* New signal velocity */ -#ifdef hydro_props_use_asymmetric_viscosity_mu +#ifdef hydro_props_use_viscosity_weighting +#if (hydro_props_viscosity_weighting_type == 0) const hydro_real_t v_sig_visc_i = signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); const hydro_real_t v_sig_visc_j = @@ -1151,6 +1189,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( #else const hydro_real_t v_sig_visc = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); +#endif #endif const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 7a15e76d68..7957855b4f 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -50,7 +50,7 @@ /*! Alpha viscosity, usually =1.0. For lower neighbor number, should be higher */ -#define const_viscosity_alpha 1.0 +#define const_viscosity_alpha 1.5 /*! Artificial conductivity alpha */ #define const_conductivity_alpha 0.05 @@ -64,29 +64,32 @@ #define const_kernel_target_neighbours 57.0 #endif -/*! Use the alternative viscosity weighting (each particle has mu_i and mu_j) */ -// #define hydro_props_use_asymmetric_viscosity_mu -/*! Use the second-order velocities in v_ij * G_ij */ -//#define hydro_props_use_second_order_velocities_in_divergence - -/*! Use double precision for all matrix/vector operations */ -//#define hydro_props_use_double_precision +/* ---------- These parameters should not be changed ---------- */ -/* ---------- These parameters should not be changed ---------- */ +/* 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 1 +/*! Use double precision for all matrix/vector operations */ +//#define hydro_props_use_double_precision -/*! Consider matrix inversion to be ill-conditioned above this limit */ #ifdef hydro_props_use_double_precision +/*! Consider matrix inversion to be ill-conditioned above this limit */ #define const_condition_number_upper_limit 99999. +/*! 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 999. -#endif - /*! 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) @@ -101,6 +104,17 @@ * Beta is defined as in e.g. Price (2010) Eqn (103) */ #define const_viscosity_beta (2.0 * const_viscosity_alpha) +/*! Use the second-order velocities in v_ij * G_ij (this doesn't work) */ +//#define hydro_props_use_second_order_velocities_in_divergence + +#ifdef hydro_props_viscosity_weighting_type +#ifndef hydro_props_use_viscosity_weighting +#define hydro_props_use_viscosity_weighting +#endif +#else +#define hydro_props_use_viscosity_weighting +#define hydro_props_viscosity_weighting_type 2 +#endif /* ---------- Structures for below ---------- */ From ef1a126757acd7f3927ba4fe456f05b17358042c Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Thu, 31 Jul 2025 09:25:09 -0400 Subject: [PATCH 28/39] Testing whether dh/dt calculation is causing energy non-conservation. --- src/hydro/MAGMA2/hydro_iact.h | 21 +++++++++++++++++++++ src/hydro/MAGMA2/hydro_parameters.h | 3 +++ 2 files changed, 24 insertions(+) diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 9107c8419a..e9e0c3daa0 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -714,9 +714,21 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ /* Get the time derivative for h. */ +#ifdef hydro_props_use_second_order_velocities_in_dhdt pi->force.h_dt -= mj * dv_dot_G_ij; pj->force.h_dt -= mi * dv_dot_G_ij; +#else + 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; + + /* Remove Hubble flow */ + const hydro_real_t dv_dot_dr = dv_dot_dr_Hubble - a2_Hubble * r2; + pi->force.h_dt -= mj * dv_dot_dr * r_inv * wi_dr; + pj->force.h_dt -= mi * dv_dot_dr * r_inv * wj_dr; +#endif /* Timestepping */ @@ -1171,9 +1183,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( visc_du_term *= dv_dot_G_ij; cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ +#ifdef hydro_props_use_second_order_velocities_in_dhdt /* Get the time derivative for h. */ pi->force.h_dt -= mj * dv_dot_G_ij; +#else + const hydro_real_t hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ + const hydro_real_t wi_dr = hi_inv_dim_plus_one * wi_dx; + + /* Remove Hubble flow */ + const hydro_real_t dv_dot_dr = dv_dot_dr_Hubble - a2_Hubble * r2; + pi->force.h_dt -= mj * dv_dot_dr * r_inv * wi_dr; +#endif /* Timestepping */ diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 7957855b4f..09861df6e2 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -107,6 +107,9 @@ /*! Use the second-order velocities in v_ij * G_ij (this doesn't work) */ //#define hydro_props_use_second_order_velocities_in_divergence +/*! Use v_ij * G_ij in dh/dt evolution */ +//#define hydro_props_use_second_order_velocities_in_dhdt + #ifdef hydro_props_viscosity_weighting_type #ifndef hydro_props_use_viscosity_weighting #define hydro_props_use_viscosity_weighting From 6f7976ef062933bc2c717babce7df71975beb38a Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Thu, 31 Jul 2025 17:22:51 -0400 Subject: [PATCH 29/39] Added the Omega_i terms that correct for smoothing length changes in the kernel gradients. Also rewrote dh/dt calculation to match what Swift actually does -- which is a number density constraint on neighbour number, not a mass density constraint! There are more correction terms that need to be added, but those require an additional neighbor loop. --- src/hydro/MAGMA2/hydro.h | 90 +++++++++++++- src/hydro/MAGMA2/hydro_iact.h | 186 ++++++++++++++++++++-------- src/hydro/MAGMA2/hydro_parameters.h | 2 +- src/hydro/MAGMA2/hydro_part.h | 15 +++ 4 files changed, 233 insertions(+), 60 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 8ad2eb00dc..6676abc5fd 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -502,8 +502,12 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->density.wcount_dh = 0.f; p->rho = 0.f; p->density.rho_dh = 0.f; +#ifdef MAGMA2_DEBUG_CHECKS p->num_ngb = 0; + p->gradients.adiabatic_f_numerator = 0.; +#endif + p->gradients.wcount = 0.; p->gradients.u_well_conditioned = 1; p->gradients.D_well_conditioned = 1; /* These must be zeroed before the density loop */ @@ -686,7 +690,6 @@ __attribute__((always_inline)) INLINE static void hydro_vec3_vec3_outer( * @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. - * @param num_ngb The number of neighbours in the scheme. */ __attribute__((always_inline)) INLINE static hydro_real_t hydro_van_leer_phi(const hydro_real_t A_ij, @@ -784,7 +787,6 @@ hydro_real_t hydro_vector_van_leer_A(const hydro_real_t (*restrict grad_i)[3], * @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. - * @param num_ngb The number of neighbours in the scheme. */ __attribute__((always_inline)) INLINE static hydro_real_t hydro_scalar_van_leer_phi(const hydro_real_t *restrict grad_i, @@ -808,7 +810,6 @@ hydro_real_t hydro_scalar_van_leer_phi(const hydro_real_t *restrict grad_i, * @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. - * @param num_ngb The number of neighbours in the scheme. */ __attribute__((always_inline)) INLINE static hydro_real_t hydro_vector_van_leer_phi(const hydro_real_t (*restrict grad_i)[3], @@ -931,6 +932,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( 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; @@ -1091,6 +1095,55 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( /* Update variables. */ p->force.pressure = pressure_including_floor; p->force.soundspeed = soundspeed; + + + /* ------ Compute the kernel correction for SPH gradients ------ */ + + + /* Note: This is only for SPH gradients, the MAGMA2 gradients are already + * corrected. */ + + /* Compute the "grad h" term - Note here that we have \tilde{x} + * as 1 as we use the local number density to find neighbours. This + * introduces a j-component that is considered in the force loop, + * meaning that this cached grad_h_term gives: + * + * f_ij = 1.f - grad_h_term_i / m_j */ + const float common_factor = p->h * hydro_dimension_inv / p->density.wcount; + float grad_h_term; + float grad_W_term = 0.f; + + /* Ignore changing-kernel effects when h ~= h_max */ + if (p->h > 0.9999f * hydro_props->h_max) { + grad_h_term = 0.f; + warning("h ~ h_max for particle with ID %lld (h: %g)", p->id, p->h); + } + else { + 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_h_term = 0.f; + grad_W_term = 0.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); + } + else { + grad_h_term = common_factor * p->density.rho_dh / (1.f + grad_W_term); + } + } + + /* Update variables. */ + p->force.f = grad_h_term; + p->gradients.grad_W_correction = 1.f + grad_W_term; + } /** @@ -1148,6 +1201,10 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ /* Normalize correctly with the smoothing length */ +#ifdef MAGMA2_DEBUG_CHECKS + p->gradients.adiabatic_f_numerator *= kernel_gamma_inv * h_inv; +#endif + p->gradients.u[0] *= h_inv_dim; p->gradients.u[1] *= h_inv_dim; p->gradients.u[2] *= h_inv_dim; @@ -1309,7 +1366,9 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( p->density.rho_dh = 0.f; p->density.wcount_dh = 0.f; +#ifdef MAGMA2_DEBUG_CHECKS p->num_ngb = 0; +#endif p->gradients.C_well_conditioned = 0; p->gradients.D_well_conditioned = 0; p->gradients.u_well_conditioned = 0; @@ -1363,6 +1422,11 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( p->h_min = p->h; p->v_sig_max = p->force.soundspeed; p->dt_min = p->h_min / p->v_sig_max; + +#ifdef MAGMA2_DEBUG_CHECKS + /* Kernel correction factor */ + p->gradients.adiabatic_f = 0.; +#endif } /** @@ -1510,11 +1574,26 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( __attribute__((always_inline)) INLINE static void hydro_end_force( struct part *restrict p, const struct cosmology *cosmo) { - const float rho_inv = 1.f / hydro_get_comoving_density(p); - p->force.h_dt *= p->h * rho_inv * hydro_dimension_inv; + const hydro_real_t wcount_inv = 1. / p->gradients.wcount; + const hydro_real_t Omega_inv = + (p->gradients.grad_W_correction > 0.) ? + 1. / p->gradients.grad_W_correction + : 1.; + + p->force.h_dt *= Omega_inv * p->h * hydro_dimension_inv * wcount_inv; /* dt_min is in physical units, and requires the kernel_gamma factor for h */ p->dt_min *= kernel_gamma * cosmo->a / cosmo->a_factor_sound_speed; + +#ifdef MAGMA2_DEBUG_CHECKS + /* 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 */ + + /* Kernel correction factors */ + p->gradients.adiabatic_f *= h_inv_dim; +#endif } /** @@ -1630,6 +1709,7 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( p->gradients.C_well_conditioned = 1; p->gradients.D_well_conditioned = 1; p->gradients.u_well_conditioned = 1; + p->gradients.grad_W_correction = 1.; #ifdef MAGMA2_DEBUG_CHECKS p->debug.C_ill_conditioned_count = 0; p->debug.D_ill_conditioned_count = 0; diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index e9e0c3daa0..df8fdb92f0 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -126,10 +126,16 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( } } +#ifdef MAGMA2_DEBUG_CHECKS /* Number of neighbors */ pi->num_ngb++; pj->num_ngb++; + /* Needed for the adiabatic kernel correction factor */ + pi->gradients.adiabatic_f_numerator += mj * r * wi_dx; + pj->gradients.adiabatic_f_numerator += mi * r * wj_dx; +#endif + } /** @@ -198,8 +204,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( } } +#ifdef MAGMA2_DEBUG_CHECKS /* Neighbour number */ pi->num_ngb++; + + /* Needed for the adiabatic kernel correction factor */ + pi->gradients.adiabatic_f_numerator += mj * r * wi_dx; +#endif + } /** @@ -267,9 +279,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; + /* Variable smoothing length term for SPH gradients in _aux */ + const hydro_real_t f_ij = 1. - pi->force.f / mj; + const hydro_real_t f_ji = 1. - pj->force.f / mi; + for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { - const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[k]; + const hydro_real_t du_k = + f_ij * pi->gradients.u_aux[k] - f_ji * pj->gradients.u_aux[k]; pi->gradients.u_hessian[k][i] += du_k * dx[i] * faci; pj->gradients.u_hessian[k][i] += du_k * dx[i] * facj; @@ -283,8 +300,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( 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]; + const hydro_real_t dv_grad_ki = + f_ij * pi->gradients.velocity_tensor_aux[k][i] - + f_ji * pj->gradients.velocity_tensor_aux[k][i]; /* Equation 19 indices: * Index i: velocity direction @@ -326,6 +344,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float H) { /* 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 rhoj = hydro_get_comoving_density(pj); const hydro_real_t rhoj_inv = 1. / rhoj; @@ -352,9 +371,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; + /* Variable smoothing length term for SPH gradients in _aux */ + const hydro_real_t f_ij = 1. - pi->force.f / mj; + const hydro_real_t f_ji = 1. - pj->force.f / mi; + for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { - const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[i]; + const hydro_real_t du_k = + f_ij * pi->gradients.u_aux[k] - f_ji * pj->gradients.u_aux[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 */ @@ -365,8 +389,9 @@ __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] - - pj->gradients.velocity_tensor_aux[k][i]; + const hydro_real_t dv_grad_ki = + f_ij * pi->gradients.velocity_tensor_aux[k][i] - + f_ji * pj->gradients.velocity_tensor_aux[k][i]; /* Equation 19 indices: * Index i: velocity direction @@ -436,6 +461,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 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], @@ -447,6 +478,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( hydro_real_t G_ij[3] = {0., 0., 0.}; /* These are set whether or not we fall back onto SPH gradients */ + /* Variable smoothing length term */ + const hydro_real_t f_ij = 1. - pi->force.f / mj; + const hydro_real_t f_ji = 1. - pj->force.f / mi; + hydro_real_t visc_acc_term = 0.; hydro_real_t sph_acc_term = (pressurei + pressurej) * rhoij_inv; @@ -673,9 +708,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); const hydro_real_t rho_ij_inv = 1. / rho_ij; - - /* Signal velocity is the velocity difference projected with Hubble flow */ - const hydro_real_t v_sig_speed = fac_mu * fabs(dv_dot_dr_Hubble) * r_inv; + const hydro_real_t dv_Hubble[3] = {dv[0] + a_Hubble * dx[0], + dv[1] + a_Hubble * dx[1], + dv[2] + a_Hubble * dx[2]}; + hydro_real_t dv_Hubble_norm = 0.; + hydro_vec3_vec3_dot(dv_Hubble, dv_Hubble, &dv_Hubble_norm); + dv_Hubble_norm = sqrt(dv_Hubble_norm); + + /* Signal velocity is the sum of pressure and speed contributions */ + const hydro_real_t v_sig_speed = fac_mu * dv_Hubble_norm; const hydro_real_t v_sig_pressure = sqrt(fabs(pressurei - pressurej) * rho_ij_inv); const hydro_real_t v_sig_cond = v_sig_speed + v_sig_pressure; @@ -683,8 +724,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; /* Add conductivity to the specific energy */ - cond_du_term = - const_conductivity_alpha * fabs(v_sig_cond) * du_ij * rho_ij_inv; + cond_du_term = const_conductivity_alpha * v_sig_cond * du_ij * rho_ij_inv; /* Finalize everything with the correct normalizations. */ @@ -692,9 +732,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ - 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]); + G_ij[0] = 0.5 * (f_ij * G_i[0] + f_ji * G_j[0]); + G_ij[1] = 0.5 * (f_ij * G_i[1] + f_ji * G_j[1]); + G_ij[2] = 0.5 * (f_ij * G_i[2] + f_ji * G_j[2]); hydro_real_t G_ij_norm = 0.; hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_norm); @@ -705,7 +745,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( #ifdef hydro_props_use_second_order_velocities_in_divergence hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); #else - hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); + hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); #endif sph_du_term_i *= dv_dot_G_ij; @@ -713,23 +753,44 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( visc_du_term *= dv_dot_G_ij; cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ + /* Get the time derivative for h. */ -#ifdef hydro_props_use_second_order_velocities_in_dhdt - pi->force.h_dt -= mj * dv_dot_G_ij; - pj->force.h_dt -= mi * dv_dot_G_ij; -#else - 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; + /* Remove Hubble flow */ const hydro_real_t dv_dot_dr = dv_dot_dr_Hubble - a2_Hubble * r2; - pi->force.h_dt -= mj * dv_dot_dr * r_inv * wi_dr; - pj->force.h_dt -= mi * dv_dot_dr * r_inv * wj_dr; + pi->force.h_dt -= dv_dot_dr * r_inv * wi_dr; + pj->force.h_dt -= dv_dot_dr * r_inv * wj_dr; + +#ifdef MAGMA2_DEBUG_CHECKS + /* Kernel correction factors */ + hydro_real_t dxdx[3][3] = {0}; + hydro_vec3_vec3_outer(dx_ij, dx_ij, dxdx); + + hydro_real_t adiabatic_f_denominator_j = 0.; + hydro_mat3x3_mat3x3_dot(pi->gradients.correction_matrix, + dxdx, &adiabatic_f_denominator_j); + adiabatic_f_denominator_j *= rhoj; + const hydro_real_t adiabatic_f_i = + (adiabatic_f_denominator_j != 0.) ? + pj->gradients.adiabatic_f_numerator / adiabatic_f_denominator_j + : 0.; + + hydro_real_t adiabatic_f_denominator_i = 0.; + hydro_mat3x3_mat3x3_dot(pj->gradients.correction_matrix, + dxdx, &adiabatic_f_denominator_i); + adiabatic_f_denominator_i *= rhoi * mj * rhoj_inv * wi; + const hydro_real_t adiabatic_f_j = + (adiabatic_f_denominator_i != 0.) ? + pi->gradients.adiabatic_f_numerator / adiabatic_f_denominator_i + : 0.; + + pi->gradients.adiabatic_f -= mj * rhoj_inv * adiabatic_f_i * wi; + pj->gradients.adiabatic_f -= mi * rhoi_inv * adiabatic_f_j * wj; #endif + /* Timestepping */ @@ -816,13 +877,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( visc_acc_term = visc; visc_du_term = 0.5 * visc_acc_term; - 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; - /* Variable smoothing length term */ - const hydro_real_t kernel_gradient = 0.5 * (wi_dr + wj_dr) * r_inv; + const hydro_real_t kernel_gradient = + 0.5 * (f_ij * wi_dr + f_ji * wj_dr) * r_inv; visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; @@ -832,8 +889,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( visc_du_term *= dvdr_Hubble * kernel_gradient; /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dvdr * r_inv * wi_dr; - pj->force.h_dt -= mi * dvdr * r_inv * wj_dr; + pi->force.h_dt -= dvdr * r_inv * wi_dr; + pj->force.h_dt -= dvdr * r_inv * wj_dr; } /* Assemble the acceleration */ @@ -885,6 +942,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t r_inv = r ? 1.0 / r : 0.0; /* Recover some data */ + const hydro_real_t mi = pi->mass; const hydro_real_t mj = pj->mass; const hydro_real_t rhoi = pi->rho; @@ -910,6 +968,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( 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 wi_dr = hi_inv_dim_plus_one * wi_dx; + /* Peculiar velocity difference vector */ const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], @@ -921,6 +983,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( hydro_real_t G_ij[3] = {0., 0., 0.}; /* These are set whether or not we fall back onto SPH gradients */ + /* Variable smoothing length term */ + const hydro_real_t f_ij = 1. - pi->force.f / mj; + const hydro_real_t f_ji = 1. - pj->force.f / mi; 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; @@ -1144,9 +1209,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t rho_ij = 0.5 * (rhoi + rhoj); const hydro_real_t rho_ij_inv = 1. / rho_ij; - - /* Signal velocity is the velocity difference projected with Hubble flow */ - const hydro_real_t v_sig_speed = fac_mu * fabs(dv_dot_dr_Hubble) * r_inv; + const hydro_real_t dv_Hubble[3] = {dv[0] + a_Hubble * dx[0], + dv[1] + a_Hubble * dx[1], + dv[2] + a_Hubble * dx[2]}; + hydro_real_t dv_Hubble_norm = 0.; + hydro_vec3_vec3_dot(dv_Hubble, dv_Hubble, &dv_Hubble_norm); + dv_Hubble_norm = sqrt(dv_Hubble_norm); + + /* Signal velocity is the sum of pressure and speed contributions */ + const hydro_real_t v_sig_speed = fac_mu * dv_Hubble_norm; const hydro_real_t v_sig_pressure = sqrt(fabs(pressurei - pressurej) * rho_ij_inv); const hydro_real_t v_sig_cond = v_sig_speed + v_sig_pressure; @@ -1154,8 +1225,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; /* Add conductivity to the specific energy */ - cond_du_term = - const_conductivity_alpha * fabs(v_sig_cond) * du_ij * rho_ij_inv; + cond_du_term = const_conductivity_alpha * v_sig_cond * du_ij * rho_ij_inv; /* Finalize the viscosity and conductivity with correct normalizations. */ @@ -1163,9 +1233,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ - 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]); + G_ij[0] = 0.5 * (f_ij * G_i[0] + f_ji * G_j[0]); + G_ij[1] = 0.5 * (f_ij * G_i[1] + f_ji * G_j[1]); + G_ij[2] = 0.5 * (f_ij * G_i[2] + f_ji * G_j[2]); hydro_real_t G_ij_norm = 0.; hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_norm); @@ -1176,24 +1246,33 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( #ifdef hydro_props_use_second_order_velocities_in_divergence hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); #else - hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); + hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); #endif sph_du_term_i *= dv_dot_G_ij; visc_du_term *= dv_dot_G_ij; cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ -#ifdef hydro_props_use_second_order_velocities_in_dhdt - /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dv_dot_G_ij; -#else - const hydro_real_t hi_inv_dim_plus_one = hi_inv * hi_inv_dim; /* 1/h^(d+1) */ - const hydro_real_t wi_dr = hi_inv_dim_plus_one * wi_dx; - /* Remove Hubble flow */ const hydro_real_t dv_dot_dr = dv_dot_dr_Hubble - a2_Hubble * r2; - pi->force.h_dt -= mj * dv_dot_dr * r_inv * wi_dr; + pi->force.h_dt -= dv_dot_dr * r_inv * wi_dr; + +#ifdef MAGMA2_DEBUG_CHECKS + /* Kernel correction factors */ + hydro_real_t dxdx[3][3] = {0}; + hydro_vec3_vec3_outer(dx_ij, dx_ij, dxdx); + + hydro_real_t adiabatic_f_denominator_j = 0.; + hydro_mat3x3_mat3x3_dot(pi->gradients.correction_matrix, + dxdx, &adiabatic_f_denominator_j); + adiabatic_f_denominator_j *= rhoj; + const hydro_real_t adiabatic_f_i = + (adiabatic_f_denominator_j != 0.) ? + pj->gradients.adiabatic_f_numerator / adiabatic_f_denominator_j + : 0.; + + pi->gradients.adiabatic_f -= mj * rhoj_inv * adiabatic_f_i * wi; #endif /* Timestepping */ @@ -1273,13 +1352,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( visc_acc_term = visc; visc_du_term = 0.5 * visc_acc_term; - 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; /* Variable smoothing length term */ - const hydro_real_t kernel_gradient = 0.5 * (wi_dr + wj_dr) * r_inv; + const hydro_real_t kernel_gradient = + 0.5 * (f_ij * wi_dr + f_ji * wj_dr) * r_inv; visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; @@ -1287,7 +1365,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( visc_du_term *= dvdr_Hubble * kernel_gradient; /* Get the time derivative for h. */ - pi->force.h_dt -= mj * dvdr * r_inv * wi_dr; + pi->force.h_dt -= dvdr * r_inv * wi_dr; } /* Assemble the acceleration */ diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 09861df6e2..9da63a58ad 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -50,7 +50,7 @@ /*! Alpha viscosity, usually =1.0. For lower neighbor number, should be higher */ -#define const_viscosity_alpha 1.5 +#define const_viscosity_alpha 2.0 /*! Artificial conductivity alpha */ #define const_conductivity_alpha 0.05 diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 639337fc4c..257d5189f5 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -129,8 +129,10 @@ struct part { /*! Particle density. */ float rho; +#ifdef MAGMA2_DEBUG_CHECKS /*! Number of neighbors in the kernel */ int num_ngb; +#endif /*! Minimum smoothing length in the kernel */ float h_min; @@ -172,6 +174,19 @@ struct part { /* Store gradients in a separate struct */ struct { +#ifdef MAGMA2_DEBUG_CHECKS + /*! Adiabatic kernel correction factor numerator */ + hydro_real_t adiabatic_f_numerator; + + /*! Adiabatic kernel correction factor */ + hydro_real_t adiabatic_f; +#endif + + /*! Sum of the kernel weights */ + hydro_real_t wcount; + + /*! Correction matrix Omega for smoothing length variation */ + hydro_real_t grad_W_correction; /*! Correction matrix (C^ki in Rosswog 2020) */ hydro_real_t correction_matrix[3][3]; From 0d9183d6e3e74d6730d9e5c3f29a3d3e49474ad4 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Fri, 1 Aug 2025 11:05:41 -0400 Subject: [PATCH 30/39] The "grad-h" terms in MAGMA2 (and non-conservative SPH-like methods) enter into the average of the kernel gradients, and only really need the 1/Omega contribution. Using the symmetric version (like in SPHENIX) causes problems in extremely complicated flows like the Sedov blastwave. The "grad-h" terms are not actually needed in the MAGMA2 gradients, but it is required for the internal energy evolution equation because of the mismatch between the adiabatic term in the density and specific energy. --- src/hydro/MAGMA2/hydro.h | 57 +++++++++++-------- src/hydro/MAGMA2/hydro_iact.h | 85 ++++++++++++++--------------- src/hydro/MAGMA2/hydro_parameters.h | 8 +-- src/hydro/MAGMA2/hydro_part.h | 5 +- 4 files changed, 79 insertions(+), 76 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 6676abc5fd..0784274ff5 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -567,6 +567,23 @@ void hydro_vec3_vec3_dot(const hydro_real_t *restrict vec_a, *result = 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_a The first vector. + * @param vec_b The second vector. + * @param result The result of the norm. + */ +__attribute__((always_inline)) INLINE static +void hydro_vec3_vec3_norm(const hydro_real_t *restrict vec_a, + const hydro_real_t *restrict vec_b, + hydro_real_t *result) { + + *result = + sqrt(vec_a[0] * vec_a[0] + vec_a[1] * vec_a[1] + vec_a[2] * vec_a[2]); +} + /** * @brief The Frobenius inner product of two matrices. * @@ -1085,7 +1102,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct pressure_floor_props *pressure_floor) { - /* Compute the sound speed */ + /* 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); @@ -1100,22 +1117,12 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( /* ------ Compute the kernel correction for SPH gradients ------ */ - /* Note: This is only for SPH gradients, the MAGMA2 gradients are already - * corrected. */ - - /* Compute the "grad h" term - Note here that we have \tilde{x} - * as 1 as we use the local number density to find neighbours. This - * introduces a j-component that is considered in the force loop, - * meaning that this cached grad_h_term gives: - * - * f_ij = 1.f - grad_h_term_i / m_j */ + /* Compute the "grad h" term */ const float common_factor = p->h * hydro_dimension_inv / p->density.wcount; - float grad_h_term; float grad_W_term = 0.f; /* Ignore changing-kernel effects when h ~= h_max */ if (p->h > 0.9999f * hydro_props->h_max) { - grad_h_term = 0.f; warning("h ~ h_max for particle with ID %lld (h: %g)", p->id, p->h); } else { @@ -1128,21 +1135,16 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( 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_h_term = 0.f; grad_W_term = 0.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); } - else { - grad_h_term = common_factor * p->density.rho_dh / (1.f + grad_W_term); - } } /* Update variables. */ - p->force.f = grad_h_term; - p->gradients.grad_W_correction = 1.f + grad_W_term; + p->force.f = (grad_W_term != 0.f) ? 1. / (1. + (hydro_real_t)grad_W_term) : 0.; } @@ -1200,7 +1202,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( const float h_inv = 1.0f / h; /* 1/h */ const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ - /* Normalize correctly with the smoothing length */ + /* Normalize correctly with the smoothing length */ #ifdef MAGMA2_DEBUG_CHECKS p->gradients.adiabatic_f_numerator *= kernel_gamma_inv * h_inv; #endif @@ -1575,10 +1577,11 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( struct part *restrict p, const struct cosmology *cosmo) { const hydro_real_t wcount_inv = 1. / p->gradients.wcount; - const hydro_real_t Omega_inv = - (p->gradients.grad_W_correction > 0.) ? - 1. / p->gradients.grad_W_correction - : 1.; +#ifdef hydro_props_use_higher_order_gradients_in_dh_dt + const hydro_real_t Omega_inv = 1.; +#else + const hydro_real_t Omega_inv = p->force.f; +#endif p->force.h_dt *= Omega_inv * p->h * hydro_dimension_inv * wcount_inv; @@ -1586,6 +1589,13 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( p->dt_min *= kernel_gamma * cosmo->a / cosmo->a_factor_sound_speed; #ifdef MAGMA2_DEBUG_CHECKS + if (fabs(p->force.h_dt / p->h) > 1000.) { + warning("Large dh/dt! Hydro end force for particle with ID %lld (h: %g, " + "wcount: %g, h_dt: %g, dt_min: %g, v_sig_max: %g, Omega_inv: %g)", + p->id, p->h, p->gradients.wcount, p->force.h_dt, p->dt_min, + p->v_sig_max, Omega_inv); + } + /* Calculate smoothing length powers */ const float h = p->h; const float h_inv = 1.0f / h; /* 1/h */ @@ -1709,7 +1719,6 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( p->gradients.C_well_conditioned = 1; p->gradients.D_well_conditioned = 1; p->gradients.u_well_conditioned = 1; - p->gradients.grad_W_correction = 1.; #ifdef MAGMA2_DEBUG_CHECKS p->debug.C_ill_conditioned_count = 0; p->debug.D_ill_conditioned_count = 0; diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index df8fdb92f0..f7ea7cdea7 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -279,14 +279,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; - /* Variable smoothing length term for SPH gradients in _aux */ - const hydro_real_t f_ij = 1. - pi->force.f / mj; - const hydro_real_t f_ji = 1. - pj->force.f / mi; for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { - const hydro_real_t du_k = - f_ij * pi->gradients.u_aux[k] - f_ji * pj->gradients.u_aux[k]; + const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[k]; pi->gradients.u_hessian[k][i] += du_k * dx[i] * faci; pj->gradients.u_hessian[k][i] += du_k * dx[i] * facj; @@ -300,9 +296,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( 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 = - f_ij * pi->gradients.velocity_tensor_aux[k][i] - - f_ji * pj->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: * Index i: velocity direction @@ -344,7 +339,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float H) { /* 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 rhoj = hydro_get_comoving_density(pj); const hydro_real_t rhoj_inv = 1. / rhoj; @@ -371,14 +365,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( pi->v[1] - pj->v[1], pi->v[2] - pj->v[2]}; - /* Variable smoothing length term for SPH gradients in _aux */ - const hydro_real_t f_ij = 1. - pi->force.f / mj; - const hydro_real_t f_ji = 1. - pj->force.f / mi; for (int k = 0; k < 3; k++) { for (int i = 0; i < 3; i++) { - const hydro_real_t du_k = - f_ij * pi->gradients.u_aux[k] - f_ji * pj->gradients.u_aux[i]; + const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[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 */ @@ -389,9 +379,8 @@ __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 = - f_ij * pi->gradients.velocity_tensor_aux[k][i] - - f_ji * pj->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: * Index i: velocity direction @@ -461,6 +450,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); + /* Variable smoothing length term */ + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; + /* 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; @@ -478,13 +471,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( hydro_real_t G_ij[3] = {0., 0., 0.}; /* These are set whether or not we fall back onto SPH gradients */ - /* Variable smoothing length term */ - const hydro_real_t f_ij = 1. - pi->force.f / mj; - const hydro_real_t f_ji = 1. - pj->force.f / mi; - 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.; @@ -712,8 +700,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( dv[1] + a_Hubble * dx[1], dv[2] + a_Hubble * dx[2]}; hydro_real_t dv_Hubble_norm = 0.; - hydro_vec3_vec3_dot(dv_Hubble, dv_Hubble, &dv_Hubble_norm); - dv_Hubble_norm = sqrt(dv_Hubble_norm); + hydro_vec3_vec3_norm(dv_Hubble, dv_Hubble, &dv_Hubble_norm); /* Signal velocity is the sum of pressure and speed contributions */ const hydro_real_t v_sig_speed = fac_mu * dv_Hubble_norm; @@ -732,13 +719,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ - G_ij[0] = 0.5 * (f_ij * G_i[0] + f_ji * G_j[0]); - G_ij[1] = 0.5 * (f_ij * G_i[1] + f_ji * G_j[1]); - G_ij[2] = 0.5 * (f_ij * G_i[2] + f_ji * G_j[2]); + 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]); hydro_real_t G_ij_norm = 0.; - hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_norm); - G_ij_norm = sqrt(G_ij_norm); + hydro_vec3_vec3_norm(G_ij, G_ij, &G_ij_norm); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ hydro_real_t dv_dot_G_ij = 0.; @@ -755,13 +741,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the time derivative for h. */ + - +#ifdef hydro_props_use_higher_order_gradients_in_dh_dt + pi->force.h_dt -= dv_dot_G_ij; + pj->force.h_dt -= dv_dot_G_ij; +#else /* Remove Hubble flow */ const hydro_real_t dv_dot_dr = dv_dot_dr_Hubble - a2_Hubble * r2; pi->force.h_dt -= dv_dot_dr * r_inv * wi_dr; pj->force.h_dt -= dv_dot_dr * r_inv * wj_dr; +#endif #ifdef MAGMA2_DEBUG_CHECKS /* Kernel correction factors */ @@ -879,7 +870,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Variable smoothing length term */ const hydro_real_t kernel_gradient = - 0.5 * (f_ij * wi_dr + f_ji * wj_dr) * r_inv; + 0.5 * (f_i * wi_dr + f_j * wj_dr) * r_inv; visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; @@ -942,7 +933,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t r_inv = r ? 1.0 / r : 0.0; /* Recover some data */ - const hydro_real_t mi = pi->mass; const hydro_real_t mj = pj->mass; const hydro_real_t rhoi = pi->rho; @@ -968,10 +958,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); + /* Variable smoothing length term */ + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; + /* 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 wi_dr = hi_inv_dim_plus_one * wi_dx; - + /* Peculiar velocity difference vector */ const hydro_real_t dv[3] = {pi->v[0] - pj->v[0], pi->v[1] - pj->v[1], @@ -983,9 +977,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( hydro_real_t G_ij[3] = {0., 0., 0.}; /* These are set whether or not we fall back onto SPH gradients */ - /* Variable smoothing length term */ - const hydro_real_t f_ij = 1. - pi->force.f / mj; - const hydro_real_t f_ji = 1. - pj->force.f / mi; 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; @@ -1213,8 +1204,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( dv[1] + a_Hubble * dx[1], dv[2] + a_Hubble * dx[2]}; hydro_real_t dv_Hubble_norm = 0.; - hydro_vec3_vec3_dot(dv_Hubble, dv_Hubble, &dv_Hubble_norm); - dv_Hubble_norm = sqrt(dv_Hubble_norm); + hydro_vec3_vec3_norm(dv_Hubble, dv_Hubble, &dv_Hubble_norm); /* Signal velocity is the sum of pressure and speed contributions */ const hydro_real_t v_sig_speed = fac_mu * dv_Hubble_norm; @@ -1233,13 +1223,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ - G_ij[0] = 0.5 * (f_ij * G_i[0] + f_ji * G_j[0]); - G_ij[1] = 0.5 * (f_ij * G_i[1] + f_ji * G_j[1]); - G_ij[2] = 0.5 * (f_ij * G_i[2] + f_ji * G_j[2]); + 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]); hydro_real_t G_ij_norm = 0.; - hydro_vec3_vec3_dot(G_ij, G_ij, &G_ij_norm); - G_ij_norm = sqrt(G_ij_norm); + hydro_vec3_vec3_norm(G_ij, G_ij, &G_ij_norm); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ hydro_real_t dv_dot_G_ij = 0.; @@ -1253,10 +1242,18 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( visc_du_term *= dv_dot_G_ij; cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ + + /* Get the time derivative for h. */ + + +#ifdef hydro_props_use_higher_order_gradients_in_dh_dt + pi->force.h_dt -= dv_dot_G_ij; +#else /* Remove Hubble flow */ const hydro_real_t dv_dot_dr = dv_dot_dr_Hubble - a2_Hubble * r2; pi->force.h_dt -= dv_dot_dr * r_inv * wi_dr; +#endif #ifdef MAGMA2_DEBUG_CHECKS /* Kernel correction factors */ @@ -1357,7 +1354,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Variable smoothing length term */ const hydro_real_t kernel_gradient = - 0.5 * (f_ij * wi_dr + f_ji * wj_dr) * r_inv; + 0.5 * (f_i * wi_dr + f_j * wj_dr) * r_inv; visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 9da63a58ad..ee4ccb517c 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -49,11 +49,11 @@ /* ---------- Viscosity & Conductivitiy parameters ---------- */ -/*! Alpha viscosity, usually =1.0. For lower neighbor number, should be higher */ +/*! Alpha viscosity, usually =1.0. For lower N_ngb, should be higher */ #define const_viscosity_alpha 2.0 -/*! Artificial conductivity alpha */ -#define const_conductivity_alpha 0.05 +/*! Alpha conductivity, usually =0.05. At lower N_ngb, should be higher */ +#define const_conductivity_alpha 0.1 /*! Desired number of neighbours -- CRITICAL that this matches hydro props */ #if defined(HYDRO_DIMENSION_1D) @@ -108,7 +108,7 @@ //#define hydro_props_use_second_order_velocities_in_divergence /*! Use v_ij * G_ij in dh/dt evolution */ -//#define hydro_props_use_second_order_velocities_in_dhdt +#define hydro_props_use_higher_order_gradients_in_dh_dt #ifdef hydro_props_viscosity_weighting_type #ifndef hydro_props_use_viscosity_weighting diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 257d5189f5..ead43e5ea8 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -143,7 +143,7 @@ struct part { /*! Minimum time-step amongst neighbours */ float dt_min; -#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]; @@ -184,9 +184,6 @@ struct part { /*! Sum of the kernel weights */ hydro_real_t wcount; - - /*! Correction matrix Omega for smoothing length variation */ - hydro_real_t grad_W_correction; /*! Correction matrix (C^ki in Rosswog 2020) */ hydro_real_t correction_matrix[3][3]; From 89a5073e4ca48b195ffebd66c6db1fb80310a272 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Fri, 1 Aug 2025 13:46:06 -0400 Subject: [PATCH 31/39] Added in hydro_props_use_adiabatic_correction which allows the use of a kernel correction term. However, it assumes that the kernel is isotropic which is most likely wrong (really it assumes that the matrix C_i^kd in Rosswog 2020 is isotropic). If we wanted the true correction factor, we would need another loop over neighbors to find the kernel anisotropies. Added a new function to weight the kernel gradient. --- src/hydro/MAGMA2/hydro.h | 144 ++++++++++++++++++++++++---- src/hydro/MAGMA2/hydro_iact.h | 128 +++++++++---------------- src/hydro/MAGMA2/hydro_parameters.h | 25 +++++ src/hydro/MAGMA2/hydro_part.h | 6 +- 4 files changed, 202 insertions(+), 101 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 0784274ff5..6e7f29d885 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -504,6 +504,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->density.rho_dh = 0.f; #ifdef MAGMA2_DEBUG_CHECKS p->num_ngb = 0; +#endif + +#ifdef hydro_props_use_adiabatic_correction p->gradients.adiabatic_f_numerator = 0.; #endif @@ -916,8 +919,104 @@ hydro_vector_second_order_reconstruction(const hydro_real_t phi, f_reconstructed[2] = f[2] + phi * (df_dx_dot_r[2] + 0.5 * df_dx_dx_dot_r2[2]); } +__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 +} + /** - e @brief Finishes the density calculation. + * @brief Finishes the density calculation. * * Multiplies the density and number of neighbours by the appropiate constants * and add the self-contribution term. @@ -1102,7 +1201,12 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( const struct cosmology *cosmo, const struct hydro_props *hydro_props, const struct pressure_floor_props *pressure_floor) { - /* Compute the sound speed */ +#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); @@ -1202,11 +1306,6 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( const float h_inv = 1.0f / h; /* 1/h */ const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ - /* Normalize correctly with the smoothing length */ -#ifdef MAGMA2_DEBUG_CHECKS - p->gradients.adiabatic_f_numerator *= kernel_gamma_inv * h_inv; -#endif - p->gradients.u[0] *= h_inv_dim; p->gradients.u[1] *= h_inv_dim; p->gradients.u[2] *= h_inv_dim; @@ -1425,9 +1524,28 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( p->v_sig_max = p->force.soundspeed; p->dt_min = p->h_min / p->v_sig_max; +#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 - /* Kernel correction factor */ - p->gradients.adiabatic_f = 0.; + 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 } @@ -1595,14 +1713,6 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( p->id, p->h, p->gradients.wcount, p->force.h_dt, p->dt_min, p->v_sig_max, Omega_inv); } - - /* 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 */ - - /* Kernel correction factors */ - p->gradients.adiabatic_f *= h_inv_dim; #endif } diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index f7ea7cdea7..9beb3482a5 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -61,30 +61,30 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( /* Compute density of pi. */ const hydro_real_t hi_inv = 1. / hi; - const float ui = r * hi_inv; + const float xi = r * hi_inv; - kernel_deval(ui, &wi, &wi_dx); + kernel_deval(xi, &wi, &wi_dx); pi->rho += mj * wi; - pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + pi->density.rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); pi->density.wcount += wi; - pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + pi->density.wcount_dh -= (hydro_dimension * wi + xi * wi_dx); - adaptive_softening_add_correction_term(pi, ui, hi_inv, mj); + adaptive_softening_add_correction_term(pi, xi, hi_inv, mj); /* Compute density of pj. */ const hydro_real_t hj_inv = 1. / hj; - const float uj = r * hj_inv; - kernel_deval(uj, &wj, &wj_dx); + const float xj = r * hj_inv; + kernel_deval(xj, &wj, &wj_dx); pj->rho += mi * wj; - pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); + pj->density.rho_dh -= mi * (hydro_dimension * wj + xj * wj_dx); pj->density.wcount += wj; - pj->density.wcount_dh -= (hydro_dimension * wj + uj * wj_dx); + pj->density.wcount_dh -= (hydro_dimension * wj + xj * wj_dx); - adaptive_softening_add_correction_term(pj, uj, hj_inv, mi); + adaptive_softening_add_correction_term(pj, xj, hj_inv, mi); /* Now we need to compute the derivative terms */ const hydro_real_t r_inv = r ? 1.0 / r : 0.0; @@ -130,10 +130,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( /* Number of neighbors */ pi->num_ngb++; pj->num_ngb++; +#endif +#ifdef hydro_props_use_adiabatic_correction /* Needed for the adiabatic kernel correction factor */ - pi->gradients.adiabatic_f_numerator += mj * r * wi_dx; - pj->gradients.adiabatic_f_numerator += mi * r * wj_dx; + pi->gradients.adiabatic_f_numerator += mj * r2 * wi; + pj->gradients.adiabatic_f_numerator += mi * r2 * wj; #endif } @@ -165,16 +167,16 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( const hydro_real_t r = sqrt(r2); const hydro_real_t h_inv = 1. / hi; - const float ui = r * h_inv; - kernel_deval(ui, &wi, &wi_dx); + const float xi = r * h_inv; + kernel_deval(xi, &wi, &wi_dx); pi->rho += mj * wi; - pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + pi->density.rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); pi->density.wcount += wi; - pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + pi->density.wcount_dh -= (hydro_dimension * wi + xi * wi_dx); - adaptive_softening_add_correction_term(pi, ui, h_inv, mj); + adaptive_softening_add_correction_term(pi, xi, h_inv, mj); const hydro_real_t r_inv = r ? 1.0 / r : 0.0; const hydro_real_t faci = mj * wi_dx * r_inv; @@ -207,9 +209,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( #ifdef MAGMA2_DEBUG_CHECKS /* Neighbour number */ pi->num_ngb++; +#endif +#ifdef hydro_props_use_adiabatic_correction /* Needed for the adiabatic kernel correction factor */ - pi->gradients.adiabatic_f_numerator += mj * r * wi_dx; + pi->gradients.adiabatic_f_numerator += mj * r2 * wi; #endif } @@ -249,11 +253,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( float wi, wi_dx, wj, wj_dx; - const float ui = r / hi; - const float uj = r / hj; + const float xi = r / hi; + const float xj = r / hj; - kernel_deval(ui, &wi, &wi_dx); - kernel_deval(uj, &wj, &wj_dx); + 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; @@ -314,6 +318,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( } } } + +#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 + } /** @@ -344,8 +355,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const hydro_real_t rhoj_inv = 1. / rhoj; const hydro_real_t r = sqrt(r2); float wi, wi_dx; - const float ui = r / hi; - kernel_deval(ui, &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 */ @@ -396,6 +407,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( } } } + +#ifdef hydro_props_use_adiabatic_correction + /* Correction terms for div v */ + pi->gradients.adiabatic_f_denominator += mj * rhoj_inv * r2 * wi; +#endif + } /** @@ -450,10 +467,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); - /* Variable smoothing length term */ - const hydro_real_t f_i = pi->force.f; - const hydro_real_t f_j = pj->force.f; - /* 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; @@ -719,9 +732,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ - 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]); + hydro_get_average_kernel_gradient(pi, pj, G_i, G_j, G_ij); hydro_real_t G_ij_norm = 0.; hydro_vec3_vec3_norm(G_ij, G_ij, &G_ij_norm); @@ -754,33 +765,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->force.h_dt -= dv_dot_dr * r_inv * wj_dr; #endif -#ifdef MAGMA2_DEBUG_CHECKS - /* Kernel correction factors */ - hydro_real_t dxdx[3][3] = {0}; - hydro_vec3_vec3_outer(dx_ij, dx_ij, dxdx); - - hydro_real_t adiabatic_f_denominator_j = 0.; - hydro_mat3x3_mat3x3_dot(pi->gradients.correction_matrix, - dxdx, &adiabatic_f_denominator_j); - adiabatic_f_denominator_j *= rhoj; - const hydro_real_t adiabatic_f_i = - (adiabatic_f_denominator_j != 0.) ? - pj->gradients.adiabatic_f_numerator / adiabatic_f_denominator_j - : 0.; - - hydro_real_t adiabatic_f_denominator_i = 0.; - hydro_mat3x3_mat3x3_dot(pj->gradients.correction_matrix, - dxdx, &adiabatic_f_denominator_i); - adiabatic_f_denominator_i *= rhoi * mj * rhoj_inv * wi; - const hydro_real_t adiabatic_f_j = - (adiabatic_f_denominator_i != 0.) ? - pi->gradients.adiabatic_f_numerator / adiabatic_f_denominator_i - : 0.; - - pi->gradients.adiabatic_f -= mj * rhoj_inv * adiabatic_f_i * wi; - pj->gradients.adiabatic_f -= mi * rhoi_inv * adiabatic_f_j * wj; -#endif - /* Timestepping */ @@ -869,6 +853,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( visc_du_term = 0.5 * visc_acc_term; /* Variable smoothing length term */ + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; const hydro_real_t kernel_gradient = 0.5 * (f_i * wi_dr + f_j * wj_dr) * r_inv; @@ -958,10 +944,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( float wj, wj_dx; kernel_deval(xj, &wj, &wj_dx); - /* Variable smoothing length term */ - const hydro_real_t f_i = pi->force.f; - const hydro_real_t f_j = pj->force.f; - /* 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 wi_dr = hi_inv_dim_plus_one * wi_dx; @@ -1223,9 +1205,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Averaged correction gradient. Note: antisymmetric, so only need * a sign flip for pj */ - 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]); + hydro_get_average_kernel_gradient(pi, pj, G_i, G_j, G_ij); hydro_real_t G_ij_norm = 0.; hydro_vec3_vec3_norm(G_ij, G_ij, &G_ij_norm); @@ -1255,22 +1235,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->force.h_dt -= dv_dot_dr * r_inv * wi_dr; #endif -#ifdef MAGMA2_DEBUG_CHECKS - /* Kernel correction factors */ - hydro_real_t dxdx[3][3] = {0}; - hydro_vec3_vec3_outer(dx_ij, dx_ij, dxdx); - - hydro_real_t adiabatic_f_denominator_j = 0.; - hydro_mat3x3_mat3x3_dot(pi->gradients.correction_matrix, - dxdx, &adiabatic_f_denominator_j); - adiabatic_f_denominator_j *= rhoj; - const hydro_real_t adiabatic_f_i = - (adiabatic_f_denominator_j != 0.) ? - pj->gradients.adiabatic_f_numerator / adiabatic_f_denominator_j - : 0.; - - pi->gradients.adiabatic_f -= mj * rhoj_inv * adiabatic_f_i * wi; -#endif /* Timestepping */ @@ -1353,6 +1317,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t wj_dr = hj_inv_dim_plus_one * wj_dx; /* Variable smoothing length term */ + const hydro_real_t f_i = pi->force.f; + const hydro_real_t f_j = pj->force.f; const hydro_real_t kernel_gradient = 0.5 * (f_i * wi_dr + f_j * wj_dr) * r_inv; diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index ee4ccb517c..9d9b5a59c1 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -74,6 +74,31 @@ * 2 = 2.0 * q_ij / (rho_i + rho_j) */ #define hydro_props_viscosity_weighting_type 1 +/*! 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 diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index ead43e5ea8..677d0fc207 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -174,12 +174,12 @@ struct part { /* Store gradients in a separate struct */ struct { -#ifdef MAGMA2_DEBUG_CHECKS +#ifdef hydro_props_use_adiabatic_correction /*! Adiabatic kernel correction factor numerator */ hydro_real_t adiabatic_f_numerator; - /*! Adiabatic kernel correction factor */ - hydro_real_t adiabatic_f; + /*! Adiabatic kernel correction factor denominator */ + hydro_real_t adiabatic_f_denominator; #endif /*! Sum of the kernel weights */ From 68b6ac18d77dff0c61c1c5acf47d93a2bc213999 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Mon, 4 Aug 2025 13:32:52 -0400 Subject: [PATCH 32/39] Cleaning up some of the convenience functions so that dot products return values. Cleaned up the viscosity so that it is in hydro.h and the ifdef statements do the selection there instead of directly in hydro_iact.h. --- src/hydro/MAGMA2/hydro.h | 270 ++++++++++++++++++---- src/hydro/MAGMA2/hydro_iact.h | 339 +++------------------------- src/hydro/MAGMA2/hydro_parameters.h | 15 -- 3 files changed, 261 insertions(+), 363 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 6e7f29d885..d9c13de589 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -563,11 +563,10 @@ double condition_number(gsl_matrix *A) { * @param result The result of the dot product. */ __attribute__((always_inline)) INLINE static -void hydro_vec3_vec3_dot(const hydro_real_t *restrict vec_a, - const hydro_real_t *restrict vec_b, - hydro_real_t *result) { +hydro_real_t hydro_vec3_vec3_dot(const hydro_real_t *restrict vec_a, + const hydro_real_t *restrict vec_b) { - *result = vec_a[0] * vec_b[0] + vec_a[1] * vec_b[1] + vec_a[2] * vec_b[2]; + return vec_a[0] * vec_b[0] + vec_a[1] * vec_b[1] + vec_a[2] * vec_b[2]; } /** @@ -579,12 +578,9 @@ void hydro_vec3_vec3_dot(const hydro_real_t *restrict vec_a, * @param result The result of the norm. */ __attribute__((always_inline)) INLINE static -void hydro_vec3_vec3_norm(const hydro_real_t *restrict vec_a, - const hydro_real_t *restrict vec_b, - hydro_real_t *result) { +hydro_real_t hydro_vec3_norm(const hydro_real_t *restrict vec) { - *result = - sqrt(vec_a[0] * vec_a[0] + vec_a[1] * vec_a[1] + vec_a[2] * vec_a[2]); + return sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]); } /** @@ -596,19 +592,18 @@ void hydro_vec3_vec3_norm(const hydro_real_t *restrict vec_a, * @param result The result of the contraction. */ __attribute__((always_inline)) INLINE static -void hydro_mat3x3_mat3x3_dot(const hydro_real_t (*restrict mat_a)[3], - const hydro_real_t (*restrict mat_b)[3], - hydro_real_t *result) { - - *result = 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]; +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]; } /** @@ -755,10 +750,8 @@ 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) { - hydro_real_t grad_dot_x_i = 0.; - hydro_real_t grad_dot_x_j = 0.; - hydro_vec3_vec3_dot(grad_i, dx, &grad_dot_x_i); - hydro_vec3_vec3_dot(grad_j, dx, &grad_dot_x_j); + 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); /* Regularize denominator */ if (grad_dot_x_j == 0.) return 0.; @@ -784,10 +777,8 @@ hydro_real_t hydro_vector_van_leer_A(const hydro_real_t (*restrict grad_i)[3], hydro_real_t delta_ij[3][3] = {0}; hydro_vec3_vec3_outer(dx, dx, delta_ij); - hydro_real_t grad_dot_x_x_i = 0.; - hydro_real_t grad_dot_x_x_j = 0.; - hydro_mat3x3_mat3x3_dot(grad_i, delta_ij, &grad_dot_x_x_i); - hydro_mat3x3_mat3x3_dot(grad_j, delta_ij, &grad_dot_x_x_j); + 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); /* Regularize denominator */ if (grad_dot_x_x_j == 0.) return 0.; @@ -870,11 +861,8 @@ hydro_scalar_second_order_reconstruction(const hydro_real_t phi, hydro_real_t delta_ij[3][3] = {0}; hydro_vec3_vec3_outer(midpoint, midpoint, delta_ij); - hydro_real_t df_dx_dot_r = 0.; - hydro_real_t df_dx_dx_dot_r2 = 0.; - - hydro_vec3_vec3_dot(grad, midpoint, &df_dx_dot_r); - hydro_mat3x3_mat3x3_dot(hess, delta_ij, &df_dx_dx_dot_r2); + 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); @@ -919,6 +907,16 @@ hydro_vector_second_order_reconstruction(const hydro_real_t phi, f_reconstructed[2] = f[2] + phi * (df_dx_dot_r[2] + 0.5 * df_dx_dx_dot_r2[2]); } +/** + * @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, @@ -1015,6 +1013,191 @@ void hydro_get_average_kernel_gradient(const struct part *restrict pi, #endif } +/** + * @brief Computes the viscosity acceleration and mu_ij terms. + * + * + * @param pi Particle pi + * @param pj Particle pj + * @param dv_ij Second order reconstructed velocity difference + * @param dx_ij Distance vector + * @param r2 Distance vector squared + * @param r_inv Inverse distance between pi and pj + * @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_and_mu(const struct part *restrict pi, + const struct part *restrict pj, + const hydro_real_t *restrict dv_ij, + const hydro_real_t *restrict dx_ij, + const hydro_real_t r2, + const hydro_real_t r_inv, + const hydro_real_t fac_mu, + const hydro_real_t a2_Hubble, + hydro_real_t *mu_i, + hydro_real_t *mu_j) { + + const hydro_real_t rhoi = pi->rho; + const hydro_real_t rhoj = pj->rho; + +#ifdef hydro_props_viscosity_weighting_type +#if (hydro_props_viscosity_weighting_type == 0) + const hydro_real_t rhoij_inv = 1. / (rhoi * rhoj); + const hydro_real_t hi_inv = 1. / pi->h; + const hydro_real_t hj_inv = 1. / pj->h; + + const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; + + const hydro_real_t eta_i[3] = {dx_ij[0] * hi_inv, + dx_ij[1] * hi_inv, + dx_ij[2] * hi_inv}; + const hydro_real_t eta_j[3] = {-dx_ij[0] * hj_inv, + -dx_ij[1] * hj_inv, + -dx_ij[2] * hj_inv}; + const hydro_real_t eta_i2 = hydro_vec3_vec3_dot(eta_i, eta_i); + const hydro_real_t eta_j2 = hydro_vec3_vec3_dot(eta_j, eta_j); + + const hydro_real_t dv_dot_eta_i = hydro_vec3_vec3_dot(dv_ij, eta_i); + /* Scale Hubble flow by hi_inv so it is overall scaled */ + const hydro_real_t dv_dot_eta_i_Hubble = + dv_dot_eta_i + a2_Hubble * r2 * hi_inv; + + const hydro_real_t dv_dot_eta_j = hydro_vec3_vec3_dot(dv_ji, eta_j); + /* Scale Hubble flow by hj_inv so it is overall scaled */ + const hydro_real_t dv_dot_eta_j_Hubble = + dv_dot_eta_j + a2_Hubble * r2 * hj_inv; + + /* Is the flow converging? */ + const char conv_i = (dv_dot_eta_i_Hubble < 0.) ? 1 : 0; + const char conv_j = (dv_dot_eta_j_Hubble < 0.) ? 1 : 0; + + /* Is the flow converging? If so, multiply mu by fac_mu. If not, zero. */ + const hydro_real_t conv = (conv_i && conv_j) ? fac_mu : 0.; + +#ifdef MAGMA2_DEBUG_CHECKS + if (conv_i != conv_j) { + warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" + "conv_i = %d, conv_j = %d\n" + "dv_dot_eta_i_Hubble = %g, dv_dot_eta_j_Hubble = %g\n" + "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" + "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" + "eta_i2 = %g, eta_j2 = %g\n" + "hi_inv = %g, hj_inv = %g\n" + "a2_Hubble = %g, r2 = %g\n", + pi->id, pj->id, + conv_i, conv_j, + dv_dot_eta_i_Hubble, dv_dot_eta_j_Hubble, + dv_ij[0], dv_ij[1], dv_ij[2], + dv_ji[0], dv_ji[1], dv_ji[2], + eta_i[0], eta_i[1], eta_i[2], + eta_j[0], eta_j[1], eta_j[2], + eta_i2, eta_j2, + hi_inv, hj_inv, + a2_Hubble, r2); + } +#endif + + /* mu_i and mu_j include the Hubble flow */ + *mu_i = conv * dv_dot_eta_i_Hubble / (eta_i2 + const_viscosity_epsilon2); + *mu_j = conv * dv_dot_eta_j_Hubble / (eta_j2 + const_viscosity_epsilon2); + + const hydro_real_t q_i_alpha = + -const_viscosity_alpha * pi->force.soundspeed * (*mu_i); + const hydro_real_t q_i_beta = const_viscosity_beta * (*mu_i) * (*mu_i); + 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_j); + const hydro_real_t q_j_beta = const_viscosity_beta * (*mu_j) * (*mu_j); + const hydro_real_t Q_j = rhoj * (q_j_alpha + q_j_beta); + + /* Add viscosity to the pressure */ + return (Q_i + Q_j) * rhoij_inv; + +#elif (hydro_props_viscosity_weighting_type == 1) + const hydro_real_t rhoij_inv = 1. / (rhoi * rhoj); + const hydro_real_t dv_dot_dr_Hubble = + hydro_vec3_vec3_dot(dv_ij, dx_ij) + a2_Hubble * r2; + const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; + const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; + 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); + + /* Load mu_i and mu_j */ + *mu_i = mu_ij; + *mu_j = mu_ij; + + /* Add viscosity to the pressure */ + return Q_ij * rhoij_inv; +#elif (hydro_props_viscosity_weighting_type == 2) + const hydro_real_t dv_dot_dr_Hubble = + hydro_vec3_vec3_dot(dv_ij, dx_ij) + a2_Hubble * r2; + const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; + const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; + 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); + + /* Load mu_i and mu_j */ + *mu_i = mu_ij; + *mu_j = mu_ij; + + /* Add viscosity to the pressure */ + return 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 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 hydro_real_t *restrict dx, + const struct part *restrict pi, + const struct part *restrict pj, + const hydro_real_t mu_i, + const hydro_real_t mu_j, + const hydro_real_t beta) { + +#ifdef hydro_props_viscosity_weighting_type +#if (hydro_props_viscosity_weighting_type == 0) + const hydro_real_t dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; + const hydro_real_t v_sig_visc_i = + hydro_signal_velocity(dx, pi, pj, mu_i, beta); + const hydro_real_t v_sig_visc_j = + hydro_signal_velocity(dx_ji, pj, pi, mu_j, beta); + return max(v_sig_visc_i, v_sig_visc_j); +#else + /* Here mu_i = mu_j */ + return hydro_signal_velocity(dx, pi, pj, mu_i, beta); +#endif +#else + error("No viscosity weighting type defined at compile time."); +#endif +} + /** * @brief Finishes the density calculation. * @@ -1223,7 +1406,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( /* Compute the "grad h" term */ const float common_factor = p->h * hydro_dimension_inv / p->density.wcount; - float grad_W_term = 0.f; + float grad_W_term = -1.f; /* Ignore changing-kernel effects when h ~= h_max */ if (p->h > 0.9999f * hydro_props->h_max) { @@ -1239,7 +1422,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( 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 = 0.f; + grad_W_term = -1.f; warning( "grad_W_term very small for particle with ID %lld (h: %g, wcount: " "%g, wcount_dh: %g)", @@ -1248,7 +1431,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_gradient( } /* Update variables. */ - p->force.f = (grad_W_term != 0.f) ? 1. / (1. + (hydro_real_t)grad_W_term) : 0.; + p->force.f = (grad_W_term > -1.f) ? 1.f / (1.f + grad_W_term) : 1.f; } @@ -1695,13 +1878,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( struct part *restrict p, const struct cosmology *cosmo) { const hydro_real_t wcount_inv = 1. / p->gradients.wcount; -#ifdef hydro_props_use_higher_order_gradients_in_dh_dt - const hydro_real_t Omega_inv = 1.; -#else - const hydro_real_t Omega_inv = p->force.f; -#endif - p->force.h_dt *= Omega_inv * p->h * hydro_dimension_inv * wcount_inv; + p->force.h_dt *= p->h * hydro_dimension_inv * wcount_inv; /* dt_min is in physical units, and requires the kernel_gamma factor for h */ p->dt_min *= kernel_gamma * cosmo->a / cosmo->a_factor_sound_speed; @@ -1709,9 +1887,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( #ifdef MAGMA2_DEBUG_CHECKS if (fabs(p->force.h_dt / p->h) > 1000.) { warning("Large dh/dt! Hydro end force for particle with ID %lld (h: %g, " - "wcount: %g, h_dt: %g, dt_min: %g, v_sig_max: %g, Omega_inv: %g)", + "wcount: %g, h_dt: %g, dt_min: %g, v_sig_max: %g)", p->id, p->h, p->gradients.wcount, p->force.h_dt, p->dt_min, - p->v_sig_max, Omega_inv); + p->v_sig_max); } #endif } diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 9beb3482a5..8e54c3fad8 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -497,9 +497,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* First order density-independent velocity gradients */ const char D_well_conditioned = (pi->gradients.D_well_conditioned && pj->gradients.D_well_conditioned); - /* First order internal energy gradients */ - const char u_well_conditioned = - (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned); /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ @@ -560,125 +557,22 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( vi_reconstructed[1] - vj_reconstructed[1], vi_reconstructed[2] - vj_reconstructed[2]}; - /* Need this for viscosity and conductivity */ - hydro_real_t dv_dot_dr_Hubble = 0.; - hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_Hubble); - dv_dot_dr_Hubble += a2_Hubble * r2; - -#ifdef hydro_props_use_viscosity_weighting -#if (hydro_props_viscosity_weighting_type == 0) - const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; - - const hydro_real_t eta_i[3] = {dx_ij[0] * hi_inv, - dx_ij[1] * hi_inv, - dx_ij[2] * hi_inv}; - const hydro_real_t eta_j[3] = {dx_ji[0] * hj_inv, - dx_ji[1] * hj_inv, - dx_ji[2] * hj_inv}; - hydro_real_t eta_i2 = 0.; - hydro_vec3_vec3_dot(eta_i, eta_i, &eta_i2); - - hydro_real_t eta_j2 = 0.; - hydro_vec3_vec3_dot(eta_j, eta_j, &eta_j2); - - hydro_real_t dv_dot_eta_i = 0.; - hydro_vec3_vec3_dot(dv_ij, eta_i, &dv_dot_eta_i); - /* Scale Hubble flow by hi_inv so it is overall scaled */ - const hydro_real_t dv_dot_eta_i_Hubble = - dv_dot_eta_i + a2_Hubble * r2 * hi_inv; - - hydro_real_t dv_dot_eta_j = 0.; - hydro_vec3_vec3_dot(dv_ji, eta_j, &dv_dot_eta_j); - /* Scale Hubble flow by hj_inv so it is overall scaled */ - const hydro_real_t dv_dot_eta_j_Hubble = - dv_dot_eta_j + a2_Hubble * r2 * hj_inv; - - /* Is the flow converging? */ - const char conv_i = (dv_dot_eta_i_Hubble < 0.) ? 1 : 0; - const char conv_j = (dv_dot_eta_j_Hubble < 0.) ? 1 : 0; - - /* Is the flow converging? If so, multiply mu by fac_mu. If not, zero. */ - const hydro_real_t conv = (conv_i && conv_j) ? fac_mu : 0.; + hydro_real_t mu_i = 0.; + hydro_real_t mu_j = 0.; -#ifdef MAGMA2_DEBUG_CHECKS - if (conv_i != conv_j) { - warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" - "conv_i = %d, conv_j = %d\n" - "dv_dot_eta_i_Hubble = %g, dv_dot_eta_j_Hubble = %g\n" - "vi_reconstructed = (%g, %g, %g), " - "vj_reconstructed = (%g, %g, %g)\n" - "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" - "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" - "eta_i2 = %g, eta_j2 = %g\n" - "hi_inv = %g, hj_inv = %g\n" - "a2_Hubble = %g, r2 = %g\n", - pi->id, pj->id, - conv_i, conv_j, - dv_dot_eta_i_Hubble, dv_dot_eta_j_Hubble, - vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], - vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], - dv_ij[0], dv_ij[1], dv_ij[2], - dv_ji[0], dv_ji[1], dv_ji[2], - eta_i[0], eta_i[1], eta_i[2], - eta_j[0], eta_j[1], eta_j[2], - eta_i2, eta_j2, - hi_inv, hj_inv, - a2_Hubble, r2); - } -#endif - - /* mu_i and mu_j include the Hubble flow */ - const hydro_real_t mu_i = - conv * dv_dot_eta_i_Hubble / (eta_i2 + const_viscosity_epsilon2); - const hydro_real_t mu_j = - conv * dv_dot_eta_j_Hubble / (eta_j2 + const_viscosity_epsilon2); - - const hydro_real_t q_i_alpha = - -const_viscosity_alpha * pi->force.soundspeed * mu_i; - const hydro_real_t q_i_beta = const_viscosity_beta * mu_i * mu_i; - 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_j; - const hydro_real_t q_j_beta = const_viscosity_beta * mu_j * mu_j; - const hydro_real_t Q_j = rhoj * (q_j_alpha + q_j_beta); - - /* Add viscosity to the pressure */ - visc_acc_term = (Q_i + Q_j) * rhoij_inv; -#elif (hydro_props_viscosity_weighting_type == 1) - const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; - const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; - 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); - - /* Add viscosity to the pressure */ - visc_acc_term = Q_ij * rhoij_inv; -#elif (hydro_props_viscosity_weighting_type == 2) - const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; - const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; - 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); - - /* Add viscosity to the pressure */ - visc_acc_term = q_ij; -#else - error("Unknown compiled hydro_props_use_viscosity_weighting value: %d\n" - "Valid values are 0, 1, or 2.\n", - (int)hydro_props_use_viscosity_weighting); -#endif -#endif + /* Get the acceleration term, depends on the weighting scheme */ + visc_acc_term = + hydro_get_visc_acc_term_and_mu(pi, pj, dv_ij, dx_ij, r2, r_inv, + fac_mu, a2_Hubble, &mu_i, &mu_j); /* Split heating between the two particles */ visc_du_term = 0.5 * visc_acc_term; + /* Viscous signal velocity */ + const hydro_real_t v_sig_visc = + hydro_get_visc_signal_velocity(dx, pi, pj, mu_i, mu_j, + const_viscosity_beta); + /* Artificial conductivity */ @@ -686,7 +580,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( hydro_real_t ui_reconstructed = 0.; hydro_real_t uj_reconstructed = 0.; - if (u_well_conditioned) { + /* First order internal energy gradients are well-conditioned */ + if (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned) { /* Compute global Van Leer limiter (scalar, not component-wise) */ const hydro_real_t phi_ij_scalar = hydro_scalar_van_leer_phi(pi->gradients.u, @@ -712,14 +607,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t dv_Hubble[3] = {dv[0] + a_Hubble * dx[0], dv[1] + a_Hubble * dx[1], dv[2] + a_Hubble * dx[2]}; - hydro_real_t dv_Hubble_norm = 0.; - hydro_vec3_vec3_norm(dv_Hubble, dv_Hubble, &dv_Hubble_norm); + const hydro_real_t dv_Hubble_norm = hydro_vec3_norm(dv_Hubble); /* Signal velocity is the sum of pressure and speed contributions */ const hydro_real_t v_sig_speed = fac_mu * dv_Hubble_norm; const hydro_real_t v_sig_pressure = sqrt(fabs(pressurei - pressurej) * rho_ij_inv); - const hydro_real_t v_sig_cond = v_sig_speed + v_sig_pressure; + const hydro_real_t v_sig_cond = 0.5 * (v_sig_speed + v_sig_pressure); const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; @@ -734,16 +628,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( * a sign flip for pj */ hydro_get_average_kernel_gradient(pi, pj, G_i, G_j, G_ij); - hydro_real_t G_ij_norm = 0.; - hydro_vec3_vec3_norm(G_ij, G_ij, &G_ij_norm); + /* For conduction term */ + const hydro_real_t G_ij_norm = hydro_vec3_norm(G_ij); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ - hydro_real_t dv_dot_G_ij = 0.; -#ifdef hydro_props_use_second_order_velocities_in_divergence - hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); -#else - hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); -#endif + const hydro_real_t dv_dot_G_ij = hydro_vec3_vec3_dot(dv, G_ij); sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; @@ -754,34 +643,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the time derivative for h. */ -#ifdef hydro_props_use_higher_order_gradients_in_dh_dt pi->force.h_dt -= dv_dot_G_ij; pj->force.h_dt -= dv_dot_G_ij; -#else - /* Remove Hubble flow */ - const hydro_real_t dv_dot_dr = dv_dot_dr_Hubble - a2_Hubble * r2; - - pi->force.h_dt -= dv_dot_dr * r_inv * wi_dr; - pj->force.h_dt -= dv_dot_dr * r_inv * wj_dr; -#endif /* Timestepping */ /* New signal velocity */ -#ifdef hydro_props_use_viscosity_weighting -#if (hydro_props_viscosity_weighting_type == 0) - const hydro_real_t v_sig_visc_i = - signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); - const hydro_real_t v_sig_visc_j = - signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); - const hydro_real_t v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); -#else - const hydro_real_t v_sig_visc = - signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); -#endif -#endif const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); /* Update if we need to */ @@ -808,8 +677,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( G_ij[2] = dx[2]; /* Compute dv dot dr. */ - hydro_real_t dvdr = 0.; - hydro_vec3_vec3_dot(dv, G_ij, &dvdr); + const hydro_real_t dvdr = hydro_vec3_vec3_dot(dv, G_ij); /* Includes the hubble flow term; not used for du/dt */ const hydro_real_t dvdr_Hubble = dvdr + a2_Hubble * r2; @@ -971,9 +839,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* First order density-independent velocity gradients */ const char D_well_conditioned = (pi->gradients.D_well_conditioned && pj->gradients.D_well_conditioned); - /* First order internal energy gradients */ - const char u_well_conditioned = - (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned); /* Always use SPH gradients between particles if one of them has an * ill-conditioned C matrix */ @@ -1034,132 +899,29 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( vi_reconstructed[1] - vj_reconstructed[1], vi_reconstructed[2] - vj_reconstructed[2]}; - /* Need this for viscosity and conductivity */ - hydro_real_t dv_dot_dr_Hubble = 0.; - hydro_vec3_vec3_dot(dv_ij, dx_ij, &dv_dot_dr_Hubble); - dv_dot_dr_Hubble += a2_Hubble * r2; - -#ifdef hydro_props_use_viscosity_weighting -#if (hydro_props_viscosity_weighting_type == 0) - const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; - - const hydro_real_t eta_i[3] = {dx_ij[0] * hi_inv, - dx_ij[1] * hi_inv, - dx_ij[2] * hi_inv}; - const hydro_real_t eta_j[3] = {dx_ji[0] * hj_inv, - dx_ji[1] * hj_inv, - dx_ji[2] * hj_inv}; - hydro_real_t eta_i2 = 0.; - hydro_vec3_vec3_dot(eta_i, eta_i, &eta_i2); - - hydro_real_t eta_j2 = 0.; - hydro_vec3_vec3_dot(eta_j, eta_j, &eta_j2); - - hydro_real_t dv_dot_eta_i = 0.; - hydro_vec3_vec3_dot(dv_ij, eta_i, &dv_dot_eta_i); - /* Scale Hubble flow by hi_inv so it is overall scaled */ - const hydro_real_t dv_dot_eta_i_Hubble = - dv_dot_eta_i + a2_Hubble * r2 * hi_inv; - - hydro_real_t dv_dot_eta_j = 0.; - hydro_vec3_vec3_dot(dv_ji, eta_j, &dv_dot_eta_j); - /* Scale Hubble flow by hj_inv so it is overall scaled */ - const hydro_real_t dv_dot_eta_j_Hubble = - dv_dot_eta_j + a2_Hubble * r2 * hj_inv; - - /* Is the flow converging? */ - const char conv_i = (dv_dot_eta_i_Hubble < 0.) ? 1 : 0; - const char conv_j = (dv_dot_eta_j_Hubble < 0.) ? 1 : 0; - - /* Is the flow converging? If so, multiply mu by fac_mu. If not, zero. */ - const hydro_real_t conv = (conv_i && conv_j) ? fac_mu : 0.; + hydro_real_t mu_i = 0.; + hydro_real_t mu_j = 0.; -#ifdef MAGMA2_DEBUG_CHECKS - if (conv_i != conv_j) { - warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" - "conv_i = %d, conv_j = %d\n" - "dv_dot_eta_i_Hubble = %g, dv_dot_eta_j_Hubble = %g\n" - "vi_reconstructed = (%g, %g, %g), " - "vj_reconstructed = (%g, %g, %g)\n" - "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" - "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" - "eta_i2 = %g, eta_j2 = %g\n" - "hi_inv = %g, hj_inv = %g\n" - "a2_Hubble = %g, r2 = %g\n", - pi->id, pj->id, - conv_i, conv_j, - dv_dot_eta_i_Hubble, dv_dot_eta_j_Hubble, - vi_reconstructed[0], vi_reconstructed[1], vi_reconstructed[2], - vj_reconstructed[0], vj_reconstructed[1], vj_reconstructed[2], - dv_ij[0], dv_ij[1], dv_ij[2], - dv_ji[0], dv_ji[1], dv_ji[2], - eta_i[0], eta_i[1], eta_i[2], - eta_j[0], eta_j[1], eta_j[2], - eta_i2, eta_j2, - hi_inv, hj_inv, - a2_Hubble, r2); - } -#endif + /* Get the acceleration term, depends on the weighting scheme */ + visc_acc_term = + hydro_get_visc_acc_term_and_mu(pi, pj, dv_ij, dx_ij, r2, r_inv, + fac_mu, a2_Hubble, &mu_i, &mu_j); + visc_du_term = 0.5 * visc_acc_term; - /* mu_i and mu_j include the Hubble flow */ - const hydro_real_t mu_i = - conv * dv_dot_eta_i_Hubble / (eta_i2 + const_viscosity_epsilon2); - const hydro_real_t mu_j = - conv * dv_dot_eta_j_Hubble / (eta_j2 + const_viscosity_epsilon2); - - const hydro_real_t q_i_alpha = - -const_viscosity_alpha * pi->force.soundspeed * mu_i; - const hydro_real_t q_i_beta = const_viscosity_beta * mu_i * mu_i; - 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_j; - const hydro_real_t q_j_beta = const_viscosity_beta * mu_j * mu_j; - const hydro_real_t Q_j = rhoj * (q_j_alpha + q_j_beta); - - /* Add viscosity to the pressure */ - visc_acc_term = (Q_i + Q_j) * rhoij_inv; -#elif (hydro_props_viscosity_weighting_type == 1) - const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; - const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; - 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); - - /* Add viscosity to the pressure */ - visc_acc_term = Q_ij * rhoij_inv; -#elif (hydro_props_viscosity_weighting_type == 2) - const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; - const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; - 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); - - /* Add viscosity to the pressure */ - visc_acc_term = q_ij; -#else - error("Unknown compiled hydro_props_use_viscosity_weighting value: %d\n" - "Valid values are 0, 1, or 2.\n", - (int)hydro_props_use_viscosity_weighting); -#endif -#endif + /* Viscous signal velocity */ + const hydro_real_t v_sig_visc = + hydro_get_visc_signal_velocity(dx, pi, pj, mu_i, mu_j, + const_viscosity_beta); - visc_du_term = 0.5 * visc_acc_term; - /* Artificial conductivity */ hydro_real_t ui_reconstructed = 0.; hydro_real_t uj_reconstructed = 0.; - if (u_well_conditioned) { + /* First order internal energy gradients are well-conditioned */ + if (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned) { /* Compute global Van Leer limiter (scalar, not component-wise) */ const hydro_real_t phi_ij_scalar = hydro_scalar_van_leer_phi(pi->gradients.u, @@ -1185,14 +947,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t dv_Hubble[3] = {dv[0] + a_Hubble * dx[0], dv[1] + a_Hubble * dx[1], dv[2] + a_Hubble * dx[2]}; - hydro_real_t dv_Hubble_norm = 0.; - hydro_vec3_vec3_norm(dv_Hubble, dv_Hubble, &dv_Hubble_norm); + const hydro_real_t dv_Hubble_norm = hydro_vec3_norm(dv_Hubble); /* Signal velocity is the sum of pressure and speed contributions */ const hydro_real_t v_sig_speed = fac_mu * dv_Hubble_norm; const hydro_real_t v_sig_pressure = sqrt(fabs(pressurei - pressurej) * rho_ij_inv); - const hydro_real_t v_sig_cond = v_sig_speed + v_sig_pressure; + const hydro_real_t v_sig_cond = 0.5 * (v_sig_speed + v_sig_pressure); const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; @@ -1207,16 +968,10 @@ __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); - hydro_real_t G_ij_norm = 0.; - hydro_vec3_vec3_norm(G_ij, G_ij, &G_ij_norm); + const hydro_real_t G_ij_norm = hydro_vec3_norm(G_ij); /* Compute dv dot G_ij, reduces to dv dot dx in regular SPH. */ - hydro_real_t dv_dot_G_ij = 0.; -#ifdef hydro_props_use_second_order_velocities_in_divergence - hydro_vec3_vec3_dot(dv_ij, G_ij, &dv_dot_G_ij); -#else - hydro_vec3_vec3_dot(dv, G_ij, &dv_dot_G_ij); -#endif + const hydro_real_t dv_dot_G_ij = hydro_vec3_vec3_dot(dv, G_ij); sph_du_term_i *= dv_dot_G_ij; visc_du_term *= dv_dot_G_ij; @@ -1226,32 +981,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get the time derivative for h. */ -#ifdef hydro_props_use_higher_order_gradients_in_dh_dt pi->force.h_dt -= dv_dot_G_ij; -#else - /* Remove Hubble flow */ - const hydro_real_t dv_dot_dr = dv_dot_dr_Hubble - a2_Hubble * r2; - - pi->force.h_dt -= dv_dot_dr * r_inv * wi_dr; -#endif /* Timestepping */ /* New signal velocity */ -#ifdef hydro_props_use_viscosity_weighting -#if (hydro_props_viscosity_weighting_type == 0) - const hydro_real_t v_sig_visc_i = - signal_velocity(dx, pi, pj, mu_i, const_viscosity_beta); - const hydro_real_t v_sig_visc_j = - signal_velocity(dx, pj, pi, mu_j, const_viscosity_beta); - const hydro_real_t v_sig_visc = max(v_sig_visc_i, v_sig_visc_j); -#else - const hydro_real_t v_sig_visc = - signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); -#endif -#endif const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); /* Update if we need to */ @@ -1272,8 +1008,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( G_ij[2] = dx[2]; /* Compute dv dot dr. */ - hydro_real_t dvdr = 0.; - hydro_vec3_vec3_dot(dv, G_ij, &dvdr); + const hydro_real_t dvdr = hydro_vec3_vec3_dot(dv, G_ij); /* Includes the hubble flow term; not used for du/dt */ const hydro_real_t dvdr_Hubble = dvdr + a2_Hubble * r2; diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 9d9b5a59c1..3ad7d86bda 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -129,21 +129,6 @@ * Beta is defined as in e.g. Price (2010) Eqn (103) */ #define const_viscosity_beta (2.0 * const_viscosity_alpha) -/*! Use the second-order velocities in v_ij * G_ij (this doesn't work) */ -//#define hydro_props_use_second_order_velocities_in_divergence - -/*! Use v_ij * G_ij in dh/dt evolution */ -#define hydro_props_use_higher_order_gradients_in_dh_dt - -#ifdef hydro_props_viscosity_weighting_type -#ifndef hydro_props_use_viscosity_weighting -#define hydro_props_use_viscosity_weighting -#endif -#else -#define hydro_props_use_viscosity_weighting -#define hydro_props_viscosity_weighting_type 2 -#endif - /* ---------- Structures for below ---------- */ From fce873a80bf2797645ec6d3817a53fa03780bbec Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Thu, 14 Aug 2025 17:54:49 -0400 Subject: [PATCH 33/39] Added a Balsara limiter and figured out all of the correct Hubble flow terms for artificial conductivity and viscosity. Must use a much lower condition number of matrices to get good results, or else everything blows up. --- src/hydro/MAGMA2/hydro.h | 434 +++++++++++---- src/hydro/MAGMA2/hydro_iact.h | 645 +++++++++++++++++------ src/hydro/MAGMA2/hydro_io.h | 18 + src/hydro/MAGMA2/hydro_parameters.h | 18 +- src/hydro/MAGMA2/hydro_part.h | 32 +- src/sink/GEAR/sink.h | 2 +- src/sink/GEAR/sink_getters.h | 7 +- src/star_formation/GEAR/star_formation.h | 4 +- 8 files changed, 856 insertions(+), 304 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index d9c13de589..8d4ccef43c 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -441,6 +441,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) return FLT_MAX; + /* CFL condition */ return hydro_properties->CFL_condition * p->dt_min; } @@ -465,6 +467,31 @@ __attribute__((always_inline)) INLINE static float hydro_signal_velocity( return 0.5f * (ci + cj - beta * mu_ij); } +/** + * @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. @@ -503,7 +530,7 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->rho = 0.f; p->density.rho_dh = 0.f; #ifdef MAGMA2_DEBUG_CHECKS - p->num_ngb = 0; + p->debug.num_ngb = 0; #endif #ifdef hydro_props_use_adiabatic_correction @@ -513,14 +540,22 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->gradients.wcount = 0.; p->gradients.u_well_conditioned = 1; p->gradients.D_well_conditioned = 1; + + p->gradients.du_min = 0.; + p->gradients.du_max = 0.; + 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.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.velocity_tensor_aux[i][j] = 0; - p->gradients.velocity_tensor_aux_norm[i][j] = 0; + p->gradients.velocity_tensor_aux[i][j] = 0.; + p->gradients.velocity_tensor_aux_norm[i][j] = 0.; } } } @@ -573,8 +608,7 @@ hydro_real_t hydro_vec3_vec3_dot(const hydro_real_t *restrict vec_a, * @brief Norm of two 3D vectors. * * - * @param vec_a The first vector. - * @param vec_b The second vector. + * @param vec The vector. * @param result The result of the norm. */ __attribute__((always_inline)) INLINE static @@ -583,6 +617,29 @@ 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. * @@ -697,6 +754,120 @@ __attribute__((always_inline)) INLINE static void hydro_vec3_vec3_outer( result[2][2] = vec_a[2] * vec_b[2]; } +/** + * @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) { + + const hydro_real_t lo0 = fmax(df_min_i, -df_max_j); + const hydro_real_t hi0 = fmin(df_max_i, -df_min_j); + const hydro_real_t lo = fmin(lo0, hi0); + const hydro_real_t hi = fmax(lo0, hi0); + + hydro_real_t df = df_reconstructed; + if (df < lo) df = lo; + if (df > hi) df = hi; + + /* If sign flips trust the original estimate over reconstructed */ + if ((df > 0) != (df_raw > 0)) df = df_raw; + + return df; +} + +/** + * @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]); +} + +/** + * @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) { + + const hydro_real_t grad_norm = hydro_vec3_norm(grad); + const hydro_real_t length = hydro_props_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 hydro_props_grad_overshoot_tolerance + const hydro_real_t tolerance = hydro_props_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; + } + } +} + /** * @brief Computes Phi_ab from Rosswog 2020 21 for a field given A. This is the * van Leer 1974 slope limiting procedure. @@ -723,18 +894,15 @@ hydro_real_t hydro_van_leer_phi(const hydro_real_t A_ij, const hydro_real_t diff = (eta_ij - const_slope_limiter_eta_crit) / const_slope_limiter_eta_fold; damping = exp(-diff * diff); - -#ifdef MAGMA2_DEBUG_CHECKS - if (eta_ij < 0.1 * const_slope_limiter_eta_crit) { - warning("Particles extremely close! <0.1 mean interparticle spacing. " - "phi_raw = %g, damping = %g, eta_ij = %g, " - "eta_crit = %g, eta_fold = %g", phi_raw, damping, eta_ij, - const_slope_limiter_eta_crit, const_slope_limiter_eta_fold); - } -#endif } - return phi_raw * damping; + phi_raw *= damping; + + /* Handle any edge cases */ + phi_raw = min(1., phi_raw); + phi_raw = max(0., phi_raw); + + return phi_raw; } /** @@ -907,6 +1075,42 @@ hydro_vector_second_order_reconstruction(const hydro_real_t phi, 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) { + + 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; +} + /** * @brief Computes the average kernel gradient between pi and pj * @@ -1019,22 +1223,20 @@ void hydro_get_average_kernel_gradient(const struct part *restrict pi, * * @param pi Particle pi * @param pj Particle pj - * @param dv_ij Second order reconstructed velocity difference - * @param dx_ij Distance vector - * @param r2 Distance vector squared - * @param r_inv Inverse distance between pi and 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 + */ +__attribute__((always_inline)) INLINE static hydro_real_t hydro_get_visc_acc_term_and_mu(const struct part *restrict pi, const struct part *restrict pj, - const hydro_real_t *restrict dv_ij, - const hydro_real_t *restrict dx_ij, - const hydro_real_t r2, - const hydro_real_t r_inv, + const hydro_real_t *restrict dv, + const hydro_real_t *restrict dx, + const hydro_real_t r, const hydro_real_t fac_mu, const hydro_real_t a2_Hubble, hydro_real_t *mu_i, @@ -1043,124 +1245,95 @@ hydro_real_t hydro_get_visc_acc_term_and_mu(const struct part *restrict pi, const hydro_real_t rhoi = pi->rho; const hydro_real_t rhoj = pj->rho; -#ifdef hydro_props_viscosity_weighting_type -#if (hydro_props_viscosity_weighting_type == 0) - const hydro_real_t rhoij_inv = 1. / (rhoi * rhoj); - const hydro_real_t hi_inv = 1. / pi->h; - const hydro_real_t hj_inv = 1. / pj->h; + const hydro_real_t hi = pi->h; + const hydro_real_t hj = pj->h; - const hydro_real_t dv_ji[3] = {-dv_ij[0], -dv_ij[1], -dv_ij[2]}; + const hydro_real_t r2 = r * r; - const hydro_real_t eta_i[3] = {dx_ij[0] * hi_inv, - dx_ij[1] * hi_inv, - dx_ij[2] * hi_inv}; - const hydro_real_t eta_j[3] = {-dx_ij[0] * hj_inv, - -dx_ij[1] * hj_inv, - -dx_ij[2] * hj_inv}; - const hydro_real_t eta_i2 = hydro_vec3_vec3_dot(eta_i, eta_i); - const hydro_real_t eta_j2 = hydro_vec3_vec3_dot(eta_j, eta_j); + const hydro_real_t bi = pi->gradients.balsara; + const hydro_real_t bj = pj->gradients.balsara; - const hydro_real_t dv_dot_eta_i = hydro_vec3_vec3_dot(dv_ij, eta_i); - /* Scale Hubble flow by hi_inv so it is overall scaled */ - const hydro_real_t dv_dot_eta_i_Hubble = - dv_dot_eta_i + a2_Hubble * r2 * hi_inv; + /* Viscosity direction */ + hydro_real_t dx_hat[3] = {0., 0., 0.}; + hydro_vec3_unit(dx, dx_hat); - const hydro_real_t dv_dot_eta_j = hydro_vec3_vec3_dot(dv_ji, eta_j); - /* Scale Hubble flow by hj_inv so it is overall scaled */ - const hydro_real_t dv_dot_eta_j_Hubble = - dv_dot_eta_j + a2_Hubble * r2 * hj_inv; + /* 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.; - /* Is the flow converging? */ - const char conv_i = (dv_dot_eta_i_Hubble < 0.) ? 1 : 0; - const char conv_j = (dv_dot_eta_j_Hubble < 0.) ? 1 : 0; + /* Softening to prevent blow-up on close particle approach */ + const hydro_real_t eps2 = const_viscosity_epsilon2; - /* Is the flow converging? If so, multiply mu by fac_mu. If not, zero. */ - const hydro_real_t conv = (conv_i && conv_j) ? fac_mu : 0.; +#ifdef hydro_props_viscosity_weighting_type +#if (hydro_props_viscosity_weighting_type == 0) -#ifdef MAGMA2_DEBUG_CHECKS - if (conv_i != conv_j) { - warning("Flow mismatch for particles pi=%lld and pj=%lld.\n" - "conv_i = %d, conv_j = %d\n" - "dv_dot_eta_i_Hubble = %g, dv_dot_eta_j_Hubble = %g\n" - "dv_ij = (%g, %g, %g), dv_ji = (%g, %g, %g)\n" - "eta_i = (%g, %g, %g), eta_j = (%g, %g, %g)\n" - "eta_i2 = %g, eta_j2 = %g\n" - "hi_inv = %g, hj_inv = %g\n" - "a2_Hubble = %g, r2 = %g\n", - pi->id, pj->id, - conv_i, conv_j, - dv_dot_eta_i_Hubble, dv_dot_eta_j_Hubble, - dv_ij[0], dv_ij[1], dv_ij[2], - dv_ji[0], dv_ji[1], dv_ji[2], - eta_i[0], eta_i[1], eta_i[2], - eta_j[0], eta_j[1], eta_j[2], - eta_i2, eta_j2, - hi_inv, hj_inv, - a2_Hubble, r2); - } -#endif + /* Each particle gets its own Q and then weighted by density */ + const hydro_real_t rhoij_inv = 1.0 / (rhoi * rhoj); + + *mu_i = conv * (dv_dot_dx_hat * r * hi) / (r2 + eps2 * (hi * hi)); + *mu_j = conv * (dv_dot_dx_hat * r * hj) / (r2 + eps2 * (hj * hj)); - /* mu_i and mu_j include the Hubble flow */ - *mu_i = conv * dv_dot_eta_i_Hubble / (eta_i2 + const_viscosity_epsilon2); - *mu_j = conv * dv_dot_eta_j_Hubble / (eta_j2 + const_viscosity_epsilon2); - const hydro_real_t q_i_alpha = -const_viscosity_alpha * pi->force.soundspeed * (*mu_i); - const hydro_real_t q_i_beta = const_viscosity_beta * (*mu_i) * (*mu_i); + const hydro_real_t q_i_beta = const_viscosity_beta * (*mu_i) * (*mu_i); 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_j); - const hydro_real_t q_j_beta = const_viscosity_beta * (*mu_j) * (*mu_j); + const hydro_real_t q_j_beta = const_viscosity_beta * (*mu_j) * (*mu_j); const hydro_real_t Q_j = rhoj * (q_j_alpha + q_j_beta); - /* Add viscosity to the pressure */ - return (Q_i + Q_j) * rhoij_inv; + 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 dv_dot_dr_Hubble = - hydro_vec3_vec3_dot(dv_ij, dx_ij) + a2_Hubble * r2; - const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; - const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; - const hydro_real_t c_ij = - 0.5 * (pi->force.soundspeed + pj->force.soundspeed); + const hydro_real_t hi2 = hi * hi; + const hydro_real_t hj2 = hj * hj; + + *mu_i = conv * (dv_dot_dx_hat * r * hi) / (r2 + eps2 * hi2); + *mu_j = conv * (dv_dot_dx_hat * r * hj) / (r2 + eps2 * hj2); + + const hydro_real_t mu_ij = 0.5 * (*mu_i + *mu_j); + 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); - - /* Load mu_i and mu_j */ - *mu_i = mu_ij; - *mu_j = mu_ij; - /* Add viscosity to the pressure */ - return Q_ij * rhoij_inv; + return b_ij * Q_ij * rhoij_inv; + #elif (hydro_props_viscosity_weighting_type == 2) - const hydro_real_t dv_dot_dr_Hubble = - hydro_vec3_vec3_dot(dv_ij, dx_ij) + a2_Hubble * r2; - const hydro_real_t conv = (dv_dot_dr_Hubble < 0.) ? fac_mu : 0.; - const hydro_real_t mu_ij = conv * dv_dot_dr_Hubble * r_inv; - const hydro_real_t c_ij = - 0.5 * (pi->force.soundspeed + pj->force.soundspeed); + /* Particles average symmetrically and arithmetically */ + const hydro_real_t b_ij = 0.5 * (bi + bj); + + const hydro_real_t hi2 = hi * hi; + const hydro_real_t hj2 = hj * hj; + + *mu_i = conv * (dv_dot_dx_hat * r * hi) / (r2 + eps2 * hi2); + *mu_j = conv * (dv_dot_dx_hat * r * hj) / (r2 + eps2 * hj2); + + const hydro_real_t mu_ij = 0.5 * (*mu_i + *mu_j); + 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); - - /* Load mu_i and mu_j */ - *mu_i = mu_ij; - *mu_j = mu_ij; - /* Add viscosity to the pressure */ - return q_ij; + 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 - } /** @@ -1188,10 +1361,11 @@ hydro_real_t hydro_get_visc_signal_velocity(const hydro_real_t *restrict dx, hydro_signal_velocity(dx, pi, pj, mu_i, beta); const hydro_real_t v_sig_visc_j = hydro_signal_velocity(dx_ji, pj, pi, mu_j, beta); - return max(v_sig_visc_i, v_sig_visc_j); + return 0.5 * (v_sig_visc_i + v_sig_visc_j); #else - /* Here mu_i = mu_j */ - return hydro_signal_velocity(dx, pi, pj, mu_i, beta); + /* Here mu_ij is the average */ + const hydro_real_t mu_ij = 0.5 * (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."); @@ -1248,6 +1422,10 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( 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; @@ -1334,6 +1512,10 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* 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]; @@ -1589,6 +1771,11 @@ __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); + /* Copy back over to the particle for later */ p->gradients.u[0] = u_gradient[0]; p->gradients.u[1] = u_gradient[1]; @@ -1599,6 +1786,10 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient( 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]; @@ -1651,17 +1842,23 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( p->density.wcount_dh = 0.f; #ifdef MAGMA2_DEBUG_CHECKS - p->num_ngb = 0; + 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.; @@ -1706,6 +1903,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( p->h_min = p->h; p->v_sig_max = p->force.soundspeed; p->dt_min = p->h_min / p->v_sig_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; @@ -1879,7 +2077,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( const hydro_real_t wcount_inv = 1. / p->gradients.wcount; - p->force.h_dt *= p->h * hydro_dimension_inv * wcount_inv; + p->force.h_dt *= p->force.f * p->h * hydro_dimension_inv * wcount_inv; /* dt_min is in physical units, and requires the kernel_gamma factor for h */ p->dt_min *= kernel_gamma * cosmo->a / cosmo->a_factor_sound_speed; @@ -2008,6 +2206,8 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( 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; diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 8e54c3fad8..6fcea7e2e4 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -86,6 +86,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( adaptive_softening_add_correction_term(pj, xj, hj_inv, mi); + /* For slope limiter */ + const hydro_real_t h_ij = 0.5 * (hi + hj); + pi->gradients.kernel_size = fmax(h_ij, pi->gradients.kernel_size); + pj->gradients.kernel_size = fmax(h_ij, 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; @@ -94,6 +99,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( /* 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; @@ -117,6 +130,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( /* 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; @@ -128,8 +149,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( #ifdef MAGMA2_DEBUG_CHECKS /* Number of neighbors */ - pi->num_ngb++; - pj->num_ngb++; + pi->debug.num_ngb++; + pj->debug.num_ngb++; #endif #ifdef hydro_props_use_adiabatic_correction @@ -178,13 +199,21 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( adaptive_softening_add_correction_term(pi, xi, h_inv, mj); + /* For slope limiter */ + const hydro_real_t h_ij = 0.5 * (hi + hj); + pi->gradients.kernel_size = fmax(h_ij, 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; @@ -200,6 +229,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( /* 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; @@ -208,7 +242,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( #ifdef MAGMA2_DEBUG_CHECKS /* Neighbour number */ - pi->num_ngb++; + pi->debug.num_ngb++; #endif #ifdef hydro_props_use_adiabatic_correction @@ -250,6 +284,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( 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; @@ -285,8 +320,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( 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++) { - const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[k]; pi->gradients.u_hessian[k][i] += du_k * dx[i] * faci; pj->gradients.u_hessian[k][i] += du_k * dx[i] * facj; @@ -351,9 +387,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( /* 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); @@ -378,8 +416,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( 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++) { - const hydro_real_t du_k = pi->gradients.u_aux[k] - pj->gradients.u_aux[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 */ @@ -434,8 +473,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Cosmological factors entering the EoMs */ const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); - const hydro_real_t a_Hubble = a * H; - const hydro_real_t a2_Hubble = a * a_Hubble; + 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; @@ -482,6 +520,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( * 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.; @@ -492,19 +539,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( hydro_real_t cond_du_term = 0.; /* Correction factor must be well-conditioned. */ - const 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 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 = + (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned); - /* Always use SPH gradients between particles if one of them has an - * ill-conditioned C matrix */ - if (C_well_conditioned && D_well_conditioned) { + /* Flag to revert to use high order gradients */ + unsigned char high_order_gradients_flag = 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]}; + /* 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.}; @@ -521,6 +568,58 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 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); + + /* 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; + + /* 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 { + /* 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.}; @@ -534,35 +633,54 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 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 = 0.5 * (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_ij_vec, dx_ji, v_i, + 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_ij_vec, dx_ij, v_j, + 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_raw[3] = {dv[0], dv[1], dv[2]}; + 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] + }; - /* Artificial viscosity */ + /* 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 */ - const hydro_real_t dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], - vi_reconstructed[1] - vj_reconstructed[1], - vi_reconstructed[2] - vj_reconstructed[2]}; hydro_real_t mu_i = 0.; hydro_real_t mu_j = 0.; /* Get the acceleration term, depends on the weighting scheme */ visc_acc_term = - hydro_get_visc_acc_term_and_mu(pi, pj, dv_ij, dx_ij, r2, r_inv, + hydro_get_visc_acc_term_and_mu(pi, pj, dv_ij, dx_ij, r, fac_mu, a2_Hubble, &mu_i, &mu_j); /* Split heating between the two particles */ @@ -580,42 +698,53 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( hydro_real_t ui_reconstructed = 0.; hydro_real_t uj_reconstructed = 0.; - /* First order internal energy gradients are well-conditioned */ - if (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned) { - /* 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); - - /* dx_ji for particle i and dx_ij for particle j */ - hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, - (hydro_real_t)pi->u, - pi->gradients.u, - pi->gradients.u_hessian, - &ui_reconstructed); - - hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ij, - (hydro_real_t)pj->u, - pj->gradients.u, - pj->gradients.u_hessian, - &uj_reconstructed); - } + + /* 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 = 0.5 * (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] + a_Hubble * dx[0], - dv[1] + a_Hubble * dx[1], - dv[2] + a_Hubble * dx[2]}; - const hydro_real_t dv_Hubble_norm = hydro_vec3_norm(dv_Hubble); + const hydro_real_t dv_ij_dx_ij_Hubble = + hydro_vec3_vec3_dot(dv_ij, dx_ij) + a2_Hubble * r2; /* Signal velocity is the sum of pressure and speed contributions */ - const hydro_real_t v_sig_speed = fac_mu * dv_Hubble_norm; + const hydro_real_t v_sig_speed = fac_mu * fabs(dv_ij_dx_ij_Hubble) * r_inv; const hydro_real_t v_sig_pressure = sqrt(fabs(pressurei - pressurej) * rho_ij_inv); const hydro_real_t v_sig_cond = 0.5 * (v_sig_speed + v_sig_pressure); - const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; + /* 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); /* Add conductivity to the specific energy */ cond_du_term = const_conductivity_alpha * v_sig_cond * du_ij * rho_ij_inv; @@ -624,57 +753,91 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Finalize everything with the correct normalizations. */ - /* 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); - - /* For conduction term */ - const hydro_real_t G_ij_norm = hydro_vec3_norm(G_ij); - /* 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, G_ij); + /* Get Hubble flow contribution along dx */ + 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]}; + hydro_real_t dv_ij_Hubble_dot_dx_ij_hat = + hydro_vec3_vec3_dot(dv_ij_Hubble, dx_ij_hat); + const hydro_real_t dv_ij_Hubble_along_dx_ij[3] = { + dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[0], + dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[1], + dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[2] + }; + + /* Get velocity divergence along dx */ + hydro_real_t dv_ij_Hubble_dot_G_rad_ij = + hydro_vec3_vec3_dot(dv_ij_Hubble_along_dx_ij, G_rad_ij); + + /* Evolve the heating terms using the velocity divergence */ sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; - visc_du_term *= dv_dot_G_ij; - cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ + visc_du_term *= dv_ij_Hubble_dot_G_rad_ij; + cond_du_term *= -G_rad_ij_norm; /* Eq. 24 Rosswog 2020 */ + +#ifdef MAGMA2_DEBUG_CHECKS + if (visc_du_term < 0.) { + error("Viscous COOLING for particle %llu with pj %llu: " + "visc_du_term = %g\n" + "dv_ij_Hubble_dot_G_rad_ij = %g, \n" + "dv_ij_Hubble = (%g, %g, %g), \n" + "G_rad_ij = (%g, %g, %g), \n" + "mu_i = %g, mu_j = %g, \n" + "G_ij_norm = %g", + pi->id, pj->id, (double)visc_du_term, + (double)dv_ij_Hubble_dot_G_rad_ij, + (double)dv_ij_Hubble[0], + (double)dv_ij_Hubble[1], + (double)dv_ij_Hubble[2], + (double)G_rad_ij[0], + (double)G_rad_ij[1], + (double)G_rad_ij[2], + (double)mu_i, (double)mu_j, + (double)G_ij_norm); + } +#endif /* Get the time derivative for h. */ - pi->force.h_dt -= dv_dot_G_ij; - pj->force.h_dt -= dv_dot_G_ij; + /* Velocity divergence is from the SPH estimator, not the G_ij vector */ + const hydro_real_t dv_dot_dx = hydro_vec3_vec3_dot(dv, dx); + pi->force.h_dt -= dv_dot_dx * r_inv * wi_dr; + pj->force.h_dt -= dv_dot_dx * r_inv * wj_dr; /* Timestepping */ /* New signal velocity */ - const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); + const hydro_real_t v_sig_max = fmax(v_sig_visc, v_sig_cond); /* Update if we need to */ - pi->v_sig_max = max(pi->v_sig_max, v_sig_max); - pj->v_sig_max = max(pj->v_sig_max, v_sig_max); + pi->v_sig_max = fmax(pi->v_sig_max, v_sig_max); + pj->v_sig_max = fmax(pj->v_sig_max, v_sig_max); /* Average softening in kernel */ const hydro_real_t h_ij = 0.5 * (hi + hj); - pi->h_min = min(pi->h_min, h_ij); - pj->h_min = min(pj->h_min, h_ij); + 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 = pi->h_min / pi->v_sig_max; const hydro_real_t dt_min_j = pj->h_min / pj->v_sig_max; - pi->dt_min = min(pi->dt_min, dt_min_i); - pj->dt_min = min(pj->dt_min, dt_min_j); + 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 { + else { /* Gasoline-like SPH fallback */ - /* Dot product with the distance vector to align - * with dW/dr (dW/dr = r_ij / |r_ij| dot grad W */ - G_ij[0] = dx[0]; - G_ij[1] = dx[1]; - G_ij[2] = dx[2]; /* Compute dv dot dr. */ const hydro_real_t dvdr = hydro_vec3_vec3_dot(dv, G_ij); @@ -683,7 +846,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t dvdr_Hubble = dvdr + a2_Hubble * r2; /* Are the particles moving towards each others ? */ - const hydro_real_t omega_ij = min(dvdr_Hubble, 0.); + const hydro_real_t omega_ij = fmin(dvdr_Hubble, 0.); /* This is 0 or negative */ const hydro_real_t mu_ij = fac_mu * r_inv * omega_ij; @@ -703,28 +866,25 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ - pi->v_sig_max = max(pi->v_sig_max, new_v_sig); - pj->v_sig_max = max(pj->v_sig_max, new_v_sig); + pi->v_sig_max = fmax(pi->v_sig_max, new_v_sig); + pj->v_sig_max = fmax(pj->v_sig_max, new_v_sig); /* Minimum softening in kernel */ const hydro_real_t h_ij = 0.5 * (hi + hj); - pi->h_min = min(pi->h_min, h_ij); - pj->h_min = min(pj->h_min, h_ij); + 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 = pi->h_min / pi->v_sig_max; const hydro_real_t dt_min_j = pj->h_min / pj->v_sig_max; - pi->dt_min = min(pi->dt_min, dt_min_i); - pj->dt_min = min(pj->dt_min, dt_min_j); + pi->dt_min = fmin(pi->dt_min, dt_min_i); + pj->dt_min = fmin(pj->dt_min, dt_min_j); visc_acc_term = visc; visc_du_term = 0.5 * visc_acc_term; - /* Variable smoothing length term */ - const hydro_real_t f_i = pi->force.f; - const hydro_real_t f_j = pj->force.f; const hydro_real_t kernel_gradient = - 0.5 * (f_i * wi_dr + f_j * wj_dr) * r_inv; + 0.5 * (pi->force.f * wi_dr + pj->force.f * wj_dr) * r_inv; visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; @@ -736,22 +896,37 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the time derivative for h. */ pi->force.h_dt -= dvdr * r_inv * wi_dr; pj->force.h_dt -= dvdr * 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 = sph_acc_term + visc_acc_term; + 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 * G_ij[0]; - pi->a_hydro[1] -= mj * acc * G_ij[1]; - pi->a_hydro[2] -= mj * acc * G_ij[2]; + 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]; - pj->a_hydro[0] += mi * acc * G_ij[0]; - pj->a_hydro[1] += mi * acc * G_ij[1]; - pj->a_hydro[2] += mi * acc * G_ij[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; @@ -759,6 +934,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Internal energy time derivative */ pi->u_dt += du_dt_i * mj; pj->u_dt += du_dt_j * mi; + } /** @@ -780,10 +956,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Cosmological factors entering the EoMs */ const hydro_real_t fac_mu = pow_three_gamma_minus_five_over_two(a); - const hydro_real_t a_Hubble = a * H; - const hydro_real_t a2_Hubble = a * a_Hubble; + const hydro_real_t a2_Hubble = a * a * H; - const hydro_real_t r = sqrtf(r2); + const hydro_real_t r = sqrt(r2); const hydro_real_t r_inv = r ? 1.0 / r : 0.0; /* Recover some data */ @@ -814,8 +989,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* 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], @@ -825,6 +1002,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( * 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.; @@ -834,19 +1020,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( hydro_real_t cond_du_term = 0.; /* Correction factor must be well-conditioned. */ - const 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 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 = + (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) { - - /* 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]}; + if (C_well_conditioned && D_well_conditioned && u_well_conditioned) { /* Corrected gradients */ hydro_real_t G_i[3] = {0., 0., 0.}; @@ -863,6 +1050,57 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( 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); + + /* 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; + + /* 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 { + /* 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.}; @@ -875,40 +1113,61 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( 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 = 0.5 * (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_ij_vec, dx_ji, v_i, + 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_ij_vec, dx_ij, v_j, + 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_raw[3] = {dv[0], dv[1], dv[2]}; + 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] + }; - /* Artificial viscosity */ + /* 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); - const hydro_real_t dv_ij[3] = {vi_reconstructed[0] - vj_reconstructed[0], - vi_reconstructed[1] - vj_reconstructed[1], - vi_reconstructed[2] - vj_reconstructed[2]}; + /* Artificial viscosity */ + hydro_real_t mu_i = 0.; hydro_real_t mu_j = 0.; /* Get the acceleration term, depends on the weighting scheme */ visc_acc_term = - hydro_get_visc_acc_term_and_mu(pi, pj, dv_ij, dx_ij, r2, r_inv, + hydro_get_visc_acc_term_and_mu(pi, pj, dv_ij, dx_ij, r, fac_mu, a2_Hubble, &mu_i, &mu_j); + + /* Split heating between the two particles */ visc_du_term = 0.5 * visc_acc_term; - /* Viscous signal velocity */ + /* Viscous signal velocity */ const hydro_real_t v_sig_visc = hydro_get_visc_signal_velocity(dx, pi, pj, mu_i, mu_j, const_viscosity_beta); @@ -920,42 +1179,52 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( hydro_real_t ui_reconstructed = 0.; hydro_real_t uj_reconstructed = 0.; - /* First order internal energy gradients are well-conditioned */ - if (pi->gradients.u_well_conditioned && pj->gradients.u_well_conditioned) { - /* 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); - - /* dx_ji for particle i and dx_ij for particle j */ - hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ji, - (hydro_real_t)pi->u, - pi->gradients.u, - pi->gradients.u_hessian, - &ui_reconstructed); - - hydro_scalar_second_order_reconstruction(phi_ij_scalar, dx_ij, - (hydro_real_t)pj->u, - pj->gradients.u, - pj->gradients.u_hessian, - &uj_reconstructed); - } + /* 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 = 0.5 * (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] + a_Hubble * dx[0], - dv[1] + a_Hubble * dx[1], - dv[2] + a_Hubble * dx[2]}; - const hydro_real_t dv_Hubble_norm = hydro_vec3_norm(dv_Hubble); + const hydro_real_t dv_ij_dx_ij_Hubble = + hydro_vec3_vec3_dot(dv_ij, dx_ij) + a2_Hubble * r2; /* Signal velocity is the sum of pressure and speed contributions */ - const hydro_real_t v_sig_speed = fac_mu * dv_Hubble_norm; + const hydro_real_t v_sig_speed = fac_mu * fabs(dv_ij_dx_ij_Hubble) * r_inv; const hydro_real_t v_sig_pressure = sqrt(fabs(pressurei - pressurej) * rho_ij_inv); const hydro_real_t v_sig_cond = 0.5 * (v_sig_speed + v_sig_pressure); - const hydro_real_t du_ij = ui_reconstructed - uj_reconstructed; + /* 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); /* Add conductivity to the specific energy */ cond_du_term = const_conductivity_alpha * v_sig_cond * du_ij * rho_ij_inv; @@ -964,48 +1233,80 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Finalize the viscosity and conductivity with correct normalizations. */ - /* 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); - - const hydro_real_t G_ij_norm = hydro_vec3_norm(G_ij); - /* 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, G_ij); + const hydro_real_t dv_dot_G_ij = hydro_vec3_vec3_dot(dv, G_ij); + + /* Get Hubble flow along dx */ + 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_dot_dx_ij_hat = + hydro_vec3_vec3_dot(dv_ij_Hubble, dx_ij_hat); + const hydro_real_t dv_ij_Hubble_along_dx_ij[3] = { + dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[0], + dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[1], + dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[2] + }; + + /* Get Hubble flow contribution */ + hydro_real_t dv_ij_Hubble_dot_G_rad_ij = + hydro_vec3_vec3_dot(dv_ij_Hubble_along_dx_ij, G_rad_ij); sph_du_term_i *= dv_dot_G_ij; - visc_du_term *= dv_dot_G_ij; - cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ + visc_du_term *= dv_ij_Hubble_dot_G_rad_ij; + cond_du_term *= -G_rad_ij_norm; /* Eq. 24 Rosswog 2020 */ + +#ifdef MAGMA2_DEBUG_CHECKS + if (visc_du_term < 0.) { + error("Viscous COOLING for particle %llu with pj %llu: visc_du_term = %g\n" + "dv_ij_Hubble_dot_G_rad_ij = %g, \n" + "dv_ij_Hubble = (%g, %g, %g), \n" + "G_rad_ij = (%g, %g, %g), \n" + "mu_i = %g, mu_j = %g, \n" + "G_ij_norm = %g", + pi->id, pj->id, (double)visc_du_term, + (double)dv_ij_Hubble_dot_G_rad_ij, + (double)dv_ij_Hubble[0], + (double)dv_ij_Hubble[1], + (double)dv_ij_Hubble[2], + (double)G_rad_ij[0], + (double)G_rad_ij[1], + (double)G_rad_ij[2], + (double)mu_i, (double)mu_j, + (double)G_ij_norm); + } +#endif /* Get the time derivative for h. */ - pi->force.h_dt -= dv_dot_G_ij; + const hydro_real_t dv_dot_dx = hydro_vec3_vec3_dot(dv, dx); + pi->force.h_dt -= dv_dot_dx * r_inv * wi_dr; /* Timestepping */ /* New signal velocity */ - const hydro_real_t v_sig_max = max(v_sig_visc, v_sig_cond); + const hydro_real_t v_sig_max = fmax(v_sig_visc, v_sig_cond); /* Update if we need to */ - pi->v_sig_max = max(pi->v_sig_max, v_sig_max); + pi->v_sig_max = fmax(pi->v_sig_max, v_sig_max); /* Compute new timestep */ const hydro_real_t h_ij = 0.5 * (hi + hj); - pi->h_min = min(pi->h_min, h_ij); + pi->h_min = fmin(pi->h_min, h_ij); const hydro_real_t dt_min_i = pi->h_min / pi->v_sig_max; - pi->dt_min = min(pi->dt_min, dt_min_i); + pi->dt_min = fmin(pi->dt_min, dt_min_i); + +#ifdef MAGMA2_DEBUG_CHECKS + pi->debug.N_force_high_order_grad++; +#endif } - else { - - /* Fallback SPH points in the direction between the particles. */ - G_ij[0] = dx[0]; - G_ij[1] = dx[1]; - G_ij[2] = dx[2]; + else { /* Gasoline2 SPH fallback*/ + /* Compute dv dot dr. */ const hydro_real_t dvdr = hydro_vec3_vec3_dot(dv, G_ij); @@ -1014,7 +1315,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t dvdr_Hubble = dvdr + a2_Hubble * r2; /* Are the particles moving towards each others ? */ - const hydro_real_t omega_ij = min(dvdr_Hubble, 0.); + const hydro_real_t omega_ij = fmin(dvdr_Hubble, 0.); /* This is 0 or negative */ const hydro_real_t mu_ij = fac_mu * r_inv * omega_ij; @@ -1034,23 +1335,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); /* Update if we need to */ - pi->v_sig_max = max(pi->v_sig_max, new_v_sig); + pi->v_sig_max = fmax(pi->v_sig_max, new_v_sig); /* Minimum softening in kernel */ const hydro_real_t h_ij = 0.5 * (hi + hj); - pi->h_min = min(pi->h_min, h_ij); + pi->h_min = fmin(pi->h_min, h_ij); /* New time-step estimate */ const hydro_real_t dt_min_i = pi->h_min / pi->v_sig_max; - pi->dt_min = min(pi->dt_min, dt_min_i); + pi->dt_min = fmin(pi->dt_min, dt_min_i); visc_acc_term = visc; visc_du_term = 0.5 * visc_acc_term; - const hydro_real_t hj_inv_dim_plus_one = hj_inv * hj_inv_dim; - const hydro_real_t wj_dr = hj_inv_dim_plus_one * wj_dx; - /* Variable smoothing length term */ const hydro_real_t f_i = pi->force.f; const hydro_real_t f_j = pj->force.f; @@ -1064,15 +1362,24 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get the time derivative for h. */ pi->force.h_dt -= dvdr * r_inv * wi_dr; + +#ifdef MAGMA2_DEBUG_CHECKS + pi->debug.N_force_low_order_grad++; +#endif } /* Assemble the acceleration */ - const hydro_real_t acc = sph_acc_term + visc_acc_term; + /* 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 * G_ij[0]; - pi->a_hydro[1] -= mj * acc * G_ij[1]; - pi->a_hydro[2] -= mj * acc * G_ij[2]; + 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; diff --git a/src/hydro/MAGMA2/hydro_io.h b/src/hydro/MAGMA2/hydro_io.h index 97efa6917d..2a4534e3a2 100644 --- a/src/hydro/MAGMA2/hydro_io.h +++ b/src/hydro/MAGMA2/hydro_io.h @@ -239,6 +239,24 @@ INLINE static void hydro_write_particles(const struct part* parts, 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, diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 3ad7d86bda..59c947e606 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -50,10 +50,10 @@ /*! Alpha viscosity, usually =1.0. For lower N_ngb, should be higher */ -#define const_viscosity_alpha 2.0 +#define const_viscosity_alpha 1.0 /*! Alpha conductivity, usually =0.05. At lower N_ngb, should be higher */ -#define const_conductivity_alpha 0.1 +#define const_conductivity_alpha 0.05 /*! Desired number of neighbours -- CRITICAL that this matches hydro props */ #if defined(HYDRO_DIMENSION_1D) @@ -67,12 +67,20 @@ /* ---------- These parameters should not be changed ---------- */ +/* Slope limiter length */ +#define hydro_props_grad_overshoot_length 0.25 + +/*! Slope limiter tolerance */ +#define hydro_props_grad_overshoot_tolerance 0.1 /* 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 1 +#define hydro_props_viscosity_weighting_type 2 + +/* Viscosity floor when G_ij is extremely misaligned with dx_ij */ +#define const_viscosity_cosine_limit 0.7 /*! Use the correction terms to make the internal energy match the mass flux */ //#define hydro_props_use_adiabatic_correction @@ -104,13 +112,13 @@ #ifdef hydro_props_use_double_precision /*! Consider matrix inversion to be ill-conditioned above this limit */ -#define const_condition_number_upper_limit 99999. +#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 999. +#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.)) diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 677d0fc207..db14cab6ba 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -129,11 +129,6 @@ struct part { /*! Particle density. */ float rho; -#ifdef MAGMA2_DEBUG_CHECKS - /*! Number of neighbors in the kernel */ - int num_ngb; -#endif - /*! Minimum smoothing length in the kernel */ float h_min; @@ -169,6 +164,15 @@ struct part { /*! 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 @@ -221,6 +225,24 @@ struct part { /*! 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. */ diff --git a/src/sink/GEAR/sink.h b/src/sink/GEAR/sink.h index 47baafcffc..8f46702ecb 100644 --- a/src/sink/GEAR/sink.h +++ b/src/sink/GEAR/sink.h @@ -458,7 +458,7 @@ INLINE static int sink_is_forming( const float maximal_density_threshold = sink_props->maximal_density_threshold; const float density = hydro_get_physical_density(p, cosmo); - const float div_v = sink_get_physical_div_v_from_part(p); + const float div_v = sink_get_physical_div_v_from_part(p, cosmo); const float h = p->h; const float sink_cut_off_radius = sink_props->cut_off_radius; diff --git a/src/sink/GEAR/sink_getters.h b/src/sink/GEAR/sink_getters.h index c25199cb1b..beb75dda3a 100644 --- a/src/sink/GEAR/sink_getters.h +++ b/src/sink/GEAR/sink_getters.h @@ -82,7 +82,8 @@ INLINE static double sink_compute_neighbour_rotation_energy_magnitude( * */ INLINE static float sink_get_physical_div_v_from_part( - const struct part* restrict p) { + const struct part* restrict p, + const struct cosmology* cosmo) { float div_v = 0.0; @@ -108,9 +109,7 @@ INLINE static float sink_get_physical_div_v_from_part( p->viscosity.velocity_gradient[2][2]); #elif MAGMA2_SPH /* Copy the velocity divergence */ - div_v = p->gradients.velocity_tensor_aux[0][0] + - p->gradients.velocity_tensor_aux[1][1] + - p->gradients.velocity_tensor_aux[2][2]; + div_v = hydro_get_physical_div_v(p, cosmo); #elif HOPKINS_PU_SPH div_v = p->density.div_v; #else diff --git a/src/star_formation/GEAR/star_formation.h b/src/star_formation/GEAR/star_formation.h index 73cc26c3fd..f0712925d6 100644 --- a/src/star_formation/GEAR/star_formation.h +++ b/src/star_formation/GEAR/star_formation.h @@ -450,9 +450,7 @@ __attribute__((always_inline)) INLINE static void star_formation_end_density( p->viscosity.velocity_gradient[2][2]); #elif MAGMA2_SPH /* Copy the velocity divergence */ - xp->sf_data.div_v = p->gradients.velocity_tensor_aux[0][0] + - p->gradients.velocity_tensor_aux[1][1] + - p->gradients.velocity_tensor_aux[2][2]; + 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; #else From 6e3f4a98e8a9473a8524d8657354074f39ed0a77 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Mon, 18 Aug 2025 17:03:59 -0400 Subject: [PATCH 34/39] Fixes to some of the symmetry issues and gradient estimation. --- src/hydro/MAGMA2/hydro.h | 18 ++-- src/hydro/MAGMA2/hydro_iact.h | 122 ++++++++++++++++------------ src/hydro/MAGMA2/hydro_parameters.h | 21 +++-- 3 files changed, 96 insertions(+), 65 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 8d4ccef43c..914073880b 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -773,6 +773,7 @@ hydro_real_t hydro_scalar_minmod_limiter(const hydro_real_t df_reconstructed, const hydro_real_t df_min_j, const hydro_real_t df_max_j) { +#ifdef hydro_props_use_strict_minmod_limiter const hydro_real_t lo0 = fmax(df_min_i, -df_max_j); const hydro_real_t hi0 = fmin(df_max_i, -df_min_j); const hydro_real_t lo = fmin(lo0, hi0); @@ -786,6 +787,9 @@ hydro_real_t hydro_scalar_minmod_limiter(const hydro_real_t df_reconstructed, if ((df > 0) != (df_raw > 0)) df = df_raw; return df; +#else + return df_reconstructed; +#endif } /** @@ -836,7 +840,7 @@ void hydro_vec_slope_limiter(const hydro_real_t df_min, hydro_real_t *restrict grad) { const hydro_real_t grad_norm = hydro_vec3_norm(grad); - const hydro_real_t length = hydro_props_grad_overshoot_length * kernel_size; + 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.) { @@ -846,8 +850,8 @@ void hydro_vec_slope_limiter(const hydro_real_t df_min, hydro_real_t bound = df_abs_min; -#ifdef hydro_props_grad_overshoot_tolerance - const hydro_real_t tolerance = hydro_props_grad_overshoot_tolerance; +#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; @@ -883,8 +887,8 @@ hydro_real_t hydro_van_leer_phi(const hydro_real_t A_ij, const hydro_real_t eta_j) { hydro_real_t phi_raw = (4. * A_ij) / ((1. + A_ij) * (1. + A_ij)); - phi_raw = min(1., phi_raw); - phi_raw = max(0., phi_raw); + 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); @@ -899,8 +903,8 @@ hydro_real_t hydro_van_leer_phi(const hydro_real_t A_ij, phi_raw *= damping; /* Handle any edge cases */ - phi_raw = min(1., phi_raw); - phi_raw = max(0., phi_raw); + phi_raw = fmin(1., phi_raw); + phi_raw = fmax(0., phi_raw); return phi_raw; } diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 6fcea7e2e4..284197650f 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -87,9 +87,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( adaptive_softening_add_correction_term(pj, xj, hj_inv, mi); /* For slope limiter */ - const hydro_real_t h_ij = 0.5 * (hi + hj); - pi->gradients.kernel_size = fmax(h_ij, pi->gradients.kernel_size); - pj->gradients.kernel_size = fmax(h_ij, pj->gradients.kernel_size); + 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; @@ -200,8 +199,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( adaptive_softening_add_correction_term(pi, xi, h_inv, mj); /* For slope limiter */ - const hydro_real_t h_ij = 0.5 * (hi + hj); - pi->gradients.kernel_size = fmax(h_ij, pi->gradients.kernel_size); + 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; @@ -284,7 +282,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( 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; @@ -387,11 +384,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( /* 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); @@ -596,10 +591,16 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Make sure we use the correct interaction */ high_order_gradients_flag = 1; +#ifdef hydro_props_use_radial_viscosity /* 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 */ @@ -660,9 +661,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t dv_raw[3] = {dv[0], dv[1], dv[2]}; 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[0] - vj_reconstructed[0], + vi_reconstructed[1] - vj_reconstructed[1], + vi_reconstructed[2] - vj_reconstructed[2] }; /* Get velocity difference, but limit reconstructed values */ @@ -757,41 +758,43 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t dv_dot_G_ij = hydro_vec3_vec3_dot(dv, G_ij); /* Get Hubble flow contribution along dx */ - 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]}; - hydro_real_t dv_ij_Hubble_dot_dx_ij_hat = - hydro_vec3_vec3_dot(dv_ij_Hubble, dx_ij_hat); - const hydro_real_t dv_ij_Hubble_along_dx_ij[3] = { - dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[0], - dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[1], - dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[2] + 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]}; + hydro_real_t dv_Hubble_dot_dx_ij_hat = + hydro_vec3_vec3_dot(dv_Hubble, dx_ij_hat); + 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 velocity divergence along dx */ - hydro_real_t dv_ij_Hubble_dot_G_rad_ij = - hydro_vec3_vec3_dot(dv_ij_Hubble_along_dx_ij, 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); /* Evolve the heating terms using the velocity divergence */ sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; - visc_du_term *= dv_ij_Hubble_dot_G_rad_ij; + visc_du_term *= fmax(dv_Hubble_dot_G_rad_ij, 0.); cond_du_term *= -G_rad_ij_norm; /* Eq. 24 Rosswog 2020 */ + if (visc_du_term <= 0.) visc_acc_term = 0.; + #ifdef MAGMA2_DEBUG_CHECKS if (visc_du_term < 0.) { error("Viscous COOLING for particle %llu with pj %llu: " "visc_du_term = %g\n" - "dv_ij_Hubble_dot_G_rad_ij = %g, \n" - "dv_ij_Hubble = (%g, %g, %g), \n" + "dv_Hubble_dot_G_rad_ij = %g, \n" + "dv_Hubble = (%g, %g, %g), \n" "G_rad_ij = (%g, %g, %g), \n" "mu_i = %g, mu_j = %g, \n" "G_ij_norm = %g", pi->id, pj->id, (double)visc_du_term, - (double)dv_ij_Hubble_dot_G_rad_ij, - (double)dv_ij_Hubble[0], - (double)dv_ij_Hubble[1], - (double)dv_ij_Hubble[2], + (double)dv_Hubble_dot_G_rad_ij, + (double)dv_Hubble[0], + (double)dv_Hubble[1], + (double)dv_Hubble[2], (double)G_rad_ij[0], (double)G_rad_ij[1], (double)G_rad_ij[2], @@ -804,10 +807,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the time derivative for h. */ +#ifdef hydro_props_use_high_order_gradients_in_h_dt + pi->force.h_dt -= dv_dot_G_ij; + pj->force.h_dt -= dv_dot_G_ij; +#else /* Velocity divergence is from the SPH estimator, not the G_ij vector */ const hydro_real_t dv_dot_dx = hydro_vec3_vec3_dot(dv, dx); pi->force.h_dt -= dv_dot_dx * r_inv * wi_dr; pj->force.h_dt -= dv_dot_dx * r_inv * wj_dr; +#endif /* Timestepping */ @@ -909,9 +917,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* 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[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 ! */ @@ -1077,10 +1085,16 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Make sure we use the correct gradients below */ high_order_gradients_flag = 1; +#ifdef hydro_props_use_radial_viscosity /* 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 */ @@ -1237,38 +1251,40 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t dv_dot_G_ij = hydro_vec3_vec3_dot(dv, G_ij); /* Get Hubble flow along dx */ - 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_dot_dx_ij_hat = - hydro_vec3_vec3_dot(dv_ij_Hubble, dx_ij_hat); - const hydro_real_t dv_ij_Hubble_along_dx_ij[3] = { - dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[0], - dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[1], - dv_ij_Hubble_dot_dx_ij_hat * dx_ij_hat[2] + 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_Hubble_dot_dx_ij_hat = + hydro_vec3_vec3_dot(dv_Hubble, dx_ij_hat); + 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 */ - hydro_real_t dv_ij_Hubble_dot_G_rad_ij = - hydro_vec3_vec3_dot(dv_ij_Hubble_along_dx_ij, 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); sph_du_term_i *= dv_dot_G_ij; - visc_du_term *= dv_ij_Hubble_dot_G_rad_ij; + visc_du_term *= fmax(dv_Hubble_dot_G_rad_ij, 0.); cond_du_term *= -G_rad_ij_norm; /* Eq. 24 Rosswog 2020 */ + if (visc_du_term <= 0.) visc_acc_term = 0.; + #ifdef MAGMA2_DEBUG_CHECKS if (visc_du_term < 0.) { error("Viscous COOLING for particle %llu with pj %llu: visc_du_term = %g\n" - "dv_ij_Hubble_dot_G_rad_ij = %g, \n" - "dv_ij_Hubble = (%g, %g, %g), \n" + "dv_Hubble_dot_G_rad_ij = %g, \n" + "dv_Hubble = (%g, %g, %g), \n" "G_rad_ij = (%g, %g, %g), \n" "mu_i = %g, mu_j = %g, \n" "G_ij_norm = %g", pi->id, pj->id, (double)visc_du_term, - (double)dv_ij_Hubble_dot_G_rad_ij, - (double)dv_ij_Hubble[0], - (double)dv_ij_Hubble[1], - (double)dv_ij_Hubble[2], + (double)dv_Hubble_dot_G_rad_ij, + (double)dv_Hubble[0], + (double)dv_Hubble[1], + (double)dv_Hubble[2], (double)G_rad_ij[0], (double)G_rad_ij[1], (double)G_rad_ij[2], @@ -1281,9 +1297,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get the time derivative for h. */ +#ifdef hydro_props_use_high_order_gradients_in_h_dt + pi->force.h_dt -= dv_dot_G_ij; +#else const hydro_real_t dv_dot_dx = hydro_vec3_vec3_dot(dv, dx); pi->force.h_dt -= dv_dot_dx * r_inv * wi_dr; - +#endif /* Timestepping */ @@ -1368,7 +1387,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( #endif } - /* Assemble the acceleration */ /* 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_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 59c947e606..3033ea049f 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -50,7 +50,7 @@ /*! Alpha viscosity, usually =1.0. For lower N_ngb, should be higher */ -#define const_viscosity_alpha 1.0 +#define const_viscosity_alpha 2.0 /*! Alpha conductivity, usually =0.05. At lower N_ngb, should be higher */ #define const_conductivity_alpha 0.05 @@ -67,11 +67,14 @@ /* ---------- These parameters should not be changed ---------- */ -/* Slope limiter length */ -#define hydro_props_grad_overshoot_length 0.25 +/* Slope limiter length, fraction of max. distance in kernel */ +#define const_grad_overshoot_length 0.25 /*! Slope limiter tolerance */ -#define hydro_props_grad_overshoot_tolerance 0.1 +//#define const_grad_overshoot_tolerance 0.1 + +/* Viscosity floor when G_ij is extremely misaligned with dx_ij */ +#define const_viscosity_cosine_limit 0.1736481 //0.7 /* Viscosity weighting scheme: * 0 = (rho_i * q_i + rho_j * q_j) / (rho_i * rho_j) @@ -79,8 +82,14 @@ * 2 = 2.0 * q_ij / (rho_i + rho_j) */ #define hydro_props_viscosity_weighting_type 2 -/* Viscosity floor when G_ij is extremely misaligned with dx_ij */ -#define const_viscosity_cosine_limit 0.7 +/* Flag to use radial gradients for viscosity and conductivity */ +#define hydro_props_use_radial_viscosity + +/* Flag to disallow sign flip in reconstructed quantities */ +//#define hydro_props_use_strict_minmod_limiter + +/* Use dv_ij dot G_ij in the h_dt evolution term */ +#define hydro_props_use_high_order_gradients_in_h_dt /*! Use the correction terms to make the internal energy match the mass flux */ //#define hydro_props_use_adiabatic_correction From ff0634536472668f6b2eed5e135d104299f285a3 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Wed, 20 Aug 2025 10:02:47 -0400 Subject: [PATCH 35/39] Added some extra compilation options to test getting cosmological simulations to work. New updates to the best parameters. --- src/hydro/MAGMA2/hydro.h | 3 + src/hydro/MAGMA2/hydro_iact.h | 127 ++++++++++++++++++++-------- src/hydro/MAGMA2/hydro_parameters.h | 24 +++--- 3 files changed, 110 insertions(+), 44 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 914073880b..1349f78e24 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -839,6 +839,7 @@ void hydro_vec_slope_limiter(const hydro_real_t df_min, 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; @@ -870,6 +871,8 @@ void hydro_vec_slope_limiter(const hydro_real_t df_min, grad[2] *= limiter; } } +#endif + } /** diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 284197650f..50c40fb4dd 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -517,7 +517,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 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]}; @@ -567,6 +566,35 @@ __attribute__((always_inline)) INLINE static void runner_iact_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 */ + 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); @@ -614,8 +642,6 @@ __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) */ @@ -730,14 +756,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 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_ij_dx_ij_Hubble = - hydro_vec3_vec3_dot(dv_ij, dx_ij) + a2_Hubble * r2; + 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 is the sum of pressure and speed contributions */ - const hydro_real_t v_sig_speed = fac_mu * fabs(dv_ij_dx_ij_Hubble) * r_inv; - const hydro_real_t v_sig_pressure = - sqrt(fabs(pressurei - pressurej) * rho_ij_inv); - const hydro_real_t v_sig_cond = 0.5 * (v_sig_speed + v_sig_pressure); + const hydro_real_t v_sig_cond = fac_mu * hydro_vec3_norm(dv_ij_Hubble); /* Get spec. energy difference, but limit reconstructed values */ const hydro_real_t du_raw = pi->u - pj->u; @@ -750,6 +776,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Add conductivity to the specific energy */ cond_du_term = const_conductivity_alpha * v_sig_cond * du_ij * rho_ij_inv; + /* Use the Balsara limiter */ + const hydro_real_t b_ij = + 0.5 * (pi->gradients.balsara + pj->gradients.balsara); + cond_du_term *= b_ij; + /* Finalize everything with the correct normalizations. */ @@ -776,10 +807,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Evolve the heating terms using the velocity divergence */ sph_du_term_i *= dv_dot_G_ij; sph_du_term_j *= dv_dot_G_ij; - visc_du_term *= fmax(dv_Hubble_dot_G_rad_ij, 0.); - cond_du_term *= -G_rad_ij_norm; /* Eq. 24 Rosswog 2020 */ + cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ + visc_du_term *= dv_Hubble_dot_G_rad_ij; - if (visc_du_term <= 0.) visc_acc_term = 0.; + if (visc_du_term <= 0.) { + visc_acc_term = 0.; + visc_du_term = 0.; + } #ifdef MAGMA2_DEBUG_CHECKS if (visc_du_term < 0.) { @@ -807,15 +841,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Get the time derivative for h. */ -#ifdef hydro_props_use_high_order_gradients_in_h_dt - pi->force.h_dt -= dv_dot_G_ij; - pj->force.h_dt -= dv_dot_G_ij; -#else /* Velocity divergence is from the SPH estimator, not the G_ij vector */ const hydro_real_t dv_dot_dx = hydro_vec3_vec3_dot(dv, dx); pi->force.h_dt -= dv_dot_dx * r_inv * wi_dr; pj->force.h_dt -= dv_dot_dx * r_inv * wj_dr; -#endif /* Timestepping */ @@ -1012,7 +1041,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( 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]}; @@ -1062,6 +1090,35 @@ __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 */ + 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); @@ -1082,6 +1139,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* 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; @@ -1108,8 +1166,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_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) */ @@ -1223,14 +1279,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( 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_ij_dx_ij_Hubble = - hydro_vec3_vec3_dot(dv_ij, dx_ij) + a2_Hubble * r2; + 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 is the sum of pressure and speed contributions */ - const hydro_real_t v_sig_speed = fac_mu * fabs(dv_ij_dx_ij_Hubble) * r_inv; - const hydro_real_t v_sig_pressure = - sqrt(fabs(pressurei - pressurej) * rho_ij_inv); - const hydro_real_t v_sig_cond = 0.5 * (v_sig_speed + v_sig_pressure); + const hydro_real_t v_sig_cond = fac_mu * hydro_vec3_norm(dv_ij_Hubble); /* Get spec. energy difference, but limit reconstructed values */ const hydro_real_t du_raw = pi->u - pj->u; @@ -1242,6 +1298,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Add conductivity to the specific energy */ cond_du_term = const_conductivity_alpha * v_sig_cond * du_ij * rho_ij_inv; + + /* Use the Balsara limiter */ + const hydro_real_t b_ij = + 0.5 * (pi->gradients.balsara + pj->gradients.balsara); + cond_du_term *= b_ij; /* Finalize the viscosity and conductivity with correct normalizations. */ @@ -1267,10 +1328,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( hydro_vec3_vec3_dot(dv_Hubble_along_dx_ij, G_rad_ij); sph_du_term_i *= dv_dot_G_ij; - visc_du_term *= fmax(dv_Hubble_dot_G_rad_ij, 0.); - cond_du_term *= -G_rad_ij_norm; /* Eq. 24 Rosswog 2020 */ + cond_du_term *= -G_ij_norm; /* Eq. 24 Rosswog 2020 */ + visc_du_term *= dv_Hubble_dot_G_rad_ij; - if (visc_du_term <= 0.) visc_acc_term = 0.; + if (visc_du_term <= 0.) { + visc_acc_term = 0.; + visc_du_term = 0.; + } #ifdef MAGMA2_DEBUG_CHECKS if (visc_du_term < 0.) { @@ -1297,12 +1361,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Get the time derivative for h. */ -#ifdef hydro_props_use_high_order_gradients_in_h_dt - pi->force.h_dt -= dv_dot_G_ij; -#else const hydro_real_t dv_dot_dx = hydro_vec3_vec3_dot(dv, dx); pi->force.h_dt -= dv_dot_dx * r_inv * wi_dr; -#endif + /* Timestepping */ diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 3033ea049f..b790a23588 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -53,7 +53,7 @@ #define const_viscosity_alpha 2.0 /*! Alpha conductivity, usually =0.05. At lower N_ngb, should be higher */ -#define const_conductivity_alpha 0.05 +#define const_conductivity_alpha 0.5 /*! Desired number of neighbours -- CRITICAL that this matches hydro props */ #if defined(HYDRO_DIMENSION_1D) @@ -67,14 +67,22 @@ /* ---------- These parameters should not be changed ---------- */ +/*! 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.1 +#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.1736481 //0.7 +#define const_viscosity_cosine_limit 0.7 /* Viscosity weighting scheme: * 0 = (rho_i * q_i + rho_j * q_j) / (rho_i * rho_j) @@ -83,13 +91,7 @@ #define hydro_props_viscosity_weighting_type 2 /* Flag to use radial gradients for viscosity and conductivity */ -#define hydro_props_use_radial_viscosity - -/* Flag to disallow sign flip in reconstructed quantities */ -//#define hydro_props_use_strict_minmod_limiter - -/* Use dv_ij dot G_ij in the h_dt evolution term */ -#define hydro_props_use_high_order_gradients_in_h_dt +//#define hydro_props_use_radial_viscosity /*! Use the correction terms to make the internal energy match the mass flux */ //#define hydro_props_use_adiabatic_correction @@ -144,7 +146,7 @@ /*! 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 (1.5*const_viscosity_alpha) /* ---------- Structures for below ---------- */ From dcfc371e24056218006a6a67ecc38299ef186518 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Wed, 20 Aug 2025 20:23:29 -0400 Subject: [PATCH 36/39] More changes to how viscosity and conductivity are done. --- src/hydro/MAGMA2/hydro.h | 111 +++++++-- src/hydro/MAGMA2/hydro_iact.h | 370 ++++++++++++++++++---------- src/hydro/MAGMA2/hydro_io.h | 12 + src/hydro/MAGMA2/hydro_parameters.h | 17 +- src/hydro/MAGMA2/hydro_part.h | 5 + 5 files changed, 358 insertions(+), 157 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 1349f78e24..e87a012b63 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -463,8 +463,9 @@ __attribute__((always_inline)) INLINE static float hydro_signal_velocity( const float ci = pi->force.soundspeed; const float cj = pj->force.soundspeed; + const float c_ij = 0.5 * (ci + cj); - return 0.5f * (ci + cj - beta * mu_ij); + return 1.25 * (c_ij + 0.75 * (const_viscosity_alpha * c_ij - beta * mu_ij)); } /** @@ -531,6 +532,8 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->density.rho_dh = 0.f; #ifdef MAGMA2_DEBUG_CHECKS p->debug.num_ngb = 0; + p->debug.v_sig_visc_max = 0.; + p->debug.v_sig_cond_max = 0.; #endif #ifdef hydro_props_use_adiabatic_correction @@ -541,8 +544,8 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->gradients.u_well_conditioned = 1; p->gradients.D_well_conditioned = 1; - p->gradients.du_min = 0.; - p->gradients.du_max = 0.; + 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 */ @@ -550,8 +553,8 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( 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.; + 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.; @@ -754,6 +757,22 @@ __attribute__((always_inline)) INLINE static void hydro_vec3_vec3_outer( 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. * @@ -774,20 +793,20 @@ hydro_real_t hydro_scalar_minmod_limiter(const hydro_real_t df_reconstructed, const hydro_real_t df_max_j) { #ifdef hydro_props_use_strict_minmod_limiter - const hydro_real_t lo0 = fmax(df_min_i, -df_max_j); - const hydro_real_t hi0 = fmin(df_max_i, -df_min_j); - const hydro_real_t lo = fmin(lo0, hi0); - const hydro_real_t hi = fmax(lo0, hi0); + 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; - hydro_real_t df = df_reconstructed; if (df < lo) df = lo; if (df > hi) df = hi; - /* If sign flips trust the original estimate over reconstructed */ - if ((df > 0) != (df_raw > 0)) df = df_raw; - return df; #else + if (df_raw * df_reconstructed < 0.) return df_raw; + return df_reconstructed; #endif } @@ -822,6 +841,13 @@ void hydro_vec_minmod_limiter(const hydro_real_t *restrict dv_reconstructed, 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.; + } } /** @@ -928,8 +954,11 @@ hydro_real_t hydro_scalar_van_leer_A(const hydro_real_t *restrict grad_i, 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 (grad_dot_x_j == 0.) return 0.; + 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; @@ -955,8 +984,11 @@ hydro_real_t hydro_vector_van_leer_A(const hydro_real_t (*restrict grad_i)[3], 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 (grad_dot_x_x_j == 0.) return 0.; + 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; @@ -1093,6 +1125,7 @@ __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.; @@ -1116,6 +1149,9 @@ hydro_real_t hydro_get_balsara_limiter(const struct part *restrict p, } return balsara; +#else + return 1.; +#endif } /** @@ -1343,6 +1379,29 @@ hydro_real_t hydro_get_visc_acc_term_and_mu(const struct part *restrict pi, #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. * @@ -1354,24 +1413,23 @@ hydro_real_t hydro_get_visc_acc_term_and_mu(const struct part *restrict pi, * @param mu_j The velocity jump indicator at pj */ __attribute__((always_inline)) INLINE static -hydro_real_t hydro_get_visc_signal_velocity(const hydro_real_t *restrict dx, +hydro_real_t hydro_get_visc_signal_velocity(const float *restrict dx, const struct part *restrict pi, const struct part *restrict pj, - const hydro_real_t mu_i, - const hydro_real_t mu_j, - const hydro_real_t beta) { + 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 hydro_real_t dx_ji[3] = {-dx[0], -dx[1], -dx[2]}; - const hydro_real_t v_sig_visc_i = + 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 hydro_real_t v_sig_visc_j = + 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 - /* Here mu_ij is the average */ - const hydro_real_t mu_ij = 0.5 * (mu_i + mu_j); + const float mu_ij = 0.5f * (mu_i + mu_j); return hydro_signal_velocity(dx, pi, pj, mu_ij, beta); #endif #else @@ -1850,6 +1908,8 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( #ifdef MAGMA2_DEBUG_CHECKS p->debug.num_ngb = 0; + p->debug.v_sig_visc_max = 0; + p->debug.v_sig_cond_max = 0; #endif p->gradients.C_well_conditioned = 0; p->gradients.D_well_conditioned = 0; @@ -1908,7 +1968,8 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* 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 = p->h; - p->v_sig_max = p->force.soundspeed; + p->v_sig_max = 1.25 * p->force.soundspeed + + 0.75 * const_viscosity_alpha * p->force.soundspeed; p->dt_min = p->h_min / p->v_sig_max; p->gradients.balsara = hydro_get_balsara_limiter(p, cosmo); diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 50c40fb4dd..0e98664a97 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -510,6 +510,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 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 @@ -517,6 +518,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 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]}; @@ -619,7 +621,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Make sure we use the correct interaction */ high_order_gradients_flag = 1; -#ifdef hydro_props_use_radial_viscosity +#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]; @@ -666,7 +668,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( dx_ji, xj, xi); /* Make sure no floating point problems */ - hydro_real_t phi_vec_sym = 0.5 * (phi_ij_vec + phi_ji_vec); + 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); @@ -685,7 +687,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->gradients.velocity_hessian, vj_reconstructed); - const hydro_real_t dv_raw[3] = {dv[0], dv[1], dv[2]}; const hydro_real_t dv_reconstructed[3] = { vi_reconstructed[0] - vj_reconstructed[0], vi_reconstructed[1] - vj_reconstructed[1], @@ -737,7 +738,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( dx_ji, xj, xi); /* Make sure no floating point problems */ - hydro_real_t phi_scalar_sym = 0.5 * (phi_ij_scalar + phi_ji_scalar); + 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); @@ -761,53 +762,95 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( dv_ij[1] + a2_Hubble * dx_ij[1], dv_ij[2] + a2_Hubble * dx_ij[2] }; + const hydro_real_t dv_ij_Hubble_dot_dx_ij_hat = + hydro_vec3_vec3_dot(dv_ij_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 */ + const hydro_real_t eps2 = const_viscosity_epsilon2; + const hydro_real_t faci = fac_mu * r * hi / (r2 + eps2 * hi * hi); + const hydro_real_t facj = fac_mu * r * hj / (r2 + eps2 * hj * hj); + hydro_real_t mu_cond_i = faci * dv_ij_Hubble_dot_dx_ij_hat; + hydro_real_t mu_cond_j = facj * dv_ij_Hubble_dot_dx_ij_hat; + const hydro_real_t mu_cond_ij = 0.5 * (mu_cond_i + mu_cond_j); + const hydro_real_t v_sig_beta = 0.75 * const_viscosity_beta * mu_cond_ij; + + /* Skip conduction if expansion beats sound speed along LOS */ + if (v_sig_alpha > v_sig_beta) { + + /* Signal velocity from speed contributions */ +#ifdef hydro_props_use_radial_artificial_terms + const hydro_real_t v_sig_speed = fabs(mu_cond_ij); +#else + const hydro_real_t v_sig_speed = fac_mu * hydro_vec3_norm(dv_ij_Hubble); +#endif - /* Signal velocity is the sum of pressure and speed contributions */ - const hydro_real_t v_sig_cond = fac_mu * hydro_vec3_norm(dv_ij_Hubble); + /* 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); - /* 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); + /* Use the Balsara limiter */ + const hydro_real_t b_ij = + 0.5 * (pi->gradients.balsara + pj->gradients.balsara); - /* Add conductivity to the specific energy */ - cond_du_term = const_conductivity_alpha * v_sig_cond * du_ij * rho_ij_inv; + const hydro_real_t alpha_cond = const_conductivity_alpha * b_ij; - /* Use the Balsara limiter */ - const hydro_real_t b_ij = - 0.5 * (pi->gradients.balsara + pj->gradients.balsara); - cond_du_term *= b_ij; + /* Add conductivity to the specific energy */ + cond_du_term = alpha_cond * fabs(v_sig_speed) * du_ij * rho_ij_inv; + } + else { + mu_cond_i = 0.; + mu_cond_j = 0.; + } + + const hydro_real_t v_sig_cond = + hydro_get_cond_signal_velocity(dx, pi, pj, mu_cond_i, mu_cond_j, + const_viscosity_beta); /* 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, G_ij); + const hydro_real_t dv_dot_G_ij = hydro_vec3_vec3_dot(dv_raw, G_ij); - /* Get Hubble flow contribution along dx */ - 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]}; - hydro_real_t dv_Hubble_dot_dx_ij_hat = + /* Raw velocities with Hubble flow */ + 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] + }; + +#ifdef hydro_props_use_radial_artificial_terms + const hydro_real_t dv_Hubble_dot_dx_ij_hat = hydro_vec3_vec3_dot(dv_Hubble, dx_ij_hat); + + /* 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] }; - /* Get velocity divergence along dx */ 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_ij_norm; /* Eq. 24 Rosswog 2020 */ + 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.) { @@ -815,36 +858,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( visc_du_term = 0.; } -#ifdef MAGMA2_DEBUG_CHECKS - if (visc_du_term < 0.) { - error("Viscous COOLING for particle %llu with pj %llu: " - "visc_du_term = %g\n" - "dv_Hubble_dot_G_rad_ij = %g, \n" - "dv_Hubble = (%g, %g, %g), \n" - "G_rad_ij = (%g, %g, %g), \n" - "mu_i = %g, mu_j = %g, \n" - "G_ij_norm = %g", - pi->id, pj->id, (double)visc_du_term, - (double)dv_Hubble_dot_G_rad_ij, - (double)dv_Hubble[0], - (double)dv_Hubble[1], - (double)dv_Hubble[2], - (double)G_rad_ij[0], - (double)G_rad_ij[1], - (double)G_rad_ij[2], - (double)mu_i, (double)mu_j, - (double)G_ij_norm); - } -#endif - /* 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 = hydro_vec3_vec3_dot(dv, dx); - pi->force.h_dt -= dv_dot_dx * r_inv * wi_dr; - pj->force.h_dt -= dv_dot_dx * r_inv * wj_dr; + const hydro_real_t dv_dot_dx_ij = hydro_vec3_vec3_dot(dv_raw, dx_ij); + pi->force.h_dt -= dv_dot_dx_ij * r_inv * wi_dr; + pj->force.h_dt -= dv_dot_dx_ij * r_inv * wj_dr; /* Timestepping */ @@ -857,6 +878,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pi->v_sig_max = fmax(pi->v_sig_max, v_sig_max); pj->v_sig_max = fmax(pj->v_sig_max, v_sig_max); +#ifdef MAGMA2_DEBUG_CHECKS + pi->debug.v_sig_visc_max = fmax(pi->debug.v_sig_visc_max, v_sig_visc); + pi->debug.v_sig_cond_max = fmax(pi->debug.v_sig_cond_max, v_sig_cond); + + pj->debug.v_sig_visc_max = fmax(pj->debug.v_sig_visc_max, v_sig_visc); + pj->debug.v_sig_cond_max = fmax(pj->debug.v_sig_cond_max, v_sig_cond); +#endif + /* Average softening in kernel */ const hydro_real_t h_ij = 0.5 * (hi + hj); pi->h_min = fmin(pi->h_min, h_ij); @@ -877,34 +906,69 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Compute dv dot dr. */ - const hydro_real_t dvdr = hydro_vec3_vec3_dot(dv, G_ij); + 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.); - /* This is 0 or negative */ + + hydro_real_t mu_cond_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 * (pi->force.soundspeed + pj->force.soundspeed) * + ? (-0.25 * alpha * cs_ij * mu_ij + - const_viscosity_beta * mu_ij * mu_ij) / + const_viscosity_beta * mu_ij * mu_ij) * b_ij / (0.5 * rho_ij) : 0.; + visc_acc_term = 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_cond_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; + + cond_du_term = + const_conductivity_alpha * fabs(mu_cond_ij) * du * rho_ij_inv; + cond_du_term *= b_ij; + } + else { + mu_cond_ij = 0.; + } + /* New signal velocity */ - const hydro_real_t new_v_sig = + const hydro_real_t new_v_sig_visc = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); + const hydro_real_t new_v_sig_cond = + signal_velocity(dx, pi, pj, mu_cond_ij, const_viscosity_beta); + + const hydro_real_t v_sig_max = fmax(new_v_sig_visc, new_v_sig_cond); /* Update if we need to */ - pi->v_sig_max = fmax(pi->v_sig_max, new_v_sig); - pj->v_sig_max = fmax(pj->v_sig_max, new_v_sig); + pi->v_sig_max = fmax(pi->v_sig_max, v_sig_max); + pj->v_sig_max = fmax(pj->v_sig_max, v_sig_max); + +#ifdef MAGMA2_DEBUG_CHECKS + pi->debug.v_sig_visc_max = fmax(pi->debug.v_sig_visc_max, new_v_sig_visc); + pi->debug.v_sig_cond_max = fmax(pi->debug.v_sig_cond_max, new_v_sig_cond); + + pj->debug.v_sig_visc_max = fmax(pj->debug.v_sig_visc_max, new_v_sig_visc); + pj->debug.v_sig_cond_max = fmax(pj->debug.v_sig_cond_max, new_v_sig_cond); +#endif /* Minimum softening in kernel */ const hydro_real_t h_ij = 0.5 * (hi + hj); @@ -917,9 +981,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pi->dt_min = fmin(pi->dt_min, dt_min_i); pj->dt_min = fmin(pj->dt_min, dt_min_j); - visc_acc_term = visc; - visc_du_term = 0.5 * visc_acc_term; - const hydro_real_t kernel_gradient = 0.5 * (pi->force.f * wi_dr + pj->force.f * wj_dr) * r_inv; @@ -929,6 +990,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 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 -= dvdr * r_inv * wi_dr; @@ -1034,6 +1096,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( 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 @@ -1041,6 +1104,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( 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]}; @@ -1143,7 +1207,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Make sure we use the correct gradients below */ high_order_gradients_flag = 1; -#ifdef hydro_props_use_radial_viscosity +#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]; @@ -1166,6 +1230,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_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) */ @@ -1189,7 +1255,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( dx_ji, xj, xi); /* Make sure no floating point problems */ - hydro_real_t phi_vec_sym = 0.5 * (phi_ij_vec + phi_ji_vec); + 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); @@ -1208,7 +1274,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pj->gradients.velocity_hessian, vj_reconstructed); - const hydro_real_t dv_raw[3] = {dv[0], dv[1], dv[2]}; const hydro_real_t dv_reconstructed[3] = { vi_reconstructed[0] - vj_reconstructed[0], vi_reconstructed[1] - vj_reconstructed[1], @@ -1260,7 +1325,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( dx_ji, xj, xi); /* Make sure no floating point problems */ - hydro_real_t phi_scalar_sym = 0.5 * (phi_ij_scalar + phi_ji_scalar); + 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); @@ -1284,51 +1349,95 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( dv_ij[1] + a2_Hubble * dx_ij[1], dv_ij[2] + a2_Hubble * dx_ij[2] }; + const hydro_real_t dv_ij_Hubble_dot_dx_ij_hat = + hydro_vec3_vec3_dot(dv_ij_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 */ + const hydro_real_t eps2 = const_viscosity_epsilon2; + const hydro_real_t fac = fac_mu * r; + const hydro_real_t faci = fac * hi / (r2 + eps2 * hi * hi); + const hydro_real_t facj = fac * hj / (r2 + eps2 * hj * hj); + hydro_real_t mu_cond_i = faci * dv_ij_Hubble_dot_dx_ij_hat; + hydro_real_t mu_cond_j = facj * dv_ij_Hubble_dot_dx_ij_hat; + const hydro_real_t mu_cond_ij = 0.5 * (mu_cond_i + mu_cond_j); + const hydro_real_t v_sig_beta = 0.75 * const_viscosity_beta * mu_cond_ij; + + /* Skip conduction if expansion beats sound speed along LOS */ + if (v_sig_alpha > v_sig_beta) { + + /* Signal velocity from speed contributions */ +#ifdef hydro_props_use_radial_artificial_terms + const hydro_real_t v_sig_speed = fabs(mu_cond_ij); +#else + const hydro_real_t v_sig_speed = fac_mu * hydro_vec3_norm(dv_ij_Hubble); +#endif - /* Signal velocity is the sum of pressure and speed contributions */ - const hydro_real_t v_sig_cond = fac_mu * hydro_vec3_norm(dv_ij_Hubble); + /* 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); - /* 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); + /* Use the Balsara limiter */ + const hydro_real_t b_ij = + 0.5 * (pi->gradients.balsara + pj->gradients.balsara); - /* Add conductivity to the specific energy */ - cond_du_term = const_conductivity_alpha * v_sig_cond * du_ij * rho_ij_inv; - - /* Use the Balsara limiter */ - const hydro_real_t b_ij = - 0.5 * (pi->gradients.balsara + pj->gradients.balsara); - cond_du_term *= b_ij; + const hydro_real_t alpha_cond = const_conductivity_alpha * b_ij; + + /* Add conductivity to the specific energy */ + cond_du_term = alpha_cond * fabs(v_sig_speed) * du_ij * rho_ij_inv; + } + else { + mu_cond_i = 0.; + mu_cond_j = 0.; + } + + const hydro_real_t v_sig_cond = + hydro_get_cond_signal_velocity(dx, pi, pj, mu_cond_i, mu_cond_j, + const_viscosity_beta); /* 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, G_ij); + const hydro_real_t dv_dot_G_ij = hydro_vec3_vec3_dot(dv_raw, G_ij); - /* Get Hubble flow along dx */ - 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]}; + /* Raw velocities with Hubble flow */ + 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] + }; + +#ifdef hydro_props_use_radial_artificial_terms const hydro_real_t dv_Hubble_dot_dx_ij_hat = hydro_vec3_vec3_dot(dv_Hubble, dx_ij_hat); + + /* 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_ij_norm; /* Eq. 24 Rosswog 2020 */ + 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.) { @@ -1336,33 +1445,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( visc_du_term = 0.; } -#ifdef MAGMA2_DEBUG_CHECKS - if (visc_du_term < 0.) { - error("Viscous COOLING for particle %llu with pj %llu: visc_du_term = %g\n" - "dv_Hubble_dot_G_rad_ij = %g, \n" - "dv_Hubble = (%g, %g, %g), \n" - "G_rad_ij = (%g, %g, %g), \n" - "mu_i = %g, mu_j = %g, \n" - "G_ij_norm = %g", - pi->id, pj->id, (double)visc_du_term, - (double)dv_Hubble_dot_G_rad_ij, - (double)dv_Hubble[0], - (double)dv_Hubble[1], - (double)dv_Hubble[2], - (double)G_rad_ij[0], - (double)G_rad_ij[1], - (double)G_rad_ij[2], - (double)mu_i, (double)mu_j, - (double)G_ij_norm); - } -#endif - /* Get the time derivative for h. */ - const hydro_real_t dv_dot_dx = hydro_vec3_vec3_dot(dv, dx); - pi->force.h_dt -= dv_dot_dx * r_inv * wi_dr; + const hydro_real_t dv_dot_dx_ij = hydro_vec3_vec3_dot(dv_raw, dx_ij); + pi->force.h_dt -= dv_dot_dx_ij * r_inv * wi_dr; /* Timestepping */ @@ -1374,6 +1462,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Update if we need to */ pi->v_sig_max = fmax(pi->v_sig_max, v_sig_max); +#ifdef MAGMA2_DEBUG_CHECKS + pi->debug.v_sig_visc_max = fmax(pi->debug.v_sig_visc_max, v_sig_visc); + pi->debug.v_sig_cond_max = fmax(pi->debug.v_sig_cond_max, v_sig_cond); +#endif + /* Compute new timestep */ const hydro_real_t h_ij = 0.5 * (hi + hj); pi->h_min = fmin(pi->h_min, h_ij); @@ -1389,33 +1482,65 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Compute dv dot dr. */ - const hydro_real_t dvdr = hydro_vec3_vec3_dot(dv, G_ij); + 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.); - /* This is 0 or negative */ + + hydro_real_t mu_cond_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 * (pi->force.soundspeed + pj->force.soundspeed) * + ? (-0.25 * alpha * cs_ij * mu_ij + - const_viscosity_beta * mu_ij * mu_ij) / + const_viscosity_beta * mu_ij * mu_ij) * b_ij / (0.5 * rho_ij) : 0.; + visc_acc_term = 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_cond_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; + + cond_du_term = + const_conductivity_alpha * fabs(mu_cond_ij) * du * rho_ij_inv; + cond_du_term *= b_ij; + } + else { + mu_cond_ij = 0.; + } + /* New signal velocity */ - const hydro_real_t new_v_sig = + const hydro_real_t new_v_sig_visc = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); + const hydro_real_t new_v_sig_cond = + signal_velocity(dx, pi, pj, mu_cond_ij, const_viscosity_beta); + + const hydro_real_t v_sig_max = fmax(new_v_sig_visc, new_v_sig_cond); /* Update if we need to */ - pi->v_sig_max = fmax(pi->v_sig_max, new_v_sig); + pi->v_sig_max = fmax(pi->v_sig_max, v_sig_max); + +#ifdef MAGMA2_DEBUG_CHECKS + pi->debug.v_sig_visc_max = fmax(pi->debug.v_sig_visc_max, new_v_sig_visc); + pi->debug.v_sig_cond_max = fmax(pi->debug.v_sig_cond_max, new_v_sig_cond); +#endif /* Minimum softening in kernel */ const hydro_real_t h_ij = 0.5 * (hi + hj); @@ -1425,20 +1550,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t dt_min_i = pi->h_min / pi->v_sig_max; pi->dt_min = fmin(pi->dt_min, dt_min_i); - - visc_acc_term = visc; - visc_du_term = 0.5 * visc_acc_term; - /* Variable smoothing length term */ - const hydro_real_t f_i = pi->force.f; - const hydro_real_t f_j = pj->force.f; const hydro_real_t kernel_gradient = - 0.5 * (f_i * wi_dr + f_j * wj_dr) * r_inv; + 0.5 * (pi->force.f * wi_dr + pj->force.f * 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 -= dvdr * r_inv * wi_dr; diff --git a/src/hydro/MAGMA2/hydro_io.h b/src/hydro/MAGMA2/hydro_io.h index 2a4534e3a2..473eeb5b5d 100644 --- a/src/hydro/MAGMA2/hydro_io.h +++ b/src/hydro/MAGMA2/hydro_io.h @@ -239,6 +239,18 @@ INLINE static void hydro_write_particles(const struct part* parts, num++; #ifdef MAGMA2_DEBUG_CHECKS + list[num] = io_make_output_field( + "MaxViscSignalVel", FLOAT, 1, UNIT_CONV_SPEED, + -1.5 * hydro_gamma_minus_one, parts, debug.v_sig_visc_max, + "Maximum viscous signal speed encountered by the particle this step."); + num++; + + list[num] = io_make_output_field( + "MaxCondSignalVel", FLOAT, 1, UNIT_CONV_SPEED, + -1.5 * hydro_gamma_minus_one, parts, debug.v_sig_cond_max, + "Maximum conduction signal speed encountered by the particle this step."); + num++; + list[num] = io_make_output_field( "NumberOfNeighbours", INT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, debug.num_ngb, diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index b790a23588..8da5b494df 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -50,10 +50,10 @@ /*! Alpha viscosity, usually =1.0. For lower N_ngb, should be higher */ -#define const_viscosity_alpha 2.0 +#define const_viscosity_alpha 1.0 /*! Alpha conductivity, usually =0.05. At lower N_ngb, should be higher */ -#define const_conductivity_alpha 0.5 +#define const_conductivity_alpha 0.05 /*! Desired number of neighbours -- CRITICAL that this matches hydro props */ #if defined(HYDRO_DIMENSION_1D) @@ -61,12 +61,15 @@ #elif defined(HYDRO_DIMENSION_2D) #define const_kernel_target_neighbours 17.0 #else -#define const_kernel_target_neighbours 57.0 +#define const_kernel_target_neighbours 256.0 #endif /* ---------- These parameters should not be changed ---------- */ +/*! 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 @@ -82,7 +85,7 @@ #endif /* Viscosity floor when G_ij is extremely misaligned with dx_ij */ -#define const_viscosity_cosine_limit 0.7 +#define const_viscosity_cosine_limit 0.1736 /* Viscosity weighting scheme: * 0 = (rho_i * q_i + rho_j * q_j) / (rho_i * rho_j) @@ -91,7 +94,7 @@ #define hydro_props_viscosity_weighting_type 2 /* Flag to use radial gradients for viscosity and conductivity */ -//#define hydro_props_use_radial_viscosity +#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 @@ -139,14 +142,14 @@ #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 +#define const_slope_limiter_eta_fold (0.4*const_kernel_mean_spacing) /*! 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 (1.5*const_viscosity_alpha) +#define const_viscosity_beta (2.*const_viscosity_alpha) /* ---------- Structures for below ---------- */ diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index db14cab6ba..7f37407809 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -173,6 +173,11 @@ struct part { /*! Number of neighbors in the kernel */ int num_ngb; + /*! The maximum viscous signal speed this step */ + hydro_real_t v_sig_visc_max; + + /*! The maximum conductive signal speed this step */ + hydro_real_t v_sig_cond_max; } debug; #endif From ee953e3fd242fa552cdb36f19a70ed2eaf8c65f6 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Fri, 22 Aug 2025 10:01:45 -0400 Subject: [PATCH 37/39] Always compute mu_ij from the raw velocities not the reconstructed velocities when computing the timestep and comparisons. Use a new timestep estimator from Gasoline2, similar to MAGMA2 paper but for adaptive timestepping. Apply a pressure difference limiter to the conductivity to prevent spurious conduction. Only allow conduction if the signal speed allows it. Added a factor that can reduce viscosity and conduction in the fallback SPH, since it can be highly over dissipative. --- src/hydro/MAGMA2/hydro.h | 124 ++++++------- src/hydro/MAGMA2/hydro_debug.h | 4 +- src/hydro/MAGMA2/hydro_iact.h | 265 +++++++++------------------- src/hydro/MAGMA2/hydro_parameters.h | 19 +- src/hydro/MAGMA2/hydro_part.h | 8 +- 5 files changed, 158 insertions(+), 262 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index e87a012b63..88b6920110 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -377,7 +377,8 @@ hydro_set_drifted_physical_internal_energy( p->force.soundspeed = soundspeed; p->force.pressure = pressure_including_floor; - p->v_sig_max = max(p->v_sig_max, soundspeed); + const float v_sig = 1.25 * (1. + 0.75 * const_viscosity_alpha) * soundspeed; + p->dt_min = min(p->dt_min, p->h_min / v_sig); } /** @@ -399,9 +400,14 @@ hydro_set_v_sig_based_on_velocity_kick(struct part *p, /* Sound speed */ const float soundspeed = hydro_get_comoving_soundspeed(p); - /* Update the signal velocity */ - p->v_sig_max = - max(soundspeed, p->v_sig_max + const_viscosity_beta * dv); + /* Signal speed */ + const float v_sig_sound = + 1.25 * (1. + 0.75 * const_viscosity_alpha) * soundspeed; + const float v_sig_kick = + 1.25 * 0.75 * const_viscosity_beta * dv; + const float v_sig = v_sig_sound + v_sig_kick; + + p->dt_min = min(p->dt_min, p->h_min / v_sig); } /** @@ -443,8 +449,25 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( 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 */ - return hydro_properties->CFL_condition * p->dt_min; + const float dt_cfl = 2. * hydro_properties->CFL_condition * conv * p->dt_min; + + /* Do not allow more than 0.25 * |u|/|du/dt| per step */ + const float dt_u = + (p->u_dt_cond != 0.) ? 0.25 * p->u / fabs(p->u_dt_cond) : FLT_MAX; + +#ifdef MAGMA2_DEBUG_CHECKS + if (dt_u < dt_cfl) { + message("dt_u < dt_cfl for pid=%lld u=%g u_dt_cond=%g dt_min=%g conv=%g " + "dt_cfl=%g dt_u=%g", + p->id, p->u, p->u_dt_cond, p->dt_min, conv, dt_cfl, dt_u); + } +#endif + + return fmin(dt_cfl, dt_u); } /** @@ -805,8 +828,6 @@ hydro_real_t hydro_scalar_minmod_limiter(const hydro_real_t df_reconstructed, return df; #else - if (df_raw * df_reconstructed < 0.) return df_raw; - return df_reconstructed; #endif } @@ -1275,24 +1296,16 @@ void hydro_get_average_kernel_gradient(const struct part *restrict pi, * @param mu_j The velocity jump indicator at pj to fill */ __attribute__((always_inline)) INLINE static -hydro_real_t hydro_get_visc_acc_term_and_mu(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 r, - const hydro_real_t fac_mu, - const hydro_real_t a2_Hubble, - hydro_real_t *mu_i, - hydro_real_t *mu_j) { +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 hi = pi->h; - const hydro_real_t hj = pj->h; - - const hydro_real_t r2 = r * r; - const hydro_real_t bi = pi->gradients.balsara; const hydro_real_t bj = pj->gradients.balsara; @@ -1307,8 +1320,10 @@ hydro_real_t hydro_get_visc_acc_term_and_mu(const struct part *restrict pi, 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.; - /* Softening to prevent blow-up on close particle approach */ - const hydro_real_t eps2 = const_viscosity_epsilon2; + /* 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) @@ -1316,17 +1331,14 @@ hydro_real_t hydro_get_visc_acc_term_and_mu(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); - *mu_i = conv * (dv_dot_dx_hat * r * hi) / (r2 + eps2 * (hi * hi)); - *mu_j = conv * (dv_dot_dx_hat * r * hj) / (r2 + eps2 * (hj * hj)); - const hydro_real_t q_i_alpha = - -const_viscosity_alpha * pi->force.soundspeed * (*mu_i); - const hydro_real_t q_i_beta = const_viscosity_beta * (*mu_i) * (*mu_i); + -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_j); - const hydro_real_t q_j_beta = const_viscosity_beta * (*mu_j) * (*mu_j); + -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; @@ -1335,16 +1347,7 @@ hydro_real_t hydro_get_visc_acc_term_and_mu(const struct part *restrict pi, /* 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 hi2 = hi * hi; - const hydro_real_t hj2 = hj * hj; - - *mu_i = conv * (dv_dot_dx_hat * r * hi) / (r2 + eps2 * hi2); - *mu_j = conv * (dv_dot_dx_hat * r * hj) / (r2 + eps2 * hj2); - - const hydro_real_t mu_ij = 0.5 * (*mu_i + *mu_j); 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; @@ -1356,14 +1359,6 @@ hydro_real_t hydro_get_visc_acc_term_and_mu(const struct part *restrict pi, /* Particles average symmetrically and arithmetically */ const hydro_real_t b_ij = 0.5 * (bi + bj); - - const hydro_real_t hi2 = hi * hi; - const hydro_real_t hj2 = hj * hj; - - *mu_i = conv * (dv_dot_dx_hat * r * hi) / (r2 + eps2 * hi2); - *mu_j = conv * (dv_dot_dx_hat * r * hj) / (r2 + eps2 * hj2); - - const hydro_real_t mu_ij = 0.5 * (*mu_i + *mu_j); 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; @@ -1899,9 +1894,8 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( /* Re-set problematic values */ p->rho = p->mass * kernel_root * h_inv_dim; - p->h_min = 0.f; - p->dt_min = 0.f; - p->v_sig_max = 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; @@ -1967,10 +1961,8 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* 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 = p->h; - p->v_sig_max = 1.25 * p->force.soundspeed + - 0.75 * const_viscosity_alpha * p->force.soundspeed; - p->dt_min = p->h_min / p->v_sig_max; + 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 @@ -2015,6 +2007,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( /* Reset the time derivatives. */ p->u_dt = 0.0f; + p->u_dt_cond = 0.0f; p->force.h_dt = 0.0f; } @@ -2049,8 +2042,11 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( p->force.pressure = pressure_including_floor; p->force.soundspeed = soundspeed; + /* Signal speed */ + const float v_sig = 1.25 * (1. + 0.75 * const_viscosity_alpha) * soundspeed; + /* Update the signal velocity, if we need to. */ - p->v_sig_max = max(p->v_sig_max, soundspeed); + p->dt_min = min(p->dt_min, p->h_min / v_sig); } /** @@ -2124,8 +2120,11 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( p->force.pressure = pressure_including_floor; p->force.soundspeed = soundspeed; + /* Signal speed */ + const float v_sig = 1.25 * (1. + 0.75 * const_viscosity_alpha) * soundspeed; + /* Update signal velocity if we need to */ - p->v_sig_max = max(p->v_sig_max, soundspeed); + p->dt_min = min(p->dt_min, p->h_min / v_sig); } /** @@ -2144,20 +2143,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( struct part *restrict p, const struct cosmology *cosmo) { const hydro_real_t wcount_inv = 1. / p->gradients.wcount; - p->force.h_dt *= p->force.f * p->h * hydro_dimension_inv * wcount_inv; - - /* dt_min is in physical units, and requires the kernel_gamma factor for h */ - p->dt_min *= kernel_gamma * cosmo->a / cosmo->a_factor_sound_speed; - -#ifdef MAGMA2_DEBUG_CHECKS - if (fabs(p->force.h_dt / p->h) > 1000.) { - warning("Large dh/dt! Hydro end force for particle with ID %lld (h: %g, " - "wcount: %g, h_dt: %g, dt_min: %g, v_sig_max: %g)", - p->id, p->h, p->gradients.wcount, p->force.h_dt, p->dt_min, - p->v_sig_max); - } -#endif } /** diff --git a/src/hydro/MAGMA2/hydro_debug.h b/src/hydro/MAGMA2/hydro_debug.h index 0e4bcf656e..84852bbb73 100644 --- a/src/hydro/MAGMA2/hydro_debug.h +++ b/src/hydro/MAGMA2/hydro_debug.h @@ -35,8 +35,8 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( 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 v_sig=%.3e, P=%.3e", p->id, p->u, - p->u_dt, p->v_sig_max, 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); diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index 0e98664a97..ecd779d40d 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -703,22 +703,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Artificial viscosity */ - hydro_real_t mu_i = 0.; - hydro_real_t mu_j = 0.; - /* Get the acceleration term, depends on the weighting scheme */ visc_acc_term = - hydro_get_visc_acc_term_and_mu(pi, pj, dv_ij, dx_ij, r, - fac_mu, a2_Hubble, &mu_i, &mu_j); + 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; - /* Viscous signal velocity */ - const hydro_real_t v_sig_visc = - hydro_get_visc_signal_velocity(dx, pi, pj, mu_i, mu_j, - const_viscosity_beta); - /* Artificial conductivity */ @@ -757,13 +748,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 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_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_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_ij_Hubble_dot_dx_ij_hat = - hydro_vec3_vec3_dot(dv_ij_Hubble, dx_ij_hat); + + 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 = @@ -771,20 +763,24 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const hydro_real_t v_sig_alpha = c_ij * (1. + 0.75 * const_viscosity_alpha); /* Must connect the particles along the LOS */ - const hydro_real_t eps2 = const_viscosity_epsilon2; - const hydro_real_t faci = fac_mu * r * hi / (r2 + eps2 * hi * hi); - const hydro_real_t facj = fac_mu * r * hj / (r2 + eps2 * hj * hj); - hydro_real_t mu_cond_i = faci * dv_ij_Hubble_dot_dx_ij_hat; - hydro_real_t mu_cond_j = facj * dv_ij_Hubble_dot_dx_ij_hat; - const hydro_real_t mu_cond_ij = 0.5 * (mu_cond_i + mu_cond_j); - const hydro_real_t v_sig_beta = 0.75 * const_viscosity_beta * mu_cond_ij; + 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 v_sig_speed = fabs(mu_cond_ij); + 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 @@ -797,24 +793,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pi->gradients.du_min, pi->gradients.du_max, pj->gradients.du_min, pj->gradients.du_max); - /* Use the Balsara limiter */ - const hydro_real_t b_ij = - 0.5 * (pi->gradients.balsara + pj->gradients.balsara); - - const hydro_real_t alpha_cond = const_conductivity_alpha * b_ij; + 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 * fabs(v_sig_speed) * du_ij * rho_ij_inv; + cond_du_term = alpha_cond * P_lim * v_sig_speed * du_ij * rho_ij_inv; } else { - mu_cond_i = 0.; - mu_cond_j = 0.; + mu_ij = 0.; } - const hydro_real_t v_sig_cond = - hydro_get_cond_signal_velocity(dx, pi, pj, mu_cond_i, mu_cond_j, - const_viscosity_beta); - /* Finalize everything with the correct normalizations. */ @@ -822,17 +811,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* 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); - /* Raw velocities with Hubble flow */ - 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] - }; - #ifdef hydro_props_use_radial_artificial_terms - const hydro_real_t dv_Hubble_dot_dx_ij_hat = - hydro_vec3_vec3_dot(dv_Hubble, dx_ij_hat); - /* 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], @@ -871,29 +850,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Timestepping */ - /* New signal velocity */ - const hydro_real_t v_sig_max = fmax(v_sig_visc, v_sig_cond); - - /* Update if we need to */ - pi->v_sig_max = fmax(pi->v_sig_max, v_sig_max); - pj->v_sig_max = fmax(pj->v_sig_max, v_sig_max); - -#ifdef MAGMA2_DEBUG_CHECKS - pi->debug.v_sig_visc_max = fmax(pi->debug.v_sig_visc_max, v_sig_visc); - pi->debug.v_sig_cond_max = fmax(pi->debug.v_sig_cond_max, v_sig_cond); - - pj->debug.v_sig_visc_max = fmax(pj->debug.v_sig_visc_max, v_sig_visc); - pj->debug.v_sig_cond_max = fmax(pj->debug.v_sig_cond_max, v_sig_cond); -#endif + /* Compute based on raw velocities */ + const hydro_real_t v_sig_visc = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); - /* Average softening in kernel */ 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 = pi->h_min / pi->v_sig_max; - const hydro_real_t dt_min_j = pj->h_min / pj->v_sig_max; + 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); @@ -914,7 +881,7 @@ __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_cond_ij = fac_mu * r_inv * dvdr_Hubble; + 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 */ @@ -932,57 +899,39 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( (0.5 * rho_ij) : 0.; - visc_acc_term = visc; + 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_cond_ij; + 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; - cond_du_term = - const_conductivity_alpha * fabs(mu_cond_ij) * du * rho_ij_inv; - cond_du_term *= b_ij; - } - else { - mu_cond_ij = 0.; + 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 new_v_sig_cond = - signal_velocity(dx, pi, pj, mu_cond_ij, const_viscosity_beta); - - const hydro_real_t v_sig_max = fmax(new_v_sig_visc, new_v_sig_cond); - - /* Update if we need to */ - pi->v_sig_max = fmax(pi->v_sig_max, v_sig_max); - pj->v_sig_max = fmax(pj->v_sig_max, v_sig_max); - -#ifdef MAGMA2_DEBUG_CHECKS - pi->debug.v_sig_visc_max = fmax(pi->debug.v_sig_visc_max, new_v_sig_visc); - pi->debug.v_sig_cond_max = fmax(pi->debug.v_sig_cond_max, new_v_sig_cond); - - pj->debug.v_sig_visc_max = fmax(pj->debug.v_sig_visc_max, new_v_sig_visc); - pj->debug.v_sig_cond_max = fmax(pj->debug.v_sig_cond_max, new_v_sig_cond); -#endif - /* Minimum softening in kernel */ 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 = pi->h_min / pi->v_sig_max; - const hydro_real_t dt_min_j = pj->h_min / pj->v_sig_max; + 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 * (pi->force.f * wi_dr + pj->force.f * wj_dr) * r_inv; + const hydro_real_t kernel_gradient = 0.5 * (wi_dr + wj_dr) * r_inv; visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; @@ -1034,6 +983,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 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; } /** @@ -1291,22 +1242,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Artificial viscosity */ - hydro_real_t mu_i = 0.; - hydro_real_t mu_j = 0.; - /* Get the acceleration term, depends on the weighting scheme */ visc_acc_term = - hydro_get_visc_acc_term_and_mu(pi, pj, dv_ij, dx_ij, r, - fac_mu, a2_Hubble, &mu_i, &mu_j); + 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; - /* Viscous signal velocity */ - const hydro_real_t v_sig_visc = - hydro_get_visc_signal_velocity(dx, pi, pj, mu_i, mu_j, - const_viscosity_beta); - /* Artificial conductivity */ @@ -1344,13 +1286,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( 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_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_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_ij_Hubble_dot_dx_ij_hat = - hydro_vec3_vec3_dot(dv_ij_Hubble, dx_ij_hat); + + 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 = @@ -1358,21 +1301,24 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t v_sig_alpha = c_ij * (1. + 0.75 * const_viscosity_alpha); /* Must connect the particles along the LOS */ - const hydro_real_t eps2 = const_viscosity_epsilon2; - const hydro_real_t fac = fac_mu * r; - const hydro_real_t faci = fac * hi / (r2 + eps2 * hi * hi); - const hydro_real_t facj = fac * hj / (r2 + eps2 * hj * hj); - hydro_real_t mu_cond_i = faci * dv_ij_Hubble_dot_dx_ij_hat; - hydro_real_t mu_cond_j = facj * dv_ij_Hubble_dot_dx_ij_hat; - const hydro_real_t mu_cond_ij = 0.5 * (mu_cond_i + mu_cond_j); - const hydro_real_t v_sig_beta = 0.75 * const_viscosity_beta * mu_cond_ij; + 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 v_sig_speed = fabs(mu_cond_ij); + 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 @@ -1385,42 +1331,24 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->gradients.du_min, pi->gradients.du_max, pj->gradients.du_min, pj->gradients.du_max); - /* Use the Balsara limiter */ - const hydro_real_t b_ij = - 0.5 * (pi->gradients.balsara + pj->gradients.balsara); - - const hydro_real_t alpha_cond = const_conductivity_alpha * b_ij; + 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 * fabs(v_sig_speed) * du_ij * rho_ij_inv; + cond_du_term = alpha_cond * P_lim * v_sig_speed * du_ij * rho_ij_inv; } else { - mu_cond_i = 0.; - mu_cond_j = 0.; + mu_ij = 0.; } - const hydro_real_t v_sig_cond = - hydro_get_cond_signal_velocity(dx, pi, pj, mu_cond_i, mu_cond_j, - const_viscosity_beta); - - /* 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); - /* Raw velocities with Hubble flow */ - 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] - }; - #ifdef hydro_props_use_radial_artificial_terms - const hydro_real_t dv_Hubble_dot_dx_ij_hat = - hydro_vec3_vec3_dot(dv_Hubble, dx_ij_hat); - /* 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], @@ -1456,22 +1384,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Timestepping */ - /* New signal velocity */ - const hydro_real_t v_sig_max = fmax(v_sig_visc, v_sig_cond); - - /* Update if we need to */ - pi->v_sig_max = fmax(pi->v_sig_max, v_sig_max); - -#ifdef MAGMA2_DEBUG_CHECKS - pi->debug.v_sig_visc_max = fmax(pi->debug.v_sig_visc_max, v_sig_visc); - pi->debug.v_sig_cond_max = fmax(pi->debug.v_sig_cond_max, v_sig_cond); -#endif + /* Compute based on raw velocities */ + const hydro_real_t v_sig_visc = + signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta); - /* Compute new timestep */ 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 = pi->h_min / pi->v_sig_max; + 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 @@ -1490,7 +1410,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Are the particles moving towards each others ? */ const hydro_real_t omega_ij = fmin(dvdr_Hubble, 0.); - hydro_real_t mu_cond_ij = fac_mu * r_inv * dvdr_Hubble; + 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 */ @@ -1508,51 +1428,37 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( (0.5 * rho_ij) : 0.; - visc_acc_term = visc; + 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_cond_ij; + 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; - cond_du_term = - const_conductivity_alpha * fabs(mu_cond_ij) * du * rho_ij_inv; - cond_du_term *= b_ij; - } - else { - mu_cond_ij = 0.; + 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 new_v_sig_cond = - signal_velocity(dx, pi, pj, mu_cond_ij, const_viscosity_beta); - - const hydro_real_t v_sig_max = fmax(new_v_sig_visc, new_v_sig_cond); - - /* Update if we need to */ - pi->v_sig_max = fmax(pi->v_sig_max, v_sig_max); - -#ifdef MAGMA2_DEBUG_CHECKS - pi->debug.v_sig_visc_max = fmax(pi->debug.v_sig_visc_max, new_v_sig_visc); - pi->debug.v_sig_cond_max = fmax(pi->debug.v_sig_cond_max, new_v_sig_cond); -#endif - /* Minimum softening in kernel */ 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 = pi->h_min / pi->v_sig_max; + 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 * (pi->force.f * wi_dr + pj->force.f * wj_dr) * r_inv; + const hydro_real_t kernel_gradient = 0.5 * (wi_dr + wj_dr) * r_inv; visc_acc_term *= kernel_gradient; sph_acc_term *= kernel_gradient; @@ -1586,6 +1492,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* 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_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 8da5b494df..5cd3ca49a6 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -50,18 +50,18 @@ /*! Alpha viscosity, usually =1.0. For lower N_ngb, should be higher */ -#define const_viscosity_alpha 1.0 +#define const_viscosity_alpha 2.0 /*! Alpha conductivity, usually =0.05. At lower N_ngb, should be higher */ -#define const_conductivity_alpha 0.05 +#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 4.0 +#define const_kernel_target_neighbours 8.0 #elif defined(HYDRO_DIMENSION_2D) -#define const_kernel_target_neighbours 17.0 +#define const_kernel_target_neighbours 34.0 #else -#define const_kernel_target_neighbours 256.0 +#define const_kernel_target_neighbours 114.0 #endif @@ -94,7 +94,7 @@ #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 @@ -142,14 +142,17 @@ #define const_slope_limiter_eta_crit (const_kernel_mean_spacing) /*! eta_fold from Frontiere+'17 Equation 51 */ -#define const_slope_limiter_eta_fold (0.4*const_kernel_mean_spacing) +#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.*const_viscosity_alpha) +#define const_viscosity_beta (2.0*const_viscosity_alpha) + +/*! Fallback multiplier for alpha/beta terms to reduce spread */ +#define const_fallback_reduction_factor 0.25 /* ---------- Structures for below ---------- */ diff --git a/src/hydro/MAGMA2/hydro_part.h b/src/hydro/MAGMA2/hydro_part.h index 7f37407809..29779f2e83 100644 --- a/src/hydro/MAGMA2/hydro_part.h +++ b/src/hydro/MAGMA2/hydro_part.h @@ -132,12 +132,12 @@ struct part { /*! Minimum smoothing length in the kernel */ float h_min; - /*! Maximum signal velocity in the kernel */ - float v_sig_max; - /*! 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 */ From d71e41ae5277317b171cac4eefa5ccdc4965f3e4 Mon Sep 17 00:00:00 2001 From: Douglas Rennehan Date: Fri, 22 Aug 2025 11:05:11 -0400 Subject: [PATCH 38/39] Added a selection for different ways to compute dh/dt. --- src/hydro/MAGMA2/hydro.h | 68 +++++++++++++++++++++++++++-- src/hydro/MAGMA2/hydro_iact.h | 15 ++++--- src/hydro/MAGMA2/hydro_parameters.h | 7 +++ 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 88b6920110..2a76cf4f9a 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -2127,6 +2127,69 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( 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. * @@ -2141,9 +2204,8 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( */ __attribute__((always_inline)) INLINE static void hydro_end_force( struct part *restrict p, const struct cosmology *cosmo) { - - const hydro_real_t wcount_inv = 1. / p->gradients.wcount; - p->force.h_dt *= p->force.f * p->h * hydro_dimension_inv * wcount_inv; + + p->force.h_dt *= hydro_get_h_dt_norm(p); } /** diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index ecd779d40d..ed3a6f3d4f 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -843,8 +843,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* 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 -= dv_dot_dx_ij * r_inv * wi_dr; - pj->force.h_dt -= dv_dot_dx_ij * 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 */ @@ -942,8 +944,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( cond_du_term *= kernel_gradient; /* Get the time derivative for h. */ - pi->force.h_dt -= dvdr * r_inv * wi_dr; - pj->force.h_dt -= dvdr * r_inv * wj_dr; + 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++; @@ -1378,7 +1380,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const hydro_real_t dv_dot_dx_ij = hydro_vec3_vec3_dot(dv_raw, dx_ij); - pi->force.h_dt -= dv_dot_dx_ij * 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 */ @@ -1467,7 +1470,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( cond_du_term *= kernel_gradient; /* Get the time derivative for h. */ - pi->force.h_dt -= dvdr * r_inv * wi_dr; + 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++; diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 5cd3ca49a6..9983465e8e 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -67,6 +67,13 @@ /* ---------- 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 From 20bf59c07dbf145be7f0ecd44ede8f32e59a2ed3 Mon Sep 17 00:00:00 2001 From: Doug Rennehan Date: Mon, 25 Aug 2025 14:48:42 -0400 Subject: [PATCH 39/39] Clean up the signal speed calculation for time-stepping. --- src/hydro/MAGMA2/hydro.h | 22 +++++++++++++--------- src/hydro/MAGMA2/hydro_iact.h | 16 ++++++++-------- src/hydro/MAGMA2/hydro_parameters.h | 7 +++++++ 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/hydro/MAGMA2/hydro.h b/src/hydro/MAGMA2/hydro.h index 2a76cf4f9a..7ffb2790ff 100644 --- a/src/hydro/MAGMA2/hydro.h +++ b/src/hydro/MAGMA2/hydro.h @@ -377,7 +377,9 @@ hydro_set_drifted_physical_internal_energy( p->force.soundspeed = soundspeed; p->force.pressure = pressure_including_floor; - const float v_sig = 1.25 * (1. + 0.75 * const_viscosity_alpha) * soundspeed; + /* Signal speed */ + const float v_sig = const_viscosity_alpha_prefactor * soundspeed; + p->dt_min = min(p->dt_min, p->h_min / v_sig); } @@ -401,10 +403,8 @@ hydro_set_v_sig_based_on_velocity_kick(struct part *p, const float soundspeed = hydro_get_comoving_soundspeed(p); /* Signal speed */ - const float v_sig_sound = - 1.25 * (1. + 0.75 * const_viscosity_alpha) * soundspeed; - const float v_sig_kick = - 1.25 * 0.75 * const_viscosity_beta * dv; + 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); @@ -486,9 +486,13 @@ __attribute__((always_inline)) INLINE static float hydro_signal_velocity( const float ci = pi->force.soundspeed; const float cj = pj->force.soundspeed; - const float c_ij = 0.5 * (ci + cj); + const float c_ij = 0.5f * (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 1.25 * (c_ij + 0.75 * (const_viscosity_alpha * c_ij - beta * mu_ij)); + return v_sig; } /** @@ -2043,7 +2047,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( p->force.soundspeed = soundspeed; /* Signal speed */ - const float v_sig = 1.25 * (1. + 0.75 * const_viscosity_alpha) * soundspeed; + 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); @@ -2121,7 +2125,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( p->force.soundspeed = soundspeed; /* Signal speed */ - const float v_sig = 1.25 * (1. + 0.75 * const_viscosity_alpha) * soundspeed; + 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); diff --git a/src/hydro/MAGMA2/hydro_iact.h b/src/hydro/MAGMA2/hydro_iact.h index ed3a6f3d4f..a6255d7577 100644 --- a/src/hydro/MAGMA2/hydro_iact.h +++ b/src/hydro/MAGMA2/hydro_iact.h @@ -760,11 +760,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* 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); + const hydro_real_t v_sig_alpha = const_viscosity_alpha_prefactor * c_ij; /* 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; + const hydro_real_t v_sig_beta = const_viscosity_beta_prefactor * mu_ij; /* Skip conduction if expansion beats sound speed along LOS */ if (v_sig_alpha > v_sig_beta) { @@ -904,8 +904,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( 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; + const hydro_real_t v_sig_alpha = const_viscosity_alpha_prefactor * c_ij; + const hydro_real_t v_sig_beta = const_viscosity_beta_prefactor * mu_full_ij; if (v_sig_alpha > v_sig_beta) { const hydro_real_t rho_ij_inv = 2. / rho_ij; @@ -1300,11 +1300,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* 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); + const hydro_real_t v_sig_alpha = const_viscosity_alpha_prefactor * c_ij; /* 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; + const hydro_real_t v_sig_beta = const_viscosity_beta_prefactor * mu_ij; /* Skip conduction if expansion beats sound speed along LOS */ if (v_sig_alpha > v_sig_beta) { @@ -1434,8 +1434,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( 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; + const hydro_real_t v_sig_alpha = const_viscosity_alpha_prefactor * c_ij; + const hydro_real_t v_sig_beta = const_viscosity_beta_prefactor * mu_full_ij; if (v_sig_alpha > v_sig_beta) { const hydro_real_t rho_ij_inv = 2. / rho_ij; diff --git a/src/hydro/MAGMA2/hydro_parameters.h b/src/hydro/MAGMA2/hydro_parameters.h index 9983465e8e..3e99e97207 100644 --- a/src/hydro/MAGMA2/hydro_parameters.h +++ b/src/hydro/MAGMA2/hydro_parameters.h @@ -158,6 +158,13 @@ * 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