/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
******************************************************************************/
#ifndef SWIFT_MULTIPOLE_ACCEPT_H
#define SWIFT_MULTIPOLE_ACCEPT_H
/* Config parameters. */
#include
/* Local includes */
#include "binomial.h"
#include "gravity_properties.h"
#include "integer_power.h"
#include "kernel_long_gravity.h"
#include "minmax.h"
#include "multipole_struct.h"
/**
* @brief Compute the inverse of the force estimator entering the MAC
*
* Note that in the unsofted case, the first condition is naturally
* never reached (as H == 0). In the non-periodic (non-truncated) case
* the second condition is never reached (as r_s == inf, r_s_inv == 0).
*
* @param H The spline softening length.
* @param r_s_inv The inverse of the scale of the gravity mesh.
* @param r2 The square of the distance between the multipoles.
*/
__attribute__((const)) INLINE static float gravity_f_MAC_inverse(
const float H, const float r_s_inv, const float r2) {
if (r2 < (25.f / 81.f) * H * H) {
/* Below softening radius */
return (25.f / 81.f) * H * H;
} else if (r_s_inv * r_s_inv * r2 > (25.f / 9.f)) {
/* Above truncation radius */
return (9.f / 25.f) * r_s_inv * r_s_inv * r2 * r2;
} else {
/* Normal Newtonian case */
return r2;
}
}
/**
* @brief Checks whether The multipole in B can be used to update the field
* tensor in A.
*
* We use the MAC of Dehnen 2014 eq. 16.
*
* Note: this is *not* symmetric in A<->B unless the purely geometric criterion
* is used.
*
* @param props The properties of the gravity scheme.
* @param A The gravity tensors that we want to update (sink).
* @param B The gravity tensors that act as a source.
* @param r2 The square of the distance between the centres of mass of A and B.
* @param use_rebuild_sizes Are we considering the sizes at the last tree-build
* (1) or current sizes (0)?
* @param periodic Are we using periodic BCs?
*/
__attribute__((nonnull, pure)) INLINE static int gravity_M2L_accept(
const struct gravity_props *props, const struct gravity_tensors *restrict A,
const struct gravity_tensors *restrict B, const float r2,
const int use_rebuild_sizes, const int periodic) {
/* Order of the expansion */
const int p = 2;
/* Sizes of the multipoles */
const float rho_A = use_rebuild_sizes ? A->r_max_rebuild : A->r_max;
const float rho_B = use_rebuild_sizes ? B->r_max_rebuild : B->r_max;
/* Max size of both multipoles */
const float rho_max = max(rho_A, rho_B);
/* Get the softening */
const float max_softening =
max(A->m_pole.max_softening, B->m_pole.max_softening);
/* Compute the error estimator (without the 1/M_B term that cancels out) */
float E_BA_term = 0.f;
for (int n = 0; n <= p; ++n) {
E_BA_term +=
binomial(p, n) * B->m_pole.power[n] * integer_powf(rho_A, p - n);
}
E_BA_term *= 8.f;
if (rho_A + rho_B > 0.f) {
E_BA_term *= rho_max;
;
E_BA_term /= (rho_A + rho_B);
}
/* Compute r^p = (r^2)^(p/2) */
const float r_to_p = integer_powf(r2, (p / 2));
float f_MAC_inv;
if (periodic && props->consider_truncation_in_MAC) {
f_MAC_inv = gravity_f_MAC_inverse(max_softening, props->r_s_inv, r2);
} else {
f_MAC_inv = r2;
}
/* Get the mimimal acceleration in A */
const float min_a_grav = A->m_pole.min_old_a_grav_norm;
/* Maximal mass */
const float M_max = max(A->m_pole.M_000, B->m_pole.M_000);
/* Get the relative tolerance */
const float eps = props->adaptive_tolerance;
/* Get the basic geometric critical angle */
const float theta_crit = props->theta_crit;
const float theta_crit2 = theta_crit * theta_crit;
/* Get the sum of the multipole sizes */
const float rho_sum = rho_A + rho_B;
if (props->use_advanced_MAC && props->use_gadget_tolerance) {
/* Gadget 4 paper -- eq. 36 */
const int power = SELF_GRAVITY_MULTIPOLE_ORDER - 1;
const float ratio = integer_powf(rho_max / sqrtf(r2), power);
const int cond_1 = M_max * ratio < eps * min_a_grav * f_MAC_inv;
const int cond_2 =
props->use_tree_below_softening || max_softening * max_softening < r2;
return cond_1 && cond_2;
} else if (props->use_advanced_MAC && !props->use_gadget_tolerance) {
#ifdef SWIFT_DEBUG_CHECKS
if (min_a_grav == 0.) error("Acceleration is 0");
#endif
/* Test the different conditions */
/* Condition 1: We are in the converging part of the Taylor expansion */
const int cond_1 = rho_sum * rho_sum < r2;
/* Condition 2: We are not below softening */
const int cond_2 =
props->use_tree_below_softening || max_softening * max_softening < r2;
/* Condition 3: The contribution is accurate enough
* (E_BA * (1 / r^(p)) * ((1 / r^2) * W) < eps * a_min) */
const int cond_3 = E_BA_term < eps * min_a_grav * r_to_p * f_MAC_inv;
return cond_1 && cond_2 && cond_3;
} else {
/* Condition 1: We are obeying the purely geometric criterion */
const int cond_1 = rho_sum * rho_sum < theta_crit2 * r2;
/* Condition 2: We are not below softening */
const int cond_2 =
props->use_tree_below_softening || max_softening * max_softening < r2;
return cond_1 && cond_2;
}
}
/**
* @brief Checks whether The multipole in B can be used to update the field
* tensor in A and whether the multipole in A can be used to update the field
* tensor in B.
*
* We use the MAC of Dehnen 2014 eq. 16.
*
* @param props The properties of the gravity scheme.
* @param A The first set of multipole and gravity tensors.
* @param B The second set of multipole and gravity tensors.
* @param r2 The square of the distance between the centres of mass of A and B.
* @param use_rebuild_sizes Are we considering the sizes at the last tree-build
* (1) or current sizes (0)?
* @param periodic Are we using periodic BCs?
*/
__attribute__((nonnull, pure)) INLINE static int gravity_M2L_accept_symmetric(
const struct gravity_props *props, const struct gravity_tensors *restrict A,
const struct gravity_tensors *restrict B, const float r2,
const int use_rebuild_sizes, const int periodic) {
return gravity_M2L_accept(props, A, B, r2, use_rebuild_sizes, periodic) &&
gravity_M2L_accept(props, B, A, r2, use_rebuild_sizes, periodic);
}
/**
* Compute the distance above which an M2L kernel is allowed to be used.
*
* This uses conservative assumptions to guarantee that all the possible cell
* pair interactions that need a direct interaction are below this distance.
*
* @param props The properties of the gravity scheme.
* @param size The size of the multipoles (here the cell size).
* @param max_softening The maximal softening accross all particles.
* @param min_a_grav The minimal acceleration accross all particles.
* @param max_mpole_power The maximum multipole power accross all the
* multipoles.
* @param periodic Are we using periodic BCs?
*/
__attribute__((nonnull, pure)) INLINE static float
gravity_M2L_min_accept_distance(
const struct gravity_props *props, const float size,
const float max_softening, const float min_a_grav,
const float max_mpole_power[SELF_GRAVITY_MULTIPOLE_ORDER + 1],
const int periodic) {
/* Order of the expansion */
const int p = SELF_GRAVITY_MULTIPOLE_ORDER;
float E_BA_term = 0.f;
for (int n = 0; n <= p; ++n) {
E_BA_term +=
binomial(p, n) * max_mpole_power[n] * integer_powf(size, p - n);
}
E_BA_term *= 4.f;
/* Get the basic geometric critical angle */
const float theta_crit = props->theta_crit;
const float theta_crit2 = theta_crit * theta_crit;
/* Get the sum of the multipole sizes */
const float size_sum = 2. * size;
/* Get the relative tolerance */
const float eps = props->adaptive_tolerance;
if (props->use_advanced_MAC) {
/* Distance obtained by solving for the geometric criterion with theta = 1
*/
const float dist_tree = size_sum;
const float dist_adapt =
powf(E_BA_term / (eps * min_a_grav), 1.f / (p + 2.f));
/* Distance obtained by demanding > softening */
const float dist_soft =
props->use_tree_below_softening ? 0.f : max_softening;
return max3(dist_tree, dist_adapt, dist_soft);
} else {
/* Distance obtained by solving for the geometric criterion */
const float dist_tree = sqrtf(size_sum * size_sum / theta_crit2);
/* Distance obtained by demanding > softening */
const float dist_soft =
props->use_tree_below_softening ? 0.f : max_softening;
return max(dist_tree, dist_soft);
}
}
/**
* @brief Checks whether The multipole in B can be used to update the particle
* pa
*
* We use the MAC of Dehnen 2014 eq. 16.
*
* @param props The properties of the gravity scheme.
* @param pa The particle we want to compute forces for (sink)
* @param B The gravity tensors that act as a source.
* @param r2 The square of the distance between pa and the centres of mass of B.
* @param periodic Are we using periodic BCs?
*/
__attribute__((nonnull, pure)) INLINE static int gravity_M2P_accept(
const struct gravity_props *props, const struct gpart *pa,
const struct gravity_tensors *B, const float r2, const int periodic) {
/* Order of the expansion */
const int p = 2;
/* Sizes of the multipoles */
const float rho_B = B->r_max;
/* Get the maximal softening */
const float max_softening =
max(B->m_pole.max_softening, gravity_get_softening(pa, props));
#ifdef SWIFT_DEBUG_CHECKS
if (rho_B == 0.) error("Size of multipole B is 0!");
#endif
/* Compute the error estimator (without the 1/M_B term that cancels out) */
const float E_BA_term = 8.f * B->m_pole.power[p];
/* Compute r^p = (r^2)^(p/2) */
const float r_to_p = integer_powf(r2, (p / 2));
float f_MAC_inv;
if (periodic && props->consider_truncation_in_MAC) {
f_MAC_inv = gravity_f_MAC_inverse(max_softening, props->r_s_inv, r2);
} else {
f_MAC_inv = r2;
}
/* Get the estimate of the acceleration */
const float old_a_grav = pa->old_a_grav_norm;
/* Get the relative tolerance */
const float eps = props->adaptive_tolerance;
/* Get the basic geometric critical angle */
const float theta_crit = props->theta_crit;
const float theta_crit2 = theta_crit * theta_crit;
if (props->use_advanced_MAC && props->use_gadget_tolerance) {
/* Gadget 4 paper -- eq. 12 */
const int power = SELF_GRAVITY_MULTIPOLE_ORDER;
const float ratio = integer_powf(rho_B / sqrtf(r2), power);
const int cond_1 = B->m_pole.M_000 * ratio < eps * old_a_grav * f_MAC_inv;
const int cond_2 =
props->use_tree_below_softening || max_softening * max_softening < r2;
return cond_1 && cond_2;
} else if (props->use_advanced_MAC && !props->use_gadget_tolerance) {
#ifdef SWIFT_DEBUG_CHECKS
if (old_a_grav == 0.) error("Acceleration is 0");
#endif
/* Test the different conditions */
/* Condition 1: We are in the converging part of the Taylor expansion */
const int cond_1 = rho_B * rho_B < r2;
/* Condition 2: We are not below softening */
const int cond_2 =
props->use_tree_below_softening || max_softening * max_softening < r2;
/* Condition 3: The contribution is accurate enough
* (E_BA * (1 / r^(p)) * ((1 / r^2) * W) < eps * a_min) */
const int cond_3 = E_BA_term < eps * old_a_grav * r_to_p * f_MAC_inv;
return cond_1 && cond_2 && cond_3;
} else {
/* Condition 1: We are obeying the purely geometric criterion */
const int cond_1 = rho_B * rho_B < theta_crit2 * r2;
/* Condition 2: We are not below softening */
const int cond_2 =
props->use_tree_below_softening || max_softening * max_softening < r2;
return cond_1 && cond_2;
}
}
#endif /* SWIFT_MULTIPOLE_ACCEPT_H */