PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Functions
poisson.h File Reference
#include "variables.h"
#include "Metric.h"
#include "Boundaries.h"
Include dependency graph for poisson.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

PetscErrorCode PoissonSolver_MG (UserMG *usermg)
 Solves the pressure-Poisson equation using a geometric multigrid method.
 
PetscErrorCode PoissonLHSNew (UserCtx *user)
 Assembles the Left-Hand-Side (LHS) matrix (Laplacian operator) for the Poisson equation on a single grid level.
 
PetscErrorCode PoissonRHS (UserCtx *user, Vec B)
 Computes the Right-Hand-Side (RHS) of the Poisson equation, which is the divergence of the intermediate velocity field.
 
PetscErrorCode UpdatePressure (UserCtx *user)
 Updates the pressure field P with the pressure correction Phi computed by the Poisson solver.
 
PetscErrorCode CorrectChannelFluxProfile (UserCtx *user)
 Enforces a constant volumetric flux profile along the entire length of a driven periodic channel.
 
PetscErrorCode Projection (UserCtx *user)
 Corrects the contravariant velocity field Ucont to be divergence-free using the gradient of the pressure correction field Phi.
 
PetscErrorCode PoissonNullSpaceFunction (MatNullSpace nullsp, Vec X, void *ctx)
 The callback function for PETSc's MatNullSpace object.
 
PetscErrorCode MyRestriction (Mat A, Vec X, Vec F)
 The callback function for the multigrid restriction operator (MatShell).
 
PetscErrorCode MyInterpolation (Mat A, Vec X, Vec F)
 The callback function for the multigrid interpolation operator (MatShell).
 
PetscErrorCode VolumeFlux (UserCtx *user, PetscReal *ibm_Flux, PetscReal *ibm_Area, PetscInt flg)
 Calculates the net flux across the immersed boundary surface.
 
PetscErrorCode VolumeFlux_rev (UserCtx *user, PetscReal *ibm_Flux, PetscReal *ibm_Area, PetscInt flg)
 A specialized version of VolumeFlux, likely for reversed normals.
 

Function Documentation

◆ PoissonSolver_MG()

PetscErrorCode PoissonSolver_MG ( UserMG usermg)
extern

Solves the pressure-Poisson equation using a geometric multigrid method.

This function orchestrates the entire multigrid V-cycle for the pressure correction equation. It assembles the Laplacian matrix on all grid levels, sets up the KSP solvers, smoothers, restriction/interpolation operators, and executes the solve.

Parameters
usermgThe UserMG context containing the entire multigrid hierarchy.
Returns
PetscErrorCode 0 on success.
Note
Testing status: This routine is exercised in runtime smoke, but still needs deeper direct bespoke coverage for debugging and branch isolation.

Solves the pressure-Poisson equation using a geometric multigrid method.

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

See also
PoissonSolver_MG()

Definition at line 3146 of file poisson.c.

3147{
3148 // --- CONTEXT ACQUISITION BLOCK ---
3149 // Get the master simulation context from the first block's UserCtx on the finest level.
3150 // This provides access to all former global variables.
3151 SimCtx *simCtx = usermg->mgctx[0].user[0].simCtx;
3152
3153 // Create local variables to mirror the legacy globals for minimal code changes.
3154 const PetscInt block_number = simCtx->block_number;
3155 const PetscInt immersed = simCtx->immersed;
3156 const PetscInt MHV = simCtx->MHV;
3157 const PetscInt LV = simCtx->LV;
3158 PetscMPIInt rank = simCtx->rank;
3159 // --- END CONTEXT ACQUISITION BLOCK ---
3160
3161 PetscErrorCode ierr;
3162 PetscInt l, bi;
3163 MGCtx *mgctx = usermg->mgctx;
3164 KSP mgksp, subksp;
3165 PC mgpc, subpc;
3166 UserCtx *user;
3167
3168 PetscFunctionBeginUser; // Moved to after variable declarations
3170 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting Multigrid Poisson Solve...\n");
3171
3172 for (bi = 0; bi < block_number; bi++) {
3173
3174 // ====================================================================
3175 // SECTION: Immersed Boundary Specific Setup (Conditional)
3176 // ====================================================================
3177 if (immersed) {
3178 LOG_ALLOW(LOCAL, LOG_DEBUG, "Block %d: Performing IBM pre-solve setup (Nvert restriction, etc.).\n", bi);
3179 for (l = usermg->mglevels - 1; l > 0; l--) {
3180 mgctx[l].user[bi].multinullspace = PETSC_FALSE;
3181 MyNvertRestriction(&mgctx[l].user[bi], &mgctx[l-1].user[bi]);
3182 }
3183 // Coarsest level check for disconnected domains due to IBM
3184 l = 0;
3185 user = mgctx[l].user;
3186 /* KSKE is allocated once by CreateAndInitializeAllVectors; FullyBlocked
3187 * rewrites every entry it reads, so no per-solve reallocation is needed. */
3188 FullyBlocked(&user[bi]);
3189 }
3190
3191
3192 l = usermg->mglevels - 1;
3193 user = mgctx[l].user;
3194
3195 // We are solving the linear system AX=B where A = Laplacian Operator Matrix; X = Unknown Phi (Pressure Correction) and B = RHS (Flux Divergence based)
3196
3197 // --- 1. Compute RHS of the Poisson Equation ---
3198 LOG_ALLOW(LOCAL, LOG_DEBUG, "Block %d: Computing Poisson RHS...\n", bi);
3199 ierr = VecDuplicate(user[bi].P, &user[bi].B); CHKERRQ(ierr);
3200
3201 PetscReal ibm_Flux, ibm_Area;
3202 PetscInt flg = immersed - 1;
3203
3204 // Calculate volume flux source terms (often from IBM)
3205 VolumeFlux(&user[bi], &ibm_Flux, &ibm_Area, flg);
3206 if (MHV || LV) {
3207 flg = ((MHV > 1 || LV) && bi == 0) ? 1 : 0;
3208 VolumeFlux_rev(&user[bi], &ibm_Flux, &ibm_Area, flg);
3209 }
3210 // Calculate the main flux divergence term B.
3211 PoissonRHS(&user[bi], user[bi].B);
3212
3213 // --- 2. Assemble LHS Matrix (Laplacian) on all MG levels ---
3214 LOG_ALLOW(LOCAL, LOG_DEBUG, "Block %d: Assembling Poisson LHS on all levels...\n", bi);
3215 for (l = usermg->mglevels - 1; l >= 0; l--) {
3216 user = mgctx[l].user;
3217 LOG_ALLOW(GLOBAL,LOG_DEBUG," Calculating LHS for Level %d.\n",l);
3218 PoissonLHSNew(&user[bi]);
3219 }
3220
3221 // --- 3. Setup PETSc KSP and PCMG (Multigrid Preconditioner) ---
3222 LOG_ALLOW(LOCAL, LOG_DEBUG, "Block %d: Configuring KSP and PCMG...\n", bi);
3223
3224 ierr = KSPCreate(PETSC_COMM_WORLD, &mgksp); CHKERRQ(ierr);
3225 ierr = KSPAppendOptionsPrefix(mgksp, "ps_"); CHKERRQ(ierr);
3226
3227 // =======================================================================
3228 DualMonitorCtx *monctx;
3229 char filen[PETSC_MAX_PATH_LEN + 128];
3230
3231 // 1. Allocate the context and set it up.
3232 ierr = PetscNew(&monctx); CHKERRQ(ierr);
3233
3234 monctx->step = simCtx->step;
3235 monctx->block_id = bi;
3236 monctx->file_handle = NULL;
3237
3238 // Only rank 0 handles the file.
3239 if (!rank) {
3240 ierr = PetscSNPrintf(filen, sizeof(filen), "%s/Poisson_Solver_Convergence_History_Block_%d.log", simCtx->log_dir, bi); CHKERRQ(ierr);
3241 // On the very first step of a fresh run, TRUNCATE the file.
3242 // In continue mode, always APPEND to preserve existing data.
3243 if (simCtx->step == simCtx->StartStep + 1 && !simCtx->continueMode) {
3244 monctx->file_handle = fopen(filen, "w");
3245 } else { // For all subsequent steps (or continue mode), APPEND.
3246 monctx->file_handle = fopen(filen, "a");
3247 }
3248
3249 if (monctx->file_handle) {
3250 if (simCtx->continueMode && simCtx->step == simCtx->StartStep + 1) {
3251 PetscFPrintf(PETSC_COMM_SELF, monctx->file_handle,
3252 "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep);
3253 }
3254 PetscFPrintf(PETSC_COMM_SELF, monctx->file_handle, "--- Convergence for Timestep %d, Block %d ---\n", (int)simCtx->step, bi);
3255 } else {
3256 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Could not open KSP monitor log file: %s", filen);
3257 }
3258 }
3259
3261
3262 ierr = KSPMonitorSet(mgksp, DualKSPMonitor, monctx, DualMonitorDestroy); CHKERRQ(ierr);
3263 // =======================================================================
3264
3265 ierr = KSPGetPC(mgksp, &mgpc); CHKERRQ(ierr);
3266 ierr = PCSetType(mgpc, PCMG); CHKERRQ(ierr);
3267
3268 ierr = PCMGSetLevels(mgpc, usermg->mglevels, PETSC_NULLPTR); CHKERRQ(ierr);
3269 ierr = PCMGSetCycleType(mgpc, PC_MG_CYCLE_V); CHKERRQ(ierr);
3270 ierr = PCMGSetType(mgpc, PC_MG_MULTIPLICATIVE); CHKERRQ(ierr);
3271 if (simCtx->mg_preItr != simCtx->mg_poItr) {
3273 "PETSc PCMG exposes one smoother count in this build; using max(pre_sweeps=%d, post_sweeps=%d).\n",
3274 simCtx->mg_preItr, simCtx->mg_poItr);
3275 }
3276 PetscInt mg_smooths = simCtx->mg_preItr > simCtx->mg_poItr ? simCtx->mg_preItr : simCtx->mg_poItr;
3277 ierr = PCMGSetNumberSmooth(mgpc, mg_smooths); CHKERRQ(ierr);
3278
3279 // --- 4. Define Restriction and Interpolation Operators for MG ---
3280 for (l = usermg->mglevels - 1; l > 0; l--) {
3281
3282 // Get stable pointers directly from the main mgctx array.
3283 // These pointers point to memory that will persist.
3284 UserCtx *fine_user_ctx = &mgctx[l].user[bi];
3285 UserCtx *coarse_user_ctx = &mgctx[l-1].user[bi];
3286
3287 // --- Configure the context pointers ---
3288 // The coarse UserCtx needs to know about the fine grid for restriction.
3289 coarse_user_ctx->da_f = &(fine_user_ctx->da);
3290 coarse_user_ctx->user_f = fine_user_ctx;
3291
3292 // The fine UserCtx needs to know about the coarse grid for interpolation.
3293 fine_user_ctx->da_c = &(coarse_user_ctx->da);
3294 fine_user_ctx->user_c = coarse_user_ctx;
3295 fine_user_ctx->lNvert_c = &(coarse_user_ctx->lNvert);
3296
3297 // --- Get matrix dimensions ---
3298 PetscInt m_c = (coarse_user_ctx->info.xm * coarse_user_ctx->info.ym * coarse_user_ctx->info.zm);
3299 PetscInt m_f = (fine_user_ctx->info.xm * fine_user_ctx->info.ym * fine_user_ctx->info.zm);
3300 PetscInt M_c = (coarse_user_ctx->info.mx * coarse_user_ctx->info.my * coarse_user_ctx->info.mz);
3301 PetscInt M_f = (fine_user_ctx->info.mx * fine_user_ctx->info.my * fine_user_ctx->info.mz);
3302
3303 LOG_ALLOW(GLOBAL,LOG_DEBUG,"level = %d; m_c = %d; m_f = %d; M_c = %d; M_f = %d.\n",l,m_c,m_f,M_c,M_f);
3304 // --- Create the MatShell objects ---
3305 // Pass the STABLE pointer coarse_user_ctx as the context for restriction.
3306 ierr = MatCreateShell(PETSC_COMM_WORLD, m_c, m_f, M_c, M_f, (void*)coarse_user_ctx, &fine_user_ctx->MR); CHKERRQ(ierr);
3307
3308 // Pass the STABLE pointer fine_user_ctx as the context for interpolation.
3309 ierr = MatCreateShell(PETSC_COMM_WORLD, m_f, m_c, M_f, M_c, (void*)fine_user_ctx, &fine_user_ctx->MP); CHKERRQ(ierr);
3310
3311 // --- Set the operations for the MatShells ---
3312 ierr = MatShellSetOperation(fine_user_ctx->MR, MATOP_MULT, (void(*)(void))RestrictResidual_SolidAware); CHKERRQ(ierr);
3313 ierr = MatShellSetOperation(fine_user_ctx->MP, MATOP_MULT, (void(*)(void))MyInterpolation); CHKERRQ(ierr);
3314
3315 // --- Register the operators with PCMG ---
3316 ierr = PCMGSetRestriction(mgpc, l, fine_user_ctx->MR); CHKERRQ(ierr);
3317 ierr = PCMGSetInterpolation(mgpc, l, fine_user_ctx->MP); CHKERRQ(ierr);
3318
3319 }
3320
3321 // --- 5. Configure Solvers on Each MG Level ---
3322 for (l = usermg->mglevels - 1; l >= 0; l--) {
3323 user = mgctx[l].user;
3324 if (l > 0) { // Smoother for fine levels
3325 ierr = PCMGGetSmoother(mgpc, l, &subksp); CHKERRQ(ierr);
3326 } else { // Direct or iterative solver for the coarsest level
3327 ierr = PCMGGetCoarseSolve(mgpc, &subksp); CHKERRQ(ierr);
3328 ierr = KSPSetTolerances(subksp, 1.e-8, PETSC_DEFAULT, PETSC_DEFAULT, 40); CHKERRQ(ierr);
3329 }
3330
3331 ierr = KSPSetOperators(subksp, user[bi].A, user[bi].A); CHKERRQ(ierr);
3332 ierr = KSPGetPC(subksp, &subpc); CHKERRQ(ierr);
3333 ierr = PCSetType(subpc, PCBJACOBI); CHKERRQ(ierr);
3334 ierr = KSPSetFromOptions(subksp); CHKERRQ(ierr);
3335
3336 PCType subpc_type;
3337 PetscBool is_bjacobi = PETSC_FALSE;
3338 ierr = PCGetType(subpc, &subpc_type); CHKERRQ(ierr);
3339 if (subpc_type) {
3340 ierr = PetscStrcmp(subpc_type, PCBJACOBI, &is_bjacobi); CHKERRQ(ierr);
3341 }
3342
3343 if (is_bjacobi) {
3344 KSP *subsubksp;
3345 PC subsubpc;
3346 PetscInt nlocal;
3347
3348 ierr = KSPSetUp(subksp); CHKERRQ(ierr); // Set up KSP to allow access to sub-KSPs
3349 ierr = PCBJacobiGetSubKSP(subpc, &nlocal, NULL, &subsubksp); CHKERRQ(ierr);
3350
3351 for (PetscInt abi = 0; abi < nlocal; abi++) {
3352 ierr = KSPGetPC(subsubksp[abi], &subsubpc); CHKERRQ(ierr);
3353 // Add the critical shift amount for the nested block-Jacobi factor PC.
3354 ierr = PCFactorSetShiftAmount(subsubpc, 1.e-10); CHKERRQ(ierr);
3355 }
3356 }
3357
3358 ierr = MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_TRUE, 0, PETSC_NULLPTR, &user[bi].nullsp); CHKERRQ(ierr);
3359 ierr = MatNullSpaceSetFunction(user[bi].nullsp, PoissonNullSpaceFunction, &user[bi]); CHKERRQ(ierr);
3360 ierr = MatSetNullSpace(user[bi].A, user[bi].nullsp); CHKERRQ(ierr);
3361
3362 ierr = PCMGSetResidual(mgpc, l, PCMGResidualDefault, user[bi].A); CHKERRQ(ierr);
3363 ierr = KSPSetUp(subksp); CHKERRQ(ierr);
3364
3365 if (l < usermg->mglevels - 1) {
3366 ierr = MatCreateVecs(user[bi].A, &user[bi].R, PETSC_NULLPTR); CHKERRQ(ierr);
3367 ierr = PCMGSetRhs(mgpc, l, user[bi].R); CHKERRQ(ierr);
3368 }
3369 }
3370
3371 // --- 6. Set Final KSP Operators and Solve ---
3372 l = usermg->mglevels - 1;
3373 user = mgctx[l].user;
3374
3375 LOG_ALLOW(LOCAL, LOG_DEBUG, "Block %d: Setting KSP operators and solving...\n", bi);
3376 ierr = KSPSetOperators(mgksp, user[bi].A, user[bi].A); CHKERRQ(ierr);
3377 ierr = MatSetNullSpace(user[bi].A, user[bi].nullsp); CHKERRQ(ierr);
3378 ierr = KSPSetFromOptions(mgksp); CHKERRQ(ierr);
3379 ierr = KSPSetUp(mgksp); CHKERRQ(ierr);
3380 ierr = KSPSolve(mgksp, user[bi].B, user[bi].Phi); CHKERRQ(ierr);
3381
3382 // --- 7. Cleanup for this block ---
3383 for (l = usermg->mglevels - 1; l >= 0; l--) {
3384 user = mgctx[l].user;
3385 MatNullSpaceDestroy(&user[bi].nullsp);
3386 MatDestroy(&user[bi].A);
3387 user[bi].assignedA = PETSC_FALSE;
3388 if (l > 0) {
3389 MatDestroy(&user[bi].MR);
3390 MatDestroy(&user[bi].MP);
3391 }
3392 if (l < usermg->mglevels - 1) {
3393 VecDestroy(&user[bi].R);
3394 }
3395 }
3396
3397 KSPDestroy(&mgksp);
3398 VecDestroy(&mgctx[usermg->mglevels-1].user[bi].B);
3399
3400 } // End of loop over blocks
3401
3402 LOG_ALLOW(GLOBAL, LOG_INFO, "Multigrid Poisson Solve complete.\n");
3404 PetscFunctionReturn(0);
3405}
PetscErrorCode DualMonitorDestroy(void **ctx)
Destroys the DualMonitorCtx.
Definition logging.c:831
PetscBool log_to_console
Definition logging.h:58
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:45
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:200
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:859
PetscInt step
Definition logging.h:60
PetscErrorCode DualKSPMonitor(KSP ksp, PetscInt it, PetscReal rnorm, void *ctx)
A custom KSP monitor that logs to a file and optionally to the console.
Definition logging.c:870
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:850
FILE * file_handle
Definition logging.h:57
PetscInt block_id
Definition logging.h:61
Context for a dual-purpose KSP monitor.
Definition logging.h:56
PetscErrorCode PoissonNullSpaceFunction(MatNullSpace nullsp, Vec X, void *ctx)
Implementation of PoissonNullSpaceFunction().
Definition poisson.c:921
PetscErrorCode PoissonLHSNew(UserCtx *user)
Internal helper implementation: PoissonLHSNew().
Definition poisson.c:1423
PetscErrorCode VolumeFlux_rev(UserCtx *user, PetscReal *ibm_Flux, PetscReal *ibm_Area, PetscInt flg)
Implementation of VolumeFlux_rev().
Definition poisson.c:2121
static PetscErrorCode RestrictResidual_SolidAware(Mat A, Vec X, Vec F)
Restrict residuals while accounting for solid-cell occupancy in the stencil.
Definition poisson.c:1235
PetscErrorCode VolumeFlux(UserCtx *user, PetscReal *ibm_Flux, PetscReal *ibm_Area, PetscInt flg)
Implementation of VolumeFlux().
Definition poisson.c:2363
PetscErrorCode PoissonRHS(UserCtx *user, Vec B)
Implementation of PoissonRHS().
Definition poisson.c:2032
PetscErrorCode MyInterpolation(Mat A, Vec X, Vec F)
Implementation of MyInterpolation().
Definition poisson.c:1125
static PetscErrorCode FullyBlocked(UserCtx *user)
Report whether a coarse-grid cell is completely blocked by solid fine-grid cells.
Definition poisson.c:2938
static PetscErrorCode MyNvertRestriction(UserCtx *user_h, UserCtx *user_c)
Restrict solid-volume fractions from fine cells to one coarse cell.
Definition poisson.c:3025
PetscInt MHV
Definition variables.h:732
PetscBool continueMode
Definition variables.h:712
UserCtx * user
Definition variables.h:571
PetscMPIInt rank
Definition variables.h:698
PetscInt LV
Definition variables.h:732
PetscInt block_number
Definition variables.h:790
UserCtx * user_f
Definition variables.h:989
Vec lNvert
Definition variables.h:939
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:909
PetscBool assignedA
Definition variables.h:970
PetscInt StartStep
Definition variables.h:705
DM * da_c
Definition variables.h:990
PetscInt mg_poItr
Definition variables.h:739
UserCtx * user_c
Definition variables.h:989
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:718
DM * da_f
Definition variables.h:990
PetscInt mglevels
Definition variables.h:578
PetscInt step
Definition variables.h:703
DMDALocalInfo info
Definition variables.h:918
PetscBool ps_ksp_pic_monitor_true_residual
Definition variables.h:753
MGCtx * mgctx
Definition variables.h:581
PetscInt mg_preItr
Definition variables.h:739
PetscBool multinullspace
Definition variables.h:967
PetscInt immersed
Definition variables.h:726
Vec * lNvert_c
Definition variables.h:991
Context for Multigrid operations.
Definition variables.h:570
The master context for the entire simulation.
Definition variables.h:695
User-defined context containing data specific to a single computational grid level.
Definition variables.h:906
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PoissonLHSNew()

PetscErrorCode PoissonLHSNew ( UserCtx user)
extern

Assembles the Left-Hand-Side (LHS) matrix (Laplacian operator) for the Poisson equation on a single grid level.

Parameters
userThe UserCtx for the grid level on which to assemble the matrix.
Returns
PetscErrorCode 0 on success.
Note
Testing status: Direct unit coverage exists for core operator assembly, but periodic and immersed-boundary stencil branches remain thinner than the Cartesian baseline.

Assembles the Left-Hand-Side (LHS) matrix (Laplacian operator) for the Poisson equation on a single grid level.

Local to this translation unit.

Definition at line 1423 of file poisson.c.

1424{
1425 PetscFunctionBeginUser;
1427 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Entering PoissonLHSNew to assemble Laplacian matrix.\n");
1428 PetscErrorCode ierr;
1429 //================================================================================
1430 // Section 1: Initialization and Data Acquisition
1431 //================================================================================
1432
1433
1434 // --- Get simulation and grid context ---
1435 DM da = user->da, fda = user->fda;
1436 DMDALocalInfo info = user->info;
1437 PetscInt IM = user->IM, JM = user->JM, KM = user->KM;
1438 PetscInt i,j,k;
1439
1440 // --- Grid dimensions ---
1441 PetscInt mx = info.mx, my = info.my, mz = info.mz;
1442 PetscInt xs = info.xs, xe = info.xs + info.xm;
1443 PetscInt ys = info.ys, ye = info.ys + info.ym;
1444 PetscInt zs = info.zs, ze = info.zs + info.zm;
1445 PetscInt gxs = info.gxs, gxe = gxs + info.gxm;
1446 PetscInt gys = info.gys, gye = gys + info.gym;
1447 PetscInt gzs = info.gzs, gze = gzs + info.gzm;
1448
1449 // --- Define constants for clarity ---
1450 const PetscReal IBM_FLUID_THRESHOLD = 0.1;
1451
1452 // --- Allocate the LHS matrix A on the first call ---
1453 if (!user->assignedA) {
1454 LOG_ALLOW(GLOBAL, LOG_INFO, "First call: Creating LHS matrix 'A' with 19-point stencil preallocation.\n");
1455 PetscInt N = mx * my * mz; // Total size
1456 PetscInt M; // Local size
1457 VecGetLocalSize(user->Phi, &M);
1458 // Create a sparse AIJ matrix, preallocating for 19 non-zeros per row (d=diagonal, o=off-diagonal)
1459 MatCreateAIJ(PETSC_COMM_WORLD, M, M, N, N, 19, PETSC_NULLPTR, 19, PETSC_NULLPTR, &(user->A));
1460 user->assignedA = PETSC_TRUE;
1461 }
1462
1463 // Zero out matrix entries from the previous solve
1464 MatZeroEntries(user->A);
1465
1466 // --- Get direct pointer access to grid metric data ---
1467 Cmpnts ***csi, ***eta, ***zet, ***icsi, ***ieta, ***izet, ***jcsi, ***jeta, ***jzet, ***kcsi, ***keta, ***kzet;
1468 PetscReal ***aj, ***iaj, ***jaj, ***kaj, ***nvert;
1469 DMDAVecGetArray(fda, user->lCsi, &csi); DMDAVecGetArray(fda, user->lEta, &eta); DMDAVecGetArray(fda, user->lZet, &zet);
1470 DMDAVecGetArray(fda, user->lICsi, &icsi); DMDAVecGetArray(fda, user->lIEta, &ieta); DMDAVecGetArray(fda, user->lIZet, &izet);
1471 DMDAVecGetArray(fda, user->lJCsi, &jcsi); DMDAVecGetArray(fda, user->lJEta, &jeta); DMDAVecGetArray(fda, user->lJZet, &jzet);
1472 DMDAVecGetArray(fda, user->lKCsi, &kcsi); DMDAVecGetArray(fda, user->lKEta, &keta); DMDAVecGetArray(fda, user->lKZet, &kzet);
1473 DMDAVecGetArray(da, user->lAj, &aj); DMDAVecGetArray(da, user->lIAj, &iaj); DMDAVecGetArray(da, user->lJAj, &jaj); DMDAVecGetArray(da, user->lKAj, &kaj);
1474 DMDAVecGetArray(da, user->lNvert, &nvert);
1475
1476 // --- Create temporary vectors for the metric tensor components G_ij ---
1477 Vec G11, G12, G13, G21, G22, G23, G31, G32, G33;
1478 PetscReal ***g11, ***g12, ***g13, ***g21, ***g22, ***g23, ***g31, ***g32, ***g33;
1479 VecDuplicate(user->lAj, &G11); VecDuplicate(user->lAj, &G12); VecDuplicate(user->lAj, &G13);
1480 VecDuplicate(user->lAj, &G21); VecDuplicate(user->lAj, &G22); VecDuplicate(user->lAj, &G23);
1481 VecDuplicate(user->lAj, &G31); VecDuplicate(user->lAj, &G32); VecDuplicate(user->lAj, &G33);
1482 DMDAVecGetArray(da, G11, &g11); DMDAVecGetArray(da, G12, &g12); DMDAVecGetArray(da, G13, &g13);
1483 DMDAVecGetArray(da, G21, &g21); DMDAVecGetArray(da, G22, &g22); DMDAVecGetArray(da, G23, &g23);
1484 DMDAVecGetArray(da, G31, &g31); DMDAVecGetArray(da, G32, &g32); DMDAVecGetArray(da, G33, &g33);
1485
1486 //================================================================================
1487 // Section 2: Pre-compute Metric Tensor Coefficients (g_ij)
1488 //================================================================================
1489 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Pre-computing metric tensor components (g_ij).\n");
1490 for (k = gzs; k < gze; k++) {
1491 for (j = gys; j < gye; j++) {
1492 for (i = gxs; i < gxe; i++) {
1493 // These coefficients represent the dot products of the grid's contravariant base vectors,
1494 // scaled by face area. They are the core of the Laplacian operator on a curvilinear grid.
1495 if(i>-1 && j>-1 && k>-1 && i<IM+1 && j<JM+1 && k<KM+1){
1496 g11[k][j][i] = (icsi[k][j][i].x * icsi[k][j][i].x + icsi[k][j][i].y * icsi[k][j][i].y + icsi[k][j][i].z * icsi[k][j][i].z) * iaj[k][j][i];
1497 g12[k][j][i] = (ieta[k][j][i].x * icsi[k][j][i].x + ieta[k][j][i].y * icsi[k][j][i].y + ieta[k][j][i].z * icsi[k][j][i].z) * iaj[k][j][i];
1498 g13[k][j][i] = (izet[k][j][i].x * icsi[k][j][i].x + izet[k][j][i].y * icsi[k][j][i].y + izet[k][j][i].z * icsi[k][j][i].z) * iaj[k][j][i];
1499 g21[k][j][i] = (jcsi[k][j][i].x * jeta[k][j][i].x + jcsi[k][j][i].y * jeta[k][j][i].y + jcsi[k][j][i].z * jeta[k][j][i].z) * jaj[k][j][i];
1500 g22[k][j][i] = (jeta[k][j][i].x * jeta[k][j][i].x + jeta[k][j][i].y * jeta[k][j][i].y + jeta[k][j][i].z * jeta[k][j][i].z) * jaj[k][j][i];
1501 g23[k][j][i] = (jzet[k][j][i].x * jeta[k][j][i].x + jzet[k][j][i].y * jeta[k][j][i].y + jzet[k][j][i].z * jeta[k][j][i].z) * jaj[k][j][i];
1502 g31[k][j][i] = (kcsi[k][j][i].x * kzet[k][j][i].x + kcsi[k][j][i].y * kzet[k][j][i].y + kcsi[k][j][i].z * kzet[k][j][i].z) * kaj[k][j][i];
1503 g32[k][j][i] = (keta[k][j][i].x * kzet[k][j][i].x + keta[k][j][i].y * kzet[k][j][i].y + keta[k][j][i].z * kzet[k][j][i].z) * kaj[k][j][i];
1504 g33[k][j][i] = (kzet[k][j][i].x * kzet[k][j][i].x + kzet[k][j][i].y * kzet[k][j][i].y + kzet[k][j][i].z * kzet[k][j][i].z) * kaj[k][j][i];
1505 }
1506 }
1507 }
1508 }
1509
1510 //================================================================================
1511 // Section 3: Assemble the LHS Matrix A
1512 //================================================================================
1513 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Assembling the LHS matrix A using a 19-point stencil.\n");
1514
1515 // --- Define domain boundaries for stencil logic, accounting for periodic BCs ---
1516 PetscInt x_str, x_end, y_str, y_end, z_str, z_end;
1517 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC) { x_end = mx - 1; x_str = 0; }
1518 else { x_end = mx - 2; x_str = 1; }
1519 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC) { y_end = my - 1; y_str = 0; }
1520 else { y_end = my - 2; y_str = 1; }
1521 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC) { z_end = mz - 1; z_str = 0; }
1522 else { z_end = mz - 2; z_str = 1; }
1523
1524 // --- Main assembly loop over all local grid points ---
1525 for (k = zs; k < ze; k++) {
1526 for (j = ys; j < ye; j++) {
1527 for (i = xs; i < xe; i++) {
1528 PetscScalar vol[19]; // Holds the 19 stencil coefficient values for the current row
1529 PetscInt idx[19]; // Holds the 19 global column indices for the current row
1530 PetscInt row = Gidx(i, j, k, user); // Global index for the current row
1531
1532 // --- Handle Domain Boundary and Immersed Solid Points ---
1533 // For these points, we don't solve the Poisson equation. We set an identity
1534 // row (A_ii = 1) to effectively fix the pressure value (usually to 0).
1535 if (i == 0 || i == mx - 1 || j == 0 || j == my - 1 || k == 0 || k == mz - 1 || nvert[k][j][i] > IBM_FLUID_THRESHOLD) {
1536 vol[CP] = 1.0;
1537 idx[CP] = row;
1538 MatSetValues(user->A, 1, &row, 1, &idx[CP], &vol[CP], INSERT_VALUES);
1539 }
1540 // --- Handle Fluid Points ---
1541 else {
1542 for (PetscInt m = 0; m < 19; m++) {
1543 vol[m] = 0.0;
1544 }
1545
1546 /************************************************************************
1547 * EAST FACE CONTRIBUTION (between i and i+1)
1548 ************************************************************************/
1549 if (nvert[k][j][i + 1] < IBM_FLUID_THRESHOLD && i != x_end) { // East neighbor is fluid
1550 // Primary derivative term: d/d_csi (g11 * dP/d_csi)
1551 vol[CP] -= g11[k][j][i];
1552 vol[EP] += g11[k][j][i];
1553
1554 // Cross-derivative term: d/d_csi (g12 * dP/d_eta).
1555 // This requires an average of dP/d_eta. If a neighbor is solid, the stencil
1556 // dynamically switches to a one-sided difference to avoid using solid points.
1557 if ((j == my-2 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC) || nvert[k][j+1][i] + nvert[k][j+1][i+1] > 0.1) {
1558 if (nvert[k][j-1][i] + nvert[k][j-1][i+1] < 0.1 && (j!=1 || (j==1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC))) {
1559 vol[CP] += g12[k][j][i] * 0.5; vol[EP] += g12[k][j][i] * 0.5;
1560 vol[SP] -= g12[k][j][i] * 0.5; vol[SE] -= g12[k][j][i] * 0.5;
1561 }
1562 }
1563 else if ((j == my-2 || j==1) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j+1][i] + nvert[k][j+1][i+1] > 0.1) {
1564 if (nvert[k][j-1][i] + nvert[k][j-1][i+1] < 0.1) {
1565 vol[CP] += g12[k][j][i] * 0.5; vol[EP] += g12[k][j][i] * 0.5;
1566 vol[SP] -= g12[k][j][i] * 0.5; vol[SE] -= g12[k][j][i] * 0.5;
1567 }
1568 }
1569 else if ((j == 1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC) || nvert[k][j-1][i] + nvert[k][j-1][i+1] > 0.1) {
1570 if (nvert[k][j+1][i] + nvert[k][j+1][i+1] < 0.1) {
1571 vol[NP] += g12[k][j][i] * 0.5; vol[NE] += g12[k][j][i] * 0.5;
1572 vol[CP] -= g12[k][j][i] * 0.5; vol[EP] -= g12[k][j][i] * 0.5;
1573 }
1574 }
1575 else if ((j == 1 || j==my-2) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j-1][i] + nvert[k][j-1][i+1] > 0.1) {
1576 if (nvert[k][j+1][i] + nvert[k][j+1][i+1] < 0.1) {
1577 vol[NP] += g12[k][j][i] * 0.5; vol[NE] += g12[k][j][i] * 0.5;
1578 vol[CP] -= g12[k][j][i] * 0.5; vol[EP] -= g12[k][j][i] * 0.5;
1579 }
1580 }
1581 else { // Centered difference
1582 vol[NP] += g12[k][j][i] * 0.25; vol[NE] += g12[k][j][i] * 0.25;
1583 vol[SP] -= g12[k][j][i] * 0.25; vol[SE] -= g12[k][j][i] * 0.25;
1584 }
1585
1586 // Cross-derivative term: d/d_csi (g13 * dP/d_zet)
1587 if ((k == mz-2 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC) || nvert[k+1][j][i] + nvert[k+1][j][i+1] > 0.1) {
1588 if (nvert[k-1][j][i] + nvert[k-1][j][i+1] < 0.1 && (k!=1 || (k==1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC))) {
1589 vol[CP] += g13[k][j][i] * 0.5; vol[EP] += g13[k][j][i] * 0.5;
1590 vol[BP] -= g13[k][j][i] * 0.5; vol[BE] -= g13[k][j][i] * 0.5;
1591 }
1592 }
1593 else if ((k == mz-2 || k==1) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k+1][j][i] + nvert[k+1][j][i+1] > 0.1) {
1594 if (nvert[k-1][j][i] + nvert[k-1][j][i+1] < 0.1) {
1595 vol[CP] += g13[k][j][i] * 0.5; vol[EP] += g13[k][j][i] * 0.5;
1596 vol[BP] -= g13[k][j][i] * 0.5; vol[BE] -= g13[k][j][i] * 0.5;
1597 }
1598 }
1599 else if ((k == 1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC) || nvert[k-1][j][i] + nvert[k-1][j][i+1] > 0.1) {
1600 if (nvert[k+1][j][i] + nvert[k+1][j][i+1] < 0.1) {
1601 vol[TP] += g13[k][j][i] * 0.5; vol[TE] += g13[k][j][i] * 0.5;
1602 vol[CP] -= g13[k][j][i] * 0.5; vol[EP] -= g13[k][j][i] * 0.5;
1603 }
1604 }
1605 else if ((k == 1 || k==mz-2) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k-1][j][i] + nvert[k-1][j][i+1] > 0.1) {
1606 if (nvert[k+1][j][i] + nvert[k+1][j][i+1] < 0.1) {
1607 vol[TP] += g13[k][j][i] * 0.5; vol[TE] += g13[k][j][i] * 0.5;
1608 vol[CP] -= g13[k][j][i] * 0.5; vol[EP] -= g13[k][j][i] * 0.5;
1609 }
1610 }
1611 else { // Centered difference
1612 vol[TP] += g13[k][j][i] * 0.25; vol[TE] += g13[k][j][i] * 0.25;
1613 vol[BP] -= g13[k][j][i] * 0.25; vol[BE] -= g13[k][j][i] * 0.25;
1614 }
1615 }
1616
1617 /************************************************************************
1618 * WEST FACE CONTRIBUTION (between i-1 and i)
1619 ************************************************************************/
1620 if (nvert[k][j][i-1] < IBM_FLUID_THRESHOLD && i != x_str) {
1621 vol[CP] -= g11[k][j][i-1];
1622 vol[WP] += g11[k][j][i-1];
1623
1624 if ((j == my-2 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC) || nvert[k][j+1][i] + nvert[k][j+1][i-1] > 0.1) {
1625 if (nvert[k][j-1][i] + nvert[k][j-1][i-1] < 0.1 && (j!=1 || (j==1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC))) {
1626 vol[CP] -= g12[k][j][i-1] * 0.5; vol[WP] -= g12[k][j][i-1] * 0.5;
1627 vol[SP] += g12[k][j][i-1] * 0.5; vol[SW] += g12[k][j][i-1] * 0.5;
1628 }
1629 }
1630 else if ((j == my-2 || j==1) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j+1][i] + nvert[k][j+1][i-1] > 0.1) {
1631 if (nvert[k][j-1][i] + nvert[k][j-1][i-1] < 0.1) {
1632 vol[CP] -= g12[k][j][i-1] * 0.5; vol[WP] -= g12[k][j][i-1] * 0.5;
1633 vol[SP] += g12[k][j][i-1] * 0.5; vol[SW] += g12[k][j][i-1] * 0.5;
1634 }
1635 }
1636 else if ((j == 1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC)|| nvert[k][j-1][i] + nvert[k][j-1][i-1] > 0.1) {
1637 if (nvert[k][j+1][i] + nvert[k][j+1][i-1] < 0.1) {
1638 vol[NP] -= g12[k][j][i-1] * 0.5; vol[NW] -= g12[k][j][i-1] * 0.5;
1639 vol[CP] += g12[k][j][i-1] * 0.5; vol[WP] += g12[k][j][i-1] * 0.5;
1640 }
1641 }
1642 else if ((j == 1 || j==my-2) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j-1][i] + nvert[k][j-1][i-1] > 0.1) {
1643 if (nvert[k][j+1][i] + nvert[k][j+1][i-1] < 0.1) {
1644 vol[NP] -= g12[k][j][i-1] * 0.5; vol[NW] -= g12[k][j][i-1] * 0.5;
1645 vol[CP] += g12[k][j][i-1] * 0.5; vol[WP] += g12[k][j][i-1] * 0.5;
1646 }
1647 }
1648 else {
1649 vol[NP] -= g12[k][j][i-1] * 0.25; vol[NW] -= g12[k][j][i-1] * 0.25;
1650 vol[SP] += g12[k][j][i-1] * 0.25; vol[SW] += g12[k][j][i-1] * 0.25;
1651 }
1652
1653 if ((k == mz-2 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC) || nvert[k+1][j][i] + nvert[k+1][j][i-1] > 0.1) {
1654 if (nvert[k-1][j][i] + nvert[k-1][j][i-1] < 0.1 && (k!=1 || (k==1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC))) {
1655 vol[CP] -= g13[k][j][i-1] * 0.5; vol[WP] -= g13[k][j][i-1] * 0.5;
1656 vol[BP] += g13[k][j][i-1] * 0.5; vol[BW] += g13[k][j][i-1] * 0.5;
1657 }
1658 }
1659 else if ((k == mz-2 || k==1) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k+1][j][i] + nvert[k+1][j][i-1] > 0.1) {
1660 if (nvert[k-1][j][i] + nvert[k-1][j][i-1] < 0.1) {
1661 vol[CP] -= g13[k][j][i-1] * 0.5; vol[WP] -= g13[k][j][i-1] * 0.5;
1662 vol[BP] += g13[k][j][i-1] * 0.5; vol[BW] += g13[k][j][i-1] * 0.5;
1663 }
1664 }
1665 else if ((k == 1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC) || nvert[k-1][j][i] + nvert[k-1][j][i-1] > 0.1) {
1666 if (nvert[k+1][j][i] + nvert[k+1][j][i-1] < 0.1) {
1667 vol[TP] -= g13[k][j][i-1] * 0.5; vol[TW] -= g13[k][j][i-1] * 0.5;
1668 vol[CP] += g13[k][j][i-1] * 0.5; vol[WP] += g13[k][j][i-1] * 0.5;
1669 }
1670 }
1671 else if ((k == 1 || k==mz-2) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k-1][j][i] + nvert[k-1][j][i-1] > 0.1) {
1672 if (nvert[k+1][j][i] + nvert[k+1][j][i-1] < 0.1) {
1673 vol[TP] -= g13[k][j][i-1] * 0.5; vol[TW] -= g13[k][j][i-1] * 0.5;
1674 vol[CP] += g13[k][j][i-1] * 0.5; vol[WP] += g13[k][j][i-1] * 0.5;
1675 }
1676 }
1677 else {
1678 vol[TP] -= g13[k][j][i-1] * 0.25; vol[TW] -= g13[k][j][i-1] * 0.25;
1679 vol[BP] += g13[k][j][i-1] * 0.25; vol[BW] += g13[k][j][i-1] * 0.25;
1680 }
1681 }
1682
1683 /************************************************************************
1684 * NORTH FACE CONTRIBUTION (between j and j+1)
1685 ************************************************************************/
1686 if (nvert[k][j+1][i] < IBM_FLUID_THRESHOLD && j != y_end) {
1687 if ((i == mx-2 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i+1] + nvert[k][j+1][i+1] > 0.1) {
1688 if (nvert[k][j][i-1] + nvert[k][j+1][i-1] < 0.1 && (i!=1 || (i==1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC))) {
1689 vol[CP] += g21[k][j][i] * 0.5; vol[NP] += g21[k][j][i] * 0.5;
1690 vol[WP] -= g21[k][j][i] * 0.5; vol[NW] -= g21[k][j][i] * 0.5;
1691 }
1692 }
1693 else if ((i == mx-2 || i==1) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i+1] + nvert[k][j+1][i+1] > 0.1) {
1694 if (nvert[k][j][i-1] + nvert[k][j+1][i-1] < 0.1) {
1695 vol[CP] += g21[k][j][i] * 0.5; vol[NP] += g21[k][j][i] * 0.5;
1696 vol[WP] -= g21[k][j][i] * 0.5; vol[NW] -= g21[k][j][i] * 0.5;
1697 }
1698 }
1699 else if ((i == 1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC) || nvert[k][j][i-1] + nvert[k][j+1][i-1] > 0.1) {
1700 if (nvert[k][j][i+1] + nvert[k][j+1][i+1] < 0.1) {
1701 vol[EP] += g21[k][j][i] * 0.5; vol[NE] += g21[k][j][i] * 0.5;
1702 vol[CP] -= g21[k][j][i] * 0.5; vol[NP] -= g21[k][j][i] * 0.5;
1703 }
1704 }
1705 else if ((i == 1 || i==mx-2) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i-1] + nvert[k][j+1][i-1] > 0.1) {
1706 if (nvert[k][j][i+1] + nvert[k][j+1][i+1] < 0.1) {
1707 vol[EP] += g21[k][j][i] * 0.5; vol[NE] += g21[k][j][i] * 0.5;
1708 vol[CP] -= g21[k][j][i] * 0.5; vol[NP] -= g21[k][j][i] * 0.5;
1709 }
1710 }
1711 else {
1712 vol[EP] += g21[k][j][i] * 0.25; vol[NE] += g21[k][j][i] * 0.25;
1713 vol[WP] -= g21[k][j][i] * 0.25; vol[NW] -= g21[k][j][i] * 0.25;
1714 }
1715
1716 vol[CP] -= g22[k][j][i];
1717 vol[NP] += g22[k][j][i];
1718
1719 if ((k == mz-2 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC)|| nvert[k+1][j][i] + nvert[k+1][j+1][i] > 0.1) {
1720 if (nvert[k-1][j][i] + nvert[k-1][j+1][i] < 0.1 && (k!=1 || (k==1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC))) {
1721 vol[CP] += g23[k][j][i] * 0.5; vol[NP] += g23[k][j][i] * 0.5;
1722 vol[BP] -= g23[k][j][i] * 0.5; vol[BN] -= g23[k][j][i] * 0.5;
1723 }
1724 }
1725 else if ((k == mz-2 || k==1 ) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k+1][j][i] + nvert[k+1][j+1][i] > 0.1) {
1726 if (nvert[k-1][j][i] + nvert[k-1][j+1][i] < 0.1) {
1727 vol[CP] += g23[k][j][i] * 0.5; vol[NP] += g23[k][j][i] * 0.5;
1728 vol[BP] -= g23[k][j][i] * 0.5; vol[BN] -= g23[k][j][i] * 0.5;
1729 }
1730 }
1731 else if ((k == 1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC)|| nvert[k-1][j][i] + nvert[k-1][j+1][i] > 0.1) {
1732 if (nvert[k+1][j][i] + nvert[k+1][j+1][i] < 0.1) {
1733 vol[TP] += g23[k][j][i] * 0.5; vol[TN] += g23[k][j][i] * 0.5;
1734 vol[CP] -= g23[k][j][i] * 0.5; vol[NP] -= g23[k][j][i] * 0.5;
1735 }
1736 }
1737 else if ((k == 1 || k==mz-2 ) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k-1][j][i] + nvert[k-1][j+1][i] > 0.1) {
1738 if (nvert[k+1][j][i] + nvert[k+1][j+1][i] < 0.1) {
1739 vol[TP] += g23[k][j][i] * 0.5; vol[TN] += g23[k][j][i] * 0.5;
1740 vol[CP] -= g23[k][j][i] * 0.5; vol[NP] -= g23[k][j][i] * 0.5;
1741 }
1742 }
1743 else {
1744 vol[TP] += g23[k][j][i] * 0.25; vol[TN] += g23[k][j][i] * 0.25;
1745 vol[BP] -= g23[k][j][i] * 0.25; vol[BN] -= g23[k][j][i] * 0.25;
1746 }
1747 }
1748
1749 /************************************************************************
1750 * SOUTH FACE CONTRIBUTION (between j-1 and j)
1751 ************************************************************************/
1752 if (nvert[k][j-1][i] < IBM_FLUID_THRESHOLD && j != y_str) {
1753 if ((i == mx-2 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC) || nvert[k][j][i+1] + nvert[k][j-1][i+1] > 0.1) {
1754 if (nvert[k][j][i-1] + nvert[k][j-1][i-1] < 0.1 && (i!=1 || (i==1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC))) {
1755 vol[CP] -= g21[k][j-1][i] * 0.5; vol[SP] -= g21[k][j-1][i] * 0.5;
1756 vol[WP] += g21[k][j-1][i] * 0.5; vol[SW] += g21[k][j-1][i] * 0.5;
1757 }
1758 }
1759 else if ((i == mx-2 || i==1) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i+1] + nvert[k][j-1][i+1] > 0.1) {
1760 if (nvert[k][j][i-1] + nvert[k][j-1][i-1] < 0.1) {
1761 vol[CP] -= g21[k][j-1][i] * 0.5; vol[SP] -= g21[k][j-1][i] * 0.5;
1762 vol[WP] += g21[k][j-1][i] * 0.5; vol[SW] += g21[k][j-1][i] * 0.5;
1763 }
1764 }
1765 else if ((i == 1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i-1] + nvert[k][j-1][i-1] > 0.1) {
1766 if (nvert[k][j][i+1] + nvert[k][j-1][i+1] < 0.1) {
1767 vol[EP] -= g21[k][j-1][i] * 0.5; vol[SE] -= g21[k][j-1][i] * 0.5;
1768 vol[CP] += g21[k][j-1][i] * 0.5; vol[SP] += g21[k][j-1][i] * 0.5;
1769 }
1770 }
1771 else if ((i == 1 || i==mx-2) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i-1] + nvert[k][j-1][i-1] > 0.1) {
1772 if (nvert[k][j][i+1] + nvert[k][j-1][i+1] < 0.1) {
1773 vol[EP] -= g21[k][j-1][i] * 0.5; vol[SE] -= g21[k][j-1][i] * 0.5;
1774 vol[CP] += g21[k][j-1][i] * 0.5; vol[SP] += g21[k][j-1][i] * 0.5;
1775 }
1776 }
1777 else {
1778 vol[EP] -= g21[k][j-1][i] * 0.25; vol[SE] -= g21[k][j-1][i] * 0.25;
1779 vol[WP] += g21[k][j-1][i] * 0.25; vol[SW] += g21[k][j-1][i] * 0.25;
1780 }
1781
1782 vol[CP] -= g22[k][j-1][i];
1783 vol[SP] += g22[k][j-1][i];
1784
1785 if ((k == mz-2 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC)|| nvert[k+1][j][i] + nvert[k+1][j-1][i] > 0.1) {
1786 if (nvert[k-1][j][i] + nvert[k-1][j-1][i] < 0.1 && (k!=1 || (k==1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC))) {
1787 vol[CP] -= g23[k][j-1][i] * 0.5; vol[SP] -= g23[k][j-1][i] * 0.5;
1788 vol[BP] += g23[k][j-1][i] * 0.5; vol[BS] += g23[k][j-1][i] * 0.5;
1789 }
1790 }
1791 else if ((k == mz-2 || k==1) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k+1][j][i] + nvert[k+1][j-1][i] > 0.1) {
1792 if (nvert[k-1][j][i] + nvert[k-1][j-1][i] < 0.1 ) {
1793 vol[CP] -= g23[k][j-1][i] * 0.5; vol[SP] -= g23[k][j-1][i] * 0.5;
1794 vol[BP] += g23[k][j-1][i] * 0.5; vol[BS] += g23[k][j-1][i] * 0.5;
1795 }
1796 }
1797 else if ((k == 1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC)|| nvert[k-1][j][i] + nvert[k-1][j-1][i] > 0.1) {
1798 if (nvert[k+1][j][i] + nvert[k+1][j-1][i] < 0.1) {
1799 vol[TP] -= g23[k][j-1][i] * 0.5; vol[TS] -= g23[k][j-1][i] * 0.5;
1800 vol[CP] += g23[k][j-1][i] * 0.5; vol[SP] += g23[k][j-1][i] * 0.5;
1801 }
1802 }
1803 else if ((k == 1 || k==mz-2) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k-1][j][i] + nvert[k-1][j-1][i] > 0.1) {
1804 if (nvert[k+1][j][i] + nvert[k+1][j-1][i] < 0.1) {
1805 vol[TP] -= g23[k][j-1][i] * 0.5; vol[TS] -= g23[k][j-1][i] * 0.5;
1806 vol[CP] += g23[k][j-1][i] * 0.5; vol[SP] += g23[k][j-1][i] * 0.5;
1807 }
1808 }
1809 else {
1810 vol[TP] -= g23[k][j-1][i] * 0.25; vol[TS] -= g23[k][j-1][i] * 0.25;
1811 vol[BP] += g23[k][j-1][i] * 0.25; vol[BS] += g23[k][j-1][i] * 0.25;
1812 }
1813 }
1814
1815 /************************************************************************
1816 * TOP FACE CONTRIBUTION (between k and k+1)
1817 ************************************************************************/
1818 if (nvert[k+1][j][i] < IBM_FLUID_THRESHOLD && k != z_end) {
1819 if ((i == mx-2 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i+1] + nvert[k+1][j][i+1] > 0.1) {
1820 if (nvert[k][j][i-1] + nvert[k+1][j][i-1] < 0.1 && (i!=1 || (i==1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC))) {
1821 vol[CP] += g31[k][j][i] * 0.5; vol[TP] += g31[k][j][i] * 0.5;
1822 vol[WP] -= g31[k][j][i] * 0.5; vol[TW] -= g31[k][j][i] * 0.5;
1823 }
1824 }
1825 else if ((i == mx-2 || i==1) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i+1] + nvert[k+1][j][i+1] > 0.1) {
1826 if (nvert[k][j][i-1] + nvert[k+1][j][i-1] < 0.1) {
1827 vol[CP] += g31[k][j][i] * 0.5; vol[TP] += g31[k][j][i] * 0.5;
1828 vol[WP] -= g31[k][j][i] * 0.5; vol[TW] -= g31[k][j][i] * 0.5;
1829 }
1830 }
1831 else if ((i == 1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i-1] + nvert[k+1][j][i-1] > 0.1) {
1832 if (nvert[k][j][i+1] + nvert[k+1][j][i+1] < 0.1) {
1833 vol[EP] += g31[k][j][i] * 0.5; vol[TE] += g31[k][j][i] * 0.5;
1834 vol[CP] -= g31[k][j][i] * 0.5; vol[TP] -= g31[k][j][i] * 0.5;
1835 }
1836 }
1837 else if ((i == 1 || i==mx-2) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i-1] + nvert[k+1][j][i-1] > 0.1) {
1838 if (nvert[k][j][i+1] + nvert[k+1][j][i+1] < 0.1) {
1839 vol[EP] += g31[k][j][i] * 0.5; vol[TE] += g31[k][j][i] * 0.5;
1840 vol[CP] -= g31[k][j][i] * 0.5; vol[TP] -= g31[k][j][i] * 0.5;
1841 }
1842 }
1843 else {
1844 vol[EP] += g31[k][j][i] * 0.25; vol[TE] += g31[k][j][i] * 0.25;
1845 vol[WP] -= g31[k][j][i] * 0.25; vol[TW] -= g31[k][j][i] * 0.25;
1846 }
1847
1848 if ((j == my-2 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC)|| nvert[k][j+1][i] + nvert[k+1][j+1][i] > 0.1) {
1849 if (nvert[k][j-1][i] + nvert[k+1][j-1][i] < 0.1 && (j!=1 || (j==1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC))) {
1850 vol[CP] += g32[k][j][i] * 0.5; vol[TP] += g32[k][j][i] * 0.5;
1851 vol[SP] -= g32[k][j][i] * 0.5; vol[TS] -= g32[k][j][i] * 0.5;
1852 }
1853 }
1854 else if ((j == my-2 || j==1) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j+1][i] + nvert[k+1][j+1][i] > 0.1) {
1855 if (nvert[k][j-1][i] + nvert[k+1][j-1][i] < 0.1) {
1856 vol[CP] += g32[k][j][i] * 0.5; vol[TP] += g32[k][j][i] * 0.5;
1857 vol[SP] -= g32[k][j][i] * 0.5; vol[TS] -= g32[k][j][i] * 0.5;
1858 }
1859 }
1860 else if ((j == 1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC)|| nvert[k][j-1][i] + nvert[k+1][j-1][i] > 0.1) {
1861 if (nvert[k][j+1][i] + nvert[k+1][j+1][i] < 0.1) {
1862 vol[NP] += g32[k][j][i] * 0.5; vol[TN] += g32[k][j][i] * 0.5;
1863 vol[CP] -= g32[k][j][i] * 0.5; vol[TP] -= g32[k][j][i] * 0.5;
1864 }
1865 }
1866 else if ((j == 1 || j==my-2) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j-1][i] + nvert[k+1][j-1][i] > 0.1) {
1867 if (nvert[k][j+1][i] + nvert[k+1][j+1][i] < 0.1) {
1868 vol[NP] += g32[k][j][i] * 0.5; vol[TN] += g32[k][j][i] * 0.5;
1869 vol[CP] -= g32[k][j][i] * 0.5; vol[TP] -= g32[k][j][i] * 0.5;
1870 }
1871 }
1872 else {
1873 vol[NP] += g32[k][j][i] * 0.25; vol[TN] += g32[k][j][i] * 0.25;
1874 vol[SP] -= g32[k][j][i] * 0.25; vol[TS] -= g32[k][j][i] * 0.25;
1875 }
1876
1877 vol[CP] -= g33[k][j][i];
1878 vol[TP] += g33[k][j][i];
1879 }
1880
1881 /************************************************************************
1882 * BOTTOM FACE CONTRIBUTION (between k-1 and k)
1883 ************************************************************************/
1884 if (nvert[k-1][j][i] < IBM_FLUID_THRESHOLD && k != z_str) {
1885 if ((i == mx-2 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i+1] + nvert[k-1][j][i+1] > 0.1) {
1886 if (nvert[k][j][i-1] + nvert[k-1][j][i-1] < 0.1 && (i!=1 || (i==1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC))) {
1887 vol[CP] -= g31[k-1][j][i] * 0.5; vol[BP] -= g31[k-1][j][i] * 0.5;
1888 vol[WP] += g31[k-1][j][i] * 0.5; vol[BW] += g31[k-1][j][i] * 0.5;
1889 }
1890 }
1891 else if ((i == mx-2 || i==1) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i+1] + nvert[k-1][j][i+1] > 0.1) {
1892 if (nvert[k][j][i-1] + nvert[k-1][j][i-1] < 0.1) {
1893 vol[CP] -= g31[k-1][j][i] * 0.5; vol[BP] -= g31[k-1][j][i] * 0.5;
1894 vol[WP] += g31[k-1][j][i] * 0.5; vol[BW] += g31[k-1][j][i] * 0.5;
1895 }
1896 }
1897 else if ((i == 1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i-1] + nvert[k-1][j][i-1] > 0.1) {
1898 if (nvert[k][j][i+1] + nvert[k-1][j][i+1] < 0.1) {
1899 vol[EP] -= g31[k-1][j][i] * 0.5; vol[BE] -= g31[k-1][j][i] * 0.5;
1900 vol[CP] += g31[k-1][j][i] * 0.5; vol[BP] += g31[k-1][j][i] * 0.5;
1901 }
1902 }
1903 else if ((i == 1 || i==mx-2) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i-1] + nvert[k-1][j][i-1] > 0.1) {
1904 if (nvert[k][j][i+1] + nvert[k-1][j][i+1] < 0.1) {
1905 vol[EP] -= g31[k-1][j][i] * 0.5; vol[BE] -= g31[k-1][j][i] * 0.5;
1906 vol[CP] += g31[k-1][j][i] * 0.5; vol[BP] += g31[k-1][j][i] * 0.5;
1907 }
1908 }
1909 else {
1910 vol[EP] -= g31[k-1][j][i] * 0.25; vol[BE] -= g31[k-1][j][i] * 0.25;
1911 vol[WP] += g31[k-1][j][i] * 0.25; vol[BW] += g31[k-1][j][i] * 0.25;
1912 }
1913
1914 if ((j == my-2 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC)|| nvert[k][j+1][i] + nvert[k-1][j+1][i] > 0.1) {
1915 if (nvert[k][j-1][i] + nvert[k-1][j-1][i] < 0.1 && (j!=1 || (j==1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC))) {
1916 vol[CP] -= g32[k-1][j][i] * 0.5; vol[BP] -= g32[k-1][j][i] * 0.5;
1917 vol[SP] += g32[k-1][j][i] * 0.5; vol[BS] += g32[k-1][j][i] * 0.5;
1918 }
1919 }
1920 else if ((j == my-2 || j==1) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j+1][i] + nvert[k-1][j+1][i] > 0.1) {
1921 if (nvert[k][j-1][i] + nvert[k-1][j-1][i] < 0.1) {
1922 vol[CP] -= g32[k-1][j][i] * 0.5; vol[BP] -= g32[k-1][j][i] * 0.5;
1923 vol[SP] += g32[k-1][j][i] * 0.5; vol[BS] += g32[k-1][j][i] * 0.5;
1924 }
1925 }
1926 else if ((j == 1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC)|| nvert[k][j-1][i] + nvert[k-1][j-1][i] > 0.1) {
1927 if (nvert[k][j+1][i] + nvert[k-1][j+1][i] < 0.1) {
1928 vol[NP] -= g32[k-1][j][i] * 0.5; vol[BN] -= g32[k-1][j][i] * 0.5;
1929 vol[CP] += g32[k-1][j][i] * 0.5; vol[BP] += g32[k-1][j][i] * 0.5;
1930 }
1931 }
1932 else if ((j == 1 || j==my-2) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j-1][i] + nvert[k-1][j-1][i] > 0.1) {
1933 if (nvert[k][j+1][i] + nvert[k-1][j+1][i] < 0.1) {
1934 vol[NP] -= g32[k-1][j][i] * 0.5; vol[BN] -= g32[k-1][j][i] * 0.5;
1935 vol[CP] += g32[k-1][j][i] * 0.5; vol[BP] += g32[k-1][j][i] * 0.5;
1936 }
1937 }
1938 else {
1939 vol[NP] -= g32[k-1][j][i] * 0.25; vol[BN] -= g32[k-1][j][i] * 0.25;
1940 vol[SP] += g32[k-1][j][i] * 0.25; vol[BS] += g32[k-1][j][i] * 0.25;
1941 }
1942
1943 vol[CP] -= g33[k-1][j][i];
1944 vol[BP] += g33[k-1][j][i];
1945 }
1946
1947 // --- Final scaling and insertion into the matrix ---
1948
1949 // Scale all stencil coefficients by the negative cell volume (-aj).
1950 for (PetscInt m = 0; m < 19; m++) {
1951 vol[m] *= -aj[k][j][i];
1952 }
1953
1954 // Set the global column indices for the 19 stencil points, handling periodic BCs.
1955 idx[CP] = Gidx(i, j, k, user);
1956 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && i==mx-2) idx[EP] = Gidx(1, j, k, user); else idx[EP] = Gidx(i+1, j, k, user);
1957 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && i==1) idx[WP] = Gidx(mx-2, j, k, user); else idx[WP] = Gidx(i-1, j, k, user);
1958 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && j==my-2) idx[NP] = Gidx(i, 1, k, user); else idx[NP] = Gidx(i, j+1, k, user);
1959 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && j==1) idx[SP] = Gidx(i, my-2, k, user); else idx[SP] = Gidx(i, j-1, k, user);
1960 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && k==mz-2) idx[TP] = Gidx(i, j, 1, user); else idx[TP] = Gidx(i, j, k+1, user);
1961 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && k==1) idx[BP] = Gidx(i, j, mz-2, user); else idx[BP] = Gidx(i, j, k-1, user);
1962 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && i==mx-2 && j==my-2) idx[NE] = Gidx(1, 1, k, user); else if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && i==mx-2) idx[NE] = Gidx(1, j+1, k, user); else if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && j==my-2) idx[NE] = Gidx(i+1, 1, k, user); else idx[NE] = Gidx(i+1, j+1, k, user);
1963 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && i==mx-2 && j==1) idx[SE] = Gidx(1, my-2, k, user); else if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && i==mx-2) idx[SE] = Gidx(1, j-1, k, user); else if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && j==1) idx[SE] = Gidx(i+1, my-2, k, user); else idx[SE] = Gidx(i+1, j-1, k, user);
1964 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && i==1 && j==my-2) idx[NW] = Gidx(mx-2, 1, k, user); else if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && i==1) idx[NW] = Gidx(mx-2, j+1, k, user); else if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && j==my-2) idx[NW] = Gidx(i-1, 1, k, user); else idx[NW] = Gidx(i-1, j+1, k, user);
1965 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && i==1 && j==1) idx[SW] = Gidx(mx-2, my-2, k, user); else if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && i==1) idx[SW] = Gidx(mx-2, j-1, k, user); else if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && j==1) idx[SW] = Gidx(i-1, my-2, k, user); else idx[SW] = Gidx(i-1, j-1, k, user);
1966 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && j==my-2 && k==mz-2) idx[TN] = Gidx(i, 1, 1, user); else if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && j==my-2) idx[TN] = Gidx(i, 1, k+1, user); else if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && k==mz-2) idx[TN] = Gidx(i, j+1, 1, user); else idx[TN] = Gidx(i, j+1, k+1, user);
1967 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && j==my-2 && k==1) idx[BN] = Gidx(i, 1, mz-2, user); else if(user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && j==my-2) idx[BN] = Gidx(i, 1, k-1, user); else if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && k==1) idx[BN] = Gidx(i, j+1, mz-2, user); else idx[BN] = Gidx(i, j+1, k-1, user);
1968 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && j==1 && k==mz-2) idx[TS] = Gidx(i, my-2, 1, user); else if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && j==1) idx[TS] = Gidx(i, my-2, k+1, user); else if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && k==mz-2) idx[TS] = Gidx(i, j-1, 1, user); else idx[TS] = Gidx(i, j-1, k+1, user);
1969 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && j==1 && k==1) idx[BS] = Gidx(i, my-2, mz-2, user); else if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && j==1) idx[BS] = Gidx(i, my-2, k-1, user); else if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && k==1) idx[BS] = Gidx(i, j-1, mz-2, user); else idx[BS] = Gidx(i, j-1, k-1, user);
1970 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && i==mx-2 && k==mz-2) idx[TE] = Gidx(1, j, 1, user); else if(user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && i==mx-2) idx[TE] = Gidx(1, j, k+1, user); else if(user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && k==mz-2) idx[TE] = Gidx(i+1, j, 1, user); else idx[TE] = Gidx(i+1, j, k+1, user);
1971 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && i==mx-2 && k==1) idx[BE] = Gidx(1, j, mz-2, user); else if(user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && i==mx-2) idx[BE] = Gidx(1, j, k-1, user); else if(user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && k==1) idx[BE] = Gidx(i+1, j, mz-2, user); else idx[BE] = Gidx(i+1, j, k-1, user);
1972 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && i==1 && k==mz-2) idx[TW] = Gidx(mx-2, j, 1, user); else if(user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && i==1) idx[TW] = Gidx(mx-2, j, k+1, user); else if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && k==mz-2) idx[TW] = Gidx(i-1, j, 1, user); else idx[TW] = Gidx(i-1, j, k+1, user);
1973 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && i==1 && k==1) idx[BW] = Gidx(mx-2, j, mz-2, user); else if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && i==1) idx[BW] = Gidx(mx-2, j, k-1, user); else if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && k==1) idx[BW] = Gidx(i-1, j, mz-2, user); else idx[BW] = Gidx(i-1, j, k-1, user);
1974
1975 // Insert the computed row into the matrix A.
1976 MatSetValues(user->A, 1, &row, 19, idx, vol, INSERT_VALUES);
1977 }
1978 }
1979 }
1980 }
1981
1982 //================================================================================
1983 // Section 4: Finalize Matrix and Cleanup
1984 //================================================================================
1985
1986 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Finalizing matrix assembly.\n");
1987 MatAssemblyBegin(user->A, MAT_FINAL_ASSEMBLY);
1988 MatAssemblyEnd(user->A, MAT_FINAL_ASSEMBLY);
1989
1990 PetscReal max_A;
1991
1992 ierr = MatNorm(user->A,NORM_INFINITY,&max_A);CHKERRQ(ierr);
1993
1994 LOG_ALLOW(GLOBAL,LOG_DEBUG," Max value in A matrix for level %d = %le.\n",user->thislevel,max_A);
1995
1996 // if (get_log_level() >= LOG_DEBUG) {
1997 // ierr = MatView(user->A,PETSC_VIEWER_STDOUT_WORLD); CHKERRQ(ierr);
1998 // }
1999
2000 // --- Restore access to all PETSc vectors and destroy temporary ones ---
2001 DMDAVecRestoreArray(da, G11, &g11); DMDAVecRestoreArray(da, G12, &g12); DMDAVecRestoreArray(da, G13, &g13);
2002 DMDAVecRestoreArray(da, G21, &g21); DMDAVecRestoreArray(da, G22, &g22); DMDAVecRestoreArray(da, G23, &g23);
2003 DMDAVecRestoreArray(da, G31, &g31); DMDAVecRestoreArray(da, G32, &g32); DMDAVecRestoreArray(da, G33, &g33);
2004
2005 VecDestroy(&G11); VecDestroy(&G12); VecDestroy(&G13);
2006 VecDestroy(&G21); VecDestroy(&G22); VecDestroy(&G23);
2007 VecDestroy(&G31); VecDestroy(&G32); VecDestroy(&G33);
2008
2009 DMDAVecRestoreArray(fda, user->lCsi, &csi); DMDAVecRestoreArray(fda, user->lEta, &eta); DMDAVecRestoreArray(fda, user->lZet, &zet);
2010 DMDAVecRestoreArray(fda, user->lICsi, &icsi); DMDAVecRestoreArray(fda, user->lIEta, &ieta); DMDAVecRestoreArray(fda, user->lIZet, &izet);
2011 DMDAVecRestoreArray(fda, user->lJCsi, &jcsi); DMDAVecRestoreArray(fda, user->lJEta, &jeta); DMDAVecRestoreArray(fda, user->lJZet, &jzet);
2012 DMDAVecRestoreArray(fda, user->lKCsi, &kcsi); DMDAVecRestoreArray(fda, user->lKEta, &keta); DMDAVecRestoreArray(fda, user->lKZet, &kzet);
2013 DMDAVecRestoreArray(da, user->lAj, &aj); DMDAVecRestoreArray(da, user->lIAj, &iaj); DMDAVecRestoreArray(da, user->lJAj, &jaj); DMDAVecRestoreArray(da, user->lKAj, &kaj);
2014 DMDAVecRestoreArray(da, user->lNvert, &nvert);
2015
2016 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Exiting PoissonLHSNew.\n");
2018 PetscFunctionReturn(0);
2019}
#define TW
Definition poisson.c:315
#define SE
Definition poisson.c:306
#define BN
Definition poisson.c:310
#define WP
Definition poisson.c:298
static PetscInt Gidx(PetscInt i, PetscInt j, PetscInt k, UserCtx *user)
Convert local logical indices to the corresponding flattened global cell identifier.
Definition poisson.c:43
#define SW
Definition poisson.c:308
#define BS
Definition poisson.c:312
#define NE
Definition poisson.c:305
#define CP
Definition poisson.c:295
#define BE
Definition poisson.c:314
#define BP
Definition poisson.c:302
#define BW
Definition poisson.c:316
#define TE
Definition poisson.c:313
#define TS
Definition poisson.c:311
#define NP
Definition poisson.c:299
#define EP
Definition poisson.c:297
#define TN
Definition poisson.c:309
#define SP
Definition poisson.c:300
#define TP
Definition poisson.c:301
#define NW
Definition poisson.c:307
@ PERIODIC
Definition variables.h:292
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:931
Vec lIEta
Definition variables.h:977
Vec lIZet
Definition variables.h:977
Vec Phi
Definition variables.h:939
PetscInt KM
Definition variables.h:920
Vec lZet
Definition variables.h:974
Vec lIAj
Definition variables.h:977
Vec lKEta
Definition variables.h:979
Vec lJCsi
Definition variables.h:978
PetscScalar x
Definition variables.h:103
Vec lKZet
Definition variables.h:979
Vec lJEta
Definition variables.h:978
Vec lCsi
Definition variables.h:974
PetscInt thislevel
Definition variables.h:988
PetscScalar z
Definition variables.h:103
Vec lKCsi
Definition variables.h:979
PetscInt JM
Definition variables.h:920
Vec lJZet
Definition variables.h:978
Vec lAj
Definition variables.h:974
Vec lICsi
Definition variables.h:977
PetscScalar y
Definition variables.h:103
PetscInt IM
Definition variables.h:920
Vec lEta
Definition variables.h:974
BCType mathematical_type
Definition variables.h:368
Vec lJAj
Definition variables.h:978
Vec lKAj
Definition variables.h:979
@ BC_FACE_NEG_X
Definition variables.h:262
@ BC_FACE_NEG_Z
Definition variables.h:264
@ BC_FACE_NEG_Y
Definition variables.h:263
A 3D point or vector with PetscScalar components.
Definition variables.h:102
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PoissonRHS()

