/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
* Matthieu Schaller (schaller@strw.leidenuniv.nl)
* 2015 Peter W. Draper (p.w.draper@durham.ac.uk)
* Angus Lepper (angus.lepper@ed.ac.uk)
* 2016 John A. Regan (john.a.regan@durham.ac.uk)
* Tom Theuns (tom.theuns@durham.ac.uk)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
******************************************************************************/
/* Config parameters. */
#include
/* Some standard headers. */
#include
#include
/* MPI headers. */
#ifdef WITH_MPI
#include
#endif
/* Load the profiler header, if needed. */
#ifdef WITH_PROFILER
#include
#endif
/* This object's header. */
#include "engine.h"
/* Local headers. */
#include "adaptive_softening.h"
#include "atomic.h"
#include "cell.h"
#include "clocks.h"
#include "cycle.h"
#include "debug.h"
#include "error.h"
#include "feedback.h"
#include "neutrino_properties.h"
#include "proxy.h"
#include "rt_properties.h"
#include "timers.h"
extern int engine_max_parts_per_ghost;
extern int engine_max_sparts_per_ghost;
extern int engine_star_resort_task_depth;
extern int engine_max_parts_per_cooling;
/**
* @brief Add send tasks for the gravity pairs to a hierarchy of cells.
*
* @param e The #engine.
* @param ci The sending #cell.
* @param cj Dummy cell containing the nodeID of the receiving node.
* @param t_grav The send_grav #task, if it has already been created.
*/
void engine_addtasks_send_gravity(struct engine *e, struct cell *ci,
struct cell *cj, struct task *t_grav_counts,
struct task *t_grav,
const int with_star_formation) {
#ifdef WITH_MPI
struct link *l = NULL;
struct scheduler *s = &e->sched;
const int nodeID = cj->nodeID;
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(ci, cell_flag_has_tasks)) return;
if (t_grav_counts == NULL && with_star_formation && ci->hydro.count > 0) {
#ifdef SWIFT_DEBUG_CHECKS
if (ci->depth != 0)
error(
"Attaching a grav_count task at a non-top level c->depth=%d "
"c->count=%d",
ci->depth, ci->hydro.count);
#endif
t_grav_counts = scheduler_addtask(
s, task_type_send, task_subtype_grav_counts, ci->mpi.tag, 0, ci, cj);
scheduler_addunlock(s, ci->hydro.star_formation, t_grav_counts);
}
/* Check if any of the gravity tasks are for the target node. */
for (l = ci->grav.grav; l != NULL; l = l->next)
if (l->t->ci->nodeID == nodeID ||
(l->t->cj != NULL && l->t->cj->nodeID == nodeID))
break;
/* If so, attach send tasks. */
if (l != NULL) {
/* Create the tasks and their dependencies? */
if (t_grav == NULL) {
/* Make sure this cell is tagged. */
cell_ensure_tagged(ci);
t_grav = scheduler_addtask(s, task_type_send, task_subtype_gpart,
ci->mpi.tag, 0, ci, cj);
/* The sends should unlock the down pass. */
scheduler_addunlock(s, t_grav, ci->grav.super->grav.down);
if (with_star_formation && ci->top->hydro.count > 0)
scheduler_addunlock(s, t_grav, ci->top->hydro.star_formation);
/* Drift before you send */
scheduler_addunlock(s, ci->grav.super->grav.drift, t_grav);
if (gravity_after_hydro_density)
scheduler_addunlock(s, ci->grav.super->grav.init_out, t_grav);
}
/* Add them to the local cell. */
engine_addlink(e, &ci->mpi.send, t_grav);
if (with_star_formation && ci->hydro.count > 0) {
engine_addlink(e, &ci->mpi.send, t_grav_counts);
}
}
/* Recurse? */
if (ci->split)
for (int k = 0; k < 8; k++)
if (ci->progeny[k] != NULL)
engine_addtasks_send_gravity(e, ci->progeny[k], cj, t_grav_counts,
t_grav, with_star_formation);
#else
error("SWIFT was not compiled with MPI support.");
#endif
}
/**
* @brief Add send tasks for the hydro pairs to a hierarchy of cells.
*
* @param e The #engine.
* @param ci The sending #cell.
* @param cj Dummy cell containing the nodeID of the receiving node.
* @param t_xv The send_xv #task, if it has already been created.
* @param t_rho The send_rho #task, if it has already been created.
* @param t_gradient The send_gradient #task, if already created.
* @param t_prep1 The send_prep1 #task, if it has already been created.
* @param t_limiter The send_limiter #task, if it has already been created.
* @param t_rt_gradient The send_rt_gradient #task, if it has already been
* created.
* @param t_rt_transport The send_rt_transport #task, if it has already been
* @param with_feedback Are we running with stellar feedback?
* @param with_limiter Are we running with the time-step limiter?
* @param with_sync Are we running with time-step synchronization?
* @param with_rt Are we running with radiative transfer?
*/
void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
struct cell *cj, struct task *t_xv,
struct task *t_rho, struct task *t_gradient,
struct task *t_prep1, struct task *t_limiter,
struct task *t_pack_limiter,
struct task *t_rt_gradient,
struct task *t_rt_transport,
const int with_feedback, const int with_limiter,
const int with_sync, const int with_rt) {
#ifdef WITH_MPI
struct link *l = NULL;
struct scheduler *s = &e->sched;
const int nodeID = cj->nodeID;
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(ci, cell_flag_has_tasks)) return;
/* Check if any of the density tasks are for the target node. */
for (l = ci->hydro.density; l != NULL; l = l->next)
if (l->t->ci->nodeID == nodeID ||
(l->t->cj != NULL && l->t->cj->nodeID == nodeID))
break;
/* If so, attach send tasks. */
if (l != NULL) {
/* Create the tasks and their dependencies? */
if (t_xv == NULL) {
/* Make sure this cell is tagged. */
cell_ensure_tagged(ci);
t_xv = scheduler_addtask(s, task_type_send, task_subtype_xv, ci->mpi.tag,
0, ci, cj);
t_rho = scheduler_addtask(s, task_type_send, task_subtype_rho,
ci->mpi.tag, 0, ci, cj);
scheduler_addunlock(s, t_xv, t_rho);
#ifdef EXTRA_HYDRO_LOOP
t_gradient = scheduler_addtask(s, task_type_send, task_subtype_gradient,
ci->mpi.tag, 0, ci, cj);
scheduler_addunlock(s, t_rho, t_gradient);
#endif
if (with_limiter) {
t_limiter = scheduler_addtask(s, task_type_send, task_subtype_limiter,
ci->mpi.tag, 0, ci, cj);
t_pack_limiter = scheduler_addtask(s, task_type_pack,
task_subtype_limiter, 0, 0, ci, cj);
scheduler_addunlock(s, t_pack_limiter, t_limiter);
}
#ifdef EXTRA_STAR_LOOPS
if (with_feedback) {
t_prep1 = scheduler_addtask(s, task_type_send, task_subtype_part_prep1,
ci->mpi.tag, 0, ci, cj);
}
#endif
if (with_rt) {
/* Add the RT sends */
t_rt_gradient =
scheduler_addtask(s, task_type_send, task_subtype_rt_gradient,
ci->mpi.tag, 0, ci, cj);
t_rt_transport =
scheduler_addtask(s, task_type_send, task_subtype_rt_transport,
ci->mpi.tag, 0, ci, cj);
}
#ifdef EXTRA_HYDRO_LOOP
scheduler_addunlock(s, t_gradient, ci->hydro.super->hydro.end_force);
scheduler_addunlock(s, ci->hydro.super->hydro.extra_ghost, t_gradient);
/* The send_rho task should unlock the super_hydro-cell's extra_ghost
* task. */
scheduler_addunlock(s, t_rho, ci->hydro.super->hydro.extra_ghost);
/* The send_rho task depends on the cell's ghost task. */
scheduler_addunlock(s, ci->hydro.super->hydro.ghost_out, t_rho);
/* The send_xv task should unlock the super_hydro-cell's ghost task. */
scheduler_addunlock(s, t_xv, ci->hydro.super->hydro.ghost_in);
#else
/* The send_rho task should unlock the super_hydro-cell's kick task. */
scheduler_addunlock(s, t_rho, ci->hydro.super->hydro.end_force);
/* The send_rho task depends on the cell's ghost task. */
scheduler_addunlock(s, ci->hydro.super->hydro.ghost_out, t_rho);
/* The send_xv task should unlock the super_hydro-cell's ghost task. */
scheduler_addunlock(s, t_xv, ci->hydro.super->hydro.ghost_in);
#endif
scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_rho);
/* Drift before you send */
scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_xv);
if (with_limiter)
scheduler_addunlock(s, ci->super->timestep, t_pack_limiter);
#ifdef EXTRA_STAR_LOOPS
/* In stellar feedback, send gas parts only after they have finished their
* hydro ghosts */
if (with_feedback) {
scheduler_addunlock(s, ci->hydro.super->hydro.prep1_ghost, t_prep1);
scheduler_addunlock(s, t_prep1, ci->hydro.super->stars.prep2_ghost);
}
#endif
if (with_rt) {
/* Don't send the transport stuff before the gradient stuff */
scheduler_addunlock(s, t_rt_gradient, t_rt_transport);
/* The send_gradient task depends on the cell's ghost1 task. */
scheduler_addunlock(s, ci->hydro.super->rt.rt_ghost1, t_rt_gradient);
/* The send_transport task depends on the cell's ghost2 task. */
scheduler_addunlock(s, ci->hydro.super->rt.rt_ghost2, t_rt_transport);
/* Safety measure: collect dependencies and make sure data is sent
* before modifying it */
scheduler_addunlock(s, t_rt_gradient, ci->hydro.super->rt.rt_ghost2);
/* Safety measure: collect dependencies and make sure data is sent
* before modifying it */
scheduler_addunlock(s, t_rt_transport,
ci->hydro.super->rt.rt_transport_out);
/* Drift before you send. Especially intended to cover inactive cells
* being sent. */
scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_rt_gradient);
scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_rt_transport);
/* Make sure the gradient sends don't run before the xv is finished.
* This can occur when a cell itself is inactive for both hydro and
* RT, but needs to be sent over for some other cell's pair task.
* The rt_gradient - xv dependency is special because when received,
* these two tasks will/may activate the sorts.*/
scheduler_addunlock(s, t_xv, t_rt_gradient);
}
} /* if t_xv == NULL */
/* Add them to the local cell. */
engine_addlink(e, &ci->mpi.send, t_xv);
engine_addlink(e, &ci->mpi.send, t_rho);
#ifdef EXTRA_HYDRO_LOOP
engine_addlink(e, &ci->mpi.send, t_gradient);
#endif
if (with_limiter) {
engine_addlink(e, &ci->mpi.send, t_limiter);
engine_addlink(e, &ci->mpi.pack, t_pack_limiter);
}
#ifdef EXTRA_STAR_LOOPS
if (with_feedback) engine_addlink(e, &ci->mpi.send, t_prep1);
#endif
if (with_rt) {
/* Add them to the local cell. */
engine_addlink(e, &ci->mpi.send, t_rt_gradient);
engine_addlink(e, &ci->mpi.send, t_rt_transport);
}
}
/* Recurse? */
if (ci->split)
for (int k = 0; k < 8; k++)
if (ci->progeny[k] != NULL)
engine_addtasks_send_hydro(
e, ci->progeny[k], cj, t_xv, t_rho, t_gradient, t_prep1, t_limiter,
t_pack_limiter, t_rt_gradient, t_rt_transport, with_feedback,
with_limiter, with_sync, with_rt);
#else
error("SWIFT was not compiled with MPI support.");
#endif
}
/**
* @brief Add send tasks for the stars pairs to a hierarchy of cells.
*
* @param e The #engine.
* @param ci The sending #cell.
* @param cj Dummy cell containing the nodeID of the receiving node.
* @param t_density The send_density #task, if it has already been created.
* @param t_prep2 The send_prep2 #task, if it has already been created.
* @param t_sf_counts The send_sf_counts, if it has been created.
* @param with_star_formation Are we running with star formation on?
*/
void engine_addtasks_send_stars(struct engine *e, struct cell *ci,
struct cell *cj, struct task *t_density,
struct task *t_prep2, struct task *t_sf_counts,
const int with_star_formation) {
#ifdef WITH_MPI
#if !defined(SWIFT_DEBUG_CHECKS)
if (e->policy & engine_policy_sinks && e->policy & engine_policy_stars) {
error("TODO: Star formation sink over MPI");
}
#endif
struct link *l = NULL;
struct scheduler *s = &e->sched;
const int nodeID = cj->nodeID;
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(ci, cell_flag_has_tasks)) return;
if (t_sf_counts == NULL && with_star_formation && ci->hydro.count > 0) {
#ifdef SWIFT_DEBUG_CHECKS
if (ci->depth != 0)
error(
"Attaching a sf_count task at a non-top level c->depth=%d "
"c->count=%d",
ci->depth, ci->hydro.count);
#endif
t_sf_counts = scheduler_addtask(s, task_type_send, task_subtype_sf_counts,
ci->mpi.tag, 0, ci, cj);
scheduler_addunlock(s, ci->hydro.star_formation, t_sf_counts);
}
/* Check if any of the density tasks are for the target node. */
for (l = ci->stars.density; l != NULL; l = l->next)
if (l->t->ci->nodeID == nodeID ||
(l->t->cj != NULL && l->t->cj->nodeID == nodeID))
break;
/* If so, attach send tasks. */
if (l != NULL) {
if (t_density == NULL) {
/* Make sure this cell is tagged. */
cell_ensure_tagged(ci);
/* Create the tasks and their dependencies? */
t_density =
scheduler_addtask(s, task_type_send, task_subtype_spart_density,
ci->mpi.tag, 0, ci, cj);
#ifdef EXTRA_STAR_LOOPS
t_prep2 = scheduler_addtask(s, task_type_send, task_subtype_spart_prep2,
ci->mpi.tag, 0, ci, cj);
#endif
#ifdef EXTRA_STAR_LOOPS
/* The first send_stars task should unlock prep1 ghost */
scheduler_addunlock(s, t_density, ci->hydro.super->stars.prep1_ghost);
/* Prep2 ghost before second send */
scheduler_addunlock(s, ci->hydro.super->stars.prep2_ghost, t_prep2);
/* The second send_stars task should unlock the super_cell's "end of star
* block" task. */
scheduler_addunlock(s, t_prep2, ci->hydro.super->stars.stars_out);
#else
/* The send_stars task should unlock the super_cell's "end of star block"
* task. */
scheduler_addunlock(s, t_density, ci->hydro.super->stars.stars_out);
#endif
/* Density ghost before first send */
scheduler_addunlock(s, ci->hydro.super->stars.density_ghost, t_density);
/* Drift before first send */
scheduler_addunlock(s, ci->hydro.super->stars.drift, t_density);
if (with_star_formation && ci->hydro.count > 0) {
scheduler_addunlock(s, t_sf_counts, t_density);
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(s, t_sf_counts, t_prep2);
#endif
}
}
engine_addlink(e, &ci->mpi.send, t_density);
#ifdef EXTRA_STAR_LOOPS
engine_addlink(e, &ci->mpi.send, t_prep2);
#endif
if (with_star_formation && ci->hydro.count > 0) {
engine_addlink(e, &ci->mpi.send, t_sf_counts);
}
}
/* Recurse? */
if (ci->split)
for (int k = 0; k < 8; k++)
if (ci->progeny[k] != NULL)
engine_addtasks_send_stars(e, ci->progeny[k], cj, t_density, t_prep2,
t_sf_counts, with_star_formation);
#else
error("SWIFT was not compiled with MPI support.");
#endif
}
/**
* @brief Add send tasks for the black holes pairs to a hierarchy of cells.
*
* @param e The #engine.
* @param ci The sending #cell.
* @param cj Dummy cell containing the nodeID of the receiving node.
* @param t_rho The density comm. task, if it has already been created.
* @param t_bh_merger The BH swallow comm. task, if it has already been created.
* @param t_gas_swallow The gas swallow comm. task, if it has already been
* created.
* @param t_feedback The send_feed #task, if it has already been created.
*/
void engine_addtasks_send_black_holes(struct engine *e, struct cell *ci,
struct cell *cj, struct task *t_rho,
struct task *t_bh_merger,
struct task *t_gas_swallow,
struct task *t_feedback) {
#ifdef WITH_MPI
struct link *l = NULL;
struct scheduler *s = &e->sched;
const int nodeID = cj->nodeID;
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(ci, cell_flag_has_tasks)) return;
/* Check if any of the density tasks are for the target node. */
for (l = ci->black_holes.density; l != NULL; l = l->next)
if (l->t->ci->nodeID == nodeID ||
(l->t->cj != NULL && l->t->cj->nodeID == nodeID))
break;
/* If so, attach send tasks. */
if (l != NULL) {
if (t_rho == NULL) {
/* Make sure this cell is tagged. */
cell_ensure_tagged(ci);
/* Create the tasks and their dependencies? */
t_rho = scheduler_addtask(s, task_type_send, task_subtype_bpart_rho,
ci->mpi.tag, 0, ci, cj);
t_bh_merger = scheduler_addtask(
s, task_type_send, task_subtype_bpart_merger, ci->mpi.tag, 0, ci, cj);
t_gas_swallow = scheduler_addtask(
s, task_type_send, task_subtype_part_swallow, ci->mpi.tag, 0, ci, cj);
t_feedback =
scheduler_addtask(s, task_type_send, task_subtype_bpart_feedback,
ci->mpi.tag, 0, ci, cj);
/* The send_black_holes task should unlock the super_cell's BH exit point
* task. */
scheduler_addunlock(s, t_feedback,
ci->hydro.super->black_holes.black_holes_out);
scheduler_addunlock(s, ci->hydro.super->black_holes.swallow_ghost_3,
t_feedback);
/* Ghost before you send */
scheduler_addunlock(s, ci->hydro.super->black_holes.drift, t_rho);
scheduler_addunlock(s, ci->hydro.super->black_holes.density_ghost, t_rho);
scheduler_addunlock(s, t_rho,
ci->hydro.super->black_holes.swallow_ghost_1);
scheduler_addunlock(s, ci->hydro.super->black_holes.swallow_ghost_1,
t_bh_merger);
scheduler_addunlock(s, t_bh_merger,
ci->hydro.super->black_holes.swallow_ghost_3);
scheduler_addunlock(s, ci->hydro.super->black_holes.swallow_ghost_1,
t_gas_swallow);
scheduler_addunlock(s, t_gas_swallow,
ci->hydro.super->black_holes.swallow_ghost_2);
}
engine_addlink(e, &ci->mpi.send, t_rho);
engine_addlink(e, &ci->mpi.send, t_bh_merger);
engine_addlink(e, &ci->mpi.send, t_gas_swallow);
engine_addlink(e, &ci->mpi.send, t_feedback);
}
/* Recurse? */
if (ci->split)
for (int k = 0; k < 8; k++)
if (ci->progeny[k] != NULL)
engine_addtasks_send_black_holes(e, ci->progeny[k], cj, t_rho,
t_bh_merger, t_gas_swallow,
t_feedback);
#else
error("SWIFT was not compiled with MPI support.");
#endif
}
/**
* @brief Add recv tasks for hydro pairs to a hierarchy of cells.
*
* @param e The #engine.
* @param c The foreign #cell.
* @param t_xv The recv_xv #task, if it has already been created.
* @param t_rho The recv_rho #task, if it has already been created.
* @param t_gradient The recv_gradient #task, if it has already been created.
* @param t_prep1 The recv_prep1 #task, if it has already been created.
* @param t_limiter The recv_limiter #task, if it has already been created.
* @param t_unpack_limiter The unpack_limiter #task, if it has already been
* created.
* @param t_rt_gradient The recv_rt_gradient #task, if it has already been
* created.
* @param t_rt_transport The recv_rt_transport #task, if it has already been
* created.
* @param t_rt_sorts The rt_sort #task, if it has already been created.
* @param tend The top-level time-step communication #task.
* @param with_feedback Are we running with stellar feedback?
* @param with_black_holes Are we running with black holes?
* @param with_limiter Are we running with the time-step limiter?
* @param with_sync Are we running with time-step synchronization?
* @param with_rt Are we running with radiative transfer?
*/
void engine_addtasks_recv_hydro(
struct engine *e, struct cell *c, struct task *t_xv, struct task *t_rho,
struct task *t_gradient, struct task *t_prep1, struct task *t_limiter,
struct task *t_unpack_limiter, struct task *t_rt_gradient,
struct task *t_rt_transport, struct task *t_rt_sorts,
struct task *const tend, const int with_feedback,
const int with_black_holes, const int with_limiter, const int with_sync,
const int with_rt) {
#ifdef WITH_MPI
struct scheduler *s = &e->sched;
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
/* Have we reached a level where there are any hydro tasks ? */
if (t_xv == NULL && c->hydro.density != NULL) {
#ifdef SWIFT_DEBUG_CHECKS
/* Make sure this cell has a valid tag. */
if (c->mpi.tag < 0) error("Trying to receive from untagged cell.");
#endif /* SWIFT_DEBUG_CHECKS */
/* Create the tasks. */
t_xv = scheduler_addtask(s, task_type_recv, task_subtype_xv, c->mpi.tag, 0,
c, NULL);
t_rho = scheduler_addtask(s, task_type_recv, task_subtype_rho, c->mpi.tag,
0, c, NULL);
scheduler_addunlock(s, t_xv, t_rho);
#ifdef EXTRA_HYDRO_LOOP
t_gradient = scheduler_addtask(s, task_type_recv, task_subtype_gradient,
c->mpi.tag, 0, c, NULL);
scheduler_addunlock(s, t_xv, t_gradient);
scheduler_addunlock(s, t_rho, t_gradient);
#endif
if (with_limiter) {
t_limiter = scheduler_addtask(s, task_type_recv, task_subtype_limiter,
c->mpi.tag, 0, c, NULL);
t_unpack_limiter = scheduler_addtask(s, task_type_unpack,
task_subtype_limiter, 0, 0, c, NULL);
scheduler_addunlock(s, t_limiter, t_unpack_limiter);
}
#ifdef EXTRA_STAR_LOOPS
if (with_feedback) {
t_prep1 = scheduler_addtask(s, task_type_recv, task_subtype_part_prep1,
c->mpi.tag, 0, c, NULL);
}
#endif
if (with_rt) {
/* Create the tasks. */
t_rt_gradient = scheduler_addtask(
s, task_type_recv, task_subtype_rt_gradient, c->mpi.tag, 0, c, NULL);
t_rt_transport = scheduler_addtask(
s, task_type_recv, task_subtype_rt_transport, c->mpi.tag, 0, c, NULL);
/* Also create the rt_advance_cell_time tasks for the foreign cells
* for the sub-cycling. */
#ifdef SWIFT_RT_DEBUG_CHECKS
if (c->super == NULL)
error("trying to add rt_advance_cell_time above super level...");
if (c->top->rt.rt_collect_times == NULL) {
error("rt_collect_times should exist already");
}
if (c->super->rt.rt_advance_cell_time == NULL) {
error("rt_advance_cell_times should exist already");
}
#endif
/* Make sure we sort after receiving RT data. The hydro sorts may or may
* not be active. Blocking them with dependencies deadlocks with MPI. So
* add a new sort task instead, which will just do nothing if the cell is
* already sorted. */
t_rt_sorts = scheduler_addtask(s, task_type_rt_sort, task_subtype_none, 0,
0, c, NULL);
c->rt.rt_sorts = t_rt_sorts;
if (c->hydro.sorts != NULL) {
/* Copy task flags. While these should always be empty for sorts, better
* be safe than spend hours looking for this. */
t_rt_sorts->flags = c->hydro.sorts->flags;
/* Make sure the normal hydro sorts run before the RT sorts run. */
scheduler_addunlock(s, c->hydro.sorts, t_rt_sorts);
/* Don't run gradients on unsorted cells. */
scheduler_addunlock(s, c->hydro.sorts, t_rt_gradient);
}
/* Make sure the second receive doesn't get enqueued before the first one
* is done */
scheduler_addunlock(s, t_rt_gradient, t_rt_sorts);
scheduler_addunlock(s, t_rt_gradient, t_rt_transport);
/* Avoid situation where we receive while the sort hasn't finished yet. */
scheduler_addunlock(s, t_rt_sorts, t_rt_transport);
/* If one or both recv tasks are active, make sure the
* rt_advance_cell_time tasks doesn't run before them */
scheduler_addunlock(s, t_rt_gradient, c->super->rt.rt_advance_cell_time);
scheduler_addunlock(s, t_rt_transport, c->super->rt.rt_advance_cell_time);
/* Make sure the gradient recv don't run before the xv is finished.
* This can occur when a cell itself is inactive for both hydro and
* RT, but needs to be sent over for some other cell's pair task.
* For active cells, you must make sure that t_rho and t_gradient have
* been received first. As there is no guarantee which message will
* arrive first, you might overwrite data otherwise. */
scheduler_addunlock(s, t_xv, t_rt_gradient);
scheduler_addunlock(s, t_rho, t_rt_gradient);
#ifdef EXTRA_HYDRO_LOOP
scheduler_addunlock(s, t_gradient, t_rt_gradient);
#endif
}
}
if (t_xv != NULL) {
engine_addlink(e, &c->mpi.recv, t_xv);
engine_addlink(e, &c->mpi.recv, t_rho);
#ifdef EXTRA_HYDRO_LOOP
engine_addlink(e, &c->mpi.recv, t_gradient);
#endif
if (with_limiter) {
engine_addlink(e, &c->mpi.recv, t_limiter);
engine_addlink(e, &c->mpi.unpack, t_unpack_limiter);
}
#ifdef EXTRA_STAR_LOOPS
if (with_feedback) engine_addlink(e, &c->mpi.recv, t_prep1);
#endif
/* Add dependencies. */
if (c->hydro.sorts != NULL) {
scheduler_addunlock(s, t_xv, c->hydro.sorts);
scheduler_addunlock(s, c->hydro.sorts, t_rho);
#if defined(MPI_SYMMETRIC_FORCE_INTERACTION) && defined(EXTRA_HYDRO_LOOP)
scheduler_addunlock(s, c->hydro.sorts, t_gradient);
#endif
}
for (struct link *l = c->hydro.density; l != NULL; l = l->next) {
scheduler_addunlock(s, t_xv, l->t);
scheduler_addunlock(s, l->t, t_rho);
}
#ifdef EXTRA_HYDRO_LOOP
for (struct link *l = c->hydro.gradient; l != NULL; l = l->next) {
scheduler_addunlock(s, t_rho, l->t);
scheduler_addunlock(s, l->t, t_gradient);
}
for (struct link *l = c->hydro.force; l != NULL; l = l->next) {
scheduler_addunlock(s, t_gradient, l->t);
scheduler_addunlock(s, l->t, tend);
}
#else
for (struct link *l = c->hydro.force; l != NULL; l = l->next) {
scheduler_addunlock(s, t_rho, l->t);
scheduler_addunlock(s, l->t, tend);
}
#endif
if (with_limiter) {
for (struct link *l = c->hydro.limiter; l != NULL; l = l->next) {
scheduler_addunlock(s, t_unpack_limiter, l->t);
}
}
/* Make sure the gas density has been computed before the
* stars compute theirs. */
if (with_feedback) {
for (struct link *l = c->stars.density; l != NULL; l = l->next) {
scheduler_addunlock(s, t_rho, l->t);
}
}
#ifdef EXTRA_STAR_LOOPS
if (with_feedback) {
/* Receive gas parts after everything is finished in prep1 loop */
for (struct link *l = c->stars.prepare1; l != NULL; l = l->next) {
scheduler_addunlock(s, l->t, t_prep1);
}
/* Start updating stars in prep2 only after the updated gas parts have
* been received */
for (struct link *l = c->stars.prepare2; l != NULL; l = l->next) {
scheduler_addunlock(s, t_prep1, l->t);
}
}
#endif
/* Make sure the part have been received before the BHs compute their
* accretion rates (depends on particles' rho). */
if (with_black_holes) {
for (struct link *l = c->black_holes.density; l != NULL; l = l->next) {
/* t_rho is not activated for cells with no active hydro, so we need
to add an additional dependency on t_xv for these cells */
scheduler_addunlock(s, t_xv, l->t);
scheduler_addunlock(s, t_rho, l->t);
}
}
if (with_rt) {
engine_addlink(e, &c->mpi.recv, t_rt_gradient);
engine_addlink(e, &c->mpi.recv, t_rt_transport);
/* RT recvs mustn't run before hydro force has completed. */
for (struct link *l = c->hydro.force; l != NULL; l = l->next) {
scheduler_addunlock(s, l->t, t_rt_gradient);
}
for (struct link *l = c->rt.rt_gradient; l != NULL; l = l->next) {
/* RT gradient tasks mustn't run before we receive necessary data */
scheduler_addunlock(s, t_rt_gradient, l->t);
/* Don't run gradient tasks without sorting */
scheduler_addunlock(s, t_rt_sorts, l->t);
/* Don't update local particles before gradient tasks are finished */
scheduler_addunlock(s, l->t, t_rt_transport);
}
for (struct link *l = c->rt.rt_transport; l != NULL; l = l->next) {
/* RT transport tasks (iact, not comm tasks!!) mustn't run before we
* receive necessary data */
scheduler_addunlock(s, t_rt_transport, l->t);
/* add dependency for the timestep communication tasks. In cases where
* RT is inactive, rt_advance_cell_time won't run, so we need to make
* sure we don't receive data before we're done with all the work. */
scheduler_addunlock(s, l->t, tend);
/* advance cell time mustn't run before transport is done */
scheduler_addunlock(s, l->t, c->super->rt.rt_advance_cell_time);
}
}
}
/* Recurse? */
if (c->split)
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL)
engine_addtasks_recv_hydro(
e, c->progeny[k], t_xv, t_rho, t_gradient, t_prep1, t_limiter,
t_unpack_limiter, t_rt_gradient, t_rt_transport, t_rt_sorts, tend,
with_feedback, with_black_holes, with_limiter, with_sync, with_rt);
#else
error("SWIFT was not compiled with MPI support.");
#endif
}
/**
* @brief Add time rt_advance_cell_time tasks to super levels of
* foreign cells. This function recurses down to the super level
* and creates the required tasks, and adds a dependency between
* rt_advance_cell_time, rt_collect_times, and tend tasks.
*
* In normal steps, tend mustn't run before rt_advance_cell_time or the
* cell's ti_rt_end_min will be updated wrongly. In sub-cycles, we don't
* have the tend tasks, so there's no worry about that. (Them missing is
* the reason we need the rt_advanced_cell_time to complete the
* sub-cycles in the first place)
*
* @param e The #engine.
* @param c The foreign #cell.
* @param tend The top-level time-step communication #task.
*/
void engine_addtasks_recv_rt_advance_cell_time(struct engine *e, struct cell *c,
struct task *const tend) {
#ifdef WITH_MPI
struct scheduler *s = &e->sched;
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
/* Have we reached the super level? */
if (c->super == c) {
#ifdef SWIFT_RT_DEBUG_CHECKS
if (c->super == NULL)
error("trying to add rt_advance_cell_time above super level...");
if (c->top->rt.rt_collect_times == NULL)
error("rt_collect_times should have been created already????");
#endif
/* Create the rt advance times task at the super level, if it hasn't
* already. also set all the dependencies */
if (c->rt.rt_advance_cell_time == NULL) {
c->rt.rt_advance_cell_time = scheduler_addtask(
s, task_type_rt_advance_cell_time, task_subtype_none, 0, 0, c, NULL);
/* don't run collect times before you run advance cell time */
scheduler_addunlock(s, c->rt.rt_advance_cell_time,
c->top->rt.rt_collect_times);
/* Add the dependency */
scheduler_addunlock(s, c->super->rt.rt_advance_cell_time, tend);
}
/* we're done. */
return;
}
/* Recurse? */
if (c->split)
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL)
engine_addtasks_recv_rt_advance_cell_time(e, c->progeny[k], tend);
#else
error("SWIFT was not compiled with MPI support.");
#endif
}
/**
* @brief Add recv tasks for stars pairs to a hierarchy of cells.
*
* @param e The #engine.
* @param c The foreign #cell.
* @param t_density The recv_density #task, if it has already been created.
* @param t_prep2 The recv_prep2 #task, if it has already been created.
* @param t_sf_counts The recv_sf_counts, if it has been created.
* @param tend The top-level time-step communication #task.
* @param with_star_formation Are we running with star formation on?
*/
void engine_addtasks_recv_stars(struct engine *e, struct cell *c,
struct task *t_density, struct task *t_prep2,
struct task *t_sf_counts,
struct task *const tend,
const int with_star_formation) {
#ifdef WITH_MPI
#if !defined(SWIFT_DEBUG_CHECKS)
if (e->policy & engine_policy_sinks && e->policy & engine_policy_stars) {
error("TODO: Star formation sink over MPI");
}
#endif
struct scheduler *s = &e->sched;
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
if (t_sf_counts == NULL && with_star_formation && c->hydro.count > 0) {
#ifdef SWIFT_DEBUG_CHECKS
if (c->depth != 0)
error(
"Attaching a sf_count task at a non-top level c->depth=%d "
"c->count=%d",
c->depth, c->hydro.count);
#endif
t_sf_counts = scheduler_addtask(s, task_type_recv, task_subtype_sf_counts,
c->mpi.tag, 0, c, NULL);
}
/* Have we reached a level where there are any stars tasks ? */
if (t_density == NULL && c->stars.density != NULL) {
#ifdef SWIFT_DEBUG_CHECKS
/* Make sure this cell has a valid tag. */
if (c->mpi.tag < 0) error("Trying to receive from untagged cell.");
#endif // SWIFT_DEBUG_CHECKS
/* Create the tasks. */
t_density = scheduler_addtask(s, task_type_recv, task_subtype_spart_density,
c->mpi.tag, 0, c, NULL);
#ifdef EXTRA_STAR_LOOPS
t_prep2 = scheduler_addtask(s, task_type_recv, task_subtype_spart_prep2,
c->mpi.tag, 0, c, NULL);
#endif
if (with_star_formation && c->hydro.count > 0) {
/* Receive the stars only once the counts have been received */
scheduler_addunlock(s, t_sf_counts, c->stars.sorts);
scheduler_addunlock(s, t_sf_counts, t_density);
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(s, t_sf_counts, t_prep2);
#endif
}
}
if (t_density != NULL) {
engine_addlink(e, &c->mpi.recv, t_density);
#ifdef EXTRA_STAR_LOOPS
engine_addlink(e, &c->mpi.recv, t_prep2);
#endif
if (with_star_formation && c->hydro.count > 0) {
engine_addlink(e, &c->mpi.recv, t_sf_counts);
}
#ifdef SWIFT_DEBUG_CHECKS
if (c->nodeID == e->nodeID) error("Local cell!");
#endif
if (c->stars.sorts != NULL) {
scheduler_addunlock(s, t_density, c->stars.sorts);
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(s, c->stars.sorts, t_prep2);
#endif
}
/* Receive stars after the density loop */
for (struct link *l = c->stars.density; l != NULL; l = l->next) {
scheduler_addunlock(s, l->t, t_density);
}
#ifdef EXTRA_STAR_LOOPS
/* Start updating local gas only after sparts have been received */
for (struct link *l = c->stars.prepare1; l != NULL; l = l->next) {
scheduler_addunlock(s, t_density, l->t);
scheduler_addunlock(s, l->t, t_prep2);
}
/* Receive stars for the second time after the prep2 loop */
for (struct link *l = c->stars.prepare2; l != NULL; l = l->next) {
scheduler_addunlock(s, l->t, t_prep2);
}
/* Start updating local gas only after sparts have been received */
for (struct link *l = c->stars.feedback; l != NULL; l = l->next) {
scheduler_addunlock(s, t_prep2, l->t);
scheduler_addunlock(s, l->t, tend);
}
#else
/* Start updating local gas only after sparts have been received */
for (struct link *l = c->stars.feedback; l != NULL; l = l->next) {
scheduler_addunlock(s, t_density, l->t);
scheduler_addunlock(s, l->t, tend);
}
#endif
}
/* Recurse? */
if (c->split)
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL)
engine_addtasks_recv_stars(e, c->progeny[k], t_density, t_prep2,
t_sf_counts, tend, with_star_formation);
#else
error("SWIFT was not compiled with MPI support.");
#endif
}
/**
* @brief Add recv tasks for black_holes pairs to a hierarchy of cells.
*
* @param e The #engine.
* @param c The foreign #cell.
* @param t_rho The density comm. task, if it has already been created.
* @param t_bh_merger The BH swallow comm. task, if it has already been created.
* @param t_gas_swallow The gas swallow comm. task, if it has already been
* created.
* @param t_feedback The recv_feed #task, if it has already been created.
* @param tend The top-level time-step communication #task.
*/
void engine_addtasks_recv_black_holes(struct engine *e, struct cell *c,
struct task *t_rho,
struct task *t_bh_merger,
struct task *t_gas_swallow,
struct task *t_feedback,
struct task *const tend) {
#ifdef WITH_MPI
struct scheduler *s = &e->sched;
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
/* Have we reached a level where there are any black_holes tasks ? */
if (t_rho == NULL && c->black_holes.density != NULL) {
#ifdef SWIFT_DEBUG_CHECKS
/* Make sure this cell has a valid tag. */
if (c->mpi.tag < 0) error("Trying to receive from untagged cell.");
#endif // SWIFT_DEBUG_CHECKS
/* Create the tasks. */
t_rho = scheduler_addtask(s, task_type_recv, task_subtype_bpart_rho,
c->mpi.tag, 0, c, NULL);
t_bh_merger = scheduler_addtask(
s, task_type_recv, task_subtype_bpart_merger, c->mpi.tag, 0, c, NULL);
t_gas_swallow = scheduler_addtask(
s, task_type_recv, task_subtype_part_swallow, c->mpi.tag, 0, c, NULL);
t_feedback = scheduler_addtask(
s, task_type_recv, task_subtype_bpart_feedback, c->mpi.tag, 0, c, NULL);
}
if (t_rho != NULL) {
engine_addlink(e, &c->mpi.recv, t_rho);
engine_addlink(e, &c->mpi.recv, t_bh_merger);
engine_addlink(e, &c->mpi.recv, t_gas_swallow);
engine_addlink(e, &c->mpi.recv, t_feedback);
#ifdef SWIFT_DEBUG_CHECKS
if (c->nodeID == e->nodeID) error("Local cell!");
#endif
for (struct link *l = c->black_holes.density; l != NULL; l = l->next) {
scheduler_addunlock(s, l->t, t_rho);
}
for (struct link *l = c->hydro.force; l != NULL; l = l->next) {
scheduler_addunlock(s, l->t, t_gas_swallow);
}
for (struct link *l = c->black_holes.swallow; l != NULL; l = l->next) {
scheduler_addunlock(s, t_rho, l->t);
scheduler_addunlock(s, l->t, t_gas_swallow);
scheduler_addunlock(s, l->t, t_bh_merger);
}
for (struct link *l = c->black_holes.do_gas_swallow; l != NULL;
l = l->next) {
scheduler_addunlock(s, t_gas_swallow, l->t);
}
for (struct link *l = c->black_holes.do_bh_swallow; l != NULL;
l = l->next) {
scheduler_addunlock(s, t_bh_merger, l->t);
scheduler_addunlock(s, l->t, t_feedback);
}
for (struct link *l = c->black_holes.feedback; l != NULL; l = l->next) {
scheduler_addunlock(s, t_feedback, l->t);
scheduler_addunlock(s, l->t, tend);
}
}
/* Recurse? */
if (c->split)
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL)
engine_addtasks_recv_black_holes(e, c->progeny[k], t_rho, t_bh_merger,
t_gas_swallow, t_feedback, tend);
#else
error("SWIFT was not compiled with MPI support.");
#endif
}
/**
* @brief Add recv tasks for gravity pairs to a hierarchy of cells.
*
* @param e The #engine.
* @param c The foreign #cell.
* @param t_grav The recv_gpart #task, if it has already been created.
* @param tend The top-level time-step communication #task.
*/
void engine_addtasks_recv_gravity(struct engine *e, struct cell *c,
struct task *t_grav_counts,
struct task *t_grav, struct task *const tend,
const int with_star_formation) {
#ifdef WITH_MPI
struct scheduler *s = &e->sched;
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
if (t_grav_counts == NULL && with_star_formation && c->hydro.count > 0) {
#ifdef SWIFT_DEBUG_CHECKS
if (c->depth != 0)
error(
"Attaching a grav_count task at a non-top level c->depth=%d "
"c->count=%d",
c->depth, c->hydro.count);
#endif
t_grav_counts = scheduler_addtask(
s, task_type_recv, task_subtype_grav_counts, c->mpi.tag, 0, c, NULL);
}
/* Have we reached a level where there are any gravity tasks ? */
if (t_grav == NULL && c->grav.grav != NULL) {
#ifdef SWIFT_DEBUG_CHECKS
/* Make sure this cell has a valid tag. */
if (c->mpi.tag < 0) error("Trying to receive from untagged cell.");
#endif // SWIFT_DEBUG_CHECKS
/* Create the tasks. */
t_grav = scheduler_addtask(s, task_type_recv, task_subtype_gpart,
c->mpi.tag, 0, c, NULL);
if (t_grav_counts != NULL) scheduler_addunlock(s, t_grav, t_grav_counts);
}
/* If we have tasks, link them. */
if (t_grav != NULL) {
engine_addlink(e, &c->mpi.recv, t_grav);
if (with_star_formation && c->hydro.count > 0) {
engine_addlink(e, &c->mpi.recv, t_grav_counts);
}
for (struct link *l = c->grav.grav; l != NULL; l = l->next) {
scheduler_addunlock(s, t_grav, l->t);
scheduler_addunlock(s, l->t, tend);
}
}
/* Recurse? */
if (c->split)
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL)
engine_addtasks_recv_gravity(e, c->progeny[k], t_grav_counts, t_grav,
tend, with_star_formation);
#else
error("SWIFT was not compiled with MPI support.");
#endif
}
/**
* @brief Generate the hydro hierarchical tasks for a hierarchy of cells -
* i.e. all the O(Npart) tasks -- timestep version
*
* Tasks are only created here. The dependencies will be added later on.
*
* Note that there is no need to recurse below the super-cell. Note also
* that we only add tasks if the relevant particles are present in the cell.
*
* @param e The #engine.
* @param c The #cell.
*/
void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) {
struct scheduler *s = &e->sched;
const int with_sinks = (e->policy & engine_policy_sinks);
const int with_stars = (e->policy & engine_policy_stars);
const int with_star_formation = (e->policy & engine_policy_star_formation);
const int with_star_formation_sink = with_sinks && with_stars;
const int with_timestep_limiter =
(e->policy & engine_policy_timestep_limiter);
const int with_timestep_sync = (e->policy & engine_policy_timestep_sync);
const int with_rt = (e->policy & engine_policy_rt);
#ifdef WITH_CSDS
const int with_csds = e->policy & engine_policy_csds;
#endif
/* Are we at the top-level? */
if (c->top == c && c->nodeID == e->nodeID) {
if (c->hydro.count > 0 || c->grav.count > 0 || c->stars.count > 0 ||
c->black_holes.count > 0 || c->sinks.count > 0) {
c->timestep_collect = scheduler_addtask(s, task_type_collect,
task_subtype_none, 0, 0, c, NULL);
}
if (with_star_formation && c->hydro.count > 0) {
c->hydro.star_formation = scheduler_addtask(
s, task_type_star_formation, task_subtype_none, 0, 0, c, NULL);
}
if (with_star_formation_sink &&
(c->hydro.count > 0 || c->sinks.count > 0)) {
c->sinks.star_formation_sink = scheduler_addtask(
s, task_type_star_formation_sink, task_subtype_none, 0, 0, c, NULL);
}
if (with_sinks) {
/* sinks.sink_formation plays the role of a ghost => always created when
* playing with sinks*/
c->sinks.sink_formation = scheduler_addtask(
s, task_type_sink_formation, task_subtype_none, 0, 0, c, NULL);
}
if (with_rt) {
c->rt.rt_collect_times = scheduler_addtask(
s, task_type_rt_collect_times, task_subtype_none, 0, 0, c, NULL);
}
}
/* Are we in a super-cell ? */
if (c->super == c) {
/* Local tasks only... */
if (c->nodeID == e->nodeID) {
/* Add the two half kicks */
c->kick1 = scheduler_addtask(s, task_type_kick1, task_subtype_none, 0, 0,
c, NULL);
c->kick2 = scheduler_addtask(s, task_type_kick2, task_subtype_none, 0, 0,
c, NULL);
/* Weighting task for neutrinos after the last kick */
if (e->neutrino_properties->use_delta_f) {
c->grav.neutrino_weight = scheduler_addtask(
s, task_type_neutrino_weight, task_subtype_none, 0, 0, c, NULL);
scheduler_addunlock(s, c->kick1, c->grav.neutrino_weight);
}
#if defined(WITH_CSDS)
struct task *kick2_or_csds;
if (with_csds) {
/* Add the hydro csds task. */
c->csds = scheduler_addtask(s, task_type_csds, task_subtype_none, 0, 0,
c, NULL);
/* Add the kick2 dependency */
scheduler_addunlock(s, c->kick2, c->csds);
/* Create a variable in order to avoid to many ifdef */
kick2_or_csds = c->csds;
} else {
kick2_or_csds = c->kick2;
}
#else
struct task *kick2_or_csds = c->kick2;
#endif
/* Add the time-step calculation task and its dependency */
c->timestep = scheduler_addtask(s, task_type_timestep, task_subtype_none,
0, 0, c, NULL);
scheduler_addunlock(s, kick2_or_csds, c->timestep);
scheduler_addunlock(s, c->timestep, c->kick1);
scheduler_addunlock(s, c->timestep, c->top->timestep_collect);
/* Subgrid tasks: star formation */
if (with_star_formation && c->hydro.count > 0) {
scheduler_addunlock(s, kick2_or_csds, c->top->hydro.star_formation);
scheduler_addunlock(s, c->top->hydro.star_formation, c->timestep);
}
/* Subgrid tasks: star formation from sinks */
if (with_star_formation_sink &&
(c->hydro.count > 0 || c->sinks.count > 0)) {
scheduler_addunlock(s, kick2_or_csds,
c->top->sinks.star_formation_sink);
scheduler_addunlock(s, c->top->sinks.star_formation_sink, c->timestep);
}
/* Subgrid tasks: sinks formation */
if (with_sinks) {
scheduler_addunlock(s, c->kick2, c->top->sinks.sink_formation);
}
/* Time-step limiter */
if (with_timestep_limiter) {
c->timestep_limiter = scheduler_addtask(
s, task_type_timestep_limiter, task_subtype_none, 0, 0, c, NULL);
scheduler_addunlock(s, c->timestep, c->timestep_limiter);
scheduler_addunlock(s, c->timestep_limiter, c->kick1);
scheduler_addunlock(s, c->timestep_limiter, c->top->timestep_collect);
}
/* Time-step synchronization */
if (with_timestep_sync) {
c->timestep_sync = scheduler_addtask(s, task_type_timestep_sync,
task_subtype_none, 0, 0, c, NULL);
scheduler_addunlock(s, c->timestep, c->timestep_sync);
scheduler_addunlock(s, c->timestep_sync, c->kick1);
scheduler_addunlock(s, c->timestep_sync, c->top->timestep_collect);
}
if (with_timestep_limiter && with_timestep_sync) {
scheduler_addunlock(s, c->timestep_limiter, c->timestep_sync);
}
}
} else { /* We are above the super-cell so need to go deeper */
/* Recurse. */
if (c->split)
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL)
engine_make_hierarchical_tasks_common(e, c->progeny[k]);
}
}
/**
* @brief Generate the hydro hierarchical tasks for a hierarchy of cells -
* i.e. all the O(Npart) tasks -- gravity version
*
* Tasks are only created here. The dependencies will be added later on.
*
* Note that there is no need to recurse below the super-cell. Note also
* that we only add tasks if the relevant particles are present in the cell.
*
* @param e The #engine.
* @param c The #cell.
*/
void engine_make_hierarchical_tasks_gravity(struct engine *e, struct cell *c) {
struct scheduler *s = &e->sched;
const int is_self_gravity = (e->policy & engine_policy_self_gravity);
const int stars_only_gravity =
(e->policy & engine_policy_stars) && !(e->policy & engine_policy_hydro);
/* Are we in a super-cell ? */
if (c->grav.super == c) {
/* Local tasks only... */
if (c->nodeID == e->nodeID) {
if (stars_only_gravity) {
/* In the special case where we have stars that just act under gravity
* we must create their drift task here and not just copy over the hydro
* behaviour. */
c->stars.drift = scheduler_addtask(s, task_type_drift_spart,
task_subtype_none, 0, 0, c, NULL);
scheduler_addunlock(s, c->stars.drift, c->super->kick2);
}
c->grav.drift = scheduler_addtask(s, task_type_drift_gpart,
task_subtype_none, 0, 0, c, NULL);
c->grav.end_force = scheduler_addtask(s, task_type_end_grav_force,
task_subtype_none, 0, 0, c, NULL);
scheduler_addunlock(s, c->grav.end_force, c->super->kick2);
if (is_self_gravity) {
/* Initialisation of the multipoles */
c->grav.init = scheduler_addtask(s, task_type_init_grav,
task_subtype_none, 0, 0, c, NULL);
/* Gravity non-neighbouring pm calculations */
c->grav.long_range = scheduler_addtask(
s, task_type_grav_long_range, task_subtype_none, 0, 0, c, NULL);
/* Gravity recursive down-pass */
c->grav.down = scheduler_addtask(s, task_type_grav_down,
task_subtype_none, 0, 0, c, NULL);
/* Implicit tasks for the up and down passes */
c->grav.drift_out = scheduler_addtask(s, task_type_drift_gpart_out,
task_subtype_none, 0, 1, c, NULL);
c->grav.init_out = scheduler_addtask(s, task_type_init_grav_out,
task_subtype_none, 0, 1, c, NULL);
c->grav.down_in = scheduler_addtask(s, task_type_grav_down_in,
task_subtype_none, 0, 1, c, NULL);
/* Long-range gravity forces (not the mesh ones!) */
scheduler_addunlock(s, c->grav.init, c->grav.long_range);
scheduler_addunlock(s, c->grav.long_range, c->grav.down);
scheduler_addunlock(s, c->grav.down, c->grav.super->grav.end_force);
/* With adaptive softening, force the hydro density to complete first */
if (gravity_after_hydro_density && c->hydro.super == c) {
scheduler_addunlock(s, c->hydro.ghost_out, c->grav.init_out);
}
/* Link in the implicit tasks */
scheduler_addunlock(s, c->grav.init, c->grav.init_out);
scheduler_addunlock(s, c->grav.drift, c->grav.drift_out);
scheduler_addunlock(s, c->grav.down_in, c->grav.down);
}
}
}
/* We are below the super-cell but not below the maximal splitting depth */
else if ((c->grav.super != NULL) &&
((c->maxdepth - c->depth) >= space_subdepth_diff_grav)) {
/* Local tasks only... */
if (c->nodeID == e->nodeID) {
if (is_self_gravity) {
c->grav.drift_out = scheduler_addtask(s, task_type_drift_gpart_out,
task_subtype_none, 0, 1, c, NULL);
c->grav.init_out = scheduler_addtask(s, task_type_init_grav_out,
task_subtype_none, 0, 1, c, NULL);
c->grav.down_in = scheduler_addtask(s, task_type_grav_down_in,
task_subtype_none, 0, 1, c, NULL);
scheduler_addunlock(s, c->parent->grav.init_out, c->grav.init_out);
scheduler_addunlock(s, c->parent->grav.drift_out, c->grav.drift_out);
scheduler_addunlock(s, c->grav.down_in, c->parent->grav.down_in);
}
}
}
/* Recurse but not below the maximal splitting depth */
if (c->split && ((c->maxdepth - c->depth) >= space_subdepth_diff_grav))
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL)
engine_make_hierarchical_tasks_gravity(e, c->progeny[k]);
}
/**
* @brief Recursively add non-implicit ghost tasks to a cell hierarchy.
*/
void engine_add_ghosts(struct engine *e, struct cell *c, struct task *ghost_in,
struct task *ghost_out) {
/* Abort as there are no hydro particles here? */
if (c->hydro.count_total == 0) return;
/* If we have reached the leaf OR have to few particles to play with*/
if (!c->split || c->hydro.count_total < engine_max_parts_per_ghost) {
/* Add the ghost task and its dependencies */
struct scheduler *s = &e->sched;
c->hydro.ghost =
scheduler_addtask(s, task_type_ghost, task_subtype_none, 0, 0, c, NULL);
scheduler_addunlock(s, ghost_in, c->hydro.ghost);
scheduler_addunlock(s, c->hydro.ghost, ghost_out);
} else {
/* Keep recursing */
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL)
engine_add_ghosts(e, c->progeny[k], ghost_in, ghost_out);
}
}
/**
* @brief Recursively add non-implicit cooling tasks to a cell hierarchy.
*/
void engine_add_cooling(struct engine *e, struct cell *c,
struct task *cooling_in, struct task *cooling_out) {
/* Abort as there are no hydro particles here? */
if (c->hydro.count_total == 0) return;
/* If we have reached the leaf OR have to few particles to play with*/
if (!c->split || c->hydro.count_total < engine_max_parts_per_cooling) {
/* Add the cooling task and its dependencies */
struct scheduler *s = &e->sched;
c->hydro.cooling = scheduler_addtask(s, task_type_cooling,
task_subtype_none, 0, 0, c, NULL);
scheduler_addunlock(s, cooling_in, c->hydro.cooling);
scheduler_addunlock(s, c->hydro.cooling, cooling_out);
} else {
/* Keep recursing */
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL)
engine_add_cooling(e, c->progeny[k], cooling_in, cooling_out);
}
}
/**
* @brief Generate the hydro hierarchical tasks for a hierarchy of cells -
* i.e. all the O(Npart) tasks -- hydro version
*
* Tasks are only created here. The dependencies will be added later on.
*
* Note that there is no need to recurse below the super-cell. Note also
* that we only add tasks if the relevant particles are present in the cell.
*
* @param e The #engine.
* @param c The #cell.
* @param star_resort_cell Pointer to the cell where the star_resort task has
* been created. NULL above that level or if not running with star formation.
*/
void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c,
struct cell *star_resort_cell) {
struct scheduler *s = &e->sched;
const int with_stars = (e->policy & engine_policy_stars);
const int with_sinks = (e->policy & engine_policy_sinks);
const int with_feedback = (e->policy & engine_policy_feedback);
const int with_cooling = (e->policy & engine_policy_cooling);
const int with_star_formation = (e->policy & engine_policy_star_formation);
const int with_star_formation_sink = (with_sinks && with_stars);
const int with_black_holes = (e->policy & engine_policy_black_holes);
const int with_rt = (e->policy & engine_policy_rt);
const int with_timestep_sync = (e->policy & engine_policy_timestep_sync);
#ifdef WITH_CSDS
const int with_csds = (e->policy & engine_policy_csds);
#endif
/* Are we are the level where we create the stars' resort tasks?
* If the tree is shallow, we need to do this at the super-level if the
* super-level is above the level we want */
if ((c->nodeID == e->nodeID) && (star_resort_cell == NULL) &&
(c->depth == engine_star_resort_task_depth || c->hydro.super == c)) {
/* If star formation from gas or sinks has happened, we need to resort */
if (with_feedback && ((c->hydro.count > 0 && with_star_formation) ||
((c->hydro.count > 0 || c->sinks.count > 0) &&
with_star_formation_sink))) {
/* Record this is the level where we re-sort */
star_resort_cell = c;
c->hydro.stars_resort = scheduler_addtask(
s, task_type_stars_resort, task_subtype_none, 0, 0, c, NULL);
/* Now add the relevant unlocks */
/* If we can make stars, we should wait until SF is done before resorting
*/
if (with_star_formation && c->hydro.count > 0) {
scheduler_addunlock(s, c->top->hydro.star_formation,
c->hydro.stars_resort);
}
/* If we can make sinks or spawn from existing ones, we should wait until
SF is done before resorting */
if (with_star_formation_sink &&
(c->hydro.count > 0 || c->sinks.count > 0)) {
scheduler_addunlock(s, c->top->sinks.star_formation_sink,
c->hydro.stars_resort);
}
}
}
/* Are we in a super-cell ? */
if (c->hydro.super == c) {
/* Add the sort task. */
c->hydro.sorts =
scheduler_addtask(s, task_type_sort, task_subtype_none, 0, 0, c, NULL);
if (with_feedback) {
c->stars.sorts = scheduler_addtask(s, task_type_stars_sort,
task_subtype_none, 0, 0, c, NULL);
}
if (with_black_holes) {
c->black_holes.swallow_ghost_1 =
scheduler_addtask(s, task_type_bh_swallow_ghost1, task_subtype_none,
0, /* implicit =*/1, c, NULL);
}
/* Local tasks only... */
if (c->nodeID == e->nodeID) {
/* Add the drift task. */
c->hydro.drift = scheduler_addtask(s, task_type_drift_part,
task_subtype_none, 0, 0, c, NULL);
/* Add the task finishing the force calculation */
c->hydro.end_force = scheduler_addtask(s, task_type_end_hydro_force,
task_subtype_none, 0, 0, c, NULL);
/* Generate the ghost tasks. */
c->hydro.ghost_in =
scheduler_addtask(s, task_type_ghost_in, task_subtype_none, 0,
/* implicit = */ 1, c, NULL);
c->hydro.ghost_out =
scheduler_addtask(s, task_type_ghost_out, task_subtype_none, 0,
/* implicit = */ 1, c, NULL);
engine_add_ghosts(e, c, c->hydro.ghost_in, c->hydro.ghost_out);
/* Generate the extra ghost task. */
#ifdef EXTRA_HYDRO_LOOP
c->hydro.extra_ghost = scheduler_addtask(
s, task_type_extra_ghost, task_subtype_none, 0, 0, c, NULL);
#endif
/* Stars */
if (with_stars) {
c->stars.drift = scheduler_addtask(s, task_type_drift_spart,
task_subtype_none, 0, 0, c, NULL);
scheduler_addunlock(s, c->stars.drift, c->super->kick2);
if (with_star_formation && c->top->hydro.count > 0)
scheduler_addunlock(s, c->stars.drift, c->top->hydro.star_formation);
}
/* Sinks */
if (with_sinks) {
c->sinks.drift = scheduler_addtask(s, task_type_drift_sink,
task_subtype_none, 0, 0, c, NULL);
scheduler_addunlock(s, c->sinks.drift, c->super->kick2);
c->sinks.sink_in =
scheduler_addtask(s, task_type_sink_in, task_subtype_none, 0,
/* implicit = */ 1, c, NULL);
c->sinks.density_ghost = scheduler_addtask(
s, task_type_sink_density_ghost, task_subtype_none, 0, 0, c, NULL);
c->sinks.sink_ghost1 =
scheduler_addtask(s, task_type_sink_ghost1, task_subtype_none, 0,
/* implicit = */ 1, c, NULL);
c->sinks.sink_ghost2 =
scheduler_addtask(s, task_type_sink_ghost2, task_subtype_none, 0,
/* implicit = */ 1, c, NULL);
c->sinks.sink_out =
scheduler_addtask(s, task_type_sink_out, task_subtype_none, 0,
/* implicit = */ 1, c, NULL);
/* Link to the main tasks */
scheduler_addunlock(s, c->super->kick2, c->sinks.sink_in);
scheduler_addunlock(s, c->sinks.sink_out, c->super->timestep);
scheduler_addunlock(s, c->top->sinks.sink_formation, c->sinks.sink_in);
if (with_timestep_sync)
scheduler_addunlock(s, c->sinks.sink_out, c->super->timestep_sync);
if (with_stars &&
(c->top->hydro.count > 0 || c->top->sinks.count > 0)) {
scheduler_addunlock(s, c->hydro.super->sinks.sink_out,
c->top->sinks.star_formation_sink);
}
}
/* Black holes */
if (with_black_holes) {
c->black_holes.drift = scheduler_addtask(
s, task_type_drift_bpart, task_subtype_none, 0, 0, c, NULL);
scheduler_addunlock(s, c->black_holes.drift, c->super->kick2);
}
/* Subgrid tasks: cooling */
if (with_cooling) {
c->hydro.cooling_in =
scheduler_addtask(s, task_type_cooling_in, task_subtype_none, 0,
/*implicit=*/1, c, NULL);
c->hydro.cooling_out =
scheduler_addtask(s, task_type_cooling_out, task_subtype_none, 0,
/*implicit=*/1, c, NULL);
engine_add_cooling(e, c, c->hydro.cooling_in, c->hydro.cooling_out);
scheduler_addunlock(s, c->hydro.end_force, c->hydro.cooling_in);
scheduler_addunlock(s, c->hydro.cooling_out, c->super->kick2);
} else {
scheduler_addunlock(s, c->hydro.end_force, c->super->kick2);
}
/* Subgrid tasks: feedback */
if (with_feedback) {
c->stars.stars_in =
scheduler_addtask(s, task_type_stars_in, task_subtype_none, 0,
/* implicit = */ 1, c, NULL);
c->stars.stars_out =
scheduler_addtask(s, task_type_stars_out, task_subtype_none, 0,
/* implicit = */ 1, c, NULL);
c->stars.density_ghost = scheduler_addtask(
s, task_type_stars_ghost, task_subtype_none, 0, 0, c, NULL);
#ifdef EXTRA_STAR_LOOPS
c->stars.prep1_ghost =
scheduler_addtask(s, task_type_stars_prep_ghost1, task_subtype_none,
0, /* implicit = */ 1, c, NULL);
c->hydro.prep1_ghost =
scheduler_addtask(s, task_type_hydro_prep_ghost1, task_subtype_none,
0, /* implicit = */ 1, c, NULL);
c->stars.prep2_ghost =
scheduler_addtask(s, task_type_stars_prep_ghost2, task_subtype_none,
0, /* implicit = */ 1, c, NULL);
#endif
#ifdef WITH_CSDS
if (with_csds) {
scheduler_addunlock(s, c->super->csds, c->stars.stars_in);
} else {
scheduler_addunlock(s, c->super->kick2, c->stars.stars_in);
}
#else
scheduler_addunlock(s, c->super->kick2, c->stars.stars_in);
#endif
scheduler_addunlock(s, c->stars.stars_out, c->super->timestep);
/* Star formation*/
if (with_feedback && ((c->hydro.count > 0 && with_star_formation) ||
((c->hydro.count > 0 || c->sinks.count > 0) &&
with_star_formation_sink))) {
scheduler_addunlock(s, star_resort_cell->hydro.stars_resort,
c->stars.stars_in);
}
}
/* Radiative Transfer */
if (with_rt) {
/* RT ghost in task */
c->rt.rt_in =
scheduler_addtask(s, task_type_rt_in, task_subtype_none, 0,
/* implicit= */ 1, c, NULL);
scheduler_addunlock(s, c->super->kick2, c->rt.rt_in);
/* Star formation */
if (c->top->hydro.count > 0 && with_star_formation)
scheduler_addunlock(s, c->top->hydro.star_formation, c->rt.rt_in);
/* Star formation from sinks */
if (with_star_formation_sink &&
(c->top->hydro.count > 0 || c->top->sinks.count > 0))
scheduler_addunlock(s, c->top->sinks.star_formation_sink,
c->rt.rt_in);
if (with_feedback)
scheduler_addunlock(s, c->stars.stars_out, c->rt.rt_in);
/* TODO: check/add dependencies from Loic's new sink SF tasks */
/* RT ghost out task */
c->rt.rt_out =
scheduler_addtask(s, task_type_rt_out, task_subtype_none, 0,
/* implicit= */ 1, c, NULL);
scheduler_addunlock(s, c->rt.rt_out, c->super->timestep);
/* In cases where nothing but RT is active, don't allow the timestep
* collect to run before we've finished */
scheduler_addunlock(s, c->rt.rt_out, c->top->timestep_collect);
/* non-implicit ghost 1 */
c->rt.rt_ghost1 = scheduler_addtask(s, task_type_rt_ghost1,
task_subtype_none, 0, 0, c, NULL);
scheduler_addunlock(s, c->rt.rt_in, c->rt.rt_ghost1);
/* non-implicit ghost 2 */
c->rt.rt_ghost2 = scheduler_addtask(s, task_type_rt_ghost2,
task_subtype_none, 0, 0, c, NULL);
/* implicit transport out */
c->rt.rt_transport_out =
scheduler_addtask(s, task_type_rt_transport_out, task_subtype_none,
0, /*implicit= */ 1, c, NULL);
/* thermochemistry */
c->rt.rt_tchem = scheduler_addtask(s, task_type_rt_tchem,
task_subtype_none, 0, 0, c, NULL);
/* Advance cell time for subcycling */
/* We need to make sure that rt_advance_cell_time is at the same level
* as the timestep task, not below. Otherwise, the updated cell times
* won't propagate up the hierarchy enough, and the cell_is_rt_active
* will return bogus results. Note that c->super is not necessarily
* c->hydro.super in general. */
/* Create the task only once ! */
if (c->super->rt.rt_advance_cell_time == NULL) {
c->super->rt.rt_advance_cell_time =
scheduler_addtask(s, task_type_rt_advance_cell_time,
task_subtype_none, 0, 0, c->super, NULL);
/* Don't run the rt_collect_times before the rt_advance_cell_time */
scheduler_addunlock(s, c->super->rt.rt_advance_cell_time,
c->top->rt.rt_collect_times);
}
scheduler_addunlock(s, c->rt.rt_transport_out, c->rt.rt_tchem);
scheduler_addunlock(s, c->rt.rt_tchem,
c->super->rt.rt_advance_cell_time);
scheduler_addunlock(s, c->super->rt.rt_advance_cell_time, c->rt.rt_out);
}
/* Subgrid tasks: black hole feedback */
if (with_black_holes) {
c->black_holes.black_holes_in =
scheduler_addtask(s, task_type_bh_in, task_subtype_none, 0,
/* implicit = */ 1, c, NULL);
c->black_holes.black_holes_out =
scheduler_addtask(s, task_type_bh_out, task_subtype_none, 0,
/* implicit = */ 1, c, NULL);
c->black_holes.density_ghost = scheduler_addtask(
s, task_type_bh_density_ghost, task_subtype_none, 0, 0, c, NULL);
c->black_holes.swallow_ghost_2 =
scheduler_addtask(s, task_type_bh_swallow_ghost2, task_subtype_none,
0, /* implicit =*/1, c, NULL);
c->black_holes.swallow_ghost_3 = scheduler_addtask(
s, task_type_bh_swallow_ghost3, task_subtype_none, 0, 0, c, NULL);
#ifdef WITH_CSDS
if (with_csds) {
scheduler_addunlock(s, c->super->csds, c->black_holes.black_holes_in);
} else {
scheduler_addunlock(s, c->super->kick2,
c->black_holes.black_holes_in);
}
#else
scheduler_addunlock(s, c->super->kick2, c->black_holes.black_holes_in);
#endif
scheduler_addunlock(s, c->black_holes.black_holes_out,
c->super->timestep);
}
if (with_black_holes && with_feedback) {
/* Make sure we don't start swallowing gas particles before the stars
have converged on their smoothing lengths. */
scheduler_addunlock(s, c->stars.density_ghost,
c->black_holes.swallow_ghost_1);
}
}
} else { /* We are above the super-cell so need to go deeper */
/* Recurse. */
if (c->split)
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL)
engine_make_hierarchical_tasks_hydro(e, c->progeny[k],
star_resort_cell);
}
}
void engine_make_hierarchical_tasks_mapper(void *map_data, int num_elements,
void *extra_data) {
struct engine *e = (struct engine *)extra_data;
const int with_hydro = (e->policy & engine_policy_hydro);
const int with_self_gravity = (e->policy & engine_policy_self_gravity);
const int with_ext_gravity = (e->policy & engine_policy_external_gravity);
for (int ind = 0; ind < num_elements; ind++) {
struct cell *c = &((struct cell *)map_data)[ind];
/* Make the common tasks (time integration) */
engine_make_hierarchical_tasks_common(e, c);
/* Add the hydro stuff */
if (with_hydro)
engine_make_hierarchical_tasks_hydro(e, c, /*star_resort_cell=*/NULL);
/* And the gravity stuff */
if (with_self_gravity || with_ext_gravity)
engine_make_hierarchical_tasks_gravity(e, c);
}
}
/**
* @brief Constructs the top-level tasks for the short-range gravity
* and long-range gravity interactions.
*
* - All top-cells get a self task.
* - All pairs within range according to the multipole acceptance
* criterion get a pair task.
*/
void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements,
void *extra_data) {
struct engine *e = (struct engine *)extra_data;
struct space *s = e->s;
struct scheduler *sched = &e->sched;
const int nodeID = e->nodeID;
const int periodic = s->periodic;
const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]};
struct cell *cells = s->cells_top;
const double max_distance = e->mesh->r_cut_max;
const double max_distance2 = max_distance * max_distance;
/* Compute maximal distance where we can expect a direct interaction */
const float distance = gravity_M2L_min_accept_distance(
e->gravity_properties, sqrtf(3) * cells[0].width[0], s->max_softening,
s->min_a_grav, s->max_mpole_power, periodic);
/* Convert the maximal search distance to a number of cells
* Define a lower and upper delta in case things are not symmetric */
const int delta = max((int)(sqrt(3) * distance / cells[0].width[0]) + 1, 2);
int delta_m = delta;
int delta_p = delta;
/* Special case where every cell is in range of every other one */
if (periodic) {
if (delta >= cdim[0] / 2) {
if (cdim[0] % 2 == 0) {
delta_m = cdim[0] / 2;
delta_p = cdim[0] / 2 - 1;
} else {
delta_m = cdim[0] / 2;
delta_p = cdim[0] / 2;
}
}
} else {
if (delta > cdim[0]) {
delta_m = cdim[0];
delta_p = cdim[0];
}
}
/* Loop through the elements, which are just byte offsets from NULL. */
for (int ind = 0; ind < num_elements; ind++) {
/* Get the cell index. */
const int cid = (size_t)(map_data) + ind;
/* Integer indices of the cell in the top-level grid */
const int i = cid / (cdim[1] * cdim[2]);
const int j = (cid / cdim[2]) % cdim[1];
const int k = cid % cdim[2];
/* Get the first cell */
struct cell *ci = &cells[cid];
/* Skip cells without gravity particles */
if (ci->grav.count == 0) continue;
/* If the cell is local build a self-interaction */
if (ci->nodeID == nodeID) {
scheduler_addtask(sched, task_type_self, task_subtype_grav, 0, 0, ci,
NULL);
}
/* Loop over every other cell within (Manhattan) range delta */
for (int ii = i - delta_m; ii <= i + delta_p; ii++) {
/* Escape if non-periodic and beyond range */
if (!periodic && (ii < 0 || ii >= cdim[0])) continue;
for (int jj = j - delta_m; jj <= j + delta_p; jj++) {
/* Escape if non-periodic and beyond range */
if (!periodic && (jj < 0 || jj >= cdim[1])) continue;
for (int kk = k - delta_m; kk <= k + delta_p; kk++) {
/* Escape if non-periodic and beyond range */
if (!periodic && (kk < 0 || kk >= cdim[2])) continue;
/* Apply periodic BC (not harmful if not using periodic BC) */
const int iii = (ii + cdim[0]) % cdim[0];
const int jjj = (jj + cdim[1]) % cdim[1];
const int kkk = (kk + cdim[2]) % cdim[2];
/* Get the second cell */
const int cjd = cell_getid(cdim, iii, jjj, kkk);
struct cell *cj = &cells[cjd];
/* Avoid duplicates, empty cells and completely foreign pairs */
if (cid >= cjd || cj->grav.count == 0 ||
(ci->nodeID != nodeID && cj->nodeID != nodeID))
continue;
#ifdef WITH_MPI
/* Recover the multipole information */
const struct gravity_tensors *multi_i = ci->grav.multipole;
const struct gravity_tensors *multi_j = cj->grav.multipole;
if (multi_i == NULL && ci->nodeID != nodeID)
error("Multipole of ci was not exchanged properly via the proxies");
if (multi_j == NULL && cj->nodeID != nodeID)
error("Multipole of cj was not exchanged properly via the proxies");
#endif
/* Minimal distance between any pair of particles */
const double min_radius2 =
cell_min_dist2_same_size(ci, cj, periodic, dim);
/* Are we beyond the distance where the truncated forces are 0 ?*/
if (periodic && min_radius2 > max_distance2) continue;
/* Are the cells too close for a MM interaction ? */
if (!cell_can_use_pair_mm(ci, cj, e, s, /*use_rebuild_data=*/1,
/*is_tree_walk=*/0)) {
/* Ok, we need to add a direct pair calculation */
scheduler_addtask(sched, task_type_pair, task_subtype_grav, 0, 0,
ci, cj);
#ifdef SWIFT_DEBUG_CHECKS
#ifdef WITH_MPI
/* Let's cross-check that we had a proxy for that cell */
if (ci->nodeID == nodeID && cj->nodeID != engine_rank) {
/* Find the proxy for this node */
const int proxy_id = e->proxy_ind[cj->nodeID];
if (proxy_id < 0)
error("No proxy exists for that foreign node %d!", cj->nodeID);
const struct proxy *p = &e->proxies[proxy_id];
/* Check whether the cell exists in the proxy */
int n = 0;
for (; n < p->nr_cells_in; n++)
if (p->cells_in[n] == cj) {
break;
}
if (n == p->nr_cells_in)
error(
"Cell %d not found in the proxy but trying to construct "
"grav task!",
cjd);
} else if (cj->nodeID == nodeID && ci->nodeID != engine_rank) {
/* Find the proxy for this node */
const int proxy_id = e->proxy_ind[ci->nodeID];
if (proxy_id < 0)
error("No proxy exists for that foreign node %d!", ci->nodeID);
const struct proxy *p = &e->proxies[proxy_id];
/* Check whether the cell exists in the proxy */
int n = 0;
for (; n < p->nr_cells_in; n++)
if (p->cells_in[n] == ci) {
break;
}
if (n == p->nr_cells_in)
error(
"Cell %d not found in the proxy but trying to construct "
"grav task!",
cid);
}
#endif /* WITH_MPI */
#endif /* SWIFT_DEBUG_CHECKS */
}
}
}
}
}
}
/**
* @brief Constructs the top-level tasks for the external gravity.
*
* @param e The #engine.
*/
void engine_make_external_gravity_tasks(struct engine *e) {
struct space *s = e->s;
struct scheduler *sched = &e->sched;
const int nodeID = e->nodeID;
struct cell *cells = s->cells_top;
const int nr_cells = s->nr_cells;
for (int cid = 0; cid < nr_cells; ++cid) {
struct cell *ci = &cells[cid];
/* Skip cells without gravity particles */
if (ci->grav.count == 0) continue;
/* Is that neighbour local ? */
if (ci->nodeID != nodeID) continue;
/* If the cell is local, build a self-interaction */
scheduler_addtask(sched, task_type_self, task_subtype_external_grav, 0, 0,
ci, NULL);
}
}
/**
* @brief Counts the tasks associated with one cell and constructs the links
*
* For each hydrodynamic and gravity task, construct the links with
* the corresponding cell. Similarly, construct the dependencies for
* all the sorting tasks.
*/
void engine_count_and_link_tasks_mapper(void *map_data, int num_elements,
void *extra_data) {
struct engine *e = (struct engine *)extra_data;
struct scheduler *const sched = &e->sched;
for (int ind = 0; ind < num_elements; ind++) {
struct task *t = &((struct task *)map_data)[ind];
struct cell *ci = t->ci;
struct cell *cj = t->cj;
const enum task_types t_type = t->type;
const enum task_subtypes t_subtype = t->subtype;
/* Link sort tasks to all the higher sort task. */
if (t_type == task_type_sort) {
for (struct cell *finger = t->ci->parent; finger != NULL;
finger = finger->parent) {
if (finger->hydro.sorts != NULL)
scheduler_addunlock(sched, t, finger->hydro.sorts);
}
/* Link stars sort tasks to all the higher sort task. */
} else if (t_type == task_type_stars_sort) {
for (struct cell *finger = t->ci->parent; finger != NULL;
finger = finger->parent) {
if (finger->stars.sorts != NULL)
scheduler_addunlock(sched, t, finger->stars.sorts);
}
/* Link self tasks to cells. */
} else if (t_type == task_type_self) {
#ifdef SWIFT_DEBUG_CHECKS
atomic_inc(&ci->nr_tasks);
#endif
if (t_subtype == task_subtype_density) {
engine_addlink(e, &ci->hydro.density, t);
} else if (t_subtype == task_subtype_grav) {
engine_addlink(e, &ci->grav.grav, t);
} else if (t_subtype == task_subtype_external_grav) {
engine_addlink(e, &ci->grav.grav, t);
}
/* Link pair tasks to cells. */
} else if (t_type == task_type_pair) {
#ifdef SWIFT_DEBUG_CHECKS
atomic_inc(&ci->nr_tasks);
atomic_inc(&cj->nr_tasks);
#endif
if (t_subtype == task_subtype_density) {
engine_addlink(e, &ci->hydro.density, t);
engine_addlink(e, &cj->hydro.density, t);
} else if (t_subtype == task_subtype_grav) {
engine_addlink(e, &ci->grav.grav, t);
engine_addlink(e, &cj->grav.grav, t);
}
#ifdef SWIFT_DEBUG_CHECKS
else if (t_subtype == task_subtype_external_grav) {
error("Found a pair/external-gravity task...");
}
#endif
/* Link sub-self tasks to cells. */
} else if (t_type == task_type_sub_self) {
#ifdef SWIFT_DEBUG_CHECKS
atomic_inc(&ci->nr_tasks);
#endif
if (t_subtype == task_subtype_density) {
engine_addlink(e, &ci->hydro.density, t);
} else if (t_subtype == task_subtype_grav) {
engine_addlink(e, &ci->grav.grav, t);
} else if (t_subtype == task_subtype_external_grav) {
engine_addlink(e, &ci->grav.grav, t);
}
/* Link sub-pair tasks to cells. */
} else if (t_type == task_type_sub_pair) {
#ifdef SWIFT_DEBUG_CHECKS
atomic_inc(&ci->nr_tasks);
atomic_inc(&cj->nr_tasks);
#endif
if (t_subtype == task_subtype_density) {
engine_addlink(e, &ci->hydro.density, t);
engine_addlink(e, &cj->hydro.density, t);
} else if (t_subtype == task_subtype_grav) {
engine_addlink(e, &ci->grav.grav, t);
engine_addlink(e, &cj->grav.grav, t);
}
#ifdef SWIFT_DEBUG_CHECKS
else if (t_subtype == task_subtype_external_grav) {
error("Found a sub-pair/external-gravity task...");
}
#endif
/* Multipole-multipole interaction of progenies */
} else if (t_type == task_type_grav_mm) {
atomic_inc(&ci->grav.nr_mm_tasks);
atomic_inc(&cj->grav.nr_mm_tasks);
engine_addlink(e, &ci->grav.mm, t);
engine_addlink(e, &cj->grav.mm, t);
}
}
}
/**
* @brief Creates all the task dependencies for the gravity
*
* @param map_data The task array passed to this pool thread.
* @param num_elements The number of tasks in this pool thread.
* @param extra_data Pointer to the #engine.
*/
void engine_link_gravity_tasks_mapper(void *map_data, int num_elements,
void *extra_data) {
struct task *tasks = (struct task *)map_data;
struct engine *e = (struct engine *)extra_data;
struct scheduler *sched = &e->sched;
const int nodeID = e->nodeID;
for (int k = 0; k < num_elements; k++) {
/* Get a pointer to the task. */
struct task *t = &tasks[k];
if (t->type == task_type_none) continue;
/* Get the cells we act on */
struct cell *restrict ci = t->ci;
struct cell *restrict cj = t->cj;
const enum task_types t_type = t->type;
const enum task_subtypes t_subtype = t->subtype;
/* Pointers to the parent cells for tasks going up and down the tree
* In the case where we are at the super-level we don't
* want the parent as no tasks are defined above that level. */
struct cell *ci_parent, *cj_parent;
if (ci->parent != NULL && ci->grav.super != ci)
ci_parent = ci->parent;
else
ci_parent = ci;
if (cj != NULL && cj->parent != NULL && cj->grav.super != cj)
cj_parent = cj->parent;
else
cj_parent = cj;
/* Node ID (if running with MPI) */
#ifdef WITH_MPI
const int ci_nodeID = ci->nodeID;
const int cj_nodeID = (cj != NULL) ? cj->nodeID : -1;
#else
const int ci_nodeID = nodeID;
const int cj_nodeID = nodeID;
#endif
/* Self-interaction for self-gravity? */
if (t_type == task_type_self && t_subtype == task_subtype_grav) {
#ifdef SWIFT_DEBUG_CHECKS
if (ci_nodeID != nodeID) error("Non-local self task");
#endif
/* drift ---+-> gravity --> grav_down */
/* init --/ */
scheduler_addunlock(sched, ci_parent->grav.drift_out, t);
scheduler_addunlock(sched, ci_parent->grav.init_out, t);
scheduler_addunlock(sched, t, ci_parent->grav.down_in);
}
/* Self-interaction for external gravity ? */
else if (t_type == task_type_self &&
t_subtype == task_subtype_external_grav) {
#ifdef SWIFT_DEBUG_CHECKS
if (ci_nodeID != nodeID) error("Non-local self task");
#endif
/* drift -----> gravity --> end_gravity_force */
scheduler_addunlock(sched, ci->grav.super->grav.drift, t);
scheduler_addunlock(sched, t, ci->grav.super->grav.end_force);
}
/* Otherwise, pair interaction? */
else if (t_type == task_type_pair && t_subtype == task_subtype_grav) {
if (ci_nodeID == nodeID) {
/* drift ---+-> gravity --> grav_down */
/* init --/ */
scheduler_addunlock(sched, ci_parent->grav.drift_out, t);
scheduler_addunlock(sched, ci_parent->grav.init_out, t);
scheduler_addunlock(sched, t, ci_parent->grav.down_in);
}
if (cj_nodeID == nodeID) {
/* drift ---+-> gravity --> grav_down */
/* init --/ */
if (ci_parent != cj_parent) { /* Avoid double unlock */
scheduler_addunlock(sched, cj_parent->grav.drift_out, t);
scheduler_addunlock(sched, cj_parent->grav.init_out, t);
scheduler_addunlock(sched, t, cj_parent->grav.down_in);
}
}
}
/* Otherwise, sub-self interaction? */
else if (t_type == task_type_sub_self && t_subtype == task_subtype_grav) {
#ifdef SWIFT_DEBUG_CHECKS
if (ci_nodeID != nodeID) error("Non-local sub-self task");
#endif
/* drift ---+-> gravity --> grav_down */
/* init --/ */
scheduler_addunlock(sched, ci_parent->grav.drift_out, t);
scheduler_addunlock(sched, ci_parent->grav.init_out, t);
scheduler_addunlock(sched, t, ci_parent->grav.down_in);
}
/* Sub-self-interaction for external gravity ? */
else if (t_type == task_type_sub_self &&
t_subtype == task_subtype_external_grav) {
#ifdef SWIFT_DEBUG_CHECKS
if (ci_nodeID != nodeID) error("Non-local sub-self task");
#endif
/* drift -----> gravity --> end_force */
scheduler_addunlock(sched, ci->grav.super->grav.drift, t);
scheduler_addunlock(sched, t, ci->grav.super->grav.end_force);
}
/* Otherwise, sub-pair interaction? */
else if (t_type == task_type_sub_pair && t_subtype == task_subtype_grav) {
if (ci_nodeID == nodeID) {
/* drift ---+-> gravity --> grav_down */
/* init --/ */
scheduler_addunlock(sched, ci_parent->grav.drift_out, t);
scheduler_addunlock(sched, ci_parent->grav.init_out, t);
scheduler_addunlock(sched, t, ci_parent->grav.down_in);
}
if (cj_nodeID == nodeID) {
/* drift ---+-> gravity --> grav_down */
/* init --/ */
if (ci_parent != cj_parent) { /* Avoid double unlock */
scheduler_addunlock(sched, cj_parent->grav.drift_out, t);
scheduler_addunlock(sched, cj_parent->grav.init_out, t);
scheduler_addunlock(sched, t, cj_parent->grav.down_in);
}
}
}
/* Otherwise M-M interaction? */
else if (t_type == task_type_grav_mm) {
if (ci_nodeID == nodeID) {
/* init -----> gravity --> grav_down */
scheduler_addunlock(sched, ci_parent->grav.init_out, t);
scheduler_addunlock(sched, t, ci_parent->grav.down_in);
}
if (cj_nodeID == nodeID) {
/* init -----> gravity --> grav_down */
if (ci_parent != cj_parent) { /* Avoid double unlock */
scheduler_addunlock(sched, cj_parent->grav.init_out, t);
scheduler_addunlock(sched, t, cj_parent->grav.down_in);
}
}
}
}
}
#ifdef EXTRA_HYDRO_LOOP
/**
* @brief Creates the dependency network for the hydro tasks of a given cell.
*
* @param sched The #scheduler.
* @param density The density task to link.
* @param gradient The gradient task to link.
* @param force The force task to link.
* @param limiter The limiter task to link.
* @param c The cell.
* @param with_cooling Do we have a cooling task ?
* @param with_limiter Do we have a time-step limiter ?
*/
static inline void engine_make_hydro_loops_dependencies(
struct scheduler *sched, struct task *density, struct task *gradient,
struct task *force, struct task *limiter, struct cell *c, int with_cooling,
int with_limiter) {
/* density loop --> ghost --> gradient loop --> extra_ghost */
/* extra_ghost --> force loop */
scheduler_addunlock(sched, density, c->hydro.super->hydro.ghost_in);
scheduler_addunlock(sched, c->hydro.super->hydro.ghost_out, gradient);
scheduler_addunlock(sched, gradient, c->hydro.super->hydro.extra_ghost);
scheduler_addunlock(sched, c->hydro.super->hydro.extra_ghost, force);
}
#else
/**
* @brief Creates the dependency network for the hydro tasks of a given cell.
*
* @param sched The #scheduler.
* @param density The density task to link.
* @param force The force task to link.
* @param limiter The limiter task to link.
* @param c The cell.
* @param with_cooling Are we running with cooling switched on?
* @param with_limiter Are we running with limiter switched on?
*/
static inline void engine_make_hydro_loops_dependencies(
struct scheduler *sched, struct task *density, struct task *force,
struct task *limiter, struct cell *c, int with_cooling, int with_limiter) {
/* density loop --> ghost --> force loop */
scheduler_addunlock(sched, density, c->hydro.super->hydro.ghost_in);
scheduler_addunlock(sched, c->hydro.super->hydro.ghost_out, force);
}
#endif
/**
* @brief Duplicates the first hydro loop and construct all the
* dependencies for the hydro part
*
* This is done by looping over all the previously constructed tasks
* and adding another task involving the same cells but this time
* corresponding to the second hydro loop over neighbours.
* With all the relevant tasks for a given cell available, we construct
* all the dependencies for that cell.
*/
void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
void *extra_data) {
struct engine *e = (struct engine *)extra_data;
struct scheduler *sched = &e->sched;
const int nodeID = e->nodeID;
const int with_cooling = (e->policy & engine_policy_cooling);
const int with_timestep_limiter =
(e->policy & engine_policy_timestep_limiter);
const int with_timestep_sync = (e->policy & engine_policy_timestep_sync);
const int with_feedback = (e->policy & engine_policy_feedback);
const int with_black_holes = (e->policy & engine_policy_black_holes);
const int with_rt = (e->policy & engine_policy_rt);
const int with_sink = (e->policy & engine_policy_sinks);
#ifdef EXTRA_HYDRO_LOOP
struct task *t_gradient = NULL;
#endif
#ifdef EXTRA_STAR_LOOPS
struct task *t_star_prep1 = NULL;
struct task *t_star_prep2 = NULL;
#endif
struct task *t_force = NULL;
struct task *t_limiter = NULL;
struct task *t_star_density = NULL;
struct task *t_star_feedback = NULL;
struct task *t_bh_density = NULL;
struct task *t_bh_swallow = NULL;
struct task *t_do_gas_swallow = NULL;
struct task *t_do_bh_swallow = NULL;
struct task *t_bh_feedback = NULL;
struct task *t_sink_density = NULL;
struct task *t_sink_swallow = NULL;
struct task *t_rt_gradient = NULL;
struct task *t_rt_transport = NULL;
struct task *t_sink_do_sink_swallow = NULL;
struct task *t_sink_do_gas_swallow = NULL;
for (int ind = 0; ind < num_elements; ind++) {
struct task *t = &((struct task *)map_data)[ind];
const enum task_types t_type = t->type;
const enum task_subtypes t_subtype = t->subtype;
const long long flags = t->flags;
struct cell *const ci = t->ci;
struct cell *const cj = t->cj;
/* Escape early */
if (t->type == task_type_none) continue;
if (t->type == task_type_stars_resort) continue;
if (t->type == task_type_star_formation) continue;
if (t->type == task_type_star_formation_sink) continue;
if (t->type == task_type_sink_formation) continue;
/* Sort tasks depend on the drift of the cell (gas version). */
if (t_type == task_type_sort && ci->nodeID == nodeID) {
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t);
}
/* Sort tasks depend on the drift of the cell (stars version). */
else if (t_type == task_type_stars_sort && ci->nodeID == nodeID) {
scheduler_addunlock(sched, ci->hydro.super->stars.drift, t);
}
/* Self-interaction? */
else if (t_type == task_type_self && t_subtype == task_subtype_density) {
const int bcount_i = ci->black_holes.count;
/* Make the self-density tasks depend on the drift only. */
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t);
/* Task for the second hydro loop, */
t_force = scheduler_addtask(sched, task_type_self, task_subtype_force,
flags, 0, ci, NULL);
/* the task for the time-step limiter */
if (with_timestep_limiter) {
t_limiter = scheduler_addtask(sched, task_type_self,
task_subtype_limiter, flags, 0, ci, NULL);
}
/* The stellar feedback tasks */
if (with_feedback) {
t_star_density =
scheduler_addtask(sched, task_type_self, task_subtype_stars_density,
flags, 0, ci, NULL);
t_star_feedback =
scheduler_addtask(sched, task_type_self,
task_subtype_stars_feedback, flags, 0, ci, NULL);
#ifdef EXTRA_STAR_LOOPS
t_star_prep1 =
scheduler_addtask(sched, task_type_self, task_subtype_stars_prep1,
flags, 0, ci, NULL);
t_star_prep2 =
scheduler_addtask(sched, task_type_self, task_subtype_stars_prep2,
flags, 0, ci, NULL);
#endif
}
/* The sink tasks */
if (with_sink) {
t_sink_density =
scheduler_addtask(sched, task_type_self, task_subtype_sink_density,
flags, 0, ci, NULL);
t_sink_swallow =
scheduler_addtask(sched, task_type_self, task_subtype_sink_swallow,
flags, 0, ci, NULL);
t_sink_do_sink_swallow = scheduler_addtask(
sched, task_type_self, task_subtype_sink_do_sink_swallow, flags, 0,
ci, NULL);
t_sink_do_gas_swallow = scheduler_addtask(
sched, task_type_self, task_subtype_sink_do_gas_swallow, flags, 0,
ci, NULL);
}
/* The black hole feedback tasks */
if (with_black_holes && bcount_i > 0) {
t_bh_density = scheduler_addtask(
sched, task_type_self, task_subtype_bh_density, flags, 0, ci, NULL);
t_bh_swallow = scheduler_addtask(
sched, task_type_self, task_subtype_bh_swallow, flags, 0, ci, NULL);
t_do_gas_swallow =
scheduler_addtask(sched, task_type_self,
task_subtype_do_gas_swallow, flags, 0, ci, NULL);
t_do_bh_swallow =
scheduler_addtask(sched, task_type_self, task_subtype_do_bh_swallow,
flags, 0, ci, NULL);
t_bh_feedback =
scheduler_addtask(sched, task_type_self, task_subtype_bh_feedback,
flags, 0, ci, NULL);
}
if (with_rt) {
t_rt_gradient =
scheduler_addtask(sched, task_type_self, task_subtype_rt_gradient,
flags, 0, ci, NULL);
t_rt_transport =
scheduler_addtask(sched, task_type_self, task_subtype_rt_transport,
flags, 0, ci, NULL);
}
/* Link the tasks to the cells */
engine_addlink(e, &ci->hydro.force, t_force);
if (with_timestep_limiter) {
engine_addlink(e, &ci->hydro.limiter, t_limiter);
}
if (with_feedback) {
engine_addlink(e, &ci->stars.density, t_star_density);
engine_addlink(e, &ci->stars.feedback, t_star_feedback);
#ifdef EXTRA_STAR_LOOPS
engine_addlink(e, &ci->stars.prepare1, t_star_prep1);
engine_addlink(e, &ci->stars.prepare2, t_star_prep2);
#endif
}
if (with_sink) {
engine_addlink(e, &ci->sinks.density, t_sink_density);
engine_addlink(e, &ci->sinks.swallow, t_sink_swallow);
engine_addlink(e, &ci->sinks.do_sink_swallow, t_sink_do_sink_swallow);
engine_addlink(e, &ci->sinks.do_gas_swallow, t_sink_do_gas_swallow);
}
if (with_black_holes && bcount_i > 0) {
engine_addlink(e, &ci->black_holes.density, t_bh_density);
engine_addlink(e, &ci->black_holes.swallow, t_bh_swallow);
engine_addlink(e, &ci->black_holes.do_gas_swallow, t_do_gas_swallow);
engine_addlink(e, &ci->black_holes.do_bh_swallow, t_do_bh_swallow);
engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback);
}
if (with_rt) {
engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient);
engine_addlink(e, &ci->rt.rt_transport, t_rt_transport);
}
#ifdef EXTRA_HYDRO_LOOP
/* Same work for the additional hydro loop */
t_gradient = scheduler_addtask(sched, task_type_self,
task_subtype_gradient, flags, 0, ci, NULL);
/* Add the link between the new loops and the cell */
engine_addlink(e, &ci->hydro.gradient, t_gradient);
/* Now, build all the dependencies for the hydro */
engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
t_limiter, ci, with_cooling,
with_timestep_limiter);
#else
/* Now, build all the dependencies for the hydro */
engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, ci,
with_cooling, with_timestep_limiter);
#endif
/* Create the task dependencies */
scheduler_addunlock(sched, t_force, ci->hydro.super->hydro.end_force);
if (with_feedback) {
if (with_cooling)
scheduler_addunlock(sched, ci->hydro.super->hydro.cooling_out,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->stars.drift,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->stars.stars_in,
t_star_density);
scheduler_addunlock(sched, t_star_density,
ci->hydro.super->stars.density_ghost);
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(sched, ci->hydro.super->stars.density_ghost,
t_star_prep1);
scheduler_addunlock(sched, t_star_prep1,
ci->hydro.super->stars.prep1_ghost);
scheduler_addunlock(sched, t_star_prep1,
ci->hydro.super->hydro.prep1_ghost);
scheduler_addunlock(sched, ci->hydro.super->stars.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, ci->hydro.super->hydro.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, t_star_prep2,
ci->hydro.super->stars.prep2_ghost);
scheduler_addunlock(sched, ci->hydro.super->stars.prep2_ghost,
t_star_feedback);
#else
scheduler_addunlock(sched, ci->hydro.super->stars.density_ghost,
t_star_feedback);
#endif
scheduler_addunlock(sched, t_star_feedback,
ci->hydro.super->stars.stars_out);
}
/* The sink's tasks. */
if (with_sink) {
/* Sink density */
scheduler_addunlock(sched, ci->hydro.super->sinks.drift,
t_sink_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_sink_density);
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_in,
t_sink_density);
scheduler_addunlock(sched, t_sink_density,
ci->hydro.super->sinks.density_ghost);
/* Do the sink_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.density_ghost,
t_sink_swallow);
scheduler_addunlock(sched, t_sink_swallow,
ci->hydro.super->sinks.sink_ghost1);
/* Do the sink_do_gas_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost1,
t_sink_do_gas_swallow);
scheduler_addunlock(sched, t_sink_do_gas_swallow,
ci->hydro.super->sinks.sink_ghost2);
/* Do the sink_do_sink_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost2,
t_sink_do_sink_swallow);
scheduler_addunlock(sched, t_sink_do_sink_swallow,
ci->hydro.super->sinks.sink_out);
}
if (with_black_holes && bcount_i > 0) {
if (with_cooling)
scheduler_addunlock(sched, ci->hydro.super->hydro.cooling_out,
t_bh_density);
scheduler_addunlock(sched, ci->hydro.super->black_holes.drift,
t_bh_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_bh_density);
scheduler_addunlock(sched, ci->hydro.super->black_holes.black_holes_in,
t_bh_density);
scheduler_addunlock(sched, t_bh_density,
ci->hydro.super->black_holes.density_ghost);
scheduler_addunlock(sched, ci->hydro.super->black_holes.density_ghost,
t_bh_swallow);
scheduler_addunlock(sched, t_bh_swallow,
ci->hydro.super->black_holes.swallow_ghost_1);
scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost_1,
t_do_gas_swallow);
scheduler_addunlock(sched, t_do_gas_swallow,
ci->hydro.super->black_holes.swallow_ghost_2);
scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost_2,
t_do_bh_swallow);
scheduler_addunlock(sched, t_do_bh_swallow,
ci->hydro.super->black_holes.swallow_ghost_3);
scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost_3,
t_bh_feedback);
scheduler_addunlock(sched, t_bh_feedback,
ci->hydro.super->black_holes.black_holes_out);
}
if (with_timestep_limiter) {
scheduler_addunlock(sched, ci->super->timestep, t_limiter);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_limiter);
scheduler_addunlock(sched, t_limiter, ci->super->kick1);
scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter);
}
if (with_timestep_sync && with_feedback) {
scheduler_addunlock(sched, t_star_feedback, ci->super->timestep_sync);
}
if (with_timestep_sync && with_black_holes && bcount_i > 0) {
scheduler_addunlock(sched, t_bh_feedback, ci->super->timestep_sync);
}
if (with_rt) {
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_gradient);
scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1,
t_rt_gradient);
scheduler_addunlock(sched, t_rt_gradient,
ci->hydro.super->rt.rt_ghost2);
scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2,
t_rt_transport);
scheduler_addunlock(sched, t_rt_transport,
ci->hydro.super->rt.rt_transport_out);
}
}
/* Otherwise, pair interaction? */
else if (t_type == task_type_pair && t_subtype == task_subtype_density) {
const int bcount_i = ci->black_holes.count;
const int bcount_j = cj->black_holes.count;
/* Make all density tasks depend on the drift */
if (ci->nodeID == nodeID) {
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t);
}
if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
scheduler_addunlock(sched, cj->hydro.super->hydro.drift, t);
}
/* Make all density tasks depend on the sorts */
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t);
if (ci->hydro.super != cj->hydro.super) {
scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t);
}
/* New task for the force */
t_force = scheduler_addtask(sched, task_type_pair, task_subtype_force,
flags, 0, ci, cj);
#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
/* The order of operations for an inactive local cell interacting
* with an active foreign cell is not guaranteed because the density
* (and gradient) iact loops don't exist in that case. So we need
* an explicit dependency here to have sorted cells. */
/* Make all force tasks depend on the sorts */
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_force);
if (ci->hydro.super != cj->hydro.super) {
scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t_force);
}
#endif
/* and the task for the time-step limiter */
if (with_timestep_limiter) {
t_limiter = scheduler_addtask(sched, task_type_pair,
task_subtype_limiter, flags, 0, ci, cj);
}
/* The stellar feedback tasks */
if (with_feedback) {
t_star_density =
scheduler_addtask(sched, task_type_pair, task_subtype_stars_density,
flags, 0, ci, cj);
t_star_feedback =
scheduler_addtask(sched, task_type_pair,
task_subtype_stars_feedback, flags, 0, ci, cj);
#ifdef EXTRA_STAR_LOOPS
t_star_prep1 = scheduler_addtask(
sched, task_type_pair, task_subtype_stars_prep1, flags, 0, ci, cj);
t_star_prep2 = scheduler_addtask(
sched, task_type_pair, task_subtype_stars_prep2, flags, 0, ci, cj);
#endif
}
/* The sink tasks */
if (with_sink) {
t_sink_density = scheduler_addtask(
sched, task_type_pair, task_subtype_sink_density, flags, 0, ci, cj);
t_sink_swallow = scheduler_addtask(
sched, task_type_pair, task_subtype_sink_swallow, flags, 0, ci, cj);
t_sink_do_sink_swallow = scheduler_addtask(
sched, task_type_pair, task_subtype_sink_do_sink_swallow, flags, 0,
ci, cj);
t_sink_do_gas_swallow = scheduler_addtask(
sched, task_type_pair, task_subtype_sink_do_gas_swallow, flags, 0,
ci, cj);
}
/* The black hole feedback tasks */
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
t_bh_density = scheduler_addtask(
sched, task_type_pair, task_subtype_bh_density, flags, 0, ci, cj);
t_bh_swallow = scheduler_addtask(
sched, task_type_pair, task_subtype_bh_swallow, flags, 0, ci, cj);
t_do_gas_swallow =
scheduler_addtask(sched, task_type_pair,
task_subtype_do_gas_swallow, flags, 0, ci, cj);
t_do_bh_swallow =
scheduler_addtask(sched, task_type_pair, task_subtype_do_bh_swallow,
flags, 0, ci, cj);
t_bh_feedback = scheduler_addtask(
sched, task_type_pair, task_subtype_bh_feedback, flags, 0, ci, cj);
}
if (with_rt) {
t_rt_gradient = scheduler_addtask(
sched, task_type_pair, task_subtype_rt_gradient, flags, 0, ci, cj);
t_rt_transport = scheduler_addtask(
sched, task_type_pair, task_subtype_rt_transport, flags, 0, ci, cj);
#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
/* The order of operations for an inactive local cell interacting
* with an active foreign cell is not guaranteed because the gradient
* iact loops don't exist in that case. So we need an explicit
* dependency here to have sorted cells. */
/* Make all force tasks depend on the sorts */
if (ci->hydro.super->rt.rt_sorts != NULL)
scheduler_addunlock(sched, ci->hydro.super->rt.rt_sorts,
t_rt_transport);
if (ci->hydro.super != cj->hydro.super) {
if (cj->hydro.super->rt.rt_sorts != NULL)
scheduler_addunlock(sched, cj->hydro.super->rt.rt_sorts,
t_rt_transport);
}
/* We need to ensure that a local inactive cell is sorted before
* the interaction in the transport loop. Local cells don't have an
* rt_sorts task. */
if (ci->hydro.super->hydro.sorts != NULL)
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts,
t_rt_transport);
if ((ci->hydro.super != cj->hydro.super) &&
(cj->hydro.super->hydro.sorts != NULL))
scheduler_addunlock(sched, cj->hydro.super->hydro.sorts,
t_rt_transport);
#endif
}
engine_addlink(e, &ci->hydro.force, t_force);
engine_addlink(e, &cj->hydro.force, t_force);
if (with_timestep_limiter) {
engine_addlink(e, &ci->hydro.limiter, t_limiter);
engine_addlink(e, &cj->hydro.limiter, t_limiter);
}
if (with_feedback) {
engine_addlink(e, &ci->stars.density, t_star_density);
engine_addlink(e, &cj->stars.density, t_star_density);
engine_addlink(e, &ci->stars.feedback, t_star_feedback);
engine_addlink(e, &cj->stars.feedback, t_star_feedback);
#ifdef EXTRA_STAR_LOOPS
engine_addlink(e, &ci->stars.prepare1, t_star_prep1);
engine_addlink(e, &cj->stars.prepare1, t_star_prep1);
engine_addlink(e, &ci->stars.prepare2, t_star_prep2);
engine_addlink(e, &cj->stars.prepare2, t_star_prep2);
#endif
}
if (with_sink) {
engine_addlink(e, &ci->sinks.density, t_sink_density);
engine_addlink(e, &cj->sinks.density, t_sink_density);
engine_addlink(e, &ci->sinks.swallow, t_sink_swallow);
engine_addlink(e, &cj->sinks.swallow, t_sink_swallow);
engine_addlink(e, &ci->sinks.do_sink_swallow, t_sink_do_sink_swallow);
engine_addlink(e, &cj->sinks.do_sink_swallow, t_sink_do_sink_swallow);
engine_addlink(e, &ci->sinks.do_gas_swallow, t_sink_do_gas_swallow);
engine_addlink(e, &cj->sinks.do_gas_swallow, t_sink_do_gas_swallow);
}
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
engine_addlink(e, &ci->black_holes.density, t_bh_density);
engine_addlink(e, &cj->black_holes.density, t_bh_density);
engine_addlink(e, &ci->black_holes.swallow, t_bh_swallow);
engine_addlink(e, &cj->black_holes.swallow, t_bh_swallow);
engine_addlink(e, &ci->black_holes.do_gas_swallow, t_do_gas_swallow);
engine_addlink(e, &cj->black_holes.do_gas_swallow, t_do_gas_swallow);
engine_addlink(e, &ci->black_holes.do_bh_swallow, t_do_bh_swallow);
engine_addlink(e, &cj->black_holes.do_bh_swallow, t_do_bh_swallow);
engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback);
engine_addlink(e, &cj->black_holes.feedback, t_bh_feedback);
}
if (with_rt) {
engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient);
engine_addlink(e, &cj->rt.rt_gradient, t_rt_gradient);
engine_addlink(e, &ci->rt.rt_transport, t_rt_transport);
engine_addlink(e, &cj->rt.rt_transport, t_rt_transport);
}
#ifdef EXTRA_HYDRO_LOOP
/* Start by constructing the task for the second and third hydro loop */
t_gradient = scheduler_addtask(sched, task_type_pair,
task_subtype_gradient, flags, 0, ci, cj);
/* Add the link between the new loop and both cells */
engine_addlink(e, &ci->hydro.gradient, t_gradient);
engine_addlink(e, &cj->hydro.gradient, t_gradient);
/* Now, build all the dependencies for the hydro for the cells */
/* that are local and are not descendant of the same super_hydro-cells */
if (ci->nodeID == nodeID) {
engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
t_limiter, ci, with_cooling,
with_timestep_limiter);
}
if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
t_limiter, cj, with_cooling,
with_timestep_limiter);
}
#else
/* Now, build all the dependencies for the hydro for the cells */
/* that are local and are not descendant of the same super_hydro-cells */
if (ci->nodeID == nodeID) {
engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, ci,
with_cooling,
with_timestep_limiter);
}
if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, cj,
with_cooling,
with_timestep_limiter);
}
#endif
if (with_feedback) {
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts,
t_star_density);
if (ci->hydro.super != cj->hydro.super) {
scheduler_addunlock(sched, cj->hydro.super->hydro.sorts,
t_star_density);
}
}
if (with_rt) {
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_rt_gradient);
if (ci->hydro.super != cj->hydro.super) {
scheduler_addunlock(sched, cj->hydro.super->hydro.sorts,
t_rt_gradient);
}
}
if (ci->nodeID == nodeID) {
scheduler_addunlock(sched, t_force, ci->hydro.super->hydro.end_force);
if (with_feedback) {
if (with_cooling)
scheduler_addunlock(sched, ci->hydro.super->hydro.cooling_out,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->stars.drift,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->stars.stars_in,
t_star_density);
scheduler_addunlock(sched, t_star_density,
ci->hydro.super->stars.density_ghost);
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(sched, ci->hydro.super->stars.density_ghost,
t_star_prep1);
scheduler_addunlock(sched, t_star_prep1,
ci->hydro.super->stars.prep1_ghost);
scheduler_addunlock(sched, t_star_prep1,
ci->hydro.super->hydro.prep1_ghost);
scheduler_addunlock(sched, ci->hydro.super->stars.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, ci->hydro.super->hydro.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, t_star_prep2,
ci->hydro.super->stars.prep2_ghost);
scheduler_addunlock(sched, ci->hydro.super->stars.prep2_ghost,
t_star_feedback);
#else
scheduler_addunlock(sched, ci->hydro.super->stars.density_ghost,
t_star_feedback);
#endif
scheduler_addunlock(sched, t_star_feedback,
ci->hydro.super->stars.stars_out);
}
if (with_sink) {
/* Sink density */
scheduler_addunlock(sched, ci->hydro.super->sinks.drift,
t_sink_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_sink_density);
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_in,
t_sink_density);
scheduler_addunlock(sched, t_sink_density,
ci->hydro.super->sinks.density_ghost);
/* Do the sink_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.density_ghost,
t_sink_swallow);
scheduler_addunlock(sched, t_sink_swallow,
ci->hydro.super->sinks.sink_ghost1);
/* Do the sink_do_gas_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost1,
t_sink_do_gas_swallow);
scheduler_addunlock(sched, t_sink_do_gas_swallow,
ci->hydro.super->sinks.sink_ghost2);
/* Do the sink_do_sink_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost2,
t_sink_do_sink_swallow);
scheduler_addunlock(sched, t_sink_do_sink_swallow,
ci->hydro.super->sinks.sink_out);
}
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
if (with_cooling)
scheduler_addunlock(sched, ci->hydro.super->hydro.cooling_out,
t_bh_density);
scheduler_addunlock(sched, ci->hydro.super->black_holes.drift,
t_bh_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_bh_density);
scheduler_addunlock(
sched, ci->hydro.super->black_holes.black_holes_in, t_bh_density);
scheduler_addunlock(sched, t_bh_density,
ci->hydro.super->black_holes.density_ghost);
scheduler_addunlock(sched, ci->hydro.super->black_holes.density_ghost,
t_bh_swallow);
scheduler_addunlock(sched, t_bh_swallow,
ci->hydro.super->black_holes.swallow_ghost_1);
scheduler_addunlock(sched,
ci->hydro.super->black_holes.swallow_ghost_1,
t_do_gas_swallow);
scheduler_addunlock(sched, t_do_gas_swallow,
ci->hydro.super->black_holes.swallow_ghost_2);
scheduler_addunlock(sched,
ci->hydro.super->black_holes.swallow_ghost_2,
t_do_bh_swallow);
scheduler_addunlock(sched, t_do_bh_swallow,
ci->hydro.super->black_holes.swallow_ghost_3);
scheduler_addunlock(sched,
ci->hydro.super->black_holes.swallow_ghost_3,
t_bh_feedback);
scheduler_addunlock(sched, t_bh_feedback,
ci->hydro.super->black_holes.black_holes_out);
}
if (with_timestep_limiter) {
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_limiter);
scheduler_addunlock(sched, ci->super->timestep, t_limiter);
scheduler_addunlock(sched, t_limiter, ci->super->kick1);
scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter);
}
if (with_timestep_sync && with_feedback) {
scheduler_addunlock(sched, t_star_feedback, ci->super->timestep_sync);
}
if (with_timestep_sync && with_black_holes &&
(bcount_i > 0 || bcount_j > 0)) {
scheduler_addunlock(sched, t_bh_feedback, ci->super->timestep_sync);
}
if (with_rt) {
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_rt_gradient);
scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1,
t_rt_gradient);
scheduler_addunlock(sched, t_rt_gradient,
ci->hydro.super->rt.rt_ghost2);
scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2,
t_rt_transport);
scheduler_addunlock(sched, t_rt_transport,
ci->hydro.super->rt.rt_transport_out);
}
} else /*(ci->nodeID != nodeID) */ {
if (with_feedback) {
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
t_star_prep1);
#endif
scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
t_star_feedback);
}
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
scheduler_addunlock(sched, t_bh_swallow,
ci->hydro.super->black_holes.swallow_ghost_1);
}
}
if (cj->nodeID == nodeID) {
if (ci->hydro.super != cj->hydro.super) {
scheduler_addunlock(sched, t_force, cj->hydro.super->hydro.end_force);
if (with_feedback) {
if (with_cooling)
scheduler_addunlock(sched, cj->hydro.super->hydro.cooling_out,
t_star_density);
scheduler_addunlock(sched, cj->hydro.super->stars.sorts,
t_star_density);
scheduler_addunlock(sched, cj->hydro.super->stars.drift,
t_star_density);
scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
t_star_density);
scheduler_addunlock(sched, cj->hydro.super->stars.stars_in,
t_star_density);
scheduler_addunlock(sched, t_star_density,
cj->hydro.super->stars.density_ghost);
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(sched, cj->hydro.super->stars.density_ghost,
t_star_prep1);
scheduler_addunlock(sched, t_star_prep1,
cj->hydro.super->stars.prep1_ghost);
scheduler_addunlock(sched, t_star_prep1,
cj->hydro.super->hydro.prep1_ghost);
scheduler_addunlock(sched, cj->hydro.super->stars.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, cj->hydro.super->hydro.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, t_star_prep2,
cj->hydro.super->stars.prep2_ghost);
scheduler_addunlock(sched, cj->hydro.super->stars.prep2_ghost,
t_star_feedback);
#else
scheduler_addunlock(sched, cj->hydro.super->stars.density_ghost,
t_star_feedback);
#endif
scheduler_addunlock(sched, t_star_feedback,
cj->hydro.super->stars.stars_out);
}
if (with_sink) {
/* Sink density */
scheduler_addunlock(sched, cj->hydro.super->sinks.drift,
t_sink_density);
scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
t_sink_density);
scheduler_addunlock(sched, cj->hydro.super->sinks.sink_in,
t_sink_density);
scheduler_addunlock(sched, t_sink_density,
cj->hydro.super->sinks.density_ghost);
/* Do the sink_swallow */
scheduler_addunlock(sched, cj->hydro.super->sinks.density_ghost,
t_sink_swallow);
scheduler_addunlock(sched, t_sink_swallow,
cj->hydro.super->sinks.sink_ghost1);
/* Do the sink_do_gas_swallow */
scheduler_addunlock(sched, cj->hydro.super->sinks.sink_ghost1,
t_sink_do_gas_swallow);
scheduler_addunlock(sched, t_sink_do_gas_swallow,
cj->hydro.super->sinks.sink_ghost2);
/* Do the sink_do_sink_swallow */
scheduler_addunlock(sched, cj->hydro.super->sinks.sink_ghost2,
t_sink_do_sink_swallow);
scheduler_addunlock(sched, t_sink_do_sink_swallow,
cj->hydro.super->sinks.sink_out);
}
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
if (with_cooling)
scheduler_addunlock(sched, cj->hydro.super->hydro.cooling_out,
t_bh_density);
scheduler_addunlock(sched, cj->hydro.super->black_holes.drift,
t_bh_density);
scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
t_bh_density);
scheduler_addunlock(sched,
cj->hydro.super->black_holes.black_holes_in,
t_bh_density);
scheduler_addunlock(sched, t_bh_density,
cj->hydro.super->black_holes.density_ghost);
scheduler_addunlock(sched,
cj->hydro.super->black_holes.density_ghost,
t_bh_swallow);
scheduler_addunlock(sched, t_bh_swallow,
cj->hydro.super->black_holes.swallow_ghost_1);
scheduler_addunlock(sched,
cj->hydro.super->black_holes.swallow_ghost_1,
t_do_gas_swallow);
scheduler_addunlock(sched, t_do_gas_swallow,
cj->hydro.super->black_holes.swallow_ghost_2);
scheduler_addunlock(sched,
cj->hydro.super->black_holes.swallow_ghost_2,
t_do_bh_swallow);
scheduler_addunlock(sched, t_do_bh_swallow,
cj->hydro.super->black_holes.swallow_ghost_3);
scheduler_addunlock(sched,
cj->hydro.super->black_holes.swallow_ghost_3,
t_bh_feedback);
scheduler_addunlock(sched, t_bh_feedback,
cj->hydro.super->black_holes.black_holes_out);
}
if (with_rt) {
scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
t_rt_gradient);
scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost1,
t_rt_gradient);
scheduler_addunlock(sched, t_rt_gradient,
cj->hydro.super->rt.rt_ghost2);
scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost2,
t_rt_transport);
scheduler_addunlock(sched, t_rt_transport,
cj->hydro.super->rt.rt_transport_out);
}
if (with_timestep_limiter) {
scheduler_addunlock(sched, cj->hydro.super->hydro.drift, t_limiter);
}
}
if (ci->super != cj->super) {
if (with_timestep_limiter) {
scheduler_addunlock(sched, cj->super->timestep, t_limiter);
scheduler_addunlock(sched, t_limiter, cj->super->kick1);
scheduler_addunlock(sched, t_limiter, cj->super->timestep_limiter);
}
if (with_timestep_sync && with_feedback) {
scheduler_addunlock(sched, t_star_feedback,
cj->super->timestep_sync);
}
if (with_timestep_sync && with_black_holes &&
(bcount_i > 0 || bcount_j > 0)) {
scheduler_addunlock(sched, t_bh_feedback, cj->super->timestep_sync);
}
}
} else /*(cj->nodeID != nodeID) */ {
if (with_feedback) {
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(sched, cj->hydro.super->stars.sorts,
t_star_prep1);
#endif
scheduler_addunlock(sched, cj->hydro.super->stars.sorts,
t_star_feedback);
}
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
scheduler_addunlock(sched, t_bh_swallow,
cj->hydro.super->black_holes.swallow_ghost_1);
}
}
}
/* Otherwise, sub-self interaction? */
else if (t_type == task_type_sub_self &&
t_subtype == task_subtype_density) {
const int bcount_i = ci->black_holes.count;
/* Make all density tasks depend on the drift and sorts. */
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t);
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t);
/* Start by constructing the task for the second hydro loop */
t_force = scheduler_addtask(sched, task_type_sub_self, task_subtype_force,
flags, 0, ci, NULL);
/* and the task for the time-step limiter */
if (with_timestep_limiter) {
t_limiter = scheduler_addtask(sched, task_type_sub_self,
task_subtype_limiter, flags, 0, ci, NULL);
}
/* The stellar feedback tasks */
if (with_feedback) {
t_star_density =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_stars_density, flags, 0, ci, NULL);
t_star_feedback =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_stars_feedback, flags, 0, ci, NULL);
#ifdef EXTRA_STAR_LOOPS
t_star_prep1 =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_stars_prep1, flags, 0, ci, NULL);
t_star_prep2 =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_stars_prep2, flags, 0, ci, NULL);
#endif
}
/* The sink tasks */
if (with_sink) {
t_sink_density =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_sink_density, flags, 0, ci, NULL);
t_sink_swallow =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_sink_swallow, flags, 0, ci, NULL);
t_sink_do_sink_swallow = scheduler_addtask(
sched, task_type_sub_self, task_subtype_sink_do_sink_swallow, flags,
0, ci, NULL);
t_sink_do_gas_swallow = scheduler_addtask(
sched, task_type_sub_self, task_subtype_sink_do_gas_swallow, flags,
0, ci, NULL);
}
/* The black hole feedback tasks */
if (with_black_holes && bcount_i > 0) {
t_bh_density =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_bh_density, flags, 0, ci, NULL);
t_bh_swallow =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_bh_swallow, flags, 0, ci, NULL);
t_do_gas_swallow =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_do_gas_swallow, flags, 0, ci, NULL);
t_do_bh_swallow =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_do_bh_swallow, flags, 0, ci, NULL);
t_bh_feedback =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_bh_feedback, flags, 0, ci, NULL);
}
if (with_rt) {
t_rt_gradient =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_rt_gradient, flags, 0, ci, NULL);
t_rt_transport =
scheduler_addtask(sched, task_type_sub_self,
task_subtype_rt_transport, flags, 0, ci, NULL);
}
/* Add the link between the new loop and the cell */
engine_addlink(e, &ci->hydro.force, t_force);
if (with_timestep_limiter) {
engine_addlink(e, &ci->hydro.limiter, t_limiter);
}
if (with_feedback) {
engine_addlink(e, &ci->stars.density, t_star_density);
engine_addlink(e, &ci->stars.feedback, t_star_feedback);
#ifdef EXTRA_STAR_LOOPS
engine_addlink(e, &ci->stars.prepare1, t_star_prep1);
engine_addlink(e, &ci->stars.prepare2, t_star_prep2);
#endif
}
if (with_sink) {
engine_addlink(e, &ci->sinks.density, t_sink_density);
engine_addlink(e, &ci->sinks.swallow, t_sink_swallow);
engine_addlink(e, &ci->sinks.do_sink_swallow, t_sink_do_sink_swallow);
engine_addlink(e, &ci->sinks.do_gas_swallow, t_sink_do_gas_swallow);
}
if (with_black_holes && bcount_i > 0) {
engine_addlink(e, &ci->black_holes.density, t_bh_density);
engine_addlink(e, &ci->black_holes.swallow, t_bh_swallow);
engine_addlink(e, &ci->black_holes.do_gas_swallow, t_do_gas_swallow);
engine_addlink(e, &ci->black_holes.do_bh_swallow, t_do_bh_swallow);
engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback);
}
if (with_rt) {
engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient);
engine_addlink(e, &ci->rt.rt_transport, t_rt_transport);
}
#ifdef EXTRA_HYDRO_LOOP
/* Start by constructing the task for the second and third hydro loop */
t_gradient = scheduler_addtask(sched, task_type_sub_self,
task_subtype_gradient, flags, 0, ci, NULL);
/* Add the link between the new loop and the cell */
engine_addlink(e, &ci->hydro.gradient, t_gradient);
/* Now, build all the dependencies for the hydro for the cells */
/* that are local and are not descendant of the same super_hydro-cells */
engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
t_limiter, ci, with_cooling,
with_timestep_limiter);
#else
/* Now, build all the dependencies for the hydro for the cells */
/* that are local and are not descendant of the same super_hydro-cells */
engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, ci,
with_cooling, with_timestep_limiter);
#endif
/* Create the task dependencies */
scheduler_addunlock(sched, t_force, ci->hydro.super->hydro.end_force);
if (with_feedback) {
if (with_cooling)
scheduler_addunlock(sched, ci->hydro.super->hydro.cooling_out,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->stars.drift,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->stars.stars_in,
t_star_density);
scheduler_addunlock(sched, t_star_density,
ci->hydro.super->stars.density_ghost);
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(sched, ci->hydro.super->stars.density_ghost,
t_star_prep1);
scheduler_addunlock(sched, t_star_prep1,
ci->hydro.super->stars.prep1_ghost);
scheduler_addunlock(sched, t_star_prep1,
ci->hydro.super->hydro.prep1_ghost);
scheduler_addunlock(sched, ci->hydro.super->stars.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, ci->hydro.super->hydro.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, t_star_prep2,
ci->hydro.super->stars.prep2_ghost);
scheduler_addunlock(sched, ci->hydro.super->stars.prep2_ghost,
t_star_feedback);
#else
scheduler_addunlock(sched, ci->hydro.super->stars.density_ghost,
t_star_feedback);
#endif
scheduler_addunlock(sched, t_star_feedback,
ci->hydro.super->stars.stars_out);
}
if (with_sink) {
/* Sink density */
scheduler_addunlock(sched, ci->hydro.super->sinks.drift,
t_sink_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_sink_density);
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_in,
t_sink_density);
scheduler_addunlock(sched, t_sink_density,
ci->hydro.super->sinks.density_ghost);
/* Do the sink_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.density_ghost,
t_sink_swallow);
scheduler_addunlock(sched, t_sink_swallow,
ci->hydro.super->sinks.sink_ghost1);
/* Do the sink_do_gas_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost1,
t_sink_do_gas_swallow);
scheduler_addunlock(sched, t_sink_do_gas_swallow,
ci->hydro.super->sinks.sink_ghost2);
/* Do the sink_do_sink_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost2,
t_sink_do_sink_swallow);
scheduler_addunlock(sched, t_sink_do_sink_swallow,
ci->hydro.super->sinks.sink_out);
}
if (with_black_holes && bcount_i > 0) {
if (with_cooling)
scheduler_addunlock(sched, ci->hydro.super->hydro.cooling_out,
t_bh_density);
scheduler_addunlock(sched, ci->hydro.super->black_holes.drift,
t_bh_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_bh_density);
scheduler_addunlock(sched, ci->hydro.super->black_holes.black_holes_in,
t_bh_density);
scheduler_addunlock(sched, t_bh_density,
ci->hydro.super->black_holes.density_ghost);
scheduler_addunlock(sched, ci->hydro.super->black_holes.density_ghost,
t_bh_swallow);
scheduler_addunlock(sched, t_bh_swallow,
ci->hydro.super->black_holes.swallow_ghost_1);
scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost_1,
t_do_gas_swallow);
scheduler_addunlock(sched, t_do_gas_swallow,
ci->hydro.super->black_holes.swallow_ghost_2);
scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost_2,
t_do_bh_swallow);
scheduler_addunlock(sched, t_do_bh_swallow,
ci->hydro.super->black_holes.swallow_ghost_3);
scheduler_addunlock(sched, ci->hydro.super->black_holes.swallow_ghost_3,
t_bh_feedback);
scheduler_addunlock(sched, t_bh_feedback,
ci->hydro.super->black_holes.black_holes_out);
}
if (with_timestep_limiter) {
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_limiter);
scheduler_addunlock(sched, ci->super->timestep, t_limiter);
scheduler_addunlock(sched, t_limiter, ci->super->kick1);
scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter);
}
if (with_timestep_sync && with_feedback) {
scheduler_addunlock(sched, t_star_feedback, ci->super->timestep_sync);
}
if (with_timestep_sync && with_black_holes && bcount_i > 0) {
scheduler_addunlock(sched, t_bh_feedback, ci->super->timestep_sync);
}
if (with_rt) {
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_gradient);
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_rt_gradient);
scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1,
t_rt_gradient);
scheduler_addunlock(sched, t_rt_gradient,
ci->hydro.super->rt.rt_ghost2);
scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2,
t_rt_transport);
scheduler_addunlock(sched, t_rt_transport,
ci->hydro.super->rt.rt_transport_out);
}
}
/* Otherwise, sub-pair interaction? */
else if (t_type == task_type_sub_pair &&
t_subtype == task_subtype_density) {
const int bcount_i = ci->black_holes.count;
const int bcount_j = cj->black_holes.count;
/* Make all density tasks depend on the drift */
if (ci->nodeID == nodeID) {
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t);
}
if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
scheduler_addunlock(sched, cj->hydro.super->hydro.drift, t);
}
/* Make all density tasks depend on the sorts */
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t);
if (ci->hydro.super != cj->hydro.super) {
scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t);
}
/* New task for the force */
t_force = scheduler_addtask(sched, task_type_sub_pair, task_subtype_force,
flags, 0, ci, cj);
#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
/* The order of operations for an inactive local cell interacting
* with an active foreign cell is not guaranteed because the density
* (and gradient) iact loops don't exist in that case. So we need
* an explicit dependency here to have sorted cells. */
/* Make all force tasks depend on the sorts */
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_force);
if (ci->hydro.super != cj->hydro.super) {
scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t_force);
}
#endif
/* and the task for the time-step limiter */
if (with_timestep_limiter) {
t_limiter = scheduler_addtask(sched, task_type_sub_pair,
task_subtype_limiter, flags, 0, ci, cj);
}
/* The stellar feedback tasks */
if (with_feedback) {
t_star_density =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_stars_density, flags, 0, ci, cj);
t_star_feedback =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_stars_feedback, flags, 0, ci, cj);
#ifdef EXTRA_STAR_LOOPS
t_star_prep1 =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_stars_prep1, flags, 0, ci, cj);
t_star_prep2 =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_stars_prep2, flags, 0, ci, cj);
#endif
}
/* The sink tasks */
if (with_sink) {
t_sink_density =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_sink_density, flags, 0, ci, cj);
t_sink_swallow =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_sink_swallow, flags, 0, ci, cj);
t_sink_do_sink_swallow = scheduler_addtask(
sched, task_type_sub_pair, task_subtype_sink_do_sink_swallow, flags,
0, ci, cj);
t_sink_do_gas_swallow = scheduler_addtask(
sched, task_type_sub_pair, task_subtype_sink_do_gas_swallow, flags,
0, ci, cj);
}
/* The black hole feedback tasks */
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
t_bh_density =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_bh_density, flags, 0, ci, cj);
t_bh_swallow =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_bh_swallow, flags, 0, ci, cj);
t_do_gas_swallow =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_do_gas_swallow, flags, 0, ci, cj);
t_do_bh_swallow =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_do_bh_swallow, flags, 0, ci, cj);
t_bh_feedback =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_bh_feedback, flags, 0, ci, cj);
}
if (with_rt) {
t_rt_gradient =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_rt_gradient, flags, 0, ci, cj);
t_rt_transport =
scheduler_addtask(sched, task_type_sub_pair,
task_subtype_rt_transport, flags, 0, ci, cj);
#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
/* The order of operations for an inactive local cell interacting
* with an active foreign cell is not guaranteed because the gradient
* iact loops don't exist in that case. So we need an explicit
* dependency here to have sorted cells. */
/* Make all force tasks depend on the sorts */
if (ci->hydro.super->rt.rt_sorts != NULL)
scheduler_addunlock(sched, ci->hydro.super->rt.rt_sorts,
t_rt_transport);
if (ci->hydro.super != cj->hydro.super) {
if (cj->hydro.super->rt.rt_sorts != NULL)
scheduler_addunlock(sched, cj->hydro.super->rt.rt_sorts,
t_rt_transport);
}
/* We need to ensure that a local inactive cell is sorted before
* the interaction in the transport loop. Local cells don't have
* an rt_sort task. */
if (ci->hydro.super->hydro.sorts != NULL)
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts,
t_rt_transport);
if ((ci->hydro.super != cj->hydro.super) &&
(cj->hydro.super->hydro.sorts != NULL))
scheduler_addunlock(sched, cj->hydro.super->hydro.sorts,
t_rt_transport);
#endif
}
engine_addlink(e, &ci->hydro.force, t_force);
engine_addlink(e, &cj->hydro.force, t_force);
if (with_timestep_limiter) {
engine_addlink(e, &ci->hydro.limiter, t_limiter);
engine_addlink(e, &cj->hydro.limiter, t_limiter);
}
if (with_feedback) {
engine_addlink(e, &ci->stars.density, t_star_density);
engine_addlink(e, &cj->stars.density, t_star_density);
engine_addlink(e, &ci->stars.feedback, t_star_feedback);
engine_addlink(e, &cj->stars.feedback, t_star_feedback);
#ifdef EXTRA_STAR_LOOPS
engine_addlink(e, &ci->stars.prepare1, t_star_prep1);
engine_addlink(e, &cj->stars.prepare1, t_star_prep1);
engine_addlink(e, &ci->stars.prepare2, t_star_prep2);
engine_addlink(e, &cj->stars.prepare2, t_star_prep2);
#endif
}
if (with_sink) {
engine_addlink(e, &ci->sinks.density, t_sink_density);
engine_addlink(e, &cj->sinks.density, t_sink_density);
engine_addlink(e, &ci->sinks.swallow, t_sink_swallow);
engine_addlink(e, &cj->sinks.swallow, t_sink_swallow);
engine_addlink(e, &ci->sinks.do_sink_swallow, t_sink_do_sink_swallow);
engine_addlink(e, &cj->sinks.do_sink_swallow, t_sink_do_sink_swallow);
engine_addlink(e, &ci->sinks.do_gas_swallow, t_sink_do_gas_swallow);
engine_addlink(e, &cj->sinks.do_gas_swallow, t_sink_do_gas_swallow);
}
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
engine_addlink(e, &ci->black_holes.density, t_bh_density);
engine_addlink(e, &cj->black_holes.density, t_bh_density);
engine_addlink(e, &ci->black_holes.swallow, t_bh_swallow);
engine_addlink(e, &cj->black_holes.swallow, t_bh_swallow);
engine_addlink(e, &ci->black_holes.do_gas_swallow, t_do_gas_swallow);
engine_addlink(e, &cj->black_holes.do_gas_swallow, t_do_gas_swallow);
engine_addlink(e, &ci->black_holes.do_bh_swallow, t_do_bh_swallow);
engine_addlink(e, &cj->black_holes.do_bh_swallow, t_do_bh_swallow);
engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback);
engine_addlink(e, &cj->black_holes.feedback, t_bh_feedback);
}
if (with_rt) {
engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient);
engine_addlink(e, &cj->rt.rt_gradient, t_rt_gradient);
engine_addlink(e, &ci->rt.rt_transport, t_rt_transport);
engine_addlink(e, &cj->rt.rt_transport, t_rt_transport);
}
#ifdef EXTRA_HYDRO_LOOP
/* Start by constructing the task for the second and third hydro loop */
t_gradient = scheduler_addtask(sched, task_type_sub_pair,
task_subtype_gradient, flags, 0, ci, cj);
/* Add the link between the new loop and both cells */
engine_addlink(e, &ci->hydro.gradient, t_gradient);
engine_addlink(e, &cj->hydro.gradient, t_gradient);
/* Now, build all the dependencies for the hydro for the cells */
/* that are local and are not descendant of the same super_hydro-cells */
if (ci->nodeID == nodeID) {
engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
t_limiter, ci, with_cooling,
with_timestep_limiter);
}
if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
t_limiter, cj, with_cooling,
with_timestep_limiter);
}
#else
/* Now, build all the dependencies for the hydro for the cells */
/* that are local and are not descendant of the same super_hydro-cells */
if (ci->nodeID == nodeID) {
engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, ci,
with_cooling,
with_timestep_limiter);
}
if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, cj,
with_cooling,
with_timestep_limiter);
}
#endif
if (with_feedback) {
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts,
t_star_density);
if (ci->hydro.super != cj->hydro.super) {
scheduler_addunlock(sched, cj->hydro.super->hydro.sorts,
t_star_density);
}
}
if (with_rt) {
scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_rt_gradient);
if (ci->hydro.super != cj->hydro.super) {
scheduler_addunlock(sched, cj->hydro.super->hydro.sorts,
t_rt_gradient);
}
}
if (ci->nodeID == nodeID) {
scheduler_addunlock(sched, t_force, ci->hydro.super->hydro.end_force);
if (with_feedback) {
if (with_cooling)
scheduler_addunlock(sched, ci->hydro.super->hydro.cooling_out,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->stars.drift,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_star_density);
scheduler_addunlock(sched, ci->hydro.super->stars.stars_in,
t_star_density);
scheduler_addunlock(sched, t_star_density,
ci->hydro.super->stars.density_ghost);
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(sched, ci->hydro.super->stars.density_ghost,
t_star_prep1);
scheduler_addunlock(sched, t_star_prep1,
ci->hydro.super->stars.prep1_ghost);
scheduler_addunlock(sched, t_star_prep1,
ci->hydro.super->hydro.prep1_ghost);
scheduler_addunlock(sched, ci->hydro.super->stars.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, ci->hydro.super->hydro.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, t_star_prep2,
ci->hydro.super->stars.prep2_ghost);
scheduler_addunlock(sched, ci->hydro.super->stars.prep2_ghost,
t_star_feedback);
#else
scheduler_addunlock(sched, ci->hydro.super->stars.density_ghost,
t_star_feedback);
#endif
scheduler_addunlock(sched, t_star_feedback,
ci->hydro.super->stars.stars_out);
}
if (with_sink) {
/* Sink density */
scheduler_addunlock(sched, ci->hydro.super->sinks.drift,
t_sink_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_sink_density);
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_in,
t_sink_density);
scheduler_addunlock(sched, t_sink_density,
ci->hydro.super->sinks.density_ghost);
/* Do the sink_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.density_ghost,
t_sink_swallow);
scheduler_addunlock(sched, t_sink_swallow,
ci->hydro.super->sinks.sink_ghost1);
/* Do the sink_do_gas_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost1,
t_sink_do_gas_swallow);
scheduler_addunlock(sched, t_sink_do_gas_swallow,
ci->hydro.super->sinks.sink_ghost2);
/* Do the sink_do_sink_swallow */
scheduler_addunlock(sched, ci->hydro.super->sinks.sink_ghost2,
t_sink_do_sink_swallow);
scheduler_addunlock(sched, t_sink_do_sink_swallow,
ci->hydro.super->sinks.sink_out);
}
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
if (with_cooling)
scheduler_addunlock(sched, ci->hydro.super->hydro.cooling_out,
t_bh_density);
scheduler_addunlock(sched, ci->hydro.super->black_holes.drift,
t_bh_density);
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_bh_density);
scheduler_addunlock(
sched, ci->hydro.super->black_holes.black_holes_in, t_bh_density);
scheduler_addunlock(sched, t_bh_density,
ci->hydro.super->black_holes.density_ghost);
scheduler_addunlock(sched, ci->hydro.super->black_holes.density_ghost,
t_bh_swallow);
scheduler_addunlock(sched, t_bh_swallow,
ci->hydro.super->black_holes.swallow_ghost_1);
scheduler_addunlock(sched,
ci->hydro.super->black_holes.swallow_ghost_1,
t_do_gas_swallow);
scheduler_addunlock(sched, t_do_gas_swallow,
ci->hydro.super->black_holes.swallow_ghost_2);
scheduler_addunlock(sched,
ci->hydro.super->black_holes.swallow_ghost_2,
t_do_bh_swallow);
scheduler_addunlock(sched, t_do_bh_swallow,
ci->hydro.super->black_holes.swallow_ghost_3);
scheduler_addunlock(sched,
ci->hydro.super->black_holes.swallow_ghost_3,
t_bh_feedback);
scheduler_addunlock(sched, t_bh_feedback,
ci->hydro.super->black_holes.black_holes_out);
}
if (with_timestep_limiter) {
scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_limiter);
scheduler_addunlock(sched, ci->super->timestep, t_limiter);
scheduler_addunlock(sched, t_limiter, ci->super->kick1);
scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter);
}
if (with_timestep_sync && with_feedback) {
scheduler_addunlock(sched, t_star_feedback, ci->super->timestep_sync);
}
if (with_timestep_sync && with_black_holes &&
(bcount_i > 0 || bcount_j > 0)) {
scheduler_addunlock(sched, t_bh_feedback, ci->super->timestep_sync);
}
if (with_rt) {
scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
t_rt_gradient);
scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1,
t_rt_gradient);
scheduler_addunlock(sched, t_rt_gradient,
ci->hydro.super->rt.rt_ghost2);
scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2,
t_rt_transport);
scheduler_addunlock(sched, t_rt_transport,
ci->hydro.super->rt.rt_transport_out);
}
} else /* ci->nodeID != nodeID */ {
if (with_feedback) {
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
t_star_prep1);
#endif
scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
t_star_feedback);
}
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
scheduler_addunlock(sched, t_bh_swallow,
ci->hydro.super->black_holes.swallow_ghost_1);
}
}
if (cj->nodeID == nodeID) {
if (ci->hydro.super != cj->hydro.super) {
scheduler_addunlock(sched, t_force, cj->hydro.super->hydro.end_force);
if (with_feedback) {
if (with_cooling)
scheduler_addunlock(sched, cj->hydro.super->hydro.cooling_out,
t_star_density);
scheduler_addunlock(sched, cj->hydro.super->stars.sorts,
t_star_density);
scheduler_addunlock(sched, cj->hydro.super->stars.drift,
t_star_density);
scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
t_star_density);
scheduler_addunlock(sched, cj->hydro.super->stars.stars_in,
t_star_density);
scheduler_addunlock(sched, t_star_density,
cj->hydro.super->stars.density_ghost);
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(sched, cj->hydro.super->stars.density_ghost,
t_star_prep1);
scheduler_addunlock(sched, t_star_prep1,
cj->hydro.super->stars.prep1_ghost);
scheduler_addunlock(sched, t_star_prep1,
cj->hydro.super->hydro.prep1_ghost);
scheduler_addunlock(sched, cj->hydro.super->stars.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, cj->hydro.super->hydro.prep1_ghost,
t_star_prep2);
scheduler_addunlock(sched, t_star_prep2,
cj->hydro.super->stars.prep2_ghost);
scheduler_addunlock(sched, cj->hydro.super->stars.prep2_ghost,
t_star_feedback);
#else
scheduler_addunlock(sched, cj->hydro.super->stars.density_ghost,
t_star_feedback);
#endif
scheduler_addunlock(sched, t_star_feedback,
cj->hydro.super->stars.stars_out);
}
if (with_sink) {
/* Sink density */
scheduler_addunlock(sched, cj->hydro.super->sinks.drift,
t_sink_density);
scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
t_sink_density);
scheduler_addunlock(sched, cj->hydro.super->sinks.sink_in,
t_sink_density);
scheduler_addunlock(sched, t_sink_density,
cj->hydro.super->sinks.density_ghost);
/* Do the sink_swallow */
scheduler_addunlock(sched, cj->hydro.super->sinks.density_ghost,
t_sink_swallow);
scheduler_addunlock(sched, t_sink_swallow,
cj->hydro.super->sinks.sink_ghost1);
/* Do the sink_do_gas_swallow */
scheduler_addunlock(sched, cj->hydro.super->sinks.sink_ghost1,
t_sink_do_gas_swallow);
scheduler_addunlock(sched, t_sink_do_gas_swallow,
cj->hydro.super->sinks.sink_ghost2);
/* Do the sink_do_sink_swallow */
scheduler_addunlock(sched, cj->hydro.super->sinks.sink_ghost2,
t_sink_do_sink_swallow);
scheduler_addunlock(sched, t_sink_do_sink_swallow,
cj->hydro.super->sinks.sink_out);
}
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
if (with_cooling)
scheduler_addunlock(sched, cj->hydro.super->hydro.cooling_out,
t_bh_density);
scheduler_addunlock(sched, cj->hydro.super->black_holes.drift,
t_bh_density);
scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
t_bh_density);
scheduler_addunlock(sched,
cj->hydro.super->black_holes.black_holes_in,
t_bh_density);
scheduler_addunlock(sched, t_bh_density,
cj->hydro.super->black_holes.density_ghost);
scheduler_addunlock(sched,
cj->hydro.super->black_holes.density_ghost,
t_bh_swallow);
scheduler_addunlock(sched, t_bh_swallow,
cj->hydro.super->black_holes.swallow_ghost_1);
scheduler_addunlock(sched,
cj->hydro.super->black_holes.swallow_ghost_1,
t_do_gas_swallow);
scheduler_addunlock(sched, t_do_gas_swallow,
cj->hydro.super->black_holes.swallow_ghost_2);
scheduler_addunlock(sched,
cj->hydro.super->black_holes.swallow_ghost_2,
t_do_bh_swallow);
scheduler_addunlock(sched, t_do_bh_swallow,
cj->hydro.super->black_holes.swallow_ghost_3);
scheduler_addunlock(sched,
cj->hydro.super->black_holes.swallow_ghost_3,
t_bh_feedback);
scheduler_addunlock(sched, t_bh_feedback,
cj->hydro.super->black_holes.black_holes_out);
}
if (with_rt) {
scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
t_rt_gradient);
scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost1,
t_rt_gradient);
scheduler_addunlock(sched, t_rt_gradient,
cj->hydro.super->rt.rt_ghost2);
scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost2,
t_rt_transport);
scheduler_addunlock(sched, t_rt_transport,
cj->hydro.super->rt.rt_transport_out);
}
if (with_timestep_limiter) {
scheduler_addunlock(sched, cj->hydro.super->hydro.drift, t_limiter);
}
}
if (ci->super != cj->super) {
if (with_timestep_limiter) {
scheduler_addunlock(sched, cj->super->timestep, t_limiter);
scheduler_addunlock(sched, t_limiter, cj->super->kick1);
scheduler_addunlock(sched, t_limiter, cj->super->timestep_limiter);
}
if (with_timestep_sync && with_feedback) {
scheduler_addunlock(sched, t_star_feedback,
cj->super->timestep_sync);
}
if (with_timestep_sync && with_black_holes &&
(bcount_i > 0 || bcount_j > 0)) {
scheduler_addunlock(sched, t_bh_feedback, cj->super->timestep_sync);
}
}
} else /* cj->nodeID != nodeID */ {
if (with_feedback) {
#ifdef EXTRA_STAR_LOOPS
scheduler_addunlock(sched, cj->hydro.super->stars.sorts,
t_star_prep1);
#endif
scheduler_addunlock(sched, cj->hydro.super->stars.sorts,
t_star_feedback);
}
if (with_black_holes && (bcount_i > 0 || bcount_j > 0)) {
scheduler_addunlock(sched, t_bh_swallow,
cj->hydro.super->black_holes.swallow_ghost_1);
}
}
}
}
}
/**
* @brief Constructs the top-level pair tasks for the first hydro loop over
* neighbours
*
* Here we construct all the tasks for all possible neighbouring non-empty
* local cells in the hierarchy. No dependencies are being added thus far.
* Additional loop over neighbours can later be added by simply duplicating
* all the tasks created by this function.
*
* @param map_data Offset of first two indices disguised as a pointer.
* @param num_elements Number of cells to traverse.
* @param extra_data The #engine.
*/
void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements,
void *extra_data) {
/* Extract the engine pointer. */
struct engine *e = (struct engine *)extra_data;
const int periodic = e->s->periodic;
const int with_feedback = (e->policy & engine_policy_feedback);
const int with_stars = (e->policy & engine_policy_stars);
const int with_sinks = (e->policy & engine_policy_sinks);
const int with_black_holes = (e->policy & engine_policy_black_holes);
struct space *s = e->s;
struct scheduler *sched = &e->sched;
const int nodeID = e->nodeID;
const int *cdim = s->cdim;
struct cell *cells = s->cells_top;
/* Loop through the elements, which are just byte offsets from NULL. */
for (int ind = 0; ind < num_elements; ind++) {
/* Get the cell index. */
const int cid = (size_t)(map_data) + ind;
/* Integer indices of the cell in the top-level grid */
const int i = cid / (cdim[1] * cdim[2]);
const int j = (cid / cdim[2]) % cdim[1];
const int k = cid % cdim[2];
/* Get the cell */
struct cell *ci = &cells[cid];
/* Skip cells without hydro or star particles */
if ((ci->hydro.count == 0) && (!with_stars || ci->stars.count == 0) &&
(!with_sinks || ci->sinks.count == 0) &&
(!with_black_holes || ci->black_holes.count == 0))
continue;
/* If the cell is local build a self-interaction */
if (ci->nodeID == nodeID) {
scheduler_addtask(sched, task_type_self, task_subtype_density, 0, 0, ci,
NULL);
}
/* Now loop over all the neighbours of this cell */
for (int ii = -1; ii < 2; ii++) {
int iii = i + ii;
if (!periodic && (iii < 0 || iii >= cdim[0])) continue;
iii = (iii + cdim[0]) % cdim[0];
for (int jj = -1; jj < 2; jj++) {
int jjj = j + jj;
if (!periodic && (jjj < 0 || jjj >= cdim[1])) continue;
jjj = (jjj + cdim[1]) % cdim[1];
for (int kk = -1; kk < 2; kk++) {
int kkk = k + kk;
if (!periodic && (kkk < 0 || kkk >= cdim[2])) continue;
kkk = (kkk + cdim[2]) % cdim[2];
/* Get the neighbouring cell */
const int cjd = cell_getid(cdim, iii, jjj, kkk);
struct cell *cj = &cells[cjd];
/* Is that neighbour local and does it have gas or star particles ? */
if ((cid >= cjd) ||
((cj->hydro.count == 0) &&
(!with_feedback || cj->stars.count == 0) &&
(!with_sinks || cj->sinks.count == 0) &&
(!with_black_holes || cj->black_holes.count == 0)) ||
(ci->nodeID != nodeID && cj->nodeID != nodeID))
continue;
/* Construct the pair task */
const int sid = sortlistID[(kk + 1) + 3 * ((jj + 1) + 3 * (ii + 1))];
scheduler_addtask(sched, task_type_pair, task_subtype_density, sid, 0,
ci, cj);
#ifdef SWIFT_DEBUG_CHECKS
#ifdef WITH_MPI
/* Let's cross-check that we had a proxy for that cell */
if (ci->nodeID == nodeID && cj->nodeID != engine_rank) {
/* Find the proxy for this node */
const int proxy_id = e->proxy_ind[cj->nodeID];
if (proxy_id < 0)
error("No proxy exists for that foreign node %d!", cj->nodeID);
const struct proxy *p = &e->proxies[proxy_id];
/* Check whether the cell exists in the proxy */
int n = 0;
for (n = 0; n < p->nr_cells_in; n++)
if (p->cells_in[n] == cj) break;
if (n == p->nr_cells_in)
error(
"Cell %d not found in the proxy but trying to construct "
"hydro task!",
cjd);
} else if (cj->nodeID == nodeID && ci->nodeID != engine_rank) {
/* Find the proxy for this node */
const int proxy_id = e->proxy_ind[ci->nodeID];
if (proxy_id < 0)
error("No proxy exists for that foreign node %d!", ci->nodeID);
const struct proxy *p = &e->proxies[proxy_id];
/* Check whether the cell exists in the proxy */
int n = 0;
for (n = 0; n < p->nr_cells_in; n++)
if (p->cells_in[n] == ci) break;
if (n == p->nr_cells_in)
error(
"Cell %d not found in the proxy but trying to construct "
"hydro task!",
cid);
}
#endif /* WITH_MPI */
#endif /* SWIFT_DEBUG_CHECKS */
}
}
}
}
}
struct cell_type_pair {
struct cell *ci, *cj;
int type;
};
/**
* @brief Recurse down to the super level and add a dependency between
* rt_advance_cell_time and tend tasks. Note: This function is intended
* for the sending side, i.e. for local cells.
*
* If we're running with RT subcycling, we need to ensure that nothing
* is sent before the advance cell time task has finished. This may
* overwrite the correct cell times, particularly so when we're sending
* over data for non-RT tasks, e.g. for gravity pair tasks. Therefore the
* send/tend task needs to be unlocked by the rt_advance_cell_time task.
*
* The send/tend task is on the top level, while the rt_advance_cell_time
* task is on the super level. This function simply recurses down to the
* super level and adds the required dependency.
*
* @param c cell to check/recurse into
* @param tend the send/tend task that needs to be unlocked.
* @param e the engine
*/
void engine_addunlock_rt_advance_cell_time_tend(struct cell *c,
struct task *tend,
struct engine *e) {
/* safety measure */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
if (cell_is_empty(c)) return;
if (c->super == c) {
/* Found the super level cell. Add dependency from rt_advance_cell_time, if
* it exists. */
if (c->super->rt.rt_advance_cell_time != NULL) {
scheduler_addunlock(&e->sched, c->super->rt.rt_advance_cell_time, tend);
}
#ifdef SWIFT_RT_DEBUG_CHECKS
else {
error("Got local super cell without rt_advance_cell_time task");
}
#endif
} else {
/* descend the tree until you find the super level */
if (c->split) {
for (int k = 0; k < 8; k++) {
if (c->progeny[k] != NULL) {
engine_addunlock_rt_advance_cell_time_tend(c->progeny[k], tend, e);
}
}
}
}
}
void engine_addtasks_send_mapper(void *map_data, int num_elements,
void *extra_data) {
struct engine *e = (struct engine *)extra_data;
const int with_star_formation = (e->policy & engine_policy_star_formation);
const int with_limiter = (e->policy & engine_policy_timestep_limiter);
const int with_feedback = (e->policy & engine_policy_feedback);
const int with_sync = (e->policy & engine_policy_timestep_sync);
const int with_rt = (e->policy & engine_policy_rt);
struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data;
#if defined(WITH_MPI) && !defined(SWIFT_DEBUG_CHECKS)
if (e->policy & engine_policy_sinks) {
error("TODO: Sink MPI tasks are not implemented yet!");
}
#endif
for (int k = 0; k < num_elements; k++) {
struct cell *ci = cell_type_pairs[k].ci;
struct cell *cj = cell_type_pairs[k].cj;
const int type = cell_type_pairs[k].type;
#ifdef WITH_MPI
if (!cell_is_empty(ci)) {
/* Add the timestep exchange task */
struct task *tend = scheduler_addtask(
&e->sched, task_type_send, task_subtype_tend, ci->mpi.tag, 0, ci, cj);
scheduler_addunlock(&e->sched, ci->timestep_collect, tend);
engine_addlink(e, &ci->mpi.send, tend);
if (with_rt && (type & proxy_cell_type_hydro))
engine_addunlock_rt_advance_cell_time_tend(ci, tend, e);
}
#endif
/* Add the send tasks for the cells in the proxy that have a hydro
* connection. */
if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro))
engine_addtasks_send_hydro(e, ci, cj, /*t_xv=*/NULL,
/*t_rho=*/NULL, /*t_gradient=*/NULL,
/*t_prep1=*/NULL,
/*t_limiter=*/NULL, /*t_pack_limiter=*/NULL,
/*t_rt_gradient=*/NULL,
/*t_rt_transport=*/NULL, with_feedback,
with_limiter, with_sync, with_rt);
/* Add the send tasks for the cells in the proxy that have a stars
* connection. */
if ((e->policy & engine_policy_feedback) && (type & proxy_cell_type_hydro))
engine_addtasks_send_stars(e, ci, cj, /*t_density=*/NULL,
/*t_prep2=*/NULL,
/*t_sf_counts=*/NULL, with_star_formation);
/* Add the send tasks for the cells in the proxy that have a black holes
* connection. */
if ((e->policy & engine_policy_black_holes) &&
(type & proxy_cell_type_hydro))
engine_addtasks_send_black_holes(e, ci, cj, /*t_rho=*/NULL,
/*t_swallow=*/NULL,
/*t_gas_swallow=*/NULL,
/*t_feedback=*/NULL);
/* Add the send tasks for the cells in the proxy that have a gravity
* connection. */
if ((e->policy & engine_policy_self_gravity) &&
(type & proxy_cell_type_gravity))
engine_addtasks_send_gravity(e, ci, cj, /*t_grav_counts=*/NULL,
/*t_grav=*/NULL, with_star_formation);
}
}
void engine_addtasks_recv_mapper(void *map_data, int num_elements,
void *extra_data) {
struct engine *e = (struct engine *)extra_data;
const int with_star_formation = (e->policy & engine_policy_star_formation);
const int with_limiter = (e->policy & engine_policy_timestep_limiter);
const int with_feedback = (e->policy & engine_policy_feedback);
const int with_black_holes = (e->policy & engine_policy_black_holes);
const int with_sync = (e->policy & engine_policy_timestep_sync);
const int with_rt = (e->policy & engine_policy_rt);
struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data;
#if defined(WITH_MPI) && !defined(SWIFT_DEBUG_CHECKS)
if (e->policy & engine_policy_sinks) {
error("TODO: Sink MPI tasks are not implemented yet!");
}
#endif
for (int k = 0; k < num_elements; k++) {
struct cell *ci = cell_type_pairs[k].ci;
const int type = cell_type_pairs[k].type;
struct task *tend = NULL;
#ifdef WITH_MPI
/* Add the timestep exchange task */
if (!cell_is_empty(ci)) {
tend = scheduler_addtask(&e->sched, task_type_recv, task_subtype_tend,
ci->mpi.tag, 0, ci, NULL);
engine_addlink(e, &ci->mpi.recv, tend);
/* If we're running with RT, there may be foreign cells that
* don't receive any actual hydro particles. These cells however
* still need to have the "advance_cell_time" and "rt_collect_times"
* tasks in order for their time variables to be correct, especially
* during syb-cycles, where the cell times aren't communicated at the
* end of the step. So we create them now. */
if (with_rt) {
#ifdef SWIFT_RT_DEBUG_CHECKS
if (ci->top == NULL) error("Working on a cell with top == NULL??");
#endif
/* Create the RT collect times task at the top level, if it hasn't
* already. */
if (ci->top->rt.rt_collect_times == NULL) {
ci->top->rt.rt_collect_times =
scheduler_addtask(&e->sched, task_type_rt_collect_times,
task_subtype_none, 0, 0, ci->top, NULL);
}
/* We don't need rt_collect_times -> tend dependencies. They never
* run at the same time. rt_collect_times runs in sub-cycles,
* tend runs on normal steps. */
/* Make sure the timestep task replacements, i.e. rt_advance_cell_time,
* exists on the super levels regardless of proxy type.
* This needs to be done before engine_addtasks_recv_hydro so we
* can set appropriate unlocks there without re-creating tasks. */
engine_addtasks_recv_rt_advance_cell_time(e, ci, tend);
}
}
#endif
/* Add the recv tasks for the cells in the proxy that have a hydro
* connection. */
if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro)) {
engine_addtasks_recv_hydro(
e, ci, /*t_xv=*/NULL, /*t_rho=*/NULL, /*t_gradient=*/NULL,
/*t_prep1=*/NULL, /*t_limiter=*/NULL, /*t_unpack_limiter=*/NULL,
/*t_rt_gradient=*/NULL, /*t_rt_transport=*/NULL,
/*t_rt_sorts=*/NULL, tend, with_feedback, with_black_holes,
with_limiter, with_sync, with_rt);
}
/* Add the recv tasks for the cells in the proxy that have a stars
* connection. */
if ((e->policy & engine_policy_feedback) && (type & proxy_cell_type_hydro))
engine_addtasks_recv_stars(e, ci, /*t_density=*/NULL, /*t_prep2=*/NULL,
/*t_sf_counts=*/NULL, tend,
with_star_formation);
/* Add the recv tasks for the cells in the proxy that have a black holes
* connection. */
if ((e->policy & engine_policy_black_holes) &&
(type & proxy_cell_type_hydro))
engine_addtasks_recv_black_holes(e, ci, /*t_rho=*/NULL,
/*t_swallow=*/NULL,
/*t_gas_swallow=*/NULL,
/*t_feedback=*/NULL, tend);
/* Add the recv tasks for the cells in the proxy that have a gravity
* connection. */
if ((e->policy & engine_policy_self_gravity) &&
(type & proxy_cell_type_gravity))
engine_addtasks_recv_gravity(e, ci, /*t_grav_counts*/ NULL,
/*t_grav=*/NULL, tend, with_star_formation);
}
}
/**
* @brief Constructs the top-level self + pair tasks for the FOF loop over
* neighbours.
*
* Here we construct all the tasks for all possible neighbouring non-empty
* local cells in the hierarchy. No dependencies are being added thus far.
* Additional loop over neighbours can later be added by simply duplicating
* all the tasks created by this function.
*
* @param map_data Offset of first two indices disguised as a pointer.
* @param num_elements Number of cells to traverse.
* @param extra_data The #engine.
*/
void engine_make_fofloop_tasks_mapper(void *map_data, int num_elements,
void *extra_data) {
/* Extract the engine pointer. */
struct engine *e = (struct engine *)extra_data;
struct space *s = e->s;
struct scheduler *sched = &e->sched;
const int nodeID = e->nodeID;
const int *cdim = s->cdim;
struct cell *cells = s->cells_top;
/* Loop through the elements, which are just byte offsets from NULL. */
for (int ind = 0; ind < num_elements; ind++) {
/* Get the cell index. */
const int cid = (size_t)(map_data) + ind;
const int i = cid / (cdim[1] * cdim[2]);
const int j = (cid / cdim[2]) % cdim[1];
const int k = cid % cdim[2];
/* Get the cell */
struct cell *ci = &cells[cid];
/* Skip cells without gravity particles */
if (ci->grav.count == 0) continue;
/* If the cells is local build a self-interaction */
if (ci->nodeID == nodeID) {
scheduler_addtask(sched, task_type_fof_self, task_subtype_none, 0, 0, ci,
NULL);
scheduler_addtask(sched, task_type_fof_attach_self, task_subtype_none, 0,
0, ci, NULL);
}
/* Now loop over all the neighbours of this cell */
for (int ii = -1; ii < 2; ii++) {
int iii = i + ii;
if (!s->periodic && (iii < 0 || iii >= cdim[0])) continue;
iii = (iii + cdim[0]) % cdim[0];
for (int jj = -1; jj < 2; jj++) {
int jjj = j + jj;
if (!s->periodic && (jjj < 0 || jjj >= cdim[1])) continue;
jjj = (jjj + cdim[1]) % cdim[1];
for (int kk = -1; kk < 2; kk++) {
int kkk = k + kk;
if (!s->periodic && (kkk < 0 || kkk >= cdim[2])) continue;
kkk = (kkk + cdim[2]) % cdim[2];
/* Get the neighbouring cell */
const int cjd = cell_getid(cdim, iii, jjj, kkk);
struct cell *cj = &cells[cjd];
/* Does that neighbour have particles ? */
if (cid >= cjd || cj->grav.count == 0) continue;
/* Construct the pair search task only for fully local pairs */
if (ci->nodeID == nodeID && cj->nodeID == nodeID)
scheduler_addtask(sched, task_type_fof_pair, task_subtype_none, 0,
0, ci, cj);
/* Construct the pair search task for pairs overlapping with the node
*/
if (ci->nodeID == nodeID || cj->nodeID == nodeID)
scheduler_addtask(sched, task_type_fof_attach_pair,
task_subtype_none, 0, 0, ci, cj);
}
}
}
}
}
/**
* @brief Fill the #space's task list with FOF tasks.
*
* @param e The #engine we are working with.
*/
void engine_make_fof_tasks(struct engine *e) {
struct space *s = e->s;
struct scheduler *sched = &e->sched;
ticks tic = getticks();
if (e->restarting) {
/* Re-set the scheduler. */
scheduler_reset(sched, engine_estimate_nr_tasks(e));
}
/* Construct a FOF loop over neighbours */
if (e->policy & engine_policy_fof)
threadpool_map(&e->threadpool, engine_make_fofloop_tasks_mapper, NULL,
s->nr_cells, 1, threadpool_auto_chunk_size, e);
if (e->verbose)
message("Making FOF tasks took %.3f %s.",
clocks_from_ticks(getticks() - tic), clocks_getunit());
tic = getticks();
/* Split the tasks. */
scheduler_splittasks(sched, /*fof_tasks=*/1, e->verbose);
if (e->verbose)
message("Splitting FOF tasks took %.3f %s.",
clocks_from_ticks(getticks() - tic), clocks_getunit());
#ifdef SWIFT_DEBUG_CHECKS
/* Verify that we are not left with invalid tasks */
for (int i = 0; i < e->sched.nr_tasks; ++i) {
const struct task *t = &e->sched.tasks[i];
if (t->ci == NULL && t->cj != NULL && !t->skip) error("Invalid task");
}
#endif
/* Report the number of tasks we actually used */
if (e->verbose)
message(
"Nr. of tasks: %d allocated tasks: %d ratio: %f memory use: %zd MB.",
e->sched.nr_tasks, e->sched.size,
(float)e->sched.nr_tasks / (float)e->sched.size,
e->sched.size * sizeof(struct task) / (1024 * 1024));
if (e->verbose)
message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
clocks_getunit());
}
/**
* @brief Fill the #space's task list.
*
* @param e The #engine we are working with.
*/
void engine_maketasks(struct engine *e) {
struct space *s = e->s;
struct scheduler *sched = &e->sched;
struct cell *cells = s->cells_top;
const int nr_cells = s->nr_cells;
const ticks tic = getticks();
/* Re-set the scheduler. */
scheduler_reset(sched, engine_estimate_nr_tasks(e));
ticks tic2 = getticks();
/* Construct the first hydro loop over neighbours */
if (e->policy & engine_policy_hydro)
threadpool_map(&e->threadpool, engine_make_hydroloop_tasks_mapper, NULL,
s->nr_cells, 1, threadpool_auto_chunk_size, e);
if (e->verbose)
message("Making hydro tasks took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
tic2 = getticks();
/* Add the self gravity tasks. */
if (e->policy & engine_policy_self_gravity) {
threadpool_map(&e->threadpool, engine_make_self_gravity_tasks_mapper, NULL,
s->nr_cells, 1, threadpool_auto_chunk_size, e);
}
if (e->verbose)
message("Making gravity tasks took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
/* Add the external gravity tasks. */
if (e->policy & engine_policy_external_gravity)
engine_make_external_gravity_tasks(e);
if (e->sched.nr_tasks == 0 && (s->nr_gparts > 0 || s->nr_parts > 0))
error("We have particles but no hydro or gravity tasks were created.");
tic2 = getticks();
/* Split the tasks. */
scheduler_splittasks(sched, /*fof_tasks=*/0, e->verbose);
if (e->verbose)
message("Splitting tasks took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
#ifdef SWIFT_DEBUG_CHECKS
/* Verify that we are not left with invalid tasks */
for (int i = 0; i < e->sched.nr_tasks; ++i) {
const struct task *t = &e->sched.tasks[i];
if (t->ci == NULL && t->cj != NULL && !t->skip) error("Invalid task");
}
#endif
/* Free the old list of cell-task links. */
if (e->links != NULL) swift_free("links", e->links);
e->size_links = e->sched.nr_tasks * e->links_per_tasks;
/* Make sure that we have space for more links than last time. */
if (e->size_links < e->nr_links * engine_rebuild_link_alloc_margin)
e->size_links = e->nr_links * engine_rebuild_link_alloc_margin;
/* Allocate the new link list */
if ((e->links = (struct link *)swift_malloc(
"links", sizeof(struct link) * e->size_links)) == NULL)
error("Failed to allocate cell-task links.");
e->nr_links = 0;
tic2 = getticks();
/* Count the number of tasks associated with each cell and
store the density tasks in each cell, and make each sort
depend on the sorts of its progeny. */
threadpool_map(&e->threadpool, engine_count_and_link_tasks_mapper,
sched->tasks, sched->nr_tasks, sizeof(struct task),
threadpool_auto_chunk_size, e);
if (e->verbose)
message("Counting and linking tasks took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
tic2 = getticks();
/* Re-set the tag counter. MPI tags are defined for top-level cells in
* cell_set_super_mapper. */
#ifdef WITH_MPI
cell_next_tag = 0;
#endif
/* Now that the self/pair tasks are at the right level, set the super
* pointers. */
threadpool_map(&e->threadpool, cell_set_super_mapper, cells, nr_cells,
sizeof(struct cell), threadpool_auto_chunk_size, e);
if (e->verbose)
message("Setting super-pointers took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
/* Append hierarchical tasks to each cell. */
threadpool_map(&e->threadpool, engine_make_hierarchical_tasks_mapper, cells,
nr_cells, sizeof(struct cell), threadpool_auto_chunk_size, e);
tic2 = getticks();
/* Run through the tasks and make force tasks for each density task.
Each force task depends on the cell ghosts and unlocks the kick task
of its super-cell. */
if (e->policy & engine_policy_hydro) {
/* Note that this does not scale well at all so we do not use the
* threadpool version here until the reason for this is found.
* We call the mapper function directly as if there was only 1 thread
* in the pool. */
engine_make_extra_hydroloop_tasks_mapper(sched->tasks, sched->nr_tasks, e);
/* threadpool_map(&e->threadpool, engine_make_extra_hydroloop_tasks_mapper,
* sched->tasks, sched->nr_tasks, sizeof(struct task),
* threadpool_auto_chunk_size, e); */
}
if (e->verbose)
message("Making extra hydroloop tasks took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
tic2 = getticks();
/* Add the dependencies for the gravity stuff */
if (e->policy &
(engine_policy_self_gravity | engine_policy_external_gravity)) {
threadpool_map(&e->threadpool, engine_link_gravity_tasks_mapper,
e->sched.tasks, e->sched.nr_tasks, sizeof(struct task),
threadpool_auto_chunk_size, e);
}
if (e->verbose)
message("Linking gravity tasks took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
tic2 = getticks();
#ifdef WITH_MPI
/* Add the communication tasks if MPI is being used. */
if (e->policy & engine_policy_mpi) {
tic2 = getticks();
/* Loop over the proxies and add the send tasks, which also generates the
* cell tags for super-cells. */
int max_num_send_cells = 0;
for (int pid = 0; pid < e->nr_proxies; pid++)
max_num_send_cells += e->proxies[pid].nr_cells_out;
struct cell_type_pair *send_cell_type_pairs = NULL;
if ((send_cell_type_pairs = (struct cell_type_pair *)malloc(
sizeof(struct cell_type_pair) * max_num_send_cells)) == NULL)
error("Failed to allocate temporary cell pointer list.");
int num_send_cells = 0;
for (int pid = 0; pid < e->nr_proxies; pid++) {
/* Get a handle on the proxy. */
struct proxy *p = &e->proxies[pid];
for (int k = 0; k < p->nr_cells_out; k++) {
send_cell_type_pairs[num_send_cells].ci = p->cells_out[k];
send_cell_type_pairs[num_send_cells].cj = p->cells_in[0];
send_cell_type_pairs[num_send_cells++].type = p->cells_out_type[k];
}
}
threadpool_map(&e->threadpool, engine_addtasks_send_mapper,
send_cell_type_pairs, num_send_cells,
sizeof(struct cell_type_pair), threadpool_auto_chunk_size,
e);
free(send_cell_type_pairs);
if (e->verbose)
message("Creating send tasks took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
tic2 = getticks();
/* Exchange the cell tags. */
proxy_tags_exchange(e->proxies, e->nr_proxies, s);
if (e->verbose)
message("Exchanging cell tags took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
tic2 = getticks();
/* Loop over the proxies and add the recv tasks, which relies on having the
* cell tags. */
int max_num_recv_cells = 0;
for (int pid = 0; pid < e->nr_proxies; pid++)
max_num_recv_cells += e->proxies[pid].nr_cells_in;
struct cell_type_pair *recv_cell_type_pairs = NULL;
if ((recv_cell_type_pairs = (struct cell_type_pair *)malloc(
sizeof(struct cell_type_pair) * max_num_recv_cells)) == NULL)
error("Failed to allocate temporary cell pointer list.");
int num_recv_cells = 0;
for (int pid = 0; pid < e->nr_proxies; pid++) {
/* Get a handle on the proxy. */
struct proxy *p = &e->proxies[pid];
for (int k = 0; k < p->nr_cells_in; k++) {
recv_cell_type_pairs[num_recv_cells].ci = p->cells_in[k];
recv_cell_type_pairs[num_recv_cells++].type = p->cells_in_type[k];
}
}
threadpool_map(&e->threadpool, engine_addtasks_recv_mapper,
recv_cell_type_pairs, num_recv_cells,
sizeof(struct cell_type_pair), threadpool_auto_chunk_size,
e);
free(recv_cell_type_pairs);
if (e->verbose)
message("Creating recv tasks took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
}
/* Allocate memory for foreign particles */
engine_allocate_foreign_particles(e, /*fof=*/0);
#endif
/* Report the number of tasks we actually used */
if (e->verbose)
message(
"Nr. of tasks: %d allocated tasks: %d ratio: %f memory use: %zd MB.",
e->sched.nr_tasks, e->sched.size,
(float)e->sched.nr_tasks / (float)e->sched.size,
e->sched.size * sizeof(struct task) / (1024 * 1024));
/* Report the number of links we actually used */
if (e->verbose)
message(
"Nr. of links: %zd allocated links: %zd ratio: %f memory use: %zd MB.",
e->nr_links, e->size_links, (float)e->nr_links / (float)e->size_links,
e->size_links * sizeof(struct link) / (1024 * 1024));
/* Report the values that could have been used */
if (e->verbose)
message("Actual usage: tasks/cell: %f links/task: %f",
(float)e->sched.nr_tasks / s->tot_cells,
(float)e->nr_links / e->sched.nr_tasks);
tic2 = getticks();
/* Set the unlocks per task. */
scheduler_set_unlocks(sched, &e->threadpool);
if (e->verbose)
message("Setting unlocks took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
tic2 = getticks();
/* Rank the tasks. */
scheduler_ranktasks(sched);
if (e->verbose)
message("Ranking the tasks took %.3f %s.",
clocks_from_ticks(getticks() - tic2), clocks_getunit());
/* Weight the tasks. */
scheduler_reweight(sched, e->verbose);
/* Set the tasks age. */
e->tasks_age = 0;
if (e->verbose)
message("took %.3f %s (including reweight).",
clocks_from_ticks(getticks() - tic), clocks_getunit());
}