/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2021 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) * * 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 . * ******************************************************************************/ #ifndef SWIFT_GHOST_STATS_H #define SWIFT_GHOST_STATS_H /* Config parameters. */ #include "minmax.h" #include "part.h" #include #include #include #ifdef SWIFT_GHOST_STATS /***************************** * ghost_stats functionality * *****************************/ /** * @brief Entry for a single iteration in the ghost statistics table. */ struct ghost_stats_entry { /*! Number of particles processed during the iteration. */ int count; /*! Number of particles in the iteration that had no neighbours. */ int count_no_ngb; /*! Minimum initial smoothing length of the particles at the start of the * iteration. */ float hmin; /*! Maximum initial smoothing length of the particles at the start of the * iteration. */ float hmax; /*! Sum of the initial smoothing lengths, useful to compute averages in * post-processing. */ double hsum; /*! Sum of the initial smoothing lengths squared, useful to compute variances * and standard deviations in post-processing. */ double hsum2; }; /** * @brief Ghost statistics stored in a cell. */ struct ghost_stats { /* Hydro ghost statistics. */ struct ghost_stats_entry hydro[SWIFT_GHOST_STATS + 1]; /* Stars ghost statistics. */ struct ghost_stats_entry stars[SWIFT_GHOST_STATS + 1]; /* Black holes ghost statistics. */ struct ghost_stats_entry black_holes[SWIFT_GHOST_STATS + 1]; }; /* ghost_stats_entry struct functions */ /** * @brief Reset the given ghost stats bin. * * @param bin Ghost stats bin to reset. */ __attribute__((always_inline)) INLINE static void ghost_stats_reset_entry( struct ghost_stats_entry *restrict bin) { bin->count = 0; bin->count_no_ngb = 0; bin->hmin = FLT_MAX; bin->hmax = 0.0f; bin->hsum = 0.; bin->hsum2 = 0.; } /** * @brief Write the given ghost stats bin to the given file. * * @param f File to write to. * @param bin Ghost stats bin to write. */ __attribute__((always_inline)) INLINE static void ghost_stats_print_entry( FILE *f, const struct ghost_stats_entry *restrict bin) { if (bin->count > 0) { fprintf(f, "\t%i\t%i\t%g\t%g\t%g\t%g", bin->count, bin->count_no_ngb, bin->hmin, bin->hmax, bin->hsum, bin->hsum2); } else { fprintf(f, "\t0\t0\t0\t0\t0\t0"); } } /* ghost_stats struct functions */ /** * @brief Reset all the entries in the ghost_stats struct. * * @param gstats Ghost stats struct. */ __attribute__((always_inline)) INLINE static void ghost_stats_reset_entries( struct ghost_stats *restrict gstats) { for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { ghost_stats_reset_entry(&gstats->hydro[b]); } for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { ghost_stats_reset_entry(&gstats->stars[b]); } for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { ghost_stats_reset_entry(&gstats->black_holes[b]); } } /** * @brief Account for the star particles that are still under consideration at * the start of a ghost decision loop (so after the neighbour loop but before * the smoothing length is updated). * * @param gstats Ghost stats struct to update. * @param iteration_number Iteration number in the high-level ghost iteration * scheme. * @param scount Number of star particles still under consideration (smoothing * length has not converged yet or will do so during this iteration). * @param sparts Star particle array. * @param sid Indices of active star particles in the array. */ __attribute__((always_inline)) INLINE static void ghost_stats_account_for_stars( struct ghost_stats *restrict gstats, int iteration_number, int scount, struct spart *restrict sparts, int *sid) { /* accumulate in highest bin, so we can spot out-of-bounds values */ int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); struct ghost_stats_entry *restrict sbin = &gstats->stars[binidx]; sbin->count += scount; for (int i = 0; i < scount; i++) { const float hi = sparts[sid[i]].h; sbin->hmin = min(sbin->hmin, hi); sbin->hmax = max(sbin->hmax, hi); sbin->hsum += hi; sbin->hsum2 += hi * hi; } } /** * @brief Account for the properties of the a converged star particle. * * @param gstats Ghost stats struct to update. * @param sp Star particle that has a converged smoothing length. */ __attribute__((always_inline)) INLINE static void ghost_stats_converged_star( struct ghost_stats *restrict gstats, struct spart *restrict sp) { struct ghost_stats_entry *restrict sbin = &gstats->stars[SWIFT_GHOST_STATS]; ++sbin->count; const float hi = sp->h; sbin->hmin = min(sbin->hmin, hi); sbin->hmax = max(sbin->hmax, hi); sbin->hsum += hi; sbin->hsum2 += hi * hi; } /** * @brief Register the occurrence of a "no neighbour" event during the current * star iteration step. * * @param gstats Ghost stats struct to update. * @param iteration_number Number of the current iteration. */ __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_star_iteration(struct ghost_stats *restrict gstats, int iteration_number) { int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); struct ghost_stats_entry *restrict sbin = &gstats->stars[binidx]; ++sbin->count_no_ngb; } /** * @brief Register the occurrence of a converged star particle without * neighbours. * * @param gstats Ghost stats struct to update. */ __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_star_converged(struct ghost_stats *restrict gstats) { struct ghost_stats_entry *restrict sbin = &gstats->stars[SWIFT_GHOST_STATS]; ++sbin->count_no_ngb; } /** * @brief Account for the black hole particles that are still under * consideration at the start of a ghost decision loop (so after the neighbour * loop but before the smoothing length is updated). * * @param gstats Ghost stats struct to update. * @param iteration_number Iteration number in the high-level ghost iteration * scheme. * @param bcount Number of black hole particles still under consideration * (smoothing length has not converged yet or will do so during this iteration). * @param bparts Black hole particle array. * @param sid Indices of active black hole particles in the array. */ __attribute__((always_inline)) INLINE static void ghost_stats_account_for_black_holes(struct ghost_stats *restrict gstats, int iteration_number, int bcount, struct bpart *restrict bparts, int *sid) { /* accumulate in highest bin, so we can spot out-of-bounds values */ int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); struct ghost_stats_entry *restrict bbin = &gstats->black_holes[binidx]; bbin->count += bcount; for (int i = 0; i < bcount; i++) { const float hi = bparts[sid[i]].h; bbin->hmin = min(bbin->hmin, hi); bbin->hmax = max(bbin->hmax, hi); bbin->hsum += hi; bbin->hsum2 += hi * hi; } } /** * @brief Account for the properties of the a converged black hole particle. * * @param gstats Ghost stats struct to update. * @param bp Black hole particle that has a converged smoothing length. */ __attribute__((always_inline)) INLINE static void ghost_stats_converged_black_hole(struct ghost_stats *restrict gstats, struct bpart *restrict bp) { struct ghost_stats_entry *restrict bbin = &gstats->black_holes[SWIFT_GHOST_STATS]; ++bbin->count; const float hi = bp->h; bbin->hmin = min(bbin->hmin, hi); bbin->hmax = max(bbin->hmax, hi); bbin->hsum += hi; bbin->hsum2 += hi * hi; } /** * @brief Register the occurrence of a "no neighbour" event during the current * black hole iteration step. * * @param gstats Ghost stats struct to update. * @param iteration_number Number of the current iteration. */ __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_black_hole_iteration(struct ghost_stats *restrict gstats, int iteration_number) { int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); struct ghost_stats_entry *restrict bbin = &gstats->black_holes[binidx]; ++bbin->count_no_ngb; } /** * @brief Register the occurrence of a converged black hole particle without * neighbours. * * @param gstats Ghost stats struct to update. */ __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_black_hole_converged(struct ghost_stats *restrict gstats) { struct ghost_stats_entry *restrict bbin = &gstats->black_holes[SWIFT_GHOST_STATS]; ++bbin->count_no_ngb; } /** * @brief Account for the gas particles that are still under consideration at * the start of a ghost decision loop (so after the neighbour loop but before * the smoothing length is updated). * * @param gstats Ghost stats struct to update. * @param iteration_number Iteration number in the high-level ghost iteration * scheme. * @param count Number of gas particles still under consideration (smoothing * length has not converged yet or will do so during this iteration). * @param parts Gas particle array. * @param sid Indices of active gas particles in the array. */ __attribute__((always_inline)) INLINE static void ghost_stats_account_for_hydro( struct ghost_stats *restrict gstats, int iteration_number, int count, struct part *restrict parts, int *pid) { /* accumulate in highest bin, so we can spot out-of-bounds values */ int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); struct ghost_stats_entry *restrict hbin = &gstats->hydro[binidx]; hbin->count += count; for (int i = 0; i < count; i++) { const float hi = parts[pid[i]].h; hbin->hmin = min(hbin->hmin, hi); hbin->hmax = max(hbin->hmax, hi); hbin->hsum += hi; hbin->hsum2 += hi * hi; } } /** * @brief Account for the properties of the a converged gas particle. * * @param gstats Ghost stats struct to update. * @param p Gas particle that has a converged smoothing length. */ __attribute__((always_inline)) INLINE static void ghost_stats_converged_hydro( struct ghost_stats *restrict gstats, struct part *restrict p) { struct ghost_stats_entry *restrict hbin = &gstats->hydro[SWIFT_GHOST_STATS]; ++hbin->count; const float hi = p->h; hbin->hmin = min(hbin->hmin, hi); hbin->hmax = max(hbin->hmax, hi); hbin->hsum += hi; hbin->hsum2 += hi * hi; } /** * @brief Register the occurrence of a "no neighbour" event during the current * hydro iteration step. * * @param gstats Ghost stats struct to update. * @param iteration_number Number of the current iteration. */ __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_hydro_iteration(struct ghost_stats *restrict gstats, int iteration_number) { int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1); struct ghost_stats_entry *restrict hbin = &gstats->hydro[binidx]; ++hbin->count_no_ngb; } /** * @brief Register the occurrence of a converged gas particle without * neighbours. * * @param gstats Ghost stats struct to update. */ __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_hydro_converged(struct ghost_stats *restrict gstats) { struct ghost_stats_entry *restrict hbin = &gstats->hydro[SWIFT_GHOST_STATS]; ++hbin->count_no_ngb; } /** * @brief Write the header of a ghost statistics file. * * @param f File to write to. */ __attribute__((always_inline)) INLINE static void ghost_stats_write_header( FILE *f) { fprintf(f, "# Ghost statistics\n"); fprintf(f, "# Values listed in blocks per particle type\n"); fprintf(f, "# Order of types: hydro, stars, black holes\n"); fprintf(f, "# Number of blocks per type: %i\n", SWIFT_GHOST_STATS + 1); fprintf(f, "# Number of values per block: 6\n"); fprintf(f, "# Last block contains converged values\n"); fprintf(f, "# Fields per block:\n"); fprintf(f, "# - count: i4\n"); fprintf(f, "# - no neighbour count: i4\n"); fprintf(f, "# - min h: f4\n"); fprintf(f, "# - max h: f4\n"); fprintf(f, "# - sum h: f8\n"); fprintf(f, "# - sum h^2: f8\n"); fprintf(f, "# First column is cellID\n"); fprintf(f, "# Cells with no values are omitted\n"); } /** * @brief Write the ghost statistics for the given cell to the given file. * * @param f File to write to. * @param gstats Ghost statistics to write. * @param cellID Cell ID to use to identify the cell. */ __attribute__((always_inline)) INLINE static void ghost_stats_write_cell_stats( FILE *f, const struct ghost_stats *restrict gstats, const long long cellID) { if (gstats->hydro[0].count + gstats->stars[0].count > 0) { fprintf(f, "%lld", cellID); for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { ghost_stats_print_entry(f, &gstats->hydro[b]); } for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { ghost_stats_print_entry(f, &gstats->stars[b]); } for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) { ghost_stats_print_entry(f, &gstats->black_holes[b]); } fprintf(f, "\n"); } } /****************** * cell interface * ******************/ struct cell; void cell_reset_ghost_histograms(struct cell *c); void cell_write_ghost_stats(FILE *f, const struct cell *c, const long long cellID); /******************* * space interface * *******************/ struct space; void space_reset_ghost_histograms(struct space *s); void space_write_ghost_stats(const struct space *s, int j); #else struct ghost_stats {}; /* stars */ __attribute__((always_inline)) INLINE static void ghost_stats_account_for_stars( struct ghost_stats *restrict gstats, int iteration_number, int scount, struct spart *restrict sparts, int *sid) {} __attribute__((always_inline)) INLINE static void ghost_stats_converged_star( struct ghost_stats *restrict gstats, struct spart *restrict sp) {} __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_star_iteration(struct ghost_stats *restrict gstats, int iteration_number) {} __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_star_converged(struct ghost_stats *restrict gstats) {} /* black holes */ __attribute__((always_inline)) INLINE static void ghost_stats_account_for_black_holes(struct ghost_stats *restrict gstats, int iteration_number, int bcount, struct bpart *restrict bparts, int *sid) {} __attribute__((always_inline)) INLINE static void ghost_stats_converged_black_hole(struct ghost_stats *restrict gstats, struct bpart *restrict bp) {} __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_black_hole_iteration(struct ghost_stats *restrict gstats, int iteration_number) {} __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_black_hole_converged(struct ghost_stats *restrict gstats) {} /* hydro */ __attribute__((always_inline)) INLINE static void ghost_stats_account_for_hydro( struct ghost_stats *restrict gstats, int iteration_number, int count, struct part *restrict parts, int *pid) {} __attribute__((always_inline)) INLINE static void ghost_stats_converged_hydro( struct ghost_stats *restrict gstats, struct part *restrict p) {} __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_hydro_iteration(struct ghost_stats *restrict gstats, int iteration_number) {} __attribute__((always_inline)) INLINE static void ghost_stats_no_ngb_hydro_converged(struct ghost_stats *restrict gstats) {} /// cell interface struct cell; __attribute__((always_inline)) INLINE static void cell_reset_ghost_histograms( struct cell *c) {} /// space interface struct space; __attribute__((always_inline)) INLINE static void space_reset_ghost_histograms( struct space *s) {} __attribute__((always_inline)) INLINE static void space_write_ghost_stats( const struct space *s, int j) {} #endif #endif /* SWIFT_GHOST_STATS */