Commit 18c2cce2 authored by Peter W. Draper's avatar Peter W. Draper
Browse files

Merge branch 'new_timeline_mpi' into 'master'

MPI send/recv fixes for inactive cells

This is open so that we have a live diff of the changes to solve the MPI crisis. Also helpful to keep track of the changes.

Contains all of @nnrw56 @pdraper @matthieu's changes to this stage.

Also see #256.

See merge request !303
parents ceca95de fbd6aac8
......@@ -290,7 +290,8 @@ int main(int argc, char *argv[]) {
/* Do we have debugging checks ? */
#ifdef SWIFT_DEBUG_CHECKS
message("WARNING: Debugging checks activated. Code will be slower !");
if (myrank == 0)
message("WARNING: Debugging checks activated. Code will be slower !");
#endif
/* Do we choke on FP-exceptions ? */
......
......@@ -41,9 +41,10 @@ __attribute__((always_inline)) INLINE static int cell_is_drifted(
#ifdef SWIFT_DEBUG_CHECKS
if (c->ti_old > e->ti_current)
error(
"Cell has been drifted too far forward in time! c->ti_old=%lld "
"e->ti_current=%lld",
c->ti_old, e->ti_current);
"Cell has been drifted too far forward in time! c->ti_old=%lld (t=%e) "
"and e->ti_current=%lld (t=%e)",
c->ti_old, c->ti_old * e->timeBase, e->ti_current,
e->ti_current * e->timeBase);
#endif
return (c->ti_old == e->ti_current);
......@@ -62,9 +63,10 @@ __attribute__((always_inline)) INLINE static int cell_is_active(
#ifdef SWIFT_DEBUG_CHECKS
if (c->ti_end_min < e->ti_current)
error(
"cell in an impossible time-zone! c->ti_end_min=%lld "
"e->ti_current=%lld",
c->ti_end_min, e->ti_current);
"cell in an impossible time-zone! c->ti_end_min=%lld (t=%e) and "
"e->ti_current=%lld (t=%e)",
c->ti_end_min, c->ti_end_min * e->timeBase, e->ti_current,
e->ti_current * e->timeBase);
#endif
return (c->ti_end_min == e->ti_current);
......
......@@ -129,7 +129,6 @@ int cell_unpack(struct pcell *pc, struct cell *c, struct space *s) {
temp->dx_max = 0.f;
temp->nodeID = c->nodeID;
temp->parent = c;
temp->ti_old = c->ti_old;
c->progeny[k] = temp;
c->split = 1;
count += cell_unpack(&pc[pc->progeny[k]], temp, s);
......@@ -244,7 +243,7 @@ int cell_pack(struct cell *c, struct pcell *pc) {
*
* @return The number of packed cells.
*/
int cell_pack_ti_ends(struct cell *c, int *ti_ends) {
int cell_pack_ti_ends(struct cell *c, integertime_t *ti_ends) {
#ifdef WITH_MPI
......@@ -275,7 +274,7 @@ int cell_pack_ti_ends(struct cell *c, int *ti_ends) {
*
* @return The number of cells created.
*/
int cell_unpack_ti_ends(struct cell *c, int *ti_ends) {
int cell_unpack_ti_ends(struct cell *c, integertime_t *ti_ends) {
#ifdef WITH_MPI
......@@ -907,6 +906,10 @@ int cell_is_drift_needed(struct cell *c, const struct engine *e) {
*/
int cell_unskip_tasks(struct cell *c, struct scheduler *s) {
#ifdef WITH_MPI
struct engine *e = s->space->e;
#endif
/* Un-skip the density tasks involved with this cell. */
for (struct link *l = c->density; l != NULL; l = l->next) {
struct task *t = l->t;
......@@ -940,8 +943,10 @@ int cell_unskip_tasks(struct cell *c, struct scheduler *s) {
/* Activate the tasks to recv foreign cell ci's data. */
scheduler_activate(s, ci->recv_xv);
scheduler_activate(s, ci->recv_rho);
scheduler_activate(s, ci->recv_ti);
if (cell_is_active(ci, e)) {
scheduler_activate(s, ci->recv_rho);
scheduler_activate(s, ci->recv_ti);
}
/* Look for the local cell cj's send tasks. */
struct link *l = NULL;
......@@ -956,24 +961,28 @@ int cell_unskip_tasks(struct cell *c, struct scheduler *s) {
else
error("Drift task missing !");
for (l = cj->send_rho; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
for (l = cj->send_ti; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
if (cell_is_active(cj, e)) {
for (l = cj->send_rho; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
for (l = cj->send_ti; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
}
} else if (cj->nodeID != engine_rank) {
/* Activate the tasks to recv foreign cell cj's data. */
scheduler_activate(s, cj->recv_xv);
scheduler_activate(s, cj->recv_rho);
scheduler_activate(s, cj->recv_ti);
if (cell_is_active(cj, e)) {
scheduler_activate(s, cj->recv_rho);
scheduler_activate(s, cj->recv_ti);
}
/* Look for the local cell ci's send tasks. */
struct link *l = NULL;
......@@ -988,17 +997,19 @@ int cell_unskip_tasks(struct cell *c, struct scheduler *s) {
else
error("Drift task missing !");
for (l = ci->send_rho; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
for (l = ci->send_ti; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
if (cell_is_active(ci, e)) {
for (l = ci->send_rho; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
for (l = ci->send_ti; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
}
}
#endif
}
......@@ -1134,3 +1145,25 @@ void cell_drift(struct cell *c, const struct engine *e) {
/* Update the time of the last drift */
c->ti_old = ti_current;
}
/**
* @brief Recursively checks that all particles in a cell have a time-step
*/
void cell_check_timesteps(struct cell *c) {
#ifdef SWIFT_DEBUG_CHECKS
if (c->ti_end_min == 0 && c->nr_tasks > 0)
error("Cell without assigned time-step");
if (c->split) {
for (int k = 0; k < 8; ++k)
if (c->progeny[k] != NULL) cell_check_timesteps(c->progeny[k]);
} else {
if (c->nodeID == engine_rank)
for (int i = 0; i < c->count; ++i)
if (c->parts[i].time_bin == 0)
error("Particle without assigned time-bin");
}
#endif
}
......@@ -35,6 +35,7 @@
#include "multipole.h"
#include "part.h"
#include "task.h"
#include "timeline.h"
/* Avoid cyclic inclusions */
struct engine;
......@@ -296,8 +297,8 @@ int cell_glocktree(struct cell *c);
void cell_gunlocktree(struct cell *c);
int cell_pack(struct cell *c, struct pcell *pc);
int cell_unpack(struct pcell *pc, struct cell *c, struct space *s);
int cell_pack_ti_ends(struct cell *c, int *ti_ends);
int cell_unpack_ti_ends(struct cell *c, int *ti_ends);
int cell_pack_ti_ends(struct cell *c, integertime_t *ti_ends);
int cell_unpack_ti_ends(struct cell *c, integertime_t *ti_ends);
int cell_getsize(struct cell *c);
int cell_link_parts(struct cell *c, struct part *parts);
int cell_link_gparts(struct cell *c, struct gpart *gparts);
......@@ -312,5 +313,6 @@ int cell_is_drift_needed(struct cell *c, const struct engine *e);
int cell_unskip_tasks(struct cell *c, struct scheduler *s);
void cell_set_super(struct cell *c, struct cell *super);
void cell_drift(struct cell *c, const struct engine *e);
void cell_check_timesteps(struct cell *c);
#endif /* SWIFT_CELL_H */
......@@ -48,6 +48,7 @@
#include "engine.h"
/* Local headers. */
#include "active.h"
#include "atomic.h"
#include "cell.h"
#include "clocks.h"
......@@ -698,14 +699,14 @@ void engine_addtasks_send(struct engine *e, struct cell *ci, struct cell *cj,
ci->super->drift = scheduler_addtask(
s, task_type_drift, task_subtype_none, 0, 0, ci->super, NULL, 0);
t_xv = scheduler_addtask(s, task_type_send, task_subtype_none,
4 * ci->tag, 0, ci, cj, 0);
t_rho = scheduler_addtask(s, task_type_send, task_subtype_none,
t_xv = scheduler_addtask(s, task_type_send, task_subtype_xv, 4 * ci->tag,
0, ci, cj, 0);
t_rho = scheduler_addtask(s, task_type_send, task_subtype_rho,
4 * ci->tag + 1, 0, ci, cj, 0);
t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend,
4 * ci->tag + 2, 0, ci, cj, 0);
#ifdef EXTRA_HYDRO_LOOP
t_gradient = scheduler_addtask(s, task_type_send, task_subtype_none,
t_gradient = scheduler_addtask(s, task_type_send, task_subtype_gradient,
4 * ci->tag + 3, 0, ci, cj, 0);
#endif
......@@ -739,8 +740,8 @@ void engine_addtasks_send(struct engine *e, struct cell *ci, struct cell *cj,
/* Drift before you send */
scheduler_addunlock(s, ci->super->drift, t_xv);
/* The super-cell's kick task should unlock the send_ti task. */
if (t_ti != NULL) scheduler_addunlock(s, ci->super->kick2, t_ti);
/* The super-cell's timestep task should unlock the send_ti task. */
scheduler_addunlock(s, ci->super->timestep, t_ti);
}
/* Add them to the local cell. */
......@@ -749,7 +750,7 @@ void engine_addtasks_send(struct engine *e, struct cell *ci, struct cell *cj,
#ifdef EXTRA_HYDRO_LOOP
engine_addlink(e, &ci->send_gradient, t_gradient);
#endif
if (t_ti != NULL) engine_addlink(e, &ci->send_ti, t_ti);
engine_addlink(e, &ci->send_ti, t_ti);
}
/* Recurse? */
......@@ -787,14 +788,14 @@ void engine_addtasks_recv(struct engine *e, struct cell *c, struct task *t_xv,
if (t_xv == NULL && c->density != NULL) {
/* Create the tasks. */
t_xv = scheduler_addtask(s, task_type_recv, task_subtype_none, 4 * c->tag,
0, c, NULL, 0);
t_rho = scheduler_addtask(s, task_type_recv, task_subtype_none,
t_xv = scheduler_addtask(s, task_type_recv, task_subtype_xv, 4 * c->tag, 0,
c, NULL, 0);
t_rho = scheduler_addtask(s, task_type_recv, task_subtype_rho,
4 * c->tag + 1, 0, c, NULL, 0);
t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend,
4 * c->tag + 2, 0, c, NULL, 0);
#ifdef EXTRA_HYDRO_LOOP
t_gradient = scheduler_addtask(s, task_type_recv, task_subtype_none,
t_gradient = scheduler_addtask(s, task_type_recv, task_subtype_gradient,
4 * c->tag + 3, 0, c, NULL, 0);
#endif
}
......@@ -815,7 +816,7 @@ void engine_addtasks_recv(struct engine *e, struct cell *c, struct task *t_xv,
}
for (struct link *l = c->force; l != NULL; l = l->next) {
scheduler_addunlock(s, t_gradient, l->t);
if (t_ti != NULL) scheduler_addunlock(s, l->t, t_ti);
scheduler_addunlock(s, l->t, t_ti);
}
if (c->sorts != NULL) scheduler_addunlock(s, t_xv, c->sorts);
#else
......@@ -825,7 +826,7 @@ void engine_addtasks_recv(struct engine *e, struct cell *c, struct task *t_xv,
}
for (struct link *l = c->force; l != NULL; l = l->next) {
scheduler_addunlock(s, t_rho, l->t);
if (t_ti != NULL) scheduler_addunlock(s, l->t, t_ti);
scheduler_addunlock(s, l->t, t_ti);
}
if (c->sorts != NULL) scheduler_addunlock(s, t_xv, c->sorts);
#endif
......@@ -2028,9 +2029,9 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
void *extra_data) {
/* Unpack the arguments. */
struct task *tasks = (struct task *)map_data;
const integertime_t ti_end = ((size_t *)extra_data)[0];
size_t *rebuild_space = &((size_t *)extra_data)[1];
struct scheduler *s = (struct scheduler *)(((size_t *)extra_data)[2]);
struct engine *e = (struct engine *)((size_t *)extra_data)[0];
for (int ind = 0; ind < num_elements; ind++) {
struct task *t = &tasks[ind];
......@@ -2041,7 +2042,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
t->type == task_type_sourceterms || t->type == task_type_sub_self) {
/* Set this task's skip. */
if (t->ci->ti_end_min <= ti_end) scheduler_activate(s, t);
if (cell_is_active(t->ci, e)) scheduler_activate(s, t);
}
/* Pair? */
......@@ -2059,7 +2060,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
*rebuild_space = 1;
/* Set this task's skip, otherwise nothing to do. */
if (ci->ti_end_min <= ti_end || cj->ti_end_min <= ti_end)
if (cell_is_active(t->ci, e) || cell_is_active(t->cj, e))
scheduler_activate(s, t);
else
continue;
......@@ -2086,8 +2087,10 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
/* Activate the tasks to recv foreign cell ci's data. */
scheduler_activate(s, ci->recv_xv);
scheduler_activate(s, ci->recv_rho);
scheduler_activate(s, ci->recv_ti);
if (cell_is_active(ci, e)) {
scheduler_activate(s, ci->recv_rho);
scheduler_activate(s, ci->recv_ti);
}
/* Look for the local cell cj's send tasks. */
struct link *l = NULL;
......@@ -2102,24 +2105,28 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
else
error("Drift task missing !");
for (l = cj->send_rho; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
for (l = cj->send_ti; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
if (cell_is_active(cj, e)) {
for (l = cj->send_rho; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
for (l = cj->send_ti; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
}
} else if (cj->nodeID != engine_rank) {
/* Activate the tasks to recv foreign cell cj's data. */
scheduler_activate(s, cj->recv_xv);
scheduler_activate(s, cj->recv_rho);
scheduler_activate(s, cj->recv_ti);
if (cell_is_active(cj, e)) {
scheduler_activate(s, cj->recv_rho);
scheduler_activate(s, cj->recv_ti);
}
/* Look for the local cell ci's send tasks. */
struct link *l = NULL;
......@@ -2134,44 +2141,35 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
else
error("Drift task missing !");
for (l = ci->send_rho; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
for (l = ci->send_ti; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
if (cell_is_active(ci, e)) {
for (l = ci->send_rho; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
for (l = ci->send_ti; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
}
}
#endif
}
/* Kick? */
else if (t->type == task_type_kick1) {
if (t->ci->ti_end_min <= ti_end) scheduler_activate(s, t);
} else if (t->type == task_type_kick2) {
if (t->ci->ti_end_min <= ti_end) scheduler_activate(s, t);
/* Kick/Drift/Init? */
else if (t->type == task_type_kick1 || t->type == task_type_kick2 ||
t->type == task_type_drift || t->type == task_type_init) {
if (cell_is_active(t->ci, e)) scheduler_activate(s, t);
}
/* Time-step? */
else if (t->type == task_type_timestep) {
t->ci->updated = 0;
t->ci->g_updated = 0;
if (t->ci->ti_end_min <= ti_end) scheduler_activate(s, t);
}
/* Drift? */
else if (t->type == task_type_drift) {
if (t->ci->ti_end_min <= ti_end) scheduler_activate(s, t);
}
/* Init? */
else if (t->type == task_type_init) {
if (t->ci->ti_end_min <= ti_end) scheduler_activate(s, t);
if (cell_is_active(t->ci, e)) scheduler_activate(s, t);
}
/* Tasks with no cells should not be skipped? */
......@@ -2194,7 +2192,7 @@ int engine_marktasks(struct engine *e) {
int rebuild_space = 0;
/* Run through the tasks and mark as skip or not. */
size_t extra_data[3] = {e->ti_current, rebuild_space, (size_t)&e->sched};
size_t extra_data[3] = {(size_t)e, rebuild_space, (size_t)&e->sched};
threadpool_map(&e->threadpool, engine_marktasks_mapper, s->tasks, s->nr_tasks,
sizeof(struct task), 10000, extra_data);
rebuild_space = extra_data[1];
......@@ -2293,15 +2291,16 @@ void engine_rebuild(struct engine *e) {
* @param drift_all Whether to drift particles before rebuilding or not. Will
* not be necessary if all particles have already been
* drifted (before repartitioning for instance).
* @param deferskip Whether to defer the skip until after the rebuild.
* Needed after a repartition.
* @param postrepart If we have just repartitioned, if so we need to defer the
* skip until after the rebuild and not check the if all
* cells have been drifted.
*/
void engine_prepare(struct engine *e, int drift_all, int deferskip) {
void engine_prepare(struct engine *e, int drift_all, int postrepart) {
TIMER_TIC;
/* Unskip active tasks and check for rebuild */
if (!deferskip) engine_unskip(e);
if (!postrepart) engine_unskip(e);
/* Run through the tasks and mark as skip or not. */
int rebuild = e->forcerebuild;
......@@ -2322,13 +2321,15 @@ void engine_prepare(struct engine *e, int drift_all, int deferskip) {
if (drift_all) engine_drift_all(e);
#ifdef SWIFT_DEBUG_CHECKS
/* Check that all cells have been drifted to the current time */
space_check_drift_point(e->s, e->ti_current);
/* Check that all cells have been drifted to the current time, unless
* we have just repartitioned, that can include cells that have not
* previously been active on this rank. */
if (!postrepart) space_check_drift_point(e->s, e->ti_current);
#endif
engine_rebuild(e);
}
if (deferskip) engine_unskip(e);
if (postrepart) engine_unskip(e);
/* Re-rank the tasks every now and then. */
if (e->tasks_age % engine_tasksreweight == 1) {
......@@ -2389,36 +2390,33 @@ void engine_barrier(struct engine *e, int tid) {
*/
void engine_collect_kick(struct cell *c) {
/* Skip super-cells (Their values are already set) */
/* Skip super-cells (Their values are already set) */
#ifdef WITH_MPI
if (c->timestep != NULL || c->recv_ti != NULL) return;
#else
if (c->timestep != NULL) return;
#endif /* WITH_MPI */
/* Counters for the different quantities. */
int updated = 0, g_updated = 0;
integertime_t ti_end_min = max_nr_timesteps;
/* Only do something is the cell is non-empty */
if (c->count != 0 || c->gcount != 0) {
/* If this cell is not split, I'm in trouble. */
if (!c->split) error("Cell is not split.");
/* Collect the values from the progeny. */
for (int k = 0; k < 8; k++) {
struct cell *cp = c->progeny[k];
if (cp != NULL && (cp->count > 0 || cp->gcount > 0)) {
/* Collect the values from the progeny. */
for (int k = 0; k < 8; k++) {
struct cell *cp = c->progeny[k];
if (cp != NULL) {
/* Recurse */
engine_collect_kick(cp);
/* Recurse */
engine_collect_kick(cp);
/* And update */
ti_end_min = min(ti_end_min, cp->ti_end_min);
updated += cp->updated;
g_updated += cp->g_updated;
/* And update */
ti_end_min = min(ti_end_min, cp->ti_end_min);
updated += cp->updated;
g_updated += cp->g_updated;
/* Collected, so clear for next time. */
cp->updated = 0;
cp->g_updated = 0;
}
/* Collected, so clear for next time. */
cp->updated = 0;
cp->g_updated = 0;
}
}
......@@ -2442,9 +2440,9 @@ void engine_collect_timestep(struct engine *e) {
const struct space *s = e->s;
/* Collect the cell data. */
for (int k = 0; k < s->nr_cells; k++)
if (s->cells_top[k].nodeID == e->nodeID) {
struct cell *c = &s->cells_top[k];
for (int k = 0; k < s->nr_cells; k++) {
struct cell *c = &s->cells_top[k];
if (c->count > 0 || c->gcount > 0) {
/* Make the top-cells recurse */
engine_collect_kick(c);
......@@ -2458,15 +2456,16 @@ void engine_collect_timestep(struct engine *e) {
c->updated = 0;
c->g_updated = 0;
}
}
/* Aggregate the data from the different nodes. */
#ifdef WITH_MPI
{
int in_i[1], out_i[1];
integertime_t in_i[1], out_i[1];
in_i[0] = 0;
out_i[0] = ti_end_min;
if (MPI_Allreduce(out_i, in_i, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD) !=
MPI_SUCCESS)
if (MPI_Allreduce(out_i, in_i, 1, MPI_LONG_LONG_INT, MPI_MIN,
MPI_COMM_WORLD) != MPI_SUCCESS)
error("Failed to aggregate t_end_min.");
ti_end_min = in_i[0];
}
......@@ -2644,7 +2643,9 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs) {
if (e->nodeID == 0) message("Converting internal energy variable.");
/* Apply the conversion */
space_map_cells_pre(s, 0, cell_convert_hydro, NULL);
// space_map_cells_pre(s, 0, cell_convert_hydro, NULL);
for (size_t i = 0; i < s->nr_parts; ++i)
hydro_convert_quantities(&s->parts[i], &s->xparts[i]);
/* Correct what we did (e.g. in PE-SPH, need to recompute rho_bar) */
if (hydro_need_extra_init_loop) {
......@@ -2665,8 +2666,12 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs) {
clocks_gettime(&time2);
#ifdef SWIFT_DEBUG_CHECKS
space_check_timesteps(e->s);
#endif
/* Ready to go */
e->step = -1;
e->step = 0;
e->forcerebuild = 1;
e->wallclock_time = (float)clocks_diff(&time1, &time2);
......@@ -2757,12 +2762,6 @@ void engine_step(struct engine *e) {
engine_launch(e, e->nr_threads);
TIMER_TOC(timer_runners);
#ifdef SWIFT_DEBUG_CHECKS
for (size_t i = 0; i < e->s->nr_parts; ++i) {
if (e->s->parts[i].time_bin == 0) error("Particle in bin 0");
}
#endif
TIMER_TOC2(timer_step);
clocks_gettime(&time2);
......@@ -2805,6 +2804,11 @@ void engine_drift_all(struct engine *e) {
threadpool_map(&e->threadpool, runner_do_drift_mapper, e->s->cells_top,
e->s->nr_cells, sizeof(struct cell), 1, e);