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