PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Data Structures | Macros | Enumerations | Functions
logging.h File Reference

Logging utilities and macros for PETSc-based applications. More...

#include <petsc.h>
#include <stdlib.h>
#include <string.h>
#include <petscsys.h>
#include <ctype.h>
#include "variables.h"
#include "Boundaries.h"
Include dependency graph for logging.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  DualMonitorCtx
 Context for a dual-purpose KSP monitor. More...
 

Macros

#define LOCAL   0
 Logging scope definitions for controlling message output.
 
#define GLOBAL   1
 Scope for global logging across all processes.
 
#define LOG(scope, level, fmt, ...)
 Logging macro for PETSc-based applications with scope control.
 
#define LOG_DEFAULT(level, fmt, ...)
 Default logging macro for PETSc-based applications.
 
#define LOG_SYNC(scope, level, fmt, ...)
 Logging macro for PETSc-based applications with scope control, using synchronized output across processes.
 
#define LOG_SYNC_DEFAULT(level, fmt, ...)
 Default synchronized logging macro for PETSc-based applications.
 
#define LOG_ALLOW(scope, level, fmt, ...)
 Logging macro that checks both the log level and whether the calling function is in the allowed-function list before printing.
 
#define LOG_ALLOW_SYNC(scope, level, fmt, ...)
 Synchronized logging macro that checks both the log level and whether the calling function is in the allow-list.
 
#define LOG_LOOP_ALLOW(scope, level, iterVar, interval, fmt, ...)
 Logs a message inside a loop, but only every interval iterations.
 
#define LOG_LOOP_ALLOW_EXACT(scope, level, var, val, fmt, ...)
 Logs a custom message if a variable equals a specific value.
 
#define LOG_ARRAY_ELEMENT_ALLOW(scope, level, arr, length, idx, fmt)
 Logs a single element of an array, given an index.
 
#define LOG_ARRAY_SUBRANGE_ALLOW(scope, level, arr, length, start, end, fmt)
 Logs a consecutive subrange of an array.
 
#define PROFILE_FUNCTION_BEGIN    _ProfilingStart(__FUNCT__)
 Marks the beginning of a profiled code block (typically a function).
 
#define PROFILE_FUNCTION_END    _ProfilingEnd(__FUNCT__)
 Marks the end of a profiled code block.
 

Enumerations

enum  LogLevel {
  LOG_ERROR = 0 , LOG_WARNING , LOG_INFO , LOG_DEBUG ,
  LOG_TRACE , LOG_VERBOSE
}
 Enumeration of logging levels. More...
 

Functions

LogLevel get_log_level ()
 Retrieves the current logging level from the environment variable LOG_LEVEL.
 
PetscErrorCode print_log_level (void)
 Prints the current logging level to the console.
 
void set_allowed_functions (const char **functionList, int count)
 Sets the global list of function names that are allowed to log.
 
PetscBool is_function_allowed (const char *functionName)
 Checks if a given function is in the allow-list.
 
PetscErrorCode LOG_CELL_VERTICES (const Cell *cell, PetscMPIInt rank)
 Prints the coordinates of a cell's vertices.
 
PetscErrorCode LOG_FACE_DISTANCES (PetscReal *d)
 Prints the signed distances to each face of the cell.
 
PetscErrorCode LOG_PARTICLE_FIELDS (UserCtx *user, PetscInt printInterval)
 Prints particle fields in a table that automatically adjusts its column widths.
 
PetscBool IsParticleConsoleSnapshotEnabled (const SimCtx *simCtx)
 Returns whether periodic particle console snapshots are enabled.
 
PetscBool ShouldEmitPeriodicParticleConsoleSnapshot (const SimCtx *simCtx, PetscInt completed_step)
 Returns whether a particle console snapshot should be emitted for the.
 
PetscErrorCode EmitParticleConsoleSnapshot (UserCtx *user, SimCtx *simCtx, PetscInt step)
 Emits one particle console snapshot into the main solver log.
 
PetscErrorCode FreeAllowedFunctions (char **funcs, PetscInt n)
 Free an array previously returned by LoadAllowedFunctionsFromFile().
 
PetscErrorCode LoadAllowedFunctionsFromFile (const char filename[], char ***funcsOut, PetscInt *nOut)
 Load function names from a text file.
 
const char * BCFaceToString (BCFace face)
 Helper function to convert BCFace enum to a string representation.
 
const char * InitialConditionModeToString (InitialConditionMode mode)
 Convert an initial-condition mode to a string representation.
 
const char * FlowDirectionToString (FlowDirection fd)
 Convert a FlowDirection enum value to its YAML token string.
 
const char * ParticleInitializationToString (ParticleInitializationType ParticleInitialization)
 Helper function to convert ParticleInitialization to a string representation.
 
const char * LESModelToString (LESModelType LESFlag)
 Helper function to convert LES Flag to a string representation.
 
const char * MomentumSolverTypeToString (MomentumSolverType SolverFlag)
 Helper function to convert Momentum Solver flag to a string representation.
 
const char * BCTypeToString (BCType type)
 Helper function to convert BCType enum to a string representation.
 
const char * BCHandlerTypeToString (BCHandlerType handler_type)
 Converts a BCHandlerType enum to its string representation.
 
PetscErrorCode DualKSPMonitor (KSP ksp, PetscInt it, PetscReal rnorm, void *ctx)
 A custom KSP monitor that logs to a file and optionally to the console.
 
PetscErrorCode DualMonitorDestroy (void **ctx)
 Destroys the DualMonitorCtx.
 
PetscErrorCode LOG_CONTINUITY_METRICS (UserCtx *user)
 Logs continuity metrics for a single block to a file.
 
PetscErrorCode LOG_SOLUTION_CONVERGENCE (SimCtx *simCtx)
 Logs physical solution-convergence metrics once per completed timestep.
 
const char * ParticleLocationStatusToString (ParticleLocationStatus level)
 A function that outputs the name of the current level in the ParticleLocation enum.
 
void PrintProgressBar (PetscInt step, PetscInt startStep, PetscInt totalSteps, PetscReal currentTime)
 Prints a progress bar to the console.
 
PetscErrorCode ProfilingInitialize (SimCtx *simCtx)
 Initializes the custom profiling system using configuration from SimCtx.
 
PetscErrorCode ProfilingResetTimestepCounters (void)
 Resets per-timestep profiling counters for the next solver step.
 
PetscErrorCode ProfilingLogTimestepSummary (SimCtx *simCtx, PetscInt step)
 Logs the performance summary for the current timestep and resets timers.
 
PetscErrorCode RuntimeMemoryLogSample (SimCtx *simCtx, PetscInt step, const char *event, const char *reason)
 Append a reduced runtime memory sample to the configured memory log.
 
PetscErrorCode ProfilingFinalize (SimCtx *simCtx)
 the profiling excercise and build a profiling summary which is then printed to a log file.
 
void _ProfilingStart (const char *func_name)
 Internal profiling hook invoked by PROFILE_FUNCTION_BEGIN.
 
void _ProfilingEnd (const char *func_name)
 Internal profiling hook invoked by PROFILE_FUNCTION_END.
 
PetscErrorCode LOG_FIELD_MIN_MAX (UserCtx *user, const char *fieldName)
 Computes and logs the local and global min/max values of a 3-component vector field.
 
PetscErrorCode LOG_FIELD_ANATOMY (UserCtx *user, const char *field_name, const char *stage_name)
 Logs the anatomy of a specified field at key boundary locations, respecting the solver's specific grid and variable architecture.
 
PetscErrorCode LOG_INTERPOLATION_ERROR (UserCtx *user)
 Logs the interpolation error between the analytical and computed solutions.
 
PetscErrorCode LOG_SCATTER_METRICS (UserCtx *user)
 Logs particle-to-grid scatter verification metrics for the prescribed scalar truth path.
 
PetscErrorCode ResetSearchMetrics (SimCtx *simCtx)
 Resets the aggregate per-timestep search instrumentation counters.
 
PetscErrorCode CalculateAdvancedParticleMetrics (UserCtx *user)
 Computes advanced particle statistics and stores them in SimCtx.
 
PetscErrorCode LOG_SEARCH_METRICS (UserCtx *user)
 Writes compact runtime search metrics to CSV and optionally to console.
 
PetscErrorCode LOG_PARTICLE_METRICS (UserCtx *user, const char *stageName)
 Logs particle swarm metrics, adapting its behavior based on a boolean flag in SimCtx.
 

Detailed Description

Logging utilities and macros for PETSc-based applications.

This header defines logging levels, scopes, and macros for consistent logging throughout the application. It provides functions to retrieve the current logging level and macros to simplify logging with scope control.

Definition in file logging.h.


Data Structure Documentation

◆ DualMonitorCtx

struct DualMonitorCtx

Context for a dual-purpose KSP monitor.

This struct holds a file viewer for unconditional logging and a boolean flag to enable/disable optional logging to the console.

Definition at line 55 of file logging.h.

Data Fields
FILE * file_handle
PetscBool log_to_console
PetscReal bnorm
PetscInt step
PetscInt block_id

Macro Definition Documentation

◆ LOCAL

#define LOCAL   0

Logging scope definitions for controlling message output.

  • LOCAL: Logs on the current process using MPI_COMM_SELF.
  • GLOBAL: Logs across all processes using MPI_COMM_WORLD. Scope for local logging on the current process.

Definition at line 44 of file logging.h.

◆ GLOBAL

#define GLOBAL   1

Scope for global logging across all processes.

Definition at line 45 of file logging.h.

◆ LOG

#define LOG (   scope,
  level,
  fmt,
  ... 
)
Value:
do { \
/* Determine the MPI communicator based on the scope */ \
MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
/* Check if the log level is within the allowed range */ \
if ((int)(level) <= (int)get_log_level()) { \
/* Print the message to the specified communicator */ \
PetscPrintf(comm, fmt, ##__VA_ARGS__); \
} \
} while (0)
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:44
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:84

Logging macro for PETSc-based applications with scope control.

This macro provides a convenient way to log messages with different scopes (LOCAL or GLOBAL) and severity levels. It utilizes PETSc's PetscPrintf function for message output.

Parameters
scopeSpecifies the logging scope:
  • LOCAL: Logs on the current process using MPI_COMM_SELF.
  • GLOBAL: Logs on all processes using MPI_COMM_WORLD.
levelThe severity level of the message (e.g., LOG_INFO, LOG_ERROR).
fmtThe format string for the message (similar to printf).
...Additional arguments for the format string (optional).

Example usage: LOG(LOCAL, LOG_ERROR, "An error occurred at index %ld.\n", idx); LOG(GLOBAL, LOG_INFO, "Grid size: %ld x %ld x %ld.\n", nx, ny, nz);

Definition at line 83 of file logging.h.

84 { \
85 /* Determine the MPI communicator based on the scope */ \
86 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
87 /* Check if the log level is within the allowed range */ \
88 if ((int)(level) <= (int)get_log_level()) { \
89 /* Print the message to the specified communicator */ \
90 PetscPrintf(comm, fmt, ##__VA_ARGS__); \
91 } \
92 } while (0)

◆ LOG_DEFAULT

#define LOG_DEFAULT (   level,
  fmt,
  ... 
)
Value:
do { \
/* Set the communicator to global (MPI_COMM_WORLD) by default */ \
MPI_Comm comm = MPI_COMM_WORLD; \
/* Check if the log level is within the allowed range */ \
if ((int)(level) <= (int)get_log_level()) { \
/* Print the message using PetscPrintf with the global communicator */ \
PetscPrintf(comm, fmt, ##__VA_ARGS__); \
} \
} while (0)

Default logging macro for PETSc-based applications.

This macro simplifies logging by defaulting the scope to GLOBAL (i.e., MPI_COMM_WORLD) and providing a convenient interface for common logging needs.

Parameters
levelThe severity level of the message (e.g., LOG_ERROR, LOG_INFO).
fmtThe format string for the log message (similar to printf).
...Additional arguments for the format string (optional).

Example usage: LOG_DEFAULT(LOG_ERROR, "Error occurred at index %ld.\n", idx); LOG_DEFAULT(LOG_INFO, "Grid size: %ld x %ld x %ld.\n", nx, ny, nz);

Note
  • By default, this macro logs across all MPI processes using MPI_COMM_WORLD.
  • If finer control (e.g., local logging) is required, use the more general LOG macro.
  • The log level is filtered based on the value returned by get_log_level().

Definition at line 114 of file logging.h.

115 { \
116 /* Set the communicator to global (MPI_COMM_WORLD) by default */ \
117 MPI_Comm comm = MPI_COMM_WORLD; \
118 /* Check if the log level is within the allowed range */ \
119 if ((int)(level) <= (int)get_log_level()) { \
120 /* Print the message using PetscPrintf with the global communicator */ \
121 PetscPrintf(comm, fmt, ##__VA_ARGS__); \
122 } \
123 } while (0)

◆ LOG_SYNC

#define LOG_SYNC (   scope,
  level,
  fmt,
  ... 
)
Value:
do { \
/* Determine the MPI communicator based on the scope */ \
MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
/* Check if the log level is within the allowed range */ \
if ((int)(level) <= (int)get_log_level()) { \
/* Synchronized print (collective) on the specified communicator */ \
PetscSynchronizedPrintf(comm, fmt, ##__VA_ARGS__); \
/* Ensure all ranks have finished printing before continuing */ \
PetscSynchronizedFlush(comm, PETSC_STDOUT); \
} \
} while (0)

Logging macro for PETSc-based applications with scope control, using synchronized output across processes.

This macro uses PetscSynchronizedPrintf and PetscSynchronizedFlush to ensure messages from different ranks are printed in a synchronized (rank-by-rank) manner, preventing interleaved outputs.

Parameters
scopeSpecifies the logging scope:
  • LOCAL: Logs on the current process using MPI_COMM_SELF.
  • GLOBAL: Logs on all processes using MPI_COMM_WORLD.
levelThe severity level of the message (e.g., LOG_INFO, LOG_ERROR).
fmtThe format string for the message (similar to printf).
...Additional arguments for the format string (optional).

Example usage: LOG_SYNC(LOCAL, LOG_ERROR, "An error occurred at index %ld.\n", idx); LOG_SYNC(GLOBAL, LOG_INFO, "Synchronized info: rank = %ld.\n", rank);

Definition at line 144 of file logging.h.

145 { \
146 /* Determine the MPI communicator based on the scope */ \
147 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
148 /* Check if the log level is within the allowed range */ \
149 if ((int)(level) <= (int)get_log_level()) { \
150 /* Synchronized print (collective) on the specified communicator */ \
151 PetscSynchronizedPrintf(comm, fmt, ##__VA_ARGS__); \
152 /* Ensure all ranks have finished printing before continuing */ \
153 PetscSynchronizedFlush(comm, PETSC_STDOUT); \
154 } \
155 } while (0)

◆ LOG_SYNC_DEFAULT

#define LOG_SYNC_DEFAULT (   level,
  fmt,
  ... 
)
Value:
do { \
if ((int)(level) <= (int)get_log_level()) { \
PetscSynchronizedPrintf(MPI_COMM_WORLD, fmt, ##__VA_ARGS__); \
PetscSynchronizedFlush(MPI_COMM_WORLD, PETSC_STDOUT); \
} \
} while (0)

Default synchronized logging macro for PETSc-based applications.

This macro simplifies logging by defaulting the scope to GLOBAL (i.e., MPI_COMM_WORLD) and provides synchronized output across all processes.

Parameters
levelThe severity level of the message (e.g., LOG_ERROR, LOG_INFO).
fmtThe format string for the log message (similar to printf).
...Additional arguments for the format string (optional).

Example usage: LOG_SYNC_DEFAULT(LOG_ERROR, "Error at index %ld.\n", idx); LOG_SYNC_DEFAULT(LOG_INFO, "Process rank: %ld.\n", rank);

Note
  • By default, this macro logs across all MPI processes using MPI_COMM_WORLD.
  • If local (per-process) logging is required, use the more general LOG_SYNC macro.
  • The log level is filtered based on the value returned by get_log_level().

Definition at line 177 of file logging.h.

178 { \
179 if ((int)(level) <= (int)get_log_level()) { \
180 PetscSynchronizedPrintf(MPI_COMM_WORLD, fmt, ##__VA_ARGS__); \
181 PetscSynchronizedFlush(MPI_COMM_WORLD, PETSC_STDOUT); \
182 } \
183 } while (0)

◆ LOG_ALLOW

#define LOG_ALLOW (   scope,
  level,
  fmt,
  ... 
)
Value:
do { \
MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
if ((int)(level) <= (int)get_log_level() && is_function_allowed(__func__)) { \
PetscPrintf(comm, "[%s] " fmt, __func__, ##__VA_ARGS__); \
} \
} while (0)
PetscBool is_function_allowed(const char *functionName)
Checks if a given function is in the allow-list.
Definition logging.c:183

Logging macro that checks both the log level and whether the calling function is in the allowed-function list before printing.

Useful for selective, per-function logging.

Parameters
scopeSpecifies the logging scope (LOCAL or GLOBAL).
levelThe severity level of the message (e.g., LOG_INFO, LOG_ERROR).
fmtThe format string for the message (similar to printf).
...Additional arguments for the format string (optional).

Example usage: LOG_ALLOW(LOCAL, LOG_DEBUG, "Debugging info in function: %s\n", func);

Definition at line 199 of file logging.h.

200 { \
201 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
202 if ((int)(level) <= (int)get_log_level() && is_function_allowed(__func__)) { \
203 PetscPrintf(comm, "[%s] " fmt, __func__, ##__VA_ARGS__); \
204 } \
205 } while (0)

◆ LOG_ALLOW_SYNC

#define LOG_ALLOW_SYNC (   scope,
  level,
  fmt,
  ... 
)
Value:
do { \
/* ------------------------------------------------------------------ */ \
/* Validate scope and pick communicator *before* any early exits. */ \
/* ------------------------------------------------------------------ */ \
MPI_Comm _comm; \
if ((scope) == LOCAL) _comm = MPI_COMM_SELF; \
else if ((scope) == GLOBAL) _comm = MPI_COMM_WORLD; \
else { \
fprintf(stderr, "LOG_ALLOW_SYNC ERROR: invalid scope (%d) at %s:%d\n", \
(scope), __FILE__, __LINE__); \
MPI_Abort(MPI_COMM_WORLD, 1); \
} \
\
/* ------------------------------------------------------------------ */ \
/* Decide whether *this* rank should actually print. */ \
/* ------------------------------------------------------------------ */ \
PetscBool _doPrint = \
is_function_allowed(__func__) && ((int)(level) <= (int)get_log_level()); \
\
if (_doPrint) { \
PetscSynchronizedPrintf(_comm, "[%s] " fmt, __func__, ##__VA_ARGS__); \
} \
\
/* ------------------------------------------------------------------ */ \
/* ALL ranks call the flush, even if they printed nothing. */ \
/* ------------------------------------------------------------------ */ \
PetscSynchronizedFlush(_comm, PETSC_STDOUT); \
} while (0)
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:45

Synchronized logging macro that checks both the log level and whether the calling function is in the allow-list.

This macro uses PetscSynchronizedPrintf and PetscSynchronizedFlush to ensure messages from different ranks are printed in a rank-ordered fashion (i.e., to avoid interleaving). It also filters out messages if the current function is not in the allow-list (is_function_allowed(__func__)) or the requested log level is higher than get_log_level().

Parameters
scopeEither LOCAL (MPI_COMM_SELF) or GLOBAL (MPI_COMM_WORLD).
levelOne of LOG_ERROR, LOG_WARNING, LOG_INFO, LOG_DEBUG.
fmtA printf-style format string (e.g., "Message: %ld\n").
...Variadic arguments to fill in fmt.

Example usage:

LOG_ALLOW_SYNC(LOCAL, LOG_DEBUG, "Debug info: rank = %ld\n", rank);
LOG_ALLOW_SYNC(GLOBAL, LOG_INFO, "Synchronized info in %s\n", __func__);
#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
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:30
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:31

Definition at line 252 of file logging.h.

253 { \
254 /* ------------------------------------------------------------------ */ \
255 /* Validate scope and pick communicator *before* any early exits. */ \
256 /* ------------------------------------------------------------------ */ \
257 MPI_Comm _comm; \
258 if ((scope) == LOCAL) _comm = MPI_COMM_SELF; \
259 else if ((scope) == GLOBAL) _comm = MPI_COMM_WORLD; \
260 else { \
261 fprintf(stderr, "LOG_ALLOW_SYNC ERROR: invalid scope (%d) at %s:%d\n", \
262 (scope), __FILE__, __LINE__); \
263 MPI_Abort(MPI_COMM_WORLD, 1); \
264 } \
265 \
266 /* ------------------------------------------------------------------ */ \
267 /* Decide whether *this* rank should actually print. */ \
268 /* ------------------------------------------------------------------ */ \
269 PetscBool _doPrint = \
270 is_function_allowed(__func__) && ((int)(level) <= (int)get_log_level()); \
271 \
272 if (_doPrint) { \
273 PetscSynchronizedPrintf(_comm, "[%s] " fmt, __func__, ##__VA_ARGS__); \
274 } \
275 \
276 /* ------------------------------------------------------------------ */ \
277 /* ALL ranks call the flush, even if they printed nothing. */ \
278 /* ------------------------------------------------------------------ */ \
279 PetscSynchronizedFlush(_comm, PETSC_STDOUT); \
280} while (0)

◆ LOG_LOOP_ALLOW

#define LOG_LOOP_ALLOW (   scope,
  level,
  iterVar,
  interval,
  fmt,
  ... 
)
Value:
do { \
if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
if ((iterVar) % (interval) == 0) { \
MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
PetscPrintf(comm, "[%s] [%s=%d] " fmt, \
__func__, #iterVar, (iterVar), ##__VA_ARGS__); \
} \
} \
} while (0)

Logs a message inside a loop, but only every interval iterations.

Parameters
scopeLOCAL or GLOBAL.
levelLOG_* level.
iterVarThe loop variable (e.g., i).
intervalOnly log when (iterVar % interval == 0).
fmtprintf-style format string.
...Variadic arguments to include in the formatted message.

Example: for (int i = 0; i < 100; i++) { LOG_LOOP_ALLOW(LOCAL, LOG_DEBUG, i, 10, "Value of i=%d\n", i); }

Definition at line 297 of file logging.h.

298 { \
299 if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
300 if ((iterVar) % (interval) == 0) { \
301 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
302 PetscPrintf(comm, "[%s] [%s=%d] " fmt, \
303 __func__, #iterVar, (iterVar), ##__VA_ARGS__); \
304 } \
305 } \
306 } while (0)

◆ LOG_LOOP_ALLOW_EXACT

#define LOG_LOOP_ALLOW_EXACT (   scope,
  level,
  var,
  val,
  fmt,
  ... 
)
Value:
do { \
/* First, perform the cheap, standard gatekeeper checks. */ \
if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
/* Only if those pass, check the user's specific condition. */ \
if ((var) == (val)) { \
MPI_Comm comm = ((scope) == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
/* Print the standard prefix, then the user's custom message. */ \
PetscPrintf(comm, "[%s] [%s=%d] " fmt, \
__func__, #var, (var), ##__VA_ARGS__); \
} \
} \
} while (0)

Logs a custom message if a variable equals a specific value.

This is a variadic macro for logging a single event when a condition is met. It is extremely useful for printing debug information at a specific iteration of a loop or when a state variable reaches a certain value.

Parameters
scopeEither LOCAL or GLOBAL.
levelThe logging level.
varThe variable to check (e.g., a loop counter 'k').
valThe value that triggers the log (e.g., 6). The log prints if var == val.
fmtA printf-style format string.
...A printf-style format string and its corresponding arguments.

Definition at line 334 of file logging.h.

335 { \
336 /* First, perform the cheap, standard gatekeeper checks. */ \
337 if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
338 /* Only if those pass, check the user's specific condition. */ \
339 if ((var) == (val)) { \
340 MPI_Comm comm = ((scope) == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
341 /* Print the standard prefix, then the user's custom message. */ \
342 PetscPrintf(comm, "[%s] [%s=%d] " fmt, \
343 __func__, #var, (var), ##__VA_ARGS__); \
344 } \
345 } \
346 } while (0)

◆ LOG_ARRAY_ELEMENT_ALLOW

#define LOG_ARRAY_ELEMENT_ALLOW (   scope,
  level,
  arr,
  length,
  idx,
  fmt 
)
Value:
do { \
if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
if ((idx) >= 0 && (idx) < (length)) { \
MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
PetscPrintf(comm, "[%s] arr[%d] = " fmt "\n", \
__func__, (idx), (arr)[idx]); \
} \
} \
} while (0)

Logs a single element of an array, given an index.

Parameters
scopeEither LOCAL or GLOBAL.
levelLOG_ERROR, LOG_WARNING, LOG_INFO, or LOG_DEBUG.
arrPointer to the array to log from.
lengthThe length of the array (to prevent out-of-bounds).
idxThe index of the element to print.
fmtThe printf-style format specifier (e.g. "%g", "%f", etc.).

This macro only logs if: 1) The current function is in the allow-list (is_function_allowed(__func__)). 2) The requested logging level <= the current global get_log_level(). 3) The index idx is valid (0 <= idx < length).

Definition at line 363 of file logging.h.

364 { \
365 if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
366 if ((idx) >= 0 && (idx) < (length)) { \
367 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
368 PetscPrintf(comm, "[%s] arr[%d] = " fmt "\n", \
369 __func__, (idx), (arr)[idx]); \
370 } \
371 } \
372 } while (0)

◆ LOG_ARRAY_SUBRANGE_ALLOW

#define LOG_ARRAY_SUBRANGE_ALLOW (   scope,
  level,
  arr,
  length,
  start,
  end,
  fmt 
)
Value:
do { \
if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
PetscInt _start = (start) < 0 ? 0 : (start); \
PetscInt _end = (end) >= (length) ? (length) - 1 : (end); \
for (PetscInt i = _start; i <= _end; i++) { \
PetscPrintf(comm, "[%s] arr[%d] = " fmt "\n", __func__, i, (arr)[i]); \
} \
} \
} while (0)

