diff --git a/src/partition.c b/src/partition.c index 0f8eb3ebe334d71228510307dd9ccc4e56e234b3..7dbbb9552e603adee45097a379200f1493ce3349 100644 --- a/src/partition.c +++ b/src/partition.c @@ -927,7 +927,10 @@ static int check_complete(struct space *s, int verbose, int nregions) { int failed = 0; for (int i = 0; i < nregions; i++) present[i] = 0; for (int i = 0; i < s->nr_cells; i++) { - present[s->cells[i].nodeID]++; + if (s->cells[i].nodeID <= nregions) + present[s->cells[i].nodeID]++; + else + message("Bad nodeID: %d", s->cells[i].nodeID); } for (int i = 0; i < nregions; i++) { if (!present[i]) { @@ -938,3 +941,53 @@ static int check_complete(struct space *s, int verbose, int nregions) { free(present); return (!failed); } + + +/** + * @brief Partition a space of cells based on another space of cells. + * + * The two spaces are expected to be at different cell sizes, so what we'd + * like to do is assign the second space to geometrically closest nodes + * of the first, with the effect of minimizing particle movement when + * rebuilding the second space from the first. + * + * Since two spaces cannot exist simultaneously the old space is actually + * required in a decomposed state. These are the old cells sizes and counts + * per dimension, along with a list of the old nodeIDs. The old nodeIDs are + * indexed by the cellid (see cell_getid()), so should be stored that way. + * + * On exit the new space cells will have their nodeIDs assigned. + * + * @param oldh the cell dimensions of old space. + * @param oldcdim number of cells per dimension in old space. + * @param oldnodeIDs the nodeIDs of cells in the old space, indexed by old cellid. + * @param s the space to be partitioned. + * + * @return 1 if the new space contains nodeIDs from all nodes, 0 otherwise. + */ +int partition_space_to_space(double *oldh, double *oldcdim, int *oldnodeIDs, + struct space *s) { + + /* Loop over all the new cells. */ + int nr_nodes = 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++) { + + /* Scale indices to old cell space. */ + int ii = rint(i * s->ih[0] * oldh[0]); + int jj = rint(j * s->ih[1] * oldh[1]); + int kk = rint(k * s->ih[2] * oldh[2]); + + int cid = cell_getid(s->cdim, i, j, k); + int oldcid = cell_getid(oldcdim, ii, jj, kk); + s->cells[cid].nodeID = oldnodeIDs[oldcid]; + + if (oldnodeIDs[oldcid] > nr_nodes) nr_nodes = oldnodeIDs[oldcid]; + } + } + } + + /* Check we have all nodeIDs present in the resample. */ + return check_complete(s, 1, nr_nodes + 1); +} diff --git a/src/partition.h b/src/partition.h index 3ab5f5a817bf5b77d45c7fb3313158b83d98e251..08906c765bbcf71b084829502e632e3159ebd0bf 100644 --- a/src/partition.h +++ b/src/partition.h @@ -56,4 +56,6 @@ void partition_repartition(enum repartition_type reparttype, int nodeID, void partition_initial_partition(struct partition *initial_partition, int nodeID, int nr_nodes, struct space *s); +int partition_space_to_space(double *oldh, double *oldcdim, int *oldnodeID, + struct space *s); #endif /* SWIFT_PARTITION_H */ diff --git a/src/space.c b/src/space.c index 0785740335f6e782a875b7ab658a9a0564288aab..c25244b0c2c1d96b7ba2fd009586f54ec3f6f090 100644 --- a/src/space.c +++ b/src/space.c @@ -1,6 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) +* 2016 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 @@ -205,9 +206,33 @@ void space_regrid(struct space *s, double cell_max, int verbose) { * global partition is recomputed and the particles redistributed. * Be prepared to do that. */ #ifdef WITH_MPI - int partition = 0; - if (cdim[0] < s->cdim[0] || cdim[1] < s->cdim[1] || cdim[2] < s->cdim[2]) - partition = 1; + double oldh[3]; + double oldcdim[3]; + 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]; + oldh[0] = s->h[0]; + oldh[1] = s->h[1]; + oldh[2] = s->h[2]; + + if ((oldnodeIDs = (int *)malloc(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[cid].nodeID; + } + } + } + } + #endif /* Do we need to re-build the upper-level cells? */ @@ -268,29 +293,36 @@ void space_regrid(struct space *s, double cell_max, int verbose) { fflush(stdout); #ifdef WITH_MPI - /* XXX create an engine_resplit() function to use the same method as the - * initial partition. Fake for now. */ - if (partition) { - if (s->e->nodeID == 0) - message("cell dimensions have decreased. Recalculating the " - "global partition."); - - /* Change the global partitioning. */ - int grid[3]; - factor(s->e->nr_nodes, &grid[0], &grid[1]); - factor(s->e->nr_nodes / grid[1], &grid[0], &grid[2]); - factor(grid[0] * grid[1], &grid[1], &grid[0]); - - /* Run through the cells and set their nodeID. */ - int ind[3]; - for (int k = 0; k < s->nr_cells; k++) { - c = &s->cells[k]; - for (int j = 0; j < 3; j++) ind[j] = c->loc[j] / s->dim[j] * grid[j]; - c->nodeID = ind[0] + grid[0] * (ind[1] + grid[1] * ind[2]); - } + 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(oldh, 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; +#ifdef 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); - /* Make the proxies. */ - engine_makeproxies(s->e); + /* Finished with these. */ + free(oldnodeIDs); } #endif } /* re-build upper-level cells? */