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

Implementation of data input/output routines, focusing on grid configuration. More...

#include "io.h"
#include "checksum.h"
#include "field_catalog.h"
#include "particle_field_catalog.h"
#include "statistics_accumulator.h"
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
Include dependency graph for io.c:

Go to the source code of this file.

Macros

#define PICURV_CHECKPOINT_FORMAT   "picurv-checkpoint"
 
#define PICURV_CHECKPOINT_VERSION   1
 
#define PICURV_CHECKPOINTS_DIRECTORY   "checkpoints"
 
#define PICURV_EULERIAN_DIRECTORY   "eulerian"
 
#define PICURV_PARTICLE_DIRECTORY   "particles"
 
#define PICURV_STATISTICS_DIRECTORY   "statistics"
 
#define PICURV_CHECKPOINT_STEP_WIDTH   12
 
#define __FUNCT__   "ReadGridGenerationInputs"
 
#define __FUNCT__   "ReadGridFile"
 
#define __FUNCT__   "GetDrivenSeamFluxFlag"
 
#define __FUNCT__   "ParseAllBoundaryConditions"
 
#define __FUNCT__   "DeterminePeriodicity"
 
#define __FUNCT__   "ReadFieldData"
 
#define __FUNCT__   "RestoreDrivenFluxTarget"
 
#define __FUNCT__   "ReadStatisticsWindowState"
 
#define PICURV_STATISTICS_REQUIRE(suffix, getter, target)
 
#define __FUNCT__   "RestoreFieldStatisticsState"
 
#define __FUNCT__   "WriteFieldData"
 
#define __FUNCT__   "WriteStatisticsFields"
 
#define __FUNCT__   "ParsePostProcessingSettings"
 
#define __FUNCT__   "ParseScalingInformation"
 

Functions

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.
 
static PetscBool CheckpointFieldIsEnabled (const SimCtx *simCtx, const FieldDescriptor *descriptor)
 Return whether a catalogued field belongs in the current checkpoint.
 
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.
 
static PetscErrorCode GatherVectorToRankZero (Vec field_vec, Vec *sequential_vec)
 Gather a vector in decomposition-independent natural ordering onto rank zero.
 
static PetscErrorCode ComputeCheckpointGeometrySHA256 (SimCtx *simCtx, UserCtx *user, char digest_hex[65])
 Compute and cache a rank-count-independent hash of the active grid geometry.
 
static PetscErrorCode FormatCheckpointStepDirectory (const char *root, PetscInt step, char *path, size_t path_size)
 Format the canonical directory name for one completed step.
 
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.
 
static PetscErrorCode CreateCheckpointDirectoryCollective (const SimCtx *simCtx, const char *path)
 Create one directory on rank zero and report failures collectively.
 
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, PetscBool *rans_saved)
 Validate a committed bundle and return selected authoritative metadata.
 
void TrimWhitespace (char *str)
 Implementation of TrimWhitespace().
 
PetscBool ShouldWriteDataOutput (const SimCtx *simCtx, PetscInt completed_step)
 Implementation of ShouldWriteDataOutput().
 
PetscErrorCode ReadGridGenerationInputs (UserCtx *user)
 Internal helper implementation: ReadGridGenerationInputs().
 
PetscErrorCode PopulateFinestUserGridResolutionFromOptions (UserCtx *finest_users, PetscInt nblk)
 Internal helper implementation: PopulateFinestUserGridResolutionFromOptions().
 
PetscErrorCode ReadGridFile (UserCtx *user)
 Internal helper implementation: ReadGridFile().
 
void FreeBC_ParamList (BC_Param *head)
 Implementation of FreeBC_ParamList().
 
PetscErrorCode StringToBCFace (const char *str, BCFace *face_out)
 Internal helper implementation: StringToBCFace().
 
PetscErrorCode StringToBCType (const char *str, BCType *type_out)
 Internal helper implementation: StringToBCType().
 
PetscErrorCode StringToBCHandlerType (const char *str, BCHandlerType *handler_out)
 Internal helper implementation: StringToBCHandlerType().
 
PetscErrorCode ValidateBCHandlerForBCType (BCType type, BCHandlerType handler)
 Internal helper implementation: ValidateBCHandlerForBCType().
 
PetscErrorCode GetBCParamReal (BC_Param *params, const char *key, PetscReal *value_out, PetscBool *found)
 Internal helper implementation: GetBCParamReal().
 
PetscErrorCode GetBCParamBool (BC_Param *params, const char *key, PetscBool *value_out, PetscBool *found)
 Internal helper implementation: GetBCParamBool().
 
PetscErrorCode GetDrivenSeamFluxFlag (BC_Param *params, PetscBool *value_out, PetscBool *found)
 Implementation of GetDrivenSeamFluxFlag().
 
PetscErrorCode ParseAllBoundaryConditions (UserCtx *user, const char *bcs_input_filename)
 Internal helper implementation: ParseAllBoundaryConditions().
 
PetscErrorCode DeterminePeriodicity (SimCtx *simCtx)
 Internal helper implementation: DeterminePeriodicity().
 
PetscErrorCode VerifyPathExistence (const char *path, PetscBool is_dir, PetscBool is_optional, const char *description, PetscBool *exists)
 Internal helper implementation: VerifyPathExistence().
 
PetscErrorCode ReadFieldData (UserCtx *user, const char *field_name, Vec field_vec, const char *ext)
 Internal helper implementation: ReadFieldData().
 
static PetscErrorCode RestoreDrivenFluxTarget (SimCtx *simCtx, UserCtx *user, const char *checkpoint_directory)
 Restore a latched driven-flow flux target from a checkpoint manifest.
 
PetscErrorCode ReadSimulationFields (UserCtx *user, PetscInt ti)
 Internal helper implementation: ReadSimulationFields().
 
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.
 
PetscErrorCode RestoreFieldStatisticsState (SimCtx *simCtx, PetscInt ti)
 Implementation of RestoreFieldStatisticsState().
 
PetscErrorCode ReadSwarmField (UserCtx *user, const char *field_name, const char *ext)
 Internal helper implementation: ReadSwarmField().
 
PetscErrorCode ReadSwarmIntField (UserCtx *user, const char *field_name, const char *ext)
 Internal helper implementation: ReadSwarmIntField().
 
PetscErrorCode ReadAllSwarmFields (UserCtx *user, PetscInt ti)
 Internal helper implementation: ReadAllSwarmFields().
 
PetscErrorCode ReadCheckpointParticleCount (UserCtx *user, PetscInt ti, PetscInt *particle_count)
 Implementation of ReadCheckpointParticleCount().
 
PetscErrorCode WriteFieldData (UserCtx *user, const char *field_name, Vec field_vec, const char *ext)
 Internal helper implementation: WriteFieldData().
 
PetscErrorCode WriteSimulationFields (UserCtx *user, const char *checkpoint_directory)
 Implementation of WriteSimulationFields().
 
static PetscErrorCode WriteStatisticsFields (UserCtx *user, const char *checkpoint_directory)
 Write every window's accumulator payloads for one block into a bundle.
 
PetscErrorCode WriteSwarmField (UserCtx *user, const char *field_name, const char *ext)
 Implementation of WriteSwarmField().
 
PetscErrorCode WriteSwarmIntField (UserCtx *user, const char *field_name, const char *ext)
 Internal helper implementation: WriteSwarmIntField().
 
PetscErrorCode WriteAllSwarmFields (UserCtx *user, const char *checkpoint_directory)
 Internal helper implementation: WriteAllSwarmFields().
 
static PetscErrorCode WriteCheckpointPayloadEntry (FILE *manifest, const char *checkpoint_directory, PetscInt payload_index, const char *relative_path, const char *kind, const char *field_name, PetscInt block, const char *layout, PetscInt components, const char *logical_type, PetscInt global_size, const char *encoding)
 Append one payload inventory entry to a checkpoint manifest.
 
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.
 
PetscErrorCode WriteCheckpointBundle (SimCtx *simCtx, const char *reason)
 Implementation of the transactional full-state checkpoint coordinator.
 
PetscErrorCode VecToArrayOnRank0 (Vec inVec, PetscInt *N, double **arrayOut)
 Internal helper implementation: VecToArrayOnRank0().
 
PetscErrorCode SwarmFieldToArrayOnRank0 (DM swarm, const char *field_name, PetscInt *n_total_particles, PetscInt *n_components, PetscDataType *field_type_out, void **gathered_array)
 Internal helper implementation: SwarmFieldToArrayOnRank0().
 
PetscErrorCode DisplayBanner (SimCtx *simCtx)
 Emit the rank-zero startup summary from the effective simulation context.
 
PetscErrorCode ParsePostProcessingSettings (SimCtx *simCtx)
 Internal helper implementation: ParsePostProcessingSettings().
 
PetscErrorCode ParseScalingInformation (SimCtx *simCtx)
 Implementation of ParseScalingInformation().
 
PetscInt ReadDataFileToArray (const char *filename, double **data_out, PetscInt *Nout, MPI_Comm comm)
 Implementation of ReadDataFileToArray().
 
PetscErrorCode ReadPositionsFromFile (PetscInt timeIndex, UserCtx *user, double **coordsArray, PetscInt *Ncoords)
 Internal helper implementation: ReadPositionsFromFile().
 
PetscErrorCode ReadFieldDataToRank0 (PetscInt timeIndex, const char *fieldName, UserCtx *user, double **scalarArray, PetscInt *Nscalars)
 Internal helper implementation: ReadFieldDataToRank0().
 

Variables

static PetscInt g_nblk_from_file = 0
 Stores the number of blocks read from the grid file.
 
static PetscInt * g_IMs_from_file = NULL
 Caches the IM dimensions for all blocks read from the grid file.
 
static PetscInt * g_JMs_from_file = NULL
 Caches the JM dimensions for all blocks read from the grid file.
 
static PetscInt * g_KMs_from_file = NULL
 Caches the KM dimensions for all blocks read from the grid file.
 
static PetscBool g_file_has_been_read = PETSC_FALSE
 A flag to ensure the grid file is read only once.
 

Detailed Description

Implementation of data input/output routines, focusing on grid configuration.

This module provides functions to parse 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.c.

Macro Definition Documentation

◆ PICURV_CHECKPOINT_FORMAT

#define PICURV_CHECKPOINT_FORMAT   "picurv-checkpoint"

Definition at line 21 of file io.c.

◆ PICURV_CHECKPOINT_VERSION

#define PICURV_CHECKPOINT_VERSION   1

Definition at line 22 of file io.c.

◆ PICURV_CHECKPOINTS_DIRECTORY

#define PICURV_CHECKPOINTS_DIRECTORY   "checkpoints"

Definition at line 23 of file io.c.

◆ PICURV_EULERIAN_DIRECTORY

#define PICURV_EULERIAN_DIRECTORY   "eulerian"

Definition at line 24 of file io.c.

◆ PICURV_PARTICLE_DIRECTORY

#define PICURV_PARTICLE_DIRECTORY   "particles"

Definition at line 25 of file io.c.

◆ PICURV_STATISTICS_DIRECTORY

#define PICURV_STATISTICS_DIRECTORY   "statistics"

Definition at line 26 of file io.c.

◆ PICURV_CHECKPOINT_STEP_WIDTH

#define PICURV_CHECKPOINT_STEP_WIDTH   12

Definition at line 27 of file io.c.

◆ __FUNCT__ [1/13]

#define __FUNCT__   "ReadGridGenerationInputs"

Definition at line 442 of file io.c.

◆ __FUNCT__ [2/13]

#define __FUNCT__   "ReadGridFile"

Definition at line 442 of file io.c.

◆ __FUNCT__ [3/13]

#define __FUNCT__   "GetDrivenSeamFluxFlag"

Definition at line 442 of file io.c.

◆ __FUNCT__ [4/13]

#define __FUNCT__   "ParseAllBoundaryConditions"

Definition at line 442 of file io.c.

◆ __FUNCT__ [5/13]

#define __FUNCT__   "DeterminePeriodicity"

Definition at line 442 of file io.c.

◆ __FUNCT__ [6/13]

#define __FUNCT__   "ReadFieldData"

Definition at line 442 of file io.c.

◆ __FUNCT__ [7/13]

#define __FUNCT__   "RestoreDrivenFluxTarget"

Definition at line 442 of file io.c.

◆ __FUNCT__ [8/13]

#define __FUNCT__   "ReadStatisticsWindowState"

Definition at line 442 of file io.c.

◆ PICURV_STATISTICS_REQUIRE

#define PICURV_STATISTICS_REQUIRE (   suffix,
  getter,
  target 
)
Value:
do { \
PetscCall(PetscSNPrintf(option_name, sizeof(option_name), \
"-checkpoint_statistics_window_%" PetscInt_FMT "_" suffix, window)); \
PetscCall(getter(options, NULL, option_name, target, &found)); \
PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED, \
"Statistics continuation requested, but '%s' records no %s for window " \
"%" PetscInt_FMT ".", metadata_path, suffix, window); \
} while (0)

◆ __FUNCT__ [9/13]

#define __FUNCT__   "RestoreFieldStatisticsState"

Definition at line 442 of file io.c.

◆ __FUNCT__ [10/13]

#define __FUNCT__   "WriteFieldData"

Definition at line 442 of file io.c.

◆ __FUNCT__ [11/13]

#define __FUNCT__   "WriteStatisticsFields"

Definition at line 442 of file io.c.

◆ __FUNCT__ [12/13]

#define __FUNCT__   "ParsePostProcessingSettings"

Definition at line 442 of file io.c.

◆ __FUNCT__ [13/13]

#define __FUNCT__   "ParseScalingInformation"

Definition at line 442 of file io.c.

Function Documentation

◆ CopyOwnedLocalScalarToGlobal()

static PetscErrorCode CopyOwnedLocalScalarToGlobal ( DM  dm,
Vec  local_vec,
Vec  global_vec 
)
static

Copies the owned entries of a ghosted scalar DMDA vector to its global vector.

DMLocalToGlobal with INSERT_VALUES is unsupported for multidirection-periodic DMDAs in PETSc. Explicitly copying only the uniquely owned region preserves INSERT semantics without reducing duplicate periodic ghost entries.

Definition at line 51 of file io.c.

52{
53 DMDALocalInfo info;
54 const PetscScalar ***local_array = NULL;
55 PetscScalar ***global_array = NULL;
56
57 PetscFunctionBeginUser;
58
59 PetscCall(DMDAGetLocalInfo(dm, &info));
60 PetscCall(DMDAVecGetArrayRead(dm, local_vec, &local_array));
61 PetscCall(DMDAVecGetArray(dm, global_vec, &global_array));
62 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k)
63 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j)
64 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i)
65 global_array[k][j][i] = local_array[k][j][i];
66 PetscCall(DMDAVecRestoreArray(dm, global_vec, &global_array));
67 PetscCall(DMDAVecRestoreArrayRead(dm, local_vec, &local_array));
68 PetscFunctionReturn(0);
69}
Here is the caller graph for this function:

◆ CheckpointFieldIsEnabled()

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

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 || simCtx->rans)) return PETSC_FALSE;
78 if ((availability & FIELD_AVAILABILITY_LES) && !simCtx->les) return PETSC_FALSE;
79 if ((availability & FIELD_AVAILABILITY_RANS) && !simCtx->rans) return PETSC_FALSE;
80 if ((availability & FIELD_AVAILABILITY_PARTICLES) && simCtx->np <= 0) return PETSC_FALSE;
81 return PETSC_TRUE;
82}
@ FIELD_CAPABILITY_CHECKPOINT
@ FIELD_AVAILABILITY_RANS
@ FIELD_AVAILABILITY_LES
@ FIELD_AVAILABILITY_PARTICLES
@ FIELD_AVAILABILITY_TURBULENCE
@ FIELD_AVAILABILITY_ALWAYS
unsigned int capabilities
unsigned int availability
PetscInt rans
Definition variables.h:821
PetscInt np
Definition variables.h:827
PetscInt les
Definition variables.h:821
Here is the caller graph for this function:

◆ FormatStatisticsPath()

static PetscErrorCode FormatStatisticsPath ( const char *  root,
PetscInt  window,
PetscInt  block,
const char *  payload_name,
char *  path,
size_t  path_size 
)
static

Format any level of the statistics subtree, from the root down to one payload.

This is the only place the statistics layout is written down. The directory creator, the payload writer, the manifest inventory, and the restart reader all ask for the level they need, so a path cannot be built one way and looked for another.

Statistics payloads are block scoped exactly as Eulerian payloads are, with a window level above the block level, so a multiblock run keeps one accumulator tree per block under each window.

Pass root NULL for a bundle-relative path, and stop early by passing a negative window or block, or a NULL payload_name.

Definition at line 99 of file io.c.

101{
102 size_t used = 0;
103
104 PetscFunctionBeginUser;
105 PetscCheck(path != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Output path is required.");
106 if (root) PetscCall(PetscSNPrintf(path, path_size, "%s/%s", root, PICURV_STATISTICS_DIRECTORY));
107 else PetscCall(PetscSNPrintf(path, path_size, "%s", PICURV_STATISTICS_DIRECTORY));
108 if (window < 0) PetscFunctionReturn(0);
109
110 PetscCall(PetscStrlen(path, &used));
111 PetscCall(PetscSNPrintf(path + used, path_size - used, "/window_%04" PetscInt_FMT, window));
112 if (block < 0) PetscFunctionReturn(0);
113
114 PetscCall(PetscStrlen(path, &used));
115 PetscCall(PetscSNPrintf(path + used, path_size - used, "/block_%04" PetscInt_FMT, block));
116 if (!payload_name) PetscFunctionReturn(0);
117
118 PetscCall(PetscStrlen(path, &used));
119 PetscCall(PetscSNPrintf(path + used, path_size - used, "/%s.dat", payload_name));
120 PetscFunctionReturn(0);
121}
#define PICURV_STATISTICS_DIRECTORY
Definition io.c:26
Here is the caller graph for this function:

◆ GatherVectorToRankZero()

static PetscErrorCode GatherVectorToRankZero ( Vec  field_vec,
Vec *  sequential_vec 
)
static

Gather a vector in decomposition-independent natural ordering onto rank zero.

Definition at line 124 of file io.c.

125{
126 DM dm = NULL;
127 const char *dm_type = NULL;
128 Vec natural_vec = NULL;
129 VecScatter scatter = NULL;
130
131 PetscFunctionBeginUser;
132 PetscCheck(field_vec != NULL && sequential_vec != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
133 "A source vector and output vector pointer are required.");
134 *sequential_vec = NULL;
135
136 PetscCall(VecGetDM(field_vec, &dm));
137 if (dm) PetscCall(DMGetType(dm, &dm_type));
138 if (dm_type && !strcmp(dm_type, DMDA)) {
139 PetscCall(DMDACreateNaturalVector(dm, &natural_vec));
140 PetscCall(DMDAGlobalToNaturalBegin(dm, field_vec, INSERT_VALUES, natural_vec));
141 PetscCall(DMDAGlobalToNaturalEnd(dm, field_vec, INSERT_VALUES, natural_vec));
142 }
143
144 PetscCall(VecScatterCreateToZero(natural_vec ? natural_vec : field_vec, &scatter, sequential_vec));
145 PetscCall(VecScatterBegin(scatter, natural_vec ? natural_vec : field_vec, *sequential_vec,
146 INSERT_VALUES, SCATTER_FORWARD));
147 PetscCall(VecScatterEnd(scatter, natural_vec ? natural_vec : field_vec, *sequential_vec,
148 INSERT_VALUES, SCATTER_FORWARD));
149 PetscCall(VecScatterDestroy(&scatter));
150 PetscCall(VecDestroy(&natural_vec));
151 PetscFunctionReturn(0);
152}
Here is the caller graph for this function:

◆ ComputeCheckpointGeometrySHA256()

static PetscErrorCode ComputeCheckpointGeometrySHA256 ( SimCtx simCtx,
UserCtx user,
char  digest_hex[65] 
)
static

Compute and cache a rank-count-independent hash of the active grid geometry.

Definition at line 155 of file io.c.

156{
157 PicurvSHA256Context hash_context;
158 char metadata[256];
159
160 PetscFunctionBeginUser;
161 PetscCheck(simCtx != NULL && user != NULL && digest_hex != NULL,
162 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Geometry hash inputs cannot be NULL.");
163 if (simCtx->checkpointGeometryHashReady) {
164 PetscCall(PetscStrncpy(digest_hex, simCtx->checkpointGeometrySHA256, 65));
165 PetscFunctionReturn(0);
166 }
167
168 if (simCtx->rank == 0) PicurvSHA256Init(&hash_context);
169 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
170 FieldView coordinates;
171 Vec sequential_vec = NULL;
172
173 PetscCall(FieldGetView(&user[block], FIELD_ID_COORDINATES, &coordinates));
174 PetscCall(GatherVectorToRankZero(coordinates.global_vec, &sequential_vec));
175 if (simCtx->rank == 0) {
176 const PetscScalar *values = NULL;
177 PetscInt value_count = 0;
178 size_t metadata_length = 0;
179
180 PetscCall(PetscSNPrintf(metadata, sizeof(metadata),
181 "block=%" PetscInt_FMT ";im=%" PetscInt_FMT ";jm=%" PetscInt_FMT
182 ";km=%" PetscInt_FMT ";periodic=%d,%d,%d;",
183 block, user[block].IM, user[block].JM, user[block].KM,
184 (int)simCtx->i_periodic, (int)simCtx->j_periodic, (int)simCtx->k_periodic));
185 PetscCall(PetscStrlen(metadata, &metadata_length));
186 PicurvSHA256Update(&hash_context, metadata, metadata_length);
187 PetscCall(VecGetSize(sequential_vec, &value_count));
188 PicurvSHA256Update(&hash_context, &value_count, sizeof(value_count));
189 PetscCall(VecGetArrayRead(sequential_vec, &values));
190 PicurvSHA256Update(&hash_context, values, (size_t)value_count * sizeof(*values));
191 PetscCall(VecRestoreArrayRead(sequential_vec, &values));
192 }
193 PetscCall(VecDestroy(&sequential_vec));
194 }
195
196 if (simCtx->rank == 0) PicurvSHA256FinalHex(&hash_context, digest_hex);
197 PetscCallMPI(MPI_Bcast(digest_hex, 65, MPI_CHAR, 0, PETSC_COMM_WORLD));
198 PetscCall(PetscStrncpy(simCtx->checkpointGeometrySHA256, digest_hex, 65));
199 simCtx->checkpointGeometryHashReady = PETSC_TRUE;
200 PetscFunctionReturn(0);
201}
void PicurvSHA256Init(PicurvSHA256Context *context)
Initialize an incremental SHA-256 calculation.
Definition checksum.c:62
void PicurvSHA256Update(PicurvSHA256Context *context, const void *data, size_t length)
Add bytes to an incremental SHA-256 calculation.
Definition checksum.c:74
void PicurvSHA256FinalHex(PicurvSHA256Context *context, char digest_hex[65])
Finish a SHA-256 calculation and return a lowercase hexadecimal digest.
Definition checksum.c:98
Incremental SHA-256 state.
Definition checksum.h:14
PetscErrorCode FieldGetView(UserCtx *user, FieldId field_id, FieldView *view)
Resolve the existing DM and global/local vectors for one field.
@ FIELD_ID_COORDINATES
Non-owning runtime objects resolved for one field and UserCtx.
static PetscErrorCode GatherVectorToRankZero(Vec field_vec, Vec *sequential_vec)
Gather a vector in decomposition-independent natural ordering onto rank zero.
Definition io.c:124
PetscMPIInt rank
Definition variables.h:698
PetscInt block_number
Definition variables.h:790
PetscInt k_periodic
Definition variables.h:791
PetscBool checkpointGeometryHashReady
Definition variables.h:722
PetscInt i_periodic
Definition variables.h:791
char checkpointGeometrySHA256[65]
Definition variables.h:721
PetscInt j_periodic
Definition variables.h:791
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FormatCheckpointStepDirectory()

static PetscErrorCode FormatCheckpointStepDirectory ( const char *  root,
PetscInt  step,
char *  path,
size_t  path_size 
)
static

Format the canonical directory name for one completed step.

Definition at line 204 of file io.c.

205{
206 PetscFunctionBeginUser;
207 PetscCheck(root != NULL && path != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
208 "Checkpoint root and output path are required.");
209 PetscCall(PetscSNPrintf(path, path_size, "%s/%s/step_%0*" PetscInt_FMT,
211 PetscFunctionReturn(0);
212}
#define PICURV_CHECKPOINT_STEP_WIDTH
Definition io.c:27
#define PICURV_CHECKPOINTS_DIRECTORY
Definition io.c:23
Here is the caller graph for this function:

◆ ResolveCheckpointStepDirectory()

static PetscErrorCode ResolveCheckpointStepDirectory ( const char *  source_root,
PetscInt  step,
char *  path,
size_t  path_size 
)
static

Resolve either an exact bundle or a run/output root to one step bundle.

Definition at line 215 of file io.c.

217{
218 char metadata_path[PETSC_MAX_PATH_LEN];
219 PetscBool exact_bundle = PETSC_FALSE;
220
221 PetscFunctionBeginUser;
222 PetscCheck(source_root != NULL && path != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
223 "Checkpoint source and output path are required.");
224 PetscCall(PetscSNPrintf(metadata_path, sizeof(metadata_path), "%s/checkpoint.meta", source_root));
225 PetscCall(PetscTestFile(metadata_path, 'r', &exact_bundle));
226 if (exact_bundle) PetscCall(PetscStrncpy(path, source_root, path_size));
227 else PetscCall(FormatCheckpointStepDirectory(source_root, step, path, path_size));
228 PetscFunctionReturn(0);
229}
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
Here is the call graph for this function:
Here is the caller graph for this function:

◆ CreateCheckpointDirectoryCollective()

static PetscErrorCode CreateCheckpointDirectoryCollective ( const SimCtx simCtx,
const char *  path 
)
static

Create one directory on rank zero and report failures collectively.

Definition at line 232 of file io.c.

233{
234 PetscMPIInt create_failed = 0;
235
236 PetscFunctionBeginUser;
237 PetscCheck(simCtx != NULL && path != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
238 "Simulation context and directory path are required.");
239 if (simCtx->rank == 0 && mkdir(path, 0777) != 0) {
240 struct stat directory_stat;
241 if (errno != EEXIST || stat(path, &directory_stat) != 0 || !S_ISDIR(directory_stat.st_mode)) {
242 create_failed = 1;
243 }
244 }
245 PetscCallMPI(MPI_Bcast(&create_failed, 1, MPI_INT, 0, PETSC_COMM_WORLD));
246 PetscCheck(!create_failed, PETSC_COMM_WORLD, PETSC_ERR_FILE_OPEN,
247 "Unable to create checkpoint directory '%s'.", path);
248 PetscFunctionReturn(0);
249}
Here is the caller graph for this function:

◆ ValidateCheckpointBundle()

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,
PetscBool *  rans_saved 
)
static

Validate a committed bundle and return selected authoritative metadata.

Definition at line 252 of file io.c.

260{
261 char metadata_path[PETSC_MAX_PATH_LEN];
262 char commit_path[PETSC_MAX_PATH_LEN];
263 char expected_digest[65] = "";
264 char actual_digest[65] = "";
265 char format[64] = "";
266 char saved_geometry_digest[65] = "";
267 char current_geometry_digest[65] = "";
268 PetscOptions options = NULL;
269 PetscInt version = 0;
270 PetscInt saved_step = -1;
271 PetscInt payload_count = 0;
272 PetscInt saved_particle_count = 0;
273 PetscReal saved_time = 0.0;
274 PetscBool saved_particles = PETSC_FALSE;
275 PetscBool saved_les = PETSC_FALSE;
276 PetscBool saved_rans = PETSC_FALSE;
277 PetscBool found = PETSC_FALSE;
278 FILE *commit_file = NULL;
279
280 PetscFunctionBeginUser;
281 PetscCheck(simCtx != NULL && user != NULL && checkpoint_directory != NULL,
282 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Checkpoint validation inputs cannot be NULL.");
283 PetscCall(PetscSNPrintf(metadata_path, sizeof(metadata_path), "%s/checkpoint.meta", checkpoint_directory));
284 PetscCall(PetscSNPrintf(commit_path, sizeof(commit_path), "%s/COMMITTED", checkpoint_directory));
285
286 PetscCall(PetscTestFile(metadata_path, 'r', &found));
287 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_OPEN,
288 "Checkpoint metadata is missing: %s", metadata_path);
289 PetscCall(PetscTestFile(commit_path, 'r', &found));
290 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_OPEN,
291 "Checkpoint is not committed: %s", checkpoint_directory);
292
293 commit_file = fopen(commit_path, "r");
294 PetscCheck(commit_file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
295 "Unable to read checkpoint commit marker '%s'.", commit_path);
296 PetscCheck(fgets(expected_digest, sizeof(expected_digest), commit_file) != NULL,
297 PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
298 "Checkpoint commit marker '%s' is empty.", commit_path);
299 PetscCheck(fclose(commit_file) == 0, PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
300 "Unable to close checkpoint commit marker '%s'.", commit_path);
301 TrimWhitespace(expected_digest);
302 PetscCheck(strlen(expected_digest) == 64, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
303 "Checkpoint commit marker '%s' does not contain a SHA-256 digest.", commit_path);
304 PetscCall(PicurvSHA256File(metadata_path, actual_digest));
305 PetscCheck(!strcmp(expected_digest, actual_digest), PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
306 "Checkpoint metadata hash mismatch in '%s'.", checkpoint_directory);
307
308 PetscCall(PetscOptionsCreate(&options));
309 PetscCall(PetscOptionsInsertFile(PETSC_COMM_WORLD, options, metadata_path, PETSC_TRUE));
310 PetscCall(PetscOptionsGetString(options, NULL, "-checkpoint_format", format, sizeof(format), &found));
311 PetscCheck(found && !strcmp(format, PICURV_CHECKPOINT_FORMAT), PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
312 "Unsupported checkpoint format '%s' in '%s'.", found ? format : "<missing>", metadata_path);
313 PetscCall(PetscOptionsGetInt(options, NULL, "-checkpoint_version", &version, &found));
314 PetscCheck(found && version == PICURV_CHECKPOINT_VERSION, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
315 "Unsupported checkpoint version %" PetscInt_FMT " in '%s'; expected %d.",
316 version, metadata_path, PICURV_CHECKPOINT_VERSION);
317 PetscCall(PetscOptionsGetInt(options, NULL, "-checkpoint_step", &saved_step, &found));
318 PetscCheck(found && saved_step == expected_step, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
319 "Checkpoint step is %" PetscInt_FMT ", expected %" PetscInt_FMT ".",
320 saved_step, expected_step);
321 PetscCall(PetscOptionsGetReal(options, NULL, "-checkpoint_time", &saved_time, &found));
322 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
323 "Checkpoint '%s' does not record physical time.", metadata_path);
324 PetscCall(PetscOptionsGetString(options, NULL, "-checkpoint_geometry_sha256",
325 saved_geometry_digest, sizeof(saved_geometry_digest), &found));
326 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
327 "Checkpoint '%s' does not record geometry identity.", metadata_path);
328 PetscCall(ComputeCheckpointGeometrySHA256(simCtx, user, current_geometry_digest));
329 PetscCheck(!strcmp(saved_geometry_digest, current_geometry_digest), PETSC_COMM_WORLD, PETSC_ERR_ARG_INCOMP,
330 "Checkpoint geometry/layout does not match the active grid.");
331
332 PetscCall(PetscOptionsGetInt(options, NULL, "-checkpoint_payload_count", &payload_count, &found));
333 PetscCheck(found && payload_count > 0, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
334 "Checkpoint '%s' has no payload inventory.", metadata_path);
335 PetscCall(PetscOptionsGetInt(options, NULL, "-checkpoint_particle_count",
336 &saved_particle_count, &found));
337 PetscCheck(found && saved_particle_count >= 0, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
338 "Checkpoint '%s' has no valid particle count.", metadata_path);
339 PetscCall(PetscOptionsGetBool(options, NULL, "-checkpoint_particles", &saved_particles, &found));
340 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
341 "Checkpoint '%s' does not record particle-state availability.", metadata_path);
342 PetscCall(PetscOptionsGetBool(options, NULL, "-checkpoint_les", &saved_les, &found));
343 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
344 "Checkpoint '%s' does not record LES-state availability.", metadata_path);
345 PetscCall(PetscOptionsGetBool(options, NULL, "-checkpoint_rans", &saved_rans, &found));
346 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
347 "Checkpoint '%s' does not record RANS-state availability.", metadata_path);
348 for (PetscInt payload = 0; payload < payload_count; ++payload) {
349 char option_name[96];
350 char relative_path[PETSC_MAX_PATH_LEN];
351 char payload_path[PETSC_MAX_PATH_LEN];
352 long long expected_bytes = -1;
353 char expected_bytes_text[64];
354 struct stat payload_stat;
355
356 PetscCall(PetscSNPrintf(option_name, sizeof(option_name),
357 "-checkpoint_payload_%" PetscInt_FMT "_path", payload));
358 PetscCall(PetscOptionsGetString(options, NULL, option_name,
359 relative_path, sizeof(relative_path), &found));
360 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
361 "Checkpoint payload %" PetscInt_FMT " has no path.", payload);
362 PetscCall(PetscSNPrintf(option_name, sizeof(option_name),
363 "-checkpoint_payload_%" PetscInt_FMT "_bytes", payload));
364 PetscCall(PetscOptionsGetString(options, NULL, option_name,
365 expected_bytes_text, sizeof(expected_bytes_text), &found));
366 if (found) expected_bytes = strtoll(expected_bytes_text, NULL, 10);
367 PetscCheck(found && expected_bytes >= 0, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
368 "Checkpoint payload %" PetscInt_FMT " has no valid byte size.", payload);
369 PetscCall(PetscSNPrintf(payload_path, sizeof(payload_path), "%s/%s",
370 checkpoint_directory, relative_path));
371 PetscCheck(stat(payload_path, &payload_stat) == 0 && S_ISREG(payload_stat.st_mode),
372 PETSC_COMM_WORLD, PETSC_ERR_FILE_OPEN,
373 "Checkpoint payload is missing: %s", payload_path);
374 PetscCheck((long long)payload_stat.st_size == expected_bytes,
375 PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
376 "Checkpoint payload '%s' is %lld bytes; expected %lld.",
377 payload_path, (long long)payload_stat.st_size, expected_bytes);
378 }
379 PetscCall(PetscOptionsDestroy(&options));
380 if (physical_time) *physical_time = saved_time;
381 if (particle_count) *particle_count = saved_particle_count;
382 if (particles_saved) *particles_saved = saved_particles;
383 if (les_saved) *les_saved = saved_les;
384 if (rans_saved) *rans_saved = saved_rans;
385 PetscFunctionReturn(0);
386}
PetscErrorCode PicurvSHA256File(const char *path, char digest_hex[65])
Compute the lowercase SHA-256 digest of a file.
Definition checksum.c:130
#define PICURV_CHECKPOINT_FORMAT
Definition io.c:21
void TrimWhitespace(char *str)
Implementation of TrimWhitespace().
Definition io.c:399
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
#define PICURV_CHECKPOINT_VERSION
Definition io.c:22
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TrimWhitespace()

void TrimWhitespace ( char *  str)

Implementation of TrimWhitespace().

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 399 of file io.c.

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

◆ ShouldWriteDataOutput()

PetscBool ShouldWriteDataOutput ( const SimCtx simCtx,
PetscInt  completed_step 
)

Implementation 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 432 of file io.c.

433{
434 if (!simCtx) {
435 return PETSC_FALSE;
436 }
437 return (PetscBool)(simCtx->tiout > 0 && completed_step > 0 && completed_step % simCtx->tiout == 0);
438}
PetscInt tiout
Definition variables.h:707
Here is the caller graph for this function:

◆ ReadGridGenerationInputs()

PetscErrorCode ReadGridGenerationInputs ( UserCtx user)

Internal helper implementation: ReadGridGenerationInputs().

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

Local to this translation unit.

Definition at line 447 of file io.c.

448{
449 PetscErrorCode ierr;
450 SimCtx *simCtx = user->simCtx;
451 PetscInt nblk = simCtx->block_number;
452 PetscInt block_index = user->_this;
453 PetscBool found;
454
455 // Temporary arrays to hold the parsed values for ALL blocks
456 PetscInt *IMs = NULL, *JMs = NULL, *KMs = NULL, *cgrids = NULL;
457 PetscReal *xMins = NULL, *xMaxs = NULL, *rxs = NULL;
458 PetscReal *yMins = NULL, *yMaxs = NULL, *rys = NULL;
459 PetscReal *zMins = NULL, *zMaxs = NULL, *rzs = NULL;
460
461 PetscFunctionBeginUser;
463
464 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Reading generated grid inputs for block %d.\n", simCtx->rank, block_index);
465
466 if (block_index >= nblk) {
467 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Block index %d is out of range for nblk=%d", block_index, nblk);
468 }
469
470 // --- Allocate temporary storage for all array options ---
471 ierr = PetscMalloc4(nblk, &IMs, nblk, &JMs, nblk, &KMs, nblk, &cgrids); CHKERRQ(ierr);
472 ierr = PetscMalloc6(nblk, &xMins, nblk, &xMaxs, nblk, &rxs, nblk, &yMins, nblk, &yMaxs, nblk, &rys); CHKERRQ(ierr);
473 ierr = PetscMalloc3(nblk, &zMins, nblk, &zMaxs, nblk, &rzs); CHKERRQ(ierr);
474
475 // --- Set default values for the temporary arrays ---
476 for (PetscInt i = 0; i < nblk; ++i) {
477 IMs[i] = 10; JMs[i] = 10; KMs[i] = 10; cgrids[i] = 0;
478 xMins[i] = 0.0; xMaxs[i] = 1.0; rxs[i] = 1.0;
479 yMins[i] = 0.0; yMaxs[i] = 1.0; rys[i] = 1.0;
480 zMins[i] = 0.0; zMaxs[i] = 1.0; rzs[i] = 1.0;
481 }
482
483 // --- Parse the array options from the command line / control file ---
484 PetscInt count;
485 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-im", IMs, &count, &found); CHKERRQ(ierr);
486 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-jm", JMs, &count, &found); CHKERRQ(ierr);
487 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-km", KMs, &count, &found); CHKERRQ(ierr);
488 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-xMins", xMins, &count, &found); CHKERRQ(ierr);
489 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-xMaxs", xMaxs, &count, &found); CHKERRQ(ierr);
490 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-rxs", rxs, &count, &found); CHKERRQ(ierr);
491 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-yMins", yMins, &count, &found); CHKERRQ(ierr);
492 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-yMaxs", yMaxs, &count, &found); CHKERRQ(ierr);
493 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-rys", rys, &count, &found); CHKERRQ(ierr);
494 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-zMins", zMins, &count, &found); CHKERRQ(ierr);
495 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-zMaxs", zMaxs, &count, &found); CHKERRQ(ierr);
496 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-rzs", rzs, &count, &found); CHKERRQ(ierr);
497 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-cgrids", cgrids, &count, &found); CHKERRQ(ierr);
498
499 // --- Assign the parsed values to the specific UserCtx struct passed in ---
500 user->IM = IMs[block_index];
501 user->JM = JMs[block_index];
502 user->KM = KMs[block_index];
503 user->Min_X = xMins[block_index];
504 user->Max_X = xMaxs[block_index];
505 user->rx = rxs[block_index];
506 user->Min_Y = yMins[block_index];
507 user->Max_Y = yMaxs[block_index];
508 user->ry = rys[block_index];
509 user->Min_Z = zMins[block_index];
510 user->Max_Z = zMaxs[block_index];
511 user->rz = rzs[block_index];
512 user->cgrid = cgrids[block_index];
513
514 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Block %d grid generation inputs set: IM=%d, JM=%d, KM=%d\n",
515 simCtx->rank, block_index, user->IM, user->JM, user->KM);
516 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Block %d bounds: X=[%.2f, %.2f], Y=[%.2f, %.2f], Z=[%.2f, %.2f]\n",
517 simCtx->rank, block_index, user->Min_X, user->Max_X, user->Min_Y, user->Max_Y, user->Min_Z, user->Max_Z);
518
519 // --- Clean up temporary storage ---
520 ierr = PetscFree4(IMs, JMs, KMs, cgrids); CHKERRQ(ierr);
521 ierr = PetscFree6(xMins, xMaxs, rxs, yMins, yMaxs, rys); CHKERRQ(ierr);
522 ierr = PetscFree3(zMins, zMaxs, rzs); CHKERRQ(ierr);
523
525 PetscFunctionReturn(0);
526}
#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:859
@ 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:850
PetscInt cgrid
Definition variables.h:926
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:909
PetscReal Min_X
Definition variables.h:921
PetscInt KM
Definition variables.h:920
PetscInt _this
Definition variables.h:924
PetscReal ry
Definition variables.h:925
PetscReal Max_Y
Definition variables.h:921
PetscReal rz
Definition variables.h:925
PetscInt JM
Definition variables.h:920
PetscReal Min_Z
Definition variables.h:921
PetscReal Max_X
Definition variables.h:921
PetscReal Min_Y
Definition variables.h:921
PetscInt IM
Definition variables.h:920
PetscReal rx
Definition variables.h:925
PetscReal Max_Z
Definition variables.h:921
The master context for the entire simulation.
Definition variables.h:695
Here is the caller graph for this function:

◆ PopulateFinestUserGridResolutionFromOptions()

PetscErrorCode PopulateFinestUserGridResolutionFromOptions ( UserCtx finest_users,
PetscInt  nblk 
)

Internal helper implementation: PopulateFinestUserGridResolutionFromOptions().

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

Local to this translation unit.

Definition at line 532 of file io.c.

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

◆ ReadGridFile()

PetscErrorCode ReadGridFile ( UserCtx user)

Internal helper implementation: ReadGridFile().

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

Local to this translation unit.

Definition at line 581 of file io.c.

582{
583 PetscErrorCode ierr;
584 SimCtx *simCtx = user->simCtx;
585 PetscInt block_index = user->_this;
586
587 PetscFunctionBeginUser;
589
590 // --- One-Time Read and Broadcast Logic ---
592 LOG_ALLOW_SYNC(GLOBAL, LOG_INFO, "First call to ReadGridFile. Reading and broadcasting grid file header from '%s'...\n", simCtx->grid_file);
593 PetscMPIInt rank = simCtx->rank;
594 PetscInt nblk = simCtx->block_number;
595
596 if (rank == 0) {
597 FILE *fd = fopen(simCtx->grid_file, "r");
598 if (!fd) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open file: %s", simCtx->grid_file);
599
600 // Read and validate the canonical PICGRID header.
601 char firstTok[32] = {0};
602 if (fscanf(fd, "%31s", firstTok) != 1)
603 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Empty grid file: %s", simCtx->grid_file);
604 if (strcmp(firstTok, "PICGRID") != 0)
605 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
606 "Grid file %s must begin with the canonical PICGRID header.", simCtx->grid_file);
607 if (fscanf(fd, "%d", &g_nblk_from_file) != 1)
608 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Expected number of blocks after \"PICGRID\" in %s", simCtx->grid_file);
609 if (g_nblk_from_file != nblk) {
610 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED, "Mismatch: -nblk is %d but grid file specifies %d blocks.", nblk, g_nblk_from_file);
611 }
612
613 ierr = PetscMalloc3(nblk, &g_IMs_from_file, nblk, &g_JMs_from_file, nblk, &g_KMs_from_file); CHKERRQ(ierr);
614 for (PetscInt i = 0; i < nblk; ++i) {
615 if (fscanf(fd, "%d %d %d\n", &g_IMs_from_file[i], &g_JMs_from_file[i], &g_KMs_from_file[i]) != 3) {
616 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Expected 3 integers for block %d in %s", i, simCtx->grid_file);
617 }
618 }
619 fclose(fd);
620 }
621
622 // Broadcast nblk to verify (optional, good practice)
623 ierr = MPI_Bcast(&g_nblk_from_file, 1, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
624
625 // Allocate on other ranks before receiving the broadcast
626 if (rank != 0) {
627 ierr = PetscMalloc3(nblk, &g_IMs_from_file, nblk, &g_JMs_from_file, nblk, &g_KMs_from_file); CHKERRQ(ierr);
628 }
629
630 // Broadcast the data arrays
631 ierr = MPI_Bcast(g_IMs_from_file, nblk, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
632 ierr = MPI_Bcast(g_JMs_from_file, nblk, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
633 ierr = MPI_Bcast(g_KMs_from_file, nblk, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
634
635 g_file_has_been_read = PETSC_TRUE;
636 LOG_ALLOW(GLOBAL, LOG_INFO, "Grid file header read and broadcast complete.\n");
637 }
638
639 // --- Per-Block Assignment Logic (runs on every call) ---
640 user->IM = g_IMs_from_file[block_index];
641 user->JM = g_JMs_from_file[block_index];
642 user->KM = g_KMs_from_file[block_index];
643
644 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Set file inputs for Block %d: IM=%d, JM=%d, KM=%d\n",
645 simCtx->rank, block_index, user->IM, user->JM, user->KM);
646
648 PetscFunctionReturn(0);
649}
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:795
Here is the caller graph for this function:

◆ FreeBC_ParamList()

void FreeBC_ParamList ( BC_Param head)

Implementation of FreeBC_ParamList().

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 664 of file io.c.

664 {
665 BC_Param *current = head;
666 while (current != NULL) {
667 BC_Param *next = current->next;
668 PetscFree(current->key);
669 PetscFree(current->value);
670 PetscFree(current);
671 current = next;
672 }
673}
struct BC_Param_s * next
Definition variables.h:339
char * key
Definition variables.h:337
char * value
Definition variables.h:338
A node in a linked list for storing key-value parameters from the bcs.dat file.
Definition variables.h:336
Here is the caller graph for this function:

◆ StringToBCFace()

PetscErrorCode StringToBCFace ( const char *  str,
BCFace face_out 
)

Internal helper implementation: StringToBCFace().

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

Local to this translation unit.

Definition at line 679 of file io.c.

679 {
680 if (strcasecmp(str, "-Xi") == 0) *face_out = BC_FACE_NEG_X;
681 else if (strcasecmp(str, "+Xi") == 0) *face_out = BC_FACE_POS_X;
682 else if (strcasecmp(str, "-Eta") == 0) *face_out = BC_FACE_NEG_Y;
683 else if (strcasecmp(str, "+Eta") == 0) *face_out = BC_FACE_POS_Y;
684 else if (strcasecmp(str, "-Zeta") == 0) *face_out = BC_FACE_NEG_Z;
685 else if (strcasecmp(str, "+Zeta") == 0) *face_out = BC_FACE_POS_Z;
686 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown face specifier: %s", str);
687 return 0;
688}
@ BC_FACE_NEG_X
Definition variables.h:262
@ BC_FACE_POS_Z
Definition variables.h:264
@ BC_FACE_POS_Y
Definition variables.h:263
@ BC_FACE_NEG_Z
Definition variables.h:264
@ BC_FACE_POS_X
Definition variables.h:262
@ BC_FACE_NEG_Y
Definition variables.h:263
Here is the caller graph for this function:

◆ StringToBCType()

PetscErrorCode StringToBCType ( const char *  str,
BCType type_out 
)

Internal helper implementation: StringToBCType().

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

Local to this translation unit.

Definition at line 694 of file io.c.

694 {
695 if (strcasecmp(str, "WALL") == 0) *type_out = WALL;
696 else if (strcasecmp(str, "SYMMETRY") == 0) *type_out = SYMMETRY;
697 else if (strcasecmp(str, "INLET") == 0) *type_out = INLET;
698 else if (strcasecmp(str, "OUTLET") == 0) *type_out = OUTLET;
699 else if (strcasecmp(str, "PERIODIC") == 0) *type_out = PERIODIC;
700 // ... add other BCTypes here ...
701 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown BC Type string: %s", str);
702 return 0;
703}
@ INLET
Definition variables.h:290
@ SYMMETRY
Definition variables.h:288
@ OUTLET
Definition variables.h:289
@ PERIODIC
Definition variables.h:292
@ WALL
Definition variables.h:286
Here is the caller graph for this function:

◆ StringToBCHandlerType()

PetscErrorCode StringToBCHandlerType ( const char *  str,
BCHandlerType handler_out 
)

Internal helper implementation: StringToBCHandlerType().

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

Local to this translation unit.

Definition at line 709 of file io.c.

709 {
710 if (strcasecmp(str, "noslip") == 0) *handler_out = BC_HANDLER_WALL_NOSLIP;
711 else if (strcasecmp(str, "constant_velocity") == 0) *handler_out = BC_HANDLER_INLET_CONSTANT_VELOCITY;
712 else if (strcasecmp(str, "conservation") == 0) *handler_out = BC_HANDLER_OUTLET_CONSERVATION;
713 else if (strcasecmp(str, "parabolic") == 0) *handler_out = BC_HANDLER_INLET_PARABOLIC;
714 else if (strcasecmp(str, "prescribed_flow") == 0) *handler_out = BC_HANDLER_INLET_PROFILE_FROM_FILE;
715 else if (strcasecmp(str,"geometric") == 0) *handler_out = BC_HANDLER_PERIODIC_GEOMETRIC;
716 else if (strcasecmp(str,"constant_flux") == 0) *handler_out = BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX;
717 else if (strcasecmp(str,"initial_flux") == 0) *handler_out = BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX;
718 // ... add other BCHandlerTypes here ...
719 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown BC Handler string: %s", str);
720 return 0;
721}
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:316
@ BC_HANDLER_INLET_PARABOLIC
Definition variables.h:309
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
Definition variables.h:308
@ BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX
Definition variables.h:319
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:318
@ BC_HANDLER_INLET_PROFILE_FROM_FILE
Definition variables.h:310
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:305
@ BC_HANDLER_OUTLET_CONSERVATION
Definition variables.h:314
Here is the caller graph for this function:

◆ ValidateBCHandlerForBCType()

PetscErrorCode ValidateBCHandlerForBCType ( BCType  type,
BCHandlerType  handler 
)

Internal helper implementation: ValidateBCHandlerForBCType().

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

Local to this translation unit.

Definition at line 727 of file io.c.

727 {
728 switch (type) {
729 case OUTLET:
730 if(handler != BC_HANDLER_OUTLET_CONSERVATION) return PETSC_ERR_ARG_WRONG;
731 break;
732 case WALL:
733 if (handler != BC_HANDLER_WALL_NOSLIP && handler != BC_HANDLER_WALL_MOVING) return PETSC_ERR_ARG_WRONG;
734 break;
735 case INLET:
736 if (handler != BC_HANDLER_INLET_CONSTANT_VELOCITY &&
737 handler != BC_HANDLER_INLET_PARABOLIC &&
738 handler != BC_HANDLER_INLET_PROFILE_FROM_FILE) return PETSC_ERR_ARG_WRONG;
739 break;
740 case PERIODIC:
741 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;
742 // ... add other validation cases here ...
743 default: break;
744 }
745 return 0; // Combination is valid
746}
@ BC_HANDLER_WALL_MOVING
Definition variables.h:306
Here is the caller graph for this function:

◆ GetBCParamReal()

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

Internal helper implementation: GetBCParamReal().

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

Local to this translation unit.

Definition at line 752 of file io.c.

752 {
753 *found = PETSC_FALSE;
754 *value_out = 0.0;
755 if (!key) return 0; // No key to search for
756
757 BC_Param *current = params;
758 while (current) {
759 if (strcasecmp(current->key, key) == 0) {
760 *value_out = atof(current->value);
761 *found = PETSC_TRUE;
762 return 0; // Found it, we're done
763 }
764 current = current->next;
765 }
766 return 0; // It's not an error to not find the key.
767}
Here is the caller graph for this function:

◆ GetBCParamBool()

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

Internal helper implementation: GetBCParamBool().

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

Local to this translation unit.

Definition at line 773 of file io.c.

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

◆ GetDrivenSeamFluxFlag()

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

Implementation of GetDrivenSeamFluxFlag().

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 814 of file io.c.

815{
816 PetscErrorCode ierr;
817 PetscFunctionBeginUser;
818
819 ierr = GetBCParamBool(params, "enforce_seam_flux", value_out, found); CHKERRQ(ierr);
820 if (!*found) {
821 ierr = GetBCParamBool(params, "apply_trim", value_out, found); CHKERRQ(ierr);
822 }
823 PetscFunctionReturn(0);
824}
PetscErrorCode GetBCParamBool(BC_Param *params, const char *key, PetscBool *value_out, PetscBool *found)
Internal helper implementation: GetBCParamBool().
Definition io.c:773
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 
)

Internal helper implementation: ParseAllBoundaryConditions().

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 837 of file io.c.

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

◆ DeterminePeriodicity()

PetscErrorCode DeterminePeriodicity ( SimCtx simCtx)

Internal helper implementation: DeterminePeriodicity().

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 1024 of file io.c.

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

Internal helper implementation: VerifyPathExistence().

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

Local to this translation unit.

Definition at line 1128 of file io.c.

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

◆ ReadFieldData()

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

Internal helper implementation: ReadFieldData().

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

Local to this translation unit.

Definition at line 1174 of file io.c.

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

◆ RestoreDrivenFluxTarget()

static PetscErrorCode RestoreDrivenFluxTarget ( SimCtx simCtx,
UserCtx user,
const char *  checkpoint_directory 
)
static

Restore a latched driven-flow flux target from a checkpoint manifest.

Only initial_flux needs this. Its target is derived from the field the run originally started with, so re-measuring it after a restart would silently retarget the controller at whatever the flux had drifted to. constant_flux reads its target from the bcs file on every run and is deliberately left alone, so that editing target_flux between segments still takes effect.

Checkpoints written before this metadata existed simply have no entry; the controller then falls back to re-measuring and says so.

Parameters
[in,out]simCtxSimulation context receiving the target.
[in]userBlock context, for its boundary handler types.
[in]checkpoint_directoryDirectory holding the validated manifest.
Returns
PetscErrorCode 0 on success.

Definition at line 1412 of file io.c.

1414{
1415 char metadata_path[PETSC_MAX_PATH_LEN];
1416 PetscOptions options = NULL;
1417 PetscBool uses_initial_flux = PETSC_FALSE;
1418 PetscBool saved_latched = PETSC_FALSE;
1419 PetscBool found = PETSC_FALSE;
1420 PetscReal saved_target = 0.0;
1421
1422 PetscFunctionBeginUser;
1423
1424 for (PetscInt face = 0; face < 6; ++face) {
1426 uses_initial_flux = PETSC_TRUE;
1427 break;
1428 }
1429 }
1430 if (!uses_initial_flux) PetscFunctionReturn(0);
1431
1432 PetscCall(PetscSNPrintf(metadata_path, sizeof(metadata_path),
1433 "%s/checkpoint.meta", checkpoint_directory));
1434 PetscCall(PetscOptionsCreate(&options));
1435 PetscCall(PetscOptionsInsertFile(PETSC_COMM_WORLD, options, metadata_path, PETSC_TRUE));
1436 PetscCall(PetscOptionsGetBool(options, NULL, "-checkpoint_driven_flux_latched", &saved_latched, &found));
1437 if (found && saved_latched) {
1438 PetscCall(PetscOptionsGetReal(options, NULL, "-checkpoint_driven_flux_target", &saved_target, &found));
1439 } else {
1440 found = PETSC_FALSE;
1441 }
1442 PetscCall(PetscOptionsDestroy(&options));
1443
1444 if (found) {
1445 simCtx->targetVolumetricFlux = saved_target;
1446 simCtx->drivenFluxTargetLatched = PETSC_TRUE;
1448 "Driven Flow: restored latched target volumetric flux %.6e from checkpoint '%s'.\n",
1449 (double)saved_target, checkpoint_directory);
1450 } else {
1452 "Driven Flow: checkpoint '%s' records no latched flux target; the initial_flux "
1453 "controller will re-measure it from the restarted field.\n", checkpoint_directory);
1454 }
1455
1456 PetscFunctionReturn(0);
1457}
PetscReal targetVolumetricFlux
Definition variables.h:807
PetscBool drivenFluxTargetLatched
Definition variables.h:812
Here is the caller graph for this function:

◆ ReadSimulationFields()

PetscErrorCode ReadSimulationFields ( UserCtx user,
PetscInt  ti 
)

Internal helper implementation: ReadSimulationFields().

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

Local to this translation unit.

Definition at line 1463 of file io.c.

1464{
1465 SimCtx *simCtx = user->simCtx;
1466 const char *source_path = NULL;
1467 char checkpoint_directory[PETSC_MAX_PATH_LEN];
1468 PetscReal checkpoint_time = 0.0;
1469 PetscBool particles_saved = PETSC_FALSE;
1470 PetscBool les_saved = PETSC_FALSE;
1471 PetscBool rans_saved = PETSC_FALSE;
1472
1473 PetscFunctionBeginUser;
1474 if(simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR){
1475 source_path = simCtx->pps->source_dir;
1476 } else if(simCtx->exec_mode == EXEC_MODE_SOLVER){
1477 source_path = simCtx->restart_dir;
1478 } else{
1479 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Invalid execution mode for reading simulation fields.");
1480 }
1481
1482 PetscCall(ResolveCheckpointStepDirectory(source_path, ti,
1483 checkpoint_directory, sizeof(checkpoint_directory)));
1484 PetscCall(ValidateCheckpointBundle(simCtx, user - user->_this,
1485 checkpoint_directory, ti, &checkpoint_time, NULL,
1486 &particles_saved, &les_saved, &rans_saved));
1487 PetscCheck(!(simCtx->np > 0 && !strcmp(simCtx->particleRestartMode, "load")) || particles_saved,
1488 PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
1489 "Particle restart_mode=load was requested, but checkpoint step %" PetscInt_FMT
1490 " contains no particle state.", ti);
1491 if (simCtx->exec_mode == EXEC_MODE_SOLVER && user->_this == 0) {
1492 simCtx->ti = checkpoint_time;
1493 if (ti == simCtx->StartStep) simCtx->StartTime = checkpoint_time;
1494 PetscCall(RestoreDrivenFluxTarget(simCtx, user, checkpoint_directory));
1495 }
1496
1497 LOG_ALLOW(GLOBAL, LOG_INFO, "Reading Eulerian checkpoint fields for block %d from '%s'.\n",
1498 user->_this, checkpoint_directory);
1499 PetscCall(PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer),
1500 "%s/%s/block_%04" PetscInt_FMT,
1501 checkpoint_directory, PICURV_EULERIAN_DIRECTORY, user->_this));
1502 simCtx->current_io_directory = simCtx->_io_context_buffer;
1503
1504 for (PetscInt raw_id = 0; raw_id < FIELD_ID_COUNT; ++raw_id) {
1505 const FieldDescriptor *descriptor = NULL;
1506 FieldView view;
1507
1508 PetscCall(FieldGetDescriptor((FieldId)raw_id, &descriptor));
1509 if (!CheckpointFieldIsEnabled(simCtx, descriptor)) continue;
1510 if ((descriptor->availability & FIELD_AVAILABILITY_PARTICLES) &&
1511 (!particles_saved || strcmp(simCtx->particleRestartMode, "load"))) continue;
1512 if ((descriptor->availability & FIELD_AVAILABILITY_LES) && !les_saved) continue;
1513 if ((descriptor->availability & FIELD_AVAILABILITY_RANS) && !rans_saved) continue;
1514 if ((descriptor->availability & FIELD_AVAILABILITY_TURBULENCE) &&
1515 !((simCtx->les && les_saved) || (simCtx->rans && rans_saved))) continue;
1516 PetscCall(FieldGetView(user, descriptor->id, &view));
1517 PetscCall(ReadFieldData(user, descriptor->canonical_name, view.global_vec, "dat"));
1518 if (view.local_vec) PetscCall(UpdateLocalGhosts(user, descriptor->id));
1519 }
1520 if (simCtx->rans) {
1521 PetscCall(VecCopy(user->K_Omega, user->K_Omega_o));
1522 PetscCall(UpdateLocalGhosts(user, FIELD_ID_K_OMEGA_O));
1523 }
1524 simCtx->restartHistoryAvailable = PETSC_TRUE;
1525 simCtx->current_io_directory = NULL;
1526 PetscFunctionReturn(0);
1527}
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_K_OMEGA_O
@ FIELD_ID_COUNT
Immutable metadata for one field identity.
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, PetscBool *rans_saved)
Validate a committed bundle and return selected authoritative metadata.
Definition io.c:252
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:1412
PetscErrorCode ReadFieldData(UserCtx *user, const char *field_name, Vec field_vec, const char *ext)
Internal helper implementation: ReadFieldData().
Definition io.c:1174
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
static PetscBool CheckpointFieldIsEnabled(const SimCtx *simCtx, const FieldDescriptor *descriptor)
Return whether a catalogued field belongs in the current checkpoint.
Definition io.c:72
#define PICURV_EULERIAN_DIRECTORY
Definition io.c:24
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
Definition setup.c:1838
PetscReal StartTime
Definition variables.h:709
Vec K_Omega_o
Definition variables.h:982
Vec K_Omega
Definition variables.h:982
PetscInt StartStep
Definition variables.h:705
char particleRestartMode[16]
Definition variables.h:833
char source_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:598
PostProcessParams * pps
Definition variables.h:890
@ EXEC_MODE_SOLVER
Definition variables.h:668
@ EXEC_MODE_POSTPROCESSOR
Definition variables.h:669
char _io_context_buffer[PETSC_MAX_PATH_LEN]
Definition variables.h:719
ExecutionMode exec_mode
Definition variables.h:714
PetscBool restartHistoryAvailable
Definition variables.h:723
PetscReal ti
Definition variables.h:704
char restart_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:716
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadStatisticsWindowState()

static PetscErrorCode ReadStatisticsWindowState ( PetscOptions  options,
PetscInt  window,
const char *  metadata_path,
PetscReal  checkpoint_time,
PetscReal  step_size,
ExecutionMode  exec_mode,
PicurvWindow state 
)
static

Restore one window's scalar bookkeeping from a validated manifest.

Definition at line 1533 of file io.c.

1537{
1538 char option_name[128];
1539 char saved_name[PICURV_WINDOW_NAME_LENGTH] = "";
1540 char saved_digest[65] = "";
1542 char saved_state[32] = "";
1543 char current_digest[65] = "";
1544 PetscBool found = PETSC_FALSE;
1545 PetscBool name_matches = PETSC_FALSE;
1546
1547 PetscFunctionBeginUser;
1549
1550#define PICURV_STATISTICS_REQUIRE(suffix, getter, target) \
1551 do { \
1552 PetscCall(PetscSNPrintf(option_name, sizeof(option_name), \
1553 "-checkpoint_statistics_window_%" PetscInt_FMT "_" suffix, window)); \
1554 PetscCall(getter(options, NULL, option_name, target, &found)); \
1555 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED, \
1556 "Statistics continuation requested, but '%s' records no %s for window " \
1557 "%" PetscInt_FMT ".", metadata_path, suffix, window); \
1558 } while (0)
1559
1560 PetscCall(PetscSNPrintf(option_name, sizeof(option_name),
1561 "-checkpoint_statistics_window_%" PetscInt_FMT "_name", window));
1562 PetscCall(PetscOptionsGetString(options, NULL, option_name, saved_name, sizeof(saved_name), &found));
1563 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
1564 "Statistics continuation requested, but '%s' records no name for window %" PetscInt_FMT ".",
1565 metadata_path, window);
1566 PetscCall(PetscStrcmp(saved_name, state->definition.name, &name_matches));
1567 PetscCheck(name_matches, PETSC_COMM_WORLD, PETSC_ERR_ARG_INCOMP,
1568 "Statistics window %" PetscInt_FMT " is '%s' in the checkpoint but '%s' in this run. "
1569 "Window order and names must match to continue.",
1570 window, saved_name, state->definition.name);
1571
1572 PetscCall(PetscSNPrintf(option_name, sizeof(option_name),
1573 "-checkpoint_statistics_window_%" PetscInt_FMT "_hash", window));
1574 PetscCall(PetscOptionsGetString(options, NULL, option_name, saved_digest, sizeof(saved_digest), &found));
1575 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
1576 "Statistics continuation requested, but '%s' records no hash for window '%s'.",
1577 metadata_path, state->definition.name);
1578 PetscCall(PicurvWindowComputeHash(&state->definition, current_digest, NULL));
1579 if (strcmp(saved_digest, current_digest)) {
1580 const char *differing = "an unrecorded property";
1581
1582 /* The saved definition itself is not stored, so the per-group digests are
1583 * what make it possible to name the property that changed rather than
1584 * reporting only that two opaque hashes differ. */
1585 PetscCall(PetscSNPrintf(option_name, sizeof(option_name),
1586 "-checkpoint_statistics_window_%" PetscInt_FMT "_hash_groups", window));
1587 PetscCall(PetscOptionsGetString(options, NULL, option_name, saved_groups,
1588 sizeof(saved_groups), &found));
1589 if (found) {
1590 PetscInt group = -1;
1591
1592 PetscCall(PicurvWindowFirstHashDifference(&state->definition, saved_groups, &group));
1593 if (group >= 0) differing = PicurvWindowHashGroupName(group);
1594 }
1595 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_INCOMP,
1596 "Statistics window '%s' was defined differently in the checkpoint: %s changed. "
1597 "Continuing would merge incompatible samples; rename the window to start a new one.",
1598 state->definition.name, differing);
1599 }
1600
1601 PICURV_STATISTICS_REQUIRE("sample_count", PetscOptionsGetInt, &state->sample_count);
1602 PICURV_STATISTICS_REQUIRE("total_weight", PetscOptionsGetReal, &state->total_weight);
1603 PICURV_STATISTICS_REQUIRE("represented_time", PetscOptionsGetReal, &state->represented_time);
1604 PICURV_STATISTICS_REQUIRE("last_accepted_time", PetscOptionsGetReal, &state->last_accepted_time);
1605 PICURV_STATISTICS_REQUIRE("effective_start", PetscOptionsGetReal, &state->effective_start);
1606 PICURV_STATISTICS_REQUIRE("effective_end", PetscOptionsGetReal, &state->effective_end);
1607 PICURV_STATISTICS_REQUIRE("activation_step", PetscOptionsGetInt, &state->activation_step);
1608 PICURV_STATISTICS_REQUIRE("last_event_step", PetscOptionsGetInt, &state->last_event_step);
1609 PICURV_STATISTICS_REQUIRE("next_time_target", PetscOptionsGetInt, &state->next_time_target);
1610 PICURV_STATISTICS_REQUIRE("restart_count", PetscOptionsGetInt, &state->restart_count);
1611#undef PICURV_STATISTICS_REQUIRE
1612
1613 PetscCall(PetscSNPrintf(option_name, sizeof(option_name),
1614 "-checkpoint_statistics_window_%" PetscInt_FMT "_state", window));
1615 PetscCall(PetscOptionsGetString(options, NULL, option_name, saved_state, sizeof(saved_state), &found));
1616 PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
1617 "Statistics continuation requested, but '%s' records no lifecycle state for window '%s'.",
1618 metadata_path, state->definition.name);
1619 if (!strcmp(saved_state, "complete")) state->state = PICURV_WINDOW_COMPLETE;
1620 else if (!strcmp(saved_state, "active")) state->state = PICURV_WINDOW_ACTIVE;
1621 else state->state = PICURV_WINDOW_PENDING;
1622
1623 /* A window saved as complete may still be resumable: page 58 §10 permits moving
1624 * a bounded end forward, and the end time is deliberately outside the hash. */
1625 if (state->state == PICURV_WINDOW_COMPLETE && state->definition.bounded &&
1626 state->definition.end_time > state->effective_end) {
1627 /* Only across an unbroken span. Under right-rectangle weighting the first
1628 * sample after the former end carries the whole interval back to it, so
1629 * resuming across a gap would weight time the window never observed. One
1630 * step of slack is the closing state's own clipping, not a gap. */
1631 PetscCheck(checkpoint_time - state->effective_end <= PetscMax(step_size, 0.0),
1632 PETSC_COMM_WORLD, PETSC_ERR_ARG_INCOMP,
1633 "Statistics window '%s' ended at t=%.17g and this checkpoint is at t=%.17g, "
1634 "so extending it to %.17g would weight %.17g of unobserved time. "
1635 "Start a new window instead.",
1636 state->definition.name, (double)state->effective_end, (double)checkpoint_time,
1637 (double)state->definition.end_time,
1638 (double)(checkpoint_time - state->effective_end));
1640 "Statistics window '%s' was complete at t=%.6g and is extended to t=%.6g.\n",
1641 state->definition.name, (double)state->effective_end,
1642 (double)state->definition.end_time);
1643 state->state = PICURV_WINDOW_ACTIVE;
1644 }
1645 PetscCheck(!state->definition.bounded || state->definition.end_time >= state->effective_end,
1646 PETSC_COMM_WORLD, PETSC_ERR_ARG_INCOMP,
1647 "Statistics window '%s' already represents time up to %.17g, past the requested end "
1648 "%.17g. A window may be extended forward but never shortened; rename it to start a new one.",
1649 state->definition.name, (double)state->effective_end,
1650 (double)state->definition.end_time);
1651 /* A post-processing read is analysis, not a continuation, so it must not look
1652 * like another restart segment. Only a solver resuming the window advances the
1653 * lineage; otherwise deriving a series of steps would inflate it once per step. */
1654 if (exec_mode == EXEC_MODE_SOLVER) state->restart_count += 1;
1656 PetscFunctionReturn(0);
1657}
#define PICURV_STATISTICS_REQUIRE(suffix, getter, target)
PetscInt last_event_step
Guards against a step being offered twice.
PetscErrorCode PicurvWindowComputeHash(const PicurvWindowDefinition *definition, char digest_hex[65], char group_digest_hex[][17])
Computes the resolved identity hash of one window definition.
#define PICURV_WINDOW_HASH_GROUP_COUNT
Number of independently hashed property groups in a window definition.
#define PICURV_WINDOW_HASH_GROUP_LENGTH
Stored length of one truncated group digest, including the terminator.
PetscReal effective_start
Origin of the first represented interval.
const char * PicurvWindowHashGroupName(PetscInt group)
Returns the stable name of one hashed property group.
PetscInt sample_count
PetscReal last_accepted_time
Right edge of the last represented interval.
PicurvWindowState state
PetscInt restart_count
Restart segments this state descends from.
PetscReal effective_end
End of the last represented interval.
PetscReal end_time
Requested end; ignored when bounded is false.
PetscReal total_weight
#define PICURV_WINDOW_NAME_LENGTH
Maximum stored length of a window name, including the terminator.
@ PICURV_WINDOW_PENDING
Requested start not yet reached.
@ PICURV_WINDOW_COMPLETE
Bounded end reached; accepts nothing further.
@ PICURV_WINDOW_ACTIVE
Accepting due states.
PetscBool bounded
False for an open-ended window.
PicurvWindowDefinition definition
PetscInt next_time_target
k in effective_start + k*time_cadence.
PetscInt activation_step
Step at which the window became active.
PetscReal represented_time
Physical time the window covers.
PetscErrorCode PicurvWindowFirstHashDifference(const PicurvWindowDefinition *definition, const char *saved_group_digests, PetscInt *group)
Reports which hashed property group first differs from saved group digests.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ RestoreFieldStatisticsState()

PetscErrorCode RestoreFieldStatisticsState ( SimCtx simCtx,
PetscInt  ti 
)

Implementation of RestoreFieldStatisticsState().

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 1667 of file io.c.

1668{
1669 UserCtx *user = NULL;
1670 PetscOptions options = NULL;
1671 char checkpoint_directory[PETSC_MAX_PATH_LEN];
1672 char metadata_path[PETSC_MAX_PATH_LEN];
1673 const char *source_path = NULL;
1674 PetscInt saved_window_count = 0;
1675 PetscReal checkpoint_time = 0.0;
1676 PetscBool found = PETSC_FALSE;
1677
1678 PetscFunctionBeginUser;
1680 PetscCheck(simCtx != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx cannot be NULL.");
1681 if (!FieldStatisticsIsActive(simCtx)) { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
1682 if (!simCtx->fieldStatisticsContinue) {
1684 "Statistics continuation was not requested; %d window(s) start from zero.\n",
1687 PetscFunctionReturn(0);
1688 }
1689
1690 user = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
1691 PetscCheck(user != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
1692 "Finest-level fields must exist before statistics state is restored.");
1693 source_path = (simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR) ? simCtx->pps->source_dir
1694 : simCtx->restart_dir;
1695 PetscCheck(source_path != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
1696 "No checkpoint source directory is set for the current execution mode.");
1697 PetscCall(ResolveCheckpointStepDirectory(source_path, ti,
1698 checkpoint_directory, sizeof(checkpoint_directory)));
1699 PetscCall(ValidateCheckpointBundle(simCtx, user, checkpoint_directory, ti,
1700 &checkpoint_time, NULL, NULL, NULL, NULL));
1701 PetscCall(PetscSNPrintf(metadata_path, sizeof(metadata_path), "%s/checkpoint.meta",
1702 checkpoint_directory));
1703
1704 PetscCall(PetscOptionsCreate(&options));
1705 PetscCall(PetscOptionsInsertFile(PETSC_COMM_WORLD, options, metadata_path, PETSC_TRUE));
1706 PetscCall(PetscOptionsGetInt(options, NULL, "-checkpoint_statistics_window_count",
1707 &saved_window_count, &found));
1708 /* Missing state for a requested continuation is fatal and never silently
1709 * zeroed: resuming from zero would report a converged average built from a
1710 * fraction of the samples its metadata claims. */
1711 PetscCheck(found && saved_window_count == simCtx->fieldStatisticsWindowCount,
1712 PETSC_COMM_WORLD, PETSC_ERR_ARG_INCOMP,
1713 "Statistics continuation requested, but checkpoint '%s' holds %" PetscInt_FMT
1714 " window(s) and this run configures %" PetscInt_FMT ".",
1715 checkpoint_directory, found ? saved_window_count : 0,
1716 simCtx->fieldStatisticsWindowCount);
1717
1718 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
1719 PetscCall(ReadStatisticsWindowState(options, window, metadata_path, checkpoint_time,
1720 simCtx->dt, simCtx->exec_mode,
1721 &simCtx->fieldStatisticsWindows[window]));
1722 }
1723 PetscCall(PetscOptionsDestroy(&options));
1724
1725 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
1726 PetscCheck(user[block].fieldStatisticsStorage != NULL, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
1727 "Statistics accumulators were not allocated for block %" PetscInt_FMT ".", block);
1728 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
1729 const PicurvWindowDefinition *definition = &simCtx->fieldStatisticsWindows[window].definition;
1730 const PicurvWindowStorage *storage = &user[block].fieldStatisticsStorage[window];
1731 PetscInt payload_count = 0;
1732
1733 PetscCall(FormatStatisticsPath(checkpoint_directory, window, block, NULL,
1734 simCtx->_io_context_buffer,
1735 sizeof(simCtx->_io_context_buffer)));
1736 simCtx->current_io_directory = simCtx->_io_context_buffer;
1737 PetscCall(PicurvWindowStoragePayloadCount(storage, &payload_count));
1738 for (PetscInt index = 0; index < payload_count; ++index) {
1740
1741 PetscCall(PicurvWindowStoragePayload(&user[block], definition, storage, index, &payload));
1742 PetscCall(ReadFieldData(&user[block], payload.name, payload.vec, "dat"));
1743 }
1744 }
1745 simCtx->current_io_directory = NULL;
1746 }
1747
1748 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
1749 const PicurvWindow *state = &simCtx->fieldStatisticsWindows[window];
1750
1752 "Statistics window '%s' continued from '%s': state %s, %d sample(s), "
1753 "total weight %.6g, represented time %.6g, restart segment %d.\n",
1754 state->definition.name, checkpoint_directory,
1756 (double)state->total_weight, (double)state->represented_time,
1757 state->restart_count);
1758 }
1760 PetscFunctionReturn(0);
1761}
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
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:1533
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.
const char * PicurvWindowStateName(PicurvWindowState state)
Returns a stable human-readable name for a window state.
PetscBool FieldStatisticsIsActive(const struct SimCtx *simCtx)
Reports whether this run has live field-statistics state.
Runtime state of one window.
The scientifically immutable definition of one window.
PetscInt fieldStatisticsWindowCount
Definition variables.h:770
UserCtx * user
Definition variables.h:571
UserMG usermg
Definition variables.h:852
PetscReal dt
Definition variables.h:710
struct PicurvWindow * fieldStatisticsWindows
Definition variables.h:771
PetscInt mglevels
Definition variables.h:578
struct PicurvWindowStorage * fieldStatisticsStorage
Definition variables.h:962
MGCtx * mgctx
Definition variables.h:581
PetscBool fieldStatisticsContinue
Definition variables.h:776
User-defined context containing data specific to a single computational grid level.
Definition variables.h:906
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadSwarmField()

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

Internal helper implementation: ReadSwarmField().

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

Local to this translation unit.

Definition at line 1767 of file io.c.

1768{
1769 PetscErrorCode ierr;
1770 DM swarm;
1771 Vec fieldVec;
1772
1773 PetscFunctionBegin;
1774
1775 swarm = user->swarm;
1776
1777 LOG_ALLOW(GLOBAL,LOG_DEBUG," ReadSwarmField Begins \n");
1778
1779 /* 2) Create a global vector that references the specified Swarm field. */
1780 ierr = DMSwarmCreateGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
1781
1782 LOG_ALLOW(GLOBAL,LOG_DEBUG," Vector created from Field \n");
1783
1784 /* 3) Use the ReadFieldData() function to read data into fieldVec. */
1785 ierr = ReadFieldData(user, field_name, fieldVec, ext);CHKERRQ(ierr);
1786
1787 /* 4) Destroy the global vector reference. */
1788 ierr = DMSwarmDestroyGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
1789
1790 PetscFunctionReturn(0);
1791}
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 
)

Internal helper implementation: ReadSwarmIntField().

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

Local to this translation unit.

Definition at line 1797 of file io.c.

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

◆ ReadAllSwarmFields()

PetscErrorCode ReadAllSwarmFields ( UserCtx user,
PetscInt  ti 
)

Internal helper implementation: ReadAllSwarmFields().

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

Local to this translation unit.

Definition at line 1864 of file io.c.

1865{
1866 PetscInt nGlobal;
1867 SimCtx *simCtx = user->simCtx;
1868 const char *source_path = NULL;
1869 char checkpoint_directory[PETSC_MAX_PATH_LEN];
1870
1871 PetscFunctionBeginUser;
1872 PetscCall(DMSwarmGetSize(user->swarm, &nGlobal));
1873 LOG_ALLOW(GLOBAL, LOG_INFO, "Reading DMSwarm fields for timestep %d (swarm size is %d).\n", ti, nGlobal);
1874
1875 if (nGlobal == 0) {
1876 LOG_ALLOW(GLOBAL, LOG_INFO, "Swarm is empty for timestep %d. Nothing to read.\n", ti);
1877 PetscFunctionReturn(0);
1878 }
1879
1880 // First, determine the top-level source directory based on the execution mode.
1881 if (simCtx->exec_mode == EXEC_MODE_SOLVER) {
1882 source_path = simCtx->restart_dir;
1883 } else if (simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR) {
1884 source_path = simCtx->pps->source_dir;
1885 } else {
1886 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "Invalid execution mode for reading simulation fields.");
1887 }
1888
1889 PetscCall(ResolveCheckpointStepDirectory(source_path, ti,
1890 checkpoint_directory, sizeof(checkpoint_directory)));
1891 PetscCall(PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer),
1892 "%s/%s", checkpoint_directory, PICURV_PARTICLE_DIRECTORY));
1893 simCtx->current_io_directory = simCtx->_io_context_buffer;
1894
1895 for (PetscInt raw_id = 0; raw_id < PARTICLE_FIELD_ID_COUNT; ++raw_id) {
1896 const ParticleFieldDescriptor *descriptor = NULL;
1897
1898 PetscCall(ParticleFieldGetDescriptor((ParticleFieldId)raw_id, &descriptor));
1899 if (!(descriptor->capabilities & PARTICLE_FIELD_CAPABILITY_CHECKPOINT)) continue;
1900 if (descriptor->data_type == PETSC_INT || descriptor->data_type == PETSC_INT64) {
1901 PetscCall(ReadSwarmIntField(user, descriptor->canonical_name, "dat"));
1902 } else {
1903 PetscCall(ReadSwarmField(user, descriptor->canonical_name, "dat"));
1904 }
1905 }
1906
1907 simCtx->current_io_directory = NULL;
1908
1909 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished reading DMSwarm fields for timestep %d.\n", ti);
1910 PetscFunctionReturn(0);
1911}
PetscErrorCode ReadSwarmField(UserCtx *user, const char *field_name, const char *ext)
Internal helper implementation: ReadSwarmField().
Definition io.c:1767
#define PICURV_PARTICLE_DIRECTORY
Definition io.c:25
PetscErrorCode ReadSwarmIntField(UserCtx *user, const char *field_name, const char *ext)
Internal helper implementation: ReadSwarmIntField().
Definition io.c:1797
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:

◆ ReadCheckpointParticleCount()

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

Implementation of ReadCheckpointParticleCount().

Read the particle count from a validated committed checkpoint.

Definition at line 1914 of file io.c.

1915{
1916 SimCtx *simCtx = NULL;
1917 const char *source_path = NULL;
1918 char checkpoint_directory[PETSC_MAX_PATH_LEN];
1919 PetscBool particles_saved = PETSC_FALSE;
1920
1921 PetscFunctionBeginUser;
1922 PetscCheck(user != NULL && particle_count != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
1923 "Simulation context and particle-count output are required.");
1924 simCtx = user->simCtx;
1925 if (simCtx->exec_mode == EXEC_MODE_SOLVER) source_path = simCtx->restart_dir;
1926 else if (simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR) source_path = simCtx->pps->source_dir;
1927 else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
1928 "Invalid execution mode for reading checkpoint particle metadata.");
1929
1930 PetscCall(ResolveCheckpointStepDirectory(source_path, ti,
1931 checkpoint_directory, sizeof(checkpoint_directory)));
1932 PetscCall(ValidateCheckpointBundle(simCtx, user - user->_this,
1933 checkpoint_directory, ti, NULL, particle_count,
1934 &particles_saved, NULL, NULL));
1935 PetscCheck(particles_saved, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
1936 "Checkpoint step %" PetscInt_FMT " contains no particle state.", ti);
1937 PetscFunctionReturn(0);
1938}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteFieldData()

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

Internal helper implementation: WriteFieldData().

Writes data from a specific PETSc vector to a file.

Local to this translation unit.

Definition at line 1947 of file io.c.

1951{
1952 MPI_Comm comm;
1953 PetscMPIInt rank;
1954 Vec sequential_vec = NULL;
1955 char filename[PETSC_MAX_PATH_LEN];
1956 SimCtx *simCtx=user->simCtx;
1957
1958 PetscFunctionBeginUser;
1960
1961 if(!simCtx->current_io_directory){
1962 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE, "I/O context directory was not set before calling WriteFieldData().");
1963 }
1964
1965 /* ------------------------------------------------------------ */
1966 /* Basic communicator information */
1967 /* ------------------------------------------------------------ */
1968 PetscCall(PetscObjectGetComm((PetscObject)field_vec,&comm));
1969 PetscCallMPI(MPI_Comm_rank(comm,&rank));
1970
1971 PetscCall(PetscSNPrintf(filename, sizeof(filename), "%s/%s.%s",
1972 simCtx->current_io_directory, field_name, ext));
1973
1974 PetscCall(GatherVectorToRankZero(field_vec, &sequential_vec));
1975 if (rank == 0) {
1976 PetscViewer viewer;
1977 PetscReal vmin, vmax;
1978
1979 PetscCall(VecMin(sequential_vec, NULL, &vmin));
1980 PetscCall(VecMax(sequential_vec, NULL, &vmax));
1982 " <%s> range = [%.4e … %.4e]\n",
1983 field_name,(double)vmin,(double)vmax);
1984 PetscCall(PetscViewerBinaryOpen(PETSC_COMM_SELF, filename, FILE_MODE_WRITE, &viewer));
1985 PetscCall(PetscViewerBinarySetSkipInfo(viewer, PETSC_TRUE));
1986 PetscCall(VecView(sequential_vec, viewer));
1987 PetscCall(PetscViewerDestroy(&viewer));
1988 LOG_ALLOW(GLOBAL, LOG_INFO, "Wrote <%s>\n", filename);
1989 }
1990 PetscCall(VecDestroy(&sequential_vec));
1991
1993 PetscFunctionReturn(0);
1994}
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 
)

Implementation of WriteSimulationFields().

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 2002 of file io.c.

2003{
2004 SimCtx *simCtx = user->simCtx;
2005
2006 PetscFunctionBeginUser;
2007 PetscCheck(checkpoint_directory != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2008 "Checkpoint destination cannot be NULL.");
2009 PetscCall(PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer),
2010 "%s/%s/block_%04" PetscInt_FMT,
2011 checkpoint_directory, PICURV_EULERIAN_DIRECTORY, user->_this));
2012 simCtx->current_io_directory = simCtx->_io_context_buffer;
2013
2014 if (simCtx->les) {
2015 PetscCall(CopyOwnedLocalScalarToGlobal(user->da, user->lCs, user->CS));
2016 PetscCall(CopyOwnedLocalScalarToGlobal(user->da, user->lNu_t, user->Nu_t));
2017 }
2018 for (PetscInt raw_id = 0; raw_id < FIELD_ID_COUNT; ++raw_id) {
2019 const FieldDescriptor *descriptor = NULL;
2020 FieldView view;
2021
2022 PetscCall(FieldGetDescriptor((FieldId)raw_id, &descriptor));
2023 if (!CheckpointFieldIsEnabled(simCtx, descriptor)) continue;
2024 PetscCall(FieldGetView(user, descriptor->id, &view));
2025 PetscCall(WriteFieldData(user, descriptor->canonical_name, view.global_vec, "dat"));
2026 }
2027 simCtx->current_io_directory = NULL;
2028 PetscFunctionReturn(0);
2029}
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:1947
Vec lCs
Definition variables.h:982
Vec lNu_t
Definition variables.h:982
Vec Nu_t
Definition variables.h:982
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteStatisticsFields()

static PetscErrorCode WriteStatisticsFields ( UserCtx user,
const char *  checkpoint_directory 
)
static

Write every window's accumulator payloads for one block into a bundle.

Definition at line 2034 of file io.c.

2035{
2036 SimCtx *simCtx = user->simCtx;
2037
2038 PetscFunctionBeginUser;
2040 PetscCheck(checkpoint_directory != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2041 "Checkpoint destination cannot be NULL.");
2042 if (!FieldStatisticsIsActive(simCtx) || !user->fieldStatisticsStorage) { PROFILE_FUNCTION_END; PetscFunctionReturn(0); }
2043
2044 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
2045 const PicurvWindowStorage *storage = &user->fieldStatisticsStorage[window];
2046 PetscInt payload_count = 0;
2047
2048 PetscCall(FormatStatisticsPath(checkpoint_directory, window, user->_this, NULL,
2049 simCtx->_io_context_buffer,
2050 sizeof(simCtx->_io_context_buffer)));
2051 simCtx->current_io_directory = simCtx->_io_context_buffer;
2052 PetscCall(PicurvWindowStoragePayloadCount(storage, &payload_count));
2053 for (PetscInt index = 0; index < payload_count; ++index) {
2055
2056 PetscCall(PicurvWindowStoragePayload(user, &simCtx->fieldStatisticsWindows[window].definition,
2057 storage, index, &payload));
2058 PetscCall(WriteFieldData(user, payload.name, payload.vec, "dat"));
2059 }
2061 "Wrote %d statistics payload(s) for window '%s' block %d.\n",
2062 (int)payload_count, simCtx->fieldStatisticsWindows[window].definition.name,
2063 (int)user->_this);
2064 }
2065 simCtx->current_io_directory = NULL;
2067 PetscFunctionReturn(0);
2068}
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 
)

Implementation of WriteSwarmField().

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 2076 of file io.c.

2077{
2078 PetscErrorCode ierr;
2079 Vec fieldVec;
2080 DM swarm;
2081
2082 PetscFunctionBeginUser; /* PETSc macro indicating start of function */
2083
2084 /*
2085 * 1) Retrieve the PetscSwarm from the user context.
2086 * Ensure user->swarm is initialized and not NULL.
2087 */
2088 swarm = user->swarm;
2089
2090 /*
2091 * 2) Create a global vector from the specified swarm field.
2092 * This function is available in PETSc 3.14.4.
2093 * It provides a read/write "view" of the swarm field as a global Vec.
2094 */
2096 "Attempting to create global vector from field: %s\n",
2097 field_name);
2098 ierr = DMSwarmCreateGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
2099
2100 /*
2101 * 3) Use your existing WriteFieldData() to write the global vector to a file.
2102 * The field name, time index, and extension are passed along for naming.
2103 */
2105 "Calling WriteFieldData for field: %s\n",
2106 field_name);
2107 ierr = WriteFieldData(user, field_name, fieldVec, ext);CHKERRQ(ierr);
2108
2109 /*
2110 * 4) Destroy the global vector once the data is successfully written.
2111 * This step is crucial for avoiding memory leaks.
2112 * DMSwarmDestroyGlobalVectorFromField() is also available in PETSc 3.14.4.
2113 */
2115 "Destroying the global vector for field: %s\n",
2116 field_name);
2117 ierr = DMSwarmDestroyGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
2118
2119 /* Log and return success. */
2121 "Successfully wrote swarm data for field: %s\n",
2122 field_name);
2123
2124 PetscFunctionReturn(0); /* PETSc macro indicating end of function */
2125}
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 
)

Internal helper implementation: WriteSwarmIntField().

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

Local to this translation unit.

Definition at line 2131 of file io.c.

2132{
2133 PetscErrorCode ierr;
2134 DM swarm = user->swarm;
2135 Vec temp_vec; // Temporary Vec to hold casted data
2136 PetscInt nlocal, nglobal,bs,i;
2137 PetscDataType field_type;
2138 void *field_array_void;
2139 PetscScalar *scalar_array; // Pointer to the temporary Vec's scalar data
2140
2141 PetscFunctionBeginUser;
2142
2143 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Casting '%s' to Vec for writing.\n", field_name);
2144
2145 // Get the swarm field properties
2146 ierr = DMSwarmGetLocalSize(swarm, &nlocal); CHKERRQ(ierr);
2147 ierr = DMSwarmGetSize(swarm, &nglobal); CHKERRQ(ierr);
2148 ierr = DMSwarmGetField(swarm, field_name, &bs, &field_type, &field_array_void); CHKERRQ(ierr);
2149 PetscCheck(field_type == PETSC_INT || field_type == PETSC_INT64,
2150 PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
2151 "Swarm field '%s' must use PETSC_INT or PETSC_INT64 for integer output, not %s.",
2152 field_name, PetscDataTypes[field_type]);
2153
2154 // Create Temporary parallel Vec wit the CORRECT layout
2155 ierr = VecCreate(PETSC_COMM_WORLD, &temp_vec); CHKERRQ(ierr);
2156 ierr = VecSetType(temp_vec, VECMPI); CHKERRQ(ierr);
2157 ierr = VecSetSizes(temp_vec, nlocal*bs, nglobal*bs); CHKERRQ(ierr);
2158 ierr = VecSetUp(temp_vec); CHKERRQ(ierr);
2159
2160 // Defining Vector field to mandatory field 'position'
2161 DMSwarmVectorDefineField(swarm,ParticleFieldName(PARTICLE_FIELD_ID_POSITION));
2162
2163 ierr = VecGetArray(temp_vec, &scalar_array); CHKERRQ(ierr);
2164
2165 if (field_type == PETSC_INT64) {
2166 PetscInt64 *int64_array = (PetscInt64 *)field_array_void;
2167 // Perform the cast from PetscInt64 to PetscScalar
2168 for (i = 0; i < nlocal*bs; i++) {
2169 scalar_array[i] = (PetscScalar)int64_array[i];
2170 }
2171 }else{
2172 PetscInt *int_array = (PetscInt *)field_array_void;
2173 //Perform the cast from PetscInt to PetscScalar
2174 for (i = 0; i < nlocal*bs; i++) {
2175 scalar_array[i] = (PetscScalar)int_array[i];
2176 }
2177 }
2178
2179 // Restore access to both arrays
2180 ierr = VecRestoreArray(temp_vec, &scalar_array); CHKERRQ(ierr);
2181 ierr = DMSwarmRestoreField(swarm, field_name, &bs, NULL, &field_array_void); CHKERRQ(ierr);
2182
2183 // Call your existing writer with the temporary, populated Vec
2184 ierr = WriteFieldData(user, field_name, temp_vec, ext); CHKERRQ(ierr);
2185
2186 // Clean up
2187 ierr = VecDestroy(&temp_vec); CHKERRQ(ierr);
2188
2189 PetscFunctionReturn(0);
2190}
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 
)

Internal helper implementation: WriteAllSwarmFields().

Writes a predefined set of PETSc Swarm fields to files.

Local to this translation unit.

Definition at line 2196 of file io.c.

2197{
2198 SimCtx *simCtx = user->simCtx;
2199
2200 PetscFunctionBeginUser;
2201
2202 // If no swarm is configured or there are no particles, do nothing and return.
2203 if (!user->swarm || simCtx->np <= 0) {
2204 PetscFunctionReturn(0);
2205 }
2206
2207 PetscCheck(checkpoint_directory != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2208 "Checkpoint destination cannot be NULL.");
2209 PetscCall(PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer),
2210 "%s/%s", checkpoint_directory, PICURV_PARTICLE_DIRECTORY));
2211 simCtx->current_io_directory = simCtx->_io_context_buffer;
2212
2213 for (PetscInt raw_id = 0; raw_id < PARTICLE_FIELD_ID_COUNT; ++raw_id) {
2214 const ParticleFieldDescriptor *descriptor = NULL;
2215
2216 PetscCall(ParticleFieldGetDescriptor((ParticleFieldId)raw_id, &descriptor));
2217 if (!(descriptor->capabilities & PARTICLE_FIELD_CAPABILITY_CHECKPOINT)) continue;
2218 if (descriptor->data_type == PETSC_INT || descriptor->data_type == PETSC_INT64) {
2219 PetscCall(WriteSwarmIntField(user, descriptor->canonical_name, "dat"));
2220 } else {
2221 PetscCall(WriteSwarmField(user, descriptor->canonical_name, "dat"));
2222 }
2223 }
2224
2225 simCtx->current_io_directory = NULL;
2226
2227 PetscFunctionReturn(0);
2228}
PetscErrorCode WriteSwarmIntField(UserCtx *user, const char *field_name, const char *ext)
Internal helper implementation: WriteSwarmIntField().
Definition io.c:2131
PetscErrorCode WriteSwarmField(UserCtx *user, const char *field_name, const char *ext)
Implementation of WriteSwarmField().
Definition io.c:2076
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteCheckpointPayloadEntry()

static PetscErrorCode WriteCheckpointPayloadEntry ( FILE *  manifest,
const char *  checkpoint_directory,
PetscInt  payload_index,
const char *  relative_path,
const char *  kind,
const char *  field_name,
PetscInt  block,
const char *  layout,
PetscInt  components,
const char *  logical_type,
PetscInt  global_size,
const char *  encoding 
)
static

Append one payload inventory entry to a checkpoint manifest.

Definition at line 2231 of file io.c.

2243{
2244 char payload_path[PETSC_MAX_PATH_LEN];
2245 struct stat payload_stat;
2246
2247 PetscFunctionBeginUser;
2248 PetscCheck(manifest != NULL && checkpoint_directory != NULL && relative_path != NULL,
2249 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Checkpoint payload metadata inputs are required.");
2250 PetscCall(PetscSNPrintf(payload_path, sizeof(payload_path), "%s/%s",
2251 checkpoint_directory, relative_path));
2252 PetscCheck(stat(payload_path, &payload_stat) == 0 && S_ISREG(payload_stat.st_mode),
2253 PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
2254 "Expected checkpoint payload was not written: %s", payload_path);
2255 PetscCheck(fprintf(manifest,
2256 "-checkpoint_payload_%" PetscInt_FMT "_path %s\n"
2257 "-checkpoint_payload_%" PetscInt_FMT "_kind %s\n"
2258 "-checkpoint_payload_%" PetscInt_FMT "_field %s\n"
2259 "-checkpoint_payload_%" PetscInt_FMT "_block %" PetscInt_FMT "\n"
2260 "-checkpoint_payload_%" PetscInt_FMT "_layout %s\n"
2261 "-checkpoint_payload_%" PetscInt_FMT "_components %" PetscInt_FMT "\n"
2262 "-checkpoint_payload_%" PetscInt_FMT "_logical_type %s\n"
2263 "-checkpoint_payload_%" PetscInt_FMT "_global_size %" PetscInt_FMT "\n"
2264 "-checkpoint_payload_%" PetscInt_FMT "_encoding %s\n"
2265 "-checkpoint_payload_%" PetscInt_FMT "_bytes %lld\n",
2266 payload_index, relative_path,
2267 payload_index, kind,
2268 payload_index, field_name,
2269 payload_index, block,
2270 payload_index, layout,
2271 payload_index, components,
2272 payload_index, logical_type,
2273 payload_index, global_size,
2274 payload_index, encoding,
2275 payload_index, (long long)payload_stat.st_size) > 0,
2276 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2277 "Unable to append payload metadata for '%s'.", relative_path);
2278 PetscFunctionReturn(0);
2279}
Here is the caller graph for this function:

◆ WriteCheckpointManifest()

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] 
)
static

Write the complete manifest after every payload has closed successfully.

Definition at line 2282 of file io.c.

2288{
2289 char metadata_path[PETSC_MAX_PATH_LEN];
2290 PetscInt payload_count = 0;
2291 PetscInt payload_index = 0;
2292 FILE *manifest = NULL;
2293
2294 PetscFunctionBeginUser;
2295 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2296 for (PetscInt raw_id = 0; raw_id < FIELD_ID_COUNT; ++raw_id) {
2297 const FieldDescriptor *descriptor = NULL;
2298 PetscCall(FieldGetDescriptor((FieldId)raw_id, &descriptor));
2299 if (CheckpointFieldIsEnabled(simCtx, descriptor)) ++payload_count;
2300 }
2301 }
2302 if (simCtx->np > 0) {
2303 for (PetscInt raw_id = 0; raw_id < PARTICLE_FIELD_ID_COUNT; ++raw_id) {
2304 const ParticleFieldDescriptor *descriptor = NULL;
2305 PetscCall(ParticleFieldGetDescriptor((ParticleFieldId)raw_id, &descriptor));
2306 if (descriptor->capabilities & PARTICLE_FIELD_CAPABILITY_CHECKPOINT) ++payload_count;
2307 }
2308 }
2309 if (FieldStatisticsIsActive(simCtx)) {
2310 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2311 if (!user[block].fieldStatisticsStorage) continue;
2312 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
2313 PetscInt storage_payloads = 0;
2314
2315 PetscCall(PicurvWindowStoragePayloadCount(&user[block].fieldStatisticsStorage[window],
2316 &storage_payloads));
2317 payload_count += storage_payloads;
2318 }
2319 }
2320 }
2321
2322 if (simCtx->rank == 0) {
2323 PetscCall(PetscSNPrintf(metadata_path, sizeof(metadata_path),
2324 "%s/checkpoint.meta", checkpoint_directory));
2325 manifest = fopen(metadata_path, "w");
2326 PetscCheck(manifest != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
2327 "Unable to create checkpoint metadata '%s'.", metadata_path);
2328 PetscCheck(fprintf(manifest,
2329 "-checkpoint_format %s\n"
2330 "-checkpoint_version %d\n"
2331 "-checkpoint_step %" PetscInt_FMT "\n"
2332 "-checkpoint_time %.17g\n"
2333 "-checkpoint_dt %.17g\n"
2334 "-checkpoint_reason %s\n"
2335 "-checkpoint_geometry_sha256 %s\n"
2336 "-checkpoint_block_count %" PetscInt_FMT "\n"
2337 "-checkpoint_particles %s\n"
2338 "-checkpoint_particle_count %" PetscInt_FMT "\n"
2339 "-checkpoint_les %s\n"
2340 "-checkpoint_rans %s\n"
2341 "-checkpoint_payload_count %" PetscInt_FMT "\n",
2343 simCtx->step, (double)simCtx->ti, (double)simCtx->dt,
2344 reason, geometry_digest, simCtx->block_number,
2345 simCtx->np > 0 ? "true" : "false",
2346 particle_count,
2347 simCtx->les ? "true" : "false",
2348 simCtx->rans ? "true" : "false",
2349 payload_count) > 0,
2350 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2351 "Unable to write checkpoint metadata '%s'.", metadata_path);
2352
2353 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2354 PetscCheck(fprintf(manifest,
2355 "-checkpoint_block_%" PetscInt_FMT "_im %" PetscInt_FMT "\n"
2356 "-checkpoint_block_%" PetscInt_FMT "_jm %" PetscInt_FMT "\n"
2357 "-checkpoint_block_%" PetscInt_FMT "_km %" PetscInt_FMT "\n",
2358 block, user[block].IM, block, user[block].JM, block, user[block].KM) > 0,
2359 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2360 "Unable to write block metadata for block %" PetscInt_FMT ".", block);
2361 }
2362 PetscCheck(fprintf(manifest, "-checkpoint_periodic %d,%d,%d\n",
2363 (int)simCtx->i_periodic, (int)simCtx->j_periodic,
2364 (int)simCtx->k_periodic) > 0,
2365 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2366 "Unable to write checkpoint periodicity metadata.");
2367
2368 /* Driven-flow controller state. `initial_flux` measures its target once
2369 * from the starting field, so a restart must read the latched value back
2370 * rather than re-measure it from a field that has since drifted. */
2371 PetscCheck(fprintf(manifest,
2372 "-checkpoint_driven_flux_latched %s\n"
2373 "-checkpoint_driven_flux_target %.17g\n",
2374 simCtx->drivenFluxTargetLatched ? "true" : "false",
2375 (double)simCtx->targetVolumetricFlux) > 0,
2376 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2377 "Unable to write driven-flow controller metadata.");
2378
2379 /* Window scalars are recorded even when no window is configured, so a
2380 * restart can tell an absent window list from an unreadable bundle. */
2381 PetscCheck(fprintf(manifest, "-checkpoint_statistics_window_count %" PetscInt_FMT "\n",
2382 FieldStatisticsIsActive(simCtx) ? simCtx->fieldStatisticsWindowCount : 0) > 0,
2383 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2384 "Unable to write statistics window count.");
2385 if (FieldStatisticsIsActive(simCtx)) {
2386 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
2387 const PicurvWindow *state = &simCtx->fieldStatisticsWindows[window];
2388 char digest[65] = "";
2390 /* Group digests plus their separators, with headroom so a longer
2391 * digest would not silently truncate into a false match. */
2393 size_t used = 0;
2394
2395 PetscCall(PicurvWindowComputeHash(&state->definition, digest, groups));
2396 for (PetscInt group = 0; group < PICURV_WINDOW_HASH_GROUP_COUNT; ++group) {
2397 PetscCall(PetscStrlen(group_list, &used));
2398 PetscCall(PetscSNPrintf(group_list + used, sizeof(group_list) - used, "%s%s",
2399 group ? "," : "", groups[group]));
2400 }
2401 PetscCheck(fprintf(manifest,
2402 "-checkpoint_statistics_window_%" PetscInt_FMT "_name %s\n"
2403 "-checkpoint_statistics_window_%" PetscInt_FMT "_hash %s\n"
2404 "-checkpoint_statistics_window_%" PetscInt_FMT "_hash_groups %s\n"
2405 "-checkpoint_statistics_window_%" PetscInt_FMT "_state %s\n"
2406 "-checkpoint_statistics_window_%" PetscInt_FMT "_sample_count %" PetscInt_FMT "\n"
2407 "-checkpoint_statistics_window_%" PetscInt_FMT "_total_weight %.17g\n"
2408 "-checkpoint_statistics_window_%" PetscInt_FMT "_represented_time %.17g\n"
2409 "-checkpoint_statistics_window_%" PetscInt_FMT "_last_accepted_time %.17g\n"
2410 "-checkpoint_statistics_window_%" PetscInt_FMT "_effective_start %.17g\n"
2411 "-checkpoint_statistics_window_%" PetscInt_FMT "_effective_end %.17g\n"
2412 "-checkpoint_statistics_window_%" PetscInt_FMT "_activation_step %" PetscInt_FMT "\n"
2413 "-checkpoint_statistics_window_%" PetscInt_FMT "_last_event_step %" PetscInt_FMT "\n"
2414 "-checkpoint_statistics_window_%" PetscInt_FMT "_next_time_target %" PetscInt_FMT "\n"
2415 "-checkpoint_statistics_window_%" PetscInt_FMT "_restart_count %" PetscInt_FMT "\n",
2416 window, state->definition.name,
2417 window, digest,
2418 window, group_list,
2419 window, PicurvWindowStateName(state->state),
2420 window, state->sample_count,
2421 window, (double)state->total_weight,
2422 window, (double)state->represented_time,
2423 window, (double)state->last_accepted_time,
2424 window, (double)state->effective_start,
2425 window, (double)state->effective_end,
2426 window, state->activation_step,
2427 window, state->last_event_step,
2428 window, state->next_time_target,
2429 window, state->restart_count) > 0,
2430 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2431 "Unable to write metadata for statistics window '%s'.",
2432 state->definition.name);
2433 }
2434 }
2435
2436 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2437 for (PetscInt raw_id = 0; raw_id < FIELD_ID_COUNT; ++raw_id) {
2438 const FieldDescriptor *descriptor = NULL;
2439 FieldView view;
2440 PetscInt global_size = 0;
2441 char relative_path[PETSC_MAX_PATH_LEN];
2442
2443 PetscCall(FieldGetDescriptor((FieldId)raw_id, &descriptor));
2444 if (!CheckpointFieldIsEnabled(simCtx, descriptor)) continue;
2445 PetscCall(FieldGetView(&user[block], descriptor->id, &view));
2446 PetscCall(VecGetSize(view.global_vec, &global_size));
2447 PetscCall(PetscSNPrintf(relative_path, sizeof(relative_path),
2448 "%s/block_%04" PetscInt_FMT "/%s.dat",
2449 PICURV_EULERIAN_DIRECTORY, block, descriptor->canonical_name));
2450 PetscCall(WriteCheckpointPayloadEntry(manifest, checkpoint_directory,
2451 payload_index++, relative_path,
2452 "eulerian", descriptor->canonical_name,
2453 block, FieldLayoutName(descriptor->layout),
2454 descriptor->dof, "PetscScalar", global_size,
2455 "petsc_vec_binary_natural"));
2456 }
2457 }
2458 if (simCtx->np > 0) {
2459 for (PetscInt raw_id = 0; raw_id < PARTICLE_FIELD_ID_COUNT; ++raw_id) {
2460 const ParticleFieldDescriptor *descriptor = NULL;
2461 char relative_path[PETSC_MAX_PATH_LEN];
2462 const char *encoding = "petsc_vec_binary_global";
2463
2464 PetscCall(ParticleFieldGetDescriptor((ParticleFieldId)raw_id, &descriptor));
2465 if (!(descriptor->capabilities & PARTICLE_FIELD_CAPABILITY_CHECKPOINT)) continue;
2466 if (descriptor->data_type == PETSC_INT || descriptor->data_type == PETSC_INT64) {
2467 encoding = "petsc_vec_binary_scalar_cast";
2468 }
2469 PetscCall(PetscSNPrintf(relative_path, sizeof(relative_path),
2470 "%s/%s.dat", PICURV_PARTICLE_DIRECTORY,
2471 descriptor->canonical_name));
2472 PetscCall(WriteCheckpointPayloadEntry(manifest, checkpoint_directory,
2473 payload_index++, relative_path,
2474 "particle", descriptor->canonical_name,
2475 -1, "DMSwarm", descriptor->components,
2476 PetscDataTypes[descriptor->data_type],
2477 particle_count * descriptor->components,
2478 encoding));
2479 }
2480 }
2481 if (FieldStatisticsIsActive(simCtx)) {
2482 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2483 if (!user[block].fieldStatisticsStorage) continue;
2484 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
2485 const PicurvWindowDefinition *definition =
2486 &simCtx->fieldStatisticsWindows[window].definition;
2487 const PicurvWindowStorage *storage = &user[block].fieldStatisticsStorage[window];
2488 PetscInt storage_payloads = 0;
2489
2490 PetscCall(PicurvWindowStoragePayloadCount(storage, &storage_payloads));
2491 for (PetscInt index = 0; index < storage_payloads; ++index) {
2493 PetscInt global_size = 0;
2494 char relative_path[PETSC_MAX_PATH_LEN];
2495 char qualified_name[PETSC_MAX_PATH_LEN];
2496
2497 PetscCall(PicurvWindowStoragePayload(&user[block], definition, storage,
2498 index, &payload));
2499 PetscCall(VecGetSize(payload.vec, &global_size));
2500 PetscCall(FormatStatisticsPath(NULL, window, block, payload.name,
2501 relative_path, sizeof(relative_path)));
2502 /* The inventory field name is qualified by window so two
2503 * windows accumulating the same field stay distinguishable
2504 * to anything reading the manifest alone. */
2505 PetscCall(PetscSNPrintf(qualified_name, sizeof(qualified_name), "%s/%s",
2506 definition->name, payload.name));
2507 PetscCall(WriteCheckpointPayloadEntry(manifest, checkpoint_directory,
2508 payload_index++, relative_path,
2509 payload.role, qualified_name,
2510 block, payload.layout,
2511 payload.components, "PetscScalar",
2512 global_size,
2513 "petsc_vec_binary_natural"));
2514 }
2515 }
2516 }
2517 }
2518 PetscCheck(fflush(manifest) == 0 && fsync(fileno(manifest)) == 0,
2519 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2520 "Unable to flush checkpoint metadata '%s'.", metadata_path);
2521 PetscCheck(fclose(manifest) == 0, PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2522 "Unable to close checkpoint metadata '%s'.", metadata_path);
2523 PetscCall(PicurvSHA256File(metadata_path, manifest_digest));
2524 }
2525 PetscCallMPI(MPI_Bcast(manifest_digest, 65, MPI_CHAR, 0, PETSC_COMM_WORLD));
2526 PetscFunctionReturn(0);
2527}
FieldLayout layout
const char * FieldLayoutName(FieldLayout layout)
Return a stable printable label for a field layout.
static PetscErrorCode WriteCheckpointPayloadEntry(FILE *manifest, const char *checkpoint_directory, PetscInt payload_index, const char *relative_path, const char *kind, const char *field_name, PetscInt block, const char *layout, PetscInt components, const char *logical_type, PetscInt global_size, const char *encoding)
Append one payload inventory entry to a checkpoint manifest.
Definition io.c:2231
PetscInt components
Degrees of freedom the vector carries.
const char * role
Inventory role: occupancy, mean, second_moment, co_moment.
const char * layout
Catalog layout name for the inventory entry.
PetscInt step
Definition variables.h:703
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteCheckpointBundle()

PetscErrorCode WriteCheckpointBundle ( SimCtx simCtx,
const char *  reason 
)

Implementation of the transactional full-state checkpoint coordinator.

Write and atomically publish one complete checkpoint bundle.

Definition at line 2530 of file io.c.

2531{
2532 UserCtx *user = NULL;
2533 char checkpoints_root[PETSC_MAX_PATH_LEN];
2534 char final_directory[PETSC_MAX_PATH_LEN];
2535 char temporary_directory[PETSC_MAX_PATH_LEN];
2536 char nested_directory[PETSC_MAX_PATH_LEN];
2537 char commit_path[PETSC_MAX_PATH_LEN];
2538 char geometry_digest[65] = "";
2539 char manifest_digest[65] = "";
2540 PetscBool final_exists = PETSC_FALSE;
2541 PetscInt particle_count = 0;
2542 PetscMPIInt writer_pid = 0;
2543
2544 PetscFunctionBeginUser;
2545 PetscCheck(simCtx != NULL && reason != NULL && reason[0] != '\0',
2546 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2547 "Simulation context and checkpoint reason are required.");
2548 user = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
2549 PetscCheck(user != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
2550 "Finest-level fields must exist before checkpoint output.");
2551
2552 PetscCall(PetscSNPrintf(checkpoints_root, sizeof(checkpoints_root), "%s/%s",
2554 PetscCall(FormatCheckpointStepDirectory(simCtx->output_dir, simCtx->step,
2555 final_directory, sizeof(final_directory)));
2556 PetscCall(PetscTestDirectory(final_directory, 'r', &final_exists));
2557 if (final_exists) {
2558 PetscReal saved_time = 0.0;
2559 PetscCall(ValidateCheckpointBundle(simCtx, user, final_directory, simCtx->step,
2560 &saved_time, NULL, NULL, NULL, NULL));
2561 PetscCheck(PetscAbsReal(saved_time - simCtx->ti) <=
2562 10.0 * PETSC_MACHINE_EPSILON * PetscMax(1.0, PetscAbsReal(simCtx->ti)),
2563 PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED,
2564 "Committed checkpoint step %" PetscInt_FMT " has time %.17g, current state has time %.17g.",
2565 simCtx->step, (double)saved_time, (double)simCtx->ti);
2566 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Checkpoint step %d is already committed; skipping duplicate write.\n",
2567 simCtx->step);
2568 PetscFunctionReturn(0);
2569 }
2570
2571 if (simCtx->rank == 0) writer_pid = (PetscMPIInt)getpid();
2572 PetscCallMPI(MPI_Bcast(&writer_pid, 1, MPI_INT, 0, PETSC_COMM_WORLD));
2573 PetscCall(PetscSNPrintf(temporary_directory, sizeof(temporary_directory),
2574 "%s/.step_%0*" PetscInt_FMT ".incomplete.%d",
2575 checkpoints_root, PICURV_CHECKPOINT_STEP_WIDTH,
2576 simCtx->step, (int)writer_pid));
2577 PetscCall(CreateCheckpointDirectoryCollective(simCtx, checkpoints_root));
2578 PetscCall(CreateCheckpointDirectoryCollective(simCtx, temporary_directory));
2579 PetscCall(PetscSNPrintf(nested_directory, sizeof(nested_directory), "%s/%s",
2580 temporary_directory, PICURV_EULERIAN_DIRECTORY));
2581 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2582 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2583 PetscCall(PetscSNPrintf(nested_directory, sizeof(nested_directory),
2584 "%s/%s/block_%04" PetscInt_FMT,
2585 temporary_directory, PICURV_EULERIAN_DIRECTORY, block));
2586 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2587 }
2588 if (simCtx->np > 0) {
2589 PetscCall(PetscSNPrintf(nested_directory, sizeof(nested_directory), "%s/%s",
2590 temporary_directory, PICURV_PARTICLE_DIRECTORY));
2591 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2592 PetscCall(DMSwarmGetSize(user->swarm, &particle_count));
2593 }
2594 if (FieldStatisticsIsActive(simCtx)) {
2595 PetscCall(FormatStatisticsPath(temporary_directory, -1, -1, NULL,
2596 nested_directory, sizeof(nested_directory)));
2597 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2598 for (PetscInt window = 0; window < simCtx->fieldStatisticsWindowCount; ++window) {
2599 PetscCall(FormatStatisticsPath(temporary_directory, window, -1, NULL,
2600 nested_directory, sizeof(nested_directory)));
2601 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2602 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2603 PetscCall(FormatStatisticsPath(temporary_directory, window, block, NULL,
2604 nested_directory, sizeof(nested_directory)));
2605 PetscCall(CreateCheckpointDirectoryCollective(simCtx, nested_directory));
2606 }
2607 }
2608 }
2609
2610 PetscCall(ComputeCheckpointGeometrySHA256(simCtx, user, geometry_digest));
2611 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2612 PetscCall(WriteSimulationFields(&user[block], temporary_directory));
2613 }
2614 if (simCtx->np > 0) PetscCall(WriteAllSwarmFields(user, temporary_directory));
2615 for (PetscInt block = 0; block < simCtx->block_number; ++block) {
2616 PetscCall(WriteStatisticsFields(&user[block], temporary_directory));
2617 }
2618 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
2619
2620 PetscCall(WriteCheckpointManifest(simCtx, user, temporary_directory, reason,
2621 geometry_digest, particle_count, manifest_digest));
2622 if (simCtx->rank == 0) {
2623 FILE *commit_file = NULL;
2624
2625 PetscCall(PetscSNPrintf(commit_path, sizeof(commit_path), "%s/COMMITTED", temporary_directory));
2626 commit_file = fopen(commit_path, "w");
2627 PetscCheck(commit_file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
2628 "Unable to create checkpoint commit marker '%s'.", commit_path);
2629 PetscCheck(fprintf(commit_file, "%s\n", manifest_digest) > 0 &&
2630 fflush(commit_file) == 0 && fsync(fileno(commit_file)) == 0,
2631 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2632 "Unable to write checkpoint commit marker '%s'.", commit_path);
2633 PetscCheck(fclose(commit_file) == 0, PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2634 "Unable to close checkpoint commit marker '%s'.", commit_path);
2635 PetscCheck(rename(temporary_directory, final_directory) == 0,
2636 PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
2637 "Unable to commit checkpoint '%s': %s", final_directory, strerror(errno));
2638 }
2639 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD));
2641 "Committed checkpoint step %d at t=%.17g (%s): %s\n",
2642 simCtx->step, (double)simCtx->ti, reason, final_directory);
2643 PetscFunctionReturn(0);
2644}
PetscErrorCode WriteAllSwarmFields(UserCtx *user, const char *checkpoint_directory)
Internal helper implementation: WriteAllSwarmFields().
Definition io.c:2196
PetscErrorCode WriteSimulationFields(UserCtx *user, const char *checkpoint_directory)
Implementation of WriteSimulationFields().
Definition io.c:2002
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 WriteStatisticsFields(UserCtx *user, const char *checkpoint_directory)
Write every window's accumulator payloads for one block into a bundle.
Definition io.c:2034
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:2282
char output_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:717
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 
)

Internal helper implementation: VecToArrayOnRank0().

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

Local to this translation unit.

Definition at line 2650 of file io.c.

2651{
2652 MPI_Comm comm;
2653 PetscMPIInt rank;
2654 Vec sequential_vec = NULL;
2655
2656 PetscFunctionBeginUser;
2657 PetscCheck(inVec != NULL && N != NULL && arrayOut != NULL,
2658 PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2659 "Vector, size output, and array output are required.");
2660 PetscCall(PetscObjectGetComm((PetscObject)inVec, &comm));
2661 PetscCallMPI(MPI_Comm_rank(comm, &rank));
2662 PetscCall(VecGetSize(inVec, N));
2663 *arrayOut = NULL;
2664
2665 PetscCall(GatherVectorToRankZero(inVec, &sequential_vec));
2666 if (rank == 0) {
2667 const PetscScalar *values = NULL;
2668 PetscInt local_size = 0;
2669
2670 PetscCall(VecGetLocalSize(sequential_vec, &local_size));
2671 PetscCall(PetscMalloc1(local_size, arrayOut));
2672 PetscCall(VecGetArrayRead(sequential_vec, &values));
2673 for (PetscInt index = 0; index < local_size; ++index) {
2674 (*arrayOut)[index] = (double)PetscRealPart(values[index]);
2675 }
2676 PetscCall(VecRestoreArrayRead(sequential_vec, &values));
2677 }
2678 PetscCall(VecDestroy(&sequential_vec));
2679 PetscFunctionReturn(0);
2680}
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 
)

Internal helper implementation: SwarmFieldToArrayOnRank0().

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

Local to this translation unit.

Definition at line 2686 of file io.c.

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

◆ DisplayBanner()

PetscErrorCode DisplayBanner ( SimCtx simCtx)

Emit the rank-zero startup summary from the effective simulation context.

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 2776 of file io.c.

2777{
2778 PetscErrorCode ierr;
2779 PetscMPIInt rank;
2780 Cmpnts global_min_coords, global_max_coords;
2781 PetscReal StartTime;
2782 PetscInt StartStep,StepsToRun,total_num_particles;
2783 PetscMPIInt num_mpi_procs;
2784 const char *log_level_name;
2785 const char *convergence_mode_name;
2786
2787 // SimCtx *simCtx = user->simCtx;
2788 UserCtx *user = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
2789 num_mpi_procs = simCtx->size;
2790 StartTime = simCtx->StartTime;
2791 StartStep = simCtx->StartStep;
2792 StepsToRun = simCtx->StepsToRun;
2793 total_num_particles = simCtx->np;
2794 BoundingBox *bboxlist_on_rank0 = simCtx->bboxlist;
2795
2796
2797 PetscFunctionBeginUser;
2798
2799 if (!user) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "DisplayBanner - UserCtx pointer is NULL.");
2800 switch (get_log_level()) {
2801 case LOG_ERROR: log_level_name = "ERROR"; break;
2802 case LOG_WARNING: log_level_name = "WARNING"; break;
2803 case LOG_INFO: log_level_name = "INFO"; break;
2804 case LOG_DEBUG: log_level_name = "DEBUG"; break;
2805 case LOG_TRACE: log_level_name = "TRACE"; break;
2806 case LOG_VERBOSE: log_level_name = "VERBOSE"; break;
2807 default: log_level_name = "UNKNOWN"; break;
2808 }
2809 switch (simCtx->solutionConvergenceMode) {
2810 case SOLUTION_CONVERGENCE_STEADY_DETERMINISTIC: convergence_mode_name = "STEADY_DETERMINISTIC"; break;
2811 case SOLUTION_CONVERGENCE_PERIODIC_DETERMINISTIC: convergence_mode_name = "PERIODIC_DETERMINISTIC"; break;
2812 case SOLUTION_CONVERGENCE_STATISTICAL_STEADY: convergence_mode_name = "STATISTICAL_STEADY"; break;
2813 case SOLUTION_CONVERGENCE_TRANSIENT: convergence_mode_name = "TRANSIENT"; break;
2814 default: convergence_mode_name = "UNKNOWN"; break;
2815 }
2816 global_min_coords = user->bbox.min_coords;
2817 global_max_coords = user->bbox.max_coords;
2818 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
2819
2820 if (rank == 0) {
2821 // If global_domain_bbox is not pre-populated in UserCtx, compute it here from bboxlist_on_rank0
2822 // This assumes bboxlist_on_rank0 is valid and contains all local bounding boxes on rank 0.
2823 if (bboxlist_on_rank0 && num_mpi_procs > 0) {
2824 global_min_coords = bboxlist_on_rank0[0].min_coords;
2825 global_max_coords = bboxlist_on_rank0[0].max_coords;
2826 for (PetscMPIInt p = 1; p < num_mpi_procs; ++p) {
2827 global_min_coords.x = PetscMin(global_min_coords.x, bboxlist_on_rank0[p].min_coords.x);
2828 global_min_coords.y = PetscMin(global_min_coords.y, bboxlist_on_rank0[p].min_coords.y);
2829 global_min_coords.z = PetscMin(global_min_coords.z, bboxlist_on_rank0[p].min_coords.z);
2830 global_max_coords.x = PetscMax(global_max_coords.x, bboxlist_on_rank0[p].max_coords.x);
2831 global_max_coords.y = PetscMax(global_max_coords.y, bboxlist_on_rank0[p].max_coords.y);
2832 global_max_coords.z = PetscMax(global_max_coords.z, bboxlist_on_rank0[p].max_coords.z);
2833 }
2834 // Optionally store this in user->global_domain_bbox if it's useful elsewhere
2835 // user->global_domain_bbox.min_coords = global_min_coords;
2836 // user->global_domain_bbox.max_coords = global_max_coords;
2837 } else {
2838 // Fallback or warning if bboxlist is not available for global calculation
2839 LOG_ALLOW(LOCAL, LOG_WARNING, "(Rank 0) - bboxlist not provided or num_mpi_procs <=0; using user->bbox for domain bounds.\n");
2840 // global_min_coords = user->bbox.min_coords; // Use local bbox of rank 0 as fallback
2841 // global_max_coords = user->bbox.max_coords;
2842 }
2843
2844 ierr = PetscPrintf(PETSC_COMM_SELF, "\n"); CHKERRQ(ierr);
2845 ierr = PetscPrintf(PETSC_COMM_SELF, "=============================================================\n"); CHKERRQ(ierr);
2846 ierr = PetscPrintf(PETSC_COMM_SELF, " CASE SUMMARY \n"); CHKERRQ(ierr);
2847 ierr = PetscPrintf(PETSC_COMM_SELF, "=============================================================\n"); CHKERRQ(ierr);
2848 ierr = PetscPrintf(PETSC_COMM_SELF, " Grid Points : %d X %d X %d\n", user->IM, user->JM, user->KM); CHKERRQ(ierr);
2849 ierr = PetscPrintf(PETSC_COMM_SELF, " Cells : %d X %d X %d\n", user->IM - 1, user->JM - 1, user->KM - 1); CHKERRQ(ierr);
2850 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);
2851 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);
2852 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);
2853 ierr = PetscPrintf(PETSC_COMM_SELF, " Periodic Axes (BC-derived) : I=%s, J=%s, K=%s\n",
2854 simCtx->i_periodic ? "YES" : "NO",
2855 simCtx->j_periodic ? "YES" : "NO",
2856 simCtx->k_periodic ? "YES" : "NO"); CHKERRQ(ierr);
2857 for (PetscInt axis = 0; axis < 3; axis++) {
2858 if (!user->periodic_translation_valid[axis]) continue;
2859 ierr = PetscPrintf(PETSC_COMM_SELF,
2860 " Periodic %c Translation : (%.6e, %.6e, %.6e)\n",
2861 "IJK"[axis],
2862 (double)user->periodic_translation[axis].x,
2863 (double)user->periodic_translation[axis].y,
2864 (double)user->periodic_translation[axis].z); CHKERRQ(ierr);
2865 }
2866 if (total_num_particles > 0 &&
2867 (simCtx->i_periodic || simCtx->j_periodic || simCtx->k_periodic)) {
2868 ierr = PetscPrintf(PETSC_COMM_SELF,
2869 " Particle Periodic Wrapping : UNSUPPORTED (Eulerian periodicity only)\n"); CHKERRQ(ierr);
2870 }
2871 if(strcmp(simCtx->eulerianSource,"load")==0 || strcmp(simCtx->eulerianSource,"solve")==0){
2872 ierr = PetscPrintf(PETSC_COMM_SELF, "-------------------- Boundary Conditions --------------------\n"); CHKERRQ(ierr);
2873 const int face_name_width = 17; // Adjusted for longer names (Zeta,Eta,Xi)
2874 for (PetscInt i_face = 0; i_face < 6; ++i_face) {
2875 BCFace current_face = (BCFace)i_face;
2876 // The BCFaceToString will now return the Xi, Eta, Zeta versions
2877 const char* face_str = BCFaceToString(current_face);
2878 const char* bc_type_str = BCTypeToString(user->boundary_faces[current_face].mathematical_type);
2879 const char* bc_handler_type_str = BCHandlerTypeToString(user->boundary_faces[current_face].handler_type);
2880 if(user->boundary_faces[current_face].mathematical_type == INLET){
2882 Cmpnts inlet_velocity = {0.0,0.0,0.0};
2883 PetscBool found;
2884 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"vx",&inlet_velocity.x,&found); CHKERRQ(ierr);
2885 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"vy",&inlet_velocity.y,&found); CHKERRQ(ierr);
2886 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"vz",&inlet_velocity.z,&found); CHKERRQ(ierr);
2887 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - [%.4f,%.4f,%.4f]\n",
2888 face_name_width, face_str, bc_type_str, bc_handler_type_str,inlet_velocity.x,inlet_velocity.y,inlet_velocity.z); CHKERRQ(ierr);
2889 } else if(user->boundary_faces[current_face].handler_type == BC_HANDLER_INLET_PARABOLIC){
2890 PetscReal v_max = 0.0;
2891 PetscBool found;
2892 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"v_max",&v_max,&found); CHKERRQ(ierr);
2893 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - v_max=%.4f\n",
2894 face_name_width, face_str, bc_type_str, bc_handler_type_str, v_max); CHKERRQ(ierr);
2895 } else if(user->boundary_faces[current_face].handler_type == BC_HANDLER_INLET_PROFILE_FROM_FILE){
2896 const char *source_file = "(missing)";
2897 for (BC_Param *param = user->boundary_faces[current_face].params; param; param = param->next) {
2898 if (strcasecmp(param->key, "source_file") == 0 && param->value) {
2899 source_file = param->value;
2900 break;
2901 }
2902 }
2903 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - source_file=%s\n",
2904 face_name_width, face_str, bc_type_str, bc_handler_type_str, source_file); CHKERRQ(ierr);
2905 }
2907 PetscBool trimflag,foundtrimflag;
2908 ierr = GetDrivenSeamFluxFlag(user->boundary_faces[current_face].params,&trimflag,&foundtrimflag); CHKERRQ(ierr);
2909 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - [from initial state] - %s\n",
2910 face_name_width, face_str, bc_type_str, bc_handler_type_str,trimflag?"Enforce seam flux":"Seam flux not enforced"); CHKERRQ(ierr);
2912 PetscReal flux;
2913 PetscBool trimflag,foundflux,foundtrimflag;
2914 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"target_flux",&flux,&foundflux); CHKERRQ(ierr);
2915 ierr = GetDrivenSeamFluxFlag(user->boundary_faces[current_face].params,&trimflag,&foundtrimflag); CHKERRQ(ierr);
2916 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - [%.4f] - %s\n",
2917 face_name_width, face_str, bc_type_str, bc_handler_type_str,flux,trimflag?"Enforce seam flux":"Seam flux not enforced"); CHKERRQ(ierr);
2918 } else{
2919 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s\n",
2920 face_name_width, face_str, bc_type_str,bc_handler_type_str); CHKERRQ(ierr);
2921 }
2922 }
2923 }
2924 ierr = PetscPrintf(PETSC_COMM_SELF, "-------------------------------------------------------------\n"); CHKERRQ(ierr);
2925 ierr = PetscPrintf(PETSC_COMM_SELF, " Run Mode : %s\n", simCtx->OnlySetup ? "SETUP ONLY" : "Full Simulation"); CHKERRQ(ierr);
2926 ierr = PetscPrintf(PETSC_COMM_SELF, " Start Time : %.4f\n", (double)StartTime); CHKERRQ(ierr);
2927 ierr = PetscPrintf(PETSC_COMM_SELF, " Timestep Size : %.4f\n", (double)simCtx->dt); CHKERRQ(ierr);
2928 ierr = PetscPrintf(PETSC_COMM_SELF, " Starting Step : %d\n", StartStep); CHKERRQ(ierr);
2929 ierr = PetscPrintf(PETSC_COMM_SELF, " Total Steps to Run : %d\n", StepsToRun); CHKERRQ(ierr);
2930 ierr = PetscPrintf(PETSC_COMM_SELF, " Ending Step : %d\n", StartStep + StepsToRun); CHKERRQ(ierr);
2931 if (simCtx->tiout > 0) {
2932 ierr = PetscPrintf(PETSC_COMM_SELF, " Field/Restart Cadence : every %d step(s)\n", simCtx->tiout); CHKERRQ(ierr);
2933 } else {
2934 ierr = PetscPrintf(PETSC_COMM_SELF, " Field/Restart Cadence : DISABLED\n"); CHKERRQ(ierr);
2935 }
2936 /* Recorded whether or not statistics are configured, so a log says plainly
2937 * whether monitoring was active rather than leaving its absence ambiguous. */
2938 if (FieldStatisticsIsActive(simCtx) && simCtx->statisticsConsoleOutputFreq > 0) {
2939 ierr = PetscPrintf(PETSC_COMM_SELF, " Statistics Console Cadence : every %d step(s), %d window(s)\n",
2940 simCtx->statisticsConsoleOutputFreq, simCtx->fieldStatisticsWindowCount); CHKERRQ(ierr);
2941 } else if (FieldStatisticsIsActive(simCtx)) {
2942 ierr = PetscPrintf(PETSC_COMM_SELF, " Statistics Console Cadence : DISABLED (%d window(s) accumulating)\n",
2943 simCtx->fieldStatisticsWindowCount); CHKERRQ(ierr);
2944 } else {
2945 ierr = PetscPrintf(PETSC_COMM_SELF, " Statistics Console Cadence : DISABLED (no window configured)\n"); CHKERRQ(ierr);
2946 }
2947 ierr = PetscPrintf(PETSC_COMM_SELF, " Immersed Boundary : %s\n", simCtx->immersed ? "ENABLED" : "DISABLED"); CHKERRQ(ierr);
2948 if (simCtx->walltimeGuardEnabled) {
2949 ierr = PetscPrintf(
2950 PETSC_COMM_SELF,
2951 " Runtime Walltime Guard : %s (warmup=%d, multiplier=%.2f, min=%.1f s, alpha=%.2f)\n",
2952 simCtx->walltimeGuardActive ? "ENABLED" : "CONFIGURED BUT INACTIVE",
2953 simCtx->walltimeGuardWarmupSteps,
2954 (double)simCtx->walltimeGuardMultiplier,
2955 (double)simCtx->walltimeGuardMinSeconds,
2956 (double)simCtx->walltimeGuardEstimatorAlpha
2957 ); CHKERRQ(ierr);
2958 } else {
2959 ierr = PetscPrintf(PETSC_COMM_SELF, " Runtime Walltime Guard : DISABLED\n"); CHKERRQ(ierr);
2960 }
2961 ierr = PetscPrintf(PETSC_COMM_SELF, " Console Log Level : %s\n", log_level_name); CHKERRQ(ierr);
2962 ierr = PetscPrintf(PETSC_COMM_SELF, " Profiling Timestep Output : %s\n", simCtx->profilingTimestepMode); CHKERRQ(ierr);
2963 ierr = PetscPrintf(PETSC_COMM_SELF, " Profiling Final Summary : %s\n", simCtx->profilingFinalSummary ? "ENABLED" : "DISABLED"); CHKERRQ(ierr);
2964 if (simCtx->runtimeMemoryLogEnabled) {
2965 ierr = PetscPrintf(PETSC_COMM_SELF, " Runtime Memory Log : ENABLED (%s)\n", simCtx->runtimeMemoryLogFile); CHKERRQ(ierr);
2966 } else {
2967 ierr = PetscPrintf(PETSC_COMM_SELF, " Runtime Memory Log : DISABLED\n"); CHKERRQ(ierr);
2968 }
2969 ierr = PetscPrintf(PETSC_COMM_SELF, " Solution Convergence Log : %s\n",
2970 simCtx->solutionConvergenceEnabled ? "ENABLED" : "DISABLED"); CHKERRQ(ierr);
2971 ierr = PetscPrintf(PETSC_COMM_SELF, " Number of MPI Processes : %d\n", num_mpi_procs); CHKERRQ(ierr);
2972 ierr = PetscPrintf(PETSC_COMM_WORLD," Number of Particles : %d\n", total_num_particles); CHKERRQ(ierr);
2973 if (simCtx->np > 0) {
2974 const char *particle_init_str = ParticleInitializationToString(simCtx->ParticleInitialization);
2975
2976 if (simCtx->particleConsoleOutputFreq > 0) {
2977 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Console Cadence : every %d step(s)\n", simCtx->particleConsoleOutputFreq); CHKERRQ(ierr);
2978 } else {
2979 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Console Cadence : DISABLED\n"); CHKERRQ(ierr);
2980 }
2981 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Log Row Sampling : every %d particle(s)\n", simCtx->LoggingFrequency); CHKERRQ(ierr);
2982 if (simCtx->StartStep > 0) {
2983 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Restart Mode : %s\n", simCtx->particleRestartMode); CHKERRQ(ierr);
2984 }
2985 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Initialization Mode: %s\n", particle_init_str); CHKERRQ(ierr);
2986 ierr = PetscPrintf(PETSC_COMM_SELF, " Interpolation Method : %s\n",
2987 simCtx->interpolationMethod == INTERP_TRILINEAR ? "Trilinear (direct cell-center)" : "CornerAveraged (legacy)"); CHKERRQ(ierr);
2990 if (user->inletFaceDefined) {
2991 ierr = PetscPrintf(PETSC_COMM_SELF, " Particles Initialized At : %s (Enum Val: %d)\n", BCFaceToString(user->identifiedInletBCFace), user->identifiedInletBCFace); CHKERRQ(ierr);
2992 } else {
2993 ierr = PetscPrintf(PETSC_COMM_SELF, " Particles Initialized At : --- (No INLET face identified)\n"); CHKERRQ(ierr);
2994 }
2995 }
2996 }
2997 if(strcmp(simCtx->eulerianSource,"solve")==0 || strcmp(simCtx->eulerianSource,"load")==0){
2998 ierr = PetscPrintf(PETSC_COMM_WORLD," Reynolds Number : %le\n", simCtx->ren); CHKERRQ(ierr);
2999 //ierr = PetscPrintf(PETSC_COMM_WORLD," Von-Neumann Number : %le\n", simCtx->vnn); CHKERRQ(ierr);
3000 if(strcmp(simCtx->eulerianSource,"solve")==0){
3001 //ierr = PetscPrintf(PETSC_COMM_WORLD," Stanton Number : %le\n", simCtx->st); CHKERRQ(ierr);
3002 ierr = PetscPrintf(PETSC_COMM_WORLD," Momentum Equation Solver : %s\n", MomentumSolverTypeToString(simCtx->mom_solver_type)); CHKERRQ(ierr);
3004 ierr = PetscPrintf(PETSC_COMM_WORLD," Initial Pseudo-CFL (Courant): %le\n", simCtx->pseudo_cfl); CHKERRQ(ierr);
3005 ierr = PetscPrintf(PETSC_COMM_WORLD," Pseudo-CFL Range : [%le, %le]\n", simCtx->min_pseudo_cfl, simCtx->max_pseudo_cfl); CHKERRQ(ierr);
3006 ierr = PetscPrintf(PETSC_COMM_WORLD," Pseudo-CFL Adaptation : growth=%le, reduction=%le, backtrack=%s\n",
3008 simCtx->no_pseudo_cfl_backtrack ? "DISABLED" : "ENABLED"); CHKERRQ(ierr);
3009 ierr = PetscPrintf(PETSC_COMM_WORLD," Pseudo-Time Iteration Limit : %d\n", simCtx->mom_max_pseudo_steps); CHKERRQ(ierr);
3010 } else if (simCtx->mom_solver_type == MOMENTUM_SOLVER_NEWTON_KRYLOV) {
3011 ierr = PetscPrintf(PETSC_COMM_WORLD," Newton-Krylov PETSc Controls: SNES/KSP options (mom_nk_*)\n"); CHKERRQ(ierr);
3012 ierr = PetscPrintf(PETSC_COMM_WORLD," Newton-Krylov History Log : %s\n", simCtx->mom_nk_monitor_history ? "ENABLED" : "DISABLED"); CHKERRQ(ierr);
3013 } else if (simCtx->mom_solver_type == MOMENTUM_SOLVER_EXPLICIT_RK) {
3014 ierr = PetscPrintf(PETSC_COMM_WORLD," Pseudo-Time Controller : NOT APPLICABLE\n"); CHKERRQ(ierr);
3015 }
3016 ierr = PetscPrintf(PETSC_COMM_WORLD," Solution Convergence Mode : %s\n", convergence_mode_name); CHKERRQ(ierr);
3018 ierr = PetscPrintf(PETSC_COMM_WORLD," Convergence Period : %d step(s)\n", simCtx->solutionConvergencePeriodSteps); CHKERRQ(ierr);
3020 ierr = PetscPrintf(PETSC_COMM_WORLD," Convergence Window : %d step(s)\n", simCtx->solutionConvergenceWindowSteps); CHKERRQ(ierr);
3021 }
3022 ierr = PetscPrintf(PETSC_COMM_WORLD," Large Eddy Simulation Model : %s\n", LESModelToString(simCtx->les)); CHKERRQ(ierr);
3023 }
3024 if (strcmp(simCtx->eulerianSource, "load") == 0) {
3025 ierr = PetscPrintf(PETSC_COMM_SELF, " Eulerian State Source : load (%s)\n",
3026 simCtx->restart_dir); CHKERRQ(ierr);
3027 } else if (simCtx->StartStep > 0) {
3028 ierr = PetscPrintf(PETSC_COMM_SELF, " Eulerian State Source : restart step %d (%s)\n",
3029 simCtx->StartStep, simCtx->restart_dir); CHKERRQ(ierr);
3030 } else {
3031 const char* field_init_str = InitialConditionModeToString(simCtx->initialConditionMode);
3032 ierr = PetscPrintf(PETSC_COMM_SELF, " Eulerian State Source : initial condition (%s)\n",
3033 field_init_str); CHKERRQ(ierr);
3034 }
3035 if (strcmp(simCtx->eulerianSource, "solve") == 0 && simCtx->StartStep == 0 &&
3037 ierr = PetscPrintf(PETSC_COMM_SELF,
3038 " Constant Velocity (Cart.) : x=%.4f y=%.4f z=%.4f\n",
3039 (double)simCtx->InitialConstantContra.x,
3040 (double)simCtx->InitialConstantContra.y,
3041 (double)simCtx->InitialConstantContra.z); CHKERRQ(ierr);
3042 } else if (strcmp(simCtx->eulerianSource, "solve") == 0 && simCtx->StartStep == 0 &&
3044 ierr = PetscPrintf(PETSC_COMM_SELF,
3045 " Constant Velocity (Curv.) : speed=%.4f direction=%s\n",
3046 (double)simCtx->icVelocityPhysical,
3047 FlowDirectionToString(simCtx->flowDirection)); CHKERRQ(ierr);
3048 } else if (strcmp(simCtx->eulerianSource, "solve") == 0 && simCtx->StartStep == 0 &&
3050 ierr = PetscPrintf(PETSC_COMM_SELF,
3051 " Poiseuille Peak Velocity : speed=%.4f direction=%s\n",
3052 (double)simCtx->icVelocityPhysical,
3053 FlowDirectionToString(simCtx->flowDirection)); CHKERRQ(ierr);
3054 } else if (strcmp(simCtx->eulerianSource, "solve") == 0 && simCtx->StartStep == 0 &&
3056 ierr = PetscPrintf(PETSC_COMM_SELF,
3057 " Initial Velocity File : field=%s directory=%s\n",
3058 simCtx->initialConditionField == IC_FIELD_UCAT ? "Ucat" : "Ucont",
3059 simCtx->initialConditionDirectory); CHKERRQ(ierr);
3060 }
3061 } else if(strcmp(simCtx->eulerianSource,"analytical")==0){
3062 ierr = PetscPrintf(PETSC_COMM_WORLD," Analytical Solution Type : %s\n", simCtx->AnalyticalSolutionType); CHKERRQ(ierr);
3063 }
3064 ierr = PetscPrintf(PETSC_COMM_SELF, "=============================================================\n"); CHKERRQ(ierr);
3065 ierr = PetscPrintf(PETSC_COMM_SELF, "\n"); CHKERRQ(ierr);
3066 }
3067 PetscFunctionReturn(0);
3068}
PetscErrorCode GetBCParamReal(BC_Param *params, const char *key, PetscReal *value_out, PetscBool *found)
Internal helper implementation: GetBCParamReal().
Definition io.c:752
PetscErrorCode GetDrivenSeamFluxFlag(BC_Param *params, PetscBool *value_out, PetscBool *found)
Implementation of GetDrivenSeamFluxFlag().
Definition io.c:814
const char * BCFaceToString(BCFace face)
Returns the canonical log token for a boundary-face enum value.
Definition logging.c:671
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:87
const char * BCTypeToString(BCType type)
Returns the canonical log token for a boundary mathematical type.
Definition logging.c:773
const char * FlowDirectionToString(FlowDirection fd)
Convert a FlowDirection enum value to its YAML token string.
Definition logging.c:705
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 * MomentumSolverTypeToString(MomentumSolverType SolverFlag)
Returns the canonical log token for a momentum-solver selector.
Definition logging.c:757
const char * ParticleInitializationToString(ParticleInitializationType ParticleInitialization)
Returns the canonical log token for a particle-initialization mode.
Definition logging.c:724
PetscReal icVelocityPhysical
Definition variables.h:759
PetscBool mom_nk_monitor_history
Definition variables.h:752
PetscBool profilingFinalSummary
Definition variables.h:868
PetscInt statisticsConsoleOutputFreq
Definition variables.h:772
PetscBool walltimeGuardActive
Definition variables.h:870
PetscReal pseudo_cfl_reduction_factor
Definition variables.h:745
InitialConditionMode initialConditionMode
Definition variables.h:754
@ PARTICLE_INIT_SURFACE_RANDOM
Random placement on the inlet face.
Definition variables.h:552
@ PARTICLE_INIT_SURFACE_EDGES
Deterministic placement at inlet face edges.
Definition variables.h:555
FlowDirection flowDirection
Definition variables.h:758
PetscBool runtimeMemoryLogEnabled
Enable the rank-reduced runtime memory log.
Definition variables.h:883
PetscReal min_pseudo_cfl
Definition variables.h:746
PetscReal ren
Definition variables.h:744
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
Definition variables.h:173
PetscBool solutionConvergenceEnabled
Definition variables.h:761
char runtimeMemoryLogFile[PETSC_MAX_PATH_LEN]
File name written under log_dir.
Definition variables.h:884
PetscInt StepsToRun
Definition variables.h:706
char profilingTimestepMode[32]
Definition variables.h:866
PetscBool no_pseudo_cfl_backtrack
Definition variables.h:748
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
Definition variables.h:172
PetscBool OnlySetup
Definition variables.h:711
@ MOMENTUM_SOLVER_DUALTIME_PICARD_JAMESON_RK
Definition variables.h:536
@ MOMENTUM_SOLVER_EXPLICIT_RK
Definition variables.h:535
@ MOMENTUM_SOLVER_NEWTON_KRYLOV
Definition variables.h:537
PetscInt solutionConvergencePeriodSteps
Definition variables.h:763
PetscScalar x
Definition variables.h:103
InterpolationMethod interpolationMethod
Definition variables.h:832
PetscReal max_pseudo_cfl
Definition variables.h:746
BoundingBox * bboxlist
Definition variables.h:830
char eulerianSource[PETSC_MAX_PATH_LEN]
Definition variables.h:715
PetscBool walltimeGuardEnabled
Definition variables.h:869
ParticleInitializationType ParticleInitialization
Definition variables.h:831
PetscScalar z
Definition variables.h:103
@ INTERP_TRILINEAR
Definition variables.h:565
PetscInt solutionConvergenceWindowSteps
Definition variables.h:764
PetscReal pseudo_cfl_growth_factor
Definition variables.h:745
char AnalyticalSolutionType[PETSC_MAX_PATH_LEN]
Definition variables.h:729
@ IC_MODE_CONSTANT_CARTESIAN
Definition variables.h:153
@ IC_MODE_POISEUILLE
Definition variables.h:154
@ IC_MODE_CONSTANT_STREAMWISE
Definition variables.h:155
@ IC_MODE_FILE
Definition variables.h:156
PetscInt particleConsoleOutputFreq
Definition variables.h:708
Cmpnts InitialConstantContra
Definition variables.h:757
PetscInt mom_max_pseudo_steps
Definition variables.h:737
PetscScalar y
Definition variables.h:103
@ IC_FIELD_UCAT
Definition variables.h:161
PetscMPIInt size
Definition variables.h:699
Cmpnts periodic_translation[3]
Definition variables.h:927
@ SOLUTION_CONVERGENCE_TRANSIENT
Definition variables.h:547
@ SOLUTION_CONVERGENCE_PERIODIC_DETERMINISTIC
Definition variables.h:545
@ SOLUTION_CONVERGENCE_STATISTICAL_STEADY
Definition variables.h:546
@ SOLUTION_CONVERGENCE_STEADY_DETERMINISTIC
Definition variables.h:544
PetscBool periodic_translation_valid[3]
Definition variables.h:928
SolutionConvergenceMode solutionConvergenceMode
Definition variables.h:762
InitialConditionField initialConditionField
Definition variables.h:755
BoundingBox bbox
Definition variables.h:922
MomentumSolverType mom_solver_type
Definition variables.h:736
PetscInt immersed
Definition variables.h:726
PetscReal pseudo_cfl
Definition variables.h:744
PetscInt LoggingFrequency
Definition variables.h:857
Defines a 3D axis-aligned bounding box.
Definition variables.h:171
A 3D point or vector with PetscScalar components.
Definition variables.h:102
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParsePostProcessingSettings()

PetscErrorCode ParsePostProcessingSettings ( SimCtx simCtx)

Internal helper implementation: ParsePostProcessingSettings().

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

Local to this translation unit.

Definition at line 3076 of file io.c.

3077{
3078 FILE *file;
3079 char line[1024];
3080 PetscBool startTimeSet, endTimeSet, timeStepSet;
3081
3082 PetscFunctionBeginUser;
3084
3085 if (!simCtx || !simCtx->pps) {
3086 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_NULL, "SimCtx or its pps member is NULL in ParsePostProcessingSettings.");
3087 }
3088
3089 char *configFile = simCtx->PostprocessingControlFile;
3090 PostProcessParams *pps = simCtx->pps;
3091
3092
3093 // --- 1. Set Sane Defaults First ---
3094 pps->startTime = 0;
3095 pps->endTime = 0;
3096 pps->timeStep = 1;
3097 pps->outputParticles = PETSC_FALSE;
3098 pps->particle_output_freq = simCtx->LoggingFrequency; // Default to logging frequency;
3099 strcpy(pps->process_pipeline, "");
3100 strcpy(pps->output_fields_instantaneous, "Ucat,P");
3101 strcpy(pps->output_prefix, "Field");
3102 strcpy(pps->particle_output_prefix,"Particle");
3103 strcpy(pps->particle_fields,"velocity,CellID,weight,pid");
3104 strcpy(pps->particle_pipeline,"");
3105 strncpy(pps->statistics_pipeline, "", MAX_PIPELINE_LENGTH - 1);
3106 strncpy(pps->statistics_output_prefix, "Stats", MAX_FILENAME_LENGTH - 1);
3107 strcpy(pps->particleExt,"dat"); // The input file format for particles.
3108 strcpy(pps->eulerianExt,"dat"); // The input file format for Eulerian fields.
3109 /* A negative source step means "the last step this recipe covers", so a recipe
3110 * that does not name one still derives from a bundle that exists. */
3111 pps->field_statistics_windows[0] = '\0';
3112 strcpy(pps->field_statistics_outputs, "mean,reynolds_stress,rms,tke,flux");
3113 strcpy(pps->field_statistics_formats, "vtk");
3115 pps->reference[0] = pps->reference[1] = pps->reference[2] = 1;
3116 strncpy(pps->source_dir, simCtx->output_dir, sizeof(pps->source_dir) - 1);
3117 pps->source_dir[sizeof(pps->source_dir) - 1] = '\0'; // Ensure null-termination
3118
3119 // --- 2. Parse the Configuration File (overrides defaults) ---
3120 file = fopen(configFile, "r");
3121 if (file) {
3122 LOG_ALLOW(GLOBAL, LOG_INFO, "Parsing post-processing config file: %s\n", configFile);
3123 while (fgets(line, sizeof(line), file)) {
3124 char *key, *value, *comment;
3125 comment = strchr(line, '#'); if (comment) *comment = '\0';
3126 TrimWhitespace(line); if (strlen(line) == 0) continue;
3127 key = strtok(line, "="); value = strtok(NULL, "=");
3128 if (key && value) {
3129 TrimWhitespace(key); TrimWhitespace(value);
3130 if (strcmp(key, "startTime") == 0) pps->startTime = atoi(value);
3131 else if (strcmp(key, "endTime") == 0) pps->endTime = atoi(value);
3132 else if (strcmp(key, "timeStep") == 0) pps->timeStep = atoi(value);
3133 else if (strcmp(key, "output_particles") == 0) {
3134 if (strcasecmp(value, "true") == 0) pps->outputParticles = PETSC_TRUE;
3135 }
3136 else if (strcasecmp(key, "process_pipeline") == 0) {
3137 strncpy(pps->process_pipeline, value, MAX_PIPELINE_LENGTH - 1);
3138 pps->process_pipeline[MAX_PIPELINE_LENGTH - 1] = '\0'; // Ensure null-termination
3139 } else if (strcasecmp(key, "field_statistics_windows") == 0) {
3140 strncpy(pps->field_statistics_windows, value, MAX_FIELD_LIST_LENGTH - 1);
3142 } else if (strcasecmp(key, "field_statistics_formats") == 0) {
3143 strncpy(pps->field_statistics_formats, value, MAX_FIELD_LIST_LENGTH - 1);
3145 } else if (strcasecmp(key, "field_statistics_outputs") == 0) {
3146 strncpy(pps->field_statistics_outputs, value, MAX_FIELD_LIST_LENGTH - 1);
3148 } else if (strcasecmp(key, "field_statistics_source_step") == 0) {
3149 pps->field_statistics_source_step = atoi(value);
3150 } else if (strcasecmp(key, "output_fields_instantaneous") == 0) {
3151 strncpy(pps->output_fields_instantaneous, value, MAX_FIELD_LIST_LENGTH - 1);
3153 } else if (strcasecmp(key, "output_prefix") == 0) {
3154 strncpy(pps->output_prefix, value, MAX_FILENAME_LENGTH - 1);
3155 pps->output_prefix[MAX_FILENAME_LENGTH - 1] = '\0';
3156 } else if (strcasecmp(key, "particle_output_prefix") == 0) {
3157 strncpy(pps->particle_output_prefix, value, MAX_FILENAME_LENGTH - 1);
3159 } else if (strcasecmp(key, "particle_fields_instantaneous") == 0) {
3160 strncpy(pps->particle_fields, value, MAX_FIELD_LIST_LENGTH - 1);
3161 pps->particle_fields[MAX_FIELD_LIST_LENGTH - 1] = '\0';
3162 } else if (strcasecmp(key, "particle_pipeline") == 0) {
3163 strncpy(pps->particle_pipeline, value, MAX_PIPELINE_LENGTH - 1);
3164 pps->particle_pipeline[MAX_PIPELINE_LENGTH - 1] = '\0';
3165 } else if (strcasecmp(key, "particle_output_freq") == 0) {
3166 pps->particle_output_freq = atoi(value);
3167 } else if (strcasecmp(key, "statistics_pipeline") == 0) {
3168 strncpy(pps->statistics_pipeline, value, MAX_PIPELINE_LENGTH - 1);
3170 } else if (strcasecmp(key, "statistics_output_prefix") == 0) {
3171 strncpy(pps->statistics_output_prefix, value, MAX_FILENAME_LENGTH - 1);
3173 } else if (strcasecmp(key, "particleExt") == 0) {
3174 strncpy(pps->particleExt, value, sizeof(pps->particleExt) - 1);
3175 pps->particleExt[sizeof(pps->particleExt) - 1] = '\0';
3176 } else if (strcasecmp(key, "eulerianExt") == 0) {
3177 strncpy(pps->eulerianExt, value, sizeof(pps->eulerianExt) - 1);
3178 pps->eulerianExt[sizeof(pps->eulerianExt) - 1] = '\0';
3179 } else if (strcmp(key, "reference_ip") == 0) {pps->reference[0] = atoi(value);
3180 } else if (strcmp(key, "reference_jp") == 0) {pps->reference[1] = atoi(value);
3181 } else if (strcmp(key, "reference_kp") == 0) {pps->reference[2] = atoi(value);
3182 } else if (strcasecmp(key, "source_directory") == 0) {
3183 strncpy(pps->source_dir, value, sizeof(pps->source_dir) - 1);
3184 pps->source_dir[sizeof(pps->source_dir) - 1] = '\0';
3185 } else if (strcasecmp(key, "spectra_signature") == 0) {
3186 /* Spectra are computed by the conductor's Python stage, not here. The
3187 key exists so a change to the spectra recipe reaches the recipe
3188 fingerprint that `--continue` compares; this executable has no use
3189 for it and accepting it silently keeps the log free of a warning
3190 that would appear on every post-processing run. */
3191 } else {
3192 LOG_ALLOW(GLOBAL, LOG_WARNING, "Unknown key '%s' in post-processing config file. Ignoring.\n", key);
3193 }
3194 // Add parsing for pipeline, fields, etc. in later phases
3195 }
3196 }
3197 fclose(file);
3198 } else {
3199 LOG_ALLOW(GLOBAL, LOG_WARNING, "Could not open post-processing config file '%s'. Using defaults and command-line overrides.\n", configFile);
3200 }
3201
3202 // --- 3. Parse Command-Line Options (overrides file settings and defaults) ---
3203 PetscOptionsGetInt(NULL, NULL, "-startTime", &pps->startTime, &startTimeSet);
3204 PetscOptionsGetInt(NULL, NULL, "-endTime", &pps->endTime, &endTimeSet);
3205 PetscOptionsGetInt(NULL, NULL, "-timeStep", &pps->timeStep, &timeStepSet);
3206 PetscOptionsGetBool(NULL, NULL, "-output_particles", &pps->outputParticles, NULL);
3207
3208 if(pps->endTime==-1){
3209 pps->endTime = simCtx->StartStep + simCtx->StepsToRun; // Total steps if endTime is set to -1.
3210 }
3211
3212 // If only startTime is given on command line, run for a single step
3213 if (startTimeSet && !endTimeSet) {
3214 pps->endTime = pps->startTime;
3215 }
3216
3217 LOG_ALLOW(GLOBAL, LOG_INFO, "Post-processing configured to run from t=%d to t=%d with step %d. Particle output: %s.\n",
3218 pps->startTime, pps->endTime, pps->timeStep, pps->outputParticles ? "TRUE" : "FALSE");
3219
3220 LOG_ALLOW(GLOBAL, LOG_INFO, "Process Pipeline: %s\n", pps->process_pipeline);
3221 LOG_ALLOW(GLOBAL, LOG_INFO, "Instantaneous Output Fields: %s\n", pps->output_fields_instantaneous);
3222 LOG_ALLOW(GLOBAL, LOG_INFO, "Output Prefix: %s\n", pps->output_prefix);
3223 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Output Prefix: %s\n", pps->particle_output_prefix);
3224 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Fields: %s\n", pps->particle_fields);
3225 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Pipeline: %s\n", pps->particle_pipeline);
3226 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Output Frequency: %d\n", pps->particle_output_freq);
3227 LOG_ALLOW(GLOBAL, LOG_INFO, "Post input extensions: Eulerian='.%s', Particle='.%s'\n", pps->eulerianExt, pps->particleExt);
3228
3230 PetscFunctionReturn(0);
3231}
char statistics_output_prefix[256]
basename for CSV output, e.g.
Definition variables.h:617
char particle_output_prefix[256]
Definition variables.h:612
char output_prefix[256]
Definition variables.h:609
#define MAX_FIELD_LIST_LENGTH
Definition variables.h:587
#define MAX_PIPELINE_LENGTH
Definition variables.h:586
PetscInt reference[3]
Definition variables.h:634
PetscInt timeStep
Definition variables.h:603
#define MAX_FILENAME_LENGTH
Definition variables.h:588
char statistics_pipeline[1024]
e.g.
Definition variables.h:616
char field_statistics_formats[1024]
Comma-separated formats: vtk for derived fields, csv for the convergence history.
Definition variables.h:625
char output_fields_instantaneous[1024]
Definition variables.h:608
char eulerianExt[8]
Definition variables.h:630
char particle_pipeline[1024]
Definition variables.h:610
char process_pipeline[1024]
Definition variables.h:607
PetscInt particle_output_freq
Definition variables.h:613
char particle_fields[1024]
Definition variables.h:611
PetscBool outputParticles
Definition variables.h:604
PetscInt field_statistics_source_step
Committed step supplying the state; negative means the step being processed.
Definition variables.h:627
char particleExt[8]
Definition variables.h:631
char field_statistics_windows[1024]
Comma-separated window names to derive; empty disables the pipeline.
Definition variables.h:621
char field_statistics_outputs[1024]
Comma-separated outputs: mean, reynolds_stress, rms, tke, flux.
Definition variables.h:623
PetscInt startTime
Definition variables.h:601
char PostprocessingControlFile[PETSC_MAX_PATH_LEN]
Definition variables.h:889
Holds all configuration parameters for a post-processing run.
Definition variables.h:596
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ParseScalingInformation()

PetscErrorCode ParseScalingInformation ( SimCtx simCtx)

Implementation of ParseScalingInformation().

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 3242 of file io.c.

3243{
3244 PetscErrorCode ierr;
3245 PetscBool flg;
3246
3247 PetscFunctionBeginUser;
3249
3250 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx is NULL in ParseScalingInformation");
3251
3252 // --- 1. Set default values to 1.0 ---
3253 // This represents a purely non-dimensional run if no scaling is provided.
3254 simCtx->scaling.L_ref = 1.0;
3255 simCtx->scaling.U_ref = 1.0;
3256 simCtx->scaling.rho_ref = 1.0;
3257
3258 // --- 2. Read overrides from the command line / control file ---
3259 ierr = PetscOptionsGetReal(NULL, NULL, "-scaling_L_ref", &simCtx->scaling.L_ref, &flg); CHKERRQ(ierr);
3260 ierr = PetscOptionsGetReal(NULL, NULL, "-scaling_U_ref", &simCtx->scaling.U_ref, &flg); CHKERRQ(ierr);
3261 ierr = PetscOptionsGetReal(NULL, NULL, "-scaling_rho_ref", &simCtx->scaling.rho_ref, &flg); CHKERRQ(ierr);
3262
3263 // --- 3. Calculate derived scaling factors ---
3264 // Check for division by zero to be safe, though U_ref should be positive.
3265 if (simCtx->scaling.U_ref <= 0.0) {
3266 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Reference velocity U_ref must be positive. Got %g", (double)simCtx->scaling.U_ref);
3267 }
3268 simCtx->scaling.P_ref = simCtx->scaling.rho_ref * simCtx->scaling.U_ref * simCtx->scaling.U_ref;
3269
3270 // --- 4. Log the final, effective scales for verification ---
3271 LOG(GLOBAL, LOG_INFO, "---------------- Physical Scales Initialized -----------------\n");
3272 LOG(GLOBAL, LOG_INFO, " L_ref: %.4f, U_ref: %.4f, rho_ref: %.4f, P_ref: %.4f\n",
3273 simCtx->scaling.L_ref, simCtx->scaling.U_ref, simCtx->scaling.rho_ref, simCtx->scaling.P_ref);
3274 LOG(GLOBAL, LOG_INFO, "--------------------------------------------------------------\n");
3275
3277 PetscFunctionReturn(0);
3278}
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:84
PetscReal L_ref
Definition variables.h:677
ScalingCtx scaling
Definition variables.h:785
PetscReal P_ref
Definition variables.h:680
PetscReal rho_ref
Definition variables.h:679
PetscReal U_ref
Definition variables.h:678
Here is the caller graph for this function:

◆ ReadDataFileToArray()

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

Implementation of 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 3286 of file io.c.

3290{
3291 /* STEP 0: Prepare local variables & log function entry */
3292 PetscMPIInt rank, size;
3293 PetscErrorCode ierr;
3294 FILE *fp = NULL;
3295 PetscInt N = 0; /* number of lines/values read on rank 0 */
3296 double *array = NULL; /* pointer to local array on each rank */
3297 PetscInt fileExistsFlag = 0; /* 0 = doesn't exist, 1 = does exist */
3298
3300 "Start reading from file: %s\n",
3301 filename);
3302
3303 /* Basic error checking: data_out, Nout must be non-null. */
3304 if (!filename || !data_out || !Nout) {
3306 "Null pointer argument provided.\n");
3307 return 1;
3308 }
3309
3310 /* Determine rank/size for coordinating I/O. */
3311 MPI_Comm_rank(comm, &rank);
3312 MPI_Comm_size(comm, &size);
3313
3314 /* STEP 1: On rank 0, check if file can be opened. */
3315 if (!rank) {
3316 fp = fopen(filename, "r");
3317 if (fp) {
3318 fileExistsFlag = 1;
3319 fclose(fp);
3320 }
3321 }
3322
3323 /* STEP 2: Broadcast file existence to all ranks. */
3324 // In ReadDataFileToArray:
3325 ierr = MPI_Bcast(&fileExistsFlag, 1, MPI_INT, 0, comm); CHKERRQ(ierr);
3326
3327 if (!fileExistsFlag) {
3328 /* If file does not exist, log & return. */
3329 if (!rank) {
3331 "File '%s' not found.\n",
3332 filename);
3333 }
3334 return 2;
3335 }
3336
3337 /* STEP 3: Rank 0 re-opens and reads the file, counting lines, etc. */
3338 if (!rank) {
3339 fp = fopen(filename, "r");
3340 if (!fp) {
3342 "File '%s' could not be opened for reading.\n",
3343 filename);
3344 return 3;
3345 }
3346
3347 /* (3a) Count lines first. */
3348 {
3349 char line[256];
3350 while (fgets(line, sizeof(line), fp)) {
3351 N++;
3352 }
3353 }
3354
3356 "File '%s' has %d lines.\n",
3357 filename, N);
3358
3359 /* (3b) Allocate array on rank 0. */
3360 array = (double*)malloc(N * sizeof(double));
3361 if (!array) {
3362 fclose(fp);
3364 "malloc failed for array.\n");
3365 return 4;
3366 }
3367
3368 /* (3c) Rewind & read values into array. */
3369 rewind(fp);
3370 {
3371 PetscInt i = 0;
3372 char line[256];
3373 while (fgets(line, sizeof(line), fp)) {
3374 double val;
3375 if (sscanf(line, "%lf", &val) == 1) {
3376 array[i++] = val;
3377 }
3378 }
3379 }
3380 fclose(fp);
3381
3383 "Successfully read %d values from '%s'.\n",
3384 N, filename);
3385 }
3386
3387 /* STEP 4: Broadcast the integer N to all ranks. */
3388 ierr = MPI_Bcast(&N, 1, MPI_INT, 0, comm); CHKERRQ(ierr);
3389
3390 /* STEP 5: Each rank allocates an array to receive the broadcast if rank>0. */
3391 if (rank) {
3392 array = (double*)malloc(N * sizeof(double));
3393 if (!array) {
3395 "malloc failed on rank %d.\n",
3396 rank);
3397 return 5;
3398 }
3399 }
3400
3401 /* STEP 6: Broadcast the actual data from rank 0 to all. */
3402 ierr = MPI_Bcast(array, N, MPI_DOUBLE, 0, comm); CHKERRQ(ierr);
3403
3404 /* STEP 7: Assign outputs on all ranks. */
3405 *data_out = array;
3406 *Nout = N;
3407
3409 "Done. Provided array of length=%d to all ranks.\n",
3410 N);
3411 return 0; /* success */
3412}

◆ ReadPositionsFromFile()

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

Internal helper implementation: ReadPositionsFromFile().

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 3418 of file io.c.

3422{
3423 PetscFunctionBeginUser;
3424
3425 PetscErrorCode ierr;
3426 Vec coordsVec;
3427
3428 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Creating coords Vec.\n");
3429 ierr = VecCreate(PETSC_COMM_WORLD, &coordsVec);CHKERRQ(ierr);
3430 ierr = VecSetFromOptions(coordsVec);CHKERRQ(ierr);
3431
3432 // For example: "position" is the name of the coordinate data
3433 ierr = ReadFieldData(user, ParticleFieldName(PARTICLE_FIELD_ID_POSITION), coordsVec, "dat");
3434 if (ierr) {
3436 "Error reading position data (ti=%d).\n",
3437 timeIndex);
3438 PetscFunctionReturn(ierr);
3439 }
3440
3441 LOG_ALLOW(GLOBAL, LOG_DEBUG, "ReadPositions - Gathering coords Vec to rank 0.\n");
3442 ierr = VecToArrayOnRank0(coordsVec, Ncoords, coordsArray);CHKERRQ(ierr);
3443
3444 ierr = VecDestroy(&coordsVec);CHKERRQ(ierr);
3445
3447 "Successfully gathered coordinates. Ncoords=%d.\n", *Ncoords);
3448 PetscFunctionReturn(0);
3449}
PetscErrorCode VecToArrayOnRank0(Vec inVec, PetscInt *N, double **arrayOut)
Internal helper implementation: VecToArrayOnRank0().
Definition io.c:2650
Here is the call graph for this function:

◆ ReadFieldDataToRank0()

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

Internal helper implementation: ReadFieldDataToRank0().

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

Local to this translation unit.

Definition at line 3456 of file io.c.

3461{
3462 PetscFunctionBeginUser;
3463
3464 PetscErrorCode ierr;
3465 Vec fieldVec;
3466
3467 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Creating field Vec.\n");
3468 ierr = VecCreate(PETSC_COMM_WORLD, &fieldVec);CHKERRQ(ierr);
3469 ierr = VecSetFromOptions(fieldVec);CHKERRQ(ierr);
3470
3471 ierr = ReadFieldData(user, fieldName, fieldVec, "dat");
3472 if (ierr) {
3474 "Error reading field '%s' (ti=%d).\n",
3475 fieldName, timeIndex);
3476 PetscFunctionReturn(ierr);
3477 }
3478
3479 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Gathering field Vec to rank 0.\n");
3480 ierr = VecToArrayOnRank0(fieldVec, Nscalars, scalarArray);CHKERRQ(ierr);
3481
3482 ierr = VecDestroy(&fieldVec);CHKERRQ(ierr);
3483
3485 "Successfully gathered field '%s'. Nscalars=%d.\n",
3486 fieldName, *Nscalars);
3487 PetscFunctionReturn(0);
3488}
Here is the call graph for this function:

Variable Documentation

◆ g_nblk_from_file

PetscInt g_nblk_from_file = 0
static

Stores the number of blocks read from the grid file.

Definition at line 34 of file io.c.

◆ g_IMs_from_file

PetscInt* g_IMs_from_file = NULL
static

Caches the IM dimensions for all blocks read from the grid file.

Definition at line 36 of file io.c.

◆ g_JMs_from_file

PetscInt* g_JMs_from_file = NULL
static

Caches the JM dimensions for all blocks read from the grid file.

Definition at line 38 of file io.c.

◆ g_KMs_from_file

PetscInt* g_KMs_from_file = NULL
static

Caches the KM dimensions for all blocks read from the grid file.

Definition at line 40 of file io.c.

◆ g_file_has_been_read

PetscBool g_file_has_been_read = PETSC_FALSE
static

A flag to ensure the grid file is read only once.

Definition at line 42 of file io.c.