/******************************************************************************* * This file is part of SWIFT. * Copyright (C) 2024 Will J. Roper (w.roper@sussex.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 /* Standard includes. */ #include #include /* Local includes. */ #include "cell.h" #include "engine.h" #include "gravity_properties.h" #include "multipole.h" #include "proxy.h" #include "space.h" #include "zoom_region/zoom.h" /** * @brief Constructs the top-level tasks for the short-range gravity * and long-range gravity interactions for the background cells. * * This mapper only considers bkg->bkg interactions. * * - All top-cells get a self task. * - All pairs within range according to the multipole acceptance * criterion get a pair task. * * This will create pair tasks between void cells and background cells (if * running without buffer cells). These pair tasks will be split into smaller * tasks during task splitting to make the most of any possible void mm * interactions above the zoom level in the cell tree. * * @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_self_gravity_tasks_mapper_bkg_cells(void *map_data, int num_elements, void *extra_data) { struct engine *e = (struct engine *)extra_data; struct space *s = e->s; const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]}; struct cell *cells = s->zoom_props->bkg_cells_top; /* We always use the mesh if the volume is periodic. */ const int use_mesh = s->periodic; /* Unlike buffer or zoom cells, background cells are periodic at the box * boundaries if the space is periodic. */ const int periodic = s->periodic; /* 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 */ /* NOTE: The 2 in the max below may not be necessary but does insure some * safety buffer. */ 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++) { /* Create a self task, and loop over neighbouring cells making pair tasks * where appropriate. */ engine_gravity_make_task_loop(e, (size_t)(map_data) + ind, cdim, cells, periodic, use_mesh, delta_m, delta_p); } } /** * @brief Constructs the top-level tasks for the short-range gravity * and long-range gravity interactions for all combinations of cell types. * * - All top level cells get a self task. * - All pairs within range according to the multipole acceptance * criterion get a pair task. * * This is a wrapper around the various mappers defined above for all the * possible combinations of cell types including: * - bkg->bkg * * This replaces the function in engine_maketasks when running with a zoom * region. * * @param s The #space. * @param e The #engine. */ void zoom_engine_make_self_gravity_tasks(struct space *s, struct engine *e) { ticks tic = getticks(); /* Background -> Background */ threadpool_map(&e->threadpool, engine_make_self_gravity_tasks_mapper_bkg_cells, NULL, s->zoom_props->nr_bkg_cells, 1, threadpool_auto_chunk_size, e); if (e->verbose) message("Making bkg->bkg gravity tasks took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); } /** * @brief Construct the hierarchical tasks for the void cell tree recursively. * * This will construct: * - The init for preparing void cell multipoles. * - The init implicit task for the void cells. * - The long-range gravity task for the void cells. * - The down-pass gravity task for the void cells. * - The down-pass implicit task for the void cells. * * @param e The #engine. * @param c The #cell. */ void zoom_engine_make_hierarchical_void_tasks_recursive(struct engine *e, struct cell *c) { struct scheduler *s = &e->sched; /* Nothing to do if there's no gravity. */ if (!(e->policy & engine_policy_self_gravity)) return; /* At the super level we have a few different tasks to make. (We don't need * any tasks above the super level) */ if (c->grav.super == c) { /* Initialisation of the multipoles */ c->grav.init = scheduler_addtask(s, task_type_init_grav, 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.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); /* Link in the implicit tasks */ scheduler_addunlock(s, c->grav.init, c->grav.init_out); scheduler_addunlock(s, c->grav.down_in, c->grav.down); } else if (c->grav.super != NULL) { /* Below the super level we just need to link in the implicit tasks. */ 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->grav.down_in, c->parent->grav.down_in); } /* Recurse but only in void cells. */ for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL && c->progeny[k]->subtype == cell_subtype_void) { zoom_engine_make_hierarchical_void_tasks_recursive(e, c->progeny[k]); } } } /** * @brief Construct the hierarchical tasks for the void cell tree. * * This will construct: * - The init for preparing void cell multipoles. * - The init implicit task for the void cells. * - The long-range gravity task for the void cells. * - The down-pass gravity task for the void cells. * - The down-pass implicit task for the void cells. * * @param e The #engine. * @param c The #cell. */ void zoom_engine_make_hierarchical_void_tasks(struct engine *e) { ticks tic = getticks(); /* Get a handle on the zoom properties. */ struct space *s = e->s; struct zoom_region_properties *zoom_props = s->zoom_props; const int nr_void_cells = zoom_props->nr_void_cells; const int *void_cells = zoom_props->void_cell_indices; struct cell *cells = s->cells_top; /* Loop through the void cells and make the hierarchical tasks. */ for (int i = 0; i < nr_void_cells; i++) { #ifdef SWIFT_DEBUG_CHECKS /* Ensure we have a void cell. */ if (cells[void_cells[i]].subtype != cell_subtype_void) { error("Cell is not a void cell."); } #endif zoom_engine_make_hierarchical_void_tasks_recursive(e, &cells[void_cells[i]]); } if (e->verbose) message("Making void cell tree tasks took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); }