PetscErrorCode PoissonRHS ( UserCtx user,
Vec  B 
)
extern

Computes the Right-Hand-Side (RHS) of the Poisson equation, which is the divergence of the intermediate velocity field.

Parameters
userThe UserCtx for the grid level.
BThe PETSc Vec where the RHS result will be stored.
Returns
PetscErrorCode 0 on success.

Computes the Right-Hand-Side (RHS) of the Poisson equation, which is the divergence of the intermediate velocity field.

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

See also
PoissonRHS()

Definition at line 2032 of file poisson.c.

2033{
2034 PetscErrorCode ierr;
2035 DMDALocalInfo info = user->info;
2036 PetscInt xs = info.xs, xe = info.xs + info.xm;
2037 PetscInt ys = info.ys, ye = info.ys + info.ym;
2038 PetscInt zs = info.zs, ze = info.zs + info.zm;
2039 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2040
2041 PetscInt i, j, k;
2042 PetscReal ***nvert, ***aj, ***rb, dt = user->simCtx->dt;
2043 struct Components{
2044 PetscReal x;
2045 PetscReal y;
2046 PetscReal z;
2047 } *** ucont;
2048
2050
2051 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Entering PoissonRHS to compute pressure equation RHS.\n");
2052
2053 DMDAVecGetArray(user->da, B, &rb);
2054 DMDAVecGetArray(user->fda, user->lUcont, &ucont);
2055 DMDAVecGetArray(user->da, user->lNvert, &nvert);
2056 DMDAVecGetArray(user->da, user->lAj, &aj);
2057
2058
2059 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Computing RHS values for each cell.\n");
2060
2061 for (k=zs; k<ze; k++) {
2062 for (j=ys; j<ye; j++) {
2063 for (i=xs; i<xe; i++) {
2064
2065 if (i==0 || i==mx-1 || j==0 || j==my-1 || k==0 || k==mz-1) {
2066 rb[k][j][i] = 0.;
2067 }
2068 else if (nvert[k][j][i] > 0.1) {
2069 rb[k][j][i] = 0;
2070 }
2071 else {
2072 rb[k][j][i] = -(ucont[k][j][i].x - ucont[k][j][i-1].x +
2073 ucont[k][j][i].y - ucont[k][j-1][i].y +
2074 ucont[k][j][i].z - ucont[k-1][j][i].z) / dt
2075 * aj[k][j][i] / 1.0 * COEF_TIME_ACCURACY; // user->simCtx->st replaced by 1.0.
2076
2077 }
2078 }
2079 }
2080 }
2081
2082
2083 // --- Check the solvability condition for the Poisson equation ---
2084 // The global sum of the RHS (proportional to the total divergence) must be zero.
2085 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Verifying solvability condition (sum of RHS terms).\n");
2086 PetscReal lsum=0., sum=0.;
2087
2088 for (k=zs; k<ze; k++) {
2089 for (j=ys; j<ye; j++) {
2090 for (i=xs; i<xe; i++) {
2091
2092 lsum += rb[k][j][i] / aj[k][j][i]* dt/COEF_TIME_ACCURACY;
2093
2094 }
2095 }
2096 }
2097
2098 ierr = MPI_Allreduce(&lsum,&sum,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2099
2100 LOG_ALLOW(GLOBAL, LOG_INFO, "Global Sum of RHS (Divergence Check): %le\n", sum);
2101
2102 user->simCtx->summationRHS = sum;
2103
2104 DMDAVecRestoreArray(user->fda, user->lUcont, &ucont);
2105 DMDAVecRestoreArray(user->da, user->lNvert, &nvert);
2106 DMDAVecRestoreArray(user->da, user->lAj, &aj);
2107 DMDAVecRestoreArray(user->da, B, &rb);
2108
2109
2111 return 0;
2112}
PetscReal dt
Definition variables.h:710
Vec lUcont
Definition variables.h:939
PetscReal summationRHS
Definition variables.h:858
#define COEF_TIME_ACCURACY
Coefficient controlling the temporal accuracy scheme (e.g., 1.5 for 2nd Order Backward Difference).
Definition variables.h:57
Here is the caller graph for this function:

◆ UpdatePressure()

PetscErrorCode UpdatePressure ( UserCtx user)
extern

Updates the pressure field P with the pressure correction Phi computed by the Poisson solver.

(P = P + Phi)

Parameters
userThe UserCtx containing the P and Phi vectors.
Returns
PetscErrorCode 0 on success.

Updates the pressure field P with the pressure correction Phi computed by the Poisson solver.

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

See also
UpdatePressure()

Definition at line 853 of file poisson.c.

854{
855 PetscErrorCode ierr;
856
857 PetscFunctionBeginUser;
859 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Entering UpdatePressure.\n");
860
861 //================================================================================
862 // Section 1: Initialization and Data Acquisition
863 //================================================================================
864 DM da = user->da;
865 DMDALocalInfo info = user->info;
866
867 // Local grid extents for the main update loop
868 PetscInt xs = info.xs, xe = info.xs + info.xm;
869 PetscInt ys = info.ys, ye = info.ys + info.ym;
870 PetscInt zs = info.zs, ze = info.zs + info.zm;
871
872 // --- Get direct pointer access to PETSc vector data for performance ---
873 PetscReal ***p, ***phi;
874 DMDAVecGetArray(da, user->P, &p);
875 DMDAVecGetArray(da, user->Phi, &phi);
876
877 //================================================================================
878 // Section 2: Core Pressure Update
879 //================================================================================
880 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Performing core pressure update (P_new = P_old + Phi).\n");
881 for (PetscInt k = zs; k < ze; k++) {
882 for (PetscInt j = ys; j < ye; j++) {
883 for (PetscInt i = xs; i < xe; i++) {
884 // This is the fundamental pressure update in a projection method.
885 p[k][j][i] += phi[k][j][i];
886 }
887 }
888 }
889
890 // Restore arrays now that the core computation is done.
891 DMDAVecRestoreArray(da, user->Phi, &phi);
892 DMDAVecRestoreArray(da, user->P, &p);
893
894
895 //================================================================================
896 // Section 3: Handle Periodic Boundary Condition Synchronization
897 //================================================================================
898 const FieldId periodic_fields[] = {FIELD_ID_P, FIELD_ID_PHI};
899 ierr = SynchronizePeriodicCellFields(user, 2, periodic_fields); CHKERRQ(ierr);
900
901 //================================================================================
902 // Section 4: Final Cleanup (pointers already restored)
903 //================================================================================
904
905 ierr = UpdateLocalGhosts(user, FIELD_ID_P); CHKERRQ(ierr);
906 ierr = UpdateLocalGhosts(user, FIELD_ID_PHI); CHKERRQ(ierr);
907
908 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Exiting UpdatePressure.\n");
910 PetscFunctionReturn(0);
911}
PetscErrorCode SynchronizePeriodicCellFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes periodic endpoint cells for a list of cell-centered fields.
FieldId
Compile-time identity for a catalogued Eulerian field.
@ FIELD_ID_PHI
@ FIELD_ID_P
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
Definition setup.c:1838
Here is the call graph for this function:
Here is the caller graph for this function:

◆ CorrectChannelFluxProfile()

PetscErrorCode CorrectChannelFluxProfile ( UserCtx user)

Enforces a constant volumetric flux profile along the entire length of a driven periodic channel.

This function is a "hard" corrector, called at the end of the projection step. The projection ensures the velocity field is divergence-free (3D continuity), but this function enforces a stricter 1D continuity condition (Flux(plane) = constant) required for physically realistic, fully-developed periodic channel/pipe flow.

The process is as follows:

  1. Introspects the boundary condition handlers to detect if a DRIVEN_ flow is active and in which direction ('X', 'Y', or 'Z'). If none is found, it exits.
  2. Measures the current volumetric flux through every single cross-sectional plane in the driven direction.
  3. For each plane, it calculates the velocity correction required to make its flux match the global targetVolumetricFlux (which was set by the controller).
  4. It applies this spatially-uniform (but plane-dependent) velocity correction directly to the ucont field, ensuring Flux(plane) = TargetFlux for all planes.
Parameters
userThe UserCtx containing the simulation state for a single block.
Returns
PetscErrorCode 0 on success.

Enforces a constant volumetric flux profile along the entire length of a driven periodic channel.

Local to this translation unit.

Definition at line 105 of file poisson.c.

106{
107 PetscErrorCode ierr;
108 SimCtx *simCtx = user->simCtx;
109
110 PetscFunctionBeginUser;
111
112 // --- Step 1: Discover if and where a driven flow is active ---
113 char drivenDirection = ' ';
114 for (int i = 0; i < 6; i++) {
115 BCHandlerType handler_type = user->boundary_faces[i].handler_type;
116 if (handler_type == BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX ||
118 {
119 switch (user->boundary_faces[i].face_id) {
120 case BC_FACE_NEG_X: case BC_FACE_POS_X: drivenDirection = 'X'; break;
121 case BC_FACE_NEG_Y: case BC_FACE_POS_Y: drivenDirection = 'Y'; break;
122 case BC_FACE_NEG_Z: case BC_FACE_POS_Z: drivenDirection = 'Z'; break;
123 }
124 break;
125 }
126 }
127
128 // --- Step 2: Early exit if no driven flow is configured ---
129 if (drivenDirection == ' ') {
130 PetscFunctionReturn(0);
131 }
132
133 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d, Block %d: Starting channel flux profile correction in '%c' direction...\n",
134 simCtx->rank, user->_this, drivenDirection);
135
136 // --- Step 3: Setup and Get PETSc Array Pointers ---
137 DMDALocalInfo info = user->info;
138 PetscInt i, j, k;
139 PetscInt mx = info.mx, my = info.my, mz = info.mz;
140 PetscInt lxs = (info.xs == 0) ? 1 : info.xs;
141 PetscInt lys = (info.ys == 0) ? 1 : info.ys;
142 PetscInt lzs = (info.zs == 0) ? 1 : info.zs;
143 PetscInt lxe = (info.xs + info.xm == mx) ? mx - 1 : info.xs + info.xm;
144 PetscInt lye = (info.ys + info.ym == my) ? my - 1 : info.ys + info.ym;
145 PetscInt lze = (info.zs + info.zm == mz) ? mz - 1 : info.zs + info.zm;
146
147 Cmpnts ***ucont, ***csi, ***eta, ***zet;
148 PetscReal ***nvert;
149 ierr = DMDAVecGetArray(user->fda, user->lUcont, &ucont); CHKERRQ(ierr);
150 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
151 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
152 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
153 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
154
155 // --- Step 4: Allocate Memory for Profile Arrays based on direction ---
156 PetscInt n_planes = 0;
157 switch (drivenDirection) {
158 case 'X': n_planes = mx - 1; break;
159 case 'Y': n_planes = my - 1; break;
160 case 'Z': n_planes = mz - 1; break;
161 }
162
163 PetscReal *localFluxProfile, *globalFluxProfile, *correctionProfile;
164 ierr = PetscMalloc1(n_planes, &localFluxProfile); CHKERRQ(ierr);
165 ierr = PetscMalloc1(n_planes, &globalFluxProfile); CHKERRQ(ierr);
166 ierr = PetscMalloc1(n_planes, &correctionProfile); CHKERRQ(ierr);
167 ierr = PetscMemzero(localFluxProfile, n_planes * sizeof(PetscReal)); CHKERRQ(ierr);
168
169 // --- Step 5: Calculate Total Cross-Sectional Area and Measure Flux Profile ---
170 PetscReal localArea = 0.0, globalArea = 0.0;
171
172 switch (drivenDirection) {
173 case 'X':
174 if (info.xs == 0) { // Area is calculated by rank(s) on the negative face
175 i = 0;
176 for (k = lzs; k < lze; k++) for (j = lys; j < lye; j++) {
177 if (nvert[k][j][i + 1] < 0.1)
178 localArea += sqrt(csi[k][j][i].x*csi[k][j][i].x + csi[k][j][i].y*csi[k][j][i].y + csi[k][j][i].z*csi[k][j][i].z);
179 }
180 }
181 for (i = info.xs; i < lxe; i++) {
182 for (k = lzs; k < lze; k++) for (j = lys; j < lye; j++) {
183 if (nvert[k][j][i + 1] < 0.1) localFluxProfile[i] += ucont[k][j][i].x;
184 }
185 }
186 break;
187 case 'Y':
188 if (info.ys == 0) {
189 j = 0;
190 for (k = lzs; k < lze; k++) for (i = lxs; i < lxe; i++) {
191 if (nvert[k][j + 1][i] < 0.1)
192 localArea += sqrt(eta[k][j][i].x*eta[k][j][i].x + eta[k][j][i].y*eta[k][j][i].y + eta[k][j][i].z*eta[k][j][i].z);
193 }
194 }
195 for (j = info.ys; j < lye; j++) {
196 for (k = lzs; k < lze; k++) for (i = lxs; i < lxe; i++) {
197 if (nvert[k][j + 1][i] < 0.1) localFluxProfile[j] += ucont[k][j][i].y;
198 }
199 }
200 break;
201 case 'Z':
202 if (info.zs == 0) {
203 k = 0;
204 for (j = lys; j < lye; j++) for (i = lxs; i < lxe; i++) {
205 if (nvert[k + 1][j][i] < 0.1)
206 localArea += sqrt(zet[k][j][i].x*zet[k][j][i].x + zet[k][j][i].y*zet[k][j][i].y + zet[k][j][i].z*zet[k][j][i].z);
207 }
208 }
209 for (k = info.zs; k < lze; k++) {
210 for (j = lys; j < lye; j++) for (i = lxs; i < lxe; i++) {
211 if (nvert[k + 1][j][i] < 0.1) localFluxProfile[k] += ucont[k][j][i].z;
212 }
213 }
214 break;
215 }
216
217 ierr = MPI_Allreduce(&localArea, &globalArea, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
218 ierr = MPI_Allreduce(localFluxProfile, globalFluxProfile, n_planes, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
219
220 // --- Step 6: Calculate Correction Profile ---
221 PetscReal targetFlux = simCtx->targetVolumetricFlux;
222 if (globalArea > 1.0e-12) {
223 for (i = 0; i < n_planes; i++) {
224 correctionProfile[i] = (targetFlux - globalFluxProfile[i]) / globalArea;
225 }
226 } else {
227 ierr = PetscMemzero(correctionProfile, n_planes * sizeof(PetscReal)); CHKERRQ(ierr);
228 }
229
230 LOG_ALLOW(GLOBAL, LOG_INFO, "Channel Flux Profile Corrector Update (Dir %c):\n", drivenDirection);
231 LOG_ALLOW(GLOBAL, LOG_INFO, " - Target Flux for all planes: %.6e\n", targetFlux);
232 LOG_ALLOW(GLOBAL, LOG_INFO, " - Measured Flux at plane 0: %.6e (Correction Velocity: %.6e)\n", globalFluxProfile[0], correctionProfile[0]);
233 LOG_ALLOW(GLOBAL, LOG_INFO, " - Measured Flux at plane %d: %.6e (Correction Velocity: %.6e)\n", (n_planes-1)/2, globalFluxProfile[(n_planes-1)/2], correctionProfile[(n_planes-1)/2]);
234
235 /* TURNED OFF IN LEGACY
236 // --- Step 7: Apply Correction to Velocity Profile ---
237 switch (drivenDirection) {
238 case 'X':
239 for (i = info.xs; i < info.xs + info.xm - 1; i++) {
240 if (PetscAbs(correctionProfile[i]) > 1e-12) {
241 for (k = lzs; k < lze; k++) for (j = lys; j < lye; j++) {
242 if (nvert[k][j][i] < 0.1) {
243 PetscReal faceArea = sqrt(csi[k][j][i].x*csi[k][j][i].x + csi[k][j][i].y*csi[k][j][i].y + csi[k][j][i].z*csi[k][j][i].z);
244 ucont[k][j][i].x += correctionProfile[i] * faceArea;
245 }
246 }
247 }
248 }
249 break;
250 case 'Y':
251 for (j = info.ys; j < info.ys + info.ym - 1; j++) {
252 if (PetscAbs(correctionProfile[j]) > 1e-12) {
253 for (k = lzs; k < lze; k++) for (i = lxs; i < lxe; i++) {
254 if (nvert[k][j][i] < 0.1) {
255 PetscReal faceArea = sqrt(eta[k][j][i].x*eta[k][j][i].x + eta[k][j][i].y*eta[k][j][i].y + eta[k][j][i].z*eta[k][j][i].z);
256 ucont[k][j][i].y += correctionProfile[j] * faceArea;
257 }
258 }
259 }
260 }
261 break;
262 case 'Z':
263 for (k = info.zs; k < info.zs + info.zm - 1; k++) {
264 if (PetscAbs(correctionProfile[k]) > 1e-12) {
265 for (j = lys; j < lye; j++) for (i = lxs; i < lxe; i++) {
266 if (nvert[k][j][i] < 0.1) {
267 PetscReal faceArea = sqrt(zet[k][j][i].x*zet[k][j][i].x + zet[k][j][i].y*zet[k][j][i].y + zet[k][j][i].z*zet[k][j][i].z);
268 ucont[k][j][i].z += correctionProfile[k] * faceArea;
269 }
270 }
271 }
272 }
273 break;
274 }
275 */
276
277 // --- Step 8: Cleanup and Restore ---
278 ierr = PetscFree(localFluxProfile); CHKERRQ(ierr);
279 ierr = PetscFree(globalFluxProfile); CHKERRQ(ierr);
280 ierr = PetscFree(correctionProfile); CHKERRQ(ierr);
281
282 ierr = DMDAVecRestoreArray(user->fda, user->lUcont, &ucont); CHKERRQ(ierr);
283 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
284 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
285 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
286 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
287
288 //LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d, Block %d: Channel flux profile correction complete.\n",
289 // simCtx->rank, user->_this);
290
291 PetscFunctionReturn(0);
292}
PetscReal targetVolumetricFlux
Definition variables.h:807
BCHandlerType
Defines the specific computational "strategy" for a boundary handler.
Definition variables.h:303
@ BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX
Definition variables.h:319
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:318
BCHandlerType handler_type
Definition variables.h:369
PetscInt _this
Definition variables.h:924
@ BC_FACE_POS_Z
Definition variables.h:264
@ BC_FACE_POS_Y
Definition variables.h:263
@ BC_FACE_POS_X
Definition variables.h:262
Here is the caller graph for this function:

◆ Projection()

PetscErrorCode Projection ( UserCtx user)
extern

Corrects the contravariant velocity field Ucont to be divergence-free using the gradient of the pressure correction field Phi.

Parameters
userThe UserCtx containing the Ucont and Phi vectors.
Returns
PetscErrorCode 0 on success.
Note
Testing status: Direct unit coverage exists for basic projection invariants; periodic and immersed-boundary correction branches remain part of the next-gap backlog.

Corrects the contravariant velocity field Ucont to be divergence-free using the gradient of the pressure correction field Phi.

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

See also
Projection()

Definition at line 326 of file poisson.c.

327{
328 PetscErrorCode ierr;
329
330 PetscFunctionBeginUser;
332 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Entering Projection step to correct velocity field.\n");
333
334 //================================================================================
335 // Section 1: Initialization and Data Acquisition
336 //================================================================================
337
338 // --- Get simulation and grid context ---
339 SimCtx *simCtx = user->simCtx;
340 DM da = user->da, fda = user->fda;
341 DMDALocalInfo info = user->info;
342
343 // --- Grid dimensions ---
344 PetscInt mx = info.mx, my = info.my, mz = info.mz;
345 PetscInt xs = info.xs, xe = info.xs + info.xm;
346 PetscInt ys = info.ys, ye = info.ys + info.ym;
347 PetscInt zs = info.zs, ze = info.zs + info.zm;
348
349 // --- Loop bounds (excluding outer ghost layers) ---
350 PetscInt lxs = (xs == 0) ? xs + 1 : xs;
351 PetscInt lxe = (xe == mx) ? xe - 1 : xe;
352 PetscInt lys = (ys == 0) ? ys + 1 : ys;
353 PetscInt lye = (ye == my) ? ye - 1 : ye;
354 PetscInt lzs = (zs == 0) ? zs + 1 : zs;
355 PetscInt lze = (ze == mz) ? ze - 1 : ze;
356
357 // --- Get direct pointer access to grid metric and field data ---
358 Cmpnts ***icsi, ***ieta, ***izet, ***jcsi, ***jeta, ***jzet, ***kcsi, ***keta, ***kzet;
359 PetscReal ***iaj, ***jaj, ***kaj, ***p, ***nvert;
360 Cmpnts ***ucont;
361 DMDAVecGetArray(fda, user->lICsi, &icsi); DMDAVecGetArray(fda, user->lIEta, &ieta); DMDAVecGetArray(fda, user->lIZet, &izet);
362 DMDAVecGetArray(fda, user->lJCsi, &jcsi); DMDAVecGetArray(fda, user->lJEta, &jeta); DMDAVecGetArray(fda, user->lJZet, &jzet);
363 DMDAVecGetArray(fda, user->lKCsi, &kcsi); DMDAVecGetArray(fda, user->lKEta, &keta); DMDAVecGetArray(fda, user->lKZet, &kzet);
364 DMDAVecGetArray(da, user->lIAj, &iaj); DMDAVecGetArray(da, user->lJAj, &jaj); DMDAVecGetArray(da, user->lKAj, &kaj);
365 DMDAVecGetArray(da, user->lNvert, &nvert);
366 DMDAVecGetArray(da, user->lPhi, &p); // Note: using lPhi, which is the pressure correction
367 //DMDAVecGetArray(da,user->lP,&p);
368 DMDAVecGetArray(fda, user->Ucont, &ucont);
369
370 // --- Constants for clarity ---
371 const PetscReal IBM_FLUID_THRESHOLD = 0.1;
372 const PetscReal scale = simCtx->dt * 1.0 / COEF_TIME_ACCURACY; // simCtx->st replaced by 1.0.
373
374 LOG_ALLOW(GLOBAL,LOG_DEBUG," Starting velocity correction: Scale = %le .\n",scale);
375
376 //================================================================================
377 // Section 2: Correct Velocity Components
378 //================================================================================
379 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Calculating pressure gradients and correcting velocity components.\n");
380
381 // --- Main loop over interior domain points ---
382 for (PetscInt k = lzs; k < lze; k++) {
383 for (PetscInt j = lys; j < lye; j++) {
384 for (PetscInt i = lxs; i < lxe; i++) {
385
386 // --- Correct U_contravariant (x-component of velocity) ---
387 PetscInt i_end = (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC) ? mx - 1 : mx - 2;
388 if (i < i_end) {
389
390 if (!(nvert[k][j][i] > IBM_FLUID_THRESHOLD || nvert[k][j][i + 1] > IBM_FLUID_THRESHOLD)) {
391 // Compute pressure derivatives (dp/d_csi, dp/d_eta, dp/d_zet) at the i-face
392
393 PetscReal dpdc = p[k][j][i + 1] - p[k][j][i];
394 PetscReal dpde = 0.0, dpdz = 0.0;
395
396 // Boundary-aware stencil for dp/d_eta
397 if ((j==my-2 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC)|| nvert[k][j+1][i]+nvert[k][j+1][i+1] > 0.1) {
398 if (nvert[k][j-1][i] + nvert[k][j-1][i+1] < 0.1 && (j!=1 || (j==1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC))) {
399 dpde = (p[k][j][i] + p[k][j][i+1] -
400 p[k][j-1][i] - p[k][j-1][i+1]) * 0.5;
401 }
402 }
403
404 else if ((j==my-2 || j==1) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j+1][i]+nvert[k][j+1][i+1] > 0.1) {
405 if (nvert[k][j-1][i] + nvert[k][j-1][i+1] < 0.1) { dpde = (p[k][j][i] + p[k][j][i+1] - p[k][j-1][i] - p[k][j-1][i+1]) * 0.5; }
406 }
407
408 else if ((j == 1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC) || nvert[k][j-1][i] + nvert[k][j-1][i+1] > 0.1) {
409 if (nvert[k][j+1][i] + nvert[k][j+1][i+1] < 0.1) { dpde = (p[k][j+1][i] + p[k][j+1][i+1] - p[k][j][i] - p[k][j][i+1]) * 0.5; }
410 }
411
412 else if ((j == 1 || j==my-2) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j-1][i] + nvert[k][j-1][i+1] > 0.1) {
413 if (nvert[k][j+1][i] + nvert[k][j+1][i+1] < 0.1) { dpde = (p[k][j+1][i] + p[k][j+1][i+1] - p[k][j][i] - p[k][j][i+1]) * 0.5; }
414 }
415
416 else { dpde = (p[k][j+1][i] + p[k][j+1][i+1] - p[k][j-1][i] - p[k][j-1][i+1]) * 0.25; }
417
418 // Boundary-aware stencil for dp/d_zet
419 if ((k == mz-2 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC) || nvert[k+1][j][i] + nvert[k+1][j][i+1] > 0.1) {
420 if (nvert[k-1][j][i] + nvert[k-1][j][i+1] < 0.1 && (k!=1 || (k==1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC))) { dpdz = (p[k][j][i] + p[k][j][i+1] - p[k-1][j][i] - p[k-1][j][i+1]) * 0.5; }
421 }
422
423 else if ((k == mz-2 || k==1) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k+1][j][i] + nvert[k+1][j][i+1] > 0.1) {
424 if (nvert[k-1][j][i] + nvert[k-1][j][i+1] < 0.1) { dpdz = (p[k][j][i] + p[k][j][i+1] - p[k-1][j][i] - p[k-1][j][i+1]) * 0.5; }
425 }
426
427 else if ((k == 1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC)|| nvert[k-1][j][i] + nvert[k-1][j][i+1] > 0.1) {
428 if (nvert[k+1][j][i] + nvert[k+1][j][i+1] < 0.1) { dpdz = (p[k+1][j][i] + p[k+1][j][i+1] - p[k][j][i] - p[k][j][i+1]) * 0.5; }
429 }
430
431 else if ((k == 1 || k==mz-2) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k-1][j][i] + nvert[k-1][j][i+1] > 0.1) {
432 if (nvert[k+1][j][i] + nvert[k+1][j][i+1] < 0.1) { dpdz = (p[k+1][j][i] + p[k+1][j][i+1] - p[k][j][i] - p[k][j][i+1]) * 0.5; }
433 }
434
435 else { dpdz = (p[k+1][j][i] + p[k+1][j][i+1] - p[k-1][j][i] - p[k-1][j][i+1]) * 0.25; }
436
437 // Apply the correction: U_new = U_old - dt * (g11*dpdc + g12*dpde + g13*dpdz)
438
439
440
441 PetscReal grad_p_x = (dpdc * (icsi[k][j][i].x * icsi[k][j][i].x + icsi[k][j][i].y * icsi[k][j][i].y
442 + icsi[k][j][i].z * icsi[k][j][i].z) * iaj[k][j][i] +
443 dpde * (ieta[k][j][i].x * icsi[k][j][i].x + ieta[k][j][i].y * icsi[k][j][i].y
444 + ieta[k][j][i].z * icsi[k][j][i].z) * iaj[k][j][i] +
445 dpdz * (izet[k][j][i].x * icsi[k][j][i].x + izet[k][j][i].y * icsi[k][j][i].y
446 + izet[k][j][i].z * icsi[k][j][i].z) * iaj[k][j][i]);
447
448 PetscReal correction = grad_p_x*scale;
449 //LOG_LOOP_ALLOW_EXACT(GLOBAL,LOG_DEBUG,k,5," Flux correction in Csi Direction: %le.\n",correction);
450 ucont[k][j][i].x -= correction;
451
452 }
453 }
454
455 // --- Correct V_contravariant (y-component of velocity) ---
456 PetscInt j_end = (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC) ? my - 1 : my - 2;
457 if (j < j_end) {
458 if (!(nvert[k][j][i] > IBM_FLUID_THRESHOLD || nvert[k][j + 1][i] > IBM_FLUID_THRESHOLD)) {
459 PetscReal dpdc = 0.0, dpde = 0.0, dpdz = 0.0;
460 dpde = p[k][j + 1][i] - p[k][j][i];
461
462 // Boundary-aware stencil for dp/d_csi
463 if ((i == mx-2 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC) || nvert[k][j][i+1] + nvert[k][j+1][i+1] > 0.1) {
464 if (nvert[k][j][i-1] + nvert[k][j+1][i-1] < 0.1 && (i!=1 || (i==1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC))) { dpdc = (p[k][j][i] + p[k][j+1][i] - p[k][j][i-1] - p[k][j+1][i-1]) * 0.5; }
465 } else if ((i == mx-2 || i==1) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i+1] + nvert[k][j+1][i+1] > 0.1) {
466 if (nvert[k][j][i-1] + nvert[k][j+1][i-1] < 0.1) { dpdc = (p[k][j][i] + p[k][j+1][i] - p[k][j][i-1] - p[k][j+1][i-1]) * 0.5; }
467 } else if ((i == 1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i-1] + nvert[k][j+1][i-1] > 0.1) {
468 if (nvert[k][j][i+1] + nvert[k][j+1][i+1] < 0.1) { dpdc = (p[k][j][i+1] + p[k][j+1][i+1] - p[k][j][i] - p[k][j+1][i]) * 0.5; }
469 } else if ((i == 1 || i==mx-2) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i-1] + nvert[k][j+1][i-1] > 0.1) {
470 if (nvert[k][j][i+1] + nvert[k][j+1][i+1] < 0.1) { dpdc = (p[k][j][i+1] + p[k][j+1][i+1] - p[k][j][i] - p[k][j+1][i]) * 0.5; }
471 } else { dpdc = (p[k][j][i+1] + p[k][j+1][i+1] - p[k][j][i-1] - p[k][j+1][i-1]) * 0.25; }
472
473 // Boundary-aware stencil for dp/d_zet
474 if ((k == mz-2 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC)|| nvert[k+1][j][i] + nvert[k+1][j+1][i] > 0.1) {
475 if (nvert[k-1][j][i] + nvert[k-1][j+1][i] < 0.1 && (k!=1 || (k==1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC))) { dpdz = (p[k][j][i] + p[k][j+1][i] - p[k-1][j][i] - p[k-1][j+1][i]) * 0.5; }
476 } else if ((k == mz-2 || k==1 ) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k+1][j][i] + nvert[k+1][j+1][i] > 0.1) {
477 if (nvert[k-1][j][i] + nvert[k-1][j+1][i] < 0.1) { dpdz = (p[k][j][i] + p[k][j+1][i] - p[k-1][j][i] - p[k-1][j+1][i]) * 0.5; }
478 } else if ((k == 1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC)|| nvert[k-1][j][i] + nvert[k-1][j+1][i] > 0.1) {
479 if (nvert[k+1][j][i] + nvert[k+1][j+1][i] < 0.1) { dpdz = (p[k+1][j][i] + p[k+1][j+1][i] - p[k][j][i] - p[k][j+1][i]) * 0.5; }
480 } else if ((k == 1 || k==mz-2) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k-1][j][i] + nvert[k-1][j+1][i] > 0.1) {
481 if (nvert[k+1][j][i] + nvert[k+1][j+1][i] < 0.1) { dpdz = (p[k+1][j][i] + p[k+1][j+1][i] - p[k][j][i] - p[k][j+1][i]) * 0.5; }
482 } else { dpdz = (p[k+1][j][i] + p[k+1][j+1][i] - p[k-1][j][i] - p[k-1][j+1][i]) * 0.25; }
483
484 PetscReal grad_p_y = (dpdc * (jcsi[k][j][i].x * jeta[k][j][i].x + jcsi[k][j][i].y * jeta[k][j][i].y + jcsi[k][j][i].z * jeta[k][j][i].z) * jaj[k][j][i] +
485 dpde * (jeta[k][j][i].x * jeta[k][j][i].x + jeta[k][j][i].y * jeta[k][j][i].y + jeta[k][j][i].z * jeta[k][j][i].z) * jaj[k][j][i] +
486 dpdz * (jzet[k][j][i].x * jeta[k][j][i].x + jzet[k][j][i].y * jeta[k][j][i].y + jzet[k][j][i].z * jeta[k][j][i].z) * jaj[k][j][i]);
487
488 PetscReal correction = grad_p_y*scale;
489 //LOG_LOOP_ALLOW_EXACT(GLOBAL,LOG_DEBUG,k,5," Flux correction in Eta Direction: %le.\n",correction);
490 ucont[k][j][i].y -= correction;
491 }
492 }
493
494 // --- Correct W_contravariant (z-component of velocity) ---
495 PetscInt k_end = (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC) ? mz - 1 : mz - 2;
496 if (k < k_end) {
497 if (!(nvert[k][j][i] > IBM_FLUID_THRESHOLD || nvert[k + 1][j][i] > IBM_FLUID_THRESHOLD)) {
498 PetscReal dpdc = 0.0, dpde = 0.0, dpdz = 0.0;
499 dpdz = p[k + 1][j][i] - p[k][j][i];
500
501 // Boundary-aware stencil for dp/d_csi
502 if ((i == mx-2 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i+1] + nvert[k+1][j][i+1] > 0.1) {
503 if (nvert[k][j][i-1] + nvert[k+1][j][i-1] < 0.1 && (i!=1 || (i==1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC))) { dpdc = (p[k][j][i] + p[k+1][j][i] - p[k][j][i-1] - p[k+1][j][i-1]) * 0.5; }
504 } else if ((i == mx-2 || i==1) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i+1] + nvert[k+1][j][i+1] > 0.1) {
505 if (nvert[k][j][i-1] + nvert[k+1][j][i-1] < 0.1) { dpdc = (p[k][j][i] + p[k+1][j][i] - p[k][j][i-1] - p[k+1][j][i-1]) * 0.5; }
506 } else if ((i == 1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i-1] + nvert[k+1][j][i-1] > 0.1) {
507 if (nvert[k][j][i+1] + nvert[k+1][j][i+1] < 0.1) { dpdc = (p[k][j][i+1] + p[k+1][j][i+1] - p[k][j][i] - p[k+1][j][i]) * 0.5; }
508 } else if ((i == 1 || i==mx-2) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i-1] + nvert[k+1][j][i-1] > 0.1) {
509 if (nvert[k][j][i+1] + nvert[k+1][j][i+1] < 0.1) { dpdc = (p[k][j][i+1] + p[k+1][j][i+1] - p[k][j][i] - p[k+1][j][i]) * 0.5; }
510 } else { dpdc = (p[k][j][i+1] + p[k+1][j][i+1] - p[k][j][i-1] - p[k+1][j][i-1]) * 0.25; }
511
512 // Boundary-aware stencil for dp/d_eta
513 if ((j == my-2 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC)|| nvert[k][j+1][i] + nvert[k+1][j+1][i] > 0.1) {
514 if (nvert[k][j-1][i] + nvert[k+1][j-1][i] < 0.1 && (j!=1 || (j==1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC))) { dpde = (p[k][j][i] + p[k+1][j][i] - p[k][j-1][i] - p[k+1][j-1][i]) * 0.5; }
515 } else if ((j == my-2 || j==1) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j+1][i] + nvert[k+1][j+1][i] > 0.1) {
516 if (nvert[k][j-1][i] + nvert[k+1][j-1][i] < 0.1) { dpde = (p[k][j][i] + p[k+1][j][i] - p[k][j-1][i] - p[k+1][j-1][i]) * 0.5; }
517 } else if ((j == 1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC)|| nvert[k][j-1][i] + nvert[k+1][j-1][i] > 0.1) {
518 if (nvert[k][j+1][i] + nvert[k+1][j+1][i] < 0.1) { dpde = (p[k][j+1][i] + p[k+1][j+1][i] - p[k][j][i] - p[k+1][j][i]) * 0.5; }
519 } else if ((j == 1 || j==my-2) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j-1][i] + nvert[k+1][j-1][i] > 0.1) {
520 if (nvert[k][j+1][i] + nvert[k+1][j+1][i] < 0.1) { dpde = (p[k][j+1][i] + p[k+1][j+1][i] - p[k][j][i] - p[k+1][j][i]) * 0.5; }
521 } else { dpde = (p[k][j+1][i] + p[k+1][j+1][i] - p[k][j-1][i] - p[k+1][j-1][i]) * 0.25; }
522
523 PetscReal grad_p_z = (dpdc * (kcsi[k][j][i].x * kzet[k][j][i].x + kcsi[k][j][i].y * kzet[k][j][i].y + kcsi[k][j][i].z * kzet[k][j][i].z) * kaj[k][j][i] +
524 dpde * (keta[k][j][i].x * kzet[k][j][i].x + keta[k][j][i].y * kzet[k][j][i].y + keta[k][j][i].z * kzet[k][j][i].z) * kaj[k][j][i] +
525 dpdz * (kzet[k][j][i].x * kzet[k][j][i].x + kzet[k][j][i].y * kzet[k][j][i].y + kzet[k][j][i].z * kzet[k][j][i].z) * kaj[k][j][i]);
526
527 // ========================= DEBUG PRINT =========================
529 "[k=%d, j=%d, i=%d] ---- Neighbor Pressures ----\n"
530 " Central Z-Neighbors: p[k+1][j][i] = %g | p[k][j][i] = %g\n"
531 " Eta-Stencil (Y-dir): p[k][j-1][i] = %g, p[k+1][j-1][i] = %g | p[k][j+1][i] = %g, p[k+1][j+1][i] = %g\n"
532 " Csi-Stencil (X-dir): p[k][j][i-1] = %g, p[k+1][j][i-1] = %g | p[k][j][i+1] = %g, p[k+1][j][i+1] = %g\n",
533 k, j, i,
534 p[k + 1][j][i], p[k][j][i],
535 p[k][j - 1][i], p[k + 1][j - 1][i], p[k][j + 1][i], p[k + 1][j + 1][i],
536 p[k][j][i - 1], p[k + 1][j][i - 1], p[k][j][i + 1], p[k + 1][j][i + 1]);
537 // ======================= END DEBUG PRINT =======================
538
539 LOG_LOOP_ALLOW_EXACT(GLOBAL,LOG_DEBUG,k,5," dpdc: %le | dpde: %le | dpdz: %le.\n",dpdc,dpde,dpdz);
540 PetscReal correction = grad_p_z*scale;
541 //LOG_LOOP_ALLOW_EXACT(GLOBAL,LOG_DEBUG,k,5," Flux correction in Zet Direction: %le.\n",correction);
542 ucont[k][j][i].z -= correction;
543 }
544 }
545 }
546 }
547 }
548
549 // --- Explicit correction for periodic boundaries (if necessary) ---
550 // The main loop handles the interior, but this handles the first physical layer at periodic boundaries.
551 // Note: This logic is largely duplicated from the main loop and could be merged, but is preserved for fidelity.
552 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0) {
553 for (PetscInt k=lzs; k<lze; k++) {
554 for (PetscInt j=lys; j<lye; j++) {
555 PetscInt i=xs;
556
557 PetscReal dpdc = p[k][j][i+1] - p[k][j][i];
558
559 PetscReal dpde = 0.;
560 PetscReal dpdz = 0.;
561
562 if ((j==my-2 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC)|| nvert[k][j+1][i]+nvert[k][j+1][i+1] > 0.1) {
563 if (nvert[k][j-1][i] + nvert[k][j-1][i+1] < 0.1 && (j!=1 || (j==1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC))) {
564 dpde = (p[k][j ][i] + p[k][j ][i+1] -
565 p[k][j-1][i] - p[k][j-1][i+1]) * 0.5;
566 }
567 }
568 else if ((j==my-2 || j==1) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j+1][i]+nvert[k][j+1][i+1] > 0.1) {
569 if (nvert[k][j-1][i] + nvert[k][j-1][i+1] < 0.1) {
570 dpde = (p[k][j ][i] + p[k][j ][i+1] -
571 p[k][j-1][i] - p[k][j-1][i+1]) * 0.5;
572 }
573 }
574 else if ((j == 1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC) || nvert[k][j-1][i] + nvert[k][j-1][i+1] > 0.1) {
575 if (nvert[k][j+1][i] + nvert[k][j+1][i+1] < 0.1) {
576 dpde = (p[k][j+1][i] + p[k][j+1][i+1] -
577 p[k][j ][i] - p[k][j ][i+1]) * 0.5;
578 }
579 }
580 else if ((j == 1 || j==my-2) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j-1][i] + nvert[k][j-1][i+1] > 0.1) {
581 if (nvert[k][j+1][i] + nvert[k][j+1][i+1] < 0.1) {
582 dpde = (p[k][j+1][i] + p[k][j+1][i+1] -
583 p[k][j ][i] - p[k][j ][i+1]) * 0.5;
584 }
585 }
586 else {
587 dpde = (p[k][j+1][i] + p[k][j+1][i+1] -
588 p[k][j-1][i] - p[k][j-1][i+1]) * 0.25;
589 }
590
591 if ((k == mz-2 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC) || nvert[k+1][j][i] + nvert[k+1][j][i+1] > 0.1) {
592 if (nvert[k-1][j][i] + nvert[k-1][j][i+1] < 0.1 && (k!=1 || (k==1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC))) {
593 dpdz = (p[k ][j][i] + p[k ][j][i+1] -
594 p[k-1][j][i] - p[k-1][j][i+1]) * 0.5;
595 }
596 }
597 else if ((k == mz-2 || k==1) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k+1][j][i] + nvert[k+1][j][i+1] > 0.1) {
598 if (nvert[k-1][j][i] + nvert[k-1][j][i+1] < 0.1) {
599 dpdz = (p[k ][j][i] + p[k ][j][i+1] -
600 p[k-1][j][i] - p[k-1][j][i+1]) * 0.5;
601 }
602 }
603 else if ((k == 1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC)|| nvert[k-1][j][i] + nvert[k-1][j][i+1] > 0.1) {
604 if (nvert[k+1][j][i] + nvert[k+1][j][i+1] < 0.1) {
605 dpdz = (p[k+1][j][i] + p[k+1][j][i+1] -
606 p[k ][j][i] - p[k ][j][i+1]) * 0.5;
607 }
608 }
609 else if ((k == 1 || k==mz-2) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k-1][j][i] + nvert[k-1][j][i+1] > 0.1) {
610 if (nvert[k+1][j][i] + nvert[k+1][j][i+1] < 0.1) {
611 dpdz = (p[k+1][j][i] + p[k+1][j][i+1] -
612 p[k ][j][i] - p[k ][j][i+1]) * 0.5;
613 }
614 }
615 else {
616 dpdz = (p[k+1][j][i] + p[k+1][j][i+1] -
617 p[k-1][j][i] - p[k-1][j][i+1]) * 0.25;
618 }
619
620
621
622 if (!(nvert[k][j][i] + nvert[k][j][i+1])) {
623 ucont[k][j][i].x -=
624 (dpdc * (icsi[k][j][i].x * icsi[k][j][i].x +
625 icsi[k][j][i].y * icsi[k][j][i].y +
626 icsi[k][j][i].z * icsi[k][j][i].z) * iaj[k][j][i] +
627 dpde * (ieta[k][j][i].x * icsi[k][j][i].x +
628 ieta[k][j][i].y * icsi[k][j][i].y +
629 ieta[k][j][i].z * icsi[k][j][i].z) * iaj[k][j][i] +
630 dpdz * (izet[k][j][i].x * icsi[k][j][i].x +
631 izet[k][j][i].y * icsi[k][j][i].y +
632 izet[k][j][i].z * icsi[k][j][i].z) * iaj[k][j][i])
633 * scale;
634
635 }
636 }
637 }
638 }
639 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0) {
640
641 for (PetscInt k=lzs; k<lze; k++) {
642 for (PetscInt i=lxs; i<lxe; i++) {
643 PetscInt j=ys;
644
645 PetscReal dpdc = 0.;
646 PetscReal dpdz = 0.;
647 if ((i == mx-2 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC) || nvert[k][j][i+1] + nvert[k][j+1][i+1] > 0.1) {
648 if (nvert[k][j][i-1] + nvert[k][j+1][i-1] < 0.1 && (i!=1 || (i==1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC))) {
649 dpdc = (p[k][j][i ] + p[k][j+1][i ] -
650 p[k][j][i-1] - p[k][j+1][i-1]) * 0.5;
651 }
652 }
653 else if ((i == mx-2 || i==1) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i+1] + nvert[k][j+1][i+1] > 0.1) {
654 if (nvert[k][j][i-1] + nvert[k][j+1][i-1] < 0.1) {
655 dpdc = (p[k][j][i ] + p[k][j+1][i ] -
656 p[k][j][i-1] - p[k][j+1][i-1]) * 0.5;
657 }
658 }
659 else if ((i == 1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i-1] + nvert[k][j+1][i-1] > 0.1) {
660 if (nvert[k][j][i+1] + nvert[k][j+1][i+1] < 0.1) {
661 dpdc = (p[k][j][i+1] + p[k][j+1][i+1] -
662 p[k][j][i ] - p[k][j+1][i ]) * 0.5;
663 }
664 }
665 else if ((i == 1 || i==mx-2) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i-1] + nvert[k][j+1][i-1] > 0.1) {
666 if (nvert[k][j][i+1] + nvert[k][j+1][i+1] < 0.1) {
667 dpdc = (p[k][j][i+1] + p[k][j+1][i+1] -
668 p[k][j][i ] - p[k][j+1][i ]) * 0.5;
669 }
670 }
671 else {
672 dpdc = (p[k][j][i+1] + p[k][j+1][i+1] -
673 p[k][j][i-1] - p[k][j+1][i-1]) * 0.25;
674 }
675
676 PetscReal dpde = p[k][j+1][i] - p[k][j][i];
677
678 if ((k == mz-2 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC)|| nvert[k+1][j][i] + nvert[k+1][j+1][i] > 0.1) {
679 if (nvert[k-1][j][i] + nvert[k-1][j+1][i] < 0.1 && (k!=1 || (k==1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC))) {
680 dpdz = (p[k ][j][i] + p[k ][j+1][i] -
681 p[k-1][j][i] - p[k-1][j+1][i]) * 0.5;
682 }
683 }
684 else if ((k == mz-2 || k==1 ) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k+1][j][i] + nvert[k+1][j+1][i] > 0.1) {
685 if (nvert[k-1][j][i] + nvert[k-1][j+1][i] < 0.1) {
686 dpdz = (p[k ][j][i] + p[k ][j+1][i] -
687 p[k-1][j][i] - p[k-1][j+1][i]) * 0.5;
688 }
689 }
690 else if ((k == 1 && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC)|| nvert[k-1][j][i] + nvert[k-1][j+1][i] > 0.1) {
691 if (nvert[k+1][j][i] + nvert[k+1][j+1][i] < 0.1) {
692 dpdz = (p[k+1][j][i] + p[k+1][j+1][i] -
693 p[k ][j][i] - p[k ][j+1][i]) * 0.5;
694 }
695 }
696 else if ((k == 1 || k==mz-2) && user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && nvert[k-1][j][i] + nvert[k-1][j+1][i] > 0.1) {
697 if (nvert[k+1][j][i] + nvert[k+1][j+1][i] < 0.1) {
698 dpdz = (p[k+1][j][i] + p[k+1][j+1][i] -
699 p[k ][j][i] - p[k ][j+1][i]) * 0.5;
700 }
701 }
702 else {
703 dpdz = (p[k+1][j][i] + p[k+1][j+1][i] -
704 p[k-1][j][i] - p[k-1][j+1][i]) * 0.25;
705 }
706
707 if (!(nvert[k][j][i] + nvert[k][j+1][i])) {
708 ucont[k][j][i].y -=
709 (dpdc * (jcsi[k][j][i].x * jeta[k][j][i].x +
710 jcsi[k][j][i].y * jeta[k][j][i].y +
711 jcsi[k][j][i].z * jeta[k][j][i].z) * jaj[k][j][i] +
712 dpde * (jeta[k][j][i].x * jeta[k][j][i].x +
713 jeta[k][j][i].y * jeta[k][j][i].y +
714 jeta[k][j][i].z * jeta[k][j][i].z) * jaj[k][j][i] +
715 dpdz * (jzet[k][j][i].x * jeta[k][j][i].x +
716 jzet[k][j][i].y * jeta[k][j][i].y +
717 jzet[k][j][i].z * jeta[k][j][i].z) * jaj[k][j][i])
718 * scale;
719 }
720 }
721 }
722 }
723
724 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0) {
725 for (PetscInt j=lys; j<lye; j++) {
726 for (PetscInt i=lxs; i<lxe; i++) {
727
728 PetscInt k=zs;
729 PetscReal dpdc = 0.;
730 PetscReal dpde = 0.;
731
732 if ((i == mx-2 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i+1] + nvert[k+1][j][i+1] > 0.1) {
733 if (nvert[k][j][i-1] + nvert[k+1][j][i-1] < 0.1 && (i!=1 || (i==1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC))) {
734 dpdc = (p[k][j][i ] + p[k+1][j][i ] -
735 p[k][j][i-1] - p[k+1][j][i-1]) * 0.5;
736 }
737 }
738 else if ((i == mx-2 || i==1) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i+1] + nvert[k+1][j][i+1] > 0.1) {
739 if (nvert[k][j][i-1] + nvert[k+1][j][i-1] < 0.1) {
740 dpdc = (p[k][j][i ] + p[k+1][j][i ] -
741 p[k][j][i-1] - p[k+1][j][i-1]) * 0.5;
742 }
743 }
744 else if ((i == 1 && user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC)|| nvert[k][j][i-1] + nvert[k+1][j][i-1] > 0.1) {
745 if (nvert[k][j][i+1] + nvert[k+1][j][i+1] < 0.1) {
746 dpdc = (p[k][j][i+1] + p[k+1][j][i+1] -
747 p[k][j][i ] - p[k+1][j][i ]) * 0.5;
748 }
749 }
750 else if ((i == 1 || i==mx-2) && user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && nvert[k][j][i-1] + nvert[k+1][j][i-1] > 0.1) {
751 if (nvert[k][j][i+1] + nvert[k+1][j][i+1] < 0.1) {
752 dpdc = (p[k][j][i+1] + p[k+1][j][i+1] -
753 p[k][j][i ] - p[k+1][j][i ]) * 0.5;
754 }
755 }
756 else {
757 dpdc = (p[k][j][i+1] + p[k+1][j][i+1] -
758 p[k][j][i-1] - p[k+1][j][i-1]) * 0.25;
759 }
760
761 if ((j == my-2 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC)|| nvert[k][j+1][i] + nvert[k+1][j+1][i] > 0.1) {
762 if (nvert[k][j-1][i] + nvert[k+1][j-1][i] < 0.1 && (j!=1 || (j==1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC))) {
763 dpde = (p[k][j ][i] + p[k+1][j ][i] -
764 p[k][j-1][i] - p[k+1][j-1][i]) * 0.5;
765 }
766 }
767 else if ((j == my-2 || j==1) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j+1][i] + nvert[k+1][j+1][i] > 0.1) {
768 if (nvert[k][j-1][i] + nvert[k+1][j-1][i] < 0.1) {
769 dpde = (p[k][j ][i] + p[k+1][j ][i] -
770 p[k][j-1][i] - p[k+1][j-1][i]) * 0.5;
771 }
772 }
773 else if ((j == 1 && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC)|| nvert[k][j-1][i] + nvert[k+1][j-1][i] > 0.1) {
774 if (nvert[k][j+1][i] + nvert[k+1][j+1][i] < 0.1) {
775 dpde = (p[k][j+1][i] + p[k+1][j+1][i] -
776 p[k][j ][i] - p[k+1][j ][i]) * 0.5;
777 }
778 }
779 else if ((j == 1 || j==my-2) && user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && nvert[k][j-1][i] + nvert[k+1][j-1][i] > 0.1) {
780 if (nvert[k][j+1][i] + nvert[k+1][j+1][i] < 0.1) {
781 dpde = (p[k][j+1][i] + p[k+1][j+1][i] -
782 p[k][j ][i] - p[k+1][j ][i]) * 0.5;
783 }
784 }
785 else {
786 dpde = (p[k][j+1][i] + p[k+1][j+1][i] -
787 p[k][j-1][i] - p[k+1][j-1][i]) * 0.25;
788 }
789
790 PetscReal dpdz = p[k+1][j][i] - p[k][j][i];
791
792 if (!(nvert[k][j][i] + nvert[k+1][j][i])) {
793
794 ucont[k][j][i].z -=
795 (dpdc * (kcsi[k][j][i].x * kzet[k][j][i].x +
796 kcsi[k][j][i].y * kzet[k][j][i].y +
797 kcsi[k][j][i].z * kzet[k][j][i].z) * kaj[k][j][i] +
798 dpde * (keta[k][j][i].x * kzet[k][j][i].x +
799 keta[k][j][i].y * kzet[k][j][i].y +
800 keta[k][j][i].z * kzet[k][j][i].z) * kaj[k][j][i] +
801 dpdz * (kzet[k][j][i].x * kzet[k][j][i].x +
802 kzet[k][j][i].y * kzet[k][j][i].y +
803 kzet[k][j][i].z * kzet[k][j][i].z) * kaj[k][j][i])
804 * scale;
805
806 }
807 }
808 }
809 }
810
811 // Corrects Flux Profile for Driven Flows if applicable.
813
814 //================================================================================
815 // Section 3: Finalization and Cleanup
816 //================================================================================
817
818 // --- Restore access to all PETSc vector arrays ---
819 DMDAVecRestoreArray(fda, user->Ucont, &ucont);
820 // DMDAVecRestoreArray(fda, user->lCsi, &csi); DMDAVecRestoreArray(fda, user->lEta, &eta); DMDAVecRestoreArray(fda, user->lZet, &zet);
821 //DMDAVecRestoreArray(da, user->lAj, &aj);
822 DMDAVecRestoreArray(fda, user->lICsi, &icsi); DMDAVecRestoreArray(fda, user->lIEta, &ieta); DMDAVecRestoreArray(fda, user->lIZet, &izet);
823 DMDAVecRestoreArray(fda, user->lJCsi, &jcsi); DMDAVecRestoreArray(fda, user->lJEta, &jeta); DMDAVecRestoreArray(fda, user->lJZet, &jzet);
824 DMDAVecRestoreArray(fda, user->lKCsi, &kcsi); DMDAVecRestoreArray(fda, user->lKEta, &keta); DMDAVecRestoreArray(fda, user->lKZet, &kzet);
825 DMDAVecRestoreArray(da, user->lIAj, &iaj); DMDAVecRestoreArray(da, user->lJAj, &jaj); DMDAVecRestoreArray(da, user->lKAj, &kaj);
826 DMDAVecRestoreArray(da, user->lPhi, &p);
827 DMDAVecRestoreArray(da, user->lNvert, &nvert);
828
829 // --- Update ghost cells for the newly corrected velocity field ---
830 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Updating ghost cells for corrected velocity.\n");
831 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
832 ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields); CHKERRQ(ierr);
833
834 // --- Convert velocity to Cartesian and update ghost nodes ---
835 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Converting velocity to Cartesian and finalizing ghost nodes.\n");
836 ierr = Contra2Cart(user); CHKERRQ(ierr);
837 ierr = FinalizePostProjectionCellFields(user); CHKERRQ(ierr);
838 //GhostNodeVelocity(user);
839
840 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Exiting Projection step.\n");
842 PetscFunctionReturn(0);
843}
PetscErrorCode SynchronizePeriodicStaggeredFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes persistent component-staggered vector fields.
PetscErrorCode FinalizePostProjectionCellFields(UserCtx *user)
Finalizes cell-centered fields after the projection step.
@ FIELD_ID_UCONT
#define LOG_LOOP_ALLOW_EXACT(scope, level, var, val, fmt,...)
Logs a custom message if a variable equals a specific value.
Definition logging.h:335
PetscErrorCode CorrectChannelFluxProfile(UserCtx *user)
Internal helper implementation: CorrectChannelFluxProfile().
Definition poisson.c:105
PetscErrorCode Contra2Cart(UserCtx *user)
Reconstructs Cartesian velocity (Ucat) at cell centers from contravariant velocity (Ucont) defined on...
Definition setup.c:2649
Vec Ucont
Definition variables.h:939
Vec lPhi
Definition variables.h:939
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PoissonNullSpaceFunction()

