/*******************************************************************************
* This file is part of SWIFT.
* Copyright (C) 2015 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 .
*
******************************************************************************/
#include
/* Some standard headers. */
#include
#include
#include
#include
#include
#include
/* Local headers. */
#include "runner_doiact_grav.h"
#include "swift.h"
const int num_tests = 100;
const double eps = 0.1;
/**
* @brief Check that a and b are consistent (up to some relative error)
*
* @param a First value
* @param b Second value
* @param s String used to identify this check in messages
* @param rel_tol Maximal relative error
* @param limit Minimal value to consider in the tests
*/
void check_value_backend(double a, double b, const char *s, double rel_tol,
double limit) {
if (fabs(a - b) / fabs(a + b) > rel_tol && fabs(a - b) > limit)
error("Values are inconsistent: SWIFT:%12.15e true:%12.15e (%s)!", a, b, s);
}
void check_value(double a, double b, const char *s) {
check_value_backend(a, b, s, 2e-6, 1e-6);
}
/* Definitions of the potential and force that match
exactly the theory document */
double S(double x) { return exp(x) / (1. + exp(x)); }
double S_prime(double x) { return exp(x) / ((1. + exp(x)) * (1. + exp(x))); }
double potential(double mass, double r, double H, double rlr) {
const double u = r / H;
const double x = r / rlr;
double pot;
if (u > 1.)
pot = -mass / r;
else
pot = -mass *
(-3. * u * u * u * u * u * u * u + 15. * u * u * u * u * u * u -
28. * u * u * u * u * u + 21. * u * u * u * u - 7. * u * u + 3.) /
H;
return pot * (2. - 2. * S(2. * x));
}
double acceleration(double mass, double r, double H, double rlr) {
const double u = r / H;
const double x = r / rlr;
double acc;
if (u > 1.)
acc = -mass / (r * r * r);
else
acc = -mass *
(21. * u * u * u * u * u - 90. * u * u * u * u + 140. * u * u * u -
84. * u * u + 14.) /
(H * H * H);
return r * acc * (4. * x * S_prime(2 * x) - 2. * S(2. * x) + 2.);
}
int main(int argc, char *argv[]) {
/* Initialize CPU frequency, this also starts time. */
unsigned long long cpufreq = 0;
clocks_set_cpufreq(cpufreq);
/* Initialise a few things to get us going */
/* Non-truncated forces first */
double rlr = FLT_MAX;
struct engine e;
e.max_active_bin = num_time_bins;
e.time = 0.1f;
e.ti_current = 8;
e.time_base = 1e-10;
e.nodeID = 0;
struct space s;
s.periodic = 0;
e.s = &s;
struct pm_mesh mesh;
mesh.periodic = 0;
mesh.dim[0] = 10.;
mesh.dim[1] = 10.;
mesh.dim[2] = 10.;
mesh.r_s = rlr;
mesh.r_s_inv = 1. / rlr;
mesh.r_cut_min = 0.;
mesh.r_cut_max = FLT_MAX;
e.mesh = &mesh;
struct gravity_props props;
props.theta_crit = 0.;
props.epsilon_DM_cur = eps;
props.epsilon_baryon_cur = eps;
e.gravity_properties = &props;
struct runner r;
bzero(&r, sizeof(struct runner));
r.e = &e;
/* Init the cache for gravity interaction */
gravity_cache_init(&r.ci_gravity_cache, num_tests);
gravity_cache_init(&r.cj_gravity_cache, num_tests);
/* Let's create one cell with a massive particle and a bunch of test particles
*/
struct cell ci, cj;
bzero(&ci, sizeof(struct cell));
bzero(&cj, sizeof(struct cell));
ci.nodeID = 0;
ci.width[0] = 1.;
ci.width[1] = 1.;
ci.width[2] = 1.;
ci.loc[0] = 0.;
ci.loc[1] = 0.;
ci.loc[2] = 0.;
ci.grav.count = 1;
ci.grav.ti_old_part = 8;
ci.grav.ti_old_multipole = 8;
ci.grav.ti_end_min = 8;
cj.nodeID = 0;
cj.width[0] = 1.;
cj.width[1] = 1.;
cj.width[2] = 1.;
cj.loc[0] = 1.;
cj.loc[1] = 0.;
cj.loc[2] = 0.;
cj.grav.count = num_tests;
cj.grav.ti_old_part = 8;
cj.grav.ti_old_multipole = 8;
cj.grav.ti_end_min = 8;
/* Allocate multipoles */
ci.grav.multipole =
(struct gravity_tensors *)malloc(sizeof(struct gravity_tensors));
cj.grav.multipole =
(struct gravity_tensors *)malloc(sizeof(struct gravity_tensors));
bzero(ci.grav.multipole, sizeof(struct gravity_tensors));
bzero(cj.grav.multipole, sizeof(struct gravity_tensors));
/* Set the multipoles */
ci.grav.multipole->r_max = 0.1;
cj.grav.multipole->r_max = 0.1;
/* Allocate the particles */
if (posix_memalign((void **)&ci.grav.parts, gpart_align,
ci.grav.count * sizeof(struct gpart)) != 0)
error("Error allocating gparts for cell ci");
bzero(ci.grav.parts, ci.grav.count * sizeof(struct gpart));
if (posix_memalign((void **)&cj.grav.parts, gpart_align,
cj.grav.count * sizeof(struct gpart)) != 0)
error("Error allocating gparts for cell ci");
bzero(cj.grav.parts, cj.grav.count * sizeof(struct gpart));
/* Create the mass-less test particles */
for (int n = 0; n < num_tests; ++n) {
struct gpart *gp = &cj.grav.parts[n];
gp->x[0] = 1. + (n + 1) / ((double)num_tests);
gp->x[1] = 0.5;
gp->x[2] = 0.5;
gp->mass = 0.;
gp->time_bin = 1;
gp->type = swift_type_dark_matter;
gp->id_or_neg_offset = n + 1;
#ifdef MULTI_SOFTENING_GRAVITY
gp->epsilon = eps;
#endif
#ifdef SWIFT_DEBUG_CHECKS
gp->ti_drift = 8;
gp->initialised = 1;
#endif
}
/***********************************************/
/* Let's start by testing the P-P interactions */
/***********************************************/
/* Create the massive particle */
ci.grav.parts[0].x[0] = 0.;
ci.grav.parts[0].x[1] = 0.5;
ci.grav.parts[0].x[2] = 0.5;
ci.grav.parts[0].mass = 1.;
ci.grav.parts[0].time_bin = 1;
ci.grav.parts[0].type = swift_type_dark_matter;
ci.grav.parts[0].id_or_neg_offset = 1;
#ifdef MULTI_SOFTENING_GRAVITY
ci.grav.parts[0].epsilon = eps;
#endif
#ifdef SWIFT_DEBUG_CHECKS
ci.grav.parts[0].ti_drift = 8;
ci.grav.parts[0].initialised = 1;
#endif
/* Now compute the forces */
runner_dopair_grav_pp(&r, &ci, &cj, 1, 1);
/* Verify everything */
for (int n = 0; n < num_tests; ++n) {
const struct gpart *gp = &cj.grav.parts[n];
const struct gpart *gp2 = &ci.grav.parts[0];
const double epsilon = gravity_get_softening(gp, &props);
#if defined(POTENTIAL_GRAVITY)
double pot_true =
potential(ci.grav.parts[0].mass, gp->x[0] - gp2->x[0], epsilon, rlr);
check_value(gp->potential, pot_true, "potential");
#endif
double acc_true =
acceleration(ci.grav.parts[0].mass, gp->x[0] - gp2->x[0], epsilon, rlr);
/* message("x=%e f=%e f_true=%e pot=%e pot_true=%e", gp->x[0] - gp2->x[0],
gp->a_grav[0], acc_true, gp->potential, pot_true); */
check_value(gp->a_grav[0], acc_true, "acceleration");
}
message("\n\t\t P-P interactions all good\n");
/* Reset the accelerations */
for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.grav.parts[n]);
/**********************************/
/* Test the basic PM interactions */
/**********************************/
/* Set an opening angle that allows P-M interactions */
props.theta_crit = 1.;
ci.grav.parts[0].mass = 0.;
ci.grav.multipole->CoM[0] = 0.;
ci.grav.multipole->CoM[1] = 0.5;
ci.grav.multipole->CoM[2] = 0.5;
bzero(&ci.grav.multipole->m_pole, sizeof(struct multipole));
bzero(&cj.grav.multipole->m_pole, sizeof(struct multipole));
cj.grav.multipole->m_pole.M_000 = 1.;
cj.grav.multipole->m_pole.max_softening = eps;
/* Now compute the forces */
runner_dopair_grav_pp(&r, &ci, &cj, /*symmetric*/ 1, /*allow_mpoles=*/1);
/* Verify everything */
for (int n = 0; n < num_tests; ++n) {
const struct gpart *gp = &cj.grav.parts[n];
const struct gravity_tensors *mpole = ci.grav.multipole;
const double epsilon = gravity_get_softening(gp, &props);
#if defined(POTENTIAL_GRAVITY)
double pot_true =
potential(mpole->m_pole.M_000, gp->x[0] - mpole->CoM[0], epsilon, rlr);
check_value(gp->potential, pot_true, "potential");
#endif
double acc_true = acceleration(mpole->m_pole.M_000,
gp->x[0] - mpole->CoM[0], epsilon, rlr);
check_value(gp->a_grav[0], acc_true, "acceleration");
/* message("x=%e f=%e f_true=%e pot=%e pot_true=%e", gp->x[0] -
* mpole->CoM[0], gp->a_grav[0], acc_true, gp->potential, pot_true); */
}
message("\n\t\t basic P-M interactions all good\n");
#ifndef GADGET2_LONG_RANGE_CORRECTION
/* Reset the accelerations */
for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.grav.parts[n]);
/***************************************/
/* Test the truncated PM interactions */
/***************************************/
rlr = 2.;
mesh.r_s = rlr;
mesh.r_s_inv = 1. / rlr;
mesh.periodic = 1;
s.periodic = 1;
props.epsilon_cur = FLT_MIN; /* No softening */
/* Now compute the forces */
runner_dopair_grav_pp(&r, &ci, &cj, 1, 1);
/* Verify everything */
for (int n = 0; n < num_tests; ++n) {
const struct gpart *gp = &cj.grav.parts[n];
const struct gravity_tensors *mpole = ci.grav.multipole;
const double epsilon = gravity_get_softening(gp, &props);
#if defined(POTENTIAL_GRAVITY)
double pot_true =
potential(mpole->m_pole.M_000, gp->x[0] - mpole->CoM[0], epsilon, rlr);
check_value(gp->potential, pot_true, "potential");
#endif
double acc_true = acceleration(mpole->m_pole.M_000,
gp->x[0] - mpole->CoM[0], epsilon, rlr);
check_value(gp->a_grav[0], acc_true, "acceleration");
/* message("x=%e f=%e f_true=%e pot=%e pot_true=%e", gp->x[0] -
* mpole->CoM[0], */
/* gp->a_grav[0], acc_true, gp->potential, pot_true); */
}
message("\n\t\t truncated P-M interactions all good\n");
#endif
/************************************************/
/* Test the high-order periodic PM interactions */
/************************************************/
/* Reset the accelerations */
for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.grav.parts[n]);
#if SELF_GRAVITY_MULTIPOLE_ORDER >= 3
/* Let's make ci more interesting */
free(ci.grav.parts);
ci.grav.count = 8;
if (posix_memalign((void **)&ci.grav.parts, gpart_align,
ci.grav.count * sizeof(struct gpart)) != 0)
error("Error allocating gparts for cell ci");
bzero(ci.grav.parts, ci.grav.count * sizeof(struct gpart));
/* Place particles on a simple cube of side-length 0.2 */
for (int n = 0; n < 8; ++n) {
if (n & 1)
ci.grav.parts[n].x[0] = 0.0 - 0.1;
else
ci.grav.parts[n].x[0] = 0.0 + 0.1;
if (n & 2)
ci.grav.parts[n].x[1] = 0.5 - 0.1;
else
ci.grav.parts[n].x[1] = 0.5 + 0.1;
if (n & 2)
ci.grav.parts[n].x[2] = 0.5 - 0.1;
else
ci.grav.parts[n].x[2] = 0.5 + 0.1;
ci.grav.parts[n].mass = 1. / 8.;
ci.grav.parts[n].time_bin = 1;
ci.grav.parts[n].type = swift_type_dark_matter;
ci.grav.parts[n].id_or_neg_offset = 1;
#ifdef MULTI_SOFTENING_GRAVITY
ci.grav.parts[0].epsilon = eps;
#endif
#ifdef SWIFT_DEBUG_CHECKS
ci.grav.parts[n].ti_drift = 8;
ci.grav.parts[n].initialised = 1;
#endif
}
/* Now let's make a multipole out of it. */
gravity_reset(ci.grav.multipole);
gravity_P2M(ci.grav.multipole, ci.grav.parts, ci.grav.count, &props);
gravity_multipole_print(&ci.grav.multipole->m_pole);
/* Compute the forces */
runner_dopair_grav_pp(&r, &ci, &cj, 1, 1);
/* Verify everything */
for (int n = 0; n < num_tests; ++n) {
const struct gpart *gp = &cj.grav.parts[n];
#if defined(POTENTIAL_GRAVITY)
double pot_true = 0;
#endif
double acc_true[3] = {0., 0., 0.};
for (int i = 0; i < 8; ++i) {
const struct gpart *gp2 = &ci.grav.parts[i];
const double epsilon = gravity_get_softening(gp, &props);
const double dx[3] = {gp2->x[0] - gp->x[0], gp2->x[1] - gp->x[1],
gp2->x[2] - gp->x[2]};
const double d = sqrt(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]);
#if defined(POTENTIAL_GRAVITY)
pot_true += potential(gp2->mass, d, epsilon, rlr);
#endif
acc_true[0] -= acceleration(gp2->mass, d, epsilon, rlr) * dx[0] / d;
acc_true[1] -= acceleration(gp2->mass, d, epsilon, rlr) * dx[1] / d;
acc_true[2] -= acceleration(gp2->mass, d, epsilon, rlr) * dx[2] / d;
}
#if defined(POTENTIAL_GRAVITY)
check_value_backend(gp->potential, pot_true, "potential", 1e-2, 1e-6);
#endif
check_value_backend(gp->a_grav[0], acc_true[0], "acceleration", 1e-2, 1e-6);
/* const struct gravity_tensors *mpole = ci.grav.multipole; */
/* message("x=%e f=%e f_true=%e pot=%e pot_true=%e %e %e", */
/* gp->x[0] - mpole->CoM[0], gp->a_grav[0], acc_true[0],
* gp->potential, */
/* pot_true, acc_true[1], acc_true[2]); */
}
message("\n\t\t high-order P-M interactions all good\n");
#endif
free(ci.grav.multipole);
free(cj.grav.multipole);
free(ci.grav.parts);
free(cj.grav.parts);
/* Clean up the caches */
gravity_cache_clean(&r.ci_gravity_cache);
gravity_cache_clean(&r.cj_gravity_cache);
return 0;
}