diff --git a/examples/main.c b/examples/main.c index ad28d16c25024f07f1f8d3433bc35c14fa24a426..37974c652a412d5ef5b152214c99ba4b96102640 100644 --- a/examples/main.c +++ b/examples/main.c @@ -418,6 +418,8 @@ int main(int argc, char *argv[]) { parser_get_param_string(params, "InitialConditions:file_name", ICfileName); const int replicate = parser_get_opt_param_int(params, "InitialConditions:replicate", 1); + const int clean_h_values = + parser_get_opt_param_int(params, "InitialConditions:cleanup_h", 0); if (myrank == 0) message("Reading ICs from file '%s'", ICfileName); fflush(stdout); @@ -619,7 +621,7 @@ int main(int argc, char *argv[]) { #endif /* Initialise the particles */ - engine_init_particles(&e, flag_entropy_ICs); + engine_init_particles(&e, flag_entropy_ICs, clean_h_values); /* Write the state of the system before starting time integration. */ engine_dump_snapshot(&e); diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index 353f0dbe34ebe14a057e877de289b54516a043fc..97e7948317387356d4eed5dbc3b9acf673486fba 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -60,6 +60,7 @@ Gravity: # Parameters related to the initial conditions InitialConditions: file_name: SedovBlast/sedov.hdf5 # The file to read + cleanup_h: 0 # (Optional) Clean the values of h that are read in. Set to 1 to activate. h_scaling: 1. # (Optional) A scaling factor to apply to all smoothing lengths in the ICs. shift_x: 0. # (Optional) A shift to apply to all particles read from the ICs (in internal units). shift_y: 0. diff --git a/src/cell.c b/src/cell.c index dbccfd2f42cabf38417cd87de0450489240884be..15ea31bef8e9219f47d4a060474deec55f54f0a1 100644 --- a/src/cell.c +++ b/src/cell.c @@ -941,53 +941,52 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, * @brief Sanitizes the smoothing length values of cells by setting large * outliers to more sensible values. * - * We compute the mean and standard deviation of the smoothing lengths in - * logarithmic space and limit values to mean + 4 sigma. + * Each cell with <1000 part will be processed. We limit h to be the size of + * the cell and replace 0s with a good estimate. * * @param c The cell. + * @param treated Has the cell already been sanitized at this level ? */ -void cell_sanitize(struct cell *c) { +void cell_sanitize(struct cell *c, int treated) { const int count = c->count; struct part *parts = c->parts; + float h_max = 0.f; - /* First collect some statistics */ - float h_mean = 0.f, h_mean2 = 0.f; - float h_min = FLT_MAX, h_max = 0.f; - for (int i = 0; i < count; ++i) { + /* Treat cells will <1000 particles */ + if (count < 1000 && !treated) { - const float h = logf(parts[i].h); - h_mean += h; - h_mean2 += h * h; - h_max = max(h_max, h); - h_min = min(h_min, h); - } - h_mean /= count; - h_mean2 /= count; - const float h_var = h_mean2 - h_mean * h_mean; - const float h_std = (h_var > 0.f) ? sqrtf(h_var) : 0.1f * h_mean; - - /* Choose a cut */ - const float h_limit = expf(h_mean + 4.f * h_std); - - /* Be verbose this is not innocuous */ - message("Cell properties: h_min= %f h_max= %f geometric mean= %f.", - expf(h_min), expf(h_max), expf(h_mean)); + /* Get an upper bound on h */ + const float upper_h_max = c->dmin / (1.2f * kernel_gamma); - if (c->h_max > h_limit) { + /* Apply it */ + for (int i = 0; i < count; ++i) { + if (parts[i].h == 0.f || parts[i].h > upper_h_max) + parts[i].h = upper_h_max; + } + } - message("Smoothing lengths will be limited to (mean + 4sigma)= %f.", - h_limit); + /* Recurse and gather the new h_max values */ + if (c->split) { - /* Apply the cut */ - for (int i = 0; i < count; ++i) parts->h = min(parts[i].h, h_limit); + for (int k = 0; k < 8; ++k) { + if (c->progeny[k] != NULL) { - c->h_max = h_limit; + /* Recurse */ + cell_sanitize(c->progeny[k], (count < 1000)); + /* And collect */ + h_max = max(h_max, c->progeny[k]->h_max); + } + } } else { - message("Smoothing lengths will not be limited."); + /* Get the new value of h_max */ + for (int i = 0; i < count; ++i) h_max = max(h_max, parts[i].h); } + + /* Record the change */ + c->h_max = h_max; } /** diff --git a/src/cell.h b/src/cell.h index 2e32533402110040310be88629d0fb33f0128c62..178f77592543ed1a617d76d3d9fae91079395353 100644 --- a/src/cell.h +++ b/src/cell.h @@ -344,7 +344,7 @@ struct cell { void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, struct cell_buff *buff, struct cell_buff *sbuff, struct cell_buff *gbuff); -void cell_sanitize(struct cell *c); +void cell_sanitize(struct cell *c, int treated); int cell_locktree(struct cell *c); void cell_unlocktree(struct cell *c); int cell_glocktree(struct cell *c); diff --git a/src/engine.c b/src/engine.c index 417c9f626d7e2f8d96d49d8d2bed942102b96e4f..66170b83ebfc814f562008ddcfe1eea8a98d8c0f 100644 --- a/src/engine.c +++ b/src/engine.c @@ -2790,8 +2790,10 @@ void engine_print_task_counts(struct engine *e) { * @brief Rebuild the space and tasks. * * @param e The #engine. + * @param clean_h_values Are we cleaning up the values of h before building + * the tasks ? */ -void engine_rebuild(struct engine *e) { +void engine_rebuild(struct engine *e, int clean_h_values) { const ticks tic = getticks(); @@ -2802,7 +2804,7 @@ void engine_rebuild(struct engine *e) { space_rebuild(e->s, e->verbose); /* Initial cleaning up session ? */ - if (e->s->sanitized == 0) space_sanitize(e->s); + if (clean_h_values) space_sanitize(e->s); /* If in parallel, exchange the cell structure. */ #ifdef WITH_MPI @@ -2856,7 +2858,7 @@ void engine_prepare(struct engine *e) { if (e->forcerepart) engine_repartition(e); /* Do we need rebuilding ? */ - if (e->forcerebuild) engine_rebuild(e); + if (e->forcerebuild) engine_rebuild(e, 0); /* Unskip active tasks and check for rebuild */ engine_unskip(e); @@ -3218,9 +3220,12 @@ void engine_launch(struct engine *e, int nr_runners) { * * @param e The #engine * @param flag_entropy_ICs Did the 'Internal Energy' of the particles actually - *contain entropy ? + * contain entropy ? + * @param clean_h_values Are we cleaning up the values of h before building + * the tasks ? */ -void engine_init_particles(struct engine *e, int flag_entropy_ICs) { +void engine_init_particles(struct engine *e, int flag_entropy_ICs, + int clean_h_values) { struct space *s = e->s; @@ -3237,7 +3242,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs) { } /* Construct all cells and tasks to start everything */ - engine_rebuild(e); + engine_rebuild(e, clean_h_values); /* No time integration. We just want the density and ghosts */ engine_skip_force_and_kick(e); diff --git a/src/engine.h b/src/engine.h index e62b12332d3ac1b985b8f6d7181ea66824ec4f13..d621f4f6fba3880811980a45c5a6b43e3009461f 100644 --- a/src/engine.h +++ b/src/engine.h @@ -272,7 +272,8 @@ void engine_init(struct engine *e, struct space *s, struct sourceterms *sourceterms); void engine_launch(struct engine *e, int nr_runners); void engine_prepare(struct engine *e); -void engine_init_particles(struct engine *e, int flag_entropy_ICs); +void engine_init_particles(struct engine *e, int flag_entropy_ICs, + int clean_h_values); void engine_step(struct engine *e); void engine_maketasks(struct engine *e); void engine_split(struct engine *e, struct partition *initial_partition); @@ -281,7 +282,7 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts, int *ind_gpart, size_t *Ngpart, size_t offset_sparts, int *ind_spart, size_t *Nspart); -void engine_rebuild(struct engine *e); +void engine_rebuild(struct engine *e, int clean_h_values); void engine_repartition(struct engine *e); void engine_repartition_trigger(struct engine *e); void engine_makeproxies(struct engine *e); diff --git a/src/space.c b/src/space.c index b1612876b6339fb29648a87e9aec93a1d8f64664..5d7e306ebe331d449daf607a3405712b4f42f4b1 100644 --- a/src/space.c +++ b/src/space.c @@ -962,28 +962,33 @@ void space_split(struct space *s, struct cell *cells, int nr_cells, } /** - * @brief Runs through the top-level cells and checks whether tasks associated - * with them can be split. If not, try to sanitize the cells. + * @brief #threadpool mapper function to sanitize the cells * - * @param s The #space to act upon. + * @param map_data Pointers towards the top-level cells. + * @param num_cells The number of top-level cells. + * @param extra_data Unused parameters. */ -void space_sanitize(struct space *s) { - - s->sanitized = 1; +void space_sanitize_mapper(void *map_data, int num_cells, void *extra_data) { + /* Unpack the inputs. */ + struct cell *cells_top = (struct cell *)map_data; - for (int k = 0; k < s->nr_cells; k++) { + for (int ind = 0; ind < num_cells; ind++) { + struct cell *c = &cells_top[ind]; + cell_sanitize(c, 0); + } +} - struct cell *c = &s->cells_top[k]; - const double min_width = c->dmin; +/** + * @brief Runs through the top-level cells and sanitize their h values + * + * @param s The #space to act upon. + */ +void space_sanitize(struct space *s) { - /* Do we have a problem ? */ - if (c->h_max * kernel_gamma * space_stretch > min_width * 0.5 && - c->count > space_maxcount) { + if (s->e->nodeID == 0) message("Cleaning up unreasonable values of h"); - /* Ok, clean-up the mess */ - cell_sanitize(c); - } - } + threadpool_map(&s->e->threadpool, space_sanitize_mapper, s->cells_top, + s->nr_cells, sizeof(struct cell), 1, NULL); } /** @@ -2630,7 +2635,6 @@ void space_init(struct space *s, const struct swift_params *params, s->dim[0] = dim[0]; s->dim[1] = dim[1]; s->dim[2] = dim[2]; - s->sanitized = 0; s->periodic = periodic; s->gravity = gravity; s->nr_parts = Npart; diff --git a/src/space.h b/src/space.h index e8e8600349c97ff8a60f0fcf2964d6ec514a7589..5c26f359e15b757b66519d958c595f670c5d6990 100644 --- a/src/space.h +++ b/src/space.h @@ -139,9 +139,6 @@ struct space { /*! Number of queues in the system. */ int nr_queues; - /*! Has this space already been sanitized ? */ - int sanitized; - /*! The associated engine. */ struct engine *e;