/******************************************************************************* * 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) * 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 #include #include #include #include #include /* MPI headers. */ #ifdef WITH_MPI #include #endif /* Switch off timers. */ #ifdef TIMER #undef TIMER #endif /* This object's header. */ #include "cell.h" /* Local headers. */ #include "engine.h" #include "error.h" #include "multipole.h" #include "space.h" #include "tools.h" /* Global variables. */ int cell_next_tag = 0; /** List of cell pairs for sub-cell recursion. For any sid, the entries in * this array contain the number of sub-cell pairs and the indices and sid * of the sub-cell pairs themselves. */ struct cell_split_pair cell_split_pairs[13] = { {1, /* ( 1 , 1 , 1 ) */ {{7, 0, 0}}}, {4, /* ( 1 , 1 , 0 ) */ {{6, 0, 1}, {7, 1, 1}, {6, 1, 0}, {7, 0, 2}}}, {1, /* ( 1 , 1 , -1 ) */ {{6, 1, 2}}}, {4, /* ( 1 , 0 , 1 ) */ {{5, 0, 3}, {7, 2, 3}, {5, 2, 0}, {7, 0, 6}}}, {16, /* ( 1 , 0 , 0 ) */ {{4, 0, 4}, {5, 0, 5}, {6, 0, 7}, {7, 0, 8}, {4, 1, 3}, {5, 1, 4}, {6, 1, 6}, {7, 1, 7}, {4, 2, 1}, {5, 2, 2}, {6, 2, 4}, {7, 2, 5}, {4, 3, 0}, {5, 3, 1}, {6, 3, 3}, {7, 3, 4}}}, {4, /* ( 1 , 0 , -1 ) */ {{4, 1, 5}, {6, 3, 5}, {4, 3, 2}, {6, 1, 8}}}, {1, /* ( 1 , -1 , 1 ) */ {{5, 2, 6}}}, {4, /* ( 1 , -1 , 0 ) */ {{4, 3, 6}, {5, 2, 8}, {4, 2, 7}, {5, 3, 7}}}, {1, /* ( 1 , -1 , -1 ) */ {{4, 3, 8}}}, {4, /* ( 0 , 1 , 1 ) */ {{3, 0, 9}, {7, 4, 9}, {3, 4, 0}, {7, 0, 8}}}, {16, /* ( 0 , 1 , 0 ) */ {{2, 0, 10}, {3, 0, 11}, {6, 0, 7}, {7, 0, 6}, {2, 1, 9}, {3, 1, 10}, {6, 1, 8}, {7, 1, 7}, {2, 4, 1}, {3, 4, 2}, {6, 4, 10}, {7, 4, 11}, {2, 5, 0}, {3, 5, 1}, {6, 5, 9}, {7, 5, 10}}}, {4, /* ( 0 , 1 , -1 ) */ {{2, 1, 11}, {6, 5, 11}, {2, 5, 2}, {6, 1, 6}}}, {16, /* ( 0 , 0 , 1 ) */ {{1, 0, 12}, {3, 0, 11}, {5, 0, 5}, {7, 0, 2}, {1, 2, 9}, {3, 2, 12}, {5, 2, 8}, {7, 2, 5}, {1, 4, 3}, {3, 4, 6}, {5, 4, 12}, {7, 4, 11}, {1, 6, 0}, {3, 6, 3}, {5, 6, 9}, {7, 6, 12}}}}; /** * @brief Get the size of the cell subtree. * * @param c The #cell. */ int cell_get_tree_size(struct cell *c) { /* Number of cells in this subtree. */ int count = 1; /* Sum up the progeny if split. */ if (c->split) for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) count += cell_get_tree_size(c->progeny[k]); /* Return the final count. */ return count; } /** * @brief Link the cells recursively to the given #part array. * * @param c The #cell. * @param parts The #part array. * * @return The number of particles linked. */ int cell_link_parts(struct cell *c, struct part *parts) { #ifdef SWIFT_DEBUG_CHECKS if (c->nodeID == engine_rank) error("Linking foreign particles in a local cell!"); if (c->hydro.parts != NULL) error("Linking parts into a cell that was already linked"); #endif c->hydro.parts = parts; /* Fill the progeny recursively, depth-first. */ if (c->split) { int offset = 0; for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) offset += cell_link_parts(c->progeny[k], &parts[offset]); } } /* Return the total number of linked particles. */ return c->hydro.count; } /** * @brief Link the cells recursively to the given #gpart array. * * @param c The #cell. * @param gparts The #gpart array. * * @return The number of particles linked. */ int cell_link_gparts(struct cell *c, struct gpart *gparts) { #ifdef SWIFT_DEBUG_CHECKS if (c->nodeID == engine_rank) error("Linking foreign particles in a local cell!"); if (c->grav.parts != NULL) error("Linking gparts into a cell that was already linked"); #endif c->grav.parts = gparts; c->grav.parts_rebuild = gparts; /* Fill the progeny recursively, depth-first. */ if (c->split) { int offset = 0; for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) offset += cell_link_gparts(c->progeny[k], &gparts[offset]); } } /* Return the total number of linked particles. */ return c->grav.count; } /** * @brief Link the cells recursively to the given #spart array. * * @param c The #cell. * @param sparts The #spart array. * * @return The number of particles linked. */ int cell_link_sparts(struct cell *c, struct spart *sparts) { #ifdef SWIFT_DEBUG_CHECKS if (c->nodeID == engine_rank) error("Linking foreign particles in a local cell!"); if (c->stars.parts != NULL) error("Linking sparts into a cell that was already linked"); #endif c->stars.parts = sparts; c->stars.parts_rebuild = sparts; /* Fill the progeny recursively, depth-first. */ if (c->split) { int offset = 0; for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) offset += cell_link_sparts(c->progeny[k], &sparts[offset]); } } /* Return the total number of linked particles. */ return c->stars.count; } /** * @brief Link the cells recursively to the given #bpart array. * * @param c The #cell. * @param bparts The #bpart array. * * @return The number of particles linked. */ int cell_link_bparts(struct cell *c, struct bpart *bparts) { #ifdef SWIFT_DEBUG_CHECKS if (c->nodeID == engine_rank) error("Linking foreign particles in a local cell!"); if (c->black_holes.parts != NULL) error("Linking bparts into a cell that was already linked"); #endif c->black_holes.parts = bparts; /* Fill the progeny recursively, depth-first. */ if (c->split) { int offset = 0; for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) offset += cell_link_bparts(c->progeny[k], &bparts[offset]); } } /* Return the total number of linked particles. */ return c->black_holes.count; } /** * @brief Link the cells recursively to the given #sink array. * * @param c The #cell. * @param sinks The #sink array. * * @return The number of particles linked. */ int cell_link_sinks(struct cell *c, struct sink *sinks) { #ifdef SWIFT_DEBUG_CHECKS if (c->nodeID == engine_rank) error("Linking foreign particles in a local cell!"); if (c->sinks.parts != NULL) error("Linking sparts into a cell that was already linked"); #endif c->sinks.parts = sinks; c->sinks.parts_rebuild = sinks; /* Fill the progeny recursively, depth-first. */ if (c->split) { int offset = 0; for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) offset += cell_link_sinks(c->progeny[k], &sinks[offset]); } } /* Return the total number of linked particles. */ return c->sinks.count; } /** * @brief Recurse down foreign cells until reaching one with hydro * tasks; then trigger the linking of the #part array from that * level. * * @param c The #cell. * @param parts The #part array. * * @return The number of particles linked. */ int cell_link_foreign_parts(struct cell *c, struct part *parts) { #ifdef WITH_MPI #ifdef SWIFT_DEBUG_CHECKS if (c->nodeID == engine_rank) error("Linking foreign particles in a local cell!"); #endif /* Do we have a hydro task at this level? */ if (cell_get_recv(c, task_subtype_xv) != NULL) { /* Recursively attach the parts */ const int counts = cell_link_parts(c, parts); #ifdef SWIFT_DEBUG_CHECKS if (counts != c->hydro.count) error("Something is wrong with the foreign counts"); #endif return counts; } /* Go deeper to find the level where the tasks are */ if (c->split) { int count = 0; for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { count += cell_link_foreign_parts(c->progeny[k], &parts[count]); } } return count; } else { return 0; } #else error("Calling linking of foregin particles in non-MPI mode."); #endif } /** * @brief Recurse down foreign cells until reaching one with gravity * tasks; then trigger the linking of the #gpart array from that * level. * * @param c The #cell. * @param gparts The #gpart array. * * @return The number of particles linked. */ int cell_link_foreign_gparts(struct cell *c, struct gpart *gparts) { #ifdef WITH_MPI #ifdef SWIFT_DEBUG_CHECKS if (c->nodeID == engine_rank) error("Linking foreign particles in a local cell!"); #endif /* Do we have a gravity task at this level? */ if (cell_get_recv(c, task_subtype_gpart) != NULL) { /* Recursively attach the gparts */ const int counts = cell_link_gparts(c, gparts); #ifdef SWIFT_DEBUG_CHECKS if (counts != c->grav.count) error("Something is wrong with the foreign counts"); #endif return counts; } else { c->grav.parts = gparts; c->grav.parts_rebuild = gparts; } /* Go deeper to find the level where the tasks are */ if (c->split) { int count = 0; for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { count += cell_link_foreign_gparts(c->progeny[k], &gparts[count]); } } return count; } else { return 0; } #else error("Calling linking of foregin particles in non-MPI mode."); #endif } /** * @brief Recursively nullify all the particle pointers in a cell hierarchy. * * Should only be used on foreign cells! * * This will make any task or action running on these cells likely crash. * Recreating the foreign links will be necessary. * * @param c The #cell to act on. */ void cell_unlink_foreign_particles(struct cell *c) { #ifdef SWIFT_DEBUG_CHECKS if (c->nodeID == engine_rank) error("Unlinking foreign particles in a local cell!"); #endif c->grav.parts = NULL; c->hydro.parts = NULL; c->stars.parts = NULL; c->black_holes.parts = NULL; c->sinks.parts = NULL; if (c->split) { for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { cell_unlink_foreign_particles(c->progeny[k]); } } } } /** * @brief Recursively count the number of #part in foreign cells that * are in cells with hydro-related tasks. * * @param c The #cell. * * @return The number of particles linked. */ int cell_count_parts_for_tasks(const struct cell *c) { #ifdef WITH_MPI #ifdef SWIFT_DEBUG_CHECKS if (c->nodeID == engine_rank) error("Counting foreign particles in a local cell!"); #endif /* Do we have a hydro task at this level? */ if (cell_get_recv(c, task_subtype_xv) != NULL) { return c->hydro.count; } if (c->split) { int count = 0; for (int k = 0; k < 8; ++k) { if (c->progeny[k] != NULL) { count += cell_count_parts_for_tasks(c->progeny[k]); } } return count; } else { return 0; } #else error("Calling linking of foregin particles in non-MPI mode."); #endif } /** * @brief Recursively count the number of #gpart in foreign cells that * are in cells with gravity-related tasks. * * @param c The #cell. * * @return The number of particles linked. */ int cell_count_gparts_for_tasks(const struct cell *c) { #ifdef WITH_MPI #ifdef SWIFT_DEBUG_CHECKS if (c->nodeID == engine_rank) error("Counting foreign particles in a local cell!"); #endif /* Do we have a gravity task at this level? */ if (cell_get_recv(c, task_subtype_gpart) != NULL) { return c->grav.count; } if (c->split) { int count = 0; for (int k = 0; k < 8; ++k) { if (c->progeny[k] != NULL) { count += cell_count_gparts_for_tasks(c->progeny[k]); } } return count; } else { return 0; } #else error("Calling linking of foregin particles in non-MPI mode."); #endif } /** * @brief Sanitizes the smoothing length values of cells by setting large * outliers to more sensible values. * * Each cell with <1000 part will be processed. We limit h to be the size of * the cell and replace 0s with a good estimate. * * @param c The cell. * @param treated Has the cell already been sanitized at this level ? */ void cell_sanitize(struct cell *c, int treated) { const int count = c->hydro.count; const int scount = c->stars.count; struct part *parts = c->hydro.parts; struct spart *sparts = c->stars.parts; float h_max = 0.f; float h_max_active = 0.f; float stars_h_max = 0.f; float stars_h_max_active = 0.f; /* Treat cells will <1000 particles */ if (count < 1000 && !treated) { /* Get an upper bound on h */ const float upper_h_max = c->dmin / (1.2f * kernel_gamma); /* Apply it */ for (int i = 0; i < count; ++i) { if (parts[i].h == 0.f || parts[i].h > upper_h_max) parts[i].h = upper_h_max; } for (int i = 0; i < scount; ++i) { if (sparts[i].h == 0.f || sparts[i].h > upper_h_max) sparts[i].h = upper_h_max; } } /* Recurse and gather the new h_max values */ if (c->split) { for (int k = 0; k < 8; ++k) { if (c->progeny[k] != NULL) { /* Recurse */ cell_sanitize(c->progeny[k], (count < 1000)); /* And collect */ h_max = max(h_max, c->progeny[k]->hydro.h_max); h_max_active = max(h_max_active, c->progeny[k]->hydro.h_max_active); stars_h_max = max(stars_h_max, c->progeny[k]->stars.h_max); stars_h_max_active = max(stars_h_max_active, c->progeny[k]->stars.h_max_active); } } } else { /* Get the new value of h_max (note all particles are active) */ for (int i = 0; i < count; ++i) h_max = max(h_max, parts[i].h); for (int i = 0; i < count; ++i) h_max_active = max(h_max_active, parts[i].h); for (int i = 0; i < scount; ++i) stars_h_max = max(stars_h_max, sparts[i].h); for (int i = 0; i < scount; ++i) stars_h_max_active = max(stars_h_max_active, sparts[i].h); } /* Record the change */ c->hydro.h_max = h_max; c->hydro.h_max_active = h_max_active; c->stars.h_max = stars_h_max; c->stars.h_max_active = stars_h_max_active; } /** * @brief Cleans the links in a given cell. * * @param c Cell to act upon * @param data Unused parameter */ void cell_clean_links(struct cell *c, void *data) { c->hydro.density = NULL; c->hydro.gradient = NULL; c->hydro.force = NULL; c->hydro.limiter = NULL; c->rt.rt_gradient = NULL; c->rt.rt_transport = NULL; c->grav.grav = NULL; c->grav.mm = NULL; c->stars.density = NULL; c->stars.prepare1 = NULL; c->stars.prepare2 = NULL; c->stars.feedback = NULL; c->sinks.swallow = NULL; c->sinks.density = NULL; c->sinks.do_sink_swallow = NULL; c->sinks.do_gas_swallow = NULL; c->black_holes.density = NULL; c->black_holes.swallow = NULL; c->black_holes.do_gas_swallow = NULL; c->black_holes.do_bh_swallow = NULL; c->black_holes.feedback = NULL; } /** * @brief Checks that the #part in a cell are at the * current point in time * * Calls error() if the cell is not at the current time. * * @param c Cell to act upon * @param data The current time on the integer time-line */ void cell_check_part_drift_point(struct cell *c, void *data) { #ifdef SWIFT_DEBUG_CHECKS const integertime_t ti_drift = *(integertime_t *)data; /* Only check local cells */ if (c->nodeID != engine_rank) return; /* Only check cells with content */ if (c->hydro.count == 0) return; if (c->hydro.ti_old_part != ti_drift) error("Cell in an incorrect time-zone! c->hydro.ti_old=%lld ti_drift=%lld", c->hydro.ti_old_part, ti_drift); for (int i = 0; i < c->hydro.count; ++i) if (c->hydro.parts[i].ti_drift != ti_drift && c->hydro.parts[i].time_bin != time_bin_inhibited) error("part in an incorrect time-zone! p->ti_drift=%lld ti_drift=%lld", c->hydro.parts[i].ti_drift, ti_drift); for (int i = 0; i < c->hydro.count; ++i) { const struct part *p = &c->hydro.parts[i]; if (p->depth_h == c->depth) { if (!(p->h >= c->h_min_allowed && p->h < c->h_max_allowed) && c->split) { error( "depth_h set incorrectly! c->depth=%d p->depth_h=%d h=%e h_min=%e " "h_max=%e", c->depth, p->depth_h, p->h, c->h_min_allowed, c->h_max_allowed); } } } #else error("Calling debugging code without debugging flag activated."); #endif } /** * @brief Checks that the #gpart in a cell are at the * current point in time * * Calls error() if the cell is not at the current time. * * @param c Cell to act upon * @param data The current time on the integer time-line */ void cell_check_gpart_drift_point(struct cell *c, void *data) { #ifdef SWIFT_DEBUG_CHECKS const integertime_t ti_drift = *(integertime_t *)data; /* Only check local cells */ if (c->nodeID != engine_rank) return; /* Only check cells with content */ if (c->grav.count == 0) return; if (c->grav.ti_old_part != ti_drift) error( "Cell in an incorrect time-zone! c->grav.ti_old_part=%lld " "ti_drift=%lld", c->grav.ti_old_part, ti_drift); for (int i = 0; i < c->grav.count; ++i) if (c->grav.parts[i].ti_drift != ti_drift && c->grav.parts[i].time_bin != time_bin_inhibited) error("g-part in an incorrect time-zone! gp->ti_drift=%lld ti_drift=%lld", c->grav.parts[i].ti_drift, ti_drift); #else error("Calling debugging code without debugging flag activated."); #endif } /** * @brief Checks that the #sink in a cell are at the * current point in time * * Calls error() if the cell is not at the current time. * * @param c Cell to act upon * @param data The current time on the integer time-line */ void cell_check_sink_drift_point(struct cell *c, void *data) { #ifdef SWIFT_DEBUG_CHECKS const integertime_t ti_drift = *(integertime_t *)data; /* Only check local cells */ if (c->nodeID != engine_rank) return; /* Only check cells with content */ if (c->sinks.count == 0) return; if (c->sinks.ti_old_part != ti_drift) error( "Cell in an incorrect time-zone! c->sinks.ti_old_part=%lld " "ti_drift=%lld", c->sinks.ti_old_part, ti_drift); for (int i = 0; i < c->sinks.count; ++i) if (c->sinks.parts[i].ti_drift != ti_drift && c->sinks.parts[i].time_bin != time_bin_inhibited) error( "sink-part in an incorrect time-zone! sink->ti_drift=%lld " "ti_drift=%lld", c->sinks.parts[i].ti_drift, ti_drift); for (int i = 0; i < c->sinks.count; ++i) { const struct sink *sp = &c->sinks.parts[i]; if (sp->depth_h == c->depth) { if (!(sp->h >= c->h_min_allowed && sp->h < c->h_max_allowed) && c->split) { error( "depth_h set incorrectly! c->depth=%d sp->depth_h=%d h=%e h_min=%e " "h_max=%e", c->depth, sp->depth_h, sp->h, c->h_min_allowed, c->h_max_allowed); } } } #else error("Calling debugging code without debugging flag activated."); #endif } /** * @brief Checks that the #spart in a cell are at the * current point in time * * Calls error() if the cell is not at the current time. * * @param c Cell to act upon * @param data The current time on the integer time-line */ void cell_check_spart_drift_point(struct cell *c, void *data) { #ifdef SWIFT_DEBUG_CHECKS const integertime_t ti_drift = *(integertime_t *)data; /* Only check local cells */ if (c->nodeID != engine_rank) return; /* Only check cells with content */ if (c->stars.count == 0) return; if (c->stars.ti_old_part != ti_drift) error( "Cell in an incorrect time-zone! c->stars.ti_old_part=%lld " "ti_drift=%lld", c->stars.ti_old_part, ti_drift); for (int i = 0; i < c->stars.count; ++i) if (c->stars.parts[i].ti_drift != ti_drift && c->stars.parts[i].time_bin != time_bin_inhibited) error("s-part in an incorrect time-zone! sp->ti_drift=%lld ti_drift=%lld", c->stars.parts[i].ti_drift, ti_drift); for (int i = 0; i < c->stars.count; ++i) { const struct spart *p = &c->stars.parts[i]; if (p->depth_h == c->depth) { if (!(p->h >= c->h_min_allowed && p->h < c->h_max_allowed) && c->split) { error( "depth_h set incorrectly! c->depth=%d p->depth_h=%d h=%e h_min=%e " "h_max=%e", c->depth, p->depth_h, p->h, c->h_min_allowed, c->h_max_allowed); } } } #else error("Calling debugging code without debugging flag activated."); #endif } /** * @brief Checks that the #bpart in a cell are at the * current point in time * * Calls error() if the cell is not at the current time. * * @param c Cell to act upon * @param data The current time on the integer time-line */ void cell_check_bpart_drift_point(struct cell *c, void *data) { #ifdef SWIFT_DEBUG_CHECKS const integertime_t ti_drift = *(integertime_t *)data; /* Only check local cells */ if (c->nodeID != engine_rank) return; /* Only check cells with content */ if (c->black_holes.count == 0) return; if (c->black_holes.ti_old_part != ti_drift) error( "Cell in an incorrect time-zone! c->black_holes.ti_old_part=%lld " "ti_drift=%lld", c->black_holes.ti_old_part, ti_drift); for (int i = 0; i < c->black_holes.count; ++i) if (c->black_holes.parts[i].ti_drift != ti_drift && c->black_holes.parts[i].time_bin != time_bin_inhibited) error("s-part in an incorrect time-zone! sp->ti_drift=%lld ti_drift=%lld", c->black_holes.parts[i].ti_drift, ti_drift); for (int i = 0; i < c->black_holes.count; ++i) { const struct bpart *bp = &c->black_holes.parts[i]; if (bp->depth_h == c->depth) { if (!(bp->h >= c->h_min_allowed && bp->h < c->h_max_allowed) && c->split) { error( "depth_h set incorrectly! c->depth=%d p->depth_h=%d h=%e h_min=%e " "h_max=%e", c->depth, bp->depth_h, bp->h, c->h_min_allowed, c->h_max_allowed); } } } #else error("Calling debugging code without debugging flag activated."); #endif } /** * @brief Checks that the multipole of a cell is at the current point in time * * Calls error() if the cell is not at the current time. * * @param c Cell to act upon * @param data The current time on the integer time-line */ void cell_check_multipole_drift_point(struct cell *c, void *data) { #ifdef SWIFT_DEBUG_CHECKS const integertime_t ti_drift = *(integertime_t *)data; /* Only check local cells */ if (c->nodeID != engine_rank) return; /* Only check cells with content */ if (c->grav.count == 0) return; if (c->grav.ti_old_multipole != ti_drift) error( "Cell multipole in an incorrect time-zone! " "c->grav.ti_old_multipole=%lld " "ti_drift=%lld (depth=%d, node=%d)", c->grav.ti_old_multipole, ti_drift, c->depth, c->nodeID); #else error("Calling debugging code without debugging flag activated."); #endif } /** * @brief Resets all the individual cell task counters to 0. * * Should only be used for debugging purposes. * * @param c The #cell to reset. */ void cell_reset_task_counters(struct cell *c) { #ifdef SWIFT_DEBUG_CHECKS for (int t = 0; t < task_type_count; ++t) c->tasks_executed[t] = 0; for (int t = 0; t < task_subtype_count; ++t) c->subtasks_executed[t] = 0; for (int k = 0; k < 8; ++k) if (c->progeny[k] != NULL) cell_reset_task_counters(c->progeny[k]); #else error("Calling debugging code without debugging flag activated."); #endif } /** * @brief Recursively construct all the multipoles in a cell hierarchy. * * @param c The #cell. * @param ti_current The current integer time. * @param grav_props The properties of the gravity scheme. */ void cell_make_multipoles(struct cell *c, integertime_t ti_current, const struct gravity_props *const grav_props) { /* Reset everything */ gravity_reset(c->grav.multipole); if (c->split) { /* Start by recursing */ for (int k = 0; k < 8; ++k) { if (c->progeny[k] != NULL) cell_make_multipoles(c->progeny[k], ti_current, grav_props); } /* Compute CoM of all progenies */ double CoM[3] = {0., 0., 0.}; double vel[3] = {0., 0., 0.}; float max_delta_vel[3] = {0.f, 0.f, 0.f}; float min_delta_vel[3] = {0.f, 0.f, 0.f}; double mass = 0.; for (int k = 0; k < 8; ++k) { if (c->progeny[k] != NULL) { const struct gravity_tensors *m = c->progeny[k]->grav.multipole; mass += m->m_pole.M_000; CoM[0] += m->CoM[0] * m->m_pole.M_000; CoM[1] += m->CoM[1] * m->m_pole.M_000; CoM[2] += m->CoM[2] * m->m_pole.M_000; vel[0] += m->m_pole.vel[0] * m->m_pole.M_000; vel[1] += m->m_pole.vel[1] * m->m_pole.M_000; vel[2] += m->m_pole.vel[2] * m->m_pole.M_000; max_delta_vel[0] = max(m->m_pole.max_delta_vel[0], max_delta_vel[0]); max_delta_vel[1] = max(m->m_pole.max_delta_vel[1], max_delta_vel[1]); max_delta_vel[2] = max(m->m_pole.max_delta_vel[2], max_delta_vel[2]); min_delta_vel[0] = min(m->m_pole.min_delta_vel[0], min_delta_vel[0]); min_delta_vel[1] = min(m->m_pole.min_delta_vel[1], min_delta_vel[1]); min_delta_vel[2] = min(m->m_pole.min_delta_vel[2], min_delta_vel[2]); } } /* Final operation on the CoM and bulk velocity */ const double mass_inv = 1. / mass; c->grav.multipole->CoM[0] = CoM[0] * mass_inv; c->grav.multipole->CoM[1] = CoM[1] * mass_inv; c->grav.multipole->CoM[2] = CoM[2] * mass_inv; c->grav.multipole->m_pole.vel[0] = vel[0] * mass_inv; c->grav.multipole->m_pole.vel[1] = vel[1] * mass_inv; c->grav.multipole->m_pole.vel[2] = vel[2] * mass_inv; /* Min max velocity along each axis */ c->grav.multipole->m_pole.max_delta_vel[0] = max_delta_vel[0]; c->grav.multipole->m_pole.max_delta_vel[1] = max_delta_vel[1]; c->grav.multipole->m_pole.max_delta_vel[2] = max_delta_vel[2]; c->grav.multipole->m_pole.min_delta_vel[0] = min_delta_vel[0]; c->grav.multipole->m_pole.min_delta_vel[1] = min_delta_vel[1]; c->grav.multipole->m_pole.min_delta_vel[2] = min_delta_vel[2]; /* Now shift progeny multipoles and add them up */ struct multipole temp; double r_max = 0.; for (int k = 0; k < 8; ++k) { if (c->progeny[k] != NULL) { const struct cell *cp = c->progeny[k]; const struct multipole *m = &cp->grav.multipole->m_pole; /* Contribution to multipole */ gravity_M2M(&temp, m, c->grav.multipole->CoM, cp->grav.multipole->CoM); gravity_multipole_add(&c->grav.multipole->m_pole, &temp); /* Upper limit of max CoM<->gpart distance */ const double dx = c->grav.multipole->CoM[0] - cp->grav.multipole->CoM[0]; const double dy = c->grav.multipole->CoM[1] - cp->grav.multipole->CoM[1]; const double dz = c->grav.multipole->CoM[2] - cp->grav.multipole->CoM[2]; const double r2 = dx * dx + dy * dy + dz * dz; r_max = max(r_max, cp->grav.multipole->r_max + sqrt(r2)); } } /* Alternative upper limit of max CoM<->gpart distance */ const double dx = c->grav.multipole->CoM[0] > c->loc[0] + c->width[0] * 0.5 ? c->grav.multipole->CoM[0] - c->loc[0] : c->loc[0] + c->width[0] - c->grav.multipole->CoM[0]; const double dy = c->grav.multipole->CoM[1] > c->loc[1] + c->width[1] * 0.5 ? c->grav.multipole->CoM[1] - c->loc[1] : c->loc[1] + c->width[1] - c->grav.multipole->CoM[1]; const double dz = c->grav.multipole->CoM[2] > c->loc[2] + c->width[2] * 0.5 ? c->grav.multipole->CoM[2] - c->loc[2] : c->loc[2] + c->width[2] - c->grav.multipole->CoM[2]; /* Take minimum of both limits */ c->grav.multipole->r_max = min(r_max, sqrt(dx * dx + dy * dy + dz * dz)); /* Compute the multipole power */ gravity_multipole_compute_power(&c->grav.multipole->m_pole); } else { if (c->grav.count > 0) { gravity_P2M(c->grav.multipole, c->grav.parts, c->grav.count, grav_props); /* Compute the multipole power */ gravity_multipole_compute_power(&c->grav.multipole->m_pole); } else { /* No gparts in that leaf cell */ /* Set the values to something sensible */ gravity_multipole_init(&c->grav.multipole->m_pole); c->grav.multipole->CoM[0] = c->loc[0] + c->width[0] * 0.5; c->grav.multipole->CoM[1] = c->loc[1] + c->width[1] * 0.5; c->grav.multipole->CoM[2] = c->loc[2] + c->width[2] * 0.5; c->grav.multipole->r_max = 0.; } } /* Also update the values at rebuild time */ c->grav.multipole->r_max_rebuild = c->grav.multipole->r_max; c->grav.multipole->CoM_rebuild[0] = c->grav.multipole->CoM[0]; c->grav.multipole->CoM_rebuild[1] = c->grav.multipole->CoM[1]; c->grav.multipole->CoM_rebuild[2] = c->grav.multipole->CoM[2]; c->grav.ti_old_multipole = ti_current; } /** * @brief Recursively verify that the multipoles are the sum of their progenies. * * This function does not check whether the multipoles match the particle * content as we may not have received the particles. * * @param c The #cell to recursively search and verify. */ void cell_check_foreign_multipole(const struct cell *c) { #ifdef SWIFT_DEBUG_CHECKS if (c->split) { double M_000 = 0.; long long num_gpart = 0; for (int k = 0; k < 8; k++) { const struct cell *cp = c->progeny[k]; if (cp != NULL) { /* Check the mass */ M_000 += cp->grav.multipole->m_pole.M_000; /* Check the number of particles */ num_gpart += cp->grav.multipole->m_pole.num_gpart; /* Now recurse */ cell_check_foreign_multipole(cp); } } if (num_gpart != c->grav.multipole->m_pole.num_gpart) error("Sum of particles in progenies does not match"); if (fabs(M_000 / c->grav.multipole->m_pole.M_000 - 1.) > 1e-2) error("Mass in progenies does not match!"); } #else error("Calling debugging code without debugging flag activated."); #endif } /** * @brief Computes the multi-pole brutally and compare to the * recursively computed one. * * @param c Cell to act upon * @param grav_props The properties of the gravity scheme. */ void cell_check_multipole(struct cell *c, const struct gravity_props *const grav_props) { #ifdef SWIFT_DEBUG_CHECKS struct gravity_tensors ma; const double tolerance = 1e-3; /* Relative */ /* First recurse */ if (c->split) for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) cell_check_multipole(c->progeny[k], grav_props); if (c->grav.count > 0) { /* Brute-force calculation */ gravity_P2M(&ma, c->grav.parts, c->grav.count, grav_props); gravity_multipole_compute_power(&ma.m_pole); /* Now compare the multipole expansion */ if (!gravity_multipole_equal(&ma, c->grav.multipole, tolerance)) { message("Multipoles are not equal at depth=%d! tol=%f", c->depth, tolerance); message("Correct answer:"); gravity_multipole_print(&ma.m_pole); message("Recursive multipole:"); gravity_multipole_print(&c->grav.multipole->m_pole); error("Aborting"); } /* Check that the upper limit of r_max is good enough */ if (!(1.1 * c->grav.multipole->r_max >= ma.r_max)) { error("Upper-limit r_max=%e too small. Should be >=%e.", c->grav.multipole->r_max, ma.r_max); } else if (c->grav.multipole->r_max * c->grav.multipole->r_max > 3. * c->width[0] * c->width[0]) { error("r_max=%e larger than cell diagonal %e.", c->grav.multipole->r_max, sqrt(3. * c->width[0] * c->width[0])); } } #else error("Calling debugging code without debugging flag activated."); #endif } /** * @brief Frees up the memory allocated for this #cell. * * @param c The #cell. */ void cell_clean(struct cell *c) { /* Hydro */ cell_free_hydro_sorts(c); /* Stars */ cell_free_stars_sorts(c); /* Grid */ cell_free_grid(c); /* Recurse */ for (int k = 0; k < 8; k++) if (c->progeny[k]) cell_clean(c->progeny[k]); } /** * @brief Clear the drift flags on the given cell. */ void cell_clear_drift_flags(struct cell *c, void *data) { cell_clear_flag(c, cell_flag_do_hydro_drift | cell_flag_do_hydro_sub_drift | cell_flag_do_grav_drift | cell_flag_do_grav_sub_drift | cell_flag_do_bh_drift | cell_flag_do_bh_sub_drift | cell_flag_do_stars_drift | cell_flag_do_stars_sub_drift | cell_flag_do_sink_drift | cell_flag_do_sink_sub_drift); } /** * @brief Clear the limiter flags on the given cell. */ void cell_clear_limiter_flags(struct cell *c, void *data) { cell_clear_flag(c, cell_flag_do_hydro_limiter | cell_flag_do_hydro_sub_limiter); } /** * @brief Set the super-cell pointers for all cells in a hierarchy. * * @param c The top-level #cell to play with. * @param super Pointer to the deepest cell with tasks in this part of the * tree. * @param with_hydro Are we running with hydrodynamics on? * @param with_grav Are we running with gravity on? */ void cell_set_super(struct cell *c, struct cell *super, const int with_hydro, const int with_grav) { /* Are we in a cell which is either the hydro or gravity super? */ if (super == NULL && ((with_hydro && c->hydro.super != NULL) || (with_grav && c->grav.super != NULL))) super = c; /* Set the super-cell */ c->super = super; /* Recurse */ if (c->split) for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) cell_set_super(c->progeny[k], super, with_hydro, with_grav); } /** * @brief Set the super-cell pointers for all cells in a hierarchy. * * @param c The top-level #cell to play with. * @param super_hydro Pointer to the deepest cell with tasks in this part of * the tree. */ void cell_set_super_hydro(struct cell *c, struct cell *super_hydro) { /* Are we in a cell with some kind of self/pair task ? */ if (super_hydro == NULL && c->hydro.density != NULL) super_hydro = c; /* Set the super-cell */ c->hydro.super = super_hydro; /* Recurse */ if (c->split) for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) cell_set_super_hydro(c->progeny[k], super_hydro); } /** * @brief Set the super-cell pointers for all cells in a hierarchy. * * @param c The top-level #cell to play with. * @param super_gravity Pointer to the deepest cell with tasks in this part of * the tree. */ void cell_set_super_gravity(struct cell *c, struct cell *super_gravity) { /* Are we in a cell with some kind of self/pair task ? */ if (super_gravity == NULL && (c->grav.grav != NULL || c->grav.mm != NULL)) super_gravity = c; /* Set the super-cell */ c->grav.super = super_gravity; /* Recurse */ if (c->split) for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) cell_set_super_gravity(c->progeny[k], super_gravity); } /** * @brief Mapper function to set the super pointer of the cells. * * @param map_data The top-level cells. * @param num_elements The number of top-level cells. * @param extra_data Unused parameter. */ void cell_set_super_mapper(void *map_data, int num_elements, void *extra_data) { const struct engine *e = (const struct engine *)extra_data; const int with_hydro = (e->policy & engine_policy_hydro); const int with_grav = (e->policy & engine_policy_self_gravity) || (e->policy & engine_policy_external_gravity); for (int ind = 0; ind < num_elements; ind++) { struct cell *c = &((struct cell *)map_data)[ind]; /* All top-level cells get an MPI tag. */ #ifdef WITH_MPI cell_ensure_tagged(c); #endif /* Super-pointer for hydro */ if (with_hydro) cell_set_super_hydro(c, NULL); /* Super-pointer for gravity */ if (with_grav) cell_set_super_gravity(c, NULL); /* Super-pointer for common operations */ cell_set_super(c, NULL, with_hydro, with_grav); } } /** * @brief Does this cell or any of its children have any task ? * * We use the timestep-related tasks to probe this as these always * exist in a cell hierarchy that has any kind of task. * * @param c The #cell to probe. */ int cell_has_tasks(struct cell *c) { #ifdef WITH_MPI return (c->timestep_collect != NULL || c->mpi.recv != NULL); #else return (c->timestep_collect != NULL); #endif } /** * @brief Resets all the sorting properties for the stars in a given cell * hierarchy. * * The clear_unused_flags argument can be used to additionally clean up all * the flags demanding a sort for the given cell. This should be used with * caution as it will prevent the sort tasks from doing anything on that cell * until these flags are reset. * * @param c The #cell to clean. * @param clear_unused_flags Do we also clean the flags demanding a sort? */ void cell_clear_stars_sort_flags(struct cell *c, const int clear_unused_flags) { /* Clear the flags that have not been reset by the sort task? */ if (clear_unused_flags) { c->stars.requires_sorts = 0; c->stars.do_sort = 0; cell_clear_flag(c, cell_flag_do_stars_sub_sort); } /* Indicate that the cell is not sorted and cancel the pointer sorting * arrays. */ c->stars.sorted = 0; cell_free_stars_sorts(c); /* Recurse if possible */ if (c->split) { for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) cell_clear_stars_sort_flags(c->progeny[k], clear_unused_flags); } } /** * @brief Resets all the sorting properties for the hydro in a given cell * hierarchy. * * The clear_unused_flags argument can be used to additionally clean up all * the flags demanding a sort for the given cell. This should be used with * caution as it will prevent the sort tasks from doing anything on that cell * until these flags are reset. * * @param c The #cell to clean. * @param clear_unused_flags Do we also clean the flags demanding a sort? */ void cell_clear_hydro_sort_flags(struct cell *c, const int clear_unused_flags) { /* Clear the flags that have not been reset by the sort task? */ if (clear_unused_flags) { c->hydro.do_sort = 0; c->hydro.requires_sorts = 0; cell_clear_flag(c, cell_flag_do_hydro_sub_sort); } /* Indicate that the cell is not sorted and cancel the pointer sorting * arrays. */ c->hydro.sorted = 0; cell_free_hydro_sorts(c); /* Recurse if possible */ if (c->split) { for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) cell_clear_hydro_sort_flags(c->progeny[k], clear_unused_flags); } } /** * @brief Recursively checks that all particles in a cell have a time-step */ void cell_check_timesteps(const struct cell *c, const integertime_t ti_current, const timebin_t max_bin) { #ifdef SWIFT_DEBUG_CHECKS if (c->hydro.ti_end_min == 0 && c->grav.ti_end_min == 0 && c->stars.ti_end_min == 0 && c->black_holes.ti_end_min == 0 && c->sinks.ti_end_min == 0 && c->rt.ti_rt_end_min == 0 && c->nr_tasks > 0) error("Cell without assigned time-step"); if (c->split) { for (int k = 0; k < 8; ++k) if (c->progeny[k] != NULL) cell_check_timesteps(c->progeny[k], ti_current, max_bin); } else { if (c->nodeID == engine_rank) for (int i = 0; i < c->hydro.count; ++i) if (c->hydro.parts[i].time_bin == 0) error("Particle without assigned time-bin"); } /* Other checks not relevent when starting-up */ if (ti_current == 0) return; integertime_t ti_end_min = max_nr_timesteps; integertime_t ti_beg_max = 0; int count = 0; for (int i = 0; i < c->hydro.count; ++i) { const struct part *p = &c->hydro.parts[i]; if (p->time_bin == time_bin_inhibited) continue; if (p->time_bin == time_bin_not_created) continue; ++count; integertime_t ti_end, ti_beg; if (p->time_bin <= max_bin) { integertime_t time_step = get_integer_timestep(p->time_bin); ti_end = get_integer_time_end(ti_current, p->time_bin) + time_step; ti_beg = get_integer_time_begin(ti_current + 1, p->time_bin); } else { ti_end = get_integer_time_end(ti_current, p->time_bin); ti_beg = get_integer_time_begin(ti_current + 1, p->time_bin); } ti_end_min = min(ti_end, ti_end_min); ti_beg_max = max(ti_beg, ti_beg_max); } /* Only check cells that have at least one non-inhibited particle */ if (count > 0) { if (count != c->hydro.count) { /* Note that we use a < as the particle with the smallest time-bin might have been swallowed. This means we will run this cell with 0 active particles but that's not wrong */ if (ti_end_min < c->hydro.ti_end_min) error( "Non-matching ti_end_min. Cell=%lld true=%lld ti_current=%lld " "depth=%d", c->hydro.ti_end_min, ti_end_min, ti_current, c->depth); } else /* Normal case: nothing was swallowed/converted */ { if (ti_end_min != c->hydro.ti_end_min) error( "Non-matching ti_end_min. Cell=%lld true=%lld ti_current=%lld " "depth=%d", c->hydro.ti_end_min, ti_end_min, ti_current, c->depth); } if (ti_beg_max != c->hydro.ti_beg_max) error( "Non-matching ti_beg_max. Cell=%lld true=%lld ti_current=%lld " "depth=%d", c->hydro.ti_beg_max, ti_beg_max, ti_current, c->depth); } #else error("Calling debugging code without debugging flag activated."); #endif } void cell_check_spart_pos(const struct cell *c, const struct spart *global_sparts) { #ifdef SWIFT_DEBUG_CHECKS /* Recurse */ if (c->split) { for (int k = 0; k < 8; ++k) if (c->progeny[k] != NULL) cell_check_spart_pos(c->progeny[k], global_sparts); } struct spart *sparts = c->stars.parts; const int count = c->stars.count; for (int i = 0; i < count; ++i) { const struct spart *sp = &sparts[i]; if ((sp->x[0] < c->loc[0] / space_stretch) || (sp->x[1] < c->loc[1] / space_stretch) || (sp->x[2] < c->loc[2] / space_stretch) || (sp->x[0] >= (c->loc[0] + c->width[0]) * space_stretch) || (sp->x[1] >= (c->loc[1] + c->width[1]) * space_stretch) || (sp->x[2] >= (c->loc[2] + c->width[2]) * space_stretch)) error("spart not in its cell!"); if (sp->time_bin != time_bin_not_created && sp->time_bin != time_bin_inhibited) { const struct gpart *gp = sp->gpart; if (gp == NULL && sp->time_bin != time_bin_not_created) error("Unlinked spart!"); if (&global_sparts[-gp->id_or_neg_offset] != sp) error("Incorrectly linked spart!"); } } #else error("Calling a degugging function outside debugging mode."); #endif } /** * @brief Checks that a cell and all its progenies have cleared their sort * flags. * * Should only be used for debugging purposes. * * @param c The #cell to check. */ void cell_check_sort_flags(const struct cell *c) { #ifdef SWIFT_DEBUG_CHECKS const int do_hydro_sub_sort = cell_get_flag(c, cell_flag_do_hydro_sub_sort); const int do_stars_sub_sort = cell_get_flag(c, cell_flag_do_stars_sub_sort); if (do_hydro_sub_sort) error( "cell %lld has a hydro sub_sort flag set. Node=%d depth=%d maxdepth=%d", c->cellID, c->nodeID, c->depth, c->maxdepth); if (do_stars_sub_sort) error( "cell %lld has a stars sub_sort flag set. Node=%d depth=%d maxdepth=%d", c->cellID, c->nodeID, c->depth, c->maxdepth); if (c->split) { for (int k = 0; k < 8; ++k) { if (c->progeny[k] != NULL) cell_check_sort_flags(c->progeny[k]); } } #endif } /** * @brief Can we use the MM interactions fo a given pair of cells? * * The two cells have to be different! * * @param ci The first #cell. * @param cj The second #cell. * @param e The #engine. * @param s The #space. * @param use_rebuild_data Are we considering the data at the last tree-build * (1) or current data (0)? * @param is_tree_walk Are we calling this in the tree walk (1) or for the * top-level task construction (0)? */ int cell_can_use_pair_mm(const struct cell *restrict ci, const struct cell *restrict cj, const struct engine *e, const struct space *s, const int use_rebuild_data, const int is_tree_walk) { const struct gravity_props *props = e->gravity_properties; const int periodic = s->periodic; const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; /* Check for trivial cases */ if (is_tree_walk && ci->grav.count <= 1) return 0; if (is_tree_walk && cj->grav.count <= 1) return 0; /* Recover the multipole information */ const struct gravity_tensors *restrict multi_i = ci->grav.multipole; const struct gravity_tensors *restrict multi_j = cj->grav.multipole; double dx, dy, dz; /* Get the distance between the CoMs */ if (use_rebuild_data) { dx = multi_i->CoM_rebuild[0] - multi_j->CoM_rebuild[0]; dy = multi_i->CoM_rebuild[1] - multi_j->CoM_rebuild[1]; dz = multi_i->CoM_rebuild[2] - multi_j->CoM_rebuild[2]; } else { dx = multi_i->CoM[0] - multi_j->CoM[0]; dy = multi_i->CoM[1] - multi_j->CoM[1]; dz = multi_i->CoM[2] - multi_j->CoM[2]; } /* Apply BC */ if (periodic) { dx = nearest(dx, dim[0]); dy = nearest(dy, dim[1]); dz = nearest(dz, dim[2]); } const double r2 = dx * dx + dy * dy + dz * dz; return gravity_M2L_accept_symmetric(props, multi_i, multi_j, r2, use_rebuild_data, periodic); }