/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
* Matthieu Schaller (schaller@strw.leidenuniv.nl)
*
* 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
/* This object's header. */
#include "engine.h"
/* Local headers. */
#include "active.h"
#include "cell.h"
#include "memswap.h"
/* Load the profiler header, if needed. */
#ifdef WITH_PROFILER
#include
#endif
/**
* @brief Broad categories of tasks.
*
* Each category is unskipped independently
* of the others.
*/
enum task_broad_types {
task_broad_types_hydro = 1,
task_broad_types_gravity,
task_broad_types_stars,
task_broad_types_sinks,
task_broad_types_black_holes,
task_broad_types_rt,
task_broad_types_count,
};
/**
* @brief Meta-data for the unskipping
*/
struct unskip_data {
/*! The #engine */
struct engine *e;
/*! Pointer to the start of the list of cells to unskip */
int *list_base;
/*! Number of times the list has been duplicated */
int multiplier;
/*! The number of active cells (without dulication) */
int num_active_cells;
/*! The #task_broad_types corresponding to each copy of the list */
enum task_broad_types task_types[task_broad_types_count];
};
/**
* @brief Unskip any hydro tasks associated with active cells.
*
* @param c The cell.
* @param e The engine.
*/
static void engine_do_unskip_hydro(struct cell *c, struct engine *e) {
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
/* Ignore empty cells. */
if (c->hydro.count == 0) return;
#ifndef MPI_SYMMETRIC_FORCE_INTERACTION
/* Skip inactive cells. */
if (!cell_is_active_hydro(c, e)) return;
#endif
/* Recurse */
if (c->split) {
for (int k = 0; k < 8; k++) {
if (c->progeny[k] != NULL) {
struct cell *cp = c->progeny[k];
engine_do_unskip_hydro(cp, e);
}
}
}
/* Unskip any active tasks. */
const int forcerebuild = cell_unskip_hydro_tasks(c, &e->sched);
if (forcerebuild) atomic_inc(&e->forcerebuild);
}
/**
* @brief Unskip any stars tasks associated with active cells.
*
* @param c The cell.
* @param e The engine.
* @param with_star_formation Are we running with star formation switched on?
*/
static void engine_do_unskip_stars(struct cell *c, struct engine *e,
const int with_star_formation,
const int with_star_formation_sink) {
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
const int non_empty =
c->stars.count > 0 || (with_star_formation && c->hydro.count > 0) ||
(with_star_formation_sink && (c->hydro.count > 0 || c->sinks.count > 0));
/* Ignore empty cells. */
if (!non_empty) return;
const int ci_active = cell_need_activating_stars(c, e, with_star_formation,
with_star_formation_sink);
/* Skip inactive cells. */
if (!ci_active) return;
/* Recurse */
if (c->split) {
for (int k = 0; k < 8; k++) {
if (c->progeny[k] != NULL) {
struct cell *cp = c->progeny[k];
engine_do_unskip_stars(cp, e, with_star_formation,
with_star_formation_sink);
}
}
}
/* Unskip any active tasks. */
const int forcerebuild = cell_unskip_stars_tasks(
c, &e->sched, with_star_formation, with_star_formation_sink);
if (forcerebuild) atomic_inc(&e->forcerebuild);
}
/**
* @brief Unskip any black hole tasks associated with active cells.
*
* @param c The cell.
* @param e The engine.
*/
static void engine_do_unskip_black_holes(struct cell *c, struct engine *e) {
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
/* Recurse */
if (c->split) {
for (int k = 0; k < 8; k++) {
if (c->progeny[k] != NULL) {
struct cell *cp = c->progeny[k];
engine_do_unskip_black_holes(cp, e);
}
}
}
/* Unskip any active tasks. */
const int forcerebuild = cell_unskip_black_holes_tasks(c, &e->sched);
if (forcerebuild) atomic_inc(&e->forcerebuild);
}
/**
* @brief Unskip any sink tasks associated with active cells.
*
* @param c The cell.
* @param e The engine.
*/
static void engine_do_unskip_sinks(struct cell *c, struct engine *e) {
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
/* Ignore empty cells. */
if (c->sinks.count == 0 && c->hydro.count == 0) return;
/* Skip inactive cells. */
if (!cell_is_active_sinks(c, e) && !cell_is_active_hydro(c, e)) return;
/* Recurse */
if (c->split) {
for (int k = 0; k < 8; k++) {
if (c->progeny[k] != NULL) {
struct cell *cp = c->progeny[k];
engine_do_unskip_sinks(cp, e);
}
}
}
/* Unskip any active tasks. */
const int forcerebuild = cell_unskip_sinks_tasks(c, &e->sched);
if (forcerebuild) atomic_inc(&e->forcerebuild);
}
/**
* @brief Unskip any gravity tasks associated with active cells.
*
* @param c The cell.
* @param e The engine.
*/
static void engine_do_unskip_gravity(struct cell *c, struct engine *e) {
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
/* Ignore empty cells. */
if (c->grav.count == 0) return;
/* Skip inactive cells. */
if (!cell_is_active_gravity(c, e)) return;
/* Recurse */
if (c->split && ((c->maxdepth - c->depth) >= space_subdepth_diff_grav)) {
for (int k = 0; k < 8; k++) {
if (c->progeny[k] != NULL) {
struct cell *cp = c->progeny[k];
engine_do_unskip_gravity(cp, e);
}
}
}
/* Unskip any active tasks. */
cell_unskip_gravity_tasks(c, &e->sched);
}
/**
* @brief Unskip any radiative transfer tasks associated with active cells.
*
* @param c The cell.
* @param e The engine.
* @param sub_cycle 1 if this is a call for a sub cycle, 0 otherwise
*/
static void engine_do_unskip_rt(struct cell *c, struct engine *e,
const int sub_cycle) {
/* Note: we only get this far if engine_policy_rt is flagged. */
#ifdef SWIFT_DEBUG_CHECKS
if (!(e->policy & engine_policy_rt))
error("Unksipping RT stuff without the policy being on");
#endif
/* Early abort (are we below the level where tasks are)? */
if (!cell_get_flag(c, cell_flag_has_tasks)) return;
/* Do we have work to do? */
if (c->hydro.count == 0) return;
if (!cell_is_rt_active(c, e)) return;
/* Recurse */
if (c->split) {
for (int k = 0; k < 8; k++) {
if (c->progeny[k] != NULL) {
engine_do_unskip_rt(c->progeny[k], e, sub_cycle);
}
}
}
/* Unskip any active tasks. */
const int forcerebuild = cell_unskip_rt_tasks(c, &e->sched, sub_cycle);
if (forcerebuild) atomic_inc(&e->forcerebuild);
}
/**
* @brief Mapper function to unskip active tasks.
*
* @param map_data An array of #cell%s.
* @param num_elements Chunk size.
* @param extra_data Pointer to an unskip_data structure.
*/
void engine_do_unskip_mapper(void *map_data, int num_elements,
void *extra_data) {
/* Unpack the meta data */
struct unskip_data *data = (struct unskip_data *)extra_data;
const int num_active_cells = data->num_active_cells;
const enum task_broad_types *const task_types = data->task_types;
const int *const list_base = data->list_base;
struct engine *e = data->e;
struct cell *const cells_top = e->s->cells_top;
/* What policies are we running? */
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;
/* The current chunk of active cells */
const int *const local_cells = (int *)map_data;
/* Loop over this thread's chunk of cells to unskip */
for (int ind = 0; ind < num_elements; ind++) {
/* Handle on the cell */
struct cell *const c = &cells_top[local_cells[ind]];
/* In what copy of the global list are we?
* This gives us the broad type of task we are working on. */
const ptrdiff_t delta = &local_cells[ind] - list_base;
const int type = delta / num_active_cells;
#ifdef SWIFT_DEBUG_CHECKS
if (type >= data->multiplier) error("Invalid broad task type!");
if (c == NULL) error("Got an invalid cell index!");
#endif
/* What broad type of tasks are we unskipping? */
switch (task_types[type]) {
case task_broad_types_hydro:
#ifdef SWIFT_DEBUG_CHECKS
if (!(e->policy & engine_policy_hydro))
error("Trying to unskip hydro tasks in a non-hydro run!");
#endif
engine_do_unskip_hydro(c, e);
break;
case task_broad_types_gravity:
#ifdef SWIFT_DEBUG_CHECKS
if (!(e->policy & engine_policy_self_gravity) &&
!(e->policy & engine_policy_external_gravity))
error("Trying to unskip gravity tasks in a non-gravity run!");
#endif
engine_do_unskip_gravity(c, e);
break;
case task_broad_types_stars:
#ifdef SWIFT_DEBUG_CHECKS
if (!(e->policy & engine_policy_stars))
error("Trying to unskip star tasks in a non-stars run!");
#endif
engine_do_unskip_stars(c, e, with_star_formation,
with_star_formation_sink);
break;
case task_broad_types_sinks:
#ifdef SWIFT_DEBUG_CHECKS
if (!(e->policy & engine_policy_sinks))
error("Trying to unskip sink tasks in a non-sinks run!");
#endif
engine_do_unskip_sinks(c, e);
break;
case task_broad_types_black_holes:
#ifdef SWIFT_DEBUG_CHECKS
if (!(e->policy & engine_policy_black_holes))
error("Trying to unskip black holes tasks in a non-BH run!");
#endif
engine_do_unskip_black_holes(c, e);
break;
case task_broad_types_rt:
#ifdef SWIFT_DEBUG_CHECKS
if (!(e->policy & engine_policy_rt))
error("Trying to unskip radiative transfer tasks in a non-rt run!");
#endif
engine_do_unskip_rt(c, e, /*sub_cycle=*/0);
break;
default:
#ifdef SWIFT_DEBUG_CHECKS
error("Invalid broad task type!");
#endif
continue;
}
}
}
/**
* @brief Unskip all the tasks that act on active cells at this time.
*
* @param e The #engine.
*/
void engine_unskip(struct engine *e) {
const ticks tic = getticks();
struct space *s = e->s;
const int nodeID = e->nodeID;
const int with_hydro = e->policy & engine_policy_hydro;
const int with_self_grav = e->policy & engine_policy_self_gravity;
const int with_ext_grav = e->policy & engine_policy_external_gravity;
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_black_holes = e->policy & engine_policy_black_holes;
const int with_rt = e->policy & engine_policy_rt;
#ifdef WITH_PROFILER
static int count = 0;
char filename[100];
sprintf(filename, "/tmp/swift_engine_do_usnkip_mapper_%06i.prof", count++);
ProfilerStart(filename);
#endif // WITH_PROFILER
/* Move the active local cells to the top of the list. */
int *local_cells = e->s->local_cells_with_tasks_top;
int num_active_cells = 0;
for (int k = 0; k < s->nr_local_cells_with_tasks; k++) {
struct cell *c = &s->cells_top[local_cells[k]];
if (cell_is_empty(c)) continue;
if ((with_hydro && cell_is_active_hydro(c, e)) ||
(with_self_grav && cell_is_active_gravity(c, e)) ||
(with_ext_grav && c->nodeID == nodeID &&
cell_is_active_gravity(c, e)) ||
(with_feedback && cell_is_active_stars(c, e)) ||
(with_stars && c->nodeID == nodeID && cell_is_active_stars(c, e)) ||
(with_sinks && cell_is_active_sinks(c, e)) ||
(with_black_holes && cell_is_active_black_holes(c, e)) ||
(with_rt && cell_is_rt_active(c, e))) {
if (num_active_cells != k)
memswap(&local_cells[k], &local_cells[num_active_cells], sizeof(int));
num_active_cells += 1;
}
}
/* What kind of tasks do we have? */
struct unskip_data data;
bzero(&data, sizeof(struct unskip_data));
int multiplier = 0;
if (with_hydro) {
data.task_types[multiplier] = task_broad_types_hydro;
multiplier++;
}
if (with_self_grav || with_ext_grav) {
data.task_types[multiplier] = task_broad_types_gravity;
multiplier++;
}
if (with_feedback || with_stars) {
data.task_types[multiplier] = task_broad_types_stars;
multiplier++;
}
if (with_sinks) {
data.task_types[multiplier] = task_broad_types_sinks;
multiplier++;
}
if (with_black_holes) {
data.task_types[multiplier] = task_broad_types_black_holes;
multiplier++;
}
if (with_rt) {
data.task_types[multiplier] = task_broad_types_rt;
multiplier++;
}
/* Should we duplicate the list of active cells to better parallelise the
unskip over the threads ? */
int *local_active_cells;
if (multiplier > 1) {
/* Make space for copies of the list */
local_active_cells =
(int *)malloc(multiplier * num_active_cells * sizeof(int));
if (local_active_cells == NULL)
error(
"Couldn't allocate memory for duplicated list of local active "
"cells.");
/* Make blind copies of the list */
for (int m = 0; m < multiplier; m++) {
memcpy(local_active_cells + m * num_active_cells, local_cells,
num_active_cells * sizeof(int));
}
} else {
local_active_cells = local_cells;
}
/* We now have a list of local active cells duplicated as many times as
* we have broad task types. We can now release all the threads on the list */
data.e = e;
data.list_base = local_active_cells;
data.num_active_cells = num_active_cells;
data.multiplier = multiplier;
/* Activate all the regular tasks */
threadpool_map(&e->threadpool, engine_do_unskip_mapper, local_active_cells,
num_active_cells * multiplier, sizeof(int), /*chunk=*/1,
&data);
#ifdef WITH_PROFILER
ProfilerStop();
#endif // WITH_PROFILER
/* Free stuff? */
if (multiplier > 1) {
free(local_active_cells);
}
if (e->verbose)
message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
clocks_getunit());
}
void engine_do_unskip_sub_cycle_mapper(void *map_data, int num_elements,
void *extra_data) {
struct engine *e = (struct engine *)extra_data;
struct cell *const cells_top = e->s->cells_top;
/* The current chunk of active cells */
const int *const local_cells = (int *)map_data;
/* Loop over this thread's chunk of cells to unskip */
for (int ind = 0; ind < num_elements; ind++) {
/* Handle on the cell */
struct cell *const c = &cells_top[local_cells[ind]];
engine_do_unskip_rt(c, e, /*sub_cycle=*/1);
}
}
/**
* @brief Unskip all the RT tasks that are active during this sub-cycle.
*
* @param e The #engine.
*/
void engine_unskip_rt_sub_cycle(struct engine *e) {
const ticks tic = getticks();
struct space *s = e->s;
const int with_rt = e->policy & engine_policy_rt;
if (!with_rt) error("Unskipping sub-cycles when running without RT!");
int *local_cells = e->s->local_cells_with_tasks_top;
int num_active_cells = 0;
for (int k = 0; k < s->nr_local_cells_with_tasks; k++) {
struct cell *c = &s->cells_top[local_cells[k]];
if (c->hydro.count == 0) continue;
if (cell_is_rt_active(c, e)) {
if (num_active_cells != k)
memswap(&local_cells[k], &local_cells[num_active_cells], sizeof(int));
num_active_cells += 1;
}
}
/* Activate all the regular tasks */
threadpool_map(&e->threadpool, engine_do_unskip_sub_cycle_mapper, local_cells,
num_active_cells, sizeof(int), /*chunk=*/1, e);
if (e->verbose)
message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
clocks_getunit());
}