/******************************************************************************* * 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 #include #include #include /* Local headers. */ #include "engine.h" #include "parser.h" #include "random.h" #include "space.h" #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; } void make_mock_space(struct space *s, struct engine *e, const double std) { /* Define the members we need for the test. */ s->with_self_gravity = 1; s->dim[0] = 1000; s->dim[1] = 1000; s->dim[2] = 1000; s->cdim[0] = 16; s->cdim[1] = 16; s->cdim[2] = 16; s->nr_gparts = 100000; s->width[0] = s->dim[0] / s->cdim[0]; s->width[1] = s->dim[1] / s->cdim[1]; s->width[2] = s->dim[2] / s->cdim[2]; /* Attach the engine (all members have been zeroed which should be sufficient * for the test). */ e->s = s; s->e = e; /* Allocate memory for the gparts. */ struct gpart *gparts = (struct gpart *)malloc(s->nr_gparts * sizeof(struct gpart)); bzero(gparts, s->nr_gparts * sizeof(struct gpart)); /* Get the "current time" */ time_t ti_current = time(NULL); /* Create gparts randomly sampled from a normal distribution centred on * the middle of the box with a width. */ for (size_t i = 0; i < s->nr_gparts; i++) { gparts[i].x[0] = random_gaussian_coordinate(s->dim[0] / 2, std, s->width[0], i, ti_current, 0); gparts[i].x[1] = random_gaussian_coordinate(s->dim[1] / 2, std, s->width[1], i, ti_current, 1); gparts[i].x[2] = random_gaussian_coordinate(s->dim[2] / 2, std, s->width[2], i, ti_current, 2); gparts[i].mass = 1.0; gparts[i].type = swift_type_dark_matter; } s->gparts = gparts; /* Allocate sub cells and multipoles. */ s->cells_sub = (struct cell **)calloc(2, sizeof(struct cell *)); s->multipoles_sub = (struct gravity_tensors **)calloc(2, sizeof(struct gravity_tensors *)); } void make_cell(struct cell *c, struct space *s) { /* Allocate a multipole for this cell. */ struct gravity_tensors *m = malloc(sizeof(struct gravity_tensors)); bzero(m, sizeof(struct gravity_tensors)); c->loc[0] = s->dim[0] / 2 - s->width[0] / 2; c->loc[1] = s->dim[1] / 2 - s->width[1] / 2; c->loc[2] = s->dim[2] / 2 - s->width[2] / 2; c->width[0] = s->width[0]; c->width[1] = s->width[1]; c->width[2] = s->width[2]; c->dmin = s->width[0]; if (s->with_self_gravity) c->grav.multipole = m; c->type = cell_type_regular; c->subtype = cell_subtype_regular; c->depth = 0; c->split = 0; c->hydro.count = 0; c->grav.count = s->nr_gparts; 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 = s->e->ti_current; c->grav.ti_old_part = s->e->ti_current; c->stars.ti_old_part = s->e->ti_current; c->sinks.ti_old_part = s->e->ti_current; c->black_holes.ti_old_part = s->e->ti_current; c->grav.ti_old_multipole = s->e->ti_current; #ifdef WITH_MPI c->mpi.tag = -1; c->mpi.recv = NULL; c->mpi.send = NULL; #if (defined(HAVE_METIS) || defined(HAVE_PARMETIS)) c->nr_vertex_edges = 0; #endif #endif #if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) cell_assign_top_level_cell_index(c, s); #endif /* Attach the gparts. */ c->grav.parts = s->gparts; } void test_cell_tree(struct cell *c, struct space *s) { /* Recurse if the the cell is split. */ if (c->split) { for (int i = 0; i < 8; i++) { if (c->progeny[i] == NULL) { continue; } test_cell_tree(c->progeny[i], s); } /* Ensure the particle counts agree. */ int count = 0; for (int i = 0; i < 8; i++) { if (c->progeny[i] == NULL) { continue; } count += c->progeny[i]->grav.count; } assert(count == c->grav.count); /* Ensure the multipole counts agree (the member to test this only exists when * configured with debugging checks). */ #ifdef SWIFT_DEBUG_CHECKS int mpole_count = 0; for (int i = 0; i < 8; i++) { if (c->progeny[i] == NULL) { continue; } mpole_count += c->progeny[i]->grav.multipole->m_pole.num_gpart; } assert(mpole_count == c->grav.multipole->m_pole.num_gpart); assert(mpole_count == c->grav.count); assert(count == mpole_count); #endif } /* Nothing to check if we're in a leaf */ } 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); /* Define the standard deviation of the particle distribution with some * randomness to test different tree depths (1-33 for default RAND_MAX). The * smaller stds will result in a more concentrated particle distribution. */ const double std = 49 * ((double)rand() / RAND_MAX) + 1; /* Create a space and engine structure. */ struct space s; struct engine e; bzero(&s, sizeof(struct space)); bzero(&e, sizeof(struct engine)); make_mock_space(&s, &e, std); /* Create a cell to hold all the particles. */ struct cell *c = NULL; // malloc(sizeof(struct cell)); if (posix_memalign((void **)&c, cell_align, sizeof(struct cell)) != 0) { error("Couldn't allocate the cell"); } // message("%p",c); bzero(c, sizeof(struct cell)); make_cell(c, &s); /* Allocate the gpart buffer and set the others to NULL. */ struct cell_buff *buff = NULL, *sbuff = NULL, *bbuff = NULL, *gbuff = NULL, *sink_buff = NULL; space_allocate_and_fill_buffers(c, &buff, &sbuff, &bbuff, &gbuff, &sink_buff); /* Recursively split the cell. */ space_split_recursive(&s, c, buff, sbuff, bbuff, gbuff, sink_buff, /*tpid*/ 0); /* Free the particle buffers. */ swift_free("tempgbuff", gbuff); /* Test the cell tree. */ test_cell_tree(c, &s); /* Clean up. */ free(s.gparts); free(s.cells_sub); free(s.multipoles_sub); free(c->grav.multipole); free(c); }