PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
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 "AnalyticalSolution.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, ...)
 ----— DEBUG ---------------------------------------— #define LOG_ALLOW(scope, level, fmt, ...) \ do { \ MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \ PetscInt current_level_val = get_log_level(); \ PetscBool __allowed_func_val = is_function_allowed(__func); \ Print BEFORE the check \ if (strcmp(func, "LocateAllParticlesInGrid") == 0) { \ printf("[DEBUG LOG_ALLOW in %s] Checking: level=%d, get_log_level() returned %d, func_allowed=%d\n", \ func, (int)level, (int)__current_level_val, (int)__allowed_func_val); \ } \ if ((int)(level) <= (int)__current_level_val && allowed_func_val) { \ Print AFTER passing the check // \ if (strcmp(__func, "LocateAllParticlesInGrid") == 0) { \ printf("[DEBUG LOG_ALLOW in %s] Check PASSED. Printing log.\n", func); \ } \ PetscPrintf(comm, "[%s] " fmt, func, ##__VA_ARGS__); \ } \
 
#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 LOG_PROFILE_MSG(scope, fmt, ...)
 
#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_PROFILE , 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.
 
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 * FieldInitializationToString (PetscInt FieldInitialization)
 Helper function to convert FieldInitialization to a string representation.
 
const char * ParticleInitializationToString (PetscInt ParticleInitialization)
 Helper function to convert ParticleInitialization to a string representation.
 
const char * LESFlagToString (PetscInt LESFlag)
 Helper function to convert LES 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.
 
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)
 
PetscErrorCode ProfilingLogTimestepSummary (PetscInt step)
 Logs the performance summary for the current timestep and resets timers.
 
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)
 
void _ProfilingEnd (const char *func_name)
 
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.
 

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 57 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 46 of file logging.h.

◆ GLOBAL

#define GLOBAL   1

Scope for global logging across all processes.

Definition at line 47 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:46
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:39

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 85 of file logging.h.

86 { \
87 /* Determine the MPI communicator based on the scope */ \
88 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
89 /* Check if the log level is within the allowed range */ \
90 if ((int)(level) <= (int)get_log_level()) { \
91 /* Print the message to the specified communicator */ \
92 PetscPrintf(comm, fmt, ##__VA_ARGS__); \
93 } \
94 } 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 116 of file logging.h.

117 { \
118 /* Set the communicator to global (MPI_COMM_WORLD) by default */ \
119 MPI_Comm comm = MPI_COMM_WORLD; \
120 /* Check if the log level is within the allowed range */ \
121 if ((int)(level) <= (int)get_log_level()) { \
122 /* Print the message using PetscPrintf with the global communicator */ \
123 PetscPrintf(comm, fmt, ##__VA_ARGS__); \
124 } \
125 } 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 146 of file logging.h.

147 { \
148 /* Determine the MPI communicator based on the scope */ \
149 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
150 /* Check if the log level is within the allowed range */ \
151 if ((int)(level) <= (int)get_log_level()) { \
152 /* Synchronized print (collective) on the specified communicator */ \
153 PetscSynchronizedPrintf(comm, fmt, ##__VA_ARGS__); \
154 /* Ensure all ranks have finished printing before continuing */ \
155 PetscSynchronizedFlush(comm, PETSC_STDOUT); \
156 } \
157 } 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 179 of file logging.h.

180 { \
181 if ((int)(level) <= (int)get_log_level()) { \
182 PetscSynchronizedPrintf(MPI_COMM_WORLD, fmt, ##__VA_ARGS__); \
183 PetscSynchronizedFlush(MPI_COMM_WORLD, PETSC_STDOUT); \
184 } \
185 } 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:157

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 201 of file logging.h.

202 { \
203 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
204 if ((int)(level) <= (int)get_log_level() && is_function_allowed(__func__)) { \
205 PetscPrintf(comm, "[%s] " fmt, __func__, ##__VA_ARGS__); \
206 } \
207 } 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:47

----— DEBUG ---------------------------------------— #define LOG_ALLOW(scope, level, fmt, ...) \ do { \ MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \ PetscInt current_level_val = get_log_level(); \ PetscBool __allowed_func_val = is_function_allowed(__func); \ Print BEFORE the check \ if (strcmp(func, "LocateAllParticlesInGrid") == 0) { \ printf("[DEBUG LOG_ALLOW in %s] Checking: level=%d, get_log_level() returned %d, func_allowed=%d\n", \ func, (int)level, (int)__current_level_val, (int)__allowed_func_val); \ } \ if ((int)(level) <= (int)__current_level_val && allowed_func_val) { \ Print AFTER passing the check // \ if (strcmp(__func, "LocateAllParticlesInGrid") == 0) { \ printf("[DEBUG LOG_ALLOW in %s] Check PASSED. Printing log.\n", func); \ } \ PetscPrintf(comm, "[%s] " fmt, func, ##__VA_ARGS__); \ } \

} while (0)

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,...)
----— DEBUG ---------------------------------------— #define LOG_ALLOW(scope, level,...
Definition logging.h:268
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:32
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:33

Definition at line 268 of file logging.h.

269 { \
270 /* ------------------------------------------------------------------ */ \
271 /* Validate scope and pick communicator *before* any early exits. */ \
272 /* ------------------------------------------------------------------ */ \
273 MPI_Comm _comm; \
274 if ((scope) == LOCAL) _comm = MPI_COMM_SELF; \
275 else if ((scope) == GLOBAL) _comm = MPI_COMM_WORLD; \
276 else { \
277 fprintf(stderr, "LOG_ALLOW_SYNC ERROR: invalid scope (%d) at %s:%d\n", \
278 (scope), __FILE__, __LINE__); \
279 MPI_Abort(MPI_COMM_WORLD, 1); \
280 } \
281 \
282 /* ------------------------------------------------------------------ */ \
283 /* Decide whether *this* rank should actually print. */ \
284 /* ------------------------------------------------------------------ */ \
285 PetscBool _doPrint = \
286 is_function_allowed(__func__) && ((int)(level) <= (int)get_log_level()); \
287 \
288 if (_doPrint) { \
289 PetscSynchronizedPrintf(_comm, "[%s] " fmt, __func__, ##__VA_ARGS__); \
290 } \
291 \
292 /* ------------------------------------------------------------------ */ \
293 /* ALL ranks call the flush, even if they printed nothing. */ \
294 /* ------------------------------------------------------------------ */ \
295 PetscSynchronizedFlush(_comm, PETSC_STDOUT); \
296} 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 313 of file logging.h.

314 { \
315 if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
316 if ((iterVar) % (interval) == 0) { \
317 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
318 PetscPrintf(comm, "[%s] [%s=%d] " fmt, \
319 __func__, #iterVar, (iterVar), ##__VA_ARGS__); \
320 } \
321 } \
322 } 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.
...A printf-style format string and its corresponding arguments.

Definition at line 349 of file logging.h.

350 { \
351 /* First, perform the cheap, standard gatekeeper checks. */ \
352 if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
353 /* Only if those pass, check the user's specific condition. */ \
354 if ((var) == (val)) { \
355 MPI_Comm comm = ((scope) == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
356 /* Print the standard prefix, then the user's custom message. */ \
357 PetscPrintf(comm, "[%s] [%s=%d] " fmt, \
358 __func__, #var, (var), ##__VA_ARGS__); \
359 } \
360 } \
361 } 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 378 of file logging.h.

379 { \
380 if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
381 if ((idx) >= 0 && (idx) < (length)) { \
382 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
383 PetscPrintf(comm, "[%s] arr[%d] = " fmt "\n", \
384 __func__, (idx), (arr)[idx]); \
385 } \
386 } \
387 } 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 402 of file logging.h.

403 { \
404 if (is_function_allowed(__func__) && (int)(level) <= (int)get_log_level()) { \
405 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
406 PetscInt _start = (start) < 0 ? 0 : (start); \
407 PetscInt _end = (end) >= (length) ? (length) - 1 : (end); \
408 for (PetscInt i = _start; i <= _end; i++) { \
409 PetscPrintf(comm, "[%s] arr[%d] = " fmt "\n", __func__, i, (arr)[i]); \
410 } \
411 } \
412 } while (0)

◆ LOG_PROFILE_MSG

#define LOG_PROFILE_MSG (   scope,
  fmt,
  ... 
)
Value:
do { \
if ((int)(LOG_PROFILE) <= (int)get_log_level()) { \
MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
PetscPrintf(comm, "[PROFILE] " fmt, ##__VA_ARGS__); \
} \
} while (0)
@ LOG_PROFILE
Exclusive log level for performance timing and profiling.
Definition logging.h:31

Definition at line 414 of file logging.h.

415 { \
416 if ((int)(LOG_PROFILE) <= (int)get_log_level()) { \
417 MPI_Comm comm = (scope == LOCAL) ? MPI_COMM_SELF : MPI_COMM_WORLD; \
418 PetscPrintf(comm, "[PROFILE] " fmt, ##__VA_ARGS__); \
419 } \
420 } 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 731 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 740 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_PROFILE 

Exclusive log level for performance timing and profiling.

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 28 of file logging.h.

28 {
29 LOG_ERROR = 0, /**< Critical errors that may halt the program */
30 LOG_WARNING, /**< Non-critical issues that warrant attention */
31 LOG_PROFILE, /**< Exclusive log level for performance timing and profiling */
32 LOG_INFO, /**< Informational messages about program execution */
33 LOG_DEBUG, /**< Detailed debugging information */
34 LOG_TRACE, /**< Very fine-grained tracing information for in-depth debugging */
35 LOG_VERBOSE /**< Extremely detailed logs, typically for development use only */
36} LogLevel;
LogLevel
Enumeration of logging levels.
Definition logging.h:28
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:29
@ LOG_TRACE
Very fine-grained tracing information for in-depth debugging.
Definition logging.h:34
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
@ LOG_VERBOSE
Extremely detailed logs, typically for development use only.
Definition logging.h:35

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 "DEBUG", "INFO", "WARNING", and defaults to "ERROR" if not set or unrecognized.

Returns
LogLevel The current logging level.

The function checks the LOG_LEVEL environment variable and sets the logging level accordingly. Supported levels are "DEBUG", "INFO", "WARNING", and defaults to "ERROR" if not set or unrecognized. The log level is cached after the first call to avoid repeated environment variable checks.

Returns
LogLevel The current logging level.

Definition at line 39 of file logging.c.

39 {
40 if (current_log_level == -1) { // Log level not set yet
41 const char *env = getenv("LOG_LEVEL");
42 if (!env) {
43 current_log_level = LOG_ERROR; // Default level
44 }
45 else if (strcmp(env, "DEBUG") == 0) {
47 }
48 else if (strcmp(env, "INFO") == 0) {
50 }
51 else if (strcmp(env, "WARNING") == 0) {
53 }
54 else if (strcmp(env, "PROFILE") == 0) { // <-- New profile level
56 }
57 else if (strcmp(env, "VERBOSE") == 0) {
59 }
60 else if (strcmp(env, "TRACE") == 0) {
62 }
63 else {
64 current_log_level = LOG_ERROR; // Default if unrecognized
65 }
66 }
67 return current_log_level;
68}
static LogLevel current_log_level
Static variable to cache the current logging level.
Definition logging.c:14
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_PROFILE (0) : Logs performance profiling details.
  • LOG_ERROR (1) : Logs only critical errors.
  • LOG_WARNING (2) : Logs warnings and errors.
  • LOG_INFO (3) : Logs general information, warnings, and errors.
  • LOG_DEBUG (4) : Logs debugging information, info, warnings, and errors.
Note
The log level is determined from the LOG_LEVEL environment variable. If LOG_LEVEL is not set, it defaults to LOG_INFO.
See also
get_log_level()

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.

Note
The log level is determined from the LOG_LEVEL environment variable. If LOG_LEVEL is not set, it defaults to LOG_INFO.
See also
get_log_level()

Definition at line 82 of file logging.c.

83{
84 PetscMPIInt rank;
85 PetscErrorCode ierr;
86 int level;
87 const char *level_name;
88
89 PetscFunctionBeginUser;
90 /* get MPI rank */
91 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRMPI(ierr);
92
93 /* decide level name */
94 level = get_log_level();
95 level_name = (level == LOG_ERROR) ? "ERROR" :
96 (level == LOG_WARNING) ? "WARNING" :
97 (level == LOG_INFO) ? "INFO" :
98 (level == LOG_DEBUG) ? "DEBUG" :
99 (level == LOG_VERBOSE) ? "VERBOSE" :
100 (level == LOG_TRACE) ? "TRACE" :
101 (level == LOG_PROFILE) ? "PROFILE" : "UNKNOWN";
102
103 /* print it out */
104 ierr = PetscPrintf(PETSC_COMM_SELF,
105 "Current log level: %s (%d) | rank: %d\n",
106 level_name, level, (int)rank);
107 CHKERRMPI(ierr);
108
109 PetscFunctionReturn(PETSC_SUCCESS);
110}
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:39
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
functionListAn array of function name strings (e.g., {"foo", "bar"}).
countThe number of entries in the array.

The existing allow-list is cleared and replaced by the new one. If you pass an empty list (count = 0), then no function will be allowed unless you change it later.

Definition at line 122 of file logging.c.

123{
124 // 1. Free any existing entries
125 if (gAllowedFunctions) {
126 for (int i = 0; i < gNumAllowed; ++i) {
127 free(gAllowedFunctions[i]); // each was strdup'ed
128 }
129 free(gAllowedFunctions);
130 gAllowedFunctions = NULL;
131 gNumAllowed = 0;
132 }
133
134 // 2. Allocate new array
135 if (count > 0) {
136 gAllowedFunctions = (char**)malloc(sizeof(char*) * count);
137 }
138
139 // 3. Copy the new entries
140 for (int i = 0; i < count; ++i) {
141 // strdup is a POSIX function. If not available, implement your own string copy.
142 gAllowedFunctions[i] = strdup(functionList[i]);
143 }
144 gNumAllowed = count;
145}
static char ** gAllowedFunctions
Global/static array of function names allowed to log.
Definition logging.c:21
static int gNumAllowed
Number of entries in the gAllowedFunctions array.
Definition logging.c:26
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.

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

Parameters
functionNameThe name of the function to check.
Returns
PETSC_TRUE if functionName is allowed, otherwise PETSC_FALSE.

If no functions are in the list, nothing is allowed by default. You can reverse this logic if you prefer to allow everything unless specified otherwise.

Definition at line 157 of file logging.c.

158{
159 /* no list ⇒ allow all */
160 if (gNumAllowed == 0) {
161 return PETSC_TRUE;
162 }
163
164 /* otherwise only the listed functions are allowed */
165 for (int i = 0; i < gNumAllowed; ++i) {
166 if (strcmp(gAllowedFunctions[i], functionName) == 0) {
167 return PETSC_TRUE;
168 }
169 }
170 return PETSC_FALSE;
171}
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..

Definition at line 187 of file logging.c.

188{
189
190 // Validate input pointers
191 if (cell == NULL) {
192 LOG_ALLOW(LOCAL,LOG_ERROR, "'cell' is NULL.\n");
193 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "LOG_CELL_VERTICES - Input parameter 'cell' is NULL.");
194 }
195
196 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Rank %d, Cell Vertices:\n", rank);
197 for(int i = 0; i < 8; i++){
198 LOG_ALLOW(LOCAL,LOG_VERBOSE, " Vertex[%d]: (%.2f, %.2f, %.2f)\n",
199 i, cell->vertices[i].x, cell->vertices[i].y, cell->vertices[i].z);
200 }
201
202 return 0; // Indicate successful execution
203}
#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:201
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:161
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.

Definition at line 227 of file logging.c.

228{
229
230 // Validate input array
231 if (d == NULL) {
232 LOG_ALLOW(LOCAL,LOG_ERROR, " 'd' is NULL.\n");
233 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, " Input array 'd' is NULL.");
234 }
235
236 PetscPrintf(PETSC_COMM_SELF, " Face Distances:\n");
237 PetscPrintf(PETSC_COMM_SELF, " LEFT(%d): %.15f\n", LEFT, d[LEFT]);
238 PetscPrintf(PETSC_COMM_SELF, " RIGHT(%d): %.15f\n", RIGHT, d[RIGHT]);
239 PetscPrintf(PETSC_COMM_SELF, " BOTTOM(%d): %.15f\n", BOTTOM, d[BOTTOM]);
240 PetscPrintf(PETSC_COMM_SELF, " TOP(%d): %.15f\n", TOP, d[TOP]);
241 PetscPrintf(PETSC_COMM_SELF, " FRONT(%d): %.15f\n", FRONT, d[FRONT]);
242 PetscPrintf(PETSC_COMM_SELF, " BACK(%d): %.15f\n", BACK, d[BACK]);
243
244 return 0; // Indicate successful execution
245}
@ 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.

Definition at line 400 of file logging.c.

401{
402 DM swarm = user->swarm;
403 PetscErrorCode ierr;
404 PetscInt localNumParticles;
405 PetscReal *positions = NULL;
406 PetscInt64 *particleIDs = NULL;
407 PetscMPIInt *particleRanks = NULL;
408 PetscInt *cellIDs = NULL;
409 PetscReal *weights = NULL;
410 PetscReal *velocities = NULL;
411 PetscMPIInt rank;
412
413 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
414 LOG_ALLOW(LOCAL,LOG_INFO, "Rank %d is retrieving particle data.\n", rank);
415
416 ierr = DMSwarmGetLocalSize(swarm, &localNumParticles); CHKERRQ(ierr);
417 LOG_ALLOW(LOCAL,LOG_DEBUG,"Rank %d has %d particles.\n", rank, localNumParticles);
418
419 ierr = DMSwarmGetField(swarm, "position", NULL, NULL, (void**)&positions); CHKERRQ(ierr);
420 ierr = DMSwarmGetField(swarm, "DMSwarm_pid", NULL, NULL, (void**)&particleIDs); CHKERRQ(ierr);
421 ierr = DMSwarmGetField(swarm, "DMSwarm_rank", NULL, NULL, (void**)&particleRanks); CHKERRQ(ierr);
422 ierr = DMSwarmGetField(swarm, "DMSwarm_CellID", NULL, NULL, (void**)&cellIDs); CHKERRQ(ierr);
423 ierr = DMSwarmGetField(swarm, "weight", NULL, NULL, (void**)&weights); CHKERRQ(ierr);
424 ierr = DMSwarmGetField(swarm, "velocity", NULL, NULL, (void**)&velocities); CHKERRQ(ierr);
425
426 /* Compute maximum column widths. */
427 int wRank, wPID, wCell, wPos, wVel, wWt;
428 wRank = wPID = wCell = wPos = wVel = wWt = 0;
429 ierr = ComputeMaxColumnWidths(localNumParticles, particleRanks, particleIDs, cellIDs,
430 positions, velocities, weights,
431 &wRank, &wPID, &wCell, &wPos, &wVel, &wWt); CHKERRQ(ierr);
432
433 /* Build a header string and a row format string. */
434 char headerFmt[256];
435 char rowFmt[256];
436 BuildHeaderString(headerFmt, sizeof(headerFmt), wRank, wPID, wCell, wPos, wVel, wWt);
437 BuildRowFormatString(wRank, wPID, wCell, wPos, wVel, wWt, rowFmt, sizeof(rowFmt));
438
439 /* Print header (using synchronized printing for parallel output). */
440 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "--------------------------------------------------------------------------------------------------------------\n"); CHKERRQ(ierr);
441 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "%s", headerFmt); CHKERRQ(ierr);
442 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "--------------------------------------------------------------------------------------------------------------\n"); CHKERRQ(ierr);
443
444 /* Loop over particles and print every printInterval-th row. */
445 char rowStr[256];
446 for (PetscInt i = 0; i < localNumParticles; i++) {
447 if (i % printInterval == 0) {
448 // ------- DEBUG
449 //char cellStr[TMP_BUF_SIZE], posStr[TMP_BUF_SIZE], velStr[TMP_BUF_SIZE], wtStr[TMP_BUF_SIZE];
450 //CellToStr(&cellIDs[3*i], cellStr, TMP_BUF_SIZE);
451 //TripleRealToStr(&positions[3*i], posStr, TMP_BUF_SIZE);
452 //TripleRealToStr(&velocities[3*i], velStr, TMP_BUF_SIZE);
453 // TripleRealToStr(&weights[3*i], wtStr, TMP_BUF_SIZE);
454
455 // if (rank == 0) { // Or whatever rank is Rank 0
456 //PetscPrintf(PETSC_COMM_SELF, "[Rank 0 DEBUG LPF] Particle %lld: PID=%lld, Rank=%d\n", (long long)i, (long long)particleIDs[i], particleRanks[i]);
457 //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]);
458 //PetscPrintf(PETSC_COMM_SELF, "[Rank 0 DEBUG LPF] Str Pos: %s\n", posStr);
459 //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]);
460 // PetscPrintf(PETSC_COMM_SELF, "[Rank 0 DEBUG LPF] Str Vel: %s\n", velStr);
461 // Add similar for cell, weights
462 // PetscPrintf(PETSC_COMM_SELF, "[Rank 0 DEBUG LPF] About to build rowStr for particle %lld\n", (long long)i);
463 // fflush(stdout);
464 // }
465
466 // snprintf(rowStr, sizeof(rowStr), rowFmt,
467 // particleRanks[i],
468 // particleIDs[i],
469 // cellStr,
470 // posStr,
471 // velStr,
472 // wtStr);
473
474
475 // ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "%s", rowStr); CHKERRQ(ierr);
476
477 // ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "%s", rowStr); CHKERRQ(ierr);
478
479 // -------- DEBUG
480 /* Format the row by converting each field to a string first.
481 * We use temporary buffers and then build the row string.
482 */
483
484 char cellStr[TMP_BUF_SIZE], posStr[TMP_BUF_SIZE], velStr[TMP_BUF_SIZE], wtStr[TMP_BUF_SIZE];
485 CellToStr(&cellIDs[3*i], cellStr, TMP_BUF_SIZE);
486 TripleRealToStr(&positions[3*i], posStr, TMP_BUF_SIZE);
487 TripleRealToStr(&velocities[3*i], velStr, TMP_BUF_SIZE);
488 TripleRealToStr(&weights[3*i], wtStr, TMP_BUF_SIZE);
489
490 /* Build the row string. Note that for the integer fields we can use the row format string. */
491 snprintf(rowStr, sizeof(rowStr), rowFmt,
492 particleRanks[i],
493 particleIDs[i],
494 cellStr,
495 posStr,
496 velStr,
497 wtStr);
498 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "%s", rowStr); CHKERRQ(ierr);
499 }
500 }
501
502
503 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "--------------------------------------------------------------------------------------------------------------\n"); CHKERRQ(ierr);
504 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, "\n"); CHKERRQ(ierr);
505 ierr = PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); CHKERRQ(ierr);
506
507 LOG_ALLOW_SYNC(GLOBAL,LOG_DEBUG,"Completed printing on Rank %d.\n", rank);
508
509 /* Restore fields */
510 ierr = DMSwarmRestoreField(swarm, "position", NULL, NULL, (void**)&positions); CHKERRQ(ierr);
511 ierr = DMSwarmRestoreField(swarm, "DMSwarm_pid", NULL, NULL, (void**)&particleIDs); CHKERRQ(ierr);
512 ierr = DMSwarmRestoreField(swarm, "DMSwarm_rank", NULL, NULL, (void**)&particleRanks); CHKERRQ(ierr);
513 ierr = DMSwarmRestoreField(swarm, "DMSwarm_CellID", NULL, NULL, (void**)&cellIDs); CHKERRQ(ierr);
514 ierr = DMSwarmRestoreField(swarm, "weight", NULL, NULL, (void**)&weights); CHKERRQ(ierr);
515 ierr = DMSwarmRestoreField(swarm, "velocity", NULL, NULL, (void**)&velocities); CHKERRQ(ierr);
516
517 LOG_ALLOW(LOCAL,LOG_DEBUG, "Restored all particle fields.\n");
518 return 0;
519}
#define TMP_BUF_SIZE
Definition logging.c:5
static void BuildRowFormatString(PetscMPIInt wRank, PetscInt wPID, PetscInt wCell, PetscInt wPos, PetscInt wVel, PetscInt wWt, char *fmtStr, size_t bufSize)
Definition logging.c:363
static void BuildHeaderString(char *headerStr, size_t bufSize, PetscMPIInt wRank, PetscInt wPID, PetscInt wCell, PetscInt wPos, PetscInt wVel, PetscInt wWt)
Definition logging.c:376
static void CellToStr(const PetscInt *cell, char *buf, size_t bufsize)
Definition logging.c:266
static void TripleRealToStr(const PetscReal *arr, char *buf, size_t bufsize)
Definition logging.c:274
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:299
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.

Definition at line 625 of file logging.c.

626{
627 PetscErrorCode ierr;
628 PetscFunctionBegin;
629 if (funcs) {
630 for (PetscInt i = 0; i < n; ++i) {
631 ierr = PetscFree(funcs[i]); CHKERRQ(ierr);
632 }
633 ierr = PetscFree(funcs); CHKERRQ(ierr);
634 }
635 PetscFunctionReturn(0);
636}

◆ 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)
Definition picsolver.c:24

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).

Definition at line 566 of file logging.c.

569{
570 FILE *fp = NULL;
571 char **funcs = NULL;
572 size_t cap = 16; /* initial capacity */
573 size_t n = 0; /* number of names */
574 char line[PETSC_MAX_PATH_LEN];
575 PetscErrorCode ierr;
576
577 PetscFunctionBegin;
578
579 /* ---------------------------------------------------------------------- */
580 /* 1. Open file */
581 fp = fopen(filename, "r");
582 if (!fp) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
583 "Cannot open %s", filename);
584
585 /* 2. Allocate initial pointer array */
586 ierr = PetscMalloc1(cap, &funcs); CHKERRQ(ierr);
587
588 /* 3. Read file line by line */
589 while (fgets(line, sizeof line, fp)) {
590 /* Strip everything after a comment character '#'. */
591 char *hash = strchr(line, '#');
592 if (hash) *hash = '\0';
593
594 trim(line); /* remove leading/trailing blanks */
595 if (!*line) continue; /* skip if empty */
596
597 /* Grow the array if necessary */
598 if (n == cap) {
599 cap *= 2;
600 ierr = PetscRealloc(cap * sizeof(*funcs), (void **)&funcs); CHKERRQ(ierr);
601 }
602
603 /* Deep‑copy the cleaned identifier */
604 ierr = PetscStrallocpy(line, &funcs[n++]); CHKERRQ(ierr);
605 }
606 fclose(fp);
607
608 /* 4. Return results to caller */
609 *funcsOut = funcs;
610 *nOut = (PetscInt)n;
611
612 PetscFunctionReturn(0);
613}
static void trim(char *s)
Definition logging.c:522
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.

Definition at line 643 of file logging.c.

643 {
644 switch (face) {
645 case BC_FACE_NEG_X: return "-Xi (I-Min)";
646 case BC_FACE_POS_X: return "+Xi (I-Max)";
647 case BC_FACE_NEG_Y: return "-Eta (J-Min)";
648 case BC_FACE_POS_Y: return "+Eta (J-Max)";
649 case BC_FACE_NEG_Z: return "-Zeta (K-Min)";
650 case BC_FACE_POS_Z: return "+Zeta (K-Max)";
651 default: return "Unknown Face";
652 }
653}
@ BC_FACE_NEG_X
Definition variables.h:201
@ BC_FACE_POS_Z
Definition variables.h:203
@ BC_FACE_POS_Y
Definition variables.h:202
@ BC_FACE_NEG_Z
Definition variables.h:203
@ BC_FACE_POS_X
Definition variables.h:201
@ BC_FACE_NEG_Y
Definition variables.h:202
Here is the caller graph for this function:

◆ FieldInitializationToString()

const char * FieldInitializationToString ( PetscInt  FieldInitialization)

Helper function to convert FieldInitialization to a string representation.

Parameters
[in]PetscIntThe FieldInitialization value.
Returns
Pointer to a constant string representing the FieldInitialization.

Definition at line 660 of file logging.c.

661{
662 switch(FieldInitialization){
663 case 0: return "Zero";
664 case 1: return "Constant Normal velocity";
665 case 2: return "Poiseuille Normal velocity";
666 default: return "Unknown Field Initialization";
667 }
668}
Here is the caller graph for this function:

◆ ParticleInitializationToString()

const char * ParticleInitializationToString ( PetscInt  ParticleInitialization)

Helper function to convert ParticleInitialization to a string representation.

Parameters
[in]PetscIntThe ParticleInitialization value.
Returns
Pointer to a constant string representing the FieldInitialization.

Definition at line 675 of file logging.c.

676{
677 switch(ParticleInitialization){
678 case 0: return "Surface: Random";
679 case 1: return "Volume";
680 case 3: return "Surface: At edges";
681 default: return "Unknown Particle Initialization";
682 }
683}
Here is the caller graph for this function:

◆ LESFlagToString()

const char * LESFlagToString ( PetscInt  LESFlag)

Helper function to convert LES Flag to a string representation.

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

Definition at line 690 of file logging.c.

691{
692 switch(LESFlag){
693 case 0: return "No LES";
694 case 1: return "Constant Smagorinsky";
695 case 2: return "Dynamic Smagorinsky";
696 default: return "Unknown LES Flag";
697 }
698}
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.

Definition at line 705 of file logging.c.

705 {
706 switch (type) {
707 // case DIRICHLET: return "DIRICHLET";
708 // case NEUMANN: return "NEUMANN";
709 case WALL: return "WALL";
710 case INLET: return "INLET";
711 case OUTLET: return "OUTLET";
712 case FARFIELD: return "FARFIELD";
713 case PERIODIC: return "PERIODIC";
714 case INTERFACE: return "INTERFACE";
715 case NOGRAD: return "NOGRAD";
716
717 // case CUSTOM: return "CUSTOM";
718 default: return "Unknown BC Type";
719 }
720}
@ INLET
Definition variables.h:214
@ NOGRAD
Definition variables.h:224
@ INTERFACE
Definition variables.h:209
@ FARFIELD
Definition variables.h:215
@ OUTLET
Definition variables.h:213
@ PERIODIC
Definition variables.h:216
@ WALL
Definition variables.h:210
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.

Definition at line 732 of file logging.c.

732 {
733 switch (handler_type) {
734 // Wall & Symmetry Handlers
735 case BC_HANDLER_WALL_NOSLIP: return "noslip";
736 case BC_HANDLER_WALL_MOVING: return "moving";
737 case BC_HANDLER_SYMMETRY_PLANE: return "symmetry_plane";
738
739 // Inlet Handlers
740 case BC_HANDLER_INLET_CONSTANT_VELOCITY: return "constant_velocity";
741 case BC_HANDLER_INLET_PULSATILE_FLUX: return "pulsatile_flux";
742 case BC_HANDLER_INLET_PARABOLIC: return "parabolic";
743
744 // Outlet Handlers
745 case BC_HANDLER_OUTLET_CONSERVATION: return "conservation";
746 case BC_HANDLER_OUTLET_PRESSURE: return "pressure";
747
748 // Other Physical Handlers
749 case BC_HANDLER_FARFIELD_NONREFLECTING: return "nonreflecting";
750 case BC_HANDLER_NOGRAD_COPY_GHOST: return "no_gradient";
751
752 // Multi-Block / Interface Handlers
753 case BC_HANDLER_PERIODIC: return "periodic";
754 case BC_HANDLER_INTERFACE_OVERSET: return "overset";
755
756 // Default case
758 default: return "UNKNOWN_HANDLER";
759 }
760}
@ BC_HANDLER_INLET_PULSATILE_FLUX
Definition variables.h:232
@ BC_HANDLER_INLET_PARABOLIC
Definition variables.h:231
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
Definition variables.h:232
@ BC_HANDLER_INTERFACE_OVERSET
Definition variables.h:236
@ BC_HANDLER_WALL_MOVING
Definition variables.h:230
@ BC_HANDLER_NOGRAD_COPY_GHOST
Definition variables.h:229
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:230
@ BC_HANDLER_OUTLET_CONSERVATION
Definition variables.h:234
@ BC_HANDLER_PERIODIC
Definition variables.h:236
@ BC_HANDLER_FARFIELD_NONREFLECTING
Definition variables.h:235
@ BC_HANDLER_OUTLET_PRESSURE
Definition variables.h:234
@ BC_HANDLER_SYMMETRY_PLANE
Definition variables.h:231
@ BC_HANDLER_UNDEFINED
Definition variables.h:229
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.

Definition at line 802 of file logging.c.

803{
804 DualMonitorCtx *monctx = (DualMonitorCtx*)ctx;
805 PetscErrorCode ierr;
806 PetscReal trnorm, relnorm;
807 Vec r;
808 char norm_buf[256];
809 PetscMPIInt rank;
810
811 PetscFunctionBeginUser;
812 ierr = MPI_Comm_rank(PETSC_COMM_WORLD,&rank); CHKERRQ(ierr);
813
814 // 1. Calculate the true residual norm.
815 ierr = KSPBuildResidual(ksp, NULL, NULL, &r); CHKERRQ(ierr);
816 ierr = VecNorm(r, NORM_2, &trnorm); CHKERRQ(ierr);
817
818 // 2. On the first iteration, compute and store the norm of the RHS vector `b`.
819 if (it == 0) {
820 Vec b;
821 ierr = KSPGetRhs(ksp, &b); CHKERRQ(ierr);
822 ierr = VecNorm(b, NORM_2, &monctx->bnorm); CHKERRQ(ierr);
823 }
824
825 if(!rank){
826 // 3. Compute the relative norm and format the output string.
827 if (monctx->bnorm > 1.e-15) {
828 relnorm = trnorm / monctx->bnorm;
829 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);
830 } else {
831 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);
832 }
833
834 // 4. Log to the file viewer (unconditionally).
835 if(monctx->file_handle){
836 ierr = PetscFPrintf(PETSC_COMM_SELF,monctx->file_handle,"%s\n", norm_buf); CHKERRQ(ierr);
837 }
838 // 5. Log to the console (conditionally).
839 if (monctx->log_to_console) {
840 PetscFPrintf(PETSC_COMM_SELF,stdout, "%s\n", norm_buf); CHKERRQ(ierr);
841 }
842
843 } //rank
844
845 PetscFunctionReturn(0);
846}
PetscBool log_to_console
Definition logging.h:59
PetscReal bnorm
Definition logging.h:60
PetscInt step
Definition logging.h:61
FILE * file_handle
Definition logging.h:58
PetscInt block_id
Definition logging.h:62
Context for a dual-purpose KSP monitor.
Definition logging.h:57
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

Definition at line 770 of file logging.c.

771{
772 DualMonitorCtx *monctx = (DualMonitorCtx*)*ctx;
773 PetscErrorCode ierr;
774 PetscMPIInt rank;
775
776 PetscFunctionBeginUser;
777 ierr = MPI_Comm_rank(PETSC_COMM_WORLD,&rank); CHKERRQ(ierr);
778 if(!rank && monctx->file_handle){
779 fclose(monctx->file_handle);
780 }
781
782 ierr = PetscFree(monctx); CHKERRQ(ierr);
783 *ctx = NULL;
784 PetscFunctionReturn(0);
785}
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.

Definition at line 862 of file logging.c.

863{
864 PetscErrorCode ierr;
865 PetscMPIInt rank;
866 SimCtx *simCtx = user->simCtx; // Get the shared SimCtx
867 const PetscInt bi = user->_this; // Get this block's specific ID
868 const PetscInt ti = simCtx->step; // Get the current timestep
869
870 PetscFunctionBeginUser;
871 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
872
873 // Only rank 0 performs file I/O.
874 if (!rank) {
875 FILE *f;
876 char filen[128];
877 sprintf(filen, "%s/Continuity_Metrics.log",simCtx->log_dir);
878
879 // Open the log file in append mode.
880 f = fopen(filen, "a");
881 if (!f) {
882 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open log file: %s", filen);
883 }
884
885 // Write a header only for the very first block (bi=0) on the very
886 // first timestep (ti=StartStep + 1). This ensures it's written only once.
887 if (ti == simCtx->StartStep + 1 && bi == 0) {
888 PetscFPrintf(PETSC_COMM_SELF, f, "%-10s | %-6s | %-18s | %-30s | %-18s | %-18s | %-18s | %-18s\n",
889 "Timestep", "Block", "Max Divergence", "Max Divergence Location ([k][j][i]=idx)", "Sum(RHS)","Total Flux In", "Total Flux Out", "Net Flux");
890 PetscFPrintf(PETSC_COMM_SELF, f, "------------------------------------------------------------------------------------------------------------------------------------------\n");
891 }
892
893 // Prepare the data strings and values for the current block.
894 PetscReal net_flux = simCtx->FluxInSum - simCtx->FluxOutSum;
895 char location_str[64];
896 sprintf(location_str, "([%d][%d][%d] = %d)", (int)simCtx->MaxDivz, (int)simCtx->MaxDivy, (int)simCtx->MaxDivx, (int)simCtx->MaxDivFlatArg);
897
898 // Write the formatted line for the current block.
899 PetscFPrintf(PETSC_COMM_SELF, f, "%-10d | %-6d | %-18.10e | %-39s | %-18.10e | %-18.10e | %-18.10e | %-18.10e\n",
900 (int)ti,
901 (int)bi,
902 (double)simCtx->MaxDiv,
903 location_str,
904 (double)simCtx->summationRHS,
905 (double)simCtx->FluxInSum,
906 (double)simCtx->FluxOutSum,
907 (double)net_flux);
908
909 fclose(f);
910 }
911
912 PetscFunctionReturn(0);
913}
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:664
PetscReal FluxOutSum
Definition variables.h:602
PetscInt _this
Definition variables.h:674
PetscInt StartStep
Definition variables.h:548
PetscReal MaxDiv
Definition variables.h:638
PetscInt MaxDivx
Definition variables.h:639
PetscInt MaxDivy
Definition variables.h:639
PetscInt MaxDivz
Definition variables.h:639
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:562
PetscInt MaxDivFlatArg
Definition variables.h:639
PetscReal FluxInSum
Definition variables.h:602
PetscInt step
Definition variables.h:546
PetscReal summationRHS
Definition variables.h:637
The master context for the entire simulation.
Definition variables.h:538
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.

Definition at line 921 of file logging.c.

922{
923 switch (level) {
924 case NEEDS_LOCATION: return "NEEDS_LOCATION";
925 case ACTIVE_AND_LOCATED: return "ACTIVE_AND_LOCATED";
926 case MIGRATING_OUT: return "MIGRATING_OUT";
927 case LOST: return "LOST";
928 case UNINITIALIZED: return "UNINITIALIZED";
929 default: return "UNKNOWN_LEVEL";
930 }
931}
@ 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.

Definition at line 1207 of file logging.c.

1208{
1209 if (totalSteps <= 0) return;
1210
1211 // --- Configuration ---
1212 const int barWidth = 50;
1213
1214 // --- Calculation ---
1215 // Calculate progress as a fraction from 0.0 to 1.0
1216 PetscReal progress = (PetscReal)(step - startStep + 1) / totalSteps;
1217 // Ensure progress doesn't exceed 1.0 due to floating point inaccuracies
1218 if (progress > 1.0) progress = 1.0;
1219
1220 int pos = (int)(barWidth * progress);
1221
1222 // --- Printing ---
1223 // Carriage return moves cursor to the beginning of the line
1224 PetscPrintf(PETSC_COMM_SELF, "\rProgress: [");
1225
1226 for (int i = 0; i < barWidth; ++i) {
1227 if (i < pos) {
1228 PetscPrintf(PETSC_COMM_SELF, "=");
1229 } else if (i == pos) {
1230 PetscPrintf(PETSC_COMM_SELF, ">");
1231 } else {
1232 PetscPrintf(PETSC_COMM_SELF, " ");
1233 }
1234 }
1235
1236 // Print percentage, step count, and current time
1237 PetscPrintf(PETSC_COMM_SELF, "] %3d%% (Step %ld/%ld, t=%.4f)",
1238 (int)(progress * 100.0),
1239 step + 1,
1240 startStep + totalSteps,
1241 currentTime);
1242
1243 // Flush the output buffer to ensure the bar is displayed immediately
1244 fflush(stdout);
1245}
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

Definition at line 998 of file logging.c.

999{
1000 PetscFunctionBeginUser;
1001 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx cannot be null for ProfilingInitialize");
1002
1003 // Iterate through the list of critical functions provided in SimCtx
1004 for (PetscInt i = 0; i < simCtx->nCriticalFuncs; ++i) {
1005 PetscInt idx;
1006 const char *func_name = simCtx->criticalFuncs[i];
1007 PetscErrorCode ierr = _FindOrCreateEntry(func_name, &idx); CHKERRQ(ierr);
1008 g_profiler_registry[idx].always_log = PETSC_TRUE;
1009
1010 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Marked '%s' as a critical function for profiling.\n", func_name);
1011 }
1012 PetscFunctionReturn(0);
1013}
PetscBool always_log
Definition logging.c:943
static PetscErrorCode _FindOrCreateEntry(const char *func_name, PetscInt *idx)
Definition logging.c:952
static ProfiledFunction * g_profiler_registry
Definition logging.c:947
char ** criticalFuncs
Definition variables.h:643
PetscInt nCriticalFuncs
Definition variables.h:644
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ProfilingResetTimestepCounters()

PetscErrorCode ProfilingResetTimestepCounters ( void  )

Definition at line 1037 of file logging.c.

1038{
1039 PetscFunctionBeginUser;
1040 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1043 }
1044 PetscFunctionReturn(0);
1045}
static PetscInt g_profiler_count
Definition logging.c:948
double current_step_time
Definition logging.c:939
long long current_step_call_count
Definition logging.c:941
Here is the caller graph for this function:

◆ ProfilingLogTimestepSummary()

PetscErrorCode ProfilingLogTimestepSummary ( PetscInt  step)

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

Depending on the current log level, this function will print:

  • LOG_PROFILE: Timings for ALL functions called during the step.
  • LOG_INFO/LOG_DEBUG: Timings for only the "always log" functions.

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
stepThe current simulation step number, for logging context.
Returns
PetscErrorCode

Definition at line 1047 of file logging.c.

1048{
1049 LogLevel log_level = get_log_level();
1050 PetscBool should_print = PETSC_FALSE;
1051
1052 PetscFunctionBeginUser;
1053
1054 // Decide if we should print anything at all
1055 if (log_level >= LOG_PROFILE) {
1056 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1057 if (g_profiler_registry[i].current_step_call_count > 0) {
1058 if (log_level == LOG_PROFILE || g_profiler_registry[i].always_log) {
1059 should_print = PETSC_TRUE;
1060 break;
1061 }
1062 }
1063 }
1064 }
1065
1066 if (should_print) {
1067 PetscPrintf(PETSC_COMM_SELF, "[PROFILE] ----- Timestep %d Summary -----\n", step);
1068 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1069 if (g_profiler_registry[i].current_step_call_count > 0) {
1070 if (log_level == LOG_PROFILE || g_profiler_registry[i].always_log) {
1071 PetscPrintf(PETSC_COMM_SELF, "[PROFILE] %-25s: %.6f s (%lld calls)\n",
1072 g_profiler_registry[i].name,
1073 g_profiler_registry[i].current_step_time,
1074 g_profiler_registry[i].current_step_call_count);
1075 }
1076 }
1077 }
1078 }
1079
1080 // Reset per-step counters for the next iteration
1081 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1084 }
1085 PetscFunctionReturn(0);
1086}
Here is the call graph for this function:
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.

Definition at line 1106 of file logging.c.

1107{
1108 PetscInt rank = simCtx->rank;
1109 PetscFunctionBeginUser;
1110 if (!rank) {
1111
1112 //--- Step 0: Create a file viewer for log file
1113 FILE *f;
1114 char filen[128];
1115 sprintf(filen, "%s/ProfilingSummary.log",simCtx->log_dir);
1116
1117 // Open the log file in write mode.
1118 f = fopen(filen,"w");
1119 if(!f){
1120 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Cannot Open log file: %s",filen);
1121 }
1122
1123 // --- Step 1: Sort the data for readability ---
1125
1126 // --- Step 2: Dynamically determine the width for the function name column ---
1127 PetscInt max_name_len = strlen("Function"); // Start with the header's length
1128 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1129 if (g_profiler_registry[i].total_call_count > 0) {
1130 PetscInt len = strlen(g_profiler_registry[i].name);
1131 if (len > max_name_len) {
1132 max_name_len = len;
1133 }
1134 }
1135 }
1136 // Add a little padding
1137 max_name_len += 2;
1138
1139 // --- Step 3: Define fixed widths for numeric columns for consistent alignment ---
1140 const int time_width = 18;
1141 const int count_width = 15;
1142 const int avg_width = 22;
1143
1144 // --- Step 4: Print the formatted table ---
1145 PetscFPrintf(PETSC_COMM_SELF, f, "=================================================================================================================\n");
1146 PetscFPrintf(PETSC_COMM_SELF, f, " FINAL PROFILING SUMMARY (Sorted by Total Time)\n");
1147 PetscFPrintf(PETSC_COMM_SELF, f, "=================================================================================================================\n");
1148
1149 // Header Row
1150 PetscFPrintf(PETSC_COMM_SELF, f, "%-*s | %-*s | %-*s | %-*s\n",
1151 max_name_len, "Function",
1152 time_width, "Total Time (s)",
1153 count_width, "Call Count",
1154 avg_width, "Avg. Time/Call (ms)");
1155
1156 // Separator Line (dynamically sized)
1157 for (int i = 0; i < max_name_len; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
1158 PetscFPrintf(PETSC_COMM_SELF, f, "-|-");
1159 for (int i = 0; i < time_width; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
1160 PetscFPrintf(PETSC_COMM_SELF, f, "-|-");
1161 for (int i = 0; i < count_width; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
1162 PetscFPrintf(PETSC_COMM_SELF, f, "-|-");
1163 for (int i = 0; i < avg_width; i++) PetscFPrintf(PETSC_COMM_SELF, f, "-");
1164 PetscFPrintf(PETSC_COMM_SELF, f, "\n");
1165
1166 // Data Rows
1167 for (PetscInt i = 0; i < g_profiler_count; ++i) {
1168 if (g_profiler_registry[i].total_call_count > 0) {
1169 double avg_time_ms = (g_profiler_registry[i].total_time / g_profiler_registry[i].total_call_count) * 1000.0;
1170 PetscFPrintf(PETSC_COMM_SELF, f, "%-*s | %*.*f | %*lld | %*.*f\n",
1171 max_name_len, g_profiler_registry[i].name,
1172 time_width, 6, g_profiler_registry[i].total_time,
1173 count_width, g_profiler_registry[i].total_call_count,
1174 avg_width, 6, avg_time_ms);
1175 PetscFPrintf(PETSC_COMM_SELF, f, "------------------------------------------------------------------------------------------------------------------\n");
1176 }
1177 }
1178 PetscFPrintf(PETSC_COMM_SELF, f, "==================================================================================================================\n");
1179
1180 fclose(f);
1181 }
1182
1183 // --- Final Cleanup ---
1184 PetscFree(g_profiler_registry);
1185 g_profiler_registry = NULL;
1186 g_profiler_count = 0;
1188 PetscFunctionReturn(0);
1189}
long long total_call_count
Definition logging.c:940
static PetscInt g_profiler_capacity
Definition logging.c:949
double total_time
Definition logging.c:938
static int _CompareProfiledFunctions(const void *a, const void *b)
Definition logging.c:1089
PetscMPIInt rank
Definition variables.h:541
Here is the call graph for this function:
Here is the caller graph for this function:

◆ _ProfilingStart()

void _ProfilingStart ( const char *  func_name)

Definition at line 1015 of file logging.c.

1016{
1017 PetscInt idx;
1018 if (_FindOrCreateEntry(func_name, &idx) != 0) return; // Fail silently
1019 PetscTime(&g_profiler_registry[idx].start_time);
1020}
Here is the call graph for this function:

◆ _ProfilingEnd()

void _ProfilingEnd ( const char *  func_name)

Definition at line 1022 of file logging.c.

1023{
1024 double end_time;
1025 PetscTime(&end_time);
1026
1027 PetscInt idx;
1028 if (_FindOrCreateEntry(func_name, &idx) != 0) return; // Fail silently
1029
1030 double elapsed = end_time - g_profiler_registry[idx].start_time;
1031 g_profiler_registry[idx].total_time += elapsed;
1032 g_profiler_registry[idx].current_step_time += elapsed;
1035}
double start_time
Definition logging.c:942
Here is the call 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.

This utility function inspects a PETSc Vec and calculates the minimum and maximum values for its components, both locally and globally.

It is "architecture-aware" and adjusts its iteration range to only include physically meaningful data points:

  • Cell-Centered Fields ("Ucat", "P"): It uses the "Shifted Index Architecture," iterating from index 1 to N-1 to exclude the ghost/tool values at indices 0 and N.
  • Node-Centered Fields ("Coordinates"): It iterates from index 0 to N-1, covering all physical nodes.
  • Face-Centered Fields ("Ucont"): It iterates from index 0 to N-1, covering all physical faces.

The results are printed to the standard output in a formatted, easy-to-read table.

Parameters
[in]userPointer to the user-defined context.
[in]fieldNameA string descriptor for the field being analyzed.
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Definition at line 1273 of file logging.c.

1274{
1275 PetscErrorCode ierr;
1276 PetscInt i, j, k;
1277 DMDALocalInfo info;
1278
1279 Vec fieldVec = NULL;
1280 DM dm = NULL;
1281 PetscInt dof;
1282 char data_layout[20];
1283
1284 PetscFunctionBeginUser;
1285
1286 // --- 1. Map string name to PETSc objects and determine data layout ---
1287 if (strcasecmp(fieldName, "Ucat") == 0) {
1288 fieldVec = user->Ucat; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
1289 } else if (strcasecmp(fieldName, "P") == 0) {
1290 fieldVec = user->P; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
1291 } else if (strcasecmp(fieldName, "Ucont") == 0) {
1292 fieldVec = user->lUcont; dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered");
1293 } else if (strcasecmp(fieldName, "Coordinates") == 0) {
1294 ierr = DMGetCoordinates(user->da, &fieldVec); CHKERRQ(ierr);
1295 dm = user->fda; dof = 3; strcpy(data_layout, "Node-Centered");
1296 } else if (strcasecmp(fieldName,"Psi") == 0) {
1297 fieldVec = user->Psi; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered"); // Assuming Psi is cell-centered
1298 } else {
1299 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Field %s not recognized.", fieldName);
1300 }
1301
1302 if (!fieldVec) {
1303 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Vector for field '%s' is NULL.", fieldName);
1304 }
1305 if (!dm) {
1306 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM for field '%s' is NULL.", fieldName);
1307 }
1308
1309 ierr = DMDAGetLocalInfo(dm, &info); CHKERRQ(ierr);
1310
1311 // --- 2. Define Architecture-Aware Loop Bounds ---
1312 PetscInt i_start, i_end, j_start, j_end, k_start, k_end;
1313
1314 if (strcmp(data_layout, "Cell-Centered") == 0) {
1315 // For cell-centered data, the physical values are stored from index 1 to N-1.
1316 // We find the intersection of the rank's owned range [xs, xe) with the
1317 // physical data range [1, IM-1).
1318 i_start = PetscMax(info.xs, 1); i_end = PetscMin(info.xs + info.xm, user->IM);
1319 j_start = PetscMax(info.ys, 1); j_end = PetscMin(info.ys + info.ym, user->JM);
1320 k_start = PetscMax(info.zs, 1); k_end = PetscMin(info.zs + info.zm, user->KM);
1321 } else { // For Node- or Face-Centered data
1322 // The physical values are stored from index 0 to N-1.
1323 // We find the intersection of the rank's owned range [xs, xe) with the
1324 // physical data range [0, IM-1].
1325 i_start = PetscMax(info.xs, 0); i_end = PetscMin(info.xs + info.xm, user->IM);
1326 j_start = PetscMax(info.ys, 0); j_end = PetscMin(info.ys + info.ym, user->JM);
1327 k_start = PetscMax(info.zs, 0); k_end = PetscMin(info.zs + info.zm, user->KM);
1328 }
1329
1330 // --- 3. Barrier for clean, grouped output ---
1331 ierr = MPI_Barrier(PETSC_COMM_WORLD); CHKERRQ(ierr);
1332 if (user->simCtx->rank == 0) {
1333 PetscPrintf(PETSC_COMM_SELF, "\n--- Field Ranges: [%s] (Layout: %s) ---\n", fieldName, data_layout);
1334 }
1335
1336 // --- 4. Branch on DoF and perform calculation with correct bounds ---
1337 if (dof == 1) {
1338 PetscReal localMin = PETSC_MAX_REAL, localMax = PETSC_MIN_REAL;
1339 PetscReal globalMin, globalMax;
1340 const PetscScalar ***array;
1341
1342 ierr = DMDAVecGetArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
1343 for (k = k_start; k < k_end; k++) {
1344 for (j = j_start; j < j_end; j++) {
1345 for (i = i_start; i < i_end; i++) {
1346 localMin = PetscMin(localMin, array[k][j][i]);
1347 localMax = PetscMax(localMax, array[k][j][i]);
1348 }
1349 }
1350 }
1351 ierr = DMDAVecRestoreArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
1352
1353 ierr = MPI_Allreduce(&localMin, &globalMin, 1, MPIU_REAL, MPI_MIN, PETSC_COMM_WORLD); CHKERRQ(ierr);
1354 ierr = MPI_Allreduce(&localMax, &globalMax, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRQ(ierr);
1355
1356 PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin, localMax);
1357 ierr = PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); CHKERRQ(ierr);
1358 if (user->simCtx->rank == 0) {
1359 PetscPrintf(PETSC_COMM_SELF, " Global Range: [ %11.4e , %11.4e ]\n", globalMin, globalMax);
1360 }
1361
1362 } else if (dof == 3) {
1363 Cmpnts localMin = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MAX_REAL};
1364 Cmpnts localMax = {PETSC_MIN_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1365 Cmpnts globalMin, globalMax;
1366 const Cmpnts ***array;
1367
1368 ierr = DMDAVecGetArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
1369 for (k = k_start; k < k_end; k++) {
1370 for (j = j_start; j < j_end; j++) {
1371 for (i = i_start; i < i_end; i++) {
1372 localMin.x = PetscMin(localMin.x, array[k][j][i].x);
1373 localMin.y = PetscMin(localMin.y, array[k][j][i].y);
1374 localMin.z = PetscMin(localMin.z, array[k][j][i].z);
1375 localMax.x = PetscMax(localMax.x, array[k][j][i].x);
1376 localMax.y = PetscMax(localMax.y, array[k][j][i].y);
1377 localMax.z = PetscMax(localMax.z, array[k][j][i].z);
1378 }
1379 }
1380 }
1381 ierr = DMDAVecRestoreArrayRead(dm, fieldVec, &array); CHKERRQ(ierr);
1382
1383 ierr = MPI_Allreduce(&localMin, &globalMin, 3, MPIU_REAL, MPI_MIN, PETSC_COMM_WORLD); CHKERRQ(ierr);
1384 ierr = MPI_Allreduce(&localMax, &globalMax, 3, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRQ(ierr);
1385
1386 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local X-Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin.x, localMax.x);
1387 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local Y-Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin.y, localMax.y);
1388 ierr = PetscSynchronizedPrintf(PETSC_COMM_WORLD, " [Rank %d] Local Z-Range: [ %11.4e , %11.4e ]\n", user->simCtx->rank, localMin.z, localMax.z);
1389 ierr = PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); CHKERRQ(ierr);
1390
1391 if (user->simCtx->rank == 0) {
1392 PetscPrintf(PETSC_COMM_SELF, " [Global] X-Range: [ %11.4e , %11.4e ]\n", globalMin.x, globalMax.x);
1393 PetscPrintf(PETSC_COMM_SELF, " [Global] Y-Range: [ %11.4e , %11.4e ]\n", globalMin.y, globalMax.y);
1394 PetscPrintf(PETSC_COMM_SELF, " [Global] Z-Range: [ %11.4e , %11.4e ]\n", globalMin.z, globalMax.z);
1395 }
1396
1397 } else {
1398 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "LogFieldStatistics only supports fields with 1 or 3 components, but field '%s' has %D.", fieldName, dof);
1399 }
1400
1401 // --- 5. Final barrier for clean output ordering ---
1402 ierr = MPI_Barrier(PETSC_COMM_WORLD); CHKERRQ(ierr);
1403 if (user->simCtx->rank == 0) {
1404 PetscPrintf(PETSC_COMM_SELF, "--------------------------------------------\n\n");
1405 }
1406
1407 PetscFunctionReturn(0);
1408}
PetscInt KM
Definition variables.h:670
Vec Ucat
Definition variables.h:688
PetscInt JM
Definition variables.h:670
Vec lUcont
Definition variables.h:688
PetscInt IM
Definition variables.h:670
Vec Psi
Definition variables.h:730
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.
  • Face-Centered Fields ("Ucont"): It uses a direct index mapping, where the value for the face at Node i is stored at index i.
  • 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.

Definition at line 1435 of file logging.c.

1436{
1437 PetscErrorCode ierr;
1438 DMDALocalInfo info;
1439 PetscMPIInt rank;
1440
1441 Vec vec_local = NULL;
1442 DM dm = NULL;
1443 PetscInt dof = 0;
1444 char data_layout[20]; // To store "Cell-Centered", "Face-Centered", etc.
1445
1446 PetscFunctionBeginUser;
1447 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
1448
1449 // --- 1. Map string name to PETSc objects and determine data layout ---
1450 if (strcasecmp(field_name, "Ucat") == 0) {
1451 vec_local = user->lUcat; dm = user->fda; dof = 3; strcpy(data_layout, "Cell-Centered");
1452 } else if (strcasecmp(field_name, "P") == 0) {
1453 vec_local = user->lP; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
1454 } else if (strcasecmp(field_name, "Psi") == 0) {
1455 vec_local = user->lPsi; dm = user->da; dof = 1; strcpy(data_layout, "Cell-Centered");
1456 } else if (strcasecmp(field_name, "Ucont") == 0) {
1457 vec_local = user->lUcont; dm = user->fda; dof = 3; strcpy(data_layout, "Face-Centered");
1458 } else if (strcasecmp(field_name, "Coordinates") == 0) {
1459 ierr = DMGetCoordinatesLocal(user->da, &vec_local); CHKERRQ(ierr);
1460 dm = user->fda; dof = 3; strcpy(data_layout, "Node-Centered");
1461 } else if (strcasecmp(field_name, "CornerField")== 0){
1462 vec_local = user->lCellFieldAtCorner; dm = user->fda; dof = 3; strcpy(data_layout, "Node-Centered");
1463 } else {
1464 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Unknown field name for LOG_FIELD_ANATOMY: %s", field_name);
1465 }
1466
1467 // --- 2. Get Grid Info and Array Pointers ---
1468 ierr = DMDAGetLocalInfo(dm, &info); CHKERRQ(ierr);
1469
1470 // Synchronize for clean output
1471 ierr = PetscBarrier(NULL);
1472 PetscPrintf(PETSC_COMM_WORLD, "\n--- Field Anatomy Log: [%s] | Stage: [%s] | Layout: [%s] ---\n", field_name, stage_name, data_layout);
1473
1474 // We will check a slice at the center of the other two dimensions (in the rank's local domain)
1475
1476 PetscInt im_phys = user->IM; // Physical size in I-direction
1477 PetscInt jm_phys = user->JM; // Physical size in J-direction
1478 PetscInt km_phys = user->KM; // Physical size in K-direction
1479
1480 PetscInt i_mid = (PetscInt)(info.xs + 0.5*info.xm);
1481 PetscInt j_mid = (PetscInt)(info.ys + 0.5*info.ym);
1482 PetscInt k_mid = (PetscInt)(info.zs + 0.5*info.zm);
1483
1484 // --- 3. Print Boundary Information based on Data Layout ---
1485
1486 // ======================================================================
1487 // === CASE 1: Cell-Centered Fields (Ucat, P) - USES SHIFTED INDEX ===
1488 // ======================================================================
1489 if (strcmp(data_layout, "Cell-Centered") == 0) {
1490 const void *l_arr; // Use void pointer for generic array access
1491 ierr = DMDAVecGetArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
1492
1493 // --- I-Direction Boundaries ---
1494 if (info.xs == 0) { // Rank on -Xi boundary
1495 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Ghost for Cell 0) = ", rank, 0);
1496 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][0]);
1497 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1498
1499 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Value for Cell 0) = ", rank, 1);
1500 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][1]);
1501 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1502 }
1503 if (info.xs + info.xm == info.mx) { // Rank on +Xi boundary
1504 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Value for Cell %d) = ", rank, im_phys - 1, im_phys - 2);
1505 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][im_phys - 1]);
1506 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1507
1508 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Ghost for Cell %d) = ", rank, im_phys, im_phys - 2);
1509 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[k_mid][j_mid][im_phys]);
1510 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1511 }
1512
1513 // --- J-Direction Boundaries ---
1514 if (info.ys == 0) { // Rank on -Eta boundary
1515 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Idx %2d (Ghost for Cell 0) = ", rank, 0);
1516 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[k_mid][0][i_mid]);
1517 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1518
1519 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Idx %2d (Value for Cell 0) = ", rank, 1);
1520 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[k_mid][1][i_mid]);
1521 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1522 }
1523
1524 if (info.ys + info.ym == info.my) { // Rank on +Eta boundary
1525 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Idx %2d (Value for Cell %d) = ", rank, jm_phys - 1, jm_phys - 2);
1526 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[k_mid][jm_phys - 1][i_mid]);
1527 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1528
1529 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Idx %2d (Ghost for Cell %d) = ", rank, jm_phys, jm_phys - 2);
1530 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[k_mid][jm_phys][i_mid]);
1531 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1532 }
1533
1534 // --- K-Direction Boundaries ---
1535 if (info.zs == 0) { // Rank on -Zeta boundary
1536 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Ghost for Cell 0) = ", rank, 0);
1537 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[0][j_mid][i_mid]);
1538 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1539 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Value for Cell 0) = ", rank, 1);
1540 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[1][j_mid][i_mid]);
1541 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1542 }
1543 if (info.zs + info.zm == info.mz) { // Rank on +Zeta boundary
1544 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Value for Cell %d) = ", rank, km_phys - 1, km_phys - 2);
1545 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[km_phys - 1][j_mid][i_mid]);
1546 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1547 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Ghost for Cell %d) = ", rank, km_phys, km_phys - 2);
1548 if(dof==1) PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e)\n", ((const PetscReal***)l_arr)[km_phys][j_mid][i_mid]);
1549 else PetscSynchronizedPrintf(PETSC_COMM_WORLD, "(%.3e, %.3e, %.3e)\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);
1550 }
1551
1552 ierr = DMDAVecRestoreArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
1553 }
1554 // ======================================================================
1555 // === CASE 2: Face-Centered & Node-Centered Fields - USES DIRECT INDEX ===
1556 // ======================================================================
1557 else if (strcmp(data_layout, "Face-Centered") == 0 || strcmp(data_layout, "Node-Centered") == 0) {
1558 const Cmpnts ***l_arr; // Both Ucont and Coordinates are Cmpnts
1559 ierr = DMDAVecGetArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
1560
1561 // --- I-Direction Boundaries ---
1562 if (info.xs == 0) { // Rank on -Xi boundary
1563 if(strcmp(data_layout, "Face-Centered")==0){
1564 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (First Physical Face) = (%.3e)\n", rank, 0,
1565 l_arr[k_mid][j_mid][0].x);
1566 }else if(strcmp(data_layout, "Node-Centered")==0){// Node-Centered
1567 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (First Physical Face/Node) = (%.3e, %.3e, %.3e)\n", rank, 0,
1568 l_arr[k_mid][j_mid][0].x, l_arr[k_mid][j_mid][0].y, l_arr[k_mid][j_mid][0].z);
1569 }
1570 }
1571 if (info.xs + info.xm == info.mx) { // Rank on +Xi boundary
1572 if(strcmp(data_layout, "Face-Centered")==0){
1573 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Last Physical Face) = (%.3e)\n", rank, im_phys - 1,
1574 l_arr[k_mid][j_mid][im_phys - 1].x);
1575 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Unused/Ghost Location) = (%.3e)\n", rank, im_phys,
1576 l_arr[k_mid][j_mid][im_phys].x);
1577 }else if(strcmp(data_layout, "Node-Centered")==0){// Node-Centered
1578 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Last Physical Face/Node) = (%.3e, %.3e, %.3e)\n", rank, im_phys - 1,
1579 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);
1580 // Also show the value at the unused memory location for verification
1581 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, I-DIR]: Idx %2d (Unused/Ghost Location) = (%.3e, %.3e, %.3e)\n", rank, im_phys,
1582 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);
1583 }
1584 }
1585
1586 // --- J-Direction
1587
1588 if (info.ys == 0) { // Rank on -Eta boundary
1589 if(strcmp(data_layout, "Face-Centered")==0){
1590 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Idx %2d (First Physical Face) = (%.3e)\n", rank, 0,
1591 l_arr[k_mid][0][i_mid].y);
1592 }else if(strcmp(data_layout, "Node-Centered")==0){// Node-Centered
1593 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Idx %2d (First Physical Face/Node) = (%.3e, %.3e, %.3e)\n", rank, 0,
1594 l_arr[k_mid][0][i_mid].x, l_arr[k_mid][0][i_mid].y, l_arr[k_mid][0][i_mid].z);
1595 }
1596 }
1597 if (info.ys + info.ym == info.my) { // Rank on +Eta boundary
1598 if(strcmp(data_layout, "Face-Centered")==0){
1599 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Idx %2d (Last Physical Face) = (%.3e)\n", rank, jm_phys - 1,
1600 l_arr[k_mid][jm_phys - 1][i_mid].y);
1601 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Idx %2d (Unused/Ghost Location) = (%.3e)\n", rank, jm_phys,
1602 l_arr[k_mid][jm_phys][i_mid].y);
1603 }else if(strcmp(data_layout, "Node-Centered")==0){// Node-Centered
1604 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Idx %2d (Last Physical Face/Node) = (%.3e, %.3e, %.3e)\n", rank, jm_phys - 1,
1605 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);
1606 // Also show the value at the unused memory location for verification
1607 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, J-DIR]: Idx %2d (Unused/Ghost Location) = (%.3e, %.3e, %.3e)\n", rank, jm_phys,
1608 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);
1609 }
1610 }
1611 // --- K-Direction ---
1612 if (info.zs == 0) { // Rank on -Zeta boundary
1613 if(strcmp(data_layout, "Face-Centered")==0){
1614 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (First Physical Face) = (%.3e)\n", rank, 0,
1615 l_arr[0][j_mid][i_mid].z);
1616 }else if(strcmp(data_layout, "Node-Centered")==0){// Node-Centered
1617 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (First Physical Face/Node) = (%.3e, %.3e, %.3e)\n", rank, 0,
1618 l_arr[0][j_mid][i_mid].x, l_arr[0][j_mid][i_mid].y, l_arr[0][j_mid][i_mid].z);
1619 }
1620 }
1621 if(info.zs + info.zm == info.mz) { // Rank on +Zeta boundary
1622 if(strcmp(data_layout, "Face-Centered")==0){
1623 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Last Physical Face) = (%.3e)\n", rank, km_phys - 1,
1624 l_arr[km_phys - 1][j_mid][i_mid].z);
1625 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Unused/Ghost Location) = (%.3e)\n", rank, km_phys,
1626 l_arr[km_phys][j_mid][i_mid].z);
1627 }else if(strcmp(data_layout, "Node-Centered")==0){// Node-Centered
1628 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Last Physical Face/Node) = (%.3e, %.3e, %.3e)\n", rank, km_phys - 1,
1629 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);
1630 // Also show the value at the unused memory location for verification
1631 PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[Rank %d, K-DIR]: Idx %2d (Unused/Ghost Location) = (%.3e, %.3e, %.3e)\n", rank, km_phys,
1632 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);
1633 }
1634 }
1635
1636 ierr = DMDAVecRestoreArrayRead(dm, vec_local, (void*)&l_arr); CHKERRQ(ierr);
1637 }
1638 else {
1639 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "LOG_FIELD_ANATOMY only supports fields with 1 or 3 components & certain data-layouts, but field '%s' has %D components and an unsupported data-layout %s \n", field_name, dof,data_layout);
1640 }
1641 ierr = PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); CHKERRQ(ierr);
1642 ierr = PetscBarrier(NULL);
1643 PetscFunctionReturn(0);
1644}
Vec lCellFieldAtCorner
Definition variables.h:693
Vec lPsi
Definition variables.h:730
Vec lUcat
Definition variables.h:688
Here is the caller graph for this function: