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 3344 of file poisson.c.

3345{
3346 // --- CONTEXT ACQUISITION BLOCK ---
3347 // Get the master simulation context from the first block's UserCtx on the finest level.
3348 // This provides access to all former global variables.
3349 SimCtx *simCtx = usermg->mgctx[0].user[0].simCtx;
3350
3351 // Create local variables to mirror the legacy globals for minimal code changes.
3352 const PetscInt block_number = simCtx->block_number;
3353 const PetscInt immersed = simCtx->immersed;
3354 const PetscInt MHV = simCtx->MHV;
3355 const PetscInt LV = simCtx->LV;
3356 PetscMPIInt rank = simCtx->rank;
3357 // --- END CONTEXT ACQUISITION BLOCK ---
3358
3359 PetscErrorCode ierr;
3360 PetscInt l, bi;
3361 MGCtx *mgctx = usermg->mgctx;
3362 KSP mgksp, subksp;
3363 PC mgpc, subpc;
3364 UserCtx *user;
3365
3366 PetscFunctionBeginUser; // Moved to after variable declarations
3368 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting Multigrid Poisson Solve...\n");
3369
3370 for (bi = 0; bi < block_number; bi++) {
3371
3372 // ====================================================================
3373 // SECTION: Immersed Boundary Specific Setup (Conditional)
3374 // ====================================================================
3375 if (immersed) {
3376 LOG_ALLOW(LOCAL, LOG_DEBUG, "Block %d: Performing IBM pre-solve setup (Nvert restriction, etc.).\n", bi);
3377 for (l = usermg->mglevels - 1; l > 0; l--) {
3378 mgctx[l].user[bi].multinullspace = PETSC_FALSE;
3379 MyNvertRestriction(&mgctx[l].user[bi], &mgctx[l-1].user[bi]);
3380 }
3381 // Coarsest level check for disconnected domains due to IBM
3382 l = 0;
3383 user = mgctx[l].user;
3384 ierr = PetscMalloc1(user[bi].info.mx * user[bi].info.my * 2, &user[bi].KSKE); CHKERRQ(ierr);
3385 FullyBlocked(&user[bi]);
3386 }
3387
3388
3389 l = usermg->mglevels - 1;
3390 user = mgctx[l].user;
3391
3392 // We are solving the linear system AX=B where A = Laplacian Operator Matrix; X = Unknown Phi (Pressure Correction) and B = RHS (Flux Divergence based)
3393
3394 // --- 1. Compute RHS of the Poisson Equation ---
3395 LOG_ALLOW(LOCAL, LOG_DEBUG, "Block %d: Computing Poisson RHS...\n", bi);
3396 ierr = VecDuplicate(user[bi].P, &user[bi].B); CHKERRQ(ierr);
3397
3398 PetscReal ibm_Flux, ibm_Area;
3399 PetscInt flg = immersed - 1;
3400
3401 // Calculate volume flux source terms (often from IBM)
3402 VolumeFlux(&user[bi], &ibm_Flux, &ibm_Area, flg);
3403 if (MHV || LV) {
3404 flg = ((MHV > 1 || LV) && bi == 0) ? 1 : 0;
3405 VolumeFlux_rev(&user[bi], &ibm_Flux, &ibm_Area, flg);
3406 }
3407 // Calculate the main flux divergence term B.
3408 PoissonRHS(&user[bi], user[bi].B);
3409
3410 // --- 2. Assemble LHS Matrix (Laplacian) on all MG levels ---
3411 LOG_ALLOW(LOCAL, LOG_DEBUG, "Block %d: Assembling Poisson LHS on all levels...\n", bi);
3412 for (l = usermg->mglevels - 1; l >= 0; l--) {
3413 user = mgctx[l].user;
3414 LOG_ALLOW(GLOBAL,LOG_DEBUG," Calculating LHS for Level %d.\n",l);
3415 PoissonLHSNew(&user[bi]);
3416 }
3417
3418 // --- 3. Setup PETSc KSP and PCMG (Multigrid Preconditioner) ---
3419 LOG_ALLOW(LOCAL, LOG_DEBUG, "Block %d: Configuring KSP and PCMG...\n", bi);
3420
3421 ierr = KSPCreate(PETSC_COMM_WORLD, &mgksp); CHKERRQ(ierr);
3422 ierr = KSPAppendOptionsPrefix(mgksp, "ps_"); CHKERRQ(ierr);
3423
3424 // =======================================================================
3425 DualMonitorCtx *monctx;
3426 char filen[PETSC_MAX_PATH_LEN + 128];
3427
3428 // 1. Allocate the context and set it up.
3429 ierr = PetscNew(&monctx); CHKERRQ(ierr);
3430
3431 monctx->step = simCtx->step;
3432 monctx->block_id = bi;
3433 monctx->file_handle = NULL;
3434
3435 // Only rank 0 handles the file.
3436 if (!rank) {
3437 ierr = PetscSNPrintf(filen, sizeof(filen), "%s/Poisson_Solver_Convergence_History_Block_%d.log", simCtx->log_dir, bi); CHKERRQ(ierr);
3438 // On the very first step of a fresh run, TRUNCATE the file.
3439 // In continue mode, always APPEND to preserve existing data.
3440 if (simCtx->step == simCtx->StartStep + 1 && !simCtx->continueMode) {
3441 monctx->file_handle = fopen(filen, "w");
3442 } else { // For all subsequent steps (or continue mode), APPEND.
3443 monctx->file_handle = fopen(filen, "a");
3444 }
3445
3446 if (monctx->file_handle) {
3447 PetscFPrintf(PETSC_COMM_SELF, monctx->file_handle, "--- Convergence for Timestep %d, Block %d ---\n", (int)simCtx->step, bi);
3448 } else {
3449 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Could not open KSP monitor log file: %s", filen);
3450 }
3451 }
3452
3454
3455 ierr = KSPMonitorSet(mgksp, DualKSPMonitor, monctx, DualMonitorDestroy); CHKERRQ(ierr);
3456 // =======================================================================
3457
3458 ierr = KSPGetPC(mgksp, &mgpc); CHKERRQ(ierr);
3459 ierr = PCSetType(mgpc, PCMG); CHKERRQ(ierr);
3460
3461 ierr = PCMGSetLevels(mgpc, usermg->mglevels, PETSC_NULLPTR); CHKERRQ(ierr);
3462 ierr = PCMGSetCycleType(mgpc, PC_MG_CYCLE_V); CHKERRQ(ierr);
3463 ierr = PCMGSetType(mgpc, PC_MG_MULTIPLICATIVE); CHKERRQ(ierr);
3464
3465 // --- 4. Define Restriction and Interpolation Operators for MG ---
3466 for (l = usermg->mglevels - 1; l > 0; l--) {
3467
3468 // Get stable pointers directly from the main mgctx array.
3469 // These pointers point to memory that will persist.
3470 UserCtx *fine_user_ctx = &mgctx[l].user[bi];
3471 UserCtx *coarse_user_ctx = &mgctx[l-1].user[bi];
3472
3473 // --- Configure the context pointers ---
3474 // The coarse UserCtx needs to know about the fine grid for restriction.
3475 coarse_user_ctx->da_f = &(fine_user_ctx->da);
3476 coarse_user_ctx->user_f = fine_user_ctx;
3477
3478 // The fine UserCtx needs to know about the coarse grid for interpolation.
3479 fine_user_ctx->da_c = &(coarse_user_ctx->da);
3480 fine_user_ctx->user_c = coarse_user_ctx;
3481 fine_user_ctx->lNvert_c = &(coarse_user_ctx->lNvert);
3482
3483 // --- Get matrix dimensions ---
3484 PetscInt m_c = (coarse_user_ctx->info.xm * coarse_user_ctx->info.ym * coarse_user_ctx->info.zm);
3485 PetscInt m_f = (fine_user_ctx->info.xm * fine_user_ctx->info.ym * fine_user_ctx->info.zm);
3486 PetscInt M_c = (coarse_user_ctx->info.mx * coarse_user_ctx->info.my * coarse_user_ctx->info.mz);
3487 PetscInt M_f = (fine_user_ctx->info.mx * fine_user_ctx->info.my * fine_user_ctx->info.mz);
3488
3489 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);
3490 // --- Create the MatShell objects ---
3491 // Pass the STABLE pointer coarse_user_ctx as the context for restriction.
3492 ierr = MatCreateShell(PETSC_COMM_WORLD, m_c, m_f, M_c, M_f, (void*)coarse_user_ctx, &fine_user_ctx->MR); CHKERRQ(ierr);
3493
3494 // Pass the STABLE pointer fine_user_ctx as the context for interpolation.
3495 ierr = MatCreateShell(PETSC_COMM_WORLD, m_f, m_c, M_f, M_c, (void*)fine_user_ctx, &fine_user_ctx->MP); CHKERRQ(ierr);
3496
3497 // --- Set the operations for the MatShells ---
3498 ierr = MatShellSetOperation(fine_user_ctx->MR, MATOP_MULT, (void(*)(void))RestrictResidual_SolidAware); CHKERRQ(ierr);
3499 ierr = MatShellSetOperation(fine_user_ctx->MP, MATOP_MULT, (void(*)(void))MyInterpolation); CHKERRQ(ierr);
3500
3501 // --- Register the operators with PCMG ---
3502 ierr = PCMGSetRestriction(mgpc, l, fine_user_ctx->MR); CHKERRQ(ierr);
3503 ierr = PCMGSetInterpolation(mgpc, l, fine_user_ctx->MP); CHKERRQ(ierr);
3504
3505 }
3506
3507 // --- 5. Configure Solvers on Each MG Level ---
3508 for (l = usermg->mglevels - 1; l >= 0; l--) {
3509 user = mgctx[l].user;
3510 if (l > 0) { // Smoother for fine levels
3511 ierr = PCMGGetSmoother(mgpc, l, &subksp); CHKERRQ(ierr);
3512 } else { // Direct or iterative solver for the coarsest level
3513 ierr = PCMGGetCoarseSolve(mgpc, &subksp); CHKERRQ(ierr);
3514 ierr = KSPSetTolerances(subksp, 1.e-8, PETSC_DEFAULT, PETSC_DEFAULT, 40); CHKERRQ(ierr);
3515 }
3516
3517 ierr = KSPSetOperators(subksp, user[bi].A, user[bi].A); CHKERRQ(ierr);
3518 ierr = KSPSetFromOptions(subksp); CHKERRQ(ierr);
3519 ierr = KSPGetPC(subksp, &subpc); CHKERRQ(ierr);
3520 ierr = PCSetType(subpc, PCBJACOBI); CHKERRQ(ierr);
3521
3522 KSP *subsubksp;
3523 PC subsubpc;
3524 PetscInt nlocal;
3525
3526 // This logic is required for both the smoother and the coarse solve
3527 // since both use PCBJACOBI.
3528 ierr = KSPSetUp(subksp); CHKERRQ(ierr); // Set up KSP to allow access to sub-KSPs
3529 ierr = PCBJacobiGetSubKSP(subpc, &nlocal, NULL, &subsubksp); CHKERRQ(ierr);
3530
3531 for (PetscInt abi = 0; abi < nlocal; abi++) {
3532 ierr = KSPGetPC(subsubksp[abi], &subsubpc); CHKERRQ(ierr);
3533 // Add the critical shift amount
3534 ierr = PCFactorSetShiftAmount(subsubpc, 1.e-10); CHKERRQ(ierr);
3535 }
3536
3537 ierr = MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_TRUE, 0, PETSC_NULLPTR, &user[bi].nullsp); CHKERRQ(ierr);
3538 ierr = MatNullSpaceSetFunction(user[bi].nullsp, PoissonNullSpaceFunction, &user[bi]); CHKERRQ(ierr);
3539 ierr = MatSetNullSpace(user[bi].A, user[bi].nullsp); CHKERRQ(ierr);
3540
3541 ierr = PCMGSetResidual(mgpc, l, PCMGResidualDefault, user[bi].A); CHKERRQ(ierr);
3542 ierr = KSPSetUp(subksp); CHKERRQ(ierr);
3543
3544 if (l < usermg->mglevels - 1) {
3545 ierr = MatCreateVecs(user[bi].A, &user[bi].R, PETSC_NULLPTR); CHKERRQ(ierr);
3546 ierr = PCMGSetRhs(mgpc, l, user[bi].R); CHKERRQ(ierr);
3547 }
3548 }
3549
3550 // --- 6. Set Final KSP Operators and Solve ---
3551 l = usermg->mglevels - 1;
3552 user = mgctx[l].user;
3553
3554 LOG_ALLOW(LOCAL, LOG_DEBUG, "Block %d: Setting KSP operators and solving...\n", bi);
3555 ierr = KSPSetOperators(mgksp, user[bi].A, user[bi].A); CHKERRQ(ierr);
3556 ierr = MatSetNullSpace(user[bi].A, user[bi].nullsp); CHKERRQ(ierr);
3557 ierr = KSPSetFromOptions(mgksp); CHKERRQ(ierr);
3558 ierr = KSPSetUp(mgksp); CHKERRQ(ierr);
3559 ierr = KSPSolve(mgksp, user[bi].B, user[bi].Phi); CHKERRQ(ierr);
3560
3561 // --- 7. Cleanup for this block ---
3562 for (l = usermg->mglevels - 1; l >= 0; l--) {
3563 user = mgctx[l].user;
3564 MatNullSpaceDestroy(&user[bi].nullsp);
3565 MatDestroy(&user[bi].A);
3566 user[bi].assignedA = PETSC_FALSE;
3567 if (l > 0) {
3568 MatDestroy(&user[bi].MR);
3569 MatDestroy(&user[bi].MP);
3570 } else if (l==0 && immersed) {
3571 PetscFree(user[bi].KSKE);
3572 }
3573 if (l < usermg->mglevels - 1) {
3574 VecDestroy(&user[bi].R);
3575 }
3576 }
3577
3578 KSPDestroy(&mgksp);
3579 VecDestroy(&mgctx[usermg->mglevels-1].user[bi].B);
3580
3581 } // End of loop over blocks
3582
3583 LOG_ALLOW(GLOBAL, LOG_INFO, "Multigrid Poisson Solve complete.\n");
3585 PetscFunctionReturn(0);
3586}
PetscErrorCode DualMonitorDestroy(void **ctx)
Destroys the DualMonitorCtx.
Definition logging.c:808
PetscBool log_to_console
Definition logging.h:57
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:44
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:45
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:199
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:779
PetscInt step
Definition logging.h:59
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:847
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:30
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:31
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:770
FILE * file_handle
Definition logging.h:56
PetscInt block_id
Definition logging.h:60
Context for a dual-purpose KSP monitor.
Definition logging.h:55
PetscErrorCode PoissonNullSpaceFunction(MatNullSpace nullsp, Vec X, void *ctx)
Implementation of PoissonNullSpaceFunction().
Definition poisson.c:1031
PetscErrorCode PoissonLHSNew(UserCtx *user)
Internal helper implementation: PoissonLHSNew().
Definition poisson.c:1532
PetscErrorCode VolumeFlux_rev(UserCtx *user, PetscReal *ibm_Flux, PetscReal *ibm_Area, PetscInt flg)
Implementation of VolumeFlux_rev().
Definition poisson.c:2230
static PetscErrorCode RestrictResidual_SolidAware(Mat A, Vec X, Vec F)
Internal helper implementation: RestrictResidual_SolidAware().
Definition poisson.c:1344
PetscErrorCode VolumeFlux(UserCtx *user, PetscReal *ibm_Flux, PetscReal *ibm_Area, PetscInt flg)
Implementation of VolumeFlux().
Definition poisson.c:2472
PetscErrorCode PoissonRHS(UserCtx *user, Vec B)
Implementation of PoissonRHS().
Definition poisson.c:2141
PetscErrorCode MyInterpolation(Mat A, Vec X, Vec F)
Implementation of MyInterpolation().
Definition poisson.c:1233
static PetscErrorCode FullyBlocked(UserCtx *user)
Internal helper implementation: FullyBlocked().
Definition poisson.c:3134
static PetscErrorCode MyNvertRestriction(UserCtx *user_h, UserCtx *user_c)
Internal helper implementation: MyNvertRestriction().
Definition poisson.c:3222
PetscInt MHV
Definition variables.h:679
PetscBool continueMode
Definition variables.h:660
UserCtx * user
Definition variables.h:528
PetscMPIInt rank
Definition variables.h:646
PetscInt LV
Definition variables.h:679
PetscInt block_number
Definition variables.h:712
UserCtx * user_f
Definition variables.h:875
Vec lNvert
Definition variables.h:837
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:814
PetscBool assignedA
Definition variables.h:854
PetscInt StartStep
Definition variables.h:653
DM * da_c
Definition variables.h:876
UserCtx * user_c
Definition variables.h:875
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:668
DM * da_f
Definition variables.h:876
PetscInt mglevels
Definition variables.h:535
PetscInt step
Definition variables.h:651
DMDALocalInfo info
Definition variables.h:818
PetscBool ps_ksp_pic_monitor_true_residual
Definition variables.h:695
MGCtx * mgctx
Definition variables.h:538
PetscBool multinullspace
Definition variables.h:851
PetscInt immersed
Definition variables.h:673
Vec * lNvert_c
Definition variables.h:877
Context for Multigrid operations.
Definition variables.h:527
The master context for the entire simulation.
Definition variables.h:643
User-defined context containing data specific to a single computational grid level.
Definition variables.h:811
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 1532 of file poisson.c.

