PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
setup.c
Go to the documentation of this file.
1/**
2 * @file setup.c // Setup code for running any simulation
3 * @brief Test program for DMSwarm interpolation using the fdf-curvIB method.
4 * Provides the setup to start any simulation with DMSwarm and DMDAs.
5 **/
6
7#include <ctype.h>
8#include <errno.h>
9
10#include "setup.h"
11
12/**
13 * @brief Implementation of \ref RuntimeWalltimeGuardParsePositiveSeconds().
14 * @details Full API contract (arguments, ownership, side effects) is documented with
15 * the header declaration in `include/setup.h`.
16 * @see RuntimeWalltimeGuardParsePositiveSeconds()
17 */
18PetscBool RuntimeWalltimeGuardParsePositiveSeconds(const char *text, PetscReal *seconds_out)
19{
20 char *endptr = NULL;
21 double parsed_value;
22
23 if (seconds_out) *seconds_out = 0.0;
24 if (!text || text[0] == '\0') return PETSC_FALSE;
25
26 errno = 0;
27 parsed_value = strtod(text, &endptr);
28 if (endptr == text || errno == ERANGE || !isfinite(parsed_value) || parsed_value <= 0.0) {
29 return PETSC_FALSE;
30 }
31
32 while (*endptr != '\0' && isspace((unsigned char)*endptr)) {
33 endptr++;
34 }
35 if (*endptr != '\0') return PETSC_FALSE;
36
37 if (seconds_out) *seconds_out = (PetscReal)parsed_value;
38 return PETSC_TRUE;
39}
40
41/**
42 * @brief Implementation of \ref InitializeSolutionConvergenceState().
43 * @details Full API contract (arguments, ownership, side effects) is documented with
44 * the header declaration in `include/setup.h`.
45 * @see InitializeSolutionConvergenceState()
46 */
48{
49 UserCtx *user = NULL;
50 PetscInt history_capacity = 0;
51
52 PetscFunctionBeginUser;
53 if (!simCtx) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx cannot be NULL.");
54 if (simCtx->exec_mode != EXEC_MODE_SOLVER) PetscFunctionReturn(0);
55 if (!simCtx->usermg.mgctx) {
56 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
57 "Multigrid hierarchy must exist before initializing solution convergence storage.");
58 }
59
60 user = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
61 if (!user) {
62 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
63 "Finest-level UserCtx must exist before initializing solution convergence storage.");
64 }
65
67
69 for (PetscInt bi = 0; bi < simCtx->block_number; ++bi) {
70 PetscCall(PetscCalloc1((size_t)simCtx->solutionConvergencePeriodSteps,
72 PetscCall(PetscCalloc1((size_t)simCtx->solutionConvergencePeriodSteps,
74 for (PetscInt phase = 0; phase < simCtx->solutionConvergencePeriodSteps; ++phase) {
75 PetscCall(VecDuplicate(user[bi].Ucat, &user[bi].solutionConvergencePeriodicUcatRef[phase]));
76 PetscCall(VecSet(user[bi].solutionConvergencePeriodicUcatRef[phase], 0.0));
77 PetscCall(VecDuplicate(user[bi].P, &user[bi].solutionConvergencePeriodicPRef[phase]));
78 PetscCall(VecSet(user[bi].solutionConvergencePeriodicPRef[phase], 0.0));
79 }
80 }
81 }
82
84 history_capacity = 2 * simCtx->solutionConvergenceWindowSteps;
85 PetscCall(PetscCalloc1((size_t)history_capacity, &simCtx->solutionConvergenceMeanSpeedHistory));
86 PetscCall(PetscCalloc1((size_t)history_capacity, &simCtx->solutionConvergenceMeanKEHistory));
87 }
88
89 PetscFunctionReturn(0);
90}
91
92/**
93 * @brief Implementation of \ref DestroySolutionConvergenceState().
94 * @details Full API contract (arguments, ownership, side effects) is documented with
95 * the header declaration in `include/setup.h`.
96 * @see DestroySolutionConvergenceState()
97 */
99{
100 UserCtx *user = NULL;
101
102 PetscFunctionBeginUser;
103 if (!simCtx || !simCtx->usermg.mgctx) PetscFunctionReturn(0);
104
105 user = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
106 if (user) {
107 for (PetscInt bi = 0; bi < simCtx->block_number; ++bi) {
108 if (user[bi].solutionConvergencePeriodicUcatRef) {
109 for (PetscInt phase = 0; phase < simCtx->solutionConvergencePeriodSteps; ++phase) {
110 if (user[bi].solutionConvergencePeriodicUcatRef[phase]) {
111 PetscCall(VecDestroy(&user[bi].solutionConvergencePeriodicUcatRef[phase]));
112 }
113 }
114 PetscCall(PetscFree(user[bi].solutionConvergencePeriodicUcatRef));
116 }
117 if (user[bi].solutionConvergencePeriodicPRef) {
118 for (PetscInt phase = 0; phase < simCtx->solutionConvergencePeriodSteps; ++phase) {
119 if (user[bi].solutionConvergencePeriodicPRef[phase]) {
120 PetscCall(VecDestroy(&user[bi].solutionConvergencePeriodicPRef[phase]));
121 }
122 }
123 PetscCall(PetscFree(user[bi].solutionConvergencePeriodicPRef));
124 user[bi].solutionConvergencePeriodicPRef = NULL;
125 }
126 }
127 }
128
130 PetscCall(PetscFree(simCtx->solutionConvergenceMeanSpeedHistory));
132 }
134 PetscCall(PetscFree(simCtx->solutionConvergenceMeanKEHistory));
136 }
138
139 PetscFunctionReturn(0);
140}
141
142#undef __FUNCT__
143#define __FUNCT__ "CreateSimulationContext"
144
145/**
146 * @brief Implementation of \ref CreateSimulationContext().
147 * @details Full API contract (arguments, ownership, side effects) is documented with
148 * the header declaration in `include/setup.h`.
149 * @see CreateSimulationContext()
150 */
151PetscErrorCode CreateSimulationContext(int argc, char **argv, SimCtx **p_simCtx)
152{
153 PetscErrorCode ierr;
154 (void)argc;
155 (void)argv;
156 SimCtx *simCtx;
157 char control_filename[PETSC_MAX_PATH_LEN] = ""; // Temporary placeholder for control file name.
158 PetscBool control_flg; // Temporary placeholder for control file tag existence check flag.
159 PetscBool particle_console_output_freq_flg = PETSC_FALSE;
160
161 PetscFunctionBeginUser;
162
164
165 // === 1. Allocate the Context Struct and Set ALL Defaults ==================
166 ierr = PetscNew(p_simCtx); CHKERRQ(ierr);
167 simCtx = *p_simCtx;
168
169 // --- Group 1: Parallelism & MPI ---
170 simCtx->rank = 0; simCtx->size = 1;
171
172 // --- Group 2: Simulation Control, Time, and I/O ---
173 simCtx->step = 0; simCtx->ti = 0.0; simCtx->StartStep = 0; simCtx->StepsToRun = 10;
174 simCtx->tiout = 10; simCtx->particleConsoleOutputFreq = simCtx->tiout;
175 simCtx->StartTime = 0.0; simCtx->dt = 0.001;
176 simCtx->OnlySetup = PETSC_FALSE;
177 simCtx->continueMode = PETSC_FALSE;
178 simCtx->logviewer = NULL;
179 strcpy(simCtx->eulerianSource,"solve");
180 strcpy(simCtx->restart_dir,"restart");
181 strcpy(simCtx->output_dir,"output");
182 strcpy(simCtx->log_dir,"logs");
183 strcpy(simCtx->euler_subdir,"eulerian");
184 strcpy(simCtx->particle_subdir,"particles");
185 simCtx->_io_context_buffer[0] = '\0';
186 simCtx->current_io_directory = NULL;
187
188 // --- Group 3: High-Level Physics & Model Selection Flags ---
189 simCtx->immersed = 0; simCtx->movefsi = 0; simCtx->rotatefsi = 0;
190 simCtx->sediment = 0; simCtx->rheology = 0; simCtx->invicid = 0;
191 simCtx->TwoD = 0; simCtx->thin = 0; simCtx->moveframe = 0;
192 simCtx->rotateframe = 0; simCtx->blank = 0;
193 simCtx->dgf_x = 0; simCtx->dgf_y = 1; simCtx->dgf_z = 0;
194 simCtx->dgf_ax = 1; simCtx->dgf_ay = 0; simCtx->dgf_az = 0;
195 strcpy(simCtx->AnalyticalSolutionType,"TGV3D");
196
197 // --- Group 4: Specific Simulation Case Flags --- (DEPRICATED)
198 simCtx->cop=0; simCtx->fish=0; simCtx->fish_c=0; simCtx->fishcyl=0;
199 simCtx->eel=0; simCtx->pizza=0; simCtx->turbine=0; simCtx->Pipe=0;
200 simCtx->wing=0; simCtx->hydro=0; simCtx->MHV=0; simCtx->LV=0;
201 simCtx->channelz = 0;
202
203 // --- Group 5: Solver & Numerics Parameters ---
205 simCtx->mom_dt_jameson_residual_norm_noise_allowance_factor = 1.1; // raised from 1.05; less aggressive rejection
206 simCtx->mom_atol = 1e-7; simCtx->mom_rtol = 1e-4;
207 simCtx->mom_resid_atol = 0.0; simCtx->mom_resid_rtol = 0.0;
208 simCtx->imp_stol = 1.e-8;
209 simCtx->mglevels = 3; simCtx->mg_MAX_IT = 30; simCtx->mg_idx = 1;
210 simCtx->mg_preItr = 1; simCtx->mg_poItr = 1;
211 simCtx->poisson = 0; simCtx->poisson_tol = 5.e-9;
212 simCtx->STRONG_COUPLING = 0;simCtx->central=0;
213 /* pseudo_cfl and its bounds are now dimensionless Courant numbers: CFL = dtau * lambda_max,
214 where lambda_max is the global spectral radius computed at each physical timestep.
215 Stable range for 4-stage Jameson RK: ~0–2.83. Initial 0.5 gives a comfortable margin. */
216 simCtx->ren = 100.0; simCtx->pseudo_cfl = 0.5;
217 simCtx->max_pseudo_cfl = 2.0; simCtx->min_pseudo_cfl = 0.001;
218 simCtx->pseudo_cfl_reduction_factor = 0.75;
219 simCtx->pseudo_cfl_growth_factor = 1.1; // raised from 1.0; controller can now increase CFL
220 simCtx->no_pseudo_cfl_backtrack = PETSC_FALSE;
221 simCtx->mom_ratio_ema_alpha = 0.3; /* moderate smoothing; set to 1.0 to recover original raw-ratio behavior */
222 simCtx->mom_last_converged = PETSC_TRUE;
223 simCtx->mom_last_lambda_max = 0.0; /* populated after first momentum solve */
224 simCtx->mom_nk_monitor_history = PETSC_FALSE;
225 simCtx->ps_ksp_pic_monitor_true_residual = PETSC_FALSE;
226 simCtx->cdisx = 0.0; simCtx->cdisy = 0.0; simCtx->cdisz = 0.0;
229 strcpy(simCtx->initialConditionDirectory, "config/initial_condition");
230 simCtx->InitialConstantContra.x = 0.0;
231 simCtx->InitialConstantContra.y = 0.0;
232 simCtx->InitialConstantContra.z = 0.0;
234 simCtx->icVelocityPhysical = 0.0;
235 simCtx->AnalyticalUniformVelocity.x = 0.0;
236 simCtx->AnalyticalUniformVelocity.y = 0.0;
237 simCtx->AnalyticalUniformVelocity.z = 0.0;
244 simCtx->verificationDiffusivity.enabled = PETSC_FALSE;
245 strcpy(simCtx->verificationDiffusivity.mode, "");
246 strcpy(simCtx->verificationDiffusivity.profile, "");
247 simCtx->verificationDiffusivity.gamma0 = 0.0;
248 simCtx->verificationDiffusivity.slope_x = 0.0;
249
250 // --- Group 6: Physical & Geometric Parameters ---
251 simCtx->NumberOfBodies = 1; simCtx->Flux_in = 1.0; simCtx->angle = 0.0;
252 simCtx->max_angle = -54. * 3.1415926 / 180.;
253 simCtx->CMx_c=0.0; simCtx->CMy_c=0.0; simCtx->CMz_c=0.0;
254 simCtx->wall_roughness_height = 1e-16;
255 simCtx->schmidt_number = 1.0; simCtx->Turbulent_schmidt_number = 0.7;
256
257 // --- Group 7: Grid, Domain, and Boundary Condition Settings ---
258 simCtx->block_number = 1; simCtx->inletprofile = 1;
259 simCtx->grid1d = 0; simCtx->Ogrid = 0;
260 simCtx->i_periodic = 0; simCtx->j_periodic = 0; simCtx->k_periodic = 0;
261 simCtx->blkpbc = 10; simCtx->pseudo_periodic = 0;
262 strcpy(simCtx->grid_file, "config/grid.run");
263 simCtx->generate_grid = PETSC_FALSE;
264 simCtx->da_procs_x = PETSC_DECIDE;
265 simCtx->da_procs_y = PETSC_DECIDE;
266 simCtx->da_procs_z = PETSC_DECIDE;
267 simCtx->grid_rotation_angle = 0.0;
268 simCtx->Croty = 0.0; simCtx->Crotz = 0.0;
269 simCtx->num_bcs_files = 1;
270 ierr = PetscMalloc1(1, &simCtx->bcs_files); CHKERRQ(ierr);
271 ierr = PetscStrallocpy("config/bcs.run", &simCtx->bcs_files[0]); CHKERRQ(ierr);
272 simCtx->FluxInSum = 0.0; simCtx->FluxOutSum = 0.0; simCtx->Fluxsum = 0.0;
273 simCtx->drivingForceMagnitude = 0.0, simCtx->forceScalingFactor = 1.8;
274 simCtx->targetVolumetricFlux = 0.0;
275 simCtx->bulkVelocityCorrection = 0.0;
276 simCtx->boundaryVelocityCorrection = 0.0;
277 simCtx->AreaInSum = 0.0; simCtx->AreaOutSum = 0.0;
278 simCtx->U_bc = 0.0; simCtx->ccc = 0;
279 simCtx->ratio = 0.0;
280
281
282 // --- Group 8: Turbulence Modeling (LES/RANS) ---
283 simCtx->averaging = PETSC_FALSE; simCtx->les = NO_LES_MODEL; simCtx->rans = 0;
284 simCtx->wallfunction = 0; simCtx->mixed = 0; simCtx->clark = 0;
285 simCtx->dynamic_freq = 1; simCtx->max_cs = 0.5;
286 simCtx->Const_CS = 0.03;
287 simCtx->testfilter_ik = 0; simCtx->testfilter_1d = 0;
288 simCtx->i_homo_filter = 0; simCtx->j_homo_filter = 0; simCtx->k_homo_filter = 0;
289
290 // --- Group 9: Particle / DMSwarm Data & Settings ---
291 simCtx->np = 0; simCtx->readFields = PETSC_FALSE;
292 simCtx->dm_swarm = NULL; simCtx->bboxlist = NULL;
295 strcpy(simCtx->particleRestartMode,"load");
296 simCtx->particlesLostLastStep = 0;
297 simCtx->particlesLostCumulative = 0;
298 simCtx->particlesMigratedLastStep = 0;
299 simCtx->occupiedCellCount = 0;
300 simCtx->particleLoadImbalance = 0.0;
301 simCtx->migrationPassesLastStep = 0;
302 simCtx->searchMetrics.searchAttempts = 0;
305 simCtx->searchMetrics.searchLostCount = 0;
307 simCtx->searchMetrics.reSearchCount = 0;
310 simCtx->searchMetrics.tieBreakCount = 0;
316 simCtx->BrownianMotionRNG = NULL;
317 simCtx->C_IEM = 2.0;
318
319 // --- Group 10: Immersed Boundary & FSI Data Object Pointers ---
320 simCtx->ibm = NULL; simCtx->ibmv = NULL; simCtx->fsi = NULL;
321 simCtx->rstart_fsi = PETSC_FALSE; simCtx->duplicate = 0;
322
323 // --- Group 11: Logging and Custom Configuration ---
324 strcpy(simCtx->allowedFile, "config/whitelist.run");
325 simCtx->useCfg = PETSC_FALSE;
326 simCtx->allowedFuncs = NULL;
327 simCtx->nAllowed = 0;
328 simCtx->LoggingFrequency = 10;
329 simCtx->summationRHS = 0.0;
330 simCtx->MaxDiv = 0.0;
331 simCtx->MaxDivFlatArg = 0; simCtx->MaxDivx = 0; simCtx->MaxDivy = 0; simCtx->MaxDivz = 0;
332 strcpy(simCtx->profilingSelectedFuncsFile, "config/profile.run");
333 simCtx->useProfilingSelectedFuncsCfg = PETSC_FALSE;
334 simCtx->profilingSelectedFuncs = NULL;
335 simCtx->nProfilingSelectedFuncs = 0;
336 strcpy(simCtx->profilingTimestepMode, "selected");
337 strcpy(simCtx->profilingTimestepFile, "Profiling_Timestep_Summary.csv");
338 simCtx->profilingFinalSummary = PETSC_TRUE;
339 simCtx->walltimeGuardEnabled = PETSC_FALSE;
340 simCtx->walltimeGuardActive = PETSC_FALSE;
341 simCtx->walltimeGuardWarmupSteps = 10;
342 simCtx->walltimeGuardMultiplier = 2.0;
343 simCtx->walltimeGuardMinSeconds = 60.0;
344 simCtx->walltimeGuardEstimatorAlpha = 0.35;
346 simCtx->walltimeGuardLimitSeconds = 0.0;
347 simCtx->walltimeGuardCompletedSteps = 0;
350 simCtx->walltimeGuardHasEWMA = PETSC_FALSE;
351 simCtx->walltimeGuardEWMASeconds = 0.0;
352 simCtx->walltimeGuardLatestStepSeconds = 0.0;
353 simCtx->runtimeMemoryLogEnabled = PETSC_TRUE;
354 strcpy(simCtx->runtimeMemoryLogFile, "Runtime_Memory.log");
355 simCtx->runtimeMemoryLogStarted = PETSC_FALSE;
356 simCtx->runtimeMemoryLogHasPrevious = PETSC_FALSE;
358 // --- Group 11: Post-Processing Information ---
359 strcpy(simCtx->PostprocessingControlFile, "config/post.run");
360 ierr = PetscNew(&simCtx->pps); CHKERRQ(ierr);
361
362 // === 2. Get MPI Info and Handle Config File =============================
363 // -- Group 1: Parallelism & MPI Information
364 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &simCtx->rank); CHKERRQ(ierr);
365 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &simCtx->size); CHKERRQ(ierr);
366
367 // First, check if the -control_file argument was provided by the user/script.
368 ierr = PetscOptionsGetString(NULL, NULL, "-control_file", control_filename, sizeof(control_filename), &control_flg); CHKERRQ(ierr);
369
370 // If the flag is NOT present or the filename is empty, abort with a helpful error.
371 if (!control_flg || strlen(control_filename) == 0) {
372 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
373 "\n\n*** MANDATORY ARGUMENT MISSING ***\n"
374 "The -control_file argument was not provided.\n"
375 "This program must be launched with a configuration file.\n"
376 "Example: mpiexec -n 4 ./simulator -control_file /path/to/your/config.control\n"
377 "This is typically handled automatically by the 'picurv' script.\n");
378 }
379
380 // At this point, we have a valid filename. Attempt to load it.
381 LOG(GLOBAL, LOG_INFO, "Loading mandatory configuration from: %s\n", control_filename);
382 ierr = PetscOptionsInsertFile(PETSC_COMM_WORLD, NULL, control_filename, PETSC_FALSE);
383 if (ierr == PETSC_ERR_FILE_OPEN) {
384 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_FILE_OPEN, "The specified control file was not found or could not be opened: %s", control_filename);
385 }
386 CHKERRQ(ierr);
387
388 // === 3. A Configure Logging System ========================================
389 // This logic determines the logging configuration and STORES it in simCtx for
390 // later reference and cleanup.
391 ierr = PetscOptionsGetString(NULL, NULL, "-whitelist_config_file", simCtx->allowedFile, PETSC_MAX_PATH_LEN, &simCtx->useCfg); CHKERRQ(ierr);
392
393 if (simCtx->useCfg) {
394 ierr = LoadAllowedFunctionsFromFile(simCtx->allowedFile, &simCtx->allowedFuncs, &simCtx->nAllowed);
395 if (ierr) {
396 // Use direct PetscPrintf as logging system isn't fully active yet.
397 PetscPrintf(PETSC_COMM_SELF, "[%s] WARNING: Failed to load allowed functions from '%s'. Falling back to default list.\n", __func__, simCtx->allowedFile);
398 simCtx->useCfg = PETSC_FALSE; // Mark as failed.
399 ierr = 0; // Clear the error to allow fallback.
400 } else if (simCtx->nAllowed == 0) {
401 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
402 "Whitelist config file '%s' is empty. Omit -whitelist_config_file to use the default allow-list, or list at least one function.",
403 simCtx->allowedFile);
404 }
405 }
406 if (!simCtx->useCfg) {
407 // Fallback to default logging functions if no file was used or if loading failed.
408 simCtx->nAllowed = 2;
409 ierr = PetscMalloc1(simCtx->nAllowed, &simCtx->allowedFuncs); CHKERRQ(ierr);
410 ierr = PetscStrallocpy("main", &simCtx->allowedFuncs[0]); CHKERRQ(ierr);
411 ierr = PetscStrallocpy("CreateSimulationContext", &simCtx->allowedFuncs[1]); CHKERRQ(ierr);
412 }
413
414 // Activate the configuration by passing it to the logging module's setup function.
415 set_allowed_functions((const char**)simCtx->allowedFuncs, (size_t)simCtx->nAllowed);
416
417 // Now that the logger is configured, we can use it.
418 LOG_ALLOW_SYNC(LOCAL, LOG_INFO, "Context created. Initializing on rank %d of %d.\n", simCtx->rank, simCtx->size);
419 print_log_level(); // This will now correctly reflect the LOG_LEVEL environment variable.
420
421 // === 3.B Configure Profiling System ========================================
422 ierr = PetscOptionsGetString(NULL, NULL, "-profiling_timestep_mode", simCtx->profilingTimestepMode, sizeof(simCtx->profilingTimestepMode), NULL); CHKERRQ(ierr);
423 ierr = PetscOptionsGetString(NULL, NULL, "-profiling_timestep_file", simCtx->profilingTimestepFile, PETSC_MAX_PATH_LEN, NULL); CHKERRQ(ierr);
424 ierr = PetscOptionsGetBool(NULL, NULL, "-profiling_final_summary", &simCtx->profilingFinalSummary, NULL); CHKERRQ(ierr);
425 if (strcmp(simCtx->profilingTimestepMode, "off") != 0 &&
426 strcmp(simCtx->profilingTimestepMode, "selected") != 0 &&
427 strcmp(simCtx->profilingTimestepMode, "all") != 0) {
428 PetscPrintf(PETSC_COMM_SELF, "[%s] WARNING: Unknown profiling timestep mode '%s'. Falling back to 'selected'.\n", __func__, simCtx->profilingTimestepMode);
429 strcpy(simCtx->profilingTimestepMode, "selected");
430 }
431
432 if (strcmp(simCtx->profilingTimestepMode, "selected") == 0) {
433 ierr = PetscOptionsGetString(NULL, NULL, "-profile_config_file", simCtx->profilingSelectedFuncsFile, PETSC_MAX_PATH_LEN, &simCtx->useProfilingSelectedFuncsCfg); CHKERRQ(ierr);
434 if (simCtx->useProfilingSelectedFuncsCfg) {
436 if (ierr) {
437 PetscPrintf(PETSC_COMM_SELF, "[%s] WARNING: Failed to load selected profiling functions from '%s'. Falling back to default list.\n", __func__, simCtx->profilingSelectedFuncsFile);
438 simCtx->useProfilingSelectedFuncsCfg = PETSC_FALSE;
439 ierr = 0;
440 }
441 }
442 if (!simCtx->useProfilingSelectedFuncsCfg) {
443 // Fallback to a hardcoded default list if no file was provided or loading failed.
444 simCtx->nProfilingSelectedFuncs = 4;
445 ierr = PetscMalloc1(simCtx->nProfilingSelectedFuncs, &simCtx->profilingSelectedFuncs); CHKERRQ(ierr);
446 ierr = PetscStrallocpy("FlowSolver", &simCtx->profilingSelectedFuncs[0]); CHKERRQ(ierr);
447 ierr = PetscStrallocpy("AdvanceSimulation", &simCtx->profilingSelectedFuncs[1]); CHKERRQ(ierr);
448 ierr = PetscStrallocpy("LocateAllParticlesInGrid", &simCtx->profilingSelectedFuncs[2]); CHKERRQ(ierr);
449 ierr = PetscStrallocpy("InterpolateAllFieldsToSwarm", &simCtx->profilingSelectedFuncs[3]); CHKERRQ(ierr);
450 }
451 }
452
453 // Initialize the profiling system with the current updated simulation context.
454 ierr = ProfilingInitialize(simCtx); CHKERRQ(ierr);
455
456 // === 4. Parse All Command Line Options ==================================
457 LOG_ALLOW(GLOBAL, LOG_INFO, "Parsing command-line options...\n");
458
459 // --- Group 2
460 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Parsing Group 2: Simulation Control,Time and I/O.\n");
461 // Read the physical time to start from.
462 // The default is already 0.0, so this will only be non-zero if the user provides it.
463 ierr = PetscOptionsGetInt(NULL, NULL, "-start_step", &simCtx->StartStep, NULL); CHKERRQ(ierr);
464 ierr = PetscOptionsGetInt(NULL,NULL, "-totalsteps", &simCtx->StepsToRun, NULL); CHKERRQ(ierr);
465 ierr = PetscOptionsGetBool(NULL, NULL, "-only_setup", &simCtx->OnlySetup, NULL); CHKERRQ(ierr);
466 ierr = PetscOptionsGetBool(NULL, NULL, "-continue_mode", &simCtx->continueMode, NULL); CHKERRQ(ierr);
467 ierr = PetscOptionsGetReal(NULL, NULL, "-dt", &simCtx->dt, NULL); CHKERRQ(ierr);
468 ierr = PetscOptionsGetInt(NULL, NULL, "-tio", &simCtx->tiout, NULL); CHKERRQ(ierr);
469 ierr = PetscOptionsGetInt(NULL, NULL, "-particle_console_output_freq", &simCtx->particleConsoleOutputFreq, &particle_console_output_freq_flg); CHKERRQ(ierr);
470 if (!particle_console_output_freq_flg) {
471 simCtx->particleConsoleOutputFreq = simCtx->tiout;
472 }
473 ierr = PetscOptionsGetString(NULL,NULL,"-euler_field_source",simCtx->eulerianSource,sizeof(simCtx->eulerianSource),NULL);CHKERRQ(ierr);
474 ierr = PetscOptionsGetString(NULL,NULL,"-output_dir",simCtx->output_dir,sizeof(simCtx->output_dir),NULL);CHKERRQ(ierr);
475 ierr = PetscOptionsGetString(NULL,NULL,"-restart_dir",simCtx->restart_dir,sizeof(simCtx->restart_dir),NULL);CHKERRQ(ierr);
476 ierr = PetscOptionsGetString(NULL,NULL,"-log_dir",simCtx->log_dir,sizeof(simCtx->log_dir),NULL);CHKERRQ(ierr);
477 ierr = PetscOptionsGetString(NULL,NULL,"-euler_subdir",simCtx->euler_subdir,sizeof(simCtx->euler_subdir),NULL);CHKERRQ(ierr);
478 ierr = PetscOptionsGetString(NULL,NULL,"-particle_subdir",simCtx->particle_subdir,sizeof(simCtx->particle_subdir),NULL);CHKERRQ(ierr);
479 ierr = PetscOptionsGetBool(NULL, NULL, "-walltime_guard_enabled", &simCtx->walltimeGuardEnabled, NULL); CHKERRQ(ierr);
480 ierr = PetscOptionsGetInt(NULL, NULL, "-walltime_guard_warmup_steps", &simCtx->walltimeGuardWarmupSteps, NULL); CHKERRQ(ierr);
481 ierr = PetscOptionsGetReal(NULL, NULL, "-walltime_guard_multiplier", &simCtx->walltimeGuardMultiplier, NULL); CHKERRQ(ierr);
482 ierr = PetscOptionsGetBool(NULL, NULL, "-runtime_memory_log_enabled", &simCtx->runtimeMemoryLogEnabled, NULL); CHKERRQ(ierr);
483 ierr = PetscOptionsGetString(NULL, NULL, "-runtime_memory_log_file", simCtx->runtimeMemoryLogFile, PETSC_MAX_PATH_LEN, NULL); CHKERRQ(ierr);
484 ierr = PetscOptionsGetReal(NULL, NULL, "-walltime_guard_min_seconds", &simCtx->walltimeGuardMinSeconds, NULL); CHKERRQ(ierr);
485 ierr = PetscOptionsGetReal(NULL, NULL, "-walltime_guard_estimator_alpha", &simCtx->walltimeGuardEstimatorAlpha, NULL); CHKERRQ(ierr);
486
487 if (simCtx->walltimeGuardWarmupSteps <= 0) {
488 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Invalid value for -walltime_guard_warmup_steps: %d. Must be > 0.", simCtx->walltimeGuardWarmupSteps);
489 }
490 if (simCtx->walltimeGuardMultiplier <= 0.0 || simCtx->walltimeGuardMultiplier > 5.0) {
491 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Invalid value for -walltime_guard_multiplier: %.6f. Must be in (0, 5].", (double)simCtx->walltimeGuardMultiplier);
492 }
493 if (simCtx->walltimeGuardMinSeconds <= 0.0) {
494 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Invalid value for -walltime_guard_min_seconds: %.6f. Must be > 0.", (double)simCtx->walltimeGuardMinSeconds);
495 }
496 if (simCtx->walltimeGuardEstimatorAlpha <= 0.0 || simCtx->walltimeGuardEstimatorAlpha > 1.0) {
497 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Invalid value for -walltime_guard_estimator_alpha: %.6f. Must be in (0, 1].", (double)simCtx->walltimeGuardEstimatorAlpha);
498 }
499
500 if(strcmp(simCtx->eulerianSource,"solve")!= 0 && strcmp(simCtx->eulerianSource,"load") != 0 && strcmp(simCtx->eulerianSource,"analytical")!=0){
501 SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_ARG_WRONG,"Invalid value for -euler_field_source. Must be 'load','analytical' or 'solve'. You provided '%s'.",simCtx->eulerianSource);
502 }
503 if (simCtx->walltimeGuardEnabled) {
504 const char *job_start_env = getenv("PICURV_JOB_START_EPOCH");
505 const char *limit_env = getenv("PICURV_WALLTIME_LIMIT_SECONDS");
506 PetscBool job_start_ok = RuntimeWalltimeGuardParsePositiveSeconds(job_start_env, &simCtx->walltimeGuardJobStartEpochSeconds);
507 PetscBool limit_ok = RuntimeWalltimeGuardParsePositiveSeconds(limit_env, &simCtx->walltimeGuardLimitSeconds);
508
509 if (!job_start_ok || !limit_ok) {
510 simCtx->walltimeGuardActive = PETSC_FALSE;
512 simCtx->walltimeGuardLimitSeconds = 0.0;
513 LOG_ALLOW(
514 GLOBAL,
516 "Runtime walltime guard enabled but %s/%s are missing or invalid. Falling back to external shutdown signals only.\n",
517 "PICURV_JOB_START_EPOCH",
518 "PICURV_WALLTIME_LIMIT_SECONDS"
519 );
520 } else {
521 simCtx->walltimeGuardActive = PETSC_TRUE;
522 }
523 }
524
525 // --- Group 3
526 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Parsing Group 3: High-Level Physics & Model Selection Flags\n");
527 ierr = PetscOptionsGetInt(NULL, NULL, "-imm", &simCtx->immersed, NULL); CHKERRQ(ierr);
528 ierr = PetscOptionsGetInt(NULL, NULL, "-fsi", &simCtx->movefsi, NULL); CHKERRQ(ierr);
529 ierr = PetscOptionsGetInt(NULL, NULL, "-rfsi", &simCtx->rotatefsi, NULL); CHKERRQ(ierr);
530 ierr = PetscOptionsGetInt(NULL, NULL, "-sediment", &simCtx->sediment, NULL); CHKERRQ(ierr);
531 ierr = PetscOptionsGetInt(NULL, NULL, "-rheology", &simCtx->rheology, NULL); CHKERRQ(ierr);
532 ierr = PetscOptionsGetInt(NULL, NULL, "-inv", &simCtx->invicid, NULL); CHKERRQ(ierr);
533 ierr = PetscOptionsGetInt(NULL, NULL, "-TwoD", &simCtx->TwoD, NULL); CHKERRQ(ierr);
534 ierr = PetscOptionsGetInt(NULL, NULL, "-thin", &simCtx->thin, NULL); CHKERRQ(ierr);
535 ierr = PetscOptionsGetInt(NULL, NULL, "-mframe", &simCtx->moveframe, NULL); CHKERRQ(ierr);
536 ierr = PetscOptionsGetInt(NULL, NULL, "-rframe", &simCtx->rotateframe, NULL); CHKERRQ(ierr);
537 ierr = PetscOptionsGetInt(NULL, NULL, "-blk", &simCtx->blank, NULL); CHKERRQ(ierr);
538 ierr = PetscOptionsGetInt(NULL, NULL, "-dgf_z", &simCtx->dgf_z, NULL); CHKERRQ(ierr);
539 ierr = PetscOptionsGetInt(NULL, NULL, "-dgf_y", &simCtx->dgf_y, NULL); CHKERRQ(ierr);
540 ierr = PetscOptionsGetInt(NULL, NULL, "-dgf_x", &simCtx->dgf_x, NULL); CHKERRQ(ierr);
541 ierr = PetscOptionsGetInt(NULL, NULL, "-dgf_az", &simCtx->dgf_az, NULL); CHKERRQ(ierr);
542 ierr = PetscOptionsGetInt(NULL, NULL, "-dgf_ay", &simCtx->dgf_ay, NULL); CHKERRQ(ierr);
543 ierr = PetscOptionsGetInt(NULL, NULL, "-dgf_ax", &simCtx->dgf_ax, NULL); CHKERRQ(ierr);
544 ierr = PetscOptionsGetString(NULL,NULL,"-analytical_type",simCtx->AnalyticalSolutionType,sizeof(simCtx->AnalyticalSolutionType),NULL);CHKERRQ(ierr);
545
546 // --- Group 4
547 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Parsing Group 4: Specific Simulation Case Flags \n");
548 ierr = PetscOptionsGetInt(NULL, NULL, "-cop", &simCtx->cop, NULL); CHKERRQ(ierr);
549 ierr = PetscOptionsGetInt(NULL, NULL, "-fish", &simCtx->fish, NULL); CHKERRQ(ierr);
550 ierr = PetscOptionsGetInt(NULL, NULL, "-pizza", &simCtx->pizza, NULL); CHKERRQ(ierr);
551 ierr = PetscOptionsGetInt(NULL, NULL, "-turbine", &simCtx->turbine, NULL); CHKERRQ(ierr);
552 ierr = PetscOptionsGetInt(NULL, NULL, "-fishcyl", &simCtx->fishcyl, NULL); CHKERRQ(ierr);
553 ierr = PetscOptionsGetInt(NULL, NULL, "-eel", &simCtx->eel, NULL); CHKERRQ(ierr);
554 ierr = PetscOptionsGetInt(NULL, NULL, "-cstart", &simCtx->fish_c, NULL); CHKERRQ(ierr);
555 ierr = PetscOptionsGetInt(NULL, NULL, "-wing", &simCtx->wing, NULL); CHKERRQ(ierr);
556 ierr = PetscOptionsGetInt(NULL, NULL, "-mhv", &simCtx->MHV, NULL); CHKERRQ(ierr);
557 ierr = PetscOptionsGetInt(NULL, NULL, "-hydro", &simCtx->hydro, NULL); CHKERRQ(ierr);
558 ierr = PetscOptionsGetInt(NULL, NULL, "-lv", &simCtx->LV, NULL); CHKERRQ(ierr);
559 ierr = PetscOptionsGetInt(NULL, NULL, "-Pipe", &simCtx->Pipe, NULL); CHKERRQ(ierr);
560 ierr = PetscOptionsGetInt(NULL, NULL, "-Turbulent_Channel_z", &simCtx->channelz, NULL); CHKERRQ(ierr);
561 ierr = PetscOptionsGetReal(NULL,NULL,"-driven_flow_initial_force",&simCtx->drivingForceMagnitude,NULL);CHKERRQ(ierr);
562 ierr = PetscOptionsGetReal(NULL,NULL,"-driven_flow_scaling_factor",&simCtx->forceScalingFactor,NULL);CHKERRQ(ierr);
563 // --- Group 5
564 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Parsing Group 5: Solver & Numerics Parameters \n");
565 char mom_solver_type_char[PETSC_MAX_PATH_LEN];
566 char solution_convergence_mode_char[PETSC_MAX_PATH_LEN];
567 PetscBool mom_solver_type_flg = PETSC_FALSE;
568 PetscBool solution_convergence_mode_flg = PETSC_FALSE;
569 ierr = PetscOptionsGetString(NULL, NULL, "-mom_solver_type", mom_solver_type_char, sizeof(mom_solver_type_char), &mom_solver_type_flg); CHKERRQ(ierr);
570 ierr = PetscOptionsGetInt(NULL, NULL, "-mom_max_pseudo_steps", &simCtx->mom_max_pseudo_steps, NULL); CHKERRQ(ierr);
571 ierr = PetscOptionsGetReal(NULL, NULL, "-mom_atol", &simCtx->mom_atol, NULL); CHKERRQ(ierr);
572 ierr = PetscOptionsGetReal(NULL, NULL, "-mom_rtol", &simCtx->mom_rtol, NULL); CHKERRQ(ierr);
573 ierr = PetscOptionsGetReal(NULL, NULL, "-mom_resid_atol", &simCtx->mom_resid_atol, NULL); CHKERRQ(ierr);
574 ierr = PetscOptionsGetReal(NULL, NULL, "-mom_resid_rtol", &simCtx->mom_resid_rtol, NULL); CHKERRQ(ierr);
575 ierr = PetscOptionsGetReal(NULL, NULL, "-imp_stol", &simCtx->imp_stol, NULL); CHKERRQ(ierr);
576 ierr = PetscOptionsGetInt(NULL, NULL, "-central", &simCtx->central, NULL); CHKERRQ(ierr);
577 ierr = PetscOptionsGetString(NULL, NULL, "-solution_convergence_mode",
578 solution_convergence_mode_char, sizeof(solution_convergence_mode_char),
579 &solution_convergence_mode_flg); CHKERRQ(ierr);
580 ierr = PetscOptionsGetInt(NULL, NULL, "-solution_convergence_period_steps", &simCtx->solutionConvergencePeriodSteps, NULL); CHKERRQ(ierr);
581 ierr = PetscOptionsGetInt(NULL, NULL, "-solution_convergence_window_steps", &simCtx->solutionConvergenceWindowSteps, NULL); CHKERRQ(ierr);
582
583 // Keep parser acceptance aligned with the enum and FlowSolver dispatch.
584 if (mom_solver_type_flg) {
585 if(strcmp(mom_solver_type_char, "DUALTIME_PICARD_JAMESON_RK") == 0 ||
586 strcmp(mom_solver_type_char, "DUALTIME_PICARD_RK4") == 0) {
588 } else if (strcmp(mom_solver_type_char, "EXPLICIT_RK") == 0) {
590 } else if (strcmp(mom_solver_type_char, "newton_krylov") == 0) {
592 } else {
593 LOG(GLOBAL, LOG_ERROR, "Invalid value for -mom_solver_type: '%s'. Valid options are: 'DUALTIME_PICARD_JAMESON_RK', 'EXPLICIT_RK', 'newton_krylov'.\n", mom_solver_type_char);
594 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Invalid value for -mom_solver_type: '%s'.", mom_solver_type_char);
595 }
596 }
597
598 if (solution_convergence_mode_flg) {
599 if (strcmp(solution_convergence_mode_char, "STEADY_DETERMINISTIC") == 0) {
601 } else if (strcmp(solution_convergence_mode_char, "PERIODIC_DETERMINISTIC") == 0) {
603 } else if (strcmp(solution_convergence_mode_char, "STATISTICAL_STEADY") == 0) {
605 } else if (strcmp(solution_convergence_mode_char, "TRANSIENT") == 0) {
607 } else {
608 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
609 "Invalid value for -solution_convergence_mode: '%s'.", solution_convergence_mode_char);
610 }
611 }
612
614 simCtx->solutionConvergencePeriodSteps <= 0) {
615 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
616 "solution convergence mode PERIODIC_DETERMINISTIC requires -solution_convergence_period_steps > 0.");
617 }
619 simCtx->solutionConvergenceWindowSteps <= 0) {
620 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
621 "solution convergence mode STATISTICAL_STEADY requires -solution_convergence_window_steps > 0.");
622 }
623
624 // --- Multigrid Options ---
625 ierr = PetscOptionsGetInt(NULL, NULL, "-mg_level", &simCtx->mglevels, NULL); CHKERRQ(ierr);
626 ierr = PetscOptionsGetInt(NULL, NULL, "-mg_max_it", &simCtx->mg_MAX_IT, NULL); CHKERRQ(ierr);
627 ierr = PetscOptionsGetInt(NULL, NULL, "-mg_idx", &simCtx->mg_idx, NULL); CHKERRQ(ierr);
628 ierr = PetscOptionsGetInt(NULL, NULL, "-mg_pre_it", &simCtx->mg_preItr, NULL); CHKERRQ(ierr);
629 ierr = PetscOptionsGetInt(NULL, NULL, "-mg_post_it", &simCtx->mg_poItr, NULL); CHKERRQ(ierr);
630
631 // --- Other Solver Options ---
632 ierr = PetscOptionsGetInt(NULL, NULL, "-poisson", &simCtx->poisson, NULL); CHKERRQ(ierr);
633 ierr = PetscOptionsGetReal(NULL, NULL, "-poisson_tol", &simCtx->poisson_tol, NULL); CHKERRQ(ierr);
634 ierr = PetscOptionsGetInt(NULL, NULL, "-str", &simCtx->STRONG_COUPLING, NULL); CHKERRQ(ierr);
635 ierr = PetscOptionsGetReal(NULL, NULL, "-ren", &simCtx->ren, NULL); CHKERRQ(ierr);
636 ierr = PetscOptionsGetReal(NULL, NULL, "-pseudo_cfl", &simCtx->pseudo_cfl, NULL); CHKERRQ(ierr);
637 ierr = PetscOptionsGetReal(NULL, NULL, "-max_pseudo_cfl", &simCtx->max_pseudo_cfl, NULL); CHKERRQ(ierr);
638 ierr = PetscOptionsGetReal(NULL, NULL, "-min_pseudo_cfl", &simCtx->min_pseudo_cfl, NULL); CHKERRQ(ierr);
639 ierr = PetscOptionsGetReal(NULL, NULL, "-pseudo_cfl_reduction_factor", &simCtx->pseudo_cfl_reduction_factor, NULL); CHKERRQ(ierr);
640 ierr = PetscOptionsGetReal(NULL, NULL, "-pseudo_cfl_growth_factor", &simCtx->pseudo_cfl_growth_factor, NULL); CHKERRQ(ierr);
641 // Read the deprecated RK4 spelling first so the canonical Jameson option wins if both are present.
642 ierr = PetscOptionsGetReal(NULL,NULL, "-mom_dt_rk4_residual_norm_noise_allowance_factor",&simCtx->mom_dt_jameson_residual_norm_noise_allowance_factor,NULL);CHKERRQ(ierr);
643 ierr = PetscOptionsGetReal(NULL,NULL, "-mom_dt_jameson_residual_norm_noise_allowance_factor",&simCtx->mom_dt_jameson_residual_norm_noise_allowance_factor,NULL);CHKERRQ(ierr);
644 ierr = PetscOptionsGetBool(NULL, NULL, "-no_pseudo_cfl_backtrack", &simCtx->no_pseudo_cfl_backtrack, NULL); CHKERRQ(ierr);
645 ierr = PetscOptionsGetReal(NULL, NULL, "-mom_ratio_ema_alpha", &simCtx->mom_ratio_ema_alpha, NULL); CHKERRQ(ierr);
646 if (simCtx->min_pseudo_cfl <= 0.0 ||
647 simCtx->pseudo_cfl < simCtx->min_pseudo_cfl ||
648 simCtx->pseudo_cfl > simCtx->max_pseudo_cfl) {
649 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE,
650 "Pseudo-CFL controls require 0 < minimum <= initial <= maximum.");
651 }
652 if (simCtx->pseudo_cfl_growth_factor < 1.0 ||
653 simCtx->pseudo_cfl_reduction_factor <= 0.0 ||
654 simCtx->pseudo_cfl_reduction_factor >= 1.0 ||
656 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE,
657 "Pseudo-CFL controls require growth_factor >= 1, 0 < reduction_factor < 1, and noise allowance >= 1.");
658 }
659 if (simCtx->mom_ratio_ema_alpha < 0.0 || simCtx->mom_ratio_ema_alpha > 1.0) {
660 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE,
661 "-mom_ratio_ema_alpha must be in [0, 1].");
662 }
663 ierr = PetscOptionsHasName(NULL, NULL, "-ps_ksp_pic_monitor_true_residual", &simCtx->ps_ksp_pic_monitor_true_residual); CHKERRQ(ierr);
664 ierr = PetscOptionsGetBool(NULL, NULL, "-mom_nk_pic_monitor", &simCtx->mom_nk_monitor_history, NULL); CHKERRQ(ierr);
665 {
666 PetscInt ic_mode = (PetscInt)simCtx->initialConditionMode;
667 PetscInt ic_field = (PetscInt)simCtx->initialConditionField;
668 ierr = PetscOptionsGetInt(NULL, NULL, "-finit", &ic_mode, NULL); CHKERRQ(ierr);
669 ierr = PetscOptionsGetInt(NULL, NULL, "-ic_field", &ic_field, NULL); CHKERRQ(ierr);
672 }
673 ierr = PetscOptionsGetString(NULL, NULL, "-ic_dir", simCtx->initialConditionDirectory,
674 sizeof(simCtx->initialConditionDirectory), NULL); CHKERRQ(ierr);
676 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE,
677 "Invalid value for -finit. Expected an initial-condition mode in [0,4], got %d.",
678 simCtx->initialConditionMode);
679 }
681 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE,
682 "Invalid value for -ic_field. Expected 0 (Ucat) or 1 (Ucont), got %d.",
683 simCtx->initialConditionField);
684 }
685 ierr = PetscOptionsGetReal(NULL, NULL, "-ucont_x", &simCtx->InitialConstantContra.x, NULL); CHKERRQ(ierr);
686 ierr = PetscOptionsGetReal(NULL, NULL, "-ucont_y", &simCtx->InitialConstantContra.y, NULL); CHKERRQ(ierr);
687 ierr = PetscOptionsGetReal(NULL, NULL, "-ucont_z", &simCtx->InitialConstantContra.z, NULL); CHKERRQ(ierr);
688 {
689 PetscInt fd_int = (PetscInt)FLOW_DIR_UNSET;
690 PetscBool fd_set = PETSC_FALSE;
691 ierr = PetscOptionsGetInt(NULL, NULL, "-flow_direction", &fd_int, &fd_set); CHKERRQ(ierr);
692 if (fd_set) simCtx->flowDirection = (FlowDirection)fd_int;
693 }
694 ierr = PetscOptionsGetReal(NULL, NULL, "-ic_velocity_physical", &simCtx->icVelocityPhysical, NULL); CHKERRQ(ierr);
695 ierr = PetscOptionsGetReal(NULL, NULL, "-analytical_uniform_u", &simCtx->AnalyticalUniformVelocity.x, NULL); CHKERRQ(ierr);
696 ierr = PetscOptionsGetReal(NULL, NULL, "-analytical_uniform_v", &simCtx->AnalyticalUniformVelocity.y, NULL); CHKERRQ(ierr);
697 ierr = PetscOptionsGetReal(NULL, NULL, "-analytical_uniform_w", &simCtx->AnalyticalUniformVelocity.z, NULL); CHKERRQ(ierr);
698 PetscBool verification_scalar_value_set = PETSC_FALSE;
699 PetscBool verification_scalar_phi0_set = PETSC_FALSE;
700 PetscBool verification_scalar_slope_x_set = PETSC_FALSE;
701 PetscBool verification_scalar_amplitude_set = PETSC_FALSE;
702 PetscBool verification_scalar_kx_set = PETSC_FALSE;
703 PetscBool verification_scalar_ky_set = PETSC_FALSE;
704 PetscBool verification_scalar_kz_set = PETSC_FALSE;
705 ierr = PetscOptionsGetString(NULL, NULL, "-verification_diffusivity_mode",
707 sizeof(simCtx->verificationDiffusivity.mode), NULL); CHKERRQ(ierr);
708 ierr = PetscOptionsGetString(NULL, NULL, "-verification_diffusivity_profile",
710 sizeof(simCtx->verificationDiffusivity.profile), NULL); CHKERRQ(ierr);
711 ierr = PetscOptionsGetReal(NULL, NULL, "-verification_diffusivity_gamma0",
712 &simCtx->verificationDiffusivity.gamma0, NULL); CHKERRQ(ierr);
713 ierr = PetscOptionsGetReal(NULL, NULL, "-verification_diffusivity_slope_x",
714 &simCtx->verificationDiffusivity.slope_x, NULL); CHKERRQ(ierr);
715 ierr = PetscOptionsGetString(NULL, NULL, "-verification_scalar_mode",
716 simCtx->verificationScalar.mode,
717 sizeof(simCtx->verificationScalar.mode), NULL); CHKERRQ(ierr);
718 ierr = PetscOptionsGetString(NULL, NULL, "-verification_scalar_profile",
720 sizeof(simCtx->verificationScalar.profile), NULL); CHKERRQ(ierr);
721 ierr = PetscOptionsGetReal(NULL, NULL, "-verification_scalar_value",
722 &simCtx->verificationScalar.value, &verification_scalar_value_set); CHKERRQ(ierr);
723 ierr = PetscOptionsGetReal(NULL, NULL, "-verification_scalar_phi0",
724 &simCtx->verificationScalar.phi0, &verification_scalar_phi0_set); CHKERRQ(ierr);
725 ierr = PetscOptionsGetReal(NULL, NULL, "-verification_scalar_slope_x",
726 &simCtx->verificationScalar.slope_x, &verification_scalar_slope_x_set); CHKERRQ(ierr);
727 ierr = PetscOptionsGetReal(NULL, NULL, "-verification_scalar_amplitude",
728 &simCtx->verificationScalar.amplitude, &verification_scalar_amplitude_set); CHKERRQ(ierr);
729 ierr = PetscOptionsGetReal(NULL, NULL, "-verification_scalar_kx",
730 &simCtx->verificationScalar.kx, &verification_scalar_kx_set); CHKERRQ(ierr);
731 ierr = PetscOptionsGetReal(NULL, NULL, "-verification_scalar_ky",
732 &simCtx->verificationScalar.ky, &verification_scalar_ky_set); CHKERRQ(ierr);
733 ierr = PetscOptionsGetReal(NULL, NULL, "-verification_scalar_kz",
734 &simCtx->verificationScalar.kz, &verification_scalar_kz_set); CHKERRQ(ierr);
736 (PetscBool)(simCtx->verificationDiffusivity.mode[0] != '\0' ||
737 simCtx->verificationDiffusivity.profile[0] != '\0');
739 (PetscBool)(simCtx->verificationScalar.mode[0] != '\0' ||
740 simCtx->verificationScalar.profile[0] != '\0');
741 if (simCtx->verificationDiffusivity.enabled) {
742 if (strcmp(simCtx->eulerianSource, "analytical") != 0) {
743 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
744 "verification diffusivity overrides require -euler_field_source \"analytical\".");
745 }
746 if (strcmp(simCtx->verificationDiffusivity.mode, "analytical") != 0) {
747 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
748 "Unsupported -verification_diffusivity_mode '%s'. Only 'analytical' is supported.",
750 }
751 if (strcmp(simCtx->verificationDiffusivity.profile, "LINEAR_X") != 0) {
752 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
753 "Unsupported -verification_diffusivity_profile '%s'. Only 'LINEAR_X' is supported.",
755 }
756 }
757 if (simCtx->verificationScalar.enabled) {
758 if (strcmp(simCtx->eulerianSource, "analytical") != 0) {
759 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
760 "verification scalar overrides require -euler_field_source \"analytical\".");
761 }
762 if (strcmp(simCtx->verificationScalar.mode, "analytical") != 0) {
763 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
764 "Unsupported -verification_scalar_mode '%s'. Only 'analytical' is supported.",
765 simCtx->verificationScalar.mode);
766 }
767 if (strcmp(simCtx->verificationScalar.profile, "CONSTANT") == 0) {
768 if (!verification_scalar_value_set) {
769 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
770 "verification scalar profile CONSTANT requires -verification_scalar_value.");
771 }
772 } else if (strcmp(simCtx->verificationScalar.profile, "LINEAR_X") == 0) {
773 if (!verification_scalar_phi0_set || !verification_scalar_slope_x_set) {
774 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
775 "verification scalar profile LINEAR_X requires -verification_scalar_phi0 and -verification_scalar_slope_x.");
776 }
777 } else if (strcmp(simCtx->verificationScalar.profile, "SIN_PRODUCT") == 0) {
778 if (!verification_scalar_amplitude_set || !verification_scalar_kx_set ||
779 !verification_scalar_ky_set || !verification_scalar_kz_set) {
780 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
781 "verification scalar profile SIN_PRODUCT requires -verification_scalar_amplitude, -verification_scalar_kx, -verification_scalar_ky, and -verification_scalar_kz.");
782 }
783 } else {
784 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
785 "Unsupported -verification_scalar_profile '%s'. Supported profiles: CONSTANT, LINEAR_X, SIN_PRODUCT.",
787 }
788 }
789 // NOTE: cdisx,cdisy,cdisz haven't been parsed, add if necessary.
790
791 // --- Group 6
792 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Parsing Group 6: Physical & Geometric Parameters \n");
793 ierr = PetscOptionsGetReal(NULL,NULL,"-schmidt_number",&simCtx->schmidt_number,NULL);CHKERRQ(ierr);
794 ierr = PetscOptionsGetReal(NULL,NULL,"-turb_schmidt_number",&simCtx->Turbulent_schmidt_number,NULL);CHKERRQ(ierr);
795 ierr = PetscOptionsGetInt(NULL, NULL, "-no_of_bodies", &simCtx->NumberOfBodies, NULL); CHKERRQ(ierr);
796 ierr = PetscOptionsGetReal(NULL,NULL,"-wall_roughness",&simCtx->wall_roughness_height,NULL);CHKERRQ(ierr);
797 // NOTE: angle is not parsed in the original code, it set programmatically. We will follow that.
798 // NOTE: max_angle is calculated based on other flags (like MHV) in the legacy code.
799 // We will defer that logic to a later setup stage and not parse them directly.
800 // The Scaling Information is calculated here
801 ierr = ParseScalingInformation(simCtx); CHKERRQ(ierr);
802
803 // --- Group 7
804 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Parsing Group 7: Grid, Domain, and Boundary Condition Settings \n");
805 ierr = PetscOptionsGetInt(NULL, NULL, "-nblk", &simCtx->block_number, NULL); CHKERRQ(ierr); // This is also a modern option
806 ierr = PetscOptionsGetInt(NULL, NULL, "-inlet", &simCtx->inletprofile, NULL); CHKERRQ(ierr);
807 ierr = PetscOptionsGetInt(NULL, NULL, "-Ogrid", &simCtx->Ogrid, NULL); CHKERRQ(ierr);
808 // NOTE: channelz was not parsed, likely set programmatically. We will omit its parsing call.
809 ierr = PetscOptionsGetInt(NULL, NULL, "-grid1d", &simCtx->grid1d, NULL); CHKERRQ(ierr);
810 ierr = PetscOptionsGetBool(NULL, NULL, "-grid", &simCtx->generate_grid, NULL); CHKERRQ(ierr);
811 ierr = PetscOptionsGetString(NULL, NULL, "-grid_file", simCtx->grid_file, PETSC_MAX_PATH_LEN, NULL); CHKERRQ(ierr);
812 ierr = PetscOptionsGetInt(NULL, NULL, "-da_processors_x", &simCtx->da_procs_x, NULL); CHKERRQ(ierr);
813 ierr = PetscOptionsGetInt(NULL, NULL, "-da_processors_y", &simCtx->da_procs_y, NULL); CHKERRQ(ierr);
814 ierr = PetscOptionsGetInt(NULL, NULL, "-da_processors_z", &simCtx->da_procs_z, NULL); CHKERRQ(ierr);
815 ierr = PetscOptionsGetInt(NULL, NULL, "-pbc_domain", &simCtx->blkpbc, NULL); CHKERRQ(ierr);
816 // NOTE: pseudo_periodic was not parsed. We will omit its parsing call.
817 ierr = PetscOptionsGetReal(NULL, NULL, "-grid_rotation_angle", &simCtx->grid_rotation_angle, NULL); CHKERRQ(ierr);
818 ierr = PetscOptionsGetReal(NULL, NULL, "-Croty", &simCtx->Croty, NULL); CHKERRQ(ierr);
819 ierr = PetscOptionsGetReal(NULL, NULL, "-Crotz", &simCtx->Crotz, NULL); CHKERRQ(ierr);
820 PetscBool bcs_flg;
821 char file_list_str[PETSC_MAX_PATH_LEN * 10]; // Buffer for comma-separated list
822
823 ierr = PetscOptionsGetString(NULL, NULL, "-bcs_files", file_list_str, sizeof(file_list_str), &bcs_flg); CHKERRQ(ierr);
824 ierr = PetscOptionsGetReal(NULL, NULL, "-U_bc", &simCtx->U_bc, NULL); CHKERRQ(ierr);
825
826 if (bcs_flg) {
827 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Found -bcs_files option, overriding default.\n");
828
829 // A. Clean up the default memory we allocated in Phase 1.
830 ierr = PetscFree(simCtx->bcs_files[0]); CHKERRQ(ierr);
831 ierr = PetscFree(simCtx->bcs_files); CHKERRQ(ierr);
832 simCtx->num_bcs_files = 0;
833 simCtx->bcs_files = NULL;
834
835 // B. Parse the user-provided comma-separated list.
836 char *token;
837 char *str_copy;
838 ierr = PetscStrallocpy(file_list_str, &str_copy); CHKERRQ(ierr);
839
840 // First pass: count the number of files.
841 token = strtok(str_copy, ",");
842 while (token) {
843 simCtx->num_bcs_files++;
844 token = strtok(NULL, ",");
845 }
846 ierr = PetscFree(str_copy); CHKERRQ(ierr);
847
848 // Second pass: allocate memory and store the filenames.
849 ierr = PetscMalloc1(simCtx->num_bcs_files, &simCtx->bcs_files); CHKERRQ(ierr);
850 ierr = PetscStrallocpy(file_list_str, &str_copy); CHKERRQ(ierr);
851 token = strtok(str_copy, ",");
852 for (PetscInt i = 0; i < simCtx->num_bcs_files; i++) {
853 ierr = PetscStrallocpy(token, &simCtx->bcs_files[i]); CHKERRQ(ierr);
854 token = strtok(NULL, ",");
855 }
856 ierr = PetscFree(str_copy); CHKERRQ(ierr);
857 }
858
859
860 // --- Group 8
861 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Parsing Group 8: Turbulence Modeling (LES/RANS) \n");
862 PetscInt temp_les_model;
863 ierr = PetscOptionsGetInt(NULL, NULL, "-les", &temp_les_model, NULL); CHKERRQ(ierr);
864 simCtx->les = (LESModelType)temp_les_model;
865 ierr = PetscOptionsGetInt(NULL, NULL, "-rans", &simCtx->rans, NULL); CHKERRQ(ierr);
866 ierr = PetscOptionsGetInt(NULL, NULL, "-wallfunction", &simCtx->wallfunction, NULL); CHKERRQ(ierr);
867 ierr = PetscOptionsGetInt(NULL, NULL, "-mixed", &simCtx->mixed, NULL); CHKERRQ(ierr);
868 ierr = PetscOptionsGetInt(NULL, NULL, "-clark", &simCtx->clark, NULL); CHKERRQ(ierr);
869 ierr = PetscOptionsGetInt(NULL, NULL, "-dynamic_freq", &simCtx->dynamic_freq, NULL); CHKERRQ(ierr);
870 ierr = PetscOptionsGetReal(NULL, NULL, "-max_cs", &simCtx->max_cs, NULL); CHKERRQ(ierr);
871 ierr = PetscOptionsGetReal(NULL, NULL, "-const_cs", &simCtx->Const_CS, NULL); CHKERRQ(ierr);
872 ierr = PetscOptionsGetInt(NULL, NULL, "-testfilter_ik", &simCtx->testfilter_ik, NULL); CHKERRQ(ierr);
873 ierr = PetscOptionsGetInt(NULL, NULL, "-testfilter_1d", &simCtx->testfilter_1d, NULL); CHKERRQ(ierr);
874 ierr = PetscOptionsGetInt(NULL, NULL, "-i_homo_filter", &simCtx->i_homo_filter, NULL); CHKERRQ(ierr);
875 ierr = PetscOptionsGetInt(NULL, NULL, "-j_homo_filter", &simCtx->j_homo_filter, NULL); CHKERRQ(ierr);
876 ierr = PetscOptionsGetInt(NULL, NULL, "-k_homo_filter", &simCtx->k_homo_filter, NULL); CHKERRQ(ierr);
877 ierr = PetscOptionsGetBool(NULL, NULL, "-averaging", &simCtx->averaging, NULL); CHKERRQ(ierr);
878
879 // --- Group 9
880 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Parsing Group 9: Particle / DMSwarm Data & Settings \n");
881 ierr = PetscOptionsGetInt(NULL, NULL, "-numParticles", &simCtx->np, NULL); CHKERRQ(ierr);
882 ierr = PetscOptionsGetBool(NULL, NULL, "-read_fields", &simCtx->readFields, NULL); CHKERRQ(ierr);
883 PetscInt temp_pinit = (PetscInt)PARTICLE_INIT_SURFACE_RANDOM;
884 ierr = PetscOptionsGetInt(NULL, NULL, "-pinit", &temp_pinit, NULL); CHKERRQ(ierr);
886 PetscInt temp_interp = (PetscInt)INTERP_TRILINEAR;
887 ierr = PetscOptionsGetInt(NULL, NULL, "-interpolation_method", &temp_interp, NULL); CHKERRQ(ierr);
888 simCtx->interpolationMethod = (InterpolationMethod)temp_interp;
889 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Interpolation method: %s\n",
890 simCtx->interpolationMethod == INTERP_TRILINEAR ? "Trilinear (direct cell-center)" : "CornerAveraged (legacy)");
891 ierr = PetscOptionsGetReal(NULL, NULL, "-psrc_x", &simCtx->psrc_x, NULL); CHKERRQ(ierr);
892 ierr = PetscOptionsGetReal(NULL, NULL, "-psrc_y", &simCtx->psrc_y, NULL); CHKERRQ(ierr);
893 ierr = PetscOptionsGetReal(NULL, NULL, "-psrc_z", &simCtx->psrc_z, NULL); CHKERRQ(ierr);
894 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Particle initialization mode: %s. Point source: (%.6f, %.6f, %.6f)\n",
896 simCtx->psrc_x, simCtx->psrc_y, simCtx->psrc_z);
897 ierr = PetscOptionsGetString(NULL,NULL,"-particle_restart_mode",simCtx->particleRestartMode,sizeof(simCtx->particleRestartMode),NULL); CHKERRQ(ierr);
898 // Validation for Particle Restart Mode
899 if (strcmp(simCtx->particleRestartMode, "load") != 0 && strcmp(simCtx->particleRestartMode, "init") != 0) {
900 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Invalid value for -particle_restart_mode. Must be 'load' or 'init'. You provided '%s'.", simCtx->particleRestartMode);
901 }
902 ierr = InitializeBrownianRNG(simCtx); CHKERRQ(ierr);
903 // --- Group 10
904 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Parsing Group 10: Immersed Boundary & FSI Data Object Pointers \n");
905 ierr = PetscOptionsGetBool(NULL, NULL, "-rs_fsi", &simCtx->rstart_fsi, NULL); CHKERRQ(ierr);
906 ierr = PetscOptionsGetInt(NULL, NULL, "-duplicate", &simCtx->duplicate, NULL); CHKERRQ(ierr);
907
908 // --- Group 11
909 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Parsing Group 11: Top-Level Managers & Custom Configuration \n");
910 ierr = PetscOptionsGetInt(NULL, NULL, "-logfreq", &simCtx->LoggingFrequency, NULL); CHKERRQ(ierr);
911
912 if (simCtx->num_bcs_files != simCtx->block_number) {
913 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_INCOMP, "Number of BC files (%d) does not match number of blocks (%d). Use -bcs_files \"file1.dat,file2.dat,...\".", simCtx->num_bcs_files, simCtx->block_number);
914 }
915
916 // --- Group 12
917 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Parsing Group 12: Post-Processing Information.\n");
918 // This logic determines the Post Processing configuration and STORES it in simCtx for later reference and cleanup.
919 ierr = PetscOptionsGetString(NULL,NULL,"-postprocessing_config_file",simCtx->PostprocessingControlFile,PETSC_MAX_PATH_LEN,NULL); CHKERRQ(ierr);
920 /* Parse post settings for both solver and post-processor binaries using the single pre-allocated pps object. */
921 ierr = ParsePostProcessingSettings(simCtx);
922
923 // === 5. Dependent Parameter Calculations ================================
924 // Some parameters depend on others, so we calculate them here.
925 simCtx->StartTime = (PetscReal)simCtx->StartStep*simCtx->dt;
926 simCtx->ti = simCtx->StartTime;
927 simCtx->step = simCtx->StartStep;
928
929 // === 5. Log Summary and Finalize Setup ==================================
930 LOG_ALLOW(GLOBAL, LOG_DEBUG, "-- Console Output Functions [Total : %d] : --\n", simCtx->nAllowed);
931 for (PetscInt i = 0; i < simCtx->nAllowed; ++i) {
932 LOG_ALLOW(GLOBAL, LOG_DEBUG, " [%2d] «%s»\n", i, simCtx->allowedFuncs[i]);
933 }
934
935 LOG_ALLOW(GLOBAL, LOG_INFO, "Configuration complete. Key parameters:\n");
936 LOG_ALLOW(GLOBAL, LOG_INFO, " - Run mode: %s\n", simCtx->OnlySetup ? "SETUP ONLY" : "Full Simulation");
937 LOG_ALLOW(GLOBAL, LOG_INFO, " - Time steps: %d (from %d to %d)\n", simCtx->StepsToRun, simCtx->StartStep, simCtx->StartStep + simCtx->StepsToRun);
938 LOG_ALLOW(GLOBAL, LOG_INFO, " - Time step size (dt): %g\n", simCtx->dt);
939 if (simCtx->tiout > 0) {
940 LOG_ALLOW(GLOBAL, LOG_INFO, " - Field/restart output cadence: every %d step(s)\n", simCtx->tiout);
941 } else {
942 LOG_ALLOW(GLOBAL, LOG_INFO, " - Field/restart output cadence: DISABLED\n");
943 }
944 LOG_ALLOW(GLOBAL, LOG_INFO, " - Immersed Boundary: %s\n", simCtx->immersed ? "ENABLED" : "DISABLED");
945 LOG_ALLOW(GLOBAL, LOG_INFO, " - Particles: %d\n", simCtx->np);
946 if (simCtx->np > 0) {
947 if (simCtx->particleConsoleOutputFreq > 0) {
948 LOG_ALLOW(GLOBAL, LOG_INFO, " - Particle console cadence: every %d step(s)\n", simCtx->particleConsoleOutputFreq);
949 } else {
950 LOG_ALLOW(GLOBAL, LOG_INFO, " - Particle console cadence: DISABLED\n");
951 }
952 LOG_ALLOW(GLOBAL, LOG_INFO, " - Particle console row subsampling: every %d particle(s)\n", simCtx->LoggingFrequency);
953 }
954 if (simCtx->StartStep > 0 && simCtx->np > 0) {
955 LOG_ALLOW(GLOBAL, LOG_INFO, " - Particle Restart Mode: %s\n", simCtx->particleRestartMode);
956 }
957
958 // --- Initialize PETSc's internal performance logging stage ---
959 ierr = PetscLogDefaultBegin(); CHKERRQ(ierr); // REDUNDANT but safe.
960 ierr = PetscMemorySetGetMaximumUsage(); CHKERRQ(ierr);
961
962 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Finished CreateSimulationContext successfully on rank %d.\n", simCtx->rank);
963
965 PetscFunctionReturn(0);
966}
967
968#undef __FUNCT__
969#define __FUNCT__ "PetscMkdirRecursive"
970/**
971 * @brief Internal helper implementation: `PetscMkdirRecursive()`.
972 * @details Local to this translation unit.
973 */
974static PetscErrorCode PetscMkdirRecursive(const char *path)
975{
976 PetscErrorCode ierr;
977 char tmp_path[PETSC_MAX_PATH_LEN];
978 char *p = NULL;
979 size_t len;
980 PetscBool exists;
981
982 PetscFunctionBeginUser;
983
984 // Create a mutable copy of the path
985 len = strlen(path);
986 if (len >= sizeof(tmp_path)) {
987 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Path is too long to process: %s", path);
988 }
989 strcpy(tmp_path, path);
990
991 // If the path ends with a separator, remove it
992 if (tmp_path[len - 1] == '/') {
993 tmp_path[len - 1] = 0;
994 }
995
996 // Iterate through the path, creating each directory level
997 for (p = tmp_path + 1; *p; p++) {
998 if (*p == '/') {
999 *p = 0; // Temporarily terminate the string
1000
1001 // Check if this directory level exists
1002 ierr = PetscTestDirectory(tmp_path, 'r', &exists); CHKERRQ(ierr);
1003 if (!exists) {
1004 ierr = PetscMkdir(tmp_path); CHKERRQ(ierr);
1005 }
1006
1007 *p = '/'; // Restore the separator
1008 }
1009 }
1010
1011 // Create the final, full directory path
1012 ierr = PetscTestDirectory(tmp_path, 'r', &exists); CHKERRQ(ierr);
1013 if (!exists) {
1014 ierr = PetscMkdir(tmp_path); CHKERRQ(ierr);
1015 }
1016
1017 PetscFunctionReturn(0);
1018}
1019
1020#undef __FUNCT__
1021#define __FUNCT__ "SetupSimulationEnvironment"
1022/**
1023 * @brief Internal helper implementation: `SetupSimulationEnvironment()`.
1024 * @details Local to this translation unit.
1025 */
1026PetscErrorCode SetupSimulationEnvironment(SimCtx *simCtx)
1027{
1028 PetscErrorCode ierr;
1029 PetscMPIInt rank;
1030 PetscBool exists;
1031
1032 PetscFunctionBeginUser;
1033 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
1034
1035 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Setting up simulation environment ---\n");
1036
1037 /* =====================================================================
1038 * Phase 1: Check for all required and optional INPUT files.
1039 * ===================================================================== */
1040 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Phase 1: Verifying input files...\n");
1041
1042 // --- Mandatory Inputs ---
1043 if (!simCtx->generate_grid) {
1044 ierr = VerifyPathExistence(simCtx->grid_file, PETSC_FALSE, PETSC_FALSE, "Grid file", &exists); CHKERRQ(ierr);
1045 }
1046 for (PetscInt i = 0; i < simCtx->num_bcs_files; i++) {
1047 char desc[128];
1048 ierr = PetscSNPrintf(desc, sizeof(desc), "BCS file #%d", i + 1); CHKERRQ(ierr);
1049 ierr = VerifyPathExistence(simCtx->bcs_files[i], PETSC_FALSE, PETSC_FALSE, desc, &exists); CHKERRQ(ierr);
1050 }
1051
1052 // --- Optional Inputs (these produce warnings if missing) ---
1053 if (simCtx->useCfg) {
1054 ierr = VerifyPathExistence(simCtx->allowedFile, PETSC_FALSE, PETSC_TRUE, "Whitelist config file", &exists); CHKERRQ(ierr);
1055 }
1056 if (simCtx->useProfilingSelectedFuncsCfg) {
1057 ierr = VerifyPathExistence(simCtx->profilingSelectedFuncsFile, PETSC_FALSE, PETSC_TRUE, "Profiling config file", &exists); CHKERRQ(ierr);
1058 }
1059 if (simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR) {
1060 ierr = VerifyPathExistence(simCtx->PostprocessingControlFile, PETSC_FALSE, PETSC_TRUE, "Post-processing control file", &exists); CHKERRQ(ierr);
1061 }
1062
1063
1064 /* =====================================================================
1065 * Phase 2: Validate directories specific to the execution mode.
1066 * ===================================================================== */
1067 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Phase 2: Verifying execution mode directories...\n");
1068 // The data source directory must exist if we intend to load any data from it.
1069 // This is true if:
1070 // 1. We are restarting from a previous time step (StartStep > 0), which implies
1071 // loading Eulerian fields and/or particle fields.
1072 // 2. We are starting from t=0 but are explicitly told to load the initial
1073 // Eulerian fields from a file (eulerianSource == "load").
1074 if (simCtx->StartStep > 0 || strcmp(simCtx->eulerianSource,"load")== 0){ // If this is a restart run
1075 ierr = VerifyPathExistence(simCtx->restart_dir, PETSC_TRUE, PETSC_FALSE, "Restart source directory", &exists); CHKERRQ(ierr);
1076 }
1077 if (simCtx->StartStep == 0 && strcmp(simCtx->eulerianSource, "solve") == 0 &&
1079 ierr = VerifyPathExistence(simCtx->initialConditionDirectory, PETSC_TRUE, PETSC_FALSE,
1080 "Initial-condition source directory", &exists); CHKERRQ(ierr);
1081 }
1082 if (simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR) {
1083 ierr = VerifyPathExistence(simCtx->pps->source_dir, PETSC_TRUE, PETSC_FALSE, "Post-processing source directory", &exists); CHKERRQ(ierr);
1084 }
1085
1086 /* =====================================================================
1087 * Phase 3: Create and prepare all OUTPUT directories.
1088 * ===================================================================== */
1089 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Phase 3: Preparing output directories...\n");
1090
1091 if (rank == 0){
1092 if(simCtx->exec_mode == EXEC_MODE_SOLVER){
1093 // --- Prepare Log Directory ---
1094 if (!simCtx->continueMode) {
1095 // Only wipe logs on fresh runs; continue mode appends to existing logs.
1096 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Creating/cleaning log directory: %s\n", simCtx->log_dir);
1097 ierr = PetscRMTree(simCtx->log_dir); // Wipes the directory and its contents
1098 if (ierr) { /* Ignore file-not-found error, but fail on others */
1099 PetscError(PETSC_COMM_SELF, __LINE__, __FUNCT__, __FILE__, ierr, PETSC_ERROR_INITIAL, "Could not remove existing log directory '%s'. Check permissions.", simCtx->log_dir);
1100 }
1101 ierr = PetscMkdir(simCtx->log_dir); CHKERRQ(ierr);
1102 } else {
1103 // In continue mode, ensure log directory exists but don't wipe it.
1104 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Continue mode: preserving existing log directory: %s\n", simCtx->log_dir);
1105 ierr = PetscMkdir(simCtx->log_dir); CHKERRQ(ierr);
1106 }
1107
1108 // --- Prepare Output Directories ---
1109 char path_buffer[PETSC_MAX_PATH_LEN];
1110
1111 // 1. Check/Create the main output directory
1112 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Verifying main output directory: %s\n", simCtx->output_dir);
1113 ierr = PetscTestDirectory(simCtx->output_dir, 'r', &exists); CHKERRQ(ierr);
1114 if (!exists) {
1115 LOG_ALLOW(GLOBAL, LOG_INFO, "Output directory not found. Creating: %s\n", simCtx->output_dir);
1116 ierr = PetscMkdir(simCtx->output_dir); CHKERRQ(ierr);
1117 }
1118
1119 // 2. Check/Create the Eulerian subdirectory
1120 ierr = PetscSNPrintf(path_buffer, sizeof(path_buffer), "%s/%s", simCtx->output_dir, simCtx->euler_subdir); CHKERRQ(ierr);
1121 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Verifying Eulerian subdirectory: %s\n", path_buffer);
1122 ierr = PetscTestDirectory(path_buffer, 'r', &exists); CHKERRQ(ierr);
1123 if (!exists) {
1124 LOG_ALLOW(GLOBAL, LOG_INFO, "Eulerian subdirectory not found. Creating: %s\n", path_buffer);
1125 ierr = PetscMkdir(path_buffer); CHKERRQ(ierr);
1126 }
1127
1128 // 3. Check/Create the Particle subdirectory if needed
1129 if (simCtx->np > 0) {
1130 ierr = PetscSNPrintf(path_buffer, sizeof(path_buffer), "%s/%s", simCtx->output_dir, simCtx->particle_subdir); CHKERRQ(ierr);
1131 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Verifying Particle subdirectory: %s\n", path_buffer);
1132 ierr = PetscTestDirectory(path_buffer, 'r', &exists); CHKERRQ(ierr);
1133 if (!exists) {
1134 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle subdirectory not found. Creating: %s\n", path_buffer);
1135 ierr = PetscMkdir(path_buffer); CHKERRQ(ierr);
1136 }
1137 }
1138 } else if(simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR){
1139 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Preparing post-processing output directories ...\n");
1140
1141 PostProcessParams *pps = simCtx->pps;
1142 char path_buffer[PETSC_MAX_PATH_LEN];
1143
1144 const char *last_slash_euler = strrchr(pps->output_prefix, '/');
1145 if(last_slash_euler){
1146 size_t dir_len = last_slash_euler - pps->output_prefix;
1147 if(dir_len > 0){
1148 if(dir_len >= sizeof(path_buffer)) SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_ARG_WRONG,"Post-processing output prefix path is too long.");
1149 strncpy(path_buffer, pps->output_prefix, dir_len);
1150 path_buffer[dir_len] = '\0';
1151
1152 ierr = PetscTestDirectory(path_buffer, 'r', &exists); CHKERRQ(ierr);
1153 if (!exists){
1154 LOG_ALLOW(GLOBAL, LOG_INFO, "Creating post-processing Eulerian output directory: %s\n", path_buffer);
1155 ierr = PetscMkdirRecursive(path_buffer); CHKERRQ(ierr);
1156 }
1157 }
1158 }
1159
1160 // Particle output directory
1161 if(pps->outputParticles){
1162 const char *last_slash_particle = strrchr(pps->particle_output_prefix, '/');
1163 if(last_slash_particle){
1164 size_t dir_len = last_slash_particle - pps->particle_output_prefix;
1165 if(dir_len > 0){
1166 if(dir_len > sizeof(path_buffer)) SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_ARG_WRONG,"Post-processing particle output prefix path is too long.");
1167 strncpy(path_buffer, pps->particle_output_prefix, dir_len);
1168 path_buffer[dir_len] = '\0';
1169
1170 ierr = PetscTestDirectory(path_buffer, 'r', &exists); CHKERRQ(ierr);
1171
1172 if (!exists){
1173 LOG_ALLOW(GLOBAL, LOG_INFO, "Creating post-processing Particle output directory: %s\n", path_buffer);
1174 ierr = PetscMkdirRecursive(path_buffer); CHKERRQ(ierr);
1175 }
1176 }
1177 }
1178 }
1179
1180 // Statistics output directory
1181 if(pps->statistics_pipeline[0] != '\0'){
1182 const char *last_slash_stats = strrchr(pps->statistics_output_prefix, '/');
1183 if(last_slash_stats){
1184 size_t dir_len = last_slash_stats - pps->statistics_output_prefix;
1185 if(dir_len > 0){
1186 if(dir_len >= sizeof(path_buffer)) SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_ARG_WRONG,"Post-processing statistics output prefix path is too long.");
1187 strncpy(path_buffer, pps->statistics_output_prefix, dir_len);
1188 path_buffer[dir_len] = '\0';
1189
1190 ierr = PetscTestDirectory(path_buffer, 'r', &exists); CHKERRQ(ierr);
1191 if (!exists){
1192 LOG_ALLOW(GLOBAL, LOG_INFO, "Creating post-processing Statistics output directory: %s\n", path_buffer);
1193 ierr = PetscMkdirRecursive(path_buffer); CHKERRQ(ierr);
1194 }
1195 }
1196 }
1197 }
1198 }
1199 }
1200
1201 // Synchronize all processes before proceeding
1202 ierr = MPI_Barrier(PETSC_COMM_WORLD); CHKERRMPI(ierr);
1203
1204 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Environment setup complete ---\n");
1205
1206 PetscFunctionReturn(0);
1207}
1208
1209#undef __FUNCT__
1210#define __FUNCT__ "AllocateContextHeirarchy"
1211/**
1212 * @brief Internal helper implementation: `AllocateContextHierarchy()`.
1213 * @details Local to this translation unit.
1214 */
1215static PetscErrorCode AllocateContextHierarchy(SimCtx *simCtx)
1216{
1217 PetscErrorCode ierr;
1218 UserMG *usermg = &simCtx->usermg;
1219 MGCtx *mgctx;
1220 PetscInt nblk = simCtx->block_number;
1221 PetscBool found;
1222 PetscFunctionBeginUser;
1224
1225 LOG_ALLOW(GLOBAL, LOG_INFO, "Allocating context hierarchy for %d levels and %d blocks...\n", simCtx->mglevels, nblk);
1226
1227 // Store the number of levels in the UserMG struct itself
1228 usermg->mglevels = simCtx->mglevels;
1229
1230 // --- 1. Allocate the array of MGCtx structs ---
1231 ierr = PetscMalloc(usermg->mglevels * sizeof(MGCtx), &usermg->mgctx); CHKERRQ(ierr);
1232 // Zero-initialize to ensure all pointers (especially packer) are NULL
1233 ierr = PetscMemzero(usermg->mgctx, usermg->mglevels * sizeof(MGCtx)); CHKERRQ(ierr);
1234 mgctx = usermg->mgctx;
1235 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Allocated MGCtx array of size %d.\n", simCtx->rank, usermg->mglevels);
1236
1237 // --- 2. Parse semi-coarsening options (logic from MG_Initial) ---
1238 // These flags determine if a dimension is coarsened in the multigrid hierarchy.
1239 PetscInt *isc, *jsc, *ksc;
1240 ierr = PetscMalloc3(nblk, &isc, nblk, &jsc, nblk, &ksc); CHKERRQ(ierr);
1241 // Set defaults to FALSE (full coarsening)
1242 for (PetscInt i = 0; i < nblk; ++i) {
1243 isc[i] = 0; jsc[i] = 0; ksc[i] = 0;
1244 }
1245
1246// Use a temporary variable for the 'count' argument to the parsing function.
1247 // This protects the original 'nblk' which is needed for the loop bounds.
1248 PetscInt n_opts_found = nblk;
1249 ierr = PetscOptionsGetIntArray(NULL, NULL, "-mg_i_semi", isc, &n_opts_found, &found); CHKERRQ(ierr);
1250
1251 n_opts_found = nblk; // Reset the temp variable before the next call
1252 ierr = PetscOptionsGetIntArray(NULL, NULL, "-mg_j_semi", jsc, &n_opts_found, &found); CHKERRQ(ierr);
1253
1254 n_opts_found = nblk; // Reset the temp variable before the next call
1255 ierr = PetscOptionsGetIntArray(NULL, NULL, "-mg_k_semi", ksc, &n_opts_found, &found); CHKERRQ(ierr);
1256
1257 // --- 3. Loop over levels and blocks to allocate UserCtx arrays ---
1258 for (PetscInt level = 0; level < simCtx->mglevels; level++) {
1259
1260 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Setting up MG Level %d...\n", simCtx->rank, level);
1261 // Allocate the array of UserCtx structs for this level
1262 ierr = PetscMalloc(nblk * sizeof(UserCtx), &mgctx[level].user); CHKERRQ(ierr);
1263 // It's good practice to zero out the memory to avoid uninitialized values
1264 ierr = PetscMemzero(mgctx[level].user, nblk * sizeof(UserCtx)); CHKERRQ(ierr);
1265 mgctx[level].thislevel = level;
1266
1267 for (PetscInt bi = 0; bi < nblk; bi++) {
1268 UserCtx *currentUser = &mgctx[level].user[bi];
1269 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Initializing UserCtx for Level %d, Block %d.\n", simCtx->rank, level, bi);
1270
1271 // --- CRITICAL STEP: Set the back-pointer to the master context ---
1272 currentUser->simCtx = simCtx;
1273
1274 // Initialize other per-context values
1275 currentUser->thislevel = level;
1276 currentUser->_this = bi; //
1277 currentUser->mglevels = usermg->mglevels;
1278
1279 // Assign semi-coarsening flags
1280 currentUser->isc = isc[bi];
1281 currentUser->jsc = jsc[bi];
1282 currentUser->ksc = ksc[bi];
1283
1284 // Link to finer/coarser contexts for multigrid operations
1285 if (level > 0) {
1286 currentUser->user_c = &mgctx[level-1].user[bi];
1287 LOG_ALLOW_SYNC(GLOBAL, LOG_DEBUG, "Rank %d: -> Linked to coarser context (user_c).\n", simCtx->rank);
1288 }
1289 if (level < usermg->mglevels - 1) {
1290 currentUser->user_f = &mgctx[level+1].user[bi];
1291 LOG_ALLOW_SYNC(GLOBAL, LOG_DEBUG, "Rank %d: -> Linked to finer context (user_f).\n", simCtx->rank);
1292 }
1293 }
1294 }
1295
1296 // Log a summary of the parsed flags on each rank.
1297 if (get_log_level() >= LOG_DEBUG && nblk > 0) {
1298 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Final semi-coarsening configuration view:\n", simCtx->rank);
1299 for (PetscInt bi = 0; bi < nblk; ++bi) {
1300 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Block %d: i-semi=%d, j-semi=%d, k-semi=%d\n", simCtx->rank, bi, isc[bi], jsc[bi], ksc[bi]);
1301 }
1302 }
1303
1304 // Clean up temporary arrays
1305 ierr = PetscFree3(isc, jsc, ksc); CHKERRQ(ierr);
1306
1307 LOG_ALLOW(GLOBAL, LOG_INFO, "Context hierarchy allocation complete.\n");
1309 PetscFunctionReturn(0);
1310}
1311
1312#undef __FUNCT__
1313#define __FUNCT__ "SetupSolverParameters"
1314/**
1315 * @brief Internal helper implementation: `SetupSolverParameters()`.
1316 * @details Local to this translation unit.
1317 */
1318static PetscErrorCode SetupSolverParameters(SimCtx *simCtx){
1319
1320 PetscFunctionBeginUser;
1322
1323 LOG_ALLOW(GLOBAL,LOG_INFO, " -- Setting up solver parameters -- .\n");
1324
1325 UserMG *usermg = &simCtx->usermg;
1326 MGCtx *mgctx = usermg->mgctx;
1327 PetscInt nblk = simCtx->block_number;
1328
1329 for (PetscInt level = usermg->mglevels-1; level >=0; level--) {
1330 for (PetscInt bi = 0; bi < nblk; bi++) {
1331 UserCtx *user = &mgctx[level].user[bi];
1332 LOG_ALLOW_SYNC(LOCAL, LOG_DEBUG, "Rank %d: Setting up parameters for level %d, block %d\n", simCtx->rank, level, bi);
1333
1334 user->assignedA = PETSC_FALSE;
1335 user->multinullspace = PETSC_FALSE;
1336 }
1337 }
1339 PetscFunctionReturn(0);
1340}
1341
1342#undef __FUNCT__
1343#define __FUNCT__ "SetupGridAndSolvers"
1344/**
1345 * @brief Implementation of \ref SetupGridAndSolvers().
1346 * @details Full API contract (arguments, ownership, side effects) is documented with
1347 * the header declaration in `include/setup.h`.
1348 * @see SetupGridAndSolvers()
1349 */
1350PetscErrorCode SetupGridAndSolvers(SimCtx *simCtx)
1351{
1352 PetscErrorCode ierr;
1353 PetscFunctionBeginUser;
1354
1356
1357 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Starting Grid and Solvers Setup ---\n");
1358
1359 // Phase 1: Allocate the UserMG and UserCtx hierarchy
1360 ierr = AllocateContextHierarchy(simCtx); CHKERRQ(ierr);
1361
1362 ierr = DefineAllGridDimensions(simCtx); CHKERRQ(ierr);
1363 ierr = InitializeAllGridDMs(simCtx); CHKERRQ(ierr);
1364 ierr = AssignAllGridCoordinates(simCtx); CHKERRQ(ierr);
1365 ierr = CreateAndInitializeAllVectors(simCtx); CHKERRQ(ierr);
1366 ierr = SetupSolverParameters(simCtx); CHKERRQ(ierr);
1367 ierr = InitializeSolutionConvergenceState(simCtx); CHKERRQ(ierr);
1368
1369 // NOTE: CalculateAllGridMetrics is now called inside SetupBoundaryConditions (not here) to ensure:
1370 // 1. Boundary condition configuration data (boundary_faces) is available for periodic BC corrections
1371 // 2. Computed metrics are available for inlet/outlet area calculations
1372 // This resolves the circular dependency between BC setup and metric calculations.
1373
1374 LOG_ALLOW(GLOBAL, LOG_INFO, "--- Grid and Solvers Setup Complete ---\n");
1375
1377 PetscFunctionReturn(0);
1378}
1379
1380
1381#undef __FUNCT__
1382#define __FUNCT__ "CreateAndInitializeAllVectors"
1383/**
1384 * @brief Internal helper implementation: `CreateAndInitializeAllVectors()`.
1385 * @details Local to this translation unit.
1386 */
1388{
1389 PetscErrorCode ierr;
1390 UserMG *usermg = &simCtx->usermg;
1391 MGCtx *mgctx = usermg->mgctx;
1392 PetscInt nblk = simCtx->block_number;
1393
1394 PetscFunctionBeginUser;
1395
1397
1398 LOG_ALLOW(GLOBAL, LOG_INFO, "Creating and initializing all simulation vectors...\n");
1399
1400 for (PetscInt level = usermg->mglevels-1; level >=0; level--) {
1401 for (PetscInt bi = 0; bi < nblk; bi++) {
1402 UserCtx *user = &mgctx[level].user[bi];
1403
1404 if(!user->da || !user->fda) {
1405 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "DMs not properly initialized in UserCtx before vector creation.");
1406 }
1407
1408 LOG_ALLOW_SYNC(LOCAL, LOG_DEBUG, "Rank %d: Creating vectors for level %d, block %d\n", simCtx->rank, level, bi);
1409
1410 // --- Group A: Primary Flow Fields (Global and Local) ---
1411 // These are the core solution variables.
1412 ierr = DMCreateGlobalVector(user->fda, &user->Ucont); CHKERRQ(ierr); ierr = VecSet(user->Ucont, 0.0); CHKERRQ(ierr);
1413 ierr = DMCreateGlobalVector(user->fda, &user->Ucat); CHKERRQ(ierr); ierr = VecSet(user->Ucat, 0.0); CHKERRQ(ierr);
1414 ierr = DMCreateGlobalVector(user->da, &user->P); CHKERRQ(ierr); ierr = VecSet(user->P, 0.0); CHKERRQ(ierr);
1415 ierr = DMCreateGlobalVector(user->da, &user->Nvert); CHKERRQ(ierr); ierr = VecSet(user->Nvert, 0.0); CHKERRQ(ierr);
1416
1417 ierr = DMCreateLocalVector(user->fda, &user->lUcont); CHKERRQ(ierr); ierr = VecSet(user->lUcont, 0.0); CHKERRQ(ierr);
1418 ierr = DMCreateLocalVector(user->fda, &user->lUcat); CHKERRQ(ierr); ierr = VecSet(user->lUcat, 0.0); CHKERRQ(ierr);
1419 ierr = DMCreateLocalVector(user->da, &user->lP); CHKERRQ(ierr); ierr = VecSet(user->lP, 0.0); CHKERRQ(ierr);
1420 ierr = DMCreateLocalVector(user->da, &user->lNvert); CHKERRQ(ierr); ierr = VecSet(user->lNvert, 0.0); CHKERRQ(ierr);
1421
1422 // -- Group A2: Derived Flow Fields (Global and Local) ---
1423 ierr = VecDuplicate(user->P,&user->Diffusivity); CHKERRQ(ierr); ierr = VecSet(user->Diffusivity, 0.0); CHKERRQ(ierr);
1424 ierr = VecDuplicate(user->lP,&user->lDiffusivity); CHKERRQ(ierr); ierr = VecSet(user->lDiffusivity, 0.0); CHKERRQ(ierr);
1425 ierr = VecDuplicate(user->Ucat,&user->DiffusivityGradient); CHKERRQ(ierr); ierr = VecSet(user->DiffusivityGradient, 0.0); CHKERRQ(ierr);
1426 ierr = VecDuplicate(user->lUcat,&user->lDiffusivityGradient); CHKERRQ(ierr); ierr = VecSet(user->lDiffusivityGradient, 0.0); CHKERRQ(ierr);
1427
1428 // -- Group B: Solver Work Vectors (Global and Local) ---
1429 ierr = VecDuplicate(user->P, &user->Phi); CHKERRQ(ierr); ierr = VecSet(user->Phi, 0.0); CHKERRQ(ierr);
1430 ierr = VecDuplicate(user->lP, &user->lPhi); CHKERRQ(ierr); ierr = VecSet(user->lPhi, 0.0); CHKERRQ(ierr);
1431
1432 // --- Group C: Time-Stepping & Workspace Fields (Finest Level Only) ---
1433 if (level == usermg->mglevels - 1) {
1434 ierr = VecDuplicate(user->Ucont, &user->Ucont_o); CHKERRQ(ierr); ierr = VecSet(user->Ucont_o, 0.0); CHKERRQ(ierr);
1435 ierr = VecDuplicate(user->Ucont, &user->Ucont_rm1); CHKERRQ(ierr); ierr = VecSet(user->Ucont_rm1, 0.0); CHKERRQ(ierr);
1436 ierr = VecDuplicate(user->Ucat, &user->Ucat_o); CHKERRQ(ierr); ierr = VecSet(user->Ucat_o, 0.0); CHKERRQ(ierr);
1437 ierr = VecDuplicate(user->P, &user->P_o); CHKERRQ(ierr); ierr = VecSet(user->P_o, 0.0); CHKERRQ(ierr);
1438 ierr = VecDuplicate(user->lUcont, &user->lUcont_o); CHKERRQ(ierr); ierr = VecSet(user->lUcont_o, 0.0); CHKERRQ(ierr);
1439 ierr = VecDuplicate(user->lUcont, &user->lUcont_rm1); CHKERRQ(ierr); ierr = VecSet(user->lUcont_rm1, 0.0); CHKERRQ(ierr);
1440 ierr = DMCreateLocalVector(user->da, &user->lNvert_o); CHKERRQ(ierr); ierr = VecSet(user->lNvert_o, 0.0); CHKERRQ(ierr);
1441 ierr = VecDuplicate(user->Nvert, &user->Nvert_o); CHKERRQ(ierr); ierr = VecSet(user->Nvert_o, 0.0); CHKERRQ(ierr);
1442 }
1443
1444 // --- Group D: Grid Metrics (Face-Centered) ---
1445 ierr = DMCreateGlobalVector(user->fda, &user->Csi); CHKERRQ(ierr); ierr = VecSet(user->Csi, 0.0); CHKERRQ(ierr);
1446 ierr = VecDuplicate(user->Csi, &user->Eta); CHKERRQ(ierr); ierr = VecSet(user->Eta, 0.0); CHKERRQ(ierr);
1447 ierr = VecDuplicate(user->Csi, &user->Zet); CHKERRQ(ierr); ierr = VecSet(user->Zet, 0.0); CHKERRQ(ierr);
1448 ierr = DMCreateGlobalVector(user->da, &user->Aj); CHKERRQ(ierr); ierr = VecSet(user->Aj, 0.0); CHKERRQ(ierr);
1449
1450 ierr = DMCreateLocalVector(user->fda, &user->lCsi); CHKERRQ(ierr); ierr = VecSet(user->lCsi, 0.0); CHKERRQ(ierr);
1451 ierr = VecDuplicate(user->lCsi, &user->lEta); CHKERRQ(ierr); ierr = VecSet(user->lEta, 0.0); CHKERRQ(ierr);
1452 ierr = VecDuplicate(user->lCsi, &user->lZet); CHKERRQ(ierr); ierr = VecSet(user->lZet, 0.0); CHKERRQ(ierr);
1453 ierr = DMCreateLocalVector(user->da, &user->lAj); CHKERRQ(ierr); ierr = VecSet(user->lAj, 0.0); CHKERRQ(ierr);
1454
1455
1456 // --- Group E: Grid Metrics (Face-Centered) ---
1457 // Vector metrics are duplicated from Csi (DOF=3, fda-based)
1458 ierr = VecDuplicate(user->Csi, &user->ICsi); CHKERRQ(ierr); ierr = VecSet(user->ICsi, 0.0); CHKERRQ(ierr);
1459 ierr = VecDuplicate(user->Csi, &user->IEta); CHKERRQ(ierr); ierr = VecSet(user->IEta, 0.0); CHKERRQ(ierr);
1460 ierr = VecDuplicate(user->Csi, &user->IZet); CHKERRQ(ierr); ierr = VecSet(user->IZet, 0.0); CHKERRQ(ierr);
1461 ierr = VecDuplicate(user->Csi, &user->JCsi); CHKERRQ(ierr); ierr = VecSet(user->JCsi, 0.0); CHKERRQ(ierr);
1462 ierr = VecDuplicate(user->Csi, &user->JEta); CHKERRQ(ierr); ierr = VecSet(user->JEta, 0.0); CHKERRQ(ierr);
1463 ierr = VecDuplicate(user->Csi, &user->JZet); CHKERRQ(ierr); ierr = VecSet(user->JZet, 0.0); CHKERRQ(ierr);
1464 ierr = VecDuplicate(user->Csi, &user->KCsi); CHKERRQ(ierr); ierr = VecSet(user->KCsi, 0.0); CHKERRQ(ierr);
1465 ierr = VecDuplicate(user->Csi, &user->KEta); CHKERRQ(ierr); ierr = VecSet(user->KEta, 0.0); CHKERRQ(ierr);
1466 ierr = VecDuplicate(user->Csi, &user->KZet); CHKERRQ(ierr); ierr = VecSet(user->KZet, 0.0); CHKERRQ(ierr);
1467 // Scalar metrics are duplicated from Aj (DOF=1, da-based)
1468 ierr = VecDuplicate(user->Aj, &user->IAj); CHKERRQ(ierr); ierr = VecSet(user->IAj, 0.0); CHKERRQ(ierr);
1469 ierr = VecDuplicate(user->Aj, &user->JAj); CHKERRQ(ierr); ierr = VecSet(user->JAj, 0.0); CHKERRQ(ierr);
1470 ierr = VecDuplicate(user->Aj, &user->KAj); CHKERRQ(ierr); ierr = VecSet(user->KAj, 0.0); CHKERRQ(ierr);
1471
1472 ierr = VecDuplicate(user->lCsi, &user->lICsi); CHKERRQ(ierr); ierr = VecSet(user->lICsi, 0.0); CHKERRQ(ierr);
1473 ierr = VecDuplicate(user->lCsi, &user->lIEta); CHKERRQ(ierr); ierr = VecSet(user->lIEta, 0.0); CHKERRQ(ierr);
1474 ierr = VecDuplicate(user->lCsi, &user->lIZet); CHKERRQ(ierr); ierr = VecSet(user->lIZet, 0.0); CHKERRQ(ierr);
1475 ierr = VecDuplicate(user->lCsi, &user->lJCsi); CHKERRQ(ierr); ierr = VecSet(user->lJCsi, 0.0); CHKERRQ(ierr);
1476 ierr = VecDuplicate(user->lCsi, &user->lJEta); CHKERRQ(ierr); ierr = VecSet(user->lJEta, 0.0); CHKERRQ(ierr);
1477 ierr = VecDuplicate(user->lCsi, &user->lJZet); CHKERRQ(ierr); ierr = VecSet(user->lJZet, 0.0); CHKERRQ(ierr);
1478 ierr = VecDuplicate(user->lCsi, &user->lKCsi); CHKERRQ(ierr); ierr = VecSet(user->lKCsi, 0.0); CHKERRQ(ierr);
1479 ierr = VecDuplicate(user->lCsi, &user->lKEta); CHKERRQ(ierr); ierr = VecSet(user->lKEta, 0.0); CHKERRQ(ierr);
1480 ierr = VecDuplicate(user->lCsi, &user->lKZet); CHKERRQ(ierr); ierr = VecSet(user->lKZet, 0.0); CHKERRQ(ierr);
1481
1482 ierr = VecDuplicate(user->lAj, &user->lIAj); CHKERRQ(ierr); ierr = VecSet(user->lIAj, 0.0); CHKERRQ(ierr);
1483 ierr = VecDuplicate(user->lAj, &user->lJAj); CHKERRQ(ierr); ierr = VecSet(user->lJAj, 0.0); CHKERRQ(ierr);
1484 ierr = VecDuplicate(user->lAj, &user->lKAj); CHKERRQ(ierr); ierr = VecSet(user->lKAj, 0.0); CHKERRQ(ierr);
1485
1486 // --- Group F: Cell/Face Center Coordinates and Grid Spacing ---
1487 ierr = DMCreateGlobalVector(user->fda, &user->Cent); CHKERRQ(ierr); ierr = VecSet(user->Cent, 0.0); CHKERRQ(ierr);
1488 ierr = DMCreateLocalVector(user->fda, &user->lCent); CHKERRQ(ierr); ierr = VecSet(user->lCent, 0.0); CHKERRQ(ierr);
1489
1490 ierr = VecDuplicate(user->Cent, &user->GridSpace); CHKERRQ(ierr); ierr = VecSet(user->GridSpace, 0.0); CHKERRQ(ierr);
1491 ierr = VecDuplicate(user->lCent, &user->lGridSpace); CHKERRQ(ierr); ierr = VecSet(user->lGridSpace, 0.0); CHKERRQ(ierr);
1492
1493 ierr = VecDuplicate(user->Cent, &user->Centx); CHKERRQ(ierr); ierr = VecSet(user->Centx, 0.0); CHKERRQ(ierr);
1494 ierr = VecDuplicate(user->Cent, &user->Centy); CHKERRQ(ierr); ierr = VecSet(user->Centy, 0.0); CHKERRQ(ierr);
1495 ierr = VecDuplicate(user->Cent, &user->Centz); CHKERRQ(ierr); ierr = VecSet(user->Centz, 0.0); CHKERRQ(ierr);
1496 ierr = VecDuplicate(user->lCent, &user->lCentx); CHKERRQ(ierr); ierr = VecSet(user->lCentx, 0.0); CHKERRQ(ierr);
1497 ierr = VecDuplicate(user->lCent, &user->lCenty); CHKERRQ(ierr); ierr = VecSet(user->lCenty, 0.0); CHKERRQ(ierr);
1498 ierr = VecDuplicate(user->lCent, &user->lCentz); CHKERRQ(ierr); ierr = VecSet(user->lCentz, 0.0); CHKERRQ(ierr);
1499
1500 if(level == usermg->mglevels -1){
1501 // --- Group G: Turbulence Models (Finest Level Only) ---
1502 if (simCtx->les || simCtx->rans) {
1503 ierr = DMCreateGlobalVector(user->da, &user->Nu_t); CHKERRQ(ierr); ierr = VecSet(user->Nu_t, 0.0); CHKERRQ(ierr);
1504 ierr = DMCreateLocalVector(user->da, &user->lNu_t); CHKERRQ(ierr); ierr = VecSet(user->lNu_t, 0.0); CHKERRQ(ierr);
1505 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Turbulence viscosity (Nu_t) vectors created for LES/RANS model.\n");
1506 if(simCtx->les){
1507 ierr = DMCreateGlobalVector(user->da,&user->CS); CHKERRQ(ierr); ierr = VecSet(user->CS,0.0); CHKERRQ(ierr);
1508 ierr = DMCreateLocalVector(user->da,&user->lCs); CHKERRQ(ierr); ierr = VecSet(user->lCs,0.0); CHKERRQ(ierr);
1509 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Smagorinsky constant (CS) vectors created for LES model.\n");
1510 }
1511
1512 if(simCtx->wallfunction){
1513 ierr = DMCreateLocalVector(user->fda,&user->lFriction_Velocity); CHKERRQ(ierr); ierr = VecSet(user->lFriction_Velocity,0.0);
1514 }
1515 // Add K_Omega etc. here as needed
1516
1517 // Note: Add any other vectors from the legacy MG_Initial here as needed.
1518 // For example: Rhs, Forcing, turbulence Vecs (K_Omega, Nu_t)...
1519
1520 }
1521 // --- Group H: Particle Methods
1522 if(simCtx->np>0){
1523 ierr = DMCreateGlobalVector(user->da,&user->ParticleCount); CHKERRQ(ierr); ierr = VecSet(user->ParticleCount,0.0); CHKERRQ(ierr);
1524 ierr = DMCreateLocalVector(user->da,&user->lParticleCount); CHKERRQ(ierr); ierr = VecSet(user->lParticleCount,0.0); CHKERRQ(ierr);
1525 // Scalar field to hold particle scalar property (e.g., temperature, concentration)
1526 ierr = DMCreateGlobalVector(user->da,&user->Psi); CHKERRQ(ierr); ierr = VecSet(user->Psi,0.0); CHKERRQ(ierr);
1527 ierr = DMCreateLocalVector(user->da,&user->lPsi); CHKERRQ(ierr); ierr = VecSet(user->lPsi,0.0); CHKERRQ(ierr);
1528 LOG_ALLOW(GLOBAL,LOG_DEBUG,"ParticleCount & Scalar(Psi) created for %d particles.\n",simCtx->np);
1529 }
1530 }
1531 // --- Group I: Boundary Condition vectors ---
1532 ierr = DMCreateGlobalVector(user->fda, &user->Bcs.Ubcs); CHKERRQ(ierr);
1533 ierr = VecSet(user->Bcs.Ubcs, 0.0); CHKERRQ(ierr);
1534 ierr = DMCreateGlobalVector(user->fda, &user->Bcs.Uch); CHKERRQ(ierr);
1535 ierr = VecSet(user->Bcs.Uch, 0.0); CHKERRQ(ierr);
1536
1537 if(level == usermg->mglevels - 1){
1538 if(simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR){
1539 LOG_ALLOW(LOCAL, LOG_DEBUG, "Post-processor mode detected. Allocating derived field vectors.\n");
1540
1541 ierr = VecDuplicate(user->P, &user->P_nodal); CHKERRQ(ierr);
1542 ierr = VecSet(user->P_nodal, 0.0); CHKERRQ(ierr);
1543
1544 ierr = VecDuplicate(user->Ucat, &user->Ucat_nodal); CHKERRQ(ierr);
1545 ierr = VecSet(user->Ucat_nodal, 0.0); CHKERRQ(ierr);
1546
1547 ierr = VecDuplicate(user->P, &user->Qcrit); CHKERRQ(ierr);
1548 ierr = VecSet(user->Qcrit, 0.0); CHKERRQ(ierr);
1549
1550 LOG_ALLOW(LOCAL, LOG_DEBUG, "Derived field vectors P_nodal, Ucat_nodal, and Qcrit created.\n");
1551
1552 if(simCtx->np>0){
1553 ierr = VecDuplicate(user->Psi, &user->Psi_nodal); CHKERRQ(ierr);
1554 ierr = VecSet(user->Psi_nodal, 0.0); CHKERRQ(ierr);
1555
1556 LOG_ALLOW(LOCAL, LOG_DEBUG, "Derived field vector Psi_nodal created for particle scalar property.\n");
1557
1558 }
1559 }else{
1560 user->P_nodal = NULL;
1561 user->Ucat_nodal = NULL;
1562 user->Qcrit = NULL;
1563 user->Psi_nodal = NULL;
1564 }
1565 }
1566
1567 }
1568}
1569
1570 LOG_ALLOW(GLOBAL, LOG_INFO, "All simulation vectors created and initialized.\n");
1571
1573 PetscFunctionReturn(0);
1574}
1575
1576#undef __FUNCT__
1577#define __FUNCT__ "RepairPeriodicNormalFaceGhosts"
1578/**
1579 * @brief Repairs the adjacent normal ghost layer for periodic face-staggered data.
1580 *
1581 * PETSc wraps every component with cell-style indexing. A face family instead
1582 * needs its adjacent normal ghost shifted by one additional physical face. With
1583 * the width-three periodic DMDA, the required value is available in the deeper
1584 * PETSc ghost at -3 or n+2. Tangential ghosts retain PETSc's native wraparound.
1585 */
1586static PetscErrorCode RepairPeriodicNormalFaceGhosts(UserCtx *user, DM dm, Vec local_vec,
1587 PetscInt dof, char face_direction,
1588 PetscBool component_staggered)
1589{
1590 DMDALocalInfo info;
1591 PetscInt xs, xe, ys, ye, zs, ze;
1592 PetscInt gxs, gxe, gys, gye, gzs, gze;
1593 PetscInt mx, my, mz;
1594
1595 PetscFunctionBeginUser;
1596 if (!face_direction && !component_staggered) PetscFunctionReturn(0);
1597
1598 PetscCall(DMDAGetLocalInfo(dm, &info));
1599 xs = info.xs; xe = info.xs + info.xm;
1600 ys = info.ys; ye = info.ys + info.ym;
1601 zs = info.zs; ze = info.zs + info.zm;
1602 gxs = info.gxs; gxe = info.gxs + info.gxm;
1603 gys = info.gys; gye = info.gys + info.gym;
1604 gzs = info.gzs; gze = info.gzs + info.gzm;
1605 mx = info.mx; my = info.my; mz = info.mz;
1606
1607 if (component_staggered) {
1608 Cmpnts ***array;
1609 PetscCall(DMDAVecGetArray(dm, local_vec, &array));
1610
1611 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0) {
1612 PetscCheck(gxs <= -3, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1613 "Periodic Ucont.x ghost repair requires DMDA stencil width at least 3.");
1614 for (PetscInt k = gzs; k < gze; k++) for (PetscInt j = gys; j < gye; j++)
1615 array[k][j][-1].x = array[k][j][-3].x;
1616 }
1617 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == mx) {
1618 PetscCheck(gxe > mx + 2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1619 "Periodic Ucont.x ghost repair requires DMDA stencil width at least 3.");
1620 for (PetscInt k = gzs; k < gze; k++) for (PetscInt j = gys; j < gye; j++)
1621 array[k][j][mx].x = array[k][j][mx + 2].x;
1622 }
1623 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0) {
1624 PetscCheck(gys <= -3, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1625 "Periodic Ucont.y ghost repair requires DMDA stencil width at least 3.");
1626 for (PetscInt k = gzs; k < gze; k++) for (PetscInt i = gxs; i < gxe; i++)
1627 array[k][-1][i].y = array[k][-3][i].y;
1628 }
1629 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == my) {
1630 PetscCheck(gye > my + 2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1631 "Periodic Ucont.y ghost repair requires DMDA stencil width at least 3.");
1632 for (PetscInt k = gzs; k < gze; k++) for (PetscInt i = gxs; i < gxe; i++)
1633 array[k][my][i].y = array[k][my + 2][i].y;
1634 }
1635 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0) {
1636 PetscCheck(gzs <= -3, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1637 "Periodic Ucont.z ghost repair requires DMDA stencil width at least 3.");
1638 for (PetscInt j = gys; j < gye; j++) for (PetscInt i = gxs; i < gxe; i++)
1639 array[-1][j][i].z = array[-3][j][i].z;
1640 }
1641 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == mz) {
1642 PetscCheck(gze > mz + 2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1643 "Periodic Ucont.z ghost repair requires DMDA stencil width at least 3.");
1644 for (PetscInt j = gys; j < gye; j++) for (PetscInt i = gxs; i < gxe; i++)
1645 array[mz][j][i].z = array[mz + 2][j][i].z;
1646 }
1647
1648 PetscCall(DMDAVecRestoreArray(dm, local_vec, &array));
1649 PetscFunctionReturn(0);
1650 }
1651
1652 if (dof == 1) {
1653 PetscReal ***array;
1654 PetscCall(DMDAVecGetArray(dm, local_vec, &array));
1655
1656 if (face_direction == 'i') {
1657 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0) {
1658 PetscCheck(gxs <= -3, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1659 "Periodic I-face ghost repair requires DMDA stencil width at least 3.");
1660 for (PetscInt k = gzs; k < gze; k++) for (PetscInt j = gys; j < gye; j++)
1661 array[k][j][-1] = array[k][j][-3];
1662 }
1663 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == mx) {
1664 PetscCheck(gxe > mx + 2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1665 "Periodic I-face ghost repair requires DMDA stencil width at least 3.");
1666 for (PetscInt k = gzs; k < gze; k++) for (PetscInt j = gys; j < gye; j++)
1667 array[k][j][mx] = array[k][j][mx + 2];
1668 }
1669 } else if (face_direction == 'j') {
1670 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0) {
1671 PetscCheck(gys <= -3, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1672 "Periodic J-face ghost repair requires DMDA stencil width at least 3.");
1673 for (PetscInt k = gzs; k < gze; k++) for (PetscInt i = gxs; i < gxe; i++)
1674 array[k][-1][i] = array[k][-3][i];
1675 }
1676 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == my) {
1677 PetscCheck(gye > my + 2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1678 "Periodic J-face ghost repair requires DMDA stencil width at least 3.");
1679 for (PetscInt k = gzs; k < gze; k++) for (PetscInt i = gxs; i < gxe; i++)
1680 array[k][my][i] = array[k][my + 2][i];
1681 }
1682 } else if (face_direction == 'k') {
1683 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0) {
1684 PetscCheck(gzs <= -3, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1685 "Periodic K-face ghost repair requires DMDA stencil width at least 3.");
1686 for (PetscInt j = gys; j < gye; j++) for (PetscInt i = gxs; i < gxe; i++)
1687 array[-1][j][i] = array[-3][j][i];
1688 }
1689 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == mz) {
1690 PetscCheck(gze > mz + 2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1691 "Periodic K-face ghost repair requires DMDA stencil width at least 3.");
1692 for (PetscInt j = gys; j < gye; j++) for (PetscInt i = gxs; i < gxe; i++)
1693 array[mz][j][i] = array[mz + 2][j][i];
1694 }
1695 }
1696
1697 PetscCall(DMDAVecRestoreArray(dm, local_vec, &array));
1698 } else {
1699 Cmpnts ***array;
1700 PetscCall(DMDAVecGetArray(dm, local_vec, &array));
1701
1702 if (face_direction == 'i') {
1703 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0) {
1704 PetscCheck(gxs <= -3, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1705 "Periodic I-face ghost repair requires DMDA stencil width at least 3.");
1706 for (PetscInt k = gzs; k < gze; k++) for (PetscInt j = gys; j < gye; j++)
1707 array[k][j][-1] = array[k][j][-3];
1708 }
1709 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == mx) {
1710 PetscCheck(gxe > mx + 2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1711 "Periodic I-face ghost repair requires DMDA stencil width at least 3.");
1712 for (PetscInt k = gzs; k < gze; k++) for (PetscInt j = gys; j < gye; j++)
1713 array[k][j][mx] = array[k][j][mx + 2];
1714 }
1715 } else if (face_direction == 'j') {
1716 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0) {
1717 PetscCheck(gys <= -3, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1718 "Periodic J-face ghost repair requires DMDA stencil width at least 3.");
1719 for (PetscInt k = gzs; k < gze; k++) for (PetscInt i = gxs; i < gxe; i++)
1720 array[k][-1][i] = array[k][-3][i];
1721 }
1722 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == my) {
1723 PetscCheck(gye > my + 2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1724 "Periodic J-face ghost repair requires DMDA stencil width at least 3.");
1725 for (PetscInt k = gzs; k < gze; k++) for (PetscInt i = gxs; i < gxe; i++)
1726 array[k][my][i] = array[k][my + 2][i];
1727 }
1728 } else if (face_direction == 'k') {
1729 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0) {
1730 PetscCheck(gzs <= -3, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1731 "Periodic K-face ghost repair requires DMDA stencil width at least 3.");
1732 for (PetscInt j = gys; j < gye; j++) for (PetscInt i = gxs; i < gxe; i++)
1733 array[-1][j][i] = array[-3][j][i];
1734 }
1735 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == mz) {
1736 PetscCheck(gze > mz + 2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
1737 "Periodic K-face ghost repair requires DMDA stencil width at least 3.");
1738 for (PetscInt j = gys; j < gye; j++) for (PetscInt i = gxs; i < gxe; i++)
1739 array[mz][j][i] = array[mz + 2][j][i];
1740 }
1741 }
1742
1743 PetscCall(DMDAVecRestoreArray(dm, local_vec, &array));
1744 }
1745
1746 PetscFunctionReturn(0);
1747}
1748
1749#undef __FUNCT__
1750#define __FUNCT__ "UpdateLocalGhosts"
1751/**
1752 * @brief Internal helper implementation: `UpdateLocalGhosts()`.
1753 * @details Local to this translation unit.
1754 */
1755PetscErrorCode UpdateLocalGhosts(UserCtx* user, const char *fieldName)
1756{
1757 PetscErrorCode ierr;
1758 PetscMPIInt rank;
1759 Vec globalVec = NULL;
1760 Vec localVec = NULL;
1761 DM dm = NULL; // The DM associated with this field pair
1762 PetscInt dof = 0;
1763 char face_direction = '\0';
1764 PetscBool component_staggered = PETSC_FALSE;
1765
1766 PetscFunctionBeginUser; // Use User version for application code
1768 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
1769 LOG_ALLOW(GLOBAL, LOG_INFO, "Rank %d: Starting ghost update for field '%s'.\n", rank, fieldName);
1770
1771 // --- 1. Identify the correct Vectors and DM ---
1772 if (strcmp(fieldName, "Coordinates") == 0) {
1773 ierr = DMGetCoordinates(user->da, &globalVec); CHKERRQ(ierr);
1774 ierr = DMGetCoordinatesLocal(user->da, &localVec); CHKERRQ(ierr);
1775 dm = user->fda;
1776 dof = 3;
1777 } else if (strcmp(fieldName, "Ucat") == 0) {
1778 globalVec = user->Ucat;
1779 localVec = user->lUcat;
1780 dm = user->fda;
1781 dof = 3;
1782 } else if (strcmp(fieldName, "Ucont") == 0) {
1783 globalVec = user->Ucont;
1784 localVec = user->lUcont;
1785 dm = user->fda;
1786 dof = 3;
1787 component_staggered = PETSC_TRUE;
1788 } else if (strcmp(fieldName, "Ucont_o") == 0) {
1789 globalVec = user->Ucont_o;
1790 localVec = user->lUcont_o;
1791 dm = user->fda;
1792 dof = 3;
1793 component_staggered = PETSC_TRUE;
1794 } else if (strcmp(fieldName, "Ucont_rm1") == 0) {
1795 globalVec = user->Ucont_rm1;
1796 localVec = user->lUcont_rm1;
1797 dm = user->fda;
1798 dof = 3;
1799 component_staggered = PETSC_TRUE;
1800 } else if (strcmp(fieldName, "P") == 0) {
1801 globalVec = user->P;
1802 localVec = user->lP;
1803 dm = user->da;
1804 } else if (strcmp(fieldName, "Nu_t") == 0 || strcmp(fieldName, "Eddy Viscosity") == 0) {
1805 globalVec = user->Nu_t;
1806 localVec = user->lNu_t;
1807 dm = user->da;
1808 } else if (strcmp(fieldName, "CS") == 0 || strcmp(fieldName, "Cs") == 0) {
1809 globalVec = user->CS;
1810 localVec = user->lCs;
1811 dm = user->da;
1812 } else if (strcmp(fieldName, "Diffusivity") == 0) {
1813 globalVec = user->Diffusivity;
1814 localVec = user->lDiffusivity;
1815 dm = user->da;
1816 } else if (strcmp(fieldName, "DiffusivityGradient") == 0) {
1817 globalVec = user->DiffusivityGradient;
1818 localVec = user->lDiffusivityGradient;
1819 dm = user->fda;
1820 } else if (strcmp(fieldName, "Csi") == 0) {
1821 globalVec = user->Csi;
1822 localVec = user->lCsi;
1823 dm = user->fda;
1824 dof = 3;
1825 face_direction = 'i';
1826 } else if (strcmp(fieldName, "Eta") == 0) {
1827 globalVec = user->Eta;
1828 localVec = user->lEta;
1829 dm = user->fda;
1830 dof = 3;
1831 face_direction = 'j';
1832 } else if (strcmp(fieldName, "Zet") == 0) {
1833 globalVec = user->Zet;
1834 localVec = user->lZet;
1835 dm = user->fda;
1836 dof = 3;
1837 face_direction = 'k';
1838 }else if (strcmp(fieldName, "Nvert") == 0) {
1839 globalVec = user->Nvert;
1840 localVec = user->lNvert;
1841 dm = user->da;
1842 // Add other fields as needed
1843 } else if (strcmp(fieldName, "Aj") == 0) {
1844 globalVec = user->Aj;
1845 localVec = user->lAj;
1846 dm = user->da;
1847 } else if (strcmp(fieldName, "Cent") == 0) {
1848 globalVec = user->Cent;
1849 localVec = user->lCent;
1850 dm = user->fda;
1851 }else if (strcmp(fieldName, "GridSpace") == 0) {
1852 globalVec = user->GridSpace;
1853 localVec = user->lGridSpace;
1854 dm = user->fda;
1855 }else if (strcmp(fieldName, "Centx") == 0) {
1856 globalVec = user->Centx;
1857 localVec = user->lCentx;
1858 dm = user->fda;
1859 dof = 3;
1860 face_direction = 'i';
1861 }else if (strcmp(fieldName, "Centy") == 0) {
1862 globalVec = user->Centy;
1863 localVec = user->lCenty;
1864 dm = user->fda;
1865 dof = 3;
1866 face_direction = 'j';
1867 }else if (strcmp(fieldName, "Centz") == 0) {
1868 globalVec = user->Centz;
1869 localVec = user->lCentz;
1870 dm = user->fda;
1871 dof = 3;
1872 face_direction = 'k';
1873 }else if (strcmp(fieldName,"ICsi") == 0){
1874 globalVec = user->ICsi;
1875 localVec = user->lICsi;
1876 dm = user->fda;
1877 dof = 3;
1878 face_direction = 'i';
1879 }else if (strcmp(fieldName,"IEta") == 0){
1880 globalVec = user->IEta;
1881 localVec = user->lIEta;
1882 dm = user->fda;
1883 dof = 3;
1884 face_direction = 'i';
1885 }else if (strcmp(fieldName,"IZet") == 0){
1886 globalVec = user->IZet;
1887 localVec = user->lIZet;
1888 dm = user->fda;
1889 dof = 3;
1890 face_direction = 'i';
1891 }else if (strcmp(fieldName,"JCsi") == 0){
1892 globalVec = user->JCsi;
1893 localVec = user->lJCsi;
1894 dm = user->fda;
1895 dof = 3;
1896 face_direction = 'j';
1897 }else if (strcmp(fieldName,"JEta") == 0){
1898 globalVec = user->JEta;
1899 localVec = user->lJEta;
1900 dm = user->fda;
1901 dof = 3;
1902 face_direction = 'j';
1903 }else if (strcmp(fieldName,"JZet") == 0){
1904 globalVec = user->JZet;
1905 localVec = user->lJZet;
1906 dm = user->fda;
1907 dof = 3;
1908 face_direction = 'j';
1909 }else if (strcmp(fieldName,"KCsi") == 0){
1910 globalVec = user->KCsi;
1911 localVec = user->lKCsi;
1912 dm = user->fda;
1913 dof = 3;
1914 face_direction = 'k';
1915 }else if (strcmp(fieldName,"KEta") == 0){
1916 globalVec = user->KEta;
1917 localVec = user->lKEta;
1918 dm = user->fda;
1919 dof = 3;
1920 face_direction = 'k';
1921 }else if (strcmp(fieldName,"KZet") == 0){
1922 globalVec = user->KZet;
1923 localVec = user->lKZet;
1924 dm = user->fda;
1925 dof = 3;
1926 face_direction = 'k';
1927 }else if (strcmp(fieldName,"IAj") == 0){
1928 globalVec = user->IAj;
1929 localVec = user->lIAj;
1930 dm = user->da;
1931 dof = 1;
1932 face_direction = 'i';
1933 }else if (strcmp(fieldName,"JAj") == 0){
1934 globalVec = user->JAj;
1935 localVec = user->lJAj;
1936 dm = user->da;
1937 dof = 1;
1938 face_direction = 'j';
1939 }else if (strcmp(fieldName,"KAj") == 0){
1940 globalVec = user->KAj;
1941 localVec = user->lKAj;
1942 dm = user->da;
1943 dof = 1;
1944 face_direction = 'k';
1945 }else if (strcmp(fieldName,"Phi") == 0){ // Pressure correction term.
1946 globalVec = user->Phi;
1947 localVec = user->lPhi;
1948 dm = user->da;
1949 }else if (strcmp(fieldName,"Psi") == 0){ // Particle scalar property.
1950 globalVec = user->Psi;
1951 localVec = user->lPsi;
1952 dm = user->da;
1953 }else if (strcmp(fieldName,"Nvert_o") == 0){
1954 globalVec = user->Nvert_o;
1955 localVec = user->lNvert_o;
1956 dm = user->da;
1957 }else if (strcmp(fieldName,"ParticleCount") == 0){
1958 globalVec = user->ParticleCount;
1959 localVec = user->lParticleCount;
1960 dm = user->da;
1961 }else if (strcmp(fieldName,"K_Omega") == 0){
1962 globalVec = user->K_Omega;
1963 localVec = user->lK_Omega;
1964 dm = user->fda2;
1965 }else if (strcmp(fieldName,"K_Omega_o") == 0){
1966 globalVec = user->K_Omega_o;
1967 localVec = user->lK_Omega_o;
1968 dm = user->fda2;
1969 }else {
1970 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Field '%s' not recognized for ghost update.", fieldName);
1971 }
1972
1973 // --- 2. Check if components were found ---
1974 if (!globalVec) {
1975 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Global vector for field '%s' is NULL.", fieldName);
1976 }
1977 if (!localVec) {
1978 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Local vector for field '%s' is NULL.", fieldName);
1979 }
1980 if (!dm) {
1981 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM for field '%s' is NULL.", fieldName);
1982 }
1983
1984 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Identified components for '%s': DM=%p, GlobalVec=%p, LocalVec=%p.\n",
1985 rank, fieldName, (void*)dm, (void*)globalVec, (void*)localVec);
1986
1987 // --- 3. Optional Debugging: Norm Before Update ---
1988 // Use your logging convention check
1989 // if (get_log_level() >= LOG_LEVEL_DEBUG && is_function_allowed("UpdateLocalGhosts")) { // Example check
1990 if(get_log_level() == LOG_DEBUG && is_function_allowed(__func__)){
1991 PetscReal norm_global_before;
1992 ierr = VecNorm(globalVec, NORM_INFINITY, &norm_global_before); CHKERRQ(ierr);
1993 LOG_ALLOW(GLOBAL, LOG_INFO,"Max norm '%s' (Global) BEFORE Ghost Update: %g\n", fieldName, norm_global_before);
1994 // Optional: Norm of local vector before update (might contain old ghost values)
1995 // PetscReal norm_local_before;
1996 // ierr = VecNorm(localVec, NORM_INFINITY, &norm_local_before); CHKERRQ(ierr);
1997 // LOG_ALLOW(GLOBAL, LOG_DEBUG,"Max norm '%s' (Local) BEFORE Ghost Update: %g\n", fieldName, norm_local_before);
1998 }
1999
2000 // --- 4. Perform the Global-to-Local Transfer (Ghost Update) ---
2001 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Calling DMGlobalToLocalBegin/End for '%s'.\n", rank, fieldName);
2002 ierr = DMGlobalToLocalBegin(dm, globalVec, INSERT_VALUES, localVec); CHKERRQ(ierr);
2003 ierr = DMGlobalToLocalEnd(dm, globalVec, INSERT_VALUES, localVec); CHKERRQ(ierr);
2004 ierr = RepairPeriodicNormalFaceGhosts(user, dm, localVec, dof, face_direction,
2005 component_staggered); CHKERRQ(ierr);
2006 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Completed DMGlobalToLocalBegin/End for '%s'.\n", rank, fieldName);
2007
2008 // --- 5. Optional Debugging: Norm After Update ---
2009 // Use your logging convention check
2010 // if (get_log_level() >= LOG_LEVEL_DEBUG && is_function_allowed("UpdateLocalGhosts")) { // Example check
2011 if(get_log_level() == LOG_DEBUG && is_function_allowed(__func__)){ // Using your specific check
2012 PetscReal norm_local_after;
2013 ierr = VecNorm(localVec, NORM_INFINITY, &norm_local_after); CHKERRQ(ierr);
2014 LOG_ALLOW(GLOBAL, LOG_INFO,"Max norm '%s' (Local) AFTER Ghost Update: %g\n", fieldName, norm_local_after);
2015
2016 // --- 6. Optional Debugging: Specific Point Checks (Example for Ucat on Rank 0/1) ---
2017 // (Keep this conditional if it's only for specific debug scenarios)
2018 if (strcmp(fieldName, "Ucat") == 0) { // Only do detailed checks for Ucat for now
2019 PetscMPIInt rank_test;
2020 MPI_Comm_rank(PETSC_COMM_WORLD, &rank_test);
2021
2022 // Get Local Info needed for indexing checks
2023 DMDALocalInfo info_check;
2024 ierr = DMDAGetLocalInfo(dm, &info_check); CHKERRQ(ierr); // Use the correct dm
2025
2026 // Buffer for array pointer
2027 Cmpnts ***lUcat_arr_test = NULL;
2028 PetscErrorCode ierr_test = 0;
2029
2030 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Testing '%s' access immediately after ghost update...\n", rank_test, fieldName);
2031 ierr_test = DMDAVecGetArrayDOFRead(dm, localVec, &lUcat_arr_test); // Use correct dm and localVec
2032
2033 if (ierr_test) {
2034 LOG_ALLOW(LOCAL, LOG_ERROR, "Rank %d: ERROR %d getting '%s' array after ghost update!\n", rank_test, ierr_test, fieldName);
2035 } else if (!lUcat_arr_test) {
2036 LOG_ALLOW(LOCAL, LOG_ERROR, "Rank %d: ERROR NULL pointer getting '%s' array after ghost update!\n", rank_test, fieldName);
2037 }
2038 else {
2039 // Check owned interior point (e.g., first interior point)
2040 PetscInt k_int = info_check.zs + (info_check.zm > 1 ? 1 : 0); // Global k index (at least zs+1 if possible)
2041 PetscInt j_int = info_check.ys + (info_check.ym > 1 ? 1 : 0); // Global j index
2042 PetscInt i_int = info_check.xs + (info_check.xm > 1 ? 1 : 0); // Global i index
2043 // Ensure indices are within global bounds if domain is very small
2044 //if (k_int >= info_check.mz-1) k_int = info_check.mz-2; if (k_int < 1) k_int = 1;
2045 //if (j_int >= info_check.my-1) j_int = info_check.my-2; if (j_int < 1) j_int = 1;
2046 // if (i_int >= info_check.mx-1) i_int = info_check.mx-2; if (i_int < 1) i_int = 1;
2047 // clamp k_int to [1 .. mz-2]
2048 if (k_int >= info_check.mz - 1) {
2049 k_int = info_check.mz - 2;
2050 }
2051 if (k_int < 1) {
2052 k_int = 1;
2053 }
2054
2055 // clamp j_int to [1 .. my-2]
2056 if (j_int >= info_check.my - 1) {
2057 j_int = info_check.my - 2;
2058 }
2059 if (j_int < 1) {
2060 j_int = 1;
2061 }
2062
2063 // clamp i_int to [1 .. mx-2]
2064 if (i_int >= info_check.mx - 1) {
2065 i_int = info_check.mx - 2;
2066 }
2067 if (i_int < 1) {
2068 i_int = 1;
2069 }
2070
2071 // Only attempt read if indices are actually owned (relevant for multi-rank)
2072 if (k_int >= info_check.zs && k_int < info_check.zs + info_check.zm &&
2073 j_int >= info_check.ys && j_int < info_check.ys + info_check.ym &&
2074 i_int >= info_check.xs && i_int < info_check.xs + info_check.xm)
2075 {
2076 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Attempting test read OWNED INTERIOR [%d][%d][%d] (Global)\n", rank_test, k_int, j_int, i_int);
2077 Cmpnts test_val_owned_interior = lUcat_arr_test[k_int][j_int][i_int];
2078 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: SUCCESS reading owned interior: x=%g\n", rank_test, test_val_owned_interior.x);
2079 } else {
2080 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Skipping interior test read for non-owned index [%d][%d][%d].\n", rank_test, k_int, j_int, i_int);
2081 }
2082
2083
2084 // Check owned boundary point (e.g., first owned point)
2085 PetscInt k_bnd = info_check.zs; // Global k index
2086 PetscInt j_bnd = info_check.ys; // Global j index
2087 PetscInt i_bnd = info_check.xs; // Global i index
2088 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Attempting test read OWNED BOUNDARY [%d][%d][%d] (Global)\n", rank_test, k_bnd, j_bnd, i_bnd);
2089 Cmpnts test_val_owned_boundary = lUcat_arr_test[k_bnd][j_bnd][i_bnd];
2090 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: SUCCESS reading owned boundary: x=%g\n", rank_test, test_val_owned_boundary.x);
2091
2092
2093 // Check ghost point (e.g., one layer below in k, if applicable)
2094 if (info_check.zs > 0) { // Only if there's a rank below
2095 PetscInt k_ghost = info_check.zs - 1;
2096 PetscInt j_ghost = info_check.ys; // Use start of owned y, simple example
2097 PetscInt i_ghost = info_check.xs; // Use start of owned x, simple example
2098 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Attempting test read GHOST [%d][%d][%d] (Global)\n", rank_test, k_ghost, j_ghost, i_ghost);
2099 Cmpnts test_val_ghost = lUcat_arr_test[k_ghost][j_ghost][i_ghost];
2100 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: SUCCESS reading ghost: x=%g\n", rank_test, test_val_ghost.x);
2101 } else {
2102 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Skipping ghost test read (zs=0).\n", rank_test);
2103 }
2104
2105 // Restore the array
2106 ierr_test = DMDAVecRestoreArrayDOFRead(dm, localVec, &lUcat_arr_test);
2107 if(ierr_test){ LOG_ALLOW(LOCAL, LOG_ERROR, "Rank %d: ERROR %d restoring '%s' array after test read!\n", rank_test, ierr_test, fieldName); }
2108 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d: Finished testing '%s' access.\n", rank_test, fieldName);
2109 }
2110 } // end if Ucat
2111 } // end debug logging check
2112
2113 LOG_ALLOW(GLOBAL, LOG_INFO, "Rank %d: Completed ghost update for field '%s'.\n", rank, fieldName);
2115 PetscFunctionReturn(0);
2116}
2117
2118#undef __FUNCT__
2119#define __FUNCT__ "SetupBoundaryConditions"
2120/**
2121 * @brief Internal helper implementation: `SetupBoundaryConditions()`.
2122 * @details Local to this translation unit.
2123 */
2124PetscErrorCode SetupBoundaryConditions(SimCtx *simCtx)
2125{
2126 PetscErrorCode ierr;
2127 PetscFunctionBeginUser;
2128
2130
2131 LOG_ALLOW(GLOBAL,LOG_INFO, "--- Setting up Boundary Conditions ---\n");
2132 // --- Phase 1: Parse and initialize BC configuration for all blocks ---
2133 LOG_ALLOW(GLOBAL,LOG_INFO,"Parsing BC configuration files and initializing boundary condition data structures.\n");
2134 UserCtx *user_finest = simCtx->usermg.mgctx[simCtx->usermg.mglevels-1].user;
2135 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
2136 LOG_ALLOW(GLOBAL,LOG_DEBUG, " -> Processing Block %d:\n", bi);
2137
2138 // --- Generate the filename for the current block ---
2139 const char *current_bc_filename = simCtx->bcs_files[bi];
2140 LOG_ALLOW(GLOBAL,LOG_DEBUG," -> Processing Block %d using config file '%s'\n", bi, current_bc_filename);
2141 // This will populate user_finest[bi].boundary_faces
2142
2143 //ierr = ParseAllBoundaryConditions(&user_finest[bi],current_bc_filename); CHKERRQ(ierr);
2144
2145 ierr = BoundarySystem_Initialize(&user_finest[bi], current_bc_filename); CHKERRQ(ierr);
2146 }
2147
2148 // Propogate BC Configuration to coarser levels.
2149 ierr = PropagateBoundaryConfigToCoarserLevels(simCtx); CHKERRQ(ierr);
2150
2151 // Validate the geometric contract before any metric consumes periodic geometry.
2152 for (PetscInt level = simCtx->usermg.mglevels - 1; level >= 0; level--) {
2153 UserCtx *level_users = simCtx->usermg.mgctx[level].user;
2154 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
2155 ierr = ValidatePeriodicGeometry(&level_users[bi]); CHKERRQ(ierr);
2156 }
2157 }
2158
2159 // --- Calculate Grid Metrics (requires BC configuration) ---
2160 // NOTE: This MUST be called here (after BC initialization but before inlet/outlet calculations) because:
2161 // 1. Periodic BC corrections in metric calculations need boundary_faces data to be populated
2162 // 2. Inlet/Outlet area calculations (below) require computed metrics (Csi, Eta, Zet) to be available
2163 // Previously this was in SetupGridAndSolvers, but that caused metrics to be computed without BC info.
2164 LOG_ALLOW(GLOBAL,LOG_INFO,"Computing grid metrics with boundary condition information.\n");
2165 ierr = CalculateAllGridMetrics(simCtx); CHKERRQ(ierr);
2166
2167 // --- Phase 2: Calculate inlet/outlet properties (requires computed metrics) ---
2168 LOG_ALLOW(GLOBAL,LOG_INFO,"Calculating inlet and outlet face properties.\n");
2169 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
2170 // Call the function to calculate the center of the inlet face & the inlet area, which may be used to calculate Boundary values.
2171 ierr = CalculateInletProperties(&user_finest[bi]); CHKERRQ(ierr);
2172
2173 // Call the function to calculate the center of the outlet face & the outlet area, which may be used to calculate Boundary values.
2174 ierr = CalculateOutletProperties(&user_finest[bi]); CHKERRQ(ierr);
2175 }
2176
2177 LOG_ALLOW(GLOBAL,LOG_INFO, "--- Boundary Conditions setup complete ---\n");
2178
2179
2181 PetscFunctionReturn(0);
2182}
2183
2184/**
2185 * @brief Internal helper implementation: `Allocate3DArrayScalar()`.
2186 * @details Local to this translation unit.
2187 */
2188PetscErrorCode Allocate3DArrayScalar(PetscReal ****array, PetscInt nz, PetscInt ny, PetscInt nx)
2189{
2190 PetscErrorCode ierr;
2191 PetscReal ***data;
2192 PetscReal *dataContiguous;
2193 PetscInt k, j;
2194
2195 PetscFunctionBegin;
2196 /* Step 1: Allocate memory for an array of nz layer pointers (zero-initialized) */
2197 ierr = PetscCalloc1(nz, &data); CHKERRQ(ierr);
2198
2199 /* Step 2: Allocate memory for all row pointers (nz * ny pointers) */
2200 ierr = PetscCalloc1(nz * ny, &data[0]); CHKERRQ(ierr);
2201 for (k = 1; k < nz; k++) {
2202 data[k] = data[0] + k * ny;
2203 }
2204
2205 /* Step 3: Allocate one contiguous block for all data elements (nz*ny*nx) */
2206 ierr = PetscCalloc1(nz * ny * nx, &dataContiguous); CHKERRQ(ierr);
2207
2208 /* Build the 3D pointer structure: each row pointer gets the correct segment of data */
2209 for (k = 0; k < nz; k++) {
2210 for (j = 0; j < ny; j++) {
2211 data[k][j] = dataContiguous + (k * ny + j) * nx;
2212 /* Memory is already zeroed by PetscCalloc1, so no manual initialization is needed */
2213 }
2214 }
2215 *array = data;
2216 PetscFunctionReturn(0);
2217}
2218
2219/**
2220 * @brief Internal helper implementation: `Deallocate3DArrayScalar()`.
2221 * @details Local to this translation unit.
2222 */
2223PetscErrorCode Deallocate3DArrayScalar(PetscReal ***array, PetscInt nz, PetscInt ny)
2224{
2225 PetscErrorCode ierr;
2226 (void)nz;
2227 (void)ny;
2228
2229 PetscFunctionBegin;
2230 if (!array || !array[0] || !array[0][0] ) { // Added more robust check
2231 LOG_ALLOW(GLOBAL, LOG_WARNING, "Deallocate3DArrayScalar called with potentially unallocated or NULL array.\n");
2232 if (array) {
2233 if (array[0]) { // Check if row pointers might exist
2234 // Cannot safely access array[0][0] if array[0] might be invalid/freed
2235 // Standard deallocation below assumes valid pointers.
2236 ierr = PetscFree(array[0]); CHKERRQ(ierr); // Free row pointers if they exist
2237 }
2238 ierr = PetscFree(array); CHKERRQ(ierr); // Free layer pointers if they exist
2239 }
2240 PetscFunctionReturn(0);
2241 }
2242
2243 // --- Standard Deallocation (assuming valid allocation) ---
2244
2245 /* 1. Free the contiguous block of PetscReal values.
2246 The starting address was stored in array[0][0]. */
2247 ierr = PetscFree(array[0][0]); CHKERRQ(ierr); // Free the ACTUAL DATA
2248
2249 /* 2. Free the contiguous block of row pointers.
2250 The starting address was stored in array[0]. */
2251 ierr = PetscFree(array[0]); CHKERRQ(ierr); // Free the ROW POINTERS
2252
2253 /* 3. Free the layer pointer array.
2254 The starting address is 'array' itself. */
2255 ierr = PetscFree(array); CHKERRQ(ierr); // Free the LAYER POINTERS
2256
2257 PetscFunctionReturn(0);
2258}
2259
2260/**
2261 * @brief Implementation of \ref Allocate3DArrayVector().
2262 * @details Full API contract (arguments, ownership, side effects) is documented with
2263 * the header declaration in `include/setup.h`.
2264 * @see Allocate3DArrayVector()
2265 */
2266PetscErrorCode Allocate3DArrayVector(Cmpnts ****array, PetscInt nz, PetscInt ny, PetscInt nx)
2267{
2268 PetscErrorCode ierr;
2269 Cmpnts ***data;
2270 Cmpnts *dataContiguous;
2271 PetscInt k, j;
2272 PetscMPIInt rank;
2273
2274 PetscFunctionBegin;
2275
2276 ierr = MPI_Comm_rank(PETSC_COMM_WORLD,&rank);
2277
2278 /* Step 1: Allocate memory for nz layer pointers (zeroed) */
2279 ierr = PetscCalloc1(nz, &data); CHKERRQ(ierr);
2280
2281 LOG_ALLOW(LOCAL,LOG_DEBUG," [Rank %d] memory allocated for outermost layer (%d k-layer pointers).\n",rank,nz);
2282
2283 /* Step 2: Allocate memory for all row pointers (nz * ny pointers) */
2284 ierr = PetscCalloc1(nz * ny, &data[0]); CHKERRQ(ierr);
2285 for (k = 1; k < nz; k++) {
2286 data[k] = data[0] + k * ny;
2287 }
2288
2289 LOG_ALLOW(LOCAL,LOG_DEBUG,"[Rank %d] memory allocated for %dx%d row pointers.\n",rank,nz,ny);
2290
2291 /* Step 3: Allocate one contiguous block for nz*ny*nx Cmpnts structures (zeroed) */
2292 ierr = PetscCalloc1(nz * ny * nx, &dataContiguous); CHKERRQ(ierr);
2293
2294 LOG_ALLOW(GLOBAL,LOG_DEBUG,"[Rank %d] memory allocated for contigous block of %dx%dx%d Cmpnts structures).\n",rank,nz,ny,nx);
2295
2296 /* Build the 3D pointer structure for vector data */
2297 for (k = 0; k < nz; k++) {
2298 for (j = 0; j < ny; j++) {
2299 data[k][j] = dataContiguous + (k * ny + j) * nx;
2300 /* The PetscCalloc1 call has already initialized each Cmpnts to zero. */
2301 }
2302 }
2303
2304 LOG_ALLOW(GLOBAL,LOG_DEBUG,"[Rank %d] 3D pointer structure for vector data created. \n",rank);
2305
2306 *array = data;
2307 PetscFunctionReturn(0);
2308}
2309
2310/**
2311 * @brief Implementation of \ref Deallocate3DArrayVector().
2312 * @details Full API contract (arguments, ownership, side effects) is documented with
2313 * the header declaration in `include/setup.h`.
2314 * @see Deallocate3DArrayVector()
2315 */
2316 PetscErrorCode Deallocate3DArrayVector(Cmpnts ***array, PetscInt nz, PetscInt ny)
2317{
2318 PetscErrorCode ierr;
2319 (void)nz;
2320 (void)ny;
2321
2322 PetscFunctionBegin;
2323 // If array is NULL or hasn't been allocated properly, just return.
2324 if (!array || !array[0] || !array[0][0] ) {
2325 LOG_ALLOW(GLOBAL, LOG_WARNING, "Deallocate3DArrayVector called with potentially unallocated or NULL array.\n");
2326 // Attempt to free what might exist, but be cautious
2327 if (array) {
2328 if (array[0]) { // Check if row pointers were allocated
2329 // We don't have a direct pointer to the contiguous data block
2330 // saved separately in this allocation scheme. The allocation relies
2331 // on array[0][0] pointing to it. If array[0] was freed first,
2332 // accessing array[0][0] is unsafe.
2333 // The allocation scheme where the contiguous data block is not
2334 // stored separately makes safe deallocation tricky if freeing
2335 // happens out of order or if parts are NULL.
2336
2337 // A SAFER ALLOCATION/DEALLOCATION would store the data pointer separately.
2338 // Given the current allocation scheme, the order MUST be:
2339 // 1. Free the data block (pointed to by array[0][0])
2340 // 2. Free the row pointer block (pointed to by array[0])
2341 // 3. Free the layer pointer block (pointed to by array)
2342
2343 // Let's assume the allocation was successful and pointers are valid.
2344 // Get pointer to the contiguous data block *before* freeing row pointers
2345 Cmpnts *dataContiguous = array[0][0];
2346 ierr = PetscFree(dataContiguous); CHKERRQ(ierr); // Free data block
2347
2348 // Now free the row pointers block
2349 ierr = PetscFree(array[0]); CHKERRQ(ierr); // Free row pointers
2350
2351 }
2352 // Finally, free the array of layer pointers
2353 ierr = PetscFree(array); CHKERRQ(ierr);
2354 }
2355 PetscFunctionReturn(0); // Return gracefully if input was NULL initially
2356 }
2357
2358
2359 // --- Standard Deallocation (assuming valid allocation) ---
2360
2361 /* 1. Free the contiguous block of Cmpnts structures.
2362 The starting address was stored in array[0][0] by Allocate3DArrayVector. */
2363 ierr = PetscFree(array[0][0]); CHKERRQ(ierr); // Free the ACTUAL DATA
2364
2365 /* 2. Free the contiguous block of row pointers.
2366 The starting address was stored in array[0]. */
2367 ierr = PetscFree(array[0]); CHKERRQ(ierr); // Free the ROW POINTERS
2368
2369 /* 3. Free the layer pointer array.
2370 The starting address is 'array' itself. */
2371 ierr = PetscFree(array); CHKERRQ(ierr); // Free the LAYER POINTERS
2372
2373 PetscFunctionReturn(0);
2374}
2375
2376#undef __FUNCT__
2377#define __FUNCT__ "GetOwnedCellRange"
2378/**
2379 * @brief Internal helper implementation: `GetOwnedCellRange()`.
2380 * @details Local to this translation unit.
2381 */
2382PetscErrorCode GetOwnedCellRange(const DMDALocalInfo *info_nodes,
2383 PetscInt dim,
2384 PetscInt *xs_cell_global_out,
2385 PetscInt *xm_cell_local_out)
2386{
2387 PetscErrorCode ierr = 0; // Standard PETSc error code, not explicitly set here but good practice.
2388 PetscInt xs_node_global_rank; // Global index of the first node owned by this rank in the specified dimension.
2389 PetscInt num_nodes_owned_rank; // Number of nodes owned by this rank in this dimension (local count, excluding ghosts).
2390 PetscInt GlobalNodesInDim_from_info; // Total number of DA points in this dimension, from DMDALocalInfo.
2391
2392 PetscFunctionBeginUser;
2393
2394 // --- 1. Input Validation ---
2395 if (!info_nodes || !xs_cell_global_out || !xm_cell_local_out) {
2396 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null pointer passed to GetOwnedCellRange.");
2397 }
2398
2399 // --- 2. Extract Node Ownership and Global Dimension Information from DMDALocalInfo ---
2400 if (dim == 0) { // I-direction
2401 xs_node_global_rank = info_nodes->xs;
2402 num_nodes_owned_rank = info_nodes->xm;
2403 GlobalNodesInDim_from_info = info_nodes->mx;
2404 } else if (dim == 1) { // J-direction
2405 xs_node_global_rank = info_nodes->ys;
2406 num_nodes_owned_rank = info_nodes->ym;
2407 GlobalNodesInDim_from_info = info_nodes->my;
2408 } else if (dim == 2) { // K-direction
2409 xs_node_global_rank = info_nodes->zs;
2410 num_nodes_owned_rank = info_nodes->zm;
2411 GlobalNodesInDim_from_info = info_nodes->mz;
2412 } else {
2413 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid dimension %d in GetOwnedCellRange. Must be 0, 1, or 2.", dim);
2414 }
2415
2416 // --- 3. Correct for User-Defined Ghost Node ---
2417 // Per the function's contract (@warning), the DA size includes an extra, non-physical
2418 // node. We subtract 1 to get the true number of physical nodes for cell calculations.
2419 const PetscInt physical_nodes_in_dim = GlobalNodesInDim_from_info - 1;
2420
2421 // --- 4. Handle Edge Cases for Physical Domain Size ---
2422 // If the physical domain has 0 or 1 node, no cells can be formed.
2423 if (physical_nodes_in_dim <= 1) {
2424 *xs_cell_global_out = xs_node_global_rank; // Still report the rank's starting node
2425 *xm_cell_local_out = 0; // But 0 cells
2426 PetscFunctionReturn(0);
2427 }
2428
2429 // --- 5. Determine Cell Ownership Based on Corrected Node Ownership ---
2430 // The first cell this rank *could* define has its origin at the first node this rank owns.
2431 *xs_cell_global_out = xs_node_global_rank;
2432
2433 // If the rank owns no nodes in this dimension, it can't form any cell origins.
2434 if (num_nodes_owned_rank == 0) {
2435 *xm_cell_local_out = 0;
2436 } else {
2437 // --- BUG FIX APPLIED HERE ---
2438 // The previous logic incorrectly assumed a cell's end node (N_{k+1}) must be on the
2439 // same rank as its origin node (N_k). The correct logic is to find the intersection
2440 // between the nodes this rank owns and the nodes that are valid origins globally.
2441
2442 // The first node owned by the rank is its first potential origin.
2443 PetscInt first_owned_origin = xs_node_global_rank;
2444
2445 // The absolute last node owned by this rank. Any node up to and including this one
2446 // is a potential cell origin from this rank's perspective.
2447 PetscInt last_node_owned_by_rank = xs_node_global_rank + num_nodes_owned_rank - 1;
2448
2449 // The absolute last node in the entire PHYSICAL domain that can serve as a cell origin.
2450 // If there are `N` physical nodes (0 to N-1), this index is `N-2`.
2451 PetscInt last_possible_origin_global_idx = physical_nodes_in_dim - 2;
2452
2453 // The actual last origin this rank can provide is the *minimum* of what it owns
2454 // and what is globally possible. This correctly handles both ranks in the middle of
2455 // the domain and the very last rank.
2456 PetscInt actual_last_origin_this_rank_can_form = PetscMin(last_node_owned_by_rank, last_possible_origin_global_idx);
2457
2458 // If the first potential origin this rank owns is already beyond the actual last
2459 // origin it can form, then this rank forms no valid cell origins. This happens if
2460 // the rank only owns the very last physical node.
2461 if (first_owned_origin > actual_last_origin_this_rank_can_form) {
2462 *xm_cell_local_out = 0;
2463 } else {
2464 // The number of cells is the count of valid origins this rank owns.
2465 // (Count = Last Index - First Index + 1)
2466 *xm_cell_local_out = actual_last_origin_this_rank_can_form - first_owned_origin + 1;
2467 }
2468 }
2469
2470 PetscFunctionReturn(ierr);
2471}
2472
2473#undef __FUNCT__
2474#define __FUNCT__ "ComputeAndStoreNeighborRanks"
2475/**
2476 * @brief Internal helper implementation: `ComputeAndStoreNeighborRanks()`.
2477 * @details Local to this translation unit.
2478 */
2480{
2481 PetscErrorCode ierr;
2482 PetscMPIInt rank;
2483 PetscMPIInt size; // MPI communicator size
2484 const PetscMPIInt *neighbor_ranks_ptr; // Pointer to raw neighbor data from PETSc
2485
2486 PetscFunctionBeginUser;
2488 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
2489 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size); CHKERRQ(ierr); // Get MPI size for validation
2490
2491 LOG_ALLOW(GLOBAL, LOG_INFO, "Rank %d: Computing DMDA neighbor ranks.\n", rank);
2492
2493 if (!user || !user->da) {
2494 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "UserCtx or user->da is NULL in ComputeAndStoreNeighborRanks.");
2495 }
2496
2497 // Get the neighbor information from the DMDA
2498 // neighbor_ranks_ptr will point to an internal PETSc array of 27 ranks.
2499 ierr = DMDAGetNeighbors(user->da, &neighbor_ranks_ptr); CHKERRQ(ierr);
2500
2501 // Log the raw values from DMDAGetNeighbors for boundary-relevant directions for debugging
2502 LOG_ALLOW_SYNC(GLOBAL, LOG_DEBUG, "[Rank %d]Raw DMDAGetNeighbors: xm_raw=%d, xp_raw=%d, ym_raw=%d, yp_raw=%d, zm_raw=%d, zp_raw=%d. MPI_PROC_NULL is %d.\n",
2503 rank,
2504 neighbor_ranks_ptr[12], neighbor_ranks_ptr[14],
2505 neighbor_ranks_ptr[10], neighbor_ranks_ptr[16],
2506 neighbor_ranks_ptr[4], neighbor_ranks_ptr[22],
2507 (int)MPI_PROC_NULL);
2508
2509 // PETSc standard indices for 3D face neighbors from the 27-point stencil:
2510 // Index = k_offset*9 + j_offset*3 + i_offset (where offsets -1,0,1 map to 0,1,2)
2511 // Center: (i_off=1, j_off=1, k_off=1) => 1*9 + 1*3 + 1 = 13
2512 // X-min: (i_off=0, j_off=1, k_off=1) => 1*9 + 1*3 + 0 = 12
2513 // X-plus: (i_off=2, j_off=1, k_off=1) => 1*9 + 1*3 + 2 = 14
2514 // Y-min: (i_off=1, j_off=0, k_off=1) => 1*9 + 0*3 + 1 = 10
2515 // Y-plus: (i_off=1, j_off=2, k_off=1) => 1*9 + 2*3 + 1 = 16
2516 // Z-min: (i_off=1, j_off=1, k_off=0) => 0*9 + 1*3 + 1 = 4
2517 // Z-plus: (i_off=1, j_off=1, k_off=2) => 2*9 + 1*3 + 1 = 22
2518
2519 if (neighbor_ranks_ptr[13] != rank) {
2520 LOG_ALLOW(GLOBAL, LOG_WARNING, "Rank %d: DMDAGetNeighbors center index (13) is %d, expected current rank %d. Neighbor indexing might be non-standard or DMDA small.\n",
2521 rank, neighbor_ranks_ptr[13], rank);
2522 // This warning is important. If the center isn't the current rank, the offsets are likely wrong.
2523 // However, PETSc should ensure this unless the DM is too small for a 3x3x3 stencil.
2524 }
2525
2526 // Assign and sanitize each neighbor rank
2527 PetscMPIInt temp_neighbor;
2528
2529 temp_neighbor = neighbor_ranks_ptr[12]; // xm
2530 if (temp_neighbor < 0 || temp_neighbor >= size) {
2531 LOG_ALLOW(GLOBAL, LOG_WARNING, "[Rank %d] Correcting invalid xm neighbor %d to MPI_PROC_NULL (%d).\n", rank, temp_neighbor, (int)MPI_PROC_NULL);
2532 user->neighbors.rank_xm = MPI_PROC_NULL;
2533 } else {
2534 user->neighbors.rank_xm = temp_neighbor;
2535 }
2536
2537 temp_neighbor = neighbor_ranks_ptr[14]; // xp
2538 if (temp_neighbor < 0 || temp_neighbor >= size) {
2539 LOG_ALLOW(GLOBAL, LOG_WARNING, "[Rank %d] Correcting invalid xp neighbor %d to MPI_PROC_NULL (%d).\n", rank, temp_neighbor, (int)MPI_PROC_NULL);
2540 user->neighbors.rank_xp = MPI_PROC_NULL;
2541 } else {
2542 user->neighbors.rank_xp = temp_neighbor;
2543 }
2544
2545 temp_neighbor = neighbor_ranks_ptr[10]; // ym
2546 if (temp_neighbor < 0 || temp_neighbor >= size) {
2547 LOG_ALLOW(GLOBAL, LOG_WARNING, "[Rank %d] Correcting invalid ym neighbor %d to MPI_PROC_NULL (%d).\n", rank, temp_neighbor, (int)MPI_PROC_NULL);
2548 user->neighbors.rank_ym = MPI_PROC_NULL;
2549 } else {
2550 user->neighbors.rank_ym = temp_neighbor;
2551 }
2552
2553 temp_neighbor = neighbor_ranks_ptr[16]; // yp
2554 if (temp_neighbor < 0 || temp_neighbor >= size) {
2555 // The log for index 16 was "zm" in your output, should be yp
2556 LOG_ALLOW(GLOBAL, LOG_WARNING, "[Rank %d] Correcting invalid yp neighbor (raw index 16) %d to MPI_PROC_NULL (%d).\n", rank, temp_neighbor, (int)MPI_PROC_NULL);
2557 user->neighbors.rank_yp = MPI_PROC_NULL;
2558 } else {
2559 user->neighbors.rank_yp = temp_neighbor;
2560 }
2561
2562 temp_neighbor = neighbor_ranks_ptr[4]; // zm
2563 if (temp_neighbor < 0 || temp_neighbor >= size) {
2564 LOG_ALLOW(GLOBAL, LOG_WARNING, "[Rank %d] Correcting invalid zm neighbor %d to MPI_PROC_NULL (%d).\n", rank, temp_neighbor, (int)MPI_PROC_NULL);
2565 user->neighbors.rank_zm = MPI_PROC_NULL;
2566 } else {
2567 user->neighbors.rank_zm = temp_neighbor;
2568 }
2569
2570 temp_neighbor = neighbor_ranks_ptr[22]; // zp
2571 if (temp_neighbor < 0 || temp_neighbor >= size) {
2572 LOG_ALLOW(GLOBAL, LOG_WARNING, "[Rank %d] Correcting invalid zp neighbor %d to MPI_PROC_NULL (%d).\n", rank, temp_neighbor, (int)MPI_PROC_NULL);
2573 user->neighbors.rank_zp = MPI_PROC_NULL;
2574 } else {
2575 user->neighbors.rank_zp = temp_neighbor;
2576 }
2577
2578 LOG_ALLOW_SYNC(GLOBAL, LOG_DEBUG, "[Rank %d] Stored user->neighbors: xm=%d, xp=%d, ym=%d, yp=%d, zm=%d, zp=%d\n", rank,
2579 user->neighbors.rank_xm, user->neighbors.rank_xp,
2580 user->neighbors.rank_ym, user->neighbors.rank_yp,
2581 user->neighbors.rank_zm, user->neighbors.rank_zp);
2582 PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT); // Ensure logs are flushed
2583
2584 // Note: neighbor_ranks_ptr memory is managed by PETSc, do not free it.
2586 PetscFunctionReturn(0);
2587}
2588
2589#undef __FUNCT__
2590#define __FUNCT__ "SetDMDAProcLayout"
2591/**
2592 * @brief Internal helper implementation: `SetDMDAProcLayout()`.
2593 * @details Local to this translation unit.
2594 */
2595PetscErrorCode SetDMDAProcLayout(DM dm, UserCtx *user)
2596{
2597 PetscErrorCode ierr;
2598 PetscMPIInt size, rank;
2599 PetscInt px = PETSC_DECIDE, py = PETSC_DECIDE, pz = PETSC_DECIDE;
2600 PetscBool px_set = PETSC_FALSE, py_set = PETSC_FALSE, pz_set = PETSC_FALSE;
2601 SimCtx *simCtx = user->simCtx;
2602
2603 // Set no.of processors in direction 1
2604 if(simCtx->da_procs_x) {
2605 px_set = PETSC_TRUE;
2606 px = simCtx->da_procs_x;
2607 }
2608 // Set no.of processors in direction 2
2609 if(simCtx->da_procs_y) {
2610 py_set = PETSC_TRUE;
2611 py = simCtx->da_procs_y;
2612 }
2613 // Set no.of processors in direction 1
2614 if(simCtx->da_procs_z) {
2615 pz_set = PETSC_TRUE;
2616 pz = simCtx->da_procs_z;
2617 }
2618
2619 PetscFunctionBeginUser;
2621 ierr = MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size); CHKERRQ(ierr);
2622 ierr = MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank); CHKERRQ(ierr);
2623 LOG_ALLOW(GLOBAL, LOG_INFO, "Rank %d: Configuring DMDA processor layout for %d total processes.\n", rank, size);
2624
2625 // --- Validate User Input (Optional but Recommended) ---
2626 // Check if specified processor counts multiply to the total MPI size
2627 if (px_set && py_set && pz_set) {
2628 if (px * py * pz != size) {
2629 SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_INCOMP,
2630 "Specified processor layout %d x %d x %d = %d does not match MPI size %d",
2631 px, py, pz, px * py * pz, size);
2632 }
2633 LOG_ALLOW(GLOBAL, LOG_INFO, "Using specified processor layout: %d x %d x %d\n", px, py, pz);
2634 } else if (px_set || py_set || pz_set) {
2635 // If only some are set, PETSC_DECIDE will be used for others
2636 LOG_ALLOW(GLOBAL, LOG_INFO, "Using partially specified processor layout: %d x %d x %d (PETSC_DECIDE for unspecified)\n", px, py, pz);
2637 } else {
2638 LOG_ALLOW(GLOBAL, LOG_INFO, "Using fully automatic processor layout (PETSC_DECIDE x PETSC_DECIDE x PETSC_DECIDE)\n");
2639 }
2640 // Additional checks: Ensure px, py, pz are positive if set
2641 if ((px_set && px <= 0) || (py_set && py <= 0) || (pz_set && pz <= 0)) {
2642 SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Specified processor counts must be positive.");
2643 }
2644
2645
2646 // --- Apply the layout to the DMDA ---
2647 ierr = DMDASetNumProcs(dm, px, py, pz); CHKERRQ(ierr);
2648 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Rank %d: DMDASetNumProcs called with px=%d, py=%d, pz=%d.\n", rank, px, py, pz);
2649
2650 // --- Store the values in UserCtx (Optional) ---
2651 // Note: If PETSC_DECIDE was used, PETSc calculates the actual values during DMSetUp.
2652 // We store the *requested* values here. To get the *actual* values used,
2653 // you would need to call DMDAGetInfo after DMSetUp.
2654 /*
2655 if (user) {
2656 user->procs_x = px;
2657 user->procs_y = py;
2658 user->procs_z = pz;
2659 }
2660 */
2662 PetscFunctionReturn(0);
2663}
2664
2665#undef __FUNCT__
2666#define __FUNCT__ "SetupDomainRankInfo"
2667/**
2668 * @brief Implementation of \ref SetupDomainRankInfo().
2669 * @details Full API contract (arguments, ownership, side effects) is documented with
2670 * the header declaration in `include/setup.h`.
2671 * @see SetupDomainRankInfo()
2672 */
2673PetscErrorCode SetupDomainRankInfo(SimCtx *simCtx)
2674{
2675 PetscErrorCode ierr;
2676 PetscInt nblk = simCtx->block_number;
2677 PetscInt size = simCtx->size;
2678 BoundingBox *final_bboxlist = NULL;
2679
2680 PetscFunctionBeginUser;
2682
2683 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting full rank communication setup for %d block(s).\n", nblk);
2684
2685 UserCtx *user_finest = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
2686
2687 // --- Step 1: Compute neighbor ranks (unchanged) ---
2688 for (int bi = 0; bi < nblk; bi++) {
2689 ierr = ComputeAndStoreNeighborRanks(&user_finest[bi]); CHKERRQ(ierr);
2690 }
2691 LOG_ALLOW(GLOBAL, LOG_INFO, "Neighbor ranks computed and stored for all blocks.\n");
2692
2693 // --- Step 2: Allocate the final, unified list on ALL ranks ---
2694 // Every rank will build this list in parallel.
2695 ierr = PetscMalloc1(size * nblk, &final_bboxlist); CHKERRQ(ierr);
2696
2697 // --- Step 3: Loop through each block, gather then broadcast its bbox list ---
2698 for (int bi = 0; bi < nblk; bi++) {
2699 // This is a temporary pointer for the current block's list.
2700 BoundingBox *block_bboxlist = NULL;
2701
2702 LOG_ALLOW(GLOBAL, LOG_INFO, "Processing bounding boxes for block %d...\n", bi);
2703
2704 // A) GATHER: On rank 0, block_bboxlist is allocated and filled. On others, it's NULL.
2705 ierr = GatherAllBoundingBoxes(&user_finest[bi], &block_bboxlist); CHKERRQ(ierr);
2706 LOG_ALLOW(GLOBAL, LOG_DEBUG, " -> Gather complete for block %d.\n", bi);
2707
2708 // B) BROADCAST: On non-root ranks, block_bboxlist is allocated. Then, the data
2709 // from rank 0 is broadcast to all ranks. After this call, ALL ranks have
2710 // an identical, complete copy of the bounding boxes for the current block.
2711 ierr = BroadcastAllBoundingBoxes(&user_finest[bi], &block_bboxlist); CHKERRQ(ierr);
2712 LOG_ALLOW(GLOBAL, LOG_DEBUG, " -> Broadcast complete for block %d.\n", bi);
2713
2714 // C) ASSEMBLE: Every rank now copies the data for this block into the
2715 // correct segment of its final, unified list.
2716 for (int r = 0; r < size; r++) {
2717 // The layout is [r0b0, r1b0, ..., r(size-1)b0, r0b1, r1b1, ...]
2718 final_bboxlist[bi * size + r] = block_bboxlist[r];
2719 }
2720 LOG_ALLOW(GLOBAL, LOG_DEBUG, " -> Assembly into final list complete for block %d.\n", bi);
2721
2722 // D) CLEANUP: Free the temporary list for this block on ALL ranks before the next iteration.
2723 // Your helper functions use malloc, so we must use free.
2724 free(block_bboxlist);
2725 }
2726
2727 // --- Step 4: Assign the final pointer and run the last setup step ---
2728 simCtx->bboxlist = final_bboxlist;
2729 LOG_ALLOW(GLOBAL, LOG_INFO, "Final unified bboxlist created on all ranks and stored in SimCtx.\n");
2730
2731 ierr = SetupDomainCellDecompositionMap(&user_finest[0]); CHKERRQ(ierr);
2732 LOG_ALLOW(GLOBAL, LOG_INFO, "Domain Cell Composition set and broadcasted.\n");
2733
2734 LOG_ALLOW(GLOBAL, LOG_INFO, "SetupDomainRankInfo: Completed successfully.\n");
2735
2737 PetscFunctionReturn(0);
2738}
2739
2740#undef __FUNCT__
2741#define __FUNCT__ "Contra2Cart"
2742/**
2743 * @brief Internal helper implementation: `Contra2Cart()`.
2744 * @details Local to this translation unit.
2745 */
2746PetscErrorCode Contra2Cart(UserCtx *user)
2747{
2748 PetscErrorCode ierr;
2749 DMDALocalInfo info;
2750 Cmpnts ***lcsi_arr, ***leta_arr, ***lzet_arr; // Local metric arrays
2751 Cmpnts ***lucont_arr; // Local contravariant velocity array
2752 Cmpnts ***gucat_arr; // Global Cartesian velocity array
2753 PetscReal ***lnvert_arr; // Local Nvert array
2754 PetscReal ***laj_arr; // Local Jacobian Determinant inverse array
2755
2756 PetscFunctionBeginUser;
2758 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Starting Contravariant-to-Cartesian velocity transformation.\n");
2759
2760 // --- 1. Get DMDA Info and Check for Valid Inputs ---
2761 // All inputs (lUcont, lCsi, etc.) and outputs (Ucat) are on DMs from the UserCtx.
2762 // We get local info from fda, which governs the layout of most arrays here.
2763 ierr = DMDAGetLocalInfo(user->fda, &info); CHKERRQ(ierr);
2764 if (!user->lUcont || !user->lCsi || !user->lEta || !user->lZet || !user->lNvert || !user->Ucat) {
2765 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Contra2Cart requires lUcont, lCsi/Eta/Zet, lNvert, and Ucat to be non-NULL.");
2766 }
2767
2768
2769 // --- 2. Get Read-Only Array Access to Local Input Vectors (with ghosts) ---
2770 ierr = DMDAVecGetArrayRead(user->fda, user->lUcont, &lucont_arr); CHKERRQ(ierr);
2771 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, &lcsi_arr); CHKERRQ(ierr);
2772 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, &leta_arr); CHKERRQ(ierr);
2773 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, &lzet_arr); CHKERRQ(ierr);
2774 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, &lnvert_arr); CHKERRQ(ierr);
2775 ierr = DMDAVecGetArrayRead(user->da, user->lAj, &laj_arr); CHKERRQ(ierr);
2776
2777 // --- 3. Get Write-Only Array Access to the Global Output Vector ---
2778 // We compute for local owned cells and write into the global vector.
2779 // PETSc handles mapping the global indices to the correct local memory locations.
2780 ierr = DMDAVecGetArray(user->fda, user->Ucat, &gucat_arr); CHKERRQ(ierr);
2781
2782
2783 // --- 4. Define Loop Bounds for INTERIOR Cells ---
2784 // We use adjusted bounds to avoid calculating Ucat on the physical domain boundaries,
2785 // as these are typically set explicitly by boundary condition functions.
2786 // The stencils use indices like i-1, j-1, k-1, so we must start loops at least at index 1.
2787 PetscInt i_start = (info.xs == 0) ? info.xs + 1 : info.xs;
2788 PetscInt i_end = (info.xs + info.xm == info.mx) ? info.xs + info.xm - 1 : info.xs + info.xm;
2789
2790 PetscInt j_start = (info.ys == 0) ? info.ys + 1 : info.ys;
2791 PetscInt j_end = (info.ys + info.ym == info.my) ? info.ys + info.ym - 1 : info.ys + info.ym;
2792
2793 PetscInt k_start = (info.zs == 0) ? info.zs + 1 : info.zs;
2794 PetscInt k_end = (info.zs + info.zm == info.mz) ? info.zs + info.zm - 1 : info.zs + info.zm;
2795
2796 // --- 5. Main Computation Loop ---
2797 // Loops over the GLOBAL indices of interior cells owned by this rank.
2798 for (PetscInt k_cell = k_start; k_cell < k_end; ++k_cell) {
2799 for (PetscInt j_cell = j_start; j_cell < j_end; ++j_cell) {
2800 for (PetscInt i_cell = i_start; i_cell < i_end; ++i_cell) {
2801
2802 // Check if the cell is a fluid cell (not solid/blanked)
2803 // if (lnvert_arr[k_cell][j_cell][i_cell] > 0.1) continue; // Skip solid/blanked cells
2804
2805 // Transformation matrix [mat] is the metric tensor at the cell center,
2806 // estimated by averaging metrics from adjacent faces.
2807 PetscReal mat[3][3];
2808
2809 // PetscReal aj_center = laj_arr[k_cell+1][j_cell+1][i_cell+1];
2810
2811 mat[0][0] = 0.5 * (lcsi_arr[k_cell][j_cell][i_cell-1].x + lcsi_arr[k_cell][j_cell][i_cell].x); //* aj_center;
2812 mat[0][1] = 0.5 * (lcsi_arr[k_cell][j_cell][i_cell-1].y + lcsi_arr[k_cell][j_cell][i_cell].y); //* aj_center;
2813 mat[0][2] = 0.5 * (lcsi_arr[k_cell][j_cell][i_cell-1].z + lcsi_arr[k_cell][j_cell][i_cell].z); //* aj_center;
2814
2815 mat[1][0] = 0.5 * (leta_arr[k_cell][j_cell-1][i_cell].x + leta_arr[k_cell][j_cell][i_cell].x); //* aj_center;
2816 mat[1][1] = 0.5 * (leta_arr[k_cell][j_cell-1][i_cell].y + leta_arr[k_cell][j_cell][i_cell].y); //* aj_center;
2817 mat[1][2] = 0.5 * (leta_arr[k_cell][j_cell-1][i_cell].z + leta_arr[k_cell][j_cell][i_cell].z); //* aj_center;
2818
2819 mat[2][0] = 0.5 * (lzet_arr[k_cell-1][j_cell][i_cell].x + lzet_arr[k_cell][j_cell][i_cell].x); //* aj_center;
2820 mat[2][1] = 0.5 * (lzet_arr[k_cell-1][j_cell][i_cell].y + lzet_arr[k_cell][j_cell][i_cell].y); //* aj_center;
2821 mat[2][2] = 0.5 * (lzet_arr[k_cell-1][j_cell][i_cell].z + lzet_arr[k_cell][j_cell][i_cell].z); //* aj_center;
2822
2823 // Contravariant velocity vector `q` at the cell center,
2824 // estimated by averaging face-based contravariant velocities.
2825 PetscReal q[3];
2826 q[0] = 0.5 * (lucont_arr[k_cell][j_cell][i_cell-1].x + lucont_arr[k_cell][j_cell][i_cell].x); // U¹ at cell center
2827 q[1] = 0.5 * (lucont_arr[k_cell][j_cell-1][i_cell].y + lucont_arr[k_cell][j_cell][i_cell].y); // U² at cell center
2828 q[2] = 0.5 * (lucont_arr[k_cell-1][j_cell][i_cell].z + lucont_arr[k_cell][j_cell][i_cell].z); // U³ at cell center
2829
2830 // Solve the 3x3 system `mat * ucat = q` using Cramer's rule.
2831 PetscReal det = mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]) -
2832 mat[0][1] * (mat[1][0] * mat[2][2] - mat[1][2] * mat[2][0]) +
2833 mat[0][2] * (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]);
2834
2835 if (PetscAbsReal(det) < 1.0e-18) {
2836 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FLOP_COUNT, "Transformation matrix determinant is near zero at cell (%d,%d,%d) \n", i_cell, j_cell, k_cell);
2837 }
2838
2839 PetscReal det_inv = 1.0 / det;
2840
2841 PetscReal det0 = q[0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]) -
2842 q[1] * (mat[0][1] * mat[2][2] - mat[0][2] * mat[2][1]) +
2843 q[2] * (mat[0][1] * mat[1][2] - mat[0][2] * mat[1][1]);
2844
2845 PetscReal det1 = -q[0] * (mat[1][0] * mat[2][2] - mat[1][2] * mat[2][0]) +
2846 q[1] * (mat[0][0] * mat[2][2] - mat[0][2] * mat[2][0]) -
2847 q[2] * (mat[0][0] * mat[1][2] - mat[0][2] * mat[1][0]);
2848
2849 PetscReal det2 = q[0] * (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]) -
2850 q[1] * (mat[0][0] * mat[2][1] - mat[0][1] * mat[2][0]) +
2851 q[2] * (mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]);
2852
2853 // Store computed Cartesian velocity in the GLOBAL Ucat array at the
2854 // array index corresponding to the cell's origin node.
2855 gucat_arr[k_cell][j_cell][i_cell].x = det0 * det_inv;
2856 gucat_arr[k_cell][j_cell][i_cell].y = det1 * det_inv;
2857 gucat_arr[k_cell][j_cell][i_cell].z = det2 * det_inv;
2858 }
2859 }
2860 }
2861
2862 // --- 6. Restore Array Access ---
2863 ierr = DMDAVecRestoreArrayRead(user->fda, user->lUcont, &lucont_arr); CHKERRQ(ierr);
2864 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, &lcsi_arr); CHKERRQ(ierr);
2865 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, &leta_arr); CHKERRQ(ierr);
2866 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, &lzet_arr); CHKERRQ(ierr);
2867 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, &lnvert_arr); CHKERRQ(ierr);
2868 ierr = DMDAVecRestoreArrayRead(user->da, user->lAj, &laj_arr); CHKERRQ(ierr);
2869 ierr = DMDAVecRestoreArray(user->fda, user->Ucat, &gucat_arr); CHKERRQ(ierr);
2870
2871 LOG_ALLOW(GLOBAL, LOG_INFO, "Completed Contravariant-to-Cartesian velocity transformation. \n");
2873 PetscFunctionReturn(0);
2874}
2875
2876#undef __FUNCT__
2877#define __FUNCT__ "Cart2Contra"
2878/**
2879 * @brief Convert a spatially varying Cartesian velocity field to contravariant fluxes.
2880 */
2881PetscErrorCode Cart2Contra(UserCtx *user)
2882{
2883 PetscErrorCode ierr;
2884 DMDALocalInfo info;
2885 const Cmpnts ***ucat_arr, ***csi_arr, ***eta_arr, ***zet_arr;
2886 Cmpnts ***ucont_arr;
2887
2888 PetscFunctionBeginUser;
2890
2891 ierr = DMDAGetLocalInfo(user->fda, &info); CHKERRQ(ierr);
2892 ierr = DMDAVecGetArrayRead(user->fda, user->lUcat, &ucat_arr); CHKERRQ(ierr);
2893 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, &csi_arr); CHKERRQ(ierr);
2894 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, &eta_arr); CHKERRQ(ierr);
2895 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, &zet_arr); CHKERRQ(ierr);
2896 ierr = DMDAVecGetArray(user->fda, user->Ucont, &ucont_arr); CHKERRQ(ierr);
2897
2898 const PetscInt i_start = PetscMax(info.xs, 1);
2899 const PetscInt j_start = PetscMax(info.ys, 1);
2900 const PetscInt k_start = PetscMax(info.zs, 1);
2901 const PetscInt i_end = PetscMin(info.xs + info.xm, info.mx - 1);
2902 const PetscInt j_end = PetscMin(info.ys + info.ym, info.my - 1);
2903 const PetscInt k_end = PetscMin(info.zs + info.zm, info.mz - 1);
2904
2905 for (PetscInt k = k_start; k < k_end; k++) {
2906 for (PetscInt j = j_start; j < j_end; j++) {
2907 for (PetscInt i = i_start; i < i_end; i++) {
2908 const Cmpnts u_xi = {
2909 0.5 * (ucat_arr[k][j][i].x + ucat_arr[k][j][i + 1].x),
2910 0.5 * (ucat_arr[k][j][i].y + ucat_arr[k][j][i + 1].y),
2911 0.5 * (ucat_arr[k][j][i].z + ucat_arr[k][j][i + 1].z)
2912 };
2913 const Cmpnts u_eta = {
2914 0.5 * (ucat_arr[k][j][i].x + ucat_arr[k][j + 1][i].x),
2915 0.5 * (ucat_arr[k][j][i].y + ucat_arr[k][j + 1][i].y),
2916 0.5 * (ucat_arr[k][j][i].z + ucat_arr[k][j + 1][i].z)
2917 };
2918 const Cmpnts u_zeta = {
2919 0.5 * (ucat_arr[k][j][i].x + ucat_arr[k + 1][j][i].x),
2920 0.5 * (ucat_arr[k][j][i].y + ucat_arr[k + 1][j][i].y),
2921 0.5 * (ucat_arr[k][j][i].z + ucat_arr[k + 1][j][i].z)
2922 };
2923 ucont_arr[k][j][i].x = csi_arr[k][j][i].x * u_xi.x + csi_arr[k][j][i].y * u_xi.y + csi_arr[k][j][i].z * u_xi.z;
2924 ucont_arr[k][j][i].y = eta_arr[k][j][i].x * u_eta.x + eta_arr[k][j][i].y * u_eta.y + eta_arr[k][j][i].z * u_eta.z;
2925 ucont_arr[k][j][i].z = zet_arr[k][j][i].x * u_zeta.x + zet_arr[k][j][i].y * u_zeta.y + zet_arr[k][j][i].z * u_zeta.z;
2926 }
2927 }
2928 }
2929
2930 ierr = DMDAVecRestoreArray(user->fda, user->Ucont, &ucont_arr); CHKERRQ(ierr);
2931 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, &zet_arr); CHKERRQ(ierr);
2932 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, &eta_arr); CHKERRQ(ierr);
2933 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, &csi_arr); CHKERRQ(ierr);
2934 ierr = DMDAVecRestoreArrayRead(user->fda, user->lUcat, &ucat_arr); CHKERRQ(ierr);
2935
2937 PetscFunctionReturn(0);
2938}
2939
2940#undef __FUNCT__
2941#define __FUNCT__ "UniformCart2Contra"
2942/**
2943 * @brief Convert a uniform Cartesian velocity (u,v,w) to contravariant fluxes in Ucont.
2944 * @details Computes the dot product of the physical velocity with each face-area vector:
2945 * U^xi = csi · (u,v,w), U^eta = eta · (u,v,w), U^zeta = zet · (u,v,w).
2946 * Writes to all owned nodes (xs..xe, ys..ye, zs..ze); boundary ghosts are
2947 * overwritten later by ApplyBoundaryConditions.
2948 * @param user UserCtx with fda, Ucont, lCsi, lEta, lZet populated.
2949 * @param u Physical Cartesian x-velocity.
2950 * @param v Physical Cartesian y-velocity.
2951 * @param w Physical Cartesian z-velocity.
2952 */
2953PetscErrorCode UniformCart2Contra(UserCtx *user, PetscReal u, PetscReal v, PetscReal w)
2954{
2955 PetscErrorCode ierr;
2956 PetscFunctionBeginUser;
2958
2959 DMDALocalInfo info;
2960 Cmpnts ***ucont_arr;
2961 const Cmpnts ***csi_arr, ***eta_arr, ***zet_arr;
2962
2963 ierr = DMDAGetLocalInfo(user->fda, &info); CHKERRQ(ierr);
2964 ierr = DMDAVecGetArray(user->fda, user->Ucont, &ucont_arr); CHKERRQ(ierr);
2965 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, &csi_arr); CHKERRQ(ierr);
2966 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, &eta_arr); CHKERRQ(ierr);
2967 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, &zet_arr); CHKERRQ(ierr);
2968
2969 const PetscInt xs = info.xs, xe = info.xs + info.xm;
2970 const PetscInt ys = info.ys, ye = info.ys + info.ym;
2971 const PetscInt zs = info.zs, ze = info.zs + info.zm;
2972
2973 for (PetscInt k = zs; k < ze; k++) {
2974 for (PetscInt j = ys; j < ye; j++) {
2975 for (PetscInt i = xs; i < xe; i++) {
2976 ucont_arr[k][j][i].x = csi_arr[k][j][i].x * u + csi_arr[k][j][i].y * v + csi_arr[k][j][i].z * w;
2977 ucont_arr[k][j][i].y = eta_arr[k][j][i].x * u + eta_arr[k][j][i].y * v + eta_arr[k][j][i].z * w;
2978 ucont_arr[k][j][i].z = zet_arr[k][j][i].x * u + zet_arr[k][j][i].y * v + zet_arr[k][j][i].z * w;
2979 }
2980 }
2981 }
2982
2983 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, &zet_arr); CHKERRQ(ierr);
2984 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, &eta_arr); CHKERRQ(ierr);
2985 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, &csi_arr); CHKERRQ(ierr);
2986 ierr = DMDAVecRestoreArray(user->fda, user->Ucont, &ucont_arr); CHKERRQ(ierr);
2987
2988 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Cart2Contra: set Ucont from Cartesian (%.3f, %.3f, %.3f).\n",
2989 (double)u, (double)v, (double)w);
2991 PetscFunctionReturn(0);
2992}
2993
2994#undef __FUNCT__
2995#define __FUNCT__ "SetupDomainCellDecompositionMap"
2996/**
2997 * @brief Internal helper implementation: `SetupDomainCellDecompositionMap()`.
2998 * @details Local to this translation unit.
2999 */
3001{
3002 PetscErrorCode ierr;
3003 DMDALocalInfo local_node_info;
3004 RankCellInfo my_cell_info;
3005 PetscMPIInt rank, size;
3006
3007 PetscFunctionBeginUser;
3009
3010 // --- 1. Input Validation and MPI Info ---
3011 if (!user) {
3012 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "UserCtx pointer is NULL in SetupDomainCellDecompositionMap.");
3013 }
3014 if (!user->da) {
3015 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "user->da is not initialized in SetupDomainCellDecompositionMap.");
3016 }
3017
3018 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
3019 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size); CHKERRQ(ierr);
3020
3021 LOG_ALLOW(GLOBAL, LOG_INFO, "Setting up domain cell decomposition map for %d ranks.\n", size);
3022
3023 // --- 2. Determine Local Cell Ownership ---
3024 // Get the local node ownership information from the primary DMDA.
3025 ierr = DMDAGetLocalInfo(user->da, &local_node_info); CHKERRQ(ierr);
3026
3027 // Use the robust helper function to convert node ownership to cell ownership.
3028 // A cell's index is defined by its origin node.
3029
3030 ierr = GetOwnedCellRange(&local_node_info, 0, &my_cell_info.xs_cell, &my_cell_info.xm_cell); CHKERRQ(ierr);
3031 ierr = GetOwnedCellRange(&local_node_info, 1, &my_cell_info.ys_cell, &my_cell_info.ym_cell); CHKERRQ(ierr);
3032 ierr = GetOwnedCellRange(&local_node_info, 2, &my_cell_info.zs_cell, &my_cell_info.zm_cell); CHKERRQ(ierr);
3033
3034 // Log the calculated local ownership for debugging purposes.
3035 LOG_ALLOW(LOCAL, LOG_DEBUG, "[Rank %d] Owns cells: i[%d, %d), j[%d, %d), k[%d, %d)\n",
3036 rank, my_cell_info.xs_cell, my_cell_info.xs_cell + my_cell_info.xm_cell,
3037 my_cell_info.ys_cell, my_cell_info.ys_cell + my_cell_info.ym_cell,
3038 my_cell_info.zs_cell, my_cell_info.zs_cell + my_cell_info.zm_cell);
3039
3040 // --- 3. Allocate and Distribute the Global Map ---
3041 // Allocate memory for the global map that will hold information from all ranks.
3042 ierr = PetscMalloc1(size, &user->RankCellInfoMap); CHKERRQ(ierr);
3043
3044 // Perform the collective communication to gather the `RankCellInfo` struct from every rank.
3045 // Each rank sends its `my_cell_info` and receives the complete array in `user->RankCellInfoMap`.
3046 // We use MPI_BYTE to ensure portability across different systems and struct padding.
3047 ierr = MPI_Allgather(&my_cell_info, sizeof(RankCellInfo), MPI_BYTE,
3048 user->RankCellInfoMap, sizeof(RankCellInfo), MPI_BYTE,
3049 PETSC_COMM_WORLD); CHKERRQ(ierr);
3050
3051 LOG_ALLOW(GLOBAL, LOG_INFO, "Domain cell decomposition map created and distributed successfully.\n");
3052
3054 PetscFunctionReturn(0);
3055}
3056
3057#undef __FUNCT__
3058#define __FUNCT__ "BinarySearchInt64"
3059/**
3060 * @brief Implementation of \ref BinarySearchInt64().
3061 * @details Full API contract (arguments, ownership, side effects) is documented with
3062 * the header declaration in `include/setup.h`.
3063 * @see BinarySearchInt64()
3064 */
3065PetscErrorCode BinarySearchInt64(PetscInt n, const PetscInt64 arr[], PetscInt64 key, PetscBool *found)
3066{
3067 PetscInt low = 0, high = n - 1;
3068
3069 PetscFunctionBeginUser;
3071
3072 // --- 1. Input Validation ---
3073 if (!found) {
3074 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Output pointer 'found' is NULL in PetscBinarySearchInt64.");
3075 }
3076 if (n > 0 && !arr) {
3077 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Input array 'arr' is NULL for n > 0.");
3078 }
3079
3080 // Initialize output
3081 *found = PETSC_FALSE;
3082
3083 // --- 2. Binary Search Algorithm ---
3084 while (low <= high) {
3085 // Use this form to prevent potential integer overflow on very large arrays
3086 PetscInt mid = low + (high - low) / 2;
3087
3088 if (arr[mid] == key) {
3089 *found = PETSC_TRUE; // Key found!
3090 break; // Exit the loop
3091 }
3092
3093 if (arr[mid] < key) {
3094 low = mid + 1; // Search in the right half
3095 } else {
3096 high = mid - 1; // Search in the left half
3097 }
3098 }
3099
3101 PetscFunctionReturn(0);
3102}
3103
3104
3105/**
3106 * @brief Internal helper implementation: `Gidx()`.
3107 * @details Local to this translation unit.
3108 */
3109static PetscInt Gidx(PetscInt i, PetscInt j, PetscInt k, UserCtx *user)
3110{
3111 PetscInt nidx;
3112 DMDALocalInfo info = user->info;
3113
3114 PetscInt mx = info.mx, my = info.my;
3115
3116 AO ao;
3117 DMDAGetAO(user->da, &ao);
3118 nidx=i+j*mx+k*mx*my;
3119
3120 AOApplicationToPetsc(ao,1,&nidx);
3121
3122 return (nidx);
3123}
3124
3125
3126#undef __FUNCT__
3127#define __FUNCT__ "ComputeDivergence"
3128/**
3129 * @brief Implementation of \ref ComputeDivergence().
3130 * @details Full API contract (arguments, ownership, side effects) is documented with
3131 * the header declaration in `include/setup.h`.
3132 * @see ComputeDivergence()
3133 */
3134
3135PetscErrorCode ComputeDivergence(UserCtx *user )
3136{
3137 DM da = user->da, fda = user->fda;
3138 DMDALocalInfo info = user->info;
3139
3140 PetscInt ti = user->simCtx->step;
3141
3142 PetscInt xs = info.xs, xe = info.xs + info.xm;
3143 PetscInt ys = info.ys, ye = info.ys + info.ym;
3144 PetscInt zs = info.zs, ze = info.zs + info.zm;
3145 PetscInt mx = info.mx, my = info.my, mz = info.mz;
3146
3147 PetscInt lxs, lys, lzs, lxe, lye, lze;
3148 PetscInt i, j, k;
3149
3150 Vec Div;
3151 PetscReal ***div, ***aj, ***nvert,***p;
3152 Cmpnts ***ucont;
3153 PetscReal maxdiv;
3154
3155 lxs = xs; lxe = xe;
3156 lys = ys; lye = ye;
3157 lzs = zs; lze = ze;
3158
3159 if (xs==0) lxs = xs+1;
3160 if (ys==0) lys = ys+1;
3161 if (zs==0) lzs = zs+1;
3162
3163 if (xe==mx) lxe = xe-1;
3164 if (ye==my) lye = ye-1;
3165 if (ze==mz) lze = ze-1;
3166
3167 PetscFunctionBeginUser;
3169
3170 DMDAVecGetArray(fda,user->lUcont, &ucont);
3171 DMDAVecGetArray(da, user->lAj, &aj);
3172 VecDuplicate(user->P, &Div);
3173 DMDAVecGetArray(da, Div, &div);
3174 DMDAVecGetArray(da, user->lNvert, &nvert);
3175 DMDAVecGetArray(da, user->P, &p);
3176 for (k=lzs; k<lze; k++) {
3177 for (j=lys; j<lye; j++){
3178 for (i=lxs; i<lxe; i++) {
3179 if (k==10 && j==10 && i==1){
3180 LOG_ALLOW(LOCAL,LOG_INFO,"Pressure[10][10][1] = %f | Pressure[10][10][0] = %f \n ",p[k][j][i],p[k][j][i-1]);
3181 }
3182
3183 if (k==10 && j==10 && i==mx-3)
3184 LOG_ALLOW(LOCAL,LOG_INFO,"Pressure[10][10][%d] = %f | Pressure[10][10][%d] = %f \n ",mx-2,p[k][j][mx-2],mx-1,p[k][j][mx-1]);
3185 }
3186 }
3187 }
3188 DMDAVecRestoreArray(da, user->P, &p);
3189
3190
3191 for (k=lzs; k<lze; k++) {
3192 for (j=lys; j<lye; j++) {
3193 for (i=lxs; i<lxe; i++) {
3194 maxdiv = fabs((ucont[k][j][i].x - ucont[k][j][i-1].x +
3195 ucont[k][j][i].y - ucont[k][j-1][i].y +
3196 ucont[k][j][i].z - ucont[k-1][j][i].z)*aj[k][j][i]);
3197 if (nvert[k][j][i] + nvert[k+1][j][i] + nvert[k-1][j][i] +
3198 nvert[k][j+1][i] + nvert[k][j-1][i] +
3199 nvert[k][j][i+1] + nvert[k][j][i-1] > 0.1) maxdiv = 0.;
3200 div[k][j][i] = maxdiv;
3201
3202 }
3203 }
3204 }
3205
3206 if (zs==0) {
3207 k=0;
3208 for (j=ys; j<ye; j++) {
3209 for (i=xs; i<xe; i++) {
3210 div[k][j][i] = 0.;
3211 }
3212 }
3213 }
3214
3215 if (ze == mz) {
3216 k=mz-1;
3217 for (j=ys; j<ye; j++) {
3218 for (i=xs; i<xe; i++) {
3219 div[k][j][i] = 0.;
3220 }
3221 }
3222 }
3223
3224 if (xs==0) {
3225 i=0;
3226 for (k=zs; k<ze; k++) {
3227 for (j=ys; j<ye; j++) {
3228 div[k][j][i] = 0.;
3229 }
3230 }
3231 }
3232
3233 if (xe==mx) {
3234 i=mx-1;
3235 for (k=zs; k<ze; k++) {
3236 for (j=ys; j<ye; j++) {
3237 div[k][j][i] = 0;
3238 }
3239 }
3240 }
3241
3242 if (ys==0) {
3243 j=0;
3244 for (k=zs; k<ze; k++) {
3245 for (i=xs; i<xe; i++) {
3246 div[k][j][i] = 0.;
3247 }
3248 }
3249 }
3250
3251 if (ye==my) {
3252 j=my-1;
3253 for (k=zs; k<ze; k++) {
3254 for (i=xs; i<xe; i++) {
3255 div[k][j][i] = 0.;
3256 }
3257 }
3258 }
3259 DMDAVecRestoreArray(da, Div, &div);
3260 PetscInt MaxFlatIndex;
3261
3262 VecMax(Div, &MaxFlatIndex, &maxdiv);
3263
3264 LOG_ALLOW(GLOBAL,LOG_INFO,"[Step %d]] The Maximum Divergence is %e at flat index %d.\n",ti,maxdiv,MaxFlatIndex);
3265
3266 user->simCtx->MaxDivFlatArg = MaxFlatIndex;
3267 user->simCtx->MaxDiv = maxdiv;
3268
3269 for (k=zs; k<ze; k++) {
3270 for (j=ys; j<ye; j++) {
3271 for (i=xs; i<xe; i++) {
3272 if (Gidx(i,j,k,user) == MaxFlatIndex) {
3273 LOG_ALLOW(GLOBAL,LOG_INFO,"[Step %d] The Maximum Divergence(%e) is at location [%d][%d][%d]. \n", ti, maxdiv,k,j,i);
3274 user->simCtx->MaxDivz = k;
3275 user->simCtx->MaxDivy = j;
3276 user->simCtx->MaxDivx = i;
3277 }
3278 }
3279 }
3280 }
3281
3282
3283 DMDAVecRestoreArray(da, user->lNvert, &nvert);
3284 DMDAVecRestoreArray(fda, user->lUcont, &ucont);
3285 DMDAVecRestoreArray(da, user->lAj, &aj);
3286 VecDestroy(&Div);
3287
3289 PetscFunctionReturn(0);
3290}
3291
3292#undef __FUNCT__
3293#define __FUNCT__ "InitializeRandomGenerators"
3294
3295/**
3296 * @brief Implementation of \ref InitializeRandomGenerators().
3297 * @details Full API contract (arguments, ownership, side effects) is documented with
3298 * the header declaration in `include/setup.h`.
3299 * @see InitializeRandomGenerators()
3300 */
3301PetscErrorCode InitializeRandomGenerators(UserCtx* user, PetscRandom *randx, PetscRandom *randy, PetscRandom *randz) {
3302 PetscErrorCode ierr; // Error code for PETSc functions
3303 PetscMPIInt rank;
3304 PetscFunctionBeginUser;
3306 MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
3307
3308 // Initialize RNG for x-coordinate
3309 ierr = PetscRandomCreate(PETSC_COMM_SELF, randx); CHKERRQ(ierr);
3310 ierr = PetscRandomSetType((*randx), PETSCRAND48); CHKERRQ(ierr);
3311 ierr = PetscRandomSetInterval(*randx, user->bbox.min_coords.x, user->bbox.max_coords.x); CHKERRQ(ierr);
3312 ierr = PetscRandomSetSeed(*randx, rank + 12345); CHKERRQ(ierr); // Unique seed per rank
3313 ierr = PetscRandomSeed(*randx); CHKERRQ(ierr);
3314 LOG_ALLOW_SYNC(LOCAL,LOG_VERBOSE, "[Rank %d]Initialized RNG for X-axis.\n",rank);
3315
3316 // Initialize RNG for y-coordinate
3317 ierr = PetscRandomCreate(PETSC_COMM_SELF, randy); CHKERRQ(ierr);
3318 ierr = PetscRandomSetType((*randy), PETSCRAND48); CHKERRQ(ierr);
3319 ierr = PetscRandomSetInterval(*randy, user->bbox.min_coords.y, user->bbox.max_coords.y); CHKERRQ(ierr);
3320 ierr = PetscRandomSetSeed(*randy, rank + 67890); CHKERRQ(ierr); // Unique seed per rank
3321 ierr = PetscRandomSeed(*randy); CHKERRQ(ierr);
3322 LOG_ALLOW_SYNC(LOCAL,LOG_VERBOSE, "[Rank %d]Initialized RNG for Y-axis.\n",rank);
3323
3324 // Initialize RNG for z-coordinate
3325 ierr = PetscRandomCreate(PETSC_COMM_SELF, randz); CHKERRQ(ierr);
3326 ierr = PetscRandomSetType((*randz), PETSCRAND48); CHKERRQ(ierr);
3327 ierr = PetscRandomSetInterval(*randz, user->bbox.min_coords.z, user->bbox.max_coords.z); CHKERRQ(ierr);
3328 ierr = PetscRandomSetSeed(*randz, rank + 54321); CHKERRQ(ierr); // Unique seed per rank
3329 ierr = PetscRandomSeed(*randz); CHKERRQ(ierr);
3330 LOG_ALLOW_SYNC(LOCAL,LOG_VERBOSE, "[Rank %d]Initialized RNG for Z-axis.\n",rank);
3331
3333 PetscFunctionReturn(0);
3334}
3335
3336#undef __FUNCT__
3337#define __FUNCT__ "InitializeLogicalSpaceRNGs"
3338/**
3339 * @brief Internal helper implementation: `InitializeLogicalSpaceRNGs()`.
3340 * @details Local to this translation unit.
3341 */
3342PetscErrorCode InitializeLogicalSpaceRNGs(PetscRandom *rand_logic_i, PetscRandom *rand_logic_j, PetscRandom *rand_logic_k) {
3343 PetscErrorCode ierr;
3344 PetscMPIInt rank;
3345 PetscFunctionBeginUser;
3346
3348
3349 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
3350
3351 // --- RNG for i-logical dimension ---
3352 ierr = PetscRandomCreate(PETSC_COMM_SELF, rand_logic_i); CHKERRQ(ierr);
3353 ierr = PetscRandomSetType((*rand_logic_i), PETSCRAND48); CHKERRQ(ierr);
3354 ierr = PetscRandomSetInterval(*rand_logic_i, 0.0, 1.0); CHKERRQ(ierr); // Key change: [0,1)
3355 ierr = PetscRandomSetSeed(*rand_logic_i, rank + 202401); CHKERRQ(ierr); // Unique seed
3356 ierr = PetscRandomSeed(*rand_logic_i); CHKERRQ(ierr);
3357 LOG_ALLOW(LOCAL,LOG_VERBOSE, "[Rank %d] Initialized RNG for i-logical dimension [0,1).\n",rank);
3358
3359 // --- RNG for j-logical dimension ---
3360 ierr = PetscRandomCreate(PETSC_COMM_SELF, rand_logic_j); CHKERRQ(ierr);
3361 ierr = PetscRandomSetType((*rand_logic_j), PETSCRAND48); CHKERRQ(ierr);
3362 ierr = PetscRandomSetInterval(*rand_logic_j, 0.0, 1.0); CHKERRQ(ierr); // Key change: [0,1)
3363 ierr = PetscRandomSetSeed(*rand_logic_j, rank + 202402); CHKERRQ(ierr);
3364 ierr = PetscRandomSeed(*rand_logic_j); CHKERRQ(ierr);
3365 LOG_ALLOW(LOCAL,LOG_VERBOSE, "[Rank %d] Initialized RNG for j-logical dimension [0,1).\n",rank);
3366
3367 // --- RNG for k-logical dimension ---
3368 ierr = PetscRandomCreate(PETSC_COMM_SELF, rand_logic_k); CHKERRQ(ierr);
3369 ierr = PetscRandomSetType((*rand_logic_k), PETSCRAND48); CHKERRQ(ierr);
3370 ierr = PetscRandomSetInterval(*rand_logic_k, 0.0, 1.0); CHKERRQ(ierr); // Key change: [0,1)
3371 ierr = PetscRandomSetSeed(*rand_logic_k, rank + 202403); CHKERRQ(ierr);
3372 ierr = PetscRandomSeed(*rand_logic_k); CHKERRQ(ierr);
3373 LOG_ALLOW(LOCAL,LOG_VERBOSE, "[Rank %d] Initialized RNG for k-logical dimension [0,1).\n",rank);
3374
3375
3377 PetscFunctionReturn(0);
3378}
3379
3380#undef __FUNCT__
3381#define __FUNCT__ "InitializeBrownianRNG"
3382/**
3383 * @brief Internal helper implementation: `InitializeBrownianRNG()`.
3384 * @details Local to this translation unit.
3385 */
3386PetscErrorCode InitializeBrownianRNG(SimCtx *simCtx) {
3387 PetscErrorCode ierr;
3388 PetscMPIInt rank;
3389
3390 PetscFunctionBeginUser;
3392
3393 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
3394
3395 // 1. Create the generator (stored in SimCtx, not UserCtx, as it is global physics)
3396 ierr = PetscRandomCreate(PETSC_COMM_WORLD, &simCtx->BrownianMotionRNG); CHKERRQ(ierr);
3397 ierr = PetscRandomSetType(simCtx->BrownianMotionRNG, PETSCRAND48); CHKERRQ(ierr);
3398
3399 // 2. CRITICAL: Set interval to [0, 1).
3400 // This is required for the Gaussian math to work.
3401 ierr = PetscRandomSetInterval(simCtx->BrownianMotionRNG, 0.0, 1.0); CHKERRQ(ierr);
3402
3403 // 3. Seed based on Rank to ensure spatial randomness
3404 // Multiplying by a large prime helps separate the streams significantly
3405 unsigned long seed = (unsigned long)rank * 987654321 + (unsigned long)time(NULL);
3406 ierr = PetscRandomSetSeed(simCtx->BrownianMotionRNG, seed); CHKERRQ(ierr);
3407 ierr = PetscRandomSeed(simCtx->BrownianMotionRNG); CHKERRQ(ierr);
3408
3409 LOG_ALLOW(LOCAL, LOG_VERBOSE, "[Rank %d] Initialized Brownian Physics RNG.\n", rank);
3410
3412 PetscFunctionReturn(0);
3413}
3414
3415/////////////// DERIVATIVE CALCULATION HELPERS ///////////////
3416
3417#undef __FUNCT__
3418#define __FUNCT__ "TransformScalarDerivativesToPhysical"
3419/**
3420 * @brief Implementation of \ref TransformScalarDerivativesToPhysical().
3421 * @details Full API contract (arguments, ownership, side effects) is documented with
3422 * the header declaration in `include/setup.h`.
3423 * @see TransformScalarDerivativesToPhysical()
3424 */
3426 Cmpnts csi_metrics,
3427 Cmpnts eta_metrics,
3428 Cmpnts zet_metrics,
3429 PetscReal dPhi_dcsi,
3430 PetscReal dPhi_deta,
3431 PetscReal dPhi_dzet,
3432 Cmpnts *gradPhi)
3433{
3434 // Gradient X component
3435 gradPhi->x = jacobian * (dPhi_dcsi * csi_metrics.x + dPhi_deta * eta_metrics.x + dPhi_dzet * zet_metrics.x);
3436
3437 // Gradient Y component
3438 gradPhi->y = jacobian * (dPhi_dcsi * csi_metrics.y + dPhi_deta * eta_metrics.y + dPhi_dzet * zet_metrics.y);
3439
3440 // Gradient Z component
3441 gradPhi->z = jacobian * (dPhi_dcsi * csi_metrics.z + dPhi_deta * eta_metrics.z + dPhi_dzet * zet_metrics.z);
3442}
3443
3444#undef __FUNCT__
3445#define __FUNCT__ "TransformDerivativesToPhysical"
3446/**
3447 * @brief Internal helper implementation: `TransformDerivativesToPhysical()`.
3448 * @details Local to this translation unit.
3449 */
3450static void TransformDerivativesToPhysical(PetscReal jacobian, Cmpnts csi_metrics, Cmpnts eta_metrics, Cmpnts zet_metrics,
3451 Cmpnts deriv_csi, Cmpnts deriv_eta, Cmpnts deriv_zet,
3452 Cmpnts *dudx, Cmpnts *dvdx, Cmpnts *dwdx)
3453{
3454 // Derivatives of the first component (u)
3455 dudx->x = jacobian * (deriv_csi.x * csi_metrics.x + deriv_eta.x * eta_metrics.x + deriv_zet.x * zet_metrics.x);
3456 dudx->y = jacobian * (deriv_csi.x * csi_metrics.y + deriv_eta.x * eta_metrics.y + deriv_zet.x * zet_metrics.y);
3457 dudx->z = jacobian * (deriv_csi.x * csi_metrics.z + deriv_eta.x * eta_metrics.z + deriv_zet.x * zet_metrics.z);
3458 // Derivatives of the second component (v)
3459 dvdx->x = jacobian * (deriv_csi.y * csi_metrics.x + deriv_eta.y * eta_metrics.x + deriv_zet.y * zet_metrics.x);
3460 dvdx->y = jacobian * (deriv_csi.y * csi_metrics.y + deriv_eta.y * eta_metrics.y + deriv_zet.y * zet_metrics.y);
3461 dvdx->z = jacobian * (deriv_csi.y * csi_metrics.z + deriv_eta.y * eta_metrics.z + deriv_zet.y * zet_metrics.z);
3462 // Derivatives of the third component (w)
3463 dwdx->x = jacobian * (deriv_csi.z * csi_metrics.x + deriv_eta.z * eta_metrics.x + deriv_zet.z * zet_metrics.x);
3464 dwdx->y = jacobian * (deriv_csi.z * csi_metrics.y + deriv_eta.z * eta_metrics.y + deriv_zet.z * zet_metrics.y);
3465 dwdx->z = jacobian * (deriv_csi.z * csi_metrics.z + deriv_eta.z * eta_metrics.z + deriv_zet.z * zet_metrics.z);
3466}
3467
3468#undef __FUNCT__
3469#define __FUNCT__ "ComputeScalarFieldDerivatives"
3470/**
3471 * @brief Internal helper implementation: `ComputeScalarFieldDerivatives()`.
3472 * @details Local to this translation unit.
3473 */
3474PetscErrorCode ComputeScalarFieldDerivatives(UserCtx *user, PetscInt i, PetscInt j, PetscInt k,
3475 PetscReal ***field_data, Cmpnts *grad)
3476{
3477 PetscErrorCode ierr;
3478 Cmpnts ***csi, ***eta, ***zet;
3479 PetscReal ***jac;
3480 PetscReal d_csi, d_eta, d_zet;
3481
3482 PetscFunctionBeginUser;
3483
3484 // 1. Get read-only access to metrics
3485 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, &csi); CHKERRQ(ierr);
3486 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, &eta); CHKERRQ(ierr);
3487 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, &zet); CHKERRQ(ierr);
3488 ierr = DMDAVecGetArrayRead(user->da, user->lAj, &jac); CHKERRQ(ierr);
3489
3490 // 2. Compute derivatives in computational space (Central Difference)
3491 // Assumes ghosts are available at i+/-1
3492 d_csi = 0.5 * (field_data[k][j][i+1] - field_data[k][j][i-1]);
3493 d_eta = 0.5 * (field_data[k][j+1][i] - field_data[k][j-1][i]);
3494 d_zet = 0.5 * (field_data[k+1][j][i] - field_data[k-1][j][i]);
3495
3496 // 3. Transform to physical space
3498 csi[k][j][i], eta[k][j][i], zet[k][j][i],
3499 d_csi, d_eta, d_zet,
3500 grad);
3501
3502 // 4. Restore arrays
3503 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, &csi); CHKERRQ(ierr);
3504 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, &eta); CHKERRQ(ierr);
3505 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, &zet); CHKERRQ(ierr);
3506 ierr = DMDAVecRestoreArrayRead(user->da, user->lAj, &jac); CHKERRQ(ierr);
3507
3508 PetscFunctionReturn(0);
3509}
3510
3511#undef __FUNCT__
3512#define __FUNCT__ "ComputeVectorFieldDerivatives"
3513/**
3514 * @brief Internal helper implementation: `ComputeVectorFieldDerivatives()`.
3515 * @details Local to this translation unit.
3516 */
3517PetscErrorCode ComputeVectorFieldDerivatives(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, Cmpnts ***field_data,
3518 Cmpnts *dudx, Cmpnts *dvdx, Cmpnts *dwdx)
3519{
3520 PetscErrorCode ierr;
3521 Cmpnts ***csi, ***eta, ***zet;
3522 PetscReal ***jac;
3523 PetscFunctionBeginUser;
3524
3525 // 1. Get read-only access to the necessary metric data arrays
3526 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, &csi); CHKERRQ(ierr);
3527 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, &eta); CHKERRQ(ierr);
3528 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, &zet); CHKERRQ(ierr);
3529 ierr = DMDAVecGetArrayRead(user->da, user->lAj, &jac); CHKERRQ(ierr);
3530
3531 // 2. Calculate derivatives in computational space using central differencing
3532 Cmpnts deriv_csi, deriv_eta, deriv_zet;
3533 deriv_csi.x = (field_data[k][j][i+1].x - field_data[k][j][i-1].x) * 0.5;
3534 deriv_csi.y = (field_data[k][j][i+1].y - field_data[k][j][i-1].y) * 0.5;
3535 deriv_csi.z = (field_data[k][j][i+1].z - field_data[k][j][i-1].z) * 0.5;
3536
3537 deriv_eta.x = (field_data[k][j+1][i].x - field_data[k][j-1][i].x) * 0.5;
3538 deriv_eta.y = (field_data[k][j+1][i].y - field_data[k][j-1][i].y) * 0.5;
3539 deriv_eta.z = (field_data[k][j+1][i].z - field_data[k][j-1][i].z) * 0.5;
3540
3541 deriv_zet.x = (field_data[k+1][j][i].x - field_data[k-1][j][i].x) * 0.5;
3542 deriv_zet.y = (field_data[k+1][j][i].y - field_data[k-1][j][i].y) * 0.5;
3543 deriv_zet.z = (field_data[k+1][j][i].z - field_data[k-1][j][i].z) * 0.5;
3544
3545 // 3. Transform derivatives to physical space
3546 TransformDerivativesToPhysical(jac[k][j][i], csi[k][j][i], eta[k][j][i], zet[k][j][i],
3547 deriv_csi, deriv_eta, deriv_zet,
3548 dudx, dvdx, dwdx);
3549
3550 // 4. Restore access to the PETSc data arrays
3551 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, &csi); CHKERRQ(ierr);
3552 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, &eta); CHKERRQ(ierr);
3553 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, &zet); CHKERRQ(ierr);
3554 ierr = DMDAVecRestoreArrayRead(user->da, user->lAj, &jac); CHKERRQ(ierr);
3555
3556 PetscFunctionReturn(0);
3557}
3558
3559//================================================================================
3560//
3561// MEMORY CLEANUP FUNCTIONS
3562//
3563//================================================================================
3564
3565#undef __FUNCT__
3566#define __FUNCT__ "DestroyUserVectors"
3567/**
3568 * @brief Internal helper implementation: `DestroyUserVectors()`.
3569 * @details Local to this translation unit.
3570 */
3571PetscErrorCode DestroyUserVectors(UserCtx *user)
3572{
3573 PetscErrorCode ierr;
3574 PetscFunctionBeginUser;
3575
3576 // --- Group A: Primary Flow Fields (Always allocated at all levels) ---
3577 if (user->Ucont) { ierr = VecDestroy(&user->Ucont); CHKERRQ(ierr); }
3578 if (user->lUcont) { ierr = VecDestroy(&user->lUcont); CHKERRQ(ierr); }
3579 if (user->Ucat) { ierr = VecDestroy(&user->Ucat); CHKERRQ(ierr); }
3580 if (user->lUcat) { ierr = VecDestroy(&user->lUcat); CHKERRQ(ierr); }
3581 if (user->P) { ierr = VecDestroy(&user->P); CHKERRQ(ierr); }
3582 if (user->lP) { ierr = VecDestroy(&user->lP); CHKERRQ(ierr); }
3583 if (user->Nvert) { ierr = VecDestroy(&user->Nvert); CHKERRQ(ierr); }
3584 if (user->lNvert) { ierr = VecDestroy(&user->lNvert); CHKERRQ(ierr); }
3585
3586 // --- Group A2: Derived Flow Fields (Conditional) ---
3587 if(user->Diffusivity) {ierr = VecDestroy(&user->Diffusivity); CHKERRQ(ierr);}
3588 if(user->lDiffusivity){ierr = VecDestroy(&user->lDiffusivity); CHKERRQ(ierr);}
3589 if(user->DiffusivityGradient){ierr = VecDestroy(&user->DiffusivityGradient); CHKERRQ(ierr);}
3590 if(user->lDiffusivityGradient){ierr = VecDestroy(&user->lDiffusivityGradient); CHKERRQ(ierr);}
3591
3592 // --- Group B: Solver Work Vectors (All levels) ---
3593 if (user->Phi) { ierr = VecDestroy(&user->Phi); CHKERRQ(ierr); }
3594 if (user->lPhi) { ierr = VecDestroy(&user->lPhi); CHKERRQ(ierr); }
3595
3596 // --- Group C: Time-Stepping Vectors (Finest level only) ---
3597 if (user->Ucont_o) { ierr = VecDestroy(&user->Ucont_o); CHKERRQ(ierr); }
3598 if (user->Ucont_rm1) { ierr = VecDestroy(&user->Ucont_rm1); CHKERRQ(ierr); }
3599 if (user->Ucat_o) { ierr = VecDestroy(&user->Ucat_o); CHKERRQ(ierr); }
3600 if (user->P_o) { ierr = VecDestroy(&user->P_o); CHKERRQ(ierr); }
3601 if (user->Nvert_o) { ierr = VecDestroy(&user->Nvert_o); CHKERRQ(ierr); }
3602 if (user->lUcont_o) { ierr = VecDestroy(&user->lUcont_o); CHKERRQ(ierr); }
3603 if (user->lUcont_rm1) { ierr = VecDestroy(&user->lUcont_rm1); CHKERRQ(ierr); }
3604 if (user->lNvert_o) { ierr = VecDestroy(&user->lNvert_o); CHKERRQ(ierr); }
3605
3606 // --- Group D: Grid Metrics - Face Centered (All levels) ---
3607 if (user->Csi) { ierr = VecDestroy(&user->Csi); CHKERRQ(ierr); }
3608 if (user->Eta) { ierr = VecDestroy(&user->Eta); CHKERRQ(ierr); }
3609 if (user->Zet) { ierr = VecDestroy(&user->Zet); CHKERRQ(ierr); }
3610 if (user->Aj) { ierr = VecDestroy(&user->Aj); CHKERRQ(ierr); }
3611 if (user->lCsi) { ierr = VecDestroy(&user->lCsi); CHKERRQ(ierr); }
3612 if (user->lEta) { ierr = VecDestroy(&user->lEta); CHKERRQ(ierr); }
3613 if (user->lZet) { ierr = VecDestroy(&user->lZet); CHKERRQ(ierr); }
3614 if (user->lAj) { ierr = VecDestroy(&user->lAj); CHKERRQ(ierr); }
3615
3616 // --- Group E: Grid Metrics - Face Centered (All levels) ---
3617 if (user->ICsi) { ierr = VecDestroy(&user->ICsi); CHKERRQ(ierr); }
3618 if (user->IEta) { ierr = VecDestroy(&user->IEta); CHKERRQ(ierr); }
3619 if (user->IZet) { ierr = VecDestroy(&user->IZet); CHKERRQ(ierr); }
3620 if (user->JCsi) { ierr = VecDestroy(&user->JCsi); CHKERRQ(ierr); }
3621 if (user->JEta) { ierr = VecDestroy(&user->JEta); CHKERRQ(ierr); }
3622 if (user->JZet) { ierr = VecDestroy(&user->JZet); CHKERRQ(ierr); }
3623 if (user->KCsi) { ierr = VecDestroy(&user->KCsi); CHKERRQ(ierr); }
3624 if (user->KEta) { ierr = VecDestroy(&user->KEta); CHKERRQ(ierr); }
3625 if (user->KZet) { ierr = VecDestroy(&user->KZet); CHKERRQ(ierr); }
3626 if (user->IAj) { ierr = VecDestroy(&user->IAj); CHKERRQ(ierr); }
3627 if (user->JAj) { ierr = VecDestroy(&user->JAj); CHKERRQ(ierr); }
3628 if (user->KAj) { ierr = VecDestroy(&user->KAj); CHKERRQ(ierr); }
3629 if (user->lICsi) { ierr = VecDestroy(&user->lICsi); CHKERRQ(ierr); }
3630 if (user->lIEta) { ierr = VecDestroy(&user->lIEta); CHKERRQ(ierr); }
3631 if (user->lIZet) { ierr = VecDestroy(&user->lIZet); CHKERRQ(ierr); }
3632 if (user->lJCsi) { ierr = VecDestroy(&user->lJCsi); CHKERRQ(ierr); }
3633 if (user->lJEta) { ierr = VecDestroy(&user->lJEta); CHKERRQ(ierr); }
3634 if (user->lJZet) { ierr = VecDestroy(&user->lJZet); CHKERRQ(ierr); }
3635 if (user->lKCsi) { ierr = VecDestroy(&user->lKCsi); CHKERRQ(ierr); }
3636 if (user->lKEta) { ierr = VecDestroy(&user->lKEta); CHKERRQ(ierr); }
3637 if (user->lKZet) { ierr = VecDestroy(&user->lKZet); CHKERRQ(ierr); }
3638 if (user->lIAj) { ierr = VecDestroy(&user->lIAj); CHKERRQ(ierr); }
3639 if (user->lJAj) { ierr = VecDestroy(&user->lJAj); CHKERRQ(ierr); }
3640 if (user->lKAj) { ierr = VecDestroy(&user->lKAj); CHKERRQ(ierr); }
3641
3642 // --- Group F: Cell/Face Coordinates and Grid Spacing (All levels) ---
3643 if (user->Cent) { ierr = VecDestroy(&user->Cent); CHKERRQ(ierr); }
3644 if (user->lCent) { ierr = VecDestroy(&user->lCent); CHKERRQ(ierr); }
3645 if (user->GridSpace) { ierr = VecDestroy(&user->GridSpace); CHKERRQ(ierr); }
3646 if (user->lGridSpace) { ierr = VecDestroy(&user->lGridSpace); CHKERRQ(ierr); }
3647 if (user->Centx) { ierr = VecDestroy(&user->Centx); CHKERRQ(ierr); }
3648 if (user->Centy) { ierr = VecDestroy(&user->Centy); CHKERRQ(ierr); }
3649 if (user->Centz) { ierr = VecDestroy(&user->Centz); CHKERRQ(ierr); }
3650 if (user->lCentx) { ierr = VecDestroy(&user->lCentx); CHKERRQ(ierr); }
3651 if (user->lCenty) { ierr = VecDestroy(&user->lCenty); CHKERRQ(ierr); }
3652 if (user->lCentz) { ierr = VecDestroy(&user->lCentz); CHKERRQ(ierr); }
3653
3654 // --- Group G: Turbulence Model Vectors (Finest level, conditional on les/rans) ---
3655 if (user->Nu_t) { ierr = VecDestroy(&user->Nu_t); CHKERRQ(ierr); }
3656 if (user->lNu_t) { ierr = VecDestroy(&user->lNu_t); CHKERRQ(ierr); }
3657 if (user->CS) { ierr = VecDestroy(&user->CS); CHKERRQ(ierr); }
3658 if (user->lCs) { ierr = VecDestroy(&user->lCs); CHKERRQ(ierr); }
3659 if (user->lFriction_Velocity) { ierr = VecDestroy(&user->lFriction_Velocity); CHKERRQ(ierr); }
3660 if (user->K_Omega) { ierr = VecDestroy(&user->K_Omega); CHKERRQ(ierr); }
3661 if (user->lK_Omega) { ierr = VecDestroy(&user->lK_Omega); CHKERRQ(ierr); }
3662 if (user->K_Omega_o) { ierr = VecDestroy(&user->K_Omega_o); CHKERRQ(ierr); }
3663 if (user->lK_Omega_o) { ierr = VecDestroy(&user->lK_Omega_o); CHKERRQ(ierr); }
3664
3665 // --- Group H: Particle Vectors (Finest level, conditional on np > 0) ---
3666 if (user->ParticleCount) { ierr = VecDestroy(&user->ParticleCount); CHKERRQ(ierr); }
3667 if (user->lParticleCount) { ierr = VecDestroy(&user->lParticleCount); CHKERRQ(ierr); }
3668 if (user->Psi) { ierr = VecDestroy(&user->Psi); CHKERRQ(ierr); }
3669 if (user->lPsi) { ierr = VecDestroy(&user->lPsi); CHKERRQ(ierr); }
3670
3671 // --- Group I: Boundary Condition Vectors (All levels) ---
3672 if (user->Bcs.Ubcs) { ierr = VecDestroy(&user->Bcs.Ubcs); CHKERRQ(ierr); }
3673 if (user->Bcs.Uch) { ierr = VecDestroy(&user->Bcs.Uch); CHKERRQ(ierr); }
3674
3675 // --- Group J: Post-Processing Vectors (Finest level, postprocessor mode) ---
3676 if (user->P_nodal) { ierr = VecDestroy(&user->P_nodal); CHKERRQ(ierr); }
3677 if (user->Ucat_nodal) { ierr = VecDestroy(&user->Ucat_nodal); CHKERRQ(ierr); }
3678 if (user->Qcrit) { ierr = VecDestroy(&user->Qcrit); CHKERRQ(ierr); }
3679 if (user->Psi_nodal) { ierr = VecDestroy(&user->Psi_nodal); CHKERRQ(ierr); }
3680
3681 // --- Group K: Interpolation Vectors (Lazy allocation) ---
3682 if (user->CellFieldAtCorner) { ierr = VecDestroy(&user->CellFieldAtCorner); CHKERRQ(ierr); }
3683 if (user->lCellFieldAtCorner) { ierr = VecDestroy(&user->lCellFieldAtCorner); CHKERRQ(ierr); }
3684
3685 // --- Group L: Statistical Averaging Vectors (If allocated) ---
3686 if (user->Ucat_sum) { ierr = VecDestroy(&user->Ucat_sum); CHKERRQ(ierr); }
3687 if (user->Ucat_cross_sum) { ierr = VecDestroy(&user->Ucat_cross_sum); CHKERRQ(ierr); }
3688 if (user->Ucat_square_sum) { ierr = VecDestroy(&user->Ucat_square_sum); CHKERRQ(ierr); }
3689 if (user->P_sum) { ierr = VecDestroy(&user->P_sum); CHKERRQ(ierr); }
3690
3691 // --- Group M: Implicit Solver Temporary Vectors (Destroyed after use, but check anyway) ---
3692 if (user->Rhs) { ierr = VecDestroy(&user->Rhs); CHKERRQ(ierr); }
3693 if (user->dUcont) { ierr = VecDestroy(&user->dUcont); CHKERRQ(ierr); }
3694 if (user->pUcont) { ierr = VecDestroy(&user->pUcont); CHKERRQ(ierr); }
3695
3696 // --- Group N: Poisson Solver Vectors (Destroyed after solve, but check anyway) ---
3697 if (user->B) { ierr = VecDestroy(&user->B); CHKERRQ(ierr); }
3698 if (user->R) { ierr = VecDestroy(&user->R); CHKERRQ(ierr); }
3699
3700 LOG_ALLOW(LOCAL, LOG_DEBUG, "All vectors destroyed for UserCtx.\n");
3701 PetscFunctionReturn(0);
3702}
3703#undef __FUNCT__
3704#define __FUNCT__ "DestroyUserContext"
3705/**
3706 * @brief Internal helper implementation: `DestroyUserContext()`.
3707 * @details Local to this translation unit.
3708 */
3709PetscErrorCode DestroyUserContext(UserCtx *user)
3710{
3711 PetscErrorCode ierr;
3712 PetscFunctionBeginUser;
3713
3714 if (!user) {
3715 LOG_ALLOW(LOCAL, LOG_WARNING, "DestroyUserContext called with NULL user pointer.\n");
3716 PetscFunctionReturn(0);
3717 }
3718
3719 LOG_ALLOW(LOCAL, LOG_INFO, "Destroying UserCtx at level %d...\n", user->thislevel);
3720
3721 // --- Step 1: Destroy Boundary Condition System ---
3722 // This handles all BC handlers and their private data.
3723 ierr = BoundarySystem_Destroy(user); CHKERRQ(ierr);
3724 LOG_ALLOW(LOCAL, LOG_DEBUG, " Boundary system destroyed.\n");
3725
3726 // --- Step 2: Destroy All Vectors ---
3727 // Handles ~74 Vec objects with proper NULL checking.
3728 ierr = DestroyUserVectors(user); CHKERRQ(ierr);
3729 LOG_ALLOW(LOCAL, LOG_DEBUG, " All vectors destroyed.\n");
3730
3731 // --- Step 3: Destroy Matrix and Solver Objects ---
3732 // Destroy pressure-Poisson matrices and solver.
3733 if (user->A) {
3734 ierr = MatDestroy(&user->A); CHKERRQ(ierr);
3735 LOG_ALLOW(LOCAL, LOG_DEBUG, " Matrix A destroyed.\n");
3736 }
3737 if (user->C) {
3738 ierr = MatDestroy(&user->C); CHKERRQ(ierr);
3739 LOG_ALLOW(LOCAL, LOG_DEBUG, " Matrix C destroyed.\n");
3740 }
3741 if (user->MR) {
3742 ierr = MatDestroy(&user->MR); CHKERRQ(ierr);
3743 LOG_ALLOW(LOCAL, LOG_DEBUG, " Matrix MR destroyed.\n");
3744 }
3745 if (user->MP) {
3746 ierr = MatDestroy(&user->MP); CHKERRQ(ierr);
3747 LOG_ALLOW(LOCAL, LOG_DEBUG, " Matrix MP destroyed.\n");
3748 }
3749 if (user->ksp) {
3750 ierr = KSPDestroy(&user->ksp); CHKERRQ(ierr);
3751 LOG_ALLOW(LOCAL, LOG_DEBUG, " KSP solver destroyed.\n");
3752 }
3753 if (user->nullsp) {
3754 ierr = MatNullSpaceDestroy(&user->nullsp); CHKERRQ(ierr);
3755 LOG_ALLOW(LOCAL, LOG_DEBUG, " MatNullSpace destroyed.\n");
3756 }
3757
3758 // --- Step 4: Destroy Application Ordering ---
3759 if (user->ao) {
3760 ierr = AODestroy(&user->ao); CHKERRQ(ierr);
3761 LOG_ALLOW(LOCAL, LOG_DEBUG, " AO destroyed.\n");
3762 }
3763
3764 // --- Step 5: Destroy DM Objects ---
3765 // Destroy in reverse order of dependency: post_swarm, swarm, fda2, fda, da
3766 if (user->post_swarm) {
3767 ierr = DMDestroy(&user->post_swarm); CHKERRQ(ierr);
3768 LOG_ALLOW(LOCAL, LOG_DEBUG, " post_swarm DM destroyed.\n");
3769 }
3770 if (user->swarm) {
3771 ierr = DMDestroy(&user->swarm); CHKERRQ(ierr);
3772 LOG_ALLOW(LOCAL, LOG_DEBUG, " swarm DM destroyed.\n");
3773 }
3774 if (user->fda2) {
3775 ierr = DMDestroy(&user->fda2); CHKERRQ(ierr);
3776 LOG_ALLOW(LOCAL, LOG_DEBUG, " fda2 DM destroyed.\n");
3777 }
3778 if (user->da) {
3779 ierr = DMDestroy(&user->da); CHKERRQ(ierr);
3780 LOG_ALLOW(LOCAL, LOG_DEBUG, " da DM destroyed.\n");
3781 }
3782
3783 // --- Step 6: Free PetscMalloc'd Arrays ---
3784 // Free arrays allocated with PetscMalloc1
3785 if (user->RankCellInfoMap) {
3786 ierr = PetscFree(user->RankCellInfoMap); CHKERRQ(ierr);
3787 user->RankCellInfoMap = NULL;
3788 LOG_ALLOW(LOCAL, LOG_DEBUG, " RankCellInfoMap freed.\n");
3789 }
3790 if (user->KSKE) {
3791 ierr = PetscFree(user->KSKE); CHKERRQ(ierr);
3792 user->KSKE = NULL;
3793 LOG_ALLOW(LOCAL, LOG_DEBUG, " KSKE array freed.\n");
3794 }
3795
3796 LOG_ALLOW(LOCAL, LOG_INFO, "UserCtx at level %d fully destroyed.\n", user->thislevel);
3797 PetscFunctionReturn(0);
3798}
3799
3800#undef __FUNCT__
3801#define __FUNCT__ "FinalizeSimulation"
3802/**
3803 * @brief Implementation of \ref FinalizeSimulation().
3804 * @details Full API contract (arguments, ownership, side effects) is documented with
3805 * the header declaration in `include/setup.h`.
3806 * @see FinalizeSimulation()
3807 */
3808PetscErrorCode FinalizeSimulation(SimCtx *simCtx)
3809{
3810 PetscErrorCode ierr;
3811 PetscFunctionBeginUser;
3812
3813 if (!simCtx) {
3814 LOG_ALLOW(GLOBAL, LOG_WARNING, "FinalizeSimulation called with NULL SimCtx pointer.\n");
3815 PetscFunctionReturn(0);
3816 }
3817
3818 LOG_ALLOW(GLOBAL, LOG_INFO, "========================================\n");
3819 LOG_ALLOW(GLOBAL, LOG_INFO, "Beginning simulation memory cleanup...\n");
3820 LOG_ALLOW(GLOBAL, LOG_INFO, "========================================\n");
3821
3822 // ============================================================================
3823 // PHASE 1: DESTROY MULTIGRID HIERARCHY (All UserCtx structures)
3824 // ============================================================================
3825
3826 ierr = DestroySolutionConvergenceState(simCtx); CHKERRQ(ierr);
3827
3828 if (simCtx->usermg.mgctx) {
3829 LOG_ALLOW(GLOBAL, LOG_INFO, "Destroying multigrid hierarchy (%d levels)...\n",
3830 simCtx->usermg.mglevels);
3831
3832 // Destroy each UserCtx from finest to coarsest (reverse order is safer)
3833 for (PetscInt level = simCtx->usermg.mglevels - 1; level >= 0; level--) {
3834 UserCtx *user = simCtx->usermg.mgctx[level].user;
3835 if (user) {
3836 LOG_ALLOW(LOCAL, LOG_INFO, " Destroying level %d of %d...\n",
3837 level, simCtx->usermg.mglevels - 1);
3838 ierr = DestroyUserContext(user); CHKERRQ(ierr);
3839
3840 // Free the UserCtx structure itself
3841 ierr = PetscFree(user); CHKERRQ(ierr);
3842 simCtx->usermg.mgctx[level].user = NULL;
3843 }
3844
3845 // Destroy the MGCtx-level packer DM
3846 if (simCtx->usermg.mgctx[level].packer) {
3847 ierr = DMDestroy(&simCtx->usermg.mgctx[level].packer); CHKERRQ(ierr);
3848 LOG_ALLOW(LOCAL, LOG_DEBUG, " MGCtx[%d].packer destroyed.\n", level);
3849 }
3850 }
3851
3852 // Free the MGCtx array itself
3853 ierr = PetscFree(simCtx->usermg.mgctx); CHKERRQ(ierr);
3854 simCtx->usermg.mgctx = NULL;
3855 LOG_ALLOW(GLOBAL, LOG_INFO, "All multigrid levels destroyed.\n");
3856 }
3857
3858 // ============================================================================
3859 // PHASE 2: DESTROY USERMG-LEVEL OBJECTS
3860 // ============================================================================
3861
3862 if (simCtx->usermg.packer) {
3863 ierr = DMDestroy(&simCtx->usermg.packer); CHKERRQ(ierr);
3864 LOG_ALLOW(LOCAL, LOG_DEBUG, "UserMG.packer DM destroyed.\n");
3865 }
3866
3867 if (simCtx->usermg.snespacker) {
3868 ierr = SNESDestroy(&simCtx->usermg.snespacker); CHKERRQ(ierr);
3869 LOG_ALLOW(LOCAL, LOG_DEBUG, "UserMG.snespacker SNES destroyed.\n");
3870 }
3871
3872 // ============================================================================
3873 // PHASE 3: DESTROY SIMCTX-LEVEL OBJECTS
3874 // ============================================================================
3875
3876 LOG_ALLOW(GLOBAL, LOG_INFO, "Destroying SimCtx-level objects...\n");
3877
3878 // --- PetscViewer for logging ---
3879 if (simCtx->logviewer) {
3880 ierr = PetscViewerDestroy(&simCtx->logviewer); CHKERRQ(ierr);
3881 LOG_ALLOW(LOCAL, LOG_DEBUG, " logviewer destroyed.\n");
3882 }
3883
3884 // --- Particle System DM ---
3885 if (simCtx->dm_swarm) {
3886 ierr = DMDestroy(&simCtx->dm_swarm); CHKERRQ(ierr);
3887 LOG_ALLOW(LOCAL, LOG_DEBUG, " dm_swarm destroyed.\n");
3888 }
3889
3890 // --- BoundingBox List (Array of BoundingBox structs) ---
3891 if (simCtx->bboxlist) {
3892 ierr = PetscFree(simCtx->bboxlist); CHKERRQ(ierr);
3893 simCtx->bboxlist = NULL;
3894 LOG_ALLOW(LOCAL, LOG_DEBUG, " bboxlist freed.\n");
3895 }
3896
3897 // --- Boundary Condition Files (Array of strings) ---
3898 if (simCtx->bcs_files) {
3899 for (PetscInt i = 0; i < simCtx->num_bcs_files; i++) {
3900 if (simCtx->bcs_files[i]) {
3901 ierr = PetscFree(simCtx->bcs_files[i]); CHKERRQ(ierr);
3902 }
3903 }
3904 ierr = PetscFree(simCtx->bcs_files); CHKERRQ(ierr);
3905 simCtx->bcs_files = NULL;
3906 LOG_ALLOW(LOCAL, LOG_DEBUG, " bcs_files array freed (%d files).\n", simCtx->num_bcs_files);
3907 }
3908
3909 // --- Brownian Motion RNG ---
3910 if (simCtx->BrownianMotionRNG) {
3911 ierr = PetscRandomDestroy(&simCtx->BrownianMotionRNG); CHKERRQ(ierr);
3912 LOG_ALLOW(LOCAL, LOG_DEBUG, " BrownianMotionRNG destroyed.\n");
3913 }
3914 // --- Post-Processing Parameters ---
3915 // pps is allocated with PetscNew and contains only static char arrays and basic types.
3916 // No internal dynamic allocations need to be freed.
3917 if (simCtx->pps) {
3918 ierr = PetscFree(simCtx->pps); CHKERRQ(ierr);
3919 simCtx->pps = NULL;
3920 LOG_ALLOW(LOCAL, LOG_DEBUG, " PostProcessParams freed.\n");
3921 }
3922
3923 // --- IBM/FSI Objects ---
3924 // Note: These are initialized to NULL and currently have no dedicated destroy functions.
3925 // If these modules are extended with cleanup routines, call them here.
3926 if (simCtx->ibm != NULL) {
3927 LOG_ALLOW(GLOBAL, LOG_WARNING, " WARNING: simCtx->ibm is non-NULL but no destroy function exists. Potential memory leak.\n");
3928 }
3929 if (simCtx->ibmv != NULL) {
3930 LOG_ALLOW(GLOBAL, LOG_WARNING, " WARNING: simCtx->ibmv is non-NULL but no destroy function exists. Potential memory leak.\n");
3931 }
3932 if (simCtx->fsi != NULL) {
3933 LOG_ALLOW(GLOBAL, LOG_WARNING, " WARNING: simCtx->fsi is non-NULL but no destroy function exists. Potential memory leak.\n");
3934 }
3935
3936 // --- Logging Allowed Functions (Array of strings) ---
3937 // Note: The logging system maintains its own copy via set_allowed_functions(),
3938 // so freeing simCtx->allowedFuncs will NOT affect LOG_ALLOW functionality.
3939 if (simCtx->allowedFuncs) {
3940 for (PetscInt i = 0; i < simCtx->nAllowed; i++) {
3941 if (simCtx->allowedFuncs[i]) {
3942 ierr = PetscFree(simCtx->allowedFuncs[i]); CHKERRQ(ierr);
3943 }
3944 }
3945 ierr = PetscFree(simCtx->allowedFuncs); CHKERRQ(ierr);
3946 simCtx->allowedFuncs = NULL;
3947 LOG_ALLOW(LOCAL, LOG_DEBUG, " allowedFuncs array freed (%d functions).\n", simCtx->nAllowed);
3948 }
3949
3950 // --- Profiling Critical Functions (Array of strings) ---
3951 if (simCtx->profilingSelectedFuncs) {
3952 for (PetscInt i = 0; i < simCtx->nProfilingSelectedFuncs; i++) {
3953 if (simCtx->profilingSelectedFuncs[i]) {
3954 ierr = PetscFree(simCtx->profilingSelectedFuncs[i]); CHKERRQ(ierr);
3955 }
3956 }
3957 ierr = PetscFree(simCtx->profilingSelectedFuncs); CHKERRQ(ierr);
3958 simCtx->profilingSelectedFuncs = NULL;
3959 LOG_ALLOW(LOCAL, LOG_DEBUG, " profilingSelectedFuncs array freed (%d functions).\n", simCtx->nProfilingSelectedFuncs);
3960 }
3961
3962 // ============================================================================
3963 // PHASE 4: FINAL SUMMARY
3964 // ============================================================================
3965
3966 LOG_ALLOW(GLOBAL, LOG_INFO, "========================================\n");
3967 LOG_ALLOW(GLOBAL, LOG_INFO, "Simulation cleanup completed successfully.\n");
3968 LOG_ALLOW(GLOBAL, LOG_INFO, "All PETSc objects have been destroyed.\n");
3969 LOG_ALLOW(GLOBAL, LOG_INFO, "========================================\n");
3970
3971 ierr = PetscFree(simCtx); CHKERRQ(ierr);
3972 PetscFunctionReturn(0);
3973}
PetscErrorCode BoundarySystem_Initialize(UserCtx *user, const char *bcs_filename)
Initializes the entire boundary system.
Definition Boundaries.c:891
PetscErrorCode PropagateBoundaryConfigToCoarserLevels(SimCtx *simCtx)
Propagates boundary condition configuration from finest to all coarser multigrid levels.
Definition Boundaries.c:988
PetscErrorCode BoundarySystem_Destroy(UserCtx *user)
Cleans up and destroys all boundary system resources.
PetscErrorCode CalculateAllGridMetrics(SimCtx *simCtx)
Orchestrates the calculation of all grid metrics.
Definition Metric.c:1942
PetscErrorCode DefineAllGridDimensions(SimCtx *simCtx)
Orchestrates the parsing and setting of grid dimensions for all blocks.
Definition grid.c:57
PetscErrorCode CalculateOutletProperties(UserCtx *user)
Calculates the center and area of the primary OUTLET face.
Definition grid.c:1119
PetscErrorCode BroadcastAllBoundingBoxes(UserCtx *user, BoundingBox **bboxlist)
Broadcasts the bounding box information collected on rank 0 to all other ranks.
Definition grid.c:1016
PetscErrorCode ValidatePeriodicGeometry(UserCtx *user)
Validates that configured geometric periodic seams match by translation.
Definition grid.c:380
PetscErrorCode InitializeAllGridDMs(SimCtx *simCtx)
Orchestrates the creation of DMDA objects for every block and multigrid level.
Definition grid.c:235
PetscErrorCode AssignAllGridCoordinates(SimCtx *simCtx)
Orchestrates the assignment of physical coordinates to all DMDA objects.
Definition grid.c:317
PetscErrorCode CalculateInletProperties(UserCtx *user)
Calculates the center and area of the primary INLET face.
Definition grid.c:1066
PetscErrorCode GatherAllBoundingBoxes(UserCtx *user, BoundingBox **allBBoxes)
Gathers local bounding boxes from all MPI processes to rank 0.
Definition grid.c:954
PetscErrorCode ParsePostProcessingSettings(SimCtx *simCtx)
Initializes post-processing settings from a config file and command-line overrides.
Definition io.c:2264
PetscErrorCode ParseScalingInformation(SimCtx *simCtx)
Parses physical scaling parameters from command-line options.
Definition io.c:2412
PetscErrorCode VerifyPathExistence(const char *path, PetscBool is_dir, PetscBool is_optional, const char *description, PetscBool *exists)
A parallel-safe helper to verify the existence of a generic file or directory path.
Definition io.c:741
void set_allowed_functions(const char **functionList, int count)
Sets the global list of function names that are allowed to log.
Definition logging.c:152
PetscBool is_function_allowed(const char *functionName)
Checks if a given function is in the allow-list.
Definition logging.c:183
#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
#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
PetscErrorCode print_log_level(void)
Prints the current logging level to the console.
Definition logging.c:116
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:827
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:83
PetscErrorCode LoadAllowedFunctionsFromFile(const char filename[], char ***funcsOut, PetscInt *nOut)
Load function names from a text file.
Definition logging.c:596
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:84
PetscErrorCode ProfilingInitialize(SimCtx *simCtx)
Initializes the custom profiling system using configuration from SimCtx.
Definition logging.c:1927
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:28
@ 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
@ LOG_VERBOSE
Extremely detailed logs, typically for development use only.
Definition logging.h:33
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:818
const char * ParticleInitializationToString(ParticleInitializationType ParticleInitialization)
Helper function to convert ParticleInitialization to a string representation.
Definition logging.c:723
PetscErrorCode ComputeVectorFieldDerivatives(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, Cmpnts ***field_data, Cmpnts *dudx, Cmpnts *dvdx, Cmpnts *dwdx)
Internal helper implementation: ComputeVectorFieldDerivatives().
Definition setup.c:3517
PetscErrorCode DestroyUserContext(UserCtx *user)
Internal helper implementation: DestroyUserContext().
Definition setup.c:3709
PetscErrorCode GetOwnedCellRange(const DMDALocalInfo *info_nodes, PetscInt dim, PetscInt *xs_cell_global_out, PetscInt *xm_cell_local_out)
Internal helper implementation: GetOwnedCellRange().
Definition setup.c:2382
PetscErrorCode SetupDomainRankInfo(SimCtx *simCtx)
Implementation of SetupDomainRankInfo().
Definition setup.c:2673
PetscErrorCode UniformCart2Contra(UserCtx *user, PetscReal u, PetscReal v, PetscReal w)
Convert a uniform Cartesian velocity (u,v,w) to contravariant fluxes in Ucont.
Definition setup.c:2953
PetscErrorCode InitializeRandomGenerators(UserCtx *user, PetscRandom *randx, PetscRandom *randy, PetscRandom *randz)
Implementation of InitializeRandomGenerators().
Definition setup.c:3301
PetscErrorCode Deallocate3DArrayVector(Cmpnts ***array, PetscInt nz, PetscInt ny)
Implementation of Deallocate3DArrayVector().
Definition setup.c:2316
PetscErrorCode SetupGridAndSolvers(SimCtx *simCtx)
Implementation of SetupGridAndSolvers().
Definition setup.c:1350
static PetscErrorCode SetupSolverParameters(SimCtx *simCtx)
Internal helper implementation: SetupSolverParameters().
Definition setup.c:1318
PetscErrorCode InitializeBrownianRNG(SimCtx *simCtx)
Internal helper implementation: InitializeBrownianRNG().
Definition setup.c:3386
static PetscInt Gidx(PetscInt i, PetscInt j, PetscInt k, UserCtx *user)
Internal helper implementation: Gidx().
Definition setup.c:3109
PetscErrorCode SetupSimulationEnvironment(SimCtx *simCtx)
Internal helper implementation: SetupSimulationEnvironment().
Definition setup.c:1026
PetscErrorCode CreateAndInitializeAllVectors(SimCtx *simCtx)
Internal helper implementation: CreateAndInitializeAllVectors().
Definition setup.c:1387
PetscErrorCode ComputeAndStoreNeighborRanks(UserCtx *user)
Internal helper implementation: ComputeAndStoreNeighborRanks().
Definition setup.c:2479
PetscErrorCode Contra2Cart(UserCtx *user)
Internal helper implementation: Contra2Cart().
Definition setup.c:2746
void TransformScalarDerivativesToPhysical(PetscReal jacobian, Cmpnts csi_metrics, Cmpnts eta_metrics, Cmpnts zet_metrics, PetscReal dPhi_dcsi, PetscReal dPhi_deta, PetscReal dPhi_dzet, Cmpnts *gradPhi)
Implementation of TransformScalarDerivativesToPhysical().
Definition setup.c:3425
static PetscErrorCode PetscMkdirRecursive(const char *path)
Internal helper implementation: PetscMkdirRecursive().
Definition setup.c:974
PetscErrorCode DestroySolutionConvergenceState(SimCtx *simCtx)
Implementation of DestroySolutionConvergenceState().
Definition setup.c:98
PetscErrorCode Allocate3DArrayScalar(PetscReal ****array, PetscInt nz, PetscInt ny, PetscInt nx)
Internal helper implementation: Allocate3DArrayScalar().
Definition setup.c:2188
PetscErrorCode CreateSimulationContext(int argc, char **argv, SimCtx **p_simCtx)
Implementation of CreateSimulationContext().
Definition setup.c:151
PetscErrorCode InitializeSolutionConvergenceState(SimCtx *simCtx)
Implementation of InitializeSolutionConvergenceState().
Definition setup.c:47
PetscErrorCode SetDMDAProcLayout(DM dm, UserCtx *user)
Internal helper implementation: SetDMDAProcLayout().
Definition setup.c:2595
static PetscErrorCode RepairPeriodicNormalFaceGhosts(UserCtx *user, DM dm, Vec local_vec, PetscInt dof, char face_direction, PetscBool component_staggered)
Repairs the adjacent normal ghost layer for periodic face-staggered data.
Definition setup.c:1586
PetscErrorCode InitializeLogicalSpaceRNGs(PetscRandom *rand_logic_i, PetscRandom *rand_logic_j, PetscRandom *rand_logic_k)
Internal helper implementation: InitializeLogicalSpaceRNGs().
Definition setup.c:3342
PetscErrorCode ComputeScalarFieldDerivatives(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscReal ***field_data, Cmpnts *grad)
Internal helper implementation: ComputeScalarFieldDerivatives().
Definition setup.c:3474
PetscErrorCode ComputeDivergence(UserCtx *user)
Implementation of ComputeDivergence().
Definition setup.c:3135
PetscErrorCode BinarySearchInt64(PetscInt n, const PetscInt64 arr[], PetscInt64 key, PetscBool *found)
Implementation of BinarySearchInt64().
Definition setup.c:3065
static PetscErrorCode AllocateContextHierarchy(SimCtx *simCtx)
Internal helper implementation: AllocateContextHierarchy().
Definition setup.c:1215
PetscErrorCode Cart2Contra(UserCtx *user)
Convert a spatially varying Cartesian velocity field to contravariant fluxes.
Definition setup.c:2881
PetscErrorCode DestroyUserVectors(UserCtx *user)
Internal helper implementation: DestroyUserVectors().
Definition setup.c:3571
PetscErrorCode Allocate3DArrayVector(Cmpnts ****array, PetscInt nz, PetscInt ny, PetscInt nx)
Implementation of Allocate3DArrayVector().
Definition setup.c:2266
PetscErrorCode UpdateLocalGhosts(UserCtx *user, const char *fieldName)
Internal helper implementation: UpdateLocalGhosts().
Definition setup.c:1755
PetscErrorCode SetupBoundaryConditions(SimCtx *simCtx)
Internal helper implementation: SetupBoundaryConditions().
Definition setup.c:2124
static void TransformDerivativesToPhysical(PetscReal jacobian, Cmpnts csi_metrics, Cmpnts eta_metrics, Cmpnts zet_metrics, Cmpnts deriv_csi, Cmpnts deriv_eta, Cmpnts deriv_zet, Cmpnts *dudx, Cmpnts *dvdx, Cmpnts *dwdx)
Internal helper implementation: TransformDerivativesToPhysical().
Definition setup.c:3450
#define __FUNCT__
Definition setup.c:143
PetscErrorCode SetupDomainCellDecompositionMap(UserCtx *user)
Internal helper implementation: SetupDomainCellDecompositionMap().
Definition setup.c:3000
PetscErrorCode FinalizeSimulation(SimCtx *simCtx)
Implementation of FinalizeSimulation().
Definition setup.c:3808
PetscErrorCode Deallocate3DArrayScalar(PetscReal ***array, PetscInt nz, PetscInt ny)
Internal helper implementation: Deallocate3DArrayScalar().
Definition setup.c:2223
PetscBool RuntimeWalltimeGuardParsePositiveSeconds(const char *text, PetscReal *seconds_out)
Implementation of RuntimeWalltimeGuardParsePositiveSeconds().
Definition setup.c:18
PetscMPIInt rank_zm
Definition variables.h:197
LESModelType
Identifies the six logical faces of a structured computational block.
Definition variables.h:518
@ NO_LES_MODEL
Definition variables.h:519
PetscReal icVelocityPhysical
Definition variables.h:747
PetscInt MHV
Definition variables.h:720
Vec lFriction_Velocity
Definition variables.h:900
Vec lDiffusivityGradient
Definition variables.h:908
PetscInt isc
Definition variables.h:889
DM packer
Definition variables.h:580
PetscInt turbine
Definition variables.h:720
PetscBool mom_nk_monitor_history
Definition variables.h:740
PetscInt fishcyl
Definition variables.h:720
PetscInt clark
Definition variables.h:790
char statistics_output_prefix[256]
basename for CSV output, e.g.
Definition variables.h:616
PetscInt movefsi
Definition variables.h:714
Vec lCent
Definition variables.h:927
@ PERIODIC
Definition variables.h:290
Vec GridSpace
Definition variables.h:927
PetscBool continueMode
Definition variables.h:701
PetscInt moveframe
Definition variables.h:715
Vec P_nodal
Definition variables.h:957
Vec JCsi
Definition variables.h:931
Vec KAj
Definition variables.h:932
PetscInt TwoD
Definition variables.h:715
PetscInt pseudo_periodic
Definition variables.h:769
UserCtx * user
Definition variables.h:569
PetscInt fish_c
Definition variables.h:720
PetscInt ys_cell
Definition variables.h:202
PetscInt dgf_z
Definition variables.h:716
Vec JEta
Definition variables.h:931
PetscReal poisson_tol
Definition variables.h:729
Vec Zet
Definition variables.h:927
Vec Rhs
Definition variables.h:912
PetscBool profilingFinalSummary
Definition variables.h:837
char particle_output_prefix[256]
Definition variables.h:611
PetscInt xs_cell
Definition variables.h:202
PetscReal schmidt_number
Definition variables.h:765
PetscMPIInt rank
Definition variables.h:687
PetscInt mglevels
Definition variables.h:944
char profilingTimestepFile[PETSC_MAX_PATH_LEN]
Definition variables.h:836
PetscInt fish
Definition variables.h:720
PetscInt LV
Definition variables.h:720
PetscReal angle
Definition variables.h:760
PetscReal Turbulent_schmidt_number
Definition variables.h:765
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:896
PetscMPIInt rank_yp
Definition variables.h:196
PetscInt64 searchLocatedCount
Definition variables.h:239
PetscInt thin
Definition variables.h:715
MatNullSpace nullsp
Definition variables.h:918
PetscInt grid1d
Definition variables.h:768
PetscInt block_number
Definition variables.h:768
Vec lIEta
Definition variables.h:930
PetscInt * KSKE
Definition variables.h:919
PetscReal mom_rtol
Definition variables.h:726
PetscInt64 searchLostCount
Definition variables.h:240
PetscInt da_procs_z
Definition variables.h:774
PetscInt blkpbc
Definition variables.h:769
PetscInt sediment
Definition variables.h:714
PetscReal targetVolumetricFlux
Definition variables.h:780
Vec * solutionConvergencePeriodicPRef
Definition variables.h:914
PetscBool walltimeGuardActive
Definition variables.h:839
SNES snespacker
Definition variables.h:581
Vec lIZet
Definition variables.h:930
UserCtx * user_f
Definition variables.h:945
PetscReal mom_last_lambda_max
Definition variables.h:739
PetscInt channelz
Definition variables.h:721
Vec lNvert
Definition variables.h:904
Vec Phi
Definition variables.h:904
char euler_subdir[PETSC_MAX_PATH_LEN]
Definition variables.h:707
PetscReal walltimeGuardWarmupTotalSeconds
Definition variables.h:847
PetscReal forceScalingFactor
Definition variables.h:779
PetscReal pseudo_cfl_reduction_factor
Definition variables.h:733
InitialConditionMode initialConditionMode
Definition variables.h:742
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:879
PetscInt rans
Definition variables.h:789
ParticleInitializationType
Enumerator to identify the particle initialization strategy.
Definition variables.h:549
@ PARTICLE_INIT_SURFACE_RANDOM
Random placement on the inlet face.
Definition variables.h:550
PetscReal StartTime
Definition variables.h:698
PetscInt dgf_az
Definition variables.h:716
PetscReal * solutionConvergenceMeanSpeedHistory
Definition variables.h:753
PetscReal FluxOutSum
Definition variables.h:777
PetscMPIInt rank_ym
Definition variables.h:196
PetscBool walltimeGuardHasEWMA
Definition variables.h:849
PetscReal CMy_c
Definition variables.h:761
Vec K_Omega_o
Definition variables.h:935
Vec IZet
Definition variables.h:930
FlowDirection flowDirection
Definition variables.h:746
PetscMPIInt rank_xp
Definition variables.h:195
Vec Centz
Definition variables.h:928
PetscBool runtimeMemoryLogEnabled
Enable the rank-reduced runtime memory log.
Definition variables.h:852
char output_prefix[256]
Definition variables.h:608
char ** bcs_files
Definition variables.h:776
PetscReal boundaryVelocityCorrection
Definition variables.h:782
PetscReal max_angle
Definition variables.h:760
Vec IEta
Definition variables.h:930
PetscReal min_pseudo_cfl
Definition variables.h:734
PetscInt64 boundaryClampCount
Definition variables.h:246
PetscInt ksc
Definition variables.h:889
PetscInt particlesLostLastStep
Definition variables.h:803
PetscInt duplicate
Definition variables.h:818
PetscInt tiout
Definition variables.h:696
Vec lZet
Definition variables.h:927
PetscBool assignedA
Definition variables.h:923
UserMG usermg
Definition variables.h:821
PetscReal walltimeGuardMinSeconds
Definition variables.h:842
char allowedFile[PETSC_MAX_PATH_LEN]
Definition variables.h:822
Vec Csi
Definition variables.h:927
Vec * solutionConvergencePeriodicUcatRef
Definition variables.h:913
PetscInt da_procs_y
Definition variables.h:774
PetscInt64 traversalStepsSum
Definition variables.h:241
Vec K_Omega
Definition variables.h:935
PetscBool mom_last_converged
Definition variables.h:738
PetscInt testfilter_1d
Definition variables.h:792
PetscReal psrc_x
Definition variables.h:762
PetscReal ren
Definition variables.h:732
PetscReal Crotz
Definition variables.h:772
DM post_swarm
Definition variables.h:956
Vec lUcont_rm1
Definition variables.h:912
PetscInt mixed
Definition variables.h:790
PetscInt zm_cell
Definition variables.h:203
PetscInt solutionConvergenceSamplesRecorded
Definition variables.h:752
Vec lIAj
Definition variables.h:930
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
Definition variables.h:171
PetscInt zs_cell
Definition variables.h:202
Vec lCellFieldAtCorner
Definition variables.h:915
IBMVNodes * ibmv
Definition variables.h:815
PetscInt _this
Definition variables.h:889
Vec lKEta
Definition variables.h:932
PetscInt64 searchPopulation
Definition variables.h:238
char output_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:706
PetscReal * solutionConvergenceMeanKEHistory
Definition variables.h:754
PetscReal walltimeGuardLatestStepSeconds
Definition variables.h:851
PetscReal dt
Definition variables.h:699
char runtimeMemoryLogFile[PETSC_MAX_PATH_LEN]
File name written under log_dir.
Definition variables.h:853
PetscBool runtimeMemoryLogStarted
True after rank 0 writes the log header.
Definition variables.h:854
PetscInt occupiedCellCount
Definition variables.h:807
PetscInt StepsToRun
Definition variables.h:695
char profilingTimestepMode[32]
Definition variables.h:835
PetscInt k_periodic
Definition variables.h:769
PetscInt inletprofile
Definition variables.h:768
Vec Ucat_nodal
Definition variables.h:958
RankNeighbors neighbors
Definition variables.h:888
PetscReal bulkVelocityCorrection
Definition variables.h:781
PetscReal cdisy
Definition variables.h:732
PetscReal mom_atol
Definition variables.h:726
Vec lPsi
Definition variables.h:953
PetscBool rstart_fsi
Definition variables.h:817
PetscInt currentSettlementPass
Definition variables.h:250
PetscInt np
Definition variables.h:796
PetscInt jsc
Definition variables.h:889
PetscInt thislevel
Definition variables.h:570
PetscBool averaging
Definition variables.h:793
PetscBool no_pseudo_cfl_backtrack
Definition variables.h:736
PetscReal C_IEM
Definition variables.h:811
Vec DiffusivityGradient
Definition variables.h:908
Vec lJCsi
Definition variables.h:931
PetscInt ccc
Definition variables.h:785
Vec lCs
Definition variables.h:935
PetscReal ratio
Definition variables.h:786
PetscInt mg_idx
Definition variables.h:727
Vec Ucont
Definition variables.h:904
PetscInt StartStep
Definition variables.h:694
PetscInt mg_MAX_IT
Definition variables.h:727
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
Definition variables.h:170
PetscBool OnlySetup
Definition variables.h:700
PetscInt rotatefsi
Definition variables.h:714
Vec Ubcs
Physical Cartesian velocity at boundary faces. Full 3D array but only boundary-face entries are meani...
Definition variables.h:121
@ MOMENTUM_SOLVER_DUALTIME_PICARD_JAMESON_RK
Definition variables.h:534
@ MOMENTUM_SOLVER_EXPLICIT_RK
Definition variables.h:533
@ MOMENTUM_SOLVER_NEWTON_KRYLOV
Definition variables.h:535
PetscInt solutionConvergencePeriodSteps
Definition variables.h:750
PetscReal cdisz
Definition variables.h:732
Vec Qcrit
Definition variables.h:959
PetscScalar x
Definition variables.h:101
Vec JZet
Definition variables.h:931
PetscInt64 reSearchCount
Definition variables.h:242
PetscInt dgf_x
Definition variables.h:716
Vec Ucat_square_sum
Definition variables.h:938
char * current_io_directory
Definition variables.h:711
PetscInt pizza
Definition variables.h:720
PetscReal MaxDiv
Definition variables.h:828
Vec P_sum
Definition variables.h:938
Vec Centx
Definition variables.h:928
BCS Bcs
Definition variables.h:899
char grid_file[PETSC_MAX_PATH_LEN]
Definition variables.h:773
Vec lPhi
Definition variables.h:904
PetscReal max_cs
Definition variables.h:791
Vec lParticleCount
Definition variables.h:952
PetscInt invicid
Definition variables.h:715
char ** allowedFuncs
Definition variables.h:824
PetscInt xm_cell
Definition variables.h:203
char statistics_pipeline[1024]
e.g.
Definition variables.h:615
Vec lUcont_o
Definition variables.h:911
PetscInt64 bboxGuessFallbackCount
Definition variables.h:248
InterpolationMethod interpolationMethod
Definition variables.h:801
RankCellInfo * RankCellInfoMap
Definition variables.h:951
PetscReal psrc_z
Point source location for PARTICLE_INIT_POINT_SOURCE.
Definition variables.h:762
PetscInt mg_poItr
Definition variables.h:727
PetscInt STRONG_COUPLING
Definition variables.h:730
PetscInt ym_cell
Definition variables.h:203
VerificationScalarConfig verificationScalar
Definition variables.h:756
PetscReal max_pseudo_cfl
Definition variables.h:734
Vec Ucat_o
Definition variables.h:911
PetscInt MaxDivx
Definition variables.h:829
UserCtx * user_c
Definition variables.h:945
PetscInt poisson
Definition variables.h:728
PetscInt k_homo_filter
Definition variables.h:792
char profilingSelectedFuncsFile[PETSC_MAX_PATH_LEN]
Definition variables.h:831
PetscInt MaxDivy
Definition variables.h:829
PetscInt NumberOfBodies
Definition variables.h:759
char particleRestartMode[16]
Definition variables.h:802
PetscInt Ogrid
Definition variables.h:768
PetscInt64 bboxGuessSuccessCount
Definition variables.h:247
char particle_subdir[PETSC_MAX_PATH_LEN]
Definition variables.h:708
PetscInt MaxDivz
Definition variables.h:829
BoundingBox * bboxlist
Definition variables.h:799
PetscInt j_homo_filter
Definition variables.h:792
Vec lKZet
Definition variables.h:932
Vec Eta
Definition variables.h:927
PetscInt eel
Definition variables.h:720
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:709
PetscInt MaxDivFlatArg
Definition variables.h:829
Vec lNu_t
Definition variables.h:935
PetscReal FluxInSum
Definition variables.h:777
PetscMPIInt rank_xm
Definition variables.h:195
Vec Nu_t
Definition variables.h:935
PetscInt walltimeGuardCompletedSteps
Definition variables.h:846
PetscInt64 maxParticlePassDepth
Definition variables.h:249
char source_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:596
Vec lJEta
Definition variables.h:931
Vec lCsi
Definition variables.h:927
PetscReal CMz_c
Definition variables.h:761
Vec lGridSpace
Definition variables.h:927
PetscInt64 maxTraversalSteps
Definition variables.h:243
PetscBool generate_grid
Definition variables.h:770
PetscInt thislevel
Definition variables.h:944
Cmpnts AnalyticalUniformVelocity
Definition variables.h:748
char eulerianSource[PETSC_MAX_PATH_LEN]
Definition variables.h:704
PetscReal imp_stol
Definition variables.h:726
PetscInt nAllowed
Definition variables.h:825
PetscBool walltimeGuardEnabled
Definition variables.h:838
Vec ICsi
Definition variables.h:930
PetscReal wall_roughness_height
Definition variables.h:764
PetscBool useProfilingSelectedFuncsCfg
Definition variables.h:832
PetscInt walltimeGuardWarmupSteps
Definition variables.h:840
ParticleInitializationType ParticleInitialization
Definition variables.h:800
PetscReal mom_dt_jameson_residual_norm_noise_allowance_factor
Definition variables.h:735
PetscScalar z
Definition variables.h:101
Vec pUcont
Definition variables.h:912
InterpolationMethod
Selects the grid-to-particle interpolation method.
Definition variables.h:562
@ INTERP_TRILINEAR
Definition variables.h:563
Vec lK_Omega_o
Definition variables.h:935
Vec lKCsi
Definition variables.h:932
Vec Ucat
Definition variables.h:904
Vec ParticleCount
Definition variables.h:952
PetscReal Const_CS
Definition variables.h:791
Vec lK_Omega
Definition variables.h:935
Vec Ucont_o
Definition variables.h:911
PetscInt i_homo_filter
Definition variables.h:792
Vec CellFieldAtCorner
Definition variables.h:915
Vec lCenty
Definition variables.h:929
PetscInt wallfunction
Definition variables.h:790
PetscInt rheology
Definition variables.h:714
PetscReal Flux_in
Definition variables.h:760
PetscBool runtimeMemoryLogHasPrevious
True after the first process-memory sample.
Definition variables.h:855
PetscInt mglevels
Definition variables.h:576
char ** profilingSelectedFuncs
Definition variables.h:833
PetscReal cdisx
Definition variables.h:732
PetscInt dgf_ax
Definition variables.h:716
PetscInt mglevels
Definition variables.h:727
DM packer
Definition variables.h:571
Vec Ucat_sum
Definition variables.h:938
PetscInt num_bcs_files
Definition variables.h:775
DM dm_swarm
Definition variables.h:798
PetscBool useCfg
Definition variables.h:823
PetscReal psrc_y
Definition variables.h:762
PetscBool readFields
Definition variables.h:797
PetscInt solutionConvergenceWindowSteps
Definition variables.h:751
PetscInt central
Definition variables.h:730
PetscReal Fluxsum
Definition variables.h:777
Vec lJZet
Definition variables.h:931
Vec Nvert_o
Definition variables.h:911
FlowDirection
Primary flow direction for streamwise IC and Poiseuille modes.
Definition variables.h:270
@ FLOW_DIR_UNSET
Definition variables.h:277
PetscReal pseudo_cfl_growth_factor
Definition variables.h:733
PetscBool outputParticles
Definition variables.h:602
PetscReal Croty
Definition variables.h:772
PetscInt particlesLostCumulative
Definition variables.h:804
PetscInt nProfilingSelectedFuncs
Definition variables.h:834
Vec IAj
Definition variables.h:930
PetscInt particlesMigratedLastStep
Definition variables.h:806
char initialConditionDirectory[PETSC_MAX_PATH_LEN]
Definition variables.h:744
PetscReal grid_rotation_angle
Definition variables.h:771
PetscInt dynamic_freq
Definition variables.h:790
Vec Psi_nodal
Definition variables.h:960
char AnalyticalSolutionType[PETSC_MAX_PATH_LEN]
Definition variables.h:717
PetscInt da_procs_x
Definition variables.h:774
Vec JAj
Definition variables.h:931
PetscReal U_bc
Definition variables.h:784
PetscReal walltimeGuardWarmupAverageSeconds
Definition variables.h:848
Vec KEta
Definition variables.h:932
InitialConditionMode
Selects the algorithm used to populate a fresh Eulerian velocity field.
Definition variables.h:149
@ IC_MODE_FILE
Definition variables.h:154
@ IC_MODE_ZERO
Definition variables.h:150
PetscInt particleConsoleOutputFreq
Definition variables.h:697
Cmpnts InitialConstantContra
Definition variables.h:745
Vec lCentx
Definition variables.h:929
PetscMPIInt rank_zp
Definition variables.h:197
Vec Ucont_rm1
Definition variables.h:912
SearchMetricsState searchMetrics
Definition variables.h:809
PetscInt i_periodic
Definition variables.h:769
PetscReal mom_resid_rtol
Definition variables.h:726
Vec lUcont
Definition variables.h:904
PetscReal runtimeMemoryLogPreviousProcessMB
Previous local process memory sample in MB.
Definition variables.h:856
PetscInt step
Definition variables.h:692
Vec Diffusivity
Definition variables.h:907
PetscReal walltimeGuardEWMASeconds
Definition variables.h:850
PetscReal AreaOutSum
Definition variables.h:783
PetscInt dgf_ay
Definition variables.h:716
PetscInt mom_max_pseudo_steps
Definition variables.h:725
Vec lAj
Definition variables.h:927
PetscRandom BrownianMotionRNG
Definition variables.h:810
Vec lICsi
Definition variables.h:930
PetscInt testfilter_ik
Definition variables.h:792
DMDALocalInfo info
Definition variables.h:883
Vec dUcont
Definition variables.h:912
PetscInt hydro
Definition variables.h:720
Vec lUcat
Definition variables.h:904
PostProcessParams * pps
Definition variables.h:860
PetscInt migrationPassesLastStep
Definition variables.h:805
PetscScalar y
Definition variables.h:101
InitialConditionField
Selects the authoritative velocity representation in a staged file IC.
Definition variables.h:158
@ IC_FIELD_UCONT
Definition variables.h:160
@ IC_FIELD_UCAT
Definition variables.h:159
PetscMPIInt size
Definition variables.h:688
@ EXEC_MODE_SOLVER
Definition variables.h:657
@ EXEC_MODE_POSTPROCESSOR
Definition variables.h:658
char _io_context_buffer[PETSC_MAX_PATH_LEN]
Definition variables.h:710
PetscReal walltimeGuardLimitSeconds
Definition variables.h:845
Vec lEta
Definition variables.h:927
KSP ksp
Definition variables.h:918
PetscBool ps_ksp_pic_monitor_true_residual
Definition variables.h:741
Vec KZet
Definition variables.h:932
Vec Cent
Definition variables.h:927
Vec Ucat_cross_sum
Definition variables.h:938
PetscReal walltimeGuardEstimatorAlpha
Definition variables.h:843
PetscInt les
Definition variables.h:789
Vec Nvert
Definition variables.h:904
Vec KCsi
Definition variables.h:932
MGCtx * mgctx
Definition variables.h:579
@ SOLUTION_CONVERGENCE_TRANSIENT
Definition variables.h:545
@ SOLUTION_CONVERGENCE_PERIODIC_DETERMINISTIC
Definition variables.h:543
@ SOLUTION_CONVERGENCE_STATISTICAL_STEADY
Definition variables.h:544
@ SOLUTION_CONVERGENCE_STEADY_DETERMINISTIC
Definition variables.h:542
PetscInt mg_preItr
Definition variables.h:727
Vec lDiffusivity
Definition variables.h:907
Vec lNvert_o
Definition variables.h:911
PetscReal mom_ratio_ema_alpha
Definition variables.h:737
BCType mathematical_type
Definition variables.h:366
Vec Centy
Definition variables.h:928
SolutionConvergenceMode solutionConvergenceMode
Definition variables.h:749
PetscViewer logviewer
Definition variables.h:702
PetscBool multinullspace
Definition variables.h:920
Vec lCentz
Definition variables.h:929
PetscInt64 searchAttempts
Definition variables.h:237
InitialConditionField initialConditionField
Definition variables.h:743
ExecutionMode exec_mode
Definition variables.h:703
PetscInt64 tieBreakCount
Definition variables.h:245
PetscReal mom_resid_atol
Definition variables.h:726
Vec lJAj
Definition variables.h:931
BoundingBox bbox
Definition variables.h:887
PetscInt cop
Definition variables.h:720
PetscReal ti
Definition variables.h:693
PetscReal walltimeGuardMultiplier
Definition variables.h:841
PetscInt Pipe
Definition variables.h:720
PetscInt rotateframe
Definition variables.h:715
IBMNodes * ibm
Definition variables.h:814
PetscReal AreaInSum
Definition variables.h:783
MomentumSolverType mom_solver_type
Definition variables.h:724
PetscReal summationRHS
Definition variables.h:827
Vec lKAj
Definition variables.h:932
PetscInt immersed
Definition variables.h:714
PetscInt64 maxTraversalFailCount
Definition variables.h:244
char PostprocessingControlFile[PETSC_MAX_PATH_LEN]
Definition variables.h:859
char restart_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:705
VerificationDiffusivityConfig verificationDiffusivity
Definition variables.h:755
PetscInt blank
Definition variables.h:715
PetscInt dgf_y
Definition variables.h:716
PetscReal walltimeGuardJobStartEpochSeconds
Definition variables.h:844
PetscReal pseudo_cfl
Definition variables.h:732
PetscInt LoggingFrequency
Definition variables.h:826
PetscReal CMx_c
Definition variables.h:761
PetscReal drivingForceMagnitude
Definition variables.h:779
Vec Psi
Definition variables.h:953
PetscReal particleLoadImbalance
Definition variables.h:808
Vec P_o
Definition variables.h:911
Vec Uch
Characteristic velocity for boundary conditions.
Definition variables.h:122
@ BC_FACE_NEG_X
Definition variables.h:260
@ BC_FACE_POS_Z
Definition variables.h:262
@ BC_FACE_POS_Y
Definition variables.h:261
@ BC_FACE_NEG_Z
Definition variables.h:262
@ BC_FACE_POS_X
Definition variables.h:260
@ BC_FACE_NEG_Y
Definition variables.h:261
PetscInt j_periodic
Definition variables.h:769
PetscInt wing
Definition variables.h:720
FSInfo * fsi
Definition variables.h:816
Defines a 3D axis-aligned bounding box.
Definition variables.h:169
A 3D point or vector with PetscScalar components.
Definition variables.h:100
Context for Multigrid operations.
Definition variables.h:568
Holds all configuration parameters for a post-processing run.
Definition variables.h:594
A lean struct to hold the global cell ownership range for a single MPI rank.
Definition variables.h:201
The master context for the entire simulation.
Definition variables.h:684
User-defined context containing data specific to a single computational grid level.
Definition variables.h:876
User-level context for managing the entire multigrid hierarchy.
Definition variables.h:575