/*******************************************************************************
* 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.02;
/**
* @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
*/
void check_value(double a, double b, const char *s) {
if (fabs(a - b) / fabs(a + b) > 1e-6 && fabs(a - b) > 1.e-6)
error("Values are inconsistent: %12.15e %12.15e (%s)!", a, b, s);
}
/* Definitions of the potential and force that match
exactly the theory document */
double S(double x) { return good_approx_exp(x) / (1. + good_approx_exp(x)); }
double S_prime(double x) {
return good_approx_exp(x) /
((1. + good_approx_exp(x)) * (1. + good_approx_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);
/* Choke on FPEs */
#ifdef HAVE_FE_ENABLE_EXCEPT
feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
#endif
/* Initialise a few things to get us going */
struct engine e;
e.max_active_bin = num_time_bins;
e.time = 0.1f;
e.ti_current = 8;
e.time_base = 1e-10;
struct pm_mesh mesh;
mesh.periodic = 0;
mesh.dim[0] = 10.;
mesh.dim[1] = 10.;
mesh.dim[2] = 10.;
mesh.r_s_inv = 0.;
mesh.r_cut_min = 0.;
e.mesh = &mesh;
struct gravity_props props;
props.a_smooth = 1.25;
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;
const double rlr = FLT_MAX;
/* Init the cache for gravity interaction */
gravity_cache_init(&r.ci_gravity_cache, num_tests * 2);
/* Let's create one cell with a massive particle and a bunch of test particles
*/
struct cell c;
bzero(&c, sizeof(struct cell));
c.width[0] = 1.;
c.width[1] = 1.;
c.width[2] = 1.;
c.loc[0] = 0.;
c.loc[1] = 0.;
c.loc[2] = 0.;
c.grav.count = 1 + num_tests;
c.grav.ti_old_part = 8;
c.grav.ti_end_min = 8;
if (posix_memalign((void **)&c.grav.parts, gpart_align,
c.grav.count * sizeof(struct gpart)) != 0)
error("Impossible to allocate memory for the gparts.");
bzero(c.grav.parts, c.grav.count * sizeof(struct gpart));
/* Create the massive particle */
c.grav.parts[0].x[0] = 0.;
c.grav.parts[0].x[1] = 0.5;
c.grav.parts[0].x[2] = 0.5;
c.grav.parts[0].mass = 1.;
c.grav.parts[0].time_bin = 1;
c.grav.parts[0].type = swift_type_dark_matter;
c.grav.parts[0].id_or_neg_offset = 1;
#ifdef MULTI_SOFTENING_GRAVITY
c.grav.parts[0].epsilon = eps;
#endif
#ifdef SWIFT_DEBUG_CHECKS
c.grav.parts[0].ti_drift = 8;
c.grav.parts[0].initialised = 1;
#endif
/* Create the mass-less particles */
for (int n = 1; n < num_tests + 1; ++n) {
struct gpart *gp = &c.grav.parts[n];
gp->x[0] = n / ((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
}
/* Now compute the forces */
runner_doself_grav_pp(&r, &c);
/* Verify everything */
for (int n = 1; n < num_tests + 1; ++n) {
const struct gpart *gp = &c.grav.parts[n];
const double epsilon = gravity_get_softening(gp, &props);
#if defined(POTENTIAL_GRAVITY)
double pot_true = potential(c.grav.parts[0].mass, gp->x[0], epsilon, rlr);
check_value(gp->potential, pot_true, "potential");
#endif
double acc_true =
acceleration(c.grav.parts[0].mass, gp->x[0], epsilon, rlr);
check_value(gp->a_grav[0], acc_true, "acceleration");
// message("x=%e f=%e f_true=%e", gp->x[0], gp->a_grav[0], acc_true);
}
free(c.grav.parts);
/* Clean up the caches */
gravity_cache_clean(&r.ci_gravity_cache);
/* All done! */
return 0;
}