1533{
1534 PetscFunctionBeginUser;
1536 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Entering PoissonLHSNew to assemble Laplacian matrix.\n");
1537 PetscErrorCode ierr;
1538 //================================================================================
1539 // Section 1: Initialization and Data Acquisition
1540 //================================================================================
1541
1542
1543 // --- Get simulation and grid context ---
1544 DM da = user->da, fda = user->fda;
1545 DMDALocalInfo info = user->info;
1546 PetscInt IM = user->IM, JM = user->JM, KM = user->KM;
1547 PetscInt i,j,k;
1548
1549 // --- Grid dimensions ---
1550 PetscInt mx = info.mx, my = info.my, mz = info.mz;
1551 PetscInt xs = info.xs, xe = info.xs + info.xm;
1552 PetscInt ys = info.ys, ye = info.ys + info.ym;
1553 PetscInt zs = info.zs, ze = info.zs + info.zm;
1554 PetscInt gxs = info.gxs, gxe = gxs + info.gxm;
1555 PetscInt gys = info.gys, gye = gys + info.gym;
1556 PetscInt gzs = info.gzs, gze = gzs + info.gzm;
1557
1558 // --- Define constants for clarity ---
1559 const PetscReal IBM_FLUID_THRESHOLD = 0.1;
1560
1561 // --- Allocate the LHS matrix A on the first call ---
1562 if (!user->assignedA) {
1563 LOG_ALLOW(GLOBAL, LOG_INFO, "First call: Creating LHS matrix 'A' with 19-point stencil preallocation.\n");
1564 PetscInt N = mx * my * mz; // Total size
1565 PetscInt M; // Local size
1566 VecGetLocalSize(user->Phi, &M);
1567 // Create a sparse AIJ matrix, preallocating for 19 non-zeros per row (d=diagonal, o=off-diagonal)
1568 MatCreateAIJ(PETSC_COMM_WORLD, M, M, N, N, 19, PETSC_NULLPTR, 19, PETSC_NULLPTR, &(user->A));
1569 user->assignedA = PETSC_TRUE;
1570 }
1571
1572 // Zero out matrix entries from the previous solve
1573 MatZeroEntries(user->A);
1574
1575 // --- Get direct pointer access to grid metric data ---
1576 Cmpnts ***csi, ***eta, ***zet, ***icsi, ***ieta, ***izet, ***jcsi, ***jeta, ***jzet, ***kcsi, ***keta, ***kzet;
1577 PetscReal ***aj, ***iaj, ***jaj, ***kaj, ***nvert;
1578 DMDAVecGetArray(fda, user->lCsi, &csi); DMDAVecGetArray(fda, user->lEta, &eta); DMDAVecGetArray(fda, user->lZet, &zet);
1579 DMDAVecGetArray(fda, user->lICsi, &icsi); DMDAVecGetArray(fda, user->lIEta, &ieta); DMDAVecGetArray(fda, user->lIZet, &izet);
1580 DMDAVecGetArray(fda, user->lJCsi, &jcsi); DMDAVecGetArray(fda, user->lJEta, &jeta); DMDAVecGetArray(fda, user->lJZet, &jzet);
1581 DMDAVecGetArray(fda, user->lKCsi, &kcsi); DMDAVecGetArray(fda, user->lKEta, &keta); DMDAVecGetArray(fda, user->lKZet, &kzet);
1582 DMDAVecGetArray(da, user->lAj, &aj); DMDAVecGetArray(da, user->lIAj, &iaj); DMDAVecGetArray(da, user->lJAj, &jaj); DMDAVecGetArray(da, user->lKAj, &kaj);
1583 DMDAVecGetArray(da, user->lNvert, &nvert);
1584
1585 // --- Create temporary vectors for the metric tensor components G_ij ---
1586 Vec G11, G12, G13, G21, G22, G23, G31, G32, G33;
1587 PetscReal ***g11, ***g12, ***g13, ***g21, ***g22, ***g23, ***g31, ***g32, ***g33;
1588 VecDuplicate(user->lAj, &G11); VecDuplicate(user->lAj, &G12); VecDuplicate(user->lAj, &G13);
1589 VecDuplicate(user->lAj, &G21); VecDuplicate(user->lAj, &G22); VecDuplicate(user->lAj, &G23);
1590 VecDuplicate(user->lAj, &G31); VecDuplicate(user->lAj, &G32); VecDuplicate(user->lAj, &G33);
1591 DMDAVecGetArray(da, G11, &g11); DMDAVecGetArray(da, G12, &g12); DMDAVecGetArray(da, G13, &g13);
1592 DMDAVecGetArray(da, G21, &g21); DMDAVecGetArray(da, G22, &g22); DMDAVecGetArray(da, G23, &g23);
1593 DMDAVecGetArray(da, G31, &g31); DMDAVecGetArray(da, G32, &g32); DMDAVecGetArray(da, G33, &g33);
1594
1595 //================================================================================
1596 // Section 2: Pre-compute Metric Tensor Coefficients (g_ij)
1597 //================================================================================
1598 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Pre-computing metric tensor components (g_ij).\n");
1599 for (k = gzs; k < gze; k++) {
1600 for (j = gys; j < gye; j++) {
1601 for (i = gxs; i < gxe; i++) {
1602 // These coefficients represent the dot products of the grid's contravariant base vectors,
1603 // scaled by face area. They are the core of the Laplacian operator on a curvilinear grid.
1604 if(i>-1 && j>-1 && k>-1 && i<IM+1 && j<JM+1 && k<KM+1){
1605 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];
1606 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];
1607 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];
1608 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];
1609 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];
1610 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];
1611 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];
1612 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];
1613 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];
1614 }
1615 }
1616 }
1617 }
1618
1619 //================================================================================
1620 // Section 3: Assemble the LHS Matrix A
1621 //================================================================================
1622 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Assembling the LHS matrix A using a 19-point stencil.\n");
1623
1624 // --- Define domain boundaries for stencil logic, accounting for periodic BCs ---
1625 PetscInt x_str, x_end, y_str, y_end, z_str, z_end;
1626 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC) { x_end = mx - 1; x_str = 0; }
1627 else { x_end = mx - 2; x_str = 1; }
1628 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC) { y_end = my - 1; y_str = 0; }
1629 else { y_end = my - 2; y_str = 1; }
1630 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC) { z_end = mz - 1; z_str = 0; }
1631 else { z_end = mz - 2; z_str = 1; }
1632
1633 // --- Main assembly loop over all local grid points ---
1634 for (k = zs; k < ze; k++) {
1635 for (j = ys; j < ye; j++) {
1636 for (i = xs; i < xe; i++) {
1637 PetscScalar vol[19]; // Holds the 19 stencil coefficient values for the current row
1638 PetscInt idx[19]; // Holds the 19 global column indices for the current row
1639 PetscInt row = Gidx(i, j, k, user); // Global index for the current row
1640
1641 // --- Handle Domain Boundary and Immersed Solid Points ---
1642 // For these points, we don't solve the Poisson equation. We set an identity
1643 // row (A_ii = 1) to effectively fix the pressure value (usually to 0).
1644 if (i == 0 || i == mx - 1 || j == 0 || j == my - 1 || k == 0 || k == mz - 1 || nvert[k][j][i] > IBM_FLUID_THRESHOLD) {
1645 vol[CP] = 1.0;
1646 idx[CP] = row;
1647 MatSetValues(user->A, 1, &row, 1, &idx[CP], &vol[CP], INSERT_VALUES);
1648 }
1649 // --- Handle Fluid Points ---
1650 else {
1651 for (PetscInt m = 0; m < 19; m++) {
1652 vol[m] = 0.0;
1653 }
1654
1655 /************************************************************************
1656 * EAST FACE CONTRIBUTION (between i and i+1)
1657 ************************************************************************/
1658 if (nvert[k][j][i + 1] < IBM_FLUID_THRESHOLD && i != x_end) { // East neighbor is fluid
1659 // Primary derivative term: d/d_csi (g11 * dP/d_csi)
1660 vol[CP] -= g11[k][j][i];
1661 vol[EP] += g11[k][j][i];
1662
1663 // Cross-derivative term: d/d_csi (g12 * dP/d_eta).
1664 // This requires an average of dP/d_eta. If a neighbor is solid, the stencil
1665 // dynamically switches to a one-sided difference to avoid using solid points.
1666 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) {
1667 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))) {
1668 vol[CP] += g12[k][j][i] * 0.5; vol[EP] += g12[k][j][i] * 0.5;
1669 vol[SP] -= g12[k][j][i] * 0.5; vol[SE] -= g12[k][j][i] * 0.5;
1670 }
1671 }
1672 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) {
1673 if (nvert[k][j-1][i] + nvert[k][j-1][i+1] < 0.1) {
1674 vol[CP] += g12[k][j][i] * 0.5; vol[EP] += g12[k][j][i] * 0.5;
1675 vol[SP] -= g12[k][j][i] * 0.5; vol[SE] -= g12[k][j][i] * 0.5;
1676 }
1677 }
1678 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) {
1679 if (nvert[k][j+1][i] + nvert[k][j+1][i+1] < 0.1) {
1680 vol[NP] += g12[k][j][i] * 0.5; vol[NE] += g12[k][j][i] * 0.5;
1681 vol[CP] -= g12[k][j][i] * 0.5; vol[EP] -= g12[k][j][i] * 0.5;
1682 }
1683 }
1684 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) {
1685 if (nvert[k][j+1][i] + nvert[k][j+1][i+1] < 0.1) {
1686 vol[NP] += g12[k][j][i] * 0.5; vol[NE] += g12[k][j][i] * 0.5;
1687 vol[CP] -= g12[k][j][i] * 0.5; vol[EP] -= g12[k][j][i] * 0.5;
1688 }
1689 }
1690 else { // Centered difference
1691 vol[NP] += g12[k][j][i] * 0.25; vol[NE] += g12[k][j][i] * 0.25;
1692 vol[SP] -= g12[k][j][i] * 0.25; vol[SE] -= g12[k][j][i] * 0.25;
1693 }
1694
1695 // Cross-derivative term: d/d_csi (g13 * dP/d_zet)
1696 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) {
1697 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))) {
1698 vol[CP] += g13[k][j][i] * 0.5; vol[EP] += g13[k][j][i] * 0.5;
1699 vol[BP] -= g13[k][j][i] * 0.5; vol[BE] -= g13[k][j][i] * 0.5;
1700 }
1701 }
1702 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) {
1703 if (nvert[k-1][j][i] + nvert[k-1][j][i+1] < 0.1) {
1704 vol[CP] += g13[k][j][i] * 0.5; vol[EP] += g13[k][j][i] * 0.5;
1705 vol[BP] -= g13[k][j][i] * 0.5; vol[BE] -= g13[k][j][i] * 0.5;
1706 }
1707 }
1708 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) {
1709 if (nvert[k+1][j][i] + nvert[k+1][j][i+1] < 0.1) {
1710 vol[TP] += g13[k][j][i] * 0.5; vol[TE] += g13[k][j][i] * 0.5;
1711 vol[CP] -= g13[k][j][i] * 0.5; vol[EP] -= g13[k][j][i] * 0.5;
1712 }
1713 }
1714 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) {
1715 if (nvert[k+1][j][i] + nvert[k+1][j][i+1] < 0.1) {
1716 vol[TP] += g13[k][j][i] * 0.5; vol[TE] += g13[k][j][i] * 0.5;
1717 vol[CP] -= g13[k][j][i] * 0.5; vol[EP] -= g13[k][j][i] * 0.5;
1718 }
1719 }
1720 else { // Centered difference
1721 vol[TP] += g13[k][j][i] * 0.25; vol[TE] += g13[k][j][i] * 0.25;
1722 vol[BP] -= g13[k][j][i] * 0.25; vol[BE] -= g13[k][j][i] * 0.25;
1723 }
1724 }
1725
1726 /************************************************************************
1727 * WEST FACE CONTRIBUTION (between i-1 and i)
1728 ************************************************************************/
1729 if (nvert[k][j][i-1] < IBM_FLUID_THRESHOLD && i != x_str) {
1730 vol[CP] -= g11[k][j][i-1];
1731 vol[WP] += g11[k][j][i-1];
1732
1733 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) {
1734 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))) {
1735 vol[CP] -= g12[k][j][i-1] * 0.5; vol[WP] -= g12[k][j][i-1] * 0.5;
1736 vol[SP] += g12[k][j][i-1] * 0.5; vol[SW] += g12[k][j][i-1] * 0.5;
1737 }
1738 }
1739 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) {
1740 if (nvert[k][j-1][i] + nvert[k][j-1][i-1] < 0.1) {
1741 vol[CP] -= g12[k][j][i-1] * 0.5; vol[WP] -= g12[k][j][i-1] * 0.5;
1742 vol[SP] += g12[k][j][i-1] * 0.5; vol[SW] += g12[k][j][i-1] * 0.5;
1743 }
1744 }
1745 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) {
1746 if (nvert[k][j+1][i] + nvert[k][j+1][i-1] < 0.1) {
1747 vol[NP] -= g12[k][j][i-1] * 0.5; vol[NW] -= g12[k][j][i-1] * 0.5;
1748 vol[CP] += g12[k][j][i-1] * 0.5; vol[WP] += g12[k][j][i-1] * 0.5;
1749 }
1750 }
1751 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) {
1752 if (nvert[k][j+1][i] + nvert[k][j+1][i-1] < 0.1) {
1753 vol[NP] -= g12[k][j][i-1] * 0.5; vol[NW] -= g12[k][j][i-1] * 0.5;
1754 vol[CP] += g12[k][j][i-1] * 0.5; vol[WP] += g12[k][j][i-1] * 0.5;
1755 }
1756 }
1757 else {
1758 vol[NP] -= g12[k][j][i-1] * 0.25; vol[NW] -= g12[k][j][i-1] * 0.25;
1759 vol[SP] += g12[k][j][i-1] * 0.25; vol[SW] += g12[k][j][i-1] * 0.25;
1760 }
1761
1762 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) {
1763 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))) {
1764 vol[CP] -= g13[k][j][i-1] * 0.5; vol[WP] -= g13[k][j][i-1] * 0.5;
1765 vol[BP] += g13[k][j][i-1] * 0.5; vol[BW] += g13[k][j][i-1] * 0.5;
1766 }
1767 }
1768 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) {
1769 if (nvert[k-1][j][i] + nvert[k-1][j][i-1] < 0.1) {
1770 vol[CP] -= g13[k][j][i-1] * 0.5; vol[WP] -= g13[k][j][i-1] * 0.5;
1771 vol[BP] += g13[k][j][i-1] * 0.5; vol[BW] += g13[k][j][i-1] * 0.5;
1772 }
1773 }
1774 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) {
1775 if (nvert[k+1][j][i] + nvert[k+1][j][i-1] < 0.1) {
1776 vol[TP] -= g13[k][j][i-1] * 0.5; vol[TW] -= g13[k][j][i-1] * 0.5;
1777 vol[CP] += g13[k][j][i-1] * 0.5; vol[WP] += g13[k][j][i-1] * 0.5;
1778 }
1779 }
1780 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) {
1781 if (nvert[k+1][j][i] + nvert[k+1][j][i-1] < 0.1) {
1782 vol[TP] -= g13[k][j][i-1] * 0.5; vol[TW] -= g13[k][j][i-1] * 0.5;
1783 vol[CP] += g13[k][j][i-1] * 0.5; vol[WP] += g13[k][j][i-1] * 0.5;
1784 }
1785 }
1786 else {
1787 vol[TP] -= g13[k][j][i-1] * 0.25; vol[TW] -= g13[k][j][i-1] * 0.25;
1788 vol[BP] += g13[k][j][i-1] * 0.25; vol[BW] += g13[k][j][i-1] * 0.25;
1789 }
1790 }
1791
1792 /************************************************************************
1793 * NORTH FACE CONTRIBUTION (between j and j+1)
1794 ************************************************************************/
1795 if (nvert[k][j+1][i] < IBM_FLUID_THRESHOLD && j != y_end) {
1796 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) {
1797 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))) {
1798 vol[CP] += g21[k][j][i] * 0.5; vol[NP] += g21[k][j][i] * 0.5;
1799 vol[WP] -= g21[k][j][i] * 0.5; vol[NW] -= g21[k][j][i] * 0.5;
1800 }
1801 }
1802 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) {
1803 if (nvert[k][j][i-1] + nvert[k][j+1][i-1] < 0.1) {
1804 vol[CP] += g21[k][j][i] * 0.5; vol[NP] += g21[k][j][i] * 0.5;
1805 vol[WP] -= g21[k][j][i] * 0.5; vol[NW] -= g21[k][j][i] * 0.5;
1806 }
1807 }
1808 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) {
1809 if (nvert[k][j][i+1] + nvert[k][j+1][i+1] < 0.1) {
1810 vol[EP] += g21[k][j][i] * 0.5; vol[NE] += g21[k][j][i] * 0.5;
1811 vol[CP] -= g21[k][j][i] * 0.5; vol[NP] -= g21[k][j][i] * 0.5;
1812 }
1813 }
1814 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) {
1815 if (nvert[k][j][i+1] + nvert[k][j+1][i+1] < 0.1) {
1816 vol[EP] += g21[k][j][i] * 0.5; vol[NE] += g21[k][j][i] * 0.5;
1817 vol[CP] -= g21[k][j][i] * 0.5; vol[NP] -= g21[k][j][i] * 0.5;
1818 }
1819 }
1820 else {
1821 vol[EP] += g21[k][j][i] * 0.25; vol[NE] += g21[k][j][i] * 0.25;
1822 vol[WP] -= g21[k][j][i] * 0.25; vol[NW] -= g21[k][j][i] * 0.25;
1823 }
1824
1825 vol[CP] -= g22[k][j][i];
1826 vol[NP] += g22[k][j][i];
1827
1828 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) {
1829 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))) {
1830 vol[CP] += g23[k][j][i] * 0.5; vol[NP] += g23[k][j][i] * 0.5;
1831 vol[BP] -= g23[k][j][i] * 0.5; vol[BN] -= g23[k][j][i] * 0.5;
1832 }
1833 }
1834 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) {
1835 if (nvert[k-1][j][i] + nvert[k-1][j+1][i] < 0.1) {
1836 vol[CP] += g23[k][j][i] * 0.5; vol[NP] += g23[k][j][i] * 0.5;
1837 vol[BP] -= g23[k][j][i] * 0.5; vol[BN] -= g23[k][j][i] * 0.5;
1838 }
1839 }
1840 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) {
1841 if (nvert[k+1][j][i] + nvert[k+1][j+1][i] < 0.1) {
1842 vol[TP] += g23[k][j][i] * 0.5; vol[TN] += g23[k][j][i] * 0.5;
1843 vol[CP] -= g23[k][j][i] * 0.5; vol[NP] -= g23[k][j][i] * 0.5;
1844 }
1845 }
1846 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) {
1847 if (nvert[k+1][j][i] + nvert[k+1][j+1][i] < 0.1) {
1848 vol[TP] += g23[k][j][i] * 0.5; vol[TN] += g23[k][j][i] * 0.5;
1849 vol[CP] -= g23[k][j][i] * 0.5; vol[NP] -= g23[k][j][i] * 0.5;
1850 }
1851 }
1852 else {
1853 vol[TP] += g23[k][j][i] * 0.25; vol[TN] += g23[k][j][i] * 0.25;
1854 vol[BP] -= g23[k][j][i] * 0.25; vol[BN] -= g23[k][j][i] * 0.25;
1855 }
1856 }
1857
1858 /************************************************************************
1859 * SOUTH FACE CONTRIBUTION (between j-1 and j)
1860 ************************************************************************/
1861 if (nvert[k][j-1][i] < IBM_FLUID_THRESHOLD && j != y_str) {
1862 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) {
1863 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))) {
1864 vol[CP] -= g21[k][j-1][i] * 0.5; vol[SP] -= g21[k][j-1][i] * 0.5;
1865 vol[WP] += g21[k][j-1][i] * 0.5; vol[SW] += g21[k][j-1][i] * 0.5;
1866 }
1867 }
1868 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) {
1869 if (nvert[k][j][i-1] + nvert[k][j-1][i-1] < 0.1) {
1870 vol[CP] -= g21[k][j-1][i] * 0.5; vol[SP] -= g21[k][j-1][i] * 0.5;
1871 vol[WP] += g21[k][j-1][i] * 0.5; vol[SW] += g21[k][j-1][i] * 0.5;
1872 }
1873 }
1874 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) {
1875 if (nvert[k][j][i+1] + nvert[k][j-1][i+1] < 0.1) {
1876 vol[EP] -= g21[k][j-1][i] * 0.5; vol[SE] -= g21[k][j-1][i] * 0.5;
1877 vol[CP] += g21[k][j-1][i] * 0.5; vol[SP] += g21[k][j-1][i] * 0.5;
1878 }
1879 }
1880 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) {
1881 if (nvert[k][j][i+1] + nvert[k][j-1][i+1] < 0.1) {
1882 vol[EP] -= g21[k][j-1][i] * 0.5; vol[SE] -= g21[k][j-1][i] * 0.5;
1883 vol[CP] += g21[k][j-1][i] * 0.5; vol[SP] += g21[k][j-1][i] * 0.5;
1884 }
1885 }
1886 else {
1887 vol[EP] -= g21[k][j-1][i] * 0.25; vol[SE] -= g21[k][j-1][i] * 0.25;
1888 vol[WP] += g21[k][j-1][i] * 0.25; vol[SW] += g21[k][j-1][i] * 0.25;
1889 }
1890
1891 vol[CP] -= g22[k][j-1][i];
1892 vol[SP] += g22[k][j-1][i];
1893
1894 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) {
1895 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))) {
1896 vol[CP] -= g23[k][j-1][i] * 0.5; vol[SP] -= g23[k][j-1][i] * 0.5;
1897 vol[BP] += g23[k][j-1][i] * 0.5; vol[BS] += g23[k][j-1][i] * 0.5;
1898 }
1899 }
1900 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) {
1901 if (nvert[k-1][j][i] + nvert[k-1][j-1][i] < 0.1 ) {
1902 vol[CP] -= g23[k][j-1][i] * 0.5; vol[SP] -= g23[k][j-1][i] * 0.5;
1903 vol[BP] += g23[k][j-1][i] * 0.5; vol[BS] += g23[k][j-1][i] * 0.5;
1904 }
1905 }
1906 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) {
1907 if (nvert[k+1][j][i] + nvert[k+1][j-1][i] < 0.1) {
1908 vol[TP] -= g23[k][j-1][i] * 0.5; vol[TS] -= g23[k][j-1][i] * 0.5;
1909 vol[CP] += g23[k][j-1][i] * 0.5; vol[SP] += g23[k][j-1][i] * 0.5;
1910 }
1911 }
1912 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) {
1913 if (nvert[k+1][j][i] + nvert[k+1][j-1][i] < 0.1) {
1914 vol[TP] -= g23[k][j-1][i] * 0.5; vol[TS] -= g23[k][j-1][i] * 0.5;
1915 vol[CP] += g23[k][j-1][i] * 0.5; vol[SP] += g23[k][j-1][i] * 0.5;
1916 }
1917 }
1918 else {
1919 vol[TP] -= g23[k][j-1][i] * 0.25; vol[TS] -= g23[k][j-1][i] * 0.25;
1920 vol[BP] += g23[k][j-1][i] * 0.25; vol[BS] += g23[k][j-1][i] * 0.25;
1921 }
1922 }
1923
1924 /************************************************************************
1925 * TOP FACE CONTRIBUTION (between k and k+1)
1926 ************************************************************************/
1927 if (nvert[k+1][j][i] < IBM_FLUID_THRESHOLD && k != z_end) {
1928 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) {
1929 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))) {
1930 vol[CP] += g31[k][j][i] * 0.5; vol[TP] += g31[k][j][i] * 0.5;
1931 vol[WP] -= g31[k][j][i] * 0.5; vol[TW] -= g31[k][j][i] * 0.5;
1932 }
1933 }
1934 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) {
1935 if (nvert[k][j][i-1] + nvert[k+1][j][i-1] < 0.1) {
1936 vol[CP] += g31[k][j][i] * 0.5; vol[TP] += g31[k][j][i] * 0.5;
1937 vol[WP] -= g31[k][j][i] * 0.5; vol[TW] -= g31[k][j][i] * 0.5;
1938 }
1939 }
1940 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) {
1941 if (nvert[k][j][i+1] + nvert[k+1][j][i+1] < 0.1) {
1942 vol[EP] += g31[k][j][i] * 0.5; vol[TE] += g31[k][j][i] * 0.5;
1943 vol[CP] -= g31[k][j][i] * 0.5; vol[TP] -= g31[k][j][i] * 0.5;
1944 }
1945 }
1946 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) {
1947 if (nvert[k][j][i+1] + nvert[k+1][j][i+1] < 0.1) {
1948 vol[EP] += g31[k][j][i] * 0.5; vol[TE] += g31[k][j][i] * 0.5;
1949 vol[CP] -= g31[k][j][i] * 0.5; vol[TP] -= g31[k][j][i] * 0.5;
1950 }
1951 }
1952 else {
1953 vol[EP] += g31[k][j][i] * 0.25; vol[TE] += g31[k][j][i] * 0.25;
1954 vol[WP] -= g31[k][j][i] * 0.25; vol[TW] -= g31[k][j][i] * 0.25;
1955 }
1956
1957 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) {
1958 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))) {
1959 vol[CP] += g32[k][j][i] * 0.5; vol[TP] += g32[k][j][i] * 0.5;
1960 vol[SP] -= g32[k][j][i] * 0.5; vol[TS] -= g32[k][j][i] * 0.5;
1961 }
1962 }
1963 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) {
1964 if (nvert[k][j-1][i] + nvert[k+1][j-1][i] < 0.1) {
1965 vol[CP] += g32[k][j][i] * 0.5; vol[TP] += g32[k][j][i] * 0.5;
1966 vol[SP] -= g32[k][j][i] * 0.5; vol[TS] -= g32[k][j][i] * 0.5;
1967 }
1968 }
1969 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) {
1970 if (nvert[k][j+1][i] + nvert[k+1][j+1][i] < 0.1) {
1971 vol[NP] += g32[k][j][i] * 0.5; vol[TN] += g32[k][j][i] * 0.5;
1972 vol[CP] -= g32[k][j][i] * 0.5; vol[TP] -= g32[k][j][i] * 0.5;
1973 }
1974 }
1975 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) {
1976 if (nvert[k][j+1][i] + nvert[k+1][j+1][i] < 0.1) {
1977 vol[NP] += g32[k][j][i] * 0.5; vol[TN] += g32[k][j][i] * 0.5;
1978 vol[CP] -= g32[k][j][i] * 0.5; vol[TP] -= g32[k][j][i] * 0.5;
1979 }
1980 }
1981 else {
1982 vol[NP] += g32[k][j][i] * 0.25; vol[TN] += g32[k][j][i] * 0.25;
1983 vol[SP] -= g32[k][j][i] * 0.25; vol[TS] -= g32[k][j][i] * 0.25;
1984 }
1985
1986 vol[CP] -= g33[k][j][i];
1987 vol[TP] += g33[k][j][i];
1988 }
1989
1990 /************************************************************************
1991 * BOTTOM FACE CONTRIBUTION (between k-1 and k)
1992 ************************************************************************/
1993 if (nvert[k-1][j][i] < IBM_FLUID_THRESHOLD && k != z_str) {
1994 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) {
1995 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))) {
1996 vol[CP] -= g31[k-1][j][i] * 0.5; vol[BP] -= g31[k-1][j][i] * 0.5;
1997 vol[WP] += g31[k-1][j][i] * 0.5; vol[BW] += g31[k-1][j][i] * 0.5;
1998 }
1999 }
2000 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) {
2001 if (nvert[k][j][i-1] + nvert[k-1][j][i-1] < 0.1) {
2002 vol[CP] -= g31[k-1][j][i] * 0.5; vol[BP] -= g31[k-1][j][i] * 0.5;
2003 vol[WP] += g31[k-1][j][i] * 0.5; vol[BW] += g31[k-1][j][i] * 0.5;
2004 }
2005 }
2006 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) {
2007 if (nvert[k][j][i+1] + nvert[k-1][j][i+1] < 0.1) {
2008 vol[EP] -= g31[k-1][j][i] * 0.5; vol[BE] -= g31[k-1][j][i] * 0.5;
2009 vol[CP] += g31[k-1][j][i] * 0.5; vol[BP] += g31[k-1][j][i] * 0.5;
2010 }
2011 }
2012 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) {
2013 if (nvert[k][j][i+1] + nvert[k-1][j][i+1] < 0.1) {
2014 vol[EP] -= g31[k-1][j][i] * 0.5; vol[BE] -= g31[k-1][j][i] * 0.5;
2015 vol[CP] += g31[k-1][j][i] * 0.5; vol[BP] += g31[k-1][j][i] * 0.5;
2016 }
2017 }
2018 else {
2019 vol[EP] -= g31[k-1][j][i] * 0.25; vol[BE] -= g31[k-1][j][i] * 0.25;
2020 vol[WP] += g31[k-1][j][i] * 0.25; vol[BW] += g31[k-1][j][i] * 0.25;
2021 }
2022
2023 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) {
2024 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))) {
2025 vol[CP] -= g32[k-1][j][i] * 0.5; vol[BP] -= g32[k-1][j][i] * 0.5;
2026 vol[SP] += g32[k-1][j][i] * 0.5; vol[BS] += g32[k-1][j][i] * 0.5;
2027 }
2028 }
2029 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) {
2030 if (nvert[k][j-1][i] + nvert[k-1][j-1][i] < 0.1) {
2031 vol[CP] -= g32[k-1][j][i] * 0.5; vol[BP] -= g32[k-1][j][i] * 0.5;
2032 vol[SP] += g32[k-1][j][i] * 0.5; vol[BS] += g32[k-1][j][i] * 0.5;
2033 }
2034 }
2035 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) {
2036 if (nvert[k][j+1][i] + nvert[k-1][j+1][i] < 0.1) {
2037 vol[NP] -= g32[k-1][j][i] * 0.5; vol[BN] -= g32[k-1][j][i] * 0.5;
2038 vol[CP] += g32[k-1][j][i] * 0.5; vol[BP] += g32[k-1][j][i] * 0.5;
2039 }
2040 }
2041 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) {
2042 if (nvert[k][j+1][i] + nvert[k-1][j+1][i] < 0.1) {
2043 vol[NP] -= g32[k-1][j][i] * 0.5; vol[BN] -= g32[k-1][j][i] * 0.5;
2044 vol[CP] += g32[k-1][j][i] * 0.5; vol[BP] += g32[k-1][j][i] * 0.5;
2045 }
2046 }
2047 else {
2048 vol[NP] -= g32[k-1][j][i] * 0.25; vol[BN] -= g32[k-1][j][i] * 0.25;
2049 vol[SP] += g32[k-1][j][i] * 0.25; vol[BS] += g32[k-1][j][i] * 0.25;
2050 }
2051
2052 vol[CP] -= g33[k-1][j][i];
2053 vol[BP] += g33[k-1][j][i];
2054 }
2055
2056 // --- Final scaling and insertion into the matrix ---
2057
2058 // Scale all stencil coefficients by the negative cell volume (-aj).
2059 for (PetscInt m = 0; m < 19; m++) {
2060 vol[m] *= -aj[k][j][i];
2061 }
2062
2063 // Set the global column indices for the 19 stencil points, handling periodic BCs.
2064 idx[CP] = Gidx(i, j, k, user);
2065 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);
2066 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);
2067 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);
2068 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);
2069 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);
2070 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);
2071 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);
2072 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);
2073 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);
2074 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);
2075 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);
2076 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);
2077 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);
2078 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);
2079 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);
2080 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);
2081 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);
2082 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);
2083
2084 // Insert the computed row into the matrix A.
2085 MatSetValues(user->A, 1, &row, 19, idx, vol, INSERT_VALUES);
2086 }
2087 }
2088 }
2089 }
2090
2091 //================================================================================
2092 // Section 4: Finalize Matrix and Cleanup
2093 //================================================================================
2094
2095 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Finalizing matrix assembly.\n");
2096 MatAssemblyBegin(user->A, MAT_FINAL_ASSEMBLY);
2097 MatAssemblyEnd(user->A, MAT_FINAL_ASSEMBLY);
2098
2099 PetscReal max_A;
2100
2101 ierr = MatNorm(user->A,NORM_INFINITY,&max_A);CHKERRQ(ierr);
2102
2103 LOG_ALLOW(GLOBAL,LOG_DEBUG," Max value in A matrix for level %d = %le.\n",user->thislevel,max_A);
2104
2105 // if (get_log_level() >= LOG_DEBUG) {
2106 // ierr = MatView(user->A,PETSC_VIEWER_STDOUT_WORLD); CHKERRQ(ierr);
2107 // }
2108
2109 // --- Restore access to all PETSc vectors and destroy temporary ones ---
2110 DMDAVecRestoreArray(da, G11, &g11); DMDAVecRestoreArray(da, G12, &g12); DMDAVecRestoreArray(da, G13, &g13);
2111 DMDAVecRestoreArray(da, G21, &g21); DMDAVecRestoreArray(da, G22, &g22); DMDAVecRestoreArray(da, G23, &g23);
2112 DMDAVecRestoreArray(da, G31, &g31); DMDAVecRestoreArray(da, G32, &g32); DMDAVecRestoreArray(da, G33, &g33);
2113
2114 VecDestroy(&G11); VecDestroy(&G12); VecDestroy(&G13);
2115 VecDestroy(&G21); VecDestroy(&G22); VecDestroy(&G23);
2116 VecDestroy(&G31); VecDestroy(&G32); VecDestroy(&G33);
2117
2118 DMDAVecRestoreArray(fda, user->lCsi, &csi); DMDAVecRestoreArray(fda, user->lEta, &eta); DMDAVecRestoreArray(fda, user->lZet, &zet);
2119 DMDAVecRestoreArray(fda, user->lICsi, &icsi); DMDAVecRestoreArray(fda, user->lIEta, &ieta); DMDAVecRestoreArray(fda, user->lIZet, &izet);
2120 DMDAVecRestoreArray(fda, user->lJCsi, &jcsi); DMDAVecRestoreArray(fda, user->lJEta, &jeta); DMDAVecRestoreArray(fda, user->lJZet, &jzet);
2121 DMDAVecRestoreArray(fda, user->lKCsi, &kcsi); DMDAVecRestoreArray(fda, user->lKEta, &keta); DMDAVecRestoreArray(fda, user->lKZet, &kzet);
2122 DMDAVecRestoreArray(da, user->lAj, &aj); DMDAVecRestoreArray(da, user->lIAj, &iaj); DMDAVecRestoreArray(da, user->lJAj, &jaj); DMDAVecRestoreArray(da, user->lKAj, &kaj);
2123 DMDAVecRestoreArray(da, user->lNvert, &nvert);
2124
2125 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Exiting PoissonLHSNew.\n");
2127 PetscFunctionReturn(0);
2128}
#define TW
Definition poisson.c:317
#define SE
Definition poisson.c:308
#define BN
Definition poisson.c:312
#define WP
Definition poisson.c:300
static PetscInt Gidx(PetscInt i, PetscInt j, PetscInt k, UserCtx *user)
Internal helper implementation: Gidx().
Definition poisson.c:44
#define SW
Definition poisson.c:310
#define BS
Definition poisson.c:314
#define NE
Definition poisson.c:307
#define CP
Definition poisson.c:297
#define BE
Definition poisson.c:316
#define BP
Definition poisson.c:304
#define BW
Definition poisson.c:318
#define TE
Definition poisson.c:315
#define TS
Definition poisson.c:313
#define NP
Definition poisson.c:301
#define EP
Definition poisson.c:299
#define TN
Definition poisson.c:311
#define SP
Definition poisson.c:302
#define TP
Definition poisson.c:303
#define NW
Definition poisson.c:309
@ PERIODIC
Definition variables.h:260
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:829
Vec lIEta
Definition variables.h:860
Vec lIZet
Definition variables.h:860
Vec Phi
Definition variables.h:837
PetscInt KM
Definition variables.h:820
Vec lZet
Definition variables.h:858
Vec lIAj
Definition variables.h:860
Vec lKEta
Definition variables.h:862
Vec lJCsi
Definition variables.h:861
PetscScalar x
Definition variables.h:101
Vec lKZet
Definition variables.h:862
Vec lJEta
Definition variables.h:861
Vec lCsi
Definition variables.h:858
PetscInt thislevel
Definition variables.h:874
PetscScalar z
Definition variables.h:101
Vec lKCsi
Definition variables.h:862
PetscInt JM
Definition variables.h:820
Vec lJZet
Definition variables.h:861
Vec lAj
Definition variables.h:858
Vec lICsi
Definition variables.h:860
PetscScalar y
Definition variables.h:101
PetscInt IM
Definition variables.h:820
Vec lEta
Definition variables.h:858
BCType mathematical_type
Definition variables.h:336
Vec lJAj
Definition variables.h:861
Vec lKAj
Definition variables.h:862
@ BC_FACE_NEG_X
Definition variables.h:245
@ BC_FACE_NEG_Z
Definition variables.h:247
@ BC_FACE_NEG_Y
Definition variables.h:246
A 3D point or vector with PetscScalar components.
Definition variables.h:100
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 2141 of file poisson.c.

2142{
2143 PetscErrorCode ierr;
2144 DMDALocalInfo info = user->info;
2145 PetscInt xs = info.xs, xe = info.xs + info.xm;
2146 PetscInt ys = info.ys, ye = info.ys + info.ym;
2147 PetscInt zs = info.zs, ze = info.zs + info.zm;
2148 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2149
2150 PetscInt i, j, k;
2151 PetscReal ***nvert, ***aj, ***rb, dt = user->simCtx->dt;
2152 struct Components{
2153 PetscReal x;
2154 PetscReal y;
2155 PetscReal z;
2156 } *** ucont;
2157
2159
2160 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Entering PoissonRHS to compute pressure equation RHS.\n");
2161
2162 DMDAVecGetArray(user->da, B, &rb);
2163 DMDAVecGetArray(user->fda, user->lUcont, &ucont);
2164 DMDAVecGetArray(user->da, user->lNvert, &nvert);
2165 DMDAVecGetArray(user->da, user->lAj, &aj);
2166
2167
2168 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Computing RHS values for each cell.\n");
2169
2170 for (k=zs; k<ze; k++) {
2171 for (j=ys; j<ye; j++) {
2172 for (i=xs; i<xe; i++) {
2173
2174 if (i==0 || i==mx-1 || j==0 || j==my-1 || k==0 || k==mz-1) {
2175 rb[k][j][i] = 0.;
2176 }
2177 else if (nvert[k][j][i] > 0.1) {
2178 rb[k][j][i] = 0;
2179 }
2180 else {
2181 rb[k][j][i] = -(ucont[k][j][i].x - ucont[k][j][i-1].x +
2182 ucont[k][j][i].y - ucont[k][j-1][i].y +
2183 ucont[k][j][i].z - ucont[k-1][j][i].z) / dt
2184 * aj[k][j][i] / 1.0 * COEF_TIME_ACCURACY; // user->simCtx->st replaced by 1.0.
2185
2186 }
2187 }
2188 }
2189 }
2190
2191
2192 // --- Check the solvability condition for the Poisson equation ---
2193 // The global sum of the RHS (proportional to the total divergence) must be zero.
2194 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Verifying solvability condition (sum of RHS terms).\n");
2195 PetscReal lsum=0., sum=0.;
2196
2197 for (k=zs; k<ze; k++) {
2198 for (j=ys; j<ye; j++) {
2199 for (i=xs; i<xe; i++) {
2200
2201 lsum += rb[k][j][i] / aj[k][j][i]* dt/COEF_TIME_ACCURACY;
2202
2203 }
2204 }
2205 }
2206
2207 ierr = MPI_Allreduce(&lsum,&sum,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2208
2209 LOG_ALLOW(GLOBAL, LOG_INFO, "Global Sum of RHS (Divergence Check): %le\n", sum);
2210
2211 user->simCtx->summationRHS = sum;
2212
2213 DMDAVecRestoreArray(user->fda, user->lUcont, &ucont);
2214 DMDAVecRestoreArray(user->da, user->lNvert, &nvert);
2215 DMDAVecRestoreArray(user->da, user->lAj, &aj);
2216 DMDAVecRestoreArray(user->da, B, &rb);
2217
2218
2220 return 0;
2221}
PetscReal dt
Definition variables.h:658
Vec lUcont
Definition variables.h:837
PetscReal summationRHS
Definition variables.h:770
#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 854 of file poisson.c.

855{
856 PetscFunctionBeginUser;
858 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Entering UpdatePressure.\n");
859
860 //================================================================================
861 // Section 1: Initialization and Data Acquisition
862 //================================================================================
863 DM da = user->da;
864 DMDALocalInfo info = user->info;
865
866 // Local grid extents for the main update loop
867 PetscInt xs = info.xs, xe = info.xs + info.xm;
868 PetscInt ys = info.ys, ye = info.ys + info.ym;
869 PetscInt zs = info.zs, ze = info.zs + info.zm;
870 PetscInt mx = info.mx, my = info.my, mz = info.mz;
871
872 PetscInt i,j,k;
873
874 // --- Get direct pointer access to PETSc vector data for performance ---
875 PetscReal ***p, ***phi;
876 DMDAVecGetArray(da, user->P, &p);
877 DMDAVecGetArray(da, user->Phi, &phi);
878
879 //================================================================================
880 // Section 2: Core Pressure Update
881 //================================================================================
882 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Performing core pressure update (P_new = P_old + Phi).\n");
883 for (PetscInt k = zs; k < ze; k++) {
884 for (PetscInt j = ys; j < ye; j++) {
885 for (PetscInt i = xs; i < xe; i++) {
886 // This is the fundamental pressure update in a projection method.
887 p[k][j][i] += phi[k][j][i];
888 }
889 }
890 }
891
892 // Restore arrays now that the core computation is done.
893 DMDAVecRestoreArray(da, user->Phi, &phi);
894 DMDAVecRestoreArray(da, user->P, &p);
895
896
897 //================================================================================
898 // Section 3: Handle Periodic Boundary Condition Synchronization
899 //================================================================================
900 // This block is executed only if at least one boundary is periodic.
901 // The original code contained many redundant Get/Restore and update calls.
902 // This refactored version performs the same effective logic but in a single,
903 // efficient pass, which is numerically equivalent and much cleaner.
907 {
908 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Synchronizing ghost cells for periodic boundaries.\n");
909
910 // First, update the local vectors (including ghost regions) with the new global data.
911 DMGlobalToLocalBegin(da, user->P, INSERT_VALUES, user->lP);
912 DMGlobalToLocalEnd(da, user->P, INSERT_VALUES, user->lP);
913 DMGlobalToLocalBegin(da, user->Phi, INSERT_VALUES, user->lPhi);
914 DMGlobalToLocalEnd(da, user->Phi, INSERT_VALUES, user->lPhi);
915
916 // Get pointers to all necessary local and global arrays ONCE.
917 PetscReal ***lp, ***lphi;
918 DMDAVecGetArray(da, user->lP, &lp);
919 DMDAVecGetArray(da, user->lPhi, &lphi);
920 DMDAVecGetArray(da, user->P, &p);
921 DMDAVecGetArray(da, user->Phi, &phi);
922
923 // --- X-Direction Periodic Update ---
925 // Update left boundary physical cells from right boundary ghost cells
926 if (xs == 0) {
927 PetscInt i = 0;
928 for (k = zs; k < ze; k++) {
929 for (j = ys; j < ye; j++) {
930 // Note: Accessing lp[...][i-2] reads from the ghost cell region.
931 p[k][j][i] = lp[k][j][i - 2];
932 phi[k][j][i] = lphi[k][j][i - 2];
933 }
934 }
935 }
936 // Update right boundary physical cells from left boundary ghost cells
937 if (xe == mx) {
938 PetscInt i = mx - 1;
939 for (k = zs; k < ze; k++) {
940 for (j = ys; j < ye; j++) {
941 p[k][j][i] = lp[k][j][i + 2];
942 phi[k][j][i] = lphi[k][j][i + 2];
943 }
944 }
945 }
946 }
947
948 // --- Y-Direction Periodic Update ---
950 // Update bottom boundary
951 if (ys == 0) {
952 PetscInt j = 0;
953 for (k = zs; k < ze; k++) {
954 for (i = xs; i < xe; i++) {
955 p[k][j][i] = lp[k][j - 2][i];
956 phi[k][j][i] = lphi[k][j - 2][i];
957 }
958 }
959 }
960 // Update top boundary
961 if (ye == my) {
962 PetscInt j = my - 1;
963 for (k = zs; k < ze; k++) {
964 for (i = xs; i < xe; i++) {
965 p[k][j][i] = lp[k][j + 2][i];
966 phi[k][j][i] = lphi[k][j + 2][i];
967 }
968 }
969 }
970 }
971
972 // --- Z-Direction Periodic Update ---
974 // Update front boundary
975 if (zs == 0) {
976 PetscInt k = 0;
977 for (j = ys; j < ye; j++) {
978 for (i = xs; i < xe; i++) {
979 p[k][j][i] = lp[k - 2][j][i];
980 phi[k][j][i] = lphi[k - 2][j][i];
981 }
982 }
983 }
984 // Update back boundary
985 if (ze == mz) {
986 PetscInt k = mz - 1;
987 for (j = ys; j < ye; j++) {
988 for (i = xs; i < xe; i++) {
989 p[k][j][i] = lp[k + 2][j][i];
990 phi[k][j][i] = lphi[k + 2][j][i];
991 }
992 }
993 }
994 }
995
996 // Restore all arrays ONCE.
997 DMDAVecRestoreArray(da, user->lP, &lp);
998 DMDAVecRestoreArray(da, user->lPhi, &lphi);
999 DMDAVecRestoreArray(da, user->P, &p);
1000 DMDAVecRestoreArray(da, user->Phi, &phi);
1001
1002 // After manually updating the physical boundary cells, we must call
1003 // DMGlobalToLocal again to ensure all processes have the updated ghost
1004 // values for the *next* function that needs them.
1005 DMGlobalToLocalBegin(da, user->P, INSERT_VALUES, user->lP);
1006 DMGlobalToLocalEnd(da, user->P, INSERT_VALUES, user->lP);
1007 DMGlobalToLocalBegin(da, user->Phi, INSERT_VALUES, user->lPhi);
1008 DMGlobalToLocalEnd(da, user->Phi, INSERT_VALUES, user->lPhi);
1009 }
1010
1011 //================================================================================
1012 // Section 4: Final Cleanup (pointers already restored)
1013 //================================================================================
1014
1015 UpdateLocalGhosts(user,"P");
1016 UpdateLocalGhosts(user,"Phi");
1017
1018 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Exiting UpdatePressure.\n");
1020 PetscFunctionReturn(0);
1021}
PetscErrorCode UpdateLocalGhosts(UserCtx *user, const char *fieldName)
Updates the local vector (including ghost points) from its corresponding global vector.
Definition setup.c:1361
Vec lPhi
Definition variables.h:837
@ BC_FACE_POS_Z
Definition variables.h:247
@ BC_FACE_POS_Y
Definition variables.h:246
@ BC_FACE_POS_X
Definition variables.h:245
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 107 of file poisson.c.

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

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->lP, &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 DMGlobalToLocalBegin(fda, user->Ucont, INSERT_VALUES, user->lUcont);
832 DMGlobalToLocalEnd(fda, user->Ucont, INSERT_VALUES, user->lUcont);
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 Contra2Cart(user);
837 UpdateLocalGhosts(user,"Ucat");
839 //GhostNodeVelocity(user);
840
841 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Exiting Projection step.\n");
843 PetscFunctionReturn(0);
844}
PetscErrorCode RefreshBoundaryGhostCells(UserCtx *user)
(Public) Orchestrates the "light" refresh of all boundary ghost cells after the projection step.
#define LOG_LOOP_ALLOW_EXACT(scope, level, var, val, fmt,...)
Logs a custom message if a variable equals a specific value.
Definition logging.h:334
PetscErrorCode CorrectChannelFluxProfile(UserCtx *user)
Internal helper implementation: CorrectChannelFluxProfile().
Definition poisson.c:107
PetscErrorCode Contra2Cart(UserCtx *user)
Reconstructs Cartesian velocity (Ucat) at cell centers from contravariant velocity (Ucont) defined on...
Definition setup.c:2247
Vec Ucont
Definition variables.h:837
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 1031 of file poisson.c.

1032{
1033 PetscErrorCode ierr;
1034 UserCtx *user = (UserCtx*)ctx;
1035 (void)nullsp;
1036
1037 DM da = user->da;
1038
1039 DMDALocalInfo info = user->info;
1040 PetscInt xs = info.xs, xe = info.xs + info.xm;
1041 PetscInt ys = info.ys, ye = info.ys + info.ym;
1042 PetscInt zs = info.zs, ze = info.zs + info.zm;
1043 PetscInt mx = info.mx, my = info.my, mz = info.mz;
1044 PetscInt lxs, lxe, lys, lye, lzs, lze;
1045
1046 PetscReal ***x, ***nvert;
1047 PetscInt i, j, k;
1048
1049/* /\* First remove a constant from the Vec field X *\/ */
1050
1051
1052 /* Then apply boundary conditions */
1053 DMDAVecGetArray(da, X, &x);
1054 DMDAVecGetArray(da, user->lNvert, &nvert);
1055
1056 lxs = xs; lxe = xe;
1057 lys = ys; lye = ye;
1058 lzs = zs; lze = ze;
1059
1060 if (xs==0) lxs = xs+1;
1061 if (ys==0) lys = ys+1;
1062 if (zs==0) lzs = zs+1;
1063
1064 if (xe==mx) lxe = xe-1;
1065 if (ye==my) lye = ye-1;
1066 if (ze==mz) lze = ze-1;
1067
1068 PetscReal lsum, sum;
1069 PetscReal lnum, num;
1070
1071 if (user->multinullspace) PetscPrintf(PETSC_COMM_WORLD, "MultiNullSpace!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
1072 if (!user->multinullspace) {
1073 lsum = 0;
1074 lnum = 0;
1075 for (k=lzs; k<lze; k++) {
1076 for (j=lys; j<lye; j++) {
1077 for (i=lxs; i<lxe; i++) {
1078 if (nvert[k][j][i] < 0.1) {
1079 lsum += x[k][j][i];
1080 lnum ++;
1081 }
1082 }
1083 }
1084 }
1085
1086 ierr = MPI_Allreduce(&lsum,&sum,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
1087 ierr = MPI_Allreduce(&lnum,&num,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
1088 /* PetscGlobalSum(&lsum, &sum, PETSC_COMM_WORLD); */
1089/* PetscGlobalSum(&lnum, &num, PETSC_COMM_WORLD); */
1090 sum = sum / (-1.0 * num);
1091
1092 for (k=lzs; k<lze; k++) {
1093 for (j=lys; j<lye; j++) {
1094 for (i=lxs; i<lxe; i++) {
1095 if (nvert[k][j][i] < 0.1) {
1096 x[k][j][i] +=sum;
1097 }
1098 }
1099 }
1100 }
1101 }
1102 else {
1103 lsum = 0;
1104 lnum = 0;
1105 for (j=lys; j<lye; j++) {
1106 for (i=lxs; i<lxe; i++) {
1107 for (k=lzs; k<lze; k++) {
1108 if (k<user->KSKE[2*(j*mx+i)] && nvert[k][j][i]<0.1) {
1109 lsum += x[k][j][i];
1110 lnum ++;
1111 }
1112 }
1113 }
1114 }
1115 ierr = MPI_Allreduce(&lsum,&sum,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
1116 ierr = MPI_Allreduce(&lnum,&num,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
1117 /* PetscGlobalSum(&lsum, &sum, PETSC_COMM_WORLD); */
1118/* PetscGlobalSum(&lnum, &num, PETSC_COMM_WORLD); */
1119 sum /= -num;
1120 for (j=lys; j<lye; j++) {
1121 for (i=lxs; i<lxe; i++) {
1122 for (k=lzs; k<lze; k++) {
1123 if (k<user->KSKE[2*(j*mx+i)] && nvert[k][j][i]<0.1) {
1124 x[k][j][i] += sum;
1125 }
1126 }
1127 }
1128 }
1129
1130 lsum = 0;
1131 lnum = 0;
1132 for (j=lys; j<lye; j++) {
1133 for (i=lxs; i<lxe; i++) {
1134 for (k=lzs; k<lze; k++) {
1135 if (k>=user->KSKE[2*(j*mx+i)] && nvert[k][j][i]<0.1) {
1136 lsum += x[k][j][i];
1137 lnum ++;
1138 }
1139 }
1140 }
1141 }
1142 ierr = MPI_Allreduce(&lsum,&sum,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
1143 ierr = MPI_Allreduce(&lnum,&num,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
1144 /* PetscGlobalSum(&lsum, &sum, PETSC_COMM_WORLD); */
1145/* PetscGlobalSum(&lnum, &num, PETSC_COMM_WORLD); */
1146 sum /= -num;
1147 for (j=lys; j<lye; j++) {
1148 for (i=lxs; i<lxe; i++) {
1149 for (k=lzs; k<lze; k++) {
1150 if (k>=user->KSKE[2*(j*mx+i)] && nvert[k][j][i]<0.1) {
1151 x[k][j][i] += sum;
1152 }
1153 }
1154 }
1155 }
1156
1157 } //if multinullspace
1158 if (zs == 0) {
1159 k = 0;
1160 for (j=ys; j<ye; j++) {
1161 for (i=xs; i<xe; i++) {
1162 x[k][j][i] = 0.;
1163 }
1164 }
1165 }
1166
1167 if (ze == mz) {
1168 k = mz-1;
1169 for (j=ys; j<ye; j++) {
1170 for (i=xs; i<xe; i++) {
1171 x[k][j][i] = 0.;
1172 }
1173 }
1174 }
1175
1176 if (ys == 0) {
1177 j = 0;
1178 for (k=zs; k<ze; k++) {
1179 for (i=xs; i<xe; i++) {
1180 x[k][j][i] = 0.;
1181 }
1182 }
1183 }
1184
1185 if (ye == my) {
1186 j = my-1;
1187 for (k=zs; k<ze; k++) {
1188 for (i=xs; i<xe; i++) {
1189 x[k][j][i] = 0.;
1190 }
1191 }
1192 }
1193
1194 if (xs == 0) {
1195 i = 0;
1196 for (k=zs; k<ze; k++) {
1197 for (j=ys; j<ye; j++) {
1198 x[k][j][i] = 0.;
1199 }
1200 }
1201 }
1202
1203 if (xe == mx) {
1204 i = mx-1;
1205 for (k=zs; k<ze; k++) {
1206 for (j=ys; j<ye; j++) {
1207 x[k][j][i] = 0.;
1208 }
1209 }
1210 }
1211
1212 for (k=zs; k<ze; k++) {
1213 for (j=ys; j<ye; j++) {
1214 for (i=xs; i<xe; i++) {
1215 if (nvert[k][j][i] > 0.1)
1216 x[k][j][i] = 0.;
1217 }
1218 }
1219 }
1220 DMDAVecRestoreArray(da, X, &x);
1221 DMDAVecRestoreArray(da, user->lNvert, &nvert);
1222
1223 return 0;
1224}
PetscInt * KSKE
Definition variables.h:850
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 1426 of file poisson.c.

1427{
1428 UserCtx *user;
1429
1430 MatShellGetContext(A, (void**)&user);
1431
1432
1433 DM da = user->da;
1434
1435 DM da_f = *user->da_f;
1436
1437 DMDALocalInfo info;
1438 DMDAGetLocalInfo(da, &info);
1439 PetscInt xs = info.xs, xe = info.xs + info.xm;
1440 PetscInt ys = info.ys, ye = info.ys + info.ym;
1441 PetscInt zs = info.zs, ze = info.zs + info.zm;
1442 PetscInt mx = info.mx, my = info.my, mz = info.mz;
1443 // PetscInt lxs, lxe, lys, lye, lzs, lze;
1444
1445 PetscReal ***f, ***x, ***nvert;
1446 PetscInt i, j, k, ih, jh, kh, ia, ja, ka;
1447
1448 DMDAVecGetArray(da, F, &f);
1449
1450 Vec lX;
1451
1452 DMCreateLocalVector(da_f, &lX);
1453 DMGlobalToLocalBegin(da_f, X, INSERT_VALUES, lX);
1454 DMGlobalToLocalEnd(da_f, X, INSERT_VALUES, lX);
1455 DMDAVecGetArray(da_f, lX, &x);
1456
1457 DMDAVecGetArray(da, user->lNvert, &nvert);
1458
1459 PetscReal ***nvert_f;
1460 DMDAVecGetArray(da_f, user->user_f->lNvert, &nvert_f);
1461
1462 if ((user->isc)) ia = 0;
1463 else ia = 1;
1464
1465 if ((user->jsc)) ja = 0;
1466 else ja = 1;
1467
1468 if ((user->ksc)) ka = 0;
1469 else ka = 1;
1470
1471 for (k=zs; k<ze; k++) {
1472 for (j=ys; j<ye; j++) {
1473 for (i=xs; i<xe; i++) {
1474 if (k==0) {
1475 f[k][j][i] = 0.;
1476 }
1477 else if (k==mz-1) {
1478 f[k][j][i] = 0.;
1479 }
1480 else if (j==0) {
1481 f[k][j][i] = 0.;
1482 }
1483 else if (j==my-1) {
1484 f[k][j][i] = 0.;
1485 }
1486 else if (i==0) {
1487 f[k][j][i] = 0.;
1488 }
1489 else if (i==mx-1) {
1490 f[k][j][i] = 0.;
1491 }
1492 else {
1493 GridRestriction(i, j, k, &ih, &jh, &kh, user);
1494 f[k][j][i] = 0.125 *
1495 (x[kh ][jh ][ih ] * PetscMax(0., 1 - nvert_f[kh ][jh ][ih ]) +
1496 x[kh ][jh ][ih-ia] * PetscMax(0., 1 - nvert_f[kh ][jh ][ih-ia]) +
1497 x[kh ][jh-ja][ih ] * PetscMax(0., 1 - nvert_f[kh ][jh-ja][ih ]) +
1498 x[kh-ka][jh ][ih ] * PetscMax(0., 1 - nvert_f[kh-ka][jh ][ih ]) +
1499 x[kh ][jh-ja][ih-ia] * PetscMax(0., 1 - nvert_f[kh ][jh-ja][ih-ia]) +
1500 x[kh-ka][jh-ja][ih ] * PetscMax(0., 1 - nvert_f[kh-ka][jh-ja][ih ]) +
1501 x[kh-ka][jh ][ih-ia] * PetscMax(0., 1 - nvert_f[kh-ka][jh ][ih-ia]) +
1502 x[kh-ka][jh-ja][ih-ia] * PetscMax(0., 1 - nvert_f[kh-ka][jh-ja][ih-ia]));
1503
1504
1505
1506 if (nvert[k][j][i] > 0.1) f[k][j][i] = 0.;
1507 }
1508 }
1509 }
1510 }
1511
1512
1513 DMDAVecRestoreArray(da_f, user->user_f->lNvert, &nvert_f);
1514
1515 DMDAVecRestoreArray(da_f, lX, &x);
1516 VecDestroy(&lX);
1517
1518 DMDAVecRestoreArray(da, F, &f);
1519 DMDAVecRestoreArray(da, user->lNvert, &nvert);
1520
1521
1522 return 0;
1523}
static PetscErrorCode GridRestriction(PetscInt i, PetscInt j, PetscInt k, PetscInt *ih, PetscInt *jh, PetscInt *kh, UserCtx *user)
Internal helper implementation: GridRestriction().
Definition poisson.c:68
PetscInt isc
Definition variables.h:824
PetscInt ksc
Definition variables.h:824
PetscInt jsc
Definition variables.h:824
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 1233 of file poisson.c.

1234{
1235 UserCtx *user;
1236
1237 MatShellGetContext(A, (void**)&user);
1238
1239
1240
1241 DM da = user->da;
1242
1243 DM da_c = *user->da_c;
1244
1245 DMDALocalInfo info = user->info;
1246 PetscInt xs = info.xs, xe = info.xs + info.xm;
1247 PetscInt ys = info.ys, ye = info.ys + info.ym;
1248 PetscInt zs = info.zs, ze = info.zs + info.zm;
1249 PetscInt mx = info.mx, my = info.my, mz = info.mz;
1250 PetscInt lxs, lxe, lys, lye, lzs, lze;
1251
1252 PetscReal ***f, ***x, ***nvert, ***nvert_c;
1253 PetscInt i, j, k, ic, jc, kc, ia, ja, ka;
1254
1255 lxs = xs; lxe = xe;
1256 lys = ys; lye = ye;
1257 lzs = zs; lze = ze;
1258
1259 if (xs==0) lxs = xs+1;
1260 if (ys==0) lys = ys+1;
1261 if (zs==0) lzs = zs+1;
1262
1263 if (xe==mx) lxe = xe-1;
1264 if (ye==my) lye = ye-1;
1265 if (ze==mz) lze = ze-1;
1266
1267
1268 DMDAVecGetArray(da, F, &f);
1269
1270
1271 Vec lX;
1272 DMCreateLocalVector(da_c, &lX);
1273
1274 DMGlobalToLocalBegin(da_c, X, INSERT_VALUES, lX);
1275 DMGlobalToLocalEnd(da_c, X, INSERT_VALUES, lX);
1276 DMDAVecGetArray(da_c, lX, &x);
1277
1278 DMDAVecGetArray(da, user->lNvert, &nvert);
1279 DMDAVecGetArray(da_c, *(user->lNvert_c), &nvert_c);
1280 for (k=lzs; k<lze; k++) {
1281 for (j=lys; j<lye; j++) {
1282 for (i=lxs; i<lxe; i++) {
1283
1284 GridInterpolation(i, j, k, ic, jc, kc, ia, ja, ka, user);
1285
1286 f[k][j][i] = (x[kc ][jc ][ic ] * 9 +
1287 x[kc ][jc+ja][ic ] * 3 +
1288 x[kc ][jc ][ic+ia] * 3 +
1289 x[kc ][jc+ja][ic+ia]) * 3./64. +
1290 (x[kc+ka][jc ][ic ] * 9 +
1291 x[kc+ka][jc+ja][ic ] * 3 +
1292 x[kc+ka][jc ][ic+ia] * 3 +
1293 x[kc+ka][jc+ja][ic+ia]) /64.;
1294 }
1295 }
1296 }
1297
1298 for (k=zs; k<ze; k++) {
1299 for (j=ys; j<ye; j++) {
1300 for (i=xs; i<xe; i++) {
1301
1302 if (i==0) {
1303 f[k][j][i] = 0.;//-f[k][j][i+1];
1304 }
1305 else if (i==mx-1) {
1306 f[k][j][i] = 0.;//-f[k][j][i-1];
1307 }
1308 else if (j==0) {
1309 f[k][j][i] = 0.;//-f[k][j+1][i];
1310 }
1311 else if (j==my-1) {
1312 f[k][j][i] = 0.;//-f[k][j-1][i];
1313 }
1314 else if (k==0) {
1315 f[k][j][i] = 0.;//-f[k+1][j][i];
1316 }
1317 else if (k==mz-1) {
1318 f[k][j][i] = 0.;//-f[k-1][j][i];
1319 }
1320 if (nvert[k][j][i] > 0.1) f[k][j][i] = 0.;
1321
1322 }
1323 }
1324 }
1325
1326 DMDAVecRestoreArray(da, user->lNvert, &nvert);
1327 DMDAVecRestoreArray(da_c, *(user->lNvert_c), &nvert_c);
1328
1329 DMDAVecRestoreArray(da_c, lX, &x);
1330
1331 VecDestroy(&lX);
1332 DMDAVecRestoreArray(da, F, &f);
1333
1334
1335
1336 return 0;
1337
1338}
#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 2472 of file poisson.c.

2473{
2474 PetscErrorCode ierr;
2475 // --- CONTEXT ACQUISITION BLOCK ---
2476 // Get the master simulation context from the UserCtx.
2477 SimCtx *simCtx = user->simCtx;
2478
2479 // Create local variables to mirror the legacy globals for minimal code changes.
2480 const PetscInt NumberOfBodies = simCtx->NumberOfBodies;
2481 // --- END CONTEXT ACQUISITION BLOCK ---
2482
2483 DM da = user->da, fda = user->fda;
2484
2485 DMDALocalInfo info = user->info;
2486
2487 PetscInt xs = info.xs, xe = info.xs + info.xm;
2488 PetscInt ys = info.ys, ye = info.ys + info.ym;
2489 PetscInt zs = info.zs, ze = info.zs + info.zm;
2490 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2491
2492 PetscInt i, j, k,ibi;
2493 PetscInt lxs, lys, lzs, lxe, lye, lze;
2494
2495 lxs = xs; lxe = xe;
2496 lys = ys; lye = ye;
2497 lzs = zs; lze = ze;
2498
2499 if (xs==0) lxs = xs+1;
2500 if (ys==0) lys = ys+1;
2501 if (zs==0) lzs = zs+1;
2502
2503 if (xe==mx) lxe = xe-1;
2504 if (ye==my) lye = ye-1;
2505 if (ze==mz) lze = ze-1;
2506
2507 PetscReal epsilon=1.e-8;
2508 PetscReal ***nvert, ibmval=1.9999;
2509
2510 struct Components {
2511 PetscReal x;
2512 PetscReal y;
2513 PetscReal z;
2514 }***ucor, ***lucor,***csi, ***eta, ***zet;
2515
2516
2517 PetscInt xend=mx-2 ,yend=my-2,zend=mz-2;
2518
2519 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC) xend=mx-1;
2520 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC) yend=my-1;
2521 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC) zend=mz-1;
2522
2523 DMDAVecGetArray(fda, user->Ucont, &ucor);
2524 DMDAVecGetArray(fda, user->lCsi, &csi);
2525 DMDAVecGetArray(fda, user->lEta, &eta);
2526 DMDAVecGetArray(fda, user->lZet, &zet);
2527 DMDAVecGetArray(da, user->lNvert, &nvert);
2528
2529 PetscReal libm_Flux, libm_area, libm_Flux_abs=0., ibm_Flux_abs;
2530 libm_Flux = 0;
2531 libm_area = 0;
2532
2533 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Entering VolumeFlux to enforce no-penetration condition.\n");
2534
2535 //Mohsen March 2017
2536 PetscReal *lIB_Flux = NULL, *lIB_area = NULL, *IB_Flux = NULL, *IB_Area = NULL;
2537 if (NumberOfBodies > 1) {
2538
2539 lIB_Flux=(PetscReal *)calloc(NumberOfBodies,sizeof(PetscReal));
2540 lIB_area=(PetscReal *)calloc(NumberOfBodies,sizeof(PetscReal));
2541 IB_Flux=(PetscReal *)calloc(NumberOfBodies,sizeof(PetscReal));
2542 IB_Area=(PetscReal *)calloc(NumberOfBodies,sizeof(PetscReal));
2543
2544
2545 for (ibi=0; ibi<NumberOfBodies; ibi++) {
2546 lIB_Flux[ibi]=0.0;
2547 lIB_area[ibi]=0.0;
2548 IB_Flux[ibi]=0.0;
2549 IB_Area[ibi]=0.0;
2550 }
2551 }
2552
2553
2554 //================================================================================
2555 // PASS 1: Calculate Uncorrected Flux and Area
2556 // This pass measures the total fluid "leakage" across the immersed boundary.
2557 //================================================================================
2558 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Pass 1: Measuring uncorrected flux and area.\n");
2559
2560 for (k=lzs; k<lze; k++) {
2561 for (j=lys; j<lye; j++) {
2562 for (i=lxs; i<lxe; i++) {
2563 if (nvert[k][j][i] < 0.1) {
2564 if (nvert[k][j][i+1] > 0.1 && nvert[k][j][i+1] < ibmval && i < xend) {
2565
2566 if (fabs(ucor[k][j][i].x)>epsilon) {
2567 libm_Flux += ucor[k][j][i].x;
2568 if (flg==3)
2569 libm_Flux_abs += fabs(ucor[k][j][i].x)/sqrt(csi[k][j][i].x * csi[k][j][i].x +
2570 csi[k][j][i].y * csi[k][j][i].y +
2571 csi[k][j][i].z * csi[k][j][i].z);
2572 else
2573 libm_Flux_abs += fabs(ucor[k][j][i].x);
2574
2575 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2576 csi[k][j][i].y * csi[k][j][i].y +
2577 csi[k][j][i].z * csi[k][j][i].z);
2578
2579 if (NumberOfBodies > 1) {
2580
2581 ibi=(int)((nvert[k][j][i+1]-1.0)*1001);
2582 lIB_Flux[ibi] += ucor[k][j][i].x;
2583 lIB_area[ibi] += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2584 csi[k][j][i].y * csi[k][j][i].y +
2585 csi[k][j][i].z * csi[k][j][i].z);
2586 }
2587 } else
2588 ucor[k][j][i].x=0.;
2589
2590 }
2591 if (nvert[k][j+1][i] > 0.1 && nvert[k][j+1][i] < ibmval && j < yend) {
2592
2593 if (fabs(ucor[k][j][i].y)>epsilon) {
2594 libm_Flux += ucor[k][j][i].y;
2595 if (flg==3)
2596 libm_Flux_abs += fabs(ucor[k][j][i].y)/sqrt(eta[k][j][i].x * eta[k][j][i].x +
2597 eta[k][j][i].y * eta[k][j][i].y +
2598 eta[k][j][i].z * eta[k][j][i].z);
2599 else
2600 libm_Flux_abs += fabs(ucor[k][j][i].y);
2601 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2602 eta[k][j][i].y * eta[k][j][i].y +
2603 eta[k][j][i].z * eta[k][j][i].z);
2604 if (NumberOfBodies > 1) {
2605
2606 ibi=(int)((nvert[k][j+1][i]-1.0)*1001);
2607
2608 lIB_Flux[ibi] += ucor[k][j][i].y;
2609 lIB_area[ibi] += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2610 eta[k][j][i].y * eta[k][j][i].y +
2611 eta[k][j][i].z * eta[k][j][i].z);
2612 }
2613 } else
2614 ucor[k][j][i].y=0.;
2615 }
2616 if (nvert[k+1][j][i] > 0.1 && nvert[k+1][j][i] < ibmval && k < zend) {
2617
2618 if (fabs(ucor[k][j][i].z)>epsilon) {
2619 libm_Flux += ucor[k][j][i].z;
2620 if (flg==3)
2621 libm_Flux_abs += fabs(ucor[k][j][i].z)/sqrt(zet[k][j][i].x * zet[k][j][i].x +
2622 zet[k][j][i].y * zet[k][j][i].y +
2623 zet[k][j][i].z * zet[k][j][i].z);
2624 else
2625 libm_Flux_abs += fabs(ucor[k][j][i].z);
2626 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2627 zet[k][j][i].y * zet[k][j][i].y +
2628 zet[k][j][i].z * zet[k][j][i].z);
2629
2630 if (NumberOfBodies > 1) {
2631
2632 ibi=(int)((nvert[k+1][j][i]-1.0)*1001);
2633 lIB_Flux[ibi] += ucor[k][j][i].z;
2634 lIB_area[ibi] += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2635 zet[k][j][i].y * zet[k][j][i].y +
2636 zet[k][j][i].z * zet[k][j][i].z);
2637 }
2638 }else
2639 ucor[k][j][i].z=0.;
2640 }
2641 }
2642
2643 if (nvert[k][j][i] > 0.1 && nvert[k][j][i] < ibmval) {
2644
2645 if (nvert[k][j][i+1] < 0.1 && i < xend) {
2646 if (fabs(ucor[k][j][i].x)>epsilon) {
2647 libm_Flux -= ucor[k][j][i].x;
2648 if (flg==3)
2649 libm_Flux_abs += fabs(ucor[k][j][i].x)/sqrt(csi[k][j][i].x * csi[k][j][i].x +
2650 csi[k][j][i].y * csi[k][j][i].y +
2651 csi[k][j][i].z * csi[k][j][i].z);
2652 else
2653 libm_Flux_abs += fabs(ucor[k][j][i].x);
2654 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2655 csi[k][j][i].y * csi[k][j][i].y +
2656 csi[k][j][i].z * csi[k][j][i].z);
2657 if (NumberOfBodies > 1) {
2658
2659 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2660 lIB_Flux[ibi] -= ucor[k][j][i].x;
2661 lIB_area[ibi] += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2662 csi[k][j][i].y * csi[k][j][i].y +
2663 csi[k][j][i].z * csi[k][j][i].z);
2664 }
2665
2666 }else
2667 ucor[k][j][i].x=0.;
2668 }
2669 if (nvert[k][j+1][i] < 0.1 && j < yend) {
2670 if (fabs(ucor[k][j][i].y)>epsilon) {
2671 libm_Flux -= ucor[k][j][i].y;
2672 if (flg==3)
2673 libm_Flux_abs += fabs(ucor[k][j][i].y)/ sqrt(eta[k][j][i].x * eta[k][j][i].x +
2674 eta[k][j][i].y * eta[k][j][i].y +
2675 eta[k][j][i].z * eta[k][j][i].z);
2676 else
2677 libm_Flux_abs += fabs(ucor[k][j][i].y);
2678 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2679 eta[k][j][i].y * eta[k][j][i].y +
2680 eta[k][j][i].z * eta[k][j][i].z);
2681 if (NumberOfBodies > 1) {
2682
2683 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2684 lIB_Flux[ibi] -= ucor[k][j][i].y;
2685 lIB_area[ibi] += 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 }
2689 }else
2690 ucor[k][j][i].y=0.;
2691 }
2692 if (nvert[k+1][j][i] < 0.1 && k < zend) {
2693 if (fabs(ucor[k][j][i].z)>epsilon) {
2694 libm_Flux -= ucor[k][j][i].z;
2695 if (flg==3)
2696 libm_Flux_abs += fabs(ucor[k][j][i].z)/sqrt(zet[k][j][i].x * zet[k][j][i].x +
2697 zet[k][j][i].y * zet[k][j][i].y +
2698 zet[k][j][i].z * zet[k][j][i].z);
2699 else
2700 libm_Flux_abs += fabs(ucor[k][j][i].z);
2701 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2702 zet[k][j][i].y * zet[k][j][i].y +
2703 zet[k][j][i].z * zet[k][j][i].z);
2704 if (NumberOfBodies > 1) {
2705
2706 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2707 lIB_Flux[ibi] -= ucor[k][j][i].z;
2708 lIB_area[ibi] += 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 }
2712 }else
2713 ucor[k][j][i].z=0.;
2714 }
2715 }
2716
2717 }
2718 }
2719 }
2720
2721 ierr = MPI_Allreduce(&libm_Flux, ibm_Flux,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2722 ierr = MPI_Allreduce(&libm_Flux_abs, &ibm_Flux_abs,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2723 ierr = MPI_Allreduce(&libm_area, ibm_Area,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2724
2725 if (NumberOfBodies > 1) {
2726 ierr = MPI_Allreduce(lIB_Flux,IB_Flux,NumberOfBodies,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
2727 ierr = MPI_Allreduce(lIB_area,IB_Area,NumberOfBodies,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD); CHKERRMPI(ierr);
2728 }
2729
2730 PetscReal correction;
2731
2732 PetscReal *Correction = NULL;
2733 if (NumberOfBodies > 1) {
2734 Correction=(PetscReal *)calloc(NumberOfBodies,sizeof(PetscReal));
2735 for (ibi=0; ibi<NumberOfBodies; ibi++) Correction[ibi]=0.0;
2736 }
2737
2738 if (*ibm_Area > 1.e-15) {
2739 if (flg>1)
2740 correction = (*ibm_Flux + user->FluxIntpSum)/ ibm_Flux_abs;
2741 else if (flg)
2742 correction = (*ibm_Flux + user->FluxIntpSum) / *ibm_Area;
2743 else
2744 correction = *ibm_Flux / *ibm_Area;
2745 if (NumberOfBodies > 1)
2746 for (ibi=0; ibi<NumberOfBodies; ibi++) if (IB_Area[ibi]>1.e-15) Correction[ibi] = IB_Flux[ibi] / IB_Area[ibi];
2747 }
2748 else {
2749 correction = 0;
2750 }
2751 // --- Log the uncorrected results and calculated correction ---
2752 LOG_ALLOW(GLOBAL, LOG_INFO, "IBM Uncorrected Flux: %g, Area: %g, Correction: %g\n", *ibm_Flux, *ibm_Area, correction);
2753 if (NumberOfBodies>1){
2754 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]);
2755 }
2756
2757 //================================================================================
2758 // PASS 2: Apply Correction to Velocity Field
2759 // This pass modifies the velocity at the boundary to enforce zero net flux.
2760 //================================================================================
2761 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Pass 2: Applying velocity corrections at the boundary.\n");
2762
2763 for (k=lzs; k<lze; k++) {
2764 for (j=lys; j<lye; j++) {
2765 for (i=lxs; i<lxe; i++) {
2766 if (nvert[k][j][i] < 0.1) {
2767 if (nvert[k][j][i+1] > 0.1 && nvert[k][j][i+1] <ibmval && i < xend) {
2768 if (fabs(ucor[k][j][i].x)>epsilon){
2769 if (flg==3)
2770 ucor[k][j][i].x -=correction*fabs(ucor[k][j][i].x)/
2771 sqrt(csi[k][j][i].x * csi[k][j][i].x +
2772 csi[k][j][i].y * csi[k][j][i].y +
2773 csi[k][j][i].z * csi[k][j][i].z);
2774 else if (flg==2)
2775 ucor[k][j][i].x -=correction*fabs(ucor[k][j][i].x);
2776 else if (NumberOfBodies > 1) {
2777 ibi=(int)((nvert[k][j][i+1]-1.0)*1001);
2778 ucor[k][j][i].x -= sqrt(csi[k][j][i].x * csi[k][j][i].x +
2779 csi[k][j][i].y * csi[k][j][i].y +
2780 csi[k][j][i].z * csi[k][j][i].z) *
2781 Correction[ibi];
2782 }
2783 else
2784 ucor[k][j][i].x -= sqrt(csi[k][j][i].x * csi[k][j][i].x +
2785 csi[k][j][i].y * csi[k][j][i].y +
2786 csi[k][j][i].z * csi[k][j][i].z) *
2787 correction;
2788 }
2789 }
2790 if (nvert[k][j+1][i] > 0.1 && nvert[k][j+1][i] < ibmval && j < yend) {
2791 if (fabs(ucor[k][j][i].y)>epsilon) {
2792 if (flg==3)
2793 ucor[k][j][i].y -=correction*fabs(ucor[k][j][i].y)/
2794 sqrt(eta[k][j][i].x * eta[k][j][i].x +
2795 eta[k][j][i].y * eta[k][j][i].y +
2796 eta[k][j][i].z * eta[k][j][i].z);
2797 else if (flg==2)
2798 ucor[k][j][i].y -=correction*fabs(ucor[k][j][i].y);
2799 else if (NumberOfBodies > 1) {
2800 ibi=(int)((nvert[k][j+1][i]-1.0)*1001);
2801 ucor[k][j][i].y -= sqrt(eta[k][j][i].x * eta[k][j][i].x +
2802 eta[k][j][i].y * eta[k][j][i].y +
2803 eta[k][j][i].z * eta[k][j][i].z) *
2804 Correction[ibi];
2805 }
2806 else
2807 ucor[k][j][i].y -= sqrt(eta[k][j][i].x * eta[k][j][i].x +
2808 eta[k][j][i].y * eta[k][j][i].y +
2809 eta[k][j][i].z * eta[k][j][i].z) *
2810 correction;
2811 }
2812 }
2813 if (nvert[k+1][j][i] > 0.1 && nvert[k+1][j][i] < ibmval && k < zend) {
2814 if (fabs(ucor[k][j][i].z)>epsilon) {
2815 if (flg==3)
2816 ucor[k][j][i].z -= correction*fabs(ucor[k][j][i].z)/
2817 sqrt(zet[k][j][i].x * zet[k][j][i].x +
2818 zet[k][j][i].y * zet[k][j][i].y +
2819 zet[k][j][i].z * zet[k][j][i].z);
2820 else if (flg==2)
2821 ucor[k][j][i].z -= correction*fabs(ucor[k][j][i].z);
2822 else if (NumberOfBodies > 1) {
2823 ibi=(int)((nvert[k+1][j][i]-1.0)*1001);
2824 ucor[k][j][i].z -= sqrt(zet[k][j][i].x * zet[k][j][i].x +
2825 zet[k][j][i].y * zet[k][j][i].y +
2826 zet[k][j][i].z * zet[k][j][i].z) *
2827 Correction[ibi];
2828 }
2829 else
2830 ucor[k][j][i].z -= sqrt(zet[k][j][i].x * zet[k][j][i].x +
2831 zet[k][j][i].y * zet[k][j][i].y +
2832 zet[k][j][i].z * zet[k][j][i].z) *
2833 correction;
2834 }
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 if (fabs(ucor[k][j][i].x)>epsilon) {
2841 if (flg==3)
2842 ucor[k][j][i].x += correction*fabs(ucor[k][j][i].x)/
2843 sqrt(csi[k][j][i].x * csi[k][j][i].x +
2844 csi[k][j][i].y * csi[k][j][i].y +
2845 csi[k][j][i].z * csi[k][j][i].z);
2846 else if (flg==2)
2847 ucor[k][j][i].x += correction*fabs(ucor[k][j][i].x);
2848 else if (NumberOfBodies > 1) {
2849 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2850 ucor[k][j][i].x += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2851 csi[k][j][i].y * csi[k][j][i].y +
2852 csi[k][j][i].z * csi[k][j][i].z) *
2853 Correction[ibi];
2854 }
2855 else
2856 ucor[k][j][i].x += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2857 csi[k][j][i].y * csi[k][j][i].y +
2858 csi[k][j][i].z * csi[k][j][i].z) *
2859 correction;
2860 }
2861 }
2862 if (nvert[k][j+1][i] < 0.1 && j < yend) {
2863 if (fabs(ucor[k][j][i].y)>epsilon) {
2864 if (flg==3)
2865 ucor[k][j][i].y +=correction*fabs(ucor[k][j][i].y)/
2866 sqrt(eta[k][j][i].x * eta[k][j][i].x +
2867 eta[k][j][i].y * eta[k][j][i].y +
2868 eta[k][j][i].z * eta[k][j][i].z);
2869 else if (flg==2)
2870 ucor[k][j][i].y +=correction*fabs(ucor[k][j][i].y);
2871 else if (NumberOfBodies > 1) {
2872 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2873 ucor[k][j][i].y += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2874 eta[k][j][i].y * eta[k][j][i].y +
2875 eta[k][j][i].z * eta[k][j][i].z) *
2876 Correction[ibi];
2877 }
2878 else
2879 ucor[k][j][i].y += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2880 eta[k][j][i].y * eta[k][j][i].y +
2881 eta[k][j][i].z * eta[k][j][i].z) *
2882 correction;
2883 }
2884 }
2885 if (nvert[k+1][j][i] < 0.1 && k < zend) {
2886 if (fabs(ucor[k][j][i].z)>epsilon) {
2887 if (flg==3)
2888 ucor[k][j][i].z += correction*fabs(ucor[k][j][i].z)/
2889 sqrt(zet[k][j][i].x * zet[k][j][i].x +
2890 zet[k][j][i].y * zet[k][j][i].y +
2891 zet[k][j][i].z * zet[k][j][i].z);
2892 else if (flg==2)
2893 ucor[k][j][i].z += correction*fabs(ucor[k][j][i].z);
2894 else if (NumberOfBodies > 1) {
2895 ibi=(int)((nvert[k][j][i]-1.0)*1001);
2896 ucor[k][j][i].z += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2897 zet[k][j][i].y * zet[k][j][i].y +
2898 zet[k][j][i].z * zet[k][j][i].z) *
2899 Correction[ibi];
2900 }
2901 else
2902 ucor[k][j][i].z += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2903 zet[k][j][i].y * zet[k][j][i].y +
2904 zet[k][j][i].z * zet[k][j][i].z) *
2905 correction;
2906 }
2907 }
2908 }
2909
2910 }
2911 }
2912 }
2913
2914 //================================================================================
2915 // PASS 3: Verification
2916 // This optional pass recalculates the flux to confirm the correction was successful.
2917 //================================================================================
2918 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Pass 3: Verifying corrected flux.\n");
2919
2920 libm_Flux = 0;
2921 libm_area = 0;
2922 for (k=lzs; k<lze; k++) {
2923 for (j=lys; j<lye; j++) {
2924 for (i=lxs; i<lxe; i++) {
2925 if (nvert[k][j][i] < 0.1) {
2926 if (nvert[k][j][i+1] > 0.1 && nvert[k][j][i+1] < ibmval && i < xend) {
2927 libm_Flux += ucor[k][j][i].x;
2928 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2929 csi[k][j][i].y * csi[k][j][i].y +
2930 csi[k][j][i].z * csi[k][j][i].z);
2931
2932 }
2933 if (nvert[k][j+1][i] > 0.1 && nvert[k][j+1][i] < ibmval && j < yend) {
2934 libm_Flux += ucor[k][j][i].y;
2935 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2936 eta[k][j][i].y * eta[k][j][i].y +
2937 eta[k][j][i].z * eta[k][j][i].z);
2938 }
2939 if (nvert[k+1][j][i] > 0.1 && nvert[k+1][j][i] < ibmval && k < zend) {
2940 libm_Flux += ucor[k][j][i].z;
2941 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2942 zet[k][j][i].y * zet[k][j][i].y +
2943 zet[k][j][i].z * zet[k][j][i].z);
2944 }
2945 }
2946
2947 if (nvert[k][j][i] > 0.1 && nvert[k][j][i] < ibmval) {
2948 if (nvert[k][j][i+1] < 0.1 && i < xend) {
2949 libm_Flux -= ucor[k][j][i].x;
2950 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2951 csi[k][j][i].y * csi[k][j][i].y +
2952 csi[k][j][i].z * csi[k][j][i].z);
2953
2954 }
2955 if (nvert[k][j+1][i] < 0.1 && j < yend) {
2956 libm_Flux -= ucor[k][j][i].y;
2957 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2958 eta[k][j][i].y * eta[k][j][i].y +
2959 eta[k][j][i].z * eta[k][j][i].z);
2960 }
2961 if (nvert[k+1][j][i] < 0.1 && k < zend) {
2962 libm_Flux -= ucor[k][j][i].z;
2963 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2964 zet[k][j][i].y * zet[k][j][i].y +
2965 zet[k][j][i].z * zet[k][j][i].z);
2966 }
2967 }
2968
2969 }
2970 }
2971 }
2972
2973 ierr = MPI_Allreduce(&libm_Flux, ibm_Flux,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2974 ierr = MPI_Allreduce(&libm_area, ibm_Area,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2975
2976 /* PetscGlobalSum(&libm_Flux, ibm_Flux, PETSC_COMM_WORLD); */
2977/* PetscGlobalSum(&libm_area, ibm_Area, PETSC_COMM_WORLD); */
2978 LOG_ALLOW(GLOBAL, LOG_INFO, "IBM Corrected (Verified) Flux: %g, Area: %g\n", *ibm_Flux, *ibm_Area);
2979
2980
2982 if (xe==mx){
2983 i=mx-2;
2984 for (k=lzs; k<lze; k++) {
2985 for (j=lys; j<lye; j++) {
2986 // if(j>0 && k>0 && j<user->JM && k<user->KM){
2987 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;
2988
2989 // }
2990 }
2991 }
2992 }
2993 }
2994
2996 if (ye==my){
2997 j=my-2;
2998 for (k=lzs; k<lze; k++) {
2999 for (i=lxs; i<lxe; i++) {
3000 // if(i>0 && k>0 && i<user->IM && k<user->KM){
3001 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;
3002 // }
3003 }
3004 }
3005 }
3006 }
3007
3009 if (ze==mz){
3010 k=mz-2;
3011 for (j=lys; j<lye; j++) {
3012 for (i=lxs; i<lxe; i++) {
3013 // if(i>0 && j>0 && i<user->IM && j<user->JM){
3014 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;
3015 // }
3016 }
3017 }
3018 }
3019 }
3020
3021
3022 DMDAVecRestoreArray(da, user->lNvert, &nvert);
3023 DMDAVecRestoreArray(fda, user->lCsi, &csi);
3024 DMDAVecRestoreArray(fda, user->lEta, &eta);
3025 DMDAVecRestoreArray(fda, user->lZet, &zet);
3026 DMDAVecRestoreArray(fda, user->Ucont, &ucor);
3027
3028 DMGlobalToLocalBegin(fda, user->Ucont, INSERT_VALUES, user->lUcont);
3029 DMGlobalToLocalEnd(fda, user->Ucont, INSERT_VALUES, user->lUcont);
3030
3031 /* periodic boundary condition update corrected flux */
3032 // Mohsen Dec 2015 (kept for transition safety; modern periodic helper path is also active)
3033 PetscBool has_periodic_ucont_seam =
3040 PetscInt periodic_seam_updates_local = 0, periodic_seam_updates_global = 0;
3041 PetscReal periodic_seam_max_delta_local = 0.0, periodic_seam_max_delta_global = 0.0;
3042
3043 if (has_periodic_ucont_seam) {
3044 LOG_ALLOW(GLOBAL, LOG_DEBUG, "VolumeFlux: entering legacy periodic Ucont seam refresh block.\n");
3045 }
3046
3047 DMDAVecGetArray(fda, user->lUcont, &lucor);
3048 DMDAVecGetArray(fda, user->Ucont, &ucor);
3049
3051 if (xs==0){
3052 i=xs;
3053 for (k=zs; k<ze; k++) {
3054 for (j=ys; j<ye; j++) {
3055 if(j>0 && k>0 && j<user->JM && k<user->KM){
3056 PetscReal old_val = ucor[k][j][i].x;
3057 PetscReal new_val = lucor[k][j][i-2].x;
3058 PetscReal delta = PetscAbsReal(new_val - old_val);
3059 ucor[k][j][i].x = new_val;
3060 if (delta > 1.0e-14) periodic_seam_updates_local++;
3061 periodic_seam_max_delta_local = PetscMax(periodic_seam_max_delta_local, delta);
3062 }
3063 }
3064 }
3065 }
3066 }
3068 if (ys==0){
3069 j=ys;
3070 for (k=zs; k<ze; k++) {
3071 for (i=xs; i<xe; i++) {
3072 if(i>0 && k>0 && i<user->IM && k<user->KM){
3073 PetscReal old_val = ucor[k][j][i].y;
3074 PetscReal new_val = lucor[k][j-2][i].y;
3075 PetscReal delta = PetscAbsReal(new_val - old_val);
3076 ucor[k][j][i].y = new_val;
3077 if (delta > 1.0e-14) periodic_seam_updates_local++;
3078 periodic_seam_max_delta_local = PetscMax(periodic_seam_max_delta_local, delta);
3079 }
3080 }
3081 }
3082 }
3083 }
3085 if (zs==0){
3086 k=zs;
3087 for (j=ys; j<ye; j++) {
3088 for (i=xs; i<xe; i++) {
3089 if(i>0 && j>0 && i<user->IM && j<user->JM){
3090 PetscReal old_val = ucor[k][j][i].z;
3091 PetscReal new_val = lucor[k-2][j][i].z;
3092 PetscReal delta = PetscAbsReal(new_val - old_val);
3093 ucor[k][j][i].z = new_val;
3094 if (delta > 1.0e-14) periodic_seam_updates_local++;
3095 periodic_seam_max_delta_local = PetscMax(periodic_seam_max_delta_local, delta);
3096 }
3097 }
3098 }
3099 }
3100 }
3101 DMDAVecRestoreArray(fda, user->lUcont, &lucor);
3102 DMDAVecRestoreArray(fda, user->Ucont, &ucor);
3103
3104 if (has_periodic_ucont_seam) {
3105 ierr = MPI_Allreduce(&periodic_seam_updates_local, &periodic_seam_updates_global, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
3106 ierr = MPI_Allreduce(&periodic_seam_max_delta_local, &periodic_seam_max_delta_global, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRMPI(ierr);
3108 "VolumeFlux: legacy periodic Ucont seam refresh updated %d values, max |delta|=%.6e.\n",
3109 (int)periodic_seam_updates_global, periodic_seam_max_delta_global);
3110 }
3111
3112 /* DMLocalToGlobalBegin(user->fda, user->lUcont, INSERT_VALUES, user->Ucont); */
3113/* DMLocalToGlobalEnd(user->fda, user->lUcont, INSERT_VALUES, user->Ucont); */
3114 DMGlobalToLocalBegin(user->fda, user->Ucont, INSERT_VALUES, user->lUcont);
3115 DMGlobalToLocalEnd(user->fda, user->Ucont, INSERT_VALUES, user->lUcont);
3116
3117 if (NumberOfBodies > 1) {
3118 free(lIB_Flux);
3119 free(lIB_area);
3120 free(IB_Flux);
3121 free(IB_Area);
3122 free(Correction);
3123 }
3124
3125 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Exiting VolumeFlux.\n");
3126
3127 return 0;
3128}
PetscInt NumberOfBodies
Definition variables.h:703
PetscReal FluxIntpSum
Definition variables.h:834
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 2230 of file poisson.c.

2232{
2233 PetscErrorCode ierr;
2234
2235 DM da = user->da, fda = user->fda;
2236
2237 DMDALocalInfo info = user->info;
2238
2239 PetscInt xs = info.xs, xe = info.xs + info.xm;
2240 PetscInt ys = info.ys, ye = info.ys + info.ym;
2241 PetscInt zs = info.zs, ze = info.zs + info.zm;
2242 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2243
2244 PetscInt i, j, k;
2245 PetscInt lxs, lys, lzs, lxe, lye, lze;
2246
2247 lxs = xs; lxe = xe;
2248 lys = ys; lye = ye;
2249 lzs = zs; lze = ze;
2250
2251 if (xs==0) lxs = xs+1;
2252 if (ys==0) lys = ys+1;
2253 if (zs==0) lzs = zs+1;
2254
2255 if (xe==mx) lxe = xe-1;
2256 if (ye==my) lye = ye-1;
2257 if (ze==mz) lze = ze-1;
2258
2259 PetscReal ***nvert, ibmval=1.5;
2260 Cmpnts ***ucor, ***csi, ***eta, ***zet;
2261 DMDAVecGetArray(fda, user->Ucont, &ucor);
2262 DMDAVecGetArray(fda, user->lCsi, &csi);
2263 DMDAVecGetArray(fda, user->lEta, &eta);
2264 DMDAVecGetArray(fda, user->lZet, &zet);
2265 DMDAVecGetArray(da, user->lNvert, &nvert);
2266
2267 PetscReal libm_Flux, libm_area;
2268 libm_Flux = 0;
2269 libm_area = 0;
2270 for (k=lzs; k<lze; k++) {
2271 for (j=lys; j<lye; j++) {
2272 for (i=lxs; i<lxe; i++) {
2273 if (nvert[k][j][i] < 0.1) {
2274 if (nvert[k][j][i+1] > ibmval-0.4 && nvert[k][j][i+1] < ibmval && i < mx-2) {
2275 libm_Flux += ucor[k][j][i].x;
2276 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2277 csi[k][j][i].y * csi[k][j][i].y +
2278 csi[k][j][i].z * csi[k][j][i].z);
2279
2280 }
2281 if (nvert[k][j+1][i] > ibmval-0.4 && nvert[k][j+1][i] < ibmval && j < my-2) {
2282 libm_Flux += ucor[k][j][i].y;
2283 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2284 eta[k][j][i].y * eta[k][j][i].y +
2285 eta[k][j][i].z * eta[k][j][i].z);
2286 }
2287 if (nvert[k+1][j][i] > ibmval-0.4 && nvert[k+1][j][i] < ibmval && k < mz-2) {
2288 libm_Flux += ucor[k][j][i].z;
2289 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2290 zet[k][j][i].y * zet[k][j][i].y +
2291 zet[k][j][i].z * zet[k][j][i].z);
2292 }
2293 }
2294
2295 if (nvert[k][j][i] > ibmval-0.4 && nvert[k][j][i] < ibmval) {
2296 if (nvert[k][j][i+1] < 0.1 && i < mx-2) {
2297 libm_Flux -= ucor[k][j][i].x;
2298 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2299 csi[k][j][i].y * csi[k][j][i].y +
2300 csi[k][j][i].z * csi[k][j][i].z);
2301
2302 }
2303 if (nvert[k][j+1][i] < 0.1 && j < my-2) {
2304 libm_Flux -= ucor[k][j][i].y;
2305 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2306 eta[k][j][i].y * eta[k][j][i].y +
2307 eta[k][j][i].z * eta[k][j][i].z);
2308 }
2309 if (nvert[k+1][j][i] < 0.1 && k < mz-2) {
2310 libm_Flux -= ucor[k][j][i].z;
2311 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2312 zet[k][j][i].y * zet[k][j][i].y +
2313 zet[k][j][i].z * zet[k][j][i].z);
2314 }
2315 }
2316
2317 }
2318 }
2319 }
2320
2321 ierr = MPI_Allreduce(&libm_Flux, ibm_Flux,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2322 ierr = MPI_Allreduce(&libm_area, ibm_Area,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2323
2324 /* PetscGlobalSum(&libm_Flux, ibm_Flux, PETSC_COMM_WORLD); */
2325/* PetscGlobalSum(&libm_area, ibm_Area, PETSC_COMM_WORLD); */
2326 PetscPrintf(PETSC_COMM_WORLD, "IBMFlux REV %le %le\n", *ibm_Flux, *ibm_Area);
2327
2328 PetscReal correction;
2329
2330 if (*ibm_Area > 1.e-15) {
2331 if (flg)
2332 correction = (*ibm_Flux + user->FluxIntpSum) / *ibm_Area;
2333 else
2334 correction = *ibm_Flux / *ibm_Area;
2335 }
2336 else {
2337 correction = 0;
2338 }
2339
2340 for (k=lzs; k<lze; k++) {
2341 for (j=lys; j<lye; j++) {
2342 for (i=lxs; i<lxe; i++) {
2343 if (nvert[k][j][i] < 0.1) {
2344 if (nvert[k][j][i+1] > ibmval-0.4 && nvert[k][j][i+1] < ibmval && i < mx-2) {
2345 ucor[k][j][i].x -= sqrt(csi[k][j][i].x * csi[k][j][i].x +
2346 csi[k][j][i].y * csi[k][j][i].y +
2347 csi[k][j][i].z * csi[k][j][i].z) *
2348 correction;
2349
2350 }
2351 if (nvert[k][j+1][i] > ibmval-0.4 && nvert[k][j+1][i] < ibmval && j < my-2) {
2352 ucor[k][j][i].y -= sqrt(eta[k][j][i].x * eta[k][j][i].x +
2353 eta[k][j][i].y * eta[k][j][i].y +
2354 eta[k][j][i].z * eta[k][j][i].z) *
2355 correction;
2356 }
2357 if (nvert[k+1][j][i] > ibmval-0.4 && nvert[k+1][j][i] < ibmval && k < mz-2) {
2358 ucor[k][j][i].z -= sqrt(zet[k][j][i].x * zet[k][j][i].x +
2359 zet[k][j][i].y * zet[k][j][i].y +
2360 zet[k][j][i].z * zet[k][j][i].z) *
2361 correction;
2362 }
2363 }
2364
2365 if (nvert[k][j][i] > ibmval-0.4 && nvert[k][j][i] < ibmval) {
2366 if (nvert[k][j][i+1] < 0.1 && i < mx-2) {
2367 ucor[k][j][i].x += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2368 csi[k][j][i].y * csi[k][j][i].y +
2369 csi[k][j][i].z * csi[k][j][i].z) *
2370 correction;
2371
2372 }
2373 if (nvert[k][j+1][i] < 0.1 && j < my-2) {
2374 ucor[k][j][i].y += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2375 eta[k][j][i].y * eta[k][j][i].y +
2376 eta[k][j][i].z * eta[k][j][i].z) *
2377 correction;
2378 }
2379 if (nvert[k+1][j][i] < 0.1 && k < mz-2) {
2380 ucor[k][j][i].z += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2381 zet[k][j][i].y * zet[k][j][i].y +
2382 zet[k][j][i].z * zet[k][j][i].z) *
2383 correction;
2384 }
2385 }
2386
2387 }
2388 }
2389 }
2390
2391
2392
2393 libm_Flux = 0;
2394 libm_area = 0;
2395 for (k=lzs; k<lze; k++) {
2396 for (j=lys; j<lye; j++) {
2397 for (i=lxs; i<lxe; i++) {
2398 if (nvert[k][j][i] < 0.1) {
2399 if (nvert[k][j][i+1] > ibmval-0.4 && nvert[k][j][i+1] < ibmval && i < mx-2) {
2400 libm_Flux += ucor[k][j][i].x;
2401 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2402 csi[k][j][i].y * csi[k][j][i].y +
2403 csi[k][j][i].z * csi[k][j][i].z);
2404
2405 }
2406 if (nvert[k][j+1][i] > ibmval-0.4 && nvert[k][j+1][i] < ibmval && j < my-2) {
2407 libm_Flux += ucor[k][j][i].y;
2408 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2409 eta[k][j][i].y * eta[k][j][i].y +
2410 eta[k][j][i].z * eta[k][j][i].z);
2411 }
2412 if (nvert[k+1][j][i] > ibmval-0.4 && nvert[k+1][j][i] < ibmval && k < mz-2) {
2413 libm_Flux += ucor[k][j][i].z;
2414 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2415 zet[k][j][i].y * zet[k][j][i].y +
2416 zet[k][j][i].z * zet[k][j][i].z);
2417 }
2418 }
2419
2420 if (nvert[k][j][i] > ibmval-0.4 && nvert[k][j][i] < ibmval) {
2421 if (nvert[k][j][i+1] < 0.1 && i < mx-2) {
2422 libm_Flux -= ucor[k][j][i].x;
2423 libm_area += sqrt(csi[k][j][i].x * csi[k][j][i].x +
2424 csi[k][j][i].y * csi[k][j][i].y +
2425 csi[k][j][i].z * csi[k][j][i].z);
2426
2427 }
2428 if (nvert[k][j+1][i] < 0.1 && j < my-2) {
2429 libm_Flux -= ucor[k][j][i].y;
2430 libm_area += sqrt(eta[k][j][i].x * eta[k][j][i].x +
2431 eta[k][j][i].y * eta[k][j][i].y +
2432 eta[k][j][i].z * eta[k][j][i].z);
2433 }
2434 if (nvert[k+1][j][i] < 0.1 && k < mz-2) {
2435 libm_Flux -= ucor[k][j][i].z;
2436 libm_area += sqrt(zet[k][j][i].x * zet[k][j][i].x +
2437 zet[k][j][i].y * zet[k][j][i].y +
2438 zet[k][j][i].z * zet[k][j][i].z);
2439 }
2440 }
2441
2442 }
2443 }
2444 }
2445
2446 ierr = MPI_Allreduce(&libm_Flux, ibm_Flux,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2447 ierr = MPI_Allreduce(&libm_area, ibm_Area,1,MPI_DOUBLE,MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
2448
2449 /* PetscGlobalSum(&libm_Flux, ibm_Flux, PETSC_COMM_WORLD); */
2450/* PetscGlobalSum(&libm_area, ibm_Area, PETSC_COMM_WORLD); */
2451 PetscPrintf(PETSC_COMM_WORLD, "IBMFlux22 REV %le %le\n", *ibm_Flux, *ibm_Area);
2452
2453 DMDAVecRestoreArray(da, user->lNvert, &nvert);
2454 DMDAVecRestoreArray(fda, user->lCsi, &csi);
2455 DMDAVecRestoreArray(fda, user->lEta, &eta);
2456 DMDAVecRestoreArray(fda, user->lZet, &zet);
2457 DMDAVecRestoreArray(fda, user->Ucont, &ucor);
2458
2459 DMGlobalToLocalBegin(fda, user->Ucont, INSERT_VALUES, user->lUcont);
2460 DMGlobalToLocalEnd(fda, user->Ucont, INSERT_VALUES, user->lUcont);
2461 return 0;
2462}
Here is the caller graph for this function: