/******************************************************************************* * 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) * * 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 "space.h" /* Local headers. */ #include "cell.h" #include "engine.h" #include "scheduler.h" /** * @brief Re-build the top-level cell grid. * * @param s The #space. * @param verbose Print messages to stdout or not. */ void space_regrid(struct space *s, int verbose) { const size_t nr_parts = s->nr_parts; const size_t nr_sparts = s->nr_sparts; const size_t nr_bparts = s->nr_bparts; const size_t nr_sinks = s->nr_sinks; const ticks tic = getticks(); const integertime_t ti_current = (s->e != NULL) ? s->e->ti_current : 0; /* Run through the cells and get the current h_max. */ // tic = getticks(); float h_max = s->cell_min / kernel_gamma / space_stretch; if (nr_parts > 0) { /* Can we use the list of local non-empty top-level cells? */ if (s->local_cells_with_particles_top != NULL) { for (int k = 0; k < s->nr_local_cells_with_particles; ++k) { const struct cell *c = &s->cells_top[s->local_cells_with_particles_top[k]]; if (c->hydro.h_max > h_max) { h_max = c->hydro.h_max; } if (c->stars.h_max > h_max) { h_max = c->stars.h_max; } if (c->black_holes.h_max > h_max) { h_max = c->black_holes.h_max; } if (c->sinks.h_max > h_max) { h_max = c->sinks.h_max; } } /* Can we instead use all the top-level cells? */ } else if (s->cells_top != NULL) { for (int k = 0; k < s->nr_cells; k++) { const struct cell *c = &s->cells_top[k]; if (c->nodeID == engine_rank && c->hydro.h_max > h_max) { h_max = c->hydro.h_max; } if (c->nodeID == engine_rank && c->stars.h_max > h_max) { h_max = c->stars.h_max; } if (c->nodeID == engine_rank && c->black_holes.h_max > h_max) { h_max = c->black_holes.h_max; } if (c->nodeID == engine_rank && c->sinks.h_max > h_max) { h_max = c->sinks.h_max; } } /* Last option: run through the particles */ } else { for (size_t k = 0; k < nr_parts; k++) { if (s->parts[k].h > h_max) h_max = s->parts[k].h; } for (size_t k = 0; k < nr_sparts; k++) { if (s->sparts[k].h > h_max) h_max = s->sparts[k].h; } for (size_t k = 0; k < nr_bparts; k++) { if (s->bparts[k].h > h_max) h_max = s->bparts[k].h; } for (size_t k = 0; k < nr_sinks; k++) { if (s->sinks[k].h > h_max) h_max = s->sinks[k].h; } } } /* If we are running in parallel, make sure everybody agrees on how large the largest cell should be. */ #ifdef WITH_MPI { float buff; if (MPI_Allreduce(&h_max, &buff, 1, MPI_FLOAT, MPI_MAX, MPI_COMM_WORLD) != MPI_SUCCESS) error("Failed to aggregate the rebuild flag across nodes."); h_max = buff; } #endif if (verbose) message("h_max is %.3e (cell_min=%.3e).", h_max, s->cell_min); /* Get the new putative cell dimensions. */ const int cdim[3] = { (int)floor(s->dim[0] / fmax(h_max * kernel_gamma * space_stretch, s->cell_min)), (int)floor(s->dim[1] / fmax(h_max * kernel_gamma * space_stretch, s->cell_min)), (int)floor(s->dim[2] / fmax(h_max * kernel_gamma * space_stretch, s->cell_min))}; /* check that we have at least 1 cell in each dimension */ if (cdim[0] == 0 || cdim[1] == 0 || cdim[2] == 0) { error( "Top level cell dimension of size 0 detected (cdim = [%i %i " "%i])!\nThis usually indicates a problem with the initial smoothing " "lengths of the particles, e.g. a smoothing length that is comparable " "in size to the box size.", cdim[0], cdim[1], cdim[2]); } /* Check if we have enough cells for periodicity. */ if (s->periodic && (cdim[0] < 3 || cdim[1] < 3 || cdim[2] < 3)) error( "Must have at least 3 cells in each spatial dimension when periodicity " "is switched on.\nThis error is often caused by any of the " "followings:\n" " - too few particles to generate a sensible grid,\n" " - the initial value of 'Scheduler:max_top_level_cells' is too " "small,\n" " - the (minimal) time-step is too large leading to particles with " "predicted smoothing lengths too large for the box size,\n" " - particles with velocities so large that they move by more than two " "box sizes per time-step.\n"); /* In MPI-Land, changing the top-level cell size requires that the * global partition is recomputed and the particles redistributed. * Be prepared to do that. */ #ifdef WITH_MPI double oldwidth[3] = {0., 0., 0.}; double oldcdim[3] = {0., 0., 0.}; int *oldnodeIDs = NULL; if (cdim[0] < s->cdim[0] || cdim[1] < s->cdim[1] || cdim[2] < s->cdim[2]) { /* Capture state of current space. */ oldcdim[0] = s->cdim[0]; oldcdim[1] = s->cdim[1]; oldcdim[2] = s->cdim[2]; oldwidth[0] = s->width[0]; oldwidth[1] = s->width[1]; oldwidth[2] = s->width[2]; if ((oldnodeIDs = (int *)swift_malloc("nodeIDs", sizeof(int) * s->nr_cells)) == NULL) error("Failed to allocate temporary nodeIDs."); int cid = 0; for (int i = 0; i < s->cdim[0]; i++) { for (int j = 0; j < s->cdim[1]; j++) { for (int k = 0; k < s->cdim[2]; k++) { cid = cell_getid(oldcdim, i, j, k); oldnodeIDs[cid] = s->cells_top[cid].nodeID; } } } } /* Are we about to allocate new top level cells without a regrid? * Can happen when restarting the application. */ const int no_regrid = (s->cells_top == NULL && oldnodeIDs == NULL); #endif /* Do we need to re-build the upper-level cells? */ // tic = getticks(); if (s->cells_top == NULL || cdim[0] < s->cdim[0] || cdim[1] < s->cdim[1] || cdim[2] < s->cdim[2]) { /* Be verbose about this. */ #ifdef SWIFT_DEBUG_CHECKS message("(re)griding space cdim=(%d %d %d)", cdim[0], cdim[1], cdim[2]); fflush(stdout); #endif /* Free the old cells, if they were allocated. */ if (s->cells_top != NULL) { space_free_cells(s); swift_free("local_cells_with_tasks_top", s->local_cells_with_tasks_top); swift_free("local_cells_top", s->local_cells_top); swift_free("cells_with_particles_top", s->cells_with_particles_top); swift_free("local_cells_with_particles_top", s->local_cells_with_particles_top); swift_free("cells_top", s->cells_top); swift_free("cells_top_updated", s->cells_top_updated); swift_free("multipoles_top", s->multipoles_top); } /* Also free the task arrays, these will be regenerated and we can use the * memory while copying the particle arrays. */ if (s->e != NULL) scheduler_free_tasks(&s->e->sched); /* Set the new cell dimensions only if smaller. */ for (int k = 0; k < 3; k++) { s->cdim[k] = cdim[k]; s->width[k] = s->dim[k] / cdim[k]; s->iwidth[k] = 1.0 / s->width[k]; } const float dmin = min3(s->width[0], s->width[1], s->width[2]); /* Allocate the highest level of cells. */ s->tot_cells = s->nr_cells = cdim[0] * cdim[1] * cdim[2]; if (swift_memalign("cells_top", (void **)&s->cells_top, cell_align, s->nr_cells * sizeof(struct cell)) != 0) error("Failed to allocate top-level cells."); bzero(s->cells_top, s->nr_cells * sizeof(struct cell)); /* Allocate the multipoles for the top-level cells. */ if (s->with_self_gravity) { if (swift_memalign("multipoles_top", (void **)&s->multipoles_top, multipole_align, s->nr_cells * sizeof(struct gravity_tensors)) != 0) error("Failed to allocate top-level multipoles."); bzero(s->multipoles_top, s->nr_cells * sizeof(struct gravity_tensors)); } if (swift_memalign("cells_top_updated", (void **)&s->cells_top_updated, cell_align, s->nr_cells * sizeof(char)) != 0) error("Failed to allocate top-level cells."); bzero(s->cells_top_updated, s->nr_cells * sizeof(char)); /* Allocate the indices of local cells */ if (swift_memalign("local_cells_top", (void **)&s->local_cells_top, SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0) error("Failed to allocate indices of local top-level cells."); bzero(s->local_cells_top, s->nr_cells * sizeof(int)); /* Allocate the indices of local cells with tasks */ if (swift_memalign("local_cells_with_tasks_top", (void **)&s->local_cells_with_tasks_top, SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0) error("Failed to allocate indices of local top-level cells with tasks."); bzero(s->local_cells_with_tasks_top, s->nr_cells * sizeof(int)); /* Allocate the indices of cells with particles */ if (swift_memalign("cells_with_particles_top", (void **)&s->cells_with_particles_top, SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0) error("Failed to allocate indices of top-level cells with particles."); bzero(s->cells_with_particles_top, s->nr_cells * sizeof(int)); /* Allocate the indices of local cells with particles */ if (swift_memalign("local_cells_with_particles_top", (void **)&s->local_cells_with_particles_top, SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0) error( "Failed to allocate indices of local top-level cells with " "particles."); bzero(s->local_cells_with_particles_top, s->nr_cells * sizeof(int)); /* Set the cells' locks */ for (int k = 0; k < s->nr_cells; k++) { if (lock_init(&s->cells_top[k].hydro.lock) != 0) error("Failed to init spinlock for hydro."); if (lock_init(&s->cells_top[k].hydro.extra_sort_lock) != 0) error("Failed to init spinlock for hydro extra sort."); if (lock_init(&s->cells_top[k].grav.plock) != 0) error("Failed to init spinlock for gravity."); if (lock_init(&s->cells_top[k].grav.mlock) != 0) error("Failed to init spinlock for multipoles."); if (lock_init(&s->cells_top[k].grav.star_formation_lock) != 0) error("Failed to init spinlock for star formation (gpart)."); if (lock_init(&s->cells_top[k].stars.lock) != 0) error("Failed to init spinlock for stars."); if (lock_init(&s->cells_top[k].sinks.lock) != 0) error("Failed to init spinlock for sinks."); if (lock_init(&s->cells_top[k].sinks.sink_formation_lock) != 0) error("Failed to init spinlock for sink formation."); if (lock_init(&s->cells_top[k].black_holes.lock) != 0) error("Failed to init spinlock for black holes."); if (lock_init(&s->cells_top[k].stars.star_formation_lock) != 0) error("Failed to init spinlock for star formation (spart)."); } /* Set the cell location and sizes. */ for (int i = 0; i < cdim[0]; i++) for (int j = 0; j < cdim[1]; j++) for (int k = 0; k < cdim[2]; k++) { const size_t cid = cell_getid(cdim, i, j, k); struct cell *restrict c = &s->cells_top[cid]; c->loc[0] = i * s->width[0]; c->loc[1] = j * s->width[1]; c->loc[2] = k * s->width[2]; c->width[0] = s->width[0]; c->width[1] = s->width[1]; c->width[2] = s->width[2]; c->dmin = dmin; c->h_min_allowed = c->dmin * 0.5 * (1. / kernel_gamma); c->h_max_allowed = c->dmin * (1. / kernel_gamma); c->depth = 0; c->split = 0; c->hydro.count = 0; c->grav.count = 0; c->stars.count = 0; c->sinks.count = 0; c->top = c; c->super = c; c->hydro.super = c; c->grav.super = c; c->hydro.ti_old_part = ti_current; c->grav.ti_old_part = ti_current; c->stars.ti_old_part = ti_current; c->sinks.ti_old_part = ti_current; c->black_holes.ti_old_part = ti_current; c->grav.ti_old_multipole = ti_current; #ifdef WITH_MPI c->mpi.tag = -1; c->mpi.recv = NULL; c->mpi.send = NULL; #endif // WITH_MPI if (s->with_self_gravity) c->grav.multipole = &s->multipoles_top[cid]; #if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) cell_assign_top_level_cell_index(c, s->cdim, s->dim, s->iwidth); #endif } /* Be verbose about the change. */ if (verbose) message("set cell dimensions to [ %i %i %i ].", cdim[0], cdim[1], cdim[2]); #ifdef WITH_MPI if (oldnodeIDs != NULL) { /* We have changed the top-level cell dimension, so need to redistribute * cells around the nodes. We repartition using the old space node * positions as a grid to resample. */ if (s->e->nodeID == 0) message( "basic cell dimensions have increased - recalculating the " "global partition."); if (!partition_space_to_space(oldwidth, oldcdim, oldnodeIDs, s)) { /* Failed, try another technique that requires no settings. */ message("Failed to get a new partition, trying less optimal method"); struct partition initial_partition; #if defined(HAVE_PARMETIS) || defined(HAVE_METIS) initial_partition.type = INITPART_METIS_NOWEIGHT; #else initial_partition.type = INITPART_VECTORIZE; #endif partition_initial_partition(&initial_partition, s->e->nodeID, s->e->nr_nodes, s); } /* Re-distribute the particles to their new nodes. */ engine_redistribute(s->e); /* Make the proxies. */ engine_makeproxies(s->e); /* Finished with these. */ swift_free("nodeIDs", oldnodeIDs); } else if (no_regrid && s->e != NULL) { /* If we have created the top-levels cells and not done an initial * partition (can happen when restarting), then the top-level cells * are not assigned to a node, we must do that and then associate the * particles with the cells. Note requires that * partition_store_celllist() was called once before, or just before * dumping the restart files.*/ partition_restore_celllist(s, s->e->reparttype); /* Now re-distribute the particles, should just add to cells? */ engine_redistribute(s->e); /* Make the proxies. */ engine_makeproxies(s->e); } #endif /* WITH_MPI */ // message( "rebuilding upper-level cells took %.3f %s." , // clocks_from_ticks(double)(getticks() - tic), clocks_getunit()); } /* re-build upper-level cells? */ else { /* Otherwise, just clean up the cells. */ /* Free the old cells, if they were allocated. */ space_free_cells(s); } if (verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); }