/******************************************************************************* * 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 headers. */ #include /* Local headers. */ #include "swift.h" #include "zoom_region/zoom.h" /** * @brief Generate a random coordinate from a gaussian distribution. * * @param mean The mean of the gaussian distribution. * @param std The standard deviation of the gaussian distribution. * @param max_width The maximum width of the cell. * @param id The ID of the particle for which to generate the number. * @param ti_current The time (on the time-line) for which to generate the * number. * @return A random number drawn from the gaussian distribution. */ double random_gaussian_coordinate(const double mean, const double std, const double max_width, const int id, const integertime_t ti_current, const enum random_number_type type) { /* Generate a random number from a normal distribution. */ double z0 = random_gaussian(mean, std, id, ti_current, type); /* We only want to go out at most by the size of a cell. If we've got a * coordinate out too far we should try again changing the random number * seed. */ if (z0 < mean - max_width / 2 || z0 > mean + max_width / 2) { return random_gaussian_coordinate( mean, std, max_width, id, ti_current, type + 3 % random_number_powerspectrum_split); } return z0; } /* This script tests the process of regridding a zoom simulation. It'll first * calculate the zoom geometry, construct the cell structures as would be done * in a run during a regrid, populates the cells and then tests all particles * have ended up where they are supposed to be. */ void make_mock_space(struct space *s) { /* Define the boxsize. */ s->dim[0] = 1000; s->dim[1] = 1000; s->dim[2] = 1000; /* The simulation is periodic */ s->periodic = 1; /* Define the gpart count (100 high and 100 low resolution) */ s->nr_gparts = 100 + 100; /* We need the engine to be NULL for the logic. */ s->e = NULL; /* Allocate memory for the gparts. */ struct gpart *gparts = NULL; if (posix_memalign((void **)&gparts, gpart_align, s->nr_gparts * sizeof(struct gpart)) != 0) { error("Failed to allocate memory for gparts"); } bzero(gparts, s->nr_gparts * sizeof(struct gpart)); /* Randomly place the background particles. */ for (int i = 0; i < 100; i++) { gparts[i].x[0] = s->dim[0] * 0.99 * ((double)rand() / RAND_MAX) + 1; gparts[i].x[1] = s->dim[1] * 0.99 * ((double)rand() / RAND_MAX) + 1; gparts[i].x[2] = s->dim[2] * 0.99 * ((double)rand() / RAND_MAX) + 1; gparts[i].type = swift_type_dark_matter_background; gparts[i].mass = 1.0; } /* Define the width of the zoom region (randomly). */ double zoom_width = 50 * ((double)rand() / RAND_MAX) + 1; message("Zoom width = %f", zoom_width); /* Get the "current time" */ time_t ti_current = time(NULL); /* Define the zoom particles by sampling from a normal distribution. */ for (int i = 100; i < 200; i++) { gparts[i].x[0] = random_gaussian_coordinate(s->dim[0] / 2, zoom_width, 100, i, ti_current, 0); gparts[i].x[1] = random_gaussian_coordinate(s->dim[1] / 2, zoom_width, 100, i, ti_current, 1); gparts[i].x[2] = random_gaussian_coordinate(s->dim[2] / 2, zoom_width, 100, i, ti_current, 2); gparts[i].type = swift_type_dark_matter; gparts[i].mass = 1.0; } s->gparts = gparts; } void associate_gparts_to_cells(struct space *s) { for (size_t i = 0; i < s->nr_gparts; i++) { struct gpart *gpart = &s->gparts[i]; int cid = cell_getid_from_pos(s, gpart->x[0], gpart->x[1], gpart->x[2]); struct cell *c = &s->cells_top[cid]; if (c == NULL) { error("Failed to find cell for gpart."); } c->grav.count++; } } int main(int argc, char *argv[]) { /* Initialize CPU frequency, this also starts time. */ unsigned long long cpufreq = 0; clocks_set_cpufreq(cpufreq); /* Choke on FPEs */ #ifdef HAVE_FE_ENABLE_EXCEPT feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); #endif /* Get some randomness going */ const int seed = time(NULL); message("Seed = %d", seed); srand(seed); /* Create a structure to read file into. */ struct swift_params param_file; /* Read the parameter file. */ parser_read_file(argv[1], ¶m_file); /* Create a space structure. (this creates fake particles to get the layout * of cells right but we will modify this shortly to get a particle per * cell) */ struct space *s = malloc(sizeof(struct space)); bzero(s, sizeof(struct space)); make_mock_space(s); /* Flag that we are running a zoom. */ s->with_zoom_region = 1; /* Run the zoom_init function. */ zoom_props_init(¶m_file, s, /*verbose*/ 0); /* Run the regridding. */ space_regrid(s, /*verbose*/ 0); /* Associate gparts. */ associate_gparts_to_cells(s); /* Ensure void cells have 0 counts. */ for (int i = 0; i < s->nr_cells; i++) { struct cell *c = &s->cells_top[i]; if (c->subtype == cell_subtype_void) { if (c->grav.count != 0) { error( "Cell %d has %d particles (c->type = %s, c->subtype = %s, c->loc = " "[%f, %f, %f])", i, c->grav.count, cellID_names[c->type], subcellID_names[c->subtype], c->loc[0], c->loc[1], c->loc[2]); } } } /* Test each cell type contains the expected counts. (This ensures the zoom * and void cells make sense too.)*/ int zoom_count = 0; int bkg_count = 0; int buffer_count = 0; for (int i = 0; i < s->nr_cells; i++) { struct cell *c = &s->cells_top[i]; if (c->type == cell_type_zoom) { zoom_count += c->grav.count; } else if (c->type == cell_type_bkg) { bkg_count += c->grav.count; } else if (c->type == cell_type_buffer) { buffer_count += c->grav.count; } else { error("Cell %d has unexpected type %s", i, cellID_names[c->type]); } } /* Because we are doing things randomly we need to check for both the buffer * cell case and no buffer cell case. */ if (s->zoom_props->with_buffer_cells) { /* Zoom region has 100 high res particles and any low res interlopers. */ assert(zoom_count == 100 + 100 - bkg_count - buffer_count); /* Buffer region + background region + zoom interlopers == 100 background * particles. */ assert(bkg_count + buffer_count + zoom_count - 100 == 100); } else { /* Zoom region has 100 high res particles and any low res interlopers. */ assert(zoom_count == 100 + 100 - bkg_count); /* Background region + zoom interlopers == 100 background particles. */ assert(bkg_count + zoom_count - 100 == 100); } /* Free the space. */ free(s->local_cells_top); free(s->multipoles_top); free(s->local_cells_with_tasks_top); free(s->cells_with_particles_top); free(s->local_cells_with_particles_top); free(s->zoom_props->local_zoom_cells_top); free(s->zoom_props->void_cell_indices); free(s->zoom_props->neighbour_cells_top); free(s->zoom_props->local_bkg_cells_top); free(s->zoom_props->local_buffer_cells_top); free(s->zoom_props->local_zoom_cells_with_particles_top); free(s->zoom_props->local_bkg_cells_with_particles_top); free(s->zoom_props->local_buffer_cells_with_particles_top); free(s->cells_top); free(s->gparts); free(s->zoom_props); free(s); return 0; }