MP-Gadget  5.0.1.dev1-76bc7d4726-dirty
test_gravity.c
Go to the documentation of this file.
1 /*Simple test for gravitational force accuracy.*/
2 
3 #include <stdarg.h>
4 #include <stddef.h>
5 #include <setjmp.h>
6 #include <cmocka.h>
7 #include <math.h>
8 #include <mpi.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <time.h>
12 #include <gsl/gsl_rng.h>
13 #include <omp.h>
14 
15 #include "stub.h"
16 
18 #include <libgadget/utils/system.h>
19 #include <libgadget/utils/endrun.h>
20 #include <libgadget/partmanager.h>
21 #include <libgadget/walltime.h>
22 #include <libgadget/domain.h>
23 #include <libgadget/forcetree.h>
24 #include <libgadget/gravity.h>
25 #include <libgadget/petapm.h>
26 #include <libgadget/timestep.h>
27 #include <libgadget/physconst.h>
28 
29 static struct ClockTable CT;
30 /* The true struct for the state variable*/
31 struct forcetree_testdata
32 {
33  gsl_rng * r;
34 };
35 static const double G = 43.0071;
36 
37 static void
38 grav_force(const int this, const int other, const double * offset, double * accns)
39 {
40 
41  double r2 = 0;
42  int d;
43  double dist[3];
44  for(d = 0; d < 3; d ++) {
45  /* the distance vector points to 'other' */
46  dist[d] = offset[d] + P[this].Pos[d] - P[other].Pos[d];
47  r2 += dist[d] * dist[d];
48  }
49 
50  const double r = sqrt(r2);
51 
52  const double h = FORCE_SOFTENING(1, 1);
53 
54  double fac = 1 / (r2 * r);
55  if(r < h) {
56  double h_inv = 1.0 / h;
57  double h3_inv = h_inv * h_inv * h_inv;
58  double u = r * h_inv;
59  if(u < 0.5)
60  fac = 1. * h3_inv * (10.666666666667 + u * u * (32.0 * u - 38.4));
61  else
62  fac =
63  1. * h3_inv * (21.333333333333 - 48.0 * u +
64  38.4 * u * u - 10.666666666667 * u * u * u - 0.066666666667 / (u * u * u));
65  }
66 
67  for(d = 0; d < 3; d ++) {
68  accns[3*this + d] += - dist[d] * fac * G * P[other].Mass;
69  accns[3*other + d] += dist[d] * fac * G * P[this].Mass;
70  }
71 }
72 
73 void check_accns(double * meanerr_tot, double * maxerr_tot, double *PairAccn, double meanacc)
74 {
75  double meanerr=0, maxerr=-1;
76  int64_t i;
77  /* This checks that the short-range force accuracy is being correctly estimated.*/
78  #pragma omp parallel for reduction(+: meanerr) reduction(max: maxerr)
79  for(i = 0; i < PartManager->NumPart; i++)
80  {
81  int k;
82  for(k=0; k<3; k++) {
83  double err = fabs((PairAccn[3*i+k] - (P[i].GravPM[k] + P[i].GravAccel[k]))/meanacc);
84  meanerr += err;
85  if(maxerr < err)
86  maxerr = err;
87  }
88  }
89  MPI_Allreduce(&meanerr, meanerr_tot, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
90  MPI_Allreduce(&maxerr, maxerr_tot, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
91  int64_t tot_npart;
92  MPI_Allreduce(&PartManager->NumPart, &tot_npart, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
93 
94  *meanerr_tot/= (tot_npart*3.);
95 }
96 
97 static void find_means(double * meangrav, double * suppmean, double * suppaccns)
98 {
99  int i;
100  double meanacc = 0, meanforce = 0;
101  #pragma omp parallel for reduction(+: meanacc) reduction(+: meanforce)
102  for(i = 0; i < PartManager->NumPart; i++)
103  {
104  int k;
105  for(k=0; k<3; k++) {
106  if(suppaccns)
107  meanacc += fabs(suppaccns[3*i+k]);
108  meanforce += fabs(P[i].GravPM[k] + P[i].GravAccel[k]);
109  }
110  }
111  int64_t tot_npart;
112  MPI_Allreduce(&PartManager->NumPart, &tot_npart, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
113  if(suppaccns) {
114  MPI_Allreduce(&meanacc, suppmean, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
115  *suppmean/= (tot_npart*3.);
116  }
117  MPI_Allreduce(&meanforce, meangrav, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
118  *meangrav/= (tot_npart*3.);
119 }
120 
121 
122 /* This checks the force on each particle using a direct summation:
123  * very slow, but accurate.
124  * Periodic boundary conditions are included by mirroring the box.*/
125 static void force_direct(double * accn)
126 {
127  memset(accn, 0, 3 * sizeof(double) * PartManager->NumPart);
128  int xx, yy, zz;
129  /* Checked that increasing this has no visible effect on the computed force accuracy*/
130  int repeat = 1;
131  /* (slowly) compute gravitational force, accounting for periodicity by just inventing extra boxes on either side.*/
132  for(xx=-repeat; xx <= repeat; xx++)
133  for(yy=-repeat; yy <= repeat; yy++)
134  for(zz=-repeat; zz <= repeat; zz++)
135  {
136  int i;
137  double offset[3] = {PartManager->BoxSize * xx, PartManager->BoxSize * yy, PartManager->BoxSize * zz};
138  for(i = 0; i < PartManager->NumPart; i++) {
139  int j;
140  for(j = i+1; j < PartManager->NumPart; j++)
141  grav_force(i, j, offset, accn);
142  }
143  }
144 }
145 
146 static int check_against_force_direct(double ErrTolForceAcc)
147 {
148  double * accn = (double *) mymalloc("accelerations", 3*sizeof(double) * PartManager->NumPart);
149  force_direct(accn);
150  double meanerr=0, maxerr=-1, meanacc=0, meanforce=0;
151  find_means(&meanacc, &meanforce, accn);
152  check_accns(&meanerr, &maxerr, accn, meanacc);
153  myfree(accn);
154  message(0, "Mean rel err is: %g max rel err is %g, meanacc %g mean grav force %g\n", meanerr, maxerr, meanacc, meanforce);
155  /*Make some statements about the force error*/
156  assert_true(maxerr < 3*ErrTolForceAcc);
157  assert_true(meanerr < 0.8*ErrTolForceAcc);
158 
159  return 0;
160 }
161 
162 static void do_force_test(int Nmesh, double Asmth, double ErrTolForceAcc, int direct)
163 {
164  /*Sort by peano key so this is more realistic*/
165  int i;
166  #pragma omp parallel for
167  for(i=0; i<PartManager->NumPart; i++) {
168  P[i].Type = 1;
169  P[i].Key = PEANO(P[i].Pos, PartManager->BoxSize);
170  P[i].Mass = 1;
171  P[i].ID = i;
172  P[i].TimeBin = 0;
173  P[i].IsGarbage = 0;
174  }
175 
176  ActiveParticles act = {0};
178 
179  DomainDecomp ddecomp = {0};
180  domain_decompose_full(&ddecomp);
181 
182  PetaPM pm = {0};
183  gravpm_init_periodic(&pm, PartManager->BoxSize, Asmth, Nmesh, G);
184  ForceTree Tree = {0};
185  force_tree_rebuild(&Tree, &ddecomp, 1, 1, NULL);
187  /* Setup cosmology*/
188  Cosmology CP ={0};
189  CP.MNu[0] = CP.MNu[1] = CP.MNu[2] = 0;
190  CP.OmegaCDM = 0.3;
191  CP.CMBTemperature = 2.72;
192  CP.HubbleParam = 0.7;
193  CP.Omega0 = 0.3;
194  CP.OmegaBaryon = 0.045;
195  CP.OmegaLambda = 0.7;
196  struct UnitSystem units = get_unitsystem(3.085678e21, 1.989e43, 1e5);
197  init_cosmology(&CP, 0.01, units);
198 
199  gravpm_force(&pm, &Tree, &CP, 0.1, CM_PER_MPC/1000., ".", 0.01, 2);
200  force_tree_rebuild(&Tree, &ddecomp, 1, 1, NULL);
201  const double rho0 = CP.Omega0 * 3 * CP.Hubble * CP.Hubble / (8 * M_PI * G);
202 
203  /* Barnes-Hut on first iteration*/
204  struct gravshort_tree_params treeacc = {0};
205  treeacc.BHOpeningAngle = 0.175;
206  treeacc.TreeUseBH = 1;
207  treeacc.Rcut = 7;
208  treeacc.ErrTolForceAcc = ErrTolForceAcc;
209  treeacc.AdaptiveSoftening = 0;
210  treeacc.FractionalGravitySoftening = 1./30.;
211 
212  set_gravshort_treepar(treeacc);
214 
215  /* Twice so the opening angle is consistent*/
216  grav_short_tree(&act, &pm, &Tree, rho0, 0, 2);
217  grav_short_tree(&act, &pm, &Tree, rho0, 0, 2);
218 
219  force_tree_free(&Tree);
220  petapm_destroy(&pm);
221  domain_free(&ddecomp);
222  if(direct)
224 }
225 
226 static void test_force_flat(void ** state) {
227  /*Set up the particle data*/
228  int numpart = PartManager->NumPart;
229  int ncbrt = cbrt(numpart);
230  P = mymalloc("part", numpart*sizeof(struct particle_data));
231  memset(P, 0, numpart*sizeof(struct particle_data));
232  /* Create a regular grid of particles, 8x8x8, all of type 1,
233  * in a box 8 kpc across.*/
234  int i;
235  #pragma omp parallel for
236  for(i=0; i<numpart; i++) {
237  P[i].Pos[0] = (PartManager->BoxSize/ncbrt) * (i/ncbrt/ncbrt);
238  P[i].Pos[1] = (PartManager->BoxSize/ncbrt) * ((i/ncbrt) % ncbrt);
239  P[i].Pos[2] = (PartManager->BoxSize/ncbrt) * (i % ncbrt);
240  }
241  PartManager->NumPart = numpart;
242  PartManager->MaxPart = numpart;
243  do_force_test(48, 1.5, 0.002, 0);
244  /* For a homogeneous mass distribution, the force should be zero*/
245  double meanerr=0, maxerr=-1;
246  #pragma omp parallel for reduction(+: meanerr) reduction(max: maxerr)
247  for(i = 0; i < PartManager->NumPart; i++)
248  {
249  int k;
250  for(k=0; k<3; k++) {
251  double err = fabs((P[i].GravPM[k] + P[i].GravAccel[k]));
252  meanerr += err;
253  if(maxerr < err)
254  maxerr = err;
255  }
256  }
257  MPI_Allreduce(MPI_IN_PLACE, &meanerr, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
258  MPI_Allreduce(MPI_IN_PLACE, &maxerr, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
259  int64_t tot_npart;
260  MPI_Allreduce(&PartManager->NumPart, &tot_npart, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
261  meanerr/= (tot_npart*3.);
262 
263  message(0, "Max force %g, mean grav force %g\n", maxerr, meanerr);
264  /*Make some statements about the force error*/
265  assert_true(maxerr < 0.015);
266  assert_true(meanerr < 0.005);
267  myfree(P);
268 }
269 
270 static void test_force_close(void ** state) {
271  /*Set up the particle data*/
272  int numpart = PartManager->NumPart;
273  int ncbrt = cbrt(numpart);
274  double close = 5000;
275  P = mymalloc("part", numpart*sizeof(struct particle_data));
276  memset(P, 0, numpart*sizeof(struct particle_data));
277  /* Create particles clustered in one place, all of type 1.*/
278  int i;
279  #pragma omp parallel for
280  for(i=0; i<numpart; i++) {
281  P[i].Pos[0] = 4. + (i/ncbrt/ncbrt)/close;
282  P[i].Pos[1] = 4. + ((i/ncbrt) % ncbrt) /close;
283  P[i].Pos[2] = 4. + (i % ncbrt)/close;
284  }
285  PartManager->NumPart = numpart;
286  PartManager->MaxPart = numpart;
287  do_force_test(48, 1.5, 0.002, 1);
288  myfree(P);
289 }
290 
291 void do_random_test(gsl_rng * r, const int numpart)
292 {
293  /* Create a regular grid of particles, 8x8x8, all of type 1,
294  * in a box 8 kpc across.*/
295  int i;
296  for(i=0; i<numpart/4; i++) {
297  int j;
298  for(j=0; j<3; j++)
299  P[i].Pos[j] = PartManager->BoxSize * gsl_rng_uniform(r);
300  }
301  for(i=numpart/4; i<3*numpart/4; i++) {
302  int j;
303  for(j=0; j<3; j++)
304  P[i].Pos[j] = PartManager->BoxSize/2 + PartManager->BoxSize/8 * exp(pow(gsl_rng_uniform(r)-0.5,2));
305  }
306  for(i=3*numpart/4; i<numpart; i++) {
307  int j;
308  for(j=0; j<3; j++)
309  P[i].Pos[j] = PartManager->BoxSize*0.1 + PartManager->BoxSize/32 * exp(pow(gsl_rng_uniform(r)-0.5,2));
310  }
311  PartManager->NumPart = numpart;
312  PartManager->MaxPart = numpart;
313  do_force_test(48, 1.5, 0.002, 1);
314 }
315 
316 static void test_force_random(void ** state) {
317  /*Set up the particle data*/
318  int numpart = PartManager->NumPart;
319  struct forcetree_testdata * data = * (struct forcetree_testdata **) state;
320  gsl_rng * r = data->r;
321  P = mymalloc("part", numpart*sizeof(struct particle_data));
322  memset(P, 0, numpart*sizeof(struct particle_data));
323  int i;
324  for(i=0; i<2; i++) {
325  do_random_test(r, numpart);
326  }
327  myfree(P);
328 }
329 
330 static int setup_tree(void **state) {
331  walltime_init(&CT);
332  /*Set up the important parts of the All structure.*/
333  /*Particles should not be outside this*/
334  PartManager->BoxSize = 8;
335  PartManager->NumPart = 16*16*16;
336 
337  struct DomainParams dp = {0};
339  dp.DomainUseGlobalSorting = 0;
340  dp.TopNodeAllocFactor = 1.;
341  dp.SetAsideFactor = 1;
342  set_domain_par(dp);
343  petapm_module_init(omp_get_max_threads());
345  /*Set up the top-level domain grid*/
346  struct forcetree_testdata *data = malloc(sizeof(struct forcetree_testdata));
347  data->r = gsl_rng_alloc(gsl_rng_mt19937);
348  gsl_rng_set(data->r, 0);
349  *state = (void *) data;
350  return 0;
351 }
352 
353 static int teardown_tree(void **state) {
354  struct forcetree_testdata * data = (struct forcetree_testdata * ) *state;
355  free(data->r);
356  free(data);
357  return 0;
358 }
359 
360 int main(void) {
361  const struct CMUnitTest tests[] = {
362  cmocka_unit_test(test_force_flat),
363  cmocka_unit_test(test_force_close),
364  cmocka_unit_test(test_force_random),
365  };
366  return cmocka_run_group_tests_mpi(tests, setup_tree, teardown_tree);
367 }
void init_cosmology(Cosmology *CP, const double TimeBegin, const struct UnitSystem units)
Definition: cosmology.c:15
void domain_free(DomainDecomp *ddecomp)
Definition: domain.c:320
void domain_decompose_full(DomainDecomp *ddecomp)
Definition: domain.c:155
void set_domain_par(DomainParams dp)
Definition: domain.c:78
void message(int where, const char *fmt,...)
Definition: endrun.c:175
void force_tree_rebuild(ForceTree *tree, DomainDecomp *ddecomp, const int HybridNuGrav, const int DoMoments, const char *EmergencyOutputDir)
Definition: forcetree.c:140
void init_forcetree_params(const int FastParticleType)
Definition: forcetree.c:43
void force_tree_free(ForceTree *tree)
Definition: forcetree.c:1409
void gravshort_fill_ntab(const enum ShortRangeForceWindowType ShortRangeForceWindowType, const double Asmth)
Definition: gravity.c:23
void gravshort_set_softenings(double MeanDMSeparation)
void set_gravshort_treepar(struct gravshort_tree_params tree_params)
double FORCE_SOFTENING(int i, int type)
void gravpm_force(PetaPM *pm, ForceTree *tree, Cosmology *CP, double Time, double UnitLength_in_cm, const char *PowerOutputDir, double TimeIC, int FastParticleType)
Definition: gravpm.c:62
void grav_short_tree(const ActiveParticles *act, PetaPM *pm, ForceTree *tree, double rho0, int NeutrinoTracer, int FastParticleType)
void gravpm_init_periodic(PetaPM *pm, double BoxSize, double Asmth, int Nmesh, double G)
Definition: gravpm.c:53
@ SHORTRANGE_FORCE_WINDOW_TYPE_EXACT
Definition: gravity.h:24
static struct gravpm_params GravPM
#define mymalloc(name, size)
Definition: mymalloc.h:15
#define myfree(x)
Definition: mymalloc.h:19
struct part_manager_type PartManager[1]
Definition: partmanager.c:11
#define P
Definition: partmanager.h:88
static peano_t PEANO(double *Pos, double BoxSize)
Definition: peano.h:14
void petapm_module_init(int Nthreads)
Definition: petapm.c:82
void petapm_destroy(PetaPM *pm)
Definition: petapm.c:215
#define CM_PER_MPC
Definition: physconst.h:20
static Cosmology * CP
Definition: power.c:27
int64_t NumActiveParticle
Definition: timestep.h:12
double Omega0
Definition: cosmology.h:10
double HubbleParam
Definition: cosmology.h:20
double CMBTemperature
Definition: cosmology.h:9
double OmegaCDM
Definition: cosmology.h:11
double OmegaLambda
Definition: cosmology.h:14
double OmegaBaryon
Definition: cosmology.h:19
double MNu[3]
Definition: cosmology.h:25
double Hubble
Definition: cosmology.h:21
int DomainOverDecompositionFactor
Definition: domain.h:53
int DomainUseGlobalSorting
Definition: domain.h:55
double TopNodeAllocFactor
Definition: domain.h:57
double SetAsideFactor
Definition: domain.h:59
Definition: petapm.h:62
double BHOpeningAngle
Definition: gravity.h:12
double ErrTolForceAcc
Definition: gravity.h:11
double FractionalGravitySoftening
Definition: gravity.h:18
#define MPI_INT64
Definition: system.h:12
static int check_against_force_direct(double ErrTolForceAcc)
Definition: test_gravity.c:146
static void test_force_random(void **state)
Definition: test_gravity.c:316
void do_random_test(gsl_rng *r, const int numpart)
Definition: test_gravity.c:291
static struct ClockTable CT
Definition: test_gravity.c:29
int main(void)
Definition: test_gravity.c:360
static void test_force_flat(void **state)
Definition: test_gravity.c:226
static int setup_tree(void **state)
Definition: test_gravity.c:330
static void do_force_test(int Nmesh, double Asmth, double ErrTolForceAcc, int direct)
Definition: test_gravity.c:162
static void force_direct(double *accn)
Definition: test_gravity.c:125
static void grav_force(const int this, const int other, const double *offset, double *accns)
Definition: test_gravity.c:38
static const double G
Definition: test_gravity.c:35
static void find_means(double *meangrav, double *suppmean, double *suppaccns)
Definition: test_gravity.c:97
static int teardown_tree(void **state)
Definition: test_gravity.c:353
static void test_force_close(void **state)
Definition: test_gravity.c:270
void check_accns(double *meanerr_tot, double *maxerr_tot, double *PairAccn, double meanacc)
Definition: test_gravity.c:73
struct UnitSystem get_unitsystem(double UnitLength_in_cm, double UnitMass_in_g, double UnitVelocity_in_cm_per_s)
Definition: unitsystem.c:6
void walltime_init(struct ClockTable *ct)
Definition: walltime.c:19