diff --git a/examples/SedovBlast_1D/sedov.yml b/examples/SedovBlast_1D/sedov.yml index 6f519835d26ff5aa851ffb8999e650815c522cd3..1ecfeb32452d05f299b98124c4fdfc79126f7504 100644 --- a/examples/SedovBlast_1D/sedov.yml +++ b/examples/SedovBlast_1D/sedov.yml @@ -21,7 +21,7 @@ Snapshots: # Parameters governing the conserved quantities statistics Statistics: - delta_time: 1e-3 # Time between statistics output + delta_time: 1e-5 # Time between statistics output # Parameters for the hydrodynamics scheme SPH: diff --git a/src/Makefile.am b/src/Makefile.am index f7cb52ba40a34269173ab8c5019d3011b0c34b61..32c6f0a27200c8c416543a291241fb2a336e9037 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -69,6 +69,8 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h cycle.h error.h inline.h hydro/Default/hydro_debug.h hydro/Default/hydro_part.h \ hydro/Gadget2/hydro.h hydro/Gadget2/hydro_iact.h hydro/Gadget2/hydro_io.h \ hydro/Gadget2/hydro_debug.h hydro/Gadget2/hydro_part.h \ + hydro/PressureEntropy/hydro.h hydro/PressureEntropy/hydro_iact.h hydro/PressureEntropy/hydro_io.h \ + hydro/PressureEntropy/hydro_debug.h hydro/PressureEntropy/hydro_part.h \ hydro/Gizmo/hydro.h hydro/Gizmo/hydro_iact.h hydro/Gizmo/hydro_io.h \ hydro/Gizmo/hydro_debug.h hydro/Gizmo/hydro_part.h \ riemann/riemann_hllc.h riemann/riemann_trrs.h \ diff --git a/src/adiabatic_index.h b/src/adiabatic_index.h index a0c9ce09e3e004af07e8b208ef9f1af5f46c9e81..59937db3c8d09fc7e6e969de0aee60f01a2e5a2c 100644 --- a/src/adiabatic_index.h +++ b/src/adiabatic_index.h @@ -410,7 +410,7 @@ __attribute__((always_inline)) INLINE static float pow_one_over_gamma(float x) { #if defined(HYDRO_GAMMA_5_3) - return powf(x, 0.6f); /* x^(3/5) */ + return powf(x, hydro_one_over_gamma); /* x^(3/5) */ #elif defined(HYDRO_GAMMA_7_5) @@ -418,7 +418,7 @@ __attribute__((always_inline)) INLINE static float pow_one_over_gamma(float x) { #elif defined(HYDRO_GAMMA_4_3) - return powf(x, 0.75f); /* x^(3/4) */ + return powf(x, hydro_one_over_gamma); /* x^(3/4) */ #elif defined(HYDRO_GAMMA_2_1) diff --git a/src/const.h b/src/const.h index 316a13c8ebba4785e8e1a66b8307f8681697c938..afad73211546cf81cbb0e6a1179569e588952fec 100644 --- a/src/const.h +++ b/src/const.h @@ -65,6 +65,7 @@ /* SPH variant to use */ //#define MINIMAL_SPH #define GADGET2_SPH +//#define HOPKINS_PE_SPH //#define DEFAULT_SPH //#define GIZMO_SPH diff --git a/src/debug.c b/src/debug.c index d73bc86a92cf5ca28c202e7b567cf7c40ba6eccb..be42485a38ea8d560797e8f1ccc5936456febcd8 100644 --- a/src/debug.c +++ b/src/debug.c @@ -44,6 +44,8 @@ #include "./hydro/Minimal/hydro_debug.h" #elif defined(GADGET2_SPH) #include "./hydro/Gadget2/hydro_debug.h" +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro_debug.h" #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro_debug.h" #elif defined(GIZMO_SPH) @@ -65,7 +67,7 @@ * * (Should be used for debugging only as it runs in O(N).) */ -void printParticle(const struct part *parts, struct xpart *xparts, +void printParticle(const struct part *parts, const struct xpart *xparts, long long int id, size_t N) { int found = 0; @@ -125,7 +127,7 @@ void printgParticle(const struct gpart *gparts, const struct part *parts, */ void printParticle_single(const struct part *p, const struct xpart *xp) { - printf("## Particle: id=%lld", p->id); + printf("## Particle: id=%lld ", p->id); hydro_debug_particle(p, xp); printf("\n"); } diff --git a/src/debug.h b/src/debug.h index 22b63820745ca7282b7699f0be09e493238d83c2..2142a22eca91338580d8f50197a57de0cf248bee 100644 --- a/src/debug.h +++ b/src/debug.h @@ -24,7 +24,7 @@ #include "part.h" #include "space.h" -void printParticle(const struct part *parts, struct xpart *xparts, +void printParticle(const struct part *parts, const struct xpart *xparts, long long int id, size_t N); void printgParticle(const struct gpart *gparts, const struct part *parts, long long int id, size_t N); diff --git a/src/engine.c b/src/engine.c index 7adc94b42709153d700fc67f8a15198d19b003b2..b73ad2e24f91426b87c0933f7987de9ff88991c9 100644 --- a/src/engine.c +++ b/src/engine.c @@ -2658,7 +2658,15 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs) { TIMER_TOC(timer_runners); /* Apply some conversions (e.g. internal energy -> entropy) */ - if (!flag_entropy_ICs) space_map_cells_pre(s, 0, cell_convert_hydro, NULL); + if (!flag_entropy_ICs) { + + /* Apply the conversion */ + space_map_cells_pre(s, 0, cell_convert_hydro, NULL); + + /* Correct what we did (e.g. in PE-SPH, need to recompute rho_bar) */ + if (hydro_need_extra_init_loop) + engine_launch(e, e->nr_threads, mask, submask); + } clocks_gettime(&time2); diff --git a/src/hydro.h b/src/hydro.h index 4a2b0bd029d494e2091b9081d22b7949cec5648c..9e02c2009e307f0623ffb535ff9068603c2d4147 100644 --- a/src/hydro.h +++ b/src/hydro.h @@ -34,6 +34,10 @@ #include "./hydro/Gadget2/hydro.h" #include "./hydro/Gadget2/hydro_iact.h" #define SPH_IMPLEMENTATION "Gadget-2 version of SPH (Springel 2005)" +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro.h" +#include "./hydro/PressureEntropy/hydro_iact.h" +#define SPH_IMPLEMENTATION "Pressure-Entropy SPH (Hopkins 2013)" #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro.h" #include "./hydro/Default/hydro_iact.h" diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h new file mode 100644 index 0000000000000000000000000000000000000000..33cf5267eb0d8652a42b70fe31a402c3d4a3e79d --- /dev/null +++ b/src/hydro/PressureEntropy/hydro.h @@ -0,0 +1,483 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_H + +/** + * @file PressureEntropy/hydro.h + * @brief Pressure-Entropy implementation of SPH (Non-neighbour loop + * equations) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +#include "adiabatic_index.h" +#include "approx_math.h" +#include "dimension.h" +#include "equation_of_state.h" +#include "hydro_properties.h" +#include "kernel_hydro.h" + +/** + * @brief Returns the internal energy of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_internal_energy( + const struct part *restrict p, float dt) { + + const float entropy = p->entropy + p->entropy_dt * dt; + + return gas_internal_energy_from_entropy(p->rho_bar, entropy); +} + +/** + * @brief Returns the pressure of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_pressure( + const struct part *restrict p, float dt) { + + const float entropy = p->entropy + p->entropy_dt * dt; + + return gas_pressure_from_entropy(p->rho_bar, entropy); +} + +/** + * @brief Returns the entropy of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_entropy( + const struct part *restrict p, float dt) { + + return p->entropy + p->entropy_dt * dt; +} + +/** + * @brief Returns the sound speed of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_soundspeed( + const struct part *restrict p, float dt) { + + return p->force.soundspeed; +} + +/** + * @brief Returns the physical density of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_density( + const struct part *restrict p) { + + return 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 Modifies the thermal state of a particle to the imposed internal + * energy + * + * This overrides the current state of the particle but does *not* change its + * time-derivatives + * + * @param p The particle + * @param u The new internal energy + */ +__attribute__((always_inline)) INLINE static void hydro_set_internal_energy( + struct part *restrict p, float u) { + + p->entropy = gas_entropy_from_internal_energy(p->rho_bar, u); + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + + /* Compute the pressure */ + const float entropy = p->entropy; + const float pressure = gas_pressure_from_entropy(p->rho_bar, entropy); + + /* Compute the sound speed from the pressure*/ + const float rho_bar_inv = 1.f / p->rho_bar; + const float soundspeed = sqrtf(hydro_gamma * pressure * rho_bar_inv); + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; +} + +/** + * @brief Modifies the thermal state of a particle to the imposed entropy + * + * This overrides the current state of the particle but does *not* change its + * time-derivatives + * + * @param p The particle + * @param S The new entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_entropy( + struct part *restrict p, float S) { + + p->entropy = S; + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + + /* Compute the pressure */ + const float entropy = p->entropy; + const float pressure = gas_pressure_from_entropy(p->rho_bar, entropy); + + /* Compute the sound speed from the pressure*/ + const float rho_bar_inv = 1.f / p->rho_bar; + const float soundspeed = sqrtf(hydro_gamma * pressure * rho_bar_inv); + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; +} + +/** + * @brief Computes the hydro time-step of a given particle + * + * @param p Pointer to the particle data + * @param xp Pointer to the extended particle data + * + */ +__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 float CFL_condition = hydro_properties->CFL_condition; + + /* CFL condition */ + const float dt_cfl = + 2.f * kernel_gamma * CFL_condition * p->h / p->force.v_sig; + + return dt_cfl; +} + +/** + * @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. + * + * @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->ti_begin = 0; + p->ti_end = 0; + p->rho_bar = 0.f; + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + xp->v_full[0] = p->v[0]; + xp->v_full[1] = p->v[1]; + xp->v_full[2] = p->v[2]; +} + +/** + * @brief Prepares a particle for the density calculation. + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the variaous density tasks + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_init_part( + struct part *restrict p) { + p->density.wcount = 0.f; + p->density.wcount_dh = 0.f; + p->rho = 0.f; + p->rho_bar = 0.f; + p->density.rho_dh = 0.f; + p->density.pressure_dh = 0.f; + + p->density.div_v = 0.f; + p->density.rot_v[0] = 0.f; + p->density.rot_v[1] = 0.f; + p->density.rot_v[2] = 0.f; +} + +/** + * @brief Finishes the density calculation. + * + * Multiplies the density and number of neighbours by the appropiate constants + * and add the self-contribution term. + * + * @param p The particle to act upon + * @param ti_current The current time (on the integer timeline) + */ +__attribute__((always_inline)) INLINE static void hydro_end_density( + struct part *restrict p, int ti_current) { + + /* 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->rho_bar += p->mass * p->entropy_one_over_gamma * kernel_root; + p->density.rho_dh -= hydro_dimension * p->mass * kernel_root; + p->density.pressure_dh -= + hydro_dimension * p->mass * p->entropy_one_over_gamma * kernel_root; + p->density.wcount += kernel_root; + + /* Finish the calculation by inserting the missing h-factors */ + p->rho *= h_inv_dim; + p->rho_bar *= h_inv_dim; + p->density.rho_dh *= h_inv_dim_plus_one; + p->density.pressure_dh *= h_inv_dim_plus_one; + p->density.wcount *= kernel_norm; + p->density.wcount_dh *= h_inv * kernel_gamma * kernel_norm; + + const float rho_inv = 1.f / p->rho; + const float entropy_minus_one_over_gamma = 1.f / p->entropy_one_over_gamma; + + /* Final operation on the weighted density */ + p->rho_bar *= entropy_minus_one_over_gamma; + + /* Compute the derivative term */ + p->density.rho_dh = + 1.f / (1.f + hydro_dimension_inv * h * p->density.rho_dh * rho_inv); + p->density.pressure_dh *= + h * rho_inv * hydro_dimension_inv * entropy_minus_one_over_gamma; + + /* Finish calculation of the velocity curl components */ + p->density.rot_v[0] *= h_inv_dim_plus_one * rho_inv; + p->density.rot_v[1] *= h_inv_dim_plus_one * rho_inv; + p->density.rot_v[2] *= h_inv_dim_plus_one * rho_inv; + + /* Finish calculation of the velocity divergence */ + p->density.div_v *= h_inv_dim_plus_one * rho_inv; +} + +/** + * @brief Prepare a particle for the force calculation. + * + * Computes viscosity term, conduction term and smoothing length gradient terms. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param ti_current The current time (on the timeline) + * @param timeBase The minimal time-step size + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_force( + struct part *restrict p, struct xpart *restrict xp, int ti_current, + double timeBase) { + + const float fac_mu = 1.f; /* Will change with cosmological integration */ + + /* Compute the norm of the curl */ + const float curl_v = sqrtf(p->density.rot_v[0] * p->density.rot_v[0] + + p->density.rot_v[1] * p->density.rot_v[1] + + p->density.rot_v[2] * p->density.rot_v[2]); + + /* Compute the norm of div v */ + const float abs_div_v = fabsf(p->density.div_v); + + /* Compute the pressure */ + const float half_dt = (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase; + const float pressure = hydro_get_pressure(p, half_dt); + + /* Compute the sound speed from the pressure*/ + const float rho_bar_inv = 1.f / p->rho_bar; + const float soundspeed = sqrtf(hydro_gamma * pressure * rho_bar_inv); + + /* Compute the Balsara switch */ + const float balsara = + abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed / fac_mu / p->h); + + /* Compute "grad h" term */ + const float grad_h_term = p->density.rho_dh * p->density.pressure_dh; + + /* Update variables. */ + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + p->force.balsara = balsara; + p->force.f = grad_h_term; +} + +/** + * @brief Reset acceleration fields of a particle + * + * Resets all hydro acceleration and time derivative fields in preparation + * for the sums taking place in the variaous 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->entropy_dt = 0.0f; + p->force.h_dt = 0.0f; + + /* Reset maximal signal velocity */ + p->force.v_sig = 0.0f; +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * @param p The particle + * @param xp The extended data of the particle + * @param dt The drift time-step. + * @param t0 The time at the start of the drift (on the timeline). + * @param t1 The time at the end of the drift (on the timeline). + * @param timeBase The minimal time-step size + */ +__attribute__((always_inline)) INLINE static void hydro_predict_extra( + struct part *restrict p, const struct xpart *restrict xp, float dt, int t0, + int t1, double timeBase) { + + const float h_inv = 1.f / p->h; + + /* Predict smoothing length */ + const float w1 = p->force.h_dt * h_inv * dt; + if (fabsf(w1) < 0.2f) + p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */ + else + p->h *= expf(w1); + + /* Predict density */ + const float w2 = -hydro_dimension * w1; + if (fabsf(w2) < 0.2f) { + p->rho *= approx_expf(w2); /* 4th order expansion of exp(w) */ + p->rho_bar *= approx_expf(w2); + } else { + p->rho *= expf(w2); + p->rho_bar *= expf(w2); + } + + /* Drift the entropy */ + const float dt_entr = (t1 - (p->ti_begin + p->ti_end) / 2) * timeBase; + const float entropy = hydro_get_entropy(p, dt_entr); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, entropy); + + /* Compute the sound speed from the pressure*/ + const float rho_bar_inv = 1.f / p->rho_bar; + const float soundspeed = sqrtf(hydro_gamma * pressure * rho_bar_inv); + + /* Update the variables */ + p->entropy_one_over_gamma = pow_one_over_gamma(entropy); + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; +} + +/** + * @brief Finishes the force calculation. + * + * Multiplies the forces and accelerationsby the appropiate constants + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_end_force( + struct part *restrict p) { + + p->force.h_dt *= p->h * hydro_dimension_inv; + + p->entropy_dt *= + 0.5f * hydro_gamma_minus_one * pow_minus_gamma_minus_one(p->rho_bar); +} + +/** + * @brief Kick the additional variables + * + * @param p The particle to act upon + * @param xp The particle extended data to act upon + * @param dt The time-step for this kick + * @param half_dt The half time-step for this kick + */ +__attribute__((always_inline)) INLINE static void hydro_kick_extra( + struct part *restrict p, struct xpart *restrict xp, float dt, + float half_dt) { + + /* Do not decrease the entropy (temperature) by more than a factor of 2*/ + const float entropy_change = p->entropy_dt * dt; + if (entropy_change > -0.5f * p->entropy) + p->entropy += entropy_change; + else + p->entropy *= 0.5f; + + /* Do not 'overcool' when timestep increases */ + if (p->entropy + p->entropy_dt * half_dt < 0.5f * p->entropy) + p->entropy_dt = -0.5f * p->entropy / half_dt; + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, p->entropy); + + /* Compute the sound speed from the pressure*/ + const float rho_bar_inv = 1.f / p->rho_bar; + const float soundspeed = sqrtf(hydro_gamma * pressure * rho_bar_inv); + + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; +} + +/** + * @brief Converts hydro quantity of a particle at the start of a run + * + * Requires the density to be known + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_convert_quantities( + struct part *restrict p) { + + /* We read u in the entropy field. We now get S from u */ + p->entropy = gas_entropy_from_internal_energy(p->rho_bar, p->entropy); + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + + /* Compute the pressure */ + const float entropy = p->entropy; + const float pressure = gas_pressure_from_entropy(p->rho_bar, entropy); + + /* Compute the sound speed from the pressure*/ + const float rho_bar_inv = 1.f / p->rho_bar; + const float soundspeed = sqrtf(hydro_gamma * pressure * rho_bar_inv); + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; +} + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_H */ diff --git a/src/hydro/PressureEntropy/hydro_debug.h b/src/hydro/PressureEntropy/hydro_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..486543793515795092e7cc97fe7b567b8230be3b --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_debug.h @@ -0,0 +1,50 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_DEBUG_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_DEBUG_H + +/** + * @file PressureEntropy/hydro_debug.h + * @brief Pressure-Entropy implementation of SPH (Debugging routines) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +__attribute__((always_inline)) INLINE static void hydro_debug_particle( + const struct part* p, const struct xpart* xp) { + printf( + "x=[%.3e,%.3e,%.3e], " + "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e],\n " + "h=%.3e, wcount=%.3f, wcount_dh=%.3e, m=%.3e, dh_drho=%.3e, rho=%.3e, " + "rho_bar=%.3e, P=%.3e, dP_dh=%.3e, P_over_rho2=%.3e, S=%.3e, S^1/g=%.3e, " + "dS/dt=%.3e,\nc=%.3e v_sig=%e dh/dt=%.3e t_begin=%d, t_end=%d\n", + p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], + xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], + p->h, p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, + p->rho, p->rho_bar, hydro_get_pressure(p, 0.), p->density.pressure_dh, + p->force.P_over_rho2, p->entropy, p->entropy_one_over_gamma, + p->entropy_dt, p->force.soundspeed, p->force.v_sig, p->force.h_dt, + p->ti_begin, p->ti_end); +} + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_DEBUG_H */ diff --git a/src/hydro/PressureEntropy/hydro_iact.h b/src/hydro/PressureEntropy/hydro_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..18e22233f6f53e488119a58a988ba4c9faf3db36 --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_iact.h @@ -0,0 +1,399 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_IACT_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_IACT_H + +/** + * @file PressureEntropy/hydro_iact.h + * @brief Pressure-Entropy implementation of SPH (Neighbour loop equations) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +/** + * @brief Density loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_density( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wi_dx; + float wj, wj_dx; + float dv[3], curlvr[3]; + + /* Get the masses. */ + const float mi = pi->mass; + const float mj = pj->mass; + + /* Get r and r inverse. */ + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* Compute the kernel function for pi */ + const float hi_inv = 1.f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the density */ + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + + /* Compute contribution to the number of neighbours */ + pi->density.wcount += wi; + pi->density.wcount_dh -= ui * wi_dx; + + /* Compute contribution to the weighted density */ + pi->rho_bar += mj * pj->entropy_one_over_gamma * wi; + pi->density.pressure_dh -= + mj * pj->entropy_one_over_gamma * (hydro_dimension * wi + ui * wi_dx); + + /* Compute the kernel function for pj */ + const float hj_inv = 1.f / hj; + const float uj = r * hj_inv; + kernel_deval(uj, &wj, &wj_dx); + + /* Compute contribution to the density */ + pj->rho += mi * wj; + pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); + + /* Compute contribution to the number of neighbours */ + pj->density.wcount += wj; + pj->density.wcount_dh -= uj * wj_dx; + + /* Compute contribution to the weighted density */ + pj->rho_bar += mi * pi->entropy_one_over_gamma * wj; + pj->density.pressure_dh -= + mi * pi->entropy_one_over_gamma * (hydro_dimension * wj + uj * wj_dx); + + const float faci = mj * wi_dx * r_inv; + const float facj = mi * wj_dx * r_inv; + + /* Compute dv dot r */ + dv[0] = pi->v[0] - pj->v[0]; + dv[1] = pi->v[1] - pj->v[1]; + dv[2] = pi->v[2] - pj->v[2]; + const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]; + + pi->density.div_v -= faci * dvdr; + pj->density.div_v -= facj * dvdr; + + /* Compute dv cross r */ + curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1]; + curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2]; + curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0]; + + pi->density.rot_v[0] += faci * curlvr[0]; + pi->density.rot_v[1] += faci * curlvr[1]; + pi->density.rot_v[2] += faci * curlvr[2]; + + pj->density.rot_v[0] += facj * curlvr[0]; + pj->density.rot_v[1] += facj * curlvr[1]; + pj->density.rot_v[2] += facj * curlvr[2]; +} + +/** + * @brief Density loop (Vectorized version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_vec_density( + float *R2, float *Dx, float *Hi, float *Hj, struct part **pi, + struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +/** + * @brief Density loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wi_dx; + float dv[3], curlvr[3]; + + /* Get the masses. */ + const float mj = pj->mass; + + /* Get r and r inverse. */ + const float r = sqrtf(r2); + const float ri = 1.0f / r; + + /* Compute the kernel function */ + const float h_inv = 1.0f / hi; + const float ui = r * h_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the density */ + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + + /* Compute contribution to the number of neighbours */ + pi->density.wcount += wi; + pi->density.wcount_dh -= ui * wi_dx; + + /* Compute contribution to the weighted density */ + pi->rho_bar += mj * pj->entropy_one_over_gamma * wi; + pi->density.pressure_dh -= + mj * pj->entropy_one_over_gamma * (hydro_dimension * wi + ui * wi_dx); + + const float fac = mj * wi_dx * ri; + + /* Compute dv dot r */ + dv[0] = pi->v[0] - pj->v[0]; + dv[1] = pi->v[1] - pj->v[1]; + dv[2] = pi->v[2] - pj->v[2]; + const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]; + pi->density.div_v -= fac * dvdr; + + /* Compute dv cross r */ + curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1]; + curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2]; + curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0]; + + pi->density.rot_v[0] += fac * curlvr[0]; + pi->density.rot_v[1] += fac * curlvr[1]; + pi->density.rot_v[2] += fac * curlvr[2]; +} + +/** + * @brief Density loop (non-symmetric vectorized version) + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_vec_density(float *R2, float *Dx, float *Hi, float *Hj, + struct part **pi, struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +/** + * @brief Force loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_force( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wj, wi_dx, wj_dx; + + const float fac_mu = 1.f; /* Will change with cosmological integration */ + + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* Get some values in local variables. */ + const float mi = pi->mass; + const float mj = pj->mass; + const float rhoi = pi->rho; + const float rhoj = pj->rho; + + /* 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 ui = r * hi_inv; + kernel_deval(ui, &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; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute gradient terms */ + const float f_i = pi->force.f; + const float f_j = pj->force.f; + + /* Compute Pressure terms */ + const float P_over_rho2_i = pi->force.P_over_rho2; + const float P_over_rho2_j = pj->force.P_over_rho2; + + /* Compute entropy terms */ + const float S_gamma_i = pi->entropy_one_over_gamma; + const float S_gamma_j = pj->entropy_one_over_gamma; + + /* Compute sound speeds */ + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + /* 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]; + + /* Balsara term */ + const float balsara_i = pi->force.balsara; + const float balsara_j = pj->force.balsara; + + /* Are the particles moving towards each others ? */ + const float omega_ij = fminf(dvdr, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Signal velocity */ + const float v_sig = ci + cj - 3.f * mu_ij; + + /* Now construct the full viscosity term */ + const float rho_ij = 0.5f * (rhoi + rhoj); + const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij * + (balsara_i + balsara_j) / rho_ij; + + /* Now, convolve with the kernel */ + const float visc_term = 0.5f * visc * (wi_dr + wj_dr); + const float sph_term = (S_gamma_j / S_gamma_i - f_i) * P_over_rho2_i * wi_dr + + (S_gamma_i / S_gamma_j - f_j) * P_over_rho2_j * wj_dr; + + /* Eventually got the acceleration */ + const float acc = (visc_term + sph_term) * r_inv; + + /* 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 h. */ + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; + + /* Update the signal velocity. */ + pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig); + pj->force.v_sig = fmaxf(pj->force.v_sig, v_sig); + + /* Change in entropy */ + pi->entropy_dt += mj * visc_term * r_inv * dvdr; + pj->entropy_dt += mi * visc_term * r_inv * dvdr; +} + +/** + * @brief Force loop (Vectorized version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_vec_force( + float *R2, float *Dx, float *Hi, float *Hj, struct part **pi, + struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +/** + * @brief Force loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wj, wi_dx, wj_dx; + + const float fac_mu = 1.f; /* Will change with cosmological integration */ + + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* Get some values in local variables. */ + // const float mi = pi->mass; + const float mj = pj->mass; + const float rhoi = pi->rho; + const float rhoj = pj->rho; + + /* 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 ui = r * hi_inv; + kernel_deval(ui, &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; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute gradient terms */ + const float f_i = pi->force.f; + const float f_j = pj->force.f; + + /* Compute Pressure terms */ + const float P_over_rho2_i = pi->force.P_over_rho2; + const float P_over_rho2_j = pj->force.P_over_rho2; + + /* Compute entropy terms */ + const float S_gamma_i = pi->entropy_one_over_gamma; + const float S_gamma_j = pj->entropy_one_over_gamma; + + /* Compute sound speeds */ + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + /* 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]; + + /* Balsara term */ + const float balsara_i = pi->force.balsara; + const float balsara_j = pj->force.balsara; + + /* Are the particles moving towards each others ? */ + const float omega_ij = fminf(dvdr, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Signal velocity */ + const float v_sig = ci + cj - 3.f * mu_ij; + + /* Now construct the full viscosity term */ + const float rho_ij = 0.5f * (rhoi + rhoj); + const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij * + (balsara_i + balsara_j) / rho_ij; + + /* Now, convolve with the kernel */ + const float visc_term = 0.5f * visc * (wi_dr + wj_dr); + const float sph_term = (S_gamma_j / S_gamma_i - f_i) * P_over_rho2_i * wi_dr + + (S_gamma_i / S_gamma_j - f_j) * P_over_rho2_j * wj_dr; + + /* Eventually got the acceleration */ + const float acc = (visc_term + sph_term) * r_inv; + + /* 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; + + /* Update the signal velocity. */ + pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig); + + /* Change in entropy */ + pi->entropy_dt += mj * visc_term * r_inv * dvdr; +} + +/** + * @brief Force loop (Vectorized non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force( + float *R2, float *Dx, float *Hi, float *Hj, struct part **pi, + struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_IACT_H */ diff --git a/src/hydro/PressureEntropy/hydro_io.h b/src/hydro/PressureEntropy/hydro_io.h new file mode 100644 index 0000000000000000000000000000000000000000..9914a656466f3f0d0a5eeb79b511706d7068ffc6 --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_io.h @@ -0,0 +1,145 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_IO_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_IO_H + +/** + * @file PressureEntropy/hydro_io.h + * @brief Pressure-Entropy implementation of SPH (i/o routines) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +#include "adiabatic_index.h" +#include "hydro.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. + */ +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_ENTROPY_PER_UNIT_MASS, parts, entropy); + 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); +} + +float convert_u(struct engine* e, struct part* p) { + + return hydro_get_internal_energy(p, 0); +} + +float convert_P(struct engine* e, struct part* p) { + + return hydro_get_pressure(p, 0); +} + +/** + * @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. + */ +void hydro_write_particles(struct part* parts, struct io_props* list, + int* num_fields) { + + *num_fields = 11; + + /* List what we want to write */ + list[0] = io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, + parts, x); + list[1] = + io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, v); + list[2] = + io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); + list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, + parts, h); + list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, + UNIT_CONV_ENERGY_PER_UNIT_MASS, + parts, entropy, convert_u); + list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, + UNIT_CONV_NO_UNITS, parts, id); + list[6] = io_make_output_field("Acceleration", FLOAT, 3, + UNIT_CONV_ACCELERATION, parts, a_hydro); + list[7] = + io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); + list[8] = io_make_output_field( + "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, entropy); + list[9] = io_make_output_field_convert_part( + "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, rho, convert_P); + list[10] = io_make_output_field("WeightedDensity", FLOAT, 1, + UNIT_CONV_DENSITY, parts, rho_bar); +} + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +void writeSPHflavour(hid_t h_grpsph) { + + /* Viscosity and thermal conduction */ + /* Nothing in this minimal model... */ + writeAttribute_s(h_grpsph, "Thermal Conductivity Model", "No treatment"); + writeAttribute_s( + h_grpsph, "Viscosity Model", + "as in Springel (2005), i.e. Monaghan (1992) with Balsara (1995) switch"); + writeAttribute_f(h_grpsph, "Viscosity alpha", const_viscosity_alpha); + writeAttribute_f(h_grpsph, "Viscosity beta", 3.f); + + /* Time integration properties */ + writeAttribute_f(h_grpsph, "Maximal Delta u change over dt", + const_max_u_change); +} + +/** + * @brief Are we writing entropy in the internal energy field ? + * + * @return 1 if entropy is in 'internal energy', 0 otherwise. + */ +int writeEntropyFlag() { return 0; } + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_IO_H */ diff --git a/src/hydro/PressureEntropy/hydro_part.h b/src/hydro/PressureEntropy/hydro_part.h new file mode 100644 index 0000000000000000000000000000000000000000..cac585ff79bae737f0e5c09860a38536cbf3a38c --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_part.h @@ -0,0 +1,143 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_PART_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_PART_H + +/** + * @file PressureEntropy/hydro_part.h + * @brief Pressure-Entropy implementation of SPH (Particle definition) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +#include "cooling_struct.h" + +/* Extra particle data not needed during the SPH loops over neighbours. */ +struct xpart { + + /*! Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /*! Velocity at the last full step. */ + float v_full[3]; + + /*! Additional data used to record cooling information */ + struct cooling_xpart_data cooling_data; + +} SWIFT_STRUCT_ALIGN; + +/* Data of a single particle. */ +struct part { + + /*! Particle position. */ + double x[3]; + + /*! Particle predicted velocity. */ + float v[3]; + + /*! Particle acceleration. */ + float a_hydro[3]; + + /*! Particle cutoff radius. */ + float h; + + /*! Particle mass. */ + float mass; + + /*! Particle time of beginning of time-step. */ + int ti_begin; + + /*! Particle time of end of time-step. */ + int ti_end; + + /*! Particle density. */ + float rho; + + /*! Particle weighted density */ + float rho_bar; + + /*! Particle entropy. */ + float entropy; + + /*! Entropy time derivative */ + float entropy_dt; + + /*! Particle entropy to the power 1/gamma. */ + float entropy_one_over_gamma; + + union { + + struct { + + /*! Number of neighbours. */ + float wcount; + + /*! Number of neighbours spatial derivative. */ + float wcount_dh; + + /*! Derivative of density with respect to h */ + float rho_dh; + + /*! Derivative of pressure with respect to h */ + float pressure_dh; + + /*! Particle velocity curl. */ + float rot_v[3]; + + /*! Particle velocity divergence. */ + float div_v; + + } density; + + struct { + + /*! Balsara switch */ + float balsara; + + /*! "Grad h" term */ + float f; + + /*! Pressure term */ + float P_over_rho2; + + /*! Particle sound speed. */ + float soundspeed; + + /*! Signal velocity. */ + float v_sig; + + /*! Time derivative of the smoothing length */ + float h_dt; + + } force; + }; + + /*! Particle ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + +} SWIFT_STRUCT_ALIGN; + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_PART_H */ diff --git a/src/hydro_io.h b/src/hydro_io.h index f0a619b90b774574c434007b1c01a0e55e75e464..05ae94ade7b103ff1b584dc2447cbab40479d1fc 100644 --- a/src/hydro_io.h +++ b/src/hydro_io.h @@ -26,6 +26,8 @@ #include "./hydro/Minimal/hydro_io.h" #elif defined(GADGET2_SPH) #include "./hydro/Gadget2/hydro_io.h" +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro_io.h" #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro_io.h" #elif defined(GIZMO_SPH) diff --git a/src/part.h b/src/part.h index af39d10fafc11d4435ddbcc087fbf08178b18959..6832e0c6c2e0f2c324d90629447cf6a5e809d6fb 100644 --- a/src/part.h +++ b/src/part.h @@ -42,12 +42,19 @@ /* Import the right hydro particle definition */ #if defined(MINIMAL_SPH) #include "./hydro/Minimal/hydro_part.h" +#define hydro_need_extra_init_loop 0 #elif defined(GADGET2_SPH) #include "./hydro/Gadget2/hydro_part.h" +#define hydro_need_extra_init_loop 0 +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro_part.h" +#define hydro_need_extra_init_loop 1 #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro_part.h" +#define hydro_need_extra_init_loop 0 #elif defined(GIZMO_SPH) #include "./hydro/Gizmo/hydro_part.h" +#define hydro_need_extra_init_loop 0 #define EXTRA_HYDRO_LOOP #else #error "Invalid choice of SPH variant" diff --git a/tests/test125cells.c b/tests/test125cells.c index e666658f43de135e3e72521b52f2a688c596a6f6..d423efba3fda764310721586e14192afdee18a85 100644 --- a/tests/test125cells.c +++ b/tests/test125cells.c @@ -95,6 +95,8 @@ void set_energy_state(struct part *part, enum pressure_field press, float size, #if defined(GADGET2_SPH) part->entropy = pressure / pow_gamma(density); +#elif defined(HOPKINS_PE_SPH) + part->entropy = pressure / pow_gamma(density); #elif defined(DEFAULT_SPH) part->u = pressure / (hydro_gamma_minus_one * density); #elif defined(MINIMAL_SPH) diff --git a/tests/test27cells.c b/tests/test27cells.c index 1a1ab88748d922b3e7fbb30a73a10809dca10863..b4c79462153e31d272915808ed5b2fdd086dd5d2 100644 --- a/tests/test27cells.c +++ b/tests/test27cells.c @@ -104,11 +104,18 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, } part->h = size * h / (float)n; part->id = ++(*partId); + #ifdef GIZMO_SPH part->conserved.mass = density * volume / count; #else part->mass = density * volume / count; #endif + +#if defined(HOPKINS_PE_SPH) + part->entropy = 1.f; + part->entropy_one_over_gamma = 1.f; +#endif + part->ti_begin = 0; part->ti_end = 1; ++part; @@ -192,17 +199,14 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, hydro_get_density(&main_cell->parts[pid]), #if defined(GIZMO_SPH) 0.f, +#elif defined(HOPKINS_PE_SPH) + main_cell->parts[pid].density.rho_dh, #else main_cell->parts[pid].rho_dh, #endif main_cell->parts[pid].density.wcount, main_cell->parts[pid].density.wcount_dh, -#if defined(GADGET2_SPH) - main_cell->parts[pid].density.div_v, - main_cell->parts[pid].density.rot_v[0], - main_cell->parts[pid].density.rot_v[1], - main_cell->parts[pid].density.rot_v[2] -#elif defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) main_cell->parts[pid].density.div_v, main_cell->parts[pid].density.rot_v[0], main_cell->parts[pid].density.rot_v[1], @@ -234,14 +238,13 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, cj->parts[pjd].v[2], hydro_get_density(&cj->parts[pjd]), #if defined(GIZMO_SPH) 0.f, +#elif defined(HOPKINS_PE_SPH) + main_cell->parts[pjd].density.rho_dh, #else main_cell->parts[pjd].rho_dh, #endif cj->parts[pjd].density.wcount, cj->parts[pjd].density.wcount_dh, -#if defined(GADGET2_SPH) - cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0], - cj->parts[pjd].density.rot_v[1], cj->parts[pjd].density.rot_v[2] -#elif defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0], cj->parts[pjd].density.rot_v[1], cj->parts[pjd].density.rot_v[2] #else diff --git a/tests/testInteractions.c b/tests/testInteractions.c index 52ad0c54258848883a9025bbcd9d68133eddc4b9..a6786e9600bde49b54031bc7c7369780dbe49111 100644 --- a/tests/testInteractions.c +++ b/tests/testInteractions.c @@ -110,7 +110,12 @@ void dump_indv_particle_fields(char *fileName, struct part *p) { "%13e %13e %13e %13e " "%13e %13e %13e %10f\n", p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], - p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->rho, p->rho_dh, + p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->rho, +#ifdef HOPKINS_PE_SPH + p->density.rho_dh, +#else + p->rho_dh, +#endif p->density.wcount, p->density.wcount_dh, p->force.h_dt, p->force.v_sig, #if defined(GADGET2_SPH) diff --git a/tests/testPair.c b/tests/testPair.c index efa1e628c2d57bf7922be8affdd5436ebca2f9cf..d19d852a857e00665b82c604f31986aebcca4d6c 100644 --- a/tests/testPair.c +++ b/tests/testPair.c @@ -138,11 +138,13 @@ void dump_particle_fields(char *fileName, struct cell *ci, struct cell *cj) { ci->parts[pid].v[2], hydro_get_density(&ci->parts[pid]), #if defined(GIZMO_SPH) 0.f, +#elif defined(HOPKINS_PE_SPH) + ci->parts[pid].density.rho_dh, #else - cj->parts[pid].rho_dh, + ci->parts[pid].rho_dh, #endif ci->parts[pid].density.wcount, ci->parts[pid].density.wcount_dh, -#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) ci->parts[pid].density.div_v, ci->parts[pid].density.rot_v[0], ci->parts[pid].density.rot_v[1], ci->parts[pid].density.rot_v[2] #else @@ -162,11 +164,13 @@ void dump_particle_fields(char *fileName, struct cell *ci, struct cell *cj) { cj->parts[pjd].v[2], hydro_get_density(&cj->parts[pjd]), #if defined(GIZMO_SPH) 0.f, +#elif defined(HOPKINS_PE_SPH) + cj->parts[pjd].density.rho_dh, #else cj->parts[pjd].rho_dh, #endif cj->parts[pjd].density.wcount, cj->parts[pjd].density.wcount_dh, -#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0], cj->parts[pjd].density.rot_v[1], cj->parts[pjd].density.rot_v[2] #else