Logs a consecutive subrange of an array.

Parameters
scopeEither LOCAL or GLOBAL.
levelLOG_ERROR, LOG_WARNING, LOG_INFO, or LOG_DEBUG.
arrPointer to the array to log from.
lengthTotal length of the array.
startStarting index of the subrange.
endEnding index of the subrange (inclusive).
fmtThe printf-style format specifier (e.g., "%g", "%f").

This macro prints each element arr[i] for i in [start, end], bounded by [0, length-1].

Definition at line 387 of file logging.h.

388 { \
389 if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
390 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
391 PetscInt _start = (start) < 0 ? 0 : (start); \
392 PetscInt _end = (end) >= (length) ? (length) - 1 : (end); \
393 for (PetscInt i = _start; i <= _end; i++) { \
394 PetscPrintf(comm, "[%s] arr[%d] = " fmt "\n", __func__, i, (arr)[i]); \
395 } \
396 } \
397 } while (0)

◆ PROFILE_FUNCTION_BEGIN

#define PROFILE_FUNCTION_BEGIN    _ProfilingStart(__FUNCT__)

Marks the beginning of a profiled code block (typically a function).

Place this macro at the very beginning of a function you wish to profile. It automatically captures the function's name and starts a wall-clock timer.

Definition at line 818 of file logging.h.

◆ PROFILE_FUNCTION_END

#define PROFILE_FUNCTION_END    _ProfilingEnd(__FUNCT__)

Marks the end of a profiled code block.

Place this macro just before every return point in a function that starts with PROFILE_FUNCTION_BEGIN. It stops the timer and accumulates the results.

Definition at line 827 of file logging.h.

Enumeration Type Documentation

◆ LogLevel

enum LogLevel

Enumeration of logging levels.

Defines various severity levels for logging messages.

Enumerator
LOG_ERROR 

Critical errors that may halt the program.

LOG_WARNING 

Non-critical issues that warrant attention.

LOG_INFO 

Informational messages about program execution.

LOG_DEBUG 

Detailed debugging information.

LOG_TRACE 

Very fine-grained tracing information for in-depth debugging.

LOG_VERBOSE 

Extremely detailed logs, typically for development use only.

Definition at line 27 of file logging.h.

27 {
28 LOG_ERROR = 0, /**< Critical errors that may halt the program */
29 LOG_WARNING, /**< Non-critical issues that warrant attention */
30 LOG_INFO, /**< Informational messages about program execution */
31 LOG_DEBUG, /**< Detailed debugging information */
32 LOG_TRACE, /**< Very fine-grained tracing information for in-depth debugging */
33 LOG_VERBOSE /**< Extremely detailed logs, typically for development use only */
34} LogLevel;
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_WARNING
Non-critical issues that warrant attention.
Definition logging.h:29
@ LOG_VERBOSE
Extremely detailed logs, typically for development use only.
Definition logging.h:33

Function Documentation

◆ get_log_level()

LogLevel get_log_level ( )

Retrieves the current logging level from the environment variable LOG_LEVEL.

The function checks the LOG_LEVEL environment variable and sets the logging level accordingly. Supported levels are "ERROR", "WARNING", "INFO", "DEBUG", "TRACE", and "VERBOSE". Unset or unrecognized values default to "ERROR".

Returns
LogLevel The current logging level.

Retrieves the current logging level from the environment variable LOG_LEVEL.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
get_log_level()

Definition at line 84 of file logging.c.

84 {
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}
static LogLevel current_log_level
Static variable to cache the current logging level.
Definition logging.c:16
Here is the caller graph for this function:

◆ print_log_level()

PetscErrorCode print_log_level ( void  )

Prints the current logging level to the console.

This function retrieves the log level using get_log_level() and prints the corresponding log level name. It helps verify the logging configuration at runtime. The log levels supported are:

  • LOG_ERROR (0) : Logs only critical errors.
  • LOG_WARNING (1) : Logs warnings and errors.
  • LOG_INFO (2) : Logs general information, warnings, and errors.
  • LOG_DEBUG (3) : Logs debugging information, info, warnings, and errors.
  • LOG_TRACE (4) : Logs fine-grained trace information.
  • LOG_VERBOSE (5) : Logs very detailed developer output. If LOG_LEVEL is not set, it defaults to LOG_ERROR.

    Returns
    PetscErrorCode 0 on success.

    Prints the current logging level to the console.

    Local to this translation unit.

Definition at line 116 of file logging.c.

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}
LogLevel get_log_level()
Implementation of get_log_level().
Definition logging.c:84
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_allowed_functions()

void set_allowed_functions ( const char **  functionList,
int  count 
)

Sets the global list of function names that are allowed to log.

You can replace the entire list of allowed function names at runtime.

Parameters
functionListParameter functionList passed to set_allowed_functions().
countParameter count passed to set_allowed_functions().

Sets the global list of function names that are allowed to log.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
set_allowed_functions()

Definition at line 152 of file logging.c.

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}
static char ** gAllowedFunctions
Global/static array of function names allowed to log.
Definition logging.c:23
static int gNumAllowed
Number of entries in the gAllowedFunctions array.
Definition logging.c:28
Here is the caller graph for this function:

◆ is_function_allowed()

PetscBool is_function_allowed ( const char *  functionName)

Checks if a given function is in the allow-list.

This helper is used internally by the LOG_ALLOW macro.

Parameters
functionNameParameter functionName passed to is_function_allowed().
Returns
PetscBool indicating the result of is_function_allowed().

Checks if a given function is in the allow-list.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
is_function_allowed()

Definition at line 183 of file logging.c.

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}
Here is the caller graph for this function:

◆ LOG_CELL_VERTICES()

PetscErrorCode LOG_CELL_VERTICES ( const Cell cell,
PetscMPIInt  rank 
)

Prints the coordinates of a cell's vertices.

This function iterates through the eight vertices of a given cell and prints their coordinates. It is primarily used for debugging purposes to verify the correctness of cell vertex assignments.

Parameters
[in]cellPointer to a Cell structure representing the cell, containing its vertices.
[in]rankMPI rank for identification (useful in parallel environments).
Returns
PetscErrorCode Returns 0 to indicate successful execution. Non-zero on failure.
Note
  • Ensure that the cell pointer is not NULL before calling this function..

Prints the coordinates of a cell's vertices.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
LOG_CELL_VERTICES()

Definition at line 205 of file logging.c.

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}
#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
PetscScalar x
Definition variables.h:101
PetscScalar z
Definition variables.h:101
PetscScalar y
Definition variables.h:101
Cmpnts vertices[8]
Coordinates of the eight vertices of the cell.
Definition variables.h:176
Here is the caller graph for this function:

◆ LOG_FACE_DISTANCES()

PetscErrorCode LOG_FACE_DISTANCES ( PetscReal *  d)

Prints the signed distances to each face of the cell.

This function iterates through the six signed distances from a point to each face of a given cell and prints their values. It is primarily used for debugging purposes to verify the correctness of distance calculations.

Parameters
[in]dAn array of six PetscReal values representing the signed distances. The indices correspond to:
  • d[LEFT]: Left Face
  • d[RIGHT]: Right Face
  • d[BOTTOM]: Bottom Face
  • d[TOP]: Top Face
  • d[FRONT]: Front Face
  • d[BACK]: Back Face
Returns
PetscErrorCode Returns 0 to indicate successful execution. Non-zero on failure.
Note
  • Ensure that the d array is correctly populated with signed distances before calling this function.

Prints the signed distances to each face of the cell.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
LOG_FACE_DISTANCES()

Definition at line 230 of file logging.c.

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}
@ 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
Here is the caller graph for this function:

◆ LOG_PARTICLE_FIELDS()

PetscErrorCode LOG_PARTICLE_FIELDS ( UserCtx user,
PetscInt  printInterval 
)

Prints particle fields in a table that automatically adjusts its column widths.

This function retrieves data from the particle swarm and prints a table where the width of each column is determined by the maximum width needed to display the data. Only every 'printInterval'-th particle is printed.

Parameters
[in]userPointer to the UserCtx structure.
[in]printIntervalOnly every printInterval‑th particle is printed.
Returns
PetscErrorCode Returns 0 on success.

Prints particle fields in a table that automatically adjusts its column widths.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
LOG_PARTICLE_FIELDS()

Definition at line 397 of file logging.c.

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}
#define TMP_BUF_SIZE
Definition logging.c:7
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 BuildHeaderString(char *headerStr, size_t bufSize, PetscMPIInt wRank, PetscInt wPID, PetscInt wCell, PetscInt wPos, PetscInt wVel, PetscInt wWt)
Definition logging.c:379
static void CellToStr(const PetscInt *cell, char *buf, size_t bufsize)
Definition logging.c:269
static void TripleRealToStr(const PetscReal *arr, char *buf, size_t bufsize)
Definition logging.c:277
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
Here is the call graph for this function:
Here is the caller graph for this function:

◆ IsParticleConsoleSnapshotEnabled()

PetscBool IsParticleConsoleSnapshotEnabled ( const SimCtx simCtx)

Returns whether periodic particle console snapshots are enabled.

This checks only the reporting contract (particles exist, cadence is enabled, and the global log level is at least INFO).

Parameters
simCtxSimulation context controlling the operation.
Returns
PetscBool indicating the result of IsParticleConsoleSnapshotEnabled().

Returns whether periodic particle console snapshots are enabled.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
IsParticleConsoleSnapshotEnabled()

Definition at line 525 of file logging.c.

526{
527 if (!simCtx) {
528 return PETSC_FALSE;
529 }
530 return (PetscBool)(simCtx->np > 0 &&
531 simCtx->particleConsoleOutputFreq > 0 &&
533}
PetscInt np
Definition variables.h:796
PetscInt particleConsoleOutputFreq
Definition variables.h:697
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ShouldEmitPeriodicParticleConsoleSnapshot()

PetscBool ShouldEmitPeriodicParticleConsoleSnapshot ( const SimCtx simCtx,
PetscInt  completed_step 
)

Returns whether a particle console snapshot should be emitted for the.

completed timestep.

Parameters
simCtxSimulation context controlling the operation.
completed_stepCompleted step index used by the decision helper.
Returns
PetscBool indicating the result of ShouldEmitPeriodicParticleConsoleSnapshot().

Returns whether a particle console snapshot should be emitted for the.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
ShouldEmitPeriodicParticleConsoleSnapshot()

Definition at line 542 of file logging.c.

543{
544 return (PetscBool)(IsParticleConsoleSnapshotEnabled(simCtx) &&
545 completed_step > 0 &&
546 completed_step % simCtx->particleConsoleOutputFreq == 0);
547}
PetscBool IsParticleConsoleSnapshotEnabled(const SimCtx *simCtx)
Implementation of IsParticleConsoleSnapshotEnabled().
Definition logging.c:525
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EmitParticleConsoleSnapshot()

PetscErrorCode EmitParticleConsoleSnapshot ( UserCtx user,
SimCtx simCtx,
PetscInt  step 
)

Emits one particle console snapshot into the main solver log.

Parameters
userPrimary UserCtx input for the operation.
simCtxSimulation context controlling the operation.
stepStep index associated with the operation.
Returns
PetscErrorCode 0 on success.

Emits one particle console snapshot into the main solver log.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
EmitParticleConsoleSnapshot()

Definition at line 556 of file logging.c.

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}
PetscErrorCode LOG_PARTICLE_FIELDS(UserCtx *user, PetscInt printInterval)
Implementation of LOG_PARTICLE_FIELDS().
Definition logging.c:397
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:83
PetscInt LoggingFrequency
Definition variables.h:826
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FreeAllowedFunctions()

PetscErrorCode FreeAllowedFunctions ( char **  funcs,
PetscInt  n 
)

Free an array previously returned by LoadAllowedFunctionsFromFile().

Parameters
[in,out]funcsArray of strings to release (may be NULL).
[in]nNumber of entries in funcs. Ignored if funcs is NULL.
Returns
0 on success or a PETSc error code.

Free an array previously returned by LoadAllowedFunctionsFromFile().

Local to this translation unit.

Definition at line 650 of file logging.c.

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}
Here is the caller graph for this function:

◆ LoadAllowedFunctionsFromFile()

PetscErrorCode LoadAllowedFunctionsFromFile ( const char  filename[],
char ***  funcsOut,
PetscInt *  nOut 
)

Load function names from a text file.

The file is expected to contain one identifier per line. Blank lines and lines whose first non‑blank character is a # are silently skipped so the file can include comments. Example:

# Allowed function list
InitializeSimulation
InterpolateAllFieldsToSwarm # inline comments are OK, too
PetscErrorCode InterpolateAllFieldsToSwarm(UserCtx *user)
Interpolates all relevant fields from the DMDA to the DMSwarm.
int main(int argc, char **argv)
Entry point for the postprocessor executable.

The routine allocates memory as needed (growing an internal buffer with PetscRealloc()) and returns the resulting array and its length to the caller. Use FreeAllowedFunctions() to clean up when done.

Parameters
[in]filenamePath of the configuration file to read.
[out]funcsOutOn success, points to a freshly‑allocated array of char* (size nOut).
[out]nOutNumber of valid entries in funcsOut.
Returns
0 on success, or a PETSc error code on failure (e.g. I/O error, OOM).

Load function names from a text file.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
LoadAllowedFunctionsFromFile()

Definition at line 596 of file logging.c.

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}
static void trim(char *s)
Internal helper implementation: trim().
Definition logging.c:571
Here is the call graph for this function:
Here is the caller graph for this function:

◆ BCFaceToString()

const char * BCFaceToString ( BCFace  face)

Helper function to convert BCFace enum to a string representation.

Parameters
[in]faceThe BCFace enum value.
Returns
Pointer to a constant string representing the face.

Helper function to convert BCFace enum to a string representation.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
BCFaceToString()

Definition at line 669 of file logging.c.

669 {
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}
@ BC_FACE_NEG_X
Definition variables.h:260
@ BC_FACE_POS_Z
Definition variables.h:262
@ BC_FACE_POS_Y
Definition variables.h:261
@ BC_FACE_NEG_Z
Definition variables.h:262
@ BC_FACE_POS_X
Definition variables.h:260
@ BC_FACE_NEG_Y
Definition variables.h:261
Here is the caller graph for this function:

◆ InitialConditionModeToString()

const char * InitialConditionModeToString ( InitialConditionMode  mode)

Convert an initial-condition mode to a string representation.

Parameters
[in]modeInitial-condition mode value.
Returns
Pointer to a constant string representing the initial-condition mode.

Convert an initial-condition mode to a string representation.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
InitialConditionModeToString()

Definition at line 687 of file logging.c.

688{
689 switch(mode){
690 case IC_MODE_ZERO: return "Zero";
691 case IC_MODE_CONSTANT_CARTESIAN: return "Cartesian Constant";
692 case IC_MODE_POISEUILLE: return "Poiseuille";
693 case IC_MODE_CONSTANT_STREAMWISE: return "Streamwise Constant";
694 case IC_MODE_FILE: return "File";
695 default: return "Unknown Initial Condition";
696 }
697}
@ IC_MODE_CONSTANT_CARTESIAN
Definition variables.h:151
@ IC_MODE_POISEUILLE
Definition variables.h:152
@ IC_MODE_CONSTANT_STREAMWISE
Definition variables.h:153
@ IC_MODE_FILE
Definition variables.h:154
@ IC_MODE_ZERO
Definition variables.h:150
Here is the caller graph for this function:

◆ FlowDirectionToString()

const char * FlowDirectionToString ( FlowDirection  fd)

Convert a FlowDirection enum value to its YAML token string.

Parameters
[in]fdFlowDirection value.
Returns
Token string such as "+Zeta", or "from INLET" when FLOW_DIR_UNSET.

Convert a FlowDirection enum value to its YAML token string.

Parameters
[in]fdFlowDirection value.
Returns
Constant string token (e.g. "+Zeta") or "from INLET" when FLOW_DIR_UNSET.

Definition at line 704 of file logging.c.

705{
706 switch ((int)fd) {
707 case FLOW_DIR_POS_XI: return "+Xi";
708 case FLOW_DIR_NEG_XI: return "-Xi";
709 case FLOW_DIR_POS_ETA: return "+Eta";
710 case FLOW_DIR_NEG_ETA: return "-Eta";
711 case FLOW_DIR_POS_ZETA: return "+Zeta";
712 case FLOW_DIR_NEG_ZETA: return "-Zeta";
713 default: return "from INLET";
714 }
715}
@ FLOW_DIR_NEG_ZETA
Definition variables.h:276
@ FLOW_DIR_NEG_ETA
Definition variables.h:274
@ FLOW_DIR_POS_ZETA
Definition variables.h:275
@ FLOW_DIR_POS_XI
Definition variables.h:271
@ FLOW_DIR_NEG_XI
Definition variables.h:272
@ FLOW_DIR_POS_ETA
Definition variables.h:273
Here is the caller graph for this function:

◆ ParticleInitializationToString()

const char * ParticleInitializationToString ( ParticleInitializationType  ParticleInitialization)

Helper function to convert ParticleInitialization to a string representation.

Parameters
[in]ParticleInitializationThe ParticleInitialization enum value.
Returns
Pointer to a constant string representing the particle initialization type.

Helper function to convert ParticleInitialization to a string representation.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
ParticleInitializationToString()

Definition at line 723 of file logging.c.

724{
725 switch(ParticleInitialization){
726 case PARTICLE_INIT_SURFACE_RANDOM: return "Surface: Random";
727 case PARTICLE_INIT_VOLUME: return "Volume";
728 case PARTICLE_INIT_POINT_SOURCE: return "Point Source";
729 case PARTICLE_INIT_SURFACE_EDGES: return "Surface: At edges";
730 default: return "Unknown Particle Initialization";
731 }
732}
@ PARTICLE_INIT_SURFACE_RANDOM
Random placement on the inlet face.
Definition variables.h:550
@ PARTICLE_INIT_SURFACE_EDGES
Deterministic placement at inlet face edges.
Definition variables.h:553
@ PARTICLE_INIT_POINT_SOURCE
All particles at a fixed (psrc_x,psrc_y,psrc_z) — for validation.
Definition variables.h:552
@ PARTICLE_INIT_VOLUME
Random volumetric distribution across the domain.
Definition variables.h:551
Here is the caller graph for this function:

◆ LESModelToString()

const char * LESModelToString ( LESModelType  LESFlag)

Helper function to convert LES Flag to a string representation.

Parameters
[in]LESFlagThe LES flag value.
Returns
Pointer to a constant string representing the LES Flag.

Helper function to convert LES Flag to a string representation.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
LESModelToString()

Definition at line 740 of file logging.c.

741{
742 switch(LESFlag){
743 case NO_LES_MODEL: return "No LES";
744 case CONSTANT_SMAGORINSKY: return "Constant Smagorinsky";
745 case DYNAMIC_SMAGORINSKY: return "Dynamic Smagorinsky";
746 default: return "Unknown LES Flag";
747 }
748}
@ DYNAMIC_SMAGORINSKY
Definition variables.h:521
@ NO_LES_MODEL
Definition variables.h:519
@ CONSTANT_SMAGORINSKY
Definition variables.h:520
Here is the caller graph for this function:

◆ MomentumSolverTypeToString()

const char * MomentumSolverTypeToString ( MomentumSolverType  SolverFlag)

Helper function to convert Momentum Solver flag to a string representation.

Parameters
[in]SolverFlagThe Momentum Solver flag value.
Returns
Pointer to a constant string representing the MomentumSolverType.

Helper function to convert Momentum Solver flag to a string representation.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
MomentumSolverTypeToString()

Definition at line 756 of file logging.c.

757{
758 switch(SolverFlag){
759 case MOMENTUM_SOLVER_EXPLICIT_RK: return "Explicit 4 stage Runge-Kutta ";
760 case MOMENTUM_SOLVER_DUALTIME_PICARD_JAMESON_RK: return "Dual Time Picard with 4-stage Jameson RK Smoothing";
761 case MOMENTUM_SOLVER_NEWTON_KRYLOV: return "Newton Krylov";
762 default: return "Unknown Momentum Solver Type";
763 }
764}
@ MOMENTUM_SOLVER_DUALTIME_PICARD_JAMESON_RK
Definition variables.h:534
@ MOMENTUM_SOLVER_EXPLICIT_RK
Definition variables.h:533
@ MOMENTUM_SOLVER_NEWTON_KRYLOV
Definition variables.h:535
Here is the caller graph for this function:

◆ BCTypeToString()

const char * BCTypeToString ( BCType  type)

Helper function to convert BCType enum to a string representation.

Parameters
[in]typeThe BCType enum value.
Returns
Pointer to a constant string representing the BC type.

Helper function to convert BCType enum to a string representation.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
BCTypeToString()

Definition at line 772 of file logging.c.

772 {
773 switch (type) {
774 // case DIRICHLET: return "DIRICHLET";
775 // case NEUMANN: return "NEUMANN";
776 case WALL: return "WALL";
777 case INLET: return "INLET";
778 case OUTLET: return "OUTLET";
779 case FARFIELD: return "FARFIELD";
780 case PERIODIC: return "PERIODIC";
781 case INTERFACE: return "INTERFACE";
782
783 // case CUSTOM: return "CUSTOM";
784 default: return "Unknown BC Type";
785 }
786}
@ INLET
Definition variables.h:288
@ INTERFACE
Definition variables.h:283
@ FARFIELD
Definition variables.h:289
@ OUTLET
Definition variables.h:287
@ PERIODIC
Definition variables.h:290
@ WALL
Definition variables.h:284
Here is the caller graph for this function:

◆ BCHandlerTypeToString()

const char * BCHandlerTypeToString ( BCHandlerType  handler_type)

Converts a BCHandlerType enum to its string representation.

Provides a descriptive string for a specific boundary condition implementation strategy. This is crucial for logging the exact behavior configured for a face.

Parameters
handler_typeThe BCHandlerType enum value (e.g., BC_HANDLER_WALL_NOSLIP).
Returns
A constant character string corresponding to the enum. Returns "UNKNOWN_HANDLER" if the enum value is not recognized.

Converts a BCHandlerType enum to its string representation.

Local to this translation unit.

Definition at line 792 of file logging.c.

792 {
793 switch (handler_type) {
794 // Wall & Symmetry Handlers
795 case BC_HANDLER_WALL_NOSLIP: return "noslip";
796 case BC_HANDLER_WALL_MOVING: return "moving";
797 case BC_HANDLER_SYMMETRY_PLANE: return "symmetry_plane";
798
799 // Inlet Handlers
800 case BC_HANDLER_INLET_CONSTANT_VELOCITY: return "constant_velocity";
801 case BC_HANDLER_INLET_PULSATILE_FLUX: return "pulsatile_flux";
802 case BC_HANDLER_INLET_PARABOLIC: return "parabolic";
803 case BC_HANDLER_INLET_PROFILE_FROM_FILE: return "prescribed_flow";
804
805 // Outlet Handlers
806 case BC_HANDLER_OUTLET_CONSERVATION: return "conservation";
807 case BC_HANDLER_OUTLET_PRESSURE: return "pressure";
808
809 // Other Physical Handlers
810 case BC_HANDLER_FARFIELD_NONREFLECTING: return "nonreflecting";
811
812 // Multi-Block / Interface Handlers
813 case BC_HANDLER_PERIODIC_GEOMETRIC: return "geometric";
814 case BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX: return "constant flux";
815 case BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX: return "initial flux";
816 case BC_HANDLER_INTERFACE_OVERSET: return "overset";
817
818 // Default case
820 default: return "UNKNOWN_HANDLER";
821 }
822}
@ BC_HANDLER_INLET_PULSATILE_FLUX
Definition variables.h:310
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:314
@ BC_HANDLER_INLET_PARABOLIC
Definition variables.h:307
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
Definition variables.h:306
@ BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX
Definition variables.h:317
@ BC_HANDLER_INTERFACE_OVERSET
Definition variables.h:315
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:316
@ BC_HANDLER_WALL_MOVING
Definition variables.h:304
@ BC_HANDLER_INLET_PROFILE_FROM_FILE
Definition variables.h:308
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:303
@ BC_HANDLER_OUTLET_CONSERVATION
Definition variables.h:312
@ BC_HANDLER_FARFIELD_NONREFLECTING
Definition variables.h:311
@ BC_HANDLER_OUTLET_PRESSURE
Definition variables.h:313
@ BC_HANDLER_SYMMETRY_PLANE
Definition variables.h:305
@ BC_HANDLER_UNDEFINED
Definition variables.h:302
Here is the caller graph for this function:

◆ DualKSPMonitor()

PetscErrorCode DualKSPMonitor ( KSP  ksp,
PetscInt  it,
PetscReal  rnorm,
void *  ctx 
)

A custom KSP monitor that logs to a file and optionally to the console.

This function unconditionally calls the standard true residual monitor to log to a file viewer provided in the context. It also checks a flag in the context and, if true, calls the monitor again to log to standard output.

Parameters
kspThe Krylov subspace context.
itThe current iteration number.
rnormThe preconditioned residual norm.
ctxA pointer to the DualMonitorCtx structure.
Returns
PetscErrorCode 0 on success.

A custom KSP monitor that logs to a file and optionally to the console.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
DualKSPMonitor()

Definition at line 869 of file logging.c.

870{
871 DualMonitorCtx *monctx = (DualMonitorCtx*)ctx;
872 PetscErrorCode ierr;
873 PetscReal trnorm, relnorm;
874 Vec r;
875 char norm_buf[256];
876 PetscMPIInt rank;
877
878 PetscFunctionBeginUser;
879 ierr = MPI_Comm_rank(PETSC_COMM_WORLD,&rank); CHKERRQ(ierr);
880
881 // 1. Calculate the true residual norm.
882 ierr = KSPBuildResidual(ksp, NULL, NULL, &r); CHKERRQ(ierr);
883 ierr = VecNorm(r, NORM_2, &trnorm); CHKERRQ(ierr);
884 ierr = VecDestroy(&r); CHKERRQ(ierr);
885
886 // 2. On the first iteration, compute and store the norm of the RHS vector `b`.
887 if (it == 0) {
888 Vec b;
889 ierr = KSPGetRhs(ksp, &b); CHKERRQ(ierr);
890 ierr = VecNorm(b, NORM_2, &monctx->bnorm); CHKERRQ(ierr);
891 }
892
893 if(!rank){
894 // 3. Compute the relative norm and format the output string.
895 if (monctx->bnorm > 1.e-15) {
896 relnorm = trnorm / monctx->bnorm;
897 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);
898 } else {
899 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);
900 }
901
902 // 4. Log to the file viewer (unconditionally).
903 if(monctx->file_handle){
904 ierr = PetscFPrintf(PETSC_COMM_SELF,monctx->file_handle,"%s\n", norm_buf); CHKERRQ(ierr);
905 }
906 // 5. Log to the console (conditionally).
907 if (monctx->log_to_console) {
908 PetscFPrintf(PETSC_COMM_SELF,stdout, "%s\n", norm_buf); CHKERRQ(ierr);
909 }
910
911 } //rank
912
913 PetscFunctionReturn(0);
914}
PetscBool log_to_console
Definition logging.h:57
PetscReal bnorm
Definition logging.h:58
PetscInt step
Definition logging.h:59
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
Here is the caller graph for this function:

◆ DualMonitorDestroy()

PetscErrorCode DualMonitorDestroy ( void **  ctx)

Destroys the DualMonitorCtx.

This function is passed to KSPMonitorSet to ensure the viewer is properly destroyed and the context memory is freed when the KSP is destroyed.

Parameters
ctxa pointer to the context pointer to be destroyed
Returns
PetscErrorCode

Destroys the DualMonitorCtx.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
DualMonitorDestroy()

Definition at line 830 of file logging.c.

831{
832 DualMonitorCtx *monctx = (DualMonitorCtx*)*ctx;
833 PetscErrorCode ierr;
834 PetscMPIInt rank;
835
836 PetscFunctionBeginUser;
837 ierr = MPI_Comm_rank(PETSC_COMM_WORLD,&rank); CHKERRQ(ierr);
838 if(!rank && monctx->file_handle){
839 fclose(monctx->file_handle);
840 }
841
842 ierr = PetscFree(monctx); CHKERRQ(ierr);
843 *ctx = NULL;
844 PetscFunctionReturn(0);
845}
Here is the caller graph for this function:

◆ LOG_CONTINUITY_METRICS()

PetscErrorCode LOG_CONTINUITY_METRICS ( UserCtx user)

Logs continuity metrics for a single block to a file.

This function should be called for each block, once per timestep. It opens a central log file in append mode. To ensure the header is written only once, it checks if it is processing block 0 on the simulation's start step.

Parameters
userA pointer to the UserCtx for the specific block whose metrics are to be logged. The function accesses both global (SimCtx) and local (user->...) data.
Returns
PetscErrorCode 0 on success.

Logs continuity metrics for a single block to a file.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
LOG_CONTINUITY_METRICS()

Definition at line 1794 of file logging.c.

1795{
1796 PetscErrorCode ierr;
1797 PetscMPIInt rank;
1798 SimCtx *simCtx = user->simCtx; // Get the shared SimCtx
1799 const PetscInt bi = user->_this; // Get this block's specific ID
1800 const PetscInt ti = simCtx->step; // Get the current timestep
1801
1802 PetscFunctionBeginUser;
1803 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
1804
1805 // Only rank 0 performs file I/O.
1806 if (!rank) {
1807 FILE *f;
1808 char filen[PETSC_MAX_PATH_LEN + 64];
1809 ierr = PetscSNPrintf(filen, sizeof(filen), "%s/Continuity_Metrics.log", simCtx->log_dir); CHKERRQ(ierr);
1810
1811 // Open the log file in append mode.
1812 f = fopen(filen, "a");
1813 if (!f) {
1814 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open log file: %s", filen);
1815 }
1816
1817 // Write a header only when the file is empty and it's the first block (bi=0).
1818 // Using ftell() instead of step comparison ensures correctness across continuations.
1819 if (ftell(f) == 0 && bi == 0) {
1820 PetscFPrintf(PETSC_COMM_SELF, f, "%-10s | %-6s | %-18s | %-30s | %-18s | %-18s | %-18s | %-18s\n",
1821 "Timestep", "Block", "Max Divergence", "Max Divergence Location ([k][j][i]=idx)", "Sum(RHS)","Total Flux In", "Total Flux Out", "Net Flux");
1822 PetscFPrintf(PETSC_COMM_SELF, f, "------------------------------------------------------------------------------------------------------------------------------------------\n");
1823 }
1824 if (simCtx->continueMode && ti == simCtx->StartStep + 1 && bi == 0) {
1825 PetscFPrintf(PETSC_COMM_SELF, f, "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep);
1826 }
1827
1828 // Prepare the data strings and values for the current block.
1829 PetscReal net_flux = simCtx->FluxInSum - simCtx->FluxOutSum;
1830 char location_str[64];
1831 sprintf(location_str, "([%d][%d][%d] = %d)", (int)simCtx->MaxDivz, (int)simCtx->MaxDivy, (int)simCtx->MaxDivx, (int)simCtx->MaxDivFlatArg);
1832
1833 // Write the formatted line for the current block.
1834 PetscFPrintf(PETSC_COMM_SELF, f, "%-10d | %-6d | %-18.10e | %-39s | %-18.10e | %-18.10e | %-18.10e | %-18.10e\n",
1835 (int)ti,
1836 (int)bi,
1837 (double)simCtx->MaxDiv,
1838 location_str,
1839 (double)simCtx->summationRHS,
1840 (double)simCtx->FluxInSum,
1841 (double)simCtx->FluxOutSum,
1842 (double)net_flux);
1843
1844 fclose(f);
1845 }
1846
1847 PetscFunctionReturn(0);
1848}
PetscBool continueMode
Definition variables.h:701
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:879
PetscReal FluxOutSum
Definition variables.h:777
PetscInt _this
Definition variables.h:889
PetscInt StartStep
Definition variables.h:694
PetscReal MaxDiv
Definition variables.h:828
PetscInt MaxDivx
Definition variables.h:829
PetscInt MaxDivy
Definition variables.h:829
PetscInt MaxDivz
Definition variables.h:829
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:709
PetscInt MaxDivFlatArg
Definition variables.h:829
PetscReal FluxInSum
Definition variables.h:777
PetscInt step
Definition variables.h:692
PetscReal summationRHS
Definition variables.h:827
The master context for the entire simulation.
Definition variables.h:684
Here is the caller graph for this function:

◆ LOG_SOLUTION_CONVERGENCE()

PetscErrorCode LOG_SOLUTION_CONVERGENCE ( SimCtx simCtx)

Logs physical solution-convergence metrics once per completed timestep.

This logger is intentionally separate from the detailed inner solver-health logs. Depending on the configured mode, it records deterministic step drift, periodic phase-aligned drift, or statistical window drift into logs/solution_convergence.log.

The logger is solver-mode only and is intended to run after a completed flow-solve / projection step but before history vectors are shifted forward. Warmup rows are handled internally:

  • steady/transient mode: the first logged step has has_reference = 0
  • periodic mode: the first cycle fills the phase reference ring
  • statistical mode: window-drift fields remain zero until enough samples exist to form the requested windows

Existing detailed inner logs remain the authoritative source for momentum, Poisson, and continuity health. This logger only summarizes physical solution-drift metrics.

Parameters
[in,out]simCtxMaster simulation context controlling the logging mode and owning any supporting runtime buffers.
Returns
PetscErrorCode 0 on success.

Logs physical solution-convergence metrics once per completed timestep.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
LOG_SOLUTION_CONVERGENCE()

Definition at line 1599 of file logging.c.

1600{
1601 PetscMPIInt rank = 0;
1602 PetscBool has_reference = PETSC_FALSE;
1603 PetscInt phase_step = -1;
1604 PetscInt samples_before = 0;
1605 PetscReal u_abs_l2 = 0.0, u_rel_l2 = 0.0, p_abs_l2 = 0.0, p_rel_l2 = 0.0;
1606 PetscReal mean_speed = 0.0, mean_speed_reference = 0.0, mean_speed_abs_drift = 0.0, mean_speed_rel_drift = 0.0;
1607 PetscReal mean_ke = 0.0, mean_ke_reference = 0.0, mean_ke_abs_drift = 0.0, mean_ke_rel_drift = 0.0;
1608 PetscReal mean_speed_window = 0.0, mean_speed_window_prev = 0.0, mean_speed_window_abs_drift = 0.0, mean_speed_window_rel_drift = 0.0;
1609 PetscReal mean_speed_rms_window = 0.0, mean_speed_rms_window_prev = 0.0, mean_speed_rms_window_abs_drift = 0.0, mean_speed_rms_window_rel_drift = 0.0;
1610 PetscReal mean_ke_window = 0.0, mean_ke_window_prev = 0.0, mean_ke_window_abs_drift = 0.0, mean_ke_window_rel_drift = 0.0;
1611 PetscReal mean_ke_rms_window = 0.0, mean_ke_rms_window_prev = 0.0, mean_ke_rms_window_abs_drift = 0.0, mean_ke_rms_window_rel_drift = 0.0;
1612
1613 PetscFunctionBeginUser;
1614 if (!simCtx) PetscFunctionReturn(0);
1615 if (simCtx->exec_mode != EXEC_MODE_SOLVER) PetscFunctionReturn(0);
1616
1617 samples_before = simCtx->solutionConvergenceSamplesRecorded;
1618
1619 switch (simCtx->solutionConvergenceMode) {
1622 PetscCall(ComputeDeterministicSolutionMetrics(simCtx, PETSC_FALSE, -1, samples_before,
1623 &has_reference,
1624 &u_abs_l2, &u_rel_l2,
1625 &p_abs_l2, &p_rel_l2,
1626 &mean_speed, &mean_speed_reference,
1627 &mean_speed_abs_drift, &mean_speed_rel_drift,
1628 &mean_ke, &mean_ke_reference,
1629 &mean_ke_abs_drift, &mean_ke_rel_drift));
1630 break;
1632 phase_step = simCtx->solutionConvergencePeriodSteps > 0 ? (simCtx->step % simCtx->solutionConvergencePeriodSteps) : -1;
1633 PetscCall(ComputeDeterministicSolutionMetrics(simCtx, PETSC_TRUE, phase_step, samples_before,
1634 &has_reference,
1635 &u_abs_l2, &u_rel_l2,
1636 &p_abs_l2, &p_rel_l2,
1637 &mean_speed, &mean_speed_reference,
1638 &mean_speed_abs_drift, &mean_speed_rel_drift,
1639 &mean_ke, &mean_ke_reference,
1640 &mean_ke_abs_drift, &mean_ke_rel_drift));
1641 break;
1643 PetscCall(ComputeCurrentFlowObservables(simCtx, &mean_speed, &mean_ke));
1644 PetscCall(AppendStatisticalObservableSample(simCtx, samples_before, mean_speed, mean_ke));
1645 PetscCall(ComputeStatisticalWindowMetrics(simCtx, samples_before + 1,
1646 &has_reference,
1647 &mean_speed_window, &mean_speed_window_prev,
1648 &mean_speed_window_abs_drift, &mean_speed_window_rel_drift,
1649 &mean_speed_rms_window, &mean_speed_rms_window_prev,
1650 &mean_speed_rms_window_abs_drift, &mean_speed_rms_window_rel_drift,
1651 &mean_ke_window, &mean_ke_window_prev,
1652 &mean_ke_window_abs_drift, &mean_ke_window_rel_drift,
1653 &mean_ke_rms_window, &mean_ke_rms_window_prev,
1654 &mean_ke_rms_window_abs_drift, &mean_ke_rms_window_rel_drift));
1655 break;
1656 default:
1657 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown solution convergence mode %d.", (int)simCtx->solutionConvergenceMode);
1658 }
1659
1660 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
1661 if (rank == 0) {
1662 char log_path[PETSC_MAX_PATH_LEN + 32];
1663 FILE *f = NULL;
1664 const char *mode_str = SolutionConvergenceModeToString(simCtx->solutionConvergenceMode);
1665
1666 PetscCall(PetscSNPrintf(log_path, sizeof(log_path), "%s/solution_convergence.log", simCtx->log_dir));
1667 f = fopen(log_path, "a");
1668 if (!f) {
1669 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open solution convergence log: %s", log_path);
1670 }
1671
1672 if (ftell(f) == 0) {
1673 switch (simCtx->solutionConvergenceMode) {
1676 fprintf(f, "==================== Solution Convergence Log [mode: %s] ====================\n", mode_str);
1677 /* 16 columns; header width = 314 chars */
1678 fprintf(f, "%-10s | %-18s | %-22s | %-3s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s\n",
1679 "step", "time", "mode", "ref",
1680 "u_abs_l2", "u_rel_l2", "p_abs_l2", "p_rel_l2",
1681 "mean_speed", "spd_ref", "spd_abs", "spd_rel",
1682 "mean_ke", "ke_ref", "ke_abs", "ke_rel");
1683 fprintf(f, "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n");
1684 break;
1686 fprintf(f, "==================== Solution Convergence Log [mode: %s | period_steps: %d] ====================\n",
1687 mode_str, (int)simCtx->solutionConvergencePeriodSteps);
1688 /* 18 columns; header width = 330 chars */
1689 fprintf(f, "%-10s | %-18s | %-22s | %-3s | %-5s | %-5s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s\n",
1690 "step", "time", "mode", "ref", "ph", "per",
1691 "u_abs_l2", "u_rel_l2", "p_abs_l2", "p_rel_l2",
1692 "mean_speed", "spd_ref", "spd_abs", "spd_rel",
1693 "mean_ke", "ke_ref", "ke_abs", "ke_rel");
1694 fprintf(f, "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n");
1695 break;
1697 fprintf(f, "==================== Solution Convergence Log [mode: %s | window_steps: %d] ====================\n",
1698 mode_str, (int)simCtx->solutionConvergenceWindowSteps);
1699 /* 21 columns; header width = 406 chars */
1700 fprintf(f, "%-10s | %-18s | %-22s | %-3s | %-5s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s | %-18s\n",
1701 "step", "time", "mode", "ref", "win",
1702 "mean_speed", "mean_ke",
1703 "spd_win", "spd_win_prev", "spd_win_abs", "spd_win_rel",
1704 "spd_rms_win", "spd_rms_abs", "spd_rms_rel",
1705 "ke_win", "ke_win_prev", "ke_win_abs", "ke_win_rel",
1706 "ke_rms_win", "ke_rms_abs", "ke_rms_rel");
1707 fprintf(f, "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n");
1708 break;
1709 default: break;
1710 }
1711 }
1712 if (simCtx->continueMode && simCtx->step == simCtx->StartStep + 1) {
1713 fprintf(f, "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep);
1714 }
1715
1716 switch (simCtx->solutionConvergenceMode) {
1719 fprintf(f,
1720 "%-10d | %-18.10e | %-22s | %-3d | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e\n",
1721 (int)simCtx->step, (double)simCtx->ti, mode_str, has_reference ? 1 : 0,
1722 (double)u_abs_l2, (double)u_rel_l2, (double)p_abs_l2, (double)p_rel_l2,
1723 (double)mean_speed, (double)mean_speed_reference,
1724 (double)mean_speed_abs_drift, (double)mean_speed_rel_drift,
1725 (double)mean_ke, (double)mean_ke_reference,
1726 (double)mean_ke_abs_drift, (double)mean_ke_rel_drift);
1727 break;
1729 fprintf(f,
1730 "%-10d | %-18.10e | %-22s | %-3d | %-5d | %-5d | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e\n",
1731 (int)simCtx->step, (double)simCtx->ti, mode_str, has_reference ? 1 : 0,
1732 (int)phase_step, (int)simCtx->solutionConvergencePeriodSteps,
1733 (double)u_abs_l2, (double)u_rel_l2, (double)p_abs_l2, (double)p_rel_l2,
1734 (double)mean_speed, (double)mean_speed_reference,
1735 (double)mean_speed_abs_drift, (double)mean_speed_rel_drift,
1736 (double)mean_ke, (double)mean_ke_reference,
1737 (double)mean_ke_abs_drift, (double)mean_ke_rel_drift);
1738 break;
1740 fprintf(f,
1741 "%-10d | %-18.10e | %-22s | %-3d | %-5d | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e | %-18.10e\n",
1742 (int)simCtx->step, (double)simCtx->ti, mode_str, has_reference ? 1 : 0,
1743 (int)simCtx->solutionConvergenceWindowSteps,
1744 (double)mean_speed, (double)mean_ke,
1745 (double)mean_speed_window, (double)mean_speed_window_prev,
1746 (double)mean_speed_window_abs_drift, (double)mean_speed_window_rel_drift,
1747 (double)mean_speed_rms_window,
1748 (double)mean_speed_rms_window_abs_drift, (double)mean_speed_rms_window_rel_drift,
1749 (double)mean_ke_window, (double)mean_ke_window_prev,
1750 (double)mean_ke_window_abs_drift, (double)mean_ke_window_rel_drift,
1751 (double)mean_ke_rms_window,
1752 (double)mean_ke_rms_window_abs_drift, (double)mean_ke_rms_window_rel_drift);
1753 break;
1754 default: break;
1755 }
1756 fclose(f);
1757 }
1758
1760 phase_step >= 0 && phase_step < simCtx->solutionConvergencePeriodSteps) {
1761 UserCtx *user = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
1762 for (PetscInt bi = 0; bi < simCtx->block_number; ++bi) {
1763 PetscCall(VecCopy(user[bi].Ucat, user[bi].solutionConvergencePeriodicUcatRef[phase_step]));
1764 PetscCall(VecCopy(user[bi].P, user[bi].solutionConvergencePeriodicPRef[phase_step]));
1765 }
1766 }
1767
1768 simCtx->solutionConvergenceSamplesRecorded = samples_before + 1;
1769
1770 PetscFunctionReturn(0);
1771}
static PetscErrorCode AppendStatisticalObservableSample(SimCtx *simCtx, PetscInt samples_before, PetscReal mean_speed, PetscReal mean_ke)
Appends one timestep's scalar observables to the statistical history.
Definition logging.c:1369
static const char * SolutionConvergenceModeToString(SolutionConvergenceMode mode)
Maps the internal solution-convergence mode enum to its log label.
Definition logging.c:1582
static PetscErrorCode ComputeStatisticalWindowMetrics(const SimCtx *simCtx, PetscInt samples_available, PetscBool *has_reference_out, PetscReal *mean_speed_window_out, PetscReal *mean_speed_window_prev_out, PetscReal *mean_speed_window_abs_out, PetscReal *mean_speed_window_rel_out, PetscReal *mean_speed_rms_window_out, PetscReal *mean_speed_rms_window_prev_out, PetscReal *mean_speed_rms_window_abs_out, PetscReal *mean_speed_rms_window_rel_out, PetscReal *mean_ke_window_out, PetscReal *mean_ke_window_prev_out, PetscReal *mean_ke_window_abs_out, PetscReal *mean_ke_window_rel_out, PetscReal *mean_ke_rms_window_out, PetscReal *mean_ke_rms_window_prev_out, PetscReal *mean_ke_rms_window_abs_out, PetscReal *mean_ke_rms_window_rel_out)
Computes adjacent-window drift metrics for statistical steady mode.
Definition logging.c:1449
static PetscErrorCode ComputeDeterministicSolutionMetrics(SimCtx *simCtx, PetscBool periodic_mode, PetscInt phase_step, PetscInt samples_before, PetscBool *has_reference_out, PetscReal *u_abs_l2_out, PetscReal *u_rel_l2_out, PetscReal *p_abs_l2_out, PetscReal *p_rel_l2_out, PetscReal *mean_speed_out, PetscReal *mean_speed_ref_out, PetscReal *mean_speed_abs_out, PetscReal *mean_speed_rel_out, PetscReal *mean_ke_out, PetscReal *mean_ke_ref_out, PetscReal *mean_ke_abs_out, PetscReal *mean_ke_rel_out)
Computes deterministic solution-drift metrics for the current step.
Definition logging.c:1091
static PetscErrorCode ComputeCurrentFlowObservables(SimCtx *simCtx, PetscReal *mean_speed_out, PetscReal *mean_ke_out)
Computes instantaneous global flow observables for statistical mode.
Definition logging.c:975
UserCtx * user
Definition variables.h:569
PetscInt block_number
Definition variables.h:768
UserMG usermg
Definition variables.h:821
PetscInt solutionConvergenceSamplesRecorded
Definition variables.h:752
PetscInt solutionConvergencePeriodSteps
Definition variables.h:750
PetscInt mglevels
Definition variables.h:576
PetscInt solutionConvergenceWindowSteps
Definition variables.h:751
@ EXEC_MODE_SOLVER
Definition variables.h:657
MGCtx * mgctx
Definition variables.h:579
@ SOLUTION_CONVERGENCE_TRANSIENT
Definition variables.h:545
@ SOLUTION_CONVERGENCE_PERIODIC_DETERMINISTIC
Definition variables.h:543
@ SOLUTION_CONVERGENCE_STATISTICAL_STEADY
Definition variables.h:544
@ SOLUTION_CONVERGENCE_STEADY_DETERMINISTIC
Definition variables.h:542
SolutionConvergenceMode solutionConvergenceMode
Definition variables.h:749
ExecutionMode exec_mode
Definition variables.h:703
PetscReal ti
Definition variables.h:693
User-defined context containing data specific to a single computational grid level.
Definition variables.h:876
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParticleLocationStatusToString()

const char * ParticleLocationStatusToString ( ParticleLocationStatus  level)

A function that outputs the name of the current level in the ParticleLocation enum.

Parameters
levelThe ParticleLocation enum value.
Returns
A constant character string corresponding to the enum. Returns "UNKNOWN_LEVEL" if the enum value is not recognized.

A function that outputs the name of the current level in the ParticleLocation enum.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
ParticleLocationStatusToString()

Definition at line 1856 of file logging.c.

1857{
1858 switch (level) {
1859 case NEEDS_LOCATION: return "NEEDS_LOCATION";
1860 case ACTIVE_AND_LOCATED: return "ACTIVE_AND_LOCATED";
1861 case MIGRATING_OUT: return "MIGRATING_OUT";
1862 case LOST: return "LOST";
1863 case UNINITIALIZED: return "UNINITIALIZED";
1864 default: return "UNKNOWN_LEVEL";
1865 }
1866}
@ 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
Here is the caller graph for this function:

◆ PrintProgressBar()

void PrintProgressBar ( PetscInt  step,
PetscInt  startStep,
PetscInt  totalSteps,
PetscReal  currentTime 
)

Prints a progress bar to the console.

This function should only be called by the root process (rank 0). It uses a carriage return \r to overwrite the same line in the terminal, creating a dynamic progress bar.

Parameters
stepThe current step index from the loop (e.g., from 0 to N-1).
startStepThe global starting step number of the simulation.
totalStepsThe total number of steps to be run in this simulation instance.
currentTimeThe current simulation time to display.

Prints a progress bar to the console.

Local to this translation unit.

Definition at line 2302 of file logging.c.

2303{
2304 if (totalSteps <= 0) return;
2305
2306 // --- Configuration ---
2307 const int barWidth = 50;
2308
2309 // --- Calculation ---
2310 // Calculate progress as a fraction from 0.0 to 1.0
2311 PetscReal progress = (PetscReal)(step - startStep + 1) / totalSteps;
2312 // Ensure progress doesn't exceed 1.0 due to floating point inaccuracies
2313 if (progress > 1.0) progress = 1.0;
2314
2315 int pos = (int)(barWidth * progress);
2316
2317 // --- Printing ---
2318 // Carriage return moves cursor to the beginning of the line
2319 PetscPrintf(PETSC_COMM_SELF, "\rProgress: [");
2320
2321 for (int i = 0; i < barWidth; ++i) {
2322 if (i < pos) {
2323 PetscPrintf(PETSC_COMM_SELF, "=");
2324 } else if (i == pos) {
2325 PetscPrintf(PETSC_COMM_SELF, ">");
2326 } else {
2327 PetscPrintf(PETSC_COMM_SELF, " ");
2328 }
2329 }
2330
2331 // Print percentage, step count, and current time
2332 PetscPrintf(PETSC_COMM_SELF, "] %3d%% (Step %" PetscInt_FMT "/%" PetscInt_FMT ", t=%.4f)",
2333 (int)(progress * 100.0),
2334 step + 1,
2335 startStep + totalSteps,
2336 currentTime);
2337
2338 // Flush the output buffer to ensure the bar is displayed immediately
2339 fflush(stdout);
2340}
Here is the caller graph for this function:

◆ ProfilingInitialize()

PetscErrorCode ProfilingInitialize ( SimCtx simCtx)

Initializes the custom profiling system using configuration from SimCtx.

This function sets up the internal data structures for tracking function performance. It reads the list of "critical functions" from the provided SimCtx and marks them for per-step logging at LOG_INFO level.

It should be called once at the beginning of the application, after CreateSimulationContext() but before the main time loop.

Parameters
simCtxThe master simulation context, which contains the list of critical function names to always log.
Returns
PetscErrorCode

Initializes the custom profiling system using configuration from SimCtx.

Local to this translation unit.

Definition at line 1927 of file logging.c.

1928{
1929 PetscFunctionBeginUser;
1930 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx cannot be null for ProfilingInitialize");
1931
1932 // Iterate through the list of critical functions provided in SimCtx
1933 for (PetscInt i = 0; i < simCtx->nProfilingSelectedFuncs; ++i) {
1934 PetscInt idx;
1935 const char *func_name = simCtx->profilingSelectedFuncs[i];
1936 PetscErrorCode ierr = _FindOrCreateEntry(func_name, &idx); CHKERRQ(ierr);
1937 g_profiler_registry[idx].always_log = PETSC_TRUE;
1938
1939 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Marked '%s' as a critical function for profiling.\n", func_name);
1940 }
1941 PetscFunctionReturn(0);
1942}
PetscBool always_log
Definition logging.c:1878
static PetscErrorCode _FindOrCreateEntry(const char *func_name, PetscInt *idx)
Internal helper implementation: _FindOrCreateEntry().
Definition logging.c:1891
static ProfiledFunction * g_profiler_registry
Definition logging.c:1882
char ** profilingSelectedFuncs
Definition variables.h:833
PetscInt nProfilingSelectedFuncs
Definition variables.h:834
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ProfilingResetTimestepCounters()

PetscErrorCode ProfilingResetTimestepCounters ( void  )

Resets per-timestep profiling counters for the next solver step.

This clears transient counters without discarding cumulative totals used by final profiling summaries.

Returns
PetscErrorCode 0 on success.

Resets per-timestep profiling counters for the next solver step.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
ProfilingResetTimestepCounters()

Definition at line 1987 of file logging.c.

1988{
1989 PetscFunctionBeginUser;
1990 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1993 }
1994 PetscFunctionReturn(0);
1995}
static PetscInt g_profiler_count
Definition logging.c:1883
double current_step_time
Definition logging.c:1874
long long current_step_call_count
Definition logging.c:1876
Here is the caller graph for this function:

◆ ProfilingLogTimestepSummary()

PetscErrorCode ProfilingLogTimestepSummary ( SimCtx simCtx,
PetscInt  step 
)

Logs the performance summary for the current timestep and resets timers.

Depending on the configured profiling timestep mode, this function writes per-step profiling rows to the configured profiling log file:

  • off: writes nothing
  • selected: writes only functions marked for per-step reporting
  • all: writes every instrumented function that ran in the timestep

It must be called once per timestep, typically at the end of the main loop. After logging, it resets the per-step counters and timers.

Parameters
simCtxThe simulation context holding profiling output settings.
stepThe current simulation step number, for logging context.
Returns
PetscErrorCode

Logs the performance summary for the current timestep and resets timers.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
ProfilingLogTimestepSummary()

Definition at line 2004 of file logging.c.

2005{
2006 PetscBool should_write = PETSC_FALSE;
2007 FILE *f = NULL;
2008 char filen[(2 * PETSC_MAX_PATH_LEN) + 16];
2009
2010 PetscFunctionBeginUser;
2011 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx cannot be null for ProfilingLogTimestepSummary");
2012
2013 if (strcmp(simCtx->profilingTimestepMode, "off") == 0) {
2014 for (PetscInt i = 0; i < g_profiler_count; ++i) {
2017 }
2018 PetscFunctionReturn(0);
2019 }
2020
2021 for (PetscInt i = 0; i < g_profiler_count; ++i) {
2022 if (g_profiler_registry[i].current_step_call_count <= 0) {
2023 continue;
2024 }
2025 if (strcmp(simCtx->profilingTimestepMode, "all") == 0 || g_profiler_registry[i].always_log) {
2026 should_write = PETSC_TRUE;
2027 break;
2028 }
2029 }
2030
2031 if (should_write && simCtx->rank == 0) {
2032 snprintf(filen, sizeof(filen), "%s/%s", simCtx->log_dir, simCtx->profilingTimestepFile);
2033 if (step == simCtx->StartStep + 1 && !simCtx->continueMode) {
2034 f = fopen(filen, "w");
2035 if (!f) {
2036 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open profiling timestep log file: %s", filen);
2037 }
2038 PetscFPrintf(PETSC_COMM_SELF, f, "step,function,calls,step_time_s\n");
2039 } else {
2040 f = fopen(filen, "a");
2041 if (!f) {
2042 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open profiling timestep log file: %s", filen);
2043 }
2044 if (step == simCtx->StartStep + 1 && ftell(f) == 0) {
2045 PetscFPrintf(PETSC_COMM_SELF, f, "step,function,calls,step_time_s\n");
2046 }
2047 }
2048 if (simCtx->continueMode && step == simCtx->StartStep + 1) {
2049 PetscFPrintf(PETSC_COMM_SELF, f, "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep);
2050 }
2051
2052 for (PetscInt i = 0; i < g_profiler_count; ++i) {
2053 if (g_profiler_registry[i].current_step_call_count <= 0) {
2054 continue;
2055 }
2056 if (strcmp(simCtx->profilingTimestepMode, "all") == 0 || g_profiler_registry[i].always_log) {
2057 PetscFPrintf(
2058 PETSC_COMM_SELF,
2059 f,
2060 "%d,%s,%lld,%.6f\n",
2061 (int)step,
2062 g_profiler_registry[i].name,
2063 g_profiler_registry[i].current_step_call_count,
2064 g_profiler_registry[i].current_step_time
2065 );
2066 }
2067 }
2068 fclose(f);
2069 }
2070
2071 // Reset per-step counters for the next iteration
2072 for (PetscInt i = 0; i < g_profiler_count; ++i) {
2075 }
2076 PetscFunctionReturn(0);
2077}
PetscMPIInt rank
Definition variables.h:687
char profilingTimestepFile[PETSC_MAX_PATH_LEN]
Definition variables.h:836
char profilingTimestepMode[32]
Definition variables.h:835
Here is the caller graph for this function:

◆ RuntimeMemoryLogSample()

PetscErrorCode RuntimeMemoryLogSample ( SimCtx simCtx,
PetscInt  step,
const char *  event,
const char *  reason 
)

Append a reduced runtime memory sample to the configured memory log.

The log is intentionally compact: every rank samples process/PETSc allocator memory, one max reduction summarizes the job, and rank 0 appends one terminal-readable row.

Parameters
simCtxThe simulation context holding log directory and memory log settings.
stepThe solver/post step associated with the sample.
eventHuman-readable event label such as "Step", "Post", "Shutdown", or "Final".
reasonHuman-readable reason, or NULL/"-" when not applicable.
Returns
PetscErrorCode

Append a reduced runtime memory sample to the configured memory log.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
RuntimeMemoryLogSample()

Definition at line 2085 of file logging.c.

2086{
2087 PetscErrorCode ierr;
2088 PetscLogDouble process_current_bytes = 0.0;
2089 PetscLogDouble process_peak_bytes = 0.0;
2090 PetscLogDouble petsc_current_bytes = 0.0;
2091 PetscLogDouble petsc_peak_bytes = 0.0;
2092 PetscReal local_values[5];
2093 PetscReal global_values[5];
2094 PetscReal process_current_mb = 0.0;
2095 PetscReal process_peak_mb = 0.0;
2096 PetscReal petsc_current_mb = 0.0;
2097 PetscReal petsc_peak_mb = 0.0;
2098 PetscReal process_change_mb = 0.0;
2099 char path[(2 * PETSC_MAX_PATH_LEN) + 16];
2100 FILE *f = NULL;
2101
2102 PetscFunctionBeginUser;
2103 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx cannot be null for RuntimeMemoryLogSample");
2104 if (!simCtx->runtimeMemoryLogEnabled) PetscFunctionReturn(0);
2105
2106 ierr = PetscMemoryGetCurrentUsage(&process_current_bytes); CHKERRQ(ierr);
2107 ierr = PetscMemoryGetMaximumUsage(&process_peak_bytes); CHKERRQ(ierr);
2108 ierr = PetscMallocGetCurrentUsage(&petsc_current_bytes); CHKERRQ(ierr);
2109 ierr = PetscMallocGetMaximumUsage(&petsc_peak_bytes); CHKERRQ(ierr);
2110
2111 process_current_mb = (PetscReal)(process_current_bytes / (1024.0 * 1024.0));
2112 process_peak_mb = (PetscReal)(process_peak_bytes / (1024.0 * 1024.0));
2113 petsc_current_mb = (PetscReal)(petsc_current_bytes / (1024.0 * 1024.0));
2114 petsc_peak_mb = (PetscReal)(petsc_peak_bytes / (1024.0 * 1024.0));
2115 if (simCtx->runtimeMemoryLogHasPrevious) {
2116 process_change_mb = process_current_mb - simCtx->runtimeMemoryLogPreviousProcessMB;
2117 }
2118
2119 local_values[0] = process_current_mb;
2120 local_values[1] = process_peak_mb;
2121 local_values[2] = petsc_current_mb;
2122 local_values[3] = petsc_peak_mb;
2123 local_values[4] = process_change_mb;
2124 ierr = MPI_Allreduce(local_values, global_values, 5, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2125
2126 simCtx->runtimeMemoryLogPreviousProcessMB = process_current_mb;
2127 simCtx->runtimeMemoryLogHasPrevious = PETSC_TRUE;
2128
2129 if (simCtx->rank == 0) {
2130 ierr = PetscSNPrintf(path, sizeof(path), "%s/%s", simCtx->log_dir, simCtx->runtimeMemoryLogFile); CHKERRQ(ierr);
2131 f = fopen(path, "a");
2132 if (!f) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open runtime memory log file: %s", path);
2133
2134 if (!simCtx->runtimeMemoryLogStarted) {
2135 fprintf(f, "# PICurv runtime memory log\n");
2136 if (simCtx->continueMode) {
2137 fprintf(f, "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep);
2138 }
2139 fprintf(
2140 f,
2141 "%-8s %-10s %22s %20s %22s %28s %22s %-18s\n",
2142 "Step",
2143 "Event",
2144 "Process Current MB Max",
2145 "Process Peak MB Max",
2146 "PETSc Allocated MB Max",
2147 "PETSc Peak Allocated MB Max",
2148 "Process Change MB Max",
2149 "Reason"
2150 );
2151 simCtx->runtimeMemoryLogStarted = PETSC_TRUE;
2152 }
2153
2154 fprintf(
2155 f,
2156 "%-8" PetscInt_FMT " %-10s %22.3f %20.3f %22.3f %28.3f %22.3f %-18s\n",
2157 step,
2158 event ? event : "-",
2159 (double)global_values[0],
2160 (double)global_values[1],
2161 (double)global_values[2],
2162 (double)global_values[3],
2163 (double)global_values[4],
2164 (reason && reason[0]) ? reason : "-"
2165 );
2166 if ((event && (strcmp(event, "Shutdown") == 0 || strcmp(event, "Final") == 0))) {
2167 fflush(f);
2168 }
2169 fclose(f);
2170 }
2171
2172 PetscFunctionReturn(0);
2173}
PetscBool runtimeMemoryLogEnabled
Enable the rank-reduced runtime memory log.
Definition variables.h:852
char runtimeMemoryLogFile[PETSC_MAX_PATH_LEN]
File name written under log_dir.
Definition variables.h:853
PetscBool runtimeMemoryLogStarted
True after rank 0 writes the log header.
Definition variables.h:854
PetscBool runtimeMemoryLogHasPrevious
True after the first process-memory sample.
Definition variables.h:855
PetscReal runtimeMemoryLogPreviousProcessMB
Previous local process memory sample in MB.
Definition variables.h:856
Here is the caller graph for this function:

◆ ProfilingFinalize()

PetscErrorCode ProfilingFinalize ( SimCtx simCtx)

the profiling excercise and build a profiling summary which is then printed to a log file.

Parameters
simCtxThe Simulation Context Structure that can contains all the data regarding the simulation.
Returns
PetscErrorCode 0 on success.

the profiling excercise and build a profiling summary which is then printed to a log file.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
ProfilingFinalize()

Definition at line 2196 of file logging.c.

2197{
2198 PetscErrorCode ierr;
2199 PetscInt rank = simCtx->rank;
2200 PetscFunctionBeginUser;
2201 if (!simCtx->profilingFinalSummary) PetscFunctionReturn(0);
2202 if (!rank) {
2203
2204 char exec_mode_modifier[32] = "Unknown";
2205 if(simCtx->exec_mode == EXEC_MODE_SOLVER) PetscCall(PetscStrncpy(exec_mode_modifier, "Solver", sizeof(exec_mode_modifier)));
2206 else if(simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR) PetscCall(PetscStrncpy(exec_mode_modifier, "PostProcessor", sizeof(exec_mode_modifier)));
2207 //--- Step 0: Create a file viewer for log file
2208 FILE *f;
2209 char filen[PETSC_MAX_PATH_LEN + 128];
2210 ierr = PetscSNPrintf(filen, sizeof(filen), "%s/ProfilingSummary_%s.log",simCtx->log_dir,exec_mode_modifier); CHKERRQ(ierr);
2211
2212 // Open the log file: append with section label in continue mode, truncate otherwise.
2213 if (simCtx->continueMode) {
2214 f = fopen(filen, "a");
2215 if (!f) {
2216 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open log file: %s", filen);
2217 }
2218 fprintf(f, "\n=== Continuation from step %" PetscInt_FMT " ===\n", simCtx->StartStep);
2219 } else {
2220 f = fopen(filen, "w");
2221 if (!f) {
2222 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open log file: %s", filen);
2223 }
2224 }
2225
2226 // --- Step 1: Sort the data for readability ---
2228
2229 // --- Step 2: Dynamically determine the width for the function name column ---
2230 PetscInt max_name_len = strlen("Function"); // Start with the header's length
2231 for (PetscInt i = 0; i < g_profiler_count; ++i) {
2232 if (g_profiler_registry[i].total_call_count > 0) {
2233 PetscInt len = strlen(g_profiler_registry[i].name);
2234 if (len > max_name_len) {
2235 max_name_len = len;
2236 }
2237 }
2238 }
2239 // Add a little padding
2240 max_name_len += 2;
2241
2242 // --- Step 3: Define fixed widths for numeric columns for consistent alignment ---
2243 const int time_width = 18;
2244 const int count_width = 15;
2245 const int avg_width = 22;
2246
2247 // --- Step 4: Print the formatted table ---
2248 PetscFPrintf(PETSC_COMM_SELF, f, "=================================================================================================================\n");
2249 PetscFPrintf(PETSC_COMM_SELF, f, " FINAL PROFILING SUMMARY (Sorted by Total Time)\n");
2250 PetscFPrintf(PETSC_COMM_SELF, f, "=================================================================================================================\n");
2251
2252 // Header Row
2253 PetscFPrintf(PETSC_COMM_SELF, f, "%-*s | %-*s | %-*s | %-*s\n",
2254 max_name_len, "Function",
2255 time_width, "Total Time (s)",
2256 count_width, "Call Count",
2257 avg_width, "Avg. Time/Call (ms)");
2258
2259 // Separator Line (dynamically sized)
2260 for (int i = 0; i < max_name_len; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
2261 PetscFPrintf(PETSC_COMM_SELF, f, "-|-");
2262 for (int i = 0; i < time_width; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
2263 PetscFPrintf(PETSC_COMM_SELF, f, "-|-");
2264 for (int i = 0; i < count_width; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
2265 PetscFPrintf(PETSC_COMM_SELF, f, "-|-");
2266 for (int i = 0; i < avg_width; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
2267 PetscFPrintf(PETSC_COMM_SELF, f, "\n");
2268
2269 // Data Rows
2270 for (PetscInt i = 0; i < g_profiler_count; ++i) {
2271 if (g_profiler_registry[i].total_call_count > 0) {
2272 double avg_time_ms = (g_profiler_registry[i].total_time / g_profiler_registry[i].total_call_count) * 1000.0;
2273 PetscFPrintf(PETSC_COMM_SELF, f, "%-*s | %*.*f | %*lld | %*.*f\n",
2274 max_name_len, g_profiler_registry[i].name,
2275 time_width, 6, g_profiler_registry[i].total_time,
2276 count_width, g_profiler_registry[i].total_call_count,
2277 avg_width, 6, avg_time_ms);
2278 PetscFPrintf(PETSC_COMM_SELF, f, "------------------------------------------------------------------------------------------------------------------\n");
2279 }
2280 }
2281 PetscFPrintf(PETSC_COMM_SELF, f, "==================================================================================================================\n");
2282
2283 fclose(f);
2284 }
2285
2286 // --- Final Cleanup ---
2287 PetscFree(g_profiler_registry);
2288 g_profiler_registry = NULL;
2289 g_profiler_count = 0;
2291 PetscFunctionReturn(0);
2292}
long long total_call_count
Definition logging.c:1875
static PetscInt g_profiler_capacity
Definition logging.c:1884
double total_time
Definition logging.c:1873
static int _CompareProfiledFunctions(const void *a, const void *b)
Internal helper implementation: _CompareProfiledFunctions().
Definition logging.c:2180
PetscBool profilingFinalSummary
Definition variables.h:837
@ EXEC_MODE_POSTPROCESSOR
Definition variables.h:658
Here is the call graph for this function:
Here is the caller graph for this function:

◆ _ProfilingStart()

void _ProfilingStart ( const char *  func_name)

Internal profiling hook invoked by PROFILE_FUNCTION_BEGIN.

Parameters
func_nameFunction name used by the profiling helper.

Internal profiling hook invoked by PROFILE_FUNCTION_BEGIN.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
_ProfilingStart()

Definition at line 1951 of file logging.c.

1952{
1953 PetscInt idx;
1954 if (_FindOrCreateEntry(func_name, &idx) != 0) return; // Fail silently
1955 PetscTime(&g_profiler_registry[idx].start_time);
1956}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ _ProfilingEnd()

void _ProfilingEnd ( const char *  func_name)

Internal profiling hook invoked by PROFILE_FUNCTION_END.

Parameters
func_nameFunction name used by the profiling helper.

Internal profiling hook invoked by PROFILE_FUNCTION_END.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
_ProfilingEnd()

Definition at line 1965 of file logging.c.

1966{
1967 double end_time;
1968 PetscTime(&end_time);
1969
1970 PetscInt idx;
1971 if (_FindOrCreateEntry(func_name, &idx) != 0) return; // Fail silently
1972
1973 double elapsed = end_time - g_profiler_registry[idx].start_time;
1974 g_profiler_registry[idx].total_time += elapsed;
1975 g_profiler_registry[idx].current_step_time += elapsed;
1978}
double start_time
Definition logging.c:1877
Here is the call graph for this function:
Here is the caller graph for this function:

◆ LOG_FIELD_MIN_MAX()

PetscErrorCode LOG_FIELD_MIN_MAX ( UserCtx user,
const char *  fieldName 
)

Computes and logs the local and global min/max values of a 3-component vector field.

This utility function inspects a PETSc Vec associated with a DMDA and calculates the minimum and maximum values for each of its three components (e.g., x, y, z) both for the local data on the current MPI rank and for the entire global domain.

It uses the same "smart" logic as the flow solver, ignoring the padding nodes at the IM, JM, and KM boundaries of the grid. The results are printed to the standard output in a formatted, easy-to-read table.

Parameters
[in]userPointer to the user-defined context. Used for grid information (IM, JM, KM) and MPI rank.
[in]fieldNameA string descriptor for the field being analyzed (e.g., "Velocity", "Coordinates"). This is used for clear log output.
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Computes and logs the local and global min/max values of a 3-component vector field.

Full API contract is documented with the header declaration in include/logging.h.

See also
LOG_FIELD_MIN_MAX()

Definition at line 2349 of file logging.c.

2350{
2351 PetscErrorCode ierr;
2352 PetscInt i, j, k;
2353 DMDALocalInfo info;
2354
2355 Vec fieldVec = NULL;
2356 DM dm = NULL;
2357 PetscInt dof;
2358 char data_layout[20];
2359
2360 PetscFunctionBeginUser;
2361
2362 // --- 1. Map string name to PETSc objects and determine data layout ---
2363 if (strcasecmp(fieldName, "Ucat") == 0) {
2364 fieldVec = user->Ucat; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
2365 } else if (strcasecmp(fieldName, "P") == 0) {
2366 fieldVec = user->P; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
2367 } else if (strcasecmp(fieldName, "Diffusivity") == 0) {
2368 fieldVec = user->Diffusivity; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
2369 } else if (strcasecmp(fieldName, "DiffusivityGradient") == 0) {
2370 fieldVec = user->DiffusivityGradient; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
2371 } else if (strcasecmp(fieldName, "Phi") == 0) {
2372 fieldVec = user->Phi; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
2373 } else if (strcasecmp(fieldName, "Nvert") == 0) {
2374 fieldVec = user->Nvert; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
2375 } else if (strcasecmp(fieldName, "Aj") == 0) {
2376 fieldVec = user->Aj; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
2377 } else if (strcasecmp(fieldName, "Cent") == 0 || strcasecmp(fieldName, "Center-Coordinates") == 0) {
2378 fieldVec = user->Cent; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
2379 } else if (strcasecmp(fieldName, "Ucont") == 0) {
2380 fieldVec = user->lUcont; dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered");
2381 } else if (strcasecmp(fieldName, "Centx") == 0 || strcasecmp(fieldName, "X-Face-Centers") == 0) {
2382 fieldVec = user->Centx; dm = user->fda; dof = 3; strcpy(data_layout, "I-Face");
2383 } else if (strcasecmp(fieldName, "Centy") == 0 || strcasecmp(fieldName, "Y-Face-Centers") == 0) {
2384 fieldVec = user->Centy; dm = user->fda; dof = 3; strcpy(data_layout, "J-Face");
2385 } else if (strcasecmp(fieldName, "Centz") == 0 || strcasecmp(fieldName, "Z-Face-Centers") == 0) {
2386 fieldVec = user->Centz; dm = user->fda; dof = 3; strcpy(data_layout, "K-Face");
2387 } else if (strcasecmp(fieldName, "Csi") == 0 || strcasecmp(fieldName, "ICsi") == 0 ||
2388 strcasecmp(fieldName, "IEta") == 0 || strcasecmp(fieldName, "IZet") == 0) {
2389 fieldVec = strcasecmp(fieldName, "Csi") == 0 ? user->Csi :
2390 (strcasecmp(fieldName, "ICsi") == 0 ? user->ICsi :
2391 (strcasecmp(fieldName, "IEta") == 0 ? user->IEta : user->IZet));
2392 dm = user->fda; dof = 3; strcpy(data_layout, "I-Face");
2393 } else if (strcasecmp(fieldName, "Eta") == 0 || strcasecmp(fieldName, "JCsi") == 0 ||
2394 strcasecmp(fieldName, "JEta") == 0 || strcasecmp(fieldName, "JZet") == 0) {
2395 fieldVec = strcasecmp(fieldName, "Eta") == 0 ? user->Eta :
2396 (strcasecmp(fieldName, "JCsi") == 0 ? user->JCsi :
2397 (strcasecmp(fieldName, "JEta") == 0 ? user->JEta : user->JZet));
2398 dm = user->fda; dof = 3; strcpy(data_layout, "J-Face");
2399 } else if (strcasecmp(fieldName, "Zet") == 0 || strcasecmp(fieldName, "KCsi") == 0 ||
2400 strcasecmp(fieldName, "KEta") == 0 || strcasecmp(fieldName, "KZet") == 0) {
2401 fieldVec = strcasecmp(fieldName, "Zet") == 0 ? user->Zet :
2402 (strcasecmp(fieldName, "KCsi") == 0 ? user->KCsi :
2403 (strcasecmp(fieldName, "KEta") == 0 ? user->KEta : user->KZet));
2404 dm = user->fda; dof = 3; strcpy(data_layout, "K-Face");
2405 } else if (strcasecmp(fieldName, "IAj") == 0 || strcasecmp(fieldName, "JAj") == 0 ||
2406 strcasecmp(fieldName, "KAj") == 0) {
2407 fieldVec = strcasecmp(fieldName, "IAj") == 0 ? user->IAj :
2408 (strcasecmp(fieldName, "JAj") == 0 ? user->JAj : user->KAj);
2409 dm = user->da; dof = 1;
2410 strcpy(data_layout, strcasecmp(fieldName, "IAj") == 0 ? "I-Face" :
2411 (strcasecmp(fieldName, "JAj") == 0 ? "J-Face" : "K-Face"));
2412 } else if (strcasecmp(fieldName, "Coordinates") == 0) {
2413 ierr = DMGetCoordinates(user->da, &fieldVec); CHKERRQ(ierr);
2414 dm = user->fda; dof = 3; strcpy(data_layout, "Node-Centered");
2415 } else if (strcasecmp(fieldName,"Psi") == 0) {
2416 fieldVec = user->Psi; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered"); // Assuming Psi is cell-centered
2417 } else {
2418 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Field %s not recognized.", fieldName);
2419 }
2420
2421 if (!fieldVec) {
2422 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Vector for field '%s' is NULL.", fieldName);
2423 }
2424 if (!dm) {
2425 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM for field '%s' is NULL.", fieldName);
2426 }
2427
2428 ierr = DMDAGetLocalInfo(dm, &info); CHKERRQ(ierr);
2429
2430 // --- 2. Define Architecture-Aware Loop Bounds ---
2431 PetscInt i_start, i_end, j_start, j_end, k_start, k_end;
2432
2433 if (strcmp(data_layout, "Cell-Centered") == 0) {
2434 // For cell-centered data, the physical values are stored from index 1 to N-1.
2435 // We find the intersection of the rank's owned range [xs, xe) with the
2436 // physical data range [1, IM-1).
2437 i_start = PetscMax(info.xs, 1); i_end = PetscMin(info.xs + info.xm, user->IM);
2438 j_start = PetscMax(info.ys, 1); j_end = PetscMin(info.ys + info.ym, user->JM);
2439 k_start = PetscMax(info.zs, 1); k_end = PetscMin(info.zs + info.zm, user->KM);
2440 } else { // For Node- or Face-Centered data
2441 // The physical values are stored from index 0 to N-1.
2442 // We find the intersection of the rank's owned range [xs, xe) with the
2443 // physical data range [0, IM-1].
2444 i_start = PetscMax(info.xs, 0); i_end = PetscMin(info.xs + info.xm, user->IM);
2445 j_start = PetscMax(info.ys, 0); j_end = PetscMin(info.ys + info.ym, user->JM);
2446 k_start = PetscMax(info.zs, 0); k_end = PetscMin(info.zs + info.zm, user->KM);
2447 }
2448
2449 // --- 3. Barrier for clean, grouped output ---
2450 ierr = MPI_Barrier(PETSC_COMM_WORLD); CHKERRQ(ierr);
2451 if (user->simCtx->rank == 0) {
2452 PetscPrintf(PETSC_COMM_SELF, "\n--- Field Ranges: [%s] (Layout: %s) ---\n", fieldName, data_layout);
2453 }
2454
2455 // --- 4. Branch on DoF and perform calculation with correct bounds ---
2456 if (dof == 1) {
2457 PetscReal localMin = PETSC_MAX_REAL, localMax = PETSC_MIN_REAL;
2458 PetscReal globalMin, globalMax;
2459 const PetscScalar ***array;
2460
2461 ierr = DMDAVecGetArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
2462 for (k = k_start; k < k_end; k++) {
2463 for (j = j_start; j < j_end; j++) {
2464 for (i = i_start; i < i_end; i++) {
2465 localMin = PetscMin(localMin, array[k][j][i]);
2466 localMax = PetscMax(localMax, array[k][j][i]);
2467 }
2468 }
2469 }
2470 ierr = DMDAVecRestoreArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
2471
2472 ierr = MPI_Allreduce(&localMin, &globalMin, 1, MPIU_REAL, MPI_MIN, PETSC_COMM_WORLD); CHKERRQ(ierr);
2473 ierr = MPI_Allreduce(&localMax, &globalMax, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRQ(ierr);
2474
2475 PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin, localMax);
2476 ierr = PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); CHKERRQ(ierr);
2477 if (user->simCtx->rank == 0) {
2478 PetscPrintf(PETSC_COMM_SELF, " Global Range: [ %11.4e , %11.4e ]\n", globalMin, globalMax);
2479 }
2480
2481 } else if (dof == 3) {
2482 Cmpnts localMin = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MAX_REAL};
2483 Cmpnts localMax = {PETSC_MIN_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
2484 Cmpnts globalMin, globalMax;
2485 const Cmpnts ***array;
2486
2487 ierr = DMDAVecGetArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
2488 for (k = k_start; k < k_end; k++) {
2489 for (j = j_start; j < j_end; j++) {
2490 for (i = i_start; i < i_end; i++) {
2491 localMin.x = PetscMin(localMin.x, array[k][j][i].x);
2492 localMin.y = PetscMin(localMin.y, array[k][j][i].y);
2493 localMin.z = PetscMin(localMin.z, array[k][j][i].z);
2494 localMax.x = PetscMax(localMax.x, array[k][j][i].x);
2495 localMax.y = PetscMax(localMax.y, array[k][j][i].y);
2496 localMax.z = PetscMax(localMax.z, array[k][j][i].z);
2497 }
2498 }
2499 }
2500 ierr = DMDAVecRestoreArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
2501
2502 ierr = MPI_Allreduce(&localMin, &globalMin, 3, MPIU_REAL, MPI_MIN, PETSC_COMM_WORLD); CHKERRQ(ierr);
2503 ierr = MPI_Allreduce(&localMax, &globalMax, 3, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRQ(ierr);
2504
2505 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local X-Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin.x, localMax.x);
2506 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local Y-Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin.y, localMax.y);
2507 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local Z-Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin.z, localMax.z);
2508 ierr = PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); CHKERRQ(ierr);
2509
2510 if (user->simCtx->rank == 0) {
2511 PetscPrintf(PETSC_COMM_SELF, " [Global] X-Range: [ %11.4e , %11.4e ]\n", globalMin.x, globalMax.x);
2512 PetscPrintf(PETSC_COMM_SELF, " [Global] Y-Range: [ %11.4e , %11.4e ]\n", globalMin.y, globalMax.y);
2513 PetscPrintf(PETSC_COMM_SELF, " [Global] Z-Range: [ %11.4e , %11.4e ]\n", globalMin.z, globalMax.z);
2514 }
2515
2516 } else {
2517 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);
2518 }
2519
2520 // --- 5. Final barrier for clean output ordering ---
2521 ierr = MPI_Barrier(PETSC_COMM_WORLD); CHKERRQ(ierr);
2522 if (user->simCtx->rank == 0) {
2523 PetscPrintf(PETSC_COMM_SELF, "--------------------------------------------\n\n");
2524 }
2525
2526 PetscFunctionReturn(0);
2527}
Vec JCsi
Definition variables.h:931
Vec KAj
Definition variables.h:932
Vec JEta
Definition variables.h:931
Vec Zet
Definition variables.h:927
Vec Phi
Definition variables.h:904
Vec IZet
Definition variables.h:930
Vec Centz
Definition variables.h:928
Vec IEta
Definition variables.h:930
PetscInt KM
Definition variables.h:885
Vec Csi
Definition variables.h:927
Vec DiffusivityGradient
Definition variables.h:908
Vec JZet
Definition variables.h:931
Vec Centx
Definition variables.h:928
Vec Eta
Definition variables.h:927
Vec ICsi
Definition variables.h:930
Vec Ucat
Definition variables.h:904
PetscInt JM
Definition variables.h:885
Vec IAj
Definition variables.h:930
Vec JAj
Definition variables.h:931
Vec KEta
Definition variables.h:932
Vec lUcont
Definition variables.h:904
Vec Diffusivity
Definition variables.h:907
PetscInt IM
Definition variables.h:885
Vec KZet
Definition variables.h:932
Vec Cent
Definition variables.h:927
Vec Nvert
Definition variables.h:904
Vec KCsi
Definition variables.h:932
Vec Centy
Definition variables.h:928
Vec Psi
Definition variables.h:953
A 3D point or vector with PetscScalar components.
Definition variables.h:100
Here is the caller graph for this function:

◆ LOG_FIELD_ANATOMY()

PetscErrorCode LOG_FIELD_ANATOMY ( UserCtx user,
const char *  field_name,
const char *  stage_name 
)

Logs the anatomy of a specified field at key boundary locations, respecting the solver's specific grid and variable architecture.

This intelligent diagnostic function inspects a PETSc Vec and prints its values at critical boundary locations (-Xi/+Xi, -Eta/+Eta, -Zeta/+Zeta). It is "architecture-aware":

  • Cell-Centered Fields ("Ucat", "P"): It correctly applies the "Shifted Index Architecture," where the value for geometric Cell i is stored at array index i+1. It labels the output to clearly distinguish between true physical values and ghost values.
  • Single-Face-Family Fields: Csi/ICsi/IEta/IZet/Centx belong to the I-face family, with corresponding J- and K-face families.
  • Component-Staggered Fields ("Ucont" and histories): x/y/z components live on I/J/K faces respectively.
  • Node-Centered Fields ("Coordinates"): It uses a direct index mapping, where the value for Node i is stored at index i.

The output is synchronized across MPI ranks to ensure readability and focuses on a slice through the center of the domain to be concise.

Parameters
userA pointer to the UserCtx structure containing the DMs and Vecs.
field_nameA string identifier for the field to log (e.g., "Ucat", "P", "Ucont", "Coordinates").
stage_nameA string identifier for the current simulation stage (e.g., "After Advection").
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Logs the anatomy of a specified field at key boundary locations, respecting the solver's specific grid and variable architecture.

Full API contract is documented with the header declaration in include/logging.h.

See also
LOG_FIELD_ANATOMY()

Definition at line 2536 of file logging.c.

2537{
2538 PetscErrorCode ierr;
2539 DMDALocalInfo info;
2540 PetscMPIInt rank;
2541
2542 Vec vec_local = NULL;
2543 DM dm = NULL;
2544 PetscInt dof = 0;
2545 char data_layout[20];
2546 char dominant_dir = '\0'; // 'x', 'y', 'z' for face-centered, 'm' for mixed (Ucont)
2547
2548 PetscFunctionBeginUser;
2549 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
2550
2551 // --- 1. Map string name to PETSc objects and determine data layout ---
2552 if (strcasecmp(field_name, "Ucat") == 0) {
2553 vec_local = user->lUcat; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
2554 } else if (strcasecmp(field_name, "P") == 0) {
2555 vec_local = user->lP; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
2556 } else if (strcasecmp(field_name, "Diffusivity") == 0) {
2557 vec_local = user->lDiffusivity; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
2558 } else if (strcasecmp(field_name, "DiffusivityGradient") == 0) {
2559 vec_local = user->lDiffusivityGradient; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
2560 } else if (strcasecmp(field_name, "Psi") == 0) {
2561 vec_local = user->lPsi; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
2562 } else if (strcasecmp(field_name, "Center-Coordinates") == 0) {
2563 vec_local = user->lCent; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
2564 } else if (strcasecmp(field_name, "Ucont") == 0 ||
2565 strcasecmp(field_name, "Ucont_o") == 0 ||
2566 strcasecmp(field_name, "Ucont_rm1") == 0) {
2567 vec_local = strcasecmp(field_name, "Ucont") == 0 ? user->lUcont :
2568 (strcasecmp(field_name, "Ucont_o") == 0 ? user->lUcont_o : user->lUcont_rm1);
2569 dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered"); dominant_dir = 'm'; // Mixed
2570 } else if (strcasecmp(field_name, "Csi") == 0 || strcasecmp(field_name, "X-Face-Centers") == 0 ||
2571 strcasecmp(field_name, "ICsi") == 0 || strcasecmp(field_name, "IEta") == 0 ||
2572 strcasecmp(field_name, "IZet") == 0) {
2573 vec_local = strcasecmp(field_name, "Csi") == 0 ? user->lCsi :
2574 (strcasecmp(field_name, "X-Face-Centers") == 0 ? user->lCentx :
2575 (strcasecmp(field_name, "ICsi") == 0 ? user->lICsi :
2576 (strcasecmp(field_name, "IEta") == 0 ? user->lIEta : user->lIZet)));
2577 dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered"); dominant_dir = 'x';
2578 } else if (strcasecmp(field_name, "Eta") == 0 || strcasecmp(field_name, "Y-Face-Centers") == 0 ||
2579 strcasecmp(field_name, "JCsi") == 0 || strcasecmp(field_name, "JEta") == 0 ||
2580 strcasecmp(field_name, "JZet") == 0) {
2581 vec_local = strcasecmp(field_name, "Eta") == 0 ? user->lEta :
2582 (strcasecmp(field_name, "Y-Face-Centers") == 0 ? user->lCenty :
2583 (strcasecmp(field_name, "JCsi") == 0 ? user->lJCsi :
2584 (strcasecmp(field_name, "JEta") == 0 ? user->lJEta : user->lJZet)));
2585 dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered"); dominant_dir = 'y';
2586 } else if (strcasecmp(field_name, "Zet") == 0 || strcasecmp(field_name, "Z-Face-Centers") == 0 ||
2587 strcasecmp(field_name, "KCsi") == 0 || strcasecmp(field_name, "KEta") == 0 ||
2588 strcasecmp(field_name, "KZet") == 0) {
2589 vec_local = strcasecmp(field_name, "Zet") == 0 ? user->lZet :
2590 (strcasecmp(field_name, "Z-Face-Centers") == 0 ? user->lCentz :
2591 (strcasecmp(field_name, "KCsi") == 0 ? user->lKCsi :
2592 (strcasecmp(field_name, "KEta") == 0 ? user->lKEta : user->lKZet)));
2593 dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered"); dominant_dir = 'z';
2594 } else if (strcasecmp(field_name, "Coordinates") == 0) {
2595 ierr = DMGetCoordinatesLocal(user->da, &vec_local); CHKERRQ(ierr);
2596 dm = user->fda; dof = 3; strcpy(data_layout, "Node-Centered");
2597 } else if (strcasecmp(field_name, "CornerField")== 0){
2598 vec_local = user->lCellFieldAtCorner; strcpy(data_layout, "Node-Centered");
2599 PetscInt bs = 1;
2600 ierr = VecGetBlockSize(user->CellFieldAtCorner, &bs); CHKERRQ(ierr);
2601 dof = bs;
2602 if(dof == 1) dm = user->da;
2603 else dm = user->fda;
2604 } else {
2605 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Unknown field name for LOG_FIELD_ANATOMY: %s", field_name);
2606 }
2607
2608 // --- 2. Get Grid Info and Array Pointers ---
2609 ierr = DMDAGetLocalInfo(dm, &info); CHKERRQ(ierr);
2610
2611 ierr = PetscBarrier(NULL);
2612 PetscPrintf(PETSC_COMM_WORLD, "\n--- Field Anatomy Log: [%s] | Stage: [%s] | Layout: [%s] ---\n", field_name, stage_name, data_layout);
2613
2614 // Global physical dimensions (number of cells)
2615 PetscInt im_phys = user->IM;
2616 PetscInt jm_phys = user->JM;
2617 PetscInt km_phys = user->KM;
2618
2619 // Slice through the center of the local domain
2620 PetscInt i_mid = (PetscInt)(info.xs + 0.5 * info.xm) - 1;
2621 PetscInt j_mid = (PetscInt)(info.ys + 0.5 * info.ym) - 1;
2622 PetscInt k_mid = (PetscInt)(info.zs + 0.5 * info.zm) - 1;
2623
2624 // --- 3. Print Boundary Information based on Data Layout ---
2625
2626 // ======================================================================
2627 // === CASE 1: Cell-Centered Fields (Ucat, P) - USES SHIFTED INDEX ===
2628 // ======================================================================
2629 if (strcmp(data_layout, "Cell-Centered") == 0) {
2630 const void *l_arr;
2631 ierr = DMDAVecGetArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
2632
2633
2634 // --- I-Direction Boundaries ---
2635 if (info.xs == 0) { // Rank on -Xi boundary
2636 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Ghost for Cell[k][j][0]) = ", rank, 0);
2637 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][0]);
2638 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);
2639
2640 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Value for Cell[k][j][0]) = ", rank, 1);
2641 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][1]);
2642 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);
2643 }
2644 if (info.xs + info.xm == info.mx) { // Rank on +Xi boundary
2645 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Value for Cell[k][j][%d]) = ", rank, im_phys - 1, im_phys - 2);
2646 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][im_phys - 1]);
2647 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);
2648
2649 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Ghost for Cell[k][j][%d]) = ", rank, im_phys, im_phys - 2);
2650 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][im_phys]);
2651 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);
2652 }
2653
2654 // --- J-Direction Boundaries ---
2655 if (info.ys == 0) { // Rank on -Eta boundary
2656 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Ghost for Cell[k][0][i]) = ", rank, 0);
2657 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][0][i_mid]);
2658 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);
2659
2660 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Value for Cell[k][0][i]) = ", rank, 1);
2661 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][1][i_mid]);
2662 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);
2663 }
2664
2665 if (info.ys + info.ym == info.my) { // Rank on +Eta boundary
2666 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Value for Cell[k][%d][i]) = ", rank, jm_phys - 1, jm_phys - 2);
2667 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][jm_phys - 1][i_mid]);
2668 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);
2669
2670 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Jdx %2d (Ghost for Cell[k][%d][i]) = ", rank, jm_phys, jm_phys - 2);
2671 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[k_mid][jm_phys][i_mid]);
2672 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);
2673 }
2674
2675 // --- K-Direction Boundaries ---
2676 if (info.zs == 0) { // Rank on -Zeta boundary
2677 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Ghost for Cell[0][j][i]) = ", rank, 0);
2678 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[0][j_mid][i_mid]);
2679 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);
2680 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Value for Cell[0][j][i]) = ", rank, 1);
2681 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[1][j_mid][i_mid]);
2682 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);
2683 }
2684 if (info.zs + info.zm == info.mz) { // Rank on +Zeta boundary
2685 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Value for Cell[%d][j][i]) = ", rank, km_phys - 1, km_phys - 2);
2686 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[km_phys - 1][j_mid][i_mid]);
2687 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);
2688 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Kdx %2d (Ghost for Cell[%d][j][i]) = ", rank, km_phys, km_phys - 2);
2689 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.5f)\n", ((const PetscReal***)l_arr)[km_phys][j_mid][i_mid]);
2690 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);
2691 }
2692 ierr = DMDAVecRestoreArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
2693 }
2694 // ======================================================================
2695 // === CASE 2: Face-Centered Fields - NUANCED DIRECTIONAL LOGIC ===
2696 // ======================================================================
2697 else if (strcmp(data_layout, "Face-Centered") == 0) {
2698 const Cmpnts ***l_arr;
2699 ierr = DMDAVecGetArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
2700
2701 // --- I-Direction Boundaries ---
2702 if (info.xs == 0) { // Rank on -Xi boundary
2703 if (dominant_dir == 'x') { // Node-like in I-dir
2704 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);
2705 } else if (dominant_dir == 'y' || dominant_dir == 'z') { // Cell-like in I-dir
2706 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);
2707 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);
2708 } else if (dominant_dir == 'm') { // Ucont: Mixed
2709 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);
2710 }
2711 }
2712 if (info.xs + info.xm == info.mx) { // Rank on +Xi boundary
2713 if (dominant_dir == 'x') { // Node-like in I-dir
2714 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);
2715 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);
2716 } else if (dominant_dir == 'y' || dominant_dir == 'z') { // Cell-like in I-dir
2717 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);
2718 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);
2719 } else if (dominant_dir == 'm') { // Ucont: Mixed
2720 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);
2721 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);
2722 }
2723 }
2724
2725 // --- J-Direction Boundaries ---
2726 if (info.ys == 0) { // Rank on -Eta boundary
2727 if (dominant_dir == 'y') { // Node-like in J-dir
2728 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);
2729 } else if (dominant_dir == 'x' || dominant_dir == 'z') { // Cell-like in J-dir
2730 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);
2731 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);
2732 } else if (dominant_dir == 'm') { // Ucont: Mixed
2733 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);
2734 }
2735 }
2736 if (info.ys + info.ym == info.my) { // Rank on +Eta boundary
2737 if (dominant_dir == 'y') { // Node-like in J-dir
2738 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);
2739 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);
2740 } else if (dominant_dir == 'x' || dominant_dir == 'z') { // Cell-like in J-dir
2741 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);
2742 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);
2743 } else if (dominant_dir == 'm') { // Ucont: Mixed
2744 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);
2745 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);
2746 }
2747 }
2748
2749 // --- K-Direction Boundaries ---
2750 if (info.zs == 0) { // Rank on -Zeta boundary
2751 if (dominant_dir == 'z') { // Node-like in K-dir
2752 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);
2753 } else if (dominant_dir == 'x' || dominant_dir == 'y') { // Cell-like in K-dir
2754 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);
2755 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);
2756 } else if (dominant_dir == 'm') { // Ucont: Mixed
2757 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);
2758 }
2759 }
2760 if (info.zs + info.zm == info.mz) { // Rank on +Zeta boundary
2761 if (dominant_dir == 'z') { // Node-like in K-dir
2762 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);
2763 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);
2764 } else if (dominant_dir == 'x' || dominant_dir == 'y') { // Cell-like in K-dir
2765 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);
2766 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);
2767 } else if (dominant_dir == 'm') { // Ucont: Mixed
2768 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);
2769 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);
2770
2771 }
2772 }
2773 ierr = DMDAVecRestoreArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
2774 }
2775 // ======================================================================
2776 // === CASE 3: Node-Centered Fields - USES DIRECT INDEX ===
2777 // ======================================================================
2778 else if (strcmp(data_layout, "Node-Centered") == 0) {
2779 const Cmpnts ***l_arr;
2780 ierr = DMDAVecGetArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
2781
2782 // --- I-Direction Boundaries ---
2783 if (info.xs == 0) {
2784 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);
2785 }
2786 if (info.xs + info.xm == info.mx) {
2787 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);
2788 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);
2789 }
2790 // --- J-Direction Boundaries ---
2791 if (info.ys == 0) {
2792 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);
2793 }
2794 if (info.ys + info.ym == info.my) {
2795 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);
2796 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);
2797 }
2798 // --- K-Direction Boundaries ---
2799 if (info.zs == 0) {
2800 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);
2801 }
2802 if(info.zs + info.zm == info.mz) {
2803 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);
2804 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);
2805 }
2806 ierr = DMDAVecRestoreArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
2807 }
2808 else {
2809 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "LOG_FIELD_ANATOMY encountered an unknown data layout: %s", data_layout);
2810 }
2811
2812 ierr = PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); CHKERRQ(ierr);
2813 ierr = PetscBarrier(NULL);
2814 PetscFunctionReturn(0);
2815}
Vec lDiffusivityGradient
Definition variables.h:908
Vec lCent
Definition variables.h:927
Vec lIEta
Definition variables.h:930
Vec lIZet
Definition variables.h:930
Vec lZet
Definition variables.h:927
Vec lUcont_rm1
Definition variables.h:912
Vec lCellFieldAtCorner
Definition variables.h:915
Vec lKEta
Definition variables.h:932
Vec lPsi
Definition variables.h:953
Vec lJCsi
Definition variables.h:931
Vec lUcont_o
Definition variables.h:911
Vec lKZet
Definition variables.h:932
Vec lJEta
Definition variables.h:931
Vec lCsi
Definition variables.h:927
Vec lKCsi
Definition variables.h:932
Vec CellFieldAtCorner
Definition variables.h:915
Vec lCenty
Definition variables.h:929
Vec lJZet
Definition variables.h:931
Vec lCentx
Definition variables.h:929
Vec lICsi
Definition variables.h:930
Vec lUcat
Definition variables.h:904
Vec lEta
Definition variables.h:927
Vec lDiffusivity
Definition variables.h:907
Vec lCentz
Definition variables.h:929
Here is the caller graph for this function:

◆ LOG_INTERPOLATION_ERROR()

PetscErrorCode LOG_INTERPOLATION_ERROR ( UserCtx user)

Logs the interpolation error between the analytical and computed solutions.

Parameters
userPrimary UserCtx input for the operation.
Returns
PetscErrorCode 0 on success.

Logs the interpolation error between the analytical and computed solutions.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
LOG_INTERPOLATION_ERROR()

Definition at line 2825 of file logging.c.

2826{
2827 SimCtx *simCtx = user->simCtx;
2828 PetscErrorCode ierr;
2829 DM swarm = user->swarm;
2830 Vec positionVec, analyticalvelocityVec, velocityVec, errorVec;
2831 PetscReal Interpolation_error = 0.0;
2832 PetscReal Maximum_Interpolation_error = 0.0;
2833 PetscReal AnalyticalSolution_magnitude = 0.0;
2834 PetscReal ErrorPercentage = 0.0;
2835
2836 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Creating global vectors.\n");
2837 ierr = DMSwarmCreateGlobalVectorFromField(swarm, "position", &positionVec); CHKERRQ(ierr);
2838 ierr = DMSwarmCreateGlobalVectorFromField(swarm, "velocity", &velocityVec); CHKERRQ(ierr);
2839
2840 ierr = VecDuplicate(positionVec, &analyticalvelocityVec); CHKERRQ(ierr);
2841 ierr = VecCopy(positionVec, analyticalvelocityVec); CHKERRQ(ierr);
2842
2843 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Computing analytical solution.\n");
2844 ierr = SetAnalyticalSolutionForParticles(analyticalvelocityVec, simCtx); CHKERRQ(ierr);
2845
2846 ierr = VecDuplicate(analyticalvelocityVec, &errorVec); CHKERRQ(ierr);
2847 ierr = VecCopy(analyticalvelocityVec, errorVec); CHKERRQ(ierr);
2848
2849 ierr = VecNorm(analyticalvelocityVec, NORM_2, &AnalyticalSolution_magnitude); CHKERRQ(ierr);
2850
2851 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Computing error.\n");
2852 ierr = VecAXPY(errorVec, -1.0, velocityVec); CHKERRQ(ierr);
2853 ierr = VecNorm(errorVec, NORM_2, &Interpolation_error); CHKERRQ(ierr);
2854 ierr = VecNorm(errorVec,NORM_INFINITY,&Maximum_Interpolation_error); CHKERRQ(ierr);
2855
2856 ErrorPercentage = (AnalyticalSolution_magnitude > 0) ?
2857 (Interpolation_error / AnalyticalSolution_magnitude * 100.0) : 0.0;
2858
2859 /* --- CSV output (always, rank 0 only) --- */
2860 if (simCtx->rank == 0) {
2861 char csv_path[PETSC_MAX_PATH_LEN + 32];
2862 ierr = PetscSNPrintf(csv_path, sizeof(csv_path), "%s/interpolation_error.csv", simCtx->log_dir); CHKERRQ(ierr);
2863 FILE *f = fopen(csv_path, "a");
2864 if (f) {
2865 if (ftell(f) == 0) {
2866 fprintf(f, "step,time,L2_error,Linf_error,L2_analytical,error_pct\n");
2867 }
2868 if (simCtx->continueMode && simCtx->step == simCtx->StartStep + 1) {
2869 fprintf(f, "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep);
2870 }
2871 PetscReal t = (PetscReal)simCtx->ti * simCtx->dt;
2872 fprintf(f, "%d,%.6e,%.6e,%.6e,%.6e,%.4f\n",
2873 (int)simCtx->step, t,
2874 Interpolation_error, Maximum_Interpolation_error,
2875 AnalyticalSolution_magnitude, ErrorPercentage);
2876 fclose(f);
2877 }
2878 }
2879
2880 /* --- Console output (only at INFO level or above) --- */
2881 if (get_log_level() >= LOG_INFO) {
2882 LOG_ALLOW(GLOBAL, LOG_INFO, "Interpolation error (%%): %g\n", ErrorPercentage);
2883 PetscPrintf(PETSC_COMM_WORLD, "Interpolation error (%%): %g\n", ErrorPercentage);
2884 LOG_ALLOW(GLOBAL, LOG_INFO, "Maximum Interpolation error: %g\n", Maximum_Interpolation_error);
2885 PetscPrintf(PETSC_COMM_WORLD, "Maximum Interpolation error: %g\n", Maximum_Interpolation_error);
2886 }
2887
2888 ierr = VecDestroy(&analyticalvelocityVec); CHKERRQ(ierr);
2889 ierr = VecDestroy(&errorVec); CHKERRQ(ierr);
2890 ierr = DMSwarmDestroyGlobalVectorFromField(swarm, "position", &positionVec); CHKERRQ(ierr);
2891 ierr = DMSwarmDestroyGlobalVectorFromField(swarm, "velocity", &velocityVec); CHKERRQ(ierr);
2892
2893 return 0;
2894}
PetscErrorCode SetAnalyticalSolutionForParticles(Vec tempVec, SimCtx *simCtx)
Applies the analytical solution to particle velocity vector.
PetscReal dt
Definition variables.h:699
Here is the call graph for this function:
Here is the caller graph for this function:

◆ LOG_SCATTER_METRICS()

PetscErrorCode LOG_SCATTER_METRICS ( UserCtx user)

Logs particle-to-grid scatter verification metrics for the prescribed scalar truth path.

Parameters
userPrimary UserCtx input for the operation.
Returns
PetscErrorCode 0 on success.

Logs particle-to-grid scatter verification metrics for the prescribed scalar truth path.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
LOG_SCATTER_METRICS()

Definition at line 2904 of file logging.c.

2905{
2906 PetscErrorCode ierr;
2907 SimCtx *simCtx = NULL;
2908 DMDALocalInfo info;
2909 PetscInt xs, xe, ys, ye, zs, ze, mx, my, mz;
2910 PetscInt lxs, lxe, lys, lye, lzs, lze;
2911 Vec reference_vec = NULL;
2912 PetscReal ***psi = NULL;
2913 PetscReal ***psi_ref = NULL;
2914 PetscReal ***aj = NULL;
2915 PetscReal ***count = NULL;
2916 PetscReal *particle_psi = NULL;
2917 PetscInt nlocal = 0;
2918 PetscReal local_l1 = 0.0, global_l1 = 0.0;
2919 PetscReal local_l2_sq = 0.0, global_l2_sq = 0.0;
2920 PetscReal local_linf = 0.0, global_linf = 0.0;
2921 PetscReal local_ref_l2_sq = 0.0, global_ref_l2_sq = 0.0;
2922 PetscReal local_grid_integral = 0.0, global_grid_integral = 0.0;
2923 PetscReal local_domain_volume = 0.0, global_domain_volume = 0.0;
2924 PetscReal local_particle_sum = 0.0, global_particle_sum = 0.0;
2925 PetscInt64 local_particle_count = 0, global_particle_count = 0;
2926 PetscInt64 local_cell_count = 0, global_cell_count = 0;
2927 PetscInt64 local_occupied_count = 0, global_occupied_count = 0;
2928 PetscReal particle_integral = 0.0;
2929 PetscReal occupancy_fraction = 0.0;
2930 PetscReal mean_particles_per_occupied_cell = 0.0;
2931 PetscReal l2_error = 0.0;
2932 PetscReal relative_l2_error = 0.0;
2933
2934 PetscFunctionBeginUser;
2935 if (!user) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "UserCtx cannot be NULL.");
2936 simCtx = user->simCtx;
2937 if (!VerificationScalarOverrideActive(simCtx) || !user->swarm || !user->Psi || !user->ParticleCount) {
2938 PetscFunctionReturn(0);
2939 }
2940
2941 info = user->info;
2942 xs = info.xs; xe = info.xs + info.xm;
2943 ys = info.ys; ye = info.ys + info.ym;
2944 zs = info.zs; ze = info.zs + info.zm;
2945 mx = info.mx; my = info.my; mz = info.mz;
2946 lxs = (xs == 0) ? xs + 1 : xs; lxe = (xe == mx) ? xe - 1 : xe;
2947 lys = (ys == 0) ? ys + 1 : ys; lye = (ye == my) ? ye - 1 : ye;
2948 lzs = (zs == 0) ? zs + 1 : zs; lze = (ze == mz) ? ze - 1 : ze;
2949
2950 ierr = VecDuplicate(user->Psi, &reference_vec); CHKERRQ(ierr);
2951 ierr = SetAnalyticalScalarFieldAtCellCenters(user, reference_vec); CHKERRQ(ierr);
2952
2953 ierr = DMDAVecGetArrayRead(user->da, user->Psi, &psi); CHKERRQ(ierr);
2954 ierr = DMDAVecGetArrayRead(user->da, reference_vec, &psi_ref); CHKERRQ(ierr);
2955 ierr = DMDAVecGetArrayRead(user->da, user->Aj, &aj); CHKERRQ(ierr);
2956 ierr = DMDAVecGetArrayRead(user->da, user->ParticleCount, &count); CHKERRQ(ierr);
2957
2958 for (PetscInt k = lzs; k < lze; ++k) {
2959 for (PetscInt j = lys; j < lye; ++j) {
2960 for (PetscInt i = lxs; i < lxe; ++i) {
2961 const PetscReal cell_volume = (PetscAbsReal(aj[k][j][i]) > 1.0e-14) ? (1.0 / aj[k][j][i]) : 0.0;
2962 const PetscReal err = psi[k][j][i] - psi_ref[k][j][i];
2963 local_cell_count += 1;
2964 local_domain_volume += cell_volume;
2965 local_grid_integral += psi[k][j][i] * cell_volume;
2966 local_l1 += PetscAbsReal(err) * cell_volume;
2967 local_l2_sq += err * err * cell_volume;
2968 local_ref_l2_sq += psi_ref[k][j][i] * psi_ref[k][j][i] * cell_volume;
2969 local_linf = PetscMax(local_linf, PetscAbsReal(err));
2970 if (count[k][j][i] > 0.0) local_occupied_count += 1;
2971 }
2972 }
2973 }
2974
2975 ierr = DMDAVecRestoreArrayRead(user->da, user->ParticleCount, &count); CHKERRQ(ierr);
2976 ierr = DMDAVecRestoreArrayRead(user->da, user->Aj, &aj); CHKERRQ(ierr);
2977 ierr = DMDAVecRestoreArrayRead(user->da, reference_vec, &psi_ref); CHKERRQ(ierr);
2978 ierr = DMDAVecRestoreArrayRead(user->da, user->Psi, &psi); CHKERRQ(ierr);
2979 ierr = VecDestroy(&reference_vec); CHKERRQ(ierr);
2980
2981 ierr = DMSwarmGetLocalSize(user->swarm, &nlocal); CHKERRQ(ierr);
2982 local_particle_count = (PetscInt64)nlocal;
2983 if (nlocal > 0) {
2984 ierr = DMSwarmGetField(user->swarm, "Psi", NULL, NULL, (void **)&particle_psi); CHKERRQ(ierr);
2985 for (PetscInt p = 0; p < nlocal; ++p) local_particle_sum += particle_psi[p];
2986 ierr = DMSwarmRestoreField(user->swarm, "Psi", NULL, NULL, (void **)&particle_psi); CHKERRQ(ierr);
2987 }
2988
2989 ierr = MPI_Allreduce(&local_l1, &global_l1, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2990 ierr = MPI_Allreduce(&local_l2_sq, &global_l2_sq, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2991 ierr = MPI_Allreduce(&local_linf, &global_linf, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2992 ierr = MPI_Allreduce(&local_ref_l2_sq, &global_ref_l2_sq, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2993 ierr = MPI_Allreduce(&local_grid_integral, &global_grid_integral, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2994 ierr = MPI_Allreduce(&local_domain_volume, &global_domain_volume, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2995 ierr = MPI_Allreduce(&local_particle_sum, &global_particle_sum, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2996 ierr = MPI_Allreduce(&local_particle_count, &global_particle_count, 1, MPIU_INT64, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2997 ierr = MPI_Allreduce(&local_cell_count, &global_cell_count, 1, MPIU_INT64, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2998 ierr = MPI_Allreduce(&local_occupied_count, &global_occupied_count, 1, MPIU_INT64, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2999
3000 l2_error = PetscSqrtReal(global_l2_sq);
3001 relative_l2_error = (global_ref_l2_sq > 0.0) ? (l2_error / PetscSqrtReal(global_ref_l2_sq)) : 0.0;
3002 occupancy_fraction = (global_cell_count > 0) ? ((PetscReal)global_occupied_count / (PetscReal)global_cell_count) : 0.0;
3003 mean_particles_per_occupied_cell =
3004 (global_occupied_count > 0) ? ((PetscReal)global_particle_count / (PetscReal)global_occupied_count) : 0.0;
3005 particle_integral =
3006 (global_particle_count > 0) ? (global_domain_volume * global_particle_sum / (PetscReal)global_particle_count) : 0.0;
3007
3008 if (simCtx->rank == 0) {
3009 char csv_path[PETSC_MAX_PATH_LEN + 32];
3010 FILE *f = NULL;
3011 ierr = PetscSNPrintf(csv_path, sizeof(csv_path), "%s/scatter_metrics.csv", simCtx->log_dir); CHKERRQ(ierr);
3012 f = fopen(csv_path, "a");
3013 if (f) {
3014 if (ftell(f) == 0) {
3015 fprintf(f,
3016 "step,time,total_particles,total_cells,occupied_cells,occupancy_fraction,"
3017 "mean_particles_per_occupied_cell,particle_integral,grid_integral,"
3018 "conservation_error_abs,L1_error,L2_error,Linf_error,relative_L2_error\n");
3019 }
3020 if (simCtx->continueMode && simCtx->step == simCtx->StartStep + 1) {
3021 fprintf(f, "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep);
3022 }
3023 fprintf(f, "%d,%.6e,%lld,%lld,%lld,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e\n",
3024 (int)simCtx->step,
3025 (double)simCtx->ti,
3026 (long long)global_particle_count,
3027 (long long)global_cell_count,
3028 (long long)global_occupied_count,
3029 (double)occupancy_fraction,
3030 (double)mean_particles_per_occupied_cell,
3031 (double)particle_integral,
3032 (double)global_grid_integral,
3033 (double)PetscAbsReal(global_grid_integral - particle_integral),
3034 (double)global_l1,
3035 (double)l2_error,
3036 (double)global_linf,
3037 (double)relative_l2_error);
3038 fclose(f);
3039 }
3040 }
3041
3042 if (get_log_level() >= LOG_INFO) {
3043 LOG_ALLOW(GLOBAL, LOG_INFO, "Scatter relative L2 error: %.6e\n", (double)relative_l2_error);
3044 LOG_ALLOW(GLOBAL, LOG_INFO, "Scatter occupancy fraction: %.6e\n", (double)occupancy_fraction);
3045 }
3046
3047 PetscFunctionReturn(0);
3048}
PetscErrorCode SetAnalyticalScalarFieldAtCellCenters(UserCtx *user, Vec targetVec)
Writes the configured verification scalar profile at physical cell centers into a scalar Vec.
Vec ParticleCount
Definition variables.h:952
DMDALocalInfo info
Definition variables.h:883
PetscBool VerificationScalarOverrideActive(const SimCtx *simCtx)
Reports whether a verification-only scalar override is active.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ResetSearchMetrics()

PetscErrorCode ResetSearchMetrics ( SimCtx simCtx)

Resets the aggregate per-timestep search instrumentation counters.

Parameters
simCtxSimulation context whose search metrics should be zeroed.
Returns
PetscErrorCode 0 on success.

Resets the aggregate per-timestep search instrumentation counters.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
ResetSearchMetrics()

Definition at line 3058 of file logging.c.

3059{
3060 PetscFunctionBeginUser;
3061 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx cannot be NULL for ResetSearchMetrics.");
3062
3063 simCtx->searchMetrics.searchAttempts = 0;
3064 simCtx->searchMetrics.searchPopulation = 0;
3066 simCtx->searchMetrics.searchLostCount = 0;
3067 simCtx->searchMetrics.traversalStepsSum = 0;
3068 simCtx->searchMetrics.reSearchCount = 0;
3069 simCtx->searchMetrics.maxTraversalSteps = 0;
3071 simCtx->searchMetrics.tieBreakCount = 0;
3077
3078 PetscFunctionReturn(0);
3079}
PetscInt64 searchLocatedCount
Definition variables.h:239
PetscInt64 searchLostCount
Definition variables.h:240
PetscInt64 boundaryClampCount
Definition variables.h:246
PetscInt64 traversalStepsSum
Definition variables.h:241
PetscInt64 searchPopulation
Definition variables.h:238
PetscInt currentSettlementPass
Definition variables.h:250
PetscInt64 reSearchCount
Definition variables.h:242
PetscInt64 bboxGuessFallbackCount
Definition variables.h:248
PetscInt64 bboxGuessSuccessCount
Definition variables.h:247
PetscInt64 maxParticlePassDepth
Definition variables.h:249
PetscInt64 maxTraversalSteps
Definition variables.h:243
SearchMetricsState searchMetrics
Definition variables.h:809
PetscInt64 searchAttempts
Definition variables.h:237
PetscInt64 tieBreakCount
Definition variables.h:245
PetscInt64 maxTraversalFailCount
Definition variables.h:244
Here is the caller graph for this function:

◆ CalculateAdvancedParticleMetrics()

PetscErrorCode CalculateAdvancedParticleMetrics ( UserCtx user)

Computes advanced particle statistics and stores them in SimCtx.

This function calculates:

  • Particle load imbalance across MPI ranks.
  • The total number of grid cells occupied by at least one particle.

It requires that CalculateParticleCountPerCell() has been called prior to its execution. It uses collective MPI operations and must be called by all ranks.

Parameters
userPointer to the UserCtx.
Returns
PetscErrorCode 0 on success.

Computes advanced particle statistics and stores them in SimCtx.

Local to this translation unit.

Definition at line 3243 of file logging.c.

3244{
3245 PetscErrorCode ierr;
3246 SimCtx *simCtx = user->simCtx;
3247 PetscMPIInt size, rank;
3248
3249 PetscFunctionBeginUser;
3250 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size); CHKERRQ(ierr);
3251 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
3252
3253 // --- 1. Particle Load Imbalance ---
3254 PetscInt nLocal, nGlobal, nLocalMax;
3255 ierr = DMSwarmGetLocalSize(user->swarm, &nLocal); CHKERRQ(ierr);
3256 ierr = DMSwarmGetSize(user->swarm, &nGlobal); CHKERRQ(ierr);
3257 ierr = MPI_Allreduce(&nLocal, &nLocalMax, 1, MPIU_INT, MPI_MAX, PETSC_COMM_WORLD); CHKERRQ(ierr);
3258
3259 PetscReal avg_per_rank = (size > 0) ? ((PetscReal)nGlobal / size) : 0.0;
3260 // Handle division by zero if there are no particles
3261 simCtx->particleLoadImbalance = (avg_per_rank > 1e-9) ? (nLocalMax / avg_per_rank) : 1.0;
3262
3263
3264 // --- 2. Number of Occupied Cells ---
3265 // This part requires access to the user->ParticleCount vector.
3266 PetscInt local_occupied_cells = 0;
3267 PetscInt global_occupied_cells;
3268 const PetscScalar *count_array;
3269 PetscInt vec_local_size;
3270
3271 ierr = VecGetLocalSize(user->ParticleCount, &vec_local_size); CHKERRQ(ierr);
3272 ierr = VecGetArrayRead(user->ParticleCount, &count_array); CHKERRQ(ierr);
3273
3274 for (PetscInt i = 0; i < vec_local_size; ++i) {
3275 if (count_array[i] > 0.5) { // Use 0.5 to be safe with floating point
3276 local_occupied_cells++;
3277 }
3278 }
3279 ierr = VecRestoreArrayRead(user->ParticleCount, &count_array); CHKERRQ(ierr);
3280
3281 ierr = MPI_Allreduce(&local_occupied_cells, &global_occupied_cells, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
3282 simCtx->occupiedCellCount = global_occupied_cells;
3283
3284 LOG_ALLOW_SYNC(GLOBAL, LOG_INFO, "[Rank %d] Advanced Metrics: Imbalance=%.2f, OccupiedCells=%d\n", rank, simCtx->particleLoadImbalance, simCtx->occupiedCellCount);
3285
3286 PetscFunctionReturn(0);
3287}
PetscInt occupiedCellCount
Definition variables.h:807
PetscReal particleLoadImbalance
Definition variables.h:808
Here is the caller graph for this function:

◆ LOG_SEARCH_METRICS()

PetscErrorCode LOG_SEARCH_METRICS ( UserCtx user)

Writes compact runtime search metrics to CSV and optionally to console.

The CSV artifact is always written for particle-enabled runs. Console output remains gated by normal logging level and function allow-listing.

Parameters
userPointer to the UserCtx.
Returns
PetscErrorCode 0 on success.

Writes compact runtime search metrics to CSV and optionally to console.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
LOG_SEARCH_METRICS()

Definition at line 3089 of file logging.c.

3090{
3091 PetscErrorCode ierr;
3092 SimCtx *simCtx = NULL;
3093 PetscInt totalParticles = 0;
3094 PetscReal local_metrics[SEARCH_METRIC_REDUCTION_LEN] = {0.0};
3095 PetscReal global_metrics[SEARCH_METRIC_REDUCTION_LEN] = {0.0};
3096 PetscReal meanTraversalSteps = 0.0;
3097 PetscReal searchFailureFraction = 0.0;
3098 PetscReal searchWorkIndex = 0.0;
3099 PetscReal reSearchFraction = 0.0;
3100 long long searchAttempts = 0;
3101 long long searchPopulation = 0;
3102 long long searchLocatedCount = 0;
3103 long long searchLostCount = 0;
3104 long long traversalStepsSum = 0;
3105 long long reSearchCount = 0;
3106 long long tieBreakCount = 0;
3107 long long boundaryClampCount = 0;
3108 long long bboxGuessSuccessCount = 0;
3109 long long bboxGuessFallbackCount = 0;
3110 long long maxTraversalFailCount = 0;
3111 long long maxTraversalSteps = 0;
3112 long long maxPassDepth = 0;
3113 MPI_Op reduction_op = MPI_OP_NULL;
3114
3115 PetscFunctionBeginUser;
3116 if (!user || !user->simCtx) {
3117 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "UserCtx and SimCtx are required for LOG_SEARCH_METRICS.");
3118 }
3119 simCtx = user->simCtx;
3120
3121 if (simCtx->np <= 0) {
3122 PetscFunctionReturn(0);
3123 }
3124
3125 ierr = DMSwarmGetSize(user->swarm, &totalParticles); CHKERRQ(ierr);
3126
3127 local_metrics[SEARCH_METRIC_SUM_SEARCH_ATTEMPTS] = (PetscReal)simCtx->searchMetrics.searchAttempts;
3128 local_metrics[SEARCH_METRIC_SUM_SEARCH_POPULATION] = (PetscReal)simCtx->searchMetrics.searchPopulation;
3129 local_metrics[SEARCH_METRIC_SUM_SEARCH_LOCATED] = (PetscReal)simCtx->searchMetrics.searchLocatedCount;
3130 local_metrics[SEARCH_METRIC_SUM_SEARCH_LOST] = (PetscReal)simCtx->searchMetrics.searchLostCount;
3131 local_metrics[SEARCH_METRIC_SUM_TRAVERSAL_STEPS] = (PetscReal)simCtx->searchMetrics.traversalStepsSum;
3132 local_metrics[SEARCH_METRIC_SUM_RESEARCH] = (PetscReal)simCtx->searchMetrics.reSearchCount;
3133 local_metrics[SEARCH_METRIC_SUM_TIE_BREAKS] = (PetscReal)simCtx->searchMetrics.tieBreakCount;
3134 local_metrics[SEARCH_METRIC_SUM_BOUNDARY_CLAMPS] = (PetscReal)simCtx->searchMetrics.boundaryClampCount;
3135 local_metrics[SEARCH_METRIC_SUM_BBOX_GUESS_SUCCESS] = (PetscReal)simCtx->searchMetrics.bboxGuessSuccessCount;
3136 local_metrics[SEARCH_METRIC_SUM_BBOX_GUESS_FALLBACK] = (PetscReal)simCtx->searchMetrics.bboxGuessFallbackCount;
3137 local_metrics[SEARCH_METRIC_SUM_MAX_TRAVERSAL_FAILS] = (PetscReal)simCtx->searchMetrics.maxTraversalFailCount;
3138 local_metrics[SEARCH_METRIC_MAX_TRAVERSAL_STEPS] = (PetscReal)simCtx->searchMetrics.maxTraversalSteps;
3139 local_metrics[SEARCH_METRIC_MAX_PASS_DEPTH] = (PetscReal)simCtx->searchMetrics.maxParticlePassDepth;
3140
3141 ierr = MPI_Op_create(SearchMetricsReduceOp, PETSC_TRUE, &reduction_op); CHKERRMPI(ierr);
3142 ierr = MPI_Allreduce(local_metrics, global_metrics, SEARCH_METRIC_REDUCTION_LEN, MPIU_REAL, reduction_op, PETSC_COMM_WORLD); CHKERRMPI(ierr);
3143 ierr = MPI_Op_free(&reduction_op); CHKERRMPI(ierr);
3144 reduction_op = MPI_OP_NULL;
3145
3146 searchAttempts = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_SEARCH_ATTEMPTS] + 0.5);
3147 searchPopulation = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_SEARCH_POPULATION] + 0.5);
3148 searchLocatedCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_SEARCH_LOCATED] + 0.5);
3149 searchLostCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_SEARCH_LOST] + 0.5);
3150 traversalStepsSum = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_TRAVERSAL_STEPS] + 0.5);
3151 reSearchCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_RESEARCH] + 0.5);
3152 tieBreakCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_TIE_BREAKS] + 0.5);
3153 boundaryClampCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_BOUNDARY_CLAMPS] + 0.5);
3154 bboxGuessSuccessCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_BBOX_GUESS_SUCCESS] + 0.5);
3155 bboxGuessFallbackCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_BBOX_GUESS_FALLBACK] + 0.5);
3156 maxTraversalFailCount = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_SUM_MAX_TRAVERSAL_FAILS] + 0.5);
3157 maxTraversalSteps = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_MAX_TRAVERSAL_STEPS] + 0.5);
3158 maxPassDepth = (long long)PetscFloorReal(global_metrics[SEARCH_METRIC_MAX_PASS_DEPTH] + 0.5);
3159
3160 if (searchAttempts > 0) {
3161 meanTraversalSteps = (PetscReal)traversalStepsSum / (PetscReal)searchAttempts;
3162 }
3163 if (searchPopulation > 0) {
3164 searchFailureFraction = (PetscReal)searchLostCount / (PetscReal)searchPopulation;
3165 searchWorkIndex = (PetscReal)traversalStepsSum / (PetscReal)searchPopulation;
3166 reSearchFraction = (PetscReal)reSearchCount / (PetscReal)searchPopulation;
3167 }
3168
3169 if (simCtx->rank == 0) {
3170 char csv_path[PETSC_MAX_PATH_LEN + 32];
3171 FILE *f = NULL;
3172
3173 ierr = PetscSNPrintf(csv_path, sizeof(csv_path), "%s/search_metrics.csv", simCtx->log_dir); CHKERRQ(ierr);
3174 f = fopen(csv_path, "a");
3175 if (!f) {
3176 LOG_ALLOW(GLOBAL, LOG_WARNING, "LOG_SEARCH_METRICS: could not open '%s' for writing.\n", csv_path);
3177 } else {
3178 if (ftell(f) == 0) {
3179 fprintf(f,
3180 "step,time,total_particles,lost,lost_cumulative,migrated,migration_passes,search_attempts,"
3181 "mean_traversal_steps,max_traversal_steps,tie_break_count,boundary_clamp_count,"
3182 "bbox_guess_success_count,bbox_guess_fallback_count,max_particle_pass_depth,load_imbalance,"
3183 "search_population,search_located_count,search_lost_count,traversal_steps_sum,re_search_count,"
3184 "max_traversal_fail_count,search_failure_fraction,search_work_index,re_search_fraction\n");
3185 }
3186 if (simCtx->continueMode && simCtx->step == simCtx->StartStep + 1) {
3187 fprintf(f, "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep);
3188 }
3189 fprintf(f,
3190 "%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",
3191 (int)simCtx->step,
3192 (double)simCtx->ti,
3193 (int)totalParticles,
3194 (int)simCtx->particlesLostLastStep,
3195 (int)simCtx->particlesLostCumulative,
3196 (int)simCtx->particlesMigratedLastStep,
3197 (int)simCtx->migrationPassesLastStep,
3198 searchAttempts,
3199 (double)meanTraversalSteps,
3200 maxTraversalSteps,
3201 tieBreakCount,
3202 boundaryClampCount,
3203 bboxGuessSuccessCount,
3204 bboxGuessFallbackCount,
3205 maxPassDepth,
3206 (double)simCtx->particleLoadImbalance,
3207 searchPopulation,
3208 searchLocatedCount,
3209 searchLostCount,
3210 traversalStepsSum,
3211 reSearchCount,
3212 maxTraversalFailCount,
3213 (double)searchFailureFraction,
3214 (double)searchWorkIndex,
3215 (double)reSearchFraction);
3216 fclose(f);
3217 }
3218 }
3219
3221 "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",
3222 (double)searchFailureFraction,
3223 (double)searchWorkIndex,
3224 (double)reSearchFraction,
3225 (int)simCtx->particlesLostLastStep,
3226 (int)simCtx->particlesLostCumulative,
3227 (int)simCtx->particlesMigratedLastStep,
3228 (int)simCtx->migrationPassesLastStep,
3229 (double)meanTraversalSteps,
3230 maxTraversalSteps,
3231 tieBreakCount,
3232 maxPassDepth);
3233
3234 PetscFunctionReturn(0);
3235}
@ 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
static void SearchMetricsReduceOp(void *invec, void *inoutvec, int *len, MPI_Datatype *datatype)
Internal reduction callback for packed search metrics.
Definition logging.c:51
Here is the call graph for this function:
Here is the caller graph for this function:

◆ LOG_PARTICLE_METRICS()

PetscErrorCode LOG_PARTICLE_METRICS ( UserCtx user,
const char *  stageName 
)

Logs particle swarm metrics, adapting its behavior based on a boolean flag in SimCtx.

This function serves a dual purpose:

  1. If simCtx->isInitializationPhase is PETSC_TRUE, it logs settlement diagnostics to "Initialization_Metrics.log", using the provided stageName.
  2. If simCtx->isInitializationPhase is PETSC_FALSE, it logs regular timestep metrics to "Particle_Metrics.log".
Parameters
userA pointer to the UserCtx.
stageNameA descriptive label recorded in the metrics log (for example, initialization stage name or "Timestep Metrics").
Returns
PetscErrorCode 0 on success.

Logs particle swarm metrics, adapting its behavior based on a boolean flag in SimCtx.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/logging.h.

See also
LOG_PARTICLE_METRICS()

Definition at line 3297 of file logging.c.

3298{
3299 PetscErrorCode ierr;
3300 PetscMPIInt rank;
3301 SimCtx *simCtx = user->simCtx;
3302 const char *stage_label = (stageName && stageName[0] != '\0') ? stageName : "N/A";
3303
3304 PetscFunctionBeginUser;
3305 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
3306
3307 PetscInt totalParticles;
3308 ierr = DMSwarmGetSize(user->swarm, &totalParticles); CHKERRQ(ierr);
3309
3310 if (!rank) {
3311 FILE *f;
3312 char filen[PETSC_MAX_PATH_LEN + 64];
3313 ierr = PetscSNPrintf(filen, sizeof(filen), "%s/Particle_Metrics.log", simCtx->log_dir); CHKERRQ(ierr);
3314 f = fopen(filen, "a");
3315 if (!f) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open particle log file: %s", filen);
3316
3317 if (ftell(f) == 0) {
3318 PetscFPrintf(PETSC_COMM_SELF, f, "%-18s | %-10s | %-12s | %-10s | %-10s | %-10s | %-15s | %-10s | %-10s\n",
3319 "Stage", "Timestep", "Total Ptls", "Lost", "Lost Total", "Migrated", "Occupied Cells", "Imbalance", "Mig Passes");
3320 PetscFPrintf(PETSC_COMM_SELF, f, "-------------------------------------------------------------------------------------------------------------------------------------------\n");
3321 }
3322 if (simCtx->continueMode && simCtx->step == simCtx->StartStep + 1) {
3323 PetscFPrintf(PETSC_COMM_SELF, f, "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep);
3324 }
3325
3326 PetscFPrintf(PETSC_COMM_SELF, f, "%-18s | %-10d | %-12d | %-10d | %-10d | %-10d | %-15d | %-10.2f | %-10d\n",
3327 stage_label, (int)simCtx->step, (int)totalParticles, (int)simCtx->particlesLostLastStep,
3328 (int)simCtx->particlesLostCumulative, (int)simCtx->particlesMigratedLastStep, (int)simCtx->occupiedCellCount,
3329 (double)simCtx->particleLoadImbalance, (int)simCtx->migrationPassesLastStep);
3330 fclose(f);
3331 }
3332 PetscFunctionReturn(0);
3333}
PetscInt particlesLostLastStep
Definition variables.h:803
PetscInt particlesLostCumulative
Definition variables.h:804
PetscInt particlesMigratedLastStep
Definition variables.h:806
PetscInt migrationPassesLastStep
Definition variables.h:805
Here is the caller graph for this function: