MP-Gadget  5.0.1.dev1-76bc7d4726-dirty
timestep.c
Go to the documentation of this file.
1 #include <mpi.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <math.h>
6 #include <omp.h>
7 
8 #include "utils.h"
9 #include "physconst.h"
10 #include "timebinmgr.h"
11 #include "domain.h"
12 #include "timefac.h"
13 #include "cosmology.h"
14 #include "checkpoint.h"
15 #include "slotsmanager.h"
16 #include "partmanager.h"
17 #include "hydra.h"
18 #include "walltime.h"
19 #include "timestep.h"
20 #include "gravity.h"
21 
26 static struct timestep_params
27 {
28  /* adjusts accuracy of time-integration */
29 
33  int ForceEqualTimesteps; /*If true, all timesteps have the same timestep, the smallest allowed.*/
34  double MinSizeTimestep,
45  double MaxGasVel; /* Limit on Gas velocity */
46  double CourantFac;
48 
49 /*Set the parameters of the hydro module*/
50 void
52 {
53  int ThisTask;
54  MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask);
55  if(ThisTask == 0) {
56  TimestepParams.ErrTolIntAccuracy = param_get_double(ps, "ErrTolIntAccuracy");
57  TimestepParams.MaxGasVel = param_get_double(ps, "MaxGasVel");
58  TimestepParams.MaxSizeTimestep = param_get_double(ps, "MaxSizeTimestep");
59 
60  TimestepParams.MinSizeTimestep = param_get_double(ps, "MinSizeTimestep");
61  TimestepParams.ForceEqualTimesteps = param_get_int(ps, "ForceEqualTimesteps");
62  TimestepParams.MaxRMSDisplacementFac = param_get_double(ps, "MaxRMSDisplacementFac");
63  TimestepParams.CourantFac = param_get_double(ps, "CourantFac");
64  }
65  MPI_Bcast(&TimestepParams, sizeof(struct timestep_params), MPI_BYTE, 0, MPI_COMM_WORLD);
66 }
67 
68 static inline int get_active_particle(const ActiveParticles * act, int pa)
69 {
70  if(act->ActiveParticle)
71  return act->ActiveParticle[pa];
72  else
73  return pa;
74 }
75 
76 static int
77 timestep_eh_slots_fork(EIBase * event, void * userdata)
78 {
79  /*Update the active particle list:
80  * if the parent is active the child should also be active.
81  * Stars must always be active on formation, but
82  * BHs need not be: a halo can be seeded when the particle in question is inactive.*/
83 
84  EISlotsFork * ev = (EISlotsFork *) event;
85 
86  int parent = ev->parent;
87  int child = ev->child;
88  ActiveParticles * act = (ActiveParticles *) userdata;
89 
90  if(is_timebin_active(P[parent].TimeBin, P[parent].Ti_drift)) {
91  int64_t childactive = atomic_fetch_and_add_64(&act->NumActiveParticle, 1);
92  if(act->ActiveParticle) {
93  /* This should never happen because we allocate as much space for active particles as we have space
94  * for particles, but just in case*/
95  if(childactive >= act->MaxActiveParticle)
96  endrun(5, "Tried to add %ld active particles, more than %ld allowed\n", childactive, act->MaxActiveParticle);
97  act->ActiveParticle[childactive] = child;
98  }
99  }
100  return 0;
101 }
102 
103 /* Enum for keeping track of which
104  * timestep criterion is limiting each particles'
105  * timestep evolution*/
107 {
108  TI_ACCEL = 0,
111  TI_NEIGH = 3,
112  TI_HSML = 4,
113 };
114 
115 static inttime_t get_timestep_ti(const int p, const inttime_t dti_max, const inttime_t Ti_Current, const double atime, const double hubble, enum TimeStepType * titype);
116 static int get_timestep_bin(inttime_t dti);
117 static void do_the_short_range_kick(int i, double dt_entr, double Fgravkick, double Fhydrokick, const double atime, const double MinEgySpec);
118 /* Get the current PM (global) timestep.*/
119 static inttime_t get_PM_timestep_ti(const DriftKickTimes * const times, const double atime, const Cosmology * CP, const int FastParticleType, const double asmth);
120 
121 /*Initialise the integer timeline*/
122 inttime_t
123 init_timebins(double TimeInit)
124 {
125  inttime_t Ti_Current = ti_from_loga(log(TimeInit));
126  /*Enforce Ti_Current is initially even*/
127  if(Ti_Current % 2 == 1)
128  Ti_Current++;
129  message(0, "Initial TimeStep at TimeInit %g Ti_Current = %d \n", TimeInit, Ti_Current);
130  return Ti_Current;
131 }
132 
134 {
135  DriftKickTimes times = {0};
136  times.Ti_Current = Ti_Current;
137  int i;
138  for(i = 0; i <= TIMEBINS; i++) {
139  times.Ti_kick[i] = times.Ti_Current;
140  times.Ti_lastactivedrift[i] = times.Ti_Current;
141  }
142  /* this makes sure the first step is a PM step. */
143  times.PM_length = 0;
144  times.PM_kick = times.Ti_Current;
145  times.PM_start = times.Ti_Current;
146  return times;
147 }
148 
149 int is_timebin_active(int i, inttime_t current) {
150  /*Bin 0 is always active and at time 0 all bins are active*/
151  if(i <= 0 || current <= 0)
152  return 1;
153  if(current % dti_from_timebin(i) == 0)
154  return 1;
155  return 0;
156 }
157 
158 /*Report whether the current timestep is the end of the PM timestep*/
159 int
160 is_PM_timestep(const DriftKickTimes * const times)
161 {
162  if(times->Ti_Current > times->PM_start + times->PM_length)
163  endrun(12, "Passed end of PM step! ti=%d, PM = %d + %d\n",times->Ti_Current, times->PM_start, times->PM_length);
164  return times->Ti_Current == times->PM_start + times->PM_length;
165 }
166 
167 double
168 get_atime(const inttime_t Ti_Current) {
169  return exp(loga_from_ti(Ti_Current));
170 }
171 
172 /* This function assigns new short-range timesteps to particles.
173  * It will also shrink the PM timestep to the longest short-range timestep.
174  * Stores the maximum and minimum timesteps in the DriftKickTimes structure.*/
175 int
176 find_timesteps(const ActiveParticles * act, DriftKickTimes * times, const double atime, int FastParticleType, const Cosmology * CP, const double asmth, const int isFirstTimeStep)
177 {
178  int pa;
179  inttime_t dti_min = TIMEBASE;
180 
181  walltime_measure("/Misc");
182 
183  /*Update the PM timestep size */
184  const int isPM = is_PM_timestep(times);
185  inttime_t dti_max = times->PM_length;
186 
187  if(isPM) {
188  dti_max = get_PM_timestep_ti(times, atime, CP, FastParticleType, asmth);
189  times->PM_length = dti_max;
190  times->PM_start = times->PM_kick;
191  }
192 
193  const double hubble = hubble_function(CP, atime);
194  /* Now assign new timesteps and kick */
196  int i;
197  #pragma omp parallel for reduction(min:dti_min)
198  for(i = 0; i < PartManager->NumPart; i++)
199  {
200  enum TimeStepType titype;
201  /* Because we don't GC on short timesteps, there can be garbage here.
202  * Avoid making it active. */
203  if(P[i].IsGarbage || P[i].Swallowed)
204  continue;
205  inttime_t dti = get_timestep_ti(i, dti_max, times->Ti_Current, atime, hubble, &titype);
206  if(dti < dti_min)
207  dti_min = dti;
208  }
209  MPI_Allreduce(MPI_IN_PLACE, &dti_min, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
210  }
211 
212  int64_t ntiaccel=0, nticourant=0, ntiaccrete=0, ntineighbour=0, ntihsml=0;
213  int badstepsizecount = 0;
214  int mTimeBin = TIMEBINS, maxTimeBin = 0;
215  #pragma omp parallel for reduction(min: mTimeBin) reduction(+: badstepsizecount, ntiaccel, nticourant, ntiaccrete, ntineighbour, ntihsml) reduction(max:maxTimeBin)
216  for(pa = 0; pa < act->NumActiveParticle; pa++)
217  {
218  const int i = get_active_particle(act, pa);
219 
220  if(P[i].IsGarbage || P[i].Swallowed)
221  continue;
222 
223  enum TimeStepType titype;
224  inttime_t dti;
226  dti = dti_min;
227  } else {
228  dti = get_timestep_ti(i, dti_max, times->Ti_Current, atime, hubble, &titype);
229  if(titype == TI_ACCEL)
230  ntiaccel++;
231  else if (titype == TI_COURANT)
232  nticourant++;
233  else if (titype == TI_ACCRETE)
234  ntiaccrete++;
235  else if (titype == TI_NEIGH)
236  ntineighbour++;
237  else if (titype == TI_HSML)
238  ntihsml++;
239  }
240 
241  /* make it a power 2 subdivision */
242  dti = round_down_power_of_two(dti);
243 
244  int bin = get_timestep_bin(dti);
245  if(bin < 1) {
246  message(1, "Time-step of integer size %d not allowed, id = %lu, debugging info follows.\n", dti, P[i].ID);
247  badstepsizecount++;
248  }
249  int binold = P[i].TimeBin;
250 
251  /* timestep wants to increase */
252  if(bin > binold)
253  {
254  /* make sure the new step is currently active,
255  * so that particles do not miss a step */
256  while(!is_timebin_active(bin, times->Ti_Current) && bin > binold && bin > 1)
257  bin--;
258  }
259  /* This moves particles between time bins:
260  * active particles always remain active
261  * until rebuild_activelist is called
262  * (after domain, on new timestep).*/
263  P[i].TimeBin = bin;
264  /*Find max and min*/
265  if(bin < mTimeBin)
266  mTimeBin = bin;
267  if(bin > maxTimeBin)
268  maxTimeBin = bin;
269  }
270 
271  MPI_Allreduce(MPI_IN_PLACE, &badstepsizecount, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
272  MPI_Allreduce(MPI_IN_PLACE, &mTimeBin, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
273  MPI_Allreduce(MPI_IN_PLACE, &maxTimeBin, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
274 
275  MPI_Allreduce(MPI_IN_PLACE, &ntiaccel, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
276  MPI_Allreduce(MPI_IN_PLACE, &nticourant, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
277  MPI_Allreduce(MPI_IN_PLACE, &ntihsml, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
278  MPI_Allreduce(MPI_IN_PLACE, &ntiaccrete, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
279  MPI_Allreduce(MPI_IN_PLACE, &ntineighbour, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
280 
281  /* Ensure that the PM timestep is not longer than the longest tree timestep;
282  * this prevents particles in the longest timestep being active and moving into a higher bin
283  * between PM timesteps, thus skipping the PM step entirely.*/
284  if(isPM && times->PM_length > dti_from_timebin(maxTimeBin))
285  times->PM_length = dti_from_timebin(maxTimeBin);
286  message(0, "PM timebin: %x (dloga: %g Max: %g). Criteria: Accel: %ld Soundspeed: %ld DivVel: %ld Accrete: %ld Neighbour: %ld\n",
288  ntiaccel, nticourant, ntihsml, ntiaccrete, ntineighbour);
289 
290  /* BH particles have their timesteps set by a timestep limiter.
291  * On the first timestep this is not effective because all the particles have zero timestep.
292  * So on the first timestep only set all BH particles to the smallest allowable timestep*/
293  if(isFirstTimeStep) {
294  #pragma omp parallel for
295  for(pa = 0; pa < PartManager->NumPart; pa++)
296  {
297  if(P[pa].Type == 5)
298  P[pa].TimeBin = mTimeBin;
299  }
300  }
301  walltime_measure("/Timeline");
302  times->mintimebin = mTimeBin;
303  times->maxtimebin = maxTimeBin;
304  return badstepsizecount;
305 }
306 
307 /* Update the last active drift times for all bins*/
308 void
310 {
311  int bin;
312  #pragma omp parallel for
313  for(bin = 0; bin <= TIMEBINS; bin++)
314  {
315  /* Update active timestep even if no particles in it*/
316  if(is_timebin_active(bin, times->Ti_Current))
317  times->Ti_lastactivedrift[bin] = times->Ti_Current;
318  }
319 }
320 
321 /* Apply half a kick, for the second half of the timestep.*/
322 void
323 apply_half_kick(const ActiveParticles * act, Cosmology * CP, DriftKickTimes * times, const double atime, const double MinEgySpec)
324 {
325  int pa, bin;
326  walltime_measure("/Misc");
327  double gravkick[TIMEBINS+1] = {0}, hydrokick[TIMEBINS+1] = {0};
328  /* Do nothing for the first timestep when the kicks are always zero*/
329  if(times->mintimebin == 0 && times->maxtimebin == 0)
330  return;
331  #pragma omp parallel for
332  for(bin = times->mintimebin; bin <= TIMEBINS; bin++)
333  {
334  /* Kick the active timebins*/
335  if(is_timebin_active(bin, times->Ti_Current)) {
336  /* do the kick for half a step*/
337  inttime_t dti = dti_from_timebin(bin);
338  inttime_t newkick = times->Ti_kick[bin] + dti/2;
339  /* Compute kick factors for occupied bins*/
340  gravkick[bin] = get_exact_gravkick_factor(CP, times->Ti_kick[bin], newkick);
341  hydrokick[bin] = get_exact_hydrokick_factor(CP, times->Ti_kick[bin], newkick);
342  // message(0, "drift %d bin %d kick: %d->%d\n", times->Ti_Current, bin, times->Ti_kick[bin], newkick);
343  times->Ti_kick[bin] = newkick;
344  }
345  }
346  /* Advance the shorter bins without particles by the minimum occupied timestep.*/
347  for(bin=1; bin < times->mintimebin; bin++)
348  times->Ti_kick[bin] += dti_from_timebin(times->mintimebin)/2;
349  // message(0, "drift %d bin %d kick: %d\n", times->Ti_Current, bin, times->Ti_kick[bin]);
350  /* Now assign new timesteps and kick */
351  #pragma omp parallel for
352  for(pa = 0; pa < act->NumActiveParticle; pa++)
353  {
354  const int i = get_active_particle(act, pa);
355  if(P[i].Swallowed || P[i].IsGarbage)
356  continue;
357  int bin = P[i].TimeBin;
358  if(bin > TIMEBINS)
359  endrun(4, "Particle %d (type %d, id %ld) had unexpected timebin %d\n", i, P[i].Type, P[i].ID, P[i].TimeBin);
360 #ifdef DEBUG
361  if(isnan(gravkick[bin]) || gravkick[bin] == 0.)
362  endrun(5, "Bad kicks %lg bin %d tik %d\n", gravkick[bin], bin, times->Ti_kick[bin]);
363 #endif
364  inttime_t dti = dti_from_timebin(bin);
365  const double dt_entr = dloga_from_dti(dti/2, times->Ti_Current);
366  /*This only changes particle i, so is thread-safe.*/
367  do_the_short_range_kick(i, dt_entr, gravkick[bin], hydrokick[bin], atime, MinEgySpec);
368  }
369  walltime_measure("/Timeline/HalfKick/Short");
370 }
371 
372 void
374 {
375  /*Always do a PM half-kick, because this should be called just after a PM step*/
376  const inttime_t tistart = times->PM_kick;
377  const inttime_t tiend = tistart + times->PM_length / 2;
378  /* Do long-range kick */
379  int i;
380  const double Fgravkick = get_exact_gravkick_factor(CP, tistart, tiend);
381 
382  #pragma omp parallel for
383  for(i = 0; i < PartManager->NumPart; i++)
384  {
385  int j;
386  if(P[i].Swallowed || P[i].IsGarbage)
387  continue;
388  for(j = 0; j < 3; j++) /* do the kick */
389  P[i].Vel[j] += P[i].GravPM[j] * Fgravkick;
390  }
391  times->PM_kick = tiend;
392  walltime_measure("/Timeline/HalfKick/Long");
393 }
394 
395 void
396 do_the_short_range_kick(int i, double dt_entr, double Fgravkick, double Fhydrokick, const double atime, const double MinEgySpec)
397 {
398  int j;
399  for(j = 0; j < 3; j++)
400  P[i].Vel[j] += P[i].GravAccel[j] * Fgravkick;
401 
402  /* Add kick from dynamic friction and hydro drag for BHs. */
403  if(P[i].Type == 5) {
404  for(j = 0; j < 3; j++){
405  P[i].Vel[j] += BHP(i).DFAccel[j] * Fgravkick;
406  P[i].Vel[j] += BHP(i).DragAccel[j] * Fgravkick;
407  }
408  }
409 
410  if(P[i].Type == 0) {
411  /* Add kick from hydro and SPH stuff */
412  for(j = 0; j < 3; j++) {
413  P[i].Vel[j] += SPHP(i).HydroAccel[j] * Fhydrokick;
414  }
415  /* Code here imposes a hard limit (default to speed of light)
416  * on the gas velocity. This should rarely be hit.*/
417  double vv=0;
418  for(j=0; j < 3; j++)
419  vv += P[i].Vel[j] * P[i].Vel[j];
420  vv = sqrt(vv);
421 
422  if(vv > 0 && vv/atime > TimestepParams.MaxGasVel) {
423  message(1,"Gas Particle ID %ld exceeded the gas velocity limit: %g > %g\n",P[i].ID, vv / atime, TimestepParams.MaxGasVel);
424  for(j=0;j < 3; j++)
425  {
426  P[i].Vel[j] *= TimestepParams.MaxGasVel * atime / vv;
427  }
428  }
429 
430  /* Update entropy for adiabatic change*/
431  SPHP(i).Entropy += SPHP(i).DtEntropy * dt_entr;
432 
433  /* Limit entropy in simulations with cooling disabled*/
434  double a3inv = 1/(atime * atime * atime);
435  const double enttou = pow(SPHP(i).Density * a3inv, GAMMA_MINUS1) / GAMMA_MINUS1;
436  if(SPHP(i).Entropy < MinEgySpec/enttou)
437  SPHP(i).Entropy = MinEgySpec / enttou;
438  }
439 #ifdef DEBUG
440  /* Check we have reasonable velocities. If we do not, try to explain why*/
441  if(isnan(P[i].Vel[0]) || isnan(P[i].Vel[1]) || isnan(P[i].Vel[2])) {
442  message(1, "Vel = %g %g %g Type = %d gk = %g a_g = %g %g %g\n",
443  P[i].Vel[0], P[i].Vel[1], P[i].Vel[2], P[i].Type,
444  Fgravkick, P[i].GravAccel[0], P[i].GravAccel[1], P[i].GravAccel[2]);
445  }
446 #endif
447 }
448 
449 static double
450 get_timestep_dloga(const int p, const inttime_t Ti_Current, const double atime, const double hubble, enum TimeStepType * titype)
451 {
452  double ac = 0;
453  double dt = 0, dt_courant = 0, dt_hsml = 0;
454 
455  /*Compute physical acceleration*/
456  {
457  const double a2inv = 1/(atime * atime);
458 
459  double ax = a2inv * P[p].GravAccel[0];
460  double ay = a2inv * P[p].GravAccel[1];
461  double az = a2inv * P[p].GravAccel[2];
462 
463  ay += a2inv * P[p].GravPM[1];
464  ax += a2inv * P[p].GravPM[0];
465  az += a2inv * P[p].GravPM[2];
466 
467  if(P[p].Type == 0)
468  {
469  const double fac2 = 1 / pow(atime, 3 * GAMMA - 2);
470  ax += fac2 * SPHP(p).HydroAccel[0];
471  ay += fac2 * SPHP(p).HydroAccel[1];
472  az += fac2 * SPHP(p).HydroAccel[2];
473  }
474 
475  ac = sqrt(ax * ax + ay * ay + az * az); /* this is now the physical acceleration */
476  }
477 
478  if(ac == 0)
479  ac = 1.0e-30;
480 
481  /* mind the factor 2.8 difference between gravity and softening used here. */
482  dt = sqrt(2 * TimestepParams.ErrTolIntAccuracy * atime * (FORCE_SOFTENING(p, P[p].Type) / 2.8) / ac);
483  *titype = TI_ACCEL;
484 
485  if(P[p].Type == 0)
486  {
487  const double fac3 = pow(atime, 3 * (1 - GAMMA) / 2.0);
488  dt_courant = 2 * TimestepParams.CourantFac * atime * P[p].Hsml / (fac3 * SPHP(p).MaxSignalVel);
489  if(dt_courant < dt) {
490  dt = dt_courant;
491  *titype = TI_COURANT;
492  }
493  /* This timestep criterion is from Gadget-4, eq. 0 of 2010.03567 and stops
494  * particles having too large a density change.*/
495  dt_hsml = TimestepParams.CourantFac * atime * atime * fabs(P[p].Hsml / (P[p].DtHsml + 1e-20));
496  if(dt_hsml < dt) {
497  dt = dt_hsml;
498  *titype = TI_HSML;
499  }
500  }
501 
502  if(P[p].Type == 5)
503  {
504  if(BHP(p).Mdot > 0 && BHP(p).Mass > 0)
505  {
506  double dt_accr = 0.25 * BHP(p).Mass / BHP(p).Mdot;
507  if(dt_accr < dt) {
508  dt = dt_accr;
509  *titype = TI_ACCRETE;
510  }
511  }
512  if(BHP(p).minTimeBin > 0 && BHP(p).minTimeBin+1 < TIMEBINS) {
513  double dt_limiter = get_dloga_for_bin(BHP(p).minTimeBin+1, Ti_Current) / hubble;
514  /* Set the black hole timestep to the minimum timesteps of neighbouring gas particles.
515  * It should be at least this for accretion accuracy, and it does not make sense to
516  * make it less than this. We go one timestep up because often the smallest
517  * timebin particle is cooling, and so increases its timestep. Then the smallest timebin
518  * contains only the BH which doesn't make much numerical sense. Accretion accuracy is not much changed
519  * by one timestep difference.*/
520  dt = dt_limiter;
521  *titype = TI_NEIGH;
522  }
523  }
524 
525  /* d a / a = dt * H */
526  double dloga = dt * hubble;
527 
528  return dloga;
529 }
530 
536 static inttime_t
537 get_timestep_ti(const int p, const inttime_t dti_max, const inttime_t Ti_Current, const double atime, const double hubble, enum TimeStepType * titype)
538 {
539  inttime_t dti;
540  /*Give a useful message if we are broken*/
541  if(dti_max == 0)
542  return 0;
543 
544  double dloga = get_timestep_dloga(p, Ti_Current, atime, hubble, titype);
545 
548 
549  dti = dti_from_dloga(dloga, Ti_Current);
550 
551  /* Check for overflow*/
552  if(dti > dti_max || dti < 0)
553  dti = dti_max;
554 
555  if(dti <= 1 || dti > (inttime_t) TIMEBASE)
556  {
557  if(P[p].Type == 0)
558  message(1, "Bad timestep (%x)! titype %d. ID=%lu Type=%d dloga=%g dtmax=%x xyz=(%g|%g|%g) tree=(%g|%g|%g) PM=(%g|%g|%g) hydro-frc=(%g|%g|%g) dens=%g hsml=%g dh = %g Entropy=%g, dtEntropy=%g maxsignal=%g\n",
559  dti, *titype, P[p].ID, P[p].Type, dloga, dti_max,
560  P[p].Pos[0], P[p].Pos[1], P[p].Pos[2],
561  P[p].GravAccel[0], P[p].GravAccel[1], P[p].GravAccel[2],
562  P[p].GravPM[0], P[p].GravPM[1], P[p].GravPM[2],
563  SPHP(p).HydroAccel[0], SPHP(p).HydroAccel[1], SPHP(p).HydroAccel[2],
564  SPHP(p).Density, P[p].Hsml, P[p].DtHsml, SPHP(p).Entropy, SPHP(p).DtEntropy, SPHP(p).MaxSignalVel);
565  else
566  message(1, "Bad timestep (%x)! titype %d. ID=%lu Type=%d dloga=%g dtmax=%x xyz=(%g|%g|%g) tree=(%g|%g|%g) PM=(%g|%g|%g)\n",
567  dti, *titype, P[p].ID, P[p].Type, dloga, dti_max,
568  P[p].Pos[0], P[p].Pos[1], P[p].Pos[2],
569  P[p].GravAccel[0], P[p].GravAccel[1], P[p].GravAccel[2],
570  P[p].GravPM[0], P[p].GravPM[1], P[p].GravPM[2]
571  );
572  }
573 
574  return dti;
575 }
576 
577 
583 double
584 get_long_range_timestep_dloga(const double atime, const Cosmology * CP, const int FastParticleType, const double asmth)
585 {
586  int i, type;
587  int count[6];
588  int64_t count_sum[6];
589  double v[6], v_sum[6], mim[6], min_mass[6];
591 
592  for(type = 0; type < 6; type++)
593  {
594  count[type] = 0;
595  v[type] = 0;
596  mim[type] = 1.0e30;
597  }
598 
599  for(i = 0; i < PartManager->NumPart; i++)
600  {
601  v[P[i].Type] += P[i].Vel[0] * P[i].Vel[0] + P[i].Vel[1] * P[i].Vel[1] + P[i].Vel[2] * P[i].Vel[2];
602  if(P[i].Mass > 0)
603  {
604  if(mim[P[i].Type] > P[i].Mass)
605  mim[P[i].Type] = P[i].Mass;
606  }
607  count[P[i].Type]++;
608  }
609 
610  MPI_Allreduce(v, v_sum, 6, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
611  MPI_Allreduce(mim, min_mass, 6, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
612 
613  sumup_large_ints(6, count, count_sum);
614 
615  /* add star, gas and black hole particles together to treat them on equal footing,
616  * using the original gas particle spacing. */
617  v_sum[0] += v_sum[4];
618  count_sum[0] += count_sum[4];
619  v_sum[4] = v_sum[0];
620  count_sum[4] = count_sum[0];
621  v_sum[0] += v_sum[5];
622  count_sum[0] += count_sum[5];
623  v_sum[5] = v_sum[0];
624  count_sum[5] = count_sum[0];
625 
626  min_mass[5] = min_mass[0];
627 
628  const double hubble = hubble_function(CP, atime);
629  for(type = 0; type < 6; type++)
630  {
631  if(count_sum[type] > 0)
632  {
633  double omega, dmean, dloga1;
634  /* Type 4 is stars, type 5 is BHs, both baryons*/
635  if(type == 0 || type == 4 || type == 5) {
636  omega = CP->OmegaBaryon;
637  }
638  /* In practice usually FastParticleType == 2
639  * so this doesn't matter. */
640  else if (type == 2) {
641  omega = get_omega_nu(&CP->ONu, 1);
642  } else {
643  omega = CP->OmegaCDM;
644  }
645  /* "Avg. radius" of smallest particle: (min_mass/total_mass)^1/3 */
646  dmean = pow(min_mass[type] / (omega * 3 * CP->Hubble * CP->Hubble / (8 * M_PI * CP->GravInternal)), 1.0 / 3);
647 
648  dloga1 = TimestepParams.MaxRMSDisplacementFac * hubble * atime * atime * DMIN(asmth, dmean) / sqrt(v_sum[type] / count_sum[type]);
649  message(0, "type=%d dmean=%g asmth=%g minmass=%g a=%g sqrt(<p^2>)=%g dloga=%g\n",
650  type, dmean, asmth, min_mass[type], atime, sqrt(v_sum[type] / count_sum[type]), dloga1);
651 
652  /* don't constrain the step to the neutrinos */
653  if(type != FastParticleType && dloga1 < dloga)
654  dloga = dloga1;
655  }
656  }
657 
660  }
661 
662  return dloga;
663 }
664 
665 /* backward compatibility with the old loop. */
666 inttime_t
667 get_PM_timestep_ti(const DriftKickTimes * const times, const double atime, const Cosmology * CP, const int FastParticleType, const double asmth)
668 {
669  double dloga = get_long_range_timestep_dloga(atime, CP, FastParticleType, asmth);
670 
671  inttime_t dti = dti_from_dloga(dloga, times->Ti_Current);
672  dti = round_down_power_of_two(dti);
673 
674  SyncPoint * next = find_next_sync_point(times->Ti_Current);
675  if(next == NULL)
676  endrun(0, "Trying to go beyond the last sync point. This happens only at TimeMax \n");
677 
678  /* go no more than the next sync point */
679  inttime_t dti_max = next->ti - times->PM_kick;
680 
681  if(dti > dti_max)
682  dti = dti_max;
683  return dti;
684 }
685 
687 {
688  int bin = -1;
689 
690  if(dti <= 0)
691  return 0;
692 
693  if(dti == 1)
694  return -1;
695 
696  while(dti)
697  {
698  bin++;
699  dti >>= 1;
700  }
701 
702  return bin;
703 }
704 
712 inttime_t find_next_kick(inttime_t Ti_Current, int minTimeBin)
713 {
714  /* Current value plus the increment for the smallest active bin. */
715  return Ti_Current + dti_from_timebin(minTimeBin);
716 }
717 
718 static void print_timebin_statistics(const DriftKickTimes * const times, const int NumCurrentTiStep, int * TimeBinCountType, const double Time);
719 
720 /* mark the bins that will be active before the next kick*/
721 int rebuild_activelist(ActiveParticles * act, const DriftKickTimes * const times, int NumCurrentTiStep, const double Time)
722 {
723  int i;
724 
725  int NumThreads = omp_get_max_threads();
726  /*Since we use a static schedule, only need NumPart/NumThreads elements per thread.*/
727  size_t narr = PartManager->NumPart / NumThreads + NumThreads;
728 
729  /*We know all particles are active on a PM timestep*/
730  if(is_PM_timestep(times)) {
731  act->ActiveParticle = NULL;
733  }
734  else {
735  /*Need space for more particles than we have, because of star formation*/
736  act->ActiveParticle = (int *) mymalloc("ActiveParticle", narr * NumThreads * sizeof(int));
737  act->NumActiveParticle = 0;
738  }
739 
740  int * TimeBinCountType = (int *) mymalloc("TimeBinCountType", 6*(TIMEBINS+1)*NumThreads * sizeof(int));
741  memset(TimeBinCountType, 0, 6 * (TIMEBINS+1) * NumThreads * sizeof(int));
742 
743  /*We want a lockless algorithm which preserves the ordering of the particle list.*/
744  size_t *NActiveThread = ta_malloc("NActiveThread", size_t, NumThreads);
745  int **ActivePartSets = ta_malloc("ActivePartSets", int *, NumThreads);
746  gadget_setup_thread_arrays(act->ActiveParticle, ActivePartSets, NActiveThread, narr, NumThreads);
747 
748  /* We enforce schedule static to imply monotonic, ensure that each thread executes on contiguous particles
749  * and ensure no thread gets more than narr particles.*/
750  size_t schedsz = PartManager->NumPart / NumThreads + 1;
751  #pragma omp parallel for schedule(static, schedsz)
752  for(i = 0; i < PartManager->NumPart; i++)
753  {
754  const int bin = P[i].TimeBin;
755  const int tid = omp_get_thread_num();
756  if(P[i].IsGarbage || P[i].Swallowed)
757  continue;
758  /* when we are in PM, all particles must have been synced. */
759  if (P[i].Ti_drift != times->Ti_Current) {
760  endrun(5, "Particle %d type %d has drift time %x not ti_current %x!",i, P[i].Type, P[i].Ti_drift, times->Ti_Current);
761  }
762 
763  if(act->ActiveParticle && is_timebin_active(bin, times->Ti_Current))
764  {
765  /* Store this particle in the ActiveSet for this thread*/
766  ActivePartSets[tid][NActiveThread[tid]] = i;
767  NActiveThread[tid]++;
768  }
769  TimeBinCountType[(TIMEBINS + 1) * (6* tid + P[i].Type) + bin] ++;
770  }
771  if(act->ActiveParticle) {
772  /*Now we want a merge step for the ActiveParticle list.*/
773  act->NumActiveParticle = gadget_compact_thread_arrays(act->ActiveParticle, ActivePartSets, NActiveThread, NumThreads);
774  }
775  ta_free(ActivePartSets);
776  ta_free(NActiveThread);
777 
778  /*Print statistics for this time bin*/
779  print_timebin_statistics(times, NumCurrentTiStep, TimeBinCountType, Time);
780  myfree(TimeBinCountType);
781 
782  /* Shrink the ActiveParticle array. We still need extra space for star formation,
783  * but we do not need space for the known-inactive particles*/
784  if(act->ActiveParticle) {
785  act->ActiveParticle = (int *) myrealloc(act->ActiveParticle, sizeof(int)*(act->NumActiveParticle + PartManager->MaxPart - PartManager->NumPart));
787  /* listen to the slots events such that we can set timebin of new particles */
788  }
790  walltime_measure("/Timeline/Active");
791 
792  return 0;
793 }
794 
796 {
797  if(act->ActiveParticle) {
798  myfree(act->ActiveParticle);
799  }
801 }
802 
807 static void print_timebin_statistics(const DriftKickTimes * const times, const int NumCurrentTiStep, int * TimeBinCountType, const double Time)
808 {
809  int i;
810  int64_t tot = 0, tot_type[6] = {0};
811  int64_t tot_count[TIMEBINS+1] = {0};
812  int64_t tot_count_type[6][TIMEBINS+1] = {{0}};
813  int64_t tot_num_force = 0;
814  int64_t TotNumPart = 0, TotNumType[6] = {0};
815 
816  int NumThreads = omp_get_max_threads();
817  /*Sum the thread-local memory*/
818  for(i = 1; i < NumThreads; i ++) {
819  int j;
820  for(j=0; j < 6 * (TIMEBINS+1); j++)
821  TimeBinCountType[j] += TimeBinCountType[6 * (TIMEBINS+1) * i + j];
822  }
823 
824  for(i = 0; i < 6; i ++) {
825  sumup_large_ints(TIMEBINS+1, &TimeBinCountType[(TIMEBINS+1) * i], tot_count_type[i]);
826  }
827 
828  for(i = 0; i<TIMEBINS+1; i++) {
829  int j;
830  for(j=0; j<6; j++) {
831  tot_count[i] += tot_count_type[j][i];
832  /*Note j*/
833  TotNumType[j] += tot_count_type[j][i];
834  TotNumPart += tot_count_type[j][i];
835  }
836  if(is_timebin_active(i, times->Ti_Current))
837  tot_num_force += tot_count[i];
838  }
839 
840  char extra[20] = {0};
841 
842  if(is_PM_timestep(times))
843  strcat(extra, "PM-Step");
844 
845  const double dloga = get_dloga_for_bin(times->mintimebin, times->Ti_Current);
846  const double z = 1.0 / (Time) - 1;
847  message(0, "Begin Step %d, Time: %g (%x), Redshift: %g, Nf = %014ld, Systemstep: %g, Dloga: %g, status: %s\n",
848  NumCurrentTiStep, Time, times->Ti_Current, z, tot_num_force,
849  dloga * Time, dloga,
850  extra);
851 
852  message(0, "TotNumPart: %013ld SPH %013ld BH %010ld STAR %013ld \n",
853  TotNumPart, TotNumType[0], TotNumType[5], TotNumType[4]);
854  message(0, "Occupied: % 12ld % 12ld % 12ld % 12ld % 12ld % 12ld dt\n", 0L, 1L, 2L, 3L, 4L, 5L);
855 
856  for(i = TIMEBINS; i >= 0; i--) {
857  if(tot_count[i] == 0) continue;
858  message(0, " %c bin=%2d % 12ld % 12ld % 12ld % 12ld % 12ld % 12ld %6g\n",
859  is_timebin_active(i, times->Ti_Current) ? 'X' : ' ',
860  i,
861  tot_count_type[0][i],
862  tot_count_type[1][i],
863  tot_count_type[2][i],
864  tot_count_type[3][i],
865  tot_count_type[4][i],
866  tot_count_type[5][i],
867  get_dloga_for_bin(i, times->Ti_Current));
868 
869  if(is_timebin_active(i, times->Ti_Current))
870  {
871  tot += tot_count[i];
872  int ptype;
873  for(ptype = 0; ptype < 6; ptype ++) {
874  tot_type[ptype] += tot_count_type[ptype][i];
875  }
876  }
877  }
878  message(0, " -----------------------------------\n");
879  message(0, "Total: % 12ld % 12ld % 12ld % 12ld % 12ld % 12ld Sum:% 14ld\n",
880  tot_type[0], tot_type[1], tot_type[2], tot_type[3], tot_type[4], tot_type[5], tot);
881 
882 }
double hubble_function(const Cosmology *CP, double a)
Definition: cosmology.c:58
void message(int where, const char *fmt,...)
Definition: endrun.c:175
void endrun(int where, const char *fmt,...)
Definition: endrun.c:147
int event_listen(EventSpec *eh, eventfunc func, void *userdata)
Definition: event.c:6
int event_unlisten(EventSpec *eh, eventfunc func, void *userdata)
Definition: event.c:26
EventSpec EventSlotsFork
Definition: event.c:54
double FORCE_SOFTENING(int i, int type)
static struct gravpm_params GravPM
static double dloga
Definition: lightcone.c:21
#define mymalloc(name, size)
Definition: mymalloc.h:15
#define ta_malloc(name, type, nele)
Definition: mymalloc.h:25
#define myrealloc(ptr, size)
Definition: mymalloc.h:18
#define ta_free(p)
Definition: mymalloc.h:28
#define myfree(x)
Definition: mymalloc.h:19
double get_omega_nu(const _omega_nu *const omnu, const double a)
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
#define GAMMA_MINUS1
Definition: physconst.h:35
#define GAMMA
Definition: physconst.h:34
static Cosmology * CP
Definition: power.c:27
#define BHP(i)
Definition: slotsmanager.h:125
#define SPHP(i)
Definition: slotsmanager.h:124
int * ActiveParticle
Definition: timestep.h:13
int64_t MaxActiveParticle
Definition: timestep.h:11
int64_t NumActiveParticle
Definition: timestep.h:12
double GravInternal
Definition: cosmology.h:32
double OmegaCDM
Definition: cosmology.h:11
double OmegaBaryon
Definition: cosmology.h:19
_omega_nu ONu
Definition: cosmology.h:24
double Hubble
Definition: cosmology.h:21
inttime_t PM_kick
Definition: timestep.h:31
inttime_t Ti_lastactivedrift[TIMEBINS+1]
Definition: timestep.h:25
int maxtimebin
Definition: timestep.h:21
inttime_t PM_length
Definition: timestep.h:29
inttime_t Ti_Current
Definition: timestep.h:27
inttime_t PM_start
Definition: timestep.h:30
inttime_t Ti_kick[TIMEBINS+1]
Definition: timestep.h:23
int mintimebin
Definition: timestep.h:20
Definition: event.h:8
int64_t child
Definition: slotsmanager.h:151
inttime_t ti
Definition: timebinmgr.h:28
double MaxSizeTimestep
Definition: timestep.c:36
double MaxGasVel
Definition: timestep.c:45
double MinSizeTimestep
Definition: timestep.c:35
double MaxRMSDisplacementFac
Definition: timestep.c:38
double ErrTolIntAccuracy
Definition: timestep.c:30
int ForceEqualTimesteps
Definition: timestep.c:33
double CourantFac
Definition: timestep.c:46
void gadget_setup_thread_arrays(int *dest, int *srcs[], size_t sizes[], size_t total_size, int narrays)
Definition: system.c:600
int64_t count_sum(int64_t countLocal)
Definition: system.c:264
void sumup_large_ints(int n, int *src, int64_t *res)
Definition: system.c:192
size_t gadget_compact_thread_arrays(int *dest, int *srcs[], size_t sizes[], int narrays)
Definition: system.c:587
static int64_t atomic_fetch_and_add_64(int64_t *ptr, int64_t value)
Definition: system.h:68
#define MPI_INT64
Definition: system.h:12
int ThisTask
Definition: test_exchange.c:23
int TotNumPart
Definition: test_exchange.c:24
#define DMIN(x, y)
Definition: test_interp.c:12
double loga_from_ti(int ti)
Definition: test_timefac.c:51
inttime_t dti_from_dloga(double loga, const inttime_t Ti_Current)
Definition: timebinmgr.c:271
inttime_t ti_from_loga(double loga)
Definition: timebinmgr.c:236
SyncPoint * find_next_sync_point(inttime_t ti)
Definition: timebinmgr.c:174
inttime_t round_down_power_of_two(inttime_t dti)
Definition: timebinmgr.c:286
double dloga_from_dti(inttime_t dti, const inttime_t Ti_Current)
Definition: timebinmgr.c:256
double get_dloga_for_bin(int timebin, const inttime_t Ti_Current)
Definition: timebinmgr.c:279
#define TIMEBINS
Definition: timebinmgr.h:13
#define TIMEBASE
Definition: timebinmgr.h:14
static inttime_t dti_from_timebin(int bin)
Definition: timebinmgr.h:44
double get_exact_hydrokick_factor(Cosmology *CP, inttime_t ti0, inttime_t ti1)
Definition: timefac.c:75
double get_exact_gravkick_factor(Cosmology *CP, inttime_t ti0, inttime_t ti1)
Definition: timefac.c:70
void apply_PM_half_kick(Cosmology *CP, DriftKickTimes *times)
Definition: timestep.c:373
static double get_timestep_dloga(const int p, const inttime_t Ti_Current, const double atime, const double hubble, enum TimeStepType *titype)
Definition: timestep.c:450
static void do_the_short_range_kick(int i, double dt_entr, double Fgravkick, double Fhydrokick, const double atime, const double MinEgySpec)
Definition: timestep.c:396
int is_PM_timestep(const DriftKickTimes *const times)
Definition: timestep.c:160
void set_timestep_params(ParameterSet *ps)
Definition: timestep.c:51
double get_atime(const inttime_t Ti_Current)
Definition: timestep.c:168
DriftKickTimes init_driftkicktime(inttime_t Ti_Current)
Definition: timestep.c:133
inttime_t init_timebins(double TimeInit)
Definition: timestep.c:123
double get_long_range_timestep_dloga(const double atime, const Cosmology *CP, const int FastParticleType, const double asmth)
Definition: timestep.c:584
static int timestep_eh_slots_fork(EIBase *event, void *userdata)
Definition: timestep.c:77
int find_timesteps(const ActiveParticles *act, DriftKickTimes *times, const double atime, int FastParticleType, const Cosmology *CP, const double asmth, const int isFirstTimeStep)
Definition: timestep.c:176
int rebuild_activelist(ActiveParticles *act, const DriftKickTimes *const times, int NumCurrentTiStep, const double Time)
Definition: timestep.c:721
static int get_active_particle(const ActiveParticles *act, int pa)
Definition: timestep.c:68
int is_timebin_active(int i, inttime_t current)
Definition: timestep.c:149
void apply_half_kick(const ActiveParticles *act, Cosmology *CP, DriftKickTimes *times, const double atime, const double MinEgySpec)
Definition: timestep.c:323
static struct timestep_params TimestepParams
inttime_t find_next_kick(inttime_t Ti_Current, int minTimeBin)
Definition: timestep.c:712
static inttime_t get_PM_timestep_ti(const DriftKickTimes *const times, const double atime, const Cosmology *CP, const int FastParticleType, const double asmth)
Definition: timestep.c:667
static inttime_t get_timestep_ti(const int p, const inttime_t dti_max, const inttime_t Ti_Current, const double atime, const double hubble, enum TimeStepType *titype)
Definition: timestep.c:537
static int get_timestep_bin(inttime_t dti)
Definition: timestep.c:686
void update_lastactive_drift(DriftKickTimes *times)
Definition: timestep.c:309
TimeStepType
Definition: timestep.c:107
@ TI_NEIGH
Definition: timestep.c:111
@ TI_COURANT
Definition: timestep.c:109
@ TI_ACCEL
Definition: timestep.c:108
@ TI_ACCRETE
Definition: timestep.c:110
@ TI_HSML
Definition: timestep.c:112
void free_activelist(ActiveParticles *act)
Definition: timestep.c:795
static void print_timebin_statistics(const DriftKickTimes *const times, const int NumCurrentTiStep, int *TimeBinCountType, const double Time)
Definition: timestep.c:807
int32_t inttime_t
Definition: types.h:8
#define walltime_measure(name)
Definition: walltime.h:8
static enum TransferType ptype
Definition: zeldovich.c:146