PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
io.c
Go to the documentation of this file.
1/**
2 * @file io.c
3 * @brief Implementation of data input/output routines, focusing on grid configuration.
4 *
5 * This module provides functions to parse grid geometry information, either from
6 * command-line options for programmatically generated grids or by reading the
7 * header of a grid definition file.
8 */
9
10#include "io.h"
11
12// =============================================================================
13// STATIC (PRIVATE) VARIABLES FOR ONE-TIME FILE READ
14// =============================================================================
15
16/** @brief Stores the number of blocks read from the grid file. */
17static PetscInt g_nblk_from_file = 0;
18/** @brief Caches the IM dimensions for all blocks read from the grid file. */
19static PetscInt* g_IMs_from_file = NULL;
20/** @brief Caches the JM dimensions for all blocks read from the grid file. */
21static PetscInt* g_JMs_from_file = NULL;
22/** @brief Caches the KM dimensions for all blocks read from the grid file. */
23static PetscInt* g_KMs_from_file = NULL;
24/** @brief A flag to ensure the grid file is read only once. */
25static PetscBool g_file_has_been_read = PETSC_FALSE;
26
27
28// =============================================================================
29// PUBLIC FUNCTION IMPLEMENTATIONS
30// =============================================================================
31
32/**
33 * @brief Implementation of \ref TrimWhitespace().
34 * @details Full API contract (arguments, ownership, side effects) is documented with
35 * the header declaration in `include/io.h`.
36 * @see TrimWhitespace()
37 */
38void TrimWhitespace(char *str) {
39 if (!str) return;
40
41 char *start = str;
42 // Find the first non-whitespace character
43 while (isspace((unsigned char)*start)) {
44 start++;
45 }
46
47 // Find the end of the string
48 char *end = str + strlen(str) - 1;
49 // Move backwards from the end to find the last non-whitespace character
50 while (end > start && isspace((unsigned char)*end)) {
51 end--;
52 }
53
54 // Null-terminate after the last non-whitespace character
55 *(end + 1) = '\0';
56
57 // If there was leading whitespace, shift the string to the left
58 if (str != start) {
59 memmove(str, start, (end - start) + 2); // +2 to include the new null terminator
60 }
61}
62
63/**
64 * @brief Implementation of \ref ShouldWriteDataOutput().
65 * @details Full API contract (arguments, ownership, side effects) is documented with
66 * the header declaration in `include/io.h`.
67 * @see ShouldWriteDataOutput()
68 */
69
70PetscBool ShouldWriteDataOutput(const SimCtx *simCtx, PetscInt completed_step)
71{
72 if (!simCtx) {
73 return PETSC_FALSE;
74 }
75 return (PetscBool)(simCtx->tiout > 0 && completed_step > 0 && completed_step % simCtx->tiout == 0);
76}
77
78
79#undef __FUNCT__
80#define __FUNCT__ "ReadGridGenerationInputs"
81/**
82 * @brief Internal helper implementation: `ReadGridGenerationInputs()`.
83 * @details Local to this translation unit.
84 */
85PetscErrorCode ReadGridGenerationInputs(UserCtx *user)
86{
87 PetscErrorCode ierr;
88 SimCtx *simCtx = user->simCtx;
89 PetscInt nblk = simCtx->block_number;
90 PetscInt block_index = user->_this;
91 PetscBool found;
92
93 // Temporary arrays to hold the parsed values for ALL blocks
94 PetscInt *IMs = NULL, *JMs = NULL, *KMs = NULL, *cgrids = NULL;
95 PetscReal *xMins = NULL, *xMaxs = NULL, *rxs = NULL;
96 PetscReal *yMins = NULL, *yMaxs = NULL, *rys = NULL;
97 PetscReal *zMins = NULL, *zMaxs = NULL, *rzs = NULL;
98
99 PetscFunctionBeginUser;
101
102 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Reading generated grid inputs for block %d.\n", simCtx->rank, block_index);
103
104 if (block_index >= nblk) {
105 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Block index %d is out of range for nblk=%d", block_index, nblk);
106 }
107
108 // --- Allocate temporary storage for all array options ---
109 ierr = PetscMalloc4(nblk, &IMs, nblk, &JMs, nblk, &KMs, nblk, &cgrids); CHKERRQ(ierr);
110 ierr = PetscMalloc6(nblk, &xMins, nblk, &xMaxs, nblk, &rxs, nblk, &yMins, nblk, &yMaxs, nblk, &rys); CHKERRQ(ierr);
111 ierr = PetscMalloc3(nblk, &zMins, nblk, &zMaxs, nblk, &rzs); CHKERRQ(ierr);
112
113 // --- Set default values for the temporary arrays ---
114 for (PetscInt i = 0; i < nblk; ++i) {
115 IMs[i] = 10; JMs[i] = 10; KMs[i] = 10; cgrids[i] = 0;
116 xMins[i] = 0.0; xMaxs[i] = 1.0; rxs[i] = 1.0;
117 yMins[i] = 0.0; yMaxs[i] = 1.0; rys[i] = 1.0;
118 zMins[i] = 0.0; zMaxs[i] = 1.0; rzs[i] = 1.0;
119 }
120
121 // --- Parse the array options from the command line / control file ---
122 PetscInt count;
123 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-im", IMs, &count, &found); CHKERRQ(ierr);
124 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-jm", JMs, &count, &found); CHKERRQ(ierr);
125 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-km", KMs, &count, &found); CHKERRQ(ierr);
126 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-xMins", xMins, &count, &found); CHKERRQ(ierr);
127 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-xMaxs", xMaxs, &count, &found); CHKERRQ(ierr);
128 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-rxs", rxs, &count, &found); CHKERRQ(ierr);
129 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-yMins", yMins, &count, &found); CHKERRQ(ierr);
130 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-yMaxs", yMaxs, &count, &found); CHKERRQ(ierr);
131 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-rys", rys, &count, &found); CHKERRQ(ierr);
132 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-zMins", zMins, &count, &found); CHKERRQ(ierr);
133 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-zMaxs", zMaxs, &count, &found); CHKERRQ(ierr);
134 count = nblk; ierr = PetscOptionsGetRealArray(NULL, NULL, "-rzs", rzs, &count, &found); CHKERRQ(ierr);
135 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-cgrids", cgrids, &count, &found); CHKERRQ(ierr);
136
137 // --- Assign the parsed values to the specific UserCtx struct passed in ---
138 user->IM = IMs[block_index];
139 user->JM = JMs[block_index];
140 user->KM = KMs[block_index];
141 user->Min_X = xMins[block_index];
142 user->Max_X = xMaxs[block_index];
143 user->rx = rxs[block_index];
144 user->Min_Y = yMins[block_index];
145 user->Max_Y = yMaxs[block_index];
146 user->ry = rys[block_index];
147 user->Min_Z = zMins[block_index];
148 user->Max_Z = zMaxs[block_index];
149 user->rz = rzs[block_index];
150 user->cgrid = cgrids[block_index];
151
152 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Block %d grid generation inputs set: IM=%d, JM=%d, KM=%d\n",
153 simCtx->rank, block_index, user->IM, user->JM, user->KM);
154 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Block %d bounds: X=[%.2f, %.2f], Y=[%.2f, %.2f], Z=[%.2f, %.2f]\n",
155 simCtx->rank, block_index, user->Min_X, user->Max_X, user->Min_Y, user->Max_Y, user->Min_Z, user->Max_Z);
156
157 // --- Clean up temporary storage ---
158 ierr = PetscFree4(IMs, JMs, KMs, cgrids); CHKERRQ(ierr);
159 ierr = PetscFree6(xMins, xMaxs, rxs, yMins, yMaxs, rys); CHKERRQ(ierr);
160 ierr = PetscFree3(zMins, zMaxs, rzs); CHKERRQ(ierr);
161
163 PetscFunctionReturn(0);
164}
165
166/**
167 * @brief Internal helper implementation: `PopulateFinestUserGridResolutionFromOptions()`.
168 * @details Local to this translation unit.
169 */
170PetscErrorCode PopulateFinestUserGridResolutionFromOptions(UserCtx *finest_users, PetscInt nblk)
171{
172 PetscErrorCode ierr;
173 PetscBool found;
174 PetscInt *IMs = NULL, *JMs = NULL, *KMs = NULL;
175 SimCtx *simCtx = NULL;
176
177 PetscFunctionBeginUser;
178
179 if (!finest_users) {
180 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "finest_users cannot be NULL.");
181 }
182 if (nblk <= 0) {
183 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "nblk must be positive. Got %d.", nblk);
184 }
185 simCtx = finest_users[0].simCtx;
186
187 ierr = PetscMalloc3(nblk, &IMs, nblk, &JMs, nblk, &KMs); CHKERRQ(ierr);
188 for (PetscInt i = 0; i < nblk; ++i) {
189 IMs[i] = 10; JMs[i] = 10; KMs[i] = 10;
190 }
191
192 PetscInt count;
193 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-im", IMs, &count, &found); CHKERRQ(ierr);
194 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-jm", JMs, &count, &found); CHKERRQ(ierr);
195 count = nblk; ierr = PetscOptionsGetIntArray(NULL, NULL, "-km", KMs, &count, &found); CHKERRQ(ierr);
196
197 for (PetscInt bi = 0; bi < nblk; ++bi) {
198 finest_users[bi].IM = IMs[bi];
199 finest_users[bi].JM = JMs[bi];
200 finest_users[bi].KM = KMs[bi];
201 if (simCtx) {
203 "Rank %d: Preloaded analytical grid resolution for block %d: IM=%d, JM=%d, KM=%d\n",
204 simCtx->rank, bi, finest_users[bi].IM, finest_users[bi].JM, finest_users[bi].KM);
205 }
206 }
207
208 ierr = PetscFree3(IMs, JMs, KMs); CHKERRQ(ierr);
209 PetscFunctionReturn(0);
210}
211
212
213#undef __FUNCT__
214#define __FUNCT__ "ReadGridFile"
215/**
216 * @brief Internal helper implementation: `ReadGridFile()`.
217 * @details Local to this translation unit.
218 */
219PetscErrorCode ReadGridFile(UserCtx *user)
220{
221 PetscErrorCode ierr;
222 SimCtx *simCtx = user->simCtx;
223 PetscInt block_index = user->_this;
224
225 PetscFunctionBeginUser;
227
228 // --- One-Time Read and Broadcast Logic ---
230 LOG_ALLOW_SYNC(GLOBAL, LOG_INFO, "First call to ReadGridFile. Reading and broadcasting grid file header from '%s'...\n", simCtx->grid_file);
231 PetscMPIInt rank = simCtx->rank;
232 PetscInt nblk = simCtx->block_number;
233
234 if (rank == 0) {
235 FILE *fd = fopen(simCtx->grid_file, "r");
236 if (!fd) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open file: %s", simCtx->grid_file);
237
238 // Read and validate the canonical PICGRID header.
239 char firstTok[32] = {0};
240 if (fscanf(fd, "%31s", firstTok) != 1)
241 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Empty grid file: %s", simCtx->grid_file);
242 if (strcmp(firstTok, "PICGRID") != 0)
243 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
244 "Grid file %s must begin with the canonical PICGRID header.", simCtx->grid_file);
245 if (fscanf(fd, "%d", &g_nblk_from_file) != 1)
246 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Expected number of blocks after \"PICGRID\" in %s", simCtx->grid_file);
247 if (g_nblk_from_file != nblk) {
248 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED, "Mismatch: -nblk is %d but grid file specifies %d blocks.", nblk, g_nblk_from_file);
249 }
250
251 ierr = PetscMalloc3(nblk, &g_IMs_from_file, nblk, &g_JMs_from_file, nblk, &g_KMs_from_file); CHKERRQ(ierr);
252 for (PetscInt i = 0; i < nblk; ++i) {
253 if (fscanf(fd, "%d %d %d\n", &g_IMs_from_file[i], &g_JMs_from_file[i], &g_KMs_from_file[i]) != 3) {
254 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Expected 3 integers for block %d in %s", i, simCtx->grid_file);
255 }
256 }
257 fclose(fd);
258 }
259
260 // Broadcast nblk to verify (optional, good practice)
261 ierr = MPI_Bcast(&g_nblk_from_file, 1, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
262
263 // Allocate on other ranks before receiving the broadcast
264 if (rank != 0) {
265 ierr = PetscMalloc3(nblk, &g_IMs_from_file, nblk, &g_JMs_from_file, nblk, &g_KMs_from_file); CHKERRQ(ierr);
266 }
267
268 // Broadcast the data arrays
269 ierr = MPI_Bcast(g_IMs_from_file, nblk, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
270 ierr = MPI_Bcast(g_JMs_from_file, nblk, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
271 ierr = MPI_Bcast(g_KMs_from_file, nblk, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
272
273 g_file_has_been_read = PETSC_TRUE;
274 LOG_ALLOW(GLOBAL, LOG_INFO, "Grid file header read and broadcast complete.\n");
275 }
276
277 // --- Per-Block Assignment Logic (runs on every call) ---
278 user->IM = g_IMs_from_file[block_index];
279 user->JM = g_JMs_from_file[block_index];
280 user->KM = g_KMs_from_file[block_index];
281
282 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Set file inputs for Block %d: IM=%d, JM=%d, KM=%d\n",
283 simCtx->rank, block_index, user->IM, user->JM, user->KM);
284
286 PetscFunctionReturn(0);
287}
288
289
290//================================================================================
291//
292// PRIVATE HELPER FUNCTIONS
293//
294//================================================================================
295
296/**
297 * @brief Implementation of \ref FreeBC_ParamList().
298 * @details Full API contract (arguments, ownership, side effects) is documented with
299 * the header declaration in `include/io.h`.
300 * @see FreeBC_ParamList()
301 */
303 BC_Param *current = head;
304 while (current != NULL) {
305 BC_Param *next = current->next;
306 PetscFree(current->key);
307 PetscFree(current->value);
308 PetscFree(current);
309 current = next;
310 }
311}
312
313/**
314 * @brief Internal helper implementation: `StringToBCFace()`.
315 * @details Local to this translation unit.
316 */
317PetscErrorCode StringToBCFace(const char* str, BCFace* face_out) {
318 if (strcasecmp(str, "-Xi") == 0) *face_out = BC_FACE_NEG_X;
319 else if (strcasecmp(str, "+Xi") == 0) *face_out = BC_FACE_POS_X;
320 else if (strcasecmp(str, "-Eta") == 0) *face_out = BC_FACE_NEG_Y;
321 else if (strcasecmp(str, "+Eta") == 0) *face_out = BC_FACE_POS_Y;
322 else if (strcasecmp(str, "-Zeta") == 0) *face_out = BC_FACE_NEG_Z;
323 else if (strcasecmp(str, "+Zeta") == 0) *face_out = BC_FACE_POS_Z;
324 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown face specifier: %s", str);
325 return 0;
326}
327
328/**
329 * @brief Internal helper implementation: `StringToBCType()`.
330 * @details Local to this translation unit.
331 */
332PetscErrorCode StringToBCType(const char* str, BCType* type_out) {
333 if (strcasecmp(str, "WALL") == 0) *type_out = WALL;
334 else if (strcasecmp(str, "SYMMETRY") == 0) *type_out = SYMMETRY;
335 else if (strcasecmp(str, "INLET") == 0) *type_out = INLET;
336 else if (strcasecmp(str, "OUTLET") == 0) *type_out = OUTLET;
337 else if (strcasecmp(str, "PERIODIC") == 0) *type_out = PERIODIC;
338 // ... add other BCTypes here ...
339 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown BC Type string: %s", str);
340 return 0;
341}
342
343/**
344 * @brief Internal helper implementation: `StringToBCHandlerType()`.
345 * @details Local to this translation unit.
346 */
347PetscErrorCode StringToBCHandlerType(const char* str, BCHandlerType* handler_out) {
348 if (strcasecmp(str, "noslip") == 0) *handler_out = BC_HANDLER_WALL_NOSLIP;
349 else if (strcasecmp(str, "constant_velocity") == 0) *handler_out = BC_HANDLER_INLET_CONSTANT_VELOCITY;
350 else if (strcasecmp(str, "conservation") == 0) *handler_out = BC_HANDLER_OUTLET_CONSERVATION;
351 else if (strcasecmp(str, "parabolic") == 0) *handler_out = BC_HANDLER_INLET_PARABOLIC;
352 else if (strcasecmp(str, "prescribed_flow") == 0) *handler_out = BC_HANDLER_INLET_PROFILE_FROM_FILE;
353 else if (strcasecmp(str,"geometric") == 0) *handler_out = BC_HANDLER_PERIODIC_GEOMETRIC;
354 else if (strcasecmp(str,"constant_flux") == 0) *handler_out = BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX;
355 else if (strcasecmp(str,"initial_flux") == 0) *handler_out = BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX;
356 // ... add other BCHandlerTypes here ...
357 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown BC Handler string: %s", str);
358 return 0;
359}
360
361/**
362 * @brief Internal helper implementation: `ValidateBCHandlerForBCType()`.
363 * @details Local to this translation unit.
364 */
365PetscErrorCode ValidateBCHandlerForBCType(BCType type, BCHandlerType handler) {
366 switch (type) {
367 case OUTLET:
368 if(handler != BC_HANDLER_OUTLET_CONSERVATION) return PETSC_ERR_ARG_WRONG;
369 break;
370 case WALL:
371 if (handler != BC_HANDLER_WALL_NOSLIP && handler != BC_HANDLER_WALL_MOVING) return PETSC_ERR_ARG_WRONG;
372 break;
373 case INLET:
374 if (handler != BC_HANDLER_INLET_CONSTANT_VELOCITY &&
375 handler != BC_HANDLER_INLET_PARABOLIC &&
376 handler != BC_HANDLER_INLET_PROFILE_FROM_FILE) return PETSC_ERR_ARG_WRONG;
377 break;
378 case PERIODIC:
379 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;
380 // ... add other validation cases here ...
381 default: break;
382 }
383 return 0; // Combination is valid
384}
385
386/**
387 * @brief Internal helper implementation: `GetBCParamReal()`.
388 * @details Local to this translation unit.
389 */
390PetscErrorCode GetBCParamReal(BC_Param *params, const char *key, PetscReal *value_out, PetscBool *found) {
391 *found = PETSC_FALSE;
392 *value_out = 0.0;
393 if (!key) return 0; // No key to search for
394
395 BC_Param *current = params;
396 while (current) {
397 if (strcasecmp(current->key, key) == 0) {
398 *value_out = atof(current->value);
399 *found = PETSC_TRUE;
400 return 0; // Found it, we're done
401 }
402 current = current->next;
403 }
404 return 0; // It's not an error to not find the key.
405}
406
407/**
408 * @brief Internal helper implementation: `GetBCParamBool()`.
409 * @details Local to this translation unit.
410 */
411PetscErrorCode GetBCParamBool(BC_Param *params, const char *key, PetscBool *value_out, PetscBool *found) {
412 *found = PETSC_FALSE;
413 *value_out = PETSC_FALSE;
414 if (!key) return 0; // No key to search for
415
416 BC_Param *current = params;
417 while (current) {
418 if (strcasecmp(current->key, key) == 0) {
419 // Key was found.
420 *found = PETSC_TRUE;
421
422 // Check the value string. Default to FALSE if the value is NULL or doesn't match a "true" string.
423 if (current->value &&
424 (strcasecmp(current->value, "true") == 0 ||
425 strcmp(current->value, "1") == 0 ||
426 strcasecmp(current->value, "yes") == 0))
427 {
428 *value_out = PETSC_TRUE;
429 } else {
430 *value_out = PETSC_FALSE;
431 }
432 return 0; // Found it, we're done
433 }
434 current = current->next;
435 }
436 return 0; // It's not an error to not find the key.
437}
438
439//================================================================================
440//
441// PUBLIC PARSING FUNCTION
442//
443//================================================================================
444#undef __FUNCT__
445#define __FUNCT__ "ParseAllBoundaryConditions"
446/**
447 * @brief Internal helper implementation: `ParseAllBoundaryConditions()`.
448 * @details Local to this translation unit.
449 */
450PetscErrorCode ParseAllBoundaryConditions(UserCtx *user, const char *bcs_input_filename)
451{
452 PetscErrorCode ierr;
453 PetscMPIInt rank;
454
455 // Temporary storage for rank 0 to build the configuration before broadcasting.
456 BoundaryFaceConfig configs_rank0[6];
457
458 PetscFunctionBeginUser;
460 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
461
462 if (rank == 0) {
463 FILE *file;
464 char line_buffer[1024];
465
466 // Initialize the temporary config array with safe defaults on rank 0.
467 for (int i = 0; i < 6; i++) {
468 configs_rank0[i].face_id = (BCFace)i;
469 configs_rank0[i].mathematical_type = WALL;
470 configs_rank0[i].handler_type = BC_HANDLER_WALL_NOSLIP;
471 configs_rank0[i].params = NULL;
472 configs_rank0[i].handler = NULL; // Handler object is not created here.
473 }
474
475 LOG_ALLOW(GLOBAL, LOG_INFO, "Parsing BC configuration from '%s' on rank 0... \n", bcs_input_filename);
476 file = fopen(bcs_input_filename, "r");
477 if (!file) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Could not open BCs file '%s'.", bcs_input_filename);
478
479 while (fgets(line_buffer, sizeof(line_buffer), file)) {
480 char *current_pos = line_buffer;
481 while (isspace((unsigned char)*current_pos)) current_pos++; // Skip leading whitespace
482 if (*current_pos == '#' || *current_pos == '\0' || *current_pos == '\n' || *current_pos == '\r') continue;
483
484 char *face_str = strtok(current_pos, " \t\n\r");
485 char *type_str = strtok(NULL, " \t\n\r");
486 char *handler_str = strtok(NULL, " \t\n\r");
487
488 if (!face_str || !type_str || !handler_str) {
489 LOG_ALLOW(GLOBAL, LOG_WARNING, "Malformed line in bcs.dat, skipping: %s \n", line_buffer);
490 continue;
491 }
492
493 BCFace face_enum;
494 BCType type_enum;
495 BCHandlerType handler_enum;
496 const char* handler_name_for_log;
497
498 // --- Convert strings to enums and validate ---
499 ierr = StringToBCFace(face_str, &face_enum); CHKERRQ(ierr);
500 ierr = StringToBCType(type_str, &type_enum); CHKERRQ(ierr);
501 ierr = StringToBCHandlerType(handler_str, &handler_enum); CHKERRQ(ierr);
502 ierr = ValidateBCHandlerForBCType(type_enum, handler_enum);
503 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);
504
505 // Store the core types for the corresponding face
506 configs_rank0[face_enum].mathematical_type = type_enum;
507 configs_rank0[face_enum].handler_type = handler_enum;
508 handler_name_for_log = BCHandlerTypeToString(handler_enum); // Assumes this utility exists
509 LOG_ALLOW(GLOBAL, LOG_DEBUG, " Parsed Face '%s': Type=%s, Handler=%s \n", face_str, type_str, handler_name_for_log);
510
511 // --- Parse optional key=value parameters for this face ---
512 FreeBC_ParamList(configs_rank0[face_enum].params); // Clear any previous (default) params
513 configs_rank0[face_enum].params = NULL;
514 BC_Param **param_next_ptr = &configs_rank0[face_enum].params; // Pointer to the 'next' pointer to build the list
515
516 char* token;
517 while ((token = strtok(NULL, " \t\n\r")) != NULL) {
518 char* equals_ptr = strchr(token, '=');
519 if (!equals_ptr) {
520 LOG_ALLOW(GLOBAL, LOG_WARNING, "Malformed parameter '%s' on face '%s', skipping. \n", token, face_str);
521 continue;
522 }
523
524 *equals_ptr = '\0'; // Temporarily split the string at '=' to separate key and value
525 char* key_str = token;
526 char* value_str = equals_ptr + 1;
527
528 BC_Param *new_param;
529 ierr = PetscMalloc1(1, &new_param); CHKERRQ(ierr);
530 ierr = PetscStrallocpy(key_str, &new_param->key); CHKERRQ(ierr);
531 ierr = PetscStrallocpy(value_str, &new_param->value); CHKERRQ(ierr);
532 new_param->next = NULL;
533
534 *param_next_ptr = new_param;
535 param_next_ptr = &new_param->next;
536 LOG_ALLOW(GLOBAL, LOG_TRACE, " - Found param: [%s] = [%s] \n", new_param->key, new_param->value);
537 }
538 }
539 fclose(file);
540 }
541
542 // =========================================================================
543 // BROADCASTING THE CONFIGURATION FROM RANK 0
544 // =========================================================================
545 // This is a critical step to ensure all processes have the same configuration.
546
547 LOG_ALLOW_SYNC(GLOBAL, LOG_DEBUG, "Rank %d broadcasting/receiving BC configuration.\n", rank);
548
549 for (int i = 0; i < 6; i++) {
550 // --- Broadcast simple enums ---
551 if (rank == 0) {
552 user->boundary_faces[i] = configs_rank0[i]; // Rank 0 populates its final struct
553 }
554 ierr = MPI_Bcast(&user->boundary_faces[i].mathematical_type, 1, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
555 ierr = MPI_Bcast(&user->boundary_faces[i].handler_type, 1, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
556
557 // --- Serialize and Broadcast the parameter linked list ---
558 PetscInt n_params = 0;
559 if (rank == 0) { // On rank 0, count the number of parameters to send
560 for (BC_Param *p = user->boundary_faces[i].params; p; p = p->next) n_params++;
561 }
562 ierr = MPI_Bcast(&n_params, 1, MPI_INT, 0, PETSC_COMM_WORLD);CHKERRQ(ierr);
563
564 if (rank != 0) { // Non-root ranks need to receive and build the list
565 FreeBC_ParamList(user->boundary_faces[i].params); // Ensure list is empty before building
566 user->boundary_faces[i].params = NULL;
567 }
568
569 BC_Param **param_next_ptr = &user->boundary_faces[i].params;
570
571 for (int j = 0; j < n_params; j++) {
572 char key_buf[256] = {0}, val_buf[256] = {0};
573 if (rank == 0) {
574 // On rank 0, navigate to the j-th param and copy its data to buffers
575 BC_Param *p = user->boundary_faces[i].params;
576 for (int k = 0; k < j; k++) p = p->next;
577 strncpy(key_buf, p->key, 255);
578 strncpy(val_buf, p->value, 255);
579 }
580
581 ierr = MPI_Bcast(key_buf, 256, MPI_CHAR, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
582 ierr = MPI_Bcast(val_buf, 256, MPI_CHAR, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
583
584 if (rank != 0) {
585 // On non-root ranks, deserialize: create a new node and append it
586 BC_Param *new_param;
587 ierr = PetscMalloc1(1, &new_param); CHKERRQ(ierr);
588 ierr = PetscStrallocpy(key_buf, &new_param->key); CHKERRQ(ierr);
589 ierr = PetscStrallocpy(val_buf, &new_param->value); CHKERRQ(ierr);
590 new_param->next = NULL;
591 *param_next_ptr = new_param;
592 param_next_ptr = &new_param->next;
593 } else {
594 // On rank 0, just advance the pointer for the next iteration
595 param_next_ptr = &((*param_next_ptr)->next);
596 }
597 }
598 user->boundary_faces[i].face_id = (BCFace)i; // Ensure face_id is set on all ranks
599 }
600
601 // --- Set particle inlet lookup fields used by the particle system ---
602 user->inletFaceDefined = PETSC_FALSE;
603 for (int i=0; i<6; i++) {
604
605 if (user->boundary_faces[i].mathematical_type == INLET && !user->inletFaceDefined) {
606 user->inletFaceDefined = PETSC_TRUE;
607 user->identifiedInletBCFace = (BCFace)i;
608 LOG_ALLOW(GLOBAL, LOG_INFO, "Inlet face for particle initialization identified as Face %d.\n", i);
609 break; // Found the first one, stop looking
610 }
611 }
612
613
614 if (rank == 0) {
615 // Rank 0 can now free the linked lists it created for the temporary storage.
616 // As written, user->boundary_faces was populated directly on rank 0, so no extra free is needed.
617 // for(int i=0; i<6; i++) FreeBC_ParamList(configs_rank0[i].params); // This would be needed if we used configs_rank0 exclusively
618 }
619
621 PetscFunctionReturn(0);
622}
623
624//================================================================================
625//
626// PRIVATE HELPER FUNCTIONS
627//
628//================================================================================
629
630// ... (existing helper functions like FreeBC_ParamList, StringToBCFace, etc.) ...
631#undef __FUNCT__
632#define __FUNCT__ "DeterminePeriodicity"
633/**
634 * @brief Internal helper implementation: `DeterminePeriodicity()`.
635 * @details Local to this translation unit.
636 */
637PetscErrorCode DeterminePeriodicity(SimCtx *simCtx)
638{
639 PetscErrorCode ierr;
640 PetscMPIInt rank;
641 PetscInt periodic_flags[3] = {0, 0, 0}; // Index 0:I, 1:J, 2:K
642
643 PetscFunctionBeginUser;
644 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
645
646 // --- Part 1: Collectively verify all BCS files exist before proceeding ---
647 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
648 const char *bcs_filename = simCtx->bcs_files[bi];
649 if (!bcs_filename) SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_NULL, "BCS filename for block %d is not set in SimCtx.", bi);
650 char desc_buf[256];
651 PetscBool file_exists;
652 snprintf(desc_buf, sizeof(desc_buf), "BCS file for block %d", bi);
653 ierr = VerifyPathExistence(bcs_filename, PETSC_FALSE, PETSC_FALSE, desc_buf, &file_exists); CHKERRQ(ierr);
654 }
655
656 // --- Part 2: Rank 0 does the parsing, since we know all files exist ---
657 if (rank == 0) {
658 PetscBool global_is_periodic[3] = {PETSC_FALSE, PETSC_FALSE, PETSC_FALSE};
659 PetscBool is_set = PETSC_FALSE;
660
661 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
662 const char *bcs_filename = simCtx->bcs_files[bi];
663 FILE *file = fopen(bcs_filename, "r");
664
665 PetscBool face_is_periodic[6] = {PETSC_FALSE};
666 char line_buffer[1024];
667
668 while (fgets(line_buffer, sizeof(line_buffer), file)) {
669 char *current_pos = line_buffer;
670 while (isspace((unsigned char)*current_pos)) current_pos++;
671 if (*current_pos == '#' || *current_pos == '\0' || *current_pos == '\n') continue;
672
673 // --- Tokenize the line exactly like the main parser ---
674 char *face_str = strtok(current_pos, " \t\n\r");
675 char *type_str = strtok(NULL, " \t\n\r");
676
677 // If the line doesn't have at least two tokens, we can't determine the type.
678 if (!face_str || !type_str) continue;
679
680 // --- Perform a direct, non-erroring check on the mathematical type string ---
681 if (strcasecmp(type_str, "PERIODIC") == 0) {
682 BCFace face_enum;
683 // A malformed face string on a periodic line IS a fatal error.
684 ierr = StringToBCFace(face_str, &face_enum); CHKERRQ(ierr);
685 face_is_periodic[face_enum] = PETSC_TRUE;
686 }
687 // Any other type_str (e.g., "WALL", "INLET") is correctly and silently ignored.
688 }
689 fclose(file);
690
691 // --- Validate consistency within this file ---
692 if (face_is_periodic[BC_FACE_NEG_X] != face_is_periodic[BC_FACE_POS_X])
693 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);
694 if (face_is_periodic[BC_FACE_NEG_Y] != face_is_periodic[BC_FACE_POS_Y])
695 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);
696 if (face_is_periodic[BC_FACE_NEG_Z] != face_is_periodic[BC_FACE_POS_Z])
697 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);
698
699 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]};
700
701 // --- Validate consistency across block files ---
702 if (!is_set) {
703 global_is_periodic[0] = local_is_periodic[0];
704 global_is_periodic[1] = local_is_periodic[1];
705 global_is_periodic[2] = local_is_periodic[2];
706 is_set = PETSC_TRUE;
707 } else {
708 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]) {
709 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
710 "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).",
711 (int)global_is_periodic[0], (int)global_is_periodic[1], (int)global_is_periodic[2],
712 bi, bcs_filename,
713 (int)local_is_periodic[0], (int)local_is_periodic[1], (int)local_is_periodic[2]);
714 }
715 }
716 } // end loop over blocks
717
718 periodic_flags[0] = (global_is_periodic[0]) ? 1 : 0;
719 periodic_flags[1] = (global_is_periodic[1]) ? 1 : 0;
720 periodic_flags[2] = (global_is_periodic[2]) ? 1 : 0;
721
722 LOG_ALLOW(GLOBAL, LOG_INFO, "Global periodicity determined: I-periodic=%d, J-periodic=%d, K-periodic=%d\n",
723 periodic_flags[0], periodic_flags[1], periodic_flags[2]);
724 }
725
726 // --- Part 3: Broadcast the final flags from rank 0 to all other ranks ---
727 ierr = MPI_Bcast(periodic_flags, 3, MPIU_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
728
729 // --- All ranks now update their SimCtx ---
730 simCtx->i_periodic = periodic_flags[0];
731 simCtx->j_periodic = periodic_flags[1];
732 simCtx->k_periodic = periodic_flags[2];
733
734 PetscFunctionReturn(0);
735}
736
737/**
738 * @brief Internal helper implementation: `VerifyPathExistence()`.
739 * @details Local to this translation unit.
740 */
741PetscErrorCode VerifyPathExistence(const char *path, PetscBool is_dir, PetscBool is_optional, const char *description, PetscBool *exists)
742{
743 PetscErrorCode ierr;
744 PetscMPIInt rank;
745 MPI_Comm comm = PETSC_COMM_WORLD;
746
747 PetscFunctionBeginUser;
748 ierr = MPI_Comm_rank(comm, &rank); CHKERRQ(ierr);
749
750 if (rank == 0) {
751 if (is_dir) {
752 ierr = PetscTestDirectory(path, 'r', exists); CHKERRQ(ierr);
753 } else {
754 ierr = PetscTestFile(path, 'r', exists); CHKERRQ(ierr);
755 }
756
757 if (!(*exists)) {
758 if (is_optional) {
759 LOG_ALLOW(GLOBAL, LOG_WARNING, "Optional %s not found at: %s (using defaults/ignoring).\n", description, path);
760 } else {
761 LOG_ALLOW(GLOBAL, LOG_ERROR, "Mandatory %s not found at: %s\n", description, path);
762 }
763 } else {
764 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Found %s: %s\n", description, path);
765 }
766 }
767
768 // Broadcast the result from Rank 0
769 PetscMPIInt exists_int = (rank == 0) ? (PetscMPIInt)(*exists) : 0;
770 ierr = MPI_Bcast(&exists_int, 1, MPI_INT, 0, comm); CHKERRMPI(ierr);
771 *exists = (PetscBool)exists_int;
772
773 // Collective error for mandatory files
774 if (!(*exists) && !is_optional) {
775 SETERRQ(comm, PETSC_ERR_FILE_OPEN, "Mandatory %s not found. Rank 0 expected it at '%s'. Check path and permissions.", description, path);
776 }
777
778 PetscFunctionReturn(0);
779}
780
781/**
782 * @brief Internal helper implementation: `CheckDataFile()`.
783 * @details Local to this translation unit.
784 */
785static PetscErrorCode CheckDataFile(UserCtx *user, PetscInt ti, const char *fieldName, const char *ext, PetscBool *fileExists)
786{
787 PetscErrorCode ierr;
788 PetscMPIInt rank;
789 MPI_Comm comm = PETSC_COMM_WORLD;
790 PetscInt placeholder_int = 0;
791 SimCtx *simCtx = user->simCtx;
792
793 PetscFunctionBeginUser;
794
795 if(!simCtx->current_io_directory) {
796 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_NULL, "I/O context directory is NULL. Ensure it is set before calling CheckDataFile().");
797 }
798
799 ierr = MPI_Comm_rank(comm, &rank); CHKERRQ(ierr);
800
801 if (rank == 0) {
802 char filename[PETSC_MAX_PATH_LEN];
803 // Use the same standardized, rank-independent filename format
804
805 ierr = PetscSNPrintf(filename, sizeof(filename), "%s/%s%05"PetscInt_FMT"_%d.%s",simCtx->current_io_directory,fieldName, ti, placeholder_int, ext);
806 ierr = PetscTestFile(filename, 'r', fileExists); CHKERRQ(ierr);
807 if (!(*fileExists)) {
808 LOG_ALLOW(GLOBAL, LOG_WARNING, "(Rank 0) - Optional data file '%s' not found.\n", filename);
809 }
810 }
811
812 // Broadcast the result from Rank 0 to all other ranks.
813 // We cast the PetscBool to a PetscMPIInt for MPI_Bcast.
814 PetscMPIInt fileExists_int = (rank == 0) ? (PetscMPIInt)(*fileExists) : 0;
815 ierr = MPI_Bcast(&fileExists_int, 1, MPI_INT, 0, comm); CHKERRMPI(ierr);
816 *fileExists = (PetscBool)fileExists_int;
817
818 PetscFunctionReturn(0);
819}
820
821/**
822 * @brief Internal helper implementation: `ReadOptionalField()`.
823 * @details Local to this translation unit.
824 */
825static PetscErrorCode ReadOptionalField(UserCtx *user, const char *field_name, const char *field_label, Vec field_vec, PetscInt ti, const char *ext)
826{
827 PetscErrorCode ierr;
828 PetscBool fileExists;
829
830 PetscFunctionBeginUser;
831
832 /* Check if the data file for this optional field exists. */
833 ierr = CheckDataFile(user,ti, field_name, ext, &fileExists); CHKERRQ(ierr);
834
835 if (fileExists) {
836 /* File exists, so we MUST be able to read it. */
837 LOG_ALLOW(GLOBAL, LOG_DEBUG, "File for %s found, attempting to read...\n", field_label);
838 ierr = ReadFieldData(user, field_name, field_vec, ti, ext);
839
840 if (ierr) {
841 /* Any error here is fatal. A PETSC_ERR_FILE_OPEN would mean a race condition or
842 permissions issue. Other errors could be size mismatch or corruption. */
843 SETERRQ(PETSC_COMM_WORLD, ierr, "Failed to read data for %s from existing file for step %d. The file may be corrupt or have an incorrect size.", field_label, ti);
844 } else {
845 LOG_ALLOW(GLOBAL, LOG_INFO, "Successfully read %s field for step %d.\n", field_label, ti);
846 }
847 } else {
848 /* File does not exist, which is acceptable for an optional field. */
849 LOG_ALLOW(GLOBAL, LOG_WARNING, "Optional %s file for step %d not found. Skipping.\n", field_label, ti);
850 }
851
852 PetscFunctionReturn(0);
853}
854
855/**
856 * @brief Internal helper implementation: `ReadOptionalSwarmField()`.
857 * @details Local to this translation unit.
858 */
859static PetscErrorCode ReadOptionalSwarmField(UserCtx *user, const char *field_name, const char *field_label, PetscInt ti, const char *ext)
860{
861 PetscErrorCode ierr;
862 PetscBool fileExists;
863
864 PetscFunctionBeginUser;
865
866 /* Check if the data file for this optional field exists. */
867 ierr = CheckDataFile(user,ti, field_name, ext, &fileExists); CHKERRQ(ierr);
868
869 if (fileExists) {
870 /* File exists, so we MUST be able to read it. */
871 LOG_ALLOW(GLOBAL, LOG_DEBUG, "File for %s found, attempting to read...\n", field_label);
872 if(strcasecmp(field_name,"DMSwarm_CellID") == 0 || strcasecmp(field_name,"DMSwarm_pid")== 0 || strcasecmp(field_name,"DMSwarm_location_status")== 0 ) {
873 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Reading integer swarm field '%s'.\n", field_name);
874 ierr = ReadSwarmIntField(user,field_name,ti,ext);
875 }
876 else{
877 ierr = ReadSwarmField(user, field_name, ti, ext);
878 }
879
880 if (ierr) {
881 /* Any error here is fatal. A PETSC_ERR_FILE_OPEN would mean a race condition or
882 permissions issue. Other errors could be size mismatch or corruption. */
883 SETERRQ(PETSC_COMM_WORLD, ierr, "Failed to read data for %s from existing file for step %d. The file may be corrupt or have an incorrect size.", field_label, ti);
884 } else {
885 LOG_ALLOW(GLOBAL, LOG_INFO, "Successfully read %s field for step %d.\n", field_label, ti);
886 }
887 } else {
888 /* File does not exist, which is acceptable for an optional field. */
889 LOG_ALLOW(GLOBAL, LOG_WARNING, "Optional %s file for step %d not found. Skipping.\n", field_label, ti);
890 }
891
892 PetscFunctionReturn(0);
893}
894
895#undef __FUNCT__
896#define __FUNCT__ "ReadFieldData"
897/**
898 * @brief Internal helper implementation: `ReadFieldData()`.
899 * @details Local to this translation unit.
900 */
901PetscErrorCode ReadFieldData(UserCtx *user,
902 const char *field_name,
903 Vec field_vec,
904 PetscInt ti,
905 const char *ext)
906{
907 PetscErrorCode ierr;
908 char filename[PETSC_MAX_PATH_LEN];
909 MPI_Comm comm;
910 PetscMPIInt rank,size;
911 SimCtx *simCtx = user->simCtx;
912
913
914 PetscFunctionBeginUser;
916
917 if(!simCtx->current_io_directory){
918 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE, "I/O context directory was not set before calling ReadFieldData().");
919 }
920
921
922 ierr = PetscObjectGetComm((PetscObject)field_vec,&comm);CHKERRQ(ierr);
923 ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
924 ierr = MPI_Comm_size(comm,&size);CHKERRQ(ierr);
925
926 const char *source_path = NULL;
927 source_path = simCtx->current_io_directory;
928
929 if(!source_path){
930 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE, "source_path was not set for the current execution mode.");
931 }
932 /* ---------------------------------------------------------------------
933 * Compose <path-to-file>/<field_name><step with 5 digits>_0.<ext>
934 * (all restart files are written by rank-0 with that naming scheme).
935 * ------------------------------------------------------------------ */
936 ierr = PetscSNPrintf(filename,sizeof(filename),
937 "%s/%s%05" PetscInt_FMT "_0.%s",
938 source_path,field_name,ti,ext);CHKERRQ(ierr);
939
941 "Attempting to read <%s> on rank %d/%d\n",
942 filename,(int)rank,(int)size);
943
944 /* ======================================================================
945 * 1. SERIAL JOB – just hand the Vec to VecLoad()
946 * ==================================================================== */
947 if(size==1)
948 {
949 PetscViewer viewer;
950 PetscBool found;
951 Vec temp_vec;
952 PetscInt expectedSize,loadedSize;
953
954 ierr = PetscTestFile(filename,'r',&found);CHKERRQ(ierr);
955 if(!found) SETERRQ(comm,PETSC_ERR_FILE_OPEN,
956 "Restart/Source file not found: %s",filename);
957
958 ierr = PetscViewerBinaryOpen(PETSC_COMM_SELF,filename,FILE_MODE_READ,&viewer);CHKERRQ(ierr);
959// ---- START MODIFICATION ----
960 // DO NOT load directly into field_vec, as this can resize it, which is
961 // illegal for DMSwarm "view" vectors. Instead, load into a temporary vector.
962 ierr = VecCreate(PETSC_COMM_SELF, &temp_vec); CHKERRQ(ierr);
963 ierr = VecLoad(temp_vec,viewer);CHKERRQ(ierr);
964 ierr = PetscViewerDestroy(&viewer);CHKERRQ(ierr);
965
966 // Sanity check: ensure the file size matches the expected vector size.
967 ierr = VecGetSize(field_vec, &expectedSize);CHKERRQ(ierr);
968 ierr = VecGetSize(temp_vec, &loadedSize);CHKERRQ(ierr);
969 if (loadedSize != expectedSize) {
970 SETERRQ(comm,PETSC_ERR_FILE_UNEXPECTED,
971 "File %s holds %d entries – expected %d for field '%s'",
972 filename, loadedSize, expectedSize, field_name);
973 }
974
975 // Now, safely copy the data from the temporary vector to the final destination.
976 ierr = VecCopy(temp_vec, field_vec);CHKERRQ(ierr);
977
978 // Clean up the temporary vector.
979 ierr = VecDestroy(&temp_vec);CHKERRQ(ierr);
980
981 // ---- END MODIFICATION ----
982
983 /* create EMPTY sequential Vec – VecLoad() will size it correctly */
984 /*
985 ierr = VecCreate(PETSC_COMM_SELF,&seq_vec);CHKERRQ(ierr);
986 ierr = VecSetType(seq_vec,VECSEQ);CHKERRQ(ierr);
987
988 ierr = PetscViewerBinaryOpen(PETSC_COMM_SELF,filename,
989 FILE_MODE_READ,&viewer);CHKERRQ(ierr);
990
991 ierr = VecLoad(field_vec,viewer);CHKERRQ(ierr);
992 ierr = PetscViewerDestroy(&viewer);CHKERRQ(ierr);
993 */
995 "Loaded <%s> (serial path)\n",filename);
996
998 PetscFunctionReturn(0);
999 }
1000
1001 /* ======================================================================
1002 * 2. PARALLEL JOB
1003 * ==================================================================== */
1004 PetscInt globalSize;
1005 ierr = VecGetSize(field_vec,&globalSize);CHKERRQ(ierr);
1006
1007 DM dm = NULL;
1008 const char *dmtype = NULL;
1009 Vec nat = NULL; /* Natural-ordered vector for DMDA */
1010
1011 /* -------------------- rank-0 : read the sequential file -------------- */
1012 Vec seq_vec = NULL; /* only valid on rank-0 */
1013 const PetscScalar *seqArray = NULL; /* borrowed pointer on rank-0 only */
1014
1015 if(rank==0)
1016 {
1017 PetscViewer viewer;
1018 PetscBool found;
1019
1020 ierr = PetscTestFile(filename,'r',&found);CHKERRQ(ierr);
1021 if(!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,
1022 "Restart file not found: %s",filename);
1023
1024 /* create EMPTY sequential Vec – VecLoad() will size it correctly */
1025 ierr = VecCreate(PETSC_COMM_SELF,&seq_vec);CHKERRQ(ierr);
1026 ierr = VecSetType(seq_vec,VECSEQ);CHKERRQ(ierr);
1027
1028 ierr = PetscViewerBinaryOpen(PETSC_COMM_SELF,filename,
1029 FILE_MODE_READ,&viewer);CHKERRQ(ierr);
1030 ierr = VecLoad(seq_vec,viewer);CHKERRQ(ierr);
1031 ierr = PetscViewerDestroy(&viewer);CHKERRQ(ierr);
1032
1033 /* size sanity-check */
1034 PetscInt loaded;
1035 ierr = VecGetSize(seq_vec,&loaded);CHKERRQ(ierr);
1036 if(loaded != globalSize)
1037 SETERRQ(comm,PETSC_ERR_FILE_UNEXPECTED,
1038 "File %s holds %d entries – expected %d",
1039 filename,loaded,globalSize);
1040
1041 /* borrow array for later Bcast */
1042 ierr = VecGetArrayRead(seq_vec,&seqArray);CHKERRQ(ierr);
1043
1045 "Rank 0 successfully loaded <%s>\n",filename);
1046 }
1047
1048 /* -------------------- Check if this is a DMDA vector ----------------- */
1049 ierr = VecGetDM(field_vec, &dm); CHKERRQ(ierr);
1050 if (dm) { ierr = DMGetType(dm, &dmtype); CHKERRQ(ierr); }
1051
1052 if (dmtype && !strcmp(dmtype, DMDA)) {
1053 /* ==================================================================
1054 * DMDA PATH: File is in natural ordering, need to convert to global
1055 * ================================================================== */
1056
1057 /* Create natural vector */
1058 ierr = DMDACreateNaturalVector(dm, &nat); CHKERRQ(ierr);
1059
1060 /* Scatter from rank 0's seq_vec to all ranks' natural vector */
1061 VecScatter scatter;
1062 Vec nat_seq = NULL; /* Sequential natural vector on rank 0 */
1063
1064 ierr = VecScatterCreateToZero(nat, &scatter, &nat_seq); CHKERRQ(ierr);
1065
1066 /* Reverse scatter: from rank 0 to all ranks */
1067 ierr = VecScatterBegin(scatter, (rank == 0 ? seq_vec : nat_seq), nat,
1068 INSERT_VALUES, SCATTER_REVERSE); CHKERRQ(ierr);
1069 ierr = VecScatterEnd(scatter, (rank == 0 ? seq_vec : nat_seq), nat,
1070 INSERT_VALUES, SCATTER_REVERSE); CHKERRQ(ierr);
1071
1072 /* Convert natural → global ordering */
1073 ierr = DMDANaturalToGlobalBegin(dm, nat, INSERT_VALUES, field_vec); CHKERRQ(ierr);
1074 ierr = DMDANaturalToGlobalEnd(dm, nat, INSERT_VALUES, field_vec); CHKERRQ(ierr);
1075
1076 /* Cleanup */
1077 ierr = VecScatterDestroy(&scatter); CHKERRQ(ierr);
1078 ierr = VecDestroy(&nat_seq); CHKERRQ(ierr);
1079 ierr = VecDestroy(&nat); CHKERRQ(ierr);
1080
1081 } else {
1082 /* ==================================================================
1083 * NON-DMDA PATH: Use broadcast and direct copy (assumes global ordering)
1084 * ================================================================== */
1085
1086 PetscScalar *buffer = NULL;
1087 if (rank == 0) {
1088 buffer = (PetscScalar *)seqArray;
1089 } else {
1090 ierr = PetscMalloc1(globalSize, &buffer); CHKERRQ(ierr);
1091 }
1092
1093 ierr = MPI_Bcast(buffer, (int)globalSize, MPIU_SCALAR, 0, comm); CHKERRQ(ierr);
1094
1095 /* Copy slice based on ownership range */
1096 PetscInt rstart, rend, loc;
1097 PetscScalar *locArray;
1098
1099 ierr = VecGetOwnershipRange(field_vec, &rstart, &rend); CHKERRQ(ierr);
1100 loc = rend - rstart;
1101
1102 ierr = VecGetArray(field_vec, &locArray); CHKERRQ(ierr);
1103 ierr = PetscMemcpy(locArray, buffer + rstart, loc * sizeof(PetscScalar)); CHKERRQ(ierr);
1104 ierr = VecRestoreArray(field_vec, &locArray); CHKERRQ(ierr);
1105
1106 if (rank != 0) {
1107 ierr = PetscFree(buffer); CHKERRQ(ierr);
1108 }
1109 }
1110
1111 /* -------------------- tidy up ---------------------------------------- */
1112 if (rank == 0) {
1113 ierr = VecRestoreArrayRead(seq_vec, &seqArray); CHKERRQ(ierr);
1114 ierr = VecDestroy(&seq_vec); CHKERRQ(ierr);
1115 }
1116
1118 "Loaded <%s> (parallel path)\n",filename);
1119
1121 PetscFunctionReturn(0);
1122}
1123
1124
1125/**
1126 * @brief Internal helper implementation: `ReadSimulationFields()`.
1127 * @details Local to this translation unit.
1128 */
1129PetscErrorCode ReadSimulationFields(UserCtx *user,PetscInt ti)
1130{
1131 PetscErrorCode ierr;
1132
1133 SimCtx *simCtx = user->simCtx;
1134 const char *source_path = NULL;
1135 const char *eulerian_ext = "dat";
1136
1137 if(simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR){
1138 source_path = simCtx->pps->source_dir;
1139 if (simCtx->pps->eulerianExt[0] != '\0') {
1140 eulerian_ext = simCtx->pps->eulerianExt;
1141 }
1142 } else if(simCtx->exec_mode == EXEC_MODE_SOLVER){
1143 source_path = simCtx->restart_dir;
1144 } else{
1145 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Invalid execution mode for reading simulation fields.");
1146 }
1147
1148 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting to read simulation fields.\n");
1149
1150 // Set the current I/O directory context
1151 ierr = PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer), "%s/%s",source_path,simCtx->euler_subdir); CHKERRQ(ierr);
1152 simCtx->current_io_directory = simCtx->_io_context_buffer;
1153
1154 // Read Cartesian velocity field
1155 ierr = ReadFieldData(user, "ufield", user->Ucat, ti, eulerian_ext); CHKERRQ(ierr);
1156
1157 // Read contravariant velocity field
1158 ierr = ReadFieldData(user, "vfield", user->Ucont, ti, eulerian_ext); CHKERRQ(ierr);
1159
1160 // Read pressure field
1161 ierr = ReadFieldData(user, "pfield", user->P, ti, eulerian_ext); CHKERRQ(ierr);
1162
1163 // Read node state field (nvert)
1164 ierr = ReadFieldData(user, "nvfield", user->Nvert, ti, eulerian_ext); CHKERRQ(ierr);
1165
1166 LOG_ALLOW(GLOBAL,LOG_INFO,"Successfully read all mandatory fields. \n");
1167
1168 if(simCtx->np>0){
1169 // Read Particle Count field
1170 if(!user->ParticleCount){
1171 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "ParticleCount Vec is NULL but np>0");
1172 }
1173 ierr = ReadOptionalField(user, "ParticleCount", "Particle Count", user->ParticleCount, ti, eulerian_ext); CHKERRQ(ierr);
1174
1175 ierr = ReadOptionalField(user, "psifield", "Scalar Psi Field", user->Psi, ti, eulerian_ext); CHKERRQ(ierr);
1176 }
1177 else{
1178 LOG_ALLOW(GLOBAL, LOG_INFO, "No particles in simulation, skipping Particle fields reading.\n");
1179 }
1180 // Process LES fields if enabled
1181 if (simCtx->les) {
1182 ierr = ReadLESFields(user,ti); CHKERRQ(ierr);
1183 }
1184
1185 // Process RANS fields if enabled
1186 if (simCtx->rans) {
1187 ierr = ReadRANSFields(user,ti); CHKERRQ(ierr);
1188 }
1189
1190 // Process statistical fields if averaging is enabled
1191 if (simCtx->averaging) {
1192 ierr = ReadStatisticalFields(user,ti); CHKERRQ(ierr);
1193 }
1194
1195 simCtx->current_io_directory = NULL; // Clear the I/O context after reading
1196
1197 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished reading simulation fields.\n");
1198
1199 return 0;
1200}
1201
1202/**
1203 * @brief Implementation of \ref ReadStatisticalFields().
1204 * @details Full API contract (arguments, ownership, side effects) is documented with
1205 * the header declaration in `include/io.h`.
1206 * @see ReadStatisticalFields()
1207 */
1208PetscErrorCode ReadStatisticalFields(UserCtx *user,PetscInt ti)
1209{
1210 PetscErrorCode ierr;
1211 const char *eulerian_ext = "dat";
1212 if (user->simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR && user->simCtx->pps->eulerianExt[0] != '\0') {
1213 eulerian_ext = user->simCtx->pps->eulerianExt;
1214 }
1215
1216 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting to read statistical fields.\n");
1217
1218 ierr = ReadOptionalField(user, "su0", "Velocity Sum", user->Ucat_sum, ti, eulerian_ext); CHKERRQ(ierr);
1219 ierr = ReadOptionalField(user, "su1", "Velocity Cross Sum", user->Ucat_cross_sum, ti, eulerian_ext); CHKERRQ(ierr);
1220 ierr = ReadOptionalField(user, "su2", "Velocity Square Sum",user->Ucat_square_sum, ti, eulerian_ext); CHKERRQ(ierr);
1221 ierr = ReadOptionalField(user, "sp", "Pressure Sum", user->P_sum, ti, eulerian_ext); CHKERRQ(ierr);
1222
1223 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished reading statistical fields.\n");
1224
1225 return 0;
1226}
1227
1228/**
1229 * @brief Internal helper implementation: `ReadLESFields()`.
1230 * @details Local to this translation unit.
1231 */
1232PetscErrorCode ReadLESFields(UserCtx *user,PetscInt ti)
1233{
1234 PetscErrorCode ierr;
1235 const char *eulerian_ext = "dat";
1236 if (user->simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR && user->simCtx->pps->eulerianExt[0] != '\0') {
1237 eulerian_ext = user->simCtx->pps->eulerianExt;
1238 }
1239
1240 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting to read LES fields.\n");
1241
1242 ierr = ReadOptionalField(user, "Nu_t", "Turbulent Viscosity", user->Nu_t, ti, eulerian_ext); CHKERRQ(ierr);
1243 ierr = ReadOptionalField(user, "cs", "Smagorinsky Constant (Cs)", user->CS, ti, eulerian_ext); CHKERRQ(ierr);
1244
1245 DMGlobalToLocalBegin(user->da, user->CS, INSERT_VALUES, user->lCs);
1246 DMGlobalToLocalEnd(user->da, user->CS, INSERT_VALUES, user->lCs);
1247
1248 DMGlobalToLocalBegin(user->da, user->Nu_t, INSERT_VALUES, user->lNu_t);
1249 DMGlobalToLocalEnd(user->da, user->Nu_t, INSERT_VALUES, user->lNu_t);
1250
1251 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished reading LES fields.\n");
1252
1253 return 0;
1254}
1255
1256/**
1257 * @brief Internal helper implementation: `ReadRANSFields()`.
1258 * @details Local to this translation unit.
1259 */
1260PetscErrorCode ReadRANSFields(UserCtx *user,PetscInt ti)
1261{
1262 PetscErrorCode ierr;
1263 const char *eulerian_ext = "dat";
1264 if (user->simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR && user->simCtx->pps->eulerianExt[0] != '\0') {
1265 eulerian_ext = user->simCtx->pps->eulerianExt;
1266 }
1267
1268 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting to read RANS fields.\n");
1269
1270 ierr = ReadOptionalField(user, "kfield", "K-Omega RANS", user->K_Omega, ti, eulerian_ext); CHKERRQ(ierr);
1271 ierr = ReadOptionalField(user, "Nu_t", "Turbulent Viscosity", user->Nu_t, ti, eulerian_ext); CHKERRQ(ierr);
1272
1273 VecCopy(user->K_Omega, user->K_Omega_o);
1274
1275 DMGlobalToLocalBegin(user->fda2, user->K_Omega, INSERT_VALUES, user->lK_Omega);
1276 DMGlobalToLocalEnd(user->fda2, user->K_Omega, INSERT_VALUES, user->lK_Omega);
1277
1278 DMGlobalToLocalBegin(user->da, user->Nu_t, INSERT_VALUES, user->lNu_t);
1279 DMGlobalToLocalEnd(user->da, user->Nu_t, INSERT_VALUES, user->lNu_t);
1280
1281 DMGlobalToLocalBegin(user->fda2, user->K_Omega_o, INSERT_VALUES, user->lK_Omega_o);
1282 DMGlobalToLocalEnd(user->fda2, user->K_Omega_o, INSERT_VALUES, user->lK_Omega_o);
1283
1284 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished reading RANS fields.\n");
1285
1286 return 0;
1287}
1288
1289
1290/**
1291 * @brief Internal helper implementation: `ReadSwarmField()`.
1292 * @details Local to this translation unit.
1293 */
1294PetscErrorCode ReadSwarmField(UserCtx *user, const char *field_name, PetscInt ti, const char *ext)
1295{
1296 PetscErrorCode ierr;
1297 DM swarm;
1298 Vec fieldVec;
1299
1300 PetscFunctionBegin;
1301
1302 swarm = user->swarm;
1303
1304 LOG_ALLOW(GLOBAL,LOG_DEBUG," ReadSwarmField Begins \n");
1305
1306 /* 2) Create a global vector that references the specified Swarm field. */
1307 ierr = DMSwarmCreateGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
1308
1309 LOG_ALLOW(GLOBAL,LOG_DEBUG," Vector created from Field \n");
1310
1311 /* 3) Use the ReadFieldData() function to read data into fieldVec. */
1312 ierr = ReadFieldData(user, field_name, fieldVec, ti, ext);CHKERRQ(ierr);
1313
1314 /* 4) Destroy the global vector reference. */
1315 ierr = DMSwarmDestroyGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
1316
1317 PetscFunctionReturn(0);
1318}
1319
1320/**
1321 * @brief Internal helper implementation: `ReadSwarmIntField()`.
1322 * @details Local to this translation unit.
1323 */
1324PetscErrorCode ReadSwarmIntField(UserCtx *user, const char *field_name, PetscInt ti, const char *ext)
1325{
1326 PetscErrorCode ierr;
1327 DM swarm = user->swarm;
1328 Vec temp_vec;
1329 PetscInt nlocal, nglobal, bs, i;
1330 const PetscScalar *scalar_array; // Read-only pointer from the temp Vec
1331 void *field_array_void;
1332
1333
1334 PetscFunctionBeginUser;
1335
1336 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Reading '%s' via temporary Vec.\n", field_name);
1337
1338 // Get the properties of the swarm field to determine the expected layout
1339 ierr = DMSwarmGetLocalSize(swarm, &nlocal); CHKERRQ(ierr);
1340 ierr = DMSwarmGetSize(swarm, &nglobal); CHKERRQ(ierr);
1341 // We get the block size but not the data pointer yet
1342 ierr = DMSwarmGetField(swarm, field_name, &bs, NULL, NULL); CHKERRQ(ierr);
1343 ierr = DMSwarmRestoreField(swarm, field_name, &bs, NULL, NULL); CHKERRQ(ierr);
1344
1345 // Create a temporary Vec with the CORRECT layout to receive the data
1346 ierr = VecCreate(PETSC_COMM_WORLD, &temp_vec); CHKERRQ(ierr);
1347 ierr = VecSetType(temp_vec, VECMPI); CHKERRQ(ierr);
1348 ierr = VecSetSizes(temp_vec, nlocal * bs, nglobal * bs); CHKERRQ(ierr);
1349 ierr = VecSetBlockSize(temp_vec, bs); CHKERRQ(ierr);
1350 ierr = VecSetUp(temp_vec); CHKERRQ(ierr);
1351
1352 // Call your existing reader to populate the temporary Vec
1353 ierr = ReadFieldData(user, field_name, temp_vec, ti, ext); CHKERRQ(ierr);
1354
1355 // Get local pointers
1356 ierr = VecGetArrayRead(temp_vec, &scalar_array); CHKERRQ(ierr);
1357 ierr = DMSwarmGetField(swarm, field_name, NULL, NULL, &field_array_void); CHKERRQ(ierr);
1358
1359 // Perform the cast back, using the correct loop size (nlocal * bs)
1360 if (strcmp(field_name, "DMSwarm_pid") == 0) {
1361 PetscInt64 *int64_array = (PetscInt64 *)field_array_void;
1362 for (i = 0; i < nlocal * bs; i++) {
1363 int64_array[i] = (PetscInt64)scalar_array[i];
1364 }
1365 } else {
1366 PetscInt *int_array = (PetscInt *)field_array_void;
1367 for (i = 0; i < nlocal * bs; i++) {
1368 int_array[i] = (PetscInt)scalar_array[i];
1369 }
1370 }
1371
1372 // Restore access
1373 ierr = DMSwarmRestoreField(swarm, field_name, NULL, NULL, &field_array_void); CHKERRQ(ierr);
1374 ierr = VecRestoreArrayRead(temp_vec, &scalar_array); CHKERRQ(ierr);
1375
1376 // 6. Clean up
1377 ierr = VecDestroy(&temp_vec); CHKERRQ(ierr);
1378
1379 PetscFunctionReturn(0);
1380}
1381
1382/**
1383 * @brief Internal helper implementation: `ReadAllSwarmFields()`.
1384 * @details Local to this translation unit.
1385 */
1386PetscErrorCode ReadAllSwarmFields(UserCtx *user, PetscInt ti)
1387{
1388 PetscErrorCode ierr;
1389 PetscInt nGlobal;
1390 SimCtx *simCtx = user->simCtx;
1391 const char *source_path = NULL;
1392 const char *particle_ext = "dat";
1393
1394 PetscFunctionBeginUser;
1395 ierr = DMSwarmGetSize(user->swarm, &nGlobal); CHKERRQ(ierr);
1396 LOG_ALLOW(GLOBAL, LOG_INFO, "Reading DMSwarm fields for timestep %d (swarm size is %d).\n", ti, nGlobal);
1397
1398 if (nGlobal == 0) {
1399 LOG_ALLOW(GLOBAL, LOG_INFO, "Swarm is empty for timestep %d. Nothing to read.\n", ti);
1400 PetscFunctionReturn(0);
1401 }
1402
1403 // First, determine the top-level source directory based on the execution mode.
1404 if (simCtx->exec_mode == EXEC_MODE_SOLVER) {
1405 source_path = simCtx->restart_dir;
1406 } else if (simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR) {
1407 source_path = simCtx->pps->source_dir;
1408 if (simCtx->pps->particleExt[0] != '\0') {
1409 particle_ext = simCtx->pps->particleExt;
1410 }
1411 } else {
1412 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "Invalid execution mode for reading simulation fields.");
1413 }
1414
1415 // Set the current I/O directory context
1416 ierr = PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer),
1417 "%s/%s", source_path, simCtx->particle_subdir); CHKERRQ(ierr);
1418
1419 simCtx->current_io_directory = simCtx->_io_context_buffer;
1420
1421 /* 1) Read positions (REQUIRED) */
1422 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Reading mandatory position field...\n");
1423 ierr = ReadSwarmField(user, "position", ti, particle_ext);
1424 if (ierr) {
1425 SETERRQ(PETSC_COMM_WORLD, ierr, "Failed to read MANDATORY 'position' field for step %d. Cannot continue.", ti);
1426 }
1427 LOG_ALLOW(GLOBAL, LOG_INFO, "Successfully read mandatory position field for step %d.\n", ti);
1428
1429 /* 2) Read all OPTIONAL fields using the helper function. */
1430 /* The helper will print a warning and continue if a file is not found. */
1431 ierr = ReadOptionalSwarmField(user, "velocity", "Velocity", ti, particle_ext); CHKERRQ(ierr);
1432 ierr = ReadOptionalSwarmField(user, "DMSwarm_pid", "Particle ID", ti, particle_ext); CHKERRQ(ierr);
1433 ierr = ReadOptionalSwarmField(user, "DMSwarm_CellID", "Cell ID", ti, particle_ext); CHKERRQ(ierr);
1434 ierr = ReadOptionalSwarmField(user, "weight", "Particle Weight", ti, particle_ext); CHKERRQ(ierr);
1435 ierr = ReadOptionalSwarmField(user, "Psi", "Scalar Psi", ti, particle_ext); CHKERRQ(ierr);
1436 ierr = ReadOptionalSwarmField(user, "DMSwarm_location_status", "Migration Status", ti, particle_ext); CHKERRQ(ierr);
1437
1438 simCtx->current_io_directory = NULL; // Clear the I/O context after reading
1439
1440 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished reading DMSwarm fields for timestep %d.\n", ti);
1441 PetscFunctionReturn(0);
1442}
1443
1444
1445#undef __FUNCT__
1446#define __FUNCT__ "WriteFieldData"
1447/**
1448 * @brief Internal helper implementation: `WriteFieldData()`.
1449 * @details Local to this translation unit.
1450 */
1451PetscErrorCode WriteFieldData(UserCtx *user,
1452 const char *field_name,
1453 Vec field_vec,
1454 PetscInt ti,
1455 const char *ext)
1456{
1457 PetscErrorCode ierr;
1458 MPI_Comm comm;
1459 PetscMPIInt rank, size;
1460
1461 const PetscInt placeholder_int = 0; /* keep legacy name */
1462 char filename[PETSC_MAX_PATH_LEN];
1463 SimCtx *simCtx=user->simCtx;
1464
1465 PetscFunctionBeginUser;
1467
1468 if(!simCtx->current_io_directory){
1469 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE, "I/O context directory was not set before calling WriteFieldData().");
1470 }
1471
1472 /* ------------------------------------------------------------ */
1473 /* Basic communicator information */
1474 /* ------------------------------------------------------------ */
1475 ierr = PetscObjectGetComm((PetscObject)field_vec,&comm);CHKERRQ(ierr);
1476 ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
1477 ierr = MPI_Comm_size(comm,&size);CHKERRQ(ierr);
1478
1479 ierr = PetscSNPrintf(filename,sizeof(filename),
1480 "%s/%s%05" PetscInt_FMT "_%d.%s",
1481 simCtx->current_io_directory,field_name,ti,placeholder_int,ext);CHKERRQ(ierr);
1482
1484 " Preparing to write <%s> on rank %d/%d\n",
1485 filename,rank,size);
1486
1487 /* ------------------------------------------------------------ */
1488 /* 1. Serial path */
1489 /* ------------------------------------------------------------ */
1490 if (size == 1) {
1491 PetscViewer viewer;
1492
1493 ierr = PetscViewerBinaryOpen(comm,filename,
1494 FILE_MODE_WRITE,&viewer);CHKERRQ(ierr);
1495 ierr = VecView(field_vec,viewer);CHKERRQ(ierr);
1496 ierr = PetscViewerDestroy(&viewer);CHKERRQ(ierr);
1497
1499 " Wrote <%s> (serial path)\n",filename);
1500 PetscFunctionReturn(0);
1501 }
1502
1503 /* ------------------------------------------------------------ */
1504 /* 2. Parallel path */
1505 /* ------------------------------------------------------------ */
1506 VecScatter scatter;
1507 Vec seq_vec=NULL; /* created by PETSc, lives only on rank 0 */
1508 DM dm = NULL;
1509 const char *dmtype = NULL;
1510 Vec nat = NULL; /* Natural-ordered vector for DMDA */
1511
1512 /* 2.1 Check if vector has DMDA and convert to natural ordering if needed */
1513 ierr = VecGetDM(field_vec, &dm); CHKERRQ(ierr);
1514 if (dm) { ierr = DMGetType(dm, &dmtype); CHKERRQ(ierr); }
1515
1516 if (dmtype && !strcmp(dmtype, DMDA)) {
1517 /* DMDA path: convert to natural ordering first */
1518 ierr = DMDACreateNaturalVector(dm, &nat); CHKERRQ(ierr);
1519 ierr = DMDAGlobalToNaturalBegin(dm, field_vec, INSERT_VALUES, nat); CHKERRQ(ierr);
1520 ierr = DMDAGlobalToNaturalEnd(dm, field_vec, INSERT_VALUES, nat); CHKERRQ(ierr);
1521
1522 /* Gather the natural-ordered vector */
1523 ierr = VecScatterCreateToZero(nat, &scatter, &seq_vec); CHKERRQ(ierr);
1524 } else {
1525 /* Non-DMDA path: direct gather in global ordering */
1526 ierr = VecScatterCreateToZero(field_vec, &scatter, &seq_vec); CHKERRQ(ierr);
1527 }
1528
1529 /* 2.2 Gather distributed → sequential (on rank 0) */
1530 ierr = VecScatterBegin(scatter, (nat ? nat : field_vec), seq_vec,
1531 INSERT_VALUES, SCATTER_FORWARD); CHKERRQ(ierr);
1532 ierr = VecScatterEnd(scatter, (nat ? nat : field_vec), seq_vec,
1533 INSERT_VALUES, SCATTER_FORWARD); CHKERRQ(ierr);
1534
1535 /* 2.3 Rank 0 writes the file */
1536 if (rank == 0) {
1537 PetscViewer viewer;
1538
1539 /* (optional) value diagnostics */
1540 PetscReal vmin,vmax;
1541 ierr = VecMin(seq_vec,NULL,&vmin);CHKERRQ(ierr);
1542 ierr = VecMax(seq_vec,NULL,&vmax);CHKERRQ(ierr);
1544 " <%s> range = [%.4e … %.4e]\n",
1545 field_name,(double)vmin,(double)vmax);
1546
1547 ierr = PetscViewerBinaryOpen(PETSC_COMM_SELF,filename,
1548 FILE_MODE_WRITE,&viewer);CHKERRQ(ierr);
1549 ierr = VecView(seq_vec,viewer);CHKERRQ(ierr);
1550 ierr = PetscViewerDestroy(&viewer);CHKERRQ(ierr);
1551
1553 " Wrote <%s> (parallel path)\n",filename);
1554 }
1555
1556 /* 2.4 Cleanup */
1557 ierr = VecScatterDestroy(&scatter);CHKERRQ(ierr);
1558 ierr = VecDestroy(&seq_vec);CHKERRQ(ierr);
1559 if (nat) { ierr = VecDestroy(&nat); CHKERRQ(ierr); }
1560
1562 PetscFunctionReturn(0);
1563}
1564
1565/**
1566 * @brief Implementation of \ref WriteSimulationFields().
1567 * @details Full API contract (arguments, ownership, side effects) is documented with
1568 * the header declaration in `include/io.h`.
1569 * @see WriteSimulationFields()
1570 */
1571PetscErrorCode WriteSimulationFields(UserCtx *user)
1572{
1573 PetscErrorCode ierr;
1574
1575 SimCtx *simCtx = user->simCtx;
1576
1577 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting to write simulation fields.\n");
1578
1579 // Set the current IO directory
1580 ierr = PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer),
1581 "%s/%s", simCtx->output_dir, simCtx->euler_subdir);CHKERRQ(ierr);
1582 simCtx->current_io_directory = simCtx->_io_context_buffer;
1583
1584 // Write contravariant velocity field
1585 ierr = WriteFieldData(user, "vfield", user->Ucont, simCtx->step, "dat"); CHKERRQ(ierr);
1586
1587 // Write Cartesian velocity field
1588 ierr = WriteFieldData(user, "ufield", user->Ucat, simCtx->step, "dat"); CHKERRQ(ierr);
1589
1590 // Write pressure field
1591 ierr = WriteFieldData(user, "pfield", user->P, simCtx->step, "dat"); CHKERRQ(ierr);
1592
1593 // Write node state field (nvert)
1594 ierr = WriteFieldData(user, "nvfield", user->Nvert, simCtx->step, "dat"); CHKERRQ(ierr);
1595
1596 // Write ParticleCountPerCell if enabled.
1597 if(simCtx->np>0){
1598 ierr = WriteFieldData(user, "ParticleCount",user->ParticleCount,simCtx->step,"dat"); CHKERRQ(ierr);
1599 ierr = WriteFieldData(user, "psifield", user->Psi, simCtx->step, "dat"); CHKERRQ(ierr);
1600 }
1601
1602 // Write LES fields if enabled
1603 if (simCtx->les) {
1604 ierr = WriteLESFields(user); CHKERRQ(ierr);
1605 }
1606
1607 // Write RANS fields if enabled
1608 if (simCtx->rans) {
1609 ierr = WriteRANSFields(user); CHKERRQ(ierr);
1610 }
1611
1612 // Write statistical fields if averaging is enabled
1613 if (simCtx->averaging) {
1614 ierr = WriteStatisticalFields(user); CHKERRQ(ierr);
1615 }
1616
1617 simCtx->current_io_directory = NULL;
1618
1619 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished writing simulation fields.\n");
1620
1621 return 0;
1622}
1623
1624/**
1625 * @brief Implementation of \ref WriteStatisticalFields().
1626 * @details Full API contract (arguments, ownership, side effects) is documented with
1627 * the header declaration in `include/io.h`.
1628 * @see WriteStatisticalFields()
1629 */
1630PetscErrorCode WriteStatisticalFields(UserCtx *user)
1631{
1632 PetscErrorCode ierr;
1633
1634 SimCtx *simCtx = user->simCtx;
1635
1636 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting to write statistical fields.\n");
1637
1638 ierr = WriteFieldData(user, "su0", user->Ucat_sum, simCtx->step, "dat"); CHKERRQ(ierr);
1639 ierr = WriteFieldData(user, "su1", user->Ucat_cross_sum, simCtx->step, "dat"); CHKERRQ(ierr);
1640 ierr = WriteFieldData(user, "su2", user->Ucat_square_sum, simCtx->step, "dat"); CHKERRQ(ierr);
1641 ierr = WriteFieldData(user, "sp", user->P_sum, simCtx->step, "dat"); CHKERRQ(ierr);
1642
1643 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished writing statistical fields.\n");
1644
1645 return 0;
1646}
1647
1648/**
1649 * @brief Internal helper implementation: `WriteLESFields()`.
1650 * @details Local to this translation unit.
1651 */
1652PetscErrorCode WriteLESFields(UserCtx *user)
1653{
1654 PetscErrorCode ierr;
1655
1656 SimCtx *simCtx = user->simCtx;
1657
1658 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting to write LES fields.\n");
1659
1660
1661 DMLocalToGlobalBegin(user->da, user->lCs, INSERT_VALUES, user->CS);
1662 DMLocalToGlobalEnd(user->da, user->lCs, INSERT_VALUES, user->CS);
1663
1664 DMLocalToGlobalBegin(user->da, user->lNu_t, INSERT_VALUES, user->Nu_t);
1665 DMLocalToGlobalEnd(user->da, user->lNu_t, INSERT_VALUES, user->Nu_t);
1666
1667 ierr = WriteFieldData(user, "Nu_t", user->Nu_t, simCtx->step, "dat"); CHKERRQ(ierr);
1668 ierr = WriteFieldData(user, "cs", user->CS, simCtx->step, "dat"); CHKERRQ(ierr);
1669
1670
1671 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished writing LES fields.\n");
1672
1673 return 0;
1674}
1675
1676/**
1677 * @brief Implementation of \ref WriteRANSFields().
1678 * @details Full API contract (arguments, ownership, side effects) is documented with
1679 * the header declaration in `include/io.h`.
1680 * @see WriteRANSFields()
1681 */
1682PetscErrorCode WriteRANSFields(UserCtx *user)
1683{
1684 PetscErrorCode ierr;
1685
1686 SimCtx *simCtx = user->simCtx;
1687
1688 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting to write RANS fields.\n");
1689
1690 ierr = WriteFieldData(user, "kfield", user->K_Omega, simCtx->step, "dat"); CHKERRQ(ierr);
1691
1692 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished writing RANS fields.\n");
1693
1694 return 0;
1695}
1696
1697/**
1698 * @brief Implementation of \ref WriteSwarmField().
1699 * @details Full API contract (arguments, ownership, side effects) is documented with
1700 * the header declaration in `include/io.h`.
1701 * @see WriteSwarmField()
1702 */
1703PetscErrorCode WriteSwarmField(UserCtx *user, const char *field_name, PetscInt ti, const char *ext)
1704{
1705 PetscErrorCode ierr;
1706 Vec fieldVec;
1707 DM swarm;
1708
1709 PetscFunctionBeginUser; /* PETSc macro indicating start of function */
1710
1711 /*
1712 * 1) Retrieve the PetscSwarm from the user context.
1713 * Ensure user->swarm is initialized and not NULL.
1714 */
1715 swarm = user->swarm;
1716
1717 /*
1718 * 2) Create a global vector from the specified swarm field.
1719 * This function is available in PETSc 3.14.4.
1720 * It provides a read/write "view" of the swarm field as a global Vec.
1721 */
1723 "Attempting to create global vector from field: %s\n",
1724 field_name);
1725 ierr = DMSwarmCreateGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
1726
1727 /*
1728 * 3) Use your existing WriteFieldData() to write the global vector to a file.
1729 * The field name, time index, and extension are passed along for naming.
1730 */
1732 "Calling WriteFieldData for field: %s\n",
1733 field_name);
1734 ierr = WriteFieldData(user, field_name, fieldVec, ti, ext);CHKERRQ(ierr);
1735
1736 /*
1737 * 4) Destroy the global vector once the data is successfully written.
1738 * This step is crucial for avoiding memory leaks.
1739 * DMSwarmDestroyGlobalVectorFromField() is also available in PETSc 3.14.4.
1740 */
1742 "Destroying the global vector for field: %s\n",
1743 field_name);
1744 ierr = DMSwarmDestroyGlobalVectorFromField(swarm, field_name, &fieldVec);CHKERRQ(ierr);
1745
1746 /* Log and return success. */
1748 "Successfully wrote swarm data for field: %s\n",
1749 field_name);
1750
1751 PetscFunctionReturn(0); /* PETSc macro indicating end of function */
1752}
1753
1754/**
1755 * @brief Internal helper implementation: `WriteSwarmIntField()`.
1756 * @details Local to this translation unit.
1757 */
1758PetscErrorCode WriteSwarmIntField(UserCtx *user, const char *field_name, PetscInt ti, const char *ext)
1759{
1760 PetscErrorCode ierr;
1761 DM swarm = user->swarm;
1762 Vec temp_vec; // Temporary Vec to hold casted data
1763 PetscInt nlocal, nglobal,bs,i;
1764 void *field_array_void;
1765 PetscScalar *scalar_array; // Pointer to the temporary Vec's scalar data
1766
1767 PetscFunctionBeginUser;
1768
1769 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Casting '%s' to Vec for writing.\n", field_name);
1770
1771 // Get the swarm field properties
1772 ierr = DMSwarmGetLocalSize(swarm, &nlocal); CHKERRQ(ierr);
1773 ierr = DMSwarmGetSize(swarm, &nglobal); CHKERRQ(ierr);
1774 ierr = DMSwarmGetField(swarm, field_name, &bs, NULL, &field_array_void); CHKERRQ(ierr);
1775
1776 // Create Temporary parallel Vec wit the CORRECT layout
1777 ierr = VecCreate(PETSC_COMM_WORLD, &temp_vec); CHKERRQ(ierr);
1778 ierr = VecSetType(temp_vec, VECMPI); CHKERRQ(ierr);
1779 ierr = VecSetSizes(temp_vec, nlocal*bs, nglobal*bs); CHKERRQ(ierr);
1780 ierr = VecSetUp(temp_vec); CHKERRQ(ierr);
1781
1782 // Defining Vector field to mandatory field 'position'
1783 DMSwarmVectorDefineField(swarm,"position");
1784
1785 ierr = VecGetArray(temp_vec, &scalar_array); CHKERRQ(ierr);
1786
1787 if(strcasecmp(field_name,"DMSwarm_pid") == 0){
1788 PetscInt64 *int64_array = (PetscInt64 *)field_array_void;
1789 // Perform the cast from PetscInt64 to PetscScalar
1790 for (i = 0; i < nlocal*bs; i++) {
1791 scalar_array[i] = (PetscScalar)int64_array[i];
1792 }
1793 }else{
1794 PetscInt *int_array = (PetscInt *)field_array_void;
1795 //Perform the cast from PetscInt to PetscScalar
1796 for (i = 0; i < nlocal*bs; i++) {
1797 scalar_array[i] = (PetscScalar)int_array[i];
1798 }
1799 }
1800
1801 // Restore access to both arrays
1802 ierr = VecRestoreArray(temp_vec, &scalar_array); CHKERRQ(ierr);
1803 ierr = DMSwarmRestoreField(swarm, field_name, &bs, NULL, &field_array_void); CHKERRQ(ierr);
1804
1805 // Call your existing writer with the temporary, populated Vec
1806 ierr = WriteFieldData(user, field_name, temp_vec, ti, ext); CHKERRQ(ierr);
1807
1808 // Clean up
1809 ierr = VecDestroy(&temp_vec); CHKERRQ(ierr);
1810
1811 PetscFunctionReturn(0);
1812}
1813
1814/**
1815 * @brief Internal helper implementation: `WriteAllSwarmFields()`.
1816 * @details Local to this translation unit.
1817 */
1818PetscErrorCode WriteAllSwarmFields(UserCtx *user)
1819{
1820 PetscErrorCode ierr;
1821 SimCtx *simCtx = user->simCtx;
1822
1823 PetscFunctionBeginUser;
1824
1825 // If no swarm is configured or there are no particles, do nothing and return.
1826 if (!user->swarm || simCtx->np <= 0) {
1827 PetscFunctionReturn(0);
1828 }
1829
1830 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting to write swarm fields.\n");
1831
1832 // Ensure the current IO directory is set
1833 ierr = PetscSNPrintf(simCtx->_io_context_buffer, sizeof(simCtx->_io_context_buffer),
1834 "%s/%s", simCtx->output_dir, simCtx->particle_subdir);CHKERRQ(ierr);
1835 simCtx->current_io_directory = simCtx->_io_context_buffer;
1836
1837 // Write particle position field
1838 ierr = WriteSwarmField(user, "position", simCtx->step, "dat"); CHKERRQ(ierr);
1839
1840 // Write particle velocity field
1841 ierr = WriteSwarmField(user, "velocity", simCtx->step, "dat"); CHKERRQ(ierr);
1842
1843 // Write particle weight field
1844 ierr = WriteSwarmField(user, "weight", simCtx->step, "dat"); CHKERRQ(ierr);
1845
1846 // Write custom particle field "Psi"
1847 ierr = WriteSwarmField(user, "Psi", simCtx->step, "dat"); CHKERRQ(ierr);
1848
1849 // Integer fields require special handling
1850
1851 // Write the background mesh cell ID for each particle
1852 ierr = WriteSwarmIntField(user, "DMSwarm_CellID", simCtx->step, "dat"); CHKERRQ(ierr);
1853
1854 // Write the particle location status (e.g., inside or outside the domain)
1855 ierr = WriteSwarmIntField(user, "DMSwarm_location_status", simCtx->step, "dat"); CHKERRQ(ierr);
1856
1857 // Write the unique particle ID
1858 ierr = WriteSwarmIntField(user, "DMSwarm_pid", simCtx->step, "dat"); CHKERRQ(ierr);
1859
1860 simCtx->current_io_directory = NULL;
1861
1862 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished writing swarm fields.\n");
1863
1864 PetscFunctionReturn(0);
1865}
1866
1867/**
1868 * @brief Internal helper implementation: `VecToArrayOnRank0()`.
1869 * @details Local to this translation unit.
1870 */
1871PetscErrorCode VecToArrayOnRank0(Vec inVec, PetscInt *N, double **arrayOut)
1872{
1873 PetscErrorCode ierr;
1874 MPI_Comm comm;
1875 PetscMPIInt rank;
1876 PetscInt globalSize;
1877 DM dm = NULL;
1878 const char *dmtype = NULL;
1879
1880 /* For DMDA path */
1881 Vec nat = NULL, seqNat = NULL;
1882 VecScatter scatNat = NULL;
1883 const PetscScalar *nar = NULL;
1884 PetscScalar *buf = NULL;
1885
1886 /* For generic (no DM) path */
1887 Vec seq = NULL;
1888 VecScatter scat = NULL;
1889 const PetscScalar *sar = NULL;
1890
1891 PetscFunctionBeginUser;
1892
1893 ierr = PetscObjectGetComm((PetscObject)inVec, &comm); CHKERRQ(ierr);
1894 ierr = MPI_Comm_rank(comm, &rank); CHKERRQ(ierr);
1895 ierr = VecGetSize(inVec, &globalSize); CHKERRQ(ierr);
1896 *N = globalSize;
1897 *arrayOut = NULL;
1898
1899 ierr = VecGetDM(inVec, &dm); CHKERRQ(ierr);
1900 if (dm) { ierr = DMGetType(dm, &dmtype); CHKERRQ(ierr); }
1901
1902 if (dmtype && !strcmp(dmtype, DMDA)) {
1903 /* --- DMDA path: go to NATURAL ordering, then gather to rank 0 --- */
1904 ierr = DMDACreateNaturalVector(dm, &nat); CHKERRQ(ierr);
1905 ierr = DMDAGlobalToNaturalBegin(dm, inVec, INSERT_VALUES, nat); CHKERRQ(ierr);
1906 ierr = DMDAGlobalToNaturalEnd (dm, inVec, INSERT_VALUES, nat); CHKERRQ(ierr);
1907
1908 ierr = VecScatterCreateToZero(nat, &scatNat, &seqNat); CHKERRQ(ierr);
1909 ierr = VecScatterBegin(scatNat, nat, seqNat, INSERT_VALUES, SCATTER_FORWARD); CHKERRQ(ierr);
1910 ierr = VecScatterEnd (scatNat, nat, seqNat, INSERT_VALUES, SCATTER_FORWARD); CHKERRQ(ierr);
1911
1912 if (rank == 0) {
1913 PetscInt nseq;
1914 ierr = VecGetLocalSize(seqNat, &nseq); CHKERRQ(ierr);
1915 ierr = VecGetArrayRead(seqNat, &nar); CHKERRQ(ierr);
1916
1917 ierr = PetscMalloc1(nseq, &buf); CHKERRQ(ierr);
1918 ierr = PetscMemcpy(buf, nar, (size_t)nseq * sizeof(PetscScalar)); CHKERRQ(ierr);
1919
1920 ierr = VecRestoreArrayRead(seqNat, &nar); CHKERRQ(ierr);
1921 *arrayOut = (double*)buf; /* hand back as double* to match the helper signature */
1922 }
1923
1924 ierr = VecScatterDestroy(&scatNat); CHKERRQ(ierr);
1925 ierr = VecDestroy(&seqNat); CHKERRQ(ierr);
1926 ierr = VecDestroy(&nat); CHKERRQ(ierr);
1927 } else {
1928 /* --- No DM attached: plain gather in Vec’s global (parallel) layout order --- */
1929 ierr = VecScatterCreateToZero(inVec, &scat, &seq); CHKERRQ(ierr);
1930 ierr = VecScatterBegin(scat, inVec, seq, INSERT_VALUES, SCATTER_FORWARD); CHKERRQ(ierr);
1931 ierr = VecScatterEnd (scat, inVec, seq, INSERT_VALUES, SCATTER_FORWARD); CHKERRQ(ierr);
1932
1933 if (rank == 0) {
1934 PetscInt nseq;
1935 ierr = VecGetLocalSize(seq, &nseq); CHKERRQ(ierr);
1936 ierr = VecGetArrayRead(seq, &sar); CHKERRQ(ierr);
1937
1938 ierr = PetscMalloc1(nseq, &buf); CHKERRQ(ierr);
1939 ierr = PetscMemcpy(buf, sar, (size_t)nseq * sizeof(PetscScalar)); CHKERRQ(ierr);
1940
1941 ierr = VecRestoreArrayRead(seq, &sar); CHKERRQ(ierr);
1942 *arrayOut = (double*)buf;
1943 }
1944
1945 ierr = VecScatterDestroy(&scat); CHKERRQ(ierr);
1946 ierr = VecDestroy(&seq); CHKERRQ(ierr);
1947 }
1948
1949 PetscFunctionReturn(0);
1950}
1951
1952/**
1953 * @brief Internal helper implementation: `SwarmFieldToArrayOnRank0()`.
1954 * @details Local to this translation unit.
1955 */
1956PetscErrorCode SwarmFieldToArrayOnRank0(DM swarm, const char *field_name, PetscInt *n_total_particles, PetscInt *n_components, void **gathered_array)
1957{
1958 PetscErrorCode ierr;
1959 PetscMPIInt rank, size;
1960 PetscInt nlocal, nglobal, bs;
1961 void *local_array_void;
1962 size_t element_size = 0;
1963
1964 PetscFunctionBeginUser;
1965
1966 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
1967 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size); CHKERRQ(ierr);
1968
1969 // All ranks get swarm properties to determine send/receive counts
1970 ierr = DMSwarmGetLocalSize(swarm, &nlocal); CHKERRQ(ierr);
1971 ierr = DMSwarmGetSize(swarm, &nglobal); CHKERRQ(ierr);
1972 ierr = DMSwarmGetField(swarm, field_name, &bs, NULL, &local_array_void); CHKERRQ(ierr);
1973
1974 // Determine the size of one element of the field's data type
1975 if (strcasecmp(field_name, "DMSwarm_pid") == 0) {
1976 element_size = sizeof(PetscInt64);
1977 } else if (strcasecmp(field_name, "DMSwarm_CellID") == 0 || strcasecmp(field_name, "DMSwarm_location_status") == 0) {
1978 element_size = sizeof(PetscInt);
1979 } else {
1980 element_size = sizeof(PetscScalar);
1981 }
1982
1983 if (rank == 0) {
1984 *n_total_particles = nglobal;
1985 *n_components = bs;
1986 *gathered_array = NULL; // Initialize output
1987 }
1988
1989 if (size == 1) { // Serial case is a simple copy
1990 if (rank == 0) {
1991 ierr = PetscMalloc(nglobal * bs * element_size, gathered_array); CHKERRQ(ierr);
1992 ierr = PetscMemcpy(*gathered_array, local_array_void, nglobal * bs * element_size); CHKERRQ(ierr);
1993 }
1994 } else { // Parallel case: use MPI_Gatherv
1995 PetscInt *recvcounts = NULL, *displs = NULL;
1996 if (rank == 0) {
1997 ierr = PetscMalloc1(size, &recvcounts); CHKERRQ(ierr);
1998 ierr = PetscMalloc1(size, &displs); CHKERRQ(ierr);
1999 }
2000 PetscInt sendcount = nlocal * bs;
2001
2002 // Gather the number of elements (not bytes) from each rank
2003 ierr = MPI_Gather(&sendcount, 1, MPIU_INT, recvcounts, 1, MPIU_INT, 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
2004
2005 if (rank == 0) {
2006 displs[0] = 0;
2007 // Convert counts and calculate displacements in terms of BYTES
2008 for (PetscMPIInt i = 0; i < size; i++) recvcounts[i] *= element_size;
2009 for (PetscMPIInt i = 1; i < size; i++) displs[i] = displs[i-1] + recvcounts[i-1];
2010
2011 ierr = PetscMalloc(nglobal * bs * element_size, gathered_array); CHKERRQ(ierr);
2012 }
2013
2014 // Use Gatherv with MPI_BYTE to handle any data type generically
2015 ierr = MPI_Gatherv(local_array_void, nlocal * bs * element_size, MPI_BYTE,
2016 *gathered_array, recvcounts, displs, MPI_BYTE,
2017 0, PETSC_COMM_WORLD); CHKERRQ(ierr);
2018
2019 if (rank == 0) {
2020 ierr = PetscFree(recvcounts); CHKERRQ(ierr);
2021 ierr = PetscFree(displs); CHKERRQ(ierr);
2022 }
2023 }
2024
2025 ierr = DMSwarmRestoreField(swarm, field_name, &bs, NULL, &local_array_void); CHKERRQ(ierr);
2026
2027 PetscFunctionReturn(0);
2028}
2029
2030/**
2031 * @brief Internal helper implementation: `DisplayBanner()`.
2032 * @details Local to this translation unit.
2033 */
2034PetscErrorCode DisplayBanner(SimCtx *simCtx) // bboxlist is only valid on rank 0
2035{
2036 PetscErrorCode ierr;
2037 PetscMPIInt rank;
2038 Cmpnts global_min_coords, global_max_coords;
2039 PetscReal StartTime;
2040 PetscInt StartStep,StepsToRun,total_num_particles;
2041 PetscMPIInt num_mpi_procs;
2042
2043 // SimCtx *simCtx = user->simCtx;
2044 UserCtx *user = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
2045 num_mpi_procs = simCtx->size;
2046 StartTime = simCtx->StartTime;
2047 StartStep = simCtx->StartStep;
2048 StepsToRun = simCtx->StepsToRun;
2049 total_num_particles = simCtx->np;
2050 BoundingBox *bboxlist_on_rank0 = simCtx->bboxlist;
2051
2052
2053 PetscFunctionBeginUser;
2054
2055 if (!user) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "DisplayBanner - UserCtx pointer is NULL.");
2056 global_min_coords = user->bbox.min_coords;
2057 global_max_coords = user->bbox.max_coords;
2058 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
2059
2060 if (rank == 0) {
2061 // If global_domain_bbox is not pre-populated in UserCtx, compute it here from bboxlist_on_rank0
2062 // This assumes bboxlist_on_rank0 is valid and contains all local bounding boxes on rank 0.
2063 if (bboxlist_on_rank0 && num_mpi_procs > 0) {
2064 global_min_coords = bboxlist_on_rank0[0].min_coords;
2065 global_max_coords = bboxlist_on_rank0[0].max_coords;
2066 for (PetscMPIInt p = 1; p < num_mpi_procs; ++p) {
2067 global_min_coords.x = PetscMin(global_min_coords.x, bboxlist_on_rank0[p].min_coords.x);
2068 global_min_coords.y = PetscMin(global_min_coords.y, bboxlist_on_rank0[p].min_coords.y);
2069 global_min_coords.z = PetscMin(global_min_coords.z, bboxlist_on_rank0[p].min_coords.z);
2070 global_max_coords.x = PetscMax(global_max_coords.x, bboxlist_on_rank0[p].max_coords.x);
2071 global_max_coords.y = PetscMax(global_max_coords.y, bboxlist_on_rank0[p].max_coords.y);
2072 global_max_coords.z = PetscMax(global_max_coords.z, bboxlist_on_rank0[p].max_coords.z);
2073 }
2074 // Optionally store this in user->global_domain_bbox if it's useful elsewhere
2075 // user->global_domain_bbox.min_coords = global_min_coords;
2076 // user->global_domain_bbox.max_coords = global_max_coords;
2077 } else {
2078 // Fallback or warning if bboxlist is not available for global calculation
2079 LOG_ALLOW(LOCAL, LOG_WARNING, "(Rank 0) - bboxlist not provided or num_mpi_procs <=0; using user->bbox for domain bounds.\n");
2080 // global_min_coords = user->bbox.min_coords; // Use local bbox of rank 0 as fallback
2081 // global_max_coords = user->bbox.max_coords;
2082 }
2083
2084 ierr = PetscPrintf(PETSC_COMM_SELF, "\n"); CHKERRQ(ierr);
2085 ierr = PetscPrintf(PETSC_COMM_SELF, "=============================================================\n"); CHKERRQ(ierr);
2086 ierr = PetscPrintf(PETSC_COMM_SELF, " CASE SUMMARY \n"); CHKERRQ(ierr);
2087 ierr = PetscPrintf(PETSC_COMM_SELF, "=============================================================\n"); CHKERRQ(ierr);
2088 ierr = PetscPrintf(PETSC_COMM_SELF, " Grid Points : %d X %d X %d\n", user->IM, user->JM, user->KM); CHKERRQ(ierr);
2089 ierr = PetscPrintf(PETSC_COMM_SELF, " Cells : %d X %d X %d\n", user->IM - 1, user->JM - 1, user->KM - 1); CHKERRQ(ierr);
2090 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);
2091 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);
2092 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);
2093 if(strcmp(simCtx->eulerianSource,"load")==0 || strcmp(simCtx->eulerianSource,"solve")==0){
2094 ierr = PetscPrintf(PETSC_COMM_SELF, "-------------------- Boundary Conditions --------------------\n"); CHKERRQ(ierr);
2095 const int face_name_width = 17; // Adjusted for longer names (Zeta,Eta,Xi)
2096 for (PetscInt i_face = 0; i_face < 6; ++i_face) {
2097 BCFace current_face = (BCFace)i_face;
2098 // The BCFaceToString will now return the Xi, Eta, Zeta versions
2099 const char* face_str = BCFaceToString(current_face);
2100 const char* bc_type_str = BCTypeToString(user->boundary_faces[current_face].mathematical_type);
2101 const char* bc_handler_type_str = BCHandlerTypeToString(user->boundary_faces[current_face].handler_type);
2102 if(user->boundary_faces[current_face].mathematical_type == INLET){
2104 Cmpnts inlet_velocity = {0.0,0.0,0.0};
2105 PetscBool found;
2106 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"vx",&inlet_velocity.x,&found); CHKERRQ(ierr);
2107 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"vy",&inlet_velocity.y,&found); CHKERRQ(ierr);
2108 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"vz",&inlet_velocity.z,&found); CHKERRQ(ierr);
2109 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - [%.4f,%.4f,%.4f]\n",
2110 face_name_width, face_str, bc_type_str, bc_handler_type_str,inlet_velocity.x,inlet_velocity.y,inlet_velocity.z); CHKERRQ(ierr);
2111 } else if(user->boundary_faces[current_face].handler_type == BC_HANDLER_INLET_PARABOLIC){
2112 PetscReal v_max = 0.0;
2113 PetscBool found;
2114 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"v_max",&v_max,&found); CHKERRQ(ierr);
2115 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - v_max=%.4f\n",
2116 face_name_width, face_str, bc_type_str, bc_handler_type_str, v_max); CHKERRQ(ierr);
2117 } else if(user->boundary_faces[current_face].handler_type == BC_HANDLER_INLET_PROFILE_FROM_FILE){
2118 const char *source_file = "(missing)";
2119 for (BC_Param *param = user->boundary_faces[current_face].params; param; param = param->next) {
2120 if (strcasecmp(param->key, "source_file") == 0 && param->value) {
2121 source_file = param->value;
2122 break;
2123 }
2124 }
2125 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - source_file=%s\n",
2126 face_name_width, face_str, bc_type_str, bc_handler_type_str, source_file); CHKERRQ(ierr);
2127 }
2129 PetscReal flux;
2130 PetscBool trimflag,foundflux,foundtrimflag;
2131 ierr = GetBCParamReal(user->boundary_faces[current_face].params,"target_flux",&flux,&foundflux); CHKERRQ(ierr);
2132 ierr = GetBCParamBool(user->boundary_faces[current_face].params,"apply_trim",&trimflag,&foundtrimflag); CHKERRQ(ierr);
2133 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s - [%.4f] - %s\n",
2134 face_name_width, face_str, bc_type_str, bc_handler_type_str,flux,trimflag?"Trim Ucont":"No Trim"); CHKERRQ(ierr);
2135 } else{
2136 ierr = PetscPrintf(PETSC_COMM_SELF, " Face %-*s : %s - %s\n",
2137 face_name_width, face_str, bc_type_str,bc_handler_type_str); CHKERRQ(ierr);
2138 }
2139 }
2140 }
2141 ierr = PetscPrintf(PETSC_COMM_SELF, "-------------------------------------------------------------\n"); CHKERRQ(ierr);
2142 ierr = PetscPrintf(PETSC_COMM_SELF, " Run Mode : %s\n", simCtx->OnlySetup ? "SETUP ONLY" : "Full Simulation"); CHKERRQ(ierr);
2143 ierr = PetscPrintf(PETSC_COMM_SELF, " Start Time : %.4f\n", (double)StartTime); CHKERRQ(ierr);
2144 ierr = PetscPrintf(PETSC_COMM_SELF, " Timestep Size : %.4f\n", (double)simCtx->dt); CHKERRQ(ierr);
2145 ierr = PetscPrintf(PETSC_COMM_SELF, " Starting Step : %d\n", StartStep); CHKERRQ(ierr);
2146 ierr = PetscPrintf(PETSC_COMM_SELF, " Total Steps to Run : %d\n", StepsToRun); CHKERRQ(ierr);
2147 ierr = PetscPrintf(PETSC_COMM_SELF, " Ending Step : %d\n", StartStep + StepsToRun); CHKERRQ(ierr);
2148 if (simCtx->tiout > 0) {
2149 ierr = PetscPrintf(PETSC_COMM_SELF, " Field/Restart Cadence : every %d step(s)\n", simCtx->tiout); CHKERRQ(ierr);
2150 } else {
2151 ierr = PetscPrintf(PETSC_COMM_SELF, " Field/Restart Cadence : DISABLED\n"); CHKERRQ(ierr);
2152 }
2153 ierr = PetscPrintf(PETSC_COMM_SELF, " Immersed Boundary : %s\n", simCtx->immersed ? "ENABLED" : "DISABLED"); CHKERRQ(ierr);
2154 if (simCtx->walltimeGuardEnabled) {
2155 ierr = PetscPrintf(
2156 PETSC_COMM_SELF,
2157 " Runtime Walltime Guard : %s (warmup=%d, multiplier=%.2f, min=%.1f s, alpha=%.2f)\n",
2158 simCtx->walltimeGuardActive ? "ENABLED" : "CONFIGURED BUT INACTIVE",
2160 (double)simCtx->walltimeGuardMultiplier,
2161 (double)simCtx->walltimeGuardMinSeconds,
2162 (double)simCtx->walltimeGuardEstimatorAlpha
2163 ); CHKERRQ(ierr);
2164 } else {
2165 ierr = PetscPrintf(PETSC_COMM_SELF, " Runtime Walltime Guard : DISABLED\n"); CHKERRQ(ierr);
2166 }
2167 ierr = PetscPrintf(PETSC_COMM_SELF, " Number of MPI Processes : %d\n", num_mpi_procs); CHKERRQ(ierr);
2168 ierr = PetscPrintf(PETSC_COMM_WORLD," Number of Particles : %d\n", total_num_particles); CHKERRQ(ierr);
2169 if (simCtx->np > 0) {
2170 const char *particle_init_str = ParticleInitializationToString(simCtx->ParticleInitialization);
2171
2172 if (simCtx->particleConsoleOutputFreq > 0) {
2173 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Console Cadence : every %d step(s)\n", simCtx->particleConsoleOutputFreq); CHKERRQ(ierr);
2174 } else {
2175 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Console Cadence : DISABLED\n"); CHKERRQ(ierr);
2176 }
2177 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Log Row Sampling : every %d particle(s)\n", simCtx->LoggingFrequency); CHKERRQ(ierr);
2178 if (simCtx->StartStep > 0) {
2179 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Restart Mode : %s\n", simCtx->particleRestartMode); CHKERRQ(ierr);
2180 }
2181 ierr = PetscPrintf(PETSC_COMM_SELF, " Particle Initialization Mode: %s\n", particle_init_str); CHKERRQ(ierr);
2182 ierr = PetscPrintf(PETSC_COMM_SELF, " Interpolation Method : %s\n",
2183 simCtx->interpolationMethod == INTERP_TRILINEAR ? "Trilinear (direct cell-center)" : "CornerAveraged (legacy)"); CHKERRQ(ierr);
2186 if (user->inletFaceDefined) {
2187 ierr = PetscPrintf(PETSC_COMM_SELF, " Particles Initialized At : %s (Enum Val: %d)\n", BCFaceToString(user->identifiedInletBCFace), user->identifiedInletBCFace); CHKERRQ(ierr);
2188 } else {
2189 ierr = PetscPrintf(PETSC_COMM_SELF, " Particles Initialized At : --- (No INLET face identified)\n"); CHKERRQ(ierr);
2190 }
2191 }
2192 }
2193 if(strcmp(simCtx->eulerianSource,"solve")==0 || strcmp(simCtx->eulerianSource,"load")==0){
2194 const char* field_init_str = FieldInitializationToString(simCtx->FieldInitialization);
2195 ierr = PetscPrintf(PETSC_COMM_WORLD," Reynolds Number : %le\n", simCtx->ren); CHKERRQ(ierr);
2196 //ierr = PetscPrintf(PETSC_COMM_WORLD," Von-Neumann Number : %le\n", simCtx->vnn); CHKERRQ(ierr);
2197 if(strcmp(simCtx->eulerianSource,"solve")==0){
2198 //ierr = PetscPrintf(PETSC_COMM_WORLD," Stanton Number : %le\n", simCtx->st); CHKERRQ(ierr);
2199 ierr = PetscPrintf(PETSC_COMM_WORLD," Momentum Equation Solver : %s\n", MomentumSolverTypeToString(simCtx->mom_solver_type)); CHKERRQ(ierr);
2200 ierr = PetscPrintf(PETSC_COMM_WORLD," Initial Pseudo-CFL : %le\n", simCtx->pseudo_cfl); CHKERRQ(ierr);
2201 ierr = PetscPrintf(PETSC_COMM_WORLD," Large Eddy Simulation Model : %s\n", LESModelToString(simCtx->les)); CHKERRQ(ierr);
2202 }
2203 ierr = PetscPrintf(PETSC_COMM_SELF, " Field Initialization Mode : %s\n", field_init_str); CHKERRQ(ierr);
2204 if (simCtx->FieldInitialization == 1) {
2205 ierr = PetscPrintf(PETSC_COMM_SELF, " Constant Velocity : x - %.4f, y - %.4f, z - %.4f \n", (double)simCtx->InitialConstantContra.x,(double)simCtx->InitialConstantContra.y,(double)simCtx->InitialConstantContra.z ); CHKERRQ(ierr);
2206 }
2207 } else if(strcmp(simCtx->eulerianSource,"analytical")==0){
2208 ierr = PetscPrintf(PETSC_COMM_WORLD," Analytical Solution Type : %s\n", simCtx->AnalyticalSolutionType); CHKERRQ(ierr);
2209 }
2210 ierr = PetscPrintf(PETSC_COMM_SELF, "=============================================================\n"); CHKERRQ(ierr);
2211 ierr = PetscPrintf(PETSC_COMM_SELF, "\n"); CHKERRQ(ierr);
2212 }
2213 PetscFunctionReturn(0);
2214}
2215
2216#undef __FUNCT__
2217#define __FUNCT__ "ParsePostProcessingSettings"
2218/**
2219 * @brief Internal helper implementation: `ParsePostProcessingSettings()`.
2220 * @details Local to this translation unit.
2221 */
2223{
2224 FILE *file;
2225 char line[1024];
2226 PetscBool startTimeSet, endTimeSet, timeStepSet;
2227
2228 PetscFunctionBeginUser;
2230
2231 if (!simCtx || !simCtx->pps) {
2232 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_NULL, "SimCtx or its pps member is NULL in ParsePostProcessingSettings.");
2233 }
2234
2235 char *configFile = simCtx->PostprocessingControlFile;
2236 PostProcessParams *pps = simCtx->pps;
2237
2238
2239 // --- 1. Set Sane Defaults First ---
2240 pps->startTime = 0;
2241 pps->endTime = 0;
2242 pps->timeStep = 1;
2243 pps->outputParticles = PETSC_FALSE;
2244 pps->particle_output_freq = simCtx->LoggingFrequency; // Default to logging frequency;
2245 strcpy(pps->process_pipeline, "");
2246 strcpy(pps->output_fields_instantaneous, "Ucat,P");
2247 strcpy(pps->output_fields_averaged, "");
2248 strcpy(pps->output_prefix, "Field");
2249 strcpy(pps->particle_output_prefix,"Particle");
2250 strcpy(pps->particle_fields,"velocity,CellID,weight,pid");
2251 strcpy(pps->particle_pipeline,"");
2252 strncpy(pps->statistics_pipeline, "", MAX_PIPELINE_LENGTH - 1);
2253 strncpy(pps->statistics_output_prefix, "Stats", MAX_FILENAME_LENGTH - 1);
2254 strcpy(pps->particleExt,"dat"); // The input file format for particles.
2255 strcpy(pps->eulerianExt,"dat"); // The input file format for Eulerian fields.
2256 pps->reference[0] = pps->reference[1] = pps->reference[2] = 1;
2257 strncpy(pps->source_dir, simCtx->output_dir, sizeof(pps->source_dir) - 1);
2258 pps->source_dir[sizeof(pps->source_dir) - 1] = '\0'; // Ensure null-termination
2259
2260 // --- 2. Parse the Configuration File (overrides defaults) ---
2261 file = fopen(configFile, "r");
2262 if (file) {
2263 LOG_ALLOW(GLOBAL, LOG_INFO, "Parsing post-processing config file: %s\n", configFile);
2264 while (fgets(line, sizeof(line), file)) {
2265 char *key, *value, *comment;
2266 comment = strchr(line, '#'); if (comment) *comment = '\0';
2267 TrimWhitespace(line); if (strlen(line) == 0) continue;
2268 key = strtok(line, "="); value = strtok(NULL, "=");
2269 if (key && value) {
2270 TrimWhitespace(key); TrimWhitespace(value);
2271 if (strcmp(key, "startTime") == 0) pps->startTime = atoi(value);
2272 else if (strcmp(key, "endTime") == 0) pps->endTime = atoi(value);
2273 else if (strcmp(key, "timeStep") == 0) pps->timeStep = atoi(value);
2274 else if (strcmp(key, "output_particles") == 0) {
2275 if (strcasecmp(value, "true") == 0) pps->outputParticles = PETSC_TRUE;
2276 }
2277 else if (strcasecmp(key, "process_pipeline") == 0) {
2278 strncpy(pps->process_pipeline, value, MAX_PIPELINE_LENGTH - 1);
2279 pps->process_pipeline[MAX_PIPELINE_LENGTH - 1] = '\0'; // Ensure null-termination
2280 } else if (strcasecmp(key, "output_fields_instantaneous") == 0) {
2281 strncpy(pps->output_fields_instantaneous, value, MAX_FIELD_LIST_LENGTH - 1);
2283 } else if (strcasecmp(key, "output_fields_averaged") == 0) {
2284 strncpy(pps->output_fields_averaged, value, MAX_FIELD_LIST_LENGTH - 1);
2286 } else if (strcasecmp(key, "output_prefix") == 0) {
2287 strncpy(pps->output_prefix, value, MAX_FILENAME_LENGTH - 1);
2288 pps->output_prefix[MAX_FILENAME_LENGTH - 1] = '\0';
2289 } else if (strcasecmp(key, "particle_output_prefix") == 0) {
2290 strncpy(pps->particle_output_prefix, value, MAX_FILENAME_LENGTH - 1);
2292 } else if (strcasecmp(key, "particle_fields_instantaneous") == 0) {
2293 strncpy(pps->particle_fields, value, MAX_FIELD_LIST_LENGTH - 1);
2294 pps->particle_fields[MAX_FIELD_LIST_LENGTH - 1] = '\0';
2295 } else if (strcasecmp(key, "particle_pipeline") == 0) {
2296 strncpy(pps->particle_pipeline, value, MAX_PIPELINE_LENGTH - 1);
2297 pps->particle_pipeline[MAX_PIPELINE_LENGTH - 1] = '\0';
2298 } else if (strcasecmp(key, "particle_output_freq") == 0) {
2299 pps->particle_output_freq = atoi(value);
2300 } else if (strcasecmp(key, "statistics_pipeline") == 0) {
2301 strncpy(pps->statistics_pipeline, value, MAX_PIPELINE_LENGTH - 1);
2303 } else if (strcasecmp(key, "statistics_output_prefix") == 0) {
2304 strncpy(pps->statistics_output_prefix, value, MAX_FILENAME_LENGTH - 1);
2306 } else if (strcasecmp(key, "particleExt") == 0) {
2307 strncpy(pps->particleExt, value, sizeof(pps->particleExt) - 1);
2308 pps->particleExt[sizeof(pps->particleExt) - 1] = '\0';
2309 } else if (strcasecmp(key, "eulerianExt") == 0) {
2310 strncpy(pps->eulerianExt, value, sizeof(pps->eulerianExt) - 1);
2311 pps->eulerianExt[sizeof(pps->eulerianExt) - 1] = '\0';
2312 } else if (strcmp(key, "reference_ip") == 0) {pps->reference[0] = atoi(value);
2313 } else if (strcmp(key, "reference_jp") == 0) {pps->reference[1] = atoi(value);
2314 } else if (strcmp(key, "reference_kp") == 0) {pps->reference[2] = atoi(value);
2315 } else if (strcasecmp(key, "source_directory") == 0) {
2316 strncpy(pps->source_dir, value, sizeof(pps->source_dir) - 1);
2317 pps->source_dir[sizeof(pps->source_dir) - 1] = '\0';
2318 } else {
2319 LOG_ALLOW(GLOBAL, LOG_WARNING, "Unknown key '%s' in post-processing config file. Ignoring.\n", key);
2320 }
2321 // Add parsing for pipeline, fields, etc. in later phases
2322 }
2323 }
2324 fclose(file);
2325 } else {
2326 LOG_ALLOW(GLOBAL, LOG_WARNING, "Could not open post-processing config file '%s'. Using defaults and command-line overrides.\n", configFile);
2327 }
2328
2329 // --- 3. Parse Command-Line Options (overrides file settings and defaults) ---
2330 PetscOptionsGetInt(NULL, NULL, "-startTime", &pps->startTime, &startTimeSet);
2331 PetscOptionsGetInt(NULL, NULL, "-endTime", &pps->endTime, &endTimeSet);
2332 PetscOptionsGetInt(NULL, NULL, "-timeStep", &pps->timeStep, &timeStepSet);
2333 PetscOptionsGetBool(NULL, NULL, "-output_particles", &pps->outputParticles, NULL);
2334
2335 if(pps->endTime==-1){
2336 pps->endTime = simCtx->StartStep + simCtx->StepsToRun; // Total steps if endTime is set to -1.
2337 }
2338
2339 // If only startTime is given on command line, run for a single step
2340 if (startTimeSet && !endTimeSet) {
2341 pps->endTime = pps->startTime;
2342 }
2343
2344 LOG_ALLOW(GLOBAL, LOG_INFO, "Post-processing configured to run from t=%d to t=%d with step %d. Particle output: %s.\n",
2345 pps->startTime, pps->endTime, pps->timeStep, pps->outputParticles ? "TRUE" : "FALSE");
2346
2347 LOG_ALLOW(GLOBAL, LOG_INFO, "Process Pipeline: %s\n", pps->process_pipeline);
2348 LOG_ALLOW(GLOBAL, LOG_INFO, "Instantaneous Output Fields: %s\n", pps->output_fields_instantaneous);
2349 LOG_ALLOW(GLOBAL, LOG_INFO, "Output Prefix: %s\n", pps->output_prefix);
2350 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Output Prefix: %s\n", pps->particle_output_prefix);
2351 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Fields: %s\n", pps->particle_fields);
2352 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Pipeline: %s\n", pps->particle_pipeline);
2353 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle Output Frequency: %d\n", pps->particle_output_freq);
2354 LOG_ALLOW(GLOBAL, LOG_INFO, "Averaged Output Fields (reserved): %s\n", pps->output_fields_averaged);
2355 LOG_ALLOW(GLOBAL, LOG_INFO, "Post input extensions: Eulerian='.%s', Particle='.%s'\n", pps->eulerianExt, pps->particleExt);
2356
2358 PetscFunctionReturn(0);
2359}
2360
2361
2362#undef __FUNCT__
2363#define __FUNCT__ "ParseScalingInformation"
2364/**
2365 * @brief Implementation of \ref ParseScalingInformation().
2366 * @details Full API contract (arguments, ownership, side effects) is documented with
2367 * the header declaration in `include/io.h`.
2368 * @see ParseScalingInformation()
2369 */
2370PetscErrorCode ParseScalingInformation(SimCtx *simCtx)
2371{
2372 PetscErrorCode ierr;
2373 PetscBool flg;
2374
2375 PetscFunctionBeginUser;
2377
2378 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx is NULL in ParseScalingInformation");
2379
2380 // --- 1. Set default values to 1.0 ---
2381 // This represents a purely non-dimensional run if no scaling is provided.
2382 simCtx->scaling.L_ref = 1.0;
2383 simCtx->scaling.U_ref = 1.0;
2384 simCtx->scaling.rho_ref = 1.0;
2385
2386 // --- 2. Read overrides from the command line / control file ---
2387 ierr = PetscOptionsGetReal(NULL, NULL, "-scaling_L_ref", &simCtx->scaling.L_ref, &flg); CHKERRQ(ierr);
2388 ierr = PetscOptionsGetReal(NULL, NULL, "-scaling_U_ref", &simCtx->scaling.U_ref, &flg); CHKERRQ(ierr);
2389 ierr = PetscOptionsGetReal(NULL, NULL, "-scaling_rho_ref", &simCtx->scaling.rho_ref, &flg); CHKERRQ(ierr);
2390
2391 // --- 3. Calculate derived scaling factors ---
2392 // Check for division by zero to be safe, though U_ref should be positive.
2393 if (simCtx->scaling.U_ref <= 0.0) {
2394 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Reference velocity U_ref must be positive. Got %g", (double)simCtx->scaling.U_ref);
2395 }
2396 simCtx->scaling.P_ref = simCtx->scaling.rho_ref * simCtx->scaling.U_ref * simCtx->scaling.U_ref;
2397
2398 // --- 4. Log the final, effective scales for verification ---
2399 LOG(GLOBAL, LOG_INFO, "---------------- Physical Scales Initialized -----------------\n");
2400 LOG(GLOBAL, LOG_INFO, " L_ref: %.4f, U_ref: %.4f, rho_ref: %.4f, P_ref: %.4f\n",
2401 simCtx->scaling.L_ref, simCtx->scaling.U_ref, simCtx->scaling.rho_ref, simCtx->scaling.P_ref);
2402 LOG(GLOBAL, LOG_INFO, "--------------------------------------------------------------\n");
2403
2405 PetscFunctionReturn(0);
2406}
2407
2408/**
2409 * @brief Implementation of \ref ReadDataFileToArray().
2410 * @details Full API contract (arguments, ownership, side effects) is documented with
2411 * the header declaration in `include/io.h`.
2412 * @see ReadDataFileToArray()
2413 */
2414PetscInt ReadDataFileToArray(const char *filename,
2415 double **data_out,
2416 PetscInt *Nout,
2417 MPI_Comm comm)
2418{
2419 /* STEP 0: Prepare local variables & log function entry */
2420 PetscMPIInt rank, size;
2421 PetscErrorCode ierr;
2422 FILE *fp = NULL;
2423 PetscInt N = 0; /* number of lines/values read on rank 0 */
2424 double *array = NULL; /* pointer to local array on each rank */
2425 PetscInt fileExistsFlag = 0; /* 0 = doesn't exist, 1 = does exist */
2426
2428 "Start reading from file: %s\n",
2429 filename);
2430
2431 /* Basic error checking: data_out, Nout must be non-null. */
2432 if (!filename || !data_out || !Nout) {
2434 "Null pointer argument provided.\n");
2435 return 1;
2436 }
2437
2438 /* Determine rank/size for coordinating I/O. */
2439 MPI_Comm_rank(comm, &rank);
2440 MPI_Comm_size(comm, &size);
2441
2442 /* STEP 1: On rank 0, check if file can be opened. */
2443 if (!rank) {
2444 fp = fopen(filename, "r");
2445 if (fp) {
2446 fileExistsFlag = 1;
2447 fclose(fp);
2448 }
2449 }
2450
2451 /* STEP 2: Broadcast file existence to all ranks. */
2452 // In ReadDataFileToArray:
2453 ierr = MPI_Bcast(&fileExistsFlag, 1, MPI_INT, 0, comm); CHKERRQ(ierr);
2454
2455 if (!fileExistsFlag) {
2456 /* If file does not exist, log & return. */
2457 if (!rank) {
2459 "File '%s' not found.\n",
2460 filename);
2461 }
2462 return 2;
2463 }
2464
2465 /* STEP 3: Rank 0 re-opens and reads the file, counting lines, etc. */
2466 if (!rank) {
2467 fp = fopen(filename, "r");
2468 if (!fp) {
2470 "File '%s' could not be opened for reading.\n",
2471 filename);
2472 return 3;
2473 }
2474
2475 /* (3a) Count lines first. */
2476 {
2477 char line[256];
2478 while (fgets(line, sizeof(line), fp)) {
2479 N++;
2480 }
2481 }
2482
2484 "File '%s' has %d lines.\n",
2485 filename, N);
2486
2487 /* (3b) Allocate array on rank 0. */
2488 array = (double*)malloc(N * sizeof(double));
2489 if (!array) {
2490 fclose(fp);
2492 "malloc failed for array.\n");
2493 return 4;
2494 }
2495
2496 /* (3c) Rewind & read values into array. */
2497 rewind(fp);
2498 {
2499 PetscInt i = 0;
2500 char line[256];
2501 while (fgets(line, sizeof(line), fp)) {
2502 double val;
2503 if (sscanf(line, "%lf", &val) == 1) {
2504 array[i++] = val;
2505 }
2506 }
2507 }
2508 fclose(fp);
2509
2511 "Successfully read %d values from '%s'.\n",
2512 N, filename);
2513 }
2514
2515 /* STEP 4: Broadcast the integer N to all ranks. */
2516 ierr = MPI_Bcast(&N, 1, MPI_INT, 0, comm); CHKERRQ(ierr);
2517
2518 /* STEP 5: Each rank allocates an array to receive the broadcast if rank>0. */
2519 if (rank) {
2520 array = (double*)malloc(N * sizeof(double));
2521 if (!array) {
2523 "malloc failed on rank %d.\n",
2524 rank);
2525 return 5;
2526 }
2527 }
2528
2529 /* STEP 6: Broadcast the actual data from rank 0 to all. */
2530 ierr = MPI_Bcast(array, N, MPI_DOUBLE, 0, comm); CHKERRQ(ierr);
2531
2532 /* STEP 7: Assign outputs on all ranks. */
2533 *data_out = array;
2534 *Nout = N;
2535
2537 "Done. Provided array of length=%d to all ranks.\n",
2538 N);
2539 return 0; /* success */
2540}
2541
2542/**
2543 * @brief Internal helper implementation: `ReadPositionsFromFile()`.
2544 * @details Local to this translation unit.
2545 */
2546PetscErrorCode ReadPositionsFromFile(PetscInt timeIndex,
2547 UserCtx *user,
2548 double **coordsArray,
2549 PetscInt *Ncoords)
2550{
2551 PetscFunctionBeginUser;
2552
2553 PetscErrorCode ierr;
2554 Vec coordsVec;
2555
2556 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Creating coords Vec.\n");
2557 ierr = VecCreate(PETSC_COMM_WORLD, &coordsVec);CHKERRQ(ierr);
2558 ierr = VecSetFromOptions(coordsVec);CHKERRQ(ierr);
2559
2560 // For example: "position" is the name of the coordinate data
2561 ierr = ReadFieldData(user, "position", coordsVec, timeIndex, "dat");
2562 if (ierr) {
2564 "Error reading position data (ti=%d).\n",
2565 timeIndex);
2566 PetscFunctionReturn(ierr);
2567 }
2568
2569 LOG_ALLOW(GLOBAL, LOG_DEBUG, "ReadPositions - Gathering coords Vec to rank 0.\n");
2570 ierr = VecToArrayOnRank0(coordsVec, Ncoords, coordsArray);CHKERRQ(ierr);
2571
2572 ierr = VecDestroy(&coordsVec);CHKERRQ(ierr);
2573
2575 "Successfully gathered coordinates. Ncoords=%d.\n", *Ncoords);
2576 PetscFunctionReturn(0);
2577}
2578
2579
2580/**
2581 * @brief Internal helper implementation: `ReadFieldDataToRank0()`.
2582 * @details Local to this translation unit.
2583 */
2584PetscErrorCode ReadFieldDataToRank0(PetscInt timeIndex,
2585 const char *fieldName,
2586 UserCtx *user,
2587 double **scalarArray,
2588 PetscInt *Nscalars)
2589{
2590 PetscFunctionBeginUser;
2591
2592 PetscErrorCode ierr;
2593 Vec fieldVec;
2594
2595 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Creating field Vec.\n");
2596 ierr = VecCreate(PETSC_COMM_WORLD, &fieldVec);CHKERRQ(ierr);
2597 ierr = VecSetFromOptions(fieldVec);CHKERRQ(ierr);
2598
2599 ierr = ReadFieldData(user, fieldName, fieldVec, timeIndex, "dat");
2600 if (ierr) {
2602 "Error reading field '%s' (ti=%d).\n",
2603 fieldName, timeIndex);
2604 PetscFunctionReturn(ierr);
2605 }
2606
2607 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Gathering field Vec to rank 0.\n");
2608 ierr = VecToArrayOnRank0(fieldVec, Nscalars, scalarArray);CHKERRQ(ierr);
2609
2610 ierr = VecDestroy(&fieldVec);CHKERRQ(ierr);
2611
2613 "Successfully gathered field '%s'. Nscalars=%d.\n",
2614 fieldName, *Nscalars);
2615 PetscFunctionReturn(0);
2616}
PetscErrorCode ReadStatisticalFields(UserCtx *user, PetscInt ti)
Implementation of ReadStatisticalFields().
Definition io.c:1208
PetscErrorCode ParsePostProcessingSettings(SimCtx *simCtx)
Internal helper implementation: ParsePostProcessingSettings().
Definition io.c:2222
PetscErrorCode WriteAllSwarmFields(UserCtx *user)
Internal helper implementation: WriteAllSwarmFields().
Definition io.c:1818
PetscErrorCode SwarmFieldToArrayOnRank0(DM swarm, const char *field_name, PetscInt *n_total_particles, PetscInt *n_components, void **gathered_array)
Internal helper implementation: SwarmFieldToArrayOnRank0().
Definition io.c:1956
PetscErrorCode ParseScalingInformation(SimCtx *simCtx)
Implementation of ParseScalingInformation().
Definition io.c:2370
PetscInt ReadDataFileToArray(const char *filename, double **data_out, PetscInt *Nout, MPI_Comm comm)
Implementation of ReadDataFileToArray().
Definition io.c:2414
PetscErrorCode ReadGridFile(UserCtx *user)
Internal helper implementation: ReadGridFile().
Definition io.c:219
PetscErrorCode StringToBCHandlerType(const char *str, BCHandlerType *handler_out)
Internal helper implementation: StringToBCHandlerType().
Definition io.c:347
PetscErrorCode ReadRANSFields(UserCtx *user, PetscInt ti)
Internal helper implementation: ReadRANSFields().
Definition io.c:1260
PetscErrorCode GetBCParamReal(BC_Param *params, const char *key, PetscReal *value_out, PetscBool *found)
Internal helper implementation: GetBCParamReal().
Definition io.c:390
PetscErrorCode ReadSwarmIntField(UserCtx *user, const char *field_name, PetscInt ti, const char *ext)
Internal helper implementation: ReadSwarmIntField().
Definition io.c:1324
PetscErrorCode WriteSwarmField(UserCtx *user, const char *field_name, PetscInt ti, const char *ext)
Implementation of WriteSwarmField().
Definition io.c:1703
static PetscInt * g_IMs_from_file
Caches the IM dimensions for all blocks read from the grid file.
Definition io.c:19
PetscErrorCode WriteStatisticalFields(UserCtx *user)
Implementation of WriteStatisticalFields().
Definition io.c:1630
PetscErrorCode ReadSimulationFields(UserCtx *user, PetscInt ti)
Internal helper implementation: ReadSimulationFields().
Definition io.c:1129
static PetscInt g_nblk_from_file
Stores the number of blocks read from the grid file.
Definition io.c:17
PetscBool ShouldWriteDataOutput(const SimCtx *simCtx, PetscInt completed_step)
Implementation of ShouldWriteDataOutput().
Definition io.c:70
PetscErrorCode WriteFieldData(UserCtx *user, const char *field_name, Vec field_vec, PetscInt ti, const char *ext)
Internal helper implementation: WriteFieldData().
Definition io.c:1451
PetscErrorCode ReadLESFields(UserCtx *user, PetscInt ti)
Internal helper implementation: ReadLESFields().
Definition io.c:1232
PetscErrorCode ReadFieldData(UserCtx *user, const char *field_name, Vec field_vec, PetscInt ti, const char *ext)
Internal helper implementation: ReadFieldData().
Definition io.c:901
PetscErrorCode ParseAllBoundaryConditions(UserCtx *user, const char *bcs_input_filename)
Internal helper implementation: ParseAllBoundaryConditions().
Definition io.c:450
PetscErrorCode ValidateBCHandlerForBCType(BCType type, BCHandlerType handler)
Internal helper implementation: ValidateBCHandlerForBCType().
Definition io.c:365
void TrimWhitespace(char *str)
Implementation of TrimWhitespace().
Definition io.c:38
PetscErrorCode WriteSimulationFields(UserCtx *user)
Implementation of WriteSimulationFields().
Definition io.c:1571
PetscErrorCode ReadAllSwarmFields(UserCtx *user, PetscInt ti)
Internal helper implementation: ReadAllSwarmFields().
Definition io.c:1386
static PetscBool g_file_has_been_read
A flag to ensure the grid file is read only once.
Definition io.c:25
PetscErrorCode ReadGridGenerationInputs(UserCtx *user)
Internal helper implementation: ReadGridGenerationInputs().
Definition io.c:85
PetscErrorCode ReadSwarmField(UserCtx *user, const char *field_name, PetscInt ti, const char *ext)
Internal helper implementation: ReadSwarmField().
Definition io.c:1294
PetscErrorCode WriteRANSFields(UserCtx *user)
Implementation of WriteRANSFields().
Definition io.c:1682
PetscErrorCode PopulateFinestUserGridResolutionFromOptions(UserCtx *finest_users, PetscInt nblk)
Internal helper implementation: PopulateFinestUserGridResolutionFromOptions().
Definition io.c:170
PetscErrorCode DeterminePeriodicity(SimCtx *simCtx)
Internal helper implementation: DeterminePeriodicity().
Definition io.c:637
PetscErrorCode ReadFieldDataToRank0(PetscInt timeIndex, const char *fieldName, UserCtx *user, double **scalarArray, PetscInt *Nscalars)
Internal helper implementation: ReadFieldDataToRank0().
Definition io.c:2584
PetscErrorCode VecToArrayOnRank0(Vec inVec, PetscInt *N, double **arrayOut)
Internal helper implementation: VecToArrayOnRank0().
Definition io.c:1871
PetscErrorCode WriteSwarmIntField(UserCtx *user, const char *field_name, PetscInt ti, const char *ext)
Internal helper implementation: WriteSwarmIntField().
Definition io.c:1758
PetscErrorCode WriteLESFields(UserCtx *user)
Internal helper implementation: WriteLESFields().
Definition io.c:1652
static PetscInt * g_KMs_from_file
Caches the KM dimensions for all blocks read from the grid file.
Definition io.c:23
static PetscInt * g_JMs_from_file
Caches the JM dimensions for all blocks read from the grid file.
Definition io.c:21
PetscErrorCode StringToBCFace(const char *str, BCFace *face_out)
Internal helper implementation: StringToBCFace().
Definition io.c:317
PetscErrorCode ReadPositionsFromFile(PetscInt timeIndex, UserCtx *user, double **coordsArray, PetscInt *Ncoords)
Internal helper implementation: ReadPositionsFromFile().
Definition io.c:2546
static PetscErrorCode ReadOptionalField(UserCtx *user, const char *field_name, const char *field_label, Vec field_vec, PetscInt ti, const char *ext)
Internal helper implementation: ReadOptionalField().
Definition io.c:825
PetscErrorCode VerifyPathExistence(const char *path, PetscBool is_dir, PetscBool is_optional, const char *description, PetscBool *exists)
Internal helper implementation: VerifyPathExistence().
Definition io.c:741
PetscErrorCode GetBCParamBool(BC_Param *params, const char *key, PetscBool *value_out, PetscBool *found)
Internal helper implementation: GetBCParamBool().
Definition io.c:411
void FreeBC_ParamList(BC_Param *head)
Implementation of FreeBC_ParamList().
Definition io.c:302
PetscErrorCode StringToBCType(const char *str, BCType *type_out)
Internal helper implementation: StringToBCType().
Definition io.c:332
PetscErrorCode DisplayBanner(SimCtx *simCtx)
Internal helper implementation: DisplayBanner().
Definition io.c:2034
static PetscErrorCode CheckDataFile(UserCtx *user, PetscInt ti, const char *fieldName, const char *ext, PetscBool *fileExists)
Internal helper implementation: CheckDataFile().
Definition io.c:785
static PetscErrorCode ReadOptionalSwarmField(UserCtx *user, const char *field_name, const char *field_label, PetscInt ti, const char *ext)
Internal helper implementation: ReadOptionalSwarmField().
Definition io.c:859
Public interface for data input/output routines.
const char * BCHandlerTypeToString(BCHandlerType handler_type)
Converts a BCHandlerType enum to its string representation.
Definition logging.c:771
const char * FieldInitializationToString(PetscInt FieldInitialization)
Helper function to convert FieldInitialization to a string representation.
Definition logging.c:687
#define LOG_ALLOW_SYNC(scope, level, fmt,...)
Synchronized logging macro that checks both the log level and whether the calling function is in the ...
Definition logging.h:252
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:44
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:45
const char * BCFaceToString(BCFace face)
Helper function to convert BCFace enum to a string representation.
Definition logging.c:669
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:199
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:820
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:83
const char * BCTypeToString(BCType type)
Helper function to convert BCType enum to a string representation.
Definition logging.c:751
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:28
@ LOG_TRACE
Very fine-grained tracing information for in-depth debugging.
Definition logging.h:32
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:30
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:29
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:31
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:811
const char * LESModelToString(LESModelType LESFlag)
Helper function to convert LES Flag to a string representation.
Definition logging.c:720
const char * MomentumSolverTypeToString(MomentumSolverType SolverFlag)
Helper function to convert Momentum Solver flag to a string representation.
Definition logging.c:736
const char * ParticleInitializationToString(ParticleInitializationType ParticleInitialization)
Helper function to convert ParticleInitialization to a string representation.
Definition logging.c:703
char statistics_output_prefix[256]
basename for CSV output, e.g.
Definition variables.h:583
BCType
Defines the general mathematical/physical Category of a boundary.
Definition variables.h:251
@ INLET
Definition variables.h:258
@ SYMMETRY
Definition variables.h:256
@ OUTLET
Definition variables.h:257
@ PERIODIC
Definition variables.h:260
@ WALL
Definition variables.h:254
UserCtx * user
Definition variables.h:536
PetscBool inletFaceDefined
Definition variables.h:849
char particle_output_prefix[256]
Definition variables.h:578
PetscMPIInt rank
Definition variables.h:654
PetscInt cgrid
Definition variables.h:845
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:848
PetscInt block_number
Definition variables.h:726
BCFace identifiedInletBCFace
Definition variables.h:850
PetscBool walltimeGuardActive
Definition variables.h:796
char euler_subdir[PETSC_MAX_PATH_LEN]
Definition variables.h:674
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:833
PetscInt rans
Definition variables.h:746
@ PARTICLE_INIT_SURFACE_RANDOM
Random placement on the inlet face.
Definition variables.h:517
@ PARTICLE_INIT_SURFACE_EDGES
Deterministic placement at inlet face edges.
Definition variables.h:520
PetscReal StartTime
Definition variables.h:665
Vec K_Omega_o
Definition variables.h:886
char output_prefix[256]
Definition variables.h:575
char ** bcs_files
Definition variables.h:734
struct BC_Param_s * next
Definition variables.h:307
PetscReal Min_X
Definition variables.h:840
char * key
Definition variables.h:305
PetscInt KM
Definition variables.h:839
PetscInt tiout
Definition variables.h:663
UserMG usermg
Definition variables.h:778
#define MAX_FIELD_LIST_LENGTH
Definition variables.h:552
PetscReal walltimeGuardMinSeconds
Definition variables.h:799
PetscReal L_ref
Definition variables.h:633
Vec K_Omega
Definition variables.h:886
BCHandlerType
Defines the specific computational "strategy" for a boundary handler.
Definition variables.h:271
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:284
@ BC_HANDLER_INLET_PARABOLIC
Definition variables.h:277
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
Definition variables.h:276
@ BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX
Definition variables.h:287
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:286
@ BC_HANDLER_WALL_MOVING
Definition variables.h:274
@ BC_HANDLER_INLET_PROFILE_FROM_FILE
Definition variables.h:278
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:273
@ BC_HANDLER_OUTLET_CONSERVATION
Definition variables.h:282
char output_fields_averaged[1024]
Definition variables.h:574
PetscInt FieldInitialization
Definition variables.h:704
#define MAX_PIPELINE_LENGTH
Definition variables.h:551
PetscReal ren
Definition variables.h:699
BCHandlerType handler_type
Definition variables.h:337
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
Definition variables.h:156
PetscInt _this
Definition variables.h:843
PetscInt reference[3]
Definition variables.h:590
char output_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:673
PetscReal dt
Definition variables.h:666
PetscReal ry
Definition variables.h:844
PetscInt StepsToRun
Definition variables.h:662
PetscInt k_periodic
Definition variables.h:727
PetscInt timeStep
Definition variables.h:568
PetscInt np
Definition variables.h:753
PetscReal Max_Y
Definition variables.h:840
PetscBool averaging
Definition variables.h:750
char * value
Definition variables.h:306
Vec lCs
Definition variables.h:886
#define MAX_FILENAME_LENGTH
Definition variables.h:553
Vec Ucont
Definition variables.h:856
PetscInt StartStep
Definition variables.h:661
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
Definition variables.h:155
PetscBool OnlySetup
Definition variables.h:667
PetscScalar x
Definition variables.h:101
Vec Ucat_square_sum
Definition variables.h:889
char * current_io_directory
Definition variables.h:678
Vec P_sum
Definition variables.h:889
char grid_file[PETSC_MAX_PATH_LEN]
Definition variables.h:731
char statistics_pipeline[1024]
e.g.
Definition variables.h:582
InterpolationMethod interpolationMethod
Definition variables.h:758
char output_fields_instantaneous[1024]
Definition variables.h:573
char particleRestartMode[16]
Definition variables.h:759
char eulerianExt[8]
Definition variables.h:586
char particle_subdir[PETSC_MAX_PATH_LEN]
Definition variables.h:675
BoundingBox * bboxlist
Definition variables.h:756
Vec lNu_t
Definition variables.h:886
Vec Nu_t
Definition variables.h:886
PetscReal rz
Definition variables.h:844
char source_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:563
char particle_pipeline[1024]
Definition variables.h:576
BC_Param * params
Definition variables.h:338
char eulerianSource[PETSC_MAX_PATH_LEN]
Definition variables.h:671
PetscBool walltimeGuardEnabled
Definition variables.h:795
PetscInt walltimeGuardWarmupSteps
Definition variables.h:797
ParticleInitializationType ParticleInitialization
Definition variables.h:757
PetscScalar z
Definition variables.h:101
@ INTERP_TRILINEAR
Definition variables.h:530
Vec lK_Omega_o
Definition variables.h:886
Vec Ucat
Definition variables.h:856
Vec ParticleCount
Definition variables.h:903
ScalingCtx scaling
Definition variables.h:721
Vec lK_Omega
Definition variables.h:886
PetscInt JM
Definition variables.h:839
PetscInt mglevels
Definition variables.h:543
PetscReal Min_Z
Definition variables.h:840
Vec Ucat_sum
Definition variables.h:889
char process_pipeline[1024]
Definition variables.h:572
PetscInt particle_output_freq
Definition variables.h:579
char particle_fields[1024]
Definition variables.h:577
PetscBool outputParticles
Definition variables.h:569
char AnalyticalSolutionType[PETSC_MAX_PATH_LEN]
Definition variables.h:684
PetscReal Max_X
Definition variables.h:840
PetscInt particleConsoleOutputFreq
Definition variables.h:664
Cmpnts InitialConstantContra
Definition variables.h:705
PetscReal Min_Y
Definition variables.h:840
PetscInt i_periodic
Definition variables.h:727
PetscInt step
Definition variables.h:659
PostProcessParams * pps
Definition variables.h:817
PetscScalar y
Definition variables.h:101
PetscMPIInt size
Definition variables.h:655
@ EXEC_MODE_SOLVER
Definition variables.h:624
@ EXEC_MODE_POSTPROCESSOR
Definition variables.h:625
PetscInt IM
Definition variables.h:839
char _io_context_buffer[PETSC_MAX_PATH_LEN]
Definition variables.h:677
char particleExt[8]
Definition variables.h:587
Vec Ucat_cross_sum
Definition variables.h:889
PetscReal walltimeGuardEstimatorAlpha
Definition variables.h:800
PetscInt les
Definition variables.h:746
Vec Nvert
Definition variables.h:856
MGCtx * mgctx
Definition variables.h:546
BCType mathematical_type
Definition variables.h:336
PetscReal P_ref
Definition variables.h:636
PetscReal rx
Definition variables.h:844
ExecutionMode exec_mode
Definition variables.h:670
PetscInt startTime
Definition variables.h:566
PetscReal rho_ref
Definition variables.h:635
BoundingBox bbox
Definition variables.h:841
PetscReal walltimeGuardMultiplier
Definition variables.h:798
PetscReal Max_Z
Definition variables.h:840
PetscReal U_ref
Definition variables.h:634
MomentumSolverType mom_solver_type
Definition variables.h:691
PetscInt immersed
Definition variables.h:681
char PostprocessingControlFile[PETSC_MAX_PATH_LEN]
Definition variables.h:816
char restart_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:672
PetscReal pseudo_cfl
Definition variables.h:699
PetscInt LoggingFrequency
Definition variables.h:783
Vec Psi
Definition variables.h:904
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:244
@ BC_FACE_NEG_X
Definition variables.h:245
@ BC_FACE_POS_Z
Definition variables.h:247
@ BC_FACE_POS_Y
Definition variables.h:246
@ BC_FACE_NEG_Z
Definition variables.h:247
@ BC_FACE_POS_X
Definition variables.h:245
@ BC_FACE_NEG_Y
Definition variables.h:246
PetscInt j_periodic
Definition variables.h:727
BoundaryCondition * handler
Definition variables.h:339
A node in a linked list for storing key-value parameters from the bcs.dat file.
Definition variables.h:304
Holds the complete configuration for one of the six boundary faces.
Definition variables.h:334
Defines a 3D axis-aligned bounding box.
Definition variables.h:154
A 3D point or vector with PetscScalar components.
Definition variables.h:100
Holds all configuration parameters for a post-processing run.
Definition variables.h:561
The master context for the entire simulation.
Definition variables.h:651
User-defined context containing data specific to a single computational grid level.
Definition variables.h:830