PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
logging.c
Go to the documentation of this file.
1// logging.c
2#include "logging.h"
5
6/* Maximum temporary buffer size for converting numbers to strings */
7#define TMP_BUF_SIZE 128
8
9// --------------------- Static Variable for Log Level ---------------------
10
11/**
12 * @brief Static variable to cache the current logging level.
13 *
14 * Initialized to -1 to indicate that the log level has not been set yet.
15 */
17
18// --------------------- Static Variables for Allow-List -------------------
19
20/**
21 * @brief Global/static array of function names allowed to log.
22 */
23static char** gAllowedFunctions = NULL;
24
25/**
26 * @brief Number of entries in the gAllowedFunctions array.
27 */
28static int gNumAllowed = 0;
29
30enum {
45};
46
47/**
48 * @brief Internal reduction callback for packed search metrics.
49 * @details Local to this translation unit.
50 */
51static void SearchMetricsReduceOp(void *invec, void *inoutvec, int *len, MPI_Datatype *datatype)
52{
53 PetscReal *in = (PetscReal *)invec;
54 PetscReal *inout = (PetscReal *)inoutvec;
55 (void)datatype;
56
57 for (int idx = 0; idx < *len; idx += SEARCH_METRIC_REDUCTION_LEN) {
71 inout[idx + SEARCH_METRIC_MAX_PASS_DEPTH] = PetscMax(inout[idx + SEARCH_METRIC_MAX_PASS_DEPTH],
73 }
74}
75
76// --------------------- Function Implementations ---------------------
77
78/**
79 * @brief Implementation of \ref get_log_level().
80 * @details Full API contract (arguments, ownership, side effects) is documented with
81 * the header declaration in `include/logging.h`.
82 * @see get_log_level()
83 */
85 if (current_log_level == -1) { // Log level not set yet
86 const char *env = getenv("LOG_LEVEL");
87 if (!env) {
88 current_log_level = LOG_ERROR; // Default level
89 }
90 else if (strcmp(env, "DEBUG") == 0) {
92 }
93 else if (strcmp(env, "INFO") == 0) {
95 }
96 else if (strcmp(env, "WARNING") == 0) {
98 }
99 else if (strcmp(env, "VERBOSE") == 0) {
101 }
102 else if (strcmp(env, "TRACE") == 0) {
104 }
105 else {
106 current_log_level = LOG_ERROR; // Default if unrecognized
107 }
108 }
109 return current_log_level;
110}
111
112/**
113 * @brief Internal helper implementation: `print_log_level()`.
114 * @details Local to this translation unit.
115 */
116PetscErrorCode print_log_level(void)
117{
118 PetscMPIInt rank;
119 PetscErrorCode ierr;
120 int level;
121 const char *level_name;
122
123 PetscFunctionBeginUser;
124 /* get MPI rank */
125 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRMPI(ierr);
126
127 /* decide level name */
128 level = get_log_level();
129 level_name = (level == LOG_ERROR) ? "ERROR" :
130 (level == LOG_WARNING) ? "WARNING" :
131 (level == LOG_INFO) ? "INFO" :
132 (level == LOG_DEBUG) ? "DEBUG" :
133 (level == LOG_VERBOSE) ? "VERBOSE" :
134 (level == LOG_TRACE) ? "TRACE" :
135 "UNKNOWN";
136
137 /* print it out */
138 ierr = PetscPrintf(PETSC_COMM_SELF,
139 "Current log level: %s (%d) | rank: %d\n",
140 level_name, level, (int)rank);
141 CHKERRMPI(ierr);
142
143 PetscFunctionReturn(PETSC_SUCCESS);
144}
145
146/**
147 * @brief Implementation of \ref set_allowed_functions().
148 * @details Full API contract (arguments, ownership, side effects) is documented with
149 * the header declaration in `include/logging.h`.
150 * @see set_allowed_functions()
151 */
152void set_allowed_functions(const char** functionList, int count)
153{
154 // 1. Free any existing entries
155 if (gAllowedFunctions) {
156 for (int i = 0; i < gNumAllowed; ++i) {
157 free(gAllowedFunctions[i]); // each was strdup'ed
158 }
159 free(gAllowedFunctions);
160 gAllowedFunctions = NULL;
161 gNumAllowed = 0;
162 }
163
164 // 2. Allocate new array
165 if (count > 0) {
166 gAllowedFunctions = (char**)malloc(sizeof(char*) * count);
167 }
168
169 // 3. Copy the new entries
170 for (int i = 0; i < count; ++i) {
171 // strdup is a POSIX function. If not available, implement your own string copy.
172 gAllowedFunctions[i] = strdup(functionList[i]);
173 }
174 gNumAllowed = count;
175}
176
177/**
178 * @brief Implementation of \ref is_function_allowed().
179 * @details Full API contract (arguments, ownership, side effects) is documented with
180 * the header declaration in `include/logging.h`.
181 * @see is_function_allowed()
182 */
183PetscBool is_function_allowed(const char* functionName)
184{
185 /* no list ⇒ allow all */
186 if (gNumAllowed == 0) {
187 return PETSC_TRUE;
188 }
189
190 /* otherwise only the listed functions are allowed */
191 for (int i = 0; i < gNumAllowed; ++i) {
192 if (strcmp(gAllowedFunctions[i], functionName) == 0) {
193 return PETSC_TRUE;
194 }
195 }
196 return PETSC_FALSE;
197}
198
199/**
200 * @brief Implementation of \ref LOG_CELL_VERTICES().
201 * @details Full API contract (arguments, ownership, side effects) is documented with
202 * the header declaration in `include/logging.h`.
203 * @see LOG_CELL_VERTICES()
204 */
205PetscErrorCode LOG_CELL_VERTICES(const Cell *cell, PetscMPIInt rank)
206{
207
208 // Validate input pointers
209 if (cell == NULL) {
210 LOG_ALLOW(LOCAL,LOG_ERROR, "'cell' is NULL.\n");
211 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "LOG_CELL_VERTICES - Input parameter 'cell' is NULL.");
212 }
213
214 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Rank %d, Cell Vertices:\n", rank);
215 for(int i = 0; i < 8; i++){
216 LOG_ALLOW(LOCAL,LOG_VERBOSE, " Vertex[%d]: (%.2f, %.2f, %.2f)\n",
217 i, cell->vertices[i].x, cell->vertices[i].y, cell->vertices[i].z);
218 }
219
220 return 0; // Indicate successful execution
221}
222
223
224/**
225 * @brief Implementation of \ref LOG_FACE_DISTANCES().
226 * @details Full API contract (arguments, ownership, side effects) is documented with
227 * the header declaration in `include/logging.h`.
228 * @see LOG_FACE_DISTANCES()
229 */
230PetscErrorCode LOG_FACE_DISTANCES(PetscReal* d)
231{
232
233 // Validate input array
234 if (d == NULL) {
235 LOG_ALLOW(LOCAL,LOG_ERROR, " 'd' is NULL.\n");
236 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, " Input array 'd' is NULL.");
237 }
238
239 PetscPrintf(PETSC_COMM_SELF, " Face Distances:\n");
240 PetscPrintf(PETSC_COMM_SELF, " LEFT(%d): %.15f\n", LEFT, d[LEFT]);
241 PetscPrintf(PETSC_COMM_SELF, " RIGHT(%d): %.15f\n", RIGHT, d[RIGHT]);
242 PetscPrintf(PETSC_COMM_SELF, " BOTTOM(%d): %.15f\n", BOTTOM, d[BOTTOM]);
243 PetscPrintf(PETSC_COMM_SELF, " TOP(%d): %.15f\n", TOP, d[TOP]);
244 PetscPrintf(PETSC_COMM_SELF, " FRONT(%d): %.15f\n", FRONT, d[FRONT]);
245 PetscPrintf(PETSC_COMM_SELF, " BACK(%d): %.15f\n", BACK, d[BACK]);
246
247 return 0; // Indicate successful execution
248}
249
250/*
251 * Helper function: Converts an integer (of type int) to a string.
252 */
253static void IntToStr(int value, char *buf, size_t bufsize)
254{
255 snprintf(buf, bufsize, "%d", value);
256}
257
258/*
259 * Helper function: Converts a 64‐bit integer to a string.
260 */
261static void Int64ToStr(PetscInt64 value, char *buf, size_t bufsize)
262{
263 snprintf(buf, bufsize, "%ld", value);
264}
265
266/*
267 * Helper function: Converts three integers into a formatted string "(i, j, k)".
268 */
269static void CellToStr(const PetscInt *cell, char *buf, size_t bufsize)
270{
271 snprintf(buf, bufsize, "(%d, %d, %d)", cell[0], cell[1], cell[2]);
272}
273
274/*
275 * Helper function: Converts three PetscReal values into a formatted string "(x, y, z)".
276 */
277static void TripleRealToStr(const PetscReal *arr, char *buf, size_t bufsize)
278{
279 snprintf(buf, bufsize, "(%.4f, %.4f, %.4f)", arr[0], arr[1], arr[2]);
280}
281
282/*
283 * Helper function: Computes the maximum string length for each column (across all particles).
284 *
285 * The function examines every particle (from 0 to nParticles-1) and converts the value to a
286 * string using the helper functions above. The maximum length is stored in the pointers provided.
287 *
288 * @param nParticles Number of particles.
289 * @param ranks Array of particle MPI ranks.
290 * @param pids Array of particle IDs.
291 * @param cellIDs Array of cell IDs (stored consecutively, 3 per particle).
292 * @param positions Array of positions (3 per particle).
293 * @param velocities Array of velocities (3 per particle).
294 * @param weights Array of weights (3 per particle).
295 * @param wRank [out] Maximum width for Rank column.
296 * @param wPID [out] Maximum width for PID column.
297 * @param wCell [out] Maximum width for Cell column.
298 * @param wPos [out] Maximum width for Position column.
299 * @param wVel [out] Maximum width for Velocity column.
300 * @param wWt [out] Maximum width for Weights column.
301 */
302static PetscErrorCode ComputeMaxColumnWidths(PetscInt nParticles,
303 const PetscMPIInt *ranks,
304 const PetscInt64 *pids,
305 const PetscInt *cellIDs,
306 const PetscReal *positions,
307 const PetscReal *velocities,
308 const PetscReal *weights,
309 int *wRank, int *wPID, int *wCell,
310 int *wPos, int *wVel, int *wWt)
311{
312 char tmp[TMP_BUF_SIZE];
313
314 *wRank = strlen("Rank"); /* Start with the header label lengths */
315 *wPID = strlen("PID");
316 *wCell = strlen("Cell (i,j,k)");
317 *wPos = strlen("Position (x,y,z)");
318 *wVel = strlen("Velocity (x,y,z)");
319 *wWt = strlen("Weights (a1,a2,a3)");
320
321 for (PetscInt i = 0; i < nParticles; i++) {
322 /* Rank */
323 IntToStr(ranks[i], tmp, TMP_BUF_SIZE);
324 if ((int)strlen(tmp) > *wRank) *wRank = (int)strlen(tmp);
325
326 /* PID */
327 Int64ToStr(pids[i], tmp, TMP_BUF_SIZE);
328 if ((int)strlen(tmp) > *wPID) *wPID = (int)strlen(tmp);
329
330 /* Cell: use the three consecutive values */
331 CellToStr(&cellIDs[3 * i], tmp, TMP_BUF_SIZE);
332 if ((int)strlen(tmp) > *wCell) *wCell = (int)strlen(tmp);
333
334 /* Position */
335 TripleRealToStr(&positions[3 * i], tmp, TMP_BUF_SIZE);
336 if ((int)strlen(tmp) > *wPos) *wPos = (int)strlen(tmp);
337
338 /* Velocity */
339 TripleRealToStr(&velocities[3 * i], tmp, TMP_BUF_SIZE);
340 if ((int)strlen(tmp) > *wVel) *wVel = (int)strlen(tmp);
341
342 /* Weights */
343 TripleRealToStr(&weights[3 * i], tmp, TMP_BUF_SIZE);
344 if ((int)strlen(tmp) > *wWt) *wWt = (int)strlen(tmp);
345 }
346 return 0;
347}
348
349/*
350 * Helper function: Builds a format string for a table row.
351 *
352 * The format string will include proper width specifiers for each column.
353 * For example, it might create something like:
354 *
355 * "| %-6s | %-8s | %-20s | %-25s | %-25s | %-25s |\n"
356 *
357 * @param wRank Maximum width for the Rank column.
358 * @param wPID Maximum width for the PID column.
359 * @param wCell Maximum width for the Cell column.
360 * @param wPos Maximum width for the Position column.
361 * @param wVel Maximum width for the Velocity column.
362 * @param wWt Maximum width for the Weights column.
363 * @param fmtStr Buffer in which to build the format string.
364 * @param bufSize Size of fmtStr.
365 */
366static void BuildRowFormatString(PetscMPIInt wRank, PetscInt wPID, PetscInt wCell, PetscInt wPos, PetscInt wVel, PetscInt wWt, char *fmtStr, size_t bufSize)
367{
368 // Build a format string using snprintf.
369 // We assume that the Rank is an int (%d), PID is a 64-bit int (%ld)
370 // and the remaining columns are strings (which have been formatted already).
371 snprintf(fmtStr, bufSize,
372 "| %%-%dd | %%-%dd | %%-%ds | %%-%ds | %%-%ds | %%-%ds |\n",
373 wRank, wPID, wCell, wPos, wVel, wWt);
374}
375
376/*
377 * Helper function: Builds a header string for the table using column titles.
378 */
379static void BuildHeaderString(char *headerStr, size_t bufSize, PetscMPIInt wRank, PetscInt wPID, PetscInt wCell, PetscInt wPos, PetscInt wVel, PetscInt wWt)
380{
381 snprintf(headerStr, bufSize,
382 "| %-*s | %-*s | %-*s | %-*s | %-*s | %-*s |\n",
383 (int)wRank, "Rank",
384 (int)wPID, "PID",
385 (int)wCell, "Cell (i,j,k)",
386 (int)wPos, "Position (x,y,z)",
387 (int)wVel, "Velocity (x,y,z)",
388 (int)wWt, "Weights (a1,a2,a3)");
389}
390
391/**
392 * @brief Implementation of \ref LOG_PARTICLE_FIELDS().
393 * @details Full API contract (arguments, ownership, side effects) is documented with
394 * the header declaration in `include/logging.h`.
395 * @see LOG_PARTICLE_FIELDS()
396 */
397PetscErrorCode LOG_PARTICLE_FIELDS(UserCtx* user, PetscInt printInterval)
398{
399 DM swarm = user->swarm;
400 PetscErrorCode ierr;
401 PetscInt localNumParticles;
402 PetscReal *positions = NULL;
403 PetscInt64 *particleIDs = NULL;
404 PetscMPIInt *particleRanks = NULL;
405 PetscInt *cellIDs = NULL;
406 PetscReal *weights = NULL;
407 PetscReal *velocities = NULL;
408 PetscMPIInt rank;
409
410 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
411 LOG_ALLOW(LOCAL,LOG_INFO, "Rank %d is retrieving particle data.\n", rank);
412
413 ierr = DMSwarmGetLocalSize(swarm, &localNumParticles); CHKERRQ(ierr);
414 LOG_ALLOW(LOCAL,LOG_DEBUG,"Rank %d has %d particles.\n", rank, localNumParticles);
415
416 ierr = DMSwarmGetField(swarm, "position", NULL, NULL, (void**)&positions); CHKERRQ(ierr);
417 ierr = DMSwarmGetField(swarm, "DMSwarm_pid", NULL, NULL, (void**)&particleIDs); CHKERRQ(ierr);
418 ierr = DMSwarmGetField(swarm, "DMSwarm_rank", NULL, NULL, (void**)&particleRanks); CHKERRQ(ierr);
419 ierr = DMSwarmGetField(swarm, "DMSwarm_CellID", NULL, NULL, (void**)&cellIDs); CHKERRQ(ierr);
420 ierr = DMSwarmGetField(swarm, "weight", NULL, NULL, (void**)&weights); CHKERRQ(ierr);
421 ierr = DMSwarmGetField(swarm, "velocity", NULL, NULL, (void**)&velocities); CHKERRQ(ierr);
422
423 /* Compute maximum column widths. */
424 int wRank, wPID, wCell, wPos, wVel, wWt;
425 wRank = wPID = wCell = wPos = wVel = wWt = 0;
426 ierr = ComputeMaxColumnWidths(localNumParticles, particleRanks, particleIDs, cellIDs,
427 positions, velocities, weights,
428 &wRank, &wPID, &wCell, &wPos, &wVel, &wWt); CHKERRQ(ierr);
429
430 /* Build a header string and a row format string. */
431 char headerFmt[256];
432 char rowFmt[256];
433 BuildHeaderString(headerFmt, sizeof(headerFmt), wRank, wPID, wCell, wPos, wVel, wWt);
434 BuildRowFormatString(wRank, wPID, wCell, wPos, wVel, wWt, rowFmt, sizeof(rowFmt));
435
436 /* Print header (using synchronized printing for parallel output). */
437 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "--------------------------------------------------------------------------------------------------------------\n"); CHKERRQ(ierr);
438 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "%s", headerFmt); CHKERRQ(ierr);
439 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "--------------------------------------------------------------------------------------------------------------\n"); CHKERRQ(ierr);
440
441 /* Loop over particles and print every printInterval-th row. */
442 char rowStr[256];
443 for (PetscInt i = 0; i < localNumParticles; i++) {
444 if (i % printInterval == 0) {
445 // ------- DEBUG
446 //char cellStr[TMP_BUF_SIZE], posStr[TMP_BUF_SIZE], velStr[TMP_BUF_SIZE], wtStr[TMP_BUF_SIZE];
447 //CellToStr(&cellIDs[3*i], cellStr, TMP_BUF_SIZE);
448 //TripleRealToStr(&positions[3*i], posStr, TMP_BUF_SIZE);
449 //TripleRealToStr(&velocities[3*i], velStr, TMP_BUF_SIZE);
450 // TripleRealToStr(&weights[3*i], wtStr, TMP_BUF_SIZE);
451
452 // if (rank == 0) { // Or whatever rank is Rank 0
453 //PetscPrintf(PETSC_COMM_SELF, "[Rank 0 DEBUG LPF] Particle %lld: PID=%lld, Rank=%d\n", (long long)i, (long long)particleIDs[i], particleRanks[i]);
454 //PetscPrintf(PETSC_COMM_SELF, "[Rank 0 DEBUG LPF] Raw Pos: (%.10e, %.10e, %.10e)\n", positions[3*i+0], positions[3*i+1], positions[3*i+2]);
455 //PetscPrintf(PETSC_COMM_SELF, "[Rank 0 DEBUG LPF] Str Pos: %s\n", posStr);
456 //PetscPrintf(PETSC_COMM_SELF, "[Rank 0 DEBUG LPF] Raw Vel: (%.10e, %.10e, %.10e)\n", velocities[3*i+0], velocities[3*i+1], velocities[3*i+2]);
457 // PetscPrintf(PETSC_COMM_SELF, "[Rank 0 DEBUG LPF] Str Vel: %s\n", velStr);
458 // Add similar for cell, weights
459 // PetscPrintf(PETSC_COMM_SELF, "[Rank 0 DEBUG LPF] About to build rowStr for particle %lld\n", (long long)i);
460 // fflush(stdout);
461 // }
462
463 // snprintf(rowStr, sizeof(rowStr), rowFmt,
464 // particleRanks[i],
465 // particleIDs[i],
466 // cellStr,
467 // posStr,
468 // velStr,
469 // wtStr);
470
471
472 // ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "%s", rowStr); CHKERRQ(ierr);
473
474 // ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "%s", rowStr); CHKERRQ(ierr);
475
476 // -------- DEBUG
477 /* Format the row by converting each field to a string first.
478 * We use temporary buffers and then build the row string.
479 */
480
481 char cellStr[TMP_BUF_SIZE], posStr[TMP_BUF_SIZE], velStr[TMP_BUF_SIZE], wtStr[TMP_BUF_SIZE];
482 CellToStr(&cellIDs[3*i], cellStr, TMP_BUF_SIZE);
483 TripleRealToStr(&positions[3*i], posStr, TMP_BUF_SIZE);
484 TripleRealToStr(&velocities[3*i], velStr, TMP_BUF_SIZE);
485 TripleRealToStr(&weights[3*i], wtStr, TMP_BUF_SIZE);
486
487 /* Build the row string. Note that for the integer fields we can use the row format string. */
488 snprintf(rowStr, sizeof(rowStr), rowFmt,
489 particleRanks[i],
490 particleIDs[i],
491 cellStr,
492 posStr,
493 velStr,
494 wtStr);
495 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "%s", rowStr); CHKERRQ(ierr);
496 }
497 }
498
499
500 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "--------------------------------------------------------------------------------------------------------------\n"); CHKERRQ(ierr);
501 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "\n"); CHKERRQ(ierr);
502 ierr = PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); CHKERRQ(ierr);
503
504 LOG_ALLOW_SYNC(GLOBAL,LOG_DEBUG,"Completed printing on Rank %d.\n", rank);
505
506 /* Restore fields */
507 ierr = DMSwarmRestoreField(swarm, "position", NULL, NULL, (void**)&positions); CHKERRQ(ierr);
508 ierr = DMSwarmRestoreField(swarm, "DMSwarm_pid", NULL, NULL, (void**)&particleIDs); CHKERRQ(ierr);
509 ierr = DMSwarmRestoreField(swarm, "DMSwarm_rank", NULL, NULL, (void**)&particleRanks); CHKERRQ(ierr);
510 ierr = DMSwarmRestoreField(swarm, "DMSwarm_CellID", NULL, NULL, (void**)&cellIDs); CHKERRQ(ierr);
511 ierr = DMSwarmRestoreField(swarm, "weight", NULL, NULL, (void**)&weights); CHKERRQ(ierr);
512 ierr = DMSwarmRestoreField(swarm, "velocity", NULL, NULL, (void**)&velocities); CHKERRQ(ierr);
513
514 LOG_ALLOW(LOCAL,LOG_DEBUG, "Restored all particle fields.\n");
515 return 0;
516}
517
518/**
519 * @brief Implementation of \ref IsParticleConsoleSnapshotEnabled().
520 * @details Full API contract (arguments, ownership, side effects) is documented with
521 * the header declaration in `include/logging.h`.
522 * @see IsParticleConsoleSnapshotEnabled()
523 */
524
526{
527 if (!simCtx) {
528 return PETSC_FALSE;
529 }
530 return (PetscBool)(simCtx->np > 0 &&
531 simCtx->particleConsoleOutputFreq > 0 &&
533}
534
535/**
536 * @brief Implementation of \ref ShouldEmitPeriodicParticleConsoleSnapshot().
537 * @details Full API contract (arguments, ownership, side effects) is documented with
538 * the header declaration in `include/logging.h`.
539 * @see ShouldEmitPeriodicParticleConsoleSnapshot()
540 */
541
542PetscBool ShouldEmitPeriodicParticleConsoleSnapshot(const SimCtx *simCtx, PetscInt completed_step)
543{
544 return (PetscBool)(IsParticleConsoleSnapshotEnabled(simCtx) &&
545 completed_step > 0 &&
546 completed_step % simCtx->particleConsoleOutputFreq == 0);
547}
548
549/**
550 * @brief Implementation of \ref EmitParticleConsoleSnapshot().
551 * @details Full API contract (arguments, ownership, side effects) is documented with
552 * the header declaration in `include/logging.h`.
553 * @see EmitParticleConsoleSnapshot()
554 */
555
556PetscErrorCode EmitParticleConsoleSnapshot(UserCtx *user, SimCtx *simCtx, PetscInt step)
557{
558 PetscErrorCode ierr;
559
560 PetscFunctionBeginUser;
561 LOG(GLOBAL, LOG_INFO, "Particle states at step %d:\n", step);
562 ierr = LOG_PARTICLE_FIELDS(user, simCtx->LoggingFrequency); CHKERRQ(ierr);
563 PetscFunctionReturn(0);
564}
565
566
567/**
568 * @brief Internal helper implementation: `trim()`.
569 * @details Local to this translation unit.
570 */
571static void trim(char *s)
572{
573 if (!s) return;
574
575 /* ---- 1. strip leading blanks ----------------------------------- */
576 char *p = s;
577 while (*p && isspace((unsigned char)*p))
578 ++p;
579
580 if (p != s) /* move the trimmed text forward */
581 memmove(s, p, strlen(p) + 1); /* +1 to copy the final NUL */
582
583 /* ---- 2. strip trailing blanks ---------------------------------- */
584 size_t len = strlen(s);
585 while (len > 0 && isspace((unsigned char)s[len - 1]))
586 s[--len] = '\0';
587}
588
589/* ------------------------------------------------------------------------- */
590/**
591 * @brief Implementation of \ref LoadAllowedFunctionsFromFile().
592 * @details Full API contract (arguments, ownership, side effects) is documented with
593 * the header declaration in `include/logging.h`.
594 * @see LoadAllowedFunctionsFromFile()
595 */
596PetscErrorCode LoadAllowedFunctionsFromFile(const char filename[],
597 char ***funcsOut,
598 PetscInt *nOut)
599{
600 FILE *fp = NULL;
601 char **funcs = NULL;
602 size_t cap = 16; /* initial capacity */
603 size_t n = 0; /* number of names */
604 char line[PETSC_MAX_PATH_LEN];
605 PetscErrorCode ierr;
606
607 PetscFunctionBegin;
608
609 /* ---------------------------------------------------------------------- */
610 /* 1. Open file */
611 fp = fopen(filename, "r");
612 if (!fp) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
613 "Cannot open %s", filename);
614
615 /* 2. Allocate initial pointer array */
616 ierr = PetscMalloc1(cap, &funcs); CHKERRQ(ierr);
617
618 /* 3. Read file line by line */
619 while (fgets(line, sizeof line, fp)) {
620 /* Strip everything after a comment character '#'. */
621 char *hash = strchr(line, '#');
622 if (hash) *hash = '\0';
623
624 trim(line); /* remove leading/trailing blanks */
625 if (!*line) continue; /* skip if empty */
626
627 /* Grow the array if necessary */
628 if (n == cap) {
629 cap *= 2;
630 ierr = PetscRealloc(cap * sizeof(*funcs), (void **)&funcs); CHKERRQ(ierr);
631 }
632
633 /* Deep‑copy the cleaned identifier */
634 ierr = PetscStrallocpy(line, &funcs[n++]); CHKERRQ(ierr);
635 }
636 fclose(fp);
637
638 /* 4. Return results to caller */
639 *funcsOut = funcs;
640 *nOut = (PetscInt)n;
641
642 PetscFunctionReturn(0);
643}
644
645/* ------------------------------------------------------------------------- */
646/**
647 * @brief Internal helper implementation: `FreeAllowedFunctions()`.
648 * @details Local to this translation unit.
649 */
650PetscErrorCode FreeAllowedFunctions(char **funcs, PetscInt n)
651{
652 PetscErrorCode ierr;
653 PetscFunctionBegin;
654 if (funcs) {
655 for (PetscInt i = 0; i < n; ++i) {
656 ierr = PetscFree(funcs[i]); CHKERRQ(ierr);
657 }
658 ierr = PetscFree(funcs); CHKERRQ(ierr);
659 }
660 PetscFunctionReturn(0);
661}
662
663/**
664 * @brief Implementation of \ref BCFaceToString().
665 * @details Full API contract (arguments, ownership, side effects) is documented with
666 * the header declaration in `include/logging.h`.
667 * @see BCFaceToString()
668 */
669const char* BCFaceToString(BCFace face) {
670 switch (face) {
671 case BC_FACE_NEG_X: return "-Xi (I-Min)";
672 case BC_FACE_POS_X: return "+Xi (I-Max)";
673 case BC_FACE_NEG_Y: return "-Eta (J-Min)";
674 case BC_FACE_POS_Y: return "+Eta (J-Max)";
675 case BC_FACE_NEG_Z: return "-Zeta (K-Min)";
676 case BC_FACE_POS_Z: return "+Zeta (K-Max)";
677 default: return "Unknown Face";
678 }
679}
680
681/**
682 * @brief Implementation of \ref FieldInitializationToString().
683 * @details Full API contract (arguments, ownership, side effects) is documented with
684 * the header declaration in `include/logging.h`.
685 * @see FieldInitializationToString()
686 */
687const char* FieldInitializationToString(PetscInt FieldInitialization)
688{
689 switch(FieldInitialization){
690 case 0: return "Zero";
691 case 1: return "Constant Normal velocity";
692 case 2: return "Poiseuille Normal velocity";
693 default: return "Unknown Field Initialization";
694 }
695}
696
697/**
698 * @brief Implementation of \ref ParticleInitializationToString().
699 * @details Full API contract (arguments, ownership, side effects) is documented with
700 * the header declaration in `include/logging.h`.
701 * @see ParticleInitializationToString()
702 */
704{
705 switch(ParticleInitialization){
706 case PARTICLE_INIT_SURFACE_RANDOM: return "Surface: Random";
707 case PARTICLE_INIT_VOLUME: return "Volume";
708 case PARTICLE_INIT_POINT_SOURCE: return "Point Source";
709 case PARTICLE_INIT_SURFACE_EDGES: return "Surface: At edges";
710 default: return "Unknown Particle Initialization";
711 }
712}
713
714/**
715 * @brief Implementation of \ref LESModelToString().
716 * @details Full API contract (arguments, ownership, side effects) is documented with
717 * the header declaration in `include/logging.h`.
718 * @see LESModelToString()
719 */
720const char* LESModelToString(LESModelType LESFlag)
721{
722 switch(LESFlag){
723 case NO_LES_MODEL: return "No LES";
724 case CONSTANT_SMAGORINSKY: return "Constant Smagorinsky";
725 case DYNAMIC_SMAGORINSKY: return "Dynamic Smagorinsky";
726 default: return "Unknown LES Flag";
727 }
728}
729
730/**
731 * @brief Implementation of \ref MomentumSolverTypeToString().
732 * @details Full API contract (arguments, ownership, side effects) is documented with
733 * the header declaration in `include/logging.h`.
734 * @see MomentumSolverTypeToString()
735 */
737{
738 switch(SolverFlag){
739 case MOMENTUM_SOLVER_EXPLICIT_RK: return "Explicit 4 stage Runge-Kutta ";
740 case MOMENTUM_SOLVER_DUALTIME_PICARD_RK4: return "Dual Time Stepping with Picard Iterations and 4 stage Runge-Kutta Smoothing";
741 default: return "Unknown Momentum Solver Type";
742 }
743}
744
745/**
746 * @brief Implementation of \ref BCTypeToString().
747 * @details Full API contract (arguments, ownership, side effects) is documented with
748 * the header declaration in `include/logging.h`.
749 * @see BCTypeToString()
750 */
751const char* BCTypeToString(BCType type) {
752 switch (type) {
753 // case DIRICHLET: return "DIRICHLET";
754 // case NEUMANN: return "NEUMANN";
755 case WALL: return "WALL";
756 case INLET: return "INLET";
757 case OUTLET: return "OUTLET";
758 case FARFIELD: return "FARFIELD";
759 case PERIODIC: return "PERIODIC";
760 case INTERFACE: return "INTERFACE";
761
762 // case CUSTOM: return "CUSTOM";
763 default: return "Unknown BC Type";
764 }
765}
766
767/**
768 * @brief Internal helper implementation: `BCHandlerTypeToString()`.
769 * @details Local to this translation unit.
770 */
771const char* BCHandlerTypeToString(BCHandlerType handler_type) {
772 switch (handler_type) {
773 // Wall & Symmetry Handlers
774 case BC_HANDLER_WALL_NOSLIP: return "noslip";
775 case BC_HANDLER_WALL_MOVING: return "moving";
776 case BC_HANDLER_SYMMETRY_PLANE: return "symmetry_plane";
777
778 // Inlet Handlers
779 case BC_HANDLER_INLET_CONSTANT_VELOCITY: return "constant_velocity";
780 case BC_HANDLER_INLET_PULSATILE_FLUX: return "pulsatile_flux";
781 case BC_HANDLER_INLET_PARABOLIC: return "parabolic";
782
783 // Outlet Handlers
784 case BC_HANDLER_OUTLET_CONSERVATION: return "conservation";
785 case BC_HANDLER_OUTLET_PRESSURE: return "pressure";
786
787 // Other Physical Handlers
788 case BC_HANDLER_FARFIELD_NONREFLECTING: return "nonreflecting";
789
790 // Multi-Block / Interface Handlers
791 case BC_HANDLER_PERIODIC_GEOMETRIC: return "geometric";
792 case BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX: return "constant flux";
793 case BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX: return "initial flux";
794 case BC_HANDLER_INTERFACE_OVERSET: return "overset";
795
796 // Default case
798 default: return "UNKNOWN_HANDLER";
799 }
800}
801
802/**
803 * @brief Implementation of \ref DualMonitorDestroy().
804 * @details Full API contract (arguments, ownership, side effects) is documented with
805 * the header declaration in `include/logging.h`.
806 * @see DualMonitorDestroy()
807 */
808PetscErrorCode DualMonitorDestroy(void **ctx)
809{
810 DualMonitorCtx *monctx = (DualMonitorCtx*)*ctx;
811 PetscErrorCode ierr;
812 PetscMPIInt rank;
813
814 PetscFunctionBeginUser;
815 ierr = MPI_Comm_rank(PETSC_COMM_WORLD,&rank); CHKERRQ(ierr);
816 if(!rank && monctx->file_handle){
817 fclose(monctx->file_handle);
818 }
819
820 ierr = PetscFree(monctx); CHKERRQ(ierr);
821 *ctx = NULL;
822 PetscFunctionReturn(0);
823}
824
825/**
826 * @brief A custom KSP monitor that logs the true residual to a file and optionally to the console.
827 *
828 * This function replicates the behavior of KSPMonitorTrueResidualNorm by calculating
829 * the true residual norm ||b - Ax|| itself. It unconditionally logs to a file
830 * viewer and conditionally logs to the console based on a flag in the context.
831 *
832 * @param ksp The Krylov subspace context.
833 * @param it The current iteration number.
834 * @param rnorm The preconditioned residual norm (ignored, we compute our own).
835 * @param ctx A pointer to the DualMonitorCtx structure.
836 * @return PetscErrorCode 0 on success.
837 */
838#undef __FUNCT__
839#define __FUNCT__ "DualKSPMonitor"
840/**
841 * @brief Implementation of \ref DualKSPMonitor().
842 * @details Full API contract (arguments, ownership, side effects) is documented with
843 * the header declaration in `include/logging.h`.
844 * @see DualKSPMonitor()
845 */
846
847PetscErrorCode DualKSPMonitor(KSP ksp, PetscInt it, PetscReal rnorm, void *ctx)
848{
849 DualMonitorCtx *monctx = (DualMonitorCtx*)ctx;
850 PetscErrorCode ierr;
851 PetscReal trnorm, relnorm;
852 Vec r;
853 char norm_buf[256];
854 PetscMPIInt rank;
855
856 PetscFunctionBeginUser;
857 ierr = MPI_Comm_rank(PETSC_COMM_WORLD,&rank); CHKERRQ(ierr);
858
859 // 1. Calculate the true residual norm.
860 ierr = KSPBuildResidual(ksp, NULL, NULL, &r); CHKERRQ(ierr);
861 ierr = VecNorm(r, NORM_2, &trnorm); CHKERRQ(ierr);
862
863 // 2. On the first iteration, compute and store the norm of the RHS vector `b`.
864 if (it == 0) {
865 Vec b;
866 ierr = KSPGetRhs(ksp, &b); CHKERRQ(ierr);
867 ierr = VecNorm(b, NORM_2, &monctx->bnorm); CHKERRQ(ierr);
868 }
869
870 if(!rank){
871 // 3. Compute the relative norm and format the output string.
872 if (monctx->bnorm > 1.e-15) {
873 relnorm = trnorm / monctx->bnorm;
874 sprintf(norm_buf, "ts: %-5d | block: %-2d | iter: %-3d | Unprecond Norm: %12.5e | True Norm: %12.5e | Rel Norm: %12.5e",(int)monctx->step, (int)monctx->block_id, (int)it, (double)rnorm, (double)trnorm, (double)relnorm);
875 } else {
876 sprintf(norm_buf,"ts: %-5d | block: %-2d | iter: %-3d | Unprecond Norm: %12.5e | True Norm: %12.5e",(int)monctx->step, (int)monctx->block_id, (int)it, (double)rnorm, (double)trnorm);
877 }
878
879 // 4. Log to the file viewer (unconditionally).
880 if(monctx->file_handle){
881 ierr = PetscFPrintf(PETSC_COMM_SELF,monctx->file_handle,"%s\n", norm_buf); CHKERRQ(ierr);
882 }
883 // 5. Log to the console (conditionally).
884 if (monctx->log_to_console) {
885 PetscFPrintf(PETSC_COMM_SELF,stdout, "%s\n", norm_buf); CHKERRQ(ierr);
886 }
887
888 } //rank
889
890 PetscFunctionReturn(0);
891}
892
893/**
894 * @brief Logs continuity metrics for a single block to a file.
895 *
896 * This function should be called for each block, once per timestep. It opens a
897 * central log file in append mode. To ensure the header is written only once,
898 * it checks if it is processing block 0 on the simulation's start step.
899 *
900 * @param user A pointer to the UserCtx for the specific block whose metrics
901 * are to be logged. The function accesses both global (SimCtx)
902 * and local (user->...) data.
903 * @return PetscErrorCode 0 on success.
904 */
905#undef __FUNCT__
906#define __FUNCT__ "LOG_CONTINUITY_METRICS"
907/**
908 * @brief Implementation of \ref LOG_CONTINUITY_METRICS().
909 * @details Full API contract (arguments, ownership, side effects) is documented with
910 * the header declaration in `include/logging.h`.
911 * @see LOG_CONTINUITY_METRICS()
912 */
913
914PetscErrorCode LOG_CONTINUITY_METRICS(UserCtx *user)
915{
916 PetscErrorCode ierr;
917 PetscMPIInt rank;
918 SimCtx *simCtx = user->simCtx; // Get the shared SimCtx
919 const PetscInt bi = user->_this; // Get this block's specific ID
920 const PetscInt ti = simCtx->step; // Get the current timestep
921
922 PetscFunctionBeginUser;
923 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
924
925 // Only rank 0 performs file I/O.
926 if (!rank) {
927 FILE *f;
928 char filen[PETSC_MAX_PATH_LEN + 64];
929 ierr = PetscSNPrintf(filen, sizeof(filen), "%s/Continuity_Metrics.log", simCtx->log_dir); CHKERRQ(ierr);
930
931 // Open the log file in append mode.
932 f = fopen(filen, "a");
933 if (!f) {
934 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open log file: %s", filen);
935 }
936
937 // Write a header only when the file is empty and it's the first block (bi=0).
938 // Using ftell() instead of step comparison ensures correctness across continuations.
939 if (ftell(f) == 0 && bi == 0) {
940 PetscFPrintf(PETSC_COMM_SELF, f, "%-10s | %-6s | %-18s | %-30s | %-18s | %-18s | %-18s | %-18s\n",
941 "Timestep", "Block", "Max Divergence", "Max Divergence Location ([k][j][i]=idx)", "Sum(RHS)","Total Flux In", "Total Flux Out", "Net Flux");
942 PetscFPrintf(PETSC_COMM_SELF, f, "------------------------------------------------------------------------------------------------------------------------------------------\n");
943 }
944
945 // Prepare the data strings and values for the current block.
946 PetscReal net_flux = simCtx->FluxInSum - simCtx->FluxOutSum;
947 char location_str[64];
948 sprintf(location_str, "([%d][%d][%d] = %d)", (int)simCtx->MaxDivz, (int)simCtx->MaxDivy, (int)simCtx->MaxDivx, (int)simCtx->MaxDivFlatArg);
949
950 // Write the formatted line for the current block.
951 PetscFPrintf(PETSC_COMM_SELF, f, "%-10d | %-6d | %-18.10e | %-39s | %-18.10e | %-18.10e | %-18.10e | %-18.10e\n",
952 (int)ti,
953 (int)bi,
954 (double)simCtx->MaxDiv,
955 location_str,
956 (double)simCtx->summationRHS,
957 (double)simCtx->FluxInSum,
958 (double)simCtx->FluxOutSum,
959 (double)net_flux);
960
961 fclose(f);
962 }
963
964 PetscFunctionReturn(0);
965}
966
967/**
968 * @brief Implementation of \ref ParticleLocationStatusToString().
969 * @details Full API contract (arguments, ownership, side effects) is documented with
970 * the header declaration in `include/logging.h`.
971 * @see ParticleLocationStatusToString()
972 */
974{
975 switch (level) {
976 case NEEDS_LOCATION: return "NEEDS_LOCATION";
977 case ACTIVE_AND_LOCATED: return "ACTIVE_AND_LOCATED";
978 case MIGRATING_OUT: return "MIGRATING_OUT";
979 case LOST: return "LOST";
980 case UNINITIALIZED: return "UNINITIALIZED";
981 default: return "UNKNOWN_LEVEL";
982 }
983}
984
985///////// Profiling System /////////
986
987// Data structure to hold profiling info for one function
988typedef struct {
989 const char *name;
994 double start_time; // Timer for the current call
995 PetscBool always_log;
997
998// Global registry for all profiled functions
1000static PetscInt g_profiler_count = 0;
1001static PetscInt g_profiler_capacity = 0;
1002
1003// Internal helper to find a function in the registry or create it
1004/**
1005 * @brief Internal helper implementation: `_FindOrCreateEntry()`.
1006 * @details Local to this translation unit.
1007 */
1008static PetscErrorCode _FindOrCreateEntry(const char *func_name, PetscInt *idx)
1009{
1010 PetscFunctionBeginUser;
1011 // Search for existing entry
1012 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1013 if (strcmp(g_profiler_registry[i].name, func_name) == 0) {
1014 *idx = i;
1015 PetscFunctionReturn(0);
1016 }
1017 }
1018
1019 // Not found, create a new entry
1021 PetscInt new_capacity = g_profiler_capacity == 0 ? 16 : g_profiler_capacity * 2;
1022 PetscErrorCode ierr = PetscRealloc(sizeof(ProfiledFunction) * new_capacity, &g_profiler_registry); CHKERRQ(ierr);
1023 g_profiler_capacity = new_capacity;
1024 }
1025
1026 *idx = g_profiler_count;
1027 g_profiler_registry[*idx].name = func_name;
1028 g_profiler_registry[*idx].total_time = 0.0;
1032 g_profiler_registry[*idx].start_time = 0.0;
1033 g_profiler_registry[*idx].always_log = PETSC_FALSE;
1035
1036 PetscFunctionReturn(0);
1037}
1038
1039// --- Public API Implementation ---
1040/**
1041 * @brief Internal helper implementation: `ProfilingInitialize()`.
1042 * @details Local to this translation unit.
1043 */
1044PetscErrorCode ProfilingInitialize(SimCtx *simCtx)
1045{
1046 PetscFunctionBeginUser;
1047 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx cannot be null for ProfilingInitialize");
1048
1049 // Iterate through the list of critical functions provided in SimCtx
1050 for (PetscInt i = 0; i < simCtx->nProfilingSelectedFuncs; ++i) {
1051 PetscInt idx;
1052 const char *func_name = simCtx->profilingSelectedFuncs[i];
1053 PetscErrorCode ierr = _FindOrCreateEntry(func_name, &idx); CHKERRQ(ierr);
1054 g_profiler_registry[idx].always_log = PETSC_TRUE;
1055
1056 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Marked '%s' as a critical function for profiling.\n", func_name);
1057 }
1058 PetscFunctionReturn(0);
1059}
1060
1061/**
1062 * @brief Implementation of \ref _ProfilingStart().
1063 * @details Full API contract (arguments, ownership, side effects) is documented with
1064 * the header declaration in `include/logging.h`.
1065 * @see _ProfilingStart()
1066 */
1067
1068void _ProfilingStart(const char *func_name)
1069{
1070 PetscInt idx;
1071 if (_FindOrCreateEntry(func_name, &idx) != 0) return; // Fail silently
1072 PetscTime(&g_profiler_registry[idx].start_time);
1073}
1074
1075/**
1076 * @brief Implementation of \ref _ProfilingEnd().
1077 * @details Full API contract (arguments, ownership, side effects) is documented with
1078 * the header declaration in `include/logging.h`.
1079 * @see _ProfilingEnd()
1080 */
1081
1082void _ProfilingEnd(const char *func_name)
1083{
1084 double end_time;
1085 PetscTime(&end_time);
1086
1087 PetscInt idx;
1088 if (_FindOrCreateEntry(func_name, &idx) != 0) return; // Fail silently
1089
1090 double elapsed = end_time - g_profiler_registry[idx].start_time;
1091 g_profiler_registry[idx].total_time += elapsed;
1092 g_profiler_registry[idx].current_step_time += elapsed;
1095}
1096
1097/**
1098 * @brief Implementation of \ref ProfilingResetTimestepCounters().
1099 * @details Full API contract (arguments, ownership, side effects) is documented with
1100 * the header declaration in `include/logging.h`.
1101 * @see ProfilingResetTimestepCounters()
1102 */
1103
1105{
1106 PetscFunctionBeginUser;
1107 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1110 }
1111 PetscFunctionReturn(0);
1112}
1113
1114/**
1115 * @brief Implementation of \ref ProfilingLogTimestepSummary().
1116 * @details Full API contract (arguments, ownership, side effects) is documented with
1117 * the header declaration in `include/logging.h`.
1118 * @see ProfilingLogTimestepSummary()
1119 */
1120
1121PetscErrorCode ProfilingLogTimestepSummary(SimCtx *simCtx, PetscInt step)
1122{
1123 PetscBool should_write = PETSC_FALSE;
1124 FILE *f = NULL;
1125 char filen[(2 * PETSC_MAX_PATH_LEN) + 16];
1126
1127 PetscFunctionBeginUser;
1128 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx cannot be null for ProfilingLogTimestepSummary");
1129
1130 if (strcmp(simCtx->profilingTimestepMode, "off") == 0) {
1131 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1134 }
1135 PetscFunctionReturn(0);
1136 }
1137
1138 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1139 if (g_profiler_registry[i].current_step_call_count <= 0) {
1140 continue;
1141 }
1142 if (strcmp(simCtx->profilingTimestepMode, "all") == 0 || g_profiler_registry[i].always_log) {
1143 should_write = PETSC_TRUE;
1144 break;
1145 }
1146 }
1147
1148 if (should_write && simCtx->rank == 0) {
1149 snprintf(filen, sizeof(filen), "%s/%s", simCtx->log_dir, simCtx->profilingTimestepFile);
1150 if (step == simCtx->StartStep + 1 && !simCtx->continueMode) {
1151 f = fopen(filen, "w");
1152 if (!f) {
1153 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open profiling timestep log file: %s", filen);
1154 }
1155 PetscFPrintf(PETSC_COMM_SELF, f, "step,function,calls,step_time_s\n");
1156 } else {
1157 f = fopen(filen, "a");
1158 if (!f) {
1159 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open profiling timestep log file: %s", filen);
1160 }
1161 if (step == simCtx->StartStep + 1 && ftell(f) == 0) {
1162 PetscFPrintf(PETSC_COMM_SELF, f, "step,function,calls,step_time_s\n");
1163 }
1164 }
1165
1166 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1167 if (g_profiler_registry[i].current_step_call_count <= 0) {
1168 continue;
1169 }
1170 if (strcmp(simCtx->profilingTimestepMode, "all") == 0 || g_profiler_registry[i].always_log) {
1171 PetscFPrintf(
1172 PETSC_COMM_SELF,
1173 f,
1174 "%d,%s,%lld,%.6f\n",
1175 (int)step,
1176 g_profiler_registry[i].name,
1177 g_profiler_registry[i].current_step_call_count,
1178 g_profiler_registry[i].current_step_time
1179 );
1180 }
1181 }
1182 fclose(f);
1183 }
1184
1185 // Reset per-step counters for the next iteration
1186 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1189 }
1190 PetscFunctionReturn(0);
1191}
1192
1193// Comparison function for qsort to sort by total_time in descending order
1194/**
1195 * @brief Internal helper implementation: `_CompareProfiledFunctions()`.
1196 * @details Local to this translation unit.
1197 */
1198static int _CompareProfiledFunctions(const void *a, const void *b)
1199{
1200 const ProfiledFunction *func_a = (const ProfiledFunction *)a;
1201 const ProfiledFunction *func_b = (const ProfiledFunction *)b;
1202
1203 if (func_a->total_time < func_b->total_time) return 1;
1204 if (func_a->total_time > func_b->total_time) return -1;
1205 return 0;
1206}
1207
1208/**
1209 * @brief Implementation of \ref ProfilingFinalize().
1210 * @details Full API contract (arguments, ownership, side effects) is documented with
1211 * the header declaration in `include/logging.h`.
1212 * @see ProfilingFinalize()
1213 */
1214PetscErrorCode ProfilingFinalize(SimCtx *simCtx)
1215{
1216 PetscErrorCode ierr;
1217 PetscInt rank = simCtx->rank;
1218 PetscFunctionBeginUser;
1219 if (!simCtx->profilingFinalSummary) PetscFunctionReturn(0);
1220 if (!rank) {
1221
1222 char exec_mode_modifier[32] = "Unknown";
1223 if(simCtx->exec_mode == EXEC_MODE_SOLVER) PetscCall(PetscStrncpy(exec_mode_modifier, "Solver", sizeof(exec_mode_modifier)));
1224 else if(simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR) PetscCall(PetscStrncpy(exec_mode_modifier, "PostProcessor", sizeof(exec_mode_modifier)));
1225 //--- Step 0: Create a file viewer for log file
1226 FILE *f;
1227 char filen[PETSC_MAX_PATH_LEN + 128];
1228 ierr = PetscSNPrintf(filen, sizeof(filen), "%s/ProfilingSummary_%s.log",simCtx->log_dir,exec_mode_modifier); CHKERRQ(ierr);
1229
1230 // Open the log file: append with section label in continue mode, truncate otherwise.
1231 if (simCtx->continueMode) {
1232 f = fopen(filen, "a");
1233 if (!f) {
1234 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open log file: %s", filen);
1235 }
1236 fprintf(f, "\n=== Continuation from step %" PetscInt_FMT " ===\n", simCtx->StartStep);
1237 } else {
1238 f = fopen(filen, "w");
1239 if (!f) {
1240 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open log file: %s", filen);
1241 }
1242 }
1243
1244 // --- Step 1: Sort the data for readability ---
1246
1247 // --- Step 2: Dynamically determine the width for the function name column ---
1248 PetscInt max_name_len = strlen("Function"); // Start with the header's length
1249 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1250 if (g_profiler_registry[i].total_call_count > 0) {
1251 PetscInt len = strlen(g_profiler_registry[i].name);
1252 if (len > max_name_len) {
1253 max_name_len = len;
1254 }
1255 }
1256 }
1257 // Add a little padding
1258 max_name_len += 2;
1259
1260 // --- Step 3: Define fixed widths for numeric columns for consistent alignment ---
1261 const int time_width = 18;
1262 const int count_width = 15;
1263 const int avg_width = 22;
1264
1265 // --- Step 4: Print the formatted table ---
1266 PetscFPrintf(PETSC_COMM_SELF, f, "=================================================================================================================\n");
1267 PetscFPrintf(PETSC_COMM_SELF, f, " FINAL PROFILING SUMMARY (Sorted by Total Time)\n");
1268 PetscFPrintf(PETSC_COMM_SELF, f, "=================================================================================================================\n");
1269
1270 // Header Row
1271 PetscFPrintf(PETSC_COMM_SELF, f, "%-*s | %-*s | %-*s | %-*s\n",
1272 max_name_len, "Function",
1273 time_width, "Total Time (s)",
1274 count_width, "Call Count",
1275 avg_width, "Avg. Time/Call (ms)");
1276
1277 // Separator Line (dynamically sized)
1278 for (int i = 0; i < max_name_len; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
1279 PetscFPrintf(PETSC_COMM_SELF, f, "-|-");
1280 for (int i = 0; i < time_width; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
1281 PetscFPrintf(PETSC_COMM_SELF, f, "-|-");
1282 for (int i = 0; i < count_width; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
1283 PetscFPrintf(PETSC_COMM_SELF, f, "-|-");
1284 for (int i = 0; i < avg_width; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
1285 PetscFPrintf(PETSC_COMM_SELF, f, "\n");
1286
1287 // Data Rows
1288 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1289 if (g_profiler_registry[i].total_call_count > 0) {
1290 double avg_time_ms = (g_profiler_registry[i].total_time / g_profiler_registry[i].total_call_count) * 1000.0;
1291 PetscFPrintf(PETSC_COMM_SELF, f, "%-*s | %*.*f | %*lld | %*.*f\n",
1292 max_name_len, g_profiler_registry[i].name,
1293 time_width, 6, g_profiler_registry[i].total_time,
1294 count_width, g_profiler_registry[i].total_call_count,
1295 avg_width, 6, avg_time_ms);
1296 PetscFPrintf(PETSC_COMM_SELF, f, "------------------------------------------------------------------------------------------------------------------\n");
1297 }
1298 }
1299 PetscFPrintf(PETSC_COMM_SELF, f, "==================================================================================================================\n");
1300
1301 fclose(f);
1302 }
1303
1304 // --- Final Cleanup ---
1305 PetscFree(g_profiler_registry);
1306 g_profiler_registry = NULL;
1307 g_profiler_count = 0;
1309 PetscFunctionReturn(0);
1310}
1311
1312/*================================================================================*
1313 * PROGRESS BAR UTILITY *
1314 *================================================================================*/
1315
1316/**
1317 * @brief Internal helper implementation: `PrintProgressBar()`.
1318 * @details Local to this translation unit.
1319 */
1320void PrintProgressBar(PetscInt step, PetscInt startStep, PetscInt totalSteps, PetscReal currentTime)
1321{
1322 if (totalSteps <= 0) return;
1323
1324 // --- Configuration ---
1325 const int barWidth = 50;
1326
1327 // --- Calculation ---
1328 // Calculate progress as a fraction from 0.0 to 1.0
1329 PetscReal progress = (PetscReal)(step - startStep + 1) / totalSteps;
1330 // Ensure progress doesn't exceed 1.0 due to floating point inaccuracies
1331 if (progress > 1.0) progress = 1.0;
1332
1333 int pos = (int)(barWidth * progress);
1334
1335 // --- Printing ---
1336 // Carriage return moves cursor to the beginning of the line
1337 PetscPrintf(PETSC_COMM_SELF, "\rProgress: [");
1338
1339 for (int i = 0; i < barWidth; ++i) {
1340 if (i < pos) {
1341 PetscPrintf(PETSC_COMM_SELF, "=");
1342 } else if (i == pos) {
1343 PetscPrintf(PETSC_COMM_SELF, ">");
1344 } else {
1345 PetscPrintf(PETSC_COMM_SELF, " ");
1346 }
1347 }
1348
1349 // Print percentage, step count, and current time
1350 PetscPrintf(PETSC_COMM_SELF, "] %3d%% (Step %" PetscInt_FMT "/%" PetscInt_FMT ", t=%.4f)",
1351 (int)(progress * 100.0),
1352 step + 1,
1353 startStep + totalSteps,
1354 currentTime);
1355
1356 // Flush the output buffer to ensure the bar is displayed immediately
1357 fflush(stdout);
1358}
1359
1360#undef __FUNCT__
1361#define __FUNCT__ "LOG_FIELD_MIN_MAX"
1362/**
1363 * @brief Implementation of \ref LOG_FIELD_MIN_MAX().
1364 * @details Full API contract is documented with the header declaration in `include/logging.h`.
1365 * @see LOG_FIELD_MIN_MAX()
1366 */
1367PetscErrorCode LOG_FIELD_MIN_MAX(UserCtx *user, const char *fieldName)
1368{
1369 PetscErrorCode ierr;
1370 PetscInt i, j, k;
1371 DMDALocalInfo info;
1372
1373 Vec fieldVec = NULL;
1374 DM dm = NULL;
1375 PetscInt dof;
1376 char data_layout[20];
1377
1378 PetscFunctionBeginUser;
1379
1380 // --- 1. Map string name to PETSc objects and determine data layout ---
1381 if (strcasecmp(fieldName, "Ucat") == 0) {
1382 fieldVec = user->Ucat; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
1383 } else if (strcasecmp(fieldName, "P") == 0) {
1384 fieldVec = user->P; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
1385 } else if (strcasecmp(fieldName, "Diffusivity") == 0) {
1386 fieldVec = user->Diffusivity; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
1387 } else if (strcasecmp(fieldName, "DiffusivityGradient") == 0) {
1388 fieldVec = user->DiffusivityGradient; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
1389 } else if (strcasecmp(fieldName, "Ucont") == 0) {
1390 fieldVec = user->lUcont; dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered");
1391 } else if (strcasecmp(fieldName, "Coordinates") == 0) {
1392 ierr = DMGetCoordinates(user->da, &fieldVec); CHKERRQ(ierr);
1393 dm = user->fda; dof = 3; strcpy(data_layout, "Node-Centered");
1394 } else if (strcasecmp(fieldName,"Psi") == 0) {
1395 fieldVec = user->Psi; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered"); // Assuming Psi is cell-centered
1396 } else {
1397 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Field %s not recognized.", fieldName);
1398 }
1399
1400 if (!fieldVec) {
1401 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Vector for field '%s' is NULL.", fieldName);
1402 }
1403 if (!dm) {
1404 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM for field '%s' is NULL.", fieldName);
1405 }
1406
1407 ierr = DMDAGetLocalInfo(dm, &info); CHKERRQ(ierr);
1408
1409 // --- 2. Define Architecture-Aware Loop Bounds ---
1410 PetscInt i_start, i_end, j_start, j_end, k_start, k_end;
1411
1412 if (strcmp(data_layout, "Cell-Centered") == 0) {
1413 // For cell-centered data, the physical values are stored from index 1 to N-1.
1414 // We find the intersection of the rank's owned range [xs, xe) with the
1415 // physical data range [1, IM-1).
1416 i_start = PetscMax(info.xs, 1); i_end = PetscMin(info.xs + info.xm, user->IM);
1417 j_start = PetscMax(info.ys, 1); j_end = PetscMin(info.ys + info.ym, user->JM);
1418 k_start = PetscMax(info.zs, 1); k_end = PetscMin(info.zs + info.zm, user->KM);
1419 } else { // For Node- or Face-Centered data
1420 // The physical values are stored from index 0 to N-1.
1421 // We find the intersection of the rank's owned range [xs, xe) with the
1422 // physical data range [0, IM-1].
1423 i_start = PetscMax(info.xs, 0); i_end = PetscMin(info.xs + info.xm, user->IM);
1424 j_start = PetscMax(info.ys, 0); j_end = PetscMin(info.ys + info.ym, user->JM);
1425 k_start = PetscMax(info.zs, 0); k_end = PetscMin(info.zs + info.zm, user->KM);
1426 }
1427
1428 // --- 3. Barrier for clean, grouped output ---
1429 ierr = MPI_Barrier(PETSC_COMM_WORLD); CHKERRQ(ierr);
1430 if (user->simCtx->rank == 0) {
1431 PetscPrintf(PETSC_COMM_SELF, "\n--- Field Ranges: [%s] (Layout: %s) ---\n", fieldName, data_layout);
1432 }
1433
1434 // --- 4. Branch on DoF and perform calculation with correct bounds ---
1435 if (dof == 1) {
1436 PetscReal localMin = PETSC_MAX_REAL, localMax = PETSC_MIN_REAL;
1437 PetscReal globalMin, globalMax;
1438 const PetscScalar ***array;
1439
1440 ierr = DMDAVecGetArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
1441 for (k = k_start; k < k_end; k++) {
1442 for (j = j_start; j < j_end; j++) {
1443 for (i = i_start; i < i_end; i++) {
1444 localMin = PetscMin(localMin, array[k][j][i]);
1445 localMax = PetscMax(localMax, array[k][j][i]);
1446 }
1447 }
1448 }
1449 ierr = DMDAVecRestoreArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
1450
1451 ierr = MPI_Allreduce(&localMin, &globalMin, 1, MPIU_REAL, MPI_MIN, PETSC_COMM_WORLD); CHKERRQ(ierr);
1452 ierr = MPI_Allreduce(&localMax, &globalMax, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRQ(ierr);
1453
1454 PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin, localMax);
1455 ierr = PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); CHKERRQ(ierr);
1456 if (user->simCtx->rank == 0) {
1457 PetscPrintf(PETSC_COMM_SELF, " Global Range: [ %11.4e , %11.4e ]\n", globalMin, globalMax);
1458 }
1459
1460 } else if (dof == 3) {
1461 Cmpnts localMin = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MAX_REAL};
1462 Cmpnts localMax = {PETSC_MIN_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1463 Cmpnts globalMin, globalMax;
1464 const Cmpnts ***array;
1465
1466 ierr = DMDAVecGetArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
1467 for (k = k_start; k < k_end; k++) {
1468 for (j = j_start; j < j_end; j++) {
1469 for (i = i_start; i < i_end; i++) {
1470 localMin.x = PetscMin(localMin.x, array[k][j][i].x);
1471 localMin.y = PetscMin(localMin.y, array[k][j][i].y);
1472 localMin.z = PetscMin(localMin.z, array[k][j][i].z);
1473 localMax.x = PetscMax(localMax.x, array[k][j][i].x);
1474 localMax.y = PetscMax(localMax.y, array[k][j][i].y);
1475 localMax.z = PetscMax(localMax.z, array[k][j][i].z);
1476 }
1477 }
1478 }
1479 ierr = DMDAVecRestoreArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
1480
1481 ierr = MPI_Allreduce(&localMin, &globalMin, 3, MPIU_REAL, MPI_MIN, PETSC_COMM_WORLD); CHKERRQ(ierr);
1482 ierr = MPI_Allreduce(&localMax, &globalMax, 3, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRQ(ierr);
1483
1484 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local X-Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin.x, localMax.x);
1485 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local Y-Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin.y, localMax.y);
1486 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local Z-Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin.z, localMax.z);
1487 ierr = PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); CHKERRQ(ierr);
1488
1489 if (user->simCtx->rank == 0) {
1490 PetscPrintf(PETSC_COMM_SELF, " [Global] X-Range: [ %11.4e , %11.4e ]\n", globalMin.x, globalMax.x);
1491 PetscPrintf(PETSC_COMM_SELF, " [Global] Y-Range: [ %11.4e , %11.4e ]\n", globalMin.y, globalMax.y);
1492 PetscPrintf(PETSC_COMM_SELF, " [Global] Z-Range: [ %11.4e , %11.4e ]\n", globalMin.z, globalMax.z);
1493 }
1494
1495 } else {
1496 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "LogFieldStatistics only supports fields with 1 or 3 components, but field '%s' has %" PetscInt_FMT ".", fieldName, dof);
1497 }
1498
1499 // --- 5. Final barrier for clean output ordering ---
1500 ierr = MPI_Barrier(PETSC_COMM_WORLD); CHKERRQ(ierr);
1501 if (user->simCtx->rank == 0) {
1502 PetscPrintf(PETSC_COMM_SELF, "--------------------------------------------\n\n");
1503 }
1504
1505 PetscFunctionReturn(0);
1506}
1507
1508#undef __FUNCT__
1509#define __FUNCT__ "LOG_FIELD_ANATOMY"
1510/**
1511 * @brief Implementation of \ref LOG_FIELD_ANATOMY().
1512 * @details Full API contract is documented with the header declaration in `include/logging.h`.
1513 * @see LOG_FIELD_ANATOMY()
1514 */
1515PetscErrorCode LOG_FIELD_ANATOMY(UserCtx *user, const char *field_name, const char *stage_name)
1516{
1517 PetscErrorCode ierr;
1518 DMDALocalInfo info;
1519 PetscMPIInt rank;
1520
1521 Vec vec_local = NULL;
1522 DM dm = NULL;
1523 PetscInt dof = 0;
1524 char data_layout[20];
1525 char dominant_dir = '\0'; // 'x', 'y', 'z' for face-centered, 'm' for mixed (Ucont)
1526
1527 PetscFunctionBeginUser;
1528 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
1529
1530 // --- 1. Map string name to PETSc objects and determine data layout ---
1531 if (strcasecmp(field_name, "Ucat") == 0) {
1532 vec_local = user->lUcat; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
1533 } else if (strcasecmp(field_name, "P") == 0) {
1534 vec_local = user->lP; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
1535 } else if (strcasecmp(field_name, "Diffusivity") == 0) {
1536 vec_local = user->lDiffusivity; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
1537 } else if (strcasecmp(field_name, "DiffusivityGradient") == 0) {
1538 vec_local = user->lDiffusivityGradient; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
1539 } else if (strcasecmp(field_name, "Psi") == 0) {
1540 vec_local = user->lPsi; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
1541 } else if (strcasecmp(field_name, "Center-Coordinates") == 0) {
1542 vec_local = user->lCent; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
1543 } else if (strcasecmp(field_name, "Ucont") == 0) {
1544 vec_local = user->lUcont; dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered"); dominant_dir = 'm'; // Mixed
1545 } else if (strcasecmp(field_name, "Csi") == 0 || strcasecmp(field_name, "X-Face-Centers") == 0) {
1546 vec_local = (strcasecmp(field_name, "Csi") == 0) ? user->lCsi : user->Centx;
1547 dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered"); dominant_dir = 'x';
1548 } else if (strcasecmp(field_name, "Eta") == 0 || strcasecmp(field_name, "Y-Face-Centers") == 0) {
1549 vec_local = (strcasecmp(field_name, "Eta") == 0) ? user->lEta : user->Centy;
1550 dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered"); dominant_dir = 'y';
1551 } else if (strcasecmp(field_name, "Zet") == 0 || strcasecmp(field_name, "Z-Face-Centers") == 0) {
1552 vec_local = (strcasecmp(field_name, "Zet") == 0) ? user->lZet : user->Centz;
1553 dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered"); dominant_dir = 'z';
1554 } else if (strcasecmp(field_name, "Coordinates") == 0) {
1555 ierr = DMGetCoordinatesLocal(user->da, &vec_local); CHKERRQ(ierr);
1556 dm = user->fda; dof = 3; strcpy(data_layout, "Node-Centered");
1557 } else if (strcasecmp(field_name, "CornerField")== 0){
1558 vec_local = user->lCellFieldAtCorner; strcpy(data_layout, "Node-Centered");
1559 PetscInt bs = 1;
1560 ierr = VecGetBlockSize(user->CellFieldAtCorner, &bs); CHKERRQ(ierr);
1561 dof = bs;
1562 if(dof == 1) dm = user->da;
1563 else dm = user->fda;
1564 } else {
1565 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Unknown field name for LOG_FIELD_ANATOMY: %s", field_name);
1566 }
1567
1568 // --- 2. Get Grid Info and Array Pointers ---
1569 ierr = DMDAGetLocalInfo(dm, &info); CHKERRQ(ierr);
1570
1571 ierr = PetscBarrier(NULL);
1572 PetscPrintf(PETSC_COMM_WORLD, "\n--- Field Anatomy Log: [%s] | Stage: [%s] | Layout: [%s] ---\n", field_name, stage_name, data_layout);
1573
1574 // Global physical dimensions (number of cells)
1575 PetscInt im_phys = user->IM;
1576 PetscInt jm_phys = user->JM;
1577 PetscInt km_phys = user->KM;
1578
1579 // Slice through the center of the local domain
1580 PetscInt i_mid = (PetscInt)(info.xs + 0.5 * info.xm) - 1;
1581 PetscInt j_mid = (PetscInt)(info.ys + 0.5 * info.ym) - 1;
1582 PetscInt k_mid = (PetscInt)(info.zs + 0.5 * info.zm) - 1;
1583
1584 // --- 3. Print Boundary Information based on Data Layout ---
1585
1586 // ======================================================================
1587 // === CASE 1: Cell-Centered Fields (Ucat, P) - USES SHIFTED INDEX ===
1588 // ======================================================================
1589 if (strcmp(data_layout, "Cell-Centered") == 0) {
1590 const void *l_arr;
1591 ierr = DMDAVecGetArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
1592
1593
1594 // --- I-Direction Boundaries ---
1595 if (info.xs == 0) { // Rank on -Xi boundary
1596 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Ghost for Cell[k][j][0]) = ", rank, 0);
1597 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][0]);
1598 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[k_mid][j_mid][0].x, ((const Cmpnts***)l_arr)[k_mid][j_mid][0].y, ((const Cmpnts***)l_arr)[k_mid][j_mid][0].z);
1599
1600 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Value for Cell[k][j][0]) = ", rank, 1);
1601 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][1]);
1602 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[k_mid][j_mid][1].x, ((const Cmpnts***)l_arr)[k_mid][j_mid][1].y, ((const Cmpnts***)l_arr)[k_mid][j_mid][1].z);
1603 }
1604 if (info.xs + info.xm == info.mx) { // Rank on +Xi boundary
1605 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Value for Cell[k][j][%d]) = ", rank, im_phys - 1, im_phys - 2);
1606 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][im_phys - 1]);
1607 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[k_mid][j_mid][im_phys - 1].x, ((const Cmpnts***)l_arr)[k_mid][j_mid][im_phys - 1].y, ((const Cmpnts***)l_arr)[k_mid][j_mid][im_phys - 1].z);
1608
1609 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Ghost for Cell[k][j][%d]) = ", rank, im_phys, im_phys - 2);
1610 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][im_phys]);
1611 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[k_mid][j_mid][im_phys].x, ((const Cmpnts***)l_arr)[k_mid][j_mid][im_phys].y, ((const Cmpnts***)l_arr)[k_mid][j_mid][im_phys].z);
1612 }
1613
1614 // --- J-Direction Boundaries ---
1615 if (info.ys == 0) { // Rank on -Eta boundary
1616 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Ghost for Cell[k][0][i]) = ", rank, 0);
1617 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][0][i_mid]);
1618 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[k_mid][0][i_mid].x, ((const Cmpnts***)l_arr)[k_mid][0][i_mid].y, ((const Cmpnts***)l_arr)[k_mid][0][i_mid].z);
1619
1620 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Value for Cell[k][0][i]) = ", rank, 1);
1621 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][1][i_mid]);
1622 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[k_mid][1][i_mid].x, ((const Cmpnts***)l_arr)[k_mid][1][i_mid].y, ((const Cmpnts***)l_arr)[k_mid][1][i_mid].z);
1623 }
1624
1625 if (info.ys + info.ym == info.my) { // Rank on +Eta boundary
1626 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Value for Cell[k][%d][i]) = ", rank, jm_phys - 1, jm_phys - 2);
1627 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][jm_phys - 1][i_mid]);
1628 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[k_mid][jm_phys - 1][i_mid].x, ((const Cmpnts***)l_arr)[k_mid][jm_phys - 1][i_mid].y, ((const Cmpnts***)l_arr)[k_mid][jm_phys - 1][i_mid].z);
1629
1630 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Ghost for Cell[k][%d][i]) = ", rank, jm_phys, jm_phys - 2);
1631 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][jm_phys][i_mid]);
1632 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[k_mid][jm_phys][i_mid].x, ((const Cmpnts***)l_arr)[k_mid][jm_phys][i_mid].y, ((const Cmpnts***)l_arr)[k_mid][jm_phys][i_mid].z);
1633 }
1634
1635 // --- K-Direction Boundaries ---
1636 if (info.zs == 0) { // Rank on -Zeta boundary
1637 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Ghost for Cell[0][j][i]) = ", rank, 0);
1638 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[0][j_mid][i_mid]);
1639 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[0][j_mid][i_mid].x, ((const Cmpnts***)l_arr)[0][j_mid][i_mid].y, ((const Cmpnts***)l_arr)[0][j_mid][i_mid].z);
1640 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Value for Cell[0][j][i]) = ", rank, 1);
1641 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[1][j_mid][i_mid]);
1642 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[1][j_mid][i_mid].x, ((const Cmpnts***)l_arr)[1][j_mid][i_mid].y, ((const Cmpnts***)l_arr)[1][j_mid][i_mid].z);
1643 }
1644 if (info.zs + info.zm == info.mz) { // Rank on +Zeta boundary
1645 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Value for Cell[%d][j][i]) = ", rank, km_phys - 1, km_phys - 2);
1646 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[km_phys - 1][j_mid][i_mid]);
1647 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[km_phys - 1][j_mid][i_mid].x, ((const Cmpnts***)l_arr)[km_phys - 1][j_mid][i_mid].y, ((const Cmpnts***)l_arr)[km_phys - 1][j_mid][i_mid].z);
1648 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Ghost for Cell[%d][j][i]) = ", rank, km_phys, km_phys - 2);
1649 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[km_phys][j_mid][i_mid]);
1650 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f, %.5f, %.5f)\n", ((const Cmpnts***)l_arr)[km_phys][j_mid][i_mid].x, ((const Cmpnts***)l_arr)[km_phys][j_mid][i_mid].y, ((const Cmpnts***)l_arr)[km_phys][j_mid][i_mid].z);
1651 }
1652 ierr = DMDAVecRestoreArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
1653 }
1654 // ======================================================================
1655 // === CASE 2: Face-Centered Fields - NUANCED DIRECTIONAL LOGIC ===
1656 // ======================================================================
1657 else if (strcmp(data_layout, "Face-Centered") == 0) {
1658 const Cmpnts ***l_arr;
1659 ierr = DMDAVecGetArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
1660
1661 // --- I-Direction Boundaries ---
1662 if (info.xs == 0) { // Rank on -Xi boundary
1663 if (dominant_dir == 'x') { // Node-like in I-dir
1664 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (First Phys. X-Face) = (%.5f, %.5f, %.5f)\n", rank, 0, l_arr[k_mid][j_mid][0].x, l_arr[k_mid][j_mid][0].y, l_arr[k_mid][j_mid][0].z);
1665 } else if (dominant_dir == 'y' || dominant_dir == 'z') { // Cell-like in I-dir
1666 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Ghost for Cell[k][j][0]) = (%.5f, %.5f, %.5f)\n", rank, 0, l_arr[k_mid][j_mid][0].x, l_arr[k_mid][j_mid][0].y, l_arr[k_mid][j_mid][0].z);
1667 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Value for Cell[k][j][0]) = (%.5f, %.5f, %.5f)\n", rank, 1, l_arr[k_mid][j_mid][1].x, l_arr[k_mid][j_mid][1].y, l_arr[k_mid][j_mid][1].z);
1668 } else if (dominant_dir == 'm') { // Ucont: Mixed
1669 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: u-comp @ Idx %2d (1st X-Face) = %.5f\n", rank, 0, l_arr[k_mid][j_mid][0].x);
1670 }
1671 }
1672 if (info.xs + info.xm == info.mx) { // Rank on +Xi boundary
1673 if (dominant_dir == 'x') { // Node-like in I-dir
1674 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Last Phys. X-Face) = (%.5f, %.5f, %.5f)\n", rank, im_phys - 1, l_arr[k_mid][j_mid][im_phys - 1].x, l_arr[k_mid][j_mid][im_phys-1].y, l_arr[k_mid][j_mid][im_phys - 1].z);
1675 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Ghost Location) = (%.5f, %.5f, %.5f)\n", rank, im_phys, l_arr[k_mid][j_mid][im_phys].x, l_arr[k_mid][j_mid][im_phys].y, l_arr[k_mid][j_mid][im_phys].z);
1676 } else if (dominant_dir == 'y' || dominant_dir == 'z') { // Cell-like in I-dir
1677 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Value for Cell[k][j][%d]) = (%.5f, %.5f, %.5f)\n", rank, im_phys - 1, im_phys - 2, l_arr[k_mid][j_mid][im_phys - 1].x, l_arr[k_mid][j_mid][im_phys - 1].y, l_arr[k_mid][j_mid][im_phys-1].z);
1678 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Ghost for Cell[k][j][%d]) = (%.5f, %.5f, %.5f)\n", rank, im_phys, im_phys - 2, l_arr[k_mid][j_mid][im_phys].x, l_arr[k_mid][j_mid][im_phys].y, l_arr[k_mid][j_mid][im_phys].z);
1679 } else if (dominant_dir == 'm') { // Ucont: Mixed
1680 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: u-comp @ Idx %2d (Last X-Face) = %.5f\n", rank, im_phys - 1, l_arr[k_mid][j_mid][im_phys - 1].x);
1681 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: u-comp @ Idx %2d (Ghost Location) = %.5f\n", rank, im_phys, l_arr[k_mid][j_mid][im_phys].x);
1682 }
1683 }
1684
1685 // --- J-Direction Boundaries ---
1686 if (info.ys == 0) { // Rank on -Eta boundary
1687 if (dominant_dir == 'y') { // Node-like in J-dir
1688 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (First Phys. Y-Face) = (%.5f, %.5f, %.5f)\n", rank, 0, l_arr[k_mid][0][i_mid].x, l_arr[k_mid][0][i_mid].y, l_arr[k_mid][0][i_mid].z);
1689 } else if (dominant_dir == 'x' || dominant_dir == 'z') { // Cell-like in J-dir
1690 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Ghost for Cell[k][0][i]) = (%.5f, %.5f, %.5f)\n", rank, 0, l_arr[k_mid][0][i_mid].x, l_arr[k_mid][0][i_mid].y, l_arr[k_mid][0][i_mid].z);
1691 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Value for Cell[k][0][i]) = (%.5f, %.5f, %.5f)\n", rank, 1, l_arr[k_mid][1][i_mid].x, l_arr[k_mid][1][i_mid].y, l_arr[k_mid][1][i_mid].z);
1692 } else if (dominant_dir == 'm') { // Ucont: Mixed
1693 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: v-comp @ Jdx %2d (1st Y-Face) = %.5f\n", rank, 0, l_arr[k_mid][0][i_mid].y);
1694 }
1695 }
1696 if (info.ys + info.ym == info.my) { // Rank on +Eta boundary
1697 if (dominant_dir == 'y') { // Node-like in J-dir
1698 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Last Phys. Y-Face) = (%.5f, %.5f, %.5f)\n", rank, jm_phys - 1, l_arr[k_mid][jm_phys - 1][i_mid].x, l_arr[k_mid][jm_phys - 1][i_mid].y, l_arr[k_mid][jm_phys - 1][i_mid].z);
1699 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Ghost Location) = (%.5f, %.5f, %.5f)\n", rank, jm_phys, l_arr[k_mid][jm_phys][i_mid].x, l_arr[k_mid][jm_phys][i_mid].y, l_arr[k_mid][jm_phys][i_mid].z);
1700 } else if (dominant_dir == 'x' || dominant_dir == 'z') { // Cell-like in J-dir
1701 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Value for Cell[k][%d][i]) = (%.5f, %.5f, %.5f)\n", rank, jm_phys-1, jm_phys-2, l_arr[k_mid][jm_phys - 1][i_mid].x, l_arr[k_mid][jm_phys - 1][i_mid].y, l_arr[k_mid][jm_phys - 1][i_mid].z);
1702 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Ghost for Cell[k][%d][i]) = (%.5f, %.5f, %.5f)\n", rank, jm_phys, jm_phys-2, l_arr[k_mid][jm_phys][i_mid].x, l_arr[k_mid][jm_phys][i_mid].y, l_arr[k_mid][jm_phys][i_mid].z);
1703 } else if (dominant_dir == 'm') { // Ucont: Mixed
1704 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: v-comp @ Jdx %2d (Last Y-Face) = %.5f\n", rank, jm_phys - 1, l_arr[k_mid][jm_phys - 1][i_mid].y);
1705 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: v-comp @ Jdx %2d (Ghost Location) = %.5f\n", rank, jm_phys, l_arr[k_mid][jm_phys][i_mid].y);
1706 }
1707 }
1708
1709 // --- K-Direction Boundaries ---
1710 if (info.zs == 0) { // Rank on -Zeta boundary
1711 if (dominant_dir == 'z') { // Node-like in K-dir
1712 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (First Phys. Z-Face) = (%.5f, %.5f, %.5f)\n", rank, 0, l_arr[0][j_mid][i_mid].x, l_arr[0][j_mid][i_mid].y, l_arr[0][j_mid][i_mid].z);
1713 } else if (dominant_dir == 'x' || dominant_dir == 'y') { // Cell-like in K-dir
1714 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Ghost for Cell[0][j][i]) = (%.5f, %.5f, %.5f)\n", rank, 0, l_arr[0][j_mid][i_mid].x, l_arr[0][j_mid][i_mid].y, l_arr[0][j_mid][i_mid].z);
1715 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Value for Cell[0][j][i]) = (%.5f, %.5f, %.5f)\n", rank, 1, l_arr[1][j_mid][i_mid].x, l_arr[1][j_mid][i_mid].y, l_arr[1][j_mid][i_mid].z);
1716 } else if (dominant_dir == 'm') { // Ucont: Mixed
1717 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: w-comp @ Idx %2d (1st Z-Face) = %.5f\n", rank, 0, l_arr[0][j_mid][i_mid].z);
1718 }
1719 }
1720 if (info.zs + info.zm == info.mz) { // Rank on +Zeta boundary
1721 if (dominant_dir == 'z') { // Node-like in K-dir
1722 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Last Phys. Z-Face) = (%.5f, %.5f, %.5f)\n", rank, km_phys - 1, l_arr[km_phys - 1][j_mid][i_mid].x, l_arr[km_phys - 1][j_mid][i_mid].y, l_arr[km_phys - 1][j_mid][i_mid].z);
1723 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Ghost Location) = (%.5f, %.5f, %.5f)\n", rank, km_phys, l_arr[km_phys][j_mid][i_mid].x, l_arr[km_phys][j_mid][i_mid].y, l_arr[km_phys][j_mid][i_mid].z);
1724 } else if (dominant_dir == 'x' || dominant_dir == 'y') { // Cell-like in K-dir
1725 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Value for Cell[%d][j][i]) = (%.5f, %.5f, %.5f)\n", rank, km_phys-1, km_phys-2, l_arr[km_phys-1][j_mid][i_mid].x, l_arr[km_phys-1][j_mid][i_mid].y, l_arr[km_phys - 1][j_mid][i_mid].z);
1726 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Ghost for Cell[%d][j][i]) = (%.5f, %.5f, %.5f)\n", rank, km_phys, km_phys-2, l_arr[km_phys][j_mid][i_mid].x, l_arr[km_phys][j_mid][i_mid].y, l_arr[km_phys][j_mid][i_mid].z);
1727 } else if (dominant_dir == 'm') { // Ucont: Mixed
1728 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: w-comp @ Idx %2d (Last Z-Face) = %.5f\n", rank, km_phys - 1, l_arr[km_phys - 1][j_mid][i_mid].z);
1729 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: w-comp @ Idx %2d (Ghost Loc.) = %.5f\n", rank, km_phys, l_arr[km_phys][j_mid][i_mid].z);
1730
1731 }
1732 }
1733 ierr = DMDAVecRestoreArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
1734 }
1735 // ======================================================================
1736 // === CASE 3: Node-Centered Fields - USES DIRECT INDEX ===
1737 // ======================================================================
1738 else if (strcmp(data_layout, "Node-Centered") == 0) {
1739 const Cmpnts ***l_arr;
1740 ierr = DMDAVecGetArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
1741
1742 // --- I-Direction Boundaries ---
1743 if (info.xs == 0) {
1744 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (First Phys. Node) = (%.5f, %.5f, %.5f)\n", rank, 0, l_arr[k_mid][j_mid][0].x, l_arr[k_mid][j_mid][0].y, l_arr[k_mid][j_mid][0].z);
1745 }
1746 if (info.xs + info.xm == info.mx) {
1747 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Last Phys. Node) = (%.5f, %.5f, %.5f)\n", rank, im_phys - 1, l_arr[k_mid][j_mid][im_phys - 1].x, l_arr[k_mid][j_mid][im_phys - 1].y, l_arr[k_mid][j_mid][im_phys - 1].z);
1748 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Unused/Ghost Loc) = (%.5f, %.5f, %.5f)\n", rank, im_phys, l_arr[k_mid][j_mid][im_phys].x, l_arr[k_mid][j_mid][im_phys].y, l_arr[k_mid][j_mid][im_phys].z);
1749 }
1750 // --- J-Direction Boundaries ---
1751 if (info.ys == 0) {
1752 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (First Phys. Node) = (%.5f, %.5f, %.5f)\n", rank, 0, l_arr[k_mid][0][i_mid].x, l_arr[k_mid][0][i_mid].y, l_arr[k_mid][0][i_mid].z);
1753 }
1754 if (info.ys + info.ym == info.my) {
1755 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Last Phys. Node) = (%.5f, %.5f, %.5f)\n", rank, jm_phys - 1, l_arr[k_mid][jm_phys - 1][i_mid].x, l_arr[k_mid][jm_phys - 1][i_mid].y, l_arr[k_mid][jm_phys - 1][i_mid].z);
1756 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Unused/Ghost Loc) = (%.5f, %.5f, %.5f)\n", rank, jm_phys, l_arr[k_mid][jm_phys][i_mid].x, l_arr[k_mid][jm_phys][i_mid].y, l_arr[k_mid][jm_phys][i_mid].z);
1757 }
1758 // --- K-Direction Boundaries ---
1759 if (info.zs == 0) {
1760 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (First Phys. Node) = (%.5f, %.5f, %.5f)\n", rank, 0, l_arr[0][j_mid][i_mid].x, l_arr[0][j_mid][i_mid].y, l_arr[0][j_mid][i_mid].z);
1761 }
1762 if(info.zs + info.zm == info.mz) {
1763 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Last Phys. Node) = (%.5f, %.5f, %.5f)\n", rank, km_phys - 1, l_arr[km_phys - 1][j_mid][i_mid].x, l_arr[km_phys - 1][j_mid][i_mid].y, l_arr[km_phys - 1][j_mid][i_mid].z);
1764 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Unused/Ghost Loc) = (%.5f, %.5f, %.5f)\n", rank, km_phys, l_arr[km_phys][j_mid][i_mid].x, l_arr[km_phys][j_mid][i_mid].y, l_arr[km_phys][j_mid][i_mid].z);
1765 }
1766 ierr = DMDAVecRestoreArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
1767 }
1768 else {
1769 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "LOG_FIELD_ANATOMY encountered an unknown data layout: %s", data_layout);
1770 }
1771
1772 ierr = PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); CHKERRQ(ierr);
1773 ierr = PetscBarrier(NULL);
1774 PetscFunctionReturn(0);
1775}
1776
1777#undef __FUNCT__
1778#define __FUNCT__ "LOG_INTERPOLATION_ERROR"
1779/**
1780 * @brief Implementation of \ref LOG_INTERPOLATION_ERROR().
1781 * @details Full API contract (arguments, ownership, side effects) is documented with
1782 * the header declaration in `include/logging.h`.
1783 * @see LOG_INTERPOLATION_ERROR()
1784 */
1786{
1787 SimCtx *simCtx = user->simCtx;
1788 PetscErrorCode ierr;
1789 DM swarm = user->swarm;
1790 Vec positionVec, analyticalvelocityVec, velocityVec, errorVec;
1791 PetscReal Interpolation_error = 0.0;
1792 PetscReal Maximum_Interpolation_error = 0.0;
1793 PetscReal AnalyticalSolution_magnitude = 0.0;
1794 PetscReal ErrorPercentage = 0.0;
1795
1796 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Creating global vectors.\n");
1797 ierr = DMSwarmCreateGlobalVectorFromField(swarm, "position", &positionVec); CHKERRQ(ierr);
1798 ierr = DMSwarmCreateGlobalVectorFromField(swarm, "velocity", &velocityVec); CHKERRQ(ierr);
1799
1800 ierr = VecDuplicate(positionVec, &analyticalvelocityVec); CHKERRQ(ierr);
1801 ierr = VecCopy(positionVec, analyticalvelocityVec); CHKERRQ(ierr);
1802
1803 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Computing analytical solution.\n");
1804 ierr = SetAnalyticalSolutionForParticles(analyticalvelocityVec, simCtx); CHKERRQ(ierr);
1805
1806 ierr = VecDuplicate(analyticalvelocityVec, &errorVec); CHKERRQ(ierr);
1807 ierr = VecCopy(analyticalvelocityVec, errorVec); CHKERRQ(ierr);
1808
1809 ierr = VecNorm(analyticalvelocityVec, NORM_2, &AnalyticalSolution_magnitude); CHKERRQ(ierr);
1810
1811 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Computing error.\n");
1812 ierr = VecAXPY(errorVec, -1.0, velocityVec); CHKERRQ(ierr);
1813 ierr = VecNorm(errorVec, NORM_2, &Interpolation_error); CHKERRQ(ierr);
1814 ierr = VecNorm(errorVec,NORM_INFINITY,&Maximum_Interpolation_error); CHKERRQ(ierr);
1815
1816 ErrorPercentage = (AnalyticalSolution_magnitude > 0) ?
1817 (Interpolation_error / AnalyticalSolution_magnitude * 100.0) : 0.0;
1818
1819 /* --- CSV output (always, rank 0 only) --- */
1820 if (simCtx->rank == 0) {
1821 char csv_path[PETSC_MAX_PATH_LEN + 32];
1822 ierr = PetscSNPrintf(csv_path, sizeof(csv_path), "%s/interpolation_error.csv", simCtx->log_dir); CHKERRQ(ierr);
1823 FILE *f = fopen(csv_path, "a");
1824 if (f) {
1825 if (ftell(f) == 0) {
1826 fprintf(f, "step,time,L2_error,Linf_error,L2_analytical,error_pct\n");
1827 }
1828 PetscReal t = (PetscReal)simCtx->ti * simCtx->dt;
1829 fprintf(f, "%d,%.6e,%.6e,%.6e,%.6e,%.4f\n",
1830 (int)simCtx->step, t,
1831 Interpolation_error, Maximum_Interpolation_error,
1832 AnalyticalSolution_magnitude, ErrorPercentage);
1833 fclose(f);
1834 }
1835 }
1836
1837 /* --- Console output (only at INFO level or above) --- */
1838 if (get_log_level() >= LOG_INFO) {
1839 LOG_ALLOW(GLOBAL, LOG_INFO, "Interpolation error (%%): %g\n", ErrorPercentage);
1840 PetscPrintf(PETSC_COMM_WORLD, "Interpolation error (%%): %g\n", ErrorPercentage);
1841 LOG_ALLOW(GLOBAL, LOG_INFO, "Maximum Interpolation error: %g\n", Maximum_Interpolation_error);
1842 PetscPrintf(PETSC_COMM_WORLD, "Maximum Interpolation error: %g\n", Maximum_Interpolation_error);
1843 }
1844
1845 ierr = VecDestroy(&analyticalvelocityVec); CHKERRQ(ierr);
1846 ierr = VecDestroy(&errorVec); CHKERRQ(ierr);
1847 ierr = DMSwarmDestroyGlobalVectorFromField(swarm, "position", &positionVec); CHKERRQ(ierr);
1848 ierr = DMSwarmDestroyGlobalVectorFromField(swarm, "velocity", &velocityVec); CHKERRQ(ierr);
1849
1850 return 0;
1851}
1852
1853#undef __FUNCT__
1854#define __FUNCT__ "LOG_SCATTER_METRICS"
1855/**
1856 * @brief Implementation of \ref LOG_SCATTER_METRICS().
1857 * @details Full API contract (arguments, ownership, side effects) is documented with
1858 * the header declaration in `include/logging.h`.
1859 * @see LOG_SCATTER_METRICS()
1860 */
1861PetscErrorCode LOG_SCATTER_METRICS(UserCtx *user)
1862{
1863 PetscErrorCode ierr;
1864 SimCtx *simCtx = NULL;
1865 DMDALocalInfo info;
1866 PetscInt xs, xe, ys, ye, zs, ze, mx, my, mz;
1867 PetscInt lxs, lxe, lys, lye, lzs, lze;
1868 Vec reference_vec = NULL;
1869 PetscReal ***psi = NULL;
1870 PetscReal ***psi_ref = NULL;
1871 PetscReal ***aj = NULL;
1872 PetscReal ***count = NULL;
1873 PetscReal *particle_psi = NULL;
1874 PetscInt nlocal = 0;
1875 PetscReal local_l1 = 0.0, global_l1 = 0.0;
1876 PetscReal local_l2_sq = 0.0, global_l2_sq = 0.0;
1877 PetscReal local_linf = 0.0, global_linf = 0.0;
1878 PetscReal local_ref_l2_sq = 0.0, global_ref_l2_sq = 0.0;
1879 PetscReal local_grid_integral = 0.0, global_grid_integral = 0.0;
1880 PetscReal local_domain_volume = 0.0, global_domain_volume = 0.0;
1881 PetscReal local_particle_sum = 0.0, global_particle_sum = 0.0;
1882 PetscInt64 local_particle_count = 0, global_particle_count = 0;
1883 PetscInt64 local_cell_count = 0, global_cell_count = 0;
1884 PetscInt64 local_occupied_count = 0, global_occupied_count = 0;
1885 PetscReal particle_integral = 0.0;
1886 PetscReal occupancy_fraction = 0.0;
1887 PetscReal mean_particles_per_occupied_cell = 0.0;
1888 PetscReal l2_error = 0.0;
1889 PetscReal relative_l2_error = 0.0;
1890
1891 PetscFunctionBeginUser;
1892 if (!user) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "UserCtx cannot be NULL.");
1893 simCtx = user->simCtx;
1894 if (!VerificationScalarOverrideActive(simCtx) || !user->swarm || !user->Psi || !user->ParticleCount) {
1895 PetscFunctionReturn(0);
1896 }
1897
1898 info = user->info;
1899 xs = info.xs; xe = info.xs + info.xm;
1900 ys = info.ys; ye = info.ys + info.ym;
1901 zs = info.zs; ze = info.zs + info.zm;
1902 mx = info.mx; my = info.my; mz = info.mz;
1903 lxs = (xs == 0) ? xs + 1 : xs; lxe = (xe == mx) ? xe - 1 : xe;
1904 lys = (ys == 0) ? ys + 1 : ys; lye = (ye == my) ? ye - 1 : ye;
1905 lzs = (zs == 0) ? zs + 1 : zs; lze = (ze == mz) ? ze - 1 : ze;
1906
1907 ierr = VecDuplicate(user->Psi, &reference_vec); CHKERRQ(ierr);
1908 ierr = SetAnalyticalScalarFieldAtCellCenters(user, reference_vec); CHKERRQ(ierr);
1909
1910 ierr = DMDAVecGetArrayRead(user->da, user->Psi, &psi); CHKERRQ(ierr);
1911 ierr = DMDAVecGetArrayRead(user->da, reference_vec, &psi_ref); CHKERRQ(ierr);
1912 ierr = DMDAVecGetArrayRead(user->da, user->Aj, &aj); CHKERRQ(ierr);
1913 ierr = DMDAVecGetArrayRead(user->da, user->ParticleCount, &count); CHKERRQ(ierr);
1914
1915 for (PetscInt k = lzs; k < lze; ++k) {
1916 for (PetscInt j = lys; j < lye; ++j) {
1917 for (PetscInt i = lxs; i < lxe; ++i) {
1918 const PetscReal cell_volume = (PetscAbsReal(aj[k][j][i]) > 1.0e-14) ? (1.0 / aj[k][j][i]) : 0.0;
1919 const PetscReal err = psi[k][j][i] - psi_ref[k][j][i];
1920 local_cell_count += 1;
1921 local_domain_volume += cell_volume;
1922 local_grid_integral += psi[k][j][i] * cell_volume;
1923 local_l1 += PetscAbsReal(err) * cell_volume;
1924 local_l2_sq += err * err * cell_volume;
1925 local_ref_l2_sq += psi_ref[k][j][i] * psi_ref[k][j][i] * cell_volume;
1926 local_linf = PetscMax(local_linf, PetscAbsReal(err));
1927 if (count[k][j][i] > 0.0) local_occupied_count += 1;
1928 }
1929 }
1930 }
1931
1932 ierr = DMDAVecRestoreArrayRead(user->da, user->ParticleCount, &count); CHKERRQ(ierr);
1933 ierr = DMDAVecRestoreArrayRead(user->da, user->Aj, &aj); CHKERRQ(ierr);
1934 ierr = DMDAVecRestoreArrayRead(user->da, reference_vec, &psi_ref); CHKERRQ(ierr);
1935 ierr = DMDAVecRestoreArrayRead(user->da, user->Psi, &psi); CHKERRQ(ierr);
1936 ierr = VecDestroy(&reference_vec); CHKERRQ(ierr);
1937
1938 ierr = DMSwarmGetLocalSize(user->swarm, &nlocal); CHKERRQ(ierr);
1939 local_particle_count = (PetscInt64)nlocal;
1940 if (nlocal > 0) {
1941 ierr = DMSwarmGetField(user->swarm, "Psi", NULL, NULL, (void **)&particle_psi); CHKERRQ(ierr);
1942 for (PetscInt p = 0; p < nlocal; ++p) local_particle_sum += particle_psi[p];
1943 ierr = DMSwarmRestoreField(user->swarm, "Psi", NULL, NULL, (void **)&particle_psi); CHKERRQ(ierr);
1944 }
1945
1946 ierr = MPI_Allreduce(&local_l1, &global_l1, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
1947 ierr = MPI_Allreduce(&local_l2_sq, &global_l2_sq, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
1948 ierr = MPI_Allreduce(&local_linf, &global_linf, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRMPI(ierr);
1949 ierr = MPI_Allreduce(&local_ref_l2_sq, &global_ref_l2_sq, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
1950 ierr = MPI_Allreduce(&local_grid_integral, &global_grid_integral, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
1951 ierr = MPI_Allreduce(&local_domain_volume, &global_domain_volume, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
1952 ierr = MPI_Allreduce(&local_particle_sum, &global_particle_sum, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
1953 ierr = MPI_Allreduce(&local_particle_count, &global_particle_count, 1, MPIU_INT64, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
1954 ierr = MPI_Allreduce(&local_cell_count, &global_cell_count, 1, MPIU_INT64, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
1955 ierr = MPI_Allreduce(&local_occupied_count, &global_occupied_count, 1, MPIU_INT64, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
1956
1957 l2_error = PetscSqrtReal(global_l2_sq);
1958 relative_l2_error = (global_ref_l2_sq > 0.0) ? (l2_error / PetscSqrtReal(global_ref_l2_sq)) : 0.0;
1959 occupancy_fraction = (global_cell_count > 0) ? ((PetscReal)global_occupied_count / (PetscReal)global_cell_count) : 0.0;
1960 mean_particles_per_occupied_cell =
1961 (global_occupied_count > 0) ? ((PetscReal)global_particle_count / (PetscReal)global_occupied_count) : 0.0;
1962 particle_integral =
1963 (global_particle_count > 0) ? (global_domain_volume * global_particle_sum / (PetscReal)global_particle_count) : 0.0;
1964
1965 if (simCtx->rank == 0) {
1966 char csv_path[PETSC_MAX_PATH_LEN + 32];
1967 FILE *f = NULL;
1968 ierr = PetscSNPrintf(csv_path, sizeof(csv_path), "%s/scatter_metrics.csv", simCtx->log_dir); CHKERRQ(ierr);
1969 f = fopen(csv_path, "a");
1970 if (f) {
1971 if (ftell(f) == 0) {
1972 fprintf(f,
1973 "step,time,total_particles,total_cells,occupied_cells,occupancy_fraction,"
1974 "mean_particles_per_occupied_cell,particle_integral,grid_integral,"
1975 "conservation_error_abs,L1_error,L2_error,Linf_error,relative_L2_error\n");
1976 }
1977 fprintf(f, "%d,%.6e,%lld,%lld,%lld,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e\n",
1978 (int)simCtx->step,
1979 (double)simCtx->ti,
1980 (long long)global_particle_count,
1981 (long long)global_cell_count,
1982 (long long)global_occupied_count,
1983 (double)occupancy_fraction,
1984 (double)mean_particles_per_occupied_cell,
1985 (double)particle_integral,
1986 (double)global_grid_integral,
1987 (double)PetscAbsReal(global_grid_integral - particle_integral),
1988 (double)global_l1,
1989 (double)l2_error,
1990 (double)global_linf,
1991 (double)relative_l2_error);
1992 fclose(f);
1993 }
1994 }
1995
1996 if (get_log_level() >= LOG_INFO) {
1997 LOG_ALLOW(GLOBAL, LOG_INFO, "Scatter relative L2 error: %.6e\n", (double)relative_l2_error);
1998 LOG_ALLOW(GLOBAL, LOG_INFO, "Scatter occupancy fraction: %.6e\n", (double)occupancy_fraction);
1999 }
2000
2001 PetscFunctionReturn(0);
2002}
2003
2004#undef __FUNCT__
2005#define __FUNCT__ "ResetSearchMetrics"
2006/**
2007 * @brief Implementation of \ref ResetSearchMetrics().
2008 * @details Full API contract (arguments, ownership, side effects) is documented with
2009 * the header declaration in `include/logging.h`.
2010 * @see ResetSearchMetrics()
2011 */
2012PetscErrorCode ResetSearchMetrics(SimCtx *simCtx)
2013{
2014 PetscFunctionBeginUser;
2015 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx cannot be NULL for ResetSearchMetrics.");
2016
2017 simCtx->searchMetrics.searchAttempts = 0;
2018 simCtx->searchMetrics.searchPopulation = 0;
2020 simCtx->searchMetrics.searchLostCount = 0;
2021 simCtx->searchMetrics.traversalStepsSum = 0;
2022 simCtx->searchMetrics.reSearchCount = 0;
2023 simCtx->searchMetrics.maxTraversalSteps = 0;
2025 simCtx->searchMetrics.tieBreakCount = 0;
2031
2032 PetscFunctionReturn(0);
2033}
2034
2035#undef __FUNCT__
2036#define __FUNCT__ "LOG_SEARCH_METRICS"
2037/**
2038 * @brief Implementation of \ref LOG_SEARCH_METRICS().
2039 * @details Full API contract (arguments, ownership, side effects) is documented with
2040 * the header declaration in `include/logging.h`.
2041 * @see LOG_SEARCH_METRICS()
2042 */
2043PetscErrorCode LOG_SEARCH_METRICS(UserCtx *user)
2044{
2045 PetscErrorCode ierr;
2046 SimCtx *simCtx = NULL;
2047 PetscInt totalParticles = 0;
2048 PetscReal local_metrics[SEARCH_METRIC_REDUCTION_LEN] = {0.0};
2049 PetscReal global_metrics[SEARCH_METRIC_REDUCTION_LEN] = {0.0};
2050 PetscReal meanTraversalSteps = 0.0;
2051 PetscReal searchFailureFraction = 0.0;
2052 PetscReal searchWorkIndex = 0.0;
2053 PetscReal reSearchFraction = 0.0;
2054 long long searchAttempts = 0;
2055 long long searchPopulation = 0;
2056 long long searchLocatedCount = 0;
2057 long long searchLostCount = 0;
2058 long long traversalStepsSum = 0;
2059 long long reSearchCount = 0;
2060 long long tieBreakCount = 0;
2061 long long boundaryClampCount = 0;
2062 long long bboxGuessSuccessCount = 0;
2063 long long bboxGuessFallbackCount = 0;
2064 long long maxTraversalFailCount = 0;
2065 long long maxTraversalSteps = 0;
2066 long long maxPassDepth = 0;
2067 MPI_Op reduction_op = MPI_OP_NULL;
2068
2069 PetscFunctionBeginUser;
2070 if (!user || !user->simCtx) {
2071 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "UserCtx and SimCtx are required for LOG_SEARCH_METRICS.");
2072 }
2073 simCtx = user->simCtx;
2074
2075 if (simCtx->np <= 0) {
2076 PetscFunctionReturn(0);
2077 }
2078
2079 ierr = DMSwarmGetSize(user->swarm, &totalParticles); CHKERRQ(ierr);
2080
2081 local_metrics[SEARCH_METRIC_SUM_SEARCH_ATTEMPTS] = (PetscReal)simCtx->searchMetrics.searchAttempts;
2082 local_metrics[SEARCH_METRIC_SUM_SEARCH_POPULATION] = (PetscReal)simCtx->searchMetrics.searchPopulation;
2083 local_metrics[SEARCH_METRIC_SUM_SEARCH_LOCATED] = (PetscReal)simCtx->searchMetrics.searchLocatedCount;
2084 local_metrics[SEARCH_METRIC_SUM_SEARCH_LOST] = (PetscReal)simCtx->searchMetrics.searchLostCount;
2085 local_metrics[SEARCH_METRIC_SUM_TRAVERSAL_STEPS] = (PetscReal)simCtx->searchMetrics.traversalStepsSum;
2086 local_metrics[SEARCH_METRIC_SUM_RESEARCH] = (PetscReal)simCtx->searchMetrics.reSearchCount;
2087 local_metrics[SEARCH_METRIC_SUM_TIE_BREAKS] = (PetscReal)simCtx->searchMetrics.tieBreakCount;
2088 local_metrics[SEARCH_METRIC_SUM_BOUNDARY_CLAMPS] = (PetscReal)simCtx->searchMetrics.boundaryClampCount;
2092 local_metrics[SEARCH_METRIC_MAX_TRAVERSAL_STEPS] = (PetscReal)simCtx->searchMetrics.maxTraversalSteps;
2093 local_metrics[SEARCH_METRIC_MAX_PASS_DEPTH] = (PetscReal)simCtx->searchMetrics.maxParticlePassDepth;
2094
2095 ierr = MPI_Op_create(SearchMetricsReduceOp, PETSC_TRUE, &reduction_op); CHKERRMPI(ierr);
2096 ierr = MPI_Allreduce(local_metrics, global_metrics, SEARCH_METRIC_REDUCTION_LEN, MPIU_REAL, reduction_op, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2097 ierr = MPI_Op_free(&reduction_op); CHKERRMPI(ierr);
2098 reduction_op = MPI_OP_NULL;
2099
2100 searchAttempts = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_SEARCH_ATTEMPTS] + 0.5);
2101 searchPopulation = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_SEARCH_POPULATION] + 0.5);
2102 searchLocatedCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_SEARCH_LOCATED] + 0.5);
2103 searchLostCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_SEARCH_LOST] + 0.5);
2104 traversalStepsSum = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_TRAVERSAL_STEPS] + 0.5);
2105 reSearchCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_RESEARCH] + 0.5);
2106 tieBreakCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_TIE_BREAKS] + 0.5);
2107 boundaryClampCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_BOUNDARY_CLAMPS] + 0.5);
2108 bboxGuessSuccessCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_BBOX_GUESS_SUCCESS] + 0.5);
2109 bboxGuessFallbackCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_BBOX_GUESS_FALLBACK] + 0.5);
2110 maxTraversalFailCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_MAX_TRAVERSAL_FAILS] + 0.5);
2111 maxTraversalSteps = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_MAX_TRAVERSAL_STEPS] + 0.5);
2112 maxPassDepth = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_MAX_PASS_DEPTH] + 0.5);
2113
2114 if (searchAttempts > 0) {
2115 meanTraversalSteps = (PetscReal)traversalStepsSum / (PetscReal)searchAttempts;
2116 }
2117 if (searchPopulation > 0) {
2118 searchFailureFraction = (PetscReal)searchLostCount / (PetscReal)searchPopulation;
2119 searchWorkIndex = (PetscReal)traversalStepsSum / (PetscReal)searchPopulation;
2120 reSearchFraction = (PetscReal)reSearchCount / (PetscReal)searchPopulation;
2121 }
2122
2123 if (simCtx->rank == 0) {
2124 char csv_path[PETSC_MAX_PATH_LEN + 32];
2125 FILE *f = NULL;
2126
2127 ierr = PetscSNPrintf(csv_path, sizeof(csv_path), "%s/search_metrics.csv", simCtx->log_dir); CHKERRQ(ierr);
2128 f = fopen(csv_path, "a");
2129 if (!f) {
2130 LOG_ALLOW(GLOBAL, LOG_WARNING, "LOG_SEARCH_METRICS: could not open '%s' for writing.\n", csv_path);
2131 } else {
2132 if (ftell(f) == 0) {
2133 fprintf(f,
2134 "step,time,total_particles,lost,lost_cumulative,migrated,migration_passes,search_attempts,"
2135 "mean_traversal_steps,max_traversal_steps,tie_break_count,boundary_clamp_count,"
2136 "bbox_guess_success_count,bbox_guess_fallback_count,max_particle_pass_depth,load_imbalance,"
2137 "search_population,search_located_count,search_lost_count,traversal_steps_sum,re_search_count,"
2138 "max_traversal_fail_count,search_failure_fraction,search_work_index,re_search_fraction\n");
2139 }
2140 fprintf(f,
2141 "%d,%.6e,%d,%d,%d,%d,%d,%lld,%.6e,%lld,%lld,%lld,%lld,%lld,%lld,%.6e,%lld,%lld,%lld,%lld,%lld,%lld,%.6e,%.6e,%.6e\n",
2142 (int)simCtx->step,
2143 (double)simCtx->ti,
2144 (int)totalParticles,
2145 (int)simCtx->particlesLostLastStep,
2146 (int)simCtx->particlesLostCumulative,
2147 (int)simCtx->particlesMigratedLastStep,
2148 (int)simCtx->migrationPassesLastStep,
2149 searchAttempts,
2150 (double)meanTraversalSteps,
2151 maxTraversalSteps,
2152 tieBreakCount,
2153 boundaryClampCount,
2154 bboxGuessSuccessCount,
2155 bboxGuessFallbackCount,
2156 maxPassDepth,
2157 (double)simCtx->particleLoadImbalance,
2158 searchPopulation,
2159 searchLocatedCount,
2160 searchLostCount,
2161 traversalStepsSum,
2162 reSearchCount,
2163 maxTraversalFailCount,
2164 (double)searchFailureFraction,
2165 (double)searchWorkIndex,
2166 (double)reSearchFraction);
2167 fclose(f);
2168 }
2169 }
2170
2172 "Search metrics: sff=%.3e swi=%.3e re_search=%.3e lost(step/total)=%d/%d migrated=%d passes=%d traversal(mean/max)=%.2f/%lld tie_breaks=%lld max_pass_depth=%lld\n",
2173 (double)searchFailureFraction,
2174 (double)searchWorkIndex,
2175 (double)reSearchFraction,
2176 (int)simCtx->particlesLostLastStep,
2177 (int)simCtx->particlesLostCumulative,
2178 (int)simCtx->particlesMigratedLastStep,
2179 (int)simCtx->migrationPassesLastStep,
2180 (double)meanTraversalSteps,
2181 maxTraversalSteps,
2182 tieBreakCount,
2183 maxPassDepth);
2184
2185 PetscFunctionReturn(0);
2186}
2187
2188#undef __FUNCT__
2189#define __FUNCT__ "CalculateAdvancedParticleMetrics"
2190/**
2191 * @brief Internal helper implementation: `CalculateAdvancedParticleMetrics()`.
2192 * @details Local to this translation unit.
2193 */
2195{
2196 PetscErrorCode ierr;
2197 SimCtx *simCtx = user->simCtx;
2198 PetscMPIInt size, rank;
2199
2200 PetscFunctionBeginUser;
2201 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size); CHKERRQ(ierr);
2202 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
2203
2204 // --- 1. Particle Load Imbalance ---
2205 PetscInt nLocal, nGlobal, nLocalMax;
2206 ierr = DMSwarmGetLocalSize(user->swarm, &nLocal); CHKERRQ(ierr);
2207 ierr = DMSwarmGetSize(user->swarm, &nGlobal); CHKERRQ(ierr);
2208 ierr = MPI_Allreduce(&nLocal, &nLocalMax, 1, MPIU_INT, MPI_MAX, PETSC_COMM_WORLD); CHKERRQ(ierr);
2209
2210 PetscReal avg_per_rank = (size > 0) ? ((PetscReal)nGlobal / size) : 0.0;
2211 // Handle division by zero if there are no particles
2212 simCtx->particleLoadImbalance = (avg_per_rank > 1e-9) ? (nLocalMax / avg_per_rank) : 1.0;
2213
2214
2215 // --- 2. Number of Occupied Cells ---
2216 // This part requires access to the user->ParticleCount vector.
2217 PetscInt local_occupied_cells = 0;
2218 PetscInt global_occupied_cells;
2219 const PetscScalar *count_array;
2220 PetscInt vec_local_size;
2221
2222 ierr = VecGetLocalSize(user->ParticleCount, &vec_local_size); CHKERRQ(ierr);
2223 ierr = VecGetArrayRead(user->ParticleCount, &count_array); CHKERRQ(ierr);
2224
2225 for (PetscInt i = 0; i < vec_local_size; ++i) {
2226 if (count_array[i] > 0.5) { // Use 0.5 to be safe with floating point
2227 local_occupied_cells++;
2228 }
2229 }
2230 ierr = VecRestoreArrayRead(user->ParticleCount, &count_array); CHKERRQ(ierr);
2231
2232 ierr = MPI_Allreduce(&local_occupied_cells, &global_occupied_cells, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
2233 simCtx->occupiedCellCount = global_occupied_cells;
2234
2235 LOG_ALLOW_SYNC(GLOBAL, LOG_INFO, "[Rank %d] Advanced Metrics: Imbalance=%.2f, OccupiedCells=%d\n", rank, simCtx->particleLoadImbalance, simCtx->occupiedCellCount);
2236
2237 PetscFunctionReturn(0);
2238}
2239
2240#undef __FUNCT__
2241#define __FUNCT__ "LOG_PARTICLE_METRICS"
2242/**
2243 * @brief Implementation of \ref LOG_PARTICLE_METRICS().
2244 * @details Full API contract (arguments, ownership, side effects) is documented with
2245 * the header declaration in `include/logging.h`.
2246 * @see LOG_PARTICLE_METRICS()
2247 */
2248PetscErrorCode LOG_PARTICLE_METRICS(UserCtx *user, const char *stageName)
2249{
2250 PetscErrorCode ierr;
2251 PetscMPIInt rank;
2252 SimCtx *simCtx = user->simCtx;
2253 const char *stage_label = (stageName && stageName[0] != '\0') ? stageName : "N/A";
2254
2255 PetscFunctionBeginUser;
2256 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
2257
2258 PetscInt totalParticles;
2259 ierr = DMSwarmGetSize(user->swarm, &totalParticles); CHKERRQ(ierr);
2260
2261 if (!rank) {
2262 FILE *f;
2263 char filen[PETSC_MAX_PATH_LEN + 64];
2264 ierr = PetscSNPrintf(filen, sizeof(filen), "%s/Particle_Metrics.log", simCtx->log_dir); CHKERRQ(ierr);
2265 f = fopen(filen, "a");
2266 if (!f) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open particle log file: %s", filen);
2267
2268 if (ftell(f) == 0) {
2269 PetscFPrintf(PETSC_COMM_SELF, f, "%-18s | %-10s | %-12s | %-10s | %-10s | %-10s | %-15s | %-10s | %-10s\n",
2270 "Stage", "Timestep", "Total Ptls", "Lost", "Lost Total", "Migrated", "Occupied Cells", "Imbalance", "Mig Passes");
2271 PetscFPrintf(PETSC_COMM_SELF, f, "-------------------------------------------------------------------------------------------------------------------------------------------\n");
2272 }
2273
2274 PetscFPrintf(PETSC_COMM_SELF, f, "%-18s | %-10d | %-12d | %-10d | %-10d | %-10d | %-15d | %-10.2f | %-10d\n",
2275 stage_label, (int)simCtx->step, (int)totalParticles, (int)simCtx->particlesLostLastStep,
2276 (int)simCtx->particlesLostCumulative, (int)simCtx->particlesMigratedLastStep, (int)simCtx->occupiedCellCount,
2277 (double)simCtx->particleLoadImbalance, (int)simCtx->migrationPassesLastStep);
2278 fclose(f);
2279 }
2280 PetscFunctionReturn(0);
2281}
PetscErrorCode SetAnalyticalScalarFieldAtCellCenters(UserCtx *user, Vec targetVec)
Writes the configured verification scalar profile at physical cell centers into a scalar Vec.
PetscErrorCode SetAnalyticalSolutionForParticles(Vec tempVec, SimCtx *simCtx)
Applies the analytical solution to particle velocity vector.
@ SEARCH_METRIC_SUM_SEARCH_LOCATED
Definition logging.c:33
@ SEARCH_METRIC_SUM_MAX_TRAVERSAL_FAILS
Definition logging.c:41
@ SEARCH_METRIC_REDUCTION_LEN
Definition logging.c:44
@ SEARCH_METRIC_SUM_BBOX_GUESS_FALLBACK
Definition logging.c:40
@ SEARCH_METRIC_SUM_RESEARCH
Definition logging.c:36
@ SEARCH_METRIC_SUM_TRAVERSAL_STEPS
Definition logging.c:35
@ SEARCH_METRIC_MAX_TRAVERSAL_STEPS
Definition logging.c:42
@ SEARCH_METRIC_SUM_BOUNDARY_CLAMPS
Definition logging.c:38
@ SEARCH_METRIC_SUM_SEARCH_POPULATION
Definition logging.c:32
@ SEARCH_METRIC_MAX_PASS_DEPTH
Definition logging.c:43
@ SEARCH_METRIC_SUM_TIE_BREAKS
Definition logging.c:37
@ SEARCH_METRIC_SUM_SEARCH_LOST
Definition logging.c:34
@ SEARCH_METRIC_SUM_BBOX_GUESS_SUCCESS
Definition logging.c:39
@ SEARCH_METRIC_SUM_SEARCH_ATTEMPTS
Definition logging.c:31
void set_allowed_functions(const char **functionList, int count)
Implementation of set_allowed_functions().
Definition logging.c:152
PetscBool always_log
Definition logging.c:995
PetscErrorCode LOG_PARTICLE_METRICS(UserCtx *user, const char *stageName)
Implementation of LOG_PARTICLE_METRICS().
Definition logging.c:2248
const char * BCHandlerTypeToString(BCHandlerType handler_type)
Internal helper implementation: BCHandlerTypeToString().
Definition logging.c:771
PetscBool is_function_allowed(const char *functionName)
Implementation of is_function_allowed().
Definition logging.c:183
static PetscInt g_profiler_count
Definition logging.c:1000
const char * FieldInitializationToString(PetscInt FieldInitialization)
Implementation of FieldInitializationToString().
Definition logging.c:687
PetscErrorCode DualMonitorDestroy(void **ctx)
Implementation of DualMonitorDestroy().
Definition logging.c:808
#define TMP_BUF_SIZE
Definition logging.c:7
static char ** gAllowedFunctions
Global/static array of function names allowed to log.
Definition logging.c:23
static LogLevel current_log_level
Static variable to cache the current logging level.
Definition logging.c:16
PetscErrorCode LOG_INTERPOLATION_ERROR(UserCtx *user)
Implementation of LOG_INTERPOLATION_ERROR().
Definition logging.c:1785
PetscBool ShouldEmitPeriodicParticleConsoleSnapshot(const SimCtx *simCtx, PetscInt completed_step)
Implementation of ShouldEmitPeriodicParticleConsoleSnapshot().
Definition logging.c:542
const char * BCFaceToString(BCFace face)
Implementation of BCFaceToString().
Definition logging.c:669
PetscErrorCode FreeAllowedFunctions(char **funcs, PetscInt n)
Internal helper implementation: FreeAllowedFunctions().
Definition logging.c:650
PetscBool IsParticleConsoleSnapshotEnabled(const SimCtx *simCtx)
Implementation of IsParticleConsoleSnapshotEnabled().
Definition logging.c:525
PetscErrorCode print_log_level(void)
Internal helper implementation: print_log_level().
Definition logging.c:116
long long total_call_count
Definition logging.c:992
PetscErrorCode EmitParticleConsoleSnapshot(UserCtx *user, SimCtx *simCtx, PetscInt step)
Implementation of EmitParticleConsoleSnapshot().
Definition logging.c:556
static PetscInt g_profiler_capacity
Definition logging.c:1001
PetscErrorCode ProfilingFinalize(SimCtx *simCtx)
Implementation of ProfilingFinalize().
Definition logging.c:1214
static void BuildRowFormatString(PetscMPIInt wRank, PetscInt wPID, PetscInt wCell, PetscInt wPos, PetscInt wVel, PetscInt wWt, char *fmtStr, size_t bufSize)
Definition logging.c:366
static void trim(char *s)
Internal helper implementation: trim().
Definition logging.c:571
PetscErrorCode LoadAllowedFunctionsFromFile(const char filename[], char ***funcsOut, PetscInt *nOut)
Implementation of LoadAllowedFunctionsFromFile().
Definition logging.c:596
PetscErrorCode LOG_FIELD_MIN_MAX(UserCtx *user, const char *fieldName)
Implementation of LOG_FIELD_MIN_MAX().
Definition logging.c:1367
static int gNumAllowed
Number of entries in the gAllowedFunctions array.
Definition logging.c:28
double total_time
Definition logging.c:990
static void BuildHeaderString(char *headerStr, size_t bufSize, PetscMPIInt wRank, PetscInt wPID, PetscInt wCell, PetscInt wPos, PetscInt wVel, PetscInt wWt)
Definition logging.c:379
void PrintProgressBar(PetscInt step, PetscInt startStep, PetscInt totalSteps, PetscReal currentTime)
Internal helper implementation: PrintProgressBar().
Definition logging.c:1320
PetscErrorCode LOG_FIELD_ANATOMY(UserCtx *user, const char *field_name, const char *stage_name)
Implementation of LOG_FIELD_ANATOMY().
Definition logging.c:1515
static PetscErrorCode _FindOrCreateEntry(const char *func_name, PetscInt *idx)
Internal helper implementation: _FindOrCreateEntry().
Definition logging.c:1008
static void CellToStr(const PetscInt *cell, char *buf, size_t bufsize)
Definition logging.c:269
LogLevel get_log_level()
Implementation of get_log_level().
Definition logging.c:84
PetscErrorCode ProfilingLogTimestepSummary(SimCtx *simCtx, PetscInt step)
Implementation of ProfilingLogTimestepSummary().
Definition logging.c:1121
PetscErrorCode LOG_FACE_DISTANCES(PetscReal *d)
Implementation of LOG_FACE_DISTANCES().
Definition logging.c:230
PetscErrorCode LOG_PARTICLE_FIELDS(UserCtx *user, PetscInt printInterval)
Implementation of LOG_PARTICLE_FIELDS().
Definition logging.c:397
void _ProfilingEnd(const char *func_name)
Implementation of _ProfilingEnd().
Definition logging.c:1082
static void TripleRealToStr(const PetscReal *arr, char *buf, size_t bufsize)
Definition logging.c:277
static void Int64ToStr(PetscInt64 value, char *buf, size_t bufsize)
Definition logging.c:261
const char * BCTypeToString(BCType type)
Implementation of BCTypeToString().
Definition logging.c:751
PetscErrorCode CalculateAdvancedParticleMetrics(UserCtx *user)
Internal helper implementation: CalculateAdvancedParticleMetrics().
Definition logging.c:2194
const char * ParticleLocationStatusToString(ParticleLocationStatus level)
Implementation of ParticleLocationStatusToString().
Definition logging.c:973
PetscErrorCode LOG_SCATTER_METRICS(UserCtx *user)
Implementation of LOG_SCATTER_METRICS().
Definition logging.c:1861
static int _CompareProfiledFunctions(const void *a, const void *b)
Internal helper implementation: _CompareProfiledFunctions().
Definition logging.c:1198
PetscErrorCode DualKSPMonitor(KSP ksp, PetscInt it, PetscReal rnorm, void *ctx)
Implementation of DualKSPMonitor().
Definition logging.c:847
PetscErrorCode LOG_CONTINUITY_METRICS(UserCtx *user)
Implementation of LOG_CONTINUITY_METRICS().
Definition logging.c:914
double current_step_time
Definition logging.c:991
long long current_step_call_count
Definition logging.c:993
PetscErrorCode LOG_SEARCH_METRICS(UserCtx *user)
Implementation of LOG_SEARCH_METRICS().
Definition logging.c:2043
static ProfiledFunction * g_profiler_registry
Definition logging.c:999
PetscErrorCode ProfilingInitialize(SimCtx *simCtx)
Internal helper implementation: ProfilingInitialize().
Definition logging.c:1044
const char * LESModelToString(LESModelType LESFlag)
Implementation of LESModelToString().
Definition logging.c:720
PetscErrorCode LOG_CELL_VERTICES(const Cell *cell, PetscMPIInt rank)
Implementation of LOG_CELL_VERTICES().
Definition logging.c:205
static void IntToStr(int value, char *buf, size_t bufsize)
Definition logging.c:253
PetscErrorCode ProfilingResetTimestepCounters(void)
Implementation of ProfilingResetTimestepCounters().
Definition logging.c:1104
PetscErrorCode ResetSearchMetrics(SimCtx *simCtx)
Implementation of ResetSearchMetrics().
Definition logging.c:2012
const char * MomentumSolverTypeToString(MomentumSolverType SolverFlag)
Implementation of MomentumSolverTypeToString().
Definition logging.c:736
static PetscErrorCode ComputeMaxColumnWidths(PetscInt nParticles, const PetscMPIInt *ranks, const PetscInt64 *pids, const PetscInt *cellIDs, const PetscReal *positions, const PetscReal *velocities, const PetscReal *weights, int *wRank, int *wPID, int *wCell, int *wPos, int *wVel, int *wWt)
Definition logging.c:302
double start_time
Definition logging.c:994
const char * ParticleInitializationToString(ParticleInitializationType ParticleInitialization)
Implementation of ParticleInitializationToString().
Definition logging.c:703
void _ProfilingStart(const char *func_name)
Implementation of _ProfilingStart().
Definition logging.c:1068
static void SearchMetricsReduceOp(void *invec, void *inoutvec, int *len, MPI_Datatype *datatype)
Internal reduction callback for packed search metrics.
Definition logging.c:51
const char * name
Definition logging.c:989
Logging utilities and macros for PETSc-based applications.
#define LOG_ALLOW_SYNC(scope, level, fmt,...)
Synchronized logging macro that checks both the log level and whether the calling function is in the ...
Definition logging.h:252
PetscBool log_to_console
Definition logging.h:57
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:44
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:45
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:199
PetscReal bnorm
Definition logging.h:58
PetscInt step
Definition logging.h:59
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:83
LogLevel
Enumeration of logging levels.
Definition logging.h:27
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:28
@ LOG_TRACE
Very fine-grained tracing information for in-depth debugging.
Definition logging.h:32
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:30
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:29
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:31
@ LOG_VERBOSE
Extremely detailed logs, typically for development use only.
Definition logging.h:33
FILE * file_handle
Definition logging.h:56
PetscInt block_id
Definition logging.h:60
Context for a dual-purpose KSP monitor.
Definition logging.h:55
LESModelType
Identifies the six logical faces of a structured computational block.
Definition variables.h:488
@ DYNAMIC_SMAGORINSKY
Definition variables.h:491
@ NO_LES_MODEL
Definition variables.h:489
@ CONSTANT_SMAGORINSKY
Definition variables.h:490
Vec lDiffusivityGradient
Definition variables.h:841
Vec lCent
Definition variables.h:858
BCType
Defines the general mathematical/physical Category of a boundary.
Definition variables.h:251
@ INLET
Definition variables.h:258
@ INTERFACE
Definition variables.h:253
@ FARFIELD
Definition variables.h:259
@ OUTLET
Definition variables.h:257
@ PERIODIC
Definition variables.h:260
@ WALL
Definition variables.h:254
PetscBool continueMode
Definition variables.h:660
PetscBool profilingFinalSummary
Definition variables.h:780
PetscMPIInt rank
Definition variables.h:646
char profilingTimestepFile[PETSC_MAX_PATH_LEN]
Definition variables.h:779
PetscInt64 searchLocatedCount
Definition variables.h:224
PetscInt64 searchLostCount
Definition variables.h:225
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:814
ParticleInitializationType
Enumerator to identify the particle initialization strategy.
Definition variables.h:508
@ PARTICLE_INIT_SURFACE_RANDOM
Random placement on the inlet face.
Definition variables.h:509
@ PARTICLE_INIT_SURFACE_EDGES
Deterministic placement at inlet face edges.
Definition variables.h:512
@ PARTICLE_INIT_POINT_SOURCE
All particles at a fixed (psrc_x,psrc_y,psrc_z) — for validation.
Definition variables.h:511
@ PARTICLE_INIT_VOLUME
Random volumetric distribution across the domain.
Definition variables.h:510
ParticleLocationStatus
Defines the state of a particle with respect to its location and migration status during the iterativ...
Definition variables.h:135
@ LOST
Definition variables.h:139
@ NEEDS_LOCATION
Definition variables.h:136
@ ACTIVE_AND_LOCATED
Definition variables.h:137
@ UNINITIALIZED
Definition variables.h:140
@ MIGRATING_OUT
Definition variables.h:138
PetscReal FluxOutSum
Definition variables.h:721
Vec Centz
Definition variables.h:859
PetscInt64 boundaryClampCount
Definition variables.h:231
PetscInt particlesLostLastStep
Definition variables.h:746
PetscInt KM
Definition variables.h:820
Vec lZet
Definition variables.h:858
PetscInt64 traversalStepsSum
Definition variables.h:226
BCHandlerType
Defines the specific computational "strategy" for a boundary handler.
Definition variables.h:271
@ BC_HANDLER_INLET_PULSATILE_FLUX
Definition variables.h:280
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:284
@ BC_HANDLER_INLET_PARABOLIC
Definition variables.h:277
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
Definition variables.h:276
@ BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX
Definition variables.h:287
@ BC_HANDLER_INTERFACE_OVERSET
Definition variables.h:285
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:286
@ BC_HANDLER_WALL_MOVING
Definition variables.h:274
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:273
@ BC_HANDLER_OUTLET_CONSERVATION
Definition variables.h:282
@ BC_HANDLER_FARFIELD_NONREFLECTING
Definition variables.h:281
@ BC_HANDLER_OUTLET_PRESSURE
Definition variables.h:283
@ BC_HANDLER_SYMMETRY_PLANE
Definition variables.h:275
@ BC_HANDLER_UNDEFINED
Definition variables.h:272
Vec lCellFieldAtCorner
Definition variables.h:846
PetscInt _this
Definition variables.h:824
PetscInt64 searchPopulation
Definition variables.h:223
PetscReal dt
Definition variables.h:658
PetscInt occupiedCellCount
Definition variables.h:750
char profilingTimestepMode[32]
Definition variables.h:778
Vec lPsi
Definition variables.h:883
PetscInt currentSettlementPass
Definition variables.h:235
PetscInt np
Definition variables.h:739
Vec DiffusivityGradient
Definition variables.h:841
PetscInt StartStep
Definition variables.h:653
MomentumSolverType
Enumerator to identify the implemented momentum solver strategies.
Definition variables.h:502
@ MOMENTUM_SOLVER_EXPLICIT_RK
Definition variables.h:503
@ MOMENTUM_SOLVER_DUALTIME_PICARD_RK4
Definition variables.h:504
PetscScalar x
Definition variables.h:101
PetscInt64 reSearchCount
Definition variables.h:227
PetscReal MaxDiv
Definition variables.h:771
Vec Centx
Definition variables.h:859
PetscInt64 bboxGuessFallbackCount
Definition variables.h:233
PetscInt MaxDivx
Definition variables.h:772
PetscInt MaxDivy
Definition variables.h:772
PetscInt64 bboxGuessSuccessCount
Definition variables.h:232
PetscInt MaxDivz
Definition variables.h:772
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:668
PetscInt MaxDivFlatArg
Definition variables.h:772
PetscReal FluxInSum
Definition variables.h:721
PetscInt64 maxParticlePassDepth
Definition variables.h:234
Vec lCsi
Definition variables.h:858
PetscInt64 maxTraversalSteps
Definition variables.h:228
PetscScalar z
Definition variables.h:101
Vec Ucat
Definition variables.h:837
Vec ParticleCount
Definition variables.h:882
Vec CellFieldAtCorner
Definition variables.h:846
PetscInt JM
Definition variables.h:820
char ** profilingSelectedFuncs
Definition variables.h:776
PetscInt particlesLostCumulative
Definition variables.h:747
PetscInt nProfilingSelectedFuncs
Definition variables.h:777
PetscInt particlesMigratedLastStep
Definition variables.h:749
PetscInt particleConsoleOutputFreq
Definition variables.h:656
SearchMetricsState searchMetrics
Definition variables.h:752
Vec lUcont
Definition variables.h:837
PetscInt step
Definition variables.h:651
Vec Diffusivity
Definition variables.h:840
DMDALocalInfo info
Definition variables.h:818
Vec lUcat
Definition variables.h:837
PetscInt migrationPassesLastStep
Definition variables.h:748
PetscScalar y
Definition variables.h:101
@ EXEC_MODE_SOLVER
Definition variables.h:616
@ EXEC_MODE_POSTPROCESSOR
Definition variables.h:617
PetscInt IM
Definition variables.h:820
@ TOP
Definition variables.h:145
@ FRONT
Definition variables.h:145
@ BOTTOM
Definition variables.h:145
@ BACK
Definition variables.h:145
@ LEFT
Definition variables.h:145
@ RIGHT
Definition variables.h:145
Vec lEta
Definition variables.h:858
Vec lDiffusivity
Definition variables.h:840
Vec Centy
Definition variables.h:859
PetscInt64 searchAttempts
Definition variables.h:222
ExecutionMode exec_mode
Definition variables.h:662
PetscInt64 tieBreakCount
Definition variables.h:230
PetscReal ti
Definition variables.h:652
PetscReal summationRHS
Definition variables.h:770
PetscInt64 maxTraversalFailCount
Definition variables.h:229
PetscInt LoggingFrequency
Definition variables.h:769
Cmpnts vertices[8]
Coordinates of the eight vertices of the cell.
Definition variables.h:161
Vec Psi
Definition variables.h:883
PetscReal particleLoadImbalance
Definition variables.h:751
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:244
@ BC_FACE_NEG_X
Definition variables.h:245
@ BC_FACE_POS_Z
Definition variables.h:247
@ BC_FACE_POS_Y
Definition variables.h:246
@ BC_FACE_NEG_Z
Definition variables.h:247
@ BC_FACE_POS_X
Definition variables.h:245
@ BC_FACE_NEG_Y
Definition variables.h:246
Defines the vertices of a single hexahedral grid cell.
Definition variables.h:160
A 3D point or vector with PetscScalar components.
Definition variables.h:100
The master context for the entire simulation.
Definition variables.h:643
User-defined context containing data specific to a single computational grid level.
Definition variables.h:811
PetscBool VerificationScalarOverrideActive(const SimCtx *simCtx)
Reports whether a verification-only scalar override is active.