diff --git a/src/engine.c b/src/engine.c index 584cf20146418ecdba67f786a1bd1e7165e3caa0..a8ec2d1ddaf9220c7b2553fb223c968c53768eec 100644 --- a/src/engine.c +++ b/src/engine.c @@ -1827,7 +1827,11 @@ void engine_maketasks(struct engine *e) { is the number of cells (s->tot_cells) times the number of neighbours (27) times the number of interaction types (2, density and force). */ if (e->links != NULL) free(e->links); +#ifdef EXTRA_HYDRO_LOOP + e->size_links = s->tot_cells * 27 * 3; +#else e->size_links = s->tot_cells * 27 * 2; +#endif if ((e->links = malloc(sizeof(struct link) * e->size_links)) == NULL) error("Failed to allocate cell-task links."); e->nr_links = 0; @@ -2716,7 +2720,7 @@ void engine_step(struct engine *e) { mask |= 1 << task_type_sub_pair; mask |= 1 << task_type_ghost; mask |= 1 << task_type_extra_ghost; /* Adding unnecessary things to the mask - does not harm */ + does not harm */ submask |= 1 << task_subtype_density; submask |= 1 << task_subtype_gradient; diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/Gizmo/hydro.h index 7bc75059e680e96585263e951f1a0e12fcd57fad..3dc8b62918c9df34cbfbbd0c7f4f34a7c524b185 100644 --- a/src/hydro/Gizmo/hydro.h +++ b/src/hydro/Gizmo/hydro.h @@ -21,14 +21,12 @@ #include "adiabatic_index.h" #include "hydro_gradients.h" -#define THERMAL_ENERGY - /** * @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 - * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + * @param hydro_properties Pointer to the hydro parameters. */ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const struct part* restrict p, const struct xpart* restrict xp, @@ -45,6 +43,12 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( * This function is called only once just after the ICs have been * read in to do some conversions. * + * In this case, we copy the particle velocities into the corresponding + * primitive variable field. We do this because the particle velocities in GIZMO + * can be independent of the actual fluid velocity. The latter is stored as a + * primitive variable and integrated using the linear momentum, a conserved + * variable. + * * @param p The particle to act upon * @param xp The extended particle data to act upon */ @@ -59,14 +63,13 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( /** * @brief Prepares a particle for the volume calculation. * + * Simply makes sure all necessary variables are initialized to zero. + * * @param p The particle to act upon */ __attribute__((always_inline)) INLINE static void hydro_init_part( struct part* p) { - /* We need to do this before we reset the volume */ - hydro_gradients_init_density_loop(p); - p->density.wcount = 0.0f; p->density.wcount_dh = 0.0f; p->geometry.volume = 0.0f; @@ -82,12 +85,24 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( } /** - * @brief Finishes the density calculation. + * @brief Finishes the volume calculation. * * Multiplies the density and number of neighbours by the appropiate constants - * and add the self-contribution term. + * and adds the self-contribution term. Calculates the volume and uses it to + * update the primitive variables (based on the conserved variables). The latter + * should only be done for active particles. This is okay, since this method is + * only called for active particles. * - * @param p The particle to act upon + * Multiplies the components of the matrix E with the appropriate constants and + * inverts it. Initializes the variables used during the gradient loop. This + * cannot be done in hydro_prepare_force, since that method is called for all + * particles, and not just the active ones. If we would initialize the + * variables there, gradients for passive particles would be zero, while we + * actually use the old gradients in the flux calculation between active and + * passive particles. + * + * @param p The particle to act upon. + * @param The current physical time. */ __attribute__((always_inline)) INLINE static void hydro_end_density( struct part* restrict p, float time) { @@ -107,10 +122,6 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( float volume; float m, momentum[3], energy; -#ifndef THERMAL_ENERGY - float momentum2; -#endif - /* Final operation on the geometry. */ /* we multiply with the smoothing kernel normalization ih3 and calculate the * volume */ @@ -129,7 +140,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( invert_dimension_by_dimension_matrix(p->geometry.matrix_E); - hydro_gradients_prepare_force_loop(p, ih, volume); + hydro_gradients_init(p); /* compute primitive variables */ /* eqns (3)-(5) */ @@ -138,31 +149,30 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( momentum[0] = p->conserved.momentum[0]; momentum[1] = p->conserved.momentum[1]; momentum[2] = p->conserved.momentum[2]; -#ifndef THERMAL_ENERGY - momentum2 = (momentum[0] * momentum[0] + momentum[1] * momentum[1] + - momentum[2] * momentum[2]); -#endif p->primitives.rho = m / volume; p->primitives.v[0] = momentum[0] / m; p->primitives.v[1] = momentum[1] / m; p->primitives.v[2] = momentum[2] / m; energy = p->conserved.energy; -#ifndef THERMAL_ENERGY - p->primitives.P = - hydro_gamma_minus_one * (energy - 0.5 * momentum2 / m) / volume; -#else p->primitives.P = hydro_gamma_minus_one * energy / volume; -#endif } } /** - * @brief Prepare a particle for the force calculation. + * @brief Prepare a particle for the gradient calculation. * - * Computes viscosity term, conduction term and smoothing length gradient terms. + * The name of this method is confusing, as this method is really called after + * the density loop and before the gradient loop. * - * @param p The particle to act upon - * @param xp The extended particle data to act upon + * 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 ti_current Current integer time. + * @param timeBase Conversion factor between integer time and physical time. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part* restrict p, struct xpart* restrict xp, int ti_current, @@ -170,6 +180,10 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Set the physical time step */ p->force.dt = (p->ti_end - p->ti_begin) * timeBase; + + /* Initialize time step criterion variables */ + p->timestepvars.vmax = 0.0f; + /* Set the actual velocity of the particle */ p->force.v_full[0] = xp->v_full[0]; p->force.v_full[1] = xp->v_full[1]; @@ -179,166 +193,24 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /** * @brief Finishes the gradient calculation. * - * @param p The particle to act upon + * Just a wrapper around hydro_gradients_finalize, which can be an empty method, + * in which case no gradients are used. + * + * @param p The particle to act upon. */ __attribute__((always_inline)) INLINE static void hydro_end_gradient( struct part* p) { -#ifndef SPH_GRADIENTS - float h, ih, ih2, ih3; -#ifdef SLOPE_LIMITER - float gradrho[3], gradv[3][3], gradP[3]; - float gradtrue, gradmax, gradmin, alpha; -#endif - - /* add kernel normalization to gradients */ - h = p->h; - ih = 1.0f / h; - ih2 = ih * ih; - ih3 = ih * ih2; - - p->primitives.gradients.rho[0] *= ih3; - p->primitives.gradients.rho[1] *= ih3; - p->primitives.gradients.rho[2] *= ih3; - - p->primitives.gradients.v[0][0] *= ih3; - p->primitives.gradients.v[0][1] *= ih3; - p->primitives.gradients.v[0][2] *= ih3; - p->primitives.gradients.v[1][0] *= ih3; - p->primitives.gradients.v[1][1] *= ih3; - p->primitives.gradients.v[1][2] *= ih3; - p->primitives.gradients.v[2][0] *= ih3; - p->primitives.gradients.v[2][1] *= ih3; - p->primitives.gradients.v[2][2] *= ih3; - - p->primitives.gradients.P[0] *= ih3; - p->primitives.gradients.P[1] *= ih3; - p->primitives.gradients.P[2] *= ih3; - -/* slope limiter */ -#ifdef SLOPE_LIMITER - gradrho[0] = p->primitives.gradients.rho[0]; - gradrho[1] = p->primitives.gradients.rho[1]; - gradrho[2] = p->primitives.gradients.rho[2]; - - gradv[0][0] = p->primitives.gradients.v[0][0]; - gradv[0][1] = p->primitives.gradients.v[0][1]; - gradv[0][2] = p->primitives.gradients.v[0][2]; - - gradv[1][0] = p->primitives.gradients.v[1][0]; - gradv[1][1] = p->primitives.gradients.v[1][1]; - gradv[1][2] = p->primitives.gradients.v[1][2]; - - gradv[2][0] = p->primitives.gradients.v[2][0]; - gradv[2][1] = p->primitives.gradients.v[2][1]; - gradv[2][2] = p->primitives.gradients.v[2][2]; - - gradP[0] = p->primitives.gradients.P[0]; - gradP[1] = p->primitives.gradients.P[1]; - gradP[2] = p->primitives.gradients.P[2]; - - gradtrue = gradrho[0] * gradrho[0] + gradrho[1] * gradrho[1] + - gradrho[2] * gradrho[2]; - /* gradtrue might be zero. In this case, there is no gradient and we don't - need to slope limit anything... */ - if (gradtrue) { - gradtrue = sqrtf(gradtrue); - gradtrue *= p->primitives.limiter.maxr; - gradmax = p->primitives.limiter.rho[1] - p->primitives.rho; - gradmin = p->primitives.rho - p->primitives.limiter.rho[0]; - /* gradmin and gradmax might be negative if the value of the current - particle is larger/smaller than all neighbouring values */ - gradmax = fabs(gradmax); - gradmin = fabs(gradmin); - alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue)); - p->primitives.gradients.rho[0] *= alpha; - p->primitives.gradients.rho[1] *= alpha; - p->primitives.gradients.rho[2] *= alpha; - } - - gradtrue = gradv[0][0] * gradv[0][0] + gradv[0][1] * gradv[0][1] + - gradv[0][2] * gradv[0][2]; - if (gradtrue) { - gradtrue = sqrtf(gradtrue); - gradtrue *= p->primitives.limiter.maxr; - gradmax = p->primitives.limiter.v[0][1] - p->primitives.v[0]; - gradmin = p->primitives.v[0] - p->primitives.limiter.v[0][0]; - gradmax = fabs(gradmax); - gradmin = fabs(gradmin); - alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue)); - p->primitives.gradients.v[0][0] *= alpha; - p->primitives.gradients.v[0][1] *= alpha; - p->primitives.gradients.v[0][2] *= alpha; - } - - gradtrue = gradv[1][0] * gradv[1][0] + gradv[1][1] * gradv[1][1] + - gradv[1][2] * gradv[1][2]; - if (gradtrue) { - gradtrue = sqrtf(gradtrue); - gradtrue *= p->primitives.limiter.maxr; - gradmax = p->primitives.limiter.v[1][1] - p->primitives.v[1]; - gradmin = p->primitives.v[1] - p->primitives.limiter.v[1][0]; - gradmax = fabs(gradmax); - gradmin = fabs(gradmin); - alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue)); - p->primitives.gradients.v[1][0] *= alpha; - p->primitives.gradients.v[1][1] *= alpha; - p->primitives.gradients.v[1][2] *= alpha; - } - - gradtrue = gradv[2][0] * gradv[2][0] + gradv[2][1] * gradv[2][1] + - gradv[2][2] * gradv[2][2]; - if (gradtrue) { - gradtrue = sqrtf(gradtrue); - gradtrue *= p->primitives.limiter.maxr; - gradmax = p->primitives.limiter.v[2][1] - p->primitives.v[2]; - gradmin = p->primitives.v[2] - p->primitives.limiter.v[2][0]; - gradmax = fabs(gradmax); - gradmin = fabs(gradmin); - alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue)); - p->primitives.gradients.v[2][0] *= alpha; - p->primitives.gradients.v[2][1] *= alpha; - p->primitives.gradients.v[2][2] *= alpha; - } - - gradtrue = gradP[0] * gradP[0] + gradP[1] * gradP[1] + gradP[2] * gradP[2]; - if (gradtrue) { - gradtrue = sqrtf(gradtrue); - gradtrue *= p->primitives.limiter.maxr; - gradmax = p->primitives.limiter.P[1] - p->primitives.P; - gradmin = p->primitives.P - p->primitives.limiter.P[0]; - gradmax = fabs(gradmax); - gradmin = fabs(gradmin); - alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue)); - p->primitives.gradients.P[0] *= alpha; - p->primitives.gradients.P[1] *= alpha; - p->primitives.gradients.P[2] *= alpha; - } -#endif // SLOPE_LIMITER - -#endif // SPH_GRADIENTS -} - -/** - * @brief Prepare a particle for the fluxes calculation. - * - * @param p The particle to act upon - * @param xp The extended particle data to act upon - */ -__attribute__((always_inline)) INLINE static void hydro_prepare_fluxes( - struct part* p, struct xpart* xp) { - - /* initialize variables used for timestep calculation */ - p->timestepvars.vmax = 0.0f; + hydro_gradients_finalize(p); } /** * @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 + * This is actually not necessary for GIZMO, since we just set the accelerations + * after the flux calculation. * - * @param p The particle to act upon + * @param p The particle to act upon. */ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( struct part* p) { @@ -350,11 +222,20 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration( } /** - * @brief Converts hydro quantity of a particle + * @brief Converts the hydrodynamic variables from the initial condition file to + * conserved variables that can be used during the integration * - * Requires the volume to be known + * Requires the volume to be known. * - * @param p The particle to act upon + * The initial condition file contains a mixture of primitive and conserved + * variables. Mass is a conserved variable, and we just copy the particle + * mass into the corresponding conserved quantity. We need the volume to + * also derive a density, which is then used to convert the internal energy + * to a pressure. However, we do not actually use these variables anymore. + * We do need to initialize the linear momentum, based on the mass and the + * velocity of the particle. + * + * @param p The particle to act upon. */ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( struct part* p) { @@ -362,9 +243,6 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( float volume; float m; float momentum[3]; -#ifndef THERMAL_ENERGY - float momentum2; -#endif volume = p->geometry.volume; p->conserved.mass = m = p->mass; @@ -376,19 +254,35 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( p->conserved.momentum[0] = momentum[0] = m * p->primitives.v[0]; p->conserved.momentum[1] = momentum[1] = m * p->primitives.v[1]; p->conserved.momentum[2] = momentum[2] = m * p->primitives.v[2]; -#ifndef THERMAL_ENERGY - momentum2 = momentum[0] * momentum[0] + momentum[1] * momentum[1] + - momentum[2] * momentum[2]; - p->conserved.energy = - p->primitives.P / hydro_gamma_minus_one * volume + 0.5 * momentum2 / m; -#else p->conserved.energy = p->primitives.P / hydro_gamma_minus_one * volume; -#endif } +/** + * @brief Extra operations to be done during the drift + * + * Not used for GIZMO. + * + * @param p Particle to act upon. + * @param xp The extended particle data to act upon. + * @param t0 Integer start time of the drift interval. + * @param t1 Integer end time of the drift interval. + * @param timeBase Conversion factor between integer and physical time. + */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( struct part* p, struct xpart* xp, int t0, int t1, double timeBase) {} +/** + * @brief Set the particle acceleration after the flux loop + * + * We use the new conserved variables to calculate the new velocity of the + * particle, and use that to derive the change of the velocity over the particle + * time step. + * + * If the particle time step is zero, we set the accelerations to zero. This + * should only happen at the start of the simulation. + * + * @param p Particle to act upon. + */ __attribute__((always_inline)) INLINE static void hydro_end_force( struct part* p) { @@ -414,17 +308,25 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( } } +/** + * @brief Extra operations done during the kick + * + * Not used for GIZMO. + * + * @param p Particle to act upon. + * @param xp Extended particle data to act upon. + * @param dt Physical time step. + * @param half_dt Half the physical time step. + */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( - struct part* p, struct xpart* xp, float dt, float half_dt) { - - /* Nothing needs to be done in this case */ -} + struct part* p, struct xpart* xp, float dt, float half_dt) {} /** * @brief Returns the internal energy of a particle * - * @param p The particle of interest - * @param dt Time since the last kick + * @param p The particle of interest. + * @param dt Time since the last kick. + * @return Internal energy of the particle. */ __attribute__((always_inline)) INLINE static float hydro_get_internal_energy( const struct part* restrict p, float dt) { @@ -435,8 +337,9 @@ __attribute__((always_inline)) INLINE static float hydro_get_internal_energy( /** * @brief Returns the entropy of a particle * - * @param p The particle of interest - * @param dt Time since the last kick + * @param p The particle of interest. + * @param dt Time since the last kick. + * @return Entropy of the particle. */ __attribute__((always_inline)) INLINE static float hydro_get_entropy( const struct part* restrict p, float dt) { @@ -447,8 +350,9 @@ __attribute__((always_inline)) INLINE static float hydro_get_entropy( /** * @brief Returns the sound speed of a particle * - * @param p The particle of interest - * @param dt Time since the last kick + * @param p The particle of interest. + * @param dt Time since the last kick. + * @param Sound speed of the particle. */ __attribute__((always_inline)) INLINE static float hydro_get_soundspeed( const struct part* restrict p, float dt) { @@ -461,6 +365,7 @@ __attribute__((always_inline)) INLINE static float hydro_get_soundspeed( * * @param p The particle of interest * @param dt Time since the last kick + * @param Pressure of the particle. */ __attribute__((always_inline)) INLINE static float hydro_get_pressure( const struct part* restrict p, float dt) { diff --git a/src/hydro/Gizmo/hydro_gradients.h b/src/hydro/Gizmo/hydro_gradients.h index ff1e39d68bc2f9b02b80743e497c270fd97337fa..5a97b014288e7a1837dd78f47a7228ff7c9e7038 100644 --- a/src/hydro/Gizmo/hydro_gradients.h +++ b/src/hydro/Gizmo/hydro_gradients.h @@ -20,43 +20,70 @@ #ifndef SWIFT_HYDRO_GRADIENTS_H #define SWIFT_HYDRO_GRADIENTS_H -#define SPH_GRADIENTS +//#define SPH_GRADIENTS +#define GIZMO_GRADIENTS #include "hydro_slope_limiters.h" #if defined(SPH_GRADIENTS) + +#define HYDRO_GRADIENT_IMPLEMENTATION "SPH gradients (Price 2012)" #include "hydro_gradients_sph.h" + #elif defined(GIZMO_GRADIENTS) + +#define HYDRO_GRADIENT_IMPLEMENTATION "GIZMO gradients (Hopkins 2015)" #include "hydro_gradients_gizmo.h" + #else /* No gradients. Perfectly acceptable, but we have to provide empty functions */ +#define HYDRO_GRADIENT_IMPLEMENTATION "No gradients (first order scheme)" /** - * @brief Initialize variables before the density loop + * @brief Initialize gradient variables + * + * @param p Particle. */ -__attribute__((always_inline)) INLINE static void -hydro_gradients_init_density_loop(struct part *p) {} +__attribute__((always_inline)) INLINE static void hydro_gradients_init( + struct part* p) {} /** - * @brief Gradient calculations done during the density loop + * @brief Gradient calculations done during the neighbour loop + * + * @param r2 Squared distance between the two particles. + * @param dx Distance vector (pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. */ -__attribute__((always_inline)) INLINE static void hydro_gradients_density_loop( - struct part *pi, struct part *pj, float wi_dx, float wj_dx, float *dx, - float r, int mode) {} +__attribute__((always_inline)) INLINE static void hydro_gradients_collect( + float r2, float* dx, float hi, float hj, struct part* pi, struct part* pj) { +} /** - * @brief Calculations done before the force loop + * @brief Gradient calculations done during the neighbour loop: non-symmetric + * version + * + * @param r2 Squared distance between the two particles. + * @param dx Distance vector (pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. */ -__attribute__((always_inline)) INLINE static void -hydro_gradients_prepare_force_loop(struct part *p, float ih2, float volume) {} +__attribute__((always_inline)) INLINE static void hydro_gradients_collect( + float r2, float* dx, float hi, float hj, struct part* pi, struct part* pj) { +} /** - * @brief Gradient calculations done during the gradient loop + * @brief Finalize the gradient variables after all data have been collected + * + * @param p Particle. */ -__attribute__((always_inline)) INLINE static void hydro_gradients_gradient_loop( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj, - int mode) {} +__attribute__((always_inline)) INLINE static void hydro_gradients_finalize( + struct part* p) {} #endif diff --git a/src/hydro/Gizmo/hydro_gradients_gizmo.h b/src/hydro/Gizmo/hydro_gradients_gizmo.h index 97bbcf98d76eca730ac49a5dcfbfae7b2f336c2c..aa6e4406b94e7a5cafcd0ca556162476003477de 100644 --- a/src/hydro/Gizmo/hydro_gradients_gizmo.h +++ b/src/hydro/Gizmo/hydro_gradients_gizmo.h @@ -18,20 +18,12 @@ ******************************************************************************/ /** - * @brief Initialize variables before the density loop - */ -__attribute__((always_inline)) INLINE static void -hydro_gradients_init_density_loop(struct part *p) {} - -/** - * @brief Gradient calculations done during the density loop + * @brief Initialize gradient variables + * + * @param p Particle. */ -__attribute__((always_inline)) INLINE static void hydro_gradients_density_loop( - struct part *pi, struct part *pj, float wi_dx, float wj_dx, float *dx, - float r, int mode) {} - -__attribute__((always_inline)) INLINE static void -hydro_gradients_prepare_force_loop(struct part *p, float ih2, volume) { +__attribute__((always_inline)) INLINE static void hydro_gradients_init( + struct part *p) { p->primitives.gradients.rho[0] = 0.0f; p->primitives.gradients.rho[1] = 0.0f; @@ -53,26 +45,21 @@ hydro_gradients_prepare_force_loop(struct part *p, float ih2, volume) { p->primitives.gradients.P[1] = 0.0f; p->primitives.gradients.P[2] = 0.0f; - p->primitives.limiter.rho[0] = FLT_MAX; - p->primitives.limiter.rho[1] = -FLT_MAX; - p->primitives.limiter.v[0][0] = FLT_MAX; - p->primitives.limiter.v[0][1] = -FLT_MAX; - p->primitives.limiter.v[1][0] = FLT_MAX; - p->primitives.limiter.v[1][1] = -FLT_MAX; - p->primitives.limiter.v[2][0] = FLT_MAX; - p->primitives.limiter.v[2][1] = -FLT_MAX; - p->primitives.limiter.P[0] = FLT_MAX; - p->primitives.limiter.P[1] = -FLT_MAX; - - p->primitives.limiter.maxr = -FLT_MAX; + hydro_slope_limit_cell_init(p); } /** - * @brief Gradient calculations done during the gradient loop + * @brief Gradient calculations done during the neighbour loop + * + * @param r2 Squared distance between the two particles. + * @param dx Distance vector (pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. */ -__attribute__((always_inline)) INLINE static void hydro_gradients_gradient_loop( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj, - int mode) { +__attribute__((always_inline)) INLINE static void hydro_gradients_collect( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { float r = sqrtf(r2); float xi, xj; @@ -157,116 +144,198 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_gradient_loop( (Wi[4] - Wj[4]) * wi * (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]); - /* basic slope limiter: collect the maximal and the minimal value for the - * primitive variables among the ngbs */ - pi->primitives.limiter.rho[0] = - fmin(pj->primitives.rho, pi->primitives.limiter.rho[0]); - pi->primitives.limiter.rho[1] = - fmax(pj->primitives.rho, pi->primitives.limiter.rho[1]); - - pi->primitives.limiter.v[0][0] = - fmin(pj->primitives.v[0], pi->primitives.limiter.v[0][0]); - pi->primitives.limiter.v[0][1] = - fmax(pj->primitives.v[0], pi->primitives.limiter.v[0][1]); - pi->primitives.limiter.v[1][0] = - fmin(pj->primitives.v[1], pi->primitives.limiter.v[1][0]); - pi->primitives.limiter.v[1][1] = - fmax(pj->primitives.v[1], pi->primitives.limiter.v[1][1]); - pi->primitives.limiter.v[2][0] = - fmin(pj->primitives.v[2], pi->primitives.limiter.v[2][0]); - pi->primitives.limiter.v[2][1] = - fmax(pj->primitives.v[2], pi->primitives.limiter.v[2][1]); - - pi->primitives.limiter.P[0] = - fmin(pj->primitives.P, pi->primitives.limiter.P[0]); - pi->primitives.limiter.P[1] = - fmax(pj->primitives.P, pi->primitives.limiter.P[1]); - - pi->primitives.limiter.maxr = fmax(r, pi->primitives.limiter.maxr); - - if (mode == 1) { - /* Compute kernel of pj. */ - hj_inv = 1.0 / hj; - xj = r * hj_inv; - kernel_deval(xj, &wj, &wj_dx); - - /* Compute gradients for pj */ - /* there is no sign difference w.r.t. eqn. (6) because dx is now what we - * want - * it to be */ - pj->primitives.gradients.rho[0] += - (Wi[0] - Wj[0]) * wj * - (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); - pj->primitives.gradients.rho[1] += - (Wi[0] - Wj[0]) * wj * - (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); - pj->primitives.gradients.rho[2] += - (Wi[0] - Wj[0]) * wj * - (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]); - - pj->primitives.gradients.v[0][0] += - (Wi[1] - Wj[1]) * wj * - (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); - pj->primitives.gradients.v[0][1] += - (Wi[1] - Wj[1]) * wj * - (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); - pj->primitives.gradients.v[0][2] += - (Wi[1] - Wj[1]) * wj * - (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]); - pj->primitives.gradients.v[1][0] += - (Wi[2] - Wj[2]) * wj * - (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); - pj->primitives.gradients.v[1][1] += - (Wi[2] - Wj[2]) * wj * - (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); - pj->primitives.gradients.v[1][2] += - (Wi[2] - Wj[2]) * wj * - (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]); - pj->primitives.gradients.v[2][0] += - (Wi[3] - Wj[3]) * wj * - (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); - pj->primitives.gradients.v[2][1] += - (Wi[3] - Wj[3]) * wj * - (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); - pj->primitives.gradients.v[2][2] += - (Wi[3] - Wj[3]) * wj * - (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]); - - pj->primitives.gradients.P[0] += - (Wi[4] - Wj[4]) * wj * - (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); - pj->primitives.gradients.P[1] += - (Wi[4] - Wj[4]) * wj * - (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); - pj->primitives.gradients.P[2] += - (Wi[4] - Wj[4]) * wj * - (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]); - - /* basic slope limiter: collect the maximal and the minimal value for the - * primitive variables among the ngbs */ - pj->primitives.limiter.rho[0] = - fmin(pi->primitives.rho, pj->primitives.limiter.rho[0]); - pj->primitives.limiter.rho[1] = - fmax(pi->primitives.rho, pj->primitives.limiter.rho[1]); - - pj->primitives.limiter.v[0][0] = - fmin(pi->primitives.v[0], pj->primitives.limiter.v[0][0]); - pj->primitives.limiter.v[0][1] = - fmax(pi->primitives.v[0], pj->primitives.limiter.v[0][1]); - pj->primitives.limiter.v[1][0] = - fmin(pi->primitives.v[1], pj->primitives.limiter.v[1][0]); - pj->primitives.limiter.v[1][1] = - fmax(pi->primitives.v[1], pj->primitives.limiter.v[1][1]); - pj->primitives.limiter.v[2][0] = - fmin(pi->primitives.v[2], pj->primitives.limiter.v[2][0]); - pj->primitives.limiter.v[2][1] = - fmax(pi->primitives.v[2], pj->primitives.limiter.v[2][1]); - - pj->primitives.limiter.P[0] = - fmin(pi->primitives.P, pj->primitives.limiter.P[0]); - pj->primitives.limiter.P[1] = - fmax(pi->primitives.P, pj->primitives.limiter.P[1]); - - pj->primitives.limiter.maxr = fmax(r, pj->primitives.limiter.maxr); + hydro_slope_limit_cell_collect(pi, pj, r); + + /* Compute kernel of pj. */ + hj_inv = 1.0 / hj; + xj = r * hj_inv; + kernel_deval(xj, &wj, &wj_dx); + + /* Compute gradients for pj */ + /* there is no sign difference w.r.t. eqn. (6) because dx is now what we + * want + * it to be */ + pj->primitives.gradients.rho[0] += + (Wi[0] - Wj[0]) * wj * + (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); + pj->primitives.gradients.rho[1] += + (Wi[0] - Wj[0]) * wj * + (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); + pj->primitives.gradients.rho[2] += + (Wi[0] - Wj[0]) * wj * + (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]); + + pj->primitives.gradients.v[0][0] += + (Wi[1] - Wj[1]) * wj * + (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); + pj->primitives.gradients.v[0][1] += + (Wi[1] - Wj[1]) * wj * + (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); + pj->primitives.gradients.v[0][2] += + (Wi[1] - Wj[1]) * wj * + (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]); + pj->primitives.gradients.v[1][0] += + (Wi[2] - Wj[2]) * wj * + (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); + pj->primitives.gradients.v[1][1] += + (Wi[2] - Wj[2]) * wj * + (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); + pj->primitives.gradients.v[1][2] += + (Wi[2] - Wj[2]) * wj * + (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]); + pj->primitives.gradients.v[2][0] += + (Wi[3] - Wj[3]) * wj * + (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); + pj->primitives.gradients.v[2][1] += + (Wi[3] - Wj[3]) * wj * + (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); + pj->primitives.gradients.v[2][2] += + (Wi[3] - Wj[3]) * wj * + (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]); + + pj->primitives.gradients.P[0] += + (Wi[4] - Wj[4]) * wj * + (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]); + pj->primitives.gradients.P[1] += + (Wi[4] - Wj[4]) * wj * + (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]); + pj->primitives.gradients.P[2] += + (Wi[4] - Wj[4]) * wj * + (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]); + + hydro_slope_limit_cell_collect(pj, pi, r); +} + +/** + * @brief Gradient calculations done during the neighbour loop + * + * @param r2 Squared distance between the two particles. + * @param dx Distance vector (pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. + */ +__attribute__((always_inline)) INLINE static void +hydro_gradients_nonsym_collect(float r2, float *dx, float hi, float hj, + struct part *pi, struct part *pj) { + + float r = sqrtf(r2); + float xi; + float hi_inv; + float wi, wi_dx; + int k, l; + float Bi[3][3]; + float Wi[5], Wj[5]; + + /* Initialize local variables */ + for (k = 0; k < 3; k++) { + for (l = 0; l < 3; l++) { + Bi[k][l] = pi->geometry.matrix_E[k][l]; + } } + Wi[0] = pi->primitives.rho; + Wi[1] = pi->primitives.v[0]; + Wi[2] = pi->primitives.v[1]; + Wi[3] = pi->primitives.v[2]; + Wi[4] = pi->primitives.P; + Wj[0] = pj->primitives.rho; + Wj[1] = pj->primitives.v[0]; + Wj[2] = pj->primitives.v[1]; + Wj[3] = pj->primitives.v[2]; + Wj[4] = pj->primitives.P; + + /* Compute kernel of pi. */ + hi_inv = 1.0 / hi; + xi = r * hi_inv; + kernel_deval(xi, &wi, &wi_dx); + + /* Compute gradients for pi */ + /* there is a sign difference w.r.t. eqn. (6) because of the inverse + * definition of dx */ + pi->primitives.gradients.rho[0] += + (Wi[0] - Wj[0]) * wi * + (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]); + pi->primitives.gradients.rho[1] += + (Wi[0] - Wj[0]) * wi * + (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]); + pi->primitives.gradients.rho[2] += + (Wi[0] - Wj[0]) * wi * + (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]); + + pi->primitives.gradients.v[0][0] += + (Wi[1] - Wj[1]) * wi * + (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]); + pi->primitives.gradients.v[0][1] += + (Wi[1] - Wj[1]) * wi * + (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]); + pi->primitives.gradients.v[0][2] += + (Wi[1] - Wj[1]) * wi * + (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]); + pi->primitives.gradients.v[1][0] += + (Wi[2] - Wj[2]) * wi * + (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]); + pi->primitives.gradients.v[1][1] += + (Wi[2] - Wj[2]) * wi * + (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]); + pi->primitives.gradients.v[1][2] += + (Wi[2] - Wj[2]) * wi * + (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]); + pi->primitives.gradients.v[2][0] += + (Wi[3] - Wj[3]) * wi * + (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]); + pi->primitives.gradients.v[2][1] += + (Wi[3] - Wj[3]) * wi * + (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]); + pi->primitives.gradients.v[2][2] += + (Wi[3] - Wj[3]) * wi * + (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]); + + pi->primitives.gradients.P[0] += + (Wi[4] - Wj[4]) * wi * + (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]); + pi->primitives.gradients.P[1] += + (Wi[4] - Wj[4]) * wi * + (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]); + pi->primitives.gradients.P[2] += + (Wi[4] - Wj[4]) * wi * + (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]); + + hydro_slope_limit_cell_collect(pi, pj, r); +} + +/** + * @brief Finalize the gradient variables after all data have been collected + * + * @param p Particle. + */ +__attribute__((always_inline)) INLINE static void hydro_gradients_finalize( + struct part *p) { + + float h, ih; + + /* add kernel normalization to gradients */ + h = p->h; + ih = 1.0f / h; + const float ihdim = pow_dimension(ih); + + p->primitives.gradients.rho[0] *= ihdim; + p->primitives.gradients.rho[1] *= ihdim; + p->primitives.gradients.rho[2] *= ihdim; + + p->primitives.gradients.v[0][0] *= ihdim; + p->primitives.gradients.v[0][1] *= ihdim; + p->primitives.gradients.v[0][2] *= ihdim; + p->primitives.gradients.v[1][0] *= ihdim; + p->primitives.gradients.v[1][1] *= ihdim; + p->primitives.gradients.v[1][2] *= ihdim; + p->primitives.gradients.v[2][0] *= ihdim; + p->primitives.gradients.v[2][1] *= ihdim; + p->primitives.gradients.v[2][2] *= ihdim; + + p->primitives.gradients.P[0] *= ihdim; + p->primitives.gradients.P[1] *= ihdim; + p->primitives.gradients.P[2] *= ihdim; + + hydro_slope_limit_cell(p); } diff --git a/src/hydro/Gizmo/hydro_gradients_sph.h b/src/hydro/Gizmo/hydro_gradients_sph.h index f8dc7ea00458dd11c45aa0a340e6139376a286ae..f635faecea549f7da280ade9b944021a5e4aeb4c 100644 --- a/src/hydro/Gizmo/hydro_gradients_sph.h +++ b/src/hydro/Gizmo/hydro_gradients_sph.h @@ -18,10 +18,12 @@ ******************************************************************************/ /** - * @brief Initialize variables before the density loop + * @brief Initialize gradient variables + * + * @param p Particle. */ -__attribute__((always_inline)) INLINE static void -hydro_gradients_init_density_loop(struct part *p) { +__attribute__((always_inline)) INLINE static void hydro_gradients_init( + struct part *p) { p->primitives.gradients.rho[0] = 0.0f; p->primitives.gradients.rho[1] = 0.0f; @@ -46,11 +48,25 @@ hydro_gradients_init_density_loop(struct part *p) { } /** - * @brief Gradient calculations done during the density loop + * @brief Gradient calculations done during the neighbour loop + * + * @param r2 Squared distance between the two particles. + * @param dx Distance vector (pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. */ -__attribute__((always_inline)) INLINE static void hydro_gradients_density_loop( - struct part *pi, struct part *pj, float wi_dx, float wj_dx, float *dx, - float r, int mode) { +__attribute__((always_inline)) INLINE static void hydro_gradients_collect( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wi_dx, xi, hi_inv; + float wj, wj_dx, xj, hj_inv; + float r = sqrtf(r2); + + hi_inv = 1.0f / hi; + xi = r * hi_inv; + kernel_deval(xi, &wi, &wi_dx); /* very basic gradient estimate */ pi->primitives.gradients.rho[0] -= @@ -90,54 +106,123 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_density_loop( hydro_slope_limit_cell_collect(pi, pj, r); - if (mode == 1) { - /* signs are the same as before, since we swap i and j twice */ - pj->primitives.gradients.rho[0] -= - wj_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) / r; - pj->primitives.gradients.rho[1] -= - wj_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) / r; - pj->primitives.gradients.rho[2] -= - wj_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) / r; - - pj->primitives.gradients.v[0][0] -= - wj_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) / r; - pj->primitives.gradients.v[0][1] -= - wj_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) / r; - pj->primitives.gradients.v[0][2] -= - wj_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) / r; - - pj->primitives.gradients.v[1][0] -= - wj_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) / r; - pj->primitives.gradients.v[1][1] -= - wj_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) / r; - pj->primitives.gradients.v[1][2] -= - wj_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) / r; - pj->primitives.gradients.v[2][0] -= - wj_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) / r; - pj->primitives.gradients.v[2][1] -= - wj_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) / r; - pj->primitives.gradients.v[2][2] -= - wj_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) / r; - - pj->primitives.gradients.P[0] -= - wj_dx * dx[0] * (pi->primitives.P - pj->primitives.P) / r; - pj->primitives.gradients.P[1] -= - wj_dx * dx[1] * (pi->primitives.P - pj->primitives.P) / r; - pj->primitives.gradients.P[2] -= - wj_dx * dx[2] * (pi->primitives.P - pj->primitives.P) / r; - - hydro_slope_limit_cell_collect(pj, pi, r); - } + hj_inv = 1.0f / hj; + xj = r * hj_inv; + kernel_deval(xj, &wj, &wj_dx); + + /* signs are the same as before, since we swap i and j twice */ + pj->primitives.gradients.rho[0] -= + wj_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) / r; + pj->primitives.gradients.rho[1] -= + wj_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) / r; + pj->primitives.gradients.rho[2] -= + wj_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) / r; + + pj->primitives.gradients.v[0][0] -= + wj_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) / r; + pj->primitives.gradients.v[0][1] -= + wj_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) / r; + pj->primitives.gradients.v[0][2] -= + wj_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) / r; + + pj->primitives.gradients.v[1][0] -= + wj_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) / r; + pj->primitives.gradients.v[1][1] -= + wj_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) / r; + pj->primitives.gradients.v[1][2] -= + wj_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) / r; + pj->primitives.gradients.v[2][0] -= + wj_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) / r; + pj->primitives.gradients.v[2][1] -= + wj_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) / r; + pj->primitives.gradients.v[2][2] -= + wj_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) / r; + + pj->primitives.gradients.P[0] -= + wj_dx * dx[0] * (pi->primitives.P - pj->primitives.P) / r; + pj->primitives.gradients.P[1] -= + wj_dx * dx[1] * (pi->primitives.P - pj->primitives.P) / r; + pj->primitives.gradients.P[2] -= + wj_dx * dx[2] * (pi->primitives.P - pj->primitives.P) / r; + + hydro_slope_limit_cell_collect(pj, pi, r); } /** - * @brief Calculations done before the force loop + * @brief Gradient calculations done during the neighbour loop: non-symmetric + * version + * + * @param r2 Squared distance between the two particles. + * @param dx Distance vector (pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. */ __attribute__((always_inline)) INLINE static void -hydro_gradients_prepare_force_loop(struct part *p, float ih, float volume) { +hydro_gradients_nonsym_collect(float r2, float *dx, float hi, float hj, + struct part *pi, struct part *pj) { + + float wi, wi_dx, xi, hi_inv; + float r = sqrtf(r2); + + hi_inv = 1.0f / hi; + xi = r * hi_inv; + kernel_deval(xi, &wi, &wi_dx); + + /* very basic gradient estimate */ + pi->primitives.gradients.rho[0] -= + wi_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) / r; + pi->primitives.gradients.rho[1] -= + wi_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) / r; + pi->primitives.gradients.rho[2] -= + wi_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) / r; + + pi->primitives.gradients.v[0][0] -= + wi_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) / r; + pi->primitives.gradients.v[0][1] -= + wi_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) / r; + pi->primitives.gradients.v[0][2] -= + wi_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) / r; + pi->primitives.gradients.v[1][0] -= + wi_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) / r; + pi->primitives.gradients.v[1][1] -= + wi_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) / r; + pi->primitives.gradients.v[1][2] -= + wi_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) / r; + + pi->primitives.gradients.v[2][0] -= + wi_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) / r; + pi->primitives.gradients.v[2][1] -= + wi_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) / r; + pi->primitives.gradients.v[2][2] -= + wi_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) / r; + + pi->primitives.gradients.P[0] -= + wi_dx * dx[0] * (pi->primitives.P - pj->primitives.P) / r; + pi->primitives.gradients.P[1] -= + wi_dx * dx[1] * (pi->primitives.P - pj->primitives.P) / r; + pi->primitives.gradients.P[2] -= + wi_dx * dx[2] * (pi->primitives.P - pj->primitives.P) / r; + + hydro_slope_limit_cell_collect(pi, pj, r); +} + +/** + * @brief Finalize the gradient variables after all data have been collected + * + * @param p Particle. + */ +__attribute__((always_inline)) INLINE static void hydro_gradients_finalize( + struct part *p) { + + const float h = p->h; + const float ih = 1.0f / h; const float ihdimp1 = pow_dimension_plus_one(ih); + float volume = p->geometry.volume; + /* finalize gradients by multiplying with volume */ p->primitives.gradients.rho[0] *= ihdimp1 * volume; p->primitives.gradients.rho[1] *= ihdimp1 * volume; @@ -161,10 +246,3 @@ hydro_gradients_prepare_force_loop(struct part *p, float ih, float volume) { hydro_slope_limit_cell(p); } - -/** - * @brief Gradient calculations done during the gradient loop - */ -__attribute__((always_inline)) INLINE static void hydro_gradients_gradient_loop( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj, - int mode) {} diff --git a/src/hydro/Gizmo/hydro_iact.h b/src/hydro/Gizmo/hydro_iact.h index f7f48997194feb27ff2d19a20b5ca01b4d04fc88..1d5fdf15322767432c87db8d35f63b99695961bb 100644 --- a/src/hydro/Gizmo/hydro_iact.h +++ b/src/hydro/Gizmo/hydro_iact.h @@ -23,11 +23,23 @@ #include "hydro_gradients.h" #include "riemann.h" -//#define USE_GRADIENTS -//#define PER_FACE_LIMITER -/* #define PRINT_ID 0 */ - -/* this corresponds to task_subtype_hydro_loop1 */ +/** + * @brief Calculate the volume interaction between particle i and particle j + * + * The volume is in essence the same as the weighted number of neighbours in a + * classical SPH density calculation. + * + * We also calculate the components of the matrix E, which is used for second + * order accurate gradient calculations and for the calculation of the interface + * surface areas. + * + * @param r2 Squared distance between particle i and particle j. + * @param dx Distance vector between the particles (dx = pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. + */ __attribute__((always_inline)) INLINE static void runner_iact_density( float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { @@ -62,11 +74,26 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( pj->geometry.volume += wj; for (k = 0; k < 3; k++) for (l = 0; l < 3; l++) pj->geometry.matrix_E[k][l] += dx[k] * dx[l] * wj; - - hydro_gradients_density_loop(pi, pj, wi_dx, wj_dx, dx, r, 1); } -/* this corresponds to task_subtype_hydro_loop1 */ +/** + * @brief Calculate the volume interaction between particle i and particle j: + * non-symmetric version + * + * The volume is in essence the same as the weighted number of neighbours in a + * classical SPH density calculation. + * + * We also calculate the components of the matrix E, which is used for second + * order accurate gradient calculations and for the calculation of the interface + * surface areas. + * + * @param r2 Squared distance between particle i and particle j. + * @param dx Distance vector between the particles (dx = pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. + */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { @@ -90,22 +117,72 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( pi->geometry.volume += wi; for (k = 0; k < 3; k++) for (l = 0; l < 3; l++) pi->geometry.matrix_E[k][l] += dx[k] * dx[l] * wi; - - hydro_gradients_density_loop(pi, pj, wi_dx, 0.0f, dx, r, 0); } +/** + * @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 Squared distance between particle i and particle j. + * @param dx Distance vector between the particles (dx = pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. + */ __attribute__((always_inline)) INLINE static void runner_iact_gradient( float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { - hydro_gradients_gradient_loop(r2, dx, hi, hj, pi, pj, 1); + hydro_gradients_collect(r2, dx, hi, hj, pi, pj); } +/** + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version + * + * This method wraps around hydro_gradients_nonsym_collect, which can be an + * empty method, in which case no gradients are used. + * + * @param r2 Squared distance between particle i and particle j. + * @param dx Distance vector between the particles (dx = pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. + */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { - hydro_gradients_gradient_loop(r2, dx, hi, hj, pi, pj, 0); + hydro_gradients_nonsym_collect(r2, dx, hi, hj, pi, pj); } +/** + * @brief Common part of the flux calculation between particle i and j + * + * Since the only difference between the symmetric and non-symmetric version + * of the flux calculation is in the update of the conserved variables at the + * very end (which is not done for particle j if mode is 0 and particle j is + * active), both runner_iact_force and runner_iact_nonsym_force call this + * method, with an appropriate mode. + * + * This method calculates the surface area of the interface between particle i + * and particle j, as well as the interface position and velocity. These are + * then used to reconstruct and predict the primitive variables, which are then + * fed to a Riemann solver that calculates a flux. This flux is used to update + * the conserved variables of particle i or both particles. + * + * This method also calculates the maximal velocity used to calculate the time + * step. + * + * @param r2 Squared distance between particle i and particle j. + * @param dx Distance vector between the particles (dx = pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. + */ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common( float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj, int mode) { @@ -327,21 +404,45 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common( } } -/* this corresponds to task_subtype_fluxes */ +/** + * @brief Flux calculation between particle i and particle j + * + * This method calls runner_iact_fluxes_common with mode 1. + * + * @param r2 Squared distance between particle i and particle j. + * @param dx Distance vector between the particles (dx = pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. + */ __attribute__((always_inline)) INLINE static void runner_iact_force( float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 1); } -/* this corresponds to task_subtype_fluxes */ +/** + * @brief Flux calculation between particle i and particle j: non-symmetric + * version + * + * This method calls runner_iact_fluxes_common with mode 0. + * + * @param r2 Squared distance between particle i and particle j. + * @param dx Distance vector between the particles (dx = pi->x - pj->x). + * @param hi Smoothing length of particle i. + * @param hj Smoothing length of particle j. + * @param pi Particle i. + * @param pj Particle j. + */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0); } -/* PLACEHOLDERS */ +//// EMPTY VECTORIZED VERSIONS (gradients methods are missing...) + __attribute__((always_inline)) INLINE static void runner_iact_vec_density( float *R2, float *Dx, float *Hi, float *Hj, struct part **pi, struct part **pj) {} diff --git a/src/hydro/Gizmo/hydro_io.h b/src/hydro/Gizmo/hydro_io.h index c94451aad943e7603f3393a400fe5864e624d16b..6c4f64b2576c76e81fc7d432a4d4a6f6ecb5189c 100644 --- a/src/hydro/Gizmo/hydro_io.h +++ b/src/hydro/Gizmo/hydro_io.h @@ -18,6 +18,8 @@ ******************************************************************************/ #include "adiabatic_index.h" +#include "hydro_gradients.h" +#include "hydro_slope_limiters.h" #include "io_properties.h" /** @@ -106,7 +108,17 @@ void hydro_write_particles(struct part* parts, struct io_props* list, * @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) {} +void writeSPHflavour(hid_t h_grpsph) { + /* Gradient information */ + writeAttribute_s(h_grpsph, "Gradient reconstruction model", + HYDRO_GRADIENT_IMPLEMENTATION); + + /* Slope limiter information */ + writeAttribute_s(h_grpsph, "Cell wide slope limiter model", + HYDRO_SLOPE_LIMITER_CELL_IMPLEMENTATION); + writeAttribute_s(h_grpsph, "Piecewise slope limiter model", + HYDRO_SLOPE_LIMITER_FACE_IMPLEMENTATION); +} /** * @brief Are we writing entropy in the internal energy field ? diff --git a/src/hydro/Gizmo/hydro_slope_limiters.h b/src/hydro/Gizmo/hydro_slope_limiters.h index 357ba2a84eb0a0c340906ddfa9d20e6a4153a405..e1f841411a65fc5fce78ecab07e272c9c6df475b 100644 --- a/src/hydro/Gizmo/hydro_slope_limiters.h +++ b/src/hydro/Gizmo/hydro_slope_limiters.h @@ -23,10 +23,32 @@ #define PER_FACE_LIMITER #define CELL_WIDE_LIMITER +#include "dimension.h" +#include "kernel_hydro.h" + #ifdef PER_FACE_LIMITER + +#define HYDRO_SLOPE_LIMITER_FACE_IMPLEMENTATION \ + "GIZMO piecewise slope limiter (Hopkins 2015)" #include "hydro_slope_limiters_face.h" + #else +#define HYDRO_SLOPE_LIMITER_FACE_IMPLEMENTATION "No piecewise slope limiter" + +/** + * @brief Slope limit the slopes at the interface between two particles + * + * @param Wi Hydrodynamic variables of particle i. + * @param Wj Hydrodynamic variables of particle j. + * @param dWi Difference between the hydrodynamic variables of particle i at the + * position of particle i and at the interface position. + * @param dWj Difference between the hydrodynamic variables of particle j at the + * position of particle j and at the interface position. + * @param xij_i Relative position vector of the interface w.r.t. particle i. + * @param xij_j Relative position vector of the interface w.r.t. partilce j. + * @param r Distance between particle i and particle j. + */ __attribute__((always_inline)) INLINE static void hydro_slope_limit_face( float *Wi, float *Wj, float *dWi, float *dWj, float *xij_i, float *xij_j, float r) {} @@ -34,15 +56,39 @@ __attribute__((always_inline)) INLINE static void hydro_slope_limit_face( #endif #ifdef CELL_WIDE_LIMITER + +#define HYDRO_SLOPE_LIMITER_CELL_IMPLEMENTATION \ + "Cell wide slope limiter (Springel 2010)" #include "hydro_slope_limiters_cell.h" + #else +#define HYDRO_SLOPE_LIMITER_CELL_IMPLEMENTATION "No cell wide slope limiter" + +/** + * @brief Initialize variables for the cell wide slope limiter + * + * @param p Particle. + */ __attribute__((always_inline)) INLINE static void hydro_slope_limit_cell_init( struct part *p) {} +/** + * @brief Collect information for the cell wide slope limiter during the + * neighbour loop + * + * @param pi Particle i. + * @param pj Particle j. + * @param r Distance between particle i and particle j. + */ __attribute__((always_inline)) INLINE static void hydro_slope_limit_cell_collect(struct part *pi, struct part *pj, float r) {} +/** + * @brief Slope limit cell gradients + * + * @param p Particle. + */ __attribute__((always_inline)) INLINE static void hydro_slope_limit_cell( struct part *p) {} diff --git a/src/hydro/Gizmo/hydro_slope_limiters_cell.h b/src/hydro/Gizmo/hydro_slope_limiters_cell.h index 32df7c53625eea90f1c7f97fcf36c6c2c3f9be4e..aa99b43721f669f47a7888a5da0b1933ca1ebd62 100644 --- a/src/hydro/Gizmo/hydro_slope_limiters_cell.h +++ b/src/hydro/Gizmo/hydro_slope_limiters_cell.h @@ -17,6 +17,13 @@ * ******************************************************************************/ +#include <float.h> + +/** + * @brief Initialize variables for the cell wide slope limiter + * + * @param p Particle. + */ __attribute__((always_inline)) INLINE static void hydro_slope_limit_cell_init( struct part* p) { @@ -34,6 +41,14 @@ __attribute__((always_inline)) INLINE static void hydro_slope_limit_cell_init( p->primitives.limiter.maxr = -FLT_MAX; } +/** + * @brief Collect information for the cell wide slope limiter during the + * neighbour loop + * + * @param pi Particle i. + * @param pj Particle j. + * @param r Distance between particle i and particle j. + */ __attribute__((always_inline)) INLINE static void hydro_slope_limit_cell_collect(struct part* pi, struct part* pj, float r) { @@ -65,6 +80,11 @@ hydro_slope_limit_cell_collect(struct part* pi, struct part* pj, float r) { pi->primitives.limiter.maxr = fmax(r, pi->primitives.limiter.maxr); } +/** + * @brief Slope limit cell gradients + * + * @param p Particle. + */ __attribute__((always_inline)) INLINE static void hydro_slope_limit_cell( struct part* p) { diff --git a/src/hydro/Gizmo/hydro_slope_limiters_face.h b/src/hydro/Gizmo/hydro_slope_limiters_face.h index 7ef9319877fbb60309afdf7b92acf296f9e117b4..6745638369fc71dcffc1c480c8d5872f70cfaf5f 100644 --- a/src/hydro/Gizmo/hydro_slope_limiters_face.h +++ b/src/hydro/Gizmo/hydro_slope_limiters_face.h @@ -17,72 +17,105 @@ * ******************************************************************************/ -__attribute__((always_inline)) INLINE static void hydro_slope_limit_face( - float *Wi, float *Wj, float *dWi, float *dWj, float *xij_i, float *xij_j, - float r) { +/** + * @brief Slope limit a single quantity at the interface + * + * @param phi_i Value of the quantity at the particle position. + * @param phi_j Value of the quantity at the neighbouring particle position. + * @param phi_mid0 Extrapolated value of the quantity at the interface position. + * @param xij_norm Distance between the particle position and the interface + * position. + * @param r Distance between the particle and its neighbour. + * @return The slope limited difference between the quantity at the particle + * position and the quantity at the interface position. + */ +__attribute__((always_inline)) INLINE static float +hydro_slope_limit_face_quantity(float phi_i, float phi_j, float phi_mid0, + float xij_norm, float r) { - float xij_i_norm; - float phi_i, phi_j; - float delta1, delta2; - float phiminus, phiplus; - float phimin, phimax; - float phibar; - /* free parameters, values from Hopkins */ - float psi1 = 0.5, psi2 = 0.25; - float phi_mid0, phi_mid; - int k; + float delta1, delta2, phimin, phimax, phibar, phiplus, phiminus, phi_mid; + const float psi1 = 0.5f; + const float psi2 = 0.25f; - for (k = 0; k < 10; k++) { - if (k < 5) { - phi_i = Wi[k]; - phi_j = Wj[k]; - phi_mid0 = Wi[k] + dWi[k]; - xij_i_norm = sqrtf(xij_i[0] * xij_i[0] + xij_i[1] * xij_i[1] + - xij_i[2] * xij_i[2]); - } else { - phi_i = Wj[k - 5]; - phi_j = Wi[k - 5]; - phi_mid0 = Wj[k - 5] + dWj[k - 5]; - xij_i_norm = sqrtf(xij_j[0] * xij_j[0] + xij_j[1] * xij_j[1] + - xij_j[2] * xij_j[2]); - } + delta1 = psi1 * fabs(phi_i - phi_j); + delta2 = psi2 * fabs(phi_i - phi_j); - delta1 = psi1 * fabs(phi_i - phi_j); - delta2 = psi2 * fabs(phi_i - phi_j); + phimin = fmin(phi_i, phi_j); + phimax = fmax(phi_i, phi_j); - phimin = fmin(phi_i, phi_j); - phimax = fmax(phi_i, phi_j); + phibar = phi_i + xij_norm / r * (phi_j - phi_i); - phibar = phi_i + xij_i_norm / r * (phi_j - phi_i); + /* if sign(phimax+delta1) == sign(phimax) */ + if ((phimax + delta1) * phimax > 0.0f) { + phiplus = phimax + delta1; + } else { + phiplus = phimax / (1.0f + delta1 / fabs(phimax)); + } - /* if sign(phimax+delta1) == sign(phimax) */ - if ((phimax + delta1) * phimax > 0.0f) { - phiplus = phimax + delta1; - } else { - phiplus = phimax / (1.0f + delta1 / fabs(phimax)); - } + /* if sign(phimin-delta1) == sign(phimin) */ + if ((phimin - delta1) * phimin > 0.0f) { + phiminus = phimin - delta1; + } else { + phiminus = phimin / (1.0f + delta1 / fabs(phimin)); + } - /* if sign(phimin-delta1) == sign(phimin) */ - if ((phimin - delta1) * phimin > 0.0f) { - phiminus = phimin - delta1; + if (phi_i == phi_j) { + phi_mid = phi_i; + } else { + if (phi_i < phi_j) { + phi_mid = fmax(phiminus, fmin(phibar + delta2, phi_mid0)); } else { - phiminus = phimin / (1.0f + delta1 / fabs(phimin)); + phi_mid = fmin(phiplus, fmax(phibar - delta2, phi_mid0)); } + } - if (phi_i == phi_j) { - phi_mid = phi_i; - } else { - if (phi_i < phi_j) { - phi_mid = fmax(phiminus, fmin(phibar + delta2, phi_mid0)); - } else { - phi_mid = fmin(phiplus, fmax(phibar - delta2, phi_mid0)); - } - } + return phi_mid - phi_i; +} - if (k < 5) { - dWi[k] = phi_mid - phi_i; - } else { - dWj[k - 5] = phi_mid - phi_i; - } - } +/** + * @brief Slope limit the slopes at the interface between two particles + * + * @param Wi Hydrodynamic variables of particle i. + * @param Wj Hydrodynamic variables of particle j. + * @param dWi Difference between the hydrodynamic variables of particle i at the + * position of particle i and at the interface position. + * @param dWj Difference between the hydrodynamic variables of particle j at the + * position of particle j and at the interface position. + * @param xij_i Relative position vector of the interface w.r.t. particle i. + * @param xij_j Relative position vector of the interface w.r.t. partilce j. + * @param r Distance between particle i and particle j. + */ +__attribute__((always_inline)) INLINE static void hydro_slope_limit_face( + float *Wi, float *Wj, float *dWi, float *dWj, float *xij_i, float *xij_j, + float r) { + + float xij_i_norm, xij_j_norm; + + xij_i_norm = + sqrtf(xij_i[0] * xij_i[0] + xij_i[1] * xij_i[1] + xij_i[2] * xij_i[2]); + + xij_j_norm = + sqrtf(xij_j[0] * xij_j[0] + xij_j[1] * xij_j[1] + xij_j[2] * xij_j[2]); + + dWi[0] = hydro_slope_limit_face_quantity(Wi[0], Wj[0], Wi[0] + dWi[0], + xij_i_norm, r); + dWi[1] = hydro_slope_limit_face_quantity(Wi[1], Wj[1], Wi[1] + dWi[1], + xij_i_norm, r); + dWi[2] = hydro_slope_limit_face_quantity(Wi[2], Wj[2], Wi[2] + dWi[2], + xij_i_norm, r); + dWi[3] = hydro_slope_limit_face_quantity(Wi[3], Wj[3], Wi[3] + dWi[3], + xij_i_norm, r); + dWi[4] = hydro_slope_limit_face_quantity(Wi[4], Wj[4], Wi[4] + dWi[4], + xij_i_norm, r); + + dWj[0] = hydro_slope_limit_face_quantity(Wj[0], Wi[0], Wj[0] + dWj[0], + xij_j_norm, r); + dWj[1] = hydro_slope_limit_face_quantity(Wj[1], Wi[1], Wj[1] + dWj[1], + xij_j_norm, r); + dWj[2] = hydro_slope_limit_face_quantity(Wj[2], Wi[2], Wj[2] + dWj[2], + xij_j_norm, r); + dWj[3] = hydro_slope_limit_face_quantity(Wj[3], Wi[3], Wj[3] + dWj[3], + xij_j_norm, r); + dWj[4] = hydro_slope_limit_face_quantity(Wj[4], Wi[4], Wj[4] + dWj[4], + xij_j_norm, r); } diff --git a/src/runner.c b/src/runner.c index f4ac9f825df807416c56534f9167a777f796b853..d8c42920675aa662106a23d754dd11e790844914 100644 --- a/src/runner.c +++ b/src/runner.c @@ -428,7 +428,39 @@ void runner_do_init(struct runner *r, struct cell *c, int timer) { if (timer) TIMER_TOC(timer_init); } -void runner_do_extra_ghost(struct runner *r, struct cell *c) {} +/** + * @brief Intermediate task after the gradient loop that does final operations + * on the gradient quantities and optionally slope limits the gradients + * + * @param r The runner thread. + * @param c The cell. + */ +void runner_do_extra_ghost(struct runner *r, struct cell *c) { + struct part *restrict parts = c->parts; + const int count = c->count; + const int ti_current = r->e->ti_current; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_extra_ghost(r, c->progeny[k]); + return; + } else { + + /* Loop over the parts in this cell. */ + for (int i = 0; i < count; i++) { + + /* Get a direct pointer on the part. */ + struct part *restrict p = &parts[i]; + + if (p->ti_end <= ti_current) { + + /* Get ready for a force calculation */ + hydro_end_gradient(p); + } + } + } +} /** * @brief Intermediate task after the density to check that the smoothing