/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2013- 2015: * Matthieu Schaller (schaller@strw.leidenuniv.nl), * Pedro Gonnet (pedro.gonnet@durham.ac.uk), * Peter W. Draper (p.w.draper@durham.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. */ /* This object's header. */ #include "debug.h" /* Some standard headers. */ #include #include #include #ifdef HAVE_BACKTRACE #include #endif /* Local includes. */ #include "active.h" #include "black_holes_debug.h" #include "cell.h" #include "chemistry_debug.h" #include "cooling_debug.h" #include "engine.h" #include "feedback_debug.h" #include "hydro.h" #include "inline.h" #include "mhd.h" #include "part.h" #include "particle_splitting.h" #include "pressure_floor_debug.h" #include "sink_debug.h" #include "space.h" #include "star_formation_debug.h" #include "tracers_debug.h" /* Import the right hydro definition */ #if defined(NONE_SPH) #include "./hydro/None/hydro_debug.h" #elif defined(MINIMAL_SPH) #include "./hydro/Minimal/hydro_debug.h" #elif defined(GADGET2_SPH) #include "./hydro/Gadget2/hydro_debug.h" #elif defined(HOPKINS_PE_SPH) #include "./hydro/PressureEntropy/hydro_debug.h" #elif defined(HOPKINS_PU_SPH) #include "./hydro/PressureEnergy/hydro_debug.h" #elif defined(HOPKINS_PU_SPH_MONAGHAN) #include "./hydro/PressureEnergyMorrisMonaghanAV/hydro_debug.h" #elif defined(PHANTOM_SPH) #include "./hydro/Phantom/hydro_debug.h" #elif defined(GIZMO_MFV_SPH) || defined(GIZMO_MFM_SPH) #include "./hydro/Gizmo/hydro_debug.h" #elif defined(SHADOWSWIFT) #include "./hydro/Shadowswift/hydro_debug.h" #elif defined(PLANETARY_SPH) #include "./hydro/Planetary/hydro_debug.h" #elif defined(SPHENIX_SPH) #include "./hydro/SPHENIX/hydro_debug.h" #elif defined(GASOLINE_SPH) #include "./hydro/Gasoline/hydro_debug.h" #elif defined(ANARCHY_PU_SPH) #include "./hydro/AnarchyPU/hydro_debug.h" #else #error "Invalid choice of SPH variant" #endif /* Import the right MHD definition */ #if defined(NONE_MHD) #include "./mhd/None/mhd_debug.h" #else #error "Invalid choice of MHD variant" #endif /* Import the right gravity definition */ #if defined(DEFAULT_GRAVITY) #include "./gravity/Default/gravity_debug.h" #elif defined(POTENTIAL_GRAVITY) #include "./gravity/Potential/gravity_debug.h" #elif defined(MULTI_SOFTENING_GRAVITY) #include "./gravity/MultiSoftening/gravity_debug.h" #else #error "Invalid choice of gravity variant" #endif /** * @brief Looks for the particle with the given id and prints its information to *the standard output. * * @param parts The array of particles. * @param xparts The array of particle extended data. * @param id The id too look for. * @param N The size of the array of particles. * * (Should be used for debugging only as it runs in O(N).) */ void printParticle(const struct part *parts, const struct xpart *xparts, long long int id, size_t N) { int found = 0; /* Look for the particle. */ for (size_t i = 0; i < N; i++) if (parts[i].id == id) { warning("[PID%lld] ## Particle[%zu]:\n id=%lld ", parts[i].id, i, parts[i].id); hydro_debug_particle(&parts[i], &xparts[i]); mhd_debug_particle(&parts[i], &xparts[i]); chemistry_debug_particle(&parts[i], &xparts[i]); cooling_debug_particle(&parts[i], &xparts[i]); particle_splitting_debug_particle(&parts[i], &xparts[i]); tracers_debug_particle(&parts[i], &xparts[i]); star_formation_debug_particle(&parts[i], &xparts[i]); feedback_debug_particle(&parts[i], &xparts[i]); black_holes_debug_particle(&parts[i], &xparts[i]); sink_debug_particle(&parts[i], &xparts[i]); pressure_floor_debug_particle(&parts[i], &xparts[i]); found = 1; break; } if (!found) printf("## Particles[???] id=%lld not found\n", id); } /** * @brief Looks for the g-particle with the given id and prints its information * to * the standard output. * * @param gparts The array of g-particles. * @param parts The array of particles. * @param id The id too look for. * @param N The size of the array of g-particles. * * (Should be used for debugging only as it runs in O(N).) */ void printgParticle(const struct gpart *gparts, const struct part *parts, long long int id, size_t N) { int found = 0; /* Look for the particle. */ for (size_t i = 0; i < N; i++) if (gparts[i].id_or_neg_offset == id) { printf("## gParticle[%zu] (DM) :\n id=%lld", i, id); gravity_debug_particle(&gparts[i]); found = 1; break; } else if (gparts[i].id_or_neg_offset < 0 && parts[-gparts[i].id_or_neg_offset].id == id) { printf("## gParticle[%zu] (hydro) :\n id=%lld", i, id); gravity_debug_particle(&gparts[i]); found = 1; break; } if (!found) printf("## Particles[???] id=%lld not found\n", id); } /** * @brief Prints the details of a given particle to stdout * * @param p The particle to print * @param xp The extended data ot the particle to print */ void printParticle_single(const struct part *p, const struct xpart *xp) { warning("[PID%lld] ## Particle: id=%lld ", p->id, p->id); hydro_debug_particle(p, xp); mhd_debug_particle(p, xp); chemistry_debug_particle(p, xp); cooling_debug_particle(p, xp); particle_splitting_debug_particle(p, xp); tracers_debug_particle(p, xp); star_formation_debug_particle(p, xp); feedback_debug_particle(p, xp); black_holes_debug_particle(p, xp); sink_debug_particle(p, xp); pressure_floor_debug_particle(p, xp); if (xp == NULL) { warning("[PID%lld] No xpart data available.", p->id); } } /** * @brief Prints the details of a given particle to stdout * * @param gp The g-particle to print */ void printgParticle_single(struct gpart *gp) { printf("## g-Particle: id=%lld ", gp->id_or_neg_offset); gravity_debug_particle(gp); printf("\n"); } /** * @brief Check that the cells and particles of a space have consistent h_max * values. * * @param s the space. * @result 1 or 0 */ int checkSpacehmax(struct space *s) { /* Loop over local cells. */ float cell_h_max = 0.0f; for (int k = 0; k < s->nr_cells; k++) { if (s->cells_top[k].nodeID == s->e->nodeID && s->cells_top[k].hydro.h_max > cell_h_max) { cell_h_max = s->cells_top[k].hydro.h_max; } } float cell_stars_h_max = 0.0f; for (int k = 0; k < s->nr_cells; k++) { if (s->cells_top[k].nodeID == s->e->nodeID && s->cells_top[k].stars.h_max > cell_stars_h_max) { cell_stars_h_max = s->cells_top[k].stars.h_max; } } float cell_sinks_h_max = 0.0f; for (int k = 0; k < s->nr_cells; k++) { if (s->cells_top[k].nodeID == s->e->nodeID && s->cells_top[k].sinks.h_max > cell_sinks_h_max) { cell_sinks_h_max = s->cells_top[k].sinks.h_max; } } /* Now all particles. */ float part_h_max = 0.0f; for (size_t k = 0; k < s->nr_parts; k++) { if (s->parts[k].h > part_h_max) { part_h_max = s->parts[k].h; } } /* Now all the sparticles. */ float spart_h_max = 0.0f; for (size_t k = 0; k < s->nr_sparts; k++) { if (s->sparts[k].h > spart_h_max) { spart_h_max = s->sparts[k].h; } } /* Now all the sinks. */ float sink_h_max = 0.0f; for (size_t k = 0; k < s->nr_sinks; k++) { if (s->sinks[k].h > sink_h_max) { sink_h_max = s->sinks[k].h; } } /* If within some epsilon we are OK. */ if (fabsf(cell_h_max - part_h_max) <= FLT_EPSILON && fabsf(cell_stars_h_max - spart_h_max) <= FLT_EPSILON && fabsf(cell_sinks_h_max - sink_h_max) <= FLT_EPSILON) return 1; /* There is a problem. Hunt it down. */ /* part */ for (int k = 0; k < s->nr_cells; k++) { if (s->cells_top[k].nodeID == s->e->nodeID) { if (s->cells_top[k].hydro.h_max > part_h_max) { message("cell %d is inconsistent (%f > %f)", k, s->cells_top[k].hydro.h_max, part_h_max); } } } for (size_t k = 0; k < s->nr_parts; k++) { if (s->parts[k].h > cell_h_max) { message("part %lld is inconsistent (%f > %f)", s->parts[k].id, s->parts[k].h, cell_h_max); } } /* spart */ for (int k = 0; k < s->nr_cells; k++) { if (s->cells_top[k].nodeID == s->e->nodeID) { if (s->cells_top[k].stars.h_max > spart_h_max) { message("cell %d is inconsistent (%f > %f)", k, s->cells_top[k].stars.h_max, spart_h_max); } } } for (size_t k = 0; k < s->nr_sparts; k++) { if (s->sparts[k].h > cell_stars_h_max) { message("spart %lld is inconsistent (%f > %f)", s->sparts[k].id, s->sparts[k].h, cell_stars_h_max); } } /* sink */ for (int k = 0; k < s->nr_cells; k++) { if (s->cells_top[k].nodeID == s->e->nodeID) { if (s->cells_top[k].sinks.h_max > sink_h_max) { message("cell %d is inconsistent (%f > %f)", k, s->cells_top[k].sinks.h_max, sink_h_max); } } } for (size_t k = 0; k < s->nr_sinks; k++) { if (s->sinks[k].h > cell_sinks_h_max) { message("spart %lld is inconsistent (%f > %f)", s->sinks[k].id, s->sinks[k].h, cell_sinks_h_max); } } return 0; } /** * @brief Check if the h_max and dx_max values of a cell's hierarchy are * consistent with the particles. Also checks if particles are correctly * in a cell. Report verbosely if not. * * @param c the top cell of the hierarchy. * @param depth the recursion depth for use in messages. Set to 0 initially. * @result 1 or 0 */ int checkCellhdxmax(const struct cell *c, int *depth) { *depth = *depth + 1; float h_max = 0.0f; float dx_max = 0.0f; float stars_h_max = 0.0f; float stars_dx_max = 0.0f; float sinks_h_max = 0.0f; float sinks_dx_max = 0.0f; int result = 1; const double loc_min[3] = {c->loc[0], c->loc[1], c->loc[2]}; const double loc_max[3] = {c->loc[0] + c->width[0], c->loc[1] + c->width[1], c->loc[2] + c->width[2]}; const size_t nr_parts = c->hydro.count; struct part *parts = c->hydro.parts; struct xpart *xparts = c->hydro.xparts; for (size_t k = 0; k < nr_parts; k++) { struct part *const p = &parts[k]; struct xpart *const xp = &xparts[k]; if (p->x[0] < loc_min[0] || p->x[0] >= loc_max[0] || p->x[1] < loc_min[1] || p->x[1] >= loc_max[1] || p->x[2] < loc_min[2] || p->x[2] >= loc_max[2]) { message( "Inconsistent part position p->x=[%e %e %e], c->loc=[%e %e %e] " "c->width=[%e %e %e]", p->x[0], p->x[1], p->x[2], c->loc[0], c->loc[1], c->loc[2], c->width[0], c->width[1], c->width[2]); result = 0; } const float dx2 = xp->x_diff[0] * xp->x_diff[0] + xp->x_diff[1] * xp->x_diff[1] + xp->x_diff[2] * xp->x_diff[2]; h_max = max(h_max, p->h); dx_max = max(dx_max, sqrtf(dx2)); } const size_t nr_sparts = c->stars.count; struct spart *sparts = c->stars.parts; for (size_t k = 0; k < nr_sparts; k++) { struct spart *const sp = &sparts[k]; if (sp->x[0] < loc_min[0] || sp->x[0] >= loc_max[0] || sp->x[1] < loc_min[1] || sp->x[1] >= loc_max[1] || sp->x[2] < loc_min[2] || sp->x[2] >= loc_max[2]) { message( "Inconsistent spart position p->x=[%e %e %e], c->loc=[%e %e %e] " "c->width=[%e %e %e]", sp->x[0], sp->x[1], sp->x[2], c->loc[0], c->loc[1], c->loc[2], c->width[0], c->width[1], c->width[2]); result = 0; } const float dx2 = sp->x_diff[0] * sp->x_diff[0] + sp->x_diff[1] * sp->x_diff[1] + sp->x_diff[2] * sp->x_diff[2]; stars_h_max = max(stars_h_max, sp->h); stars_dx_max = max(stars_dx_max, sqrtf(dx2)); } const size_t nr_sinks = c->sinks.count; struct sink *sinks = c->sinks.parts; for (size_t k = 0; k < nr_sinks; k++) { struct sink *const sp = &sinks[k]; if (sp->x[0] < loc_min[0] || sp->x[0] >= loc_max[0] || sp->x[1] < loc_min[1] || sp->x[1] >= loc_max[1] || sp->x[2] < loc_min[2] || sp->x[2] >= loc_max[2]) { message( "Inconsistent sink position p->x=[%e %e %e], c->loc=[%e %e %e] " "c->width=[%e %e %e]", sp->x[0], sp->x[1], sp->x[2], c->loc[0], c->loc[1], c->loc[2], c->width[0], c->width[1], c->width[2]); result = 0; } const float dx2 = sp->x_diff[0] * sp->x_diff[0] + sp->x_diff[1] * sp->x_diff[1] + sp->x_diff[2] * sp->x_diff[2]; sinks_h_max = max(sinks_h_max, sp->h); sinks_dx_max = max(sinks_dx_max, sqrtf(dx2)); } if (c->split) { for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { struct cell *cp = c->progeny[k]; checkCellhdxmax(cp, depth); } } } /* Check. */ if (c->hydro.h_max != h_max) { message("%d Inconsistent h_max: cell %f != parts %f", *depth, c->hydro.h_max, h_max); message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]); result = 0; } if (c->hydro.dx_max_part != dx_max) { message("%d Inconsistent dx_max: %f != %f", *depth, c->hydro.dx_max_part, dx_max); message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]); result = 0; } if (c->stars.h_max != stars_h_max) { message("%d Inconsistent stars_h_max: cell %f != parts %f", *depth, c->stars.h_max, stars_h_max); message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]); result = 0; } if (c->stars.dx_max_part != stars_dx_max) { message("%d Inconsistent stars_dx_max: %f != %f", *depth, c->stars.dx_max_part, stars_dx_max); message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]); result = 0; } if (c->sinks.h_max != sinks_h_max) { message("%d Inconsistent sinks_h_max: cell %f != parts %f", *depth, c->sinks.h_max, sinks_h_max); message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]); result = 0; } if (c->sinks.dx_max_part != sinks_dx_max) { message("%d Inconsistent stars_dx_max: %f != %f", *depth, c->sinks.dx_max_part, sinks_dx_max); message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]); result = 0; } return result; } /** * @brief map function for dumping cells. */ static void dumpCells_map(struct cell *c, void *data) { size_t *ldata = (size_t *)data; FILE *file = (FILE *)ldata[0]; struct engine *e = (struct engine *)ldata[1]; int super = (int)ldata[2]; int active = (int)ldata[3]; int mpiactive = (int)ldata[4]; int pactive = (int)ldata[5]; /* Only cells with particles are dumped. */ if (c->hydro.count > 0 || c->grav.count > 0 || c->stars.count > 0) { /* In MPI mode we may only output cells with foreign partners. * These define the edges of the partitions. */ int ismpiactive = 0; #if WITH_MPI ismpiactive = (c->mpi.send != NULL); if (mpiactive) mpiactive = ismpiactive; else mpiactive = 1; #else mpiactive = 1; #endif /* Active cells, otherwise all. */ if (active) active = cell_is_active_hydro(c, e); else active = 1; /* So output local super cells or top-level cells that are active and have * MPI * tasks as requested. */ if (c->nodeID == e->nodeID && (!super || ((super && c->super == c) || (c->parent == NULL))) && active && mpiactive) { /* The c->nr_tasks field does not include all the tasks. So let's check * this the hard way. Note pairs share the task 50/50 with the other * cell. Also accumulate all the time used by tasks of this cell and * form some idea of the effective task depth. */ float ntasks = 0.0f; struct task *tasks = e->sched.tasks; int nr_tasks = e->sched.nr_tasks; double ticsum = 0.0; /* Sum of work for this cell. */ double dsum = 0.0; for (int k = 0; k < nr_tasks; k++) { if (tasks[k].cj == NULL) { if (tasks[k].ci != NULL) { if (c == tasks[k].ci || c == tasks[k].ci->super) { ntasks = ntasks + 1.0f; ticsum += (tasks[k].toc - tasks[k].tic); dsum += tasks[k].ci->depth; } } } else { if (c == tasks[k].ci || c == tasks[k].ci->super || c == tasks[k].cj || c == tasks[k].cj->super) { ntasks = ntasks + 0.5f; ticsum += 0.5 * (tasks[k].toc - tasks[k].tic); if (tasks[k].ci != NULL) dsum += (tasks[k].ci->depth * 0.5); dsum += (tasks[k].cj->depth * 0.5); } } } dsum /= (double)ntasks; /* If requested we work out how many particles are active in this cell. */ int pactcount = 0; if (pactive) { const struct part *parts = c->hydro.parts; for (int k = 0; k < c->hydro.count; k++) if (part_is_active(&parts[k], e)) pactcount++; struct gpart *gparts = c->grav.parts; for (int k = 0; k < c->grav.count; k++) if (gpart_is_active(&gparts[k], e)) pactcount++; struct spart *sparts = c->stars.parts; for (int k = 0; k < c->stars.count; k++) if (spart_is_active(&sparts[k], e)) pactcount++; } fprintf( file, " %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6d %6d %6d %6d %6d %6d %6d " "%6.1f %20lld %6d %6d %6d %6d %6d %6d %6d %f %f\n", c->loc[0], c->loc[1], c->loc[2], c->width[0], c->width[1], c->width[2], e->step, c->hydro.count, c->grav.count, c->stars.count, pactcount, c->depth, c->maxdepth, ntasks, c->hydro.ti_end_min, get_time_bin(c->hydro.ti_end_min), (c->super == c), (c->parent == NULL), cell_is_active_hydro(c, e), c->nodeID, c->nodeID == e->nodeID, ismpiactive, ticsum, dsum); } } } /** * @brief Dump the location, depth, task counts and timebins and active state, * for all cells to a simple text file. A more costly count of the active * particles in a cell can also be output. * * @param prefix base output filename, result is written to * %prefix%_%rank%_%step%.dat * @param super just output the super cells. * @param active just output active cells. * @param mpiactive just output MPI active cells, i.e. those with foreign cells. * @param pactive also output a count of active particles. * @param s the space holding the cells to dump. * @param rank node ID of MPI rank, or 0 if not relevant. * @param step the current engine step, or some unique integer. */ void dumpCells(const char *prefix, int super, int active, int mpiactive, int pactive, struct space *s, int rank, int step) { FILE *file = NULL; /* Name of output file. */ char fname[200]; sprintf(fname, "%s_%03d_%03d.dat", prefix, rank, step); file = fopen(fname, "w"); if (file == NULL) error("Could not create file '%s'.", fname); /* Header. */ fprintf(file, "# %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s " "%20s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", "x", "y", "z", "xw", "yw", "zw", "step", "count", "gcount", "scount", "actcount", "depth", "maxdepth", "tasks", "ti_end_min", "timebin", "issuper", "istop", "active", "rank", "local", "mpiactive", "ticsum", "avedepth"); size_t data[6]; data[0] = (size_t)file; data[1] = (size_t)s->e; data[2] = (size_t)super; data[3] = (size_t)active; data[4] = (size_t)mpiactive; data[5] = (size_t)pactive; space_map_cells_pre(s, 1, dumpCells_map, &data); fclose(file); } #if defined(WITH_MPI) && (defined(HAVE_METIS) || defined(HAVE_PARMETIS)) /** * @brief Dump a graph in METIS standard format, simple format and weights * only, to a file. * * The standard format output can be read into the METIS and some ParMETIS * command-line tools. The simple format is just the cell connectivity (this * should not change between calls). The weights format is the standard one, * minus the cell connectivity. * * The output filenames are generated from the prefix and the sequence number * of calls. So the first is called {prefix}_std_001.dat, *{prefix}_simple_001.dat, * {prefix}_weights_001.dat, etc. * * @param prefix base output filename * @param nvertices the number of vertices * @param nvertexweights the number vertex weights * @param cellconruns first part of cell connectivity info (CSR) * @param cellcon second part of cell connectivity info (CSR) * @param vertexweights weights of vertices * @param vertexsizes size of vertices * @param edgeweights weights of edges */ void dumpMETISGraph(const char *prefix, idx_t nvertices, idx_t nvertexweights, idx_t *cellconruns, idx_t *cellcon, idx_t *vertexweights, idx_t *vertexsizes, idx_t *edgeweights) { FILE *stdfile = NULL; FILE *simplefile = NULL; FILE *weightfile = NULL; char fname[200]; int haveedgeweight = 0; int havevertexsize = 0; int havevertexweight = 0; static int nseq = 0; nseq++; if (vertexweights != NULL) { for (idx_t i = 0; i < nvertices * nvertexweights; i++) { if (vertexweights[i] != 1) { havevertexweight = 1; break; } } } if (vertexsizes != NULL) { for (idx_t i = 0; i < nvertices; i++) { if (vertexsizes[i] != 1) { havevertexsize = 1; break; } } } if (edgeweights != NULL) { for (idx_t i = 0; i < cellconruns[nvertices]; i++) { if (edgeweights[i] != 1) { haveedgeweight = 1; break; } } } /* Open output files. */ sprintf(fname, "%s_std_%03d.dat", prefix, nseq); stdfile = fopen(fname, "w"); if (stdfile == NULL) error("Could not create file '%s'.", fname); sprintf(fname, "%s_simple_%03d.dat", prefix, nseq); simplefile = fopen(fname, "w"); if (simplefile == NULL) error("Could not create file '%s'.", fname); if (havevertexweight || havevertexsize || haveedgeweight) { sprintf(fname, "%s_weights_%03d.dat", prefix, nseq); weightfile = fopen(fname, "w"); if (weightfile == NULL) error("Could not create file '%s'.", fname); } /* Write the header lines. */ fprintf(stdfile, "%" PRIDX " %" PRIDX, nvertices, cellconruns[nvertices] / 2); fprintf(simplefile, "%" PRIDX " %" PRIDX, nvertices, cellconruns[nvertices] / 2); if (havevertexweight || havevertexsize || haveedgeweight) { fprintf(weightfile, "%" PRIDX " %" PRIDX, nvertices, cellconruns[nvertices] / 2); fprintf(stdfile, " %d%d%d", havevertexsize, havevertexweight, haveedgeweight); fprintf(weightfile, " %d%d%d", havevertexsize, havevertexweight, haveedgeweight); if (havevertexweight) { fprintf(stdfile, " %d", (int)nvertexweights); fprintf(weightfile, " %d", (int)nvertexweights); } } /* Write the rest of the graph. */ for (idx_t i = 0; i < nvertices; i++) { fprintf(stdfile, "\n"); fprintf(simplefile, "\n"); if (weightfile != NULL) { fprintf(weightfile, "\n"); } if (havevertexsize) { fprintf(stdfile, " %" PRIDX, vertexsizes[i]); fprintf(weightfile, " %" PRIDX, vertexsizes[i]); } if (havevertexweight) { for (idx_t j = 0; j < nvertexweights; j++) { fprintf(stdfile, " %" PRIDX, vertexweights[i * nvertexweights + j]); fprintf(weightfile, " %" PRIDX, vertexweights[i * nvertexweights + j]); } } for (idx_t j = cellconruns[i]; j < cellconruns[i + 1]; j++) { fprintf(stdfile, " %" PRIDX, cellcon[j] + 1); fprintf(simplefile, " %" PRIDX, cellcon[j] + 1); if (haveedgeweight) { fprintf(stdfile, " %" PRIDX, edgeweights[j]); fprintf(weightfile, " %" PRIDX, edgeweights[j]); } } } fprintf(stdfile, "\n"); fprintf(simplefile, "\n"); if (weightfile != NULL) { fprintf(weightfile, "\n"); } fclose(stdfile); fclose(simplefile); if (weightfile != NULL) { fclose(weightfile); } } #endif /* HAVE_METIS || HAVE_PARMETIS */ #ifdef HAVE_MPI /** * @brief Dump the positions and MPI ranks of the given top-level cells * to a simple text file. * * Can be used to visualise the partitioning of an MPI run. Note should * be used immediately after repartitioning when the top-level cells * have been assigned their nodes. Each time this is called a new file * with the given prefix, a unique integer and type of .dat is created. * * @param prefix base output filename * @param cells_top the top-level cells. * @param nr_cells the number of cells. */ void dumpCellRanks(const char *prefix, struct cell *cells_top, int nr_cells) { FILE *file = NULL; /* Name of output file. */ static int nseq = 0; char fname[200]; sprintf(fname, "%s_%03d.dat", prefix, nseq); nseq++; file = fopen(fname, "w"); if (file == NULL) error("Could not create file '%s'.", fname); /* Header. */ fprintf(file, "# %6s %6s %6s %6s %6s %6s %6s\n", "x", "y", "z", "xw", "yw", "zw", "rank"); /* Output */ for (int i = 0; i < nr_cells; i++) { struct cell *c = &cells_top[i]; fprintf(file, " %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6d\n", c->loc[0], c->loc[1], c->loc[2], c->width[0], c->width[1], c->width[2], c->nodeID); } fclose(file); } #endif /* HAVE_MPI */ /** * @brief Output a backtrace of the current calling stack. * * Requires the glibc extension backtrace(). * * @param description some string to output along with the stack. */ void print_backtrace(const char *description) { #ifdef HAVE_BACKTRACE message("%s", description); /* Boiler plate from the man page. */ void *buffer[100]; int nptrs = backtrace(buffer, 100); char **strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { perror("backtrace_symbols"); } else { for (int j = 0; j < nptrs; j++) message("%s", strings[j]); } #endif }