diff --git a/examples/MultiTypes/multiTypes.yml b/examples/MultiTypes/multiTypes.yml index 4d54f95fcdd09464b03d0f9987398cd2710b2e44..05d45595e9ec43b0849ed574f6b9922c19021a33 100644 --- a/examples/MultiTypes/multiTypes.yml +++ b/examples/MultiTypes/multiTypes.yml @@ -32,6 +32,7 @@ SPH: # Parameters related to the initial conditions InitialConditions: file_name: ./multiTypes.hdf5 # The file to read + replicate: 2 # Replicate all particles twice along each axis # External potential parameters PointMassPotential: diff --git a/examples/MultiTypes/run.sh b/examples/MultiTypes/run.sh index 508a5097f8961f446a51204e889875e33d4f634e..24c87b77a0bab83a8b5f153f2787382998f1e480 100755 --- a/examples/MultiTypes/run.sh +++ b/examples/MultiTypes/run.sh @@ -4,7 +4,7 @@ if [ ! -e multiTypes.hdf5 ] then echo "Generating initial conditions for the multitype box example..." - python makeIC.py 17 24 12 + python makeIC.py 9 13 7 fi ../swift -s -g -S -t 1 multiTypes.yml 2>&1 | tee output.log diff --git a/examples/main.c b/examples/main.c index cf0c37eda2ec5a4a14ebe3f5191030124d2fe6ed..9abbb5433666f3674e8c59389e72dc1d25dfe3c6 100644 --- a/examples/main.c +++ b/examples/main.c @@ -371,6 +371,8 @@ int main(int argc, char *argv[]) { /* Read particles and space information from (GADGET) ICs */ char ICfileName[200] = ""; parser_get_param_string(params, "InitialConditions:file_name", ICfileName); + const int replicate = + parser_get_opt_param_int(params, "InitialConditions:replicate", 1); if (myrank == 0) message("Reading ICs from file '%s'", ICfileName); fflush(stdout); @@ -441,7 +443,7 @@ int main(int argc, char *argv[]) { if (myrank == 0) clocks_gettime(&tic); struct space s; space_init(&s, params, dim, parts, gparts, sparts, Ngas, Ngpart, Nspart, - periodic, with_self_gravity, talking, dry_run); + periodic, replicate, with_self_gravity, talking, dry_run); if (myrank == 0) { clocks_gettime(&toc); message("space_init took %.3f %s.", clocks_diff(&tic, &toc), @@ -458,6 +460,7 @@ int main(int argc, char *argv[]) { s.cdim[1], s.cdim[2]); message("%zi parts in %i cells.", s.nr_parts, s.tot_cells); message("%zi gparts in %i cells.", s.nr_gparts, s.tot_cells); + message("%zi sparts in %i cells.", s.nr_sparts, s.tot_cells); message("maximum depth is %d.", s.maxdepth); fflush(stdout); } @@ -521,6 +524,18 @@ int main(int argc, char *argv[]) { for (k = 0; k < runner_hist_N; k++) runner_hist_bins[k] = 0; #endif +#if defined(WITH_MPI) + N_long[0] = s.nr_parts; + N_long[1] = s.nr_gparts; + N_long[2] = s.nr_sparts; + MPI_Reduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM, 0, + MPI_COMM_WORLD); +#else + N_total[0] = s.nr_parts; + N_total[1] = s.nr_gparts; + N_total[2] = s.nr_sparts; +#endif + /* Get some info to the user. */ if (myrank == 0) { long long N_DM = N_total[1] - N_total[2] - N_total[0]; diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index 9681e880e9f4b44d4f77195eecf5b8642266ce52..31b948ea4c59de42bce7c3b80b8ae2526b10e0cf 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -55,6 +55,7 @@ InitialConditions: shift_x: 0. # (Optional) A shift to apply to all particles read from the ICs (in internal units). shift_y: 0. shift_z: 0. + replicate: 2 # (Optional) Replicate all particles along each axis a given number of times. Default 1. # Parameters governing domain decomposition DomainDecomposition: diff --git a/src/space.c b/src/space.c index 802dc30d1bcd44d4cce46b2a803afade07f5d685..503727cfef81bffabd1d1cf568be6954bad175cf 100644 --- a/src/space.c +++ b/src/space.c @@ -2358,6 +2358,7 @@ void space_init_sparts(struct space *s) { * @param Ngpart The number of Gravity particles in the space. * @param Nspart The number of star particles in the space. * @param periodic flag whether the domain is periodic or not. + * @param replicate How many replications along each direction do we want ? * @param gravity flag whether we are doing gravity or not. * @param verbose Print messages to stdout or not. * @param dry_run If 1, just initialise stuff, don't do anything with the parts. @@ -2370,8 +2371,8 @@ void space_init_sparts(struct space *s) { void space_init(struct space *s, const struct swift_params *params, double dim[3], struct part *parts, struct gpart *gparts, struct spart *sparts, size_t Npart, size_t Ngpart, - size_t Nspart, int periodic, int gravity, int verbose, - int dry_run) { + size_t Nspart, int periodic, int replicate, int gravity, + int verbose, int dry_run) { /* Clean-up everything */ bzero(s, sizeof(struct space)); @@ -2394,15 +2395,29 @@ void space_init(struct space *s, const struct swift_params *params, s->sparts = sparts; s->nr_queues = 1; /* Temporary value until engine construction */ + /* Are we replicating the space ? */ + if (replicate < 1) + error("Value of 'InitialConditions:replicate' (%d) is too small", + replicate); + if (replicate > 1) { + space_replicate(s, replicate, verbose); + parts = s->parts; + gparts = s->gparts; + sparts = s->sparts; + Npart = s->nr_parts; + Ngpart = s->nr_gparts; + Nspart = s->nr_sparts; + } + /* Decide on the minimal top-level cell size */ - const double dmax = max(max(dim[0], dim[1]), dim[2]); + const double dmax = max(max(s->dim[0], s->dim[1]), s->dim[2]); int maxtcells = parser_get_opt_param_int(params, "Scheduler:max_top_level_cells", space_max_top_level_cells_default); s->cell_min = 0.99 * dmax / maxtcells; /* Check that it is big enough. */ - const double dmin = min(min(dim[0], dim[1]), dim[2]); + const double dmin = min(min(s->dim[0], s->dim[1]), s->dim[2]); int needtcells = 3 * dmax / dmin; if (maxtcells < needtcells) error( @@ -2464,13 +2479,13 @@ void space_init(struct space *s, const struct swift_params *params, if (periodic) { for (size_t k = 0; k < Npart; k++) for (int j = 0; j < 3; j++) { - while (parts[k].x[j] < 0) parts[k].x[j] += dim[j]; - while (parts[k].x[j] >= dim[j]) parts[k].x[j] -= dim[j]; + while (parts[k].x[j] < 0) parts[k].x[j] += s->dim[j]; + while (parts[k].x[j] >= s->dim[j]) parts[k].x[j] -= s->dim[j]; } } else { for (size_t k = 0; k < Npart; k++) for (int j = 0; j < 3; j++) - if (parts[k].x[j] < 0 || parts[k].x[j] >= dim[j]) + if (parts[k].x[j] < 0 || parts[k].x[j] >= s->dim[j]) error("Not all particles are within the specified domain."); } @@ -2478,13 +2493,13 @@ void space_init(struct space *s, const struct swift_params *params, if (periodic) { for (size_t k = 0; k < Ngpart; k++) for (int j = 0; j < 3; j++) { - while (gparts[k].x[j] < 0) gparts[k].x[j] += dim[j]; - while (gparts[k].x[j] >= dim[j]) gparts[k].x[j] -= dim[j]; + while (gparts[k].x[j] < 0) gparts[k].x[j] += s->dim[j]; + while (gparts[k].x[j] >= s->dim[j]) gparts[k].x[j] -= s->dim[j]; } } else { for (size_t k = 0; k < Ngpart; k++) for (int j = 0; j < 3; j++) - if (gparts[k].x[j] < 0 || gparts[k].x[j] >= dim[j]) + if (gparts[k].x[j] < 0 || gparts[k].x[j] >= s->dim[j]) error("Not all g-particles are within the specified domain."); } @@ -2492,13 +2507,13 @@ void space_init(struct space *s, const struct swift_params *params, if (periodic) { for (size_t k = 0; k < Nspart; k++) for (int j = 0; j < 3; j++) { - while (sparts[k].x[j] < 0) sparts[k].x[j] += dim[j]; - while (sparts[k].x[j] >= dim[j]) sparts[k].x[j] -= dim[j]; + while (sparts[k].x[j] < 0) sparts[k].x[j] += s->dim[j]; + while (sparts[k].x[j] >= s->dim[j]) sparts[k].x[j] -= s->dim[j]; } } else { for (size_t k = 0; k < Nspart; k++) for (int j = 0; j < 3; j++) - if (sparts[k].x[j] < 0 || sparts[k].x[j] >= dim[j]) + if (sparts[k].x[j] < 0 || sparts[k].x[j] >= s->dim[j]) error("Not all s-particles are within the specified domain."); } } @@ -2524,6 +2539,127 @@ void space_init(struct space *s, const struct swift_params *params, if (!dry_run) space_regrid(s, verbose); } +/** + * @brief Replicate the content of a space along each axis. + * + * Should only be called during initialisation. + * + * @param s The #space to replicate. + * @param replicate The number of copies along each axis. + * @param verbose Are we talkative ? + */ +void space_replicate(struct space *s, int replicate, int verbose) { + + if (replicate < 1) error("Invalid replicate value: %d", replicate); + + message("Replicating space %d times along each axis.", replicate); + + const int factor = replicate * replicate * replicate; + + /* Store the current values */ + const size_t nr_parts = s->nr_parts; + const size_t nr_gparts = s->nr_gparts; + const size_t nr_sparts = s->nr_sparts; + const size_t nr_dm = nr_gparts - nr_parts - nr_sparts; + + s->size_parts = s->nr_parts = nr_parts * factor; + s->size_gparts = s->nr_gparts = nr_gparts * factor; + s->size_sparts = s->nr_sparts = nr_sparts * factor; + + /* Allocate space for new particles */ + struct part *parts = NULL; + struct gpart *gparts = NULL; + struct spart *sparts = NULL; + + if (posix_memalign((void *)&parts, part_align, + s->nr_parts * sizeof(struct part)) != 0) + error("Failed to allocate new part array."); + + if (posix_memalign((void *)&gparts, gpart_align, + s->nr_gparts * sizeof(struct gpart)) != 0) + error("Failed to allocate new gpart array."); + + if (posix_memalign((void *)&sparts, spart_align, + s->nr_sparts * sizeof(struct spart)) != 0) + error("Failed to allocate new spart array."); + + /* Replicate everything */ + for (int i = 0; i < replicate; ++i) { + for (int j = 0; j < replicate; ++j) { + for (int k = 0; k < replicate; ++k) { + const size_t offset = i * replicate * replicate + j * replicate + k; + + /* First copy the data */ + memcpy(parts + offset * nr_parts, s->parts, + nr_parts * sizeof(struct part)); + memcpy(sparts + offset * nr_sparts, s->sparts, + nr_sparts * sizeof(struct spart)); + memcpy(gparts + offset * nr_gparts, s->gparts, + nr_gparts * sizeof(struct gpart)); + + /* Shift the positions */ + const double shift[3] = {i * s->dim[0], j * s->dim[1], k * s->dim[2]}; + + for (size_t n = offset * nr_parts; n < (offset + 1) * nr_parts; ++n) { + parts[n].x[0] += shift[0]; + parts[n].x[1] += shift[1]; + parts[n].x[2] += shift[2]; + } + for (size_t n = offset * nr_gparts; n < (offset + 1) * nr_gparts; ++n) { + gparts[n].x[0] += shift[0]; + gparts[n].x[1] += shift[1]; + gparts[n].x[2] += shift[2]; + } + for (size_t n = offset * nr_sparts; n < (offset + 1) * nr_sparts; ++n) { + sparts[n].x[0] += shift[0]; + sparts[n].x[1] += shift[1]; + sparts[n].x[2] += shift[2]; + } + + /* Set the correct links (recall gpart are sorted by type at start-up): + first DM (unassociated gpart), then gas, then stars */ + if (nr_parts > 0 && nr_gparts > 0) { + const size_t offset_part = offset * nr_parts; + const size_t offset_gpart = offset * nr_gparts + nr_dm; + + for (size_t n = 0; n < nr_parts; ++n) { + parts[offset_part + n].gpart = &gparts[offset_gpart + n]; + gparts[offset_gpart + n].id_or_neg_offset = -(offset_part + n); + } + } + if (nr_sparts > 0 && nr_gparts > 0) { + const size_t offset_spart = offset * nr_sparts; + const size_t offset_gpart = offset * nr_gparts + nr_dm + nr_parts; + + for (size_t n = 0; n < nr_sparts; ++n) { + sparts[offset_spart + n].gpart = &gparts[offset_gpart + n]; + gparts[offset_gpart + n].id_or_neg_offset = -(offset_spart + n); + } + } + } + } + } + + /* Replace the content of the space */ + free(s->parts); + free(s->gparts); + free(s->sparts); + s->parts = parts; + s->gparts = gparts; + s->sparts = sparts; + + /* Finally, update the domain size */ + s->dim[0] *= replicate; + s->dim[1] *= replicate; + s->dim[2] *= replicate; + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that everything is correct */ + part_verify_links(s->parts, s->gparts, s->sparts, s->nr_parts, s->nr_gparts, + s->nr_sparts, verbose); +#endif +} + /** * @brief Cleans-up all the cell links in the space * diff --git a/src/space.h b/src/space.h index a25149e8fe6971b24856a2a60cae23747fbc56ac..327f0225085663d1859bcdbd82a6ac2583fc2b43 100644 --- a/src/space.h +++ b/src/space.h @@ -165,8 +165,8 @@ int space_getsid(struct space *s, struct cell **ci, struct cell **cj, void space_init(struct space *s, const struct swift_params *params, double dim[3], struct part *parts, struct gpart *gparts, struct spart *sparts, size_t Npart, size_t Ngpart, - size_t Nspart, int periodic, int gravity, int verbose, - int dry_run); + size_t Nspart, int periodic, int replicate, int gravity, + int verbose, int dry_run); void space_sanitize(struct space *s); void space_map_cells_pre(struct space *s, int full, void (*fun)(struct cell *c, void *data), void *data); @@ -206,6 +206,7 @@ void space_init_sparts(struct space *s); void space_link_cleanup(struct space *s); void space_check_drift_point(struct space *s, integertime_t ti_current); void space_check_timesteps(struct space *s); +void space_replicate(struct space *s, int replicate, int verbose); void space_clean(struct space *s); #endif /* SWIFT_SPACE_H */