PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Macros | Functions
poisson.c File Reference
#include "poisson.h"
#include "logging.h"
#include "solvers.h"
Include dependency graph for poisson.c:

Go to the source code of this file.

Macros

#define GridInterpolation(i, j, k, ic, jc, kc, ia, ja, ka, user)
 
#define __FUNCT__   "GridRestriction"
 
#define __FUNCT__   "CorrectChannelFluxProfile"
 
#define CP   0
 
#define EP   1
 
#define WP   2
 
#define NP   3
 
#define SP   4
 
#define TP   5
 
#define BP   6
 
#define NE   7
 
#define SE   8
 
#define NW   9
 
#define SW   10
 
#define TN   11
 
#define BN   12
 
#define TS   13
 
#define BS   14
 
#define TE   15
 
#define BE   16
 
#define TW   17
 
#define BW   18
 
#define __FUNCT__   "Projection"
 
#define __FUNCT__   "UpdatePressure"
 
#define __FUNCT__   "PoissonNullSpaceFunction"
 
#define __FUNCT__   "PoissonLHSNew"
 
#define __FUNCT__   "PoissonRHS"
 
#define __FUNCT__   "PoissonSolver_MG"
 

Functions

static PetscInt Gidx (PetscInt i, PetscInt j, PetscInt k, UserCtx *user)
 Convert local logical indices to the corresponding flattened global cell identifier.
 
static PetscErrorCode GridRestriction (PetscInt i, PetscInt j, PetscInt k, PetscInt *ih, PetscInt *jh, PetscInt *kh, UserCtx *user)
 Restrict a fine-grid scalar value onto its associated coarse-grid location.
 
PetscErrorCode CorrectChannelFluxProfile (UserCtx *user)
 Internal helper implementation: CorrectChannelFluxProfile().
 
PetscErrorCode Projection (UserCtx *user)
 Implementation of Projection().
 
PetscErrorCode UpdatePressure (UserCtx *user)
 Implementation of UpdatePressure().
 
PetscErrorCode PoissonNullSpaceFunction (MatNullSpace nullsp, Vec X, void *ctx)
 Implementation of PoissonNullSpaceFunction().
 
PetscErrorCode MyInterpolation (Mat A, Vec X, Vec F)
 Implementation of MyInterpolation().
 
static PetscErrorCode RestrictResidual_SolidAware (Mat A, Vec X, Vec F)
 Restrict residuals while accounting for solid-cell occupancy in the stencil.
 
PetscErrorCode MyRestriction (Mat A, Vec X, Vec F)
 Implementation of MyRestriction().
 
PetscErrorCode PoissonLHSNew (UserCtx *user)
 Internal helper implementation: PoissonLHSNew().
 
PetscErrorCode PoissonRHS (UserCtx *user, Vec B)
 Implementation of PoissonRHS().
 
PetscErrorCode VolumeFlux_rev (UserCtx *user, PetscReal *ibm_Flux, PetscReal *ibm_Area, PetscInt flg)
 Implementation of VolumeFlux_rev().
 
PetscErrorCode VolumeFlux (UserCtx *user, PetscReal *ibm_Flux, PetscReal *ibm_Area, PetscInt flg)
 Implementation of VolumeFlux().
 
static PetscErrorCode FullyBlocked (UserCtx *user)
 Report whether a coarse-grid cell is completely blocked by solid fine-grid cells.
 
static PetscErrorCode MyNvertRestriction (UserCtx *user_h, UserCtx *user_c)
 Restrict solid-volume fractions from fine cells to one coarse cell.
 
PetscErrorCode PoissonSolver_MG (UserMG *usermg)
 Implementation of PoissonSolver_MG().
 

Macro Definition Documentation

◆ GridInterpolation

#define GridInterpolation (   i,
  j,
  k,
  ic,
  jc,
  kc,
  ia,
  ja,
  ka,
  user 
)

Definition at line 5 of file poisson.c.

6 { \
7 ic = i; \
8 ia = 0; \
9 } \
10 else { \
11 ic = (i+1) / 2; \
12 ia = (i - 2 * (ic)) == 0 ? 1 : -1; \
13 if (i==1 || i==mx-2) ia = 0; \
14 }\
15 if ((user->jsc)) { \
16 jc = j; \
17 ja = 0; \
18 } \
19 else { \
20 jc = (j+1) / 2; \
21 ja = (j - 2 * (jc)) == 0 ? 1 : -1; \
22 if (j==1 || j==my-2) ja = 0; \
23 } \
24 if ((user->ksc)) { \
25 kc = k; \
26 ka = 0; \
27 } \
28 else { \
29 kc = (k+1) / 2; \
30 ka = (k - 2 * (kc)) == 0 ? 1 : -1; \
31 if (k==1 || k==mz-2) ka = 0; \
32 } \
33 if (ka==-1 && nvert_c[kc-1][jc][ic] > 0.1) ka = 0; \
34 else if (ka==1 && nvert_c[kc+1][jc][ic] > 0.1) ka = 0; \
35 if (ja==-1 && nvert_c[kc][jc-1][ic] > 0.1) ja = 0; \
36 else if (ja==1 && nvert_c[kc][jc+1][ic] > 0.1) ja = 0; \
37 if (ia==-1 && nvert_c[kc][jc][ic-1] > 0.1) ia = 0; \
38 else if (ia==1 && nvert_c[kc][jc][ic+1] > 0.1) ia = 0;

◆ __FUNCT__ [1/8]

#define __FUNCT__   "GridRestriction"

Definition at line 61 of file poisson.c.

◆ __FUNCT__ [2/8]

#define __FUNCT__   "CorrectChannelFluxProfile"

Definition at line 61 of file poisson.c.

◆ CP

#define CP   0

Definition at line 295 of file poisson.c.

◆ EP

#define EP   1

Definition at line 297 of file poisson.c.

◆ WP

#define WP   2

Definition at line 298 of file poisson.c.

◆ NP

#define NP   3

Definition at line 299 of file poisson.c.

◆ SP

#define SP   4

Definition at line 300 of file poisson.c.

◆ TP

#define TP   5

Definition at line 301 of file poisson.c.

◆ BP

#define BP   6

Definition at line 302 of file poisson.c.

◆ NE

#define NE   7

Definition at line 305 of file poisson.c.

◆ SE

#define SE   8

Definition at line 306 of file poisson.c.

◆ NW

#define NW   9

Definition at line 307 of file poisson.c.

◆ SW

#define SW   10

Definition at line 308 of file poisson.c.

◆ TN

#define TN   11

Definition at line 309 of file poisson.c.

◆ BN

#define BN   12

Definition at line 310 of file poisson.c.

◆ TS

#define TS   13

Definition at line 311 of file poisson.c.

◆ BS

#define BS   14

Definition at line 312 of file poisson.c.

◆ TE

#define TE   15

Definition at line 313 of file poisson.c.

◆ BE

#define BE   16

Definition at line 314 of file poisson.c.

◆ TW

#define TW   17

Definition at line 315 of file poisson.c.

◆ BW

#define BW   18

Definition at line 316 of file poisson.c.

◆ __FUNCT__ [3/8]

#define __FUNCT__   "Projection"

Definition at line 61 of file poisson.c.

◆ __FUNCT__ [4/8]

#define __FUNCT__   "UpdatePressure"

Definition at line 61 of file poisson.c.

◆ __FUNCT__ [5/8]

#define __FUNCT__   "PoissonNullSpaceFunction"

Definition at line 61 of file poisson.c.

◆ __FUNCT__ [6/8]

#define __FUNCT__   "PoissonLHSNew"

Definition at line 61 of file poisson.c.

◆ __FUNCT__ [7/8]

#define __FUNCT__   "PoissonRHS"

Definition at line 61 of file poisson.c.

◆ __FUNCT__ [8/8]

#define __FUNCT__   "PoissonSolver_MG"

Definition at line 61 of file poisson.c.

Function Documentation

◆ Gidx()

static PetscInt Gidx ( PetscInt  i,
PetscInt  j,
PetscInt  k,
UserCtx user 
)
static

Convert local logical indices to the corresponding flattened global cell identifier.

Definition at line 43 of file poisson.c.

45{
46 PetscInt nidx;
47 DMDALocalInfo info = user->info;
48
49 PetscInt mx = info.mx, my = info.my;
50
51 AO ao;
52 DMDAGetAO(user->da, &ao);
53 nidx=i+j*mx+k*mx*my;
54
55 AOApplicationToPetsc(ao,1,&nidx);
56
57 return (nidx);
58}
DMDALocalInfo info
Definition variables.h:918
Here is the caller graph for this function:

◆ GridRestriction()

static PetscErrorCode GridRestriction ( PetscInt  i,
PetscInt  j,
PetscInt  k,
PetscInt *  ih,
PetscInt *  jh,
PetscInt *  kh,
UserCtx user 
)
static

Restrict a fine-grid scalar value onto its associated coarse-grid location.

Definition at line 66 of file poisson.c.

69{
70 PetscFunctionBeginUser;
72 if ((user->isc)) {
73 *ih = i;
74 }
75 else {
76 *ih = 2 * i;
77 }
78
79 if ((user->jsc)) {
80 *jh = j;
81 }
82 else {
83 *jh = 2 * j;
84 }
85
86 if ((user->ksc)) {
87 *kh = k;
88 }
89 else {
90 *kh = 2 * k;
91 }
92
94 PetscFunctionReturn(0);
95}
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:859
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:850
PetscInt isc
Definition variables.h:924
PetscInt ksc
Definition variables.h:924
PetscInt jsc
Definition variables.h:924
Here is the caller graph for this function:

◆ CorrectChannelFluxProfile()

PetscErrorCode CorrectChannelFluxProfile ( UserCtx user)

Internal helper implementation: CorrectChannelFluxProfile().

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

Local to this translation unit.

Definition at line 105 of file poisson.c.

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

◆ Projection()

PetscErrorCode Projection ( UserCtx user)

Implementation of Projection().

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

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

See also
Projection()

Definition at line 326 of file poisson.c.

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

◆ UpdatePressure()

PetscErrorCode UpdatePressure ( UserCtx user)

Implementation of UpdatePressure().

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

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

See also
UpdatePressure()

Definition at line 853 of file poisson.c.

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

◆ PoissonNullSpaceFunction()

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

Implementation of PoissonNullSpaceFunction().

The callback function for PETSc's MatNullSpace object.

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

See also
PoissonNullSpaceFunction()

Definition at line 921 of file poisson.c.

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

◆ MyInterpolation()

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

Implementation of MyInterpolation().

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

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

See also
MyInterpolation()

Definition at line 1125 of file poisson.c.

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

◆ RestrictResidual_SolidAware()

static PetscErrorCode RestrictResidual_SolidAware ( Mat  A,
Vec  X,
Vec  F 
)
static

Restrict residuals while accounting for solid-cell occupancy in the stencil.

Definition at line 1235 of file poisson.c.

1236{
1237 UserCtx *user;
1238 MatShellGetContext(A, (void**)&user);
1239
1240 DM da = user->da;
1241 DM da_f = *user->da_f;
1242
1243 DMDALocalInfo info;
1244 DMDAGetLocalInfo(da, &info);
1245 PetscInt xs = info.xs, xe = info.xs + info.xm;
1246 PetscInt ys = info.ys, ye = info.ys + info.ym;
1247 PetscInt zs = info.zs, ze = info.zs + info.zm;
1248 PetscInt mx = info.mx, my = info.my, mz = info.mz;
1249
1250 PetscReal ***f, ***x, ***nvert;
1251 PetscInt i, j, k, ih, jh, kh, ia, ja, ka;
1252
1253 DMDAVecGetArray(da, F, &f);
1254
1255 Vec lX;
1256 DMCreateLocalVector(da_f, &lX);
1257 DMGlobalToLocalBegin(da_f, X, INSERT_VALUES, lX);
1258 DMGlobalToLocalEnd(da_f, X, INSERT_VALUES, lX);
1259 DMDAVecGetArray(da_f, lX, &x);
1260
1261 DMDAVecGetArray(da, user->lNvert, &nvert);
1262
1263 PetscReal ***nvert_f;
1264 DMDAVecGetArray(da_f, user->user_f->lNvert, &nvert_f);
1265
1266 if ((user->isc)) ia = 0;
1267 else ia = 1;
1268
1269 if ((user->jsc)) ja = 0;
1270 else ja = 1;
1271
1272 if ((user->ksc)) ka = 0;
1273 else ka = 1;
1274
1275 for (k=zs; k<ze; k++) {
1276 for (j=ys; j<ye; j++) {
1277 for (i=xs; i<xe; i++) {
1278 // --- CORRECTED LOGIC ---
1279 // First, check if the current point is a boundary point.
1280 // If it is, it does not contribute to the coarse grid residual.
1281 if (i==0 || i==mx-1 || j==0 || j==my-1 || k==0 || k==mz-1 || nvert[k][j][i] > 0.1) {
1282 f[k][j][i] = 0.0;
1283 }
1284 // Only if it's a true interior fluid point, perform the restriction.
1285 else {
1286 GridRestriction(i, j, k, &ih, &jh, &kh, user);
1287 f[k][j][i] = 0.125 *
1288 (x[kh ][jh ][ih ] * PetscMax(0., 1 - nvert_f[kh ][jh ][ih ]) +
1289 x[kh ][jh ][ih-ia] * PetscMax(0., 1 - nvert_f[kh ][jh ][ih-ia]) +
1290 x[kh ][jh-ja][ih ] * PetscMax(0., 1 - nvert_f[kh ][jh-ja][ih ]) +
1291 x[kh-ka][jh ][ih ] * PetscMax(0., 1 - nvert_f[kh-ka][jh ][ih ]) +
1292 x[kh ][jh-ja][ih-ia] * PetscMax(0., 1 - nvert_f[kh ][jh-ja][ih-ia]) +
1293 x[kh-ka][jh-ja][ih ] * PetscMax(0., 1 - nvert_f[kh-ka][jh-ja][ih ]) +
1294 x[kh-ka][jh ][ih-ia] * PetscMax(0., 1 - nvert_f[kh-ka][jh ][ih-ia]) +
1295 x[kh-ka][jh-ja][ih-ia] * PetscMax(0., 1 - nvert_f[kh-ka][jh-ja][ih-ia]));
1296 }
1297 }
1298 }
1299 }
1300
1301 DMDAVecRestoreArray(da_f, user->user_f->lNvert, &nvert_f);
1302 DMDAVecRestoreArray(da_f, lX, &x);
1303 VecDestroy(&lX);
1304 DMDAVecRestoreArray(da, F, &f);
1305 DMDAVecRestoreArray(da, user->lNvert, &nvert);
1306
1307 return 0;
1308}
static PetscErrorCode GridRestriction(PetscInt i, PetscInt j, PetscInt k, PetscInt *ih, PetscInt *jh, PetscInt *kh, UserCtx *user)
Restrict a fine-grid scalar value onto its associated coarse-grid location.
Definition poisson.c:66
UserCtx * user_f
Definition variables.h:989
DM * da_f
Definition variables.h:990
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MyRestriction()

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

Implementation of MyRestriction().

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

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

See also
MyRestriction()

Definition at line 1317 of file poisson.c.

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

◆ PoissonLHSNew()

PetscErrorCode PoissonLHSNew ( UserCtx user)

Internal helper implementation: PoissonLHSNew().

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

Local to this translation unit.

Definition at line 1423 of file poisson.c.

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

◆ PoissonRHS()

PetscErrorCode PoissonRHS ( UserCtx user,
Vec  B 
)

Implementation of PoissonRHS().

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

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

See also
PoissonRHS()

Definition at line 2032 of file poisson.c.

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

◆ VolumeFlux_rev()

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

Implementation of VolumeFlux_rev().

A specialized version of VolumeFlux, likely for reversed normals.

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

See also
VolumeFlux_rev()

Definition at line 2121 of file poisson.c.

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

◆ VolumeFlux()

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

Implementation of VolumeFlux().

Calculates the net flux across the immersed boundary surface.

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

See also
VolumeFlux()

Definition at line 2363 of file poisson.c.

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

◆ FullyBlocked()

static PetscErrorCode FullyBlocked ( UserCtx user)
static

Report whether a coarse-grid cell is completely blocked by solid fine-grid cells.

Definition at line 2938 of file poisson.c.

2939{
2940 PetscErrorCode ierr;
2941 DM da = user->da;
2942 Vec nNvert;
2943 DMDALocalInfo info = user->info;
2944
2945 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2946
2947 PetscInt i, j, k;
2948
2949 PetscInt *KSKE = user->KSKE;
2950 PetscReal ***nvert;
2951 PetscBool *Blocked;
2952
2953 DMDACreateNaturalVector(da, &nNvert);
2954 DMDAGlobalToNaturalBegin(da, user->Nvert, INSERT_VALUES, nNvert);
2955 DMDAGlobalToNaturalEnd(da, user->Nvert, INSERT_VALUES, nNvert);
2956
2957 VecScatter ctx;
2958 Vec Zvert;
2959 VecScatterCreateToZero(nNvert, &ctx, &Zvert);
2960
2961 VecScatterBegin(ctx, nNvert, Zvert, INSERT_VALUES, SCATTER_FORWARD);
2962 VecScatterEnd(ctx, nNvert, Zvert, INSERT_VALUES, SCATTER_FORWARD);
2963
2964 VecScatterDestroy(&ctx);
2965 VecDestroy(&nNvert);
2966
2967 PetscInt rank;
2968 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRMPI(ierr);
2969
2970 if (!rank) {
2971
2972 VecGetArray3d(Zvert, mz, my, mx, 0, 0, 0, &nvert);
2973 PetscMalloc(mx*my*sizeof(PetscBool), &Blocked);
2974 for (j=1; j<my-1; j++) {
2975 for (i=1; i<mx-1; i++) {
2976 Blocked[j*mx+i] = PETSC_FALSE;
2977 for (k=0; k<mz; k++) {
2978 if (nvert[k][j][i] > 0.1) {
2979 if (!Blocked[j*mx+i]) {
2980 KSKE[2*(j*mx+i)] = k;
2981 Blocked[j*mx+i] = PETSC_TRUE;
2982 }
2983 else {
2984 KSKE[2*(j*mx+i)] = PetscMin(KSKE[2*(j*mx+i)], k);
2985 }
2986 }
2987 }
2988 }
2989 }
2990
2991
2992 user->multinullspace = PETSC_TRUE;
2993 for (j=1; j<my-1; j++) {
2994 for (i=1; i<mx-1; i++) {
2995 if (!Blocked[j*mx+i]) {
2996 user->multinullspace = PETSC_FALSE;
2997 break;
2998 }
2999 }
3000 }
3001 PetscFree(Blocked);
3002 VecRestoreArray3d(Zvert, mz, my, mx, 0, 0, 0, &nvert);
3003 ierr = MPI_Bcast(&user->multinullspace, 1, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRMPI(ierr);
3004 if (user->multinullspace) {
3005 ierr = MPI_Bcast(user->KSKE, 2*mx*my, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRMPI(ierr);
3006
3007 }
3008 }
3009 else {
3010 ierr = MPI_Bcast(&user->multinullspace, 1, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRMPI(ierr);
3011 if (user->multinullspace) {
3012 ierr = MPI_Bcast(user->KSKE, 2*mx*my, MPI_INT, 0, PETSC_COMM_WORLD); CHKERRMPI(ierr);
3013 }
3014 }
3015
3016
3017
3018 VecDestroy(&Zvert);
3019 return 0;
3020}
Vec Nvert
Definition variables.h:939
Here is the caller graph for this function:

◆ MyNvertRestriction()

static PetscErrorCode MyNvertRestriction ( UserCtx user_h,
UserCtx user_c 
)
static

Restrict solid-volume fractions from fine cells to one coarse cell.

Definition at line 3025 of file poisson.c.

3026{
3027 PetscErrorCode ierr;
3028 // DA da = user_c->da, fda = user_c->fda;
3029
3030
3031
3032 DMDALocalInfo info = user_c->info;
3033 PetscInt xs = info.xs, xe = info.xs + info.xm;
3034 PetscInt ys = info.ys, ye = info.ys + info.ym;
3035 PetscInt zs = info.zs, ze = info.zs + info.zm;
3036 PetscInt mx = info.mx, my = info.my, mz = info.mz;
3037
3038 PetscInt i,j,k;
3039 PetscInt ih, jh, kh, ia, ja, ka;
3040 PetscInt lxs, lxe, lys, lye, lzs, lze;
3041
3042 PetscReal ***nvert, ***nvert_h;
3043
3044 DMDAVecGetArray(user_h->da, user_h->lNvert, &nvert_h);
3045 DMDAVecGetArray(user_c->da, user_c->Nvert, &nvert);
3046
3047 lxs = xs; lxe = xe;
3048 lys = ys; lye = ye;
3049 lzs = zs; lze = ze;
3050
3051 if (xs==0) lxs = xs+1;
3052 if (ys==0) lys = ys+1;
3053 if (zs==0) lzs = zs+1;
3054
3055 if (xe==mx) lxe = xe-1;
3056 if (ye==my) lye = ye-1;
3057 if (ze==mz) lze = ze-1;
3058
3059 if ((user_c->isc)) ia = 0;
3060 else ia = 1;
3061
3062 if ((user_c->jsc)) ja = 0;
3063 else ja = 1;
3064
3065 if ((user_c->ksc)) ka = 0;
3066 else ka = 1;
3067
3068 VecSet(user_c->Nvert, 0.);
3069 if (user_c->thislevel > 0) {
3070 for (k=lzs; k<lze; k++) {
3071 for (j=lys; j<lye; j++) {
3072 for (i=lxs; i<lxe; i++) {
3073 GridRestriction(i, j, k, &ih, &jh, &kh, user_c);
3074 if (nvert_h[kh ][jh ][ih ] *
3075 nvert_h[kh ][jh ][ih-ia] *
3076 nvert_h[kh ][jh-ja][ih ] *
3077 nvert_h[kh-ka][jh ][ih ] *
3078 nvert_h[kh ][jh-ja][ih-ia] *
3079 nvert_h[kh-ka][jh ][ih-ia] *
3080 nvert_h[kh-ka][jh-ja][ih ] *
3081 nvert_h[kh-ka][jh-ja][ih-ia] > 0.1) {
3082 nvert[k][j][i] = PetscMax(1., nvert[k][j][i]);
3083 }
3084 }
3085 }
3086 }
3087 }
3088 else {
3089 for (k=lzs; k<lze; k++) {
3090 for (j=lys; j<lye; j++) {
3091 for (i=lxs; i<lxe; i++) {
3092 GridRestriction(i, j, k, &ih, &jh, &kh, user_c);
3093 if (nvert_h[kh ][jh ][ih ] *
3094 nvert_h[kh ][jh ][ih-ia] *
3095 nvert_h[kh ][jh-ja][ih ] *
3096 nvert_h[kh-ka][jh ][ih ] *
3097 nvert_h[kh ][jh-ja][ih-ia] *
3098 nvert_h[kh-ka][jh ][ih-ia] *
3099 nvert_h[kh-ka][jh-ja][ih ] *
3100 nvert_h[kh-ka][jh-ja][ih-ia] > 0.1) {
3101 nvert[k][j][i] = PetscMax(1., nvert[k][j][i]);
3102 }
3103 }
3104 }
3105 }
3106 }
3107 DMDAVecRestoreArray(user_h->da, user_h->lNvert, &nvert_h);
3108 DMDAVecRestoreArray(user_c->da, user_c->Nvert, &nvert);
3109
3110 ierr = UpdateLocalGhosts(user_c, FIELD_ID_NVERT); CHKERRQ(ierr);
3111 //Mohsen Dec 2015
3112 DMDAVecGetArray(user_c->da, user_c->lNvert, &nvert);
3113 DMDAVecGetArray(user_c->da, user_c->Nvert, &nvert_h);
3114
3115 for (k=lzs; k<lze; k++) {
3116 for (j=lys; j<lye; j++) {
3117 for (i=lxs; i<lxe; i++) {
3118 if (nvert_h[k][j][i] < 0.1) {
3119 if (nvert[k][j][i+1] + nvert[k][j][i-1] > 1.1 &&
3120 nvert[k][j+1][i] + nvert[k][j-1][i] > 1.1 &&
3121 nvert[k+1][j][i] + nvert[k-1][j][i] > 1.1) {
3122 nvert_h[k][j][i] = 1.;
3123 }
3124 }
3125 }
3126 }
3127 }
3128
3129 DMDAVecRestoreArray(user_c->da, user_c->lNvert, &nvert);
3130 DMDAVecRestoreArray(user_c->da, user_c->Nvert, &nvert_h);
3131 ierr = UpdateLocalGhosts(user_c, FIELD_ID_NVERT); CHKERRQ(ierr);
3132 /* DMLocalToGlobalBegin(user_c->da, user_c->lNvert, INSERT_VALUES, user_c->Nvert); */
3133/* DMLocalToGlobalEnd(user_c->da, user_c->lNvert, INSERT_VALUES, user_c->Nvert); */
3134 return 0;
3135}
@ FIELD_ID_NVERT
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PoissonSolver_MG()

PetscErrorCode PoissonSolver_MG ( UserMG usermg)

Implementation of PoissonSolver_MG().

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

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

See also
PoissonSolver_MG()

Definition at line 3146 of file poisson.c.

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