PetscErrorCode PoissonNullSpaceFunction ( MatNullSpace  nullsp,
Vec  X,
void *  ctx 
)
extern

The callback function for PETSc's MatNullSpace object.

This function removes the null space from the Poisson solution vector by ensuring the average pressure is zero, which is necessary for problems with pure Neumann boundary conditions.

Parameters
nullspThe MatNullSpace context.
XThe vector to be corrected.
ctxA void pointer to the UserCtx.
Returns
PetscErrorCode 0 on success.

The callback function for PETSc's MatNullSpace object.

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

See also
PoissonNullSpaceFunction()

Definition at line 921 of file poisson.c.

922{
923 PetscErrorCode ierr;
924 UserCtx *user = (UserCtx*)ctx;
925 (void)nullsp;
926
927 DM da = user->da;
928
929 DMDALocalInfo info = user->info;
930 PetscInt xs = info.xs, xe = info.xs + info.xm;
931 PetscInt ys = info.ys, ye = info.ys + info.ym;
932 PetscInt zs = info.zs, ze = info.zs + info.zm;
933 PetscInt mx = info.mx, my = info.my, mz = info.mz;
934 PetscInt lxs, lxe, lys, lye, lzs, lze;
935
936 PetscReal ***x, ***nvert;
937 PetscInt i, j, k;
938
939/* /\* First remove a constant from the Vec field X *\/ */
940
941
942 /* Then apply boundary conditions */
943 DMDAVecGetArray(da, X, &x);
944 DMDAVecGetArray(da, user->lNvert, &nvert);
945
946 lxs = xs; lxe = xe;
947 lys = ys; lye = ye;
948 lzs = zs; lze = ze;
949
950 if (xs==0) lxs = xs+1;
951 if (ys==0) lys = ys+1;
952 if (zs==0) lzs = zs+1;
953
954 if (xe==mx) lxe = xe-1;
955 if (ye==my) lye = ye-1;
956 if (ze==mz) lze = ze-1;
957
958 PetscReal lsum, sum;
959 PetscReal lnum, num;
960
961 if (user->multinullspace) {
962 LOG_ALLOW(GLOBAL, LOG_INFO, "Poisson solve is using the configured multi-nullspace.\n");
963 }
964 if (!user->multinullspace) {
965 lsum = 0;
966 lnum = 0;
967 for (k=lzs; k<lze; k++) {
968 for (j=lys; j<lye; j++) {
969 for (i=lxs; i<lxe; i++) {
970 if (nvert[k][j][i] < 0.1) {
971 lsum += x[k][j][i];
972 lnum ++;
973 }
974 }
975 }
976 }
977
978 ierr = MPI_Allreduce(&lsum,&sum,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
979 ierr = MPI_Allreduce(&lnum,&num,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
980 /* PetscGlobalSum(&lsum, &sum, PETSC_COMM_WORLD); */
981/* PetscGlobalSum(&lnum, &num, PETSC_COMM_WORLD); */
982 sum = sum / (-1.0 * num);
983
984 for (k=lzs; k<lze; k++) {
985 for (j=lys; j<lye; j++) {
986 for (i=lxs; i<lxe; i++) {
987 if (nvert[k][j][i] < 0.1) {
988 x[k][j][i] +=sum;
989 }
990 }
991 }
992 }
993 }
994 else {
995 lsum = 0;
996 lnum = 0;
997 for (j=lys; j<lye; j++) {
998 for (i=lxs; i<lxe; i++) {
999 for (k=lzs; k<lze; k++) {
1000 if (k<user->KSKE[2*(j*mx+i)] && nvert[k][j][i]<0.1) {
1001 lsum += x[k][j][i];
1002 lnum ++;
1003 }
1004 }
1005 }
1006 }
1007 ierr = MPI_Allreduce(&lsum,&sum,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
1008 ierr = MPI_Allreduce(&lnum,&num,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
1009 /* PetscGlobalSum(&lsum, &sum, PETSC_COMM_WORLD); */
1010/* PetscGlobalSum(&lnum, &num, PETSC_COMM_WORLD); */
1011 sum /= -num;
1012 for (j=lys; j<lye; j++) {
1013 for (i=lxs; i<lxe; i++) {
1014 for (k=lzs; k<lze; k++) {
1015 if (k<user->KSKE[2*(j*mx+i)] && nvert[k][j][i]<0.1) {
1016 x[k][j][i] += sum;
1017 }
1018 }
1019 }
1020 }
1021
1022 lsum = 0;
1023 lnum = 0;
1024 for (j=lys; j<lye; j++) {
1025 for (i=lxs; i<lxe; i++) {
1026 for (k=lzs; k<lze; k++) {
1027 if (k>=user->KSKE[2*(j*mx+i)] && nvert[k][j][i]<0.1) {
1028 lsum += x[k][j][i];
1029 lnum ++;
1030 }
1031 }
1032 }
1033 }
1034 ierr = MPI_Allreduce(&lsum,&sum,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
1035 ierr = MPI_Allreduce(&lnum,&num,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
1036 /* PetscGlobalSum(&lsum, &sum, PETSC_COMM_WORLD); */
1037/* PetscGlobalSum(&lnum, &num, PETSC_COMM_WORLD); */
1038 sum /= -num;
1039 for (j=lys; j<lye; j++) {
1040 for (i=lxs; i<lxe; i++) {
1041 for (k=lzs; k<lze; k++) {
1042 if (k>=user->KSKE[2*(j*mx+i)] && nvert[k][j][i]<0.1) {
1043 x[k][j][i] += sum;
1044 }
1045 }
1046 }
1047 }
1048
1049 } //if multinullspace
1050 if (zs == 0) {
1051 k = 0;
1052 for (j=ys; j<ye; j++) {
1053 for (i=xs; i<xe; i++) {
1054 x[k][j][i] = 0.;
1055 }
1056 }
1057 }
1058
1059 if (ze == mz) {
1060 k = mz-1;
1061 for (j=ys; j<ye; j++) {
1062 for (i=xs; i<xe; i++) {
1063 x[k][j][i] = 0.;
1064 }
1065 }
1066 }
1067
1068 if (ys == 0) {
1069 j = 0;
1070 for (k=zs; k<ze; k++) {
1071 for (i=xs; i<xe; i++) {
1072 x[k][j][i] = 0.;
1073 }
1074 }
1075 }
1076
1077 if (ye == my) {
1078 j = my-1;
1079 for (k=zs; k<ze; k++) {
1080 for (i=xs; i<xe; i++) {
1081 x[k][j][i] = 0.;
1082 }
1083 }
1084 }
1085
1086 if (xs == 0) {
1087 i = 0;
1088 for (k=zs; k<ze; k++) {
1089 for (j=ys; j<ye; j++) {
1090 x[k][j][i] = 0.;
1091 }
1092 }
1093 }
1094
1095 if (xe == mx) {
1096 i = mx-1;
1097 for (k=zs; k<ze; k++) {
1098 for (j=ys; j<ye; j++) {
1099 x[k][j][i] = 0.;
1100 }
1101 }
1102 }
1103
1104 for (k=zs; k<ze; k++) {
1105 for (j=ys; j<ye; j++) {
1106 for (i=xs; i<xe; i++) {
1107 if (nvert[k][j][i] > 0.1)
1108 x[k][j][i] = 0.;
1109 }
1110 }
1111 }
1112 DMDAVecRestoreArray(da, X, &x);
1113 DMDAVecRestoreArray(da, user->lNvert, &nvert);
1114
1115 return 0;
1116}
PetscInt * KSKE
Definition variables.h:966
Here is the caller graph for this function:

◆ MyRestriction()

PetscErrorCode MyRestriction ( Mat  A,
Vec  X,
Vec  F 
)
extern

The callback function for the multigrid restriction operator (MatShell).

Defines the fine-to-coarse grid transfer for the Poisson residual.

Parameters
AThe shell matrix context.
XThe fine-grid source vector.
FThe coarse-grid destination vector.
Returns
PetscErrorCode 0 on success.

The callback function for the multigrid restriction operator (MatShell).

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

See also
MyRestriction()

Definition at line 1317 of file poisson.c.

1318{
1319 UserCtx *user;
1320
1321 MatShellGetContext(A, (void**)&user);
1322
1323
1324 DM da = user->da;
1325
1326 DM da_f = *user->da_f;
1327
1328 DMDALocalInfo info;
1329 DMDAGetLocalInfo(da, &info);
1330 PetscInt xs = info.xs, xe = info.xs + info.xm;
1331 PetscInt ys = info.ys, ye = info.ys + info.ym;
1332 PetscInt zs = info.zs, ze = info.zs + info.zm;
1333 PetscInt mx = info.mx, my = info.my, mz = info.mz;
1334 // PetscInt lxs, lxe, lys, lye, lzs, lze;
1335
1336 PetscReal ***f, ***x, ***nvert;
1337 PetscInt i, j, k, ih, jh, kh, ia, ja, ka;
1338
1339 DMDAVecGetArray(da, F, &f);
1340
1341 Vec lX;
1342
1343 DMCreateLocalVector(da_f, &lX);
1344 DMGlobalToLocalBegin(da_f, X, INSERT_VALUES, lX);
1345 DMGlobalToLocalEnd(da_f, X, INSERT_VALUES, lX);
1346 DMDAVecGetArray(da_f, lX, &x);
1347
1348 DMDAVecGetArray(da, user->lNvert, &nvert);
1349
1350 PetscReal ***nvert_f;
1351 DMDAVecGetArray(da_f, user->user_f->lNvert, &nvert_f);
1352
1353 if ((user->isc)) ia = 0;
1354 else ia = 1;
1355
1356 if ((user->jsc)) ja = 0;
1357 else ja = 1;
1358
1359 if ((user->ksc)) ka = 0;
1360 else ka = 1;
1361
1362 for (k=zs; k<ze; k++) {
1363 for (j=ys; j<ye; j++) {
1364 for (i=xs; i<xe; i++) {
1365 if (k==0) {
1366 f[k][j][i] = 0.;
1367 }
1368 else if (k==mz-1) {
1369 f[k][j][i] = 0.;
1370 }
1371 else if (j==0) {
1372 f[k][j][i] = 0.;
1373 }
1374 else if (j==my-1) {
1375 f[k][j][i] = 0.;
1376 }
1377 else if (i==0) {
1378 f[k][j][i] = 0.;
1379 }
1380 else if (i==mx-1) {
1381 f[k][j][i] = 0.;
1382 }
1383 else {
1384 GridRestriction(i, j, k, &ih, &jh, &kh, user);
1385 f[k][j][i] = 0.125 *
1386 (x[kh ][jh ][ih ] * PetscMax(0., 1 - nvert_f[kh ][jh ][ih ]) +
1387 x[kh ][jh ][ih-ia] * PetscMax(0., 1 - nvert_f[kh ][jh ][ih-ia]) +
1388 x[kh ][jh-ja][ih ] * PetscMax(0., 1 - nvert_f[kh ][jh-ja][ih ]) +
1389 x[kh-ka][jh ][ih ] * PetscMax(0., 1 - nvert_f[kh-ka][jh ][ih ]) +
1390 x[kh ][jh-ja][ih-ia] * PetscMax(0., 1 - nvert_f[kh ][jh-ja][ih-ia]) +
1391 x[kh-ka][jh-ja][ih ] * PetscMax(0., 1 - nvert_f[kh-ka][jh-ja][ih ]) +
1392 x[kh-ka][jh ][ih-ia] * PetscMax(0., 1 - nvert_f[kh-ka][jh ][ih-ia]) +
1393 x[kh-ka][jh-ja][ih-ia] * PetscMax(0., 1 - nvert_f[kh-ka][jh-ja][ih-ia]));
1394
1395
1396
1397 if (nvert[k][j][i] > 0.1) f[k][j][i] = 0.;
1398 }
1399 }
1400 }
1401 }
1402
1403
1404 DMDAVecRestoreArray(da_f, user->user_f->lNvert, &nvert_f);
1405
1406 DMDAVecRestoreArray(da_f, lX, &x);
1407 VecDestroy(&lX);
1408
1409 DMDAVecRestoreArray(da, F, &f);
1410 DMDAVecRestoreArray(da, user->lNvert, &nvert);
1411
1412
1413 return 0;
1414}
static PetscErrorCode GridRestriction(PetscInt i, PetscInt j, PetscInt k, PetscInt *ih, PetscInt *jh, PetscInt *kh, UserCtx *user)
Restrict a fine-grid scalar value onto its associated coarse-grid location.
Definition poisson.c:66
PetscInt isc
Definition variables.h:924
PetscInt ksc
Definition variables.h:924
PetscInt jsc
Definition variables.h:924
Here is the call graph for this function:

◆ MyInterpolation()

PetscErrorCode MyInterpolation ( Mat  A,
Vec  X,
Vec  F 
)
extern

The callback function for the multigrid interpolation operator (MatShell).

Defines the coarse-to-fine grid transfer for the pressure correction.

Parameters
AThe shell matrix context.
XThe coarse-grid source vector.
FThe fine-grid destination vector.
Returns
PetscErrorCode 0 on success.

The callback function for the multigrid interpolation operator (MatShell).

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

See also
MyInterpolation()

Definition at line 1125 of file poisson.c.

1126{
1127 UserCtx *user;
1128
1129 MatShellGetContext(A, (void**)&user);
1130
1131
1132
1133 DM da = user->da;
1134
1135 DM da_c = *user->da_c;
1136
1137 DMDALocalInfo info = user->info;
1138 PetscInt xs = info.xs, xe = info.xs + info.xm;
1139 PetscInt ys = info.ys, ye = info.ys + info.ym;
1140 PetscInt zs = info.zs, ze = info.zs + info.zm;
1141 PetscInt mx = info.mx, my = info.my, mz = info.mz;
1142 PetscInt lxs, lxe, lys, lye, lzs, lze;
1143
1144 PetscReal ***f, ***x, ***nvert, ***nvert_c;
1145 PetscInt i, j, k, ic, jc, kc, ia, ja, ka;
1146
1147 lxs = xs; lxe = xe;
1148 lys = ys; lye = ye;
1149 lzs = zs; lze = ze;
1150
1151 if (xs==0) lxs = xs+1;
1152 if (ys==0) lys = ys+1;
1153 if (zs==0) lzs = zs+1;
1154
1155 if (xe==mx) lxe = xe-1;
1156 if (ye==my) lye = ye-1;
1157 if (ze==mz) lze = ze-1;
1158
1159
1160 DMDAVecGetArray(da, F, &f);
1161
1162
1163 Vec lX;
1164 DMCreateLocalVector(da_c, &lX);
1165
1166 DMGlobalToLocalBegin(da_c, X, INSERT_VALUES, lX);
1167 DMGlobalToLocalEnd(da_c, X, INSERT_VALUES, lX);
1168 DMDAVecGetArray(da_c, lX, &x);
1169
1170 DMDAVecGetArray(da, user->lNvert, &nvert);
1171 DMDAVecGetArray(da_c, *(user->lNvert_c), &nvert_c);
1172 for (k=lzs; k<lze; k++) {
1173 for (j=lys; j<lye; j++) {
1174 for (i=lxs; i<lxe; i++) {
1175
1176 GridInterpolation(i, j, k, ic, jc, kc, ia, ja, ka, user);
1177
1178 f[k][j][i] = (x[kc ][jc ][ic ] * 9 +
1179 x[kc ][jc+ja][ic ] * 3 +
1180 x[kc ][jc ][ic+ia] * 3 +
1181 x[kc ][jc+ja][ic+ia]) * 3./64. +
1182 (x[kc+ka][jc ][ic ] * 9 +
1183 x[kc+ka][jc+ja][ic ] * 3 +
1184 x[kc+ka][jc ][ic+ia] * 3 +
1185 x[kc+ka][jc+ja][ic+ia]) /64.;
1186 }
1187 }
1188 }
1189
1190 for (k=zs; k<ze; k++) {
1191 for (j=ys; j<ye; j++) {
1192 for (i=xs; i<xe; i++) {
1193
1194 if (i==0) {
1195 f[k][j][i] = 0.;//-f[k][j][i+1];
1196 }
1197 else if (i==mx-1) {
1198 f[k][j][i] = 0.;//-f[k][j][i-1];
1199 }
1200 else if (j==0) {
1201 f[k][j][i] = 0.;//-f[k][j+1][i];
1202 }
1203 else if (j==my-1) {
1204 f[k][j][i] = 0.;//-f[k][j-1][i];
1205 }
1206 else if (k==0) {
1207 f[k][j][i] = 0.;//-f[k+1][j][i];
1208 }
1209 else if (k==mz-1) {
1210 f[k][j][i] = 0.;//-f[k-1][j][i];
1211 }
1212 if (nvert[k][j][i] > 0.1) f[k][j][i] = 0.;
1213
1214 }
1215 }
1216 }
1217
1218 DMDAVecRestoreArray(da, user->lNvert, &nvert);
1219 DMDAVecRestoreArray(da_c, *(user->lNvert_c), &nvert_c);
1220
1221 DMDAVecRestoreArray(da_c, lX, &x);
1222
1223 VecDestroy(&lX);
1224 DMDAVecRestoreArray(da, F, &f);
1225
1226
1227
1228 return 0;
1229
1230}
#define GridInterpolation(i, j, k, ic, jc, kc, ia, ja, ka, user)
Definition poisson.c:5
Here is the caller graph for this function:

◆ VolumeFlux()

PetscErrorCode VolumeFlux ( UserCtx user,
PetscReal *  ibm_Flux,
PetscReal *  ibm_Area,
PetscInt  flg 
)
extern

Calculates the net flux across the immersed boundary surface.

Parameters
userThe UserCtx for the grid level.
ibm_Flux(Output) The calculated net flux.
ibm_Area(Output) The total surface area of the IB.
flgA flag controlling the correction behavior.
Returns
PetscErrorCode 0 on success.

Calculates the net flux across the immersed boundary surface.

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

See also
VolumeFlux()

Definition at line 2363 of file poisson.c.

2364{
2365 PetscErrorCode ierr;
2366 // --- CONTEXT ACQUISITION BLOCK ---
2367 // Get the master simulation context from the UserCtx.
2368 SimCtx *simCtx = user->simCtx;
2369
2370 // Create local variables to mirror the legacy globals for minimal code changes.
2371 const PetscInt NumberOfBodies = simCtx->NumberOfBodies;
2372 // --- END CONTEXT ACQUISITION BLOCK ---
2373
2374 DM da = user->da, fda = user->fda;
2375
2376 DMDALocalInfo info = user->info;
2377
2378 PetscInt xs = info.xs, xe = info.xs + info.xm;
2379 PetscInt ys = info.ys, ye = info.ys + info.ym;
2380 PetscInt zs = info.zs, ze = info.zs + info.zm;
2381 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2382
2383 PetscInt i, j, k,ibi;
2384 PetscInt lxs, lys, lzs, lxe, lye, lze;
2385
2386 lxs = xs; lxe = xe;
2387 lys = ys; lye = ye;
2388 lzs = zs; lze = ze;
2389
2390 if (xs==0) lxs = xs+1;
2391 if (ys==0) lys = ys+1;
2392 if (zs==0) lzs = zs+1;
2393
2394 if (xe==mx) lxe = xe-1;
2395 if (ye==my) lye = ye-1;
2396 if (ze==mz) lze = ze-1;
2397
2398 PetscReal epsilon=1.e-8;
2399 PetscReal ***nvert, ibmval=1.9999;
2400
2401 struct Components {
2402 PetscReal x;
2403 PetscReal y;
2404 PetscReal z;
2405 }***ucor, ***csi, ***eta, ***zet;
2406
2407
2408 PetscInt xend=mx-2 ,yend=my-2,zend=mz-2;
2409
2410 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC) xend=mx-1;
2411 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC) yend=my-1;
2412 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC) zend=mz-1;
2413
2414 DMDAVecGetArray(fda, user->Ucont, &ucor);
2415 DMDAVecGetArray(fda, user->lCsi, &csi);
2416 DMDAVecGetArray(fda, user->lEta, &eta);
2417 DMDAVecGetArray(fda, user->lZet, &zet);
2418 DMDAVecGetArray(da, user->lNvert, &nvert);
2419
2420 PetscReal libm_Flux, libm_area, libm_Flux_abs=0., ibm_Flux_abs;
2421 libm_Flux = 0;
2422 libm_area = 0;
2423
2424 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Entering VolumeFlux to enforce no-penetration condition.\n");
2425
2426 //Mohsen March 2017
2427 PetscReal *lIB_Flux = NULL, *lIB_area = NULL, *IB_Flux = NULL, *IB_Area = NULL;
2428 if (NumberOfBodies > 1) {
2429
2430 lIB_Flux=(PetscReal *)calloc(NumberOfBodies,sizeof(PetscReal));
2431 lIB_area=(PetscReal *)calloc(NumberOfBodies,sizeof(PetscReal));
2432 IB_Flux=(PetscReal *)calloc(NumberOfBodies,sizeof(PetscReal));
2433 IB_Area=(PetscReal *)calloc(NumberOfBodies,sizeof(PetscReal));
2434
2435
2436 for (ibi=0; ibi<NumberOfBodies; ibi++) {
2437 lIB_Flux[ibi]=0.0;
2438 lIB_area[ibi]=0.0;
2439 IB_Flux[ibi]=0.0;
2440 IB_Area[ibi]=0.0;
2441 }
2442 }
2443
2444
2445 //================================================================================
2446 // PASS 1: Calculate Uncorrected Flux and Area
2447 // This pass measures the total fluid "leakage" across the immersed boundary.
2448 //================================================================================
2449 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Pass 1: Measuring uncorrected flux and area.\n");
2450
2451 for (k=lzs; k<lze; k++) {
2452 for (j=lys; j<lye; j++) {
2453 for (i=lxs; i<lxe; i++) {
2454 if (nvert[k][j][i] < 0.1) {
2455 if (nvert[k][j][i+1] > 0.1 && nvert[k][j][i+1] < ibmval && i < xend) {
2456
2457 if (fabs(ucor[k][j][i].x)>epsilon) {
2458 libm_Flux += ucor[k][j][i].x;
2459 if (flg==3)
2460 libm_Flux_abs += fabs(ucor[k][j][i].x)/sqrt(csi[k][j][i].x * csi[k][j][i].x +
2461 csi[k][j][i].y * csi[k][j][i].y +
2462 csi[k][j][i].z * csi[k][j][i].z);
2463 else
2464 libm_Flux_abs += fabs(ucor[k][j][i].x);
2465
2466 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2467 csi[k][j][i].y * csi[k][j][i].y +
2468 csi[k][j][i].z * csi[k][j][i].z);
2469
2470 if (NumberOfBodies > 1) {
2471
2472 ibi=(int)((nvert[k][j][i+1]-1.0)*1001);
2473 lIB_Flux[ibi] += ucor[k][j][i].x;
2474 lIB_area[ibi] += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2475 csi[k][j][i].y * csi[k][j][i].y +
2476 csi[k][j][i].z * csi[k][j][i].z);
2477 }
2478 } else
2479 ucor[k][j][i].x=0.;
2480
2481 }
2482 if (nvert[k][j+1][i] > 0.1 && nvert[k][j+1][i] < ibmval && j < yend) {
2483
2484 if (fabs(ucor[k][j][i].y)>epsilon) {
2485 libm_Flux += ucor[k][j][i].y;
2486 if (flg==3)
2487 libm_Flux_abs += fabs(ucor[k][j][i].y)/sqrt(eta[k][j][i].x * eta[k][j][i].x +
2488 eta[k][j][i].y * eta[k][j][i].y +
2489 eta[k][j][i].z * eta[k][j][i].z);
2490 else
2491 libm_Flux_abs += fabs(ucor[k][j][i].y);
2492 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2493 eta[k][j][i].y * eta[k][j][i].y +
2494 eta[k][j][i].z * eta[k][j][i].z);
2495 if (NumberOfBodies > 1) {
2496
2497 ibi=(int)((nvert[k][j+1][i]-1.0)*1001);
2498
2499 lIB_Flux[ibi] += ucor[k][j][i].y;
2500 lIB_area[ibi] += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2501 eta[k][j][i].y * eta[k][j][i].y +
2502 eta[k][j][i].z * eta[k][j][i].z);
2503 }
2504 } else
2505 ucor[k][j][i].y=0.;
2506 }
2507 if (nvert[k+1][j][i] > 0.1 && nvert[k+1][j][i] < ibmval && k < zend) {
2508
2509 if (fabs(ucor[k][j][i].z)>epsilon) {
2510 libm_Flux += ucor[k][j][i].z;
2511 if (flg==3)
2512 libm_Flux_abs += fabs(ucor[k][j][i].z)/sqrt(zet[k][j][i].x * zet[k][j][i].x +
2513 zet[k][j][i].y * zet[k][j][i].y +
2514 zet[k][j][i].z * zet[k][j][i].z);
2515 else
2516 libm_Flux_abs += fabs(ucor[k][j][i].z);
2517 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2518 zet[k][j][i].y * zet[k][j][i].y +
2519 zet[k][j][i].z * zet[k][j][i].z);
2520
2521 if (NumberOfBodies > 1) {
2522
2523 ibi=(int)((nvert[k+1][j][i]-1.0)*1001);
2524 lIB_Flux[ibi] += ucor[k][j][i].z;
2525 lIB_area[ibi] += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2526 zet[k][j][i].y * zet[k][j][i].y +
2527 zet[k][j][i].z * zet[k][j][i].z);
2528 }
2529 }else
2530 ucor[k][j][i].z=0.;
2531 }
2532 }
2533
2534 if (nvert[k][j][i] > 0.1 && nvert[k][j][i] < ibmval) {
2535
2536 if (nvert[k][j][i+1] < 0.1 && i < xend) {
2537 if (fabs(ucor[k][j][i].x)>epsilon) {
2538 libm_Flux -= ucor[k][j][i].x;
2539 if (flg==3)
2540 libm_Flux_abs += fabs(ucor[k][j][i].x)/sqrt(csi[k][j][i].x * csi[k][j][i].x +
2541 csi[k][j][i].y * csi[k][j][i].y +
2542 csi[k][j][i].z * csi[k][j][i].z);
2543 else
2544 libm_Flux_abs += fabs(ucor[k][j][i].x);
2545 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2546 csi[k][j][i].y * csi[k][j][i].y +
2547 csi[k][j][i].z * csi[k][j][i].z);
2548 if (NumberOfBodies > 1) {
2549
2550 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2551 lIB_Flux[ibi] -= ucor[k][j][i].x;
2552 lIB_area[ibi] += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2553 csi[k][j][i].y * csi[k][j][i].y +
2554 csi[k][j][i].z * csi[k][j][i].z);
2555 }
2556
2557 }else
2558 ucor[k][j][i].x=0.;
2559 }
2560 if (nvert[k][j+1][i] < 0.1 && j < yend) {
2561 if (fabs(ucor[k][j][i].y)>epsilon) {
2562 libm_Flux -= ucor[k][j][i].y;
2563 if (flg==3)
2564 libm_Flux_abs += fabs(ucor[k][j][i].y)/ sqrt(eta[k][j][i].x * eta[k][j][i].x +
2565 eta[k][j][i].y * eta[k][j][i].y +
2566 eta[k][j][i].z * eta[k][j][i].z);
2567 else
2568 libm_Flux_abs += fabs(ucor[k][j][i].y);
2569 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2570 eta[k][j][i].y * eta[k][j][i].y +
2571 eta[k][j][i].z * eta[k][j][i].z);
2572 if (NumberOfBodies > 1) {
2573
2574 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2575 lIB_Flux[ibi] -= ucor[k][j][i].y;
2576 lIB_area[ibi] += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2577 eta[k][j][i].y * eta[k][j][i].y +
2578 eta[k][j][i].z * eta[k][j][i].z);
2579 }
2580 }else
2581 ucor[k][j][i].y=0.;
2582 }
2583 if (nvert[k+1][j][i] < 0.1 && k < zend) {
2584 if (fabs(ucor[k][j][i].z)>epsilon) {
2585 libm_Flux -= ucor[k][j][i].z;
2586 if (flg==3)
2587 libm_Flux_abs += fabs(ucor[k][j][i].z)/sqrt(zet[k][j][i].x * zet[k][j][i].x +
2588 zet[k][j][i].y * zet[k][j][i].y +
2589 zet[k][j][i].z * zet[k][j][i].z);
2590 else
2591 libm_Flux_abs += fabs(ucor[k][j][i].z);
2592 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2593 zet[k][j][i].y * zet[k][j][i].y +
2594 zet[k][j][i].z * zet[k][j][i].z);
2595 if (NumberOfBodies > 1) {
2596
2597 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2598 lIB_Flux[ibi] -= ucor[k][j][i].z;
2599 lIB_area[ibi] += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2600 zet[k][j][i].y * zet[k][j][i].y +
2601 zet[k][j][i].z * zet[k][j][i].z);
2602 }
2603 }else
2604 ucor[k][j][i].z=0.;
2605 }
2606 }
2607
2608 }
2609 }
2610 }
2611
2612 ierr = MPI_Allreduce(&libm_Flux, ibm_Flux,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2613 ierr = MPI_Allreduce(&libm_Flux_abs, &ibm_Flux_abs,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2614 ierr = MPI_Allreduce(&libm_area, ibm_Area,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2615
2616 if (NumberOfBodies > 1) {
2617 ierr = MPI_Allreduce(lIB_Flux,IB_Flux,NumberOfBodies,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
2618 ierr = MPI_Allreduce(lIB_area,IB_Area,NumberOfBodies,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
2619 }
2620
2621 PetscReal correction;
2622
2623 PetscReal *Correction = NULL;
2624 if (NumberOfBodies > 1) {
2625 Correction=(PetscReal *)calloc(NumberOfBodies,sizeof(PetscReal));
2626 for (ibi=0; ibi<NumberOfBodies; ibi++) Correction[ibi]=0.0;
2627 }
2628
2629 if (*ibm_Area > 1.e-15) {
2630 if (flg>1)
2631 correction = (*ibm_Flux + user->FluxIntpSum)/ ibm_Flux_abs;
2632 else if (flg)
2633 correction = (*ibm_Flux + user->FluxIntpSum) / *ibm_Area;
2634 else
2635 correction = *ibm_Flux / *ibm_Area;
2636 if (NumberOfBodies > 1)
2637 for (ibi=0; ibi<NumberOfBodies; ibi++) if (IB_Area[ibi]>1.e-15) Correction[ibi] = IB_Flux[ibi] / IB_Area[ibi];
2638 }
2639 else {
2640 correction = 0;
2641 }
2642 // --- Log the uncorrected results and calculated correction ---
2643 LOG_ALLOW(GLOBAL, LOG_INFO, "IBM Uncorrected Flux: %g, Area: %g, Correction: %g\n", *ibm_Flux, *ibm_Area, correction);
2644 if (NumberOfBodies>1){
2645 for (ibi=0; ibi<NumberOfBodies; ibi++) LOG_ALLOW(GLOBAL, LOG_INFO, " [Body %d] Uncorrected Flux: %g, Area: %g, Correction: %g\n", ibi, IB_Flux[ibi], IB_Area[ibi], Correction[ibi]);
2646 }
2647
2648 //================================================================================
2649 // PASS 2: Apply Correction to Velocity Field
2650 // This pass modifies the velocity at the boundary to enforce zero net flux.
2651 //================================================================================
2652 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Pass 2: Applying velocity corrections at the boundary.\n");
2653
2654 for (k=lzs; k<lze; k++) {
2655 for (j=lys; j<lye; j++) {
2656 for (i=lxs; i<lxe; i++) {
2657 if (nvert[k][j][i] < 0.1) {
2658 if (nvert[k][j][i+1] > 0.1 && nvert[k][j][i+1] <ibmval && i < xend) {
2659 if (fabs(ucor[k][j][i].x)>epsilon){
2660 if (flg==3)
2661 ucor[k][j][i].x -=correction*fabs(ucor[k][j][i].x)/
2662 sqrt(csi[k][j][i].x * csi[k][j][i].x +
2663 csi[k][j][i].y * csi[k][j][i].y +
2664 csi[k][j][i].z * csi[k][j][i].z);
2665 else if (flg==2)
2666 ucor[k][j][i].x -=correction*fabs(ucor[k][j][i].x);
2667 else if (NumberOfBodies > 1) {
2668 ibi=(int)((nvert[k][j][i+1]-1.0)*1001);
2669 ucor[k][j][i].x -= sqrt(csi[k][j][i].x * csi[k][j][i].x +
2670 csi[k][j][i].y * csi[k][j][i].y +
2671 csi[k][j][i].z * csi[k][j][i].z) *
2672 Correction[ibi];
2673 }
2674 else
2675 ucor[k][j][i].x -= sqrt(csi[k][j][i].x * csi[k][j][i].x +
2676 csi[k][j][i].y * csi[k][j][i].y +
2677 csi[k][j][i].z * csi[k][j][i].z) *
2678 correction;
2679 }
2680 }
2681 if (nvert[k][j+1][i] > 0.1 && nvert[k][j+1][i] < ibmval && j < yend) {
2682 if (fabs(ucor[k][j][i].y)>epsilon) {
2683 if (flg==3)
2684 ucor[k][j][i].y -=correction*fabs(ucor[k][j][i].y)/
2685 sqrt(eta[k][j][i].x * eta[k][j][i].x +
2686 eta[k][j][i].y * eta[k][j][i].y +
2687 eta[k][j][i].z * eta[k][j][i].z);
2688 else if (flg==2)
2689 ucor[k][j][i].y -=correction*fabs(ucor[k][j][i].y);
2690 else if (NumberOfBodies > 1) {
2691 ibi=(int)((nvert[k][j+1][i]-1.0)*1001);
2692 ucor[k][j][i].y -= sqrt(eta[k][j][i].x * eta[k][j][i].x +
2693 eta[k][j][i].y * eta[k][j][i].y +
2694 eta[k][j][i].z * eta[k][j][i].z) *
2695 Correction[ibi];
2696 }
2697 else
2698 ucor[k][j][i].y -= sqrt(eta[k][j][i].x * eta[k][j][i].x +
2699 eta[k][j][i].y * eta[k][j][i].y +
2700 eta[k][j][i].z * eta[k][j][i].z) *
2701 correction;
2702 }
2703 }
2704 if (nvert[k+1][j][i] > 0.1 && nvert[k+1][j][i] < ibmval && k < zend) {
2705 if (fabs(ucor[k][j][i].z)>epsilon) {
2706 if (flg==3)
2707 ucor[k][j][i].z -= correction*fabs(ucor[k][j][i].z)/
2708 sqrt(zet[k][j][i].x * zet[k][j][i].x +
2709 zet[k][j][i].y * zet[k][j][i].y +
2710 zet[k][j][i].z * zet[k][j][i].z);
2711 else if (flg==2)
2712 ucor[k][j][i].z -= correction*fabs(ucor[k][j][i].z);
2713 else if (NumberOfBodies > 1) {
2714 ibi=(int)((nvert[k+1][j][i]-1.0)*1001);
2715 ucor[k][j][i].z -= sqrt(zet[k][j][i].x * zet[k][j][i].x +
2716 zet[k][j][i].y * zet[k][j][i].y +
2717 zet[k][j][i].z * zet[k][j][i].z) *
2718 Correction[ibi];
2719 }
2720 else
2721 ucor[k][j][i].z -= sqrt(zet[k][j][i].x * zet[k][j][i].x +
2722 zet[k][j][i].y * zet[k][j][i].y +
2723 zet[k][j][i].z * zet[k][j][i].z) *
2724 correction;
2725 }
2726 }
2727 }
2728
2729 if (nvert[k][j][i] > 0.1 && nvert[k][j][i] < ibmval) {
2730 if (nvert[k][j][i+1] < 0.1 && i < xend) {
2731 if (fabs(ucor[k][j][i].x)>epsilon) {
2732 if (flg==3)
2733 ucor[k][j][i].x += correction*fabs(ucor[k][j][i].x)/
2734 sqrt(csi[k][j][i].x * csi[k][j][i].x +
2735 csi[k][j][i].y * csi[k][j][i].y +
2736 csi[k][j][i].z * csi[k][j][i].z);
2737 else if (flg==2)
2738 ucor[k][j][i].x += correction*fabs(ucor[k][j][i].x);
2739 else if (NumberOfBodies > 1) {
2740 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2741 ucor[k][j][i].x += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2742 csi[k][j][i].y * csi[k][j][i].y +
2743 csi[k][j][i].z * csi[k][j][i].z) *
2744 Correction[ibi];
2745 }
2746 else
2747 ucor[k][j][i].x += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2748 csi[k][j][i].y * csi[k][j][i].y +
2749 csi[k][j][i].z * csi[k][j][i].z) *
2750 correction;
2751 }
2752 }
2753 if (nvert[k][j+1][i] < 0.1 && j < yend) {
2754 if (fabs(ucor[k][j][i].y)>epsilon) {
2755 if (flg==3)
2756 ucor[k][j][i].y +=correction*fabs(ucor[k][j][i].y)/
2757 sqrt(eta[k][j][i].x * eta[k][j][i].x +
2758 eta[k][j][i].y * eta[k][j][i].y +
2759 eta[k][j][i].z * eta[k][j][i].z);
2760 else if (flg==2)
2761 ucor[k][j][i].y +=correction*fabs(ucor[k][j][i].y);
2762 else if (NumberOfBodies > 1) {
2763 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2764 ucor[k][j][i].y += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2765 eta[k][j][i].y * eta[k][j][i].y +
2766 eta[k][j][i].z * eta[k][j][i].z) *
2767 Correction[ibi];
2768 }
2769 else
2770 ucor[k][j][i].y += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2771 eta[k][j][i].y * eta[k][j][i].y +
2772 eta[k][j][i].z * eta[k][j][i].z) *
2773 correction;
2774 }
2775 }
2776 if (nvert[k+1][j][i] < 0.1 && k < zend) {
2777 if (fabs(ucor[k][j][i].z)>epsilon) {
2778 if (flg==3)
2779 ucor[k][j][i].z += correction*fabs(ucor[k][j][i].z)/
2780 sqrt(zet[k][j][i].x * zet[k][j][i].x +
2781 zet[k][j][i].y * zet[k][j][i].y +
2782 zet[k][j][i].z * zet[k][j][i].z);
2783 else if (flg==2)
2784 ucor[k][j][i].z += correction*fabs(ucor[k][j][i].z);
2785 else if (NumberOfBodies > 1) {
2786 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2787 ucor[k][j][i].z += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2788 zet[k][j][i].y * zet[k][j][i].y +
2789 zet[k][j][i].z * zet[k][j][i].z) *
2790 Correction[ibi];
2791 }
2792 else
2793 ucor[k][j][i].z += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2794 zet[k][j][i].y * zet[k][j][i].y +
2795 zet[k][j][i].z * zet[k][j][i].z) *
2796 correction;
2797 }
2798 }
2799 }
2800
2801 }
2802 }
2803 }
2804
2805 //================================================================================
2806 // PASS 3: Verification
2807 // This optional pass recalculates the flux to confirm the correction was successful.
2808 //================================================================================
2809 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Pass 3: Verifying corrected flux.\n");
2810
2811 libm_Flux = 0;
2812 libm_area = 0;
2813 for (k=lzs; k<lze; k++) {
2814 for (j=lys; j<lye; j++) {
2815 for (i=lxs; i<lxe; i++) {
2816 if (nvert[k][j][i] < 0.1) {
2817 if (nvert[k][j][i+1] > 0.1 && nvert[k][j][i+1] < ibmval && i < xend) {
2818 libm_Flux += ucor[k][j][i].x;
2819 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2820 csi[k][j][i].y * csi[k][j][i].y +
2821 csi[k][j][i].z * csi[k][j][i].z);
2822
2823 }
2824 if (nvert[k][j+1][i] > 0.1 && nvert[k][j+1][i] < ibmval && j < yend) {
2825 libm_Flux += ucor[k][j][i].y;
2826 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2827 eta[k][j][i].y * eta[k][j][i].y +
2828 eta[k][j][i].z * eta[k][j][i].z);
2829 }
2830 if (nvert[k+1][j][i] > 0.1 && nvert[k+1][j][i] < ibmval && k < zend) {
2831 libm_Flux += ucor[k][j][i].z;
2832 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2833 zet[k][j][i].y * zet[k][j][i].y +
2834 zet[k][j][i].z * zet[k][j][i].z);
2835 }
2836 }
2837
2838 if (nvert[k][j][i] > 0.1 && nvert[k][j][i] < ibmval) {
2839 if (nvert[k][j][i+1] < 0.1 && i < xend) {
2840 libm_Flux -= ucor[k][j][i].x;
2841 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2842 csi[k][j][i].y * csi[k][j][i].y +
2843 csi[k][j][i].z * csi[k][j][i].z);
2844
2845 }
2846 if (nvert[k][j+1][i] < 0.1 && j < yend) {
2847 libm_Flux -= ucor[k][j][i].y;
2848 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2849 eta[k][j][i].y * eta[k][j][i].y +
2850 eta[k][j][i].z * eta[k][j][i].z);
2851 }
2852 if (nvert[k+1][j][i] < 0.1 && k < zend) {
2853 libm_Flux -= ucor[k][j][i].z;
2854 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2855 zet[k][j][i].y * zet[k][j][i].y +
2856 zet[k][j][i].z * zet[k][j][i].z);
2857 }
2858 }
2859
2860 }
2861 }
2862 }
2863
2864 ierr = MPI_Allreduce(&libm_Flux, ibm_Flux,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2865 ierr = MPI_Allreduce(&libm_area, ibm_Area,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2866
2867 /* PetscGlobalSum(&libm_Flux, ibm_Flux, PETSC_COMM_WORLD); */
2868/* PetscGlobalSum(&libm_area, ibm_Area, PETSC_COMM_WORLD); */
2869 LOG_ALLOW(GLOBAL, LOG_INFO, "IBM Corrected (Verified) Flux: %g, Area: %g\n", *ibm_Flux, *ibm_Area);
2870
2871
2873 if (xe==mx){
2874 i=mx-2;
2875 for (k=lzs; k<lze; k++) {
2876 for (j=lys; j<lye; j++) {
2877 // if(j>0 && k>0 && j<user->JM && k<user->KM){
2878 if ((nvert[k][j][i]>ibmval && nvert[k][j][i+1]<0.1) || (nvert[k][j][i]<0.1 && nvert[k][j][i+1]>ibmval)) ucor[k][j][i].x=0.0;
2879
2880 // }
2881 }
2882 }
2883 }
2884 }
2885
2887 if (ye==my){
2888 j=my-2;
2889 for (k=lzs; k<lze; k++) {
2890 for (i=lxs; i<lxe; i++) {
2891 // if(i>0 && k>0 && i<user->IM && k<user->KM){
2892 if ((nvert[k][j][i]>ibmval && nvert[k][j+1][i]<0.1) || (nvert[k][j][i]<0.1 && nvert[k][j+1][i]>ibmval)) ucor[k][j][i].y=0.0;
2893 // }
2894 }
2895 }
2896 }
2897 }
2898
2900 if (ze==mz){
2901 k=mz-2;
2902 for (j=lys; j<lye; j++) {
2903 for (i=lxs; i<lxe; i++) {
2904 // if(i>0 && j>0 && i<user->IM && j<user->JM){
2905 if ((nvert[k][j][i]>ibmval && nvert[k+1][j][i]<0.1) || (nvert[k][j][i]<0.1 && nvert[k+1][j][i]>ibmval)) ucor[k][j][i].z=0.0;
2906 // }
2907 }
2908 }
2909 }
2910 }
2911
2912
2913 DMDAVecRestoreArray(da, user->lNvert, &nvert);
2914 DMDAVecRestoreArray(fda, user->lCsi, &csi);
2915 DMDAVecRestoreArray(fda, user->lEta, &eta);
2916 DMDAVecRestoreArray(fda, user->lZet, &zet);
2917 DMDAVecRestoreArray(fda, user->Ucont, &ucor);
2918
2919 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
2920 ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields); CHKERRQ(ierr);
2921
2922 if (NumberOfBodies > 1) {
2923 free(lIB_Flux);
2924 free(lIB_area);
2925 free(IB_Flux);
2926 free(IB_Area);
2927 free(Correction);
2928 }
2929
2930 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Exiting VolumeFlux.\n");
2931
2932 return 0;
2933}
PetscInt NumberOfBodies
Definition variables.h:781
PetscReal FluxIntpSum
Definition variables.h:936
Here is the call graph for this function:
Here is the caller graph for this function:

◆ VolumeFlux_rev()

PetscErrorCode VolumeFlux_rev ( UserCtx user,
PetscReal *  ibm_Flux,
PetscReal *  ibm_Area,
PetscInt  flg 
)
extern

A specialized version of VolumeFlux, likely for reversed normals.

Parameters
userThe UserCtx for the grid level.
ibm_Flux(Output) The calculated net flux.
ibm_Area(Output) The total surface area of the IB.
flgA flag controlling the correction behavior.
Returns
PetscErrorCode 0 on success.

A specialized version of VolumeFlux, likely for reversed normals.

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

See also
VolumeFlux_rev()

Definition at line 2121 of file poisson.c.

2123{
2124 PetscErrorCode ierr;
2125
2126 DM da = user->da, fda = user->fda;
2127
2128 DMDALocalInfo info = user->info;
2129
2130 PetscInt xs = info.xs, xe = info.xs + info.xm;
2131 PetscInt ys = info.ys, ye = info.ys + info.ym;
2132 PetscInt zs = info.zs, ze = info.zs + info.zm;
2133 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2134
2135 PetscInt i, j, k;
2136 PetscInt lxs, lys, lzs, lxe, lye, lze;
2137
2138 lxs = xs; lxe = xe;
2139 lys = ys; lye = ye;
2140 lzs = zs; lze = ze;
2141
2142 if (xs==0) lxs = xs+1;
2143 if (ys==0) lys = ys+1;
2144 if (zs==0) lzs = zs+1;
2145
2146 if (xe==mx) lxe = xe-1;
2147 if (ye==my) lye = ye-1;
2148 if (ze==mz) lze = ze-1;
2149
2150 PetscReal ***nvert, ibmval=1.5;
2151 Cmpnts ***ucor, ***csi, ***eta, ***zet;
2152 DMDAVecGetArray(fda, user->Ucont, &ucor);
2153 DMDAVecGetArray(fda, user->lCsi, &csi);
2154 DMDAVecGetArray(fda, user->lEta, &eta);
2155 DMDAVecGetArray(fda, user->lZet, &zet);
2156 DMDAVecGetArray(da, user->lNvert, &nvert);
2157
2158 PetscReal libm_Flux, libm_area;
2159 libm_Flux = 0;
2160 libm_area = 0;
2161 for (k=lzs; k<lze; k++) {
2162 for (j=lys; j<lye; j++) {
2163 for (i=lxs; i<lxe; i++) {
2164 if (nvert[k][j][i] < 0.1) {
2165 if (nvert[k][j][i+1] > ibmval-0.4 && nvert[k][j][i+1] < ibmval && i < mx-2) {
2166 libm_Flux += ucor[k][j][i].x;
2167 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2168 csi[k][j][i].y * csi[k][j][i].y +
2169 csi[k][j][i].z * csi[k][j][i].z);
2170
2171 }
2172 if (nvert[k][j+1][i] > ibmval-0.4 && nvert[k][j+1][i] < ibmval && j < my-2) {
2173 libm_Flux += ucor[k][j][i].y;
2174 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2175 eta[k][j][i].y * eta[k][j][i].y +
2176 eta[k][j][i].z * eta[k][j][i].z);
2177 }
2178 if (nvert[k+1][j][i] > ibmval-0.4 && nvert[k+1][j][i] < ibmval && k < mz-2) {
2179 libm_Flux += ucor[k][j][i].z;
2180 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2181 zet[k][j][i].y * zet[k][j][i].y +
2182 zet[k][j][i].z * zet[k][j][i].z);
2183 }
2184 }
2185
2186 if (nvert[k][j][i] > ibmval-0.4 && nvert[k][j][i] < ibmval) {
2187 if (nvert[k][j][i+1] < 0.1 && i < mx-2) {
2188 libm_Flux -= ucor[k][j][i].x;
2189 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2190 csi[k][j][i].y * csi[k][j][i].y +
2191 csi[k][j][i].z * csi[k][j][i].z);
2192
2193 }
2194 if (nvert[k][j+1][i] < 0.1 && j < my-2) {
2195 libm_Flux -= ucor[k][j][i].y;
2196 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2197 eta[k][j][i].y * eta[k][j][i].y +
2198 eta[k][j][i].z * eta[k][j][i].z);
2199 }
2200 if (nvert[k+1][j][i] < 0.1 && k < mz-2) {
2201 libm_Flux -= ucor[k][j][i].z;
2202 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2203 zet[k][j][i].y * zet[k][j][i].y +
2204 zet[k][j][i].z * zet[k][j][i].z);
2205 }
2206 }
2207
2208 }
2209 }
2210 }
2211
2212 ierr = MPI_Allreduce(&libm_Flux, ibm_Flux,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2213 ierr = MPI_Allreduce(&libm_area, ibm_Area,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2214
2215 /* PetscGlobalSum(&libm_Flux, ibm_Flux, PETSC_COMM_WORLD); */
2216/* PetscGlobalSum(&libm_area, ibm_Area, PETSC_COMM_WORLD); */
2217 LOG_ALLOW(GLOBAL, LOG_DEBUG, "IBM flux correction: flux=%le, area=%le\n", *ibm_Flux, *ibm_Area);
2218
2219 PetscReal correction;
2220
2221 if (*ibm_Area > 1.e-15) {
2222 if (flg)
2223 correction = (*ibm_Flux + user->FluxIntpSum) / *ibm_Area;
2224 else
2225 correction = *ibm_Flux / *ibm_Area;
2226 }
2227 else {
2228 correction = 0;
2229 }
2230
2231 for (k=lzs; k<lze; k++) {
2232 for (j=lys; j<lye; j++) {
2233 for (i=lxs; i<lxe; i++) {
2234 if (nvert[k][j][i] < 0.1) {
2235 if (nvert[k][j][i+1] > ibmval-0.4 && nvert[k][j][i+1] < ibmval && i < mx-2) {
2236 ucor[k][j][i].x -= sqrt(csi[k][j][i].x * csi[k][j][i].x +
2237 csi[k][j][i].y * csi[k][j][i].y +
2238 csi[k][j][i].z * csi[k][j][i].z) *
2239 correction;
2240
2241 }
2242 if (nvert[k][j+1][i] > ibmval-0.4 && nvert[k][j+1][i] < ibmval && j < my-2) {
2243 ucor[k][j][i].y -= sqrt(eta[k][j][i].x * eta[k][j][i].x +
2244 eta[k][j][i].y * eta[k][j][i].y +
2245 eta[k][j][i].z * eta[k][j][i].z) *
2246 correction;
2247 }
2248 if (nvert[k+1][j][i] > ibmval-0.4 && nvert[k+1][j][i] < ibmval && k < mz-2) {
2249 ucor[k][j][i].z -= sqrt(zet[k][j][i].x * zet[k][j][i].x +
2250 zet[k][j][i].y * zet[k][j][i].y +
2251 zet[k][j][i].z * zet[k][j][i].z) *
2252 correction;
2253 }
2254 }
2255
2256 if (nvert[k][j][i] > ibmval-0.4 && nvert[k][j][i] < ibmval) {
2257 if (nvert[k][j][i+1] < 0.1 && i < mx-2) {
2258 ucor[k][j][i].x += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2259 csi[k][j][i].y * csi[k][j][i].y +
2260 csi[k][j][i].z * csi[k][j][i].z) *
2261 correction;
2262
2263 }
2264 if (nvert[k][j+1][i] < 0.1 && j < my-2) {
2265 ucor[k][j][i].y += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2266 eta[k][j][i].y * eta[k][j][i].y +
2267 eta[k][j][i].z * eta[k][j][i].z) *
2268 correction;
2269 }
2270 if (nvert[k+1][j][i] < 0.1 && k < mz-2) {
2271 ucor[k][j][i].z += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2272 zet[k][j][i].y * zet[k][j][i].y +
2273 zet[k][j][i].z * zet[k][j][i].z) *
2274 correction;
2275 }
2276 }
2277
2278 }
2279 }
2280 }
2281
2282
2283
2284 libm_Flux = 0;
2285 libm_area = 0;
2286 for (k=lzs; k<lze; k++) {
2287 for (j=lys; j<lye; j++) {
2288 for (i=lxs; i<lxe; i++) {
2289 if (nvert[k][j][i] < 0.1) {
2290 if (nvert[k][j][i+1] > ibmval-0.4 && nvert[k][j][i+1] < ibmval && i < mx-2) {
2291 libm_Flux += ucor[k][j][i].x;
2292 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2293 csi[k][j][i].y * csi[k][j][i].y +
2294 csi[k][j][i].z * csi[k][j][i].z);
2295
2296 }
2297 if (nvert[k][j+1][i] > ibmval-0.4 && nvert[k][j+1][i] < ibmval && j < my-2) {
2298 libm_Flux += ucor[k][j][i].y;
2299 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2300 eta[k][j][i].y * eta[k][j][i].y +
2301 eta[k][j][i].z * eta[k][j][i].z);
2302 }
2303 if (nvert[k+1][j][i] > ibmval-0.4 && nvert[k+1][j][i] < ibmval && k < mz-2) {
2304 libm_Flux += ucor[k][j][i].z;
2305 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2306 zet[k][j][i].y * zet[k][j][i].y +
2307 zet[k][j][i].z * zet[k][j][i].z);
2308 }
2309 }
2310
2311 if (nvert[k][j][i] > ibmval-0.4 && nvert[k][j][i] < ibmval) {
2312 if (nvert[k][j][i+1] < 0.1 && i < mx-2) {
2313 libm_Flux -= ucor[k][j][i].x;
2314 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2315 csi[k][j][i].y * csi[k][j][i].y +
2316 csi[k][j][i].z * csi[k][j][i].z);
2317
2318 }
2319 if (nvert[k][j+1][i] < 0.1 && j < my-2) {
2320 libm_Flux -= ucor[k][j][i].y;
2321 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2322 eta[k][j][i].y * eta[k][j][i].y +
2323 eta[k][j][i].z * eta[k][j][i].z);
2324 }
2325 if (nvert[k+1][j][i] < 0.1 && k < mz-2) {
2326 libm_Flux -= ucor[k][j][i].z;
2327 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2328 zet[k][j][i].y * zet[k][j][i].y +
2329 zet[k][j][i].z * zet[k][j][i].z);
2330 }
2331 }
2332
2333 }
2334 }
2335 }
2336
2337 ierr = MPI_Allreduce(&libm_Flux, ibm_Flux,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2338 ierr = MPI_Allreduce(&libm_area, ibm_Area,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2339
2340 /* PetscGlobalSum(&libm_Flux, ibm_Flux, PETSC_COMM_WORLD); */
2341/* PetscGlobalSum(&libm_area, ibm_Area, PETSC_COMM_WORLD); */
2342 LOG_ALLOW(GLOBAL, LOG_DEBUG, "IBM flux measurement: flux=%le, area=%le\n", *ibm_Flux, *ibm_Area);
2343
2344 DMDAVecRestoreArray(da, user->lNvert, &nvert);
2345 DMDAVecRestoreArray(fda, user->lCsi, &csi);
2346 DMDAVecRestoreArray(fda, user->lEta, &eta);
2347 DMDAVecRestoreArray(fda, user->lZet, &zet);
2348 DMDAVecRestoreArray(fda, user->Ucont, &ucor);
2349
2350 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
2351 ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields); CHKERRQ(ierr);
2352 return 0;
2353}
Here is the call graph for this function:
Here is the caller graph for this function: