MP-Gadget  5.0.1.dev1-76bc7d4726-dirty
fof.c
Go to the documentation of this file.
1 #include <mpi.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stddef.h>
5 #include <string.h>
6 #include <math.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <gsl/gsl_math.h>
10 #include <inttypes.h>
11 #include <omp.h>
12 
13 #include "utils.h"
14 #include "utils/mpsort.h"
15 
16 #include "walltime.h"
17 #include "sfr_eff.h"
18 #include "blackhole.h"
19 #include "domain.h"
20 #include "winds.h"
21 
22 #include "forcetree.h"
23 #include "treewalk.h"
24 #include "slotsmanager.h"
25 #include "partmanager.h"
26 #include "densitykernel.h"
27 
32 #include "fof.h"
33 
34 #define LARGE 1e29
35 #define MAXITER 400
36 
37 struct FOFParams
38 {
39  int FOFSaveParticles ; /* saving particles in the fof group */
40  double MinFoFMassForNewSeed; /* Halo mass required before new seed is put in */
41  double MinMStarForNewSeed; /* Minimum stellar mass required before new seed */
43  double FOFHaloComovingLinkingLength; /* in code units */
48 
49 /*Set the parameters of the BH module*/
51 {
52  int ThisTask;
53  MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask);
54  if(ThisTask == 0) {
55  fof_params.FOFSaveParticles = param_get_int(ps, "FOFSaveParticles");
56  fof_params.FOFHaloLinkingLength = param_get_double(ps, "FOFHaloLinkingLength");
57  fof_params.FOFHaloMinLength = param_get_int(ps, "FOFHaloMinLength");
58  fof_params.MinFoFMassForNewSeed = param_get_double(ps, "MinFoFMassForNewSeed");
59  fof_params.MinMStarForNewSeed = param_get_double(ps, "MinMStarForNewSeed");
60  fof_params.FOFPrimaryLinkTypes = param_get_int(ps, "FOFPrimaryLinkTypes");
61  fof_params.FOFSecondaryLinkTypes = param_get_int(ps, "FOFSecondaryLinkTypes");
62  }
63  MPI_Bcast(&fof_params, sizeof(struct FOFParams), MPI_BYTE, 0, MPI_COMM_WORLD);
64 }
65 
66 void fof_init(double DMMeanSeparation)
67 {
69 }
70 
71 static double fof_periodic(double x, double BoxSize)
72 {
73  if(x >= 0.5 * BoxSize)
74  x -= BoxSize;
75  if(x < -0.5 * BoxSize)
76  x += BoxSize;
77  return x;
78 }
79 
80 
81 static double fof_periodic_wrap(double x, double BoxSize)
82 {
83  while(x >= BoxSize)
84  x -= BoxSize;
85  while(x < 0)
86  x += BoxSize;
87  return x;
88 }
89 
91 {
93  int MinIDTask;
94  int Pindex;
95 };
96 
97 static void fof_label_secondary(struct fof_particle_list * HaloLabel, ForceTree * tree);
98 static int fof_compare_HaloLabel_MinID(const void *a, const void *b);
100 static int fof_compare_Group_MinIDTask(const void *a, const void *b);
101 static int fof_compare_Group_OriginalIndex(const void *a, const void *b);
102 static int fof_compare_Group_MinID(const void *a, const void *b);
103 static void fof_reduce_groups(
104  void * groups,
105  int nmemb,
106  size_t elsize,
107  void (*reduce_group)(void * gdst, void * gsrc), MPI_Comm Comm);
108 
109 static void fof_finish_group_properties(FOFGroups * fof, double BoxSize);
110 
111 static int fof_compile_base(struct BaseGroup * base, int NgroupsExt, struct fof_particle_list * HaloLabel, MPI_Comm Comm);
112 static void fof_compile_catalogue(FOFGroups * fof, const int NgroupsExt, struct fof_particle_list * HaloLabel, MPI_Comm Comm);
113 
114 static struct Group *
115 fof_alloc_group(const struct BaseGroup * base, const int NgroupsExt);
116 
117 static void fof_assign_grnr(struct BaseGroup * base, const int NgroupsExt, MPI_Comm Comm);
118 
119 void fof_label_primary(struct fof_particle_list * HaloLabel, ForceTree * tree, MPI_Comm Comm);
120 
121 typedef struct {
126  int pad;
128 
129 typedef struct {
134  int pad;
136 
137 typedef struct {
140 
141 
142 static MPI_Datatype MPI_TYPE_GROUP;
143 
144 /*
145  * The FOF finder will produce Group[], which is allocated to the top side of the
146  * main heap.
147  *
148  **/
149 
150 FOFGroups
151 fof_fof(DomainDecomp * ddecomp, const int StoreGrNr, MPI_Comm Comm)
152 {
153  int i;
154 
155  message(0, "Begin to compute FoF group catalogues. (allocated: %g MB)\n",
156  mymalloc_usedbytes() / (1024.0 * 1024.0));
157 
158  walltime_measure("/Misc");
159 
160  message(0, "Comoving linking length: %g\n", fof_params.FOFHaloComovingLinkingLength);
161 
162  struct fof_particle_list * HaloLabel = (struct fof_particle_list *) mymalloc("HaloLabel", PartManager->NumPart * sizeof(struct fof_particle_list));
163 
164  /* HaloLabel stores the MinID and MinIDTask of particles, this pair serves as a halo label. */
165  #pragma omp parallel for
166  for(i = 0; i < PartManager->NumPart; i++) {
167  HaloLabel[i].Pindex = i;
168  }
169 
170  /* We only need a tree containing primary linking particles only. No moments*/
171  ForceTree dmtree = {0};
172  force_tree_rebuild_mask(&dmtree, ddecomp, fof_params.FOFPrimaryLinkTypes, 0, NULL);
173 
174  /* Fill FOFP_List of primary */
175  fof_label_primary(HaloLabel, &dmtree, Comm);
176 
177  MPIU_Barrier(Comm);
178  message(0, "Group finding done.\n");
179  walltime_measure("/FOF/Primary");
180 
181  /* Fill FOFP_List of secondary */
182  fof_label_secondary(HaloLabel, &dmtree);
183  force_tree_free(&dmtree);
184 
185  MPIU_Barrier(Comm);
186  message(0, "Attached gas and star particles to nearest dm particles.\n");
187 
188  walltime_measure("/FOF/Secondary");
189 
190  /* sort HaloLabel according to MinID, because we need that for compiling catalogues */
192 
193  int NgroupsExt = 0;
194 
195  for(i = 0; i < PartManager->NumPart; i ++) {
196  if(i == 0 || HaloLabel[i].MinID != HaloLabel[i - 1].MinID) NgroupsExt ++;
197  }
198 
199  /* The first round is to eliminate groups that are too short. */
200  /* We create the smaller 'BaseGroup' data set for this. */
201  struct BaseGroup * base = (struct BaseGroup *) mymalloc("BaseGroup", sizeof(struct BaseGroup) * NgroupsExt);
202 
203  NgroupsExt = fof_compile_base(base, NgroupsExt, HaloLabel, Comm);
204 
205  MPIU_Barrier(Comm);
206  message(0, "Compiled local group data and catalogue.\n");
207 
208  walltime_measure("/FOF/Compile");
209 
210  fof_assign_grnr(base, NgroupsExt, Comm);
211 
212  /*Store the group number in the particle struct*/
213  if(StoreGrNr) {
214  #pragma omp parallel for
215  for(i = 0; i < PartManager->NumPart; i++)
216  P[i].GrNr = -1; /* will mark particles that are not in any group */
217 
218  int64_t start = 0;
219  for(i = 0; i < NgroupsExt; i++)
220  {
221  for(;start < PartManager->NumPart; start++) {
222  if (HaloLabel[start].MinID >= base[i].MinID)
223  break;
224  }
225 
226  for(;start < PartManager->NumPart; start++) {
227  if (HaloLabel[start].MinID != base[i].MinID)
228  break;
229  P[HaloLabel[start].Pindex].GrNr = base[i].GrNr;
230  }
231  }
232  }
233 
234  /*Initialise the Group object from the BaseGroup*/
235  FOFGroups fof;
236  MPI_Type_contiguous(sizeof(fof.Group[0]), MPI_BYTE, &MPI_TYPE_GROUP);
237  MPI_Type_commit(&MPI_TYPE_GROUP);
238 
239  fof.Group = fof_alloc_group(base, NgroupsExt);
240 
241  myfree(base);
242 
243  fof_compile_catalogue(&fof, NgroupsExt, HaloLabel, Comm);
244 
245  MPIU_Barrier(Comm);
246  message(0, "Finished FoF. Group properties are now allocated.. (presently allocated=%g MB)\n",
247  mymalloc_usedbytes() / (1024.0 * 1024.0));
248 
249  walltime_measure("/FOF/Prop");
250 
251  myfree(HaloLabel);
252 
253  return fof;
254 }
255 
256 void
258 {
259  myfree(fof->Group);
260 
261  message(0, "Finished computing FoF groups. (presently allocated=%g MB)\n",
262  mymalloc_usedbytes() / (1024.0 * 1024.0));
263 
264  walltime_measure("/FOF/MISC");
265 
266  MPI_Type_free(&MPI_TYPE_GROUP);
267 }
268 
270  int * Head;
271  struct SpinLocks * spin;
275 };
276 #define FOF_PRIMARY_GET_PRIV(tw) ((struct FOFPrimaryPriv *) (tw->priv))
277 
278 /* This function walks the particle tree starting at particle i until it reaches
279  * a particle which has Head[i] = i, the root node (particles are initialised in
280  * this state, so this is equivalent to finding a particle which has yet to be merged).
281  * Once it reaches a root, it returns that particle number.
282  * Arguments:
283  *
284  * stop: When this particle is reached, return -1. We use this to find an already merged tree.
285  *
286  * Returns:
287  * root particle if found
288  * -1 if stop particle reached
289  */
290 static int
291 HEADl(int stop, int i, const int * const Head)
292 {
293  int next = i;
294 
295  do {
296  i = next;
297  /* Reached stop, return*/
298  if(i == stop)
299  return -1;
300  /* atomic read because we may change
301  * this in update_root: not necessary on x86_64, but avoids tears elsewhere*/
302  #pragma omp atomic read
303  next = Head[i];
304  } while(next != i);
305 
306  /* return unmerged particle*/
307  return i;
308 }
309 
310 /* Rewrite a tree so that all values in it point directly to the true root.
311  * This means that the trees are O(1) deep and speeds up future accesses.
312  * See https://arxiv.org/abs/1607.03224 */
313 static void
314 update_root(int i, const int r, int * Head)
315 {
316  int t = i;
317  do {
318  i = t;
319  #pragma omp atomic capture
320  {
321  t = Head[i];
322  Head[i]= r;
323  }
324  /* Stop if we reached the top (new head is the same as the old)
325  * or if the new head is less than or equal to the desired head, indicating
326  * another thread changed us*/
327  } while(t != i && (t > r));
328 }
329 
330 /* Find the current head particle by walking the tree. No updates are done
331  * so this can be performed from a threaded context. */
332 static int
333 HEAD(int i, const int * const Head)
334 {
335  int r = i;
336  while(Head[r] != r) {
337  r = Head[r];
338  }
339  return r;
340 }
341 
342 static void fof_primary_copy(int place, TreeWalkQueryFOF * I, TreeWalk * tw) {
343  /* The copied data is *only* used for the
344  * secondary treewalk, so fill up garbage for the primary treewalk.
345  * The copy is a technical race otherwise. */
346  if(I->base.NodeList[0] == tw->tree->firstnode) {
347  I->MinID = -1;
348  I->MinIDTask = -1;
349  return;
350  }
351  /* Secondary treewalk, no need for locking here*/
352  int head = HEAD(place, FOF_PRIMARY_GET_PRIV(tw)->Head);
353  I->MinID = FOF_PRIMARY_GET_PRIV(tw)->HaloLabel[head].MinID;
354  I->MinIDTask = FOF_PRIMARY_GET_PRIV(tw)->HaloLabel[head].MinIDTask;
355 }
356 
357 static int fof_primary_haswork(int n, TreeWalk * tw) {
358  if(P[n].IsGarbage || P[n].Swallowed)
359  return 0;
360  return (((1 << P[n].Type) & (fof_params.FOFPrimaryLinkTypes))) && FOF_PRIMARY_GET_PRIV(tw)->PrimaryActive[n];
361 }
362 
363 static void
365  TreeWalkResultFOF * O,
366  TreeWalkNgbIterFOF * iter,
367  LocalTreeWalk * lv);
368 
369 void fof_label_primary(struct fof_particle_list * HaloLabel, ForceTree * tree, MPI_Comm Comm)
370 {
371  int i;
372  int64_t link_across;
373  int64_t link_across_tot;
374  double t0, t1;
375  int ThisTask;
376  MPI_Comm_rank(Comm, &ThisTask);
377 
378  message(0, "Start linking particles (presently allocated=%g MB)\n", mymalloc_usedbytes() / (1024.0 * 1024.0));
379 
380  TreeWalk tw[1] = {{0}};
381  tw->ev_label = "FOF_FIND_GROUPS";
385 
388  tw->reduce = NULL;
389  tw->type = TREEWALK_ALL;
390  tw->query_type_elsize = sizeof(TreeWalkQueryFOF);
392  tw->tree = tree;
393  struct FOFPrimaryPriv priv[1];
394  tw->priv = priv;
395 
396  FOF_PRIMARY_GET_PRIV(tw)->Head = (int*) mymalloc("FOF_Links", PartManager->NumPart * sizeof(int));
397  FOF_PRIMARY_GET_PRIV(tw)->PrimaryActive = (char*) mymalloc("FOFActive", PartManager->NumPart * sizeof(char));
398  FOF_PRIMARY_GET_PRIV(tw)->OldMinID = (MyIDType *) mymalloc("FOFActive", PartManager->NumPart * sizeof(MyIDType));
399  FOF_PRIMARY_GET_PRIV(tw)->HaloLabel = HaloLabel;
400  /* allocate buffers to arrange communication */
401 
402  #pragma omp parallel for
403  for(i = 0; i < PartManager->NumPart; i++)
404  {
405  FOF_PRIMARY_GET_PRIV(tw)->Head[i] = i;
406  FOF_PRIMARY_GET_PRIV(tw)->OldMinID[i]= P[i].ID;
407  FOF_PRIMARY_GET_PRIV(tw)->PrimaryActive[i] = 1;
408 
409  HaloLabel[i].MinID = P[i].ID;
411  }
412 
413  /* The lock is used to protect MinID*/
415  do
416  {
417  t0 = second();
418 
419  treewalk_run(tw, NULL, PartManager->NumPart);
420 
421  t1 = second();
422  /* This sets the MinID of the head particle to the minimum ID
423  * of the child particles. We set this inside the treewalk,
424  * but the locking allows a race, where the particle with MinID set
425  * is no longer the one which is the true Head of the group.
426  * So we must check it again here.*/
427  #pragma omp parallel for
428  for(i = 0; i < PartManager->NumPart; i++) {
429  int head = HEAD(i, FOF_PRIMARY_GET_PRIV(tw)->Head);
430  /* Don't check against ourself*/
431  if(head == i)
432  continue;
433  MyIDType headminid;
434  #pragma omp atomic read
435  headminid = HaloLabel[head].MinID;
436  /* No atomic needed for i as this is not a head*/
437  if(headminid > HaloLabel[i].MinID) {
438  lock_spinlock(head, priv->spin);
439  if(HaloLabel[head].MinID > HaloLabel[i].MinID) {
440  #pragma omp atomic write
441  HaloLabel[head].MinID = HaloLabel[i].MinID;
443  }
444  unlock_spinlock(head, priv->spin);
445  }
446  }
447  /* let's check out which particles have changed their MinID,
448  * mark them for next round. */
449  link_across = 0;
450 #pragma omp parallel for reduction(+: link_across)
451  for(i = 0; i < PartManager->NumPart; i++) {
452  int head = HEAD(i, FOF_PRIMARY_GET_PRIV(tw)->Head);
453  /* This loop sets the MinID of the children to the minID of the head.
454  * The minID of the head is set above and is stable at this point.*/
455  if(i != head) {
456  HaloLabel[i].MinID = HaloLabel[head].MinID;
458  }
459  MyIDType newMinID = HaloLabel[head].MinID;
460  if(newMinID != FOF_PRIMARY_GET_PRIV(tw)->OldMinID[i]) {
461  FOF_PRIMARY_GET_PRIV(tw)->PrimaryActive[i] = 1;
462  FOF_PRIMARY_GET_PRIV(tw)->OldMinID[i] = newMinID;
463  link_across ++;
464  } else {
465  FOF_PRIMARY_GET_PRIV(tw)->PrimaryActive[i] = 0;
466  }
467  }
468  MPI_Allreduce(&link_across, &link_across_tot, 1, MPI_INT64, MPI_SUM, Comm);
469  message(0, "Linked %ld particles %g seconds\n", link_across_tot, t1 - t0);
470  }
471  while(link_across_tot > 0);
472 
473  free_spinlocks(priv[0].spin);
474 
475  message(0, "Local groups found.\n");
476 
480 }
481 
482 static void
483 fofp_merge(int target, int other, TreeWalk * tw)
484 {
485  /* this will lock h1 */
486  int * Head = FOF_PRIMARY_GET_PRIV(tw)->Head;
487  int h1, h2;
488  do {
489  h1 = HEADl(-1, target, Head);
490  /* Done if we find h1 along the path
491  * (because other is already in the same halo) */
492  h2 = HEADl(h1, other, Head);
493  if(h2 < 0)
494  return;
495  /* Ensure that we always merge to the lower entry.
496  * This avoids circular loops in the Head entries:
497  * a -> b -> a */
498  if(h1 > h2) {
499  int tmp = h2;
500  h2 = h1;
501  h1 = tmp;
502  }
503  /* Atomic compare exchange to make h2 a subtree of h1.
504  * Set Head[h2] = h1 iff Head[h2] is still h2. Otherwise loop.*/
505  } while(!__atomic_compare_exchange(&Head[h2], &h2, &h1, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
506 
507  struct SpinLocks * spin = FOF_PRIMARY_GET_PRIV(tw)->spin;
508 
509  /* update MinID of h1: h2 is now just another child of h1
510  * so we don't need to check that h2 changes its head.
511  * It might happen that h1 is added to another halo at this point
512  * and the addition gets the wrong MinID.
513  * For this reason we recompute the MinIDs after the main treewalk.
514  * We also lock h2 for a copy in case it is the h1 in another thread,
515  * and may have inconsistent MinID and MinIDTask.*/
516 
517  /* Get a copy of h2 under the lock, which ensures
518  * that MinID and MinIDTask do not change independently. */
519  struct fof_particle_list * HaloLabel = FOF_PRIMARY_GET_PRIV(tw)->HaloLabel;
520  struct fof_particle_list h2label;
521  lock_spinlock(h2, spin);
522  h2label.MinID = HaloLabel[h2].MinID;
523  h2label.MinIDTask = HaloLabel[h2].MinIDTask;
524  unlock_spinlock(h2, spin);
525 
526  /* Now lock h1 so we don't change MinID but not MinIDTask.*/
527  lock_spinlock(h1, spin);
528  if(HaloLabel[h1].MinID > h2label.MinID)
529  {
530  HaloLabel[h1].MinID = h2label.MinID;
531  HaloLabel[h1].MinIDTask = h2label.MinIDTask;
532  }
533  unlock_spinlock(h1, spin);
534 
535  /* h1 must be the root of other and target both:
536  * do the splay to speed up future accesses.
537  * We do not need to have h2 locked, because h2 is
538  * now just another child of h1: these do not change the root,
539  * they make the tree shallow.*/
540  update_root(target, h1, Head);
541  update_root(other, h1, Head);
542 
543 }
544 
545 static void
547  TreeWalkResultFOF * O,
548  TreeWalkNgbIterFOF * iter,
549  LocalTreeWalk * lv)
550 {
551  TreeWalk * tw = lv->tw;
552  if(iter->base.other == -1) {
556  return;
557  }
558  int other = iter->base.other;
559 
560  if(lv->mode == 0) {
561  /* Local FOF */
562  if(lv->target <= other) {
563  // printf("locked merge %d %d by %d\n", lv->target, other, omp_get_thread_num());
564  fofp_merge(lv->target, other, tw);
565  }
566  }
567  else /* mode is 1, target is a ghost */
568  {
569  int head = HEAD(other, FOF_PRIMARY_GET_PRIV(tw)->Head);
570  struct fof_particle_list * HaloLabel = FOF_PRIMARY_GET_PRIV(tw)->HaloLabel;
571  struct SpinLocks * spin = FOF_PRIMARY_GET_PRIV(tw)->spin;
572 // printf("locking %d by %d in ngbiter\n", other, omp_get_thread_num());
573  lock_spinlock(head, spin);
574  if(HaloLabel[head].MinID > I->MinID)
575  {
576  HaloLabel[head].MinID = I->MinID;
577  HaloLabel[head].MinIDTask = I->MinIDTask;
578  }
579 // printf("unlocking %d by %d in ngbiter\n", other, omp_get_thread_num());
580  unlock_spinlock(head, spin);
581  }
582 }
583 
584 static void fof_reduce_base_group(void * pdst, void * psrc) {
585  struct BaseGroup * gdst = (struct BaseGroup *) pdst;
586  struct BaseGroup * gsrc = (struct BaseGroup *) psrc;
587  gdst->Length += gsrc->Length;
588  /* preserve the dst FirstPos so all other base group gets the same FirstPos */
589 }
590 
591 static void fof_reduce_group(void * pdst, void * psrc) {
592  struct Group * gdst = (struct Group *) pdst;
593  struct Group * gsrc = (struct Group *) psrc;
594  int j;
595  gdst->Length += gsrc->Length;
596  gdst->Mass += gsrc->Mass;
597 
598  for(j = 0; j < 6; j++)
599  {
600  gdst->LenType[j] += gsrc->LenType[j];
601  gdst->MassType[j] += gsrc->MassType[j];
602  }
603 
604  gdst->Sfr += gsrc->Sfr;
605  gdst->GasMetalMass += gsrc->GasMetalMass;
606  gdst->StellarMetalMass += gsrc->StellarMetalMass;
607  gdst->MassHeIonized += gsrc->MassHeIonized;
608  for(j = 0; j < NMETALS; j++) {
609  gdst->GasMetalElemMass[j] += gsrc->GasMetalElemMass[j];
610  gdst->StellarMetalElemMass[j] += gsrc->StellarMetalElemMass[j];
611  }
612  gdst->BH_Mdot += gsrc->BH_Mdot;
613  gdst->BH_Mass += gsrc->BH_Mass;
614  if(gsrc->MaxDens > gdst->MaxDens)
615  {
616  gdst->MaxDens = gsrc->MaxDens;
617  gdst->seed_index = gsrc->seed_index;
618  gdst->seed_task = gsrc->seed_task;
619  }
620 
621  int d1, d2;
622  for(d1 = 0; d1 < 3; d1++)
623  {
624  gdst->CM[d1] += gsrc->CM[d1];
625  gdst->Vel[d1] += gsrc->Vel[d1];
626  gdst->Jmom[d1] += gsrc->Jmom[d1];
627  for(d2 = 0; d2 < 3; d2 ++) {
628  gdst->Imom[d1][d2] += gsrc->Imom[d1][d2];
629  }
630  }
631 
632 }
633 
634 static void add_particle_to_group(struct Group * gdst, int i, int ThisTask) {
635 
636  /* My local number of particles contributing to the full catalogue. */
637  const int index = i;
638  if(gdst->Length == 0) {
639  struct BaseGroup base = gdst->base;
640  memset(gdst, 0, sizeof(gdst[0]));
641  gdst->base = base;
642  gdst->seed_index = gdst->seed_task = -1;
643  }
644 
645  gdst->Length ++;
646  gdst->Mass += P[index].Mass;
647  gdst->LenType[P[index].Type]++;
648  gdst->MassType[P[index].Type] += P[index].Mass;
649 
650  if(P[index].Type == 0) {
651  gdst->MassHeIonized += P[index].Mass * P[index].HeIIIionized;
652  gdst->Sfr += SPHP(index).Sfr;
653  gdst->GasMetalMass += SPHP(index).Metallicity * P[index].Mass;
654  int j;
655  for(j = 0; j < NMETALS; j++)
656  gdst->GasMetalElemMass[j] += SPHP(index).Metals[j] * P[index].Mass;
657  }
658  if(P[index].Type == 4) {
659  int j;
660  gdst->StellarMetalMass += STARP(index).Metallicity * P[index].Mass;
661  for(j = 0; j < NMETALS; j++)
662  gdst->StellarMetalElemMass[j] += STARP(index).Metals[j] * P[index].Mass;
663  }
664 
665  if(P[index].Type == 5)
666  {
667  gdst->BH_Mdot += BHP(index).Mdot;
668  gdst->BH_Mass += BHP(index).Mass;
669  }
670  /*This used to depend on black holes being enabled, but I do not see why.
671  * I think because it is only useful for seeding*/
672  /* Don't make bh in wind.*/
673  if(P[index].Type == 0 && !winds_is_particle_decoupled(index))
674  if(SPHP(index).Density > gdst->MaxDens)
675  {
676  gdst->MaxDens = SPHP(index).Density;
677  gdst->seed_index = index;
678  gdst->seed_task = ThisTask;
679  }
680 
681  int d1, d2;
682  double xyz[3];
683  double rel[3];
684  double vel[3];
685  double jmom[3];
686 
687  for(d1 = 0; d1 < 3; d1++)
688  {
689  double first = gdst->base.FirstPos[d1];
690  rel[d1] = fof_periodic(P[index].Pos[d1] - first, PartManager->BoxSize) ;
691  xyz[d1] = rel[d1] + first;
692  vel[d1] = P[index].Vel[d1];
693  }
694 
695  crossproduct(rel, vel, jmom);
696 
697  for(d1 = 0; d1 < 3; d1++) {
698  gdst->CM[d1] += P[index].Mass * xyz[d1];
699  gdst->Vel[d1] += P[index].Mass * vel[d1];
700  gdst->Jmom[d1] += P[index].Mass * jmom[d1];
701 
702  for(d2 = 0; d2 < 3; d2++) {
703  gdst->Imom[d1][d2] += P[index].Mass * rel[d1] * rel[d2];
704  }
705  }
706 }
707 
708 static void
709 fof_finish_group_properties(struct FOFGroups * fof, double BoxSize)
710 {
711  int i;
712 
713  for(i = 0; i < fof->Ngroups; i++)
714  {
715  int d1, d2;
716  double cm[3];
717  double rel[3];
718  double jcm[3];
719  double vcm[3];
720 
721  struct Group * gdst = &fof->Group[i];
722  for(d1 = 0; d1 < 3; d1++)
723  {
724  gdst->Vel[d1] /= gdst->Mass;
725  vcm[d1] = gdst->Vel[d1];
726  cm[d1] = gdst->CM[d1] / gdst->Mass;
727 
728  rel[d1] = fof_periodic(cm[d1] - gdst->base.FirstPos[d1], BoxSize);
729 
730  cm[d1] = fof_periodic_wrap(cm[d1], BoxSize);
731  gdst->CM[d1] = cm[d1];
732 
733  }
734  crossproduct(rel, vcm, jcm);
735 
736  for(d1 = 0; d1 < 3; d1 ++) {
737  gdst->Jmom[d1] -= jcm[d1] * gdst->Mass;
738  }
739 
740  for(d1 = 0; d1 < 3; d1 ++) {
741  for(d2 = 0; d2 < 3; d2++) {
742  /* Parallel Axis theorem:
743  * https://en.wikipedia.org/wiki/Parallel_axis_theorem ;
744  * J was relative to FirstPos, I is relative to CM.
745  *
746  * Note that our definition of Imom follows the astronomy one,
747  *
748  * I_ij = sum x_i x_j (where x_i x_j is relative displacement)
749  * */
750 
751  double diff = rel[d1] * rel[d2];
752 
753  gdst->Imom[d1][d2] -= gdst->Mass * diff;
754  }
755  }
756  }
757 
758 }
759 
760 static int
761 fof_compile_base(struct BaseGroup * base, int NgroupsExt, struct fof_particle_list * HaloLabel, MPI_Comm Comm)
762 {
763  memset(base, 0, sizeof(base[0]) * NgroupsExt);
764 
765  int i;
766  int start;
767 
768  start = 0;
769  for(i = 0; i < PartManager->NumPart; i++)
770  {
771  if(i == 0 || HaloLabel[i].MinID != HaloLabel[i - 1].MinID) {
772  base[start].MinID = HaloLabel[i].MinID;
773  base[start].MinIDTask = HaloLabel[i].MinIDTask;
774  int d;
775  for(d = 0; d < 3; d ++) {
776  base[start].FirstPos[d] = P[HaloLabel[i].Pindex].Pos[d];
777  }
778  start ++;
779  }
780  }
781 
782  /* count local lengths */
783  /* This works because base is sorted by MinID by construction. */
784  start = 0;
785  for(i = 0; i < NgroupsExt; i++)
786  {
787  /* find the first particle */
788  for(;start < PartManager->NumPart; start++) {
789  if(HaloLabel[start].MinID >= base[i].MinID) break;
790  }
791  /* count particles */
792  for(;start < PartManager->NumPart; start++) {
793  if(HaloLabel[start].MinID != base[i].MinID) {
794  break;
795  }
796  base[i].Length ++;
797  }
798  }
799 
800  /* update global attributes */
801  fof_reduce_groups(base, NgroupsExt, sizeof(base[0]), fof_reduce_base_group, Comm);
802 
803  /* eliminate all groups that are too small */
804  for(i = 0; i < NgroupsExt; i++)
805  {
807  {
808  base[i] = base[NgroupsExt - 1];
809  NgroupsExt--;
810  i--;
811  }
812  }
813  return NgroupsExt;
814 }
815 
816 /* Allocate memory for and initialise a Group object
817  * from a BaseGroup object.*/
818 static struct Group *
819 fof_alloc_group(const struct BaseGroup * base, const int NgroupsExt)
820 {
821  int i;
822  struct Group * Group = (struct Group *) mymalloc2("Group", sizeof(struct Group) * NgroupsExt);
823  memset(Group, 0, sizeof(Group[0]) * NgroupsExt);
824 
825  /* copy in the base properties */
826  /* at this point base group shall be sorted by MinID */
827  #pragma omp parallel for
828  for(i = 0; i < NgroupsExt; i ++) {
829  Group[i].base = base[i];
830  }
831  return Group;
832 }
833 
834 static void
835 fof_compile_catalogue(struct FOFGroups * fof, const int NgroupsExt, struct fof_particle_list * HaloLabel, MPI_Comm Comm)
836 {
837  int i, start, ThisTask;
838 
839  MPI_Comm_rank(Comm, &ThisTask);
840 
841  start = 0;
842  for(i = 0; i < NgroupsExt; i++)
843  {
844  /* find the first particle */
845  for(;start < PartManager->NumPart; start++) {
846  if(HaloLabel[start].MinID >= fof->Group[i].base.MinID) break;
847  }
848  /* add particles */
849  for(;start < PartManager->NumPart; start++) {
850  if(HaloLabel[start].MinID != fof->Group[i].base.MinID) {
851  break;
852  }
853  add_particle_to_group(&fof->Group[i], HaloLabel[start].Pindex, ThisTask);
854  }
855  }
856 
857  /* collect global properties */
858  fof_reduce_groups(fof->Group, NgroupsExt, sizeof(fof->Group[0]), fof_reduce_group, Comm);
859 
860  /* count Groups and number of particles hosted by me */
861  fof->Ngroups = 0;
862  int64_t Nids = 0;
863  for(i = 0; i < NgroupsExt; i ++) {
864  if(fof->Group[i].base.MinIDTask != ThisTask) continue;
865 
866  fof->Ngroups++;
867  Nids += fof->Group[i].base.Length;
868 
869  if(fof->Group[i].base.Length != fof->Group[i].Length) {
870  /* These two shall be consistent */
871  endrun(3333, "i=%d Group base Length %d != Group Length %d\n", i, fof->Group[i].base.Length, fof->Group[i].Length);
872  }
873  }
874 
876 
877  int64_t TotNids;
878  sumup_large_ints(1, &fof->Ngroups, &fof->TotNgroups);
879  MPI_Allreduce(&Nids, &TotNids, 1, MPI_INT64, MPI_SUM, Comm);
880 
881  /* report some statistics */
882  int largestloc_tot = 0;
883  double largestmass_tot= 0;
884  if(fof->TotNgroups > 0)
885  {
886  double largestmass = 0;
887  int largestlength = 0;
888 
889  for(i = 0; i < NgroupsExt; i++)
890  if(fof->Group[i].Length > largestlength) {
891  largestlength = fof->Group[i].Length;
892  largestmass = fof->Group[i].Mass;
893  }
894  MPI_Allreduce(&largestlength, &largestloc_tot, 1, MPI_INT, MPI_MAX, Comm);
895  MPI_Allreduce(&largestmass, &largestmass_tot, 1, MPI_DOUBLE, MPI_MAX, Comm);
896  }
897 
898  message(0, "Total number of groups with at least %d particles: %ld\n", fof_params.FOFHaloMinLength, fof->TotNgroups);
899  if(fof->TotNgroups > 0)
900  {
901  message(0, "Largest group has %d particles, mass %g.\n", largestloc_tot, largestmass_tot);
902  message(0, "Total number of particles in groups: %012ld\n", TotNids);
903  }
904 }
905 
906 
907 static void fof_reduce_groups(
908  void * groups,
909  int nmemb,
910  size_t elsize,
911  void (*reduce_group)(void * gdst, void * gsrc), MPI_Comm Comm)
912 {
913 
914  int NTask, ThisTask;
915  MPI_Comm_size(Comm, &NTask);
916  MPI_Comm_rank(Comm, &ThisTask);
917  /* slangs:
918  * prime: groups hosted by ThisTask
919  * ghosts: groups that spans into ThisTask but not hosted by ThisTask;
920  * part of the local catalogue
921  * images: ghosts that are sent from another rank.
922  * images are reduced to prime, then the prime attributes
923  * are copied to images, and sent back to the ghosts.
924  *
925  * in the begining, prime and ghosts contains local group attributes.
926  * in the end, prime and ghosts all contain full group attributes.
927  **/
928  int * Send_count = ta_malloc("Send_count", int, NTask);
929  int * Recv_count = ta_malloc("Recv_count", int, NTask);
930 
931  void * images = NULL;
932  void * ghosts = NULL;
933  int i;
934  int start;
935 
936  MPI_Datatype dtype;
937 
938  MPI_Type_contiguous(elsize, MPI_BYTE, &dtype);
939  MPI_Type_commit(&dtype);
940 
941  /*Set global data for the comparison*/
943  /* local groups will be moved to the beginning, we skip them with offset */
944  qsort_openmp(groups, nmemb, elsize, fof_compare_Group_MinIDTask);
945  /* count how many we have of each task */
946  memset(Send_count, 0, sizeof(int) * NTask);
947 
948  for(i = 0; i < nmemb; i++) {
949  struct BaseGroup * gi = (struct BaseGroup *) (((char*) groups) + i * elsize);
950  Send_count[gi->MinIDTask]++;
951  }
952 
953  /* Skip local groups */
954  int Nmine = Send_count[ThisTask];
955  Send_count[ThisTask] = 0;
956 
957  MPI_Alltoall(Send_count, 1, MPI_INT, Recv_count, 1, MPI_INT, Comm);
958 
959  int nimport = 0;
960  for(i = 0; i < NTask; i ++) {
961  nimport += Recv_count[i];
962  }
963 
964  images = mymalloc("images", nimport * elsize);
965  ghosts = ((char*) groups) + elsize * Nmine;
966 
967  MPI_Alltoallv_smart(ghosts, Send_count, NULL, dtype,
968  images, Recv_count, NULL, dtype, Comm);
969 
970  for(i = 0; i < nimport; i++) {
971  struct BaseGroup * gi = (struct BaseGroup*) ((char*) images + i * elsize);
972  gi->OriginalIndex = i;
973  }
974 
975  /* sort the groups according to MinID */
976  qsort_openmp(groups, Nmine, elsize, fof_compare_Group_MinID);
977  qsort_openmp(images, nimport, elsize, fof_compare_Group_MinID);
978 
979  /* merge the imported ones with the local ones */
980  start = 0;
981  for(i = 0; i < Nmine; i++) {
982  for(;start < nimport; start++) {
983  struct BaseGroup * prime = (struct BaseGroup*) ((char*) groups + i * elsize);
984  struct BaseGroup * image = (struct BaseGroup*) ((char*) images + start * elsize);
985  if(image->MinID >= prime->MinID) {
986  break;
987  }
988  }
989  for(;start < nimport; start++) {
990  struct BaseGroup * prime = (struct BaseGroup*) ((char*) groups + i * elsize);
991  struct BaseGroup * image = (struct BaseGroup*) ((char*) images + start * elsize);
992  if(image->MinID != prime->MinID) {
993  break;
994  }
995  reduce_group(prime, image);
996  }
997  }
998 
999  /* update the images, such that they can be send back to the ghosts */
1000  start = 0;
1001  for(i = 0; i < Nmine; i++)
1002  {
1003  for(;start < nimport; start++) {
1004  struct BaseGroup * prime = (struct BaseGroup*) ((char*) groups + i * elsize);
1005  struct BaseGroup * image = (struct BaseGroup*) ((char*) images + start * elsize);
1006  if(image->MinID >= prime->MinID) {
1007  break;
1008  }
1009  }
1010  for(;start < nimport; start++) {
1011  struct BaseGroup * prime = (struct BaseGroup*) ((char*) groups + i * elsize);
1012  struct BaseGroup * image = (struct BaseGroup*) ((char*) images + start * elsize);
1013  if(image->MinID != prime->MinID) {
1014  break;
1015  }
1016  int save = image->OriginalIndex;
1017  memcpy(image, prime, elsize);
1018  image->OriginalIndex = save;
1019  }
1020  }
1021 
1022  /* reset the ordering of imported list, such that it can be properly returned */
1023  qsort_openmp(images, nimport, elsize, fof_compare_Group_OriginalIndex);
1024 
1025  for(i = 0; i < nimport; i++) {
1026  struct BaseGroup * gi = (struct BaseGroup*) ((char*) images + i * elsize);
1027  if(gi->MinIDTask != ThisTask) {
1028  abort();
1029  }
1030  }
1031  void * ghosts2 = mymalloc("TMP", nmemb * elsize);
1032 
1033  MPI_Alltoallv_smart(images, Recv_count, NULL, dtype,
1034  ghosts2, Send_count, NULL, dtype,
1035  Comm);
1036  for(i = 0; i < nmemb - Nmine; i ++) {
1037  struct BaseGroup * g1 = (struct BaseGroup*) ((char*) ghosts + i * elsize);
1038  struct BaseGroup * g2 = (struct BaseGroup*) ((char*) ghosts2 + i* elsize);
1039  if(g1->MinID != g2->MinID) {
1040  endrun(2, "g1 minID %lu, g2 minID %lu\n", g1->MinID, g2->MinID);
1041  }
1042  if(g1->MinIDTask != g2->MinIDTask) {
1043  endrun(2, "g1 minIDTask %d, g2 minIDTask %d\n", g1->MinIDTask, g2->MinIDTask);
1044  }
1045  }
1046  memcpy(ghosts, ghosts2, elsize * (nmemb - Nmine));
1047  myfree(ghosts2);
1048 
1049  myfree(images);
1050 
1051  MPI_Type_free(&dtype);
1052 
1053  /* At this point, each Group entry has the reduced attribute of the full group */
1054  /* And the local groups (MinIDTask == ThisTask) are placed at the begining of the list*/
1055  ta_free(Recv_count);
1056  ta_free(Send_count);
1057 }
1058 
1059 static void fof_radix_Group_TotalCountTaskDiffMinID(const void * a, void * radix, void * arg);
1060 static void fof_radix_Group_OriginalTaskMinID(const void * a, void * radix, void * arg);
1061 
1062 static void fof_assign_grnr(struct BaseGroup * base, const int NgroupsExt, MPI_Comm Comm)
1063 {
1064  int i, j, NTask, ThisTask;
1065  int64_t ngr;
1066  MPI_Comm_size(Comm, &NTask);
1067  MPI_Comm_rank(Comm, &ThisTask);
1068 
1069  #pragma omp parallel for
1070  for(i = 0; i < NgroupsExt; i++)
1071  {
1072  base[i].OriginalTask = ThisTask; /* original task */
1073  }
1074 
1075  mpsort_mpi(base, NgroupsExt, sizeof(base[0]),
1076  fof_radix_Group_TotalCountTaskDiffMinID, 24, NULL, Comm);
1077 
1078  /* assign group numbers
1079  * at this point, both Group are is sorted by length,
1080  * and the every time OriginalTask == MinIDTask, a list of ghost base is stored.
1081  * they shall get the same GrNr.
1082  * */
1083  ngr = 0;
1084  for(i = 0; i < NgroupsExt; i++)
1085  {
1086  if(base[i].OriginalTask == base[i].MinIDTask) {
1087  ngr++;
1088  }
1089  base[i].GrNr = ngr;
1090  }
1091 
1092  int64_t * ngra = ta_malloc("NGRA", int64_t, NTask);
1093 
1094  MPI_Allgather(&ngr, 1, MPI_INT64, ngra, 1, MPI_INT64, Comm);
1095 
1096  /* shift to the global grnr. */
1097  int64_t groffset = 0;
1098  #pragma omp parallel for reduction(+: groffset)
1099  for(j = 0; j < ThisTask; j++)
1100  groffset += ngra[j];
1101  #pragma omp parallel for
1102  for(i = 0; i < NgroupsExt; i++)
1103  base[i].GrNr += groffset;
1104 
1105  ta_free(ngra);
1106 
1107  /* bring the group list back into the original task, sorted by MinID */
1108  mpsort_mpi(base, NgroupsExt, sizeof(base[0]),
1109  fof_radix_Group_OriginalTaskMinID, 16, NULL, Comm);
1110 }
1111 
1112 void
1113 fof_save_groups(FOFGroups * fof, const char * OutputDir, const char * FOFFileBase, int num, Cosmology * CP, double atime, const double * MassTable, int MetalReturnOn, int BlackholeOn, MPI_Comm Comm)
1114 {
1115  fof_save_particles(fof, OutputDir, FOFFileBase, num, fof_params.FOFSaveParticles, CP, atime, MassTable, MetalReturnOn, BlackholeOn, Comm);
1116 }
1117 
1118 /* FIXME: these shall goto the private member of secondary tree walk */
1120  float *distance;
1121  float *hsml;
1122  int *npleft;
1124 };
1125 
1126 #define FOF_SECONDARY_GET_PRIV(tw) ((struct FOFSecondaryPriv *) (tw->priv))
1127 
1128 static void fof_secondary_copy(int place, TreeWalkQueryFOF * I, TreeWalk * tw) {
1129 
1130  I->Hsml = FOF_SECONDARY_GET_PRIV(tw)->hsml[place];
1131  I->MinID = FOF_SECONDARY_GET_PRIV(tw)->HaloLabel[place].MinID;
1132  I->MinIDTask = FOF_SECONDARY_GET_PRIV(tw)->HaloLabel[place].MinIDTask;
1133 }
1134 
1135 static int fof_secondary_haswork(int n, TreeWalk * tw) {
1136  if(P[n].IsGarbage || P[n].Swallowed)
1137  return 0;
1138  /* Exclude particles where we already found a neighbour*/
1139  if(FOF_SECONDARY_GET_PRIV(tw)->distance[n] < 0.5 * LARGE)
1140  return 0;
1141  return ((1 << P[n].Type) & fof_params.FOFSecondaryLinkTypes);
1142 }
1143 static void fof_secondary_reduce(int place, TreeWalkResultFOF * O, enum TreeWalkReduceMode mode, TreeWalk * tw) {
1144  if(O->Distance < FOF_SECONDARY_GET_PRIV(tw)->distance[place] && O->Distance >= 0 && O->Distance < 0.5 * LARGE)
1145  {
1146  FOF_SECONDARY_GET_PRIV(tw)->distance[place] = O->Distance;
1147  FOF_SECONDARY_GET_PRIV(tw)->HaloLabel[place].MinID = O->MinID;
1148  FOF_SECONDARY_GET_PRIV(tw)->HaloLabel[place].MinIDTask = O->MinIDTask;
1149  }
1150 }
1151 
1152 static void
1154  TreeWalkResultFOF * O,
1155  TreeWalkNgbIterFOF * iter,
1156  LocalTreeWalk * lv)
1157 {
1158  if(iter->base.other == -1) {
1159  O->Distance = LARGE;
1160  O->MinID = I->MinID;
1161  O->MinIDTask = I->MinIDTask;
1162  iter->base.Hsml = I->Hsml;
1165  return;
1166  }
1167  int other = iter->base.other;
1168  double r = iter->base.r;
1169  if(r < O->Distance)
1170  {
1171  O->Distance = r;
1172  O->MinID = FOF_SECONDARY_GET_PRIV(lv->tw)->HaloLabel[other].MinID;
1173  O->MinIDTask = FOF_SECONDARY_GET_PRIV(lv->tw)->HaloLabel[other].MinIDTask;
1174  }
1175  /* No need to search nodes at a greater distance
1176  * now that we have a neighbour.*/
1177  iter->base.Hsml = iter->base.r;
1178 }
1179 
1180 static void
1182 {
1183  /* More work needed: add this particle to the redo queue*/
1184  int tid = omp_get_thread_num();
1185 
1186  if(FOF_SECONDARY_GET_PRIV(tw)->distance[p] > 0.5 * LARGE)
1187  {
1188  if(FOF_SECONDARY_GET_PRIV(tw)->hsml[p] < 4 * fof_params.FOFHaloComovingLinkingLength) /* we only search out to a maximum distance */
1189  {
1190  /* need to redo this particle */
1191  FOF_SECONDARY_GET_PRIV(tw)->npleft[tid]++;
1192  FOF_SECONDARY_GET_PRIV(tw)->hsml[p] *= 2.0;
1193 /*
1194  if(iter >= MAXITER - 10)
1195  {
1196  endrun(1, "i=%d task=%d ID=%llu Hsml=%g pos=(%g|%g|%g)\n",
1197  p, ThisTask, P[p].ID, FOF_SECONDARY_GET_PRIV(tw)->hsml[p],
1198  P[p].Pos[0], P[p].Pos[1], P[p].Pos[2]);
1199  }
1200 */
1201  } else {
1202  FOF_SECONDARY_GET_PRIV(tw)->distance[p] = -1; /* we not continue to search for this particle */
1203  }
1204  }
1205 }
1206 
1207 static void fof_label_secondary(struct fof_particle_list * HaloLabel, ForceTree * tree)
1208 {
1209  int n;
1210 
1211  TreeWalk tw[1] = {{0}};
1212  tw->ev_label = "FOF_FIND_NEAREST";
1220  tw->type = TREEWALK_ALL;
1221  tw->query_type_elsize = sizeof(TreeWalkQueryFOF);
1222  tw->result_type_elsize = sizeof(TreeWalkResultFOF);
1223  tw->tree = tree;
1224  struct FOFSecondaryPriv priv[1];
1225 
1226  tw->priv = priv;
1227 
1228  message(0, "Start finding nearest dm-particle (presently allocated=%g MB)\n",
1229  mymalloc_usedbytes() / (1024.0 * 1024.0));
1230 
1231  FOF_SECONDARY_GET_PRIV(tw)->distance = (float *) mymalloc("FOF_SECONDARY->distance", sizeof(float) * PartManager->NumPart);
1232  FOF_SECONDARY_GET_PRIV(tw)->hsml = (float *) mymalloc("FOF_SECONDARY->hsml", sizeof(float) * PartManager->NumPart);
1233  FOF_SECONDARY_GET_PRIV(tw)->HaloLabel = HaloLabel;
1234 
1235  #pragma omp parallel for
1236  for(n = 0; n < PartManager->NumPart; n++)
1237  {
1238  FOF_SECONDARY_GET_PRIV(tw)->distance[n] = LARGE;
1240 
1241  if((P[n].Type == 0 || P[n].Type == 4 || P[n].Type == 5) && FOF_SECONDARY_GET_PRIV(tw)->hsml[n] < 0.5 * P[n].Hsml) {
1242  /* use gas sml as a hint (faster convergence than 0.1 fof_params.FOFHaloComovingLinkingLength at high-z */
1243  FOF_SECONDARY_GET_PRIV(tw)->hsml[n] = 0.5 * P[n].Hsml;
1244  }
1245  }
1246 
1247  int64_t ntot;
1248 
1249  /* we will repeat the whole thing for those particles where we didn't find enough neighbours */
1250 
1251  message(0, "fof-nearest iteration started\n");
1252  int NumThreads = omp_get_max_threads();
1253  FOF_SECONDARY_GET_PRIV(tw)->npleft = ta_malloc("NPLeft", int, NumThreads);
1254 
1255  do
1256  {
1257  memset(FOF_SECONDARY_GET_PRIV(tw)->npleft, 0, sizeof(int) * NumThreads);
1258 
1259  treewalk_run(tw, NULL, PartManager->NumPart);
1260 
1261  for(n = 1; n < NumThreads; n++) {
1262  FOF_SECONDARY_GET_PRIV(tw)->npleft[0] += FOF_SECONDARY_GET_PRIV(tw)->npleft[n];
1263  }
1264  sumup_large_ints(1, &FOF_SECONDARY_GET_PRIV(tw)->npleft[0], &ntot);
1265 
1266  if(ntot < 0 || (ntot > 0 && tw->Niteration > MAXITER))
1267  endrun(1159, "Failed to converge in fof-nearest: ntot %ld", ntot);
1268  }
1269  while(ntot > 0);
1270 
1274 }
1275 
1276 /*
1277  * Deal with seeding of particles At each FOF stage,
1278  * if seed_index is >= 0, then that particle on seed_task
1279  * will be converted to a seed.
1280  *
1281  * */
1282 static int cmp_seed_task(const void * c1, const void * c2) {
1283  const struct Group * g1 = (const struct Group *) c1;
1284  const struct Group * g2 = (const struct Group *) c2;
1285 
1286  return g1->seed_task - g2->seed_task;
1287 }
1288 static void fof_seed_make_one(struct Group * g, int ThisTask, const double atime);
1289 
1290 void fof_seed(FOFGroups * fof, ActiveParticles * act, double atime, MPI_Comm Comm)
1291 {
1292  int i, j, n, ntot;
1293 
1294  int NTask;
1295  MPI_Comm_size(Comm, &NTask);
1296 
1297  char * Marked = (char *) mymalloc2("SeedMark", fof->Ngroups);
1298 
1299  int Nexport = 0;
1300  #pragma omp parallel for reduction(+:Nexport)
1301  for(i = 0; i < fof->Ngroups; i++)
1302  {
1303  Marked[i] =
1305  && (fof->Group[i].MassType[4] >= fof_params.MinMStarForNewSeed)
1306  && (fof->Group[i].LenType[5] == 0)
1307  && (fof->Group[i].seed_index >= 0);
1308 
1309  if(Marked[i]) Nexport ++;
1310  }
1311  struct Group * ExportGroups = (struct Group *) mymalloc("Export", sizeof(fof->Group[0]) * Nexport);
1312  j = 0;
1313  for(i = 0; i < fof->Ngroups; i ++) {
1314  if(Marked[i]) {
1315  ExportGroups[j] = fof->Group[i];
1316  j++;
1317  }
1318  }
1319  myfree(Marked);
1320 
1321  qsort_openmp(ExportGroups, Nexport, sizeof(ExportGroups[0]), cmp_seed_task);
1322 
1323  int * Send_count = ta_malloc("Send_count", int, NTask);
1324  int * Recv_count = ta_malloc("Recv_count", int, NTask);
1325 
1326  memset(Send_count, 0, NTask * sizeof(int));
1327  for(i = 0; i < Nexport; i++) {
1328  Send_count[ExportGroups[i].seed_task]++;
1329  }
1330 
1331  MPI_Alltoall(Send_count, 1, MPI_INT, Recv_count, 1, MPI_INT, Comm);
1332 
1333  int Nimport = 0;
1334 
1335  for(j = 0; j < NTask; j++)
1336  {
1337  Nimport += Recv_count[j];
1338  }
1339 
1340  struct Group * ImportGroups = (struct Group *)
1341  mymalloc2("ImportGroups", Nimport * sizeof(struct Group));
1342 
1343  MPI_Alltoallv_smart(ExportGroups, Send_count, NULL, MPI_TYPE_GROUP,
1344  ImportGroups, Recv_count, NULL, MPI_TYPE_GROUP,
1345  Comm);
1346 
1347  myfree(ExportGroups);
1348  ta_free(Recv_count);
1349  ta_free(Send_count);
1350 
1351  MPI_Allreduce(&Nimport, &ntot, 1, MPI_INT, MPI_SUM, Comm);
1352 
1353  message(0, "Making %d new black hole particles.\n", ntot);
1354 
1355  /* Do we have enough black hole slots to create this many black holes?
1356  * If not, allocate more slots. */
1357  if(Nimport + SlotsManager->info[5].size > SlotsManager->info[5].maxsize)
1358  {
1359  int *ActiveParticle_tmp=NULL;
1360  /* This is only called on a PM step, so the condition should never be true*/
1361  if(act->ActiveParticle) {
1362  ActiveParticle_tmp = (int *) mymalloc2("ActiveParticle_tmp", act->NumActiveParticle * sizeof(int));
1363  memmove(ActiveParticle_tmp, act->ActiveParticle, act->NumActiveParticle * sizeof(int));
1364  myfree(act->ActiveParticle);
1365  }
1366 
1367  /*Now we can extend the slots! */
1368  int64_t atleast[6];
1369  int64_t i;
1370  for(i = 0; i < 6; i++)
1371  atleast[i] = SlotsManager->info[i].maxsize;
1372  atleast[5] += ntot*1.1;
1373  slots_reserve(1, atleast, SlotsManager);
1374 
1375  /*And now we need our memory back in the right place*/
1376  if(ActiveParticle_tmp) {
1377  act->ActiveParticle = (int *) mymalloc("ActiveParticle", sizeof(int)*(act->NumActiveParticle + PartManager->MaxPart - PartManager->NumPart));
1378  memmove(act->ActiveParticle, ActiveParticle_tmp, act->NumActiveParticle * sizeof(int));
1379  myfree(ActiveParticle_tmp);
1380  }
1381  }
1382 
1383  int ThisTask;
1384  MPI_Comm_rank(Comm, &ThisTask);
1385 
1386  for(n = 0; n < Nimport; n++)
1387  {
1388  fof_seed_make_one(&ImportGroups[n], ThisTask, atime);
1389  }
1390 
1391  myfree(ImportGroups);
1392 
1393  walltime_measure("/FOF/Seeding");
1394 }
1395 
1396 static void fof_seed_make_one(struct Group * g, int ThisTask, const double atime) {
1397  if(g->seed_task != ThisTask) {
1398  endrun(7771, "Seed does not belong to the right task");
1399  }
1400  int index = g->seed_index;
1401  blackhole_make_one(index, atime);
1402 }
1403 
1404 static int fof_compare_HaloLabel_MinID(const void *a, const void *b)
1405 {
1406  if(((struct fof_particle_list *) a)->MinID < ((struct fof_particle_list *) b)->MinID)
1407  return -1;
1408 
1409  if(((struct fof_particle_list *) a)->MinID > ((struct fof_particle_list *) b)->MinID)
1410  return +1;
1411 
1412  return 0;
1413 }
1414 
1415 static int fof_compare_Group_MinID(const void *a, const void *b)
1416 
1417 {
1418  if(((struct BaseGroup *) a)->MinID < ((struct BaseGroup *) b)->MinID)
1419  return -1;
1420 
1421  if(((struct BaseGroup *) a)->MinID > ((struct BaseGroup *) b)->MinID)
1422  return +1;
1423 
1424  return 0;
1425 }
1426 
1427 static int fof_compare_Group_MinIDTask(const void *a, const void *b)
1428 {
1429  const struct BaseGroup * p1 = (const struct BaseGroup *) a;
1430  const struct BaseGroup * p2 = (const struct BaseGroup *) b;
1431  int t1 = p1->MinIDTask;
1432  int t2 = p2->MinIDTask;
1433  if(t1 == _fof_compare_Group_MinIDTask_ThisTask) t1 = -1;
1434  if(t2 == _fof_compare_Group_MinIDTask_ThisTask) t2 = -1;
1435 
1436  if(t1 < t2) return -1;
1437  if(t1 > t2) return +1;
1438  return 0;
1439 
1440 }
1441 
1442 static int fof_compare_Group_OriginalIndex(const void *a, const void *b)
1443 
1444 {
1445  return ((struct BaseGroup *) a)->OriginalIndex - ((struct BaseGroup *) b)->OriginalIndex;
1446 }
1447 
1448 static void fof_radix_Group_TotalCountTaskDiffMinID(const void * a, void * radix, void * arg) {
1449  uint64_t * u = (uint64_t *) radix;
1450  struct BaseGroup * f = (struct BaseGroup *) a;
1451  u[0] = labs(f->OriginalTask - f->MinIDTask);
1452  u[1] = f->MinID;
1453  u[2] = UINT64_MAX - (f->Length);
1454 }
1455 
1456 static void fof_radix_Group_OriginalTaskMinID(const void * a, void * radix, void * arg) {
1457  uint64_t * u = (uint64_t *) radix;
1458  struct BaseGroup * f = (struct BaseGroup *) a;
1459  u[0] = f->MinID;
1460  u[1] = f->OriginalTask;
1461 }
void blackhole_make_one(int index, const double atime)
Definition: blackhole.c:1576
static void crossproduct(const double v1[3], const double v2[3], double out[3])
Definition: densitykernel.h:63
void message(int where, const char *fmt,...)
Definition: endrun.c:175
void endrun(int where, const char *fmt,...)
Definition: endrun.c:147
static int fof_compare_Group_MinID(const void *a, const void *b)
Definition: fof.c:1415
static void update_root(int i, const int r, int *Head)
Definition: fof.c:314
static void fof_compile_catalogue(FOFGroups *fof, const int NgroupsExt, struct fof_particle_list *HaloLabel, MPI_Comm Comm)
Definition: fof.c:835
static struct Group * fof_alloc_group(const struct BaseGroup *base, const int NgroupsExt)
Definition: fof.c:819
static int fof_primary_haswork(int n, TreeWalk *tw)
Definition: fof.c:357
#define FOF_SECONDARY_GET_PRIV(tw)
Definition: fof.c:1126
static int fof_compare_Group_OriginalIndex(const void *a, const void *b)
Definition: fof.c:1442
void fof_save_groups(FOFGroups *fof, const char *OutputDir, const char *FOFFileBase, int num, Cosmology *CP, double atime, const double *MassTable, int MetalReturnOn, int BlackholeOn, MPI_Comm Comm)
Definition: fof.c:1113
static int fof_secondary_haswork(int n, TreeWalk *tw)
Definition: fof.c:1135
static int fof_compare_HaloLabel_MinID(const void *a, const void *b)
Definition: fof.c:1404
static double fof_periodic(double x, double BoxSize)
Definition: fof.c:71
static void fof_radix_Group_OriginalTaskMinID(const void *a, void *radix, void *arg)
Definition: fof.c:1456
static int fof_compile_base(struct BaseGroup *base, int NgroupsExt, struct fof_particle_list *HaloLabel, MPI_Comm Comm)
Definition: fof.c:761
static void fof_secondary_postprocess(int p, TreeWalk *tw)
Definition: fof.c:1181
static void fof_secondary_copy(int place, TreeWalkQueryFOF *I, TreeWalk *tw)
Definition: fof.c:1128
static void fof_radix_Group_TotalCountTaskDiffMinID(const void *a, void *radix, void *arg)
Definition: fof.c:1448
void set_fof_params(ParameterSet *ps)
Definition: fof.c:50
static void fof_seed_make_one(struct Group *g, int ThisTask, const double atime)
Definition: fof.c:1396
static void add_particle_to_group(struct Group *gdst, int i, int ThisTask)
Definition: fof.c:634
static void fof_finish_group_properties(FOFGroups *fof, double BoxSize)
Definition: fof.c:709
static void fof_primary_ngbiter(TreeWalkQueryFOF *I, TreeWalkResultFOF *O, TreeWalkNgbIterFOF *iter, LocalTreeWalk *lv)
Definition: fof.c:546
static void fof_secondary_reduce(int place, TreeWalkResultFOF *O, enum TreeWalkReduceMode mode, TreeWalk *tw)
Definition: fof.c:1143
void fof_init(double DMMeanSeparation)
Definition: fof.c:66
static int HEADl(int stop, int i, const int *const Head)
Definition: fof.c:291
static int HEAD(int i, const int *const Head)
Definition: fof.c:333
#define MAXITER
Definition: fof.c:35
static int cmp_seed_task(const void *c1, const void *c2)
Definition: fof.c:1282
static int _fof_compare_Group_MinIDTask_ThisTask
Definition: fof.c:99
static int fof_compare_Group_MinIDTask(const void *a, const void *b)
Definition: fof.c:1427
void fof_finish(FOFGroups *fof)
Definition: fof.c:257
static void fof_reduce_group(void *pdst, void *psrc)
Definition: fof.c:591
static void fof_secondary_ngbiter(TreeWalkQueryFOF *I, TreeWalkResultFOF *O, TreeWalkNgbIterFOF *iter, LocalTreeWalk *lv)
Definition: fof.c:1153
static void fof_primary_copy(int place, TreeWalkQueryFOF *I, TreeWalk *tw)
Definition: fof.c:342
static double fof_periodic_wrap(double x, double BoxSize)
Definition: fof.c:81
#define LARGE
Definition: fof.c:34
void fof_seed(FOFGroups *fof, ActiveParticles *act, double atime, MPI_Comm Comm)
Definition: fof.c:1290
static MPI_Datatype MPI_TYPE_GROUP
Definition: fof.c:142
static void fof_label_secondary(struct fof_particle_list *HaloLabel, ForceTree *tree)
Definition: fof.c:1207
void fof_label_primary(struct fof_particle_list *HaloLabel, ForceTree *tree, MPI_Comm Comm)
Definition: fof.c:369
static void fofp_merge(int target, int other, TreeWalk *tw)
Definition: fof.c:483
static void fof_reduce_base_group(void *pdst, void *psrc)
Definition: fof.c:584
#define FOF_PRIMARY_GET_PRIV(tw)
Definition: fof.c:276
static void fof_assign_grnr(struct BaseGroup *base, const int NgroupsExt, MPI_Comm Comm)
Definition: fof.c:1062
static void fof_reduce_groups(void *groups, int nmemb, size_t elsize, void(*reduce_group)(void *gdst, void *gsrc), MPI_Comm Comm)
Definition: fof.c:907
FOFGroups fof_fof(DomainDecomp *ddecomp, const int StoreGrNr, MPI_Comm Comm)
Definition: fof.c:151
struct FOFParams fof_params
void fof_save_particles(FOFGroups *fof, const char *OutputDir, const char *FOFFileBase, int num, int SaveParticles, Cosmology *CP, double atime, const double *MassTable, int MetalReturnOn, int BlackholeOn, MPI_Comm Comm)
Definition: fofpetaio.c:32
void force_tree_rebuild_mask(ForceTree *tree, DomainDecomp *ddecomp, int mask, const int HybridNuGrav, const char *EmergencyOutputDir)
Definition: forcetree.c:161
void force_tree_free(ForceTree *tree)
Definition: forcetree.c:1409
#define mpsort_mpi(base, nmemb, elsize, radix, rsize, arg, comm)
Definition: mpsort.h:26
#define mymalloc(name, size)
Definition: mymalloc.h:15
#define ta_malloc(name, type, nele)
Definition: mymalloc.h:25
#define ta_free(p)
Definition: mymalloc.h:28
#define mymalloc2(name, size)
Definition: mymalloc.h:16
#define mymalloc_usedbytes()
Definition: mymalloc.h:32
#define myfree(x)
Definition: mymalloc.h:19
double param_get_double(ParameterSet *ps, const char *name)
Definition: paramset.c:336
int param_get_int(ParameterSet *ps, const char *name)
Definition: paramset.c:368
struct part_manager_type PartManager[1]
Definition: partmanager.c:11
#define P
Definition: partmanager.h:88
static Cosmology * CP
Definition: power.c:27
size_t slots_reserve(int where, int64_t atleast[6], struct slots_manager_type *sman)
Definition: slotsmanager.c:475
struct slots_manager_type SlotsManager[1]
Definition: slotsmanager.c:7
#define BHP(i)
Definition: slotsmanager.h:125
#define SPHP(i)
Definition: slotsmanager.h:124
#define STARP(i)
Definition: slotsmanager.h:126
#define NMETALS
Definition: slotsmanager.h:60
void free_spinlocks(struct SpinLocks *spin)
Definition: spinlocks.c:70
void lock_spinlock(int i, struct SpinLocks *spin)
Definition: spinlocks.c:23
struct SpinLocks * init_spinlocks(int NumLock)
Definition: spinlocks.c:49
static struct SpinLocks spin
Definition: spinlocks.c:21
void unlock_spinlock(int i, struct SpinLocks *spin)
Definition: spinlocks.c:30
int * ActiveParticle
Definition: timestep.h:13
int64_t NumActiveParticle
Definition: timestep.h:12
Definition: fof.h:13
MyIDType MinID
Definition: fof.h:18
int Length
Definition: fof.h:16
float FirstPos[3]
Definition: fof.h:22
int OriginalTask
Definition: fof.h:14
int GrNr
Definition: fof.h:17
int OriginalIndex
Definition: fof.h:15
int MinIDTask
Definition: fof.h:19
Definition: fof.h:62
struct Group * Group
Definition: fof.h:63
int Ngroups
Definition: fof.h:66
int64_t TotNgroups
Definition: fof.h:67
Definition: fof.c:38
double FOFHaloComovingLinkingLength
Definition: fof.c:43
double MinFoFMassForNewSeed
Definition: fof.c:40
int FOFPrimaryLinkTypes
Definition: fof.c:45
int FOFSecondaryLinkTypes
Definition: fof.c:46
int FOFHaloMinLength
Definition: fof.c:44
int FOFSaveParticles
Definition: fof.c:39
double MinMStarForNewSeed
Definition: fof.c:41
double FOFHaloLinkingLength
Definition: fof.c:42
int * Head
Definition: fof.c:270
char * PrimaryActive
Definition: fof.c:272
struct SpinLocks * spin
Definition: fof.c:271
MyIDType * OldMinID
Definition: fof.c:273
struct fof_particle_list * HaloLabel
Definition: fof.c:274
float * hsml
Definition: fof.c:1121
struct fof_particle_list * HaloLabel
Definition: fof.c:1123
float * distance
Definition: fof.c:1120
int * npleft
Definition: fof.c:1122
int firstnode
Definition: forcetree.h:84
Definition: fof.h:26
double Sfr
Definition: fof.h:40
struct BaseGroup base
Definition: fof.h:27
double MaxDens
Definition: fof.h:54
double GasMetalMass
Definition: fof.h:44
float GasMetalElemMass[NMETALS]
Definition: fof.h:47
double StellarMetalMass
Definition: fof.h:45
double Imom[3][3]
Definition: fof.h:37
double BH_Mdot
Definition: fof.h:53
int seed_index
Definition: fof.h:56
double CM[3]
Definition: fof.h:34
int Length
Definition: fof.h:28
double Jmom[3]
Definition: fof.h:38
double MassType[6]
Definition: fof.h:30
float MassHeIonized
Definition: fof.h:50
double BH_Mass
Definition: fof.h:52
float StellarMetalElemMass[NMETALS]
Definition: fof.h:46
int seed_task
Definition: fof.h:57
int LenType[6]
Definition: fof.h:29
double Vel[3]
Definition: fof.h:35
double Mass
Definition: fof.h:31
TreeWalk * tw
Definition: treewalk.h:47
enum NgbTreeFindSymmetric symmetric
Definition: treewalk.h:37
TreeWalkNgbIterBase base
Definition: fof.c:138
int NodeList[NODELISTLENGTH]
Definition: treewalk.h:27
MyIDType MinID
Definition: fof.c:124
TreeWalkQueryBase base
Definition: fof.c:122
int MinIDTask
Definition: fof.c:125
MyFloat Hsml
Definition: fof.c:123
MyFloat Distance
Definition: fof.c:131
MyIDType MinID
Definition: fof.c:132
TreeWalkResultBase base
Definition: fof.c:130
int MinIDTask
Definition: fof.c:133
int64_t Niteration
Definition: treewalk.h:142
enum TreeWalkType type
Definition: treewalk.h:93
void * priv
Definition: treewalk.h:85
TreeWalkHasWorkFunction haswork
Definition: treewalk.h:100
size_t ngbiter_type_elsize
Definition: treewalk.h:97
const ForceTree * tree
Definition: treewalk.h:88
TreeWalkProcessFunction postprocess
Definition: treewalk.h:104
TreeWalkNgbIterFunction ngbiter
Definition: treewalk.h:103
TreeWalkReduceResultFunction reduce
Definition: treewalk.h:102
const char * ev_label
Definition: treewalk.h:91
TreeWalkVisitFunction visit
Definition: treewalk.h:99
size_t result_type_elsize
Definition: treewalk.h:96
TreeWalkFillQueryFunction fill
Definition: treewalk.h:101
size_t query_type_elsize
Definition: treewalk.h:95
int Pindex
Definition: fof.c:94
int MinIDTask
Definition: fof.c:93
MyIDType MinID
Definition: fof.c:92
int64_t maxsize
Definition: slotsmanager.h:11
int64_t size
Definition: slotsmanager.h:12
struct slot_info info[6]
Definition: slotsmanager.h:112
int MPI_Alltoallv_smart(void *sendbuf, int *sendcnts, int *sdispls, MPI_Datatype sendtype, void *recvbuf, int *recvcnts, int *rdispls, MPI_Datatype recvtype, MPI_Comm comm)
Definition: system.c:278
void sumup_large_ints(int n, int *src, int64_t *res)
Definition: system.c:192
double second(void)
Definition: system.c:83
#define MPIU_Barrier(comm)
Definition: system.h:103
#define MPI_INT64
Definition: system.h:12
int ThisTask
Definition: test_exchange.c:23
int NTask
Definition: test_exchange.c:23
#define qsort_openmp
Definition: test_exchange.c:14
int treewalk_visit_ngbiter(TreeWalkQueryBase *I, TreeWalkResultBase *O, LocalTreeWalk *lv)
Definition: treewalk.c:924
int treewalk_visit_nolist_ngbiter(TreeWalkQueryBase *I, TreeWalkResultBase *O, LocalTreeWalk *lv)
Definition: treewalk.c:1143
void treewalk_run(TreeWalk *tw, int *active_set, size_t size)
Definition: treewalk.c:619
int(* TreeWalkVisitFunction)(TreeWalkQueryBase *input, TreeWalkResultBase *output, LocalTreeWalk *lv)
Definition: treewalk.h:68
void(* TreeWalkReduceResultFunction)(const int j, TreeWalkResultBase *result, const enum TreeWalkReduceMode mode, TreeWalk *tw)
Definition: treewalk.h:76
@ TREEWALK_ALL
Definition: treewalk.h:80
TreeWalkReduceMode
Definition: treewalk.h:15
void(* TreeWalkProcessFunction)(const int i, TreeWalk *tw)
Definition: treewalk.h:73
void(* TreeWalkNgbIterFunction)(TreeWalkQueryBase *input, TreeWalkResultBase *output, TreeWalkNgbIterBase *iter, LocalTreeWalk *lv)
Definition: treewalk.h:70
void(* TreeWalkFillQueryFunction)(const int j, TreeWalkQueryBase *query, TreeWalk *tw)
Definition: treewalk.h:75
@ NGB_TREEFIND_ASYMMETRIC
Definition: treewalk.h:12
uint64_t MyIDType
Definition: types.h:10
LOW_PRECISION MyFloat
Definition: types.h:19
#define walltime_measure(name)
Definition: walltime.h:8
int winds_is_particle_decoupled(int i)
Definition: winds.c:124