MP-Gadget  5.0.1.dev1-76bc7d4726-dirty
test_forcetree.c
Go to the documentation of this file.
1 /*Simple test for the tree building functions*/
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 <string.h>
10 #include <stdio.h>
11 #include <time.h>
12 #include <omp.h>
13 #include <gsl/gsl_rng.h>
14 
15 #include <libgadget/forcetree.h>
16 #include <libgadget/partmanager.h>
17 #include <libgadget/domain.h>
18 
19 
20 #include "stub.h"
21 
22 /*Particle data.*/
25 
26 /* The true struct for the state variable*/
28 {
30  gsl_rng * r;
31 };
32 
33 /*Dummy versions of functions that implement only what we need for the tests:
34  * most of these are used in the non-tested globally accessible parts of forcetree.c and
35  * so not executed by our tests anyway.*/
36 
37 void dump_snapshot() { }
38 
39 double walltime_measure_full(char * name, char * file, int line) {
40  return MPI_Wtime();
41 }
42 
43 /*End dummies*/
44 
45 static int
46 order_by_type_and_key(const void *a, const void *b)
47 {
48  const struct particle_data * pa = (const struct particle_data *) a;
49  const struct particle_data * pb = (const struct particle_data *) b;
50 
51  if(pa->Type < pb->Type)
52  return -1;
53  if(pa->Type > pb->Type)
54  return +1;
55  if(pa->Key < pb->Key)
56  return -1;
57  if(pa->Key > pb->Key)
58  return +1;
59 
60  return 0;
61 }
62 
63 #define NODECACHE_SIZE 100
64 
65 /*This checks that the moments of the force tree in Nodes are valid:
66  * that it the mass and flags are correct.*/
67 static int check_moments(const ForceTree * tb, const int numpart, const int nrealnode)
68 {
69  double * oldmass = malloc(sizeof(double) * tb->numnodes);
70  int i;
71 
72  for(i=tb->firstnode; i < tb->numnodes + tb->firstnode; i ++) {
73  oldmass[i - tb->firstnode] = tb->Nodes[i].mom.mass;
74  }
75 
76  for(i=0; i<numpart; i++)
77  {
78  int fnode = force_get_father(i, tb);
79  /*Subtract mass so that nothing is left.*/
80  assert_true(fnode >= tb->firstnode && fnode < tb->lastnode);
81  while(fnode > 0) {
82  tb->Nodes[fnode].mom.mass -= P[i].Mass;
83  fnode = tb->Nodes[fnode].father;
84  /*Validate father*/
85  assert_true((fnode >= tb->firstnode && fnode < tb->lastnode) || fnode == -1);
86  }
87  }
88  int node = tb->firstnode;
89  int counter = 0;
90  int sibcntr = 0;
91  while(node >= 0) {
92  /* Assert a real node*/
93  assert_true(node >= -1 && node < tb->lastnode && node >= tb->firstnode);
94  struct NODE * nop = &tb->Nodes[node];
95 
96  /*Check sibling*/
97  assert_true(tb->Nodes[node].sibling >= -1 && tb->Nodes[node].sibling < tb->lastnode);
98  int sib = tb->Nodes[node].sibling;
99  int sfather = force_get_father(sib, tb);
100  int father = force_get_father(node, tb);
101  /* Our sibling should either be a true sibling, with the same father,
102  * or should be the child of one of our ancestors*/
103  if(sfather != father && sib != -1) {
104  int ances = father;
105  while(ances >= 0) {
106  assert_true(ances >= tb->firstnode);
107  ances = force_get_father(ances, tb);
108  if(ances == sfather)
109  break;
110  }
111  assert_int_equal(ances, sfather);
112 /* printf("node %d ances %d sib %d next %d father %d sfather %d\n",node, ances, sib, nop->s.suns[0], father, sfather); */
113  }
114  else if(sib == -1)
115  sibcntr++;
116 
117  if(!(tb->Nodes[node].mom.mass < 0.5 && tb->Nodes[node].mom.mass > -0.5)) {
118  printf("node %d (%d) mass %g / %g TL %d DLM %d ITL %d\n",
119  node, node - tb->firstnode, tb->Nodes[node].mom.mass, oldmass[node - tb->firstnode],
120  tb->Nodes[node].f.TopLevel,
121  tb->Nodes[node].f.DependsOnLocalMass,
122  tb->Nodes[node].f.InternalTopLevel
123  );
124  /* something is wrong show the particles */
125  if(tb->Nodes[node].f.ChildType == PARTICLE_NODE_TYPE)
126  for(i = 0; i < nop->s.noccupied; i++) {
127  int nn = nop->s.suns[i];
128  printf("particles P[%d], Mass=%g\n", nn, P[nn].Mass);
129  }
130  }
131  assert_true(tb->Nodes[node].mom.mass < 0.5 && tb->Nodes[node].mom.mass > -0.5);
132  /*Check center of mass moments*/
133  for(i=0; i<3; i++)
134  assert_true(tb->Nodes[node].mom.cofm[i] <= PartManager->BoxSize && tb->Nodes[node].mom.cofm[i] >= 0);
135  counter++;
136 
137  if(nop->f.ChildType == PARTICLE_NODE_TYPE)
138  node = nop->sibling;
139  else
140  node = nop->s.suns[0];
141  }
142 // message(5, "count %d real %d\n", counter, nrealnode);
143  assert_true(counter <= nrealnode);
144  assert_true(sibcntr < counter/100);
145 
146  free(oldmass);
147  return nrealnode;
148 }
149 
150 /*This checks that the force tree in Nodes is valid:
151  * that it contains every particle and that each parent
152  * node contains particles within the right subnode.*/
153 static int check_tree(const ForceTree * tb, const int nnodes, const int numpart)
154 {
155  const int firstnode = tb->firstnode;
156  int tot_empty = 0, nrealnode = 0, sevens = 0;
157  int i;
158  for(i=firstnode; i<nnodes+firstnode; i++)
159  {
160  struct NODE * pNode = &(tb->Nodes[i]);
161  /*Just reserved free space with nothing in it*/
162  if(pNode->father < -1.5)
163  continue;
164 
165  int j;
166  /* Full of particles*/
167  if(pNode->s.noccupied < 1<<16) {
168  tot_empty += NMAXCHILD - pNode->s.noccupied;
169  if(pNode->s.noccupied == 0)
170  sevens++;
171  for(j=0; j<pNode->s.noccupied; j++) {
172  int child = pNode->s.suns[j];
173  assert_true(child >= 0);
174  assert_true(child < firstnode);
175  P[child].PI += 1;
176  assert_int_equal(force_get_father(child, tb), i);
177  }
178  }
179  /* Node is full of other nodes*/
180  else {
181  for(j=0; j<8; j++) {
182  /*Check children*/
183  int child = pNode->s.suns[j];
184  assert_true(child < firstnode+nnodes);
185  assert_true(child >= firstnode);
186  assert_true(fabs(tb->Nodes[child].len/pNode->len - 0.5) < 1e-4);
187  int k;
188  for(k=0; k<3; k++) {
189  if(j & (1<<k))
190  assert_true(tb->Nodes[child].center[k] > pNode->center[k]);
191  else
192  assert_true(tb->Nodes[child].center[k] <= pNode->center[k]);
193  }
194  }
195  }
196  nrealnode++;
197  }
198 
199  for(i=0; i<numpart; i++)
200  {
201  assert_int_equal(P[i].PI, 1);
202  }
203  printf("Tree filling factor: %g on %d nodes (wasted: %d empty: %d)\n", tot_empty/(8.*nrealnode), nrealnode, nnodes - nrealnode, sevens);
204  return nrealnode - sevens;
205 }
206 
207 static void do_tree_test(const int numpart, ForceTree tb, DomainDecomp * ddecomp)
208 {
209  /*Sort by peano key so this is more realistic*/
210  int i;
211  #pragma omp parallel for
212  for(i=0; i<numpart; i++) {
213  P[i].Key = PEANO(P[i].Pos, PartManager->BoxSize);
214  P[i].Mass = 1;
215  P[i].PI = 0;
216  P[i].IsGarbage = 0;
217  }
218  qsort(P, numpart, sizeof(struct particle_data), order_by_type_and_key);
219  int maxnode = tb.lastnode - tb.firstnode;
220  PartManager->MaxPart = numpart;
221  PartManager->NumPart = numpart;
222  assert_true(tb.Nodes != NULL);
223  /*So we know which nodes we have initialised*/
224  for(i=0; i< maxnode; i++)
225  tb.Nodes_base[i].father = -2;
226  /*Time creating the nodes*/
227  double start, end;
228  start = MPI_Wtime();
229  int nodes = force_tree_create_nodes(tb, numpart, ALLMASK, ddecomp, 0);
230  tb.numnodes = nodes;
231  assert_true(nodes < maxnode);
232  end = MPI_Wtime();
233  double ms = (end - start)*1000;
234  printf("Number of nodes used: %d. Built tree in %.3g ms\n", nodes,ms);
235  int nrealnode = check_tree(&tb, nodes, numpart);
236  /* now compute the multipole moments recursively */
237  start = MPI_Wtime();
238  force_update_node_parallel(&tb, ddecomp);
239  end = MPI_Wtime();
240  ms = (end - start)*1000;
241  printf("Updated moments in %.3g ms. Total mass: %g\n", ms, tb.Nodes[tb.firstnode].mom.mass);
242  assert_true(fabs(tb.Nodes[tb.firstnode].mom.mass - numpart) < 0.5);
243  check_moments(&tb, numpart, nrealnode);
244 }
245 
246 static void test_rebuild_flat(void ** state) {
247  /*Set up the particle data*/
248  int ncbrt = 128;
249  int numpart = ncbrt*ncbrt*ncbrt;
250  P = malloc(numpart*sizeof(struct particle_data));
251  /* Create a regular grid of particles, 8x8x8, all of type 1,
252  * in a box 8 kpc across.*/
253  int i;
254  #pragma omp parallel for
255  for(i=0; i<numpart; i++) {
256  P[i].Type = 1;
257  P[i].Pos[0] = (PartManager->BoxSize/ncbrt) * (i/ncbrt/ncbrt);
258  P[i].Pos[1] = (PartManager->BoxSize/ncbrt) * ((i/ncbrt) % ncbrt);
259  P[i].Pos[2] = (PartManager->BoxSize/ncbrt) * (i % ncbrt);
260  }
261  /*Allocate tree*/
262  /*Base pointer*/
263  struct forcetree_testdata * data = * (struct forcetree_testdata **) state;
264  DomainDecomp ddecomp = data->ddecomp;
265  ddecomp.TopLeaves[0].topnode = numpart;
266  ForceTree tb = force_treeallocate(numpart, numpart, &ddecomp);
267  /* So unused memory has Father < 0*/
268  for(i = tb.firstnode; i < tb.lastnode; i++)
269  tb.Nodes[i].father = -10;
270 
271  do_tree_test(numpart, tb, &ddecomp);
272  force_tree_free(&tb);
273  free(P);
274 }
275 
276 static void test_rebuild_close(void ** state) {
277  /*Set up the particle data*/
278  int ncbrt = 128;
279  int numpart = ncbrt*ncbrt*ncbrt;
280  double close = 5000;
281  P = malloc(numpart*sizeof(struct particle_data));
282  /* Create particles clustered in one place, all of type 1.*/
283  int i;
284  #pragma omp parallel for
285  for(i=0; i<numpart; i++) {
286  P[i].Type = 1;
287  P[i].Pos[0] = 4. + (i/ncbrt/ncbrt)/close;
288  P[i].Pos[1] = 4. + ((i/ncbrt) % ncbrt) /close;
289  P[i].Pos[2] = 4. + (i % ncbrt)/close;
290  }
291  struct forcetree_testdata * data = * (struct forcetree_testdata **) state;
292  DomainDecomp ddecomp = data->ddecomp;
293  ddecomp.TopLeaves[0].topnode = numpart;
294  ForceTree tb = force_treeallocate(numpart, numpart, &ddecomp);
295  do_tree_test(numpart, tb, &ddecomp);
296  force_tree_free(&tb);
297  free(P);
298 }
299 
300 void do_random_test(gsl_rng * r, const int numpart, const ForceTree tb, DomainDecomp * ddecomp)
301 {
302  /* Create a regular grid of particles, 8x8x8, all of type 1,
303  * in a box 8 kpc across.*/
304  int i;
305  for(i=0; i<numpart/4; i++) {
306  P[i].Type = 1;
307  int j;
308  for(j=0; j<3; j++)
309  P[i].Pos[j] = PartManager->BoxSize * gsl_rng_uniform(r);
310  }
311  for(i=numpart/4; i<3*numpart/4; i++) {
312  P[i].Type = 1;
313  int j;
314  for(j=0; j<3; j++)
315  P[i].Pos[j] = PartManager->BoxSize/2 + PartManager->BoxSize/8 * exp(pow(gsl_rng_uniform(r)-0.5,2));
316  }
317  for(i=3*numpart/4; i<numpart; i++) {
318  P[i].Type = 1;
319  int j;
320  for(j=0; j<3; j++)
321  P[i].Pos[j] = PartManager->BoxSize*0.1 + PartManager->BoxSize/32 * exp(pow(gsl_rng_uniform(r)-0.5,2));
322  }
323  do_tree_test(numpart, tb, ddecomp);
324 }
325 
326 static void test_rebuild_random(void ** state) {
327  /*Set up the particle data*/
328  int ncbrt = 64;
329  struct forcetree_testdata * data = * (struct forcetree_testdata **) state;
330  DomainDecomp ddecomp = data->ddecomp;
331  gsl_rng * r = (gsl_rng *) data->r;
332  int numpart = ncbrt*ncbrt*ncbrt;
333  /*Allocate tree*/
334  /*Base pointer*/
335  ddecomp.TopLeaves[0].topnode = numpart;
336  ForceTree tb = force_treeallocate(numpart, numpart, &ddecomp);
337  assert_true(tb.Nodes != NULL);
338  P = malloc(numpart*sizeof(struct particle_data));
339  int i;
340  for(i=0; i<2; i++) {
341  do_random_test(r, numpart, tb, &ddecomp);
342  }
343  force_tree_free(&tb);
344  free(P);
345 }
346 
347 /*Make a simple trivial domain for all data on a single processor*/
349 {
350  /* The whole tree goes into one topnode.
351  * Set up just enough of the TopNode structure that
352  * domain_get_topleaf works*/
354  ddecomp->NTopNodes = 1;
355  ddecomp->NTopLeaves = 1;
356  ddecomp->TopNodes = malloc(sizeof(struct topnode_data));
357  ddecomp->TopNodes[0].Daughter = -1;
358  ddecomp->TopNodes[0].Leaf = 0;
359  ddecomp->TopLeaves = malloc(sizeof(struct topleaf_data));
360  ddecomp->TopLeaves[0].Task = 0;
361  ddecomp->TopLeaves[0].topnode = 0;
362  /*These are not used*/
363  ddecomp->TopNodes[0].StartKey = 0;
365  /*To tell the code we are in serial*/
366  ddecomp->Tasks = malloc(sizeof(struct task_data));
367  ddecomp->Tasks[0].StartLeaf = 0;
368  ddecomp->Tasks[0].EndLeaf = 1;
369 }
370 
371 static int setup_tree(void **state) {
372  /*Set up the important parts of the All structure.*/
373  /*Particles should not be outside this*/
374  memset(PartManager, 0, sizeof(PartManager[0]));
375  memset(SlotsManager, 0, sizeof(SlotsManager[0]));
376  PartManager->BoxSize = 8;
378  /*Set up the top-level domain grid*/
379  struct forcetree_testdata *data = malloc(sizeof(struct forcetree_testdata));
380  trivial_domain(&data->ddecomp);
381  data->r = gsl_rng_alloc(gsl_rng_mt19937);
382  gsl_rng_set(data->r, 0);
383  *state = (void *) data;
384  return 0;
385 }
386 
387 static int teardown_tree(void **state) {
388  struct forcetree_testdata * data = (struct forcetree_testdata * ) *state;
389  free(data->ddecomp.TopNodes);
390  free(data->ddecomp.TopLeaves);
391  free(data->ddecomp.Tasks);
392  free(data->r);
393  free(data);
394  return 0;
395 }
396 
397 int main(void) {
398  const struct CMUnitTest tests[] = {
399  cmocka_unit_test(test_rebuild_flat),
400  cmocka_unit_test(test_rebuild_close),
401  cmocka_unit_test(test_rebuild_random),
402  };
403  return cmocka_run_group_tests_mpi(tests, setup_tree, teardown_tree);
404 }
const char * name
Definition: densitykernel.c:93
int force_tree_create_nodes(const ForceTree tb, const int npart, int mask, DomainDecomp *ddecomp, const int HybridNuGrav)
Definition: forcetree.c:661
ForceTree force_treeallocate(int64_t maxnodes, int64_t maxpart, DomainDecomp *ddecomp)
Definition: forcetree.c:1381
void force_update_node_parallel(const ForceTree *tree, const DomainDecomp *ddecomp)
Definition: forcetree.c:1081
void init_forcetree_params(const int FastParticleType)
Definition: forcetree.c:43
void force_tree_free(ForceTree *tree)
Definition: forcetree.c:1409
int force_get_father(int no, const ForceTree *tree)
Definition: forcetree.c:905
#define ALLMASK
Definition: forcetree.h:20
#define PARTICLE_NODE_TYPE
Definition: forcetree.h:15
#define NMAXCHILD
Definition: forcetree.h:12
#define P
Definition: partmanager.h:88
static peano_t PEANO(double *Pos, double BoxSize)
Definition: peano.h:14
#define BITS_PER_DIMENSION
Definition: peano.h:9
struct topnode_data * TopNodes
Definition: domain.h:36
struct task_data * Tasks
Definition: domain.h:41
int NTopNodes
Definition: domain.h:39
struct topleaf_data * TopLeaves
Definition: domain.h:37
int NTopLeaves
Definition: domain.h:40
int domain_allocated_flag
Definition: domain.h:34
struct NODE * Nodes_base
Definition: forcetree.h:101
struct NODE * Nodes
Definition: forcetree.h:97
int firstnode
Definition: forcetree.h:84
int numnodes
Definition: forcetree.h:88
int lastnode
Definition: forcetree.h:86
Definition: forcetree.h:39
int sibling
Definition: forcetree.h:40
struct NODE::@9 mom
unsigned int DependsOnLocalMass
Definition: forcetree.h:48
MyFloat center[3]
Definition: forcetree.h:43
unsigned int ChildType
Definition: forcetree.h:49
MyFloat mass
Definition: forcetree.h:56
int father
Definition: forcetree.h:41
MyFloat cofm[3]
Definition: forcetree.h:55
unsigned int TopLevel
Definition: forcetree.h:47
struct NodeChild s
Definition: forcetree.h:66
unsigned int InternalTopLevel
Definition: forcetree.h:46
struct NODE::@8 f
MyFloat len
Definition: forcetree.h:42
int noccupied
Definition: forcetree.h:35
int suns[NMAXCHILD]
Definition: forcetree.h:32
DomainDecomp ddecomp
peano_t Key
Definition: partmanager.h:66
unsigned int Type
Definition: partmanager.h:17
int EndLeaf
Definition: domain.h:30
int StartLeaf
Definition: domain.h:29
int topnode
Definition: domain.h:23
int Task
Definition: domain.h:21
peano_t StartKey
Definition: domain.h:14
int Leaf
Definition: domain.h:17
int Daughter
Definition: domain.h:15
int Shift
Definition: domain.h:16
static void test_rebuild_flat(void **state)
static void test_rebuild_close(void **state)
struct part_manager_type PartManager[1]
int main(void)
double walltime_measure_full(char *name, char *file, int line)
static void do_tree_test(const int numpart, ForceTree tb, DomainDecomp *ddecomp)
static int setup_tree(void **state)
struct slots_manager_type SlotsManager[1]
static int order_by_type_and_key(const void *a, const void *b)
void trivial_domain(DomainDecomp *ddecomp)
void dump_snapshot()
static int check_moments(const ForceTree *tb, const int numpart, const int nrealnode)
static int teardown_tree(void **state)
static int check_tree(const ForceTree *tb, const int nnodes, const int numpart)
void do_random_test(gsl_rng *r, const int numpart, const ForceTree tb, DomainDecomp *ddecomp)
static void test_rebuild_random(void **state)