PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Functions
io.h File Reference

Public interface for data input/output routines. More...

#include "variables.h"
#include "logging.h"
#include "Boundaries.h"
Include dependency graph for io.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

PetscErrorCode ReadGridGenerationInputs (UserCtx *user)
 Parses command-line options for a programmatically generated grid for a SINGLE block.
 
PetscErrorCode PopulateFinestUserGridResolutionFromOptions (UserCtx *finest_users, PetscInt nblk)
 Parses grid resolution arrays (-im, -jm, -km) once and applies them to all finest-grid blocks.
 
PetscErrorCode ReadGridFile (UserCtx *user)
 Sets grid dimensions from a file for a SINGLE block using a one-time read cache.
 
PetscErrorCode VerifyPathExistence (const char *path, PetscBool is_dir, PetscBool is_optional, const char *description, PetscBool *exists)
 A parallel-safe helper to verify the existence of a generic file or directory path.
 
PetscBool ShouldWriteDataOutput (const SimCtx *simCtx, PetscInt completed_step)
 Returns whether full field/restart output should be written for the.
 
PetscErrorCode WriteCheckpointBundle (SimCtx *simCtx, const char *reason)
 Write and atomically publish one complete checkpoint bundle.
 
PetscErrorCode ReadSimulationFields (UserCtx *user, PetscInt ti)
 Reads binary field data for velocity, pressure, and other required vectors.
 
PetscErrorCode RestoreFieldStatisticsState (SimCtx *simCtx, PetscInt ti)
 Restores field-statistics window state and accumulators from a checkpoint.
 
PetscErrorCode ReadFieldData (UserCtx *user, const char *field_name, Vec field_vec, const char *ext)
 Reads data for a specific field from a file into the provided vector.
 
PetscErrorCode WriteFieldData (UserCtx *user, const char *field_name, Vec field_vec, const char *ext)
 Writes data from a specific PETSc vector to a file.
 
PetscErrorCode WriteSimulationFields (UserCtx *user, const char *checkpoint_directory)
 Writes simulation fields to files.
 
PetscErrorCode WriteSwarmField (UserCtx *user, const char *field_name, const char *ext)
 Writes data from a specific field in a PETSc Swarm to a file.
 
PetscErrorCode WriteSwarmIntField (UserCtx *user, const char *field_name, const char *ext)
 Writes integer data from a specific PETSc Swarm field to a file.
 
PetscErrorCode WriteAllSwarmFields (UserCtx *user, const char *checkpoint_directory)
 Writes a predefined set of PETSc Swarm fields to files.
 
PetscInt ReadDataFileToArray (const char *filename, double **data_out, PetscInt *Nout, MPI_Comm comm)
 Reads a simple ASCII data file containing one numeric value per line.
 
PetscInt CreateVTKFileFromMetadata (const char *filename, const VTKMetaData *meta, MPI_Comm comm)
 Creates a VTK file from prepared metadata and field payloads.
 
PetscErrorCode VecToArrayOnRank0 (Vec inVec, PetscInt *N, double **arrayOut)
 Gathers the contents of a distributed PETSc Vec into a single array on rank 0.
 
PetscErrorCode SwarmFieldToArrayOnRank0 (DM swarm, const char *field_name, PetscInt *n_total_particles, PetscInt *n_components, PetscDataType *field_type_out, void **gathered_array)
 Gathers any DMSwarm field from all ranks to a single, contiguous array on rank 0.
 
PetscErrorCode ReadSwarmField (UserCtx *user, const char *field_name, const char *ext)
 Reads data from a file into a specified field of a PETSc DMSwarm.
 
PetscErrorCode ReadSwarmIntField (UserCtx *user, const char *field_name, const char *ext)
 Reads integer swarm data by using ReadFieldData and casting the result.
 
PetscErrorCode ReadAllSwarmFields (UserCtx *user, PetscInt ti)
 Reads multiple fields (positions, velocity, CellID, and weight) into a DMSwarm.
 
PetscErrorCode ReadCheckpointParticleCount (UserCtx *user, PetscInt ti, PetscInt *particle_count)
 Read the particle count from a validated committed checkpoint.
 
PetscErrorCode ReadPositionsFromFile (PetscInt timeIndex, UserCtx *user, double **coordsArray, PetscInt *Ncoords)
 Reads coordinate data (for particles) from file into a PETSc Vec, then gathers it to rank 0.
 
PetscErrorCode ReadFieldDataToRank0 (PetscInt timeIndex, const char *fieldName, UserCtx *user, double **scalarArray, PetscInt *Nscalars)
 Reads a named field from file into a PETSc Vec, then gathers it to rank 0.
 
PetscErrorCode DisplayBanner (SimCtx *simCtx)
 Displays a structured banner summarizing the simulation configuration.
 
PetscErrorCode StringToBCFace (const char *str, BCFace *face_out)
 Converts a face-token string (e.g., "-Xi", "+Eta") to the internal BCFace enum.
 
PetscErrorCode StringToBCType (const char *str, BCType *type_out)
 Converts a mathematical BC type string (e.g., "PERIODIC", "WALL") to BCType.
 
PetscErrorCode StringToBCHandlerType (const char *str, BCHandlerType *handler_out)
 Converts a BC handler token (implementation strategy) to BCHandlerType.
 
PetscErrorCode ValidateBCHandlerForBCType (BCType type, BCHandlerType handler)
 Validates that a selected handler is compatible with a mathematical BC type.
 
void FreeBC_ParamList (BC_Param *head)
 Frees an entire linked list of boundary-condition parameters.
 
PetscErrorCode GetBCParamReal (BC_Param *params, const char *key, PetscReal *value_out, PetscBool *found)
 Searches a BC_Param linked list for a key and returns its value as a double.
 
PetscErrorCode GetBCParamBool (BC_Param *params, const char *key, PetscBool *value_out, PetscBool *found)
 Searches a BC_Param linked list for a key and returns its value as a bool.
 
PetscErrorCode GetDrivenSeamFluxFlag (BC_Param *params, PetscBool *value_out, PetscBool *found)
 Read the driven-flow seam-flux flag, accepting its deprecated apply_trim spelling.
 
PetscErrorCode ParseAllBoundaryConditions (UserCtx *user, const char *bcs_input_filename)
 Parses the boundary conditions file to configure the type, handler, and any associated parameters for all 6 global faces of the domain.
 
PetscErrorCode DeterminePeriodicity (SimCtx *simCtx)
 Scans all block-specific boundary condition files to determine a globally consistent periodicity for each dimension, reusing the core type parser.
 
void TrimWhitespace (char *str)
 Removes leading and trailing ASCII whitespace from a mutable string.
 
PetscBool CheckpointFieldIsEnabled (const SimCtx *simCtx, const FieldDescriptor *descriptor)
 Return whether a catalogued field belongs in the current checkpoint.
 
PetscErrorCode PicurvPhysicalTime (const SimCtx *simCtx, PetscReal solver_time, PetscReal *physical)
 Convert a solver time to physical seconds, t * L_ref / U_ref.
 
PetscErrorCode PicurvFieldReferenceScale (SimCtx *simCtx, const char *field_name, PetscReal *scale, char *description, size_t description_length)
 Physical scale one field is multiplied by to leave non-dimensional form.
 
PetscErrorCode ParsePostProcessingSettings (SimCtx *simCtx)
 Initializes post-processing settings from a config file and command-line overrides.
 
PetscErrorCode ParseScalingInformation (SimCtx *simCtx)
 Parses physical scaling parameters from command-line options.
 

Detailed Description

Public interface for data input/output routines.

This header declares functions responsible for parsing grid geometry information, either from command-line options for programmatically generated grids or by reading the header of a grid definition file.

Definition in file io.h.

Function Documentation

◆ ReadGridGenerationInputs()

PetscErrorCode ReadGridGenerationInputs ( UserCtx *  user)

Parses command-line options for a programmatically generated grid for a SINGLE block.

This function reads all per-block array options related to grid geometry, such as dimensions (-im), domain bounds (-xMins), and stretching ratios (-rxs). It then populates the fields of the provided UserCtx struct using its internal block index user->_this.

Parameters
userPointer to the UserCtx for a specific block. The function will populate the geometric fields (IM, Min_X, rx, etc.) within this struct.
Returns
PetscErrorCode 0 on success, or a PETSc error code on failure.

Parses command-line options for a programmatically generated grid for a SINGLE block.

Local to this translation unit.

Definition at line 441 of file io.c.

442{
443 PetscErrorCode ierr;
444 SimCtx *simCtx = user->simCtx;
445 PetscInt nblk = simCtx->block_number;
446 PetscInt block_index = user->_this;
447 PetscBool found;
448
449 // Temporary arrays to hold the parsed values for ALL blocks
450 PetscInt *IMs = NULL, *JMs = NULL, *KMs = NULL, *cgrids = NULL;
451 PetscReal *xMins = NULL, *xMaxs = NULL, *rxs = NULL;
452 PetscReal *yMins = NULL, *yMaxs = NULL, *rys = NULL;
453 PetscReal *zMins = NULL, *zMaxs = NULL, *rzs = NULL;
454
455 PetscFunctionBeginUser;
457
458 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Reading generated grid inputs for block %d.\n", simCtx->rank, block_index);
459
460 if (block_index >= nblk) {
461 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Block index %d is out of range for nblk=%d", block_index, nblk);
462 }
463
464 // --- Allocate temporary storage for all array options ---
465 ierr = PetscMalloc4(nblk, &IMs, nblk, &JMs, nblk, &KMs, nblk, &cgrids); CHKERRQ(ierr);
466 ierr = PetscMalloc6(nblk, &xMins, nblk, &xMaxs, nblk, &rxs, nblk, &yMins, nblk, &yMaxs, nblk, &rys); CHKERRQ(ierr);
467 ierr = PetscMalloc3(nblk, &zMins, nblk, &zMaxs, nblk, &rzs); CHKERRQ(ierr);
468
469 // --- Set default values for the temporary arrays ---
470 for (PetscInt i = 0; i < nblk; ++i) {
471 IMs[i] = 10; JMs[i] = 10; KMs[i] = 10; cgrids[i] = 0;
472 xMins[i] = 0.0; xMaxs[i] = 1.0; rxs[i] = 1.0;
473 yMins[i] = 0.0; yMaxs[i] = 1.0; rys[i] = 1.0;
474 zMins[i] = 0.0; zMaxs[i] = 1.0; rzs[i] = 1.0;
475 }
476
477 // --- Parse the array options from the command line / control file ---
478 PetscInt count;
479 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-im", IMs, &count, &found); CHKERRQ(ierr);
480 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-jm", JMs, &count, &found); CHKERRQ(ierr);
481 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-km", KMs, &count, &found); CHKERRQ(ierr);
482 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-xMins", xMins, &count, &found); CHKERRQ(ierr);
483 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-xMaxs", xMaxs, &count, &found); CHKERRQ(ierr);
484 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-rxs", rxs, &count, &found); CHKERRQ(ierr);
485 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-yMins", yMins, &count, &found); CHKERRQ(ierr);
486 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-yMaxs", yMaxs, &count, &found); CHKERRQ(ierr);
487 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-rys", rys, &count, &found); CHKERRQ(ierr);
488 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-zMins", zMins, &count, &found); CHKERRQ(ierr);
489 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-zMaxs", zMaxs, &count, &found); CHKERRQ(ierr);
490 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-rzs", rzs, &count, &found); CHKERRQ(ierr);
491 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-cgrids", cgrids, &count, &found); CHKERRQ(ierr);
492
493 /* Domain bounds are supplied in physical units, exactly like a `.picgrid`. A file
494 * grid is divided by the reference length when the conductor publishes it as an
495 * asset; a programmatically generated one has no such staging step, so the same
496 * division happens here. Without it this mode alone would take non-dimensional
497 * input while post-processing multiplied its coordinates back by L_ref, inflating
498 * the reported geometry whenever length_ref differed from one. */
499 const PetscReal length_ref = simCtx->scaling.L_ref;
500
501 PetscCheck(length_ref > 0.0, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE,
502 "Reference length L_ref must be positive to non-dimensionalize the "
503 "programmatic domain bounds. Got %g", (double)length_ref);
504
505 // --- Assign the parsed values to the specific UserCtx struct passed in ---
506 user->IM = IMs[block_index];
507 user->JM = JMs[block_index];
508 user->KM = KMs[block_index];
509 user->Min_X = xMins[block_index] / length_ref;
510 user->Max_X = xMaxs[block_index] / length_ref;
511 user->rx = rxs[block_index];
512 user->Min_Y = yMins[block_index] / length_ref;
513 user->Max_Y = yMaxs[block_index] / length_ref;
514 user->ry = rys[block_index];
515 user->Min_Z = zMins[block_index] / length_ref;
516 user->Max_Z = zMaxs[block_index] / length_ref;
517 user->rz = rzs[block_index];
518 user->cgrid = cgrids[block_index];
519
520 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Block %d grid generation inputs set: IM=%d, JM=%d, KM=%d\n",
521 simCtx->rank, block_index, user->IM, user->JM, user->KM);
522 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Block %d bounds after L_ref=%.4g: X=[%.4g, %.4g], "
523 "Y=[%.4g, %.4g], Z=[%.4g, %.4g]\n",
524 simCtx->rank, block_index, (double)length_ref,
525 user->Min_X, user->Max_X, user->Min_Y, user->Max_Y, user->Min_Z, user->Max_Z);
526
527 // --- Clean up temporary storage ---
528 ierr = PetscFree4(IMs, JMs, KMs, cgrids); CHKERRQ(ierr);
529 ierr = PetscFree6(xMins, xMaxs, rxs, yMins, yMaxs, rys); CHKERRQ(ierr);
530 ierr = PetscFree3(zMins, zMaxs, rzs); CHKERRQ(ierr);
531
533 PetscFunctionReturn(0);
534}
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:45
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:200
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:894
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:885
PetscMPIInt rank
Definition variables.h:862
PetscInt cgrid
Definition variables.h:1094
PetscInt block_number
Definition variables.h:952
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:1077
PetscReal Min_X
Definition variables.h:1089
PetscInt KM
Definition variables.h:1088
PetscReal L_ref
Definition variables.h:841
PetscInt _this
Definition variables.h:1092
PetscReal ry
Definition variables.h:1093
PetscReal Max_Y
Definition variables.h:1089
PetscReal rz
Definition variables.h:1093
ScalingCtx scaling
Definition variables.h:946
PetscInt JM
Definition variables.h:1088
PetscReal Min_Z
Definition variables.h:1089
PetscReal Max_X
Definition variables.h:1089
PetscReal Min_Y
Definition variables.h:1089
PetscInt IM
Definition variables.h:1088
PetscReal rx
Definition variables.h:1093
PetscReal Max_Z
Definition variables.h:1089
The master context for the entire simulation.
Definition variables.h:859
Here is the caller graph for this function:

◆ PopulateFinestUserGridResolutionFromOptions()

PetscErrorCode PopulateFinestUserGridResolutionFromOptions ( UserCtx *  finest_users,
PetscInt  nblk 
)

Parses grid resolution arrays (-im, -jm, -km) once and applies them to all finest-grid blocks.

This helper centralizes one-time resolution ingestion for analytical grid setup. It fills IM/JM/KM in each element of the finest-level UserCtx array.

Parameters
finest_usersPointer to the finest-level UserCtx array (length nblk).
nblkNumber of blocks in the finest-level array.
Returns
PetscErrorCode 0 on success, or a PETSc error code on failure.

Parses grid resolution arrays (-im, -jm, -km) once and applies them to all finest-grid blocks.

Local to this translation unit.

Definition at line 540 of file io.c.

541{
542 PetscErrorCode ierr;
543 PetscBool found;
544 PetscInt *IMs = NULL, *JMs = NULL, *KMs = NULL;
545 SimCtx *simCtx = NULL;
546
547 PetscFunctionBeginUser;
548
549 if (!finest_users) {
550 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "finest_users cannot be NULL.");
551 }
552 if (nblk <= 0) {
553 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "nblk must be positive. Got %d.", nblk);
554 }
555 simCtx = finest_users[0].simCtx;
556
557 ierr = PetscMalloc3(nblk, &IMs, nblk, &JMs, nblk, &KMs); CHKERRQ(ierr);
558 for (PetscInt i = 0; i < nblk; ++i) {
559 IMs[i] = 10; JMs[i] = 10; KMs[i] = 10;
560 }
561
562 PetscInt count;
563 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-im", IMs, &count, &found); CHKERRQ(ierr);
564 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-jm", JMs, &count, &found); CHKERRQ(ierr);
565 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-km", KMs, &count, &found); CHKERRQ(ierr);
566
567 for (PetscInt bi = 0; bi < nblk; ++bi) {
568 finest_users[bi].IM = IMs[bi];
569 finest_users[bi].JM = JMs[bi];
570 finest_users[bi].KM = KMs[bi];
571 if (simCtx) {
573 "Rank %d: Preloaded analytical grid resolution for block %d: IM=%d, JM=%d, KM=%d\n",
574 simCtx->rank, bi, finest_users[bi].IM, finest_users[bi].JM, finest_users[bi].KM);
575 }
576 }
577
578 ierr = PetscFree3(IMs, JMs, KMs); CHKERRQ(ierr);
579 PetscFunctionReturn(0);
580}
Here is the caller graph for this function:

◆ ReadGridFile()

PetscErrorCode ReadGridFile ( UserCtx *  user)

Sets grid dimensions from a file for a SINGLE block using a one-time read cache.

This function uses a static-variable pattern to ensure the grid file header is read only once, collectively, by all processes on the first call. Subsequent calls simply retrieve the pre-loaded and broadcasted data for the specified block.

Parameters
userPointer to the UserCtx for a specific block. This function will populate the IM, JM, and KM fields.
Returns
PetscErrorCode 0 on success, or a PETSc error code on failure.

Sets grid dimensions from a file for a SINGLE block using a one-time read cache.

Local to this translation unit.

Definition at line 589 of file io.c.

590{
591 PetscErrorCode ierr;
592 SimCtx *simCtx = user->simCtx;
593 PetscInt block_index = user->_this;
594
595 PetscFunctionBeginUser;
597
598 // --- One-Time Read and Broadcast Logic ---
600 LOG_ALLOW_SYNC(GLOBAL, LOG_INFO, "First call to ReadGridFile. Reading and broadcasting grid file header from '%s'...\n", simCtx->grid_file);
601 PetscMPIInt rank = simCtx->rank;
602 PetscInt nblk = simCtx->block_number;
603
604 if (rank == 0) {
605 FILE *fd = fopen(simCtx->grid_file, "r");
606 if (!fd) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open file: %s", simCtx->grid_file);
607
608 // Read and validate the canonical PICGRID header.
609 char firstTok[32] = {0};
610 if (fscanf(fd, "%31s", firstTok) != 1)
611 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Empty grid file: %s", simCtx->grid_file);
612 if (strcmp(firstTok, "PICGRID") != 0)
613 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
614 "Grid file %s must begin with the canonical PICGRID header.", simCtx->grid_file);
615 if (fscanf(fd, "%d", &g_nblk_from_file) != 1)
616 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Expected number of blocks after \"PICGRID\" in %s", simCtx->grid_file);
617 if (g_nblk_from_file != nblk) {
618 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED, "Mismatch: -nblk is %d but grid file specifies %d blocks.", nblk, g_nblk_from_file);
619 }
620
621 ierr = PetscMalloc3(nblk, &g_IMs_from_file, nblk, &g_JMs_from_file, nblk, &g_KMs_from_file); CHKERRQ(ierr);
622 for (PetscInt i = 0; i < nblk; ++i) {
623 if (fscanf(fd, "%d %d %d\n", &g_IMs_from_file[i], &g_JMs_from_file[i], &g_KMs_from_file[i]) != 3) {
624 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Expected 3 integers for block %d in %s", i, simCtx->grid_file);
625 }
626 }
627 fclose(fd);
628 }
629
630 // Broadcast nblk to verify (optional, good practice)
631 ierr = MPI_Bcast(&g_nblk_from_file, 1, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
632
633 // Allocate on other ranks before receiving the broadcast
634 if (rank != 0) {
635 ierr = PetscMalloc3(nblk, &g_IMs_from_file, nblk, &g_JMs_from_file, nblk, &g_KMs_from_file); CHKERRQ(ierr);
636 }
637
638 // Broadcast the data arrays
639 ierr = MPI_Bcast(g_IMs_from_file, nblk, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
640 ierr = MPI_Bcast(g_JMs_from_file, nblk, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
641 ierr = MPI_Bcast(g_KMs_from_file, nblk, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
642
643 g_file_has_been_read = PETSC_TRUE;
644 LOG_ALLOW(GLOBAL, LOG_INFO, "Grid file header read and broadcast complete.\n");
645 }
646
647 // --- Per-Block Assignment Logic (runs on every call) ---
648 user->IM = g_IMs_from_file[block_index];
649 user->JM = g_JMs_from_file[block_index];
650 user->KM = g_KMs_from_file[block_index];
651
652 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Set file inputs for Block %d: IM=%d, JM=%d, KM=%d\n",
653 simCtx->rank, block_index, user->IM, user->JM, user->KM);
654
656 PetscFunctionReturn(0);
657}
static PetscInt * g_IMs_from_file
Caches the IM dimensions for all blocks read from the grid file.
Definition io.c:36
static PetscInt g_nblk_from_file
Stores the number of blocks read from the grid file.
Definition io.c:34
static PetscBool g_file_has_been_read
A flag to ensure the grid file is read only once.
Definition io.c:42
static PetscInt * g_KMs_from_file
Caches the KM dimensions for all blocks read from the grid file.
Definition io.c:40
static PetscInt * g_JMs_from_file
Caches the JM dimensions for all blocks read from the grid file.
Definition io.c:38
#define LOG_ALLOW_SYNC(scope, level, fmt,...)
Synchronized logging macro that checks both the log level and whether the calling function is in the ...
Definition logging.h:253
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
char grid_file[PETSC_MAX_PATH_LEN]
Definition variables.h:955
Here is the caller graph for this function:

◆ VerifyPathExistence()

PetscErrorCode VerifyPathExistence ( const char *  path,
PetscBool  is_dir,
PetscBool  is_optional,
const char *  description,
PetscBool *  exists 
)

A parallel-safe helper to verify the existence of a generic file or directory path.

This function centralizes the logic for checking arbitrary paths. Only Rank 0 performs the filesystem check, and the result is broadcast to all other processes. This ensures collective and synchronized decision-making across all ranks. It is intended for configuration files, source directories, etc., where the path is known completely.

Parameters
[in]pathThe full path to the file or directory to check.
[in]is_dirPETSC_TRUE if checking for a directory, PETSC_FALSE for a file.
[in]is_optionalPETSC_TRUE if the path is optional (results in a warning), PETSC_FALSE if mandatory (results in an error).
[in]descriptionA user-friendly description of the path for logging (e.g., "Grid file").
[out]existsThe result of the check (identical on all ranks).
Returns
PetscErrorCode

A parallel-safe helper to verify the existence of a generic file or directory path.

Local to this translation unit.

Definition at line 1136 of file io.c.

1137{
1138 PetscErrorCode ierr;
1139 PetscMPIInt rank;
1140 MPI_Comm comm = PETSC_COMM_WORLD;
1141
1142 PetscFunctionBeginUser;
1143 ierr = MPI_Comm_rank(comm, &rank); CHKERRQ(ierr);
1144
1145 if (rank == 0) {
1146 if (is_dir) {
1147 ierr = PetscTestDirectory(path, 'r', exists); CHKERRQ(ierr);
1148 } else {
1149 ierr = PetscTestFile(path, 'r', exists); CHKERRQ(ierr);
1150 }
1151
1152 if (!(*exists)) {
1153 if (is_optional) {
1154 LOG_ALLOW(GLOBAL, LOG_WARNING, "Optional %s not found at: %s (using defaults/ignoring).\n", description, path);
1155 } else {
1156 LOG_ALLOW(GLOBAL, LOG_ERROR, "Mandatory %s not found at: %s\n", description, path);
1157 }
1158 } else {
1159 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Found %s: %s\n", description, path);
1160 }
1161 }
1162
1163 // Broadcast the result from Rank 0
1164 PetscMPIInt exists_int = (rank == 0) ? (PetscMPIInt)(*exists) : 0;
1165 ierr = MPI_Bcast(&exists_int, 1, MPI_INT, 0, comm); CHKERRMPI(ierr);
1166 *exists = (PetscBool)exists_int;
1167
1168 // Collective error for mandatory files
1169 if (!(*exists) && !is_optional) {
1170 SETERRQ(comm, PETSC_ERR_FILE_OPEN, "Mandatory %s not found. Rank 0 expected it at '%s'. Check path and permissions.", description, path);
1171 }
1172
1173 PetscFunctionReturn(0);
1174}
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:29
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
Here is the caller graph for this function:

◆ ShouldWriteDataOutput()

PetscBool ShouldWriteDataOutput ( const SimCtx *  simCtx,
PetscInt  completed_step 
)

Returns whether full field/restart output should be written for the.

completed timestep.

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

Returns whether full field/restart output should be written for the.

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

See also
ShouldWriteDataOutput()

Definition at line 426 of file io.c.

427{
428 if (!simCtx) {
429 return PETSC_FALSE;
430 }
431 return (PetscBool)(simCtx->tiout > 0 && completed_step > 0 && completed_step % simCtx->tiout == 0);
432}
PetscInt tiout
Definition variables.h:871
Here is the caller graph for this function:

◆ WriteCheckpointBundle()

PetscErrorCode WriteCheckpointBundle ( SimCtx *  simCtx,
const char *  reason 
)

Write and atomically publish one complete checkpoint bundle.

Parameters
[in,out]simCtxSimulation context owning all enabled checkpoint state.
[in]reasonStable lifecycle reason such as initial, cadence, final, or signal.
Returns
Zero on success or a PETSc/MPI/filesystem error.

Write and atomically publish one complete checkpoint bundle.

Definition at line 2554 of file io.c.

2555{
2556 UserCtx *user = NULL;
2557 char checkpoints_root[PETSC_MAX_PATH_LEN];
2558 char final_directory[PETSC_MAX_PATH_LEN];
2559 char temporary_directory[PETSC_MAX_PATH_LEN];
2560 char nested_directory[PETSC_MAX_PATH_LEN];
2561 char commit_path[PETSC_MAX_PATH_LEN];
2562 char geometry_digest[65] = "";
2563 char manifest_digest[65] = "";
2564 PetscBool final_exists = PETSC_FALSE;
2565 PetscInt particle_count = 0;
2566 PetscMPIInt writer_pid = 0;
2567
2568 PetscFunctionBeginUser;
2569 PetscCheck(simCtx != NULL && reason != NULL && reason[0] != '\0',
2570 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2571 "Simulation context and checkpoint reason are required.");
2572 user = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
2573 PetscCheck(user != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
2574 "Finest-level fields must exist before checkpoint output.");
2575
2576 PetscCall(PetscSNPrintf(checkpoints_root, sizeof(checkpoints_root), "%s/%s",
2578 PetscCall(FormatCheckpointStepDirectory(simCtx->output_dir, simCtx->step,
2579 final_directory, sizeof(final_directory)));
2580 PetscCall(PetscTestDirectory(final_directory, 'r', &final_exists));
2581 if (final_exists) {
2582 PetscReal saved_time = 0.0;
2583 PetscCall(ValidateCheckpointBundle(simCtx, user, final_directory, simCtx->step,
2584 &saved_time, NULL, NULL, NULL));
2585 PetscCheck(PetscAbsReal(saved_time - simCtx->ti) <=
2586 10.0 * PETSC_MACHINE_EPSILON * PetscMax(1.0, PetscAbsReal(simCtx->ti)),
2587 PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
2588 "Committed checkpoint step %" PetscInt_FMT " has time %.17g, current state has time %.17g.",
2589 simCtx->step, (double)saved_time, (double)simCtx->ti);
2590 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Checkpoint step %d is already committed; skipping duplicate write.\n",
2591 simCtx->step);
2592 PetscFunctionReturn(0);
2593 }
2594
2595 if (simCtx->rank == 0) writer_pid = (PetscMPIInt)getpid();
2596 PetscCallMPI(MPI_Bcast(&writer_pid, 1, MPI_INT, 0, PETSC_COMM_WORLD));
2597 PetscCall(PetscSNPrintf(temporary_directory, sizeof(temporary_directory),
2598 "%s/.step_%0*" PetscInt_FMT ".incomplete.%d",
2599 checkpoints_root, PICURV_CHECKPOINT_STEP_WIDTH,
2600 simCtx->step, (int)writer_pid));
2601 PetscCall(CreateCheckpointDirectoryCollective(simCtx, checkpoints_root));
2602 PetscCall(CreateCheckpointDirectoryCollective(simCtx, temporary_directory));
2603 PetscCall(PetscSNPrintf(nested_directory, sizeof(nested_directory), "%s/%s",
2604 temporary_directory, PICURV_EULERIAN_DIRECTORY));
2605 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2606 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2607 PetscCall(PetscSNPrintf(nested_directory, sizeof(nested_directory),
2608 "%s/%s/block_%04" PetscInt_FMT,
2609 temporary_directory, PICURV_EULERIAN_DIRECTORY, block));
2610 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2611 }
2612 if (simCtx->np > 0) {
2613 PetscCall(PetscSNPrintf(nested_directory, sizeof(nested_directory), "%s/%s",
2614 temporary_directory, PICURV_PARTICLE_DIRECTORY));
2615 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2616 PetscCall(DMSwarmGetSize(user->swarm, &particle_count));
2617 }
2618 if (FieldStatisticsIsActive(simCtx)) {
2619 PetscCall(FormatStatisticsPath(temporary_directory, -1, -1, NULL,
2620 nested_directory, sizeof(nested_directory)));
2621 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2622 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
2623 PetscCall(FormatStatisticsPath(temporary_directory, window, -1, NULL,
2624 nested_directory, sizeof(nested_directory)));
2625 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2626 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2627 PetscCall(FormatStatisticsPath(temporary_directory, window, block, NULL,
2628 nested_directory, sizeof(nested_directory)));
2629 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2630 }
2631 }
2632 }
2633
2634 PetscCall(ComputeCheckpointGeometrySHA256(simCtx, user, geometry_digest));
2635 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2636 PetscCall(WriteSimulationFields(&user[block], temporary_directory));
2637 }
2638 if (simCtx->np > 0) PetscCall(WriteAllSwarmFields(user, temporary_directory));
2639 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2640 PetscCall(WriteStatisticsFields(&user[block], temporary_directory));
2641 }
2642 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
2643
2644 PetscCall(WriteCheckpointManifest(simCtx, user, temporary_directory, reason,
2645 geometry_digest, particle_count, manifest_digest));
2646 if (simCtx->rank == 0) {
2647 FILE *commit_file = NULL;
2648
2649 PetscCall(PetscSNPrintf(commit_path, sizeof(commit_path), "%s/COMMITTED", temporary_directory));
2650 commit_file = fopen(commit_path, "w");
2651 PetscCheck(commit_file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
2652 "Unable to create checkpoint commit marker '%s'.", commit_path);
2653 PetscCheck(fprintf(commit_file, "%s\n", manifest_digest) > 0 &&
2654 fflush(commit_file) == 0 && fsync(fileno(commit_file)) == 0,
2655 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2656 "Unable to write checkpoint commit marker '%s'.", commit_path);
2657 PetscCheck(fclose(commit_file) == 0, PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2658 "Unable to close checkpoint commit marker '%s'.", commit_path);
2659 PetscCheck(rename(temporary_directory, final_directory) == 0,
2660 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2661 "Unable to commit checkpoint '%s': %s", final_directory, strerror(errno));
2662 }
2663 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
2665 "Committed checkpoint step %d at t=%.17g (%s): %s\n",
2666 simCtx->step, (double)simCtx->ti, reason, final_directory);
2667 PetscFunctionReturn(0);
2668}
#define PICURV_CHECKPOINT_STEP_WIDTH
Definition io.c:27
#define PICURV_CHECKPOINTS_DIRECTORY
Definition io.c:23
PetscErrorCode WriteAllSwarmFields(UserCtx *user, const char *checkpoint_directory)
Internal helper implementation: WriteAllSwarmFields().
Definition io.c:2218
static PetscErrorCode FormatStatisticsPath(const char *root, PetscInt window, PetscInt block, const char *payload_name, char *path, size_t path_size)
Format any level of the statistics subtree, from the root down to one payload.
Definition io.c:99
#define PICURV_PARTICLE_DIRECTORY
Definition io.c:25
static PetscErrorCode FormatCheckpointStepDirectory(const char *root, PetscInt step, char *path, size_t path_size)
Format the canonical directory name for one completed step.
Definition io.c:204
PetscErrorCode WriteSimulationFields(UserCtx *user, const char *checkpoint_directory)
Implementation of WriteSimulationFields().
Definition io.c:2021
static PetscErrorCode CreateCheckpointDirectoryCollective(const SimCtx *simCtx, const char *path)
Create one directory on rank zero and report failures collectively.
Definition io.c:232
static PetscErrorCode ComputeCheckpointGeometrySHA256(SimCtx *simCtx, UserCtx *user, char digest_hex[65])
Compute and cache a rank-count-independent hash of the active grid geometry.
Definition io.c:155
static PetscErrorCode ValidateCheckpointBundle(SimCtx *simCtx, UserCtx *user, const char *checkpoint_directory, PetscInt expected_step, PetscReal *physical_time, PetscInt *particle_count, PetscBool *particles_saved, PetscBool *les_saved)
Validate a committed bundle and return selected authoritative metadata.
Definition io.c:252
static PetscErrorCode WriteStatisticsFields(UserCtx *user, const char *checkpoint_directory)
Write every window's accumulator payloads for one block into a bundle.
Definition io.c:2056
#define PICURV_EULERIAN_DIRECTORY
Definition io.c:24
static PetscErrorCode WriteCheckpointManifest(SimCtx *simCtx, UserCtx *user, const char *checkpoint_directory, const char *reason, const char *geometry_digest, PetscInt particle_count, char manifest_digest[65])
Write the complete manifest after every payload has closed successfully.
Definition io.c:2304
PetscBool FieldStatisticsIsActive(const struct SimCtx *simCtx)
Reports whether this run has live field-statistics state.
PetscInt fieldStatisticsWindowCount
Definition variables.h:932
UserCtx * user
Definition variables.h:729
UserMG usermg
Definition variables.h:1015
char output_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:881
PetscInt np
Definition variables.h:990
PetscInt mglevels
Definition variables.h:736
PetscInt step
Definition variables.h:867
MGCtx * mgctx
Definition variables.h:739
PetscReal ti
Definition variables.h:868
User-defined context containing data specific to a single computational grid level.
Definition variables.h:1074
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadSimulationFields()

PetscErrorCode ReadSimulationFields ( UserCtx *  user,
PetscInt  ti 
)

Reads binary field data for velocity, pressure, and other required vectors.

Validates one committed checkpoint bundle and reads every enabled Eulerian checkpoint field through the field catalog.

Parameters
[in,out]userPointer to the UserCtx structure containing the simulation context.
[in]tiCheckpoint step resolved below the configured source root.
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Reads binary field data for velocity, pressure, and other required vectors.

Local to this translation unit.

Definition at line 1471 of file io.c.

1472{
1473 SimCtx *simCtx = user->simCtx;
1474 const char *source_path = NULL;
1475 char checkpoint_directory[PETSC_MAX_PATH_LEN];
1476 PetscReal checkpoint_time = 0.0;
1477 PetscBool particles_saved = PETSC_FALSE;
1478 PetscBool les_saved = PETSC_FALSE;
1479
1480 PetscFunctionBeginUser;
1481 if(simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR){
1482 source_path = simCtx->pps->source_dir;
1483 } else if(simCtx->exec_mode == EXEC_MODE_SOLVER){
1484 source_path = simCtx->restart_dir;
1485 } else{
1486 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Invalid execution mode for reading simulation fields.");
1487 }
1488
1489 PetscCall(ResolveCheckpointStepDirectory(source_path, ti,
1490 checkpoint_directory, sizeof(checkpoint_directory)));
1491 PetscCall(ValidateCheckpointBundle(simCtx, user - user->_this,
1492 checkpoint_directory, ti, &checkpoint_time, NULL,
1493 &particles_saved, &les_saved));
1494 PetscCheck(!(simCtx->np > 0 && !strcmp(simCtx->particleRestartMode, "load")) || particles_saved,
1495 PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
1496 "Particle restart_mode=load was requested, but checkpoint step %" PetscInt_FMT
1497 " contains no particle state.", ti);
1498 if (simCtx->exec_mode == EXEC_MODE_SOLVER && user->_this == 0) {
1499 simCtx->ti = checkpoint_time;
1500 if (ti == simCtx->StartStep) simCtx->StartTime = checkpoint_time;
1501 PetscCall(RestoreDrivenFluxTarget(simCtx, user, checkpoint_directory));
1502 }
1503
1504 LOG_ALLOW(GLOBAL, LOG_INFO, "Reading Eulerian checkpoint fields for block %d from '%s'.\n",
1505 user->_this, checkpoint_directory);
1506 PetscCall(PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer),
1507 "%s/%s/block_%04" PetscInt_FMT,
1508 checkpoint_directory, PICURV_EULERIAN_DIRECTORY, user->_this));
1509 simCtx->current_io_directory = simCtx->_io_context_buffer;
1510
1511 for (PetscInt raw_id = 0; raw_id < FIELD_ID_COUNT; ++raw_id) {
1512 const FieldDescriptor *descriptor = NULL;
1513 FieldView view;
1514
1515 PetscCall(FieldGetDescriptor((FieldId)raw_id, &descriptor));
1516 if (!CheckpointFieldIsEnabled(simCtx, descriptor)) continue;
1517 /* A solver restart that reseeds particles (restart_mode init) must not restore the
1518 averages of the particles it discards; post-processing shows what was saved. */
1519 if ((descriptor->availability & FIELD_AVAILABILITY_PARTICLES) &&
1520 (!particles_saved ||
1521 (simCtx->exec_mode == EXEC_MODE_SOLVER && strcmp(simCtx->particleRestartMode, "load")))) continue;
1522 if ((descriptor->availability & FIELD_AVAILABILITY_LES_DYNAMIC) && !les_saved) continue;
1523 /* Wall-model state is derived, not carried: the first boundary pass of the
1524 restarted run recomputes it from the restored velocity field. Writing it keeps
1525 it available to postprocessing; reading it would only tie a restart to whether
1526 the source checkpoint happened to have a wall model enabled. */
1527 if (descriptor->availability & FIELD_AVAILABILITY_WALL_MODEL) continue;
1528 if ((descriptor->availability & FIELD_AVAILABILITY_TURBULENCE) &&
1529 !(simCtx->les && les_saved)) continue;
1530 PetscCall(FieldGetView(user, descriptor->id, &view));
1531 PetscCall(ReadFieldData(user, descriptor->canonical_name, view.global_vec, "dat"));
1532 /* A dimensionalizing post-processor scales each field as it is loaded, so a field
1533 is scaled exactly once per load: one that this step skipped keeps its earlier,
1534 already scaled, value instead of being scaled again. */
1535 if (simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR && simCtx->pps && simCtx->pps->dimensionalize) {
1536 PetscReal scale = 1.0;
1537
1538 PetscCall(PicurvFieldReferenceScale(simCtx, descriptor->canonical_name, &scale, NULL, 0));
1539 if (scale != 1.0) PetscCall(VecScale(view.global_vec, scale));
1540 }
1541 if (view.local_vec) PetscCall(UpdateLocalGhosts(user, descriptor->id));
1542 }
1543 simCtx->restartHistoryAvailable = PETSC_TRUE;
1544 simCtx->current_io_directory = NULL;
1545 PetscFunctionReturn(0);
1546}
@ FIELD_AVAILABILITY_WALL_MODEL
@ FIELD_AVAILABILITY_PARTICLES
@ FIELD_AVAILABILITY_TURBULENCE
@ FIELD_AVAILABILITY_LES_DYNAMIC
unsigned int availability
PetscErrorCode FieldGetView(UserCtx *user, FieldId field_id, FieldView *view)
Resolve the existing DM and global/local vectors for one field.
const char * canonical_name
PetscErrorCode FieldGetDescriptor(FieldId field_id, const FieldDescriptor **descriptor)
Return immutable metadata for a valid field identifier.
FieldId
Compile-time identity for a catalogued Eulerian field.
@ FIELD_ID_COUNT
Immutable metadata for one field identity.
Non-owning runtime objects resolved for one field and UserCtx.
PetscErrorCode PicurvFieldReferenceScale(SimCtx *simCtx, const char *field_name, PetscReal *scale, char *description, size_t description_length)
Implementation of PicurvFieldReferenceScale().
Definition io.c:3197
static PetscErrorCode RestoreDrivenFluxTarget(SimCtx *simCtx, UserCtx *user, const char *checkpoint_directory)
Restore a latched driven-flow flux target from a checkpoint manifest.
Definition io.c:1420
PetscBool CheckpointFieldIsEnabled(const SimCtx *simCtx, const FieldDescriptor *descriptor)
Implementation of CheckpointFieldIsEnabled().
Definition io.c:72
PetscErrorCode ReadFieldData(UserCtx *user, const char *field_name, Vec field_vec, const char *ext)
Internal helper implementation: ReadFieldData().
Definition io.c:1182
static PetscErrorCode ResolveCheckpointStepDirectory(const char *source_root, PetscInt step, char *path, size_t path_size)
Resolve either an exact bundle or a run/output root to one step bundle.
Definition io.c:215
str source_path(str source)
Extract a repository path from a path-or-symbol source declaration.
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
Definition setup.c:2489
PetscReal StartTime
Definition variables.h:873
PetscBool dimensionalize
Whether derived output leaves non-dimensional form, from global_operations.dimensionalize.
Definition variables.h:789
PetscInt StartStep
Definition variables.h:869
char * current_io_directory
Definition variables.h:885
char particleRestartMode[16]
Definition variables.h:996
char source_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:756
PostProcessParams * pps
Definition variables.h:1058
@ EXEC_MODE_SOLVER
Definition variables.h:832
@ EXEC_MODE_POSTPROCESSOR
Definition variables.h:833
char _io_context_buffer[PETSC_MAX_PATH_LEN]
Definition variables.h:884
PetscInt les
Active LES closure; an LESModelType value.
Definition variables.h:984
ExecutionMode exec_mode
Definition variables.h:878
PetscBool restartHistoryAvailable
Definition variables.h:888
char restart_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:880
Here is the call graph for this function:
Here is the caller graph for this function:

◆ RestoreFieldStatisticsState()

PetscErrorCode RestoreFieldStatisticsState ( SimCtx *  simCtx,
PetscInt  ti 
)

Restores field-statistics window state and accumulators from a checkpoint.

Resumes every configured window from the bundle at ti when continuation was requested, per 7. Checkpoints, Restart, and Continuation. Window scalars come from the bundle manifest and accumulator payloads through the same natural-ordering vector reader Eulerian fields use, so a restart on a different MPI rank count restores identical state.

Does nothing when statistics are disabled, no window is configured, or continuation was not requested. Anything else is fatal rather than silently zeroed: a window count mismatch, a renamed window, a changed definition hash, a requested end earlier than the represented span, or missing state.

Parameters
[in,out]simCtxSimulation context carrying the windows and accumulators.
[in]tiStep whose committed bundle supplies the state.
Returns
Zero on success, or a PETSc error when continuation cannot be honored.

Restores field-statistics window state and accumulators from a checkpoint.

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

See also
RestoreFieldStatisticsState()

Definition at line 1686 of file io.c.

1687{
1688 UserCtx *user = NULL;
1689 PetscOptions options = NULL;
1690 char checkpoint_directory[PETSC_MAX_PATH_LEN];
1691 char metadata_path[PETSC_MAX_PATH_LEN];
1692 const char *source_path = NULL;
1693 PetscInt saved_window_count = 0;
1694 PetscReal checkpoint_time = 0.0;
1695 PetscBool found = PETSC_FALSE;
1696
1697 PetscFunctionBeginUser;
1699 PetscCheck(simCtx != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx cannot be NULL.");
1700 if (!FieldStatisticsIsActive(simCtx)) { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
1701 if (!simCtx->fieldStatisticsContinue) {
1703 "Statistics continuation was not requested; %d window(s) start from zero.\n",
1706 PetscFunctionReturn(0);
1707 }
1708
1709 user = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
1710 PetscCheck(user != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
1711 "Finest-level fields must exist before statistics state is restored.");
1713 : simCtx->restart_dir;
1714 PetscCheck(source_path != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
1715 "No checkpoint source directory is set for the current execution mode.");
1716 PetscCall(ResolveCheckpointStepDirectory(source_path, ti,
1717 checkpoint_directory, sizeof(checkpoint_directory)));
1718 PetscCall(ValidateCheckpointBundle(simCtx, user, checkpoint_directory, ti,
1719 &checkpoint_time, NULL, NULL, NULL));
1720 PetscCall(PetscSNPrintf(metadata_path, sizeof(metadata_path), "%s/checkpoint.meta",
1721 checkpoint_directory));
1722
1723 PetscCall(PetscOptionsCreate(&options));
1724 PetscCall(PetscOptionsInsertFile(PETSC_COMM_WORLD, options, metadata_path, PETSC_TRUE));
1725 PetscCall(PetscOptionsGetInt(options, NULL, "-checkpoint_statistics_window_count",
1726 &saved_window_count, &found));
1727 /* Missing state for a requested continuation is fatal and never silently
1728 * zeroed: resuming from zero would report a converged average built from a
1729 * fraction of the samples its metadata claims. */
1730 PetscCheck(found && saved_window_count == simCtx->fieldStatisticsWindowCount,
1731 PETSC_COMM_WORLD, PETSC_ERR_ARG_INCOMP,
1732 "Statistics continuation requested, but checkpoint '%s' holds %" PetscInt_FMT
1733 " window(s) and this run configures %" PetscInt_FMT ".",
1734 checkpoint_directory, found ? saved_window_count : 0,
1735 simCtx->fieldStatisticsWindowCount);
1736
1737 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
1738 PetscCall(ReadStatisticsWindowState(options, window, metadata_path, checkpoint_time,
1739 simCtx->dt, simCtx->exec_mode,
1740 &simCtx->fieldStatisticsWindows[window]));
1741 }
1742 PetscCall(PetscOptionsDestroy(&options));
1743
1744 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
1745 PetscCheck(user[block].fieldStatisticsStorage != NULL, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
1746 "Statistics accumulators were not allocated for block %" PetscInt_FMT ".", block);
1747 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
1748 const PicurvWindowDefinition *definition = &simCtx->fieldStatisticsWindows[window].definition;
1749 const PicurvWindowStorage *storage = &user[block].fieldStatisticsStorage[window];
1750 PetscInt payload_count = 0;
1751
1752 PetscCall(FormatStatisticsPath(checkpoint_directory, window, block, NULL,
1753 simCtx->_io_context_buffer,
1754 sizeof(simCtx->_io_context_buffer)));
1755 simCtx->current_io_directory = simCtx->_io_context_buffer;
1756 PetscCall(PicurvWindowStoragePayloadCount(storage, &payload_count));
1757 for (PetscInt index = 0; index < payload_count; ++index) {
1759
1760 PetscCall(PicurvWindowStoragePayload(&user[block], definition, storage, index, &payload));
1761 PetscCall(ReadFieldData(&user[block], payload.name, payload.vec, "dat"));
1762 }
1763 }
1764 simCtx->current_io_directory = NULL;
1765 }
1766
1767 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
1768 const PicurvWindow *state = &simCtx->fieldStatisticsWindows[window];
1769
1771 "Statistics window '%s' continued from '%s': state %s, %d sample(s), "
1772 "total weight %.6g, represented time %.6g, restart segment %d.\n",
1773 state->definition.name, checkpoint_directory,
1775 (double)state->total_weight, (double)state->represented_time,
1776 state->restart_count);
1777 }
1779 PetscFunctionReturn(0);
1780}
static PetscErrorCode ReadStatisticsWindowState(PetscOptions options, PetscInt window, const char *metadata_path, PetscReal checkpoint_time, PetscReal step_size, ExecutionMode exec_mode, PicurvWindow *state)
Restore one window's scalar bookkeeping from a validated manifest.
Definition io.c:1552
Vec vec
Borrowed accumulator vector; never owned by the caller.
PetscErrorCode PicurvWindowStoragePayload(UserCtx *user, const PicurvWindowDefinition *definition, const PicurvWindowStorage *storage, PetscInt index, PicurvStatisticsPayload *payload)
Resolves one enumerated payload of a window's storage.
char name[96]
File basename, no extension.
PetscErrorCode PicurvWindowStoragePayloadCount(const PicurvWindowStorage *storage, PetscInt *count)
Reports how many checkpointable vectors one window's storage holds.
One checkpointable accumulator vector, resolved by enumeration index.
Independent accumulator state for one window on one block.
PetscInt sample_count
PicurvWindowState state
PetscInt restart_count
Restart segments this state descends from.
PetscReal total_weight
const char * PicurvWindowStateName(PicurvWindowState state)
Returns a stable human-readable name for a window state.
PicurvWindowDefinition definition
PetscReal represented_time
Physical time the window covers.
Runtime state of one window.
The scientifically immutable definition of one window.
PetscReal dt
Definition variables.h:874
struct PicurvWindow * fieldStatisticsWindows
Definition variables.h:933
struct PicurvWindowStorage * fieldStatisticsStorage
Definition variables.h:1136
PetscBool fieldStatisticsContinue
Definition variables.h:938
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadFieldData()

PetscErrorCode ReadFieldData ( UserCtx *  user,
const char *  field_name,
Vec  field_vec,
const char *  ext 
)

Reads data for a specific field from a file into the provided vector.

This function uses the field name to construct the file path and reads the data from the corresponding file into the provided PETSc vector.

Parameters
[in]userPointer to the UserCtx structure containing simulation context.
[in]field_nameName of the field (e.g., "ufield", "vfield", "pfield").
[out]field_vecPETSc vector to store the field data.
[in]extFile extension (e.g., "dat").
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Reads data for a specific field from a file into the provided vector.

Local to this translation unit.

Definition at line 1182 of file io.c.

1186{
1187 PetscErrorCode ierr;
1188 char filename[PETSC_MAX_PATH_LEN];
1189 MPI_Comm comm;
1190 PetscMPIInt rank,size;
1191 SimCtx *simCtx = user->simCtx;
1192
1193
1194 PetscFunctionBeginUser;
1196
1197 if(!simCtx->current_io_directory){
1198 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE, "I/O context directory was not set before calling ReadFieldData().");
1199 }
1200
1201
1202 ierr = PetscObjectGetComm((PetscObject)field_vec,&comm);CHKERRQ(ierr);
1203 ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
1204 ierr = MPI_Comm_size(comm,&size);CHKERRQ(ierr);
1205
1206 const char *source_path = NULL;
1208
1209 if(!source_path){
1210 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE, "source_path was not set for the current execution mode.");
1211 }
1212 ierr = PetscSNPrintf(filename, sizeof(filename), "%s/%s.%s",
1213 source_path, field_name, ext); CHKERRQ(ierr);
1214
1216 "Attempting to read <%s> on rank %d/%d\n",
1217 filename,(int)rank,(int)size);
1218
1219 /* ======================================================================
1220 * 1. SERIAL JOB – just hand the Vec to VecLoad()
1221 * ==================================================================== */
1222 if(size==1)
1223 {
1224 PetscViewer viewer;
1225 PetscBool found;
1226 Vec temp_vec;
1227 PetscInt expectedSize,loadedSize;
1228
1229 ierr = PetscTestFile(filename,'r',&found);CHKERRQ(ierr);
1230 if(!found) SETERRQ(comm,PETSC_ERR_FILE_OPEN,
1231 "Restart/Source file not found: %s",filename);
1232
1233 ierr = PetscViewerBinaryOpen(PETSC_COMM_SELF,filename,FILE_MODE_READ,&viewer);CHKERRQ(ierr);
1234// ---- START MODIFICATION ----
1235 // DO NOT load directly into field_vec, as this can resize it, which is
1236 // illegal for DMSwarm "view" vectors. Instead, load into a temporary vector.
1237 ierr = VecCreate(PETSC_COMM_SELF, &temp_vec); CHKERRQ(ierr);
1238 ierr = VecLoad(temp_vec,viewer);CHKERRQ(ierr);
1239 ierr = PetscViewerDestroy(&viewer);CHKERRQ(ierr);
1240
1241 // Sanity check: ensure the file size matches the expected vector size.
1242 ierr = VecGetSize(field_vec, &expectedSize);CHKERRQ(ierr);
1243 ierr = VecGetSize(temp_vec, &loadedSize);CHKERRQ(ierr);
1244 if (loadedSize != expectedSize) {
1245 SETERRQ(comm,PETSC_ERR_FILE_UNEXPECTED,
1246 "File %s holds %d entries – expected %d for field '%s'",
1247 filename, loadedSize, expectedSize, field_name);
1248 }
1249
1250 // Now, safely copy the data from the temporary vector to the final destination.
1251 ierr = VecCopy(temp_vec, field_vec);CHKERRQ(ierr);
1252
1253 // Clean up the temporary vector.
1254 ierr = VecDestroy(&temp_vec);CHKERRQ(ierr);
1255
1256 // ---- END MODIFICATION ----
1257
1258 /* create EMPTY sequential Vec – VecLoad() will size it correctly */
1259 /*
1260 ierr = VecCreate(PETSC_COMM_SELF,&seq_vec);CHKERRQ(ierr);
1261 ierr = VecSetType(seq_vec,VECSEQ);CHKERRQ(ierr);
1262
1263 ierr = PetscViewerBinaryOpen(PETSC_COMM_SELF,filename,
1264 FILE_MODE_READ,&viewer);CHKERRQ(ierr);
1265
1266 ierr = VecLoad(field_vec,viewer);CHKERRQ(ierr);
1267 ierr = PetscViewerDestroy(&viewer);CHKERRQ(ierr);
1268 */
1270 "Loaded <%s> (serial path)\n",filename);
1271
1273 PetscFunctionReturn(0);
1274 }
1275
1276 /* ======================================================================
1277 * 2. PARALLEL JOB
1278 * ==================================================================== */
1279 PetscInt globalSize;
1280 ierr = VecGetSize(field_vec,&globalSize);CHKERRQ(ierr);
1281
1282 DM dm = NULL;
1283 const char *dmtype = NULL;
1284 Vec nat = NULL; /* Natural-ordered vector for DMDA */
1285
1286 /* -------------------- rank-0 : read the sequential file -------------- */
1287 Vec seq_vec = NULL; /* only valid on rank-0 */
1288 const PetscScalar *seqArray = NULL; /* borrowed pointer on rank-0 only */
1289
1290 if(rank==0)
1291 {
1292 PetscViewer viewer;
1293 PetscBool found;
1294
1295 ierr = PetscTestFile(filename,'r',&found);CHKERRQ(ierr);
1296 if(!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,
1297 "Restart file not found: %s",filename);
1298
1299 /* create EMPTY sequential Vec – VecLoad() will size it correctly */
1300 ierr = VecCreate(PETSC_COMM_SELF,&seq_vec);CHKERRQ(ierr);
1301 ierr = VecSetType(seq_vec,VECSEQ);CHKERRQ(ierr);
1302
1303 ierr = PetscViewerBinaryOpen(PETSC_COMM_SELF,filename,
1304 FILE_MODE_READ,&viewer);CHKERRQ(ierr);
1305 ierr = VecLoad(seq_vec,viewer);CHKERRQ(ierr);
1306 ierr = PetscViewerDestroy(&viewer);CHKERRQ(ierr);
1307
1308 /* size sanity-check */
1309 PetscInt loaded;
1310 ierr = VecGetSize(seq_vec,&loaded);CHKERRQ(ierr);
1311 if(loaded != globalSize)
1312 SETERRQ(comm,PETSC_ERR_FILE_UNEXPECTED,
1313 "File %s holds %d entries – expected %d",
1314 filename,loaded,globalSize);
1315
1316 /* borrow array for later Bcast */
1317 ierr = VecGetArrayRead(seq_vec,&seqArray);CHKERRQ(ierr);
1318
1320 "Rank 0 successfully loaded <%s>\n",filename);
1321 }
1322
1323 /* -------------------- Check if this is a DMDA vector ----------------- */
1324 ierr = VecGetDM(field_vec, &dm); CHKERRQ(ierr);
1325 if (dm) { ierr = DMGetType(dm, &dmtype); CHKERRQ(ierr); }
1326
1327 if (dmtype && !strcmp(dmtype, DMDA)) {
1328 /* ==================================================================
1329 * DMDA PATH: File is in natural ordering, need to convert to global
1330 * ================================================================== */
1331
1332 /* Create natural vector */
1333 ierr = DMDACreateNaturalVector(dm, &nat); CHKERRQ(ierr);
1334
1335 /* Scatter from rank 0's seq_vec to all ranks' natural vector */
1336 VecScatter scatter;
1337 Vec nat_seq = NULL; /* Sequential natural vector on rank 0 */
1338
1339 ierr = VecScatterCreateToZero(nat, &scatter, &nat_seq); CHKERRQ(ierr);
1340
1341 /* Reverse scatter: from rank 0 to all ranks */
1342 ierr = VecScatterBegin(scatter, (rank == 0 ? seq_vec : nat_seq), nat,
1343 INSERT_VALUES, SCATTER_REVERSE); CHKERRQ(ierr);
1344 ierr = VecScatterEnd(scatter, (rank == 0 ? seq_vec : nat_seq), nat,
1345 INSERT_VALUES, SCATTER_REVERSE); CHKERRQ(ierr);
1346
1347 /* Convert natural → global ordering */
1348 ierr = DMDANaturalToGlobalBegin(dm, nat, INSERT_VALUES, field_vec); CHKERRQ(ierr);
1349 ierr = DMDANaturalToGlobalEnd(dm, nat, INSERT_VALUES, field_vec); CHKERRQ(ierr);
1350
1351 /* Cleanup */
1352 ierr = VecScatterDestroy(&scatter); CHKERRQ(ierr);
1353 ierr = VecDestroy(&nat_seq); CHKERRQ(ierr);
1354 ierr = VecDestroy(&nat); CHKERRQ(ierr);
1355
1356 } else {
1357 /* ==================================================================
1358 * NON-DMDA PATH: Use broadcast and direct copy (assumes global ordering)
1359 * ================================================================== */
1360
1361 PetscScalar *buffer = NULL;
1362 if (rank == 0) {
1363 buffer = (PetscScalar *)seqArray;
1364 } else {
1365 ierr = PetscMalloc1(globalSize, &buffer); CHKERRQ(ierr);
1366 }
1367
1368 ierr = MPI_Bcast(buffer, (int)globalSize, MPIU_SCALAR, 0, comm); CHKERRQ(ierr);
1369
1370 /* Copy slice based on ownership range */
1371 PetscInt rstart, rend, loc;
1372 PetscScalar *locArray;
1373
1374 ierr = VecGetOwnershipRange(field_vec, &rstart, &rend); CHKERRQ(ierr);
1375 loc = rend - rstart;
1376
1377 ierr = VecGetArray(field_vec, &locArray); CHKERRQ(ierr);
1378 ierr = PetscMemcpy(locArray, buffer + rstart, loc * sizeof(PetscScalar)); CHKERRQ(ierr);
1379 ierr = VecRestoreArray(field_vec, &locArray); CHKERRQ(ierr);
1380
1381 if (rank != 0) {
1382 ierr = PetscFree(buffer); CHKERRQ(ierr);
1383 }
1384 }
1385
1386 /* -------------------- tidy up ---------------------------------------- */
1387 if (rank == 0) {
1388 ierr = VecRestoreArrayRead(seq_vec, &seqArray); CHKERRQ(ierr);
1389 ierr = VecDestroy(&seq_vec); CHKERRQ(ierr);
1390 }
1391
1393 "Loaded <%s> (parallel path)\n",filename);
1394
1396 PetscFunctionReturn(0);
1397}
@ LOG_TRACE
Very fine-grained tracing information for in-depth debugging.
Definition logging.h:33
Here is the caller graph for this function:

◆ WriteFieldData()

PetscErrorCode WriteFieldData ( UserCtx *  user,
const char *  field_name,
Vec  field_vec,
const char *  ext 
)

Writes data from a specific PETSc vector to a file.

This function uses the field name to construct the file path and writes the data from the provided PETSc vector to the corresponding file.

Parameters
[in]userPointer to the UserCtx structure containing simulation context.
[in]field_nameName of the field (e.g., "ufield", "vfield", "pfield").
[in]field_vecPETSc vector containing the field data to write.
[in]extFile extension (e.g., "dat").
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Writes data from a specific PETSc vector to a file.

Local to this translation unit.

Definition at line 1966 of file io.c.

1970{
1971 MPI_Comm comm;
1972 PetscMPIInt rank;
1973 Vec sequential_vec = NULL;
1974 char filename[PETSC_MAX_PATH_LEN];
1975 SimCtx *simCtx=user->simCtx;
1976
1977 PetscFunctionBeginUser;
1979
1980 if(!simCtx->current_io_directory){
1981 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE, "I/O context directory was not set before calling WriteFieldData().");
1982 }
1983
1984 /* ------------------------------------------------------------ */
1985 /* Basic communicator information */
1986 /* ------------------------------------------------------------ */
1987 PetscCall(PetscObjectGetComm((PetscObject)field_vec,&comm));
1988 PetscCallMPI(MPI_Comm_rank(comm,&rank));
1989
1990 PetscCall(PetscSNPrintf(filename, sizeof(filename), "%s/%s.%s",
1991 simCtx->current_io_directory, field_name, ext));
1992
1993 PetscCall(GatherVectorToRankZero(field_vec, &sequential_vec));
1994 if (rank == 0) {
1995 PetscViewer viewer;
1996 PetscReal vmin, vmax;
1997
1998 PetscCall(VecMin(sequential_vec, NULL, &vmin));
1999 PetscCall(VecMax(sequential_vec, NULL, &vmax));
2001 " <%s> range = [%.4e … %.4e]\n",
2002 field_name,(double)vmin,(double)vmax);
2003 PetscCall(PetscViewerBinaryOpen(PETSC_COMM_SELF, filename, FILE_MODE_WRITE, &viewer));
2004 PetscCall(PetscViewerBinarySetSkipInfo(viewer, PETSC_TRUE));
2005 PetscCall(VecView(sequential_vec, viewer));
2006 PetscCall(PetscViewerDestroy(&viewer));
2007 LOG_ALLOW(GLOBAL, LOG_INFO, "Wrote <%s>\n", filename);
2008 }
2009 PetscCall(VecDestroy(&sequential_vec));
2010
2012 PetscFunctionReturn(0);
2013}
static PetscErrorCode GatherVectorToRankZero(Vec field_vec, Vec *sequential_vec)
Gather a vector in decomposition-independent natural ordering onto rank zero.
Definition io.c:124
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteSimulationFields()

PetscErrorCode WriteSimulationFields ( UserCtx *  user,
const char *  checkpoint_directory 
)

Writes simulation fields to files.

This function writes contravariant velocity, Cartesian velocity, pressure, and node state fields to their respective binary files. It also conditionally writes LES model fields when those models are enabled.

Parameters
[in]userPointer to the UserCtx structure containing simulation context.
[in]checkpoint_directoryRoot of the in-progress checkpoint bundle.
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Writes simulation fields to files.

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

See also
WriteSimulationFields()

Definition at line 2021 of file io.c.

2022{
2023 SimCtx *simCtx = user->simCtx;
2024
2025 PetscFunctionBeginUser;
2026 PetscCheck(checkpoint_directory != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2027 "Checkpoint destination cannot be NULL.");
2028 PetscCall(PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer),
2029 "%s/%s/block_%04" PetscInt_FMT,
2030 checkpoint_directory, PICURV_EULERIAN_DIRECTORY, user->_this));
2031 simCtx->current_io_directory = simCtx->_io_context_buffer;
2032
2033 if (simCtx->les) {
2034 // The constant model holds no coefficient field to stage.
2035 if (simCtx->les == DYNAMIC_SMAGORINSKY) {
2036 PetscCall(CopyOwnedLocalScalarToGlobal(user->da, user->lCs, user->CS));
2037 }
2038 PetscCall(CopyOwnedLocalScalarToGlobal(user->da, user->lNu_t, user->Nu_t));
2039 }
2040 for (PetscInt raw_id = 0; raw_id < FIELD_ID_COUNT; ++raw_id) {
2041 const FieldDescriptor *descriptor = NULL;
2042 FieldView view;
2043
2044 PetscCall(FieldGetDescriptor((FieldId)raw_id, &descriptor));
2045 if (!CheckpointFieldIsEnabled(simCtx, descriptor)) continue;
2046 PetscCall(FieldGetView(user, descriptor->id, &view));
2047 PetscCall(WriteFieldData(user, descriptor->canonical_name, view.global_vec, "dat"));
2048 }
2049 simCtx->current_io_directory = NULL;
2050 PetscFunctionReturn(0);
2051}
static PetscErrorCode CopyOwnedLocalScalarToGlobal(DM dm, Vec local_vec, Vec global_vec)
Copies the owned entries of a ghosted scalar DMDA vector to its global vector.
Definition io.c:51
PetscErrorCode WriteFieldData(UserCtx *user, const char *field_name, Vec field_vec, const char *ext)
Internal helper implementation: WriteFieldData().
Definition io.c:1966
@ DYNAMIC_SMAGORINSKY
Definition variables.h:551
Vec lNu_t
Definition variables.h:1156
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteSwarmField()

PetscErrorCode WriteSwarmField ( UserCtx *  user,
const char *  field_name,
const char *  ext 
)

Writes data from a specific field in a PETSc Swarm to a file.

This function retrieves the Swarm from the UserCtx (i.e., user->swarm) and creates a global PETSc vector from the specified Swarm field. It then calls the existing WriteFieldData() function to handle the actual I/O operation. After writing the data, the function destroys the temporary global vector to avoid memory leaks.

Parameters
[in]userPointer to the UserCtx structure containing simulation context and the PetscSwarm (as user->swarm).
[in]field_nameName of the Swarm field to be written (e.g., "my_field").
[in]extFile extension (e.g., "dat", "bin").
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.
Note
Compatible with PETSc 3.14.4.

Writes data from a specific field in a PETSc Swarm to a file.

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

See also
WriteSwarmField()

Definition at line 2098 of file io.c.

2099{
2100 PetscErrorCode ierr;
2101 Vec fieldVec;
2102 DM swarm;
2103
2104 PetscFunctionBeginUser; /* PETSc macro indicating start of function */
2105
2106 /*
2107 * 1) Retrieve the PetscSwarm from the user context.
2108 * Ensure user->swarm is initialized and not NULL.
2109 */
2110 swarm = user->swarm;
2111
2112 /*
2113 * 2) Create a global vector from the specified swarm field.
2114 * This function is available in PETSc 3.14.4.
2115 * It provides a read/write "view" of the swarm field as a global Vec.
2116 */
2118 "Attempting to create global vector from field: %s\n",
2119 field_name);
2120 ierr = DMSwarmCreateGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
2121
2122 /*
2123 * 3) Use your existing WriteFieldData() to write the global vector to a file.
2124 * The field name, time index, and extension are passed along for naming.
2125 */
2127 "Calling WriteFieldData for field: %s\n",
2128 field_name);
2129 ierr = WriteFieldData(user, field_name, fieldVec, ext);CHKERRQ(ierr);
2130
2131 /*
2132 * 4) Destroy the global vector once the data is successfully written.
2133 * This step is crucial for avoiding memory leaks.
2134 * DMSwarmDestroyGlobalVectorFromField() is also available in PETSc 3.14.4.
2135 */
2137 "Destroying the global vector for field: %s\n",
2138 field_name);
2139 ierr = DMSwarmDestroyGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
2140
2141 /* Log and return success. */
2143 "Successfully wrote swarm data for field: %s\n",
2144 field_name);
2145
2146 PetscFunctionReturn(0); /* PETSc macro indicating end of function */
2147}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteSwarmIntField()

PetscErrorCode WriteSwarmIntField ( UserCtx *  user,
const char *  field_name,
const char *  ext 
)

Writes integer data from a specific PETSc Swarm field to a file.

This function is designed for swarm fields that store integer data (e.g., DMSwarm_CellID), which cannot be converted to a standard PETSc Vec of PetscScalars. It accesses the raw data pointer for the field on each rank using DMSwarmGetField(), writes the local data to a rank-specific binary file, and then restores the field access.

Parameters
[in]userPointer to the UserCtx structure containing the PetscSwarm.
[in]field_nameName of the integer Swarm field to be written.
[in]extFile extension (e.g., "dat", "bin").
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Writes integer data from a specific PETSc Swarm field to a file.

Local to this translation unit.

Definition at line 2153 of file io.c.

2154{
2155 PetscErrorCode ierr;
2156 DM swarm = user->swarm;
2157 Vec temp_vec; // Temporary Vec to hold casted data
2158 PetscInt nlocal, nglobal,bs,i;
2159 PetscDataType field_type;
2160 void *field_array_void;
2161 PetscScalar *scalar_array; // Pointer to the temporary Vec's scalar data
2162
2163 PetscFunctionBeginUser;
2164
2165 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Casting '%s' to Vec for writing.\n", field_name);
2166
2167 // Get the swarm field properties
2168 ierr = DMSwarmGetLocalSize(swarm, &nlocal); CHKERRQ(ierr);
2169 ierr = DMSwarmGetSize(swarm, &nglobal); CHKERRQ(ierr);
2170 ierr = DMSwarmGetField(swarm, field_name, &bs, &field_type, &field_array_void); CHKERRQ(ierr);
2171 PetscCheck(field_type == PETSC_INT || field_type == PETSC_INT64,
2172 PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
2173 "Swarm field '%s' must use PETSC_INT or PETSC_INT64 for integer output, not %s.",
2174 field_name, PetscDataTypes[field_type]);
2175
2176 // Create Temporary parallel Vec wit the CORRECT layout
2177 ierr = VecCreate(PETSC_COMM_WORLD, &temp_vec); CHKERRQ(ierr);
2178 ierr = VecSetType(temp_vec, VECMPI); CHKERRQ(ierr);
2179 ierr = VecSetSizes(temp_vec, nlocal*bs, nglobal*bs); CHKERRQ(ierr);
2180 ierr = VecSetUp(temp_vec); CHKERRQ(ierr);
2181
2182 // Defining Vector field to mandatory field 'position'
2183 DMSwarmVectorDefineField(swarm,ParticleFieldName(PARTICLE_FIELD_ID_POSITION));
2184
2185 ierr = VecGetArray(temp_vec, &scalar_array); CHKERRQ(ierr);
2186
2187 if (field_type == PETSC_INT64) {
2188 PetscInt64 *int64_array = (PetscInt64 *)field_array_void;
2189 // Perform the cast from PetscInt64 to PetscScalar
2190 for (i = 0; i < nlocal*bs; i++) {
2191 scalar_array[i] = (PetscScalar)int64_array[i];
2192 }
2193 }else{
2194 PetscInt *int_array = (PetscInt *)field_array_void;
2195 //Perform the cast from PetscInt to PetscScalar
2196 for (i = 0; i < nlocal*bs; i++) {
2197 scalar_array[i] = (PetscScalar)int_array[i];
2198 }
2199 }
2200
2201 // Restore access to both arrays
2202 ierr = VecRestoreArray(temp_vec, &scalar_array); CHKERRQ(ierr);
2203 ierr = DMSwarmRestoreField(swarm, field_name, &bs, NULL, &field_array_void); CHKERRQ(ierr);
2204
2205 // Call your existing writer with the temporary, populated Vec
2206 ierr = WriteFieldData(user, field_name, temp_vec, ext); CHKERRQ(ierr);
2207
2208 // Clean up
2209 ierr = VecDestroy(&temp_vec); CHKERRQ(ierr);
2210
2211 PetscFunctionReturn(0);
2212}
const char * ParticleFieldName(ParticleFieldId field_id)
Return the canonical PETSc DMSwarm name for an ID.
@ PARTICLE_FIELD_ID_POSITION
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteAllSwarmFields()

PetscErrorCode WriteAllSwarmFields ( UserCtx *  user,
const char *  checkpoint_directory 
)

Writes a predefined set of PETSc Swarm fields to files.

This function iterates through checkpoint-capable particle catalog entries and delegates each payload to the existing generic swarm writer.

This function will only execute if particles are enabled in the simulation (i.e., user->simCtx->np > 0 and user->swarm is not NULL).

Parameters
[in]userPointer to the UserCtx structure containing the simulation context and the PetscSwarm.
[in]checkpoint_directoryRoot of the in-progress checkpoint bundle.
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Writes a predefined set of PETSc Swarm fields to files.

Local to this translation unit.

Definition at line 2218 of file io.c.

2219{
2220 SimCtx *simCtx = user->simCtx;
2221
2222 PetscFunctionBeginUser;
2223
2224 // If no swarm is configured or there are no particles, do nothing and return.
2225 if (!user->swarm || simCtx->np <= 0) {
2226 PetscFunctionReturn(0);
2227 }
2228
2229 PetscCheck(checkpoint_directory != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2230 "Checkpoint destination cannot be NULL.");
2231 PetscCall(PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer),
2232 "%s/%s", checkpoint_directory, PICURV_PARTICLE_DIRECTORY));
2233 simCtx->current_io_directory = simCtx->_io_context_buffer;
2234
2235 for (PetscInt raw_id = 0; raw_id < PARTICLE_FIELD_ID_COUNT; ++raw_id) {
2236 const ParticleFieldDescriptor *descriptor = NULL;
2237
2238 PetscCall(ParticleFieldGetDescriptor((ParticleFieldId)raw_id, &descriptor));
2239 if (!(descriptor->capabilities & PARTICLE_FIELD_CAPABILITY_CHECKPOINT)) continue;
2240 if (descriptor->data_type == PETSC_INT || descriptor->data_type == PETSC_INT64) {
2241 PetscCall(WriteSwarmIntField(user, descriptor->canonical_name, "dat"));
2242 } else {
2243 PetscCall(WriteSwarmField(user, descriptor->canonical_name, "dat"));
2244 }
2245 }
2246
2247 simCtx->current_io_directory = NULL;
2248
2249 PetscFunctionReturn(0);
2250}
PetscErrorCode WriteSwarmIntField(UserCtx *user, const char *field_name, const char *ext)
Internal helper implementation: WriteSwarmIntField().
Definition io.c:2153
PetscErrorCode WriteSwarmField(UserCtx *user, const char *field_name, const char *ext)
Implementation of WriteSwarmField().
Definition io.c:2098
ParticleFieldId
Compile-time identity for a persistent solver-particle field.
@ PARTICLE_FIELD_ID_COUNT
@ PARTICLE_FIELD_CAPABILITY_CHECKPOINT
PetscErrorCode ParticleFieldGetDescriptor(ParticleFieldId field_id, const ParticleFieldDescriptor **descriptor)
Return immutable metadata for a valid particle field ID.
Immutable metadata for one persistent particle field.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadDataFileToArray()

PetscInt ReadDataFileToArray ( const char *  filename,
double **  data_out,
PetscInt *  Nout,
MPI_Comm  comm 
)

Reads a simple ASCII data file containing one numeric value per line.

This helper performs rank-0 file I/O, broadcasts the parsed result to the rest of the communicator, and returns a replicated array on every rank.

Parameters
filenamePath to the input data file.
data_outOutput pointer to the allocated scalar array.
NoutOutput pointer storing the number of values read.
commMPI communicator used for the coordinated read/broadcast sequence.
Returns
Integer value produced by ReadDataFileToArray().

Reads a simple ASCII data file containing one numeric value per line.

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

See also
ReadDataFileToArray()

Definition at line 3469 of file io.c.

3473{
3474 /* STEP 0: Prepare local variables & log function entry */
3475 PetscMPIInt rank, size;
3476 PetscErrorCode ierr;
3477 FILE *fp = NULL;
3478 PetscInt N = 0; /* number of lines/values read on rank 0 */
3479 double *array = NULL; /* pointer to local array on each rank */
3480 PetscInt fileExistsFlag = 0; /* 0 = doesn't exist, 1 = does exist */
3481
3483 "Start reading from file: %s\n",
3484 filename);
3485
3486 /* Basic error checking: data_out, Nout must be non-null. */
3487 if (!filename || !data_out || !Nout) {
3489 "Null pointer argument provided.\n");
3490 return 1;
3491 }
3492
3493 /* Determine rank/size for coordinating I/O. */
3494 MPI_Comm_rank(comm, &rank);
3495 MPI_Comm_size(comm, &size);
3496
3497 /* STEP 1: On rank 0, check if file can be opened. */
3498 if (!rank) {
3499 fp = fopen(filename, "r");
3500 if (fp) {
3501 fileExistsFlag = 1;
3502 fclose(fp);
3503 }
3504 }
3505
3506 /* STEP 2: Broadcast file existence to all ranks. */
3507 // In ReadDataFileToArray:
3508 ierr = MPI_Bcast(&fileExistsFlag, 1, MPI_INT, 0, comm); CHKERRQ(ierr);
3509
3510 if (!fileExistsFlag) {
3511 /* If file does not exist, log & return. */
3512 if (!rank) {
3514 "File '%s' not found.\n",
3515 filename);
3516 }
3517 return 2;
3518 }
3519
3520 /* STEP 3: Rank 0 re-opens and reads the file, counting lines, etc. */
3521 if (!rank) {
3522 fp = fopen(filename, "r");
3523 if (!fp) {
3525 "File '%s' could not be opened for reading.\n",
3526 filename);
3527 return 3;
3528 }
3529
3530 /* (3a) Count lines first. */
3531 {
3532 char line[256];
3533 while (fgets(line, sizeof(line), fp)) {
3534 N++;
3535 }
3536 }
3537
3539 "File '%s' has %d lines.\n",
3540 filename, N);
3541
3542 /* (3b) Allocate array on rank 0. */
3543 array = (double*)malloc(N * sizeof(double));
3544 if (!array) {
3545 fclose(fp);
3547 "malloc failed for array.\n");
3548 return 4;
3549 }
3550
3551 /* (3c) Rewind & read values into array. */
3552 rewind(fp);
3553 {
3554 PetscInt i = 0;
3555 char line[256];
3556 while (fgets(line, sizeof(line), fp)) {
3557 double val;
3558 if (sscanf(line, "%lf", &val) == 1) {
3559 array[i++] = val;
3560 }
3561 }
3562 }
3563 fclose(fp);
3564
3566 "Successfully read %d values from '%s'.\n",
3567 N, filename);
3568 }
3569
3570 /* STEP 4: Broadcast the integer N to all ranks. */
3571 ierr = MPI_Bcast(&N, 1, MPI_INT, 0, comm); CHKERRQ(ierr);
3572
3573 /* STEP 5: Each rank allocates an array to receive the broadcast if rank>0. */
3574 if (rank) {
3575 array = (double*)malloc(N * sizeof(double));
3576 if (!array) {
3578 "malloc failed on rank %d.\n",
3579 rank);
3580 return 5;
3581 }
3582 }
3583
3584 /* STEP 6: Broadcast the actual data from rank 0 to all. */
3585 ierr = MPI_Bcast(array, N, MPI_DOUBLE, 0, comm); CHKERRQ(ierr);
3586
3587 /* STEP 7: Assign outputs on all ranks. */
3588 *data_out = array;
3589 *Nout = N;
3590
3592 "Done. Provided array of length=%d to all ranks.\n",
3593 N);
3594 return 0; /* success */
3595}

◆ CreateVTKFileFromMetadata()

PetscInt CreateVTKFileFromMetadata ( const char *  filename,
const VTKMetaData *  meta,
MPI_Comm  comm 
)

Creates a VTK file from prepared metadata and field payloads.

This helper dispatches to the structured-grid or polydata writer based on the metadata contents and emits the assembled VTK file on the requested communicator.

Parameters
filenamePath to the output VTK file.
metaVTK metadata describing the output geometry and field payloads.
commMPI communicator used by the write operation.
Returns
Integer value produced by CreateVTKFileFromMetadata().

Creates a VTK file from prepared metadata and field payloads.

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

See also
CreateVTKFileFromMetadata()

Definition at line 149 of file vtk_io.c.

150{
151 PetscMPIInt rank;
152 MPI_Comm_rank(comm, &rank);
153 PetscErrorCode ierr = 0;
154
155 if (!rank) {
156 char tmp_filename[PETSC_MAX_PATH_LEN];
157 FILE *fp = NULL;
158
159 ierr = PetscSNPrintf(tmp_filename, sizeof(tmp_filename), "%s.tmp", filename);CHKERRQ(ierr);
160 LOG_ALLOW(GLOBAL, LOG_INFO, "Rank 0 writing combined VTK file '%s'.\n", filename);
161 fp = fopen(tmp_filename, "wb");
162 if (!fp) {
163 LOG_ALLOW(GLOBAL, LOG_ERROR, "fopen failed for %s.\n", tmp_filename);
164 return PETSC_ERR_FILE_OPEN;
165 }
166
167 PetscInt boffset = 0;
168
169 ierr = WriteVTKFileHeader(fp, meta, &boffset);
170 if (ierr) {
171 fclose(fp);
172 remove(tmp_filename);
173 return ierr;
174 }
175
176 if (meta->coords) {
177 ierr = WriteVTKAppendedBlock(fp, meta->coords, 3 * meta->npoints, sizeof(PetscScalar));
178 if (ierr) {
179 fclose(fp);
180 remove(tmp_filename);
181 return ierr;
182 }
183 }
184
185 for (PetscInt i = 0; i < meta->num_point_data_fields; i++) {
186 const VTKFieldInfo *field = &meta->point_data_fields[i];
187 if (field->data) {
188 ierr = WriteVTKAppendedBlock(fp, field->data, field->num_components * meta->npoints, sizeof(PetscScalar));
189 if (ierr) {
190 fclose(fp);
191 remove(tmp_filename);
192 return ierr;
193 }
194 }
195 }
196 if (meta->fileType == VTK_POLYDATA) {
197 if (meta->connectivity) {
198 ierr = WriteVTKAppendedBlock(fp, meta->connectivity, meta->npoints, sizeof(PetscInt));
199 if (ierr) {
200 fclose(fp);
201 remove(tmp_filename);
202 return ierr;
203 }
204 }
205 if (meta->offsets) {
206 ierr = WriteVTKAppendedBlock(fp, meta->offsets, meta->npoints, sizeof(PetscInt));
207 if (ierr) {
208 fclose(fp);
209 remove(tmp_filename);
210 return ierr;
211 }
212 }
213 }
214 ierr = WriteVTKFileFooter(fp, meta);
215 if (ierr) {
216 fclose(fp);
217 remove(tmp_filename);
218 return ierr;
219 }
220 if (fclose(fp) != 0) {
221 remove(tmp_filename);
222 return PETSC_ERR_FILE_WRITE;
223 }
224 if (rename(tmp_filename, filename) != 0) {
225 remove(tmp_filename);
226 return PETSC_ERR_FILE_WRITE;
227 }
228 LOG_ALLOW(GLOBAL, LOG_INFO, "Rank 0 finished writing VTK file '%s'.\n", filename);
229 }
230 return 0;
231}
PetscInt npoints
Definition variables.h:820
PetscInt num_components
Definition variables.h:807
PetscInt num_point_data_fields
Definition variables.h:823
PetscInt * connectivity
Definition variables.h:824
PetscInt * offsets
Definition variables.h:825
VTKFileType fileType
Definition variables.h:818
PetscScalar * data
Definition variables.h:808
PetscScalar * coords
Definition variables.h:821
VTKFieldInfo point_data_fields[20]
Definition variables.h:822
@ VTK_POLYDATA
Definition variables.h:814
Stores all necessary information for a single data array in a VTK file.
Definition variables.h:805
static PetscErrorCode WriteVTKFileFooter(FILE *fp, const VTKMetaData *meta)
Close a VTK XML document after all appended data have been written.
Definition vtk_io.c:132
static PetscErrorCode WriteVTKAppendedBlock(FILE *fp, const void *data, PetscInt num_elements, size_t element_size)
Write one binary data block in VTK appended-data format.
Definition vtk_io.c:21
static PetscErrorCode WriteVTKFileHeader(FILE *fp, const VTKMetaData *meta, PetscInt *boffset)
Open a VTK XML document and write its file-level header.
Definition vtk_io.c:119
Here is the call graph for this function:
Here is the caller graph for this function:

◆ VecToArrayOnRank0()

PetscErrorCode VecToArrayOnRank0 ( Vec  inVec,
PetscInt *  N,
double **  arrayOut 
)

Gathers the contents of a distributed PETSc Vec into a single array on rank 0.

Parameters
[in]inVecThe input (possibly distributed) Vec.
[out]NThe global size of the vector.
[out]arrayOutOn rank 0, points to the newly allocated array holding all data. On other ranks, it is set to NULL.
Returns
PetscErrorCode Return 0 on success, nonzero on failure.

Gathers the contents of a distributed PETSc Vec into a single array on rank 0.

Local to this translation unit.

Definition at line 2674 of file io.c.

2675{
2676 MPI_Comm comm;
2677 PetscMPIInt rank;
2678 Vec sequential_vec = NULL;
2679
2680 PetscFunctionBeginUser;
2681 PetscCheck(inVec != NULL && N != NULL && arrayOut != NULL,
2682 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2683 "Vector, size output, and array output are required.");
2684 PetscCall(PetscObjectGetComm((PetscObject)inVec, &comm));
2685 PetscCallMPI(MPI_Comm_rank(comm, &rank));
2686 PetscCall(VecGetSize(inVec, N));
2687 *arrayOut = NULL;
2688
2689 PetscCall(GatherVectorToRankZero(inVec, &sequential_vec));
2690 if (rank == 0) {
2691 const PetscScalar *values = NULL;
2692 PetscInt local_size = 0;
2693
2694 PetscCall(VecGetLocalSize(sequential_vec, &local_size));
2695 PetscCall(PetscMalloc1(local_size, arrayOut));
2696 PetscCall(VecGetArrayRead(sequential_vec, &values));
2697 for (PetscInt index = 0; index < local_size; ++index) {
2698 (*arrayOut)[index] = (double)PetscRealPart(values[index]);
2699 }
2700 PetscCall(VecRestoreArrayRead(sequential_vec, &values));
2701 }
2702 PetscCall(VecDestroy(&sequential_vec));
2703 PetscFunctionReturn(0);
2704}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SwarmFieldToArrayOnRank0()

PetscErrorCode SwarmFieldToArrayOnRank0 ( DM  swarm,
const char *  field_name,
PetscInt *  n_total_particles,
PetscInt *  n_components,
PetscDataType *  field_type_out,
void **  gathered_array 
)

Gathers any DMSwarm field from all ranks to a single, contiguous array on rank 0.

This is a generic, type-aware version of SwarmFieldToArrayOnRank0. It is a COLLECTIVE operation.

Parameters
[in]swarmThe DMSwarm to gather from.
[in]field_nameThe name of the field to gather.
[out]n_total_particlesTotal number of particles in the global swarm on every rank.
[out]n_componentsNumber of components for the field on every rank.
[out]field_type_outPETSc storage type for the field on all ranks.
[out]gathered_arrayOn rank 0, a newly allocated array containing the full, gathered data; NULL elsewhere. The caller is responsible for freeing this memory and for casting it to the correct type.
Returns
PetscErrorCode

Gathers any DMSwarm field from all ranks to a single, contiguous array on rank 0.

Local to this translation unit.

Definition at line 2710 of file io.c.

2713{
2714 PetscErrorCode ierr;
2715 PetscMPIInt rank, size;
2716 PetscInt nlocal, nglobal, bs;
2717 PetscDataType field_type;
2718 void *local_array_void;
2719 size_t element_size = 0;
2720
2721 PetscFunctionBeginUser;
2722
2723 PetscCheck(swarm != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "DMSwarm cannot be NULL.");
2724 PetscCheck(field_name != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Swarm field name cannot be NULL.");
2725 PetscCheck(n_total_particles != NULL && n_components != NULL && field_type_out != NULL && gathered_array != NULL,
2726 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2727 "Swarm gather output pointers cannot be NULL.");
2728
2729 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
2730 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size); CHKERRQ(ierr);
2731
2732 // All ranks get swarm properties to determine send/receive counts
2733 ierr = DMSwarmGetLocalSize(swarm, &nlocal); CHKERRQ(ierr);
2734 ierr = DMSwarmGetSize(swarm, &nglobal); CHKERRQ(ierr);
2735 ierr = DMSwarmGetField(swarm, field_name, &bs, &field_type, &local_array_void); CHKERRQ(ierr);
2736
2737 // Determine the size of one element of the field's data type
2738 if (field_type == PETSC_INT64) element_size = sizeof(PetscInt64);
2739 else if (field_type == PETSC_INT) element_size = sizeof(PetscInt);
2740 else if (field_type == PETSC_REAL) element_size = sizeof(PetscReal);
2741#if defined(PETSC_USE_COMPLEX)
2742 else if (field_type == PETSC_SCALAR) element_size = sizeof(PetscScalar);
2743#endif
2744 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP,
2745 "Swarm field '%s' uses unsupported gathered data type %s.",
2746 field_name, PetscDataTypes[field_type]);
2747
2748 *field_type_out = field_type;
2749 *n_total_particles = nglobal;
2750 *n_components = bs;
2751 *gathered_array = NULL;
2752
2753 if (size == 1) { // Serial case is a simple copy
2754 if (rank == 0) {
2755 ierr = PetscMalloc(nglobal * bs * element_size, gathered_array); CHKERRQ(ierr);
2756 ierr = PetscMemcpy(*gathered_array, local_array_void, nglobal * bs * element_size); CHKERRQ(ierr);
2757 }
2758 } else { // Parallel case: use MPI_Gatherv
2759 PetscInt *recvcounts = NULL, *displs = NULL;
2760 if (rank == 0) {
2761 ierr = PetscMalloc1(size, &recvcounts); CHKERRQ(ierr);
2762 ierr = PetscMalloc1(size, &displs); CHKERRQ(ierr);
2763 }
2764 PetscInt sendcount = nlocal * bs;
2765
2766 // Gather the number of elements (not bytes) from each rank
2767 ierr = MPI_Gather(&sendcount, 1, MPIU_INT, recvcounts, 1, MPIU_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
2768
2769 if (rank == 0) {
2770 displs[0] = 0;
2771 // Convert counts and calculate displacements in terms of BYTES
2772 for (PetscMPIInt i = 0; i < size; i++) recvcounts[i] *= element_size;
2773 for (PetscMPIInt i = 1; i < size; i++) displs[i] = displs[i-1] + recvcounts[i-1];
2774
2775 ierr = PetscMalloc(nglobal * bs * element_size, gathered_array); CHKERRQ(ierr);
2776 }
2777
2778 // Use Gatherv with MPI_BYTE to handle any data type generically
2779 ierr = MPI_Gatherv(local_array_void, nlocal * bs * element_size, MPI_BYTE,
2780 *gathered_array, recvcounts, displs, MPI_BYTE,
2781 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
2782
2783 if (rank == 0) {
2784 ierr = PetscFree(recvcounts); CHKERRQ(ierr);
2785 ierr = PetscFree(displs); CHKERRQ(ierr);
2786 }
2787 }
2788
2789 ierr = DMSwarmRestoreField(swarm, field_name, &bs, NULL, &local_array_void); CHKERRQ(ierr);
2790
2791 PetscFunctionReturn(0);
2792}
Here is the caller graph for this function:

◆ ReadSwarmField()

PetscErrorCode ReadSwarmField ( UserCtx *  user,
const char *  field_name,
const char *  ext 
)

Reads data from a file into a specified field of a PETSc DMSwarm.

This function is the counterpart to WriteSwarmField(). It creates a global PETSc vector that references the specified DMSwarm field, uses ReadFieldData() to read the data from a file, and then destroys the global vector reference.

Parameters
[in]userPointer to the UserCtx structure (containing user->swarm).
[in]field_nameName of the DMSwarm field to read into (must be previously declared/allocated).
[in]extFile extension (e.g., "dat" or "bin").
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.
Note
Compatible with PETSc 3.14.x.

Reads data from a file into a specified field of a PETSc DMSwarm.

Local to this translation unit.

Definition at line 1786 of file io.c.

1787{
1788 PetscErrorCode ierr;
1789 DM swarm;
1790 Vec fieldVec;
1791
1792 PetscFunctionBegin;
1793
1794 swarm = user->swarm;
1795
1796 LOG_ALLOW(GLOBAL,LOG_DEBUG," ReadSwarmField Begins \n");
1797
1798 /* 2) Create a global vector that references the specified Swarm field. */
1799 ierr = DMSwarmCreateGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
1800
1801 LOG_ALLOW(GLOBAL,LOG_DEBUG," Vector created from Field \n");
1802
1803 /* 3) Use the ReadFieldData() function to read data into fieldVec. */
1804 ierr = ReadFieldData(user, field_name, fieldVec, ext);CHKERRQ(ierr);
1805
1806 /* 4) Destroy the global vector reference. */
1807 ierr = DMSwarmDestroyGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
1808
1809 PetscFunctionReturn(0);
1810}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadSwarmIntField()

PetscErrorCode ReadSwarmIntField ( UserCtx *  user,
const char *  field_name,
const char *  ext 
)

Reads integer swarm data by using ReadFieldData and casting the result.

This function is the counterpart to WriteSwarmIntField. It reads a file containing floating-point data (that was originally integer) into a temporary Vec and then casts it back to the integer swarm field. It works by:

  1. Creating a temporary parallel Vec.
  2. Calling the standard ReadFieldData() to populate this Vec.
  3. Accessing the local data of both the Vec and the swarm field.
  4. Populating the swarm's integer field by casting each PetscScalar back to a PetscInt.
  5. Destroying the temporary Vec.
Parameters
[in]userPointer to the UserCtx structure.
[in]field_nameName of the integer Swarm field to be read.
[in]extFile extension.
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Reads integer swarm data by using ReadFieldData and casting the result.

Local to this translation unit.

Definition at line 1816 of file io.c.

1817{
1818 PetscErrorCode ierr;
1819 DM swarm = user->swarm;
1820 Vec temp_vec;
1821 PetscInt nlocal, nglobal, bs, i;
1822 PetscDataType field_type;
1823 const PetscScalar *scalar_array; // Read-only pointer from the temp Vec
1824 void *field_array_void;
1825
1826
1827 PetscFunctionBeginUser;
1828
1829 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Reading '%s' via temporary Vec.\n", field_name);
1830
1831 // Get the properties of the swarm field to determine the expected layout
1832 ierr = DMSwarmGetLocalSize(swarm, &nlocal); CHKERRQ(ierr);
1833 ierr = DMSwarmGetSize(swarm, &nglobal); CHKERRQ(ierr);
1834 // We get the block size but not the data pointer yet
1835 ierr = DMSwarmGetField(swarm, field_name, &bs, &field_type, NULL); CHKERRQ(ierr);
1836 ierr = DMSwarmRestoreField(swarm, field_name, &bs, NULL, NULL); CHKERRQ(ierr);
1837 PetscCheck(field_type == PETSC_INT || field_type == PETSC_INT64,
1838 PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
1839 "Swarm field '%s' must use PETSC_INT or PETSC_INT64 for integer restart input, not %s.",
1840 field_name, PetscDataTypes[field_type]);
1841
1842 // Create a temporary Vec with the CORRECT layout to receive the data
1843 ierr = VecCreate(PETSC_COMM_WORLD, &temp_vec); CHKERRQ(ierr);
1844 ierr = VecSetType(temp_vec, VECMPI); CHKERRQ(ierr);
1845 ierr = VecSetSizes(temp_vec, nlocal * bs, nglobal * bs); CHKERRQ(ierr);
1846 ierr = VecSetBlockSize(temp_vec, bs); CHKERRQ(ierr);
1847 ierr = VecSetUp(temp_vec); CHKERRQ(ierr);
1848
1849 // Call your existing reader to populate the temporary Vec
1850 ierr = ReadFieldData(user, field_name, temp_vec, ext); CHKERRQ(ierr);
1851
1852 // Get local pointers
1853 ierr = VecGetArrayRead(temp_vec, &scalar_array); CHKERRQ(ierr);
1854 ierr = DMSwarmGetField(swarm, field_name, NULL, NULL, &field_array_void); CHKERRQ(ierr);
1855
1856 // Perform the cast back, using the correct loop size (nlocal * bs)
1857 if (field_type == PETSC_INT64) {
1858 PetscInt64 *int64_array = (PetscInt64 *)field_array_void;
1859 for (i = 0; i < nlocal * bs; i++) {
1860 int64_array[i] = (PetscInt64)scalar_array[i];
1861 }
1862 } else {
1863 PetscInt *int_array = (PetscInt *)field_array_void;
1864 for (i = 0; i < nlocal * bs; i++) {
1865 int_array[i] = (PetscInt)scalar_array[i];
1866 }
1867 }
1868
1869 // Restore access
1870 ierr = DMSwarmRestoreField(swarm, field_name, NULL, NULL, &field_array_void); CHKERRQ(ierr);
1871 ierr = VecRestoreArrayRead(temp_vec, &scalar_array); CHKERRQ(ierr);
1872
1873 // 6. Clean up
1874 ierr = VecDestroy(&temp_vec); CHKERRQ(ierr);
1875
1876 PetscFunctionReturn(0);
1877}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadAllSwarmFields()

PetscErrorCode ReadAllSwarmFields ( UserCtx *  user,
PetscInt  ti 
)

Reads multiple fields (positions, velocity, CellID, and weight) into a DMSwarm.

This function is analogous to ReadSimulationFields() but targets a DMSwarm. Each Swarm field is read from a separate file using ReadSwarmField().

Parameters
[in,out]userPointer to the UserCtx structure containing the DMSwarm (user->swarm).
[in]tiTime index for constructing the file name.
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Reads multiple fields (positions, velocity, CellID, and weight) into a DMSwarm.

Local to this translation unit.

Definition at line 1883 of file io.c.

1884{
1885 PetscInt nGlobal;
1886 SimCtx *simCtx = user->simCtx;
1887 const char *source_path = NULL;
1888 char checkpoint_directory[PETSC_MAX_PATH_LEN];
1889
1890 PetscFunctionBeginUser;
1891 PetscCall(DMSwarmGetSize(user->swarm, &nGlobal));
1892 LOG_ALLOW(GLOBAL, LOG_INFO, "Reading DMSwarm fields for timestep %d (swarm size is %d).\n", ti, nGlobal);
1893
1894 if (nGlobal == 0) {
1895 LOG_ALLOW(GLOBAL, LOG_INFO, "Swarm is empty for timestep %d. Nothing to read.\n", ti);
1896 PetscFunctionReturn(0);
1897 }
1898
1899 // First, determine the top-level source directory based on the execution mode.
1900 if (simCtx->exec_mode == EXEC_MODE_SOLVER) {
1901 source_path = simCtx->restart_dir;
1902 } else if (simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR) {
1903 source_path = simCtx->pps->source_dir;
1904 } else {
1905 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "Invalid execution mode for reading simulation fields.");
1906 }
1907
1908 PetscCall(ResolveCheckpointStepDirectory(source_path, ti,
1909 checkpoint_directory, sizeof(checkpoint_directory)));
1910 PetscCall(PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer),
1911 "%s/%s", checkpoint_directory, PICURV_PARTICLE_DIRECTORY));
1912 simCtx->current_io_directory = simCtx->_io_context_buffer;
1913
1914 for (PetscInt raw_id = 0; raw_id < PARTICLE_FIELD_ID_COUNT; ++raw_id) {
1915 const ParticleFieldDescriptor *descriptor = NULL;
1916
1917 PetscCall(ParticleFieldGetDescriptor((ParticleFieldId)raw_id, &descriptor));
1918 if (!(descriptor->capabilities & PARTICLE_FIELD_CAPABILITY_CHECKPOINT)) continue;
1919 if (descriptor->data_type == PETSC_INT || descriptor->data_type == PETSC_INT64) {
1920 PetscCall(ReadSwarmIntField(user, descriptor->canonical_name, "dat"));
1921 } else {
1922 PetscCall(ReadSwarmField(user, descriptor->canonical_name, "dat"));
1923 }
1924 }
1925
1926 simCtx->current_io_directory = NULL;
1927
1928 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished reading DMSwarm fields for timestep %d.\n", ti);
1929 PetscFunctionReturn(0);
1930}
PetscErrorCode ReadSwarmField(UserCtx *user, const char *field_name, const char *ext)
Internal helper implementation: ReadSwarmField().
Definition io.c:1786
PetscErrorCode ReadSwarmIntField(UserCtx *user, const char *field_name, const char *ext)
Internal helper implementation: ReadSwarmIntField().
Definition io.c:1816
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadCheckpointParticleCount()

PetscErrorCode ReadCheckpointParticleCount ( UserCtx *  user,
PetscInt  ti,
PetscInt *  particle_count 
)

Read the particle count from a validated committed checkpoint.

Parameters
[in,out]userSimulation context and active grid used for validation.
[in]tiCheckpoint step resolved below the configured source root.
[out]particle_countNumber of particles recorded by the bundle.
Returns
Zero on success or a checkpoint-validation error.

Read the particle count from a validated committed checkpoint.

Definition at line 1933 of file io.c.

1934{
1935 SimCtx *simCtx = NULL;
1936 const char *source_path = NULL;
1937 char checkpoint_directory[PETSC_MAX_PATH_LEN];
1938 PetscBool particles_saved = PETSC_FALSE;
1939
1940 PetscFunctionBeginUser;
1941 PetscCheck(user != NULL && particle_count != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
1942 "Simulation context and particle-count output are required.");
1943 simCtx = user->simCtx;
1944 if (simCtx->exec_mode == EXEC_MODE_SOLVER) source_path = simCtx->restart_dir;
1945 else if (simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR) source_path = simCtx->pps->source_dir;
1946 else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
1947 "Invalid execution mode for reading checkpoint particle metadata.");
1948
1949 PetscCall(ResolveCheckpointStepDirectory(source_path, ti,
1950 checkpoint_directory, sizeof(checkpoint_directory)));
1951 PetscCall(ValidateCheckpointBundle(simCtx, user - user->_this,
1952 checkpoint_directory, ti, NULL, particle_count,
1953 &particles_saved, NULL));
1954 PetscCheck(particles_saved, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
1955 "Checkpoint step %" PetscInt_FMT " contains no particle state.", ti);
1956 PetscFunctionReturn(0);
1957}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadPositionsFromFile()

PetscErrorCode ReadPositionsFromFile ( PetscInt  timeIndex,
UserCtx *  user,
double **  coordsArray,
PetscInt *  Ncoords 
)

Reads coordinate data (for particles) from file into a PETSc Vec, then gathers it to rank 0.

This function uses ReadFieldData to fill a PETSc Vec with coordinate data, then leverages VecToArrayOnRank0 to gather that data into a contiguous array (valid on rank 0 only).

Parameters
[in]timeIndexThe time index used to construct file names.
[in]userPointer to the user context.
[out]coordsArrayOn rank 0, will point to a newly allocated array holding the coordinates.
[out]NcoordsOn rank 0, the length of coordsArray. On other ranks, 0.
Returns
PetscErrorCode Returns 0 on success, or non-zero on failures.

Reads coordinate data (for particles) from file into a PETSc Vec, then gathers it to rank 0.

Local to this translation unit.

Definition at line 3601 of file io.c.

3605{
3606 PetscFunctionBeginUser;
3607
3608 PetscErrorCode ierr;
3609 Vec coordsVec;
3610
3611 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Creating coords Vec.\n");
3612 ierr = VecCreate(PETSC_COMM_WORLD, &coordsVec);CHKERRQ(ierr);
3613 ierr = VecSetFromOptions(coordsVec);CHKERRQ(ierr);
3614
3615 // For example: "position" is the name of the coordinate data
3616 ierr = ReadFieldData(user, ParticleFieldName(PARTICLE_FIELD_ID_POSITION), coordsVec, "dat");
3617 if (ierr) {
3619 "Error reading position data (ti=%d).\n",
3620 timeIndex);
3621 PetscFunctionReturn(ierr);
3622 }
3623
3624 LOG_ALLOW(GLOBAL, LOG_DEBUG, "ReadPositions - Gathering coords Vec to rank 0.\n");
3625 ierr = VecToArrayOnRank0(coordsVec, Ncoords, coordsArray);CHKERRQ(ierr);
3626
3627 ierr = VecDestroy(&coordsVec);CHKERRQ(ierr);
3628
3630 "Successfully gathered coordinates. Ncoords=%d.\n", *Ncoords);
3631 PetscFunctionReturn(0);
3632}
PetscErrorCode VecToArrayOnRank0(Vec inVec, PetscInt *N, double **arrayOut)
Internal helper implementation: VecToArrayOnRank0().
Definition io.c:2674
Here is the call graph for this function:

◆ ReadFieldDataToRank0()

PetscErrorCode ReadFieldDataToRank0 ( PetscInt  timeIndex,
const char *  fieldName,
UserCtx *  user,
double **  scalarArray,
PetscInt *  Nscalars 
)

Reads a named field from file into a PETSc Vec, then gathers it to rank 0.

This function wraps ReadFieldData and VecToArrayOnRank0 into a single step. The gathered data is stored in scalarArray on rank 0, with its length in Nscalars.

Parameters
[in]timeIndexThe time index used to construct file names.
[in]fieldNameName of the field to be read (e.g., "velocity").
[in]userPointer to the user context.
[out]scalarArrayOn rank 0, a newly allocated array holding the field data.
[out]NscalarsOn rank 0, length of scalarArray. On other ranks, 0.
Returns
PetscErrorCode Returns 0 on success, or non-zero on failures.

Reads a named field from file into a PETSc Vec, then gathers it to rank 0.

Local to this translation unit.

Definition at line 3639 of file io.c.

3644{
3645 PetscFunctionBeginUser;
3646
3647 PetscErrorCode ierr;
3648 Vec fieldVec;
3649
3650 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Creating field Vec.\n");
3651 ierr = VecCreate(PETSC_COMM_WORLD, &fieldVec);CHKERRQ(ierr);
3652 ierr = VecSetFromOptions(fieldVec);CHKERRQ(ierr);
3653
3654 ierr = ReadFieldData(user, fieldName, fieldVec, "dat");
3655 if (ierr) {
3657 "Error reading field '%s' (ti=%d).\n",
3658 fieldName, timeIndex);
3659 PetscFunctionReturn(ierr);
3660 }
3661
3662 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Gathering field Vec to rank 0.\n");
3663 ierr = VecToArrayOnRank0(fieldVec, Nscalars, scalarArray);CHKERRQ(ierr);
3664
3665 ierr = VecDestroy(&fieldVec);CHKERRQ(ierr);
3666
3668 "Successfully gathered field '%s'. Nscalars=%d.\n",
3669 fieldName, *Nscalars);
3670 PetscFunctionReturn(0);
3671}
Here is the call graph for this function:

◆ DisplayBanner()

PetscErrorCode DisplayBanner ( SimCtx *  simCtx)

Displays a structured banner summarizing the simulation configuration.

This function prints effective key simulation parameters to standard output on MPI rank 0. It retrieves global domain bounds and block metadata from simCtx, and suppresses settings that do not apply to the selected mode. For example, Initial Pseudo-CFL (Courant) appears only for the Dual Time Picard–Jameson RK momentum solver; it is not a Newton–Krylov control. The report also identifies active logging, profiling, runtime-memory, and solution-convergence modes so the startup log records the effective observability contract.

Parameters
[in]simCtxPointer to the master simulation context.
Returns
PetscErrorCode Returns 0 on success.

Displays a structured banner summarizing the simulation configuration.

Reports only configuration that applies to the selected run mode. In particular, pseudo-CFL is a Dual Time Picard–Jameson RK control and is deliberately omitted for explicit and Newton–Krylov momentum solves.

Definition at line 2800 of file io.c.

2801{
2802 PetscErrorCode ierr;
2803 PetscMPIInt rank;
2804 Cmpnts global_min_coords, global_max_coords;
2805 PetscReal StartTime;
2806 PetscInt StartStep,StepsToRun,total_num_particles;
2807 PetscMPIInt num_mpi_procs;
2808 const char *log_level_name;
2809 const char *convergence_mode_name;
2810
2811 // SimCtx *simCtx = user->simCtx;
2812 UserCtx *user = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
2813 num_mpi_procs = simCtx->size;
2814 StartTime = simCtx->StartTime;
2815 StartStep = simCtx->StartStep;
2816 StepsToRun = simCtx->StepsToRun;
2817 total_num_particles = simCtx->np;
2818 BoundingBox *bboxlist_on_rank0 = simCtx->bboxlist;
2819
2820
2821 PetscFunctionBeginUser;
2822
2823 if (!user) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "DisplayBanner - UserCtx pointer is NULL.");
2824 switch (get_log_level()) {
2825 case LOG_ERROR: log_level_name = "ERROR"; break;
2826 case LOG_WARNING: log_level_name = "WARNING"; break;
2827 case LOG_INFO: log_level_name = "INFO"; break;
2828 case LOG_DEBUG: log_level_name = "DEBUG"; break;
2829 case LOG_TRACE: log_level_name = "TRACE"; break;
2830 case LOG_VERBOSE: log_level_name = "VERBOSE"; break;
2831 default: log_level_name = "UNKNOWN"; break;
2832 }
2833 switch (simCtx->solutionConvergenceMode) {
2834 case SOLUTION_CONVERGENCE_STEADY_DETERMINISTIC: convergence_mode_name = "STEADY_DETERMINISTIC"; break;
2835 case SOLUTION_CONVERGENCE_PERIODIC_DETERMINISTIC: convergence_mode_name = "PERIODIC_DETERMINISTIC"; break;
2836 case SOLUTION_CONVERGENCE_STATISTICAL_STEADY: convergence_mode_name = "STATISTICAL_STEADY"; break;
2837 case SOLUTION_CONVERGENCE_TRANSIENT: convergence_mode_name = "TRANSIENT"; break;
2838 default: convergence_mode_name = "UNKNOWN"; break;
2839 }
2840 global_min_coords = user->bbox.min_coords;
2841 global_max_coords = user->bbox.max_coords;
2842 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
2843
2844 if (rank == 0) {
2845 // If global_domain_bbox is not pre-populated in UserCtx, compute it here from bboxlist_on_rank0
2846 // This assumes bboxlist_on_rank0 is valid and contains all local bounding boxes on rank 0.
2847 if (bboxlist_on_rank0 && num_mpi_procs > 0) {
2848 global_min_coords = bboxlist_on_rank0[0].min_coords;
2849 global_max_coords = bboxlist_on_rank0[0].max_coords;
2850 for (PetscMPIInt p = 1; p < num_mpi_procs; ++p) {
2851 global_min_coords.x = PetscMin(global_min_coords.x, bboxlist_on_rank0[p].min_coords.x);
2852 global_min_coords.y = PetscMin(global_min_coords.y, bboxlist_on_rank0[p].min_coords.y);
2853 global_min_coords.z = PetscMin(global_min_coords.z, bboxlist_on_rank0[p].min_coords.z);
2854 global_max_coords.x = PetscMax(global_max_coords.x, bboxlist_on_rank0[p].max_coords.x);
2855 global_max_coords.y = PetscMax(global_max_coords.y, bboxlist_on_rank0[p].max_coords.y);
2856 global_max_coords.z = PetscMax(global_max_coords.z, bboxlist_on_rank0[p].max_coords.z);
2857 }
2858 // Optionally store this in user->global_domain_bbox if it's useful elsewhere
2859 // user->global_domain_bbox.min_coords = global_min_coords;
2860 // user->global_domain_bbox.max_coords = global_max_coords;
2861 } else {
2862 // Fallback or warning if bboxlist is not available for global calculation
2863 LOG_ALLOW(LOCAL, LOG_WARNING, "(Rank 0) - bboxlist not provided or num_mpi_procs <=0; using user->bbox for domain bounds.\n");
2864 // global_min_coords = user->bbox.min_coords; // Use local bbox of rank 0 as fallback
2865 // global_max_coords = user->bbox.max_coords;
2866 }
2867
2868 ierr = PetscPrintf(PETSC_COMM_SELF, "\n"); CHKERRQ(ierr);
2869 ierr = PetscPrintf(PETSC_COMM_SELF, "=============================================================\n"); CHKERRQ(ierr);
2870 ierr = PetscPrintf(PETSC_COMM_SELF, " CASE SUMMARY \n"); CHKERRQ(ierr);
2871 ierr = PetscPrintf(PETSC_COMM_SELF, "=============================================================\n"); CHKERRQ(ierr);
2872 ierr = PetscPrintf(PETSC_COMM_SELF, " Grid Points : %d X %d X %d\n", user->IM, user->JM, user->KM); CHKERRQ(ierr);
2873 ierr = PetscPrintf(PETSC_COMM_SELF, " Cells : %d X %d X %d\n", user->IM - 1, user->JM - 1, user->KM - 1); CHKERRQ(ierr);
2874 ierr = PetscPrintf(PETSC_COMM_SELF, " Global Domain Bounds (X) : %.6f to %.6f\n", (double)global_min_coords.x, (double)global_max_coords.x); CHKERRQ(ierr);
2875 ierr = PetscPrintf(PETSC_COMM_SELF, " Global Domain Bounds (Y) : %.6f to %.6f\n", (double)global_min_coords.y, (double)global_max_coords.y); CHKERRQ(ierr);
2876 ierr = PetscPrintf(PETSC_COMM_SELF, " Global Domain Bounds (Z) : %.6f to %.6f\n", (double)global_min_coords.z, (double)global_max_coords.z); CHKERRQ(ierr);
2877 ierr = PetscPrintf(PETSC_COMM_SELF, " Periodic Axes (BC-derived) : I=%s, J=%s, K=%s\n",
2878 simCtx->i_periodic ? "YES" : "NO",
2879 simCtx->j_periodic ? "YES" : "NO",
2880 simCtx->k_periodic ? "YES" : "NO"); CHKERRQ(ierr);
2881 for (PetscInt axis = 0; axis < 3; axis++) {
2882 if (!user->periodic_translation_valid[axis]) continue;
2883 ierr = PetscPrintf(PETSC_COMM_SELF,
2884 " Periodic %c Translation : (%.6e, %.6e, %.6e)\n",
2885 "IJK"[axis],
2886 (double)user->periodic_translation[axis].x,
2887 (double)user->periodic_translation[axis].y,
2888 (double)user->periodic_translation[axis].z); CHKERRQ(ierr);
2889 }
2890 if (total_num_particles > 0 &&
2891 (simCtx->i_periodic || simCtx->j_periodic || simCtx->k_periodic)) {
2892 ierr = PetscPrintf(PETSC_COMM_SELF,
2893 " Particle Periodic Wrapping : UNSUPPORTED (Eulerian periodicity only)\n"); CHKERRQ(ierr);
2894 }
2895 if(strcmp(simCtx->eulerianSource,"load")==0 || strcmp(simCtx->eulerianSource,"solve")==0){
2896 ierr = PetscPrintf(PETSC_COMM_SELF, "-------------------- Boundary Conditions --------------------\n"); CHKERRQ(ierr);
2897 const int face_name_width = 17; // Adjusted for longer names (Zeta,Eta,Xi)
2898 for (PetscInt i_face = 0; i_face < 6; ++i_face) {
2899 BCFace current_face = (BCFace)i_face;
2900 // The BCFaceToString will now return the Xi, Eta, Zeta versions
2901 const char* face_str = BCFaceToString(current_face);
2902 const char* bc_type_str = BCTypeToString(user->boundary_faces[current_face].mathematical_type);
2903 const char* bc_handler_type_str = BCHandlerTypeToString(user->boundary_faces[current_face].handler_type);
2904 if(user->boundary_faces[current_face].mathematical_type == INLET){
2906 Cmpnts inlet_velocity = {0.0,0.0,0.0};
2907 PetscBool found;
2908 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"vx",&inlet_velocity.x,&found); CHKERRQ(ierr);
2909 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"vy",&inlet_velocity.y,&found); CHKERRQ(ierr);
2910 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"vz",&inlet_velocity.z,&found); CHKERRQ(ierr);
2911 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - [%.4f,%.4f,%.4f]\n",
2912 face_name_width, face_str, bc_type_str, bc_handler_type_str,inlet_velocity.x,inlet_velocity.y,inlet_velocity.z); CHKERRQ(ierr);
2913 } else if(user->boundary_faces[current_face].handler_type == BC_HANDLER_INLET_PARABOLIC){
2914 PetscReal v_max = 0.0;
2915 PetscBool found;
2916 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"v_max",&v_max,&found); CHKERRQ(ierr);
2917 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - v_max=%.4f\n",
2918 face_name_width, face_str, bc_type_str, bc_handler_type_str, v_max); CHKERRQ(ierr);
2919 } else if(user->boundary_faces[current_face].handler_type == BC_HANDLER_INLET_PROFILE_FROM_FILE){
2920 const char *source_file = "(missing)";
2921 for (BC_Param *param = user->boundary_faces[current_face].params; param; param = param->next) {
2922 if (strcasecmp(param->key, "source_file") == 0 && param->value) {
2923 source_file = param->value;
2924 break;
2925 }
2926 }
2927 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - source_file=%s\n",
2928 face_name_width, face_str, bc_type_str, bc_handler_type_str, source_file); CHKERRQ(ierr);
2929 }
2931 PetscBool trimflag,foundtrimflag;
2932 ierr = GetDrivenSeamFluxFlag(user->boundary_faces[current_face].params,&trimflag,&foundtrimflag); CHKERRQ(ierr);
2933 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - [from initial state] - %s\n",
2934 face_name_width, face_str, bc_type_str, bc_handler_type_str,trimflag?"Enforce seam flux":"Seam flux not enforced"); CHKERRQ(ierr);
2936 PetscReal flux;
2937 PetscBool trimflag,foundflux,foundtrimflag;
2938 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"target_flux",&flux,&foundflux); CHKERRQ(ierr);
2939 ierr = GetDrivenSeamFluxFlag(user->boundary_faces[current_face].params,&trimflag,&foundtrimflag); CHKERRQ(ierr);
2940 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - [%.4f] - %s\n",
2941 face_name_width, face_str, bc_type_str, bc_handler_type_str,flux,trimflag?"Enforce seam flux":"Seam flux not enforced"); CHKERRQ(ierr);
2942 } else{
2943 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s\n",
2944 face_name_width, face_str, bc_type_str,bc_handler_type_str); CHKERRQ(ierr);
2945 }
2946 }
2947 }
2948 ierr = PetscPrintf(PETSC_COMM_SELF, "-------------------------------------------------------------\n"); CHKERRQ(ierr);
2949 ierr = PetscPrintf(PETSC_COMM_SELF, " Run Mode : %s\n", simCtx->OnlySetup ? "SETUP ONLY" : "Full Simulation"); CHKERRQ(ierr);
2950 ierr = PetscPrintf(PETSC_COMM_SELF, " Start Time : %.4f\n", (double)StartTime); CHKERRQ(ierr);
2951 ierr = PetscPrintf(PETSC_COMM_SELF, " Timestep Size : %.4f\n", (double)simCtx->dt); CHKERRQ(ierr);
2952 ierr = PetscPrintf(PETSC_COMM_SELF, " Starting Step : %d\n", StartStep); CHKERRQ(ierr);
2953 ierr = PetscPrintf(PETSC_COMM_SELF, " Total Steps to Run : %d\n", StepsToRun); CHKERRQ(ierr);
2954 ierr = PetscPrintf(PETSC_COMM_SELF, " Ending Step : %d\n", StartStep + StepsToRun); CHKERRQ(ierr);
2955 if (simCtx->tiout > 0) {
2956 ierr = PetscPrintf(PETSC_COMM_SELF, " Field/Restart Cadence : every %d step(s)\n", simCtx->tiout); CHKERRQ(ierr);
2957 } else {
2958 ierr = PetscPrintf(PETSC_COMM_SELF, " Field/Restart Cadence : DISABLED\n"); CHKERRQ(ierr);
2959 }
2960 /* Recorded whether or not statistics are configured, so a log says plainly
2961 * whether monitoring was active rather than leaving its absence ambiguous. */
2962 if (FieldStatisticsIsActive(simCtx) && simCtx->statisticsConsoleOutputFreq > 0) {
2963 ierr = PetscPrintf(PETSC_COMM_SELF, " Statistics Console Cadence : every %d step(s), %d window(s)\n",
2964 simCtx->statisticsConsoleOutputFreq, simCtx->fieldStatisticsWindowCount); CHKERRQ(ierr);
2965 } else if (FieldStatisticsIsActive(simCtx)) {
2966 ierr = PetscPrintf(PETSC_COMM_SELF, " Statistics Console Cadence : DISABLED (%d window(s) accumulating)\n",
2967 simCtx->fieldStatisticsWindowCount); CHKERRQ(ierr);
2968 } else {
2969 ierr = PetscPrintf(PETSC_COMM_SELF, " Statistics Console Cadence : DISABLED (no window configured)\n"); CHKERRQ(ierr);
2970 }
2971 ierr = PetscPrintf(PETSC_COMM_SELF, " Immersed Boundary : %s\n", simCtx->immersed ? "ENABLED" : "DISABLED"); CHKERRQ(ierr);
2972 if (simCtx->walltimeGuardEnabled) {
2973 ierr = PetscPrintf(
2974 PETSC_COMM_SELF,
2975 " Runtime Walltime Guard : %s (warmup=%d, multiplier=%.2f, min=%.1f s, alpha=%.2f)\n",
2976 simCtx->walltimeGuardActive ? "ENABLED" : "CONFIGURED BUT INACTIVE",
2977 simCtx->walltimeGuardWarmupSteps,
2978 (double)simCtx->walltimeGuardMultiplier,
2979 (double)simCtx->walltimeGuardMinSeconds,
2980 (double)simCtx->walltimeGuardEstimatorAlpha
2981 ); CHKERRQ(ierr);
2982 } else {
2983 ierr = PetscPrintf(PETSC_COMM_SELF, " Runtime Walltime Guard : DISABLED\n"); CHKERRQ(ierr);
2984 }
2985 ierr = PetscPrintf(PETSC_COMM_SELF, " Console Log Level : %s\n", log_level_name); CHKERRQ(ierr);
2986 ierr = PetscPrintf(PETSC_COMM_SELF, " Profiling Timestep Output : %s\n", simCtx->profilingTimestepMode); CHKERRQ(ierr);
2987 ierr = PetscPrintf(PETSC_COMM_SELF, " Profiling Final Summary : %s\n", simCtx->profilingFinalSummary ? "ENABLED" : "DISABLED"); CHKERRQ(ierr);
2988 if (simCtx->runtimeMemoryLogEnabled) {
2989 ierr = PetscPrintf(PETSC_COMM_SELF, " Runtime Memory Log : ENABLED (%s)\n", simCtx->runtimeMemoryLogFile); CHKERRQ(ierr);
2990 } else {
2991 ierr = PetscPrintf(PETSC_COMM_SELF, " Runtime Memory Log : DISABLED\n"); CHKERRQ(ierr);
2992 }
2993 ierr = PetscPrintf(PETSC_COMM_SELF, " Solution Convergence Log : %s\n",
2994 simCtx->solutionConvergenceEnabled ? "ENABLED" : "DISABLED"); CHKERRQ(ierr);
2995 ierr = PetscPrintf(PETSC_COMM_SELF, " Number of MPI Processes : %d\n", num_mpi_procs); CHKERRQ(ierr);
2996 ierr = PetscPrintf(PETSC_COMM_WORLD," Number of Particles : %d\n", total_num_particles); CHKERRQ(ierr);
2997 if (simCtx->np > 0) {
2998 const char *particle_init_str = ParticleInitializationToString(simCtx->ParticleInitialization);
2999
3000 if (simCtx->particleConsoleOutputFreq > 0) {
3001 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Console Cadence : every %d step(s)\n", simCtx->particleConsoleOutputFreq); CHKERRQ(ierr);
3002 } else {
3003 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Console Cadence : DISABLED\n"); CHKERRQ(ierr);
3004 }
3005 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Log Row Sampling : every %d particle(s)\n", simCtx->LoggingFrequency); CHKERRQ(ierr);
3006 if (simCtx->StartStep > 0) {
3007 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Restart Mode : %s\n", simCtx->particleRestartMode); CHKERRQ(ierr);
3008 }
3009 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Initialization Mode: %s\n", particle_init_str); CHKERRQ(ierr);
3010 ierr = PetscPrintf(PETSC_COMM_SELF, " Interpolation Method : %s\n",
3011 simCtx->interpolationMethod == INTERP_TRILINEAR ? "Trilinear (direct cell-center)" : "CornerAveraged (legacy)"); CHKERRQ(ierr);
3014 if (user->inletFaceDefined) {
3015 ierr = PetscPrintf(PETSC_COMM_SELF, " Particles Initialized At : %s (Enum Val: %d)\n", BCFaceToString(user->identifiedInletBCFace), user->identifiedInletBCFace); CHKERRQ(ierr);
3016 } else {
3017 ierr = PetscPrintf(PETSC_COMM_SELF, " Particles Initialized At : --- (No INLET face identified)\n"); CHKERRQ(ierr);
3018 }
3019 }
3020 }
3021 if(strcmp(simCtx->eulerianSource,"solve")==0 || strcmp(simCtx->eulerianSource,"load")==0){
3022 ierr = PetscPrintf(PETSC_COMM_WORLD," Reynolds Number : %le\n", simCtx->ren); CHKERRQ(ierr);
3023 //ierr = PetscPrintf(PETSC_COMM_WORLD," Von-Neumann Number : %le\n", simCtx->vnn); CHKERRQ(ierr);
3024 if(strcmp(simCtx->eulerianSource,"solve")==0){
3025 //ierr = PetscPrintf(PETSC_COMM_WORLD," Stanton Number : %le\n", simCtx->st); CHKERRQ(ierr);
3026 ierr = PetscPrintf(PETSC_COMM_WORLD," Momentum Equation Solver : %s\n", MomentumSolverTypeToString(simCtx->mom_solver_type)); CHKERRQ(ierr);
3028 ierr = PetscPrintf(PETSC_COMM_WORLD," Initial Pseudo-CFL (Courant): %le\n", simCtx->pseudo_cfl); CHKERRQ(ierr);
3029 ierr = PetscPrintf(PETSC_COMM_WORLD," Pseudo-CFL Range : [%le, %le]\n", simCtx->min_pseudo_cfl, simCtx->max_pseudo_cfl); CHKERRQ(ierr);
3030 ierr = PetscPrintf(PETSC_COMM_WORLD," Pseudo-CFL Adaptation : growth=%le, reduction=%le, backtrack=%s\n",
3032 simCtx->no_pseudo_cfl_backtrack ? "DISABLED" : "ENABLED"); CHKERRQ(ierr);
3033 ierr = PetscPrintf(PETSC_COMM_WORLD," Pseudo-Time Iteration Limit : %d\n", simCtx->mom_max_pseudo_steps); CHKERRQ(ierr);
3034 } else if (simCtx->mom_solver_type == MOMENTUM_SOLVER_NEWTON_KRYLOV) {
3035 ierr = PetscPrintf(PETSC_COMM_WORLD," Newton-Krylov PETSc Controls: SNES/KSP options (mom_nk_*)\n"); CHKERRQ(ierr);
3036 ierr = PetscPrintf(PETSC_COMM_WORLD," Newton-Krylov History Log : %s\n", simCtx->mom_nk_monitor_history ? "ENABLED" : "DISABLED"); CHKERRQ(ierr);
3037 } else if (simCtx->mom_solver_type == MOMENTUM_SOLVER_EXPLICIT_RK) {
3038 ierr = PetscPrintf(PETSC_COMM_WORLD," Pseudo-Time Controller : NOT APPLICABLE\n"); CHKERRQ(ierr);
3039 }
3040 ierr = PetscPrintf(PETSC_COMM_WORLD," Solution Convergence Mode : %s\n", convergence_mode_name); CHKERRQ(ierr);
3042 ierr = PetscPrintf(PETSC_COMM_WORLD," Convergence Period : %d step(s)\n", simCtx->solutionConvergencePeriodSteps); CHKERRQ(ierr);
3044 ierr = PetscPrintf(PETSC_COMM_WORLD," Convergence Window : %d step(s)\n", simCtx->solutionConvergenceWindowSteps); CHKERRQ(ierr);
3045 }
3046 ierr = PetscPrintf(PETSC_COMM_WORLD," Large Eddy Simulation Model : %s\n", LESModelToString(simCtx->les)); CHKERRQ(ierr);
3047 if (simCtx->les != NO_LES_MODEL) {
3048 const LESConfig *les = &simCtx->les_config;
3049
3050 /* Reported with the user-facing spellings, so a line here can be
3051 matched against the case file that produced it. */
3052 if (simCtx->les == VREMAN) {
3053 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Filter Width : per direction (cell edges; filter_width unused)\n"); CHKERRQ(ierr);
3054 } else {
3055 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Filter Width : %s\n",
3056 LESFilterWidthModelToString(les->filter_width_model)); CHKERRQ(ierr);
3057 }
3058 if (simCtx->les == VREMAN) {
3059 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Vreman Coefficient : %.4f (no coefficient field)\n",
3060 (double)les->vreman_coefficient); CHKERRQ(ierr);
3061 } else if (simCtx->les == WALE) {
3062 ierr = PetscPrintf(PETSC_COMM_WORLD," LES WALE Coefficient : %.4f (no coefficient field)\n",
3063 (double)les->wale_coefficient); CHKERRQ(ierr);
3064 } else if (simCtx->les == CONSTANT_SMAGORINSKY) {
3065 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Smagorinsky Constant : %.4f (no coefficient field)\n",
3066 (double)les->constant_cs); CHKERRQ(ierr);
3067 } else {
3068 char directions[8] = "";
3069 PetscInt used = 0;
3070
3071 if (les->averaging_direction[0]) directions[used++] = 'i';
3072 if (les->averaging_direction[1]) directions[used++] = 'j';
3073 if (les->averaging_direction[2]) directions[used++] = 'k';
3074 directions[used] = '\0';
3075
3076 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Test Filter : %s (width ratio %.3f)\n",
3078 (double)les->test_filter_width_ratio); CHKERRQ(ierr);
3079 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Dynamic Update Cadence : every %d step(s)\n",
3080 (int)les->dynamic_frequency); CHKERRQ(ierr);
3081 /* An empty direction list under homogeneous averaging is not a
3082 missing answer: the directions are derived from periodicity at
3083 the first update, once the block is known. */
3084 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Coefficient Averaging : %s%s%s\n",
3086 used > 0 ? " over " : "",
3087 used > 0 ? directions : ""); CHKERRQ(ierr);
3088 if (les->clip_mode == LES_CLIP_CLAMP) {
3089 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Coefficient Limiting : clamp (max Cs %.3f)\n",
3090 (double)les->max_cs); CHKERRQ(ierr);
3091 } else {
3092 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Coefficient Limiting : %s\n",
3093 LESClipModeToString(les->clip_mode)); CHKERRQ(ierr);
3094 }
3095 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Total Viscosity Floor : %.3f x molecular\n",
3096 (double)les->min_viscosity_ratio); CHKERRQ(ierr);
3097 }
3098 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Gradient (Clark) Term : %s\n",
3099 simCtx->les_gradient_model ? "ENABLED" : "DISABLED"); CHKERRQ(ierr);
3100 if (les->diagnostics_enabled) {
3101 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Coefficient Diagnostics : ENABLED (les_coefficient.csv, every %d step(s))\n",
3102 (int)les->diagnostics_cadence); CHKERRQ(ierr);
3103 /* The Yoshizawa coefficient scales only the reported subgrid kinetic
3104 energy, so it belongs with the diagnostics line rather than above it. */
3105 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Yoshizawa Coefficient : %.4f (subgrid energy estimate)\n",
3106 (double)les->yoshizawa_ci); CHKERRQ(ierr);
3107 } else {
3108 ierr = PetscPrintf(PETSC_COMM_WORLD," LES Coefficient Diagnostics : DISABLED\n"); CHKERRQ(ierr);
3109 }
3110 }
3111 /* Reported independently of the LES closure, because a wall function is
3112 configured independently of it. */
3113 if (simCtx->wallfunction) {
3114 /* Only the log law has a roughness term. Printing the height beside a
3115 model that discards it would report a control that is not in force. */
3117 ierr = PetscPrintf(PETSC_COMM_WORLD," Wall Function Model : %s (roughness height %.3e)\n",
3119 (double)simCtx->wall_roughness_height); CHKERRQ(ierr);
3120 } else {
3121 ierr = PetscPrintf(PETSC_COMM_WORLD," Wall Function Model : %s (smooth wall; no roughness term)\n",
3123 }
3124 } else {
3125 ierr = PetscPrintf(PETSC_COMM_WORLD," Wall Function Model : DISABLED\n"); CHKERRQ(ierr);
3126 }
3127 }
3128 if (strcmp(simCtx->eulerianSource, "load") == 0) {
3129 ierr = PetscPrintf(PETSC_COMM_SELF, " Eulerian State Source : load (%s)\n",
3130 simCtx->restart_dir); CHKERRQ(ierr);
3131 } else if (simCtx->StartStep > 0) {
3132 ierr = PetscPrintf(PETSC_COMM_SELF, " Eulerian State Source : restart step %d (%s)\n",
3133 simCtx->StartStep, simCtx->restart_dir); CHKERRQ(ierr);
3134 } else {
3135 const char* field_init_str = InitialConditionModeToString(simCtx->initialConditionMode);
3136 ierr = PetscPrintf(PETSC_COMM_SELF, " Eulerian State Source : initial condition (%s)\n",
3137 field_init_str); CHKERRQ(ierr);
3138 }
3139 if (strcmp(simCtx->eulerianSource, "solve") == 0 && simCtx->StartStep == 0 &&
3141 ierr = PetscPrintf(PETSC_COMM_SELF,
3142 " Constant Velocity (Cart.) : x=%.4f y=%.4f z=%.4f\n",
3143 (double)simCtx->InitialConstantContra.x,
3144 (double)simCtx->InitialConstantContra.y,
3145 (double)simCtx->InitialConstantContra.z); CHKERRQ(ierr);
3146 } else if (strcmp(simCtx->eulerianSource, "solve") == 0 && simCtx->StartStep == 0 &&
3148 ierr = PetscPrintf(PETSC_COMM_SELF,
3149 " Constant Velocity (Curv.) : speed=%.4f direction=%s\n",
3150 (double)simCtx->icVelocityPhysical,
3151 FlowDirectionToString(simCtx->flowDirection)); CHKERRQ(ierr);
3152 } else if (strcmp(simCtx->eulerianSource, "solve") == 0 && simCtx->StartStep == 0 &&
3154 ierr = PetscPrintf(PETSC_COMM_SELF,
3155 " Poiseuille Peak Velocity : speed=%.4f direction=%s\n",
3156 (double)simCtx->icVelocityPhysical,
3157 FlowDirectionToString(simCtx->flowDirection)); CHKERRQ(ierr);
3158 } else if (strcmp(simCtx->eulerianSource, "solve") == 0 && simCtx->StartStep == 0 &&
3160 ierr = PetscPrintf(PETSC_COMM_SELF,
3161 " Initial Velocity File : field=%s directory=%s\n",
3162 simCtx->initialConditionField == IC_FIELD_UCAT ? "Ucat" : "Ucont",
3163 simCtx->initialConditionDirectory); CHKERRQ(ierr);
3164 }
3165 } else if(strcmp(simCtx->eulerianSource,"analytical")==0){
3166 ierr = PetscPrintf(PETSC_COMM_WORLD," Analytical Solution Type : %s\n", simCtx->AnalyticalSolutionType); CHKERRQ(ierr);
3167 }
3168 ierr = PetscPrintf(PETSC_COMM_SELF, "=============================================================\n"); CHKERRQ(ierr);
3169 ierr = PetscPrintf(PETSC_COMM_SELF, "\n"); CHKERRQ(ierr);
3170 }
3171 PetscFunctionReturn(0);
3172}
PetscErrorCode GetBCParamReal(BC_Param *params, const char *key, PetscReal *value_out, PetscBool *found)
Internal helper implementation: GetBCParamReal().
Definition io.c:760
PetscErrorCode GetDrivenSeamFluxFlag(BC_Param *params, PetscBool *value_out, PetscBool *found)
Implementation of GetDrivenSeamFluxFlag().
Definition io.c:822
const char * LESTestFilterKernelToString(LESTestFilterKernel kernel)
Returns the user-facing name of an LES test-filter kernel.
Definition logging.c:782
const char * BCHandlerTypeToString(BCHandlerType handler_type)
Converts a BCHandlerType enum to its string representation.
Definition logging.c:889
const char * BCFaceToString(BCFace face)
Returns the canonical log token for a boundary-face enum value.
Definition logging.c:671
const char * WallFunctionModelToString(WallFunctionModel model)
Returns the user-facing name of a wall-function model.
Definition logging.c:835
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:87
const char * LESFilterWidthModelToString(LESFilterWidthModel model)
Returns the user-facing name of an LES grid-filter-width model.
Definition logging.c:761
const char * BCTypeToString(BCType type)
Returns the canonical log token for a boundary mathematical type.
Definition logging.c:869
const char * FlowDirectionToString(FlowDirection fd)
Convert a FlowDirection enum value to its YAML token string.
Definition logging.c:705
const char * LESAveragingModeToString(LESAveragingMode mode)
Returns the user-facing name of an LES coefficient-averaging mode.
Definition logging.c:799
const char * InitialConditionModeToString(InitialConditionMode mode)
Convert an initial-condition mode to a string representation.
Definition logging.c:689
@ LOG_VERBOSE
Extremely detailed logs, typically for development use only.
Definition logging.h:34
const char * LESModelToString(LESModelType LESFlag)
Returns the canonical log token for an LES model selector.
Definition logging.c:741
const char * LESClipModeToString(LESClipMode mode)
Returns the user-facing name of an LES coefficient-limiting mode.
Definition logging.c:817
const char * MomentumSolverTypeToString(MomentumSolverType SolverFlag)
Returns the canonical log token for a momentum-solver selector.
Definition logging.c:853
const char * ParticleInitializationToString(ParticleInitializationType ParticleInitialization)
Returns the canonical log token for a particle-initialization mode.
Definition logging.c:724
@ VREMAN
Definition variables.h:552
@ NO_LES_MODEL
Definition variables.h:549
@ WALE
Definition variables.h:553
@ CONSTANT_SMAGORINSKY
Definition variables.h:550
PetscReal icVelocityPhysical
Definition variables.h:921
PetscBool mom_nk_monitor_history
Definition variables.h:914
@ INLET
Definition variables.h:316
PetscReal yoshizawa_ci
Yoshizawa constant for the reported SGS kinetic energy.
Definition variables.h:642
PetscBool inletFaceDefined
Definition variables.h:1100
PetscBool profilingFinalSummary
Definition variables.h:1036
PetscInt dynamic_frequency
Recompute the dynamic coefficient every N steps.
Definition variables.h:630
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:1099
LESFilterWidthModel filter_width_model
How the grid filter width Delta is derived per cell.
Definition variables.h:634
PetscInt statisticsConsoleOutputFreq
Definition variables.h:934
BCFace identifiedInletBCFace
Definition variables.h:1101
PetscBool walltimeGuardActive
Definition variables.h:1038
LESTestFilterKernel test_filter_kernel
Discrete test-filter stencil.
Definition variables.h:635
LESConfig les_config
Parameters of the LES closure selected by les.
Definition variables.h:987
PetscReal pseudo_cfl_reduction_factor
Definition variables.h:907
InitialConditionMode initialConditionMode
Definition variables.h:916
@ PARTICLE_INIT_SURFACE_RANDOM
Random placement on the inlet face.
Definition variables.h:710
@ PARTICLE_INIT_SURFACE_EDGES
Deterministic placement at inlet face edges.
Definition variables.h:713
FlowDirection flowDirection
Definition variables.h:920
PetscBool runtimeMemoryLogEnabled
Enable the rank-reduced runtime memory log.
Definition variables.h:1051
PetscReal min_pseudo_cfl
Definition variables.h:908
@ BC_HANDLER_INLET_PARABOLIC
Definition variables.h:335
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
Definition variables.h:334
@ BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX
Definition variables.h:343
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:342
@ BC_HANDLER_INLET_PROFILE_FROM_FILE
Definition variables.h:336
PetscReal ren
Definition variables.h:906
BCHandlerType handler_type
Definition variables.h:393
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
Definition variables.h:199
PetscBool solutionConvergenceEnabled
Definition variables.h:923
char runtimeMemoryLogFile[PETSC_MAX_PATH_LEN]
File name written under log_dir.
Definition variables.h:1052
@ LES_CLIP_CLAMP
Definition variables.h:618
PetscInt StepsToRun
Definition variables.h:870
char profilingTimestepMode[32]
Definition variables.h:1034
PetscInt k_periodic
Definition variables.h:953
WallFunctionModel
Selects the wall model applied on WALL faces.
Definition variables.h:565
@ WALL_FUNCTION_LOG_LAW
Definition variables.h:567
PetscReal max_cs
Ceiling on Cs under LES_CLIP_CLAMP.
Definition variables.h:640
LESClipMode clip_mode
Admissible range for the coefficient.
Definition variables.h:639
PetscBool no_pseudo_cfl_backtrack
Definition variables.h:910
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
Definition variables.h:198
PetscBool OnlySetup
Definition variables.h:875
@ MOMENTUM_SOLVER_DUALTIME_PICARD_JAMESON_RK
Definition variables.h:694
@ MOMENTUM_SOLVER_EXPLICIT_RK
Definition variables.h:693
@ MOMENTUM_SOLVER_NEWTON_KRYLOV
Definition variables.h:695
PetscInt solutionConvergencePeriodSteps
Definition variables.h:925
PetscScalar x
Definition variables.h:122
PetscBool averaging_direction[3]
Averaged-over logical directions (xi, eta, zeta).
Definition variables.h:638
PetscReal wale_coefficient
Model constant C_w for WALE (Nicoud & Ducros 1999).
Definition variables.h:633
InterpolationMethod interpolationMethod
Definition variables.h:995
PetscReal max_pseudo_cfl
Definition variables.h:908
BoundingBox * bboxlist
Definition variables.h:993
PetscInt les_gradient_model
Add the Clark gradient (tensor-diffusivity) term to the viscous flux.
Definition variables.h:986
BC_Param * params
Definition variables.h:394
char eulerianSource[PETSC_MAX_PATH_LEN]
Definition variables.h:879
PetscBool walltimeGuardEnabled
Definition variables.h:1037
PetscReal wall_roughness_height
Definition variables.h:947
ParticleInitializationType ParticleInitialization
Definition variables.h:994
PetscScalar z
Definition variables.h:122
@ INTERP_TRILINEAR
Definition variables.h:723
PetscInt diagnostics_cadence
Steps between diagnostic rows.
Definition variables.h:644
PetscReal min_viscosity_ratio
Enforce nu + nu_t >= ratio * nu.
Definition variables.h:641
PetscInt wallfunction
Enable wall functions on WALL faces.
Definition variables.h:985
PetscBool diagnostics_enabled
Append per-step coefficient statistics to the run log directory.
Definition variables.h:643
PetscReal constant_cs
Fixed Cs for CONSTANT_SMAGORINSKY; unused by the dynamic model.
Definition variables.h:631
PetscInt solutionConvergenceWindowSteps
Definition variables.h:926
PetscReal test_filter_width_ratio
Test-to-grid ratio per filtered direction; alpha is its square for the box, ratio^(4/3) for Simpson.
Definition variables.h:636
PetscReal pseudo_cfl_growth_factor
Definition variables.h:907
LESAveragingMode averaging_mode
Averaging set for the Germano contractions.
Definition variables.h:637
char AnalyticalSolutionType[PETSC_MAX_PATH_LEN]
Definition variables.h:893
@ IC_MODE_CONSTANT_CARTESIAN
Definition variables.h:179
@ IC_MODE_POISEUILLE
Definition variables.h:180
@ IC_MODE_CONSTANT_STREAMWISE
Definition variables.h:181
@ IC_MODE_FILE
Definition variables.h:182
PetscInt particleConsoleOutputFreq
Definition variables.h:872
Cmpnts InitialConstantContra
Definition variables.h:919
PetscInt i_periodic
Definition variables.h:953
PetscReal vreman_coefficient
Model constant c for VREMAN (Vreman 2004: 2.5 Cs^2).
Definition variables.h:632
PetscInt mom_max_pseudo_steps
Definition variables.h:900
PetscScalar y
Definition variables.h:122
@ IC_FIELD_UCAT
Definition variables.h:187
PetscMPIInt size
Definition variables.h:863
Cmpnts periodic_translation[3]
Definition variables.h:1095
@ SOLUTION_CONVERGENCE_TRANSIENT
Definition variables.h:705
@ SOLUTION_CONVERGENCE_PERIODIC_DETERMINISTIC
Definition variables.h:703
@ SOLUTION_CONVERGENCE_STATISTICAL_STEADY
Definition variables.h:704
@ SOLUTION_CONVERGENCE_STEADY_DETERMINISTIC
Definition variables.h:702
PetscBool periodic_translation_valid[3]
Definition variables.h:1096
BCType mathematical_type
Definition variables.h:392
SolutionConvergenceMode solutionConvergenceMode
Definition variables.h:924
InitialConditionField initialConditionField
Definition variables.h:917
BoundingBox bbox
Definition variables.h:1090
MomentumSolverType mom_solver_type
Definition variables.h:899
PetscInt immersed
Definition variables.h:891
PetscReal pseudo_cfl
Definition variables.h:906
PetscInt LoggingFrequency
Definition variables.h:1020
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:287
PetscInt j_periodic
Definition variables.h:953
A node in a linked list for storing key-value parameters from the bcs.dat file.
Definition variables.h:360
Defines a 3D axis-aligned bounding box.
Definition variables.h:197
A 3D point or vector with PetscScalar components.
Definition variables.h:121
Every user-selectable parameter of the LES closure.
Definition variables.h:629
Here is the call graph for this function:
Here is the caller graph for this function:

◆ StringToBCFace()

PetscErrorCode StringToBCFace ( const char *  str,
BCFace *  face_out 
)

Converts a face-token string (e.g., "-Xi", "+Eta") to the internal BCFace enum.

Parameters
[in]strInput token from configuration.
[out]face_outParsed enum value on success.
Returns
PetscErrorCode 0 on success, non-zero for invalid tokens or null pointers.

Converts a face-token string (e.g., "-Xi", "+Eta") to the internal BCFace enum.

Local to this translation unit.

Definition at line 687 of file io.c.

687 {
688 if (strcasecmp(str, "-Xi") == 0) *face_out = BC_FACE_NEG_X;
689 else if (strcasecmp(str, "+Xi") == 0) *face_out = BC_FACE_POS_X;
690 else if (strcasecmp(str, "-Eta") == 0) *face_out = BC_FACE_NEG_Y;
691 else if (strcasecmp(str, "+Eta") == 0) *face_out = BC_FACE_POS_Y;
692 else if (strcasecmp(str, "-Zeta") == 0) *face_out = BC_FACE_NEG_Z;
693 else if (strcasecmp(str, "+Zeta") == 0) *face_out = BC_FACE_POS_Z;
694 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown face specifier: %s", str);
695 return 0;
696}
@ BC_FACE_NEG_X
Definition variables.h:288
@ BC_FACE_POS_Z
Definition variables.h:290
@ BC_FACE_POS_Y
Definition variables.h:289
@ BC_FACE_NEG_Z
Definition variables.h:290
@ BC_FACE_POS_X
Definition variables.h:288
@ BC_FACE_NEG_Y
Definition variables.h:289
Here is the caller graph for this function:

◆ StringToBCType()

PetscErrorCode StringToBCType ( const char *  str,
BCType *  type_out 
)

Converts a mathematical BC type string (e.g., "PERIODIC", "WALL") to BCType.

Parameters
[in]strInput token from configuration.
[out]type_outParsed enum value on success.
Returns
PetscErrorCode 0 on success, non-zero for invalid tokens or null pointers.

Converts a mathematical BC type string (e.g., "PERIODIC", "WALL") to BCType.

Local to this translation unit.

Definition at line 702 of file io.c.

702 {
703 if (strcasecmp(str, "WALL") == 0) *type_out = WALL;
704 else if (strcasecmp(str, "SYMMETRY") == 0) *type_out = SYMMETRY;
705 else if (strcasecmp(str, "INLET") == 0) *type_out = INLET;
706 else if (strcasecmp(str, "OUTLET") == 0) *type_out = OUTLET;
707 else if (strcasecmp(str, "PERIODIC") == 0) *type_out = PERIODIC;
708 // ... add other BCTypes here ...
709 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown BC Type string: %s", str);
710 return 0;
711}
@ SYMMETRY
Definition variables.h:314
@ OUTLET
Definition variables.h:315
@ PERIODIC
Definition variables.h:318
@ WALL
Definition variables.h:312
Here is the caller graph for this function:

◆ StringToBCHandlerType()

PetscErrorCode StringToBCHandlerType ( const char *  str,
BCHandlerType *  handler_out 
)

Converts a BC handler token (implementation strategy) to BCHandlerType.

Parameters
[in]strInput handler token from configuration.
[out]handler_outParsed enum value on success.
Returns
PetscErrorCode 0 on success, non-zero for invalid tokens or null pointers.

Converts a BC handler token (implementation strategy) to BCHandlerType.

Local to this translation unit.

Definition at line 717 of file io.c.

717 {
718 if (strcasecmp(str, "noslip") == 0) *handler_out = BC_HANDLER_WALL_NOSLIP;
719 else if (strcasecmp(str, "constant_velocity") == 0) *handler_out = BC_HANDLER_INLET_CONSTANT_VELOCITY;
720 else if (strcasecmp(str, "conservation") == 0) *handler_out = BC_HANDLER_OUTLET_CONSERVATION;
721 else if (strcasecmp(str, "parabolic") == 0) *handler_out = BC_HANDLER_INLET_PARABOLIC;
722 else if (strcasecmp(str, "prescribed_flow") == 0) *handler_out = BC_HANDLER_INLET_PROFILE_FROM_FILE;
723 else if (strcasecmp(str,"geometric") == 0) *handler_out = BC_HANDLER_PERIODIC_GEOMETRIC;
724 else if (strcasecmp(str,"constant_flux") == 0) *handler_out = BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX;
725 else if (strcasecmp(str,"initial_flux") == 0) *handler_out = BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX;
726 // ... add other BCHandlerTypes here ...
727 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown BC Handler string: %s", str);
728 return 0;
729}
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:340
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:331
@ BC_HANDLER_OUTLET_CONSERVATION
Definition variables.h:338
Here is the caller graph for this function:

◆ ValidateBCHandlerForBCType()

PetscErrorCode ValidateBCHandlerForBCType ( BCType  type,
BCHandlerType  handler 
)

Validates that a selected handler is compatible with a mathematical BC type.

Parameters
[in]typeMathematical BC type (e.g., WALL, PERIODIC).
[in]handlerSelected handler implementation enum.
Returns
PetscErrorCode 0 if compatible, non-zero if the combination is invalid.

Validates that a selected handler is compatible with a mathematical BC type.

Local to this translation unit.

Definition at line 735 of file io.c.

735 {
736 switch (type) {
737 case OUTLET:
738 if(handler != BC_HANDLER_OUTLET_CONSERVATION) return PETSC_ERR_ARG_WRONG;
739 break;
740 case WALL:
741 if (handler != BC_HANDLER_WALL_NOSLIP && handler != BC_HANDLER_WALL_MOVING) return PETSC_ERR_ARG_WRONG;
742 break;
743 case INLET:
744 if (handler != BC_HANDLER_INLET_CONSTANT_VELOCITY &&
745 handler != BC_HANDLER_INLET_PARABOLIC &&
746 handler != BC_HANDLER_INLET_PROFILE_FROM_FILE) return PETSC_ERR_ARG_WRONG;
747 break;
748 case PERIODIC:
749 if(handler != BC_HANDLER_PERIODIC_GEOMETRIC && handler != BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX && handler != BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX) return PETSC_ERR_ARG_WRONG;
750 // ... add other validation cases here ...
751 default: break;
752 }
753 return 0; // Combination is valid
754}
@ BC_HANDLER_WALL_MOVING
Definition variables.h:332
Here is the caller graph for this function:

◆ FreeBC_ParamList()

void FreeBC_ParamList ( BC_Param *  head)

Frees an entire linked list of boundary-condition parameters.

Parameters
[in,out]headHead pointer of the BC_Param list to destroy.

Frees an entire linked list of boundary-condition parameters.

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

See also
FreeBC_ParamList()

Definition at line 672 of file io.c.

672 {
673 BC_Param *current = head;
674 while (current != NULL) {
675 BC_Param *next = current->next;
676 PetscFree(current->key);
677 PetscFree(current->value);
678 PetscFree(current);
679 current = next;
680 }
681}
struct BC_Param_s * next
Definition variables.h:363
char * key
Definition variables.h:361
char * value
Definition variables.h:362
Here is the caller graph for this function:

◆ GetBCParamReal()

PetscErrorCode GetBCParamReal ( BC_Param *  params,
const char *  key,
PetscReal *  value_out,
PetscBool *  found 
)

Searches a BC_Param linked list for a key and returns its value as a double.

Parameters
paramsThe head of the BC_Param linked list.
keyThe key to search for (case-insensitive).
[out]value_outThe found value, converted to a PetscReal.
[out]foundSet to PETSC_TRUE if the key was found, PETSC_FALSE otherwise.
Returns
0 on success.

Searches a BC_Param linked list for a key and returns its value as a double.

Local to this translation unit.

Definition at line 760 of file io.c.

760 {
761 *found = PETSC_FALSE;
762 *value_out = 0.0;
763 if (!key) return 0; // No key to search for
764
765 BC_Param *current = params;
766 while (current) {
767 if (strcasecmp(current->key, key) == 0) {
768 *value_out = atof(current->value);
769 *found = PETSC_TRUE;
770 return 0; // Found it, we're done
771 }
772 current = current->next;
773 }
774 return 0; // It's not an error to not find the key.
775}
Here is the caller graph for this function:

◆ GetBCParamBool()

PetscErrorCode GetBCParamBool ( BC_Param *  params,
const char *  key,
PetscBool *  value_out,
PetscBool *  found 
)

Searches a BC_Param linked list for a key and returns its value as a bool.

Parameters
paramsThe head of the BC_Param linked list.
keyThe key to search for (case-insensitive).
[out]value_outThe found value, converted to a PetscBool.
[out]foundSet to PETSC_TRUE if the key was found, PETSC_FALSE otherwise.
Returns
0 on success.

Searches a BC_Param linked list for a key and returns its value as a bool.

Local to this translation unit.

Definition at line 781 of file io.c.

781 {
782 *found = PETSC_FALSE;
783 *value_out = PETSC_FALSE;
784 if (!key) return 0; // No key to search for
785
786 BC_Param *current = params;
787 while (current) {
788 if (strcasecmp(current->key, key) == 0) {
789 // Key was found.
790 *found = PETSC_TRUE;
791
792 // Check the value string. Default to FALSE if the value is NULL or doesn't match a "true" string.
793 if (current->value &&
794 (strcasecmp(current->value, "true") == 0 ||
795 strcmp(current->value, "1") == 0 ||
796 strcasecmp(current->value, "yes") == 0))
797 {
798 *value_out = PETSC_TRUE;
799 } else {
800 *value_out = PETSC_FALSE;
801 }
802 return 0; // Found it, we're done
803 }
804 current = current->next;
805 }
806 return 0; // It's not an error to not find the key.
807}
Here is the caller graph for this function:

◆ GetDrivenSeamFluxFlag()

PetscErrorCode GetDrivenSeamFluxFlag ( BC_Param *  params,
PetscBool *  value_out,
PetscBool *  found 
)

Read the driven-flow seam-flux flag, accepting its deprecated apply_trim spelling.

Parameters
[in]paramsParameter list for the face.
[out]value_outResolved flag value.
[out]foundWhether either spelling was present.
Returns
PetscErrorCode 0 on success.

Read the driven-flow seam-flux flag, accepting its deprecated apply_trim spelling.

The option was originally spelled apply_trim, which said that something was trimmed but not what or why. The canonical name is now enforce_seam_flux. Generated bcs.run files carry the canonical name, but a hand-written or archived one may still use the old spelling, so both are accepted here and the canonical name wins. The argument contract lives with the header declaration in include/io.h.

See also
GetDrivenSeamFluxFlag()

Definition at line 822 of file io.c.

823{
824 PetscErrorCode ierr;
825 PetscFunctionBeginUser;
826
827 ierr = GetBCParamBool(params, "enforce_seam_flux", value_out, found); CHKERRQ(ierr);
828 if (!*found) {
829 ierr = GetBCParamBool(params, "apply_trim", value_out, found); CHKERRQ(ierr);
830 }
831 PetscFunctionReturn(0);
832}
PetscErrorCode GetBCParamBool(BC_Param *params, const char *key, PetscBool *value_out, PetscBool *found)
Internal helper implementation: GetBCParamBool().
Definition io.c:781
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseAllBoundaryConditions()

PetscErrorCode ParseAllBoundaryConditions ( UserCtx *  user,
const char *  bcs_input_filename 
)

Parses the boundary conditions file to configure the type, handler, and any associated parameters for all 6 global faces of the domain.

This function performs the following steps:

  1. On MPI rank 0, it reads the specified configuration file line-by-line.
  2. It parses each line for <Face> <Type> <Handler> [param=value]... format.
  3. It validates the parsed strings and stores the configuration, including a linked list of parameters, in a temporary array.
  4. It then serializes this configuration and broadcasts it to all other MPI ranks.
  5. All ranks (including rank 0) then deserialize the broadcasted data to populate their local user->boundary_faces array identically.
  6. It also sets the particle inlet lookup fields in UserCtx.
Parameters
[in,out]userThe main UserCtx struct where the final configuration for all ranks will be stored.
[in]bcs_input_filenameThe path to the boundary conditions configuration file.
Returns
PetscErrorCode 0 on success, error code on failure.

Parses the boundary conditions file to configure the type, handler, and any associated parameters for all 6 global faces of the domain.

Local to this translation unit.

Definition at line 845 of file io.c.

846{
847 PetscErrorCode ierr;
848 PetscMPIInt rank;
849
850 // Temporary storage for rank 0 to build the configuration before broadcasting.
851 BoundaryFaceConfig configs_rank0[6];
852
853 PetscFunctionBeginUser;
855 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
856
857 if (rank == 0) {
858 FILE *file;
859 char line_buffer[1024];
860
861 // Initialize the temporary config array with safe defaults on rank 0.
862 for (int i = 0; i < 6; i++) {
863 configs_rank0[i].face_id = (BCFace)i;
864 configs_rank0[i].mathematical_type = WALL;
865 configs_rank0[i].handler_type = BC_HANDLER_WALL_NOSLIP;
866 configs_rank0[i].params = NULL;
867 configs_rank0[i].handler = NULL; // Handler object is not created here.
868 }
869
870 LOG_ALLOW(GLOBAL, LOG_INFO, "Parsing BC configuration from '%s' on rank 0... \n", bcs_input_filename);
871 file = fopen(bcs_input_filename, "r");
872 if (!file) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Could not open BCs file '%s'.", bcs_input_filename);
873
874 while (fgets(line_buffer, sizeof(line_buffer), file)) {
875 char *current_pos = line_buffer;
876 while (isspace((unsigned char)*current_pos)) current_pos++; // Skip leading whitespace
877 if (*current_pos == '#' || *current_pos == '\0' || *current_pos == '\n' || *current_pos == '\r') continue;
878
879 char *face_str = strtok(current_pos, " \t\n\r");
880 char *type_str = strtok(NULL, " \t\n\r");
881 char *handler_str = strtok(NULL, " \t\n\r");
882
883 if (!face_str || !type_str || !handler_str) {
884 LOG_ALLOW(GLOBAL, LOG_WARNING, "Malformed line in bcs.dat, skipping: %s \n", line_buffer);
885 continue;
886 }
887
888 BCFace face_enum;
889 BCType type_enum;
890 BCHandlerType handler_enum;
891 const char* handler_name_for_log;
892
893 // --- Convert strings to enums and validate ---
894 ierr = StringToBCFace(face_str, &face_enum); CHKERRQ(ierr);
895 ierr = StringToBCType(type_str, &type_enum); CHKERRQ(ierr);
896 ierr = StringToBCHandlerType(handler_str, &handler_enum); CHKERRQ(ierr);
897 ierr = ValidateBCHandlerForBCType(type_enum, handler_enum);
898 if (ierr) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Validation failed: Handler '%s' is not valid for Type '%s' on Face '%s'.\n", handler_str, type_str, face_str);
899
900 // Store the core types for the corresponding face
901 configs_rank0[face_enum].mathematical_type = type_enum;
902 configs_rank0[face_enum].handler_type = handler_enum;
903 handler_name_for_log = BCHandlerTypeToString(handler_enum); // Assumes this utility exists
904 LOG_ALLOW(GLOBAL, LOG_DEBUG, " Parsed Face '%s': Type=%s, Handler=%s \n", face_str, type_str, handler_name_for_log);
905
906 // --- Parse optional key=value parameters for this face ---
907 FreeBC_ParamList(configs_rank0[face_enum].params); // Clear any previous (default) params
908 configs_rank0[face_enum].params = NULL;
909 BC_Param **param_next_ptr = &configs_rank0[face_enum].params; // Pointer to the 'next' pointer to build the list
910
911 char* token;
912 while ((token = strtok(NULL, " \t\n\r")) != NULL) {
913 char* equals_ptr = strchr(token, '=');
914 if (!equals_ptr) {
915 LOG_ALLOW(GLOBAL, LOG_WARNING, "Malformed parameter '%s' on face '%s', skipping. \n", token, face_str);
916 continue;
917 }
918
919 *equals_ptr = '\0'; // Temporarily split the string at '=' to separate key and value
920 char* key_str = token;
921 char* value_str = equals_ptr + 1;
922
923 BC_Param *new_param;
924 ierr = PetscMalloc1(1, &new_param); CHKERRQ(ierr);
925 ierr = PetscStrallocpy(key_str, &new_param->key); CHKERRQ(ierr);
926 ierr = PetscStrallocpy(value_str, &new_param->value); CHKERRQ(ierr);
927 new_param->next = NULL;
928
929 *param_next_ptr = new_param;
930 param_next_ptr = &new_param->next;
931 LOG_ALLOW(GLOBAL, LOG_TRACE, " - Found param: [%s] = [%s] \n", new_param->key, new_param->value);
932 }
933 }
934 fclose(file);
935 }
936
937 // =========================================================================
938 // BROADCASTING THE CONFIGURATION FROM RANK 0
939 // =========================================================================
940 // This is a critical step to ensure all processes have the same configuration.
941
942 LOG_ALLOW_SYNC(GLOBAL, LOG_DEBUG, "Rank %d broadcasting/receiving BC configuration.\n", rank);
943
944 for (int i = 0; i < 6; i++) {
945 // --- Broadcast simple enums ---
946 if (rank == 0) {
947 user->boundary_faces[i] = configs_rank0[i]; // Rank 0 populates its final struct
948 }
949 ierr = MPI_Bcast(&user->boundary_faces[i].mathematical_type, 1, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
950 ierr = MPI_Bcast(&user->boundary_faces[i].handler_type, 1, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
951
952 // --- Serialize and Broadcast the parameter linked list ---
953 PetscInt n_params = 0;
954 if (rank == 0) { // On rank 0, count the number of parameters to send
955 for (BC_Param *p = user->boundary_faces[i].params; p; p = p->next) n_params++;
956 }
957 ierr = MPI_Bcast(&n_params, 1, MPI_INT, 0, PETSC_COMM_WORLD);CHKERRQ(ierr);
958
959 if (rank != 0) { // Non-root ranks need to receive and build the list
960 FreeBC_ParamList(user->boundary_faces[i].params); // Ensure list is empty before building
961 user->boundary_faces[i].params = NULL;
962 }
963
964 BC_Param **param_next_ptr = &user->boundary_faces[i].params;
965
966 for (int j = 0; j < n_params; j++) {
967 char key_buf[256] = {0}, val_buf[256] = {0};
968 if (rank == 0) {
969 // On rank 0, navigate to the j-th param and copy its data to buffers
970 BC_Param *p = user->boundary_faces[i].params;
971 for (int k = 0; k < j; k++) p = p->next;
972 strncpy(key_buf, p->key, 255);
973 strncpy(val_buf, p->value, 255);
974 }
975
976 ierr = MPI_Bcast(key_buf, 256, MPI_CHAR, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
977 ierr = MPI_Bcast(val_buf, 256, MPI_CHAR, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
978
979 if (rank != 0) {
980 // On non-root ranks, deserialize: create a new node and append it
981 BC_Param *new_param;
982 ierr = PetscMalloc1(1, &new_param); CHKERRQ(ierr);
983 ierr = PetscStrallocpy(key_buf, &new_param->key); CHKERRQ(ierr);
984 ierr = PetscStrallocpy(val_buf, &new_param->value); CHKERRQ(ierr);
985 new_param->next = NULL;
986 *param_next_ptr = new_param;
987 param_next_ptr = &new_param->next;
988 } else {
989 // On rank 0, just advance the pointer for the next iteration
990 param_next_ptr = &((*param_next_ptr)->next);
991 }
992 }
993 user->boundary_faces[i].face_id = (BCFace)i; // Ensure face_id is set on all ranks
994 }
995
996 // --- Set particle inlet lookup fields used by the particle system ---
997 user->inletFaceDefined = PETSC_FALSE;
998 for (int i=0; i<6; i++) {
999
1000 if (user->boundary_faces[i].mathematical_type == INLET && !user->inletFaceDefined) {
1001 user->inletFaceDefined = PETSC_TRUE;
1002 user->identifiedInletBCFace = (BCFace)i;
1003 LOG_ALLOW(GLOBAL, LOG_INFO, "Inlet face for particle initialization identified as Face %d.\n", i);
1004 break; // Found the first one, stop looking
1005 }
1006 }
1007
1008
1009 if (rank == 0) {
1010 // Rank 0 can now free the linked lists it created for the temporary storage.
1011 // As written, user->boundary_faces was populated directly on rank 0, so no extra free is needed.
1012 // for(int i=0; i<6; i++) FreeBC_ParamList(configs_rank0[i].params); // This would be needed if we used configs_rank0 exclusively
1013 }
1014
1016 PetscFunctionReturn(0);
1017}
PetscErrorCode StringToBCHandlerType(const char *str, BCHandlerType *handler_out)
Internal helper implementation: StringToBCHandlerType().
Definition io.c:717
PetscErrorCode ValidateBCHandlerForBCType(BCType type, BCHandlerType handler)
Internal helper implementation: ValidateBCHandlerForBCType().
Definition io.c:735
PetscErrorCode StringToBCFace(const char *str, BCFace *face_out)
Internal helper implementation: StringToBCFace().
Definition io.c:687
void FreeBC_ParamList(BC_Param *head)
Implementation of FreeBC_ParamList().
Definition io.c:672
PetscErrorCode StringToBCType(const char *str, BCType *type_out)
Internal helper implementation: StringToBCType().
Definition io.c:702
BCType
Defines the general mathematical/physical Category of a boundary.
Definition variables.h:309
BCHandlerType
Defines the specific computational "strategy" for a boundary handler.
Definition variables.h:329
BoundaryCondition * handler
Definition variables.h:395
Holds the complete configuration for one of the six boundary faces.
Definition variables.h:390
Here is the call graph for this function:
Here is the caller graph for this function:

◆ DeterminePeriodicity()

PetscErrorCode DeterminePeriodicity ( SimCtx *  simCtx)

Scans all block-specific boundary condition files to determine a globally consistent periodicity for each dimension, reusing the core type parser.

This is a lightweight pre-parser intended to be called before DMDA creation. It ensures that the periodicity setting is consistent across all blocks, which is a physical requirement for the domain.

  1. It collectively verifies that the mandatory BCS file for each block exists.
  2. On MPI rank 0, it then iterates through the files.
  3. For each line, it attempts to convert the type string to a BCType enum using the standard StringToBCType helper.
  4. If the conversion is successful AND the type is PERIODIC, it flags the corresponding face.
  5. If the conversion fails (e.g., for "WALL", "INLET", etc.), the error is cleared and the line is simply ignored, as it's not relevant to periodicity.
  6. It validates consistency (e.g., -Xi and +Xi match) and ensures all block files specify the same global periodicity.
  7. It broadcasts the final three flags (as integers 0 or 1) to all MPI ranks.
  8. All ranks update the i_periodic, j_periodic, and k_periodic fields in their SimCtx.
Parameters
[in,out]simCtxThe master SimCtx struct, containing the bcs_files list and where the final periodicity flags will be stored.
Returns
PetscErrorCode 0 on success, error code on failure.

Scans all block-specific boundary condition files to determine a globally consistent periodicity for each dimension, reusing the core type parser.

Local to this translation unit.

Definition at line 1032 of file io.c.

1033{
1034 PetscErrorCode ierr;
1035 PetscMPIInt rank;
1036 PetscInt periodic_flags[3] = {0, 0, 0}; // Index 0:I, 1:J, 2:K
1037
1038 PetscFunctionBeginUser;
1039 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
1040
1041 // --- Part 1: Collectively verify all BCS files exist before proceeding ---
1042 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
1043 const char *bcs_filename = simCtx->bcs_files[bi];
1044 if (!bcs_filename) SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_NULL, "BCS filename for block %d is not set in SimCtx.", bi);
1045 char desc_buf[256];
1046 PetscBool file_exists;
1047 snprintf(desc_buf, sizeof(desc_buf), "BCS file for block %d", bi);
1048 ierr = VerifyPathExistence(bcs_filename, PETSC_FALSE, PETSC_FALSE, desc_buf, &file_exists); CHKERRQ(ierr);
1049 }
1050
1051 // --- Part 2: Rank 0 does the parsing, since we know all files exist ---
1052 if (rank == 0) {
1053 PetscBool global_is_periodic[3] = {PETSC_FALSE, PETSC_FALSE, PETSC_FALSE};
1054 PetscBool is_set = PETSC_FALSE;
1055
1056 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
1057 const char *bcs_filename = simCtx->bcs_files[bi];
1058 FILE *file = fopen(bcs_filename, "r");
1059
1060 PetscBool face_is_periodic[6] = {PETSC_FALSE};
1061 char line_buffer[1024];
1062
1063 while (fgets(line_buffer, sizeof(line_buffer), file)) {
1064 char *current_pos = line_buffer;
1065 while (isspace((unsigned char)*current_pos)) current_pos++;
1066 if (*current_pos == '#' || *current_pos == '\0' || *current_pos == '\n') continue;
1067
1068 // --- Tokenize the line exactly like the main parser ---
1069 char *face_str = strtok(current_pos, " \t\n\r");
1070 char *type_str = strtok(NULL, " \t\n\r");
1071
1072 // If the line doesn't have at least two tokens, we can't determine the type.
1073 if (!face_str || !type_str) continue;
1074
1075 // --- Perform a direct, non-erroring check on the mathematical type string ---
1076 if (strcasecmp(type_str, "PERIODIC") == 0) {
1077 BCFace face_enum;
1078 // A malformed face string on a periodic line IS a fatal error.
1079 ierr = StringToBCFace(face_str, &face_enum); CHKERRQ(ierr);
1080 face_is_periodic[face_enum] = PETSC_TRUE;
1081 }
1082 // Any other type_str (e.g., "WALL", "INLET") is correctly and silently ignored.
1083 }
1084 fclose(file);
1085
1086 // --- Validate consistency within this file ---
1087 if (face_is_periodic[BC_FACE_NEG_X] != face_is_periodic[BC_FACE_POS_X])
1088 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Inconsistent X-periodicity in file '%s' for block %d. Both -Xi and +Xi must be periodic or neither.", bcs_filename, bi);
1089 if (face_is_periodic[BC_FACE_NEG_Y] != face_is_periodic[BC_FACE_POS_Y])
1090 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Inconsistent Y-periodicity in file '%s' for block %d. Both -Eta and +Eta must be periodic or neither.", bcs_filename, bi);
1091 if (face_is_periodic[BC_FACE_NEG_Z] != face_is_periodic[BC_FACE_POS_Z])
1092 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Inconsistent Z-periodicity in file '%s' for block %d. Both -Zeta and +Zeta must be periodic or neither.", bcs_filename, bi);
1093
1094 PetscBool local_is_periodic[3] = {face_is_periodic[BC_FACE_NEG_X], face_is_periodic[BC_FACE_NEG_Y], face_is_periodic[BC_FACE_NEG_Z]};
1095
1096 // --- Validate consistency across block files ---
1097 if (!is_set) {
1098 global_is_periodic[0] = local_is_periodic[0];
1099 global_is_periodic[1] = local_is_periodic[1];
1100 global_is_periodic[2] = local_is_periodic[2];
1101 is_set = PETSC_TRUE;
1102 } else {
1103 if (global_is_periodic[0] != local_is_periodic[0] || global_is_periodic[1] != local_is_periodic[1] || global_is_periodic[2] != local_is_periodic[2]) {
1104 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
1105 "Periodicity mismatch between blocks. Block 0 requires (I:%d, J:%d, K:%d), but block %d (file '%s') has (I:%d, J:%d, K:%d).",
1106 (int)global_is_periodic[0], (int)global_is_periodic[1], (int)global_is_periodic[2],
1107 bi, bcs_filename,
1108 (int)local_is_periodic[0], (int)local_is_periodic[1], (int)local_is_periodic[2]);
1109 }
1110 }
1111 } // end loop over blocks
1112
1113 periodic_flags[0] = (global_is_periodic[0]) ? 1 : 0;
1114 periodic_flags[1] = (global_is_periodic[1]) ? 1 : 0;
1115 periodic_flags[2] = (global_is_periodic[2]) ? 1 : 0;
1116
1117 LOG_ALLOW(GLOBAL, LOG_INFO, "Global periodicity determined: I-periodic=%d, J-periodic=%d, K-periodic=%d\n",
1118 periodic_flags[0], periodic_flags[1], periodic_flags[2]);
1119 }
1120
1121 // --- Part 3: Broadcast the final flags from rank 0 to all other ranks ---
1122 ierr = MPI_Bcast(periodic_flags, 3, MPIU_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
1123
1124 // --- All ranks now update their SimCtx ---
1125 simCtx->i_periodic = periodic_flags[0];
1126 simCtx->j_periodic = periodic_flags[1];
1127 simCtx->k_periodic = periodic_flags[2];
1128
1129 PetscFunctionReturn(0);
1130}
PetscErrorCode VerifyPathExistence(const char *path, PetscBool is_dir, PetscBool is_optional, const char *description, PetscBool *exists)
Internal helper implementation: VerifyPathExistence().
Definition io.c:1136
char ** bcs_files
Definition variables.h:958
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TrimWhitespace()

void TrimWhitespace ( char *  str)

Removes leading and trailing ASCII whitespace from a mutable string.

Parameters
[in,out]strNUL-terminated string compacted in place.

Removes leading and trailing ASCII whitespace from a mutable string.

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

See also
TrimWhitespace()

Definition at line 393 of file io.c.

393 {
394 if (!str) return;
395 if (str[0] == '\0') return;
396
397 char *start = str;
398 // Find the first non-whitespace character
399 while (isspace((unsigned char)*start)) {
400 start++;
401 }
402
403 // Find the end of the string
404 char *end = str + strlen(str) - 1;
405 // Move backwards from the end to find the last non-whitespace character
406 while (end > start && isspace((unsigned char)*end)) {
407 end--;
408 }
409
410 // Null-terminate after the last non-whitespace character
411 *(end + 1) = '\0';
412
413 // If there was leading whitespace, shift the string to the left
414 if (str != start) {
415 memmove(str, start, (end - start) + 2); // +2 to include the new null terminator
416 }
417}
Here is the caller graph for this function:

◆ CheckpointFieldIsEnabled()

PetscBool CheckpointFieldIsEnabled ( const SimCtx *  simCtx,
const FieldDescriptor *  descriptor 
)

Return whether a catalogued field belongs in the current checkpoint.

A checkpointed field whose subsystem is active (turbulence, dynamic LES, particles, wall model). These are exactly the fields a checkpoint read can load, so consumers of loaded state use the same predicate.

Parameters
[in]simCtxRun context whose active subsystems decide availability.
[in]descriptorCatalog entry to test.
Returns
PETSC_TRUE when the field is checkpointed in this run.

Return whether a catalogued field belongs in the current checkpoint.

Definition at line 72 of file io.c.

73{
74 const unsigned int availability = descriptor ? descriptor->availability : FIELD_AVAILABILITY_ALWAYS;
75
76 if (!simCtx || !descriptor || !(descriptor->capabilities & FIELD_CAPABILITY_CHECKPOINT)) return PETSC_FALSE;
77 if ((availability & FIELD_AVAILABILITY_TURBULENCE) && !simCtx->les) return PETSC_FALSE;
78 if ((availability & FIELD_AVAILABILITY_LES_DYNAMIC) && simCtx->les != DYNAMIC_SMAGORINSKY) return PETSC_FALSE;
79 if ((availability & FIELD_AVAILABILITY_PARTICLES) && simCtx->np <= 0) return PETSC_FALSE;
80 if ((availability & FIELD_AVAILABILITY_WALL_MODEL) && !simCtx->wallfunction) return PETSC_FALSE;
81 return PETSC_TRUE;
82}
@ FIELD_CAPABILITY_CHECKPOINT
@ FIELD_AVAILABILITY_ALWAYS
unsigned int capabilities
Here is the caller graph for this function:

◆ PicurvPhysicalTime()

PetscErrorCode PicurvPhysicalTime ( const SimCtx *  simCtx,
PetscReal  solver_time,
PetscReal *  physical 
)

Convert a solver time to physical seconds, t * L_ref / U_ref.

Runtime diagnostics record solver time; each also reports this physical time so a history can be plotted in seconds without the case's scales at hand.

Parameters
[in]simCtxContext carrying the reference scales.
[in]solver_timeTime in solver units.
[out]physicalTime in seconds.
Returns
Zero on success.

Convert a solver time to physical seconds, t * L_ref / U_ref.

Definition at line 3177 of file io.c.

3178{
3179 const FieldDimension time_dimension = FIELD_DIM_TIME;
3180 PetscReal scale = 1.0;
3181
3182 PetscFunctionBeginUser;
3183 PetscCheck(simCtx && physical, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
3184 "Context and physical-time output are required.");
3185 PetscCall(FieldDimensionReferenceScale(&simCtx->scaling, time_dimension, &scale));
3186 *physical = solver_time * scale;
3187 PetscFunctionReturn(0);
3188}
PetscErrorCode FieldDimensionReferenceScale(const ScalingCtx *scaling, FieldDimension dimension, PetscReal *scale)
Return the factor that turns a solver value of one dimension into physical units.
#define FIELD_DIM_TIME
Physical dimension as exponents of the reference length, velocity, and density.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PicurvFieldReferenceScale()

PetscErrorCode PicurvFieldReferenceScale ( SimCtx *  simCtx,
const char *  field_name,
PetscReal *  scale,
char *  description,
size_t  description_length 
)

Physical scale one field is multiplied by to leave non-dimensional form.

Each field's dimension is recorded once, on its entry in the Eulerian or particle field catalog; this resolves a name against both and applies the run's reference scales. Field dimensionalization, derived field statistics, and anything else needing a physical scale read it from here.

Parameters
[in]simCtxContext carrying the resolved reference scales.
[in]field_nameCase-insensitive catalogued field name or alias.
[out]scaleMultiplicative factor.
[out]descriptionOptional human-readable dimension, may be NULL.
[in]description_lengthCapacity of description.
Returns
0 on success; PETSC_ERR_ARG_UNKNOWN_TYPE for a name in neither catalog, and PETSC_ERR_ARG_WRONGSTATE for a field without a fixed dimension (bookkeeping, or staging storage whose dimension is its source's).

Physical scale one field is multiplied by to leave non-dimensional form.

The argument and return contract is documented in include/io.h.

See also
PicurvFieldReferenceScale()

Definition at line 3197 of file io.c.

3200{
3201 FieldDimension dimension;
3202 FieldId field_id = FIELD_ID_INVALID;
3204 PetscBool found = PETSC_FALSE;
3205
3206 PetscFunctionBeginUser;
3207 PetscCheck(simCtx && field_name && scale, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
3208 "Context, field name, and output scale are required.");
3209
3210 /* The dimension is a property of the field, recorded on its catalog entry. */
3211 PetscCall(FieldTryIdFromName(field_name, &field_id, &found));
3212 if (found) {
3213 const FieldDescriptor *descriptor = NULL;
3214
3215 PetscCall(FieldGetDescriptor(field_id, &descriptor));
3216 dimension = descriptor->dimension;
3217 } else {
3218 const ParticleFieldDescriptor *descriptor = NULL;
3219
3220 PetscCall(ParticleFieldTryIdFromName(field_name, &particle_id, &found));
3221 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE,
3222 "Field '%s' is in neither the Eulerian nor the particle field catalog.", field_name);
3223 PetscCall(ParticleFieldGetDescriptor(particle_id, &descriptor));
3224 dimension = descriptor->dimension;
3225 }
3226 PetscCheck(dimension.kind == FIELD_DIMENSION_FIXED, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
3227 "Field '%s' has no fixed physical dimension, so it has no reference scale.", field_name);
3228 PetscCall(FieldDimensionReferenceScale(&simCtx->scaling, dimension, scale));
3229 if (description && description_length) {
3230 PetscCall(FieldDimensionLabel(dimension, description, description_length));
3231 }
3232 PetscFunctionReturn(0);
3233}
@ FIELD_DIMENSION_FIXED
The exponents are the field's dimension.
PetscErrorCode FieldTryIdFromName(const char *field_name, FieldId *field_id, PetscBool *found)
Look a name up without treating an unknown name as an error.
FieldDimension dimension
FieldDimensionKind kind
PetscErrorCode FieldDimensionLabel(FieldDimension dimension, char *label, size_t length)
Write a printable form of a dimension, such as L^2 U or dimensionless.
@ FIELD_ID_INVALID
@ PARTICLE_FIELD_ID_INVALID
PetscErrorCode ParticleFieldTryIdFromName(const char *field_name, ParticleFieldId *field_id, PetscBool *found)
Look a name up without treating an unknown name as an error.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParsePostProcessingSettings()

PetscErrorCode ParsePostProcessingSettings ( SimCtx *  simCtx)

Initializes post-processing settings from a config file and command-line overrides.

This function establishes the configuration for a post-processing run by:

  1. Setting hardcoded default values in the PostProcessParams struct.
  2. Reading a configuration file to override the defaults.
  3. Parsing command-line options (-startTime, -endTime, etc.) which can override both the defaults and the file settings.
Parameters
simCtxThe pointer to the simulation context that contains the postprocessing file and struct.
Returns
PetscErrorCode

Initializes post-processing settings from a config file and command-line overrides.

Local to this translation unit.

Definition at line 3240 of file io.c.

3241{
3242 FILE *file;
3243 char line[1024];
3244 PetscBool startTimeSet, endTimeSet, timeStepSet;
3245
3246 PetscFunctionBeginUser;
3248
3249 if (!simCtx || !simCtx->pps) {
3250 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_NULL, "SimCtx or its pps member is NULL in ParsePostProcessingSettings.");
3251 }
3252
3253 char *configFile = simCtx->PostprocessingControlFile;
3254 PostProcessParams *pps = simCtx->pps;
3255
3256
3257 // --- 1. Set Sane Defaults First ---
3258 pps->startTime = 0;
3259 pps->endTime = 0;
3260 pps->timeStep = 1;
3261 pps->step_list_file[0] = '\0';
3262 pps->outputParticles = PETSC_FALSE;
3263 pps->particle_output_freq = simCtx->LoggingFrequency; // Default to logging frequency;
3264 strcpy(pps->process_pipeline, "");
3265 strcpy(pps->output_fields_instantaneous, "Ucat,P");
3266 strcpy(pps->output_prefix, "Field");
3267 strcpy(pps->particle_output_prefix,"Particle");
3268 strcpy(pps->particle_fields,"velocity,CellID,weight,pid");
3269 strcpy(pps->particle_pipeline,"");
3270 strncpy(pps->statistics_pipeline, "", MAX_PIPELINE_LENGTH - 1);
3271 strncpy(pps->statistics_output_prefix, "Stats", MAX_FILENAME_LENGTH - 1);
3272 strcpy(pps->particleExt,"dat"); // The input file format for particles.
3273 strcpy(pps->eulerianExt,"dat"); // The input file format for Eulerian fields.
3274 /* A negative source step means "the last step this recipe covers", so a recipe
3275 * that does not name one still derives from a bundle that exists. */
3276 pps->field_statistics_windows[0] = '\0';
3277 strcpy(pps->field_statistics_outputs, "mean,reynolds_stress,rms,tke,flux");
3278 strcpy(pps->field_statistics_formats, "vtk");
3279 strcpy(pps->field_statistics_output_prefix, "Field");
3280 pps->dimensionalize = PETSC_FALSE;
3282 pps->reference[0] = pps->reference[1] = pps->reference[2] = 1;
3283 strncpy(pps->source_dir, simCtx->output_dir, sizeof(pps->source_dir) - 1);
3284 pps->source_dir[sizeof(pps->source_dir) - 1] = '\0'; // Ensure null-termination
3285
3286 // --- 2. Parse the Configuration File (overrides defaults) ---
3287 file = fopen(configFile, "r");
3288 if (file) {
3289 LOG_ALLOW(GLOBAL, LOG_INFO, "Parsing post-processing config file: %s\n", configFile);
3290 while (fgets(line, sizeof(line), file)) {
3291 char *key, *value, *comment;
3292 comment = strchr(line, '#'); if (comment) *comment = '\0';
3293 TrimWhitespace(line); if (strlen(line) == 0) continue;
3294 key = strtok(line, "="); value = strtok(NULL, "=");
3295 if (key && value) {
3296 TrimWhitespace(key); TrimWhitespace(value);
3297 if (strcmp(key, "startTime") == 0) pps->startTime = atoi(value);
3298 else if (strcmp(key, "endTime") == 0) pps->endTime = atoi(value);
3299 else if (strcmp(key, "timeStep") == 0) pps->timeStep = atoi(value);
3300 else if (strcmp(key, "step_list_file") == 0) {
3301 strncpy(pps->step_list_file, value, sizeof(pps->step_list_file) - 1);
3302 pps->step_list_file[sizeof(pps->step_list_file) - 1] = '\0';
3303 }
3304 else if (strcmp(key, "output_particles") == 0) {
3305 if (strcasecmp(value, "true") == 0) pps->outputParticles = PETSC_TRUE;
3306 }
3307 else if (strcasecmp(key, "process_pipeline") == 0) {
3308 strncpy(pps->process_pipeline, value, MAX_PIPELINE_LENGTH - 1);
3309 pps->process_pipeline[MAX_PIPELINE_LENGTH - 1] = '\0'; // Ensure null-termination
3310 } else if (strcasecmp(key, "field_statistics_windows") == 0) {
3311 strncpy(pps->field_statistics_windows, value, MAX_FIELD_LIST_LENGTH - 1);
3313 } else if (strcasecmp(key, "field_statistics_formats") == 0) {
3314 strncpy(pps->field_statistics_formats, value, MAX_FIELD_LIST_LENGTH - 1);
3316 } else if (strcasecmp(key, "field_statistics_outputs") == 0) {
3317 strncpy(pps->field_statistics_outputs, value, MAX_FIELD_LIST_LENGTH - 1);
3319 } else if (strcasecmp(key, "field_statistics_source_step") == 0) {
3320 pps->field_statistics_source_step = atoi(value);
3321 } else if (strcasecmp(key, "dimensionalize") == 0) {
3322 pps->dimensionalize = (PetscBool)(strcasecmp(value, "true") == 0
3323 || strcmp(value, "1") == 0);
3324 } else if (strcasecmp(key, "field_statistics_output_prefix") == 0) {
3325 strncpy(pps->field_statistics_output_prefix, value,
3326 sizeof(pps->field_statistics_output_prefix) - 1);
3328 sizeof(pps->field_statistics_output_prefix) - 1] = '\0';
3329 } else if (strcasecmp(key, "output_fields_instantaneous") == 0) {
3330 strncpy(pps->output_fields_instantaneous, value, MAX_FIELD_LIST_LENGTH - 1);
3332 } else if (strcasecmp(key, "output_prefix") == 0) {
3333 strncpy(pps->output_prefix, value, MAX_FILENAME_LENGTH - 1);
3334 pps->output_prefix[MAX_FILENAME_LENGTH - 1] = '\0';
3335 } else if (strcasecmp(key, "particle_output_prefix") == 0) {
3336 strncpy(pps->particle_output_prefix, value, MAX_FILENAME_LENGTH - 1);
3338 } else if (strcasecmp(key, "particle_fields_instantaneous") == 0) {
3339 strncpy(pps->particle_fields, value, MAX_FIELD_LIST_LENGTH - 1);
3340 pps->particle_fields[MAX_FIELD_LIST_LENGTH - 1] = '\0';
3341 } else if (strcasecmp(key, "particle_pipeline") == 0) {
3342 strncpy(pps->particle_pipeline, value, MAX_PIPELINE_LENGTH - 1);
3343 pps->particle_pipeline[MAX_PIPELINE_LENGTH - 1] = '\0';
3344 } else if (strcasecmp(key, "particle_output_freq") == 0) {
3345 pps->particle_output_freq = atoi(value);
3346 } else if (strcasecmp(key, "statistics_pipeline") == 0) {
3347 strncpy(pps->statistics_pipeline, value, MAX_PIPELINE_LENGTH - 1);
3349 } else if (strcasecmp(key, "statistics_output_prefix") == 0) {
3350 strncpy(pps->statistics_output_prefix, value, MAX_FILENAME_LENGTH - 1);
3352 } else if (strcasecmp(key, "particleExt") == 0) {
3353 strncpy(pps->particleExt, value, sizeof(pps->particleExt) - 1);
3354 pps->particleExt[sizeof(pps->particleExt) - 1] = '\0';
3355 } else if (strcasecmp(key, "eulerianExt") == 0) {
3356 strncpy(pps->eulerianExt, value, sizeof(pps->eulerianExt) - 1);
3357 pps->eulerianExt[sizeof(pps->eulerianExt) - 1] = '\0';
3358 } else if (strcmp(key, "reference_ip") == 0) {pps->reference[0] = atoi(value);
3359 } else if (strcmp(key, "reference_jp") == 0) {pps->reference[1] = atoi(value);
3360 } else if (strcmp(key, "reference_kp") == 0) {pps->reference[2] = atoi(value);
3361 } else if (strcasecmp(key, "source_directory") == 0) {
3362 strncpy(pps->source_dir, value, sizeof(pps->source_dir) - 1);
3363 pps->source_dir[sizeof(pps->source_dir) - 1] = '\0';
3364 } else if (strcasecmp(key, "spectra_signature") == 0) {
3365 /* Spectra are computed by the conductor's Python stage, not here. The
3366 key exists so a change to the spectra recipe reaches the recipe
3367 fingerprint each step's output record carries; this executable has
3368 no use for it and accepting it silently keeps the log free of a
3369 warning that would appear on every post-processing run. */
3370 } else {
3371 LOG_ALLOW(GLOBAL, LOG_WARNING, "Unknown key '%s' in post-processing config file. Ignoring.\n", key);
3372 }
3373 // Add parsing for pipeline, fields, etc. in later phases
3374 }
3375 }
3376 fclose(file);
3377 } else {
3378 LOG_ALLOW(GLOBAL, LOG_WARNING, "Could not open post-processing config file '%s'. Using defaults and command-line overrides.\n", configFile);
3379 }
3380
3381 // --- 3. Parse Command-Line Options (overrides file settings and defaults) ---
3382 PetscOptionsGetInt(NULL, NULL, "-startTime", &pps->startTime, &startTimeSet);
3383 PetscOptionsGetInt(NULL, NULL, "-endTime", &pps->endTime, &endTimeSet);
3384 PetscCall(PetscOptionsGetInt(NULL, NULL, "-timeStep", &pps->timeStep, &timeStepSet));
3385 PetscCheck(pps->timeStep > 0, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE,
3386 "Post-processing timeStep must be positive (got %" PetscInt_FMT "). "
3387 "For one checkpoint, set startTime=endTime and timeStep=1.", pps->timeStep);
3388 PetscOptionsGetBool(NULL, NULL, "-output_particles", &pps->outputParticles, NULL);
3389
3390 if(pps->endTime==-1){
3391 pps->endTime = simCtx->StartStep + simCtx->StepsToRun; // Total steps if endTime is set to -1.
3392 }
3393
3394 // If only startTime is given on command line, run for a single step
3395 if (startTimeSet && !endTimeSet) {
3396 pps->endTime = pps->startTime;
3397 }
3398
3399 LOG_ALLOW(GLOBAL, LOG_INFO, "Post-processing configured to run from t=%d to t=%d with step %d. Particle output: %s.\n",
3400 pps->startTime, pps->endTime, pps->timeStep, pps->outputParticles ? "TRUE" : "FALSE");
3401
3402 LOG_ALLOW(GLOBAL, LOG_INFO, "Process Pipeline: %s\n", pps->process_pipeline);
3403 LOG_ALLOW(GLOBAL, LOG_INFO, "Instantaneous Output Fields: %s\n", pps->output_fields_instantaneous);
3404 LOG_ALLOW(GLOBAL, LOG_INFO, "Output Prefix: %s\n", pps->output_prefix);
3405 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Output Prefix: %s\n", pps->particle_output_prefix);
3406 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Fields: %s\n", pps->particle_fields);
3407 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Pipeline: %s\n", pps->particle_pipeline);
3408 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Output Frequency: %d\n", pps->particle_output_freq);
3409 LOG_ALLOW(GLOBAL, LOG_INFO, "Post input extensions: Eulerian='.%s', Particle='.%s'\n", pps->eulerianExt, pps->particleExt);
3410
3412 PetscFunctionReturn(0);
3413}
void TrimWhitespace(char *str)
Implementation of TrimWhitespace().
Definition io.c:393
char statistics_output_prefix[256]
basename for CSV output, e.g.
Definition variables.h:777
char particle_output_prefix[256]
Definition variables.h:772
char output_prefix[256]
Definition variables.h:769
char field_statistics_output_prefix[PETSC_MAX_PATH_LEN]
Analysis prefix for CSV summaries; VTK continues to use output_prefix.
Definition variables.h:787
#define MAX_FIELD_LIST_LENGTH
Definition variables.h:745
#define MAX_PIPELINE_LENGTH
Definition variables.h:744
char step_list_file[PETSC_MAX_PATH_LEN]
File listing the exact steps to process, one per line; empty uses the time controls.
Definition variables.h:763
PetscInt reference[3]
Definition variables.h:798
PetscInt timeStep
Definition variables.h:761
#define MAX_FILENAME_LENGTH
Definition variables.h:746
char statistics_pipeline[1024]
e.g.
Definition variables.h:776
char field_statistics_formats[1024]
Comma-separated formats: vtk for derived fields, csv for the convergence history.
Definition variables.h:785
char output_fields_instantaneous[1024]
Definition variables.h:768
char eulerianExt[8]
Definition variables.h:794
char particle_pipeline[1024]
Definition variables.h:770
char process_pipeline[1024]
Definition variables.h:767
PetscInt particle_output_freq
Definition variables.h:773
char particle_fields[1024]
Definition variables.h:771
PetscBool outputParticles
Definition variables.h:764
PetscInt field_statistics_source_step
Committed step supplying the state; negative means the step being processed.
Definition variables.h:791
char particleExt[8]
Definition variables.h:795
char field_statistics_windows[1024]
Comma-separated window names to derive; empty disables the pipeline.
Definition variables.h:781
char field_statistics_outputs[1024]
Comma-separated outputs: mean, reynolds_stress, rms, tke, flux.
Definition variables.h:783
PetscInt startTime
Definition variables.h:759
char PostprocessingControlFile[PETSC_MAX_PATH_LEN]
Definition variables.h:1057
Holds all configuration parameters for a post-processing run.
Definition variables.h:754
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseScalingInformation()

PetscErrorCode ParseScalingInformation ( SimCtx *  simCtx)

Parses physical scaling parameters from command-line options.

This function reads the reference length, velocity, and density from the PETSc options database (provided via -scaling_L_ref, etc.). It populates the simCtx->scaling struct and calculates the derived reference pressure. It sets default values of 1.0 for a fully non-dimensional case if the options are not provided.

Parameters
[in,out]simCtxThe simulation context whose 'scaling' member will be populated.
Returns
PetscErrorCode

Parses physical scaling parameters from command-line options.

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

See also
ParseScalingInformation()

Definition at line 3425 of file io.c.

3426{
3427 PetscErrorCode ierr;
3428 PetscBool flg;
3429
3430 PetscFunctionBeginUser;
3432
3433 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx is NULL in ParseScalingInformation");
3434
3435 // --- 1. Set default values to 1.0 ---
3436 // This represents a purely non-dimensional run if no scaling is provided.
3437 simCtx->scaling.L_ref = 1.0;
3438 simCtx->scaling.U_ref = 1.0;
3439 simCtx->scaling.rho_ref = 1.0;
3440
3441 // --- 2. Read overrides from the command line / control file ---
3442 ierr = PetscOptionsGetReal(NULL, NULL, "-scaling_L_ref", &simCtx->scaling.L_ref, &flg); CHKERRQ(ierr);
3443 ierr = PetscOptionsGetReal(NULL, NULL, "-scaling_U_ref", &simCtx->scaling.U_ref, &flg); CHKERRQ(ierr);
3444 ierr = PetscOptionsGetReal(NULL, NULL, "-scaling_rho_ref", &simCtx->scaling.rho_ref, &flg); CHKERRQ(ierr);
3445
3446 // --- 3. Calculate derived scaling factors ---
3447 // Check for division by zero to be safe, though U_ref should be positive.
3448 if (simCtx->scaling.U_ref <= 0.0) {
3449 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Reference velocity U_ref must be positive. Got %g", (double)simCtx->scaling.U_ref);
3450 }
3451 simCtx->scaling.P_ref = simCtx->scaling.rho_ref * simCtx->scaling.U_ref * simCtx->scaling.U_ref;
3452
3453 // --- 4. Log the final, effective scales for verification ---
3454 LOG(GLOBAL, LOG_INFO, "---------------- Physical Scales Initialized -----------------\n");
3455 LOG(GLOBAL, LOG_INFO, " L_ref: %.4f, U_ref: %.4f, rho_ref: %.4f, P_ref: %.4f\n",
3456 simCtx->scaling.L_ref, simCtx->scaling.U_ref, simCtx->scaling.rho_ref, simCtx->scaling.P_ref);
3457 LOG(GLOBAL, LOG_INFO, "--------------------------------------------------------------\n");
3458
3460 PetscFunctionReturn(0);
3461}
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:84
PetscReal P_ref
Definition variables.h:844
PetscReal rho_ref
Definition variables.h:843
PetscReal U_ref
Definition variables.h:842
Here is the caller graph